2021-07-21 11:59:46 +00:00
|
|
|
package handler
|
|
|
|
|
|
|
|
import (
|
|
|
|
"crypto/ecdsa"
|
2021-08-30 07:55:42 +00:00
|
|
|
"crypto/rand"
|
|
|
|
"crypto/sha256"
|
2021-07-21 11:59:46 +00:00
|
|
|
"encoding/hex"
|
2021-08-30 07:55:42 +00:00
|
|
|
"io"
|
2021-07-21 11:59:46 +00:00
|
|
|
"net/http"
|
|
|
|
"testing"
|
|
|
|
|
|
|
|
"github.com/nspcc-dev/neo-go/pkg/crypto/keys"
|
|
|
|
"github.com/nspcc-dev/neofs-s3-gw/api"
|
2021-11-15 12:56:16 +00:00
|
|
|
"github.com/nspcc-dev/neofs-sdk-go/eacl"
|
|
|
|
"github.com/nspcc-dev/neofs-sdk-go/object"
|
2022-02-08 16:54:04 +00:00
|
|
|
oid "github.com/nspcc-dev/neofs-sdk-go/object/id"
|
2021-07-21 11:59:46 +00:00
|
|
|
"github.com/stretchr/testify/require"
|
|
|
|
)
|
|
|
|
|
|
|
|
func TestTableToAst(t *testing.T) {
|
2021-08-30 07:55:42 +00:00
|
|
|
b := make([]byte, 32)
|
|
|
|
_, err := io.ReadFull(rand.Reader, b)
|
|
|
|
require.NoError(t, err)
|
2022-04-25 09:57:58 +00:00
|
|
|
var id oid.ID
|
2022-02-08 16:54:04 +00:00
|
|
|
id.SetSHA256(sha256.Sum256(b))
|
2021-08-30 07:55:42 +00:00
|
|
|
|
2021-07-21 11:59:46 +00:00
|
|
|
key, err := keys.NewPrivateKey()
|
|
|
|
require.NoError(t, err)
|
|
|
|
key2, err := keys.NewPrivateKey()
|
|
|
|
require.NoError(t, err)
|
|
|
|
|
|
|
|
table := new(eacl.Table)
|
|
|
|
record := eacl.NewRecord()
|
|
|
|
record.SetAction(eacl.ActionAllow)
|
|
|
|
record.SetOperation(eacl.OperationGet)
|
|
|
|
eacl.AddFormedTarget(record, eacl.RoleOthers)
|
|
|
|
table.AddRecord(record)
|
|
|
|
record2 := eacl.NewRecord()
|
|
|
|
record2.SetAction(eacl.ActionDeny)
|
|
|
|
record2.SetOperation(eacl.OperationPut)
|
2022-07-06 12:39:15 +00:00
|
|
|
eacl.AddFormedTarget(record2, eacl.RoleUnknown, *(*ecdsa.PublicKey)(key.PublicKey()), *((*ecdsa.PublicKey)(key2.PublicKey())))
|
2021-07-21 11:59:46 +00:00
|
|
|
record2.AddObjectAttributeFilter(eacl.MatchStringEqual, object.AttributeFileName, "objectName")
|
2022-02-08 16:54:04 +00:00
|
|
|
record2.AddObjectIDFilter(eacl.MatchStringEqual, id)
|
2021-07-21 11:59:46 +00:00
|
|
|
table.AddRecord(record2)
|
|
|
|
|
|
|
|
expectedAst := &ast{
|
|
|
|
Resources: []*astResource{
|
|
|
|
{
|
2021-08-30 07:55:42 +00:00
|
|
|
resourceInfo: resourceInfo{Bucket: "bucketName"},
|
2021-07-21 11:59:46 +00:00
|
|
|
Operations: []*astOperation{{
|
2022-07-06 12:39:15 +00:00
|
|
|
IsGroupGrantee: true,
|
|
|
|
Op: eacl.OperationGet,
|
|
|
|
Action: eacl.ActionAllow,
|
2021-07-21 11:59:46 +00:00
|
|
|
}}},
|
|
|
|
{
|
2021-08-30 07:55:42 +00:00
|
|
|
resourceInfo: resourceInfo{
|
|
|
|
Bucket: "bucketName",
|
|
|
|
Object: "objectName",
|
2022-05-25 17:25:43 +00:00
|
|
|
Version: id.EncodeToString(),
|
2021-08-30 07:55:42 +00:00
|
|
|
},
|
2021-07-21 11:59:46 +00:00
|
|
|
Operations: []*astOperation{{
|
|
|
|
Users: []string{
|
|
|
|
hex.EncodeToString(key.PublicKey().Bytes()),
|
|
|
|
hex.EncodeToString(key2.PublicKey().Bytes()),
|
|
|
|
},
|
2022-07-06 12:39:15 +00:00
|
|
|
IsGroupGrantee: false,
|
|
|
|
Op: eacl.OperationPut,
|
|
|
|
Action: eacl.ActionDeny,
|
2021-07-21 11:59:46 +00:00
|
|
|
}}},
|
|
|
|
},
|
|
|
|
}
|
|
|
|
|
2021-08-30 07:55:42 +00:00
|
|
|
actualAst := tableToAst(table, expectedAst.Resources[0].Bucket)
|
2021-07-21 11:59:46 +00:00
|
|
|
|
2021-08-30 07:55:42 +00:00
|
|
|
if actualAst.Resources[0].Name() == expectedAst.Resources[0].Name() {
|
2021-07-21 11:59:46 +00:00
|
|
|
require.Equal(t, expectedAst, actualAst)
|
|
|
|
} else {
|
|
|
|
require.Equal(t, len(expectedAst.Resources), len(actualAst.Resources))
|
|
|
|
require.Equal(t, expectedAst.Resources[0], actualAst.Resources[1])
|
|
|
|
require.Equal(t, expectedAst.Resources[1], actualAst.Resources[0])
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
func TestPolicyToAst(t *testing.T) {
|
|
|
|
key, err := keys.NewPrivateKey()
|
|
|
|
require.NoError(t, err)
|
|
|
|
|
|
|
|
policy := &bucketPolicy{
|
|
|
|
Statement: []statement{
|
|
|
|
{
|
|
|
|
Effect: "Allow",
|
|
|
|
Principal: principal{AWS: allUsersWildcard},
|
|
|
|
Action: []string{"s3:PutObject"},
|
|
|
|
Resource: []string{"arn:aws:s3:::bucketName"},
|
|
|
|
},
|
|
|
|
{
|
|
|
|
Effect: "Deny",
|
|
|
|
Principal: principal{
|
|
|
|
CanonicalUser: hex.EncodeToString(key.PublicKey().Bytes()),
|
|
|
|
},
|
|
|
|
Action: []string{"s3:GetObject"},
|
|
|
|
Resource: []string{"arn:aws:s3:::bucketName/object"},
|
|
|
|
}},
|
|
|
|
}
|
2021-08-30 07:55:42 +00:00
|
|
|
policy.Bucket = "bucketName"
|
2021-07-21 11:59:46 +00:00
|
|
|
|
|
|
|
expectedAst := &ast{
|
|
|
|
Resources: []*astResource{
|
|
|
|
{
|
2021-08-30 07:55:42 +00:00
|
|
|
resourceInfo: resourceInfo{
|
|
|
|
Bucket: "bucketName",
|
|
|
|
},
|
2021-07-21 11:59:46 +00:00
|
|
|
Operations: []*astOperation{{
|
2022-07-06 12:39:15 +00:00
|
|
|
IsGroupGrantee: true,
|
|
|
|
Op: eacl.OperationPut,
|
|
|
|
Action: eacl.ActionAllow,
|
2021-07-21 11:59:46 +00:00
|
|
|
}},
|
|
|
|
},
|
|
|
|
{
|
2021-08-30 07:55:42 +00:00
|
|
|
resourceInfo: resourceInfo{
|
|
|
|
Bucket: "bucketName",
|
|
|
|
Object: "object",
|
|
|
|
},
|
2022-07-06 12:39:15 +00:00
|
|
|
Operations: getReadOps(key, false, eacl.ActionDeny),
|
2021-07-21 11:59:46 +00:00
|
|
|
},
|
|
|
|
},
|
|
|
|
}
|
|
|
|
|
|
|
|
actualAst, err := policyToAst(policy)
|
|
|
|
require.NoError(t, err)
|
|
|
|
|
2021-08-30 07:55:42 +00:00
|
|
|
if actualAst.Resources[0].Name() == expectedAst.Resources[0].Name() {
|
2021-07-21 11:59:46 +00:00
|
|
|
require.Equal(t, expectedAst, actualAst)
|
|
|
|
} else {
|
|
|
|
require.Equal(t, len(expectedAst.Resources), len(actualAst.Resources))
|
|
|
|
require.Equal(t, expectedAst.Resources[0], actualAst.Resources[1])
|
|
|
|
require.Equal(t, expectedAst.Resources[1], actualAst.Resources[0])
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2022-07-06 12:39:15 +00:00
|
|
|
func getReadOps(key *keys.PrivateKey, groupGrantee bool, action eacl.Action) []*astOperation {
|
2021-07-21 11:59:46 +00:00
|
|
|
var result []*astOperation
|
|
|
|
|
|
|
|
for _, op := range readOps {
|
|
|
|
result = append(result, &astOperation{
|
2022-07-06 12:39:15 +00:00
|
|
|
Users: []string{hex.EncodeToString(key.PublicKey().Bytes())},
|
|
|
|
IsGroupGrantee: groupGrantee,
|
|
|
|
Op: op,
|
|
|
|
Action: action,
|
2021-07-21 11:59:46 +00:00
|
|
|
})
|
|
|
|
}
|
|
|
|
|
|
|
|
return result
|
|
|
|
}
|
|
|
|
|
|
|
|
func TestMergeAstUnModified(t *testing.T) {
|
|
|
|
key, err := keys.NewPrivateKey()
|
|
|
|
require.NoError(t, err)
|
|
|
|
|
|
|
|
child := &ast{
|
|
|
|
Resources: []*astResource{
|
|
|
|
{
|
2021-08-30 07:55:42 +00:00
|
|
|
resourceInfo: resourceInfo{
|
|
|
|
Bucket: "bucket",
|
|
|
|
Object: "objectName",
|
|
|
|
},
|
2021-07-21 11:59:46 +00:00
|
|
|
Operations: []*astOperation{{
|
2022-07-06 12:39:15 +00:00
|
|
|
Users: []string{hex.EncodeToString(key.PublicKey().Bytes())},
|
|
|
|
IsGroupGrantee: false,
|
|
|
|
Op: eacl.OperationPut,
|
|
|
|
Action: eacl.ActionDeny,
|
2021-07-21 11:59:46 +00:00
|
|
|
}},
|
|
|
|
},
|
|
|
|
},
|
|
|
|
}
|
|
|
|
|
|
|
|
parent := &ast{
|
|
|
|
Resources: []*astResource{
|
|
|
|
{
|
2021-08-30 07:55:42 +00:00
|
|
|
resourceInfo: resourceInfo{
|
|
|
|
Bucket: "bucket",
|
|
|
|
},
|
2021-07-21 11:59:46 +00:00
|
|
|
Operations: []*astOperation{{
|
2022-07-06 12:39:15 +00:00
|
|
|
IsGroupGrantee: true,
|
|
|
|
Op: eacl.OperationGet,
|
|
|
|
Action: eacl.ActionAllow,
|
2021-07-21 11:59:46 +00:00
|
|
|
}},
|
|
|
|
},
|
|
|
|
child.Resources[0],
|
|
|
|
},
|
|
|
|
}
|
|
|
|
|
|
|
|
result, updated := mergeAst(parent, child)
|
|
|
|
require.False(t, updated)
|
|
|
|
require.Equal(t, parent, result)
|
|
|
|
}
|
|
|
|
|
|
|
|
func TestMergeAstModified(t *testing.T) {
|
|
|
|
child := &ast{
|
|
|
|
Resources: []*astResource{
|
|
|
|
{
|
2021-08-30 07:55:42 +00:00
|
|
|
resourceInfo: resourceInfo{
|
|
|
|
Bucket: "bucket",
|
|
|
|
Object: "objectName",
|
|
|
|
},
|
2021-07-21 11:59:46 +00:00
|
|
|
Operations: []*astOperation{{
|
2022-07-06 12:39:15 +00:00
|
|
|
IsGroupGrantee: true,
|
|
|
|
Op: eacl.OperationPut,
|
|
|
|
Action: eacl.ActionDeny,
|
2021-07-21 11:59:46 +00:00
|
|
|
}, {
|
2022-07-06 12:39:15 +00:00
|
|
|
Users: []string{"user2"},
|
|
|
|
IsGroupGrantee: false,
|
|
|
|
Op: eacl.OperationGet,
|
|
|
|
Action: eacl.ActionDeny,
|
2021-07-21 11:59:46 +00:00
|
|
|
}},
|
|
|
|
},
|
|
|
|
},
|
|
|
|
}
|
|
|
|
|
|
|
|
parent := &ast{
|
|
|
|
Resources: []*astResource{
|
|
|
|
{
|
2021-08-30 07:55:42 +00:00
|
|
|
resourceInfo: resourceInfo{
|
|
|
|
Bucket: "bucket",
|
|
|
|
Object: "objectName",
|
|
|
|
},
|
2021-07-21 11:59:46 +00:00
|
|
|
Operations: []*astOperation{{
|
2022-07-06 12:39:15 +00:00
|
|
|
Users: []string{"user1"},
|
|
|
|
IsGroupGrantee: false,
|
|
|
|
Op: eacl.OperationGet,
|
|
|
|
Action: eacl.ActionDeny,
|
2021-07-21 11:59:46 +00:00
|
|
|
}},
|
|
|
|
},
|
|
|
|
},
|
|
|
|
}
|
|
|
|
|
|
|
|
expected := &ast{
|
|
|
|
Resources: []*astResource{
|
|
|
|
{
|
2021-08-30 07:55:42 +00:00
|
|
|
resourceInfo: resourceInfo{
|
|
|
|
Bucket: "bucket",
|
|
|
|
Object: "objectName",
|
|
|
|
},
|
2021-07-21 11:59:46 +00:00
|
|
|
Operations: []*astOperation{
|
|
|
|
child.Resources[0].Operations[0],
|
|
|
|
{
|
2022-07-06 12:39:15 +00:00
|
|
|
Users: []string{"user1", "user2"},
|
|
|
|
IsGroupGrantee: false,
|
|
|
|
Op: eacl.OperationGet,
|
|
|
|
Action: eacl.ActionDeny,
|
2021-07-21 11:59:46 +00:00
|
|
|
},
|
|
|
|
},
|
|
|
|
},
|
|
|
|
},
|
|
|
|
}
|
|
|
|
|
|
|
|
actual, updated := mergeAst(parent, child)
|
|
|
|
require.True(t, updated)
|
|
|
|
require.Equal(t, expected, actual)
|
|
|
|
}
|
|
|
|
|
|
|
|
func TestMergeAstModifiedConflict(t *testing.T) {
|
|
|
|
child := &ast{
|
|
|
|
Resources: []*astResource{
|
|
|
|
{
|
2021-08-30 07:55:42 +00:00
|
|
|
resourceInfo: resourceInfo{
|
|
|
|
Bucket: "bucket",
|
|
|
|
Object: "objectName",
|
|
|
|
},
|
2021-07-21 11:59:46 +00:00
|
|
|
Operations: []*astOperation{{
|
2022-07-06 12:39:15 +00:00
|
|
|
Users: []string{"user1"},
|
|
|
|
IsGroupGrantee: false,
|
|
|
|
Op: eacl.OperationPut,
|
|
|
|
Action: eacl.ActionDeny,
|
2021-07-21 11:59:46 +00:00
|
|
|
}, {
|
2022-07-06 12:39:15 +00:00
|
|
|
Users: []string{"user3"},
|
|
|
|
IsGroupGrantee: false,
|
|
|
|
Op: eacl.OperationGet,
|
|
|
|
Action: eacl.ActionAllow,
|
2021-07-21 11:59:46 +00:00
|
|
|
}},
|
|
|
|
},
|
|
|
|
},
|
|
|
|
}
|
|
|
|
|
|
|
|
parent := &ast{
|
|
|
|
Resources: []*astResource{
|
|
|
|
{
|
2021-08-30 07:55:42 +00:00
|
|
|
resourceInfo: resourceInfo{
|
|
|
|
Bucket: "bucket",
|
|
|
|
Object: "objectName",
|
|
|
|
},
|
2021-07-21 11:59:46 +00:00
|
|
|
Operations: []*astOperation{{
|
2022-07-06 12:39:15 +00:00
|
|
|
Users: []string{"user1"},
|
|
|
|
IsGroupGrantee: false,
|
|
|
|
Op: eacl.OperationPut,
|
|
|
|
Action: eacl.ActionAllow,
|
2021-07-21 11:59:46 +00:00
|
|
|
}, {
|
2022-07-06 12:39:15 +00:00
|
|
|
Users: []string{"user2"},
|
|
|
|
IsGroupGrantee: false,
|
|
|
|
Op: eacl.OperationPut,
|
|
|
|
Action: eacl.ActionDeny,
|
2021-07-21 11:59:46 +00:00
|
|
|
}, {
|
2022-07-06 12:39:15 +00:00
|
|
|
Users: []string{"user3"},
|
|
|
|
IsGroupGrantee: false,
|
|
|
|
Op: eacl.OperationGet,
|
|
|
|
Action: eacl.ActionDeny,
|
2021-07-21 11:59:46 +00:00
|
|
|
}},
|
|
|
|
},
|
|
|
|
},
|
|
|
|
}
|
|
|
|
|
|
|
|
expected := &ast{
|
|
|
|
Resources: []*astResource{
|
|
|
|
{
|
2021-08-30 07:55:42 +00:00
|
|
|
resourceInfo: resourceInfo{
|
|
|
|
Bucket: "bucket",
|
|
|
|
Object: "objectName",
|
|
|
|
},
|
2021-07-21 11:59:46 +00:00
|
|
|
Operations: []*astOperation{
|
|
|
|
{
|
2022-07-06 12:39:15 +00:00
|
|
|
Users: []string{"user2", "user1"},
|
|
|
|
IsGroupGrantee: false,
|
|
|
|
Op: eacl.OperationPut,
|
|
|
|
Action: eacl.ActionDeny,
|
2021-07-21 11:59:46 +00:00
|
|
|
}, {
|
2022-07-06 12:39:15 +00:00
|
|
|
Users: []string{"user3"},
|
|
|
|
IsGroupGrantee: false,
|
|
|
|
Op: eacl.OperationGet,
|
|
|
|
Action: eacl.ActionAllow,
|
2021-07-21 11:59:46 +00:00
|
|
|
},
|
|
|
|
},
|
|
|
|
},
|
|
|
|
},
|
|
|
|
}
|
|
|
|
|
|
|
|
actual, updated := mergeAst(parent, child)
|
|
|
|
require.True(t, updated)
|
|
|
|
require.Equal(t, expected, actual)
|
|
|
|
}
|
|
|
|
|
|
|
|
func TestAstToTable(t *testing.T) {
|
|
|
|
key, err := keys.NewPrivateKey()
|
|
|
|
require.NoError(t, err)
|
|
|
|
|
|
|
|
ast := &ast{
|
|
|
|
Resources: []*astResource{
|
|
|
|
{
|
2021-08-30 07:55:42 +00:00
|
|
|
resourceInfo: resourceInfo{
|
|
|
|
Bucket: "bucketName",
|
|
|
|
},
|
2021-07-21 11:59:46 +00:00
|
|
|
Operations: []*astOperation{{
|
2022-07-06 12:39:15 +00:00
|
|
|
Users: []string{hex.EncodeToString(key.PublicKey().Bytes())},
|
|
|
|
IsGroupGrantee: false,
|
|
|
|
Op: eacl.OperationPut,
|
|
|
|
Action: eacl.ActionAllow,
|
2021-07-21 11:59:46 +00:00
|
|
|
}},
|
|
|
|
},
|
|
|
|
{
|
2021-08-30 07:55:42 +00:00
|
|
|
resourceInfo: resourceInfo{
|
|
|
|
Bucket: "bucketName",
|
|
|
|
Object: "objectName",
|
|
|
|
},
|
2021-07-21 11:59:46 +00:00
|
|
|
Operations: []*astOperation{{
|
2022-07-06 12:39:15 +00:00
|
|
|
IsGroupGrantee: true,
|
|
|
|
Op: eacl.OperationGet,
|
|
|
|
Action: eacl.ActionDeny,
|
2021-07-21 11:59:46 +00:00
|
|
|
}},
|
|
|
|
},
|
|
|
|
},
|
|
|
|
}
|
|
|
|
|
|
|
|
expectedTable := eacl.NewTable()
|
|
|
|
record := eacl.NewRecord()
|
|
|
|
record.SetAction(eacl.ActionAllow)
|
|
|
|
record.SetOperation(eacl.OperationPut)
|
2022-07-06 12:39:15 +00:00
|
|
|
eacl.AddFormedTarget(record, eacl.RoleUnknown, *(*ecdsa.PublicKey)(key.PublicKey()))
|
2021-07-21 11:59:46 +00:00
|
|
|
expectedTable.AddRecord(record)
|
|
|
|
record2 := eacl.NewRecord()
|
|
|
|
record2.SetAction(eacl.ActionDeny)
|
|
|
|
record2.SetOperation(eacl.OperationGet)
|
|
|
|
eacl.AddFormedTarget(record2, eacl.RoleOthers)
|
|
|
|
record2.AddObjectAttributeFilter(eacl.MatchStringEqual, object.AttributeFileName, "objectName")
|
|
|
|
expectedTable.AddRecord(record2)
|
|
|
|
|
2021-08-30 07:55:42 +00:00
|
|
|
actualTable, err := astToTable(ast)
|
2021-07-21 11:59:46 +00:00
|
|
|
require.NoError(t, err)
|
|
|
|
require.Equal(t, expectedTable, actualTable)
|
|
|
|
}
|
|
|
|
|
|
|
|
func TestRemoveUsers(t *testing.T) {
|
|
|
|
resource := &astResource{
|
2021-08-30 07:55:42 +00:00
|
|
|
resourceInfo: resourceInfo{
|
|
|
|
Bucket: "bucket",
|
|
|
|
},
|
2021-07-21 11:59:46 +00:00
|
|
|
Operations: []*astOperation{{
|
2022-07-06 12:39:15 +00:00
|
|
|
Users: []string{"user1", "user3", "user4"},
|
|
|
|
IsGroupGrantee: false,
|
|
|
|
Op: eacl.OperationPut,
|
|
|
|
Action: eacl.ActionAllow,
|
2021-07-21 11:59:46 +00:00
|
|
|
}},
|
|
|
|
}
|
|
|
|
|
|
|
|
op := &astOperation{
|
2022-07-06 12:39:15 +00:00
|
|
|
IsGroupGrantee: false,
|
|
|
|
Op: eacl.OperationPut,
|
|
|
|
Action: eacl.ActionAllow,
|
2021-07-21 11:59:46 +00:00
|
|
|
}
|
|
|
|
|
2022-07-06 13:01:47 +00:00
|
|
|
removeUsers(resource, op, []string{"user1", "user2", "user4"})
|
2021-07-21 11:59:46 +00:00
|
|
|
|
|
|
|
require.Equal(t, len(resource.Operations), 1)
|
2022-07-06 13:01:47 +00:00
|
|
|
require.Equal(t, []string{"user3"}, resource.Operations[0].Users)
|
2021-07-21 11:59:46 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
func TestBucketAclToPolicy(t *testing.T) {
|
|
|
|
key, err := keys.NewPrivateKey()
|
|
|
|
require.NoError(t, err)
|
|
|
|
key2, err := keys.NewPrivateKey()
|
|
|
|
require.NoError(t, err)
|
|
|
|
|
|
|
|
id := hex.EncodeToString(key.PublicKey().Bytes())
|
|
|
|
id2 := hex.EncodeToString(key2.PublicKey().Bytes())
|
|
|
|
|
|
|
|
acl := &AccessControlPolicy{
|
|
|
|
Owner: Owner{
|
|
|
|
ID: id,
|
|
|
|
DisplayName: "user1",
|
|
|
|
},
|
|
|
|
AccessControlList: []*Grant{{
|
|
|
|
Grantee: &Grantee{
|
|
|
|
URI: allUsersGroup,
|
|
|
|
Type: acpGroup,
|
|
|
|
},
|
|
|
|
Permission: aclRead,
|
|
|
|
}, {
|
|
|
|
Grantee: &Grantee{
|
|
|
|
ID: id2,
|
|
|
|
Type: acpCanonicalUser,
|
|
|
|
},
|
|
|
|
Permission: aclWrite,
|
|
|
|
}},
|
2021-08-30 07:55:42 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
resInfo := &resourceInfo{
|
|
|
|
Bucket: "bucketName",
|
2021-07-21 11:59:46 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
expectedPolicy := &bucketPolicy{
|
|
|
|
Statement: []statement{
|
|
|
|
{
|
|
|
|
Effect: "Allow",
|
|
|
|
Principal: principal{
|
|
|
|
CanonicalUser: id,
|
|
|
|
},
|
|
|
|
Action: []string{"s3:ListBucket", "s3:ListBucketVersions", "s3:ListBucketMultipartUploads", "s3:PutObject", "s3:DeleteObject"},
|
2021-08-30 07:55:42 +00:00
|
|
|
Resource: []string{arnAwsPrefix + resInfo.Name()},
|
2021-07-21 11:59:46 +00:00
|
|
|
}, {
|
|
|
|
Effect: "Allow",
|
|
|
|
Principal: principal{AWS: allUsersWildcard},
|
|
|
|
Action: []string{"s3:ListBucket", "s3:ListBucketVersions", "s3:ListBucketMultipartUploads"},
|
2021-08-30 07:55:42 +00:00
|
|
|
Resource: []string{arnAwsPrefix + resInfo.Name()},
|
2021-07-21 11:59:46 +00:00
|
|
|
}, {
|
|
|
|
Effect: "Allow",
|
|
|
|
Principal: principal{
|
|
|
|
CanonicalUser: id2,
|
|
|
|
},
|
|
|
|
Action: []string{"s3:PutObject", "s3:DeleteObject"},
|
2021-08-30 07:55:42 +00:00
|
|
|
Resource: []string{arnAwsPrefix + resInfo.Name()},
|
2021-07-21 11:59:46 +00:00
|
|
|
},
|
|
|
|
},
|
|
|
|
}
|
|
|
|
|
2021-08-30 07:55:42 +00:00
|
|
|
actualPolicy, err := aclToPolicy(acl, resInfo)
|
2021-07-21 11:59:46 +00:00
|
|
|
require.NoError(t, err)
|
|
|
|
require.Equal(t, expectedPolicy, actualPolicy)
|
|
|
|
}
|
|
|
|
|
|
|
|
func TestObjectAclToPolicy(t *testing.T) {
|
|
|
|
key, err := keys.NewPrivateKey()
|
|
|
|
require.NoError(t, err)
|
|
|
|
key2, err := keys.NewPrivateKey()
|
|
|
|
require.NoError(t, err)
|
|
|
|
|
|
|
|
id := hex.EncodeToString(key.PublicKey().Bytes())
|
|
|
|
id2 := hex.EncodeToString(key2.PublicKey().Bytes())
|
|
|
|
|
|
|
|
acl := &AccessControlPolicy{
|
|
|
|
Owner: Owner{
|
|
|
|
ID: id,
|
|
|
|
DisplayName: "user1",
|
|
|
|
},
|
|
|
|
AccessControlList: []*Grant{{
|
|
|
|
Grantee: &Grantee{
|
|
|
|
ID: id,
|
|
|
|
Type: acpCanonicalUser,
|
|
|
|
},
|
|
|
|
Permission: aclFullControl,
|
|
|
|
}, {
|
|
|
|
Grantee: &Grantee{
|
|
|
|
ID: id2,
|
|
|
|
Type: acpCanonicalUser,
|
|
|
|
},
|
|
|
|
Permission: aclFullControl,
|
|
|
|
}, {
|
|
|
|
Grantee: &Grantee{
|
|
|
|
URI: allUsersGroup,
|
|
|
|
Type: acpGroup,
|
|
|
|
},
|
|
|
|
Permission: aclRead,
|
|
|
|
}},
|
2021-08-30 07:55:42 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
resInfo := &resourceInfo{
|
|
|
|
Bucket: "bucketName",
|
|
|
|
Object: "object",
|
2021-07-21 11:59:46 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
expectedPolicy := &bucketPolicy{
|
|
|
|
Statement: []statement{
|
|
|
|
{
|
|
|
|
Effect: "Allow",
|
|
|
|
Principal: principal{
|
|
|
|
CanonicalUser: id,
|
|
|
|
},
|
|
|
|
Action: []string{"s3:GetObject", "s3:GetObjectVersion"},
|
2021-08-30 07:55:42 +00:00
|
|
|
Resource: []string{arnAwsPrefix + resInfo.Name()},
|
2021-07-21 11:59:46 +00:00
|
|
|
},
|
|
|
|
{
|
|
|
|
Effect: "Allow",
|
|
|
|
Principal: principal{
|
|
|
|
CanonicalUser: id2,
|
|
|
|
},
|
|
|
|
Action: []string{"s3:GetObject", "s3:GetObjectVersion"},
|
2021-08-30 07:55:42 +00:00
|
|
|
Resource: []string{arnAwsPrefix + resInfo.Name()},
|
2021-07-21 11:59:46 +00:00
|
|
|
}, {
|
|
|
|
Effect: "Allow",
|
|
|
|
Principal: principal{AWS: allUsersWildcard},
|
|
|
|
Action: []string{"s3:GetObject", "s3:GetObjectVersion"},
|
2021-08-30 07:55:42 +00:00
|
|
|
Resource: []string{arnAwsPrefix + resInfo.Name()},
|
2021-07-21 11:59:46 +00:00
|
|
|
},
|
|
|
|
},
|
|
|
|
}
|
|
|
|
|
2021-08-30 07:55:42 +00:00
|
|
|
actualPolicy, err := aclToPolicy(acl, resInfo)
|
2021-07-21 11:59:46 +00:00
|
|
|
require.NoError(t, err)
|
|
|
|
require.Equal(t, expectedPolicy, actualPolicy)
|
|
|
|
}
|
|
|
|
|
|
|
|
func TestParseCannedACLHeaders(t *testing.T) {
|
|
|
|
key, err := keys.NewPrivateKey()
|
|
|
|
require.NoError(t, err)
|
|
|
|
|
|
|
|
id := hex.EncodeToString(key.PublicKey().Bytes())
|
|
|
|
address := key.PublicKey().Address()
|
|
|
|
|
|
|
|
req := &http.Request{
|
|
|
|
Header: map[string][]string{
|
|
|
|
api.AmzACL: {basicACLReadOnly},
|
|
|
|
},
|
|
|
|
}
|
|
|
|
|
|
|
|
expectedACL := &AccessControlPolicy{
|
|
|
|
Owner: Owner{
|
|
|
|
ID: id,
|
|
|
|
DisplayName: address,
|
|
|
|
},
|
|
|
|
AccessControlList: []*Grant{{
|
|
|
|
Grantee: &Grantee{
|
|
|
|
ID: id,
|
|
|
|
DisplayName: address,
|
|
|
|
Type: acpCanonicalUser,
|
|
|
|
},
|
|
|
|
Permission: aclFullControl,
|
|
|
|
}, {
|
|
|
|
Grantee: &Grantee{
|
|
|
|
URI: allUsersGroup,
|
|
|
|
Type: acpGroup,
|
|
|
|
},
|
|
|
|
Permission: aclRead,
|
|
|
|
}},
|
|
|
|
}
|
|
|
|
|
2021-10-19 15:08:07 +00:00
|
|
|
actualACL, err := parseACLHeaders(req.Header, key.PublicKey())
|
2021-07-21 11:59:46 +00:00
|
|
|
require.NoError(t, err)
|
|
|
|
require.Equal(t, expectedACL, actualACL)
|
|
|
|
}
|
|
|
|
|
|
|
|
func TestParseACLHeaders(t *testing.T) {
|
|
|
|
key, err := keys.NewPrivateKey()
|
|
|
|
require.NoError(t, err)
|
|
|
|
|
|
|
|
id := hex.EncodeToString(key.PublicKey().Bytes())
|
|
|
|
address := key.PublicKey().Address()
|
|
|
|
|
|
|
|
req := &http.Request{
|
|
|
|
Header: map[string][]string{
|
|
|
|
api.AmzGrantFullControl: {"id=\"user1\""},
|
|
|
|
api.AmzGrantRead: {"uri=\"" + allUsersGroup + "\", id=\"user2\""},
|
|
|
|
api.AmzGrantWrite: {"id=\"user2\", id=\"user3\""},
|
|
|
|
},
|
|
|
|
}
|
|
|
|
|
|
|
|
expectedACL := &AccessControlPolicy{
|
|
|
|
Owner: Owner{
|
|
|
|
ID: id,
|
|
|
|
DisplayName: address,
|
|
|
|
},
|
|
|
|
AccessControlList: []*Grant{{
|
|
|
|
Grantee: &Grantee{
|
|
|
|
ID: id,
|
|
|
|
DisplayName: address,
|
|
|
|
Type: acpCanonicalUser,
|
|
|
|
},
|
|
|
|
Permission: aclFullControl,
|
|
|
|
}, {
|
|
|
|
Grantee: &Grantee{
|
|
|
|
ID: "user1",
|
|
|
|
Type: acpCanonicalUser,
|
|
|
|
},
|
|
|
|
Permission: aclFullControl,
|
|
|
|
}, {
|
|
|
|
Grantee: &Grantee{
|
|
|
|
URI: allUsersGroup,
|
|
|
|
Type: acpGroup,
|
|
|
|
},
|
|
|
|
Permission: aclRead,
|
|
|
|
}, {
|
|
|
|
Grantee: &Grantee{
|
|
|
|
ID: "user2",
|
|
|
|
Type: acpCanonicalUser,
|
|
|
|
},
|
|
|
|
Permission: aclRead,
|
|
|
|
}, {
|
|
|
|
Grantee: &Grantee{
|
|
|
|
ID: "user2",
|
|
|
|
Type: acpCanonicalUser,
|
|
|
|
},
|
|
|
|
Permission: aclWrite,
|
|
|
|
}, {
|
|
|
|
Grantee: &Grantee{
|
|
|
|
ID: "user3",
|
|
|
|
Type: acpCanonicalUser,
|
|
|
|
},
|
|
|
|
Permission: aclWrite,
|
|
|
|
}},
|
|
|
|
}
|
|
|
|
|
2021-10-19 15:08:07 +00:00
|
|
|
actualACL, err := parseACLHeaders(req.Header, key.PublicKey())
|
2021-07-21 11:59:46 +00:00
|
|
|
require.NoError(t, err)
|
|
|
|
require.Equal(t, expectedACL, actualACL)
|
|
|
|
}
|
|
|
|
|
|
|
|
func TestAddGranteeError(t *testing.T) {
|
|
|
|
headers := map[string][]string{
|
|
|
|
api.AmzGrantFullControl: {"i=\"user1\""},
|
|
|
|
api.AmzGrantRead: {"uri, id=\"user2\""},
|
|
|
|
api.AmzGrantWrite: {"emailAddress=\"user2\""},
|
|
|
|
"unknown header": {"something"},
|
|
|
|
}
|
|
|
|
|
|
|
|
expectedList := []*Grant{{
|
|
|
|
Permission: "predefined",
|
|
|
|
}}
|
|
|
|
|
|
|
|
actualList, err := addGrantees(expectedList, headers, "unknown header1")
|
|
|
|
require.NoError(t, err)
|
|
|
|
require.Equal(t, expectedList, actualList)
|
|
|
|
|
|
|
|
actualList, err = addGrantees(expectedList, headers, "unknown header")
|
|
|
|
require.Error(t, err)
|
|
|
|
require.Nil(t, actualList)
|
|
|
|
|
|
|
|
actualList, err = addGrantees(expectedList, headers, api.AmzGrantFullControl)
|
|
|
|
require.Error(t, err)
|
|
|
|
require.Nil(t, actualList)
|
|
|
|
|
|
|
|
actualList, err = addGrantees(expectedList, headers, api.AmzGrantRead)
|
|
|
|
require.Error(t, err)
|
|
|
|
require.Nil(t, actualList)
|
|
|
|
|
|
|
|
actualList, err = addGrantees(expectedList, headers, api.AmzGrantWrite)
|
|
|
|
require.Error(t, err)
|
|
|
|
require.Nil(t, actualList)
|
|
|
|
}
|
|
|
|
|
|
|
|
func TestBucketAclToTable(t *testing.T) {
|
|
|
|
key, err := keys.NewPrivateKey()
|
|
|
|
require.NoError(t, err)
|
|
|
|
key2, err := keys.NewPrivateKey()
|
|
|
|
require.NoError(t, err)
|
|
|
|
|
|
|
|
id := hex.EncodeToString(key.PublicKey().Bytes())
|
|
|
|
id2 := hex.EncodeToString(key2.PublicKey().Bytes())
|
|
|
|
|
|
|
|
acl := &AccessControlPolicy{
|
|
|
|
Owner: Owner{
|
|
|
|
ID: id,
|
|
|
|
DisplayName: "user1",
|
|
|
|
},
|
|
|
|
AccessControlList: []*Grant{{
|
|
|
|
Grantee: &Grantee{
|
|
|
|
URI: allUsersGroup,
|
|
|
|
Type: acpGroup,
|
|
|
|
},
|
|
|
|
Permission: aclRead,
|
|
|
|
}, {
|
|
|
|
Grantee: &Grantee{
|
|
|
|
ID: id2,
|
|
|
|
Type: acpCanonicalUser,
|
|
|
|
},
|
|
|
|
Permission: aclWrite,
|
|
|
|
}},
|
|
|
|
}
|
|
|
|
|
|
|
|
expectedTable := new(eacl.Table)
|
|
|
|
for _, op := range readOps {
|
|
|
|
expectedTable.AddRecord(getOthersRecord(op, eacl.ActionAllow))
|
|
|
|
}
|
|
|
|
for _, op := range writeOps {
|
|
|
|
expectedTable.AddRecord(getAllowRecord(op, key2.PublicKey()))
|
|
|
|
}
|
|
|
|
for _, op := range fullOps {
|
|
|
|
expectedTable.AddRecord(getAllowRecord(op, key.PublicKey()))
|
|
|
|
}
|
|
|
|
for _, op := range fullOps {
|
|
|
|
expectedTable.AddRecord(getOthersRecord(op, eacl.ActionDeny))
|
|
|
|
}
|
2021-08-30 07:55:42 +00:00
|
|
|
resInfo := &resourceInfo{
|
|
|
|
Bucket: "bucketName",
|
|
|
|
}
|
2021-07-21 11:59:46 +00:00
|
|
|
|
2021-08-30 07:55:42 +00:00
|
|
|
actualTable, err := bucketACLToTable(acl, resInfo)
|
2021-07-21 11:59:46 +00:00
|
|
|
require.NoError(t, err)
|
|
|
|
require.Equal(t, expectedTable.Records(), actualTable.Records())
|
|
|
|
}
|
2021-08-30 07:55:42 +00:00
|
|
|
|
|
|
|
func TestObjectAclToAst(t *testing.T) {
|
|
|
|
b := make([]byte, 32)
|
|
|
|
_, err := io.ReadFull(rand.Reader, b)
|
|
|
|
require.NoError(t, err)
|
2022-04-25 09:57:58 +00:00
|
|
|
var objID oid.ID
|
2022-02-08 16:54:04 +00:00
|
|
|
objID.SetSHA256(sha256.Sum256(b))
|
2021-08-30 07:55:42 +00:00
|
|
|
|
|
|
|
key, err := keys.NewPrivateKey()
|
|
|
|
require.NoError(t, err)
|
|
|
|
key2, err := keys.NewPrivateKey()
|
|
|
|
require.NoError(t, err)
|
|
|
|
|
|
|
|
id := hex.EncodeToString(key.PublicKey().Bytes())
|
|
|
|
id2 := hex.EncodeToString(key2.PublicKey().Bytes())
|
|
|
|
|
|
|
|
acl := &AccessControlPolicy{
|
|
|
|
Owner: Owner{
|
|
|
|
ID: id,
|
|
|
|
DisplayName: "user1",
|
|
|
|
},
|
|
|
|
AccessControlList: []*Grant{{
|
|
|
|
Grantee: &Grantee{
|
|
|
|
ID: id,
|
|
|
|
Type: acpCanonicalUser,
|
|
|
|
},
|
|
|
|
Permission: aclFullControl,
|
|
|
|
}, {
|
|
|
|
Grantee: &Grantee{
|
|
|
|
ID: id2,
|
|
|
|
Type: acpCanonicalUser,
|
|
|
|
},
|
|
|
|
Permission: aclRead,
|
|
|
|
},
|
|
|
|
},
|
|
|
|
}
|
|
|
|
|
|
|
|
resInfo := &resourceInfo{
|
|
|
|
Bucket: "bucketName",
|
|
|
|
Object: "object",
|
2022-05-25 17:25:43 +00:00
|
|
|
Version: objID.EncodeToString(),
|
2021-08-30 07:55:42 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
var operations []*astOperation
|
|
|
|
for _, op := range readOps {
|
|
|
|
astOp := &astOperation{Users: []string{
|
|
|
|
hex.EncodeToString(key.PublicKey().Bytes()),
|
|
|
|
hex.EncodeToString(key2.PublicKey().Bytes()),
|
|
|
|
},
|
2022-07-06 12:39:15 +00:00
|
|
|
IsGroupGrantee: false,
|
|
|
|
Op: op,
|
|
|
|
Action: eacl.ActionAllow,
|
2021-08-30 07:55:42 +00:00
|
|
|
}
|
|
|
|
operations = append(operations, astOp)
|
|
|
|
}
|
|
|
|
|
|
|
|
expectedAst := &ast{
|
|
|
|
Resources: []*astResource{
|
|
|
|
{
|
|
|
|
resourceInfo: *resInfo,
|
|
|
|
Operations: operations,
|
|
|
|
},
|
|
|
|
},
|
|
|
|
}
|
|
|
|
|
|
|
|
actualAst, err := aclToAst(acl, resInfo)
|
|
|
|
require.NoError(t, err)
|
|
|
|
require.Equal(t, expectedAst, actualAst)
|
|
|
|
}
|
|
|
|
|
|
|
|
func TestBucketAclToAst(t *testing.T) {
|
|
|
|
b := make([]byte, 32)
|
|
|
|
_, err := io.ReadFull(rand.Reader, b)
|
|
|
|
require.NoError(t, err)
|
2022-04-25 09:57:58 +00:00
|
|
|
var objID oid.ID
|
2022-02-08 16:54:04 +00:00
|
|
|
objID.SetSHA256(sha256.Sum256(b))
|
2021-08-30 07:55:42 +00:00
|
|
|
|
|
|
|
key, err := keys.NewPrivateKey()
|
|
|
|
require.NoError(t, err)
|
|
|
|
key2, err := keys.NewPrivateKey()
|
|
|
|
require.NoError(t, err)
|
|
|
|
|
|
|
|
id := hex.EncodeToString(key.PublicKey().Bytes())
|
|
|
|
id2 := hex.EncodeToString(key2.PublicKey().Bytes())
|
|
|
|
|
|
|
|
acl := &AccessControlPolicy{
|
|
|
|
Owner: Owner{
|
|
|
|
ID: id,
|
|
|
|
DisplayName: "user1",
|
|
|
|
},
|
|
|
|
AccessControlList: []*Grant{
|
|
|
|
{
|
|
|
|
Grantee: &Grantee{
|
|
|
|
ID: id2,
|
|
|
|
Type: acpCanonicalUser,
|
|
|
|
},
|
|
|
|
Permission: aclWrite,
|
|
|
|
}, {
|
|
|
|
Grantee: &Grantee{
|
|
|
|
URI: allUsersGroup,
|
|
|
|
Type: acpGroup,
|
|
|
|
},
|
|
|
|
Permission: aclRead,
|
|
|
|
},
|
|
|
|
},
|
|
|
|
}
|
|
|
|
|
|
|
|
var operations []*astOperation
|
|
|
|
for _, op := range readOps {
|
|
|
|
astOp := &astOperation{Users: []string{
|
|
|
|
hex.EncodeToString(key.PublicKey().Bytes()),
|
|
|
|
},
|
2022-07-06 12:39:15 +00:00
|
|
|
IsGroupGrantee: false,
|
|
|
|
Op: op,
|
|
|
|
Action: eacl.ActionAllow,
|
2021-08-30 07:55:42 +00:00
|
|
|
}
|
|
|
|
operations = append(operations, astOp)
|
|
|
|
}
|
|
|
|
for _, op := range writeOps {
|
|
|
|
astOp := &astOperation{Users: []string{
|
|
|
|
hex.EncodeToString(key.PublicKey().Bytes()),
|
|
|
|
hex.EncodeToString(key2.PublicKey().Bytes()),
|
|
|
|
},
|
2022-07-06 12:39:15 +00:00
|
|
|
IsGroupGrantee: false,
|
|
|
|
Op: op,
|
|
|
|
Action: eacl.ActionAllow,
|
2021-08-30 07:55:42 +00:00
|
|
|
}
|
|
|
|
operations = append(operations, astOp)
|
|
|
|
}
|
|
|
|
for _, op := range readOps {
|
|
|
|
astOp := &astOperation{
|
2022-07-06 12:39:15 +00:00
|
|
|
IsGroupGrantee: true,
|
|
|
|
Op: op,
|
|
|
|
Action: eacl.ActionAllow,
|
2021-08-30 07:55:42 +00:00
|
|
|
}
|
|
|
|
operations = append(operations, astOp)
|
|
|
|
}
|
|
|
|
|
|
|
|
resInfo := &resourceInfo{Bucket: "bucketName"}
|
|
|
|
|
|
|
|
expectedAst := &ast{
|
|
|
|
Resources: []*astResource{
|
|
|
|
{
|
|
|
|
resourceInfo: *resInfo,
|
|
|
|
Operations: operations,
|
|
|
|
},
|
|
|
|
},
|
|
|
|
}
|
|
|
|
|
|
|
|
actualAst, err := aclToAst(acl, resInfo)
|
|
|
|
require.NoError(t, err)
|
|
|
|
require.Equal(t, expectedAst, actualAst)
|
|
|
|
}
|