[#32] Use classifier in basic ACL check

Signed-off-by: Alex Vanin <alexey@nspcc.ru>
This commit is contained in:
Alex Vanin 2020-09-21 19:12:52 +03:00
parent 5045b0c3d4
commit ad36a2cd8f
3 changed files with 202 additions and 62 deletions

1
go.sum
View file

@ -6,6 +6,7 @@ cloud.google.com/go v0.38.0/go.mod h1:990N+gfupTy94rShfmMCWGDn0LpTmnzTp2qbd1dvSR
cloud.google.com/go v0.44.1/go.mod h1:iSa0KzasP4Uvy3f1mN/7PiObzGgflwredwwASm/v6AU= cloud.google.com/go v0.44.1/go.mod h1:iSa0KzasP4Uvy3f1mN/7PiObzGgflwredwwASm/v6AU=
cloud.google.com/go v0.44.2/go.mod h1:60680Gw3Yr4ikxnPRS/oxxkBccT6SA1yMk63TGekxKY= cloud.google.com/go v0.44.2/go.mod h1:60680Gw3Yr4ikxnPRS/oxxkBccT6SA1yMk63TGekxKY=
cloud.google.com/go v0.45.1/go.mod h1:RpBamKRgapWJb87xiFSdk4g1CME7QZg3uwTez+TSTjc= cloud.google.com/go v0.45.1/go.mod h1:RpBamKRgapWJb87xiFSdk4g1CME7QZg3uwTez+TSTjc=
cloud.google.com/go v0.46.3 h1:AVXDdKsrtX33oR9fbCMu/+c1o8Ofjq6Ku/MInaLVg5Y=
cloud.google.com/go v0.46.3/go.mod h1:a6bKKbmY7er1mI7TEI4lsAkts/mkhTSZK8w33B4RAg0= cloud.google.com/go v0.46.3/go.mod h1:a6bKKbmY7er1mI7TEI4lsAkts/mkhTSZK8w33B4RAg0=
cloud.google.com/go/bigquery v1.0.1/go.mod h1:i/xbL2UlR5RvWAURpBYZTtm/cXjCha9lbfbpx4poX+o= cloud.google.com/go/bigquery v1.0.1/go.mod h1:i/xbL2UlR5RvWAURpBYZTtm/cXjCha9lbfbpx4poX+o=
cloud.google.com/go/datastore v1.0.0/go.mod h1:LXYbyblFSglQ5pkeyhO+Qmw7ukd3C+pD7TKLgZqpHYE= cloud.google.com/go/datastore v1.0.0/go.mod h1:LXYbyblFSglQ5pkeyhO+Qmw7ukd3C+pD7TKLgZqpHYE=

View file

@ -12,6 +12,7 @@ import (
type ( type (
// ContainerGetter accesses NeoFS container storage. // ContainerGetter accesses NeoFS container storage.
// fixme: use core.container interface implementation
ContainerGetter interface { ContainerGetter interface {
Get(*refs.ContainerID) (*container.Container, error) Get(*refs.ContainerID) (*container.Container, error)
} }
@ -22,41 +23,52 @@ type (
// BasicChecker checks basic ACL rules. // BasicChecker checks basic ACL rules.
BasicChecker struct { BasicChecker struct {
sender SenderClassifier containers ContainerGetter
next object.Service sender SenderClassifier
next object.Service
} }
putStreamBasicChecker struct { putStreamBasicChecker struct {
sender SenderClassifier source *BasicChecker
next object.PutObjectStreamer next object.PutObjectStreamer
} }
getStreamBasicChecker struct { getStreamBasicChecker struct {
sender SenderClassifier object.GetObjectStreamer
next object.GetObjectStreamer
} }
searchStreamBasicChecker struct { searchStreamBasicChecker struct {
sender SenderClassifier object.SearchObjectStreamer
next object.SearchObjectStreamer
} }
getRangeStreamBasicChecker struct { getRangeStreamBasicChecker struct {
sender SenderClassifier object.GetRangeObjectStreamer
next object.GetRangeObjectStreamer }
requestInfo struct {
basicACL uint32
requestRole acl.Role
operation acl.Operation // put, get, head, etc.
} }
) )
var ( var (
ErrMalformedRequest = errors.New("malformed request") ErrMalformedRequest = errors.New("malformed request")
ErrUnknownRole = errors.New("can't classify request sender") 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. // 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{ return BasicChecker{
sender: c, containers: cnr,
next: next, sender: c,
next: next,
} }
} }
@ -64,31 +76,29 @@ func (b BasicChecker) Get(
ctx context.Context, ctx context.Context,
request *object.GetRequest) (object.GetObjectStreamer, error) { request *object.GetRequest) (object.GetObjectStreamer, error) {
// get container address and do not panic at malformed request cid, err := getContainerIDFromRequest(request)
var addr *refs.Address if err != nil {
if body := request.GetBody(); body == nil { return nil, err
return nil, ErrMalformedRequest
} else {
addr = body.GetAddress()
} }
role := b.sender.Classify(request, addr.GetContainerID()) reqInfo, err := b.findRequestInfo(request, cid, acl.OperationGet)
if role == acl.RoleUnknown { if err != nil {
return nil, ErrUnknownRole return nil, err
}
if !basicACLCheck(reqInfo) {
return nil, ErrBasicAccessDenied
} }
stream, err := b.next.Get(ctx, request) stream, err := b.next.Get(ctx, request)
return getStreamBasicChecker{ return getStreamBasicChecker{stream}, err
sender: b.sender,
next: stream,
}, err
} }
func (b BasicChecker) Put(ctx context.Context) (object.PutObjectStreamer, error) { func (b BasicChecker) Put(ctx context.Context) (object.PutObjectStreamer, error) {
streamer, err := b.next.Put(ctx) streamer, err := b.next.Put(ctx)
return putStreamBasicChecker{ return putStreamBasicChecker{
sender: b.sender, source: &b,
next: streamer, next: streamer,
}, err }, err
} }
@ -97,6 +107,20 @@ func (b BasicChecker) Head(
ctx context.Context, ctx context.Context,
request *object.HeadRequest) (*object.HeadResponse, error) { 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) return b.next.Head(ctx, request)
} }
@ -104,17 +128,44 @@ func (b BasicChecker) Search(
ctx context.Context, ctx context.Context,
request *object.SearchRequest) (object.SearchObjectStreamer, error) { 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) stream, err := b.next.Search(ctx, request)
return searchStreamBasicChecker{ return searchStreamBasicChecker{stream}, err
sender: b.sender,
next: stream,
}, err
} }
func (b BasicChecker) Delete( func (b BasicChecker) Delete(
ctx context.Context, ctx context.Context,
request *object.DeleteRequest) (*object.DeleteResponse, error) { 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) return b.next.Delete(ctx, request)
} }
@ -122,21 +173,68 @@ func (b BasicChecker) GetRange(
ctx context.Context, ctx context.Context,
request *object.GetRangeRequest) (object.GetRangeObjectStreamer, error) { 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) stream, err := b.next.GetRange(ctx, request)
return getRangeStreamBasicChecker{ return getRangeStreamBasicChecker{stream}, err
sender: b.sender,
next: stream,
}, err
} }
func (b BasicChecker) GetRangeHash( func (b BasicChecker) GetRangeHash(
ctx context.Context, ctx context.Context,
request *object.GetRangeHashRequest) (*object.GetRangeHashResponse, error) { 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) return b.next.GetRangeHash(ctx, request)
} }
func (p putStreamBasicChecker) Send(request *object.PutRequest) error { 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) return p.next.Send(request)
} }
@ -144,14 +242,63 @@ func (p putStreamBasicChecker) CloseAndRecv() (*object.PutResponse, error) {
return p.next.CloseAndRecv() return p.next.CloseAndRecv()
} }
func (g getStreamBasicChecker) Recv() (*object.GetResponse, error) { func (b BasicChecker) findRequestInfo(
return g.next.Recv() 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) { func getContainerIDFromRequest(req interface{}) (id *refs.ContainerID, err error) {
return s.next.Recv() 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) { func basicACLCheck(info requestInfo) bool {
return g.next.Recv() panic("implement me")
} }

View file

@ -15,12 +15,6 @@ import (
) )
type ( 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 // fixme: use core.netmap interface implementation
NetmapFetcher interface { NetmapFetcher interface {
Current() (netmap.Netmap, error) Current() (netmap.Netmap, error)
@ -37,18 +31,23 @@ type (
} }
SenderClassifier struct { SenderClassifier struct {
containers ContainerFetcher innerRing InnerRingFetcher
innerRing InnerRingFetcher netmap NetmapFetcher
netmap NetmapFetcher
} }
) )
// fixme: update classifier constructor // fixme: update classifier constructor
func NewSenderClassifier() SenderClassifier { func NewSenderClassifier(ir InnerRingFetcher, nm NetmapFetcher) SenderClassifier {
return 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 { if cid == nil || req == nil {
// log there // log there
return acl.RoleUnknown 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 // 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 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 return acl.RoleUser
} }