[#606] Keep eacl records order on conflict
Signed-off-by: Denis Kirillov <denis@nspcc.ru>
This commit is contained in:
parent
ffd259671a
commit
4082cd6b54
3 changed files with 116 additions and 5 deletions
|
@ -698,9 +698,10 @@ func mergeAst(parent, child *ast) (*ast, bool) {
|
||||||
|
|
||||||
var newOps []*astOperation
|
var newOps []*astOperation
|
||||||
for _, astOp := range resource.Operations {
|
for _, astOp := range resource.Operations {
|
||||||
|
// get parent matched operations
|
||||||
ops := getAstOps(parentResource, astOp)
|
ops := getAstOps(parentResource, astOp)
|
||||||
switch len(ops) {
|
switch len(ops) {
|
||||||
case 2:
|
case 2: // parent contains different actions for the same child operation
|
||||||
// potential inconsistency
|
// potential inconsistency
|
||||||
if groupGrantee := astOp.IsGroupGrantee(); groupGrantee {
|
if groupGrantee := astOp.IsGroupGrantee(); groupGrantee {
|
||||||
// it is not likely (such state must be detected early)
|
// 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) {
|
if handleRemoveOperations(parentResource, astOp, opToDelete) {
|
||||||
updated = true
|
updated = true
|
||||||
}
|
}
|
||||||
case 1:
|
case 1: // parent contains some action for the same child operation
|
||||||
if astOp.Action != ops[0].Action {
|
if astOp.Action != ops[0].Action {
|
||||||
// potential inconsistency
|
// potential inconsistency
|
||||||
if groupGrantee := astOp.IsGroupGrantee(); groupGrantee {
|
if groupGrantee := astOp.IsGroupGrantee(); groupGrantee {
|
||||||
// inconsistency
|
// inconsistency
|
||||||
removeAstOp(parentResource, groupGrantee, astOp.Op, ops[0].Action)
|
ops[0].Action = astOp.Action
|
||||||
parentResource.Operations = append(parentResource.Operations, astOp)
|
|
||||||
updated = true
|
updated = true
|
||||||
continue
|
continue
|
||||||
}
|
}
|
||||||
|
@ -746,7 +746,7 @@ func mergeAst(parent, child *ast) (*ast, bool) {
|
||||||
if handleAddOperations(parentResource, astOp, ops[0]) {
|
if handleAddOperations(parentResource, astOp, ops[0]) {
|
||||||
updated = true
|
updated = true
|
||||||
}
|
}
|
||||||
case 0:
|
case 0: // parent doesn't contain actions for the same child operation
|
||||||
newOps = append(newOps, astOp)
|
newOps = append(newOps, astOp)
|
||||||
updated = true
|
updated = true
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,6 +1,7 @@
|
||||||
package handler
|
package handler
|
||||||
|
|
||||||
import (
|
import (
|
||||||
|
"context"
|
||||||
"crypto/ecdsa"
|
"crypto/ecdsa"
|
||||||
"crypto/rand"
|
"crypto/rand"
|
||||||
"crypto/sha256"
|
"crypto/sha256"
|
||||||
|
@ -12,9 +13,13 @@ import (
|
||||||
|
|
||||||
"github.com/nspcc-dev/neo-go/pkg/crypto/keys"
|
"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"
|
||||||
|
"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/eacl"
|
||||||
"github.com/nspcc-dev/neofs-sdk-go/object"
|
"github.com/nspcc-dev/neofs-sdk-go/object"
|
||||||
oid "github.com/nspcc-dev/neofs-sdk-go/object/id"
|
oid "github.com/nspcc-dev/neofs-sdk-go/object/id"
|
||||||
|
"github.com/nspcc-dev/neofs-sdk-go/session"
|
||||||
"github.com/stretchr/testify/require"
|
"github.com/stretchr/testify/require"
|
||||||
)
|
)
|
||||||
|
|
||||||
|
@ -1287,3 +1292,81 @@ func TestBucketAclToAst(t *testing.T) {
|
||||||
require.NoError(t, err)
|
require.NoError(t, err)
|
||||||
require.Equal(t, expectedAst, actualAst)
|
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)
|
||||||
|
}
|
||||||
|
|
|
@ -5,6 +5,7 @@ import (
|
||||||
"context"
|
"context"
|
||||||
"crypto/rand"
|
"crypto/rand"
|
||||||
"crypto/sha256"
|
"crypto/sha256"
|
||||||
|
"errors"
|
||||||
"fmt"
|
"fmt"
|
||||||
"io"
|
"io"
|
||||||
"time"
|
"time"
|
||||||
|
@ -13,6 +14,7 @@ import (
|
||||||
"github.com/nspcc-dev/neofs-sdk-go/checksum"
|
"github.com/nspcc-dev/neofs-sdk-go/checksum"
|
||||||
"github.com/nspcc-dev/neofs-sdk-go/container"
|
"github.com/nspcc-dev/neofs-sdk-go/container"
|
||||||
cid "github.com/nspcc-dev/neofs-sdk-go/container/id"
|
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"
|
"github.com/nspcc-dev/neofs-sdk-go/object"
|
||||||
oid "github.com/nspcc-dev/neofs-sdk-go/object/id"
|
oid "github.com/nspcc-dev/neofs-sdk-go/object/id"
|
||||||
"github.com/nspcc-dev/neofs-sdk-go/session"
|
"github.com/nspcc-dev/neofs-sdk-go/session"
|
||||||
|
@ -24,6 +26,7 @@ type TestNeoFS struct {
|
||||||
|
|
||||||
objects map[string]*object.Object
|
objects map[string]*object.Object
|
||||||
containers map[string]*container.Container
|
containers map[string]*container.Container
|
||||||
|
eaclTables map[string]*eacl.Table
|
||||||
currentEpoch uint64
|
currentEpoch uint64
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -31,6 +34,7 @@ func NewTestNeoFS() *TestNeoFS {
|
||||||
return &TestNeoFS{
|
return &TestNeoFS{
|
||||||
objects: make(map[string]*object.Object),
|
objects: make(map[string]*object.Object),
|
||||||
containers: make(map[string]*container.Container),
|
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
|
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
|
||||||
|
}
|
||||||
|
|
Loading…
Reference in a new issue