diff --git a/api/handler/acl.go b/api/handler/acl.go index e4901a79..d214a186 100644 --- a/api/handler/acl.go +++ b/api/handler/acl.go @@ -929,15 +929,15 @@ func formRecords(resource *astResource) ([]*eacl.Record, error) { eacl.AddFormedTarget(record, eacl.RoleUnknown, targetKeys...) } if len(resource.Object) != 0 { - if len(resource.Version) != 0 { - var id oid.ID - if err := id.DecodeString(resource.Version); err != nil { - return nil, fmt.Errorf("parse object version (oid): %w", err) - } - record.AddObjectIDFilter(eacl.MatchStringEqual, id) - } else { - record.AddObjectAttributeFilter(eacl.MatchStringEqual, object.AttributeFilePath, resource.Object) + record.AddObjectAttributeFilter(eacl.MatchStringEqual, object.AttributeFilePath, resource.Object) + } + + if len(resource.Version) != 0 { + var id oid.ID + if err := id.DecodeString(resource.Version); err != nil { + return nil, fmt.Errorf("parse object version (oid): %w", err) } + record.AddObjectIDFilter(eacl.MatchStringEqual, id) } res = append(res, record) } @@ -960,7 +960,19 @@ func addToList(operations []*astOperation, rec eacl.Record, target eacl.Target) if found != nil { if !groupTarget { for _, key := range target.BinaryKeys() { - found.Users = append(found.Users, hex.EncodeToString(key)) + pubKey := hex.EncodeToString(key) + var exist bool + + for _, userPubKey := range found.Users { + if userPubKey == pubKey { + exist = true + break + } + } + + if !exist { + found.Users = append(found.Users, pubKey) + } } } } else { @@ -1143,10 +1155,7 @@ func aclToAst(acl *AccessControlPolicy, resInfo *resourceInfo) (*ast, error) { resource := &astResource{resourceInfo: *resInfo} - ops := readOps - if resInfo.IsBucket() { - ops = append(ops, writeOps...) - } + ops := append(readOps, writeOps...) // Expect to have at least 1 full control grant for owner which is set in // parseACLHeaders(). If there is no other grants, then user sets private @@ -1280,7 +1289,7 @@ func getActions(permission amazonS3Permission, isBucket bool) []string { if isBucket { res = []string{s3ListBucket, s3ListBucketVersions, s3ListBucketMultipartUploads, s3PutObject, s3DeleteObject} } else { - res = []string{s3GetObject, s3GetObjectVersion} + res = []string{s3GetObject, s3GetObjectVersion, s3PutObject, s3DeleteObject} } } diff --git a/api/handler/acl_test.go b/api/handler/acl_test.go index 08e7eb57..5d545a6f 100644 --- a/api/handler/acl_test.go +++ b/api/handler/acl_test.go @@ -818,7 +818,7 @@ func TestObjectAclToPolicy(t *testing.T) { Principal: principal{ CanonicalUser: id, }, - Action: []string{"s3:GetObject", "s3:GetObjectVersion"}, + Action: []string{s3GetObject, s3GetObjectVersion, s3PutObject, s3DeleteObject}, Resource: []string{arnAwsPrefix + resInfo.Name()}, }, { @@ -826,13 +826,13 @@ func TestObjectAclToPolicy(t *testing.T) { Principal: principal{ CanonicalUser: id2, }, - Action: []string{"s3:GetObject", "s3:GetObjectVersion"}, + Action: []string{s3GetObject, s3GetObjectVersion, s3PutObject, s3DeleteObject}, Resource: []string{arnAwsPrefix + resInfo.Name()}, }, { Effect: "Allow", Principal: principal{AWS: allUsersWildcard}, - Action: []string{"s3:GetObject", "s3:GetObjectVersion"}, + Action: []string{s3GetObject, s3GetObjectVersion}, Resource: []string{arnAwsPrefix + resInfo.Name()}, }, }, @@ -881,34 +881,53 @@ func TestObjectWithVersionAclToTable(t *testing.T) { } func allowedTableForPrivateObject(t *testing.T, key *keys.PrivateKey, resInfo *resourceInfo) *eacl.Table { - var isVersion bool var objID oid.ID + var zeroObjectID oid.ID + if resInfo.Version != "" { - isVersion = true err := objID.DecodeString(resInfo.Version) require.NoError(t, err) } expectedTable := eacl.NewTable() + applyFilters := func(r *eacl.Record) { + if resInfo.Object != "" { + r.AddObjectAttributeFilter(eacl.MatchStringEqual, object.AttributeFilePath, resInfo.Object) + } + if !objID.Equals(zeroObjectID) { + r.AddObjectIDFilter(eacl.MatchStringEqual, objID) + } + } + + // Order of these loops is important for test. + for i := len(writeOps) - 1; i >= 0; i-- { + op := writeOps[i] + record := getAllowRecord(op, key.PublicKey()) + + applyFilters(record) + expectedTable.AddRecord(record) + } for i := len(readOps) - 1; i >= 0; i-- { op := readOps[i] record := getAllowRecord(op, key.PublicKey()) - if isVersion { - record.AddObjectIDFilter(eacl.MatchStringEqual, objID) - } else { - record.AddObjectAttributeFilter(eacl.MatchStringEqual, object.AttributeFilePath, resInfo.Object) - } + + applyFilters(record) + expectedTable.AddRecord(record) + } + + for i := len(writeOps) - 1; i >= 0; i-- { + op := writeOps[i] + record := getOthersRecord(op, eacl.ActionDeny) + + applyFilters(record) expectedTable.AddRecord(record) } for i := len(readOps) - 1; i >= 0; i-- { op := readOps[i] record := getOthersRecord(op, eacl.ActionDeny) - if isVersion { - record.AddObjectIDFilter(eacl.MatchStringEqual, objID) - } else { - record.AddObjectAttributeFilter(eacl.MatchStringEqual, object.AttributeFilePath, resInfo.Object) - } + + applyFilters(record) expectedTable.AddRecord(record) } @@ -1192,6 +1211,16 @@ func TestObjectAclToAst(t *testing.T) { operations = append(operations, astOp) } + for _, op := range writeOps { + astOp := &astOperation{Users: []string{ + hex.EncodeToString(key.PublicKey().Bytes()), + }, + Op: op, + Action: eacl.ActionAllow, + } + operations = append(operations, astOp) + } + expectedAst := &ast{ Resources: []*astResource{ { diff --git a/go.mod b/go.mod index cb72a04b..74c23e22 100644 --- a/go.mod +++ b/go.mod @@ -3,7 +3,7 @@ module github.com/nspcc-dev/neofs-s3-gw go 1.19 require ( - github.com/aws/aws-sdk-go v1.44.6 + github.com/aws/aws-sdk-go v1.46.1 github.com/bluele/gcache v0.0.2 github.com/google/uuid v1.3.1 github.com/gorilla/mux v1.8.0 diff --git a/go.sum b/go.sum index 15ad5bc2..7e2be1d8 100644 --- a/go.sum +++ b/go.sum @@ -54,8 +54,8 @@ github.com/antlr/antlr4/runtime/Go/antlr/v4 v4.0.0-20221202181307-76fa05c21b12/g github.com/armon/circbuf v0.0.0-20150827004946-bbbad097214e/go.mod h1:3U/XgcO3hCbHZ8TKRvWD2dDTCfh9M9ya+I9JpbB7O8o= github.com/armon/go-metrics v0.0.0-20180917152333-f0300d1749da/go.mod h1:Q73ZrmVTwzkszR9V5SSuryQ31EELlFMUz1kKyl939pY= github.com/armon/go-radix v0.0.0-20180808171621-7fddfc383310/go.mod h1:ufUuZ+zHj4x4TnLV4JWEpy2hxWSpsRywHrMgIH9cCH8= -github.com/aws/aws-sdk-go v1.44.6 h1:Y+uHxmZfhRTLX2X3khkdxCoTZAyGEX21aOUHe1U6geg= -github.com/aws/aws-sdk-go v1.44.6/go.mod h1:y4AeaBuwd2Lk+GepC1E9v0qOiTws0MIWAX4oIKwKHZo= +github.com/aws/aws-sdk-go v1.46.1 h1:U26quvBWFZMQuultLw5tloW4GnmWaChEwMZNq8uYatw= +github.com/aws/aws-sdk-go v1.46.1/go.mod h1:aVsgQcEevwlmQ7qHE9I3h+dtQgpqhFB+i8Phjh7fkwI= github.com/beorn7/perks v0.0.0-20180321164747-3a771d992973/go.mod h1:Dwedo/Wpr24TaqPxmxbtue+5NUziq4I4S80YR8gNf3Q= github.com/beorn7/perks v1.0.0/go.mod h1:KWe93zE9D1o94FZ5RNwFwVgaQK1VOXiVxmqh+CedLV8= github.com/beorn7/perks v1.0.1 h1:VlbKKnNfV8bJzeqoa4cOKqO6bYr3WgKZxO8Z16+hsOM= @@ -416,6 +416,7 @@ github.com/yuin/goldmark v1.1.27/go.mod h1:3hX8gzYuyVAZsxl0MRgGTJEmQBFcNTphYh9de github.com/yuin/goldmark v1.1.32/go.mod h1:3hX8gzYuyVAZsxl0MRgGTJEmQBFcNTphYh9decYSb74= github.com/yuin/goldmark v1.2.1/go.mod h1:3hX8gzYuyVAZsxl0MRgGTJEmQBFcNTphYh9decYSb74= github.com/yuin/goldmark v1.3.5/go.mod h1:mwnBkeHKe2W/ZEtQ+71ViKU8L12m81fl3OWwC1Zlc8k= +github.com/yuin/goldmark v1.4.13/go.mod h1:6yULJ656Px+3vBD8DxQVa3kxgyrAnzto9xy5taEt/CY= github.com/yusufpapurcu/wmi v1.2.3 h1:E1ctvB7uKFMOJw3fdOW32DwGE9I7t++CRUEMKvFoFiw= go.etcd.io/bbolt v1.3.7 h1:j+zJOnnEjF/kyHlDDgGnVL/AIqIJPq8UoB2GSNfkUfQ= go.etcd.io/etcd/api/v3 v3.5.0/go.mod h1:cbVKeC6lCfl7j/8jBhAK6aIYO9XOjdptoxU/nLQcPvs= @@ -447,6 +448,7 @@ golang.org/x/crypto v0.0.0-20190605123033-f99c8df09eb5/go.mod h1:yigFU9vqHzYiE8U golang.org/x/crypto v0.0.0-20190820162420-60c769a6c586/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI= golang.org/x/crypto v0.0.0-20191011191535-87dc89f01550/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI= golang.org/x/crypto v0.0.0-20200622213623-75b288015ac9/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto= +golang.org/x/crypto v0.0.0-20210921155107-089bfa567519/go.mod h1:GvvjBRRGRdwPK5ydBHafDWAxML/pGHZbMvKqRZ5+Abc= golang.org/x/crypto v0.14.0 h1:wBqGXzWJW6m1XrIKlAH0Hs1JJ7+9KBwnIO8v66Q9cHc= golang.org/x/crypto v0.14.0/go.mod h1:MVFd36DqK4CsrnJYDkBA3VC4m2GkXAM0PvzMCn4JQf4= golang.org/x/exp v0.0.0-20190121172915-509febef88a4/go.mod h1:CJ0aWSM057203Lf6IL+f9T1iT9GByDxfZKAQTCR3kQA= @@ -486,6 +488,7 @@ golang.org/x/mod v0.3.0/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA= golang.org/x/mod v0.4.0/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA= golang.org/x/mod v0.4.1/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA= golang.org/x/mod v0.4.2/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA= +golang.org/x/mod v0.6.0-dev.0.20220419223038-86c51ed26bb4/go.mod h1:jJ57K6gSWd91VN4djpZkiMVwK6gcyfeH4XE8wZrZaV4= golang.org/x/mod v0.12.0 h1:rmsUpXtvNzj340zd98LZ4KntptpfRHwpFOHG188oHXc= golang.org/x/net v0.0.0-20180724234803-3673e40ba225/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= golang.org/x/net v0.0.0-20180826012351-8a410e7b638d/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= @@ -528,6 +531,8 @@ golang.org/x/net v0.0.0-20210405180319-a5a99cb37ef4/go.mod h1:p54w0d4576C0XHj96b golang.org/x/net v0.0.0-20210525063256-abc453219eb5/go.mod h1:9nx3DQGgdP8bBQD5qxJ1jj9UTztislL4KSBs9R2vV5Y= golang.org/x/net v0.0.0-20220127200216-cd36cc0744dd/go.mod h1:CfG3xpIq0wQ8r1q4Su4UZFWDARRcnwPjda9FqA0JpMk= golang.org/x/net v0.0.0-20220225172249-27dd8689420f/go.mod h1:CfG3xpIq0wQ8r1q4Su4UZFWDARRcnwPjda9FqA0JpMk= +golang.org/x/net v0.0.0-20220722155237-a158d28d115b/go.mod h1:XRhObCWvk6IyKnWLug+ECip1KBveYUHfp+8e9klMJ9c= +golang.org/x/net v0.1.0/go.mod h1:Cx3nUiGt4eDBEyega/BKRp+/AlGL8hYe7U9odMt2Cco= golang.org/x/net v0.17.0 h1:pVaXccu2ozPjCXewfr1S7xza/zcXTity9cCdXQYSjIM= golang.org/x/net v0.17.0/go.mod h1:NxSsAGuq816PNPmqtQdLE42eU2Fs7NoRIZrHJAlaCOE= golang.org/x/oauth2 v0.0.0-20180821212333-d2e6202438be/go.mod h1:N/0e6XlmueqKjAGxoOufVs8QHGRruUQn6yWY3a++T0U= @@ -610,10 +615,14 @@ golang.org/x/sys v0.0.0-20210603081109-ebe580a85c40/go.mod h1:oPkhp1MJrh7nUepCBc golang.org/x/sys v0.0.0-20210615035016-665e8c7367d1/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.0.0-20211216021012-1d35b9e2eb4e/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.0.0-20220114195835-da31bd327af9/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= +golang.org/x/sys v0.0.0-20220520151302-bc2c85ada10a/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= +golang.org/x/sys v0.0.0-20220722155257-8c9f86f7a55f/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= +golang.org/x/sys v0.1.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.13.0 h1:Af8nKPmuFypiUBjVoU9V20FiaFXOcuZI21p0ycVYYGE= golang.org/x/sys v0.13.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/term v0.0.0-20201126162022-7de9c90e9dd1/go.mod h1:bj7SfCRtBDWHUb9snDiAeCFNEtKQo2Wmx5Cou7ajbmo= golang.org/x/term v0.0.0-20210927222741-03fcf44c2211/go.mod h1:jbD1KX2456YbFQfuXm/mYQcufACuNUgVhRMnK/tPxf8= +golang.org/x/term v0.1.0/go.mod h1:jbD1KX2456YbFQfuXm/mYQcufACuNUgVhRMnK/tPxf8= golang.org/x/term v0.13.0 h1:bb+I9cTfFazGW51MZqBVmZy7+JEJMouUHTUSKVQLBek= golang.org/x/term v0.13.0/go.mod h1:LTmsnFJwVN6bCy1rVCoS+qHT1HhALEFxKncY3WNNh4U= golang.org/x/text v0.0.0-20170915032832-14c0d48ead0c/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ= @@ -625,6 +634,7 @@ golang.org/x/text v0.3.4/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ= golang.org/x/text v0.3.5/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ= golang.org/x/text v0.3.6/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ= golang.org/x/text v0.3.7/go.mod h1:u+2+/6zg+i71rQMx5EYifcz6MCKuco9NR6JIITiCfzQ= +golang.org/x/text v0.4.0/go.mod h1:mrYo+phRRbMaCq/xk9113O4dZlRixOauAjOtrjsXDZ8= golang.org/x/text v0.13.0 h1:ablQoSUd0tRdKxZewP80B+BaqeKJuVhuRxj/dkrun3k= golang.org/x/text v0.13.0/go.mod h1:TvPlkZtksWOMsz7fbANvkp4WM8x/WCo/om8BMLbz+aE= golang.org/x/time v0.0.0-20181108054448-85acf8d2951c/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ= @@ -682,6 +692,7 @@ golang.org/x/tools v0.0.0-20210105154028-b0ab187a4818/go.mod h1:emZCQorbCU4vsT4f golang.org/x/tools v0.0.0-20210106214847-113979e3529a/go.mod h1:emZCQorbCU4vsT4fOWvOPXz4eW1wZW4PmDk9uLelYpA= golang.org/x/tools v0.1.0/go.mod h1:xkSsbof2nBLbhDlRMhhhyNLN/zl3eTqcnHD5viDpcZ0= golang.org/x/tools v0.1.2/go.mod h1:o0xws9oXOQQZyjljx8fwUC0k7L1pTE6eaCbjGeHmOkk= +golang.org/x/tools v0.1.12/go.mod h1:hNGJHUnrk76NpqgfD5Aqm5Crs+Hm0VOH/i9J2+nxYbc= golang.org/x/tools v0.11.1 h1:ojD5zOW8+7dOGzdnNgersm8aPfcDjhMp12UfG93NIMc= golang.org/x/xerrors v0.0.0-20190717185122-a985d3407aa7/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= golang.org/x/xerrors v0.0.0-20191011141410-1b5146add898/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=