diff --git a/api/handler/acl.go b/api/handler/acl.go index 228230227..0c644e3f0 100644 --- a/api/handler/acl.go +++ b/api/handler/acl.go @@ -698,9 +698,10 @@ func mergeAst(parent, child *ast) (*ast, bool) { var newOps []*astOperation for _, astOp := range resource.Operations { + // get parent matched operations ops := getAstOps(parentResource, astOp) switch len(ops) { - case 2: + case 2: // parent contains different actions for the same child operation // potential inconsistency if groupGrantee := astOp.IsGroupGrantee(); groupGrantee { // it is not likely (such state must be detected early) @@ -725,13 +726,12 @@ func mergeAst(parent, child *ast) (*ast, bool) { if handleRemoveOperations(parentResource, astOp, opToDelete) { updated = true } - case 1: + case 1: // parent contains some action for the same child operation if astOp.Action != ops[0].Action { // potential inconsistency if groupGrantee := astOp.IsGroupGrantee(); groupGrantee { // inconsistency - removeAstOp(parentResource, groupGrantee, astOp.Op, ops[0].Action) - parentResource.Operations = append(parentResource.Operations, astOp) + ops[0].Action = astOp.Action updated = true continue } @@ -746,7 +746,7 @@ func mergeAst(parent, child *ast) (*ast, bool) { if handleAddOperations(parentResource, astOp, ops[0]) { updated = true } - case 0: + case 0: // parent doesn't contain actions for the same child operation newOps = append(newOps, astOp) updated = true } diff --git a/api/handler/acl_test.go b/api/handler/acl_test.go index 28a12bba7..181893686 100644 --- a/api/handler/acl_test.go +++ b/api/handler/acl_test.go @@ -1,6 +1,7 @@ package handler import ( + "context" "crypto/ecdsa" "crypto/rand" "crypto/sha256" @@ -12,9 +13,13 @@ import ( "github.com/nspcc-dev/neo-go/pkg/crypto/keys" "github.com/nspcc-dev/neofs-s3-gw/api" + "github.com/nspcc-dev/neofs-s3-gw/api/data" + "github.com/nspcc-dev/neofs-s3-gw/creds/accessbox" + "github.com/nspcc-dev/neofs-sdk-go/bearer" "github.com/nspcc-dev/neofs-sdk-go/eacl" "github.com/nspcc-dev/neofs-sdk-go/object" oid "github.com/nspcc-dev/neofs-sdk-go/object/id" + "github.com/nspcc-dev/neofs-sdk-go/session" "github.com/stretchr/testify/require" ) @@ -1287,3 +1292,81 @@ func TestBucketAclToAst(t *testing.T) { require.NoError(t, err) require.Equal(t, expectedAst, actualAst) } + +func TestPutBucketACL(t *testing.T) { + tc := prepareHandlerContext(t) + bktName := "bucket-for-acl" + + box := createAccessBox(t) + bktInfo := createBucket(t, tc, bktName, box) + + header := map[string]string{api.AmzACL: "public-read"} + putBucketACL(t, tc, bktName, box, header) + + header = map[string]string{api.AmzACL: "private"} + putBucketACL(t, tc, bktName, box, header) + checkLastRecords(t, tc, bktInfo, eacl.ActionDeny) +} + +func checkLastRecords(t *testing.T, tc *handlerContext, bktInfo *data.BucketInfo, action eacl.Action) { + bktACL, err := tc.Layer().GetBucketACL(tc.Context(), bktInfo) + require.NoError(t, err) + + length := len(bktACL.EACL.Records()) + + if length < 7 { + t.Fatalf("length of records is less than 7: '%d'", length) + } + + for _, rec := range bktACL.EACL.Records()[length-7:] { + if rec.Action() != action || rec.Targets()[0].Role() != eacl.RoleOthers { + t.Fatalf("inavid last record: '%s', '%s', '%s',", rec.Action(), rec.Operation(), rec.Targets()[0].Role()) + } + } +} + +func createAccessBox(t *testing.T) *accessbox.Box { + key, err := keys.NewPrivateKey() + require.NoError(t, err) + + var bearerToken bearer.Token + err = bearerToken.Sign(key.PrivateKey) + require.NoError(t, err) + + tok := new(session.Container) + tok.ForVerb(session.VerbContainerSetEACL) + + tok2 := new(session.Container) + tok2.ForVerb(session.VerbContainerPut) + box := &accessbox.Box{ + Gate: &accessbox.GateData{ + SessionTokens: []*session.Container{tok, tok2}, + BearerToken: &bearerToken, + }, + } + + return box +} + +func createBucket(t *testing.T, tc *handlerContext, bktName string, box *accessbox.Box) *data.BucketInfo { + w, r := prepareTestRequest(t, bktName, "", nil) + ctx := context.WithValue(r.Context(), api.BoxData, box) + r = r.WithContext(ctx) + tc.Handler().CreateBucketHandler(w, r) + assertStatus(t, w, http.StatusOK) + + bktInfo, err := tc.Layer().GetBucketInfo(tc.Context(), bktName) + require.NoError(t, err) + return bktInfo +} + +func putBucketACL(t *testing.T, tc *handlerContext, bktName string, box *accessbox.Box, header map[string]string) { + w, r := prepareTestRequest(t, bktName, "", nil) + for key, val := range header { + r.Header.Set(key, val) + } + ctx := context.WithValue(r.Context(), api.BoxData, box) + r = r.WithContext(ctx) + tc.Handler().PutBucketACLHandler(w, r) + assertStatus(t, w, http.StatusOK) +} diff --git a/api/layer/neofs_mock.go b/api/layer/neofs_mock.go index 8687e499a..d5b674b59 100644 --- a/api/layer/neofs_mock.go +++ b/api/layer/neofs_mock.go @@ -5,6 +5,7 @@ import ( "context" "crypto/rand" "crypto/sha256" + "errors" "fmt" "io" "time" @@ -13,6 +14,7 @@ import ( "github.com/nspcc-dev/neofs-sdk-go/checksum" "github.com/nspcc-dev/neofs-sdk-go/container" cid "github.com/nspcc-dev/neofs-sdk-go/container/id" + "github.com/nspcc-dev/neofs-sdk-go/eacl" "github.com/nspcc-dev/neofs-sdk-go/object" oid "github.com/nspcc-dev/neofs-sdk-go/object/id" "github.com/nspcc-dev/neofs-sdk-go/session" @@ -24,6 +26,7 @@ type TestNeoFS struct { objects map[string]*object.Object containers map[string]*container.Container + eaclTables map[string]*eacl.Table currentEpoch uint64 } @@ -31,6 +34,7 @@ func NewTestNeoFS() *TestNeoFS { return &TestNeoFS{ objects: make(map[string]*object.Object), containers: make(map[string]*container.Container), + eaclTables: make(map[string]*eacl.Table), } } @@ -232,3 +236,27 @@ func (t *TestNeoFS) AllObjects(cnrID cid.ID) []oid.ID { return result } + +func (t *TestNeoFS) SetContainerEACL(_ context.Context, table eacl.Table, _ *session.Container) error { + cnrID, ok := table.CID() + if !ok { + return errors.New("invalid cid") + } + + if _, ok = t.containers[cnrID.EncodeToString()]; !ok { + return errors.New("not found") + } + + t.eaclTables[cnrID.EncodeToString()] = &table + + return nil +} + +func (t *TestNeoFS) ContainerEACL(_ context.Context, cnrID cid.ID) (*eacl.Table, error) { + table, ok := t.eaclTables[cnrID.EncodeToString()] + if !ok { + return nil, errors.New("not found") + } + + return table, nil +}