2021-07-21 11:59:46 +00:00
|
|
|
package handler
|
|
|
|
|
|
|
|
import (
|
2022-11-09 14:25:02 +00:00
|
|
|
"bytes"
|
2021-07-21 11:59:46 +00:00
|
|
|
"crypto/ecdsa"
|
2021-08-30 07:55:42 +00:00
|
|
|
"crypto/rand"
|
|
|
|
"crypto/sha256"
|
2021-07-21 11:59:46 +00:00
|
|
|
"encoding/hex"
|
2022-11-09 14:25:02 +00:00
|
|
|
"encoding/json"
|
2022-07-12 10:39:25 +00:00
|
|
|
"fmt"
|
2021-08-30 07:55:42 +00:00
|
|
|
"io"
|
2021-07-21 11:59:46 +00:00
|
|
|
"net/http"
|
2023-06-30 09:03:55 +00:00
|
|
|
"net/http/httptest"
|
2021-07-21 11:59:46 +00:00
|
|
|
"testing"
|
|
|
|
|
2023-03-07 14:38:08 +00:00
|
|
|
"git.frostfs.info/TrueCloudLab/frostfs-s3-gw/api"
|
|
|
|
"git.frostfs.info/TrueCloudLab/frostfs-s3-gw/api/data"
|
2023-06-30 09:03:55 +00:00
|
|
|
s3errors "git.frostfs.info/TrueCloudLab/frostfs-s3-gw/api/errors"
|
2024-02-12 12:28:55 +00:00
|
|
|
"git.frostfs.info/TrueCloudLab/frostfs-s3-gw/api/layer"
|
2023-07-05 14:05:45 +00:00
|
|
|
"git.frostfs.info/TrueCloudLab/frostfs-s3-gw/api/middleware"
|
2023-03-07 14:38:08 +00:00
|
|
|
"git.frostfs.info/TrueCloudLab/frostfs-s3-gw/creds/accessbox"
|
|
|
|
"git.frostfs.info/TrueCloudLab/frostfs-sdk-go/bearer"
|
2024-02-12 12:28:55 +00:00
|
|
|
"git.frostfs.info/TrueCloudLab/frostfs-sdk-go/container/acl"
|
2023-03-07 14:38:08 +00:00
|
|
|
"git.frostfs.info/TrueCloudLab/frostfs-sdk-go/eacl"
|
|
|
|
"git.frostfs.info/TrueCloudLab/frostfs-sdk-go/object"
|
|
|
|
oid "git.frostfs.info/TrueCloudLab/frostfs-sdk-go/object/id"
|
|
|
|
"git.frostfs.info/TrueCloudLab/frostfs-sdk-go/session"
|
2023-12-08 07:44:13 +00:00
|
|
|
engineiam "git.frostfs.info/TrueCloudLab/policy-engine/iam"
|
2024-02-12 14:29:36 +00:00
|
|
|
"git.frostfs.info/TrueCloudLab/policy-engine/pkg/engine"
|
2021-07-21 11:59:46 +00:00
|
|
|
"github.com/nspcc-dev/neo-go/pkg/crypto/keys"
|
|
|
|
"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-07 09:00:09 +00:00
|
|
|
// Unknown role is used, because it is ignored when keys are set
|
2022-07-06 12:39:15 +00:00
|
|
|
eacl.AddFormedTarget(record2, eacl.RoleUnknown, *(*ecdsa.PublicKey)(key.PublicKey()), *((*ecdsa.PublicKey)(key2.PublicKey())))
|
2022-09-07 06:59:24 +00:00
|
|
|
record2.AddObjectAttributeFilter(eacl.MatchStringEqual, object.AttributeFilePath, "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 13:28:28 +00:00
|
|
|
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 13:28:28 +00:00
|
|
|
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 13:28:28 +00:00
|
|
|
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 {
|
2022-07-06 13:28:28 +00:00
|
|
|
var (
|
|
|
|
result []*astOperation
|
|
|
|
users []string
|
|
|
|
)
|
|
|
|
if !groupGrantee {
|
|
|
|
users = append(users, hex.EncodeToString(key.PublicKey().Bytes()))
|
|
|
|
}
|
2021-07-21 11:59:46 +00:00
|
|
|
|
|
|
|
for _, op := range readOps {
|
|
|
|
result = append(result, &astOperation{
|
2022-07-06 13:28:28 +00:00
|
|
|
Users: users,
|
|
|
|
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 13:28:28 +00:00
|
|
|
Users: []string{hex.EncodeToString(key.PublicKey().Bytes())},
|
|
|
|
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 13:28:28 +00:00
|
|
|
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 13:28:28 +00:00
|
|
|
Op: eacl.OperationPut,
|
|
|
|
Action: eacl.ActionDeny,
|
2021-07-21 11:59:46 +00:00
|
|
|
}, {
|
2022-07-06 13:28:28 +00:00
|
|
|
Users: []string{"user2"},
|
|
|
|
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 13:28:28 +00:00
|
|
|
Users: []string{"user1"},
|
|
|
|
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 13:28:28 +00:00
|
|
|
Users: []string{"user1", "user2"},
|
|
|
|
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)
|
|
|
|
}
|
|
|
|
|
2022-07-17 03:33:47 +00:00
|
|
|
func TestMergeAppended(t *testing.T) {
|
|
|
|
key, err := keys.NewPrivateKey()
|
|
|
|
require.NoError(t, err)
|
|
|
|
users := []string{hex.EncodeToString(key.PublicKey().Bytes())}
|
|
|
|
|
|
|
|
parent := &ast{
|
|
|
|
Resources: []*astResource{
|
|
|
|
{
|
|
|
|
resourceInfo: resourceInfo{
|
|
|
|
Bucket: "bucket",
|
|
|
|
},
|
|
|
|
Operations: []*astOperation{
|
|
|
|
{
|
|
|
|
Users: users,
|
|
|
|
Op: eacl.OperationGet,
|
|
|
|
Action: eacl.ActionAllow,
|
|
|
|
},
|
|
|
|
{
|
|
|
|
Users: users,
|
|
|
|
Op: eacl.OperationPut,
|
|
|
|
Action: eacl.ActionAllow,
|
|
|
|
},
|
|
|
|
{
|
|
|
|
Users: users,
|
|
|
|
Op: eacl.OperationDelete,
|
|
|
|
Action: eacl.ActionAllow,
|
|
|
|
},
|
|
|
|
{
|
|
|
|
Op: eacl.OperationGet,
|
|
|
|
Action: eacl.ActionDeny,
|
|
|
|
},
|
|
|
|
{
|
|
|
|
Op: eacl.OperationPut,
|
|
|
|
Action: eacl.ActionDeny,
|
|
|
|
},
|
|
|
|
{
|
|
|
|
Op: eacl.OperationDelete,
|
|
|
|
Action: eacl.ActionDeny,
|
|
|
|
},
|
|
|
|
},
|
|
|
|
},
|
|
|
|
},
|
|
|
|
}
|
|
|
|
|
|
|
|
child := &ast{
|
|
|
|
Resources: []*astResource{
|
|
|
|
{
|
|
|
|
resourceInfo: resourceInfo{
|
|
|
|
Bucket: "bucket",
|
|
|
|
Object: "objectName",
|
|
|
|
},
|
|
|
|
Operations: []*astOperation{
|
|
|
|
{
|
|
|
|
Users: users,
|
|
|
|
Op: eacl.OperationGet,
|
|
|
|
Action: eacl.ActionAllow,
|
|
|
|
},
|
|
|
|
{
|
|
|
|
Users: users,
|
|
|
|
Op: eacl.OperationPut,
|
|
|
|
Action: eacl.ActionAllow,
|
|
|
|
},
|
|
|
|
{
|
|
|
|
Users: users,
|
|
|
|
Op: eacl.OperationDelete,
|
|
|
|
Action: eacl.ActionAllow,
|
|
|
|
},
|
|
|
|
{
|
|
|
|
Op: eacl.OperationGet,
|
|
|
|
Action: eacl.ActionAllow,
|
|
|
|
},
|
|
|
|
{
|
|
|
|
Op: eacl.OperationPut,
|
|
|
|
Action: eacl.ActionAllow,
|
|
|
|
},
|
|
|
|
{
|
|
|
|
Op: eacl.OperationDelete,
|
|
|
|
Action: eacl.ActionAllow,
|
|
|
|
},
|
|
|
|
},
|
|
|
|
},
|
|
|
|
},
|
|
|
|
}
|
|
|
|
|
|
|
|
expected := &ast{
|
|
|
|
Resources: []*astResource{
|
|
|
|
{
|
|
|
|
resourceInfo: resourceInfo{
|
|
|
|
Bucket: "bucket",
|
|
|
|
},
|
|
|
|
Operations: []*astOperation{
|
|
|
|
{
|
|
|
|
Users: users,
|
|
|
|
Op: eacl.OperationGet,
|
|
|
|
Action: eacl.ActionAllow,
|
|
|
|
},
|
|
|
|
{
|
|
|
|
Users: users,
|
|
|
|
Op: eacl.OperationPut,
|
|
|
|
Action: eacl.ActionAllow,
|
|
|
|
},
|
|
|
|
{
|
|
|
|
Users: users,
|
|
|
|
Op: eacl.OperationDelete,
|
|
|
|
Action: eacl.ActionAllow,
|
|
|
|
},
|
|
|
|
{
|
|
|
|
Op: eacl.OperationGet,
|
|
|
|
Action: eacl.ActionDeny,
|
|
|
|
},
|
|
|
|
{
|
|
|
|
Op: eacl.OperationPut,
|
|
|
|
Action: eacl.ActionDeny,
|
|
|
|
},
|
|
|
|
{
|
|
|
|
Op: eacl.OperationDelete,
|
|
|
|
Action: eacl.ActionDeny,
|
|
|
|
},
|
|
|
|
},
|
|
|
|
},
|
|
|
|
{
|
|
|
|
resourceInfo: resourceInfo{
|
|
|
|
Bucket: "bucket",
|
|
|
|
Object: "objectName",
|
|
|
|
},
|
|
|
|
Operations: []*astOperation{
|
|
|
|
{
|
|
|
|
Users: users,
|
|
|
|
Op: eacl.OperationGet,
|
|
|
|
Action: eacl.ActionAllow,
|
|
|
|
},
|
|
|
|
{
|
|
|
|
Users: users,
|
|
|
|
Op: eacl.OperationPut,
|
|
|
|
Action: eacl.ActionAllow,
|
|
|
|
},
|
|
|
|
{
|
|
|
|
Users: users,
|
|
|
|
Op: eacl.OperationDelete,
|
|
|
|
Action: eacl.ActionAllow,
|
|
|
|
},
|
|
|
|
{
|
|
|
|
Op: eacl.OperationGet,
|
|
|
|
Action: eacl.ActionAllow,
|
|
|
|
},
|
|
|
|
{
|
|
|
|
Op: eacl.OperationPut,
|
|
|
|
Action: eacl.ActionAllow,
|
|
|
|
},
|
|
|
|
{
|
|
|
|
Op: eacl.OperationDelete,
|
|
|
|
Action: eacl.ActionAllow,
|
|
|
|
},
|
|
|
|
},
|
|
|
|
},
|
|
|
|
},
|
|
|
|
}
|
|
|
|
actual, updated := mergeAst(parent, child)
|
|
|
|
require.True(t, updated)
|
|
|
|
require.Equal(t, expected, actual)
|
|
|
|
}
|
|
|
|
|
|
|
|
func TestOrder(t *testing.T) {
|
|
|
|
key, err := keys.NewPrivateKey()
|
|
|
|
require.NoError(t, err)
|
|
|
|
users := []string{hex.EncodeToString(key.PublicKey().Bytes())}
|
|
|
|
targetUser := eacl.NewTarget()
|
|
|
|
targetUser.SetBinaryKeys([][]byte{key.PublicKey().Bytes()})
|
|
|
|
targetOther := eacl.NewTarget()
|
|
|
|
targetOther.SetRole(eacl.RoleOthers)
|
|
|
|
bucketName := "bucket"
|
|
|
|
objectName := "objectName"
|
|
|
|
|
|
|
|
expectedAst := &ast{
|
|
|
|
Resources: []*astResource{
|
|
|
|
{
|
|
|
|
resourceInfo: resourceInfo{
|
|
|
|
Bucket: bucketName,
|
|
|
|
},
|
|
|
|
Operations: []*astOperation{
|
|
|
|
{
|
|
|
|
Users: users,
|
|
|
|
Op: eacl.OperationGet,
|
|
|
|
Action: eacl.ActionAllow,
|
|
|
|
},
|
|
|
|
{
|
|
|
|
Op: eacl.OperationGet,
|
|
|
|
Action: eacl.ActionDeny,
|
|
|
|
},
|
|
|
|
},
|
|
|
|
},
|
|
|
|
{
|
|
|
|
resourceInfo: resourceInfo{
|
|
|
|
Bucket: bucketName,
|
|
|
|
Object: objectName,
|
|
|
|
},
|
|
|
|
Operations: []*astOperation{
|
|
|
|
{
|
|
|
|
Users: users,
|
2022-07-15 14:35:11 +00:00
|
|
|
Op: eacl.OperationPut,
|
2022-07-17 03:33:47 +00:00
|
|
|
Action: eacl.ActionAllow,
|
|
|
|
},
|
|
|
|
{
|
2022-07-15 14:35:11 +00:00
|
|
|
Op: eacl.OperationPut,
|
2022-07-17 03:33:47 +00:00
|
|
|
Action: eacl.ActionDeny,
|
|
|
|
},
|
|
|
|
},
|
|
|
|
},
|
|
|
|
},
|
|
|
|
}
|
2022-07-15 14:35:11 +00:00
|
|
|
bucketServiceRec := &ServiceRecord{Resource: expectedAst.Resources[0].Name(), GroupRecordsLength: 2}
|
|
|
|
bucketUsersGetRec := eacl.NewRecord()
|
|
|
|
bucketUsersGetRec.SetOperation(eacl.OperationGet)
|
|
|
|
bucketUsersGetRec.SetAction(eacl.ActionAllow)
|
|
|
|
bucketUsersGetRec.SetTargets(*targetUser)
|
|
|
|
bucketOtherGetRec := eacl.NewRecord()
|
|
|
|
bucketOtherGetRec.SetOperation(eacl.OperationGet)
|
|
|
|
bucketOtherGetRec.SetAction(eacl.ActionDeny)
|
|
|
|
bucketOtherGetRec.SetTargets(*targetOther)
|
|
|
|
objectServiceRec := &ServiceRecord{Resource: expectedAst.Resources[1].Name(), GroupRecordsLength: 2}
|
|
|
|
objectUsersPutRec := eacl.NewRecord()
|
|
|
|
objectUsersPutRec.SetOperation(eacl.OperationPut)
|
|
|
|
objectUsersPutRec.SetAction(eacl.ActionAllow)
|
2022-09-07 06:59:24 +00:00
|
|
|
objectUsersPutRec.AddObjectAttributeFilter(eacl.MatchStringEqual, object.AttributeFilePath, objectName)
|
2022-07-15 14:35:11 +00:00
|
|
|
objectUsersPutRec.SetTargets(*targetUser)
|
|
|
|
objectOtherPutRec := eacl.NewRecord()
|
|
|
|
objectOtherPutRec.SetOperation(eacl.OperationPut)
|
|
|
|
objectOtherPutRec.SetAction(eacl.ActionDeny)
|
2022-09-07 06:59:24 +00:00
|
|
|
objectOtherPutRec.AddObjectAttributeFilter(eacl.MatchStringEqual, object.AttributeFilePath, objectName)
|
2022-07-15 14:35:11 +00:00
|
|
|
objectOtherPutRec.SetTargets(*targetOther)
|
2022-07-17 03:33:47 +00:00
|
|
|
|
|
|
|
expectedEacl := eacl.NewTable()
|
2022-07-15 14:35:11 +00:00
|
|
|
expectedEacl.AddRecord(objectServiceRec.ToEACLRecord())
|
|
|
|
expectedEacl.AddRecord(objectOtherPutRec)
|
|
|
|
expectedEacl.AddRecord(objectUsersPutRec)
|
|
|
|
expectedEacl.AddRecord(bucketServiceRec.ToEACLRecord())
|
|
|
|
expectedEacl.AddRecord(bucketOtherGetRec)
|
|
|
|
expectedEacl.AddRecord(bucketUsersGetRec)
|
2022-07-17 03:33:47 +00:00
|
|
|
|
|
|
|
t.Run("astToTable order and vice versa", func(t *testing.T) {
|
|
|
|
actualEacl, err := astToTable(expectedAst)
|
|
|
|
require.NoError(t, err)
|
|
|
|
require.Equal(t, expectedEacl, actualEacl)
|
|
|
|
|
|
|
|
actualAst := tableToAst(actualEacl, bucketName)
|
|
|
|
require.Equal(t, expectedAst, actualAst)
|
|
|
|
})
|
|
|
|
|
|
|
|
t.Run("tableToAst order and vice versa", func(t *testing.T) {
|
|
|
|
actualAst := tableToAst(expectedEacl, bucketName)
|
|
|
|
require.Equal(t, expectedAst, actualAst)
|
|
|
|
|
|
|
|
actualEacl, err := astToTable(actualAst)
|
|
|
|
require.NoError(t, err)
|
|
|
|
require.Equal(t, expectedEacl, actualEacl)
|
|
|
|
})
|
|
|
|
|
|
|
|
t.Run("append a resource", func(t *testing.T) {
|
|
|
|
childName := "child"
|
|
|
|
child := &ast{Resources: []*astResource{{
|
|
|
|
resourceInfo: resourceInfo{
|
|
|
|
Bucket: bucketName,
|
|
|
|
Object: childName,
|
|
|
|
},
|
|
|
|
Operations: []*astOperation{{Op: eacl.OperationDelete, Action: eacl.ActionDeny}}}},
|
|
|
|
}
|
|
|
|
|
|
|
|
childRecord := eacl.NewRecord()
|
|
|
|
childRecord.SetOperation(eacl.OperationDelete)
|
|
|
|
childRecord.SetAction(eacl.ActionDeny)
|
|
|
|
childRecord.SetTargets(*targetOther)
|
2022-09-07 06:59:24 +00:00
|
|
|
childRecord.AddObjectAttributeFilter(eacl.MatchStringEqual, object.AttributeFilePath, childName)
|
2022-07-17 03:33:47 +00:00
|
|
|
|
|
|
|
mergedAst, updated := mergeAst(expectedAst, child)
|
|
|
|
require.True(t, updated)
|
|
|
|
|
|
|
|
mergedEacl, err := astToTable(mergedAst)
|
|
|
|
require.NoError(t, err)
|
|
|
|
|
2022-07-15 14:35:11 +00:00
|
|
|
require.Equal(t, *childRecord, mergedEacl.Records()[1])
|
2022-07-17 03:33:47 +00:00
|
|
|
})
|
|
|
|
}
|
|
|
|
|
2021-07-21 11:59:46 +00:00
|
|
|
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 13:28:28 +00:00
|
|
|
Users: []string{"user1"},
|
|
|
|
Op: eacl.OperationPut,
|
|
|
|
Action: eacl.ActionDeny,
|
2021-07-21 11:59:46 +00:00
|
|
|
}, {
|
2022-07-06 13:28:28 +00:00
|
|
|
Users: []string{"user3"},
|
|
|
|
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 13:28:28 +00:00
|
|
|
Users: []string{"user1"},
|
|
|
|
Op: eacl.OperationPut,
|
|
|
|
Action: eacl.ActionAllow,
|
2021-07-21 11:59:46 +00:00
|
|
|
}, {
|
2022-07-06 13:28:28 +00:00
|
|
|
Users: []string{"user2"},
|
|
|
|
Op: eacl.OperationPut,
|
|
|
|
Action: eacl.ActionDeny,
|
2021-07-21 11:59:46 +00:00
|
|
|
}, {
|
2022-07-06 13:28:28 +00:00
|
|
|
Users: []string{"user3"},
|
|
|
|
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 13:28:28 +00:00
|
|
|
Users: []string{"user2", "user1"},
|
|
|
|
Op: eacl.OperationPut,
|
|
|
|
Action: eacl.ActionDeny,
|
2021-07-21 11:59:46 +00:00
|
|
|
}, {
|
2022-07-06 13:28:28 +00:00
|
|
|
Users: []string{"user3"},
|
|
|
|
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 13:28:28 +00:00
|
|
|
Users: []string{hex.EncodeToString(key.PublicKey().Bytes())},
|
|
|
|
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 13:28:28 +00:00
|
|
|
Op: eacl.OperationGet,
|
|
|
|
Action: eacl.ActionDeny,
|
2021-07-21 11:59:46 +00:00
|
|
|
}},
|
|
|
|
},
|
|
|
|
},
|
|
|
|
}
|
|
|
|
|
|
|
|
expectedTable := eacl.NewTable()
|
2022-07-15 14:35:11 +00:00
|
|
|
serviceRec1 := &ServiceRecord{Resource: ast.Resources[0].Name(), GroupRecordsLength: 1}
|
|
|
|
record1 := eacl.NewRecord()
|
|
|
|
record1.SetAction(eacl.ActionAllow)
|
|
|
|
record1.SetOperation(eacl.OperationPut)
|
|
|
|
// Unknown role is used, because it is ignored when keys are set
|
|
|
|
eacl.AddFormedTarget(record1, eacl.RoleUnknown, *(*ecdsa.PublicKey)(key.PublicKey()))
|
2022-07-17 03:33:47 +00:00
|
|
|
|
2022-07-15 14:35:11 +00:00
|
|
|
serviceRec2 := &ServiceRecord{Resource: ast.Resources[1].Name(), GroupRecordsLength: 1}
|
2021-07-21 11:59:46 +00:00
|
|
|
record2 := eacl.NewRecord()
|
2022-07-15 14:35:11 +00:00
|
|
|
record2.SetAction(eacl.ActionDeny)
|
|
|
|
record2.SetOperation(eacl.OperationGet)
|
|
|
|
eacl.AddFormedTarget(record2, eacl.RoleOthers)
|
2022-09-07 06:59:24 +00:00
|
|
|
record2.AddObjectAttributeFilter(eacl.MatchStringEqual, object.AttributeFilePath, "objectName")
|
2022-07-15 14:35:11 +00:00
|
|
|
|
|
|
|
expectedTable.AddRecord(serviceRec2.ToEACLRecord())
|
2021-07-21 11:59:46 +00:00
|
|
|
expectedTable.AddRecord(record2)
|
2022-07-15 14:35:11 +00:00
|
|
|
expectedTable.AddRecord(serviceRec1.ToEACLRecord())
|
|
|
|
expectedTable.AddRecord(record1)
|
2021-07-21 11:59:46 +00:00
|
|
|
|
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 13:28:28 +00:00
|
|
|
Users: []string{"user1", "user3", "user4"},
|
|
|
|
Op: eacl.OperationPut,
|
|
|
|
Action: eacl.ActionAllow,
|
|
|
|
},
|
|
|
|
{
|
|
|
|
Users: []string{"user5"},
|
|
|
|
Op: eacl.OperationGet,
|
|
|
|
Action: eacl.ActionDeny,
|
|
|
|
},
|
|
|
|
},
|
2021-07-21 11:59:46 +00:00
|
|
|
}
|
|
|
|
|
2022-07-06 13:28:28 +00:00
|
|
|
op1 := &astOperation{
|
|
|
|
Op: eacl.OperationPut,
|
|
|
|
Action: eacl.ActionAllow,
|
|
|
|
}
|
|
|
|
op2 := &astOperation{
|
|
|
|
Op: eacl.OperationGet,
|
|
|
|
Action: eacl.ActionDeny,
|
2021-07-21 11:59:46 +00:00
|
|
|
}
|
|
|
|
|
2022-07-06 13:28:28 +00:00
|
|
|
removeUsers(resource, op1, []string{"user1", "user2", "user4"}) // modify astOperation
|
|
|
|
removeUsers(resource, op2, []string{"user5"}) // remove astOperation
|
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{
|
2022-07-12 10:39:25 +00:00
|
|
|
Bucket: resInfo.Bucket,
|
2021-07-21 11:59:46 +00:00
|
|
|
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()},
|
2022-07-12 10:39:25 +00:00
|
|
|
},
|
|
|
|
{
|
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()},
|
2022-07-12 10:39:25 +00:00
|
|
|
},
|
|
|
|
{
|
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{
|
2022-07-12 10:39:25 +00:00
|
|
|
Bucket: resInfo.Bucket,
|
2021-07-21 11:59:46 +00:00
|
|
|
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()},
|
2022-07-12 10:39:25 +00:00
|
|
|
},
|
|
|
|
{
|
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)
|
|
|
|
}
|
|
|
|
|
2022-07-12 10:39:25 +00:00
|
|
|
func TestObjectWithVersionAclToTable(t *testing.T) {
|
|
|
|
key, err := keys.NewPrivateKey()
|
|
|
|
require.NoError(t, err)
|
|
|
|
id := hex.EncodeToString(key.PublicKey().Bytes())
|
|
|
|
|
|
|
|
acl := &AccessControlPolicy{
|
|
|
|
Owner: Owner{
|
|
|
|
ID: id,
|
|
|
|
DisplayName: "user1",
|
|
|
|
},
|
|
|
|
AccessControlList: []*Grant{{
|
|
|
|
Grantee: &Grantee{
|
|
|
|
ID: id,
|
|
|
|
Type: acpCanonicalUser,
|
|
|
|
},
|
|
|
|
Permission: aclFullControl,
|
|
|
|
}},
|
|
|
|
}
|
|
|
|
|
|
|
|
resInfoObject := &resourceInfo{
|
|
|
|
Bucket: "bucketName",
|
|
|
|
Object: "object",
|
|
|
|
}
|
2022-07-21 09:55:33 +00:00
|
|
|
expectedTable := allowedTableForPrivateObject(t, key, resInfoObject)
|
2022-07-12 10:39:25 +00:00
|
|
|
actualTable := tableFromACL(t, acl, resInfoObject)
|
|
|
|
checkTables(t, expectedTable, actualTable)
|
|
|
|
|
|
|
|
resInfoObjectVersion := &resourceInfo{
|
|
|
|
Bucket: "bucketName",
|
|
|
|
Object: "objectVersion",
|
|
|
|
Version: "Gfrct4Afhio8pCGCCKVNTf1kyexQjMBeaUfvDtQCkAvg",
|
|
|
|
}
|
2022-07-21 09:55:33 +00:00
|
|
|
expectedTable = allowedTableForPrivateObject(t, key, resInfoObjectVersion)
|
2022-07-12 10:39:25 +00:00
|
|
|
actualTable = tableFromACL(t, acl, resInfoObjectVersion)
|
|
|
|
checkTables(t, expectedTable, actualTable)
|
|
|
|
}
|
|
|
|
|
2022-07-21 09:55:33 +00:00
|
|
|
func allowedTableForPrivateObject(t *testing.T, key *keys.PrivateKey, resInfo *resourceInfo) *eacl.Table {
|
2022-07-12 10:39:25 +00:00
|
|
|
var isVersion bool
|
|
|
|
var objID oid.ID
|
|
|
|
if resInfo.Version != "" {
|
|
|
|
isVersion = true
|
|
|
|
err := objID.DecodeString(resInfo.Version)
|
|
|
|
require.NoError(t, err)
|
|
|
|
}
|
|
|
|
|
|
|
|
expectedTable := eacl.NewTable()
|
2022-07-21 09:55:33 +00:00
|
|
|
serviceRec := &ServiceRecord{Resource: resInfo.Name(), GroupRecordsLength: len(readOps) * 2}
|
2022-07-15 14:35:11 +00:00
|
|
|
expectedTable.AddRecord(serviceRec.ToEACLRecord())
|
|
|
|
|
|
|
|
for i := len(readOps) - 1; i >= 0; i-- {
|
|
|
|
op := readOps[i]
|
2022-07-12 10:39:25 +00:00
|
|
|
record := getAllowRecord(op, key.PublicKey())
|
|
|
|
if isVersion {
|
|
|
|
record.AddObjectIDFilter(eacl.MatchStringEqual, objID)
|
|
|
|
} else {
|
2022-09-07 06:59:24 +00:00
|
|
|
record.AddObjectAttributeFilter(eacl.MatchStringEqual, object.AttributeFilePath, resInfo.Object)
|
2022-07-12 10:39:25 +00:00
|
|
|
}
|
|
|
|
expectedTable.AddRecord(record)
|
|
|
|
}
|
2022-07-21 09:55:33 +00:00
|
|
|
for i := len(readOps) - 1; i >= 0; i-- {
|
|
|
|
op := readOps[i]
|
|
|
|
record := getOthersRecord(op, eacl.ActionDeny)
|
|
|
|
if isVersion {
|
|
|
|
record.AddObjectIDFilter(eacl.MatchStringEqual, objID)
|
|
|
|
} else {
|
2022-09-07 06:59:24 +00:00
|
|
|
record.AddObjectAttributeFilter(eacl.MatchStringEqual, object.AttributeFilePath, resInfo.Object)
|
2022-07-21 09:55:33 +00:00
|
|
|
}
|
|
|
|
expectedTable.AddRecord(record)
|
|
|
|
}
|
2022-07-12 10:39:25 +00:00
|
|
|
|
|
|
|
return expectedTable
|
|
|
|
}
|
|
|
|
|
|
|
|
func tableFromACL(t *testing.T, acl *AccessControlPolicy, resInfo *resourceInfo) *eacl.Table {
|
|
|
|
actualPolicy, err := aclToPolicy(acl, resInfo)
|
|
|
|
require.NoError(t, err)
|
|
|
|
actualAst, err := policyToAst(actualPolicy)
|
|
|
|
require.NoError(t, err)
|
|
|
|
actualTable, err := astToTable(actualAst)
|
|
|
|
require.NoError(t, err)
|
|
|
|
return actualTable
|
|
|
|
}
|
|
|
|
|
|
|
|
func checkTables(t *testing.T, expectedTable, actualTable *eacl.Table) {
|
|
|
|
require.Equal(t, len(expectedTable.Records()), len(actualTable.Records()), "different number of records")
|
|
|
|
for i, record := range expectedTable.Records() {
|
|
|
|
actRecord := actualTable.Records()[i]
|
|
|
|
|
|
|
|
require.Equal(t, len(record.Targets()), len(actRecord.Targets()), "different number of targets")
|
|
|
|
for j, target := range record.Targets() {
|
|
|
|
actTarget := actRecord.Targets()[j]
|
|
|
|
|
|
|
|
expected := fmt.Sprintf("%s %v", target.Role().String(), target.BinaryKeys())
|
|
|
|
actual := fmt.Sprintf("%s %v", actTarget.Role().String(), actTarget.BinaryKeys())
|
|
|
|
require.Equalf(t, target, actTarget, "want: '%s'\ngot: '%s'", expected, actual)
|
|
|
|
}
|
|
|
|
|
|
|
|
require.Equal(t, len(record.Filters()), len(actRecord.Filters()), "different number of filters")
|
|
|
|
for j, filter := range record.Filters() {
|
|
|
|
actFilter := actRecord.Filters()[j]
|
|
|
|
|
|
|
|
expected := fmt.Sprintf("%s:%s %s %s", filter.From().String(), filter.Key(), filter.Matcher().String(), filter.Value())
|
|
|
|
actual := fmt.Sprintf("%s:%s %s %s", actFilter.From().String(), actFilter.Key(), actFilter.Matcher().String(), actFilter.Value())
|
|
|
|
require.Equalf(t, filter, actFilter, "want: '%s'\ngot: '%s'", expected, actual)
|
|
|
|
}
|
|
|
|
|
|
|
|
require.Equal(t, record.Action().String(), actRecord.Action().String())
|
|
|
|
require.Equal(t, record.Operation().String(), actRecord.Operation().String())
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2021-07-21 11:59:46 +00:00
|
|
|
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 13:28:28 +00:00
|
|
|
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 13:28:28 +00:00
|
|
|
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 13:28:28 +00:00
|
|
|
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 13:28:28 +00:00
|
|
|
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)
|
|
|
|
}
|
2022-09-06 07:43:04 +00:00
|
|
|
|
|
|
|
func TestPutBucketACL(t *testing.T) {
|
|
|
|
tc := prepareHandlerContext(t)
|
|
|
|
bktName := "bucket-for-acl"
|
|
|
|
|
2022-11-09 14:25:02 +00:00
|
|
|
box, _ := createAccessBox(t)
|
2024-02-12 12:28:55 +00:00
|
|
|
bktInfo := createBucketOldACL(tc, bktName, box)
|
2022-09-06 07:43:04 +00:00
|
|
|
|
|
|
|
header := map[string]string{api.AmzACL: "public-read"}
|
2024-02-12 14:29:36 +00:00
|
|
|
putBucketACL(tc, bktName, box, header)
|
2022-09-06 07:43:04 +00:00
|
|
|
|
|
|
|
header = map[string]string{api.AmzACL: "private"}
|
2024-02-12 14:29:36 +00:00
|
|
|
putBucketACL(tc, bktName, box, header)
|
2022-09-06 07:43:04 +00:00
|
|
|
checkLastRecords(t, tc, bktInfo, eacl.ActionDeny)
|
|
|
|
}
|
|
|
|
|
2024-02-12 14:29:36 +00:00
|
|
|
func TestPutBucketAPE(t *testing.T) {
|
|
|
|
hc := prepareHandlerContext(t)
|
|
|
|
bktName := "bucket-for-acl-ape"
|
|
|
|
|
|
|
|
info := createBucket(hc, bktName)
|
|
|
|
|
|
|
|
_, err := hc.tp.ContainerEACL(hc.Context(), info.BktInfo.CID)
|
|
|
|
require.ErrorContains(t, err, "not found")
|
|
|
|
|
2024-02-13 08:50:11 +00:00
|
|
|
chains, err := hc.h.ape.(*apeMock).ListChains(engine.NamespaceTarget(""))
|
2024-02-12 14:29:36 +00:00
|
|
|
require.NoError(t, err)
|
|
|
|
require.Len(t, chains, 2)
|
|
|
|
}
|
|
|
|
|
|
|
|
func TestPutBucketObjectACLErrorAPE(t *testing.T) {
|
|
|
|
hc := prepareHandlerContext(t)
|
|
|
|
bktName, objName := "bucket-for-acl-ape", "object"
|
|
|
|
|
|
|
|
info := createBucket(hc, bktName)
|
|
|
|
putObject(hc, bktName, objName)
|
|
|
|
|
|
|
|
aclBody := &AccessControlPolicy{}
|
|
|
|
putBucketACLAssertS3Error(hc, bktName, info.Box, nil, aclBody, s3errors.ErrAccessControlListNotSupported)
|
|
|
|
|
|
|
|
putObjectACLAssertS3Error(hc, bktName, objName, info.Box, nil, aclBody, s3errors.ErrAccessControlListNotSupported)
|
|
|
|
getObjectACLAssertS3Error(hc, bktName, objName, s3errors.ErrAccessControlListNotSupported)
|
|
|
|
}
|
|
|
|
|
|
|
|
func TestGetBucketACLAPE(t *testing.T) {
|
|
|
|
hc := prepareHandlerContext(t)
|
|
|
|
bktName := "bucket-for-acl-ape"
|
|
|
|
|
|
|
|
info := createBucket(hc, bktName)
|
|
|
|
|
|
|
|
aclRes := getBucketACL(hc, bktName)
|
|
|
|
checkPrivateBucketACL(t, aclRes, info.Key.PublicKey())
|
|
|
|
|
|
|
|
putBucketACL(hc, bktName, info.Box, map[string]string{api.AmzACL: basicACLPrivate})
|
|
|
|
aclRes = getBucketACL(hc, bktName)
|
|
|
|
checkPrivateBucketACL(t, aclRes, info.Key.PublicKey())
|
|
|
|
|
|
|
|
putBucketACL(hc, bktName, info.Box, map[string]string{api.AmzACL: basicACLReadOnly})
|
|
|
|
aclRes = getBucketACL(hc, bktName)
|
|
|
|
checkPublicReadBucketACL(t, aclRes, info.Key.PublicKey())
|
|
|
|
|
|
|
|
putBucketACL(hc, bktName, info.Box, map[string]string{api.AmzACL: basicACLPublic})
|
|
|
|
aclRes = getBucketACL(hc, bktName)
|
|
|
|
checkPublicReadWriteBucketACL(t, aclRes, info.Key.PublicKey())
|
|
|
|
}
|
|
|
|
|
|
|
|
func checkPrivateBucketACL(t *testing.T, aclRes *AccessControlPolicy, ownerKey *keys.PublicKey) {
|
|
|
|
checkBucketACLOwner(t, aclRes, ownerKey, 1)
|
|
|
|
}
|
|
|
|
|
|
|
|
func checkPublicReadBucketACL(t *testing.T, aclRes *AccessControlPolicy, ownerKey *keys.PublicKey) {
|
|
|
|
checkBucketACLOwner(t, aclRes, ownerKey, 2)
|
|
|
|
|
|
|
|
require.Equal(t, allUsersGroup, aclRes.AccessControlList[1].Grantee.URI)
|
|
|
|
require.Equal(t, aclRead, aclRes.AccessControlList[1].Permission)
|
|
|
|
}
|
|
|
|
|
|
|
|
func checkPublicReadWriteBucketACL(t *testing.T, aclRes *AccessControlPolicy, ownerKey *keys.PublicKey) {
|
|
|
|
checkBucketACLOwner(t, aclRes, ownerKey, 3)
|
|
|
|
|
|
|
|
require.Equal(t, allUsersGroup, aclRes.AccessControlList[1].Grantee.URI)
|
|
|
|
require.Equal(t, aclWrite, aclRes.AccessControlList[1].Permission)
|
|
|
|
|
|
|
|
require.Equal(t, allUsersGroup, aclRes.AccessControlList[2].Grantee.URI)
|
|
|
|
require.Equal(t, aclRead, aclRes.AccessControlList[2].Permission)
|
|
|
|
}
|
|
|
|
|
|
|
|
func checkBucketACLOwner(t *testing.T, aclRes *AccessControlPolicy, ownerKey *keys.PublicKey, ln int) {
|
|
|
|
ownerIDStr := hex.EncodeToString(ownerKey.Bytes())
|
|
|
|
ownerNameStr := ownerKey.Address()
|
|
|
|
|
|
|
|
require.Equal(t, ownerIDStr, aclRes.Owner.ID)
|
|
|
|
require.Equal(t, ownerNameStr, aclRes.Owner.DisplayName)
|
|
|
|
|
|
|
|
require.Len(t, aclRes.AccessControlList, ln)
|
|
|
|
|
|
|
|
require.Equal(t, ownerIDStr, aclRes.AccessControlList[0].Grantee.ID)
|
|
|
|
require.Equal(t, ownerNameStr, aclRes.AccessControlList[0].Grantee.DisplayName)
|
|
|
|
require.Equal(t, aclFullControl, aclRes.AccessControlList[0].Permission)
|
|
|
|
}
|
|
|
|
|
2022-11-09 14:25:02 +00:00
|
|
|
func TestBucketPolicy(t *testing.T) {
|
|
|
|
hc := prepareHandlerContext(t)
|
|
|
|
bktName := "bucket-for-policy"
|
|
|
|
|
2023-12-08 07:44:13 +00:00
|
|
|
createTestBucket(hc, bktName)
|
2022-11-09 14:25:02 +00:00
|
|
|
|
2023-12-08 07:44:13 +00:00
|
|
|
getBucketPolicy(hc, bktName, s3errors.ErrNoSuchBucketPolicy)
|
2022-11-09 14:25:02 +00:00
|
|
|
|
2023-12-08 07:44:13 +00:00
|
|
|
newPolicy := engineiam.Policy{
|
|
|
|
Statement: []engineiam.Statement{{
|
|
|
|
Principal: map[engineiam.PrincipalType][]string{engineiam.Wildcard: {}},
|
|
|
|
Effect: engineiam.DenyEffect,
|
|
|
|
Action: engineiam.Action{"s3:PutObject"},
|
|
|
|
Resource: engineiam.Resource{"arn:aws:s3:::test/*"},
|
2022-11-09 14:25:02 +00:00
|
|
|
}},
|
|
|
|
}
|
|
|
|
|
2023-12-08 07:44:13 +00:00
|
|
|
putBucketPolicy(hc, bktName, newPolicy, s3errors.ErrMalformedPolicy)
|
2022-11-09 14:25:02 +00:00
|
|
|
|
2023-12-08 07:44:13 +00:00
|
|
|
newPolicy.Statement[0].Resource[0] = arnAwsPrefix + bktName + "/*"
|
|
|
|
putBucketPolicy(hc, bktName, newPolicy)
|
2022-11-09 14:25:02 +00:00
|
|
|
|
2023-12-08 07:44:13 +00:00
|
|
|
bktPolicy := getBucketPolicy(hc, bktName)
|
|
|
|
require.Equal(t, newPolicy, bktPolicy)
|
2022-11-09 14:25:02 +00:00
|
|
|
}
|
|
|
|
|
2023-02-17 12:52:52 +00:00
|
|
|
func TestBucketPolicyUnmarshal(t *testing.T) {
|
|
|
|
for _, tc := range []struct {
|
|
|
|
name string
|
|
|
|
policy string
|
|
|
|
}{
|
|
|
|
{
|
|
|
|
name: "action/resource array",
|
|
|
|
policy: `
|
|
|
|
{
|
|
|
|
"Version": "2012-10-17",
|
|
|
|
"Statement": [{
|
|
|
|
"Principal": {
|
|
|
|
"AWS": "arn:aws:iam::111122223333:role/JohnDoe"
|
|
|
|
},
|
|
|
|
"Effect": "Allow",
|
|
|
|
"Action": [
|
2023-03-24 12:49:23 +00:00
|
|
|
"s3:GetObject",
|
2023-02-17 12:52:52 +00:00
|
|
|
"s3:GetObjectVersion"
|
|
|
|
],
|
|
|
|
"Resource": [
|
|
|
|
"arn:aws:s3:::DOC-EXAMPLE-BUCKET/*",
|
|
|
|
"arn:aws:s3:::DOC-EXAMPLE-BUCKET2/*"
|
|
|
|
]
|
|
|
|
}]
|
|
|
|
}
|
|
|
|
`,
|
|
|
|
},
|
|
|
|
{
|
|
|
|
name: "action/resource string",
|
|
|
|
policy: `
|
|
|
|
{
|
|
|
|
"Version": "2012-10-17",
|
|
|
|
"Statement": [{
|
|
|
|
"Principal": {
|
|
|
|
"AWS": "arn:aws:iam::111122223333:role/JohnDoe"
|
|
|
|
},
|
|
|
|
"Effect": "Deny",
|
|
|
|
"Action": "s3:GetObject",
|
|
|
|
"Resource": "arn:aws:s3:::DOC-EXAMPLE-BUCKET/*"
|
|
|
|
}]
|
|
|
|
}
|
|
|
|
`,
|
|
|
|
},
|
|
|
|
} {
|
|
|
|
t.Run(tc.name, func(t *testing.T) {
|
|
|
|
bktPolicy := &bucketPolicy{}
|
|
|
|
err := json.Unmarshal([]byte(tc.policy), bktPolicy)
|
|
|
|
require.NoError(t, err)
|
|
|
|
})
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
func TestPutBucketPolicy(t *testing.T) {
|
|
|
|
bktPolicy := `
|
|
|
|
{
|
|
|
|
"Version": "2012-10-17",
|
|
|
|
"Statement": [{
|
2023-12-08 07:44:13 +00:00
|
|
|
"Principal": "*",
|
2023-02-17 12:52:52 +00:00
|
|
|
"Effect": "Deny",
|
|
|
|
"Action": "s3:GetObject",
|
|
|
|
"Resource": "arn:aws:s3:::bucket-for-policy/*"
|
|
|
|
}]
|
|
|
|
}
|
|
|
|
`
|
|
|
|
hc := prepareHandlerContext(t)
|
|
|
|
bktName := "bucket-for-policy"
|
|
|
|
|
2023-12-08 07:44:13 +00:00
|
|
|
createTestBucket(hc, bktName)
|
2023-02-17 12:52:52 +00:00
|
|
|
|
|
|
|
w, r := prepareTestPayloadRequest(hc, bktName, "", bytes.NewReader([]byte(bktPolicy)))
|
|
|
|
hc.Handler().PutBucketPolicyHandler(w, r)
|
|
|
|
assertStatus(hc.t, w, http.StatusOK)
|
|
|
|
}
|
|
|
|
|
2023-12-08 07:44:13 +00:00
|
|
|
func getBucketPolicy(hc *handlerContext, bktName string, errCode ...s3errors.ErrorCode) engineiam.Policy {
|
2022-11-09 14:25:02 +00:00
|
|
|
w, r := prepareTestRequest(hc, bktName, "", nil)
|
|
|
|
hc.Handler().GetBucketPolicyHandler(w, r)
|
|
|
|
|
2023-12-08 07:44:13 +00:00
|
|
|
var policy engineiam.Policy
|
|
|
|
if len(errCode) == 0 {
|
|
|
|
assertStatus(hc.t, w, http.StatusOK)
|
|
|
|
err := json.NewDecoder(w.Result().Body).Decode(&policy)
|
|
|
|
require.NoError(hc.t, err)
|
|
|
|
} else {
|
|
|
|
assertS3Error(hc.t, w, s3errors.GetAPIError(errCode[0]))
|
|
|
|
}
|
|
|
|
|
2022-11-09 14:25:02 +00:00
|
|
|
return policy
|
|
|
|
}
|
|
|
|
|
2023-12-08 07:44:13 +00:00
|
|
|
func putBucketPolicy(hc *handlerContext, bktName string, bktPolicy engineiam.Policy, errCode ...s3errors.ErrorCode) {
|
2022-11-09 14:25:02 +00:00
|
|
|
body, err := json.Marshal(bktPolicy)
|
|
|
|
require.NoError(hc.t, err)
|
|
|
|
|
|
|
|
w, r := prepareTestPayloadRequest(hc, bktName, "", bytes.NewReader(body))
|
|
|
|
hc.Handler().PutBucketPolicyHandler(w, r)
|
2023-12-08 07:44:13 +00:00
|
|
|
|
|
|
|
if len(errCode) == 0 {
|
|
|
|
assertStatus(hc.t, w, http.StatusOK)
|
|
|
|
} else {
|
|
|
|
assertS3Error(hc.t, w, s3errors.GetAPIError(errCode[0]))
|
|
|
|
}
|
2022-11-09 14:25:02 +00:00
|
|
|
}
|
|
|
|
|
2022-09-06 07:43:04 +00:00
|
|
|
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())
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2022-11-09 14:25:02 +00:00
|
|
|
func createAccessBox(t *testing.T) (*accessbox.Box, *keys.PrivateKey) {
|
2022-09-06 07:43:04 +00:00
|
|
|
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)
|
2023-06-30 09:03:55 +00:00
|
|
|
err = tok.Sign(key.PrivateKey)
|
|
|
|
require.NoError(t, err)
|
2022-09-06 07:43:04 +00:00
|
|
|
|
|
|
|
tok2 := new(session.Container)
|
|
|
|
tok2.ForVerb(session.VerbContainerPut)
|
2023-06-30 09:03:55 +00:00
|
|
|
err = tok2.Sign(key.PrivateKey)
|
|
|
|
require.NoError(t, err)
|
|
|
|
|
2022-09-06 07:43:04 +00:00
|
|
|
box := &accessbox.Box{
|
|
|
|
Gate: &accessbox.GateData{
|
|
|
|
SessionTokens: []*session.Container{tok, tok2},
|
|
|
|
BearerToken: &bearerToken,
|
|
|
|
},
|
|
|
|
}
|
|
|
|
|
2022-11-09 14:25:02 +00:00
|
|
|
return box, key
|
2022-09-06 07:43:04 +00:00
|
|
|
}
|
|
|
|
|
2024-02-12 14:29:36 +00:00
|
|
|
type createBucketInfo struct {
|
|
|
|
BktInfo *data.BucketInfo
|
|
|
|
Box *accessbox.Box
|
|
|
|
Key *keys.PrivateKey
|
|
|
|
}
|
|
|
|
|
|
|
|
func createBucket(hc *handlerContext, bktName string) *createBucketInfo {
|
|
|
|
box, key := createAccessBox(hc.t)
|
2024-02-12 12:28:55 +00:00
|
|
|
|
2023-06-30 09:03:55 +00:00
|
|
|
w := createBucketBase(hc, bktName, box)
|
2024-02-12 12:28:55 +00:00
|
|
|
assertStatus(hc.t, w, http.StatusOK)
|
2022-09-06 07:43:04 +00:00
|
|
|
|
2023-06-30 09:03:55 +00:00
|
|
|
bktInfo, err := hc.Layer().GetBucketInfo(hc.Context(), bktName)
|
2024-02-12 12:28:55 +00:00
|
|
|
require.NoError(hc.t, err)
|
2024-02-12 14:29:36 +00:00
|
|
|
|
|
|
|
return &createBucketInfo{
|
|
|
|
BktInfo: bktInfo,
|
|
|
|
Box: box,
|
|
|
|
Key: key,
|
|
|
|
}
|
2024-02-12 12:28:55 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
func createBucketOldACL(hc *handlerContext, bktName string, box *accessbox.Box) *data.BucketInfo {
|
|
|
|
w := createBucketBase(hc, bktName, box)
|
|
|
|
assertStatus(hc.t, w, http.StatusOK)
|
|
|
|
|
|
|
|
cnrID, err := hc.tp.ContainerID(bktName)
|
|
|
|
require.NoError(hc.t, err)
|
|
|
|
|
|
|
|
cnr, err := hc.tp.Container(hc.Context(), cnrID)
|
|
|
|
require.NoError(hc.t, err)
|
|
|
|
cnr.SetBasicACL(acl.PublicRWExtended)
|
|
|
|
cnr.SetAttribute(layer.AttributeAPEEnabled, "false")
|
|
|
|
hc.tp.SetContainer(cnrID, cnr)
|
|
|
|
table := eacl.NewTable()
|
|
|
|
table.SetCID(cnrID)
|
|
|
|
|
|
|
|
key, err := hc.h.bearerTokenIssuerKey(hc.Context())
|
|
|
|
require.NoError(hc.t, err)
|
|
|
|
for _, op := range fullOps {
|
|
|
|
table.AddRecord(getAllowRecord(op, key))
|
|
|
|
}
|
|
|
|
|
|
|
|
for _, op := range fullOps {
|
|
|
|
table.AddRecord(getOthersRecord(op, eacl.ActionDeny))
|
|
|
|
}
|
|
|
|
err = hc.tp.SetContainerEACL(hc.Context(), *table, nil)
|
|
|
|
require.NoError(hc.t, err)
|
|
|
|
|
|
|
|
bktInfo, err := hc.Layer().GetBucketInfo(hc.Context(), bktName)
|
|
|
|
require.NoError(hc.t, err)
|
|
|
|
|
|
|
|
settings, err := hc.tree.GetSettingsNode(hc.Context(), bktInfo)
|
|
|
|
require.NoError(hc.t, err)
|
|
|
|
settings.CannedACL = ""
|
|
|
|
err = hc.Layer().PutBucketSettings(hc.Context(), &layer.PutSettingsParams{BktInfo: bktInfo, Settings: settings})
|
|
|
|
require.NoError(hc.t, err)
|
|
|
|
|
|
|
|
bktInfo, err = hc.Layer().GetBucketInfo(hc.Context(), bktName)
|
|
|
|
require.NoError(hc.t, err)
|
|
|
|
|
2022-09-06 07:43:04 +00:00
|
|
|
return bktInfo
|
|
|
|
}
|
|
|
|
|
2023-06-30 09:03:55 +00:00
|
|
|
func createBucketAssertS3Error(hc *handlerContext, bktName string, box *accessbox.Box, code s3errors.ErrorCode) {
|
|
|
|
w := createBucketBase(hc, bktName, box)
|
|
|
|
assertS3Error(hc.t, w, s3errors.GetAPIError(code))
|
|
|
|
}
|
|
|
|
|
|
|
|
func createBucketBase(hc *handlerContext, bktName string, box *accessbox.Box) *httptest.ResponseRecorder {
|
|
|
|
w, r := prepareTestRequest(hc, bktName, "", nil)
|
2023-08-14 15:34:41 +00:00
|
|
|
ctx := middleware.SetBoxData(r.Context(), box)
|
2023-06-30 09:03:55 +00:00
|
|
|
r = r.WithContext(ctx)
|
|
|
|
hc.Handler().CreateBucketHandler(w, r)
|
|
|
|
return w
|
|
|
|
}
|
|
|
|
|
2024-02-12 14:29:36 +00:00
|
|
|
func putBucketACL(hc *handlerContext, bktName string, box *accessbox.Box, header map[string]string) {
|
|
|
|
w := putBucketACLBase(hc, bktName, box, header, nil)
|
|
|
|
assertStatus(hc.t, w, http.StatusOK)
|
|
|
|
}
|
|
|
|
|
|
|
|
func putBucketACLAssertS3Error(hc *handlerContext, bktName string, box *accessbox.Box, header map[string]string, body *AccessControlPolicy, code s3errors.ErrorCode) {
|
|
|
|
w := putBucketACLBase(hc, bktName, box, header, body)
|
|
|
|
assertS3Error(hc.t, w, s3errors.GetAPIError(code))
|
|
|
|
}
|
|
|
|
|
|
|
|
func putBucketACLBase(hc *handlerContext, bktName string, box *accessbox.Box, header map[string]string, body *AccessControlPolicy) *httptest.ResponseRecorder {
|
|
|
|
w, r := prepareTestRequest(hc, bktName, "", body)
|
2022-09-06 07:43:04 +00:00
|
|
|
for key, val := range header {
|
|
|
|
r.Header.Set(key, val)
|
|
|
|
}
|
2023-08-14 15:34:41 +00:00
|
|
|
ctx := middleware.SetBoxData(r.Context(), box)
|
2022-09-06 07:43:04 +00:00
|
|
|
r = r.WithContext(ctx)
|
2024-02-12 14:29:36 +00:00
|
|
|
hc.Handler().PutBucketACLHandler(w, r)
|
|
|
|
return w
|
|
|
|
}
|
|
|
|
|
|
|
|
func getBucketACL(hc *handlerContext, bktName string) *AccessControlPolicy {
|
|
|
|
w := getBucketACLBase(hc, bktName)
|
|
|
|
assertStatus(hc.t, w, http.StatusOK)
|
|
|
|
res := &AccessControlPolicy{}
|
|
|
|
parseTestResponse(hc.t, w, res)
|
|
|
|
return res
|
|
|
|
}
|
|
|
|
|
|
|
|
func getBucketACLBase(hc *handlerContext, bktName string) *httptest.ResponseRecorder {
|
|
|
|
w, r := prepareTestRequest(hc, bktName, "", nil)
|
|
|
|
hc.Handler().GetBucketACLHandler(w, r)
|
|
|
|
return w
|
|
|
|
}
|
|
|
|
|
|
|
|
func putObjectACLAssertS3Error(hc *handlerContext, bktName, objName string, box *accessbox.Box, header map[string]string, body *AccessControlPolicy, code s3errors.ErrorCode) {
|
|
|
|
w := putObjectACLBase(hc, bktName, objName, box, header, body)
|
|
|
|
assertS3Error(hc.t, w, s3errors.GetAPIError(code))
|
|
|
|
}
|
|
|
|
|
|
|
|
func putObjectACLBase(hc *handlerContext, bktName, objName string, box *accessbox.Box, header map[string]string, body *AccessControlPolicy) *httptest.ResponseRecorder {
|
|
|
|
w, r := prepareTestRequest(hc, bktName, objName, body)
|
|
|
|
for key, val := range header {
|
|
|
|
r.Header.Set(key, val)
|
|
|
|
}
|
|
|
|
ctx := middleware.SetBoxData(r.Context(), box)
|
|
|
|
r = r.WithContext(ctx)
|
|
|
|
hc.Handler().PutObjectACLHandler(w, r)
|
|
|
|
return w
|
|
|
|
}
|
|
|
|
|
|
|
|
func getObjectACLAssertS3Error(hc *handlerContext, bktName, objName string, code s3errors.ErrorCode) {
|
|
|
|
w := getObjectACLBase(hc, bktName, objName)
|
|
|
|
assertS3Error(hc.t, w, s3errors.GetAPIError(code))
|
|
|
|
}
|
|
|
|
|
|
|
|
func getObjectACLBase(hc *handlerContext, bktName, objName string) *httptest.ResponseRecorder {
|
|
|
|
w, r := prepareTestRequest(hc, bktName, objName, nil)
|
|
|
|
hc.Handler().GetObjectACLHandler(w, r)
|
|
|
|
return w
|
2022-09-06 07:43:04 +00:00
|
|
|
}
|