diff --git a/pkg/services/object/acl/acl.go b/pkg/services/object/acl/acl.go index a068eadb..87d2f9c8 100644 --- a/pkg/services/object/acl/acl.go +++ b/pkg/services/object/acl/acl.go @@ -14,6 +14,7 @@ import ( bearerSDK "git.frostfs.info/TrueCloudLab/frostfs-sdk-go/bearer" "git.frostfs.info/TrueCloudLab/frostfs-sdk-go/client" "git.frostfs.info/TrueCloudLab/frostfs-sdk-go/container/acl" + cid "git.frostfs.info/TrueCloudLab/frostfs-sdk-go/container/id" frostfsecdsa "git.frostfs.info/TrueCloudLab/frostfs-sdk-go/crypto/ecdsa" eaclSDK "git.frostfs.info/TrueCloudLab/frostfs-sdk-go/eacl" "git.frostfs.info/TrueCloudLab/frostfs-sdk-go/user" @@ -118,8 +119,6 @@ func (c *Checker) StickyBitCheck(info v2.RequestInfo, owner user.ID) bool { } // CheckEACL is a main check function for extended ACL. -// -// nolint: funlen func (c *Checker) CheckEACL(msg any, reqInfo v2.RequestInfo) error { basicACL := reqInfo.BasicACL() if !basicACL.Extendable() { @@ -154,6 +153,44 @@ func (c *Checker) CheckEACL(msg any, reqInfo v2.RequestInfo) error { return err } + hdrSrc, err := c.getHeaderSource(cnr, msg, reqInfo) + if err != nil { + return err + } + + eaclRole := getRole(reqInfo) + + action, _ := c.validator.CalculateAction(new(eaclSDK.ValidationUnit). + WithRole(eaclRole). + WithOperation(eaclSDK.Operation(reqInfo.Operation())). + WithContainerID(&cnr). + WithSenderKey(reqInfo.SenderKey()). + WithHeaderSource(hdrSrc). + WithEACLTable(&table), + ) + + if action != eaclSDK.ActionAllow { + return errEACLDeniedByRule + } + return nil +} + +func getRole(reqInfo v2.RequestInfo) eaclSDK.Role { + var eaclRole eaclSDK.Role + switch op := reqInfo.RequestRole(); op { + default: + eaclRole = eaclSDK.Role(op) + case acl.RoleOwner: + eaclRole = eaclSDK.RoleUser + case acl.RoleInnerRing, acl.RoleContainer: + eaclRole = eaclSDK.RoleSystem + case acl.RoleOthers: + eaclRole = eaclSDK.RoleOthers + } + return eaclRole +} + +func (c *Checker) getHeaderSource(cnr cid.ID, msg any, reqInfo v2.RequestInfo) (eaclSDK.TypedHeaderSource, error) { hdrSrcOpts := make([]eaclV2.Option, 0, 3) hdrSrcOpts = append(hdrSrcOpts, @@ -175,34 +212,9 @@ func (c *Checker) CheckEACL(msg any, reqInfo v2.RequestInfo) error { hdrSrc, err := eaclV2.NewMessageHeaderSource(hdrSrcOpts...) if err != nil { - return fmt.Errorf("can't parse headers: %w", err) + return nil, fmt.Errorf("can't parse headers: %w", err) } - - var eaclRole eaclSDK.Role - switch op := reqInfo.RequestRole(); op { - default: - eaclRole = eaclSDK.Role(op) - case acl.RoleOwner: - eaclRole = eaclSDK.RoleUser - case acl.RoleInnerRing, acl.RoleContainer: - eaclRole = eaclSDK.RoleSystem - case acl.RoleOthers: - eaclRole = eaclSDK.RoleOthers - } - - action, _ := c.validator.CalculateAction(new(eaclSDK.ValidationUnit). - WithRole(eaclRole). - WithOperation(eaclSDK.Operation(reqInfo.Operation())). - WithContainerID(&cnr). - WithSenderKey(reqInfo.SenderKey()). - WithHeaderSource(hdrSrc). - WithEACLTable(&table), - ) - - if action != eaclSDK.ActionAllow { - return errEACLDeniedByRule - } - return nil + return hdrSrc, nil } // isValidBearer checks whether bearer token was correctly signed by authorized diff --git a/pkg/services/object/acl/eacl/v2/headers.go b/pkg/services/object/acl/eacl/v2/headers.go index 63e733cf..736c0576 100644 --- a/pkg/services/object/acl/eacl/v2/headers.go +++ b/pkg/services/object/acl/eacl/v2/headers.go @@ -101,96 +101,103 @@ func requestHeaders(msg xHeaderSource) []eaclSDK.Header { var errMissingOID = errors.New("object ID is missing") -// nolint: funlen func (h *cfg) readObjectHeaders(dst *headerSource) error { switch m := h.msg.(type) { default: panic(fmt.Sprintf("unexpected message type %T", h.msg)) case requestXHeaderSource: - switch req := m.req.(type) { - case - *objectV2.GetRequest, - *objectV2.HeadRequest: - if h.obj == nil { - return errMissingOID - } - - objHeaders, completed := h.localObjectHeaders(h.cnr, h.obj) - - dst.objectHeaders = objHeaders - dst.incompleteObjectHeaders = !completed - case - *objectV2.GetRangeRequest, - *objectV2.GetRangeHashRequest, - *objectV2.DeleteRequest: - if h.obj == nil { - return errMissingOID - } - - dst.objectHeaders = addressHeaders(h.cnr, h.obj) - case *objectV2.PutRequest: - if v, ok := req.GetBody().GetObjectPart().(*objectV2.PutObjectPartInit); ok { - oV2 := new(objectV2.Object) - oV2.SetObjectID(v.GetObjectID()) - oV2.SetHeader(v.GetHeader()) - - dst.objectHeaders = headersFromObject(object.NewFromV2(oV2), h.cnr, h.obj) - } - case *objectV2.SearchRequest: - cnrV2 := req.GetBody().GetContainerID() - var cnr cid.ID - - if cnrV2 != nil { - if err := cnr.ReadFromV2(*cnrV2); err != nil { - return fmt.Errorf("can't parse container ID: %w", err) - } - } - - dst.objectHeaders = []eaclSDK.Header{cidHeader(cnr)} - } + return h.readObjectHeadersFromRequestXHeaderSource(m, dst) case responseXHeaderSource: - switch resp := m.resp.(type) { - default: - objectHeaders, completed := h.localObjectHeaders(h.cnr, h.obj) + return h.readObjectHeadersResponseXHeaderSource(m, dst) + } +} - dst.objectHeaders = objectHeaders - dst.incompleteObjectHeaders = !completed - case *objectV2.GetResponse: - if v, ok := resp.GetBody().GetObjectPart().(*objectV2.GetObjectPartInit); ok { - oV2 := new(objectV2.Object) - oV2.SetObjectID(v.GetObjectID()) - oV2.SetHeader(v.GetHeader()) +func (h *cfg) readObjectHeadersFromRequestXHeaderSource(m requestXHeaderSource, dst *headerSource) error { + switch req := m.req.(type) { + case + *objectV2.GetRequest, + *objectV2.HeadRequest: + if h.obj == nil { + return errMissingOID + } - dst.objectHeaders = headersFromObject(object.NewFromV2(oV2), h.cnr, h.obj) - } - case *objectV2.HeadResponse: + objHeaders, completed := h.localObjectHeaders(h.cnr, h.obj) + + dst.objectHeaders = objHeaders + dst.incompleteObjectHeaders = !completed + case + *objectV2.GetRangeRequest, + *objectV2.GetRangeHashRequest, + *objectV2.DeleteRequest: + if h.obj == nil { + return errMissingOID + } + + dst.objectHeaders = addressHeaders(h.cnr, h.obj) + case *objectV2.PutRequest: + if v, ok := req.GetBody().GetObjectPart().(*objectV2.PutObjectPartInit); ok { oV2 := new(objectV2.Object) - - var hdr *objectV2.Header - - switch v := resp.GetBody().GetHeaderPart().(type) { - case *objectV2.ShortHeader: - hdr = new(objectV2.Header) - - var idV2 refsV2.ContainerID - h.cnr.WriteToV2(&idV2) - - hdr.SetContainerID(&idV2) - hdr.SetVersion(v.GetVersion()) - hdr.SetCreationEpoch(v.GetCreationEpoch()) - hdr.SetOwnerID(v.GetOwnerID()) - hdr.SetObjectType(v.GetObjectType()) - hdr.SetPayloadLength(v.GetPayloadLength()) - case *objectV2.HeaderWithSignature: - hdr = v.GetHeader() - } - - oV2.SetHeader(hdr) + oV2.SetObjectID(v.GetObjectID()) + oV2.SetHeader(v.GetHeader()) dst.objectHeaders = headersFromObject(object.NewFromV2(oV2), h.cnr, h.obj) } - } + case *objectV2.SearchRequest: + cnrV2 := req.GetBody().GetContainerID() + var cnr cid.ID + if cnrV2 != nil { + if err := cnr.ReadFromV2(*cnrV2); err != nil { + return fmt.Errorf("can't parse container ID: %w", err) + } + } + + dst.objectHeaders = []eaclSDK.Header{cidHeader(cnr)} + } + return nil +} + +func (h *cfg) readObjectHeadersResponseXHeaderSource(m responseXHeaderSource, dst *headerSource) error { + switch resp := m.resp.(type) { + default: + objectHeaders, completed := h.localObjectHeaders(h.cnr, h.obj) + + dst.objectHeaders = objectHeaders + dst.incompleteObjectHeaders = !completed + case *objectV2.GetResponse: + if v, ok := resp.GetBody().GetObjectPart().(*objectV2.GetObjectPartInit); ok { + oV2 := new(objectV2.Object) + oV2.SetObjectID(v.GetObjectID()) + oV2.SetHeader(v.GetHeader()) + + dst.objectHeaders = headersFromObject(object.NewFromV2(oV2), h.cnr, h.obj) + } + case *objectV2.HeadResponse: + oV2 := new(objectV2.Object) + + var hdr *objectV2.Header + + switch v := resp.GetBody().GetHeaderPart().(type) { + case *objectV2.ShortHeader: + hdr = new(objectV2.Header) + + var idV2 refsV2.ContainerID + h.cnr.WriteToV2(&idV2) + + hdr.SetContainerID(&idV2) + hdr.SetVersion(v.GetVersion()) + hdr.SetCreationEpoch(v.GetCreationEpoch()) + hdr.SetOwnerID(v.GetOwnerID()) + hdr.SetObjectType(v.GetObjectType()) + hdr.SetPayloadLength(v.GetPayloadLength()) + case *objectV2.HeaderWithSignature: + hdr = v.GetHeader() + } + + oV2.SetHeader(hdr) + + dst.objectHeaders = headersFromObject(object.NewFromV2(oV2), h.cnr, h.obj) + } return nil } diff --git a/pkg/services/object/acl/v2/service.go b/pkg/services/object/acl/v2/service.go index 1e451a99..6544d78d 100644 --- a/pkg/services/object/acl/v2/service.go +++ b/pkg/services/object/acl/v2/service.go @@ -443,7 +443,6 @@ func (b Service) GetRangeHash( return b.next.GetRangeHash(ctx, request) } -// nolint: funlen func (p putStreamBasicChecker) Send(ctx context.Context, request *objectV2.PutRequest) error { body := request.GetBody() if body == nil { @@ -482,27 +481,9 @@ func (p putStreamBasicChecker) Send(ctx context.Context, request *objectV2.PutRe } var sTok *sessionSDK.Object - - if tokV2 := request.GetMetaHeader().GetSessionToken(); tokV2 != nil { - sTok = new(sessionSDK.Object) - - err = sTok.ReadFromV2(*tokV2) - if err != nil { - return fmt.Errorf("invalid session token: %w", err) - } - - if sTok.AssertVerb(sessionSDK.VerbObjectDelete) { - // if session relates to object's removal, we don't check - // relation of the tombstone to the session here since user - // can't predict tomb's ID. - err = assertSessionRelation(*sTok, cnr, nil) - } else { - err = assertSessionRelation(*sTok, cnr, obj) - } - - if err != nil { - return err - } + sTok, err = p.readSessionToken(cnr, obj, request) + if err != nil { + return err } bTok, err := originalBearerToken(request.GetMetaHeader()) @@ -534,6 +515,34 @@ func (p putStreamBasicChecker) Send(ctx context.Context, request *objectV2.PutRe return p.next.Send(ctx, request) } +func (p putStreamBasicChecker) readSessionToken(cnr cid.ID, obj *oid.ID, request *objectV2.PutRequest) (*sessionSDK.Object, error) { + var sTok *sessionSDK.Object + + if tokV2 := request.GetMetaHeader().GetSessionToken(); tokV2 != nil { + sTok = new(sessionSDK.Object) + + err := sTok.ReadFromV2(*tokV2) + if err != nil { + return nil, fmt.Errorf("invalid session token: %w", err) + } + + if sTok.AssertVerb(sessionSDK.VerbObjectDelete) { + // if session relates to object's removal, we don't check + // relation of the tombstone to the session here since user + // can't predict tomb's ID. + err = assertSessionRelation(*sTok, cnr, nil) + } else { + err = assertSessionRelation(*sTok, cnr, obj) + } + + if err != nil { + return nil, err + } + } + + return sTok, nil +} + func (p putStreamBasicChecker) CloseAndRecv(ctx context.Context) (*objectV2.PutResponse, error) { return p.next.CloseAndRecv(ctx) } diff --git a/pkg/services/object/acl/v2/util.go b/pkg/services/object/acl/v2/util.go index 5e3be6e5..aa5d6758 100644 --- a/pkg/services/object/acl/v2/util.go +++ b/pkg/services/object/acl/v2/util.go @@ -166,7 +166,6 @@ func isOwnerFromKey(id user.ID, key *keys.PublicKey) bool { // assertVerb checks that token verb corresponds to op. func assertVerb(tok sessionSDK.Object, op acl.Op) bool { - //nolint:exhaustive switch op { case acl.OpObjectPut: return tok.AssertVerb(sessionSDK.VerbObjectPut, sessionSDK.VerbObjectDelete)