forked from TrueCloudLab/frostfs-node
[#32] Use classifier in basic ACL check
Signed-off-by: Alex Vanin <alexey@nspcc.ru>
This commit is contained in:
parent
5045b0c3d4
commit
ad36a2cd8f
3 changed files with 201 additions and 62 deletions
BIN
go.sum
BIN
go.sum
Binary file not shown.
|
@ -12,6 +12,7 @@ import (
|
|||
|
||||
type (
|
||||
// ContainerGetter accesses NeoFS container storage.
|
||||
// fixme: use core.container interface implementation
|
||||
ContainerGetter interface {
|
||||
Get(*refs.ContainerID) (*container.Container, error)
|
||||
}
|
||||
|
@ -22,41 +23,52 @@ type (
|
|||
|
||||
// BasicChecker checks basic ACL rules.
|
||||
BasicChecker struct {
|
||||
sender SenderClassifier
|
||||
next object.Service
|
||||
containers ContainerGetter
|
||||
sender SenderClassifier
|
||||
next object.Service
|
||||
}
|
||||
|
||||
putStreamBasicChecker struct {
|
||||
sender SenderClassifier
|
||||
source *BasicChecker
|
||||
next object.PutObjectStreamer
|
||||
}
|
||||
|
||||
getStreamBasicChecker struct {
|
||||
sender SenderClassifier
|
||||
next object.GetObjectStreamer
|
||||
object.GetObjectStreamer
|
||||
}
|
||||
|
||||
searchStreamBasicChecker struct {
|
||||
sender SenderClassifier
|
||||
next object.SearchObjectStreamer
|
||||
object.SearchObjectStreamer
|
||||
}
|
||||
|
||||
getRangeStreamBasicChecker struct {
|
||||
sender SenderClassifier
|
||||
next object.GetRangeObjectStreamer
|
||||
object.GetRangeObjectStreamer
|
||||
}
|
||||
|
||||
requestInfo struct {
|
||||
basicACL uint32
|
||||
requestRole acl.Role
|
||||
operation acl.Operation // put, get, head, etc.
|
||||
}
|
||||
)
|
||||
|
||||
var (
|
||||
ErrMalformedRequest = errors.New("malformed request")
|
||||
ErrUnknownRole = errors.New("can't classify request sender")
|
||||
ErrMalformedRequest = errors.New("malformed request")
|
||||
ErrUnknownRole = errors.New("can't classify request sender")
|
||||
ErrUnknownContainer = errors.New("can't fetch container info")
|
||||
ErrBasicAccessDenied = errors.New("access denied by basic ACL")
|
||||
)
|
||||
|
||||
// NewBasicChecker is a constructor for basic ACL checker of object requests.
|
||||
func NewBasicChecker(c SenderClassifier, next object.Service) BasicChecker {
|
||||
func NewBasicChecker(
|
||||
c SenderClassifier,
|
||||
cnr ContainerGetter,
|
||||
next object.Service) BasicChecker {
|
||||
|
||||
return BasicChecker{
|
||||
sender: c,
|
||||
next: next,
|
||||
containers: cnr,
|
||||
sender: c,
|
||||
next: next,
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -64,31 +76,29 @@ func (b BasicChecker) Get(
|
|||
ctx context.Context,
|
||||
request *object.GetRequest) (object.GetObjectStreamer, error) {
|
||||
|
||||
// get container address and do not panic at malformed request
|
||||
var addr *refs.Address
|
||||
if body := request.GetBody(); body == nil {
|
||||
return nil, ErrMalformedRequest
|
||||
} else {
|
||||
addr = body.GetAddress()
|
||||
cid, err := getContainerIDFromRequest(request)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
role := b.sender.Classify(request, addr.GetContainerID())
|
||||
if role == acl.RoleUnknown {
|
||||
return nil, ErrUnknownRole
|
||||
reqInfo, err := b.findRequestInfo(request, cid, acl.OperationGet)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
if !basicACLCheck(reqInfo) {
|
||||
return nil, ErrBasicAccessDenied
|
||||
}
|
||||
|
||||
stream, err := b.next.Get(ctx, request)
|
||||
return getStreamBasicChecker{
|
||||
sender: b.sender,
|
||||
next: stream,
|
||||
}, err
|
||||
return getStreamBasicChecker{stream}, err
|
||||
}
|
||||
|
||||
func (b BasicChecker) Put(ctx context.Context) (object.PutObjectStreamer, error) {
|
||||
streamer, err := b.next.Put(ctx)
|
||||
|
||||
return putStreamBasicChecker{
|
||||
sender: b.sender,
|
||||
source: &b,
|
||||
next: streamer,
|
||||
}, err
|
||||
}
|
||||
|
@ -97,6 +107,20 @@ func (b BasicChecker) Head(
|
|||
ctx context.Context,
|
||||
request *object.HeadRequest) (*object.HeadResponse, error) {
|
||||
|
||||
cid, err := getContainerIDFromRequest(request)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
reqInfo, err := b.findRequestInfo(request, cid, acl.OperationHead)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
if !basicACLCheck(reqInfo) {
|
||||
return nil, ErrBasicAccessDenied
|
||||
}
|
||||
|
||||
return b.next.Head(ctx, request)
|
||||
}
|
||||
|
||||
|
@ -104,17 +128,44 @@ func (b BasicChecker) Search(
|
|||
ctx context.Context,
|
||||
request *object.SearchRequest) (object.SearchObjectStreamer, error) {
|
||||
|
||||
var cid *refs.ContainerID
|
||||
|
||||
cid, err := getContainerIDFromRequest(request)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
reqInfo, err := b.findRequestInfo(request, cid, acl.OperationSearch)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
if !basicACLCheck(reqInfo) {
|
||||
return nil, ErrBasicAccessDenied
|
||||
}
|
||||
|
||||
stream, err := b.next.Search(ctx, request)
|
||||
return searchStreamBasicChecker{
|
||||
sender: b.sender,
|
||||
next: stream,
|
||||
}, err
|
||||
return searchStreamBasicChecker{stream}, err
|
||||
}
|
||||
|
||||
func (b BasicChecker) Delete(
|
||||
ctx context.Context,
|
||||
request *object.DeleteRequest) (*object.DeleteResponse, error) {
|
||||
|
||||
cid, err := getContainerIDFromRequest(request)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
reqInfo, err := b.findRequestInfo(request, cid, acl.OperationDelete)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
if !basicACLCheck(reqInfo) {
|
||||
return nil, ErrBasicAccessDenied
|
||||
}
|
||||
|
||||
return b.next.Delete(ctx, request)
|
||||
}
|
||||
|
||||
|
@ -122,21 +173,68 @@ func (b BasicChecker) GetRange(
|
|||
ctx context.Context,
|
||||
request *object.GetRangeRequest) (object.GetRangeObjectStreamer, error) {
|
||||
|
||||
cid, err := getContainerIDFromRequest(request)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
reqInfo, err := b.findRequestInfo(request, cid, acl.OperationRange)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
if !basicACLCheck(reqInfo) {
|
||||
return nil, ErrBasicAccessDenied
|
||||
}
|
||||
|
||||
stream, err := b.next.GetRange(ctx, request)
|
||||
return getRangeStreamBasicChecker{
|
||||
sender: b.sender,
|
||||
next: stream,
|
||||
}, err
|
||||
return getRangeStreamBasicChecker{stream}, err
|
||||
}
|
||||
|
||||
func (b BasicChecker) GetRangeHash(
|
||||
ctx context.Context,
|
||||
request *object.GetRangeHashRequest) (*object.GetRangeHashResponse, error) {
|
||||
|
||||
cid, err := getContainerIDFromRequest(request)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
reqInfo, err := b.findRequestInfo(request, cid, acl.OperationRangeHash)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
if !basicACLCheck(reqInfo) {
|
||||
return nil, ErrBasicAccessDenied
|
||||
}
|
||||
|
||||
return b.next.GetRangeHash(ctx, request)
|
||||
}
|
||||
|
||||
func (p putStreamBasicChecker) Send(request *object.PutRequest) error {
|
||||
body := request.GetBody()
|
||||
if body == nil {
|
||||
return ErrMalformedRequest
|
||||
}
|
||||
|
||||
part := body.GetObjectPart()
|
||||
if _, ok := part.(*object.PutObjectPartInit); ok {
|
||||
cid, err := getContainerIDFromRequest(request)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
reqInfo, err := p.source.findRequestInfo(request, cid, acl.OperationPut)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
if !basicACLCheck(reqInfo) {
|
||||
return ErrBasicAccessDenied
|
||||
}
|
||||
}
|
||||
|
||||
return p.next.Send(request)
|
||||
}
|
||||
|
||||
|
@ -144,14 +242,63 @@ func (p putStreamBasicChecker) CloseAndRecv() (*object.PutResponse, error) {
|
|||
return p.next.CloseAndRecv()
|
||||
}
|
||||
|
||||
func (g getStreamBasicChecker) Recv() (*object.GetResponse, error) {
|
||||
return g.next.Recv()
|
||||
func (b BasicChecker) findRequestInfo(
|
||||
req RequestV2,
|
||||
cid *refs.ContainerID,
|
||||
op acl.Operation) (info requestInfo, err error) {
|
||||
|
||||
// fetch actual container
|
||||
cnr, err := b.containers.Get(cid)
|
||||
if err != nil || cnr.GetOwnerID() == nil {
|
||||
return info, ErrUnknownContainer
|
||||
}
|
||||
|
||||
// find request role
|
||||
role := b.sender.Classify(req, cid, cnr)
|
||||
if role == acl.RoleUnknown {
|
||||
return info, ErrUnknownRole
|
||||
}
|
||||
|
||||
info.basicACL = cnr.GetBasicACL()
|
||||
info.requestRole = role
|
||||
info.operation = op
|
||||
|
||||
return info, nil
|
||||
}
|
||||
|
||||
func (s searchStreamBasicChecker) Recv() (*object.SearchResponse, error) {
|
||||
return s.next.Recv()
|
||||
func getContainerIDFromRequest(req interface{}) (id *refs.ContainerID, err error) {
|
||||
defer func() {
|
||||
// if there is a NPE on get body and get address
|
||||
if r := recover(); r != nil {
|
||||
err = ErrMalformedRequest
|
||||
}
|
||||
}()
|
||||
|
||||
switch v := req.(type) {
|
||||
case *object.GetRequest:
|
||||
return v.GetBody().GetAddress().GetContainerID(), nil
|
||||
case *object.PutRequest:
|
||||
objPart := v.GetBody().GetObjectPart()
|
||||
if part, ok := objPart.(*object.PutObjectPartInit); ok {
|
||||
return part.GetHeader().GetContainerID(), nil
|
||||
} else {
|
||||
return nil, errors.New("can't get cid in chunk")
|
||||
}
|
||||
case *object.HeadRequest:
|
||||
return v.GetBody().GetAddress().GetContainerID(), nil
|
||||
case *object.SearchRequest:
|
||||
return v.GetBody().GetContainerID(), nil
|
||||
case *object.DeleteRequest:
|
||||
return v.GetBody().GetAddress().GetContainerID(), nil
|
||||
case *object.GetRangeRequest:
|
||||
return v.GetBody().GetAddress().GetContainerID(), nil
|
||||
case *object.GetRangeHashRequest:
|
||||
return v.GetBody().GetAddress().GetContainerID(), nil
|
||||
default:
|
||||
return nil, errors.New("unknown request type")
|
||||
}
|
||||
}
|
||||
|
||||
func (g getRangeStreamBasicChecker) Recv() (*object.GetRangeResponse, error) {
|
||||
return g.next.Recv()
|
||||
func basicACLCheck(info requestInfo) bool {
|
||||
panic("implement me")
|
||||
}
|
||||
|
|
|
@ -15,12 +15,6 @@ import (
|
|||
)
|
||||
|
||||
type (
|
||||
// ContainerFetcher accesses NeoFS container storage.
|
||||
// fixme: use core.container interface implementation
|
||||
ContainerFetcher interface {
|
||||
Fetch(*refs.ContainerID) (*container.Container, error)
|
||||
}
|
||||
|
||||
// fixme: use core.netmap interface implementation
|
||||
NetmapFetcher interface {
|
||||
Current() (netmap.Netmap, error)
|
||||
|
@ -37,18 +31,23 @@ type (
|
|||
}
|
||||
|
||||
SenderClassifier struct {
|
||||
containers ContainerFetcher
|
||||
innerRing InnerRingFetcher
|
||||
netmap NetmapFetcher
|
||||
innerRing InnerRingFetcher
|
||||
netmap NetmapFetcher
|
||||
}
|
||||
)
|
||||
|
||||
// fixme: update classifier constructor
|
||||
func NewSenderClassifier() SenderClassifier {
|
||||
return SenderClassifier{}
|
||||
func NewSenderClassifier(ir InnerRingFetcher, nm NetmapFetcher) SenderClassifier {
|
||||
return SenderClassifier{
|
||||
innerRing: ir,
|
||||
netmap: nm,
|
||||
}
|
||||
}
|
||||
|
||||
func (c SenderClassifier) Classify(req RequestV2, cid *refs.ContainerID) acl.Role {
|
||||
func (c SenderClassifier) Classify(
|
||||
req RequestV2,
|
||||
cid *refs.ContainerID,
|
||||
cnr *container.Container) acl.Role {
|
||||
if cid == nil || req == nil {
|
||||
// log there
|
||||
return acl.RoleUnknown
|
||||
|
@ -62,15 +61,8 @@ func (c SenderClassifier) Classify(req RequestV2, cid *refs.ContainerID) acl.Rol
|
|||
|
||||
// todo: get owner from neofs.id if present
|
||||
|
||||
// fetch actual container
|
||||
cnr, err := c.containers.Fetch(cid)
|
||||
if err != nil || cnr.GetOwnerID() == nil {
|
||||
// log there
|
||||
return acl.RoleUnknown
|
||||
}
|
||||
|
||||
// if request owner is the same as container owner, return RoleUser
|
||||
if bytes.Equal(cnr.GetOwnerID().GetValue(), cid.GetValue()) {
|
||||
if bytes.Equal(cnr.GetOwnerID().GetValue(), ownerID.GetValue()) {
|
||||
return acl.RoleUser
|
||||
}
|
||||
|
||||
|
|
Loading…
Reference in a new issue