Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 2 additions & 0 deletions pkg/services/object/acl/v2/errors.go
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,8 @@ var (
ErrUnknownRole = errors.New("can't classify request sender")
// ErrUnknownContainer is returned when container fetching errors appeared.
ErrUnknownContainer = errors.New("can't fetch container info")
// ErrInvalidVerb is returned when session token verb doesn't include necessary operation.
ErrInvalidVerb = errors.New("session token verb is invalid")
)

type accessErr struct {
Expand Down
5 changes: 4 additions & 1 deletion pkg/services/object/acl/v2/service.go
Original file line number Diff line number Diff line change
Expand Up @@ -425,7 +425,10 @@ func (b Service) findRequestInfo(
}

// find verb from token if it is present
verb := sourceVerbOfRequest(req, op)
verb, isUnknown := sourceVerbOfRequest(req.token, op)
if !isUnknown && verb != op && !isVerbCompatible(verb, op) {
return info, ErrInvalidVerb
}

info.basicACL = cnr.BasicACL()
info.requestRole = res.role
Expand Down
32 changes: 23 additions & 9 deletions pkg/services/object/acl/v2/util.go
Original file line number Diff line number Diff line change
Expand Up @@ -102,18 +102,17 @@ func getObjectOwnerFromMessage(req interface{}) (id *owner.ID, err error) {
}

// sourceVerbOfRequest looks for verb in session token and if it is not found,
// returns reqVerb.
func sourceVerbOfRequest(req MetaWithToken, reqVerb eaclSDK.Operation) eaclSDK.Operation {
if req.token != nil {
switch v := req.token.Context().(type) {
case *sessionSDK.ObjectContext:
return tokenVerbToOperation(v)
default:
// do nothing, return request verb
// returns reqVerb. Second return value is true if operation is unknown.
func sourceVerbOfRequest(tok *sessionSDK.Token, reqVerb eaclSDK.Operation) (eaclSDK.Operation, bool) {
ctx, ok := tok.Context().(*sessionSDK.ObjectContext)
if ok {
op := tokenVerbToOperation(ctx)
if op != eaclSDK.OperationUnknown {
return op, false
}
}

return reqVerb
return reqVerb, true
}

func useObjectIDFromSession(req *RequestInfo, token *sessionSDK.Token) {
Expand Down Expand Up @@ -195,3 +194,18 @@ func isOwnerFromKey(id *owner.ID, key *keys.PublicKey) bool {

return id.Equal(owner.NewIDFromPublicKey((*ecdsa.PublicKey)(key)))
}

// isVerbCompatible checks that tokenVerb operation can create auxiliary op operation.
func isVerbCompatible(tokenVerb, op eaclSDK.Operation) bool {
switch tokenVerb {
case eaclSDK.OperationGet:
return op == eaclSDK.OperationGet || op == eaclSDK.OperationHead
case eaclSDK.OperationDelete:
return op == eaclSDK.OperationPut || op == eaclSDK.OperationHead ||
op == eaclSDK.OperationSearch
case eaclSDK.OperationRange, eaclSDK.OperationRangeHash:
return op == eaclSDK.OperationRange || op == eaclSDK.OperationHead
default:
return tokenVerb == op
}
}
39 changes: 39 additions & 0 deletions pkg/services/object/acl/v2/util_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,7 @@ import (
acltest "github.com/nspcc-dev/neofs-api-go/v2/acl/test"
"github.com/nspcc-dev/neofs-api-go/v2/session"
sessiontest "github.com/nspcc-dev/neofs-api-go/v2/session/test"
"github.com/nspcc-dev/neofs-sdk-go/eacl"
sessionSDK "github.com/nspcc-dev/neofs-sdk-go/session"
bearerSDK "github.com/nspcc-dev/neofs-sdk-go/token"
"github.com/stretchr/testify/require"
Expand Down Expand Up @@ -36,3 +37,41 @@ func testGenerateMetaHeader(depth uint32, b *acl.BearerToken, s *session.Session

return metaHeader
}

func TestIsVerbCompatible(t *testing.T) {
// Source: https://nspcc.ru/upload/neofs-spec-latest.pdf#page=28
table := map[eacl.Operation][]eacl.Operation{
eacl.OperationPut: {eacl.OperationPut},
eacl.OperationDelete: {eacl.OperationPut, eacl.OperationHead, eacl.OperationSearch},
eacl.OperationHead: {eacl.OperationHead},
eacl.OperationRange: {eacl.OperationRange, eacl.OperationHead},
eacl.OperationRangeHash: {eacl.OperationRange, eacl.OperationHead},
eacl.OperationGet: {eacl.OperationGet, eacl.OperationHead},
eacl.OperationSearch: {eacl.OperationSearch},
}

ops := []eacl.Operation{
eacl.OperationPut,
eacl.OperationDelete,
eacl.OperationHead,
eacl.OperationRange,
eacl.OperationRangeHash,
eacl.OperationGet,
eacl.OperationSearch,
}

for _, opToken := range ops {
for _, op := range ops {
var contains bool
for _, o := range table[opToken] {
if o == op {
contains = true
break
}
}

require.Equal(t, contains, isVerbCompatible(opToken, op),
"%s in token, %s executing", opToken, op)
}
}
}