forked from TrueCloudLab/frostfs-s3-gw
[#680] Add new converter
Signed-off-by: Denis Kirillov <d.kirillov@yadro.com>
This commit is contained in:
parent
0ba6989197
commit
4d305d0d0f
5 changed files with 542 additions and 114 deletions
2
go.mod
2
go.mod
|
@ -9,7 +9,7 @@ require (
|
|||
git.frostfs.info/TrueCloudLab/frostfs-qos v0.0.0-20250227072915-25102d1e1aa3
|
||||
git.frostfs.info/TrueCloudLab/frostfs-sdk-go v0.0.0-20250317082814-87bb55f992dc
|
||||
git.frostfs.info/TrueCloudLab/multinet v0.0.0-20241015075604-6cb0d80e0972
|
||||
git.frostfs.info/TrueCloudLab/policy-engine v0.0.0-20250317084311-594ac20859fc
|
||||
git.frostfs.info/TrueCloudLab/policy-engine v0.0.0-20250402100642-acd94d200f88
|
||||
git.frostfs.info/TrueCloudLab/zapjournald v0.0.0-20240124114243-cb2e66427d02
|
||||
github.com/aws/aws-sdk-go-v2 v1.34.0
|
||||
github.com/aws/aws-sdk-go-v2/config v1.27.32
|
||||
|
|
4
go.sum
4
go.sum
|
@ -52,8 +52,8 @@ git.frostfs.info/TrueCloudLab/hrw v1.2.1 h1:ccBRK21rFvY5R1WotI6LNoPlizk7qSvdfD8l
|
|||
git.frostfs.info/TrueCloudLab/hrw v1.2.1/go.mod h1:C1Ygde2n843yTZEQ0FP69jYiuaYV0kriLvP4zm8JuvM=
|
||||
git.frostfs.info/TrueCloudLab/multinet v0.0.0-20241015075604-6cb0d80e0972 h1:/960fWeyn2AFHwQUwDsWB3sbP6lTEnFnMzLMM6tx6N8=
|
||||
git.frostfs.info/TrueCloudLab/multinet v0.0.0-20241015075604-6cb0d80e0972/go.mod h1:2hM42MBrlhvN6XToaW6OWNk5ZLcu1FhaukGgxtfpDDI=
|
||||
git.frostfs.info/TrueCloudLab/policy-engine v0.0.0-20250317084311-594ac20859fc h1:o56+epHNuC7o0NwLJylvT+jf/icpgTZCTQPr7ydanek=
|
||||
git.frostfs.info/TrueCloudLab/policy-engine v0.0.0-20250317084311-594ac20859fc/go.mod h1:GZTk55RI4dKzsK6BCn5h2xxE28UHNfgoq/NJxW/LQ6A=
|
||||
git.frostfs.info/TrueCloudLab/policy-engine v0.0.0-20250402100642-acd94d200f88 h1:V0a7ia84ZpSM2YxpJq1SKLQfeYmsqFWqcxwweBHJIzc=
|
||||
git.frostfs.info/TrueCloudLab/policy-engine v0.0.0-20250402100642-acd94d200f88/go.mod h1:GZTk55RI4dKzsK6BCn5h2xxE28UHNfgoq/NJxW/LQ6A=
|
||||
git.frostfs.info/TrueCloudLab/rfc6979 v0.4.0 h1:M2KR3iBj7WpY3hP10IevfIB9MURr4O9mwVfJ+SjT3HA=
|
||||
git.frostfs.info/TrueCloudLab/rfc6979 v0.4.0/go.mod h1:okpbKfVYf/BpejtfFTfhZqFP+sZ8rsHrP8Rr/jYPNRc=
|
||||
git.frostfs.info/TrueCloudLab/tzhash v1.8.0 h1:UFMnUIk0Zh17m8rjGHJMqku2hCgaXDqjqZzS4gsb4UA=
|
||||
|
|
235
pkg/policy-engine/converter_test.go
Normal file
235
pkg/policy-engine/converter_test.go
Normal file
|
@ -0,0 +1,235 @@
|
|||
package policyengine
|
||||
|
||||
import (
|
||||
"encoding/json"
|
||||
"errors"
|
||||
"fmt"
|
||||
"testing"
|
||||
|
||||
s3common "git.frostfs.info/TrueCloudLab/frostfs-s3-gw/pkg/policy-engine/common"
|
||||
"git.frostfs.info/TrueCloudLab/policy-engine/pkg/chain"
|
||||
"git.frostfs.info/TrueCloudLab/policy-engine/pkg/engine"
|
||||
"git.frostfs.info/TrueCloudLab/policy-engine/pkg/engine/inmemory"
|
||||
"git.frostfs.info/TrueCloudLab/policy-engine/pkg/resource/testutil"
|
||||
"git.frostfs.info/TrueCloudLab/policy-engine/schema/native"
|
||||
"github.com/stretchr/testify/require"
|
||||
)
|
||||
|
||||
type apeConverterMock struct {
|
||||
version ConverterVersion
|
||||
}
|
||||
|
||||
func (a apeConverterMock) ConverterVersion() ConverterVersion {
|
||||
return a.version
|
||||
}
|
||||
|
||||
type resolverMock struct {
|
||||
users map[string]string
|
||||
containers map[string]string
|
||||
namespace string
|
||||
}
|
||||
|
||||
func newMockUserResolver(accountUsers []string, buckets []string, namespace string) *resolverMock {
|
||||
userMap := make(map[string]string, len(accountUsers))
|
||||
for _, user := range accountUsers {
|
||||
userMap[user] = user + "/resolvedValue"
|
||||
}
|
||||
|
||||
containerMap := make(map[string]string, len(buckets))
|
||||
for _, bkt := range buckets {
|
||||
containerMap[bkt] = bkt + "/resolvedValues"
|
||||
}
|
||||
|
||||
return &resolverMock{users: userMap, containers: containerMap, namespace: namespace}
|
||||
}
|
||||
|
||||
func (m *resolverMock) GetUserAddress(account, user string) (string, error) {
|
||||
key, ok := m.users[account+"/"+user]
|
||||
if !ok {
|
||||
return "", errors.New("not found")
|
||||
}
|
||||
|
||||
return key, nil
|
||||
}
|
||||
|
||||
func (m *resolverMock) GetUserKey(account, user string) (string, error) {
|
||||
key, ok := m.users[account+"/"+user]
|
||||
if !ok {
|
||||
return "", errors.New("not found")
|
||||
}
|
||||
|
||||
return key, nil
|
||||
}
|
||||
|
||||
func (m *resolverMock) GetBucketInfo(bkt string) (*s3common.BucketInfo, error) {
|
||||
cnr, ok := m.containers[bkt]
|
||||
if !ok {
|
||||
return nil, errors.New("not found")
|
||||
}
|
||||
|
||||
return &s3common.BucketInfo{Container: cnr, Namespace: m.namespace}, nil
|
||||
}
|
||||
|
||||
func TestChainsIsAllowed(t *testing.T) {
|
||||
t.Run("ListBucket Allow", func(t *testing.T) {
|
||||
policy := `
|
||||
{
|
||||
"Version": "2012-10-17",
|
||||
"Statement": [
|
||||
{
|
||||
"Effect": "Allow",
|
||||
"Principal": "*",
|
||||
"Action": "s3:ListBucket",
|
||||
"Resource": "arn:aws:s3:::bkt"
|
||||
}
|
||||
]
|
||||
}
|
||||
`
|
||||
var p s3common.Policy
|
||||
err := json.Unmarshal([]byte(policy), &p)
|
||||
require.NoError(t, err)
|
||||
|
||||
resolver := newMockUserResolver(nil, []string{"bkt"}, "")
|
||||
|
||||
for _, tc := range []struct {
|
||||
version ConverterVersion
|
||||
status chain.Status
|
||||
}{
|
||||
{
|
||||
version: V1,
|
||||
status: chain.NoRuleFound,
|
||||
},
|
||||
{
|
||||
version: V2,
|
||||
status: chain.Allow,
|
||||
},
|
||||
} {
|
||||
t.Run(string(tc.version), func(t *testing.T) {
|
||||
converter := NewConverter(Config{VersionFetcher: apeConverterMock{version: tc.version}})
|
||||
nativeChain, err := converter.ToNativeChain(p, resolver)
|
||||
require.NoError(t, err)
|
||||
|
||||
s := inmemory.NewInMemory()
|
||||
_, _, err = s.MorphRuleChainStorage().AddMorphRuleChain(chain.Ingress, engine.NamespaceTarget(""), nativeChain)
|
||||
require.NoError(t, err)
|
||||
|
||||
res := testutil.NewResource(fmt.Sprintf(native.ResourceFormatRootContainerObjects, resolver.containers["bkt"]), map[string]string{native.ProperyKeyTreeID: "system"})
|
||||
req := testutil.NewRequest(native.MethodGetObject, res, nil)
|
||||
status, _, err := s.IsAllowed(chain.Ingress, engine.NewRequestTargetWithNamespace(""), req)
|
||||
require.NoError(t, err)
|
||||
require.Equal(t, tc.status.String(), status.String())
|
||||
})
|
||||
}
|
||||
})
|
||||
|
||||
t.Run("PutObject Deny/Allow", func(t *testing.T) {
|
||||
user1, user2 := "user1", "user2"
|
||||
principal1, principal2 := "/"+user1, "/"+user2
|
||||
|
||||
policy := fmt.Sprintf(`
|
||||
{
|
||||
"Version": "2012-10-17",
|
||||
"Statement": [
|
||||
{
|
||||
"Effect": "Deny",
|
||||
"Principal": {"AWS": ["arn:aws:iam:::user/%s"]},
|
||||
"Action": "s3:PutObject",
|
||||
"Resource": "arn:aws:s3:::bkt/*"
|
||||
},
|
||||
{
|
||||
"Effect": "Allow",
|
||||
"Principal": {"AWS": ["arn:aws:iam:::user/%s"]},
|
||||
"Action": "s3:PutObject",
|
||||
"Resource": "arn:aws:s3:::bkt/*"
|
||||
}
|
||||
]
|
||||
}
|
||||
`, user1, user2)
|
||||
|
||||
var p s3common.Policy
|
||||
err := json.Unmarshal([]byte(policy), &p)
|
||||
require.NoError(t, err)
|
||||
|
||||
resolver := newMockUserResolver([]string{principal1, principal2}, []string{"bkt"}, "")
|
||||
|
||||
for _, tc := range []struct {
|
||||
version ConverterVersion
|
||||
status chain.Status
|
||||
}{
|
||||
{
|
||||
version: V1,
|
||||
status: chain.Allow, // because we skip deny rules for native chains
|
||||
},
|
||||
{
|
||||
version: V2,
|
||||
status: chain.Allow,
|
||||
},
|
||||
} {
|
||||
t.Run(string(tc.version), func(t *testing.T) {
|
||||
converter := NewConverter(Config{VersionFetcher: apeConverterMock{version: tc.version}})
|
||||
nativeChain, err := converter.ToNativeChain(p, resolver)
|
||||
require.NoError(t, err)
|
||||
|
||||
s := inmemory.NewInMemory()
|
||||
_, _, err = s.MorphRuleChainStorage().AddMorphRuleChain(chain.Ingress, engine.NamespaceTarget(""), nativeChain)
|
||||
require.NoError(t, err)
|
||||
|
||||
res := testutil.NewResource(fmt.Sprintf(native.ResourceFormatRootContainerObjects, resolver.containers["bkt"]), map[string]string{native.ProperyKeyTreeID: "system"})
|
||||
req := testutil.NewRequest(native.MethodPutObject, res, map[string]string{native.PropertyKeyActorPublicKey: resolver.users[principal2]})
|
||||
status, _, err := s.IsAllowed(chain.Ingress, engine.NewRequestTargetWithNamespace(""), req)
|
||||
require.NoError(t, err)
|
||||
require.Equal(t, tc.status.String(), status.String())
|
||||
})
|
||||
}
|
||||
})
|
||||
|
||||
t.Run("GetObject Allow", func(t *testing.T) {
|
||||
policy := `
|
||||
{
|
||||
"Version": "2012-10-17",
|
||||
"Statement": [{
|
||||
"Effect": "Allow",
|
||||
"Principal": "*",
|
||||
"Action": "s3:GetObject",
|
||||
"Resource": "arn:aws:s3:::bkt/object"
|
||||
}]
|
||||
}
|
||||
`
|
||||
|
||||
var p s3common.Policy
|
||||
err := json.Unmarshal([]byte(policy), &p)
|
||||
require.NoError(t, err)
|
||||
|
||||
resolver := newMockUserResolver(nil, []string{"bkt"}, "")
|
||||
|
||||
for _, tc := range []struct {
|
||||
version ConverterVersion
|
||||
status chain.Status
|
||||
}{
|
||||
{
|
||||
version: V1,
|
||||
status: chain.NoRuleFound,
|
||||
},
|
||||
{
|
||||
version: V2,
|
||||
status: chain.Allow,
|
||||
},
|
||||
} {
|
||||
t.Run(string(tc.version), func(t *testing.T) {
|
||||
converter := NewConverter(Config{VersionFetcher: apeConverterMock{version: tc.version}})
|
||||
nativeChain, err := converter.ToNativeChain(p, resolver)
|
||||
require.NoError(t, err)
|
||||
|
||||
s := inmemory.NewInMemory()
|
||||
_, _, err = s.MorphRuleChainStorage().AddMorphRuleChain(chain.Ingress, engine.NamespaceTarget(""), nativeChain)
|
||||
require.NoError(t, err)
|
||||
|
||||
res := testutil.NewResource(fmt.Sprintf(native.ResourceFormatRootContainerObjects, resolver.containers["bkt"]), map[string]string{native.ProperyKeyTreeID: "version"})
|
||||
req := testutil.NewRequest(native.MethodGetObject, res, nil)
|
||||
status, _, err := s.IsAllowed(chain.Ingress, engine.NewRequestTargetWithNamespace(""), req)
|
||||
require.NoError(t, err)
|
||||
require.Equal(t, tc.status.String(), status.String())
|
||||
})
|
||||
}
|
||||
})
|
||||
}
|
|
@ -12,58 +12,63 @@ import (
|
|||
|
||||
const PropertyKeyFilePath = "FilePath"
|
||||
|
||||
var actionToNativeOpMap = map[string][]string{
|
||||
s3common.S3ActionAbortMultipartUpload: {native.MethodGetContainer, native.MethodDeleteObject, native.MethodHeadObject, native.MethodGetObject, native.MethodPutObject},
|
||||
s3common.S3ActionCreateBucket: {native.MethodGetContainer, native.MethodPutContainer, native.MethodSetContainerEACL, native.MethodGetObject, native.MethodPutObject},
|
||||
s3common.S3ActionDeleteBucket: {native.MethodGetContainer, native.MethodDeleteContainer, native.MethodSearchObject, native.MethodHeadObject, native.MethodGetObject},
|
||||
s3common.S3ActionDeleteBucketPolicy: {native.MethodGetContainer},
|
||||
s3common.S3ActionDeleteObject: {native.MethodGetContainer, native.MethodDeleteObject, native.MethodPutObject, native.MethodHeadObject, native.MethodGetObject, native.MethodRangeObject},
|
||||
s3common.S3ActionDeleteObjectTagging: {native.MethodGetContainer, native.MethodHeadObject, native.MethodGetObject, native.MethodPutObject},
|
||||
s3common.S3ActionDeleteObjectVersion: {native.MethodGetContainer, native.MethodDeleteObject, native.MethodPutObject, native.MethodHeadObject, native.MethodGetObject, native.MethodRangeObject},
|
||||
s3common.S3ActionDeleteObjectVersionTagging: {native.MethodGetContainer, native.MethodHeadObject, native.MethodGetObject, native.MethodPutObject},
|
||||
s3common.S3ActionGetBucketACL: {native.MethodGetContainer, native.MethodGetContainerEACL, native.MethodGetObject},
|
||||
s3common.S3ActionGetBucketCORS: {native.MethodGetContainer, native.MethodGetObject, native.MethodHeadObject},
|
||||
s3common.S3ActionGetBucketLocation: {native.MethodGetContainer},
|
||||
s3common.S3ActionGetBucketNotification: {native.MethodGetContainer, native.MethodGetObject, native.MethodHeadObject},
|
||||
s3common.S3ActionGetBucketObjectLockConfiguration: {native.MethodGetContainer, native.MethodGetObject},
|
||||
s3common.S3ActionGetBucketPolicy: {native.MethodGetContainer},
|
||||
s3common.S3ActionGetBucketPolicyStatus: {native.MethodGetContainer},
|
||||
s3common.S3ActionGetBucketTagging: {native.MethodGetContainer, native.MethodGetObject},
|
||||
s3common.S3ActionGetBucketVersioning: {native.MethodGetContainer, native.MethodGetObject},
|
||||
s3common.S3ActionGetLifecycleConfiguration: {native.MethodGetContainer, native.MethodGetObject, native.MethodHeadObject},
|
||||
s3common.S3ActionGetObject: {native.MethodGetContainer, native.MethodGetObject, native.MethodHeadObject, native.MethodSearchObject, native.MethodRangeObject, native.MethodHashObject},
|
||||
s3common.S3ActionGetObjectACL: {native.MethodGetContainer, native.MethodGetContainerEACL, native.MethodGetObject, native.MethodHeadObject},
|
||||
s3common.S3ActionGetObjectAttributes: {native.MethodGetContainer, native.MethodGetObject, native.MethodHeadObject},
|
||||
s3common.S3ActionGetObjectLegalHold: {native.MethodGetContainer, native.MethodHeadObject, native.MethodGetObject},
|
||||
s3common.S3ActionGetObjectRetention: {native.MethodGetContainer, native.MethodHeadObject, native.MethodGetObject},
|
||||
s3common.S3ActionGetObjectTagging: {native.MethodGetContainer, native.MethodHeadObject, native.MethodGetObject},
|
||||
s3common.S3ActionGetObjectVersion: {native.MethodGetContainer, native.MethodGetObject, native.MethodHeadObject, native.MethodSearchObject, native.MethodRangeObject, native.MethodHashObject},
|
||||
s3common.S3ActionGetObjectVersionACL: {native.MethodGetContainer, native.MethodGetContainerEACL, native.MethodGetObject, native.MethodHeadObject},
|
||||
s3common.S3ActionGetObjectVersionAttributes: {native.MethodGetContainer, native.MethodGetObject, native.MethodHeadObject},
|
||||
s3common.S3ActionGetObjectVersionTagging: {native.MethodGetContainer, native.MethodHeadObject, native.MethodGetObject},
|
||||
s3common.S3ActionListAllMyBuckets: {native.MethodListContainers, native.MethodGetContainer},
|
||||
s3common.S3ActionListBucket: {native.MethodGetContainer, native.MethodGetObject, native.MethodHeadObject, native.MethodSearchObject, native.MethodRangeObject, native.MethodHashObject},
|
||||
s3common.S3ActionListBucketMultipartUploads: {native.MethodGetContainer, native.MethodGetObject},
|
||||
s3common.S3ActionListBucketVersions: {native.MethodGetContainer, native.MethodGetObject, native.MethodHeadObject, native.MethodSearchObject, native.MethodRangeObject, native.MethodHashObject},
|
||||
s3common.S3ActionListMultipartUploadParts: {native.MethodGetContainer, native.MethodGetObject},
|
||||
s3common.S3ActionPutBucketACL: {native.MethodGetContainer, native.MethodSetContainerEACL, native.MethodGetObject, native.MethodPutObject},
|
||||
s3common.S3ActionPutBucketCORS: {native.MethodGetContainer, native.MethodGetObject, native.MethodPutObject},
|
||||
s3common.S3ActionPutBucketNotification: {native.MethodGetContainer, native.MethodHeadObject, native.MethodDeleteObject, native.MethodGetObject, native.MethodPutObject},
|
||||
s3common.S3ActionPutBucketObjectLockConfiguration: {native.MethodGetContainer, native.MethodGetObject, native.MethodPutObject},
|
||||
s3common.S3ActionPutBucketPolicy: {native.MethodGetContainer},
|
||||
s3common.S3ActionPutBucketTagging: {native.MethodGetContainer, native.MethodGetObject, native.MethodPutObject},
|
||||
s3common.S3ActionPutBucketVersioning: {native.MethodGetContainer, native.MethodGetObject, native.MethodPutObject},
|
||||
s3common.S3ActionPutLifecycleConfiguration: {native.MethodGetContainer, native.MethodGetObject, native.MethodHeadObject, native.MethodPutObject, native.MethodDeleteObject},
|
||||
s3common.S3ActionPutObject: {native.MethodGetContainer, native.MethodPutObject, native.MethodGetObject, native.MethodHeadObject, native.MethodRangeObject},
|
||||
s3common.S3ActionPutObjectACL: {native.MethodGetContainer, native.MethodGetContainerEACL, native.MethodSetContainerEACL, native.MethodGetObject, native.MethodHeadObject},
|
||||
s3common.S3ActionPutObjectLegalHold: {native.MethodGetContainer, native.MethodHeadObject, native.MethodGetObject, native.MethodPutObject},
|
||||
s3common.S3ActionPutObjectRetention: {native.MethodGetContainer, native.MethodHeadObject, native.MethodGetObject, native.MethodPutObject},
|
||||
s3common.S3ActionPutObjectTagging: {native.MethodGetContainer, native.MethodHeadObject, native.MethodGetObject, native.MethodPutObject},
|
||||
s3common.S3ActionPutObjectVersionACL: {native.MethodGetContainer, native.MethodGetContainerEACL, native.MethodSetContainerEACL, native.MethodGetObject, native.MethodHeadObject},
|
||||
s3common.S3ActionPutObjectVersionTagging: {native.MethodGetContainer, native.MethodHeadObject, native.MethodGetObject, native.MethodPutObject},
|
||||
s3common.S3ActionPatchObject: {native.MethodGetContainer, native.MethodGetObject, native.MethodHeadObject, native.MethodPatchObject, native.MethodPutObject, native.MethodRangeObject},
|
||||
s3common.S3ActionPutBucketPublicAccessBlock: {native.MethodGetContainer, native.MethodPutObject, native.MethodDeleteObject, native.MethodGetObject},
|
||||
s3common.S3ActionGetBucketPublicAccessBlock: {native.MethodGetContainer, native.MethodGetObject},
|
||||
type nativeOperationInfo struct {
|
||||
operations []string
|
||||
needTreeWrite bool
|
||||
}
|
||||
|
||||
var actionToNativeOpMap = map[string]nativeOperationInfo{
|
||||
s3common.S3ActionAbortMultipartUpload: {operations: []string{native.MethodGetContainer, native.MethodDeleteObject, native.MethodHeadObject, native.MethodGetObject, native.MethodPutObject}, needTreeWrite: true},
|
||||
s3common.S3ActionCreateBucket: {operations: []string{native.MethodGetContainer, native.MethodPutContainer, native.MethodSetContainerEACL}, needTreeWrite: true},
|
||||
s3common.S3ActionDeleteBucket: {operations: []string{native.MethodGetContainer, native.MethodDeleteContainer, native.MethodSearchObject, native.MethodHeadObject, native.MethodGetObject, native.MethodPutObject}, needTreeWrite: true},
|
||||
s3common.S3ActionDeleteBucketPolicy: {operations: []string{native.MethodGetContainer}},
|
||||
s3common.S3ActionDeleteObject: {operations: []string{native.MethodGetContainer, native.MethodDeleteObject, native.MethodPutObject, native.MethodHeadObject, native.MethodGetObject, native.MethodRangeObject}, needTreeWrite: true},
|
||||
s3common.S3ActionDeleteObjectTagging: {operations: []string{native.MethodGetContainer}, needTreeWrite: true},
|
||||
s3common.S3ActionDeleteObjectVersion: {operations: []string{native.MethodGetContainer, native.MethodDeleteObject, native.MethodPutObject, native.MethodHeadObject, native.MethodGetObject, native.MethodRangeObject}, needTreeWrite: true},
|
||||
s3common.S3ActionDeleteObjectVersionTagging: {operations: []string{native.MethodGetContainer}, needTreeWrite: true},
|
||||
s3common.S3ActionGetBucketACL: {operations: []string{native.MethodGetContainer}},
|
||||
s3common.S3ActionGetBucketCORS: {operations: []string{native.MethodGetContainer, native.MethodGetObject, native.MethodHeadObject}},
|
||||
s3common.S3ActionGetBucketLocation: {operations: []string{native.MethodGetContainer}},
|
||||
s3common.S3ActionGetBucketNotification: {operations: []string{native.MethodGetContainer, native.MethodGetObject, native.MethodHeadObject}}, // not supported
|
||||
s3common.S3ActionGetBucketObjectLockConfiguration: {operations: []string{native.MethodGetContainer}},
|
||||
s3common.S3ActionGetBucketPolicy: {operations: []string{native.MethodGetContainer}},
|
||||
s3common.S3ActionGetBucketPolicyStatus: {operations: []string{native.MethodGetContainer}},
|
||||
s3common.S3ActionGetBucketTagging: {operations: []string{native.MethodGetContainer}},
|
||||
s3common.S3ActionGetBucketVersioning: {operations: []string{native.MethodGetContainer}},
|
||||
s3common.S3ActionGetLifecycleConfiguration: {operations: []string{native.MethodGetContainer, native.MethodGetObject, native.MethodHeadObject}},
|
||||
s3common.S3ActionGetObject: {operations: []string{native.MethodGetContainer, native.MethodGetObject, native.MethodHeadObject, native.MethodSearchObject, native.MethodRangeObject, native.MethodHashObject}},
|
||||
s3common.S3ActionGetObjectACL: {operations: []string{native.MethodGetContainer}},
|
||||
s3common.S3ActionGetObjectAttributes: {operations: []string{native.MethodGetContainer, native.MethodHeadObject}},
|
||||
s3common.S3ActionGetObjectLegalHold: {operations: []string{native.MethodGetContainer}},
|
||||
s3common.S3ActionGetObjectRetention: {operations: []string{native.MethodGetContainer}},
|
||||
s3common.S3ActionGetObjectTagging: {operations: []string{native.MethodGetContainer}},
|
||||
s3common.S3ActionGetObjectVersion: {operations: []string{native.MethodGetContainer, native.MethodGetObject, native.MethodHeadObject, native.MethodSearchObject, native.MethodRangeObject, native.MethodHashObject}},
|
||||
s3common.S3ActionGetObjectVersionACL: {operations: []string{native.MethodGetContainer}},
|
||||
s3common.S3ActionGetObjectVersionAttributes: {operations: []string{native.MethodGetContainer, native.MethodHeadObject}},
|
||||
s3common.S3ActionGetObjectVersionTagging: {operations: []string{native.MethodGetContainer}},
|
||||
s3common.S3ActionListAllMyBuckets: {operations: []string{native.MethodListContainers, native.MethodGetContainer}},
|
||||
s3common.S3ActionListBucket: {operations: []string{native.MethodGetContainer, native.MethodGetObject, native.MethodHeadObject, native.MethodSearchObject, native.MethodRangeObject, native.MethodHashObject}},
|
||||
s3common.S3ActionListBucketMultipartUploads: {operations: []string{native.MethodGetContainer}},
|
||||
s3common.S3ActionListBucketVersions: {operations: []string{native.MethodGetContainer, native.MethodGetObject, native.MethodHeadObject, native.MethodSearchObject, native.MethodRangeObject, native.MethodHashObject}},
|
||||
s3common.S3ActionListMultipartUploadParts: {operations: []string{native.MethodGetContainer}},
|
||||
s3common.S3ActionPutBucketACL: {operations: []string{native.MethodGetContainer}, needTreeWrite: true},
|
||||
s3common.S3ActionPutBucketCORS: {operations: []string{native.MethodGetContainer, native.MethodGetObject, native.MethodPutObject}, needTreeWrite: true},
|
||||
s3common.S3ActionPutBucketNotification: {operations: []string{native.MethodGetContainer, native.MethodHeadObject, native.MethodDeleteObject, native.MethodGetObject, native.MethodPutObject}, needTreeWrite: true}, // not supported
|
||||
s3common.S3ActionPutBucketObjectLockConfiguration: {operations: []string{native.MethodGetContainer}, needTreeWrite: true},
|
||||
s3common.S3ActionPutBucketPolicy: {operations: []string{native.MethodGetContainer}},
|
||||
s3common.S3ActionPutBucketTagging: {operations: []string{native.MethodGetContainer}, needTreeWrite: true},
|
||||
s3common.S3ActionPutBucketVersioning: {operations: []string{native.MethodGetContainer}, needTreeWrite: true},
|
||||
s3common.S3ActionPutLifecycleConfiguration: {operations: []string{native.MethodGetContainer, native.MethodGetObject, native.MethodHeadObject, native.MethodPutObject, native.MethodDeleteObject}, needTreeWrite: true},
|
||||
s3common.S3ActionPutObject: {operations: []string{native.MethodGetContainer, native.MethodPutObject, native.MethodGetObject, native.MethodHeadObject, native.MethodRangeObject}, needTreeWrite: true},
|
||||
s3common.S3ActionPutObjectACL: {operations: []string{native.MethodGetContainer}}, // not supported
|
||||
s3common.S3ActionPutObjectLegalHold: {operations: []string{native.MethodGetContainer, native.MethodHeadObject, native.MethodGetObject, native.MethodPutObject}, needTreeWrite: true},
|
||||
s3common.S3ActionPutObjectRetention: {operations: []string{native.MethodGetContainer, native.MethodHeadObject, native.MethodGetObject, native.MethodPutObject}, needTreeWrite: true},
|
||||
s3common.S3ActionPutObjectTagging: {operations: []string{native.MethodGetContainer}, needTreeWrite: true},
|
||||
s3common.S3ActionPutObjectVersionACL: {operations: []string{native.MethodGetContainer}}, // not supported
|
||||
s3common.S3ActionPutObjectVersionTagging: {operations: []string{native.MethodGetContainer}, needTreeWrite: true},
|
||||
s3common.S3ActionPatchObject: {operations: []string{native.MethodGetContainer, native.MethodGetObject, native.MethodHeadObject, native.MethodPatchObject, native.MethodPutObject, native.MethodRangeObject}, needTreeWrite: true},
|
||||
s3common.S3ActionPutBucketPublicAccessBlock: {operations: []string{native.MethodGetContainer}, needTreeWrite: true},
|
||||
s3common.S3ActionGetBucketPublicAccessBlock: {operations: []string{native.MethodGetContainer}},
|
||||
}
|
||||
|
||||
var containerNativeOperations = map[string]struct{}{
|
||||
|
@ -99,11 +104,12 @@ func ConvertToNativeChain(p s3common.Policy, resolver s3common.NativeResolver) (
|
|||
if status != chain.Allow {
|
||||
// Most s3 methods share the same native operations. Deny rules must not affect shared native operations,
|
||||
// therefore this code skips all deny rules for native protocol. Deny is applied for s3 protocol only, in this case.
|
||||
// Consider adding new condition for object operations (so that $Tree:ID be empty) if we will stop skipping deny rules.
|
||||
continue
|
||||
}
|
||||
|
||||
action, actionInverted := statement.GetAction()
|
||||
nativeActions, err := formNativeActionNames(action)
|
||||
nativeActions, treeWrite, err := formNativeActionNames(action)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
@ -113,7 +119,7 @@ func ConvertToNativeChain(p s3common.Policy, resolver s3common.NativeResolver) (
|
|||
}
|
||||
|
||||
resource, resourceInverted := statement.GetResource()
|
||||
groupedResources, err := formNativeResourceNamesAndConditions(resource, resolver, getActionTypes(nativeActions))
|
||||
groupedResources, treeRes, err := formNativeResourceNamesAndConditions(resource, resolver, getActionTypes(nativeActions))
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
@ -132,6 +138,8 @@ func ConvertToNativeChain(p s3common.Policy, resolver s3common.NativeResolver) (
|
|||
return nil, err
|
||||
}
|
||||
|
||||
engineChain.Rules = append(engineChain.Rules, getTreeRule(treeRes, principals, principalCondFn, treeWrite)...)
|
||||
|
||||
for _, groupedResource := range groupedResources {
|
||||
for _, principal := range principals {
|
||||
for _, conditions := range splitConditions {
|
||||
|
@ -164,6 +172,50 @@ func ConvertToNativeChain(p s3common.Policy, resolver s3common.NativeResolver) (
|
|||
return &engineChain, nil
|
||||
}
|
||||
|
||||
func getTreeRule(resources []string, principals []string, principalCondFn s3common.FormPrincipalConditionFunc, needWrite bool) []chain.Rule {
|
||||
ops := []string{native.MethodGetObject}
|
||||
if needWrite {
|
||||
ops = append(ops, native.MethodPutObject)
|
||||
}
|
||||
|
||||
treeCondition := chain.Condition{
|
||||
Op: chain.CondStringNotEquals,
|
||||
Kind: chain.KindResource,
|
||||
Key: native.ProperyKeyTreeID,
|
||||
Value: "",
|
||||
}
|
||||
|
||||
var principalTreeConditions []chain.Condition
|
||||
for _, principal := range principals {
|
||||
if principal == s3common.Wildcard {
|
||||
principalTreeConditions = principalTreeConditions[:0]
|
||||
break
|
||||
}
|
||||
principalTreeConditions = append(principalTreeConditions, principalCondFn(principal))
|
||||
}
|
||||
|
||||
if len(principalTreeConditions) == 0 {
|
||||
return []chain.Rule{{
|
||||
Status: chain.Allow,
|
||||
Actions: chain.Actions{Names: ops},
|
||||
Resources: chain.Resources{Names: resources},
|
||||
Condition: []chain.Condition{treeCondition},
|
||||
}}
|
||||
}
|
||||
|
||||
res := make([]chain.Rule, len(principalTreeConditions))
|
||||
for i, condition := range principalTreeConditions {
|
||||
res[i] = chain.Rule{
|
||||
Status: chain.Allow,
|
||||
Actions: chain.Actions{Names: ops},
|
||||
Resources: chain.Resources{Names: resources},
|
||||
Condition: []chain.Condition{treeCondition, condition},
|
||||
}
|
||||
}
|
||||
|
||||
return res
|
||||
}
|
||||
|
||||
func getActionTypes(nativeActions []string) ActionTypes {
|
||||
var res ActionTypes
|
||||
for _, action := range nativeActions {
|
||||
|
@ -258,23 +310,24 @@ type ActionTypes struct {
|
|||
Container bool
|
||||
}
|
||||
|
||||
func formNativeResourceNamesAndConditions(names []string, resolver s3common.NativeResolver, actionTypes ActionTypes) ([]GroupedResources, error) {
|
||||
func formNativeResourceNamesAndConditions(names []string, resolver s3common.NativeResolver, actionTypes ActionTypes) ([]GroupedResources, []string, error) {
|
||||
if !actionTypes.Object && !actionTypes.Container {
|
||||
return nil, s3common.ErrActionsNotApplicable
|
||||
return nil, nil, s3common.ErrActionsNotApplicable
|
||||
}
|
||||
|
||||
res := make([]GroupedResources, 0, len(names))
|
||||
treeResMap := make(map[string]struct{}, len(names))
|
||||
|
||||
combined := make(map[string]struct{})
|
||||
|
||||
for _, resource := range names {
|
||||
if err := s3common.ValidateResource(resource); err != nil {
|
||||
return nil, err
|
||||
return nil, nil, err
|
||||
}
|
||||
|
||||
if resource == s3common.Wildcard {
|
||||
res = res[:0]
|
||||
return append(res, formWildcardNativeResource(actionTypes)), nil
|
||||
return append(res, formWildcardNativeResource(actionTypes)), []string{native.ResourceFormatAllObjects}, nil
|
||||
}
|
||||
|
||||
if !strings.HasPrefix(resource, s3common.S3ResourcePrefix) {
|
||||
|
@ -285,7 +338,7 @@ func formNativeResourceNamesAndConditions(names []string, resolver s3common.Nati
|
|||
s3Resource := strings.TrimPrefix(resource, s3common.S3ResourcePrefix)
|
||||
if s3Resource == s3common.Wildcard {
|
||||
res = res[:0]
|
||||
return append(res, formWildcardNativeResource(actionTypes)), nil
|
||||
return append(res, formWildcardNativeResource(actionTypes)), []string{native.ResourceFormatAllObjects}, nil
|
||||
}
|
||||
|
||||
if sepIndex := strings.Index(s3Resource, "/"); sepIndex < 0 {
|
||||
|
@ -300,9 +353,11 @@ func formNativeResourceNamesAndConditions(names []string, resolver s3common.Nati
|
|||
|
||||
bktInfo, err := resolver.GetBucketInfo(bkt)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
return nil, nil, err
|
||||
}
|
||||
|
||||
treeResMap[fmt.Sprintf(native.ResourceFormatNamespaceContainerObjects, bktInfo.Namespace, bktInfo.Container)] = struct{}{}
|
||||
|
||||
if obj == s3common.Wildcard && actionTypes.Object { // this corresponds to arn:aws:s3:::BUCKET/ or arn:aws:s3:::BUCKET/*
|
||||
combined[fmt.Sprintf(native.ResourceFormatNamespaceContainerObjects, bktInfo.Namespace, bktInfo.Container)] = struct{}{}
|
||||
combined[fmt.Sprintf(native.ResourceFormatNamespaceContainer, bktInfo.Namespace, bktInfo.Container)] = struct{}{}
|
||||
|
@ -338,7 +393,12 @@ func formNativeResourceNamesAndConditions(names []string, resolver s3common.Nati
|
|||
res = append(res, gr)
|
||||
}
|
||||
|
||||
return res, nil
|
||||
treeRes := make([]string, 0, len(treeResMap))
|
||||
for k := range treeResMap {
|
||||
treeRes = append(treeRes, k)
|
||||
}
|
||||
|
||||
return res, treeRes, nil
|
||||
}
|
||||
|
||||
func formWildcardNativeResource(actionTypes ActionTypes) GroupedResources {
|
||||
|
@ -380,17 +440,18 @@ func formPrincipalKey(principal string, resolver s3common.NativeResolver) (strin
|
|||
return key, nil
|
||||
}
|
||||
|
||||
func formNativeActionNames(names []string) ([]string, error) {
|
||||
func formNativeActionNames(names []string) ([]string, bool, error) {
|
||||
uniqueActions := make(map[string]struct{}, len(names))
|
||||
|
||||
var treeWrite bool
|
||||
for _, action := range names {
|
||||
if action == s3common.Wildcard {
|
||||
return []string{s3common.Wildcard}, nil
|
||||
return []string{s3common.Wildcard}, true, nil
|
||||
}
|
||||
|
||||
isIAM, err := s3common.ValidateAction(action)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
return nil, false, err
|
||||
}
|
||||
|
||||
if isIAM {
|
||||
|
@ -398,15 +459,18 @@ func formNativeActionNames(names []string) ([]string, error) {
|
|||
}
|
||||
|
||||
if action[len(s3common.S3ActionPrefix):] == s3common.Wildcard {
|
||||
return []string{s3common.Wildcard}, nil
|
||||
return []string{s3common.Wildcard}, true, nil
|
||||
}
|
||||
|
||||
nativeActions := actionToNativeOpMap[action]
|
||||
if len(nativeActions) == 0 {
|
||||
return nil, s3common.ErrActionsNotApplicable
|
||||
if nativeActions.needTreeWrite {
|
||||
treeWrite = true
|
||||
}
|
||||
if len(nativeActions.operations) == 0 {
|
||||
return nil, false, s3common.ErrActionsNotApplicable
|
||||
}
|
||||
|
||||
for _, nativeAction := range nativeActions {
|
||||
for _, nativeAction := range nativeActions.operations {
|
||||
uniqueActions[nativeAction] = struct{}{}
|
||||
}
|
||||
}
|
||||
|
@ -416,5 +480,5 @@ func formNativeActionNames(names []string) ([]string, error) {
|
|||
res = append(res, key)
|
||||
}
|
||||
|
||||
return res, nil
|
||||
return res, treeWrite, nil
|
||||
}
|
||||
|
|
|
@ -137,6 +137,27 @@ func TestConverters(t *testing.T) {
|
|||
}
|
||||
|
||||
expected := &chain.Chain{Rules: []chain.Rule{
|
||||
{
|
||||
Status: chain.Allow,
|
||||
Actions: chain.Actions{Names: []string{native.MethodGetObject, native.MethodPutObject}},
|
||||
Resources: chain.Resources{Names: []string{
|
||||
fmt.Sprintf(native.ResourceFormatNamespaceContainerObjects, namespace, mockResolver.containers[bktName]),
|
||||
}},
|
||||
Condition: []chain.Condition{
|
||||
{
|
||||
Op: chain.CondStringEquals,
|
||||
Kind: chain.KindRequest,
|
||||
Key: native.PropertyKeyActorPublicKey,
|
||||
Value: mockResolver.users[user],
|
||||
},
|
||||
{
|
||||
Op: chain.CondStringNotEquals,
|
||||
Kind: chain.KindResource,
|
||||
Key: native.ProperyKeyTreeID,
|
||||
Value: "",
|
||||
},
|
||||
},
|
||||
},
|
||||
{
|
||||
Status: chain.Allow,
|
||||
Actions: chain.Actions{Names: []string{native.MethodGetContainer, native.MethodPutObject,
|
||||
|
@ -212,6 +233,27 @@ func TestConverters(t *testing.T) {
|
|||
}
|
||||
|
||||
expected := &chain.Chain{Rules: []chain.Rule{
|
||||
{
|
||||
Status: chain.Allow,
|
||||
Actions: chain.Actions{Names: []string{native.MethodGetObject, native.MethodPutObject}},
|
||||
Resources: chain.Resources{Names: []string{
|
||||
fmt.Sprintf(native.ResourceFormatNamespaceContainerObjects, namespace, mockResolver.containers[bktName]),
|
||||
}},
|
||||
Condition: []chain.Condition{
|
||||
{
|
||||
Op: chain.CondStringEquals,
|
||||
Kind: chain.KindRequest,
|
||||
Key: native.PropertyKeyActorPublicKey,
|
||||
Value: mockResolver.users[user],
|
||||
},
|
||||
{
|
||||
Op: chain.CondStringNotEquals,
|
||||
Kind: chain.KindResource,
|
||||
Key: native.ProperyKeyTreeID,
|
||||
Value: "",
|
||||
},
|
||||
},
|
||||
},
|
||||
{
|
||||
Status: chain.Allow,
|
||||
Actions: chain.Actions{Names: []string{
|
||||
|
@ -357,7 +399,27 @@ func TestConverters(t *testing.T) {
|
|||
require.NoError(t, err)
|
||||
assertChainsEqual(t, s3Expected, s3Chain)
|
||||
|
||||
nativeExpected := &chain.Chain{Rules: []chain.Rule{{
|
||||
nativeExpected := &chain.Chain{Rules: []chain.Rule{
|
||||
{
|
||||
Status: chain.Allow,
|
||||
Actions: chain.Actions{Names: []string{native.MethodGetObject, native.MethodPutObject}},
|
||||
Resources: chain.Resources{Names: []string{native.ResourceFormatAllObjects}},
|
||||
Condition: []chain.Condition{
|
||||
{
|
||||
Op: chain.CondStringEquals,
|
||||
Kind: chain.KindRequest,
|
||||
Key: native.PropertyKeyActorPublicKey,
|
||||
Value: mockResolver.users[user],
|
||||
},
|
||||
{
|
||||
Op: chain.CondStringNotEquals,
|
||||
Kind: chain.KindResource,
|
||||
Key: native.ProperyKeyTreeID,
|
||||
Value: "",
|
||||
},
|
||||
},
|
||||
},
|
||||
{
|
||||
Status: chain.Allow,
|
||||
Actions: chain.Actions{Names: []string{native.MethodGetContainer, native.MethodDeleteObject, native.MethodPutObject, native.MethodHeadObject, native.MethodGetObject, native.MethodRangeObject}},
|
||||
Resources: chain.Resources{Names: []string{native.ResourceFormatAllObjects, native.ResourceFormatAllContainers}},
|
||||
|
@ -367,7 +429,8 @@ func TestConverters(t *testing.T) {
|
|||
Key: native.PropertyKeyActorPublicKey,
|
||||
Value: mockResolver.users[user],
|
||||
}},
|
||||
}}}
|
||||
},
|
||||
}}
|
||||
|
||||
nativeChain, err := ConvertToNativeChain(p, mockResolver)
|
||||
require.NoError(t, err)
|
||||
|
@ -695,7 +758,19 @@ func TestIPConditions(t *testing.T) {
|
|||
require.Equal(t, s3Expected, s3Chain)
|
||||
|
||||
nativeExpected := &chain.Chain{
|
||||
Rules: []chain.Rule{{
|
||||
Rules: []chain.Rule{
|
||||
{
|
||||
Status: chain.Allow,
|
||||
Actions: chain.Actions{Names: []string{native.MethodGetObject, native.MethodPutObject}},
|
||||
Resources: chain.Resources{Names: []string{native.ResourceFormatAllObjects}},
|
||||
Condition: []chain.Condition{{
|
||||
Op: chain.CondStringNotEquals,
|
||||
Kind: chain.KindResource,
|
||||
Key: native.ProperyKeyTreeID,
|
||||
Value: "",
|
||||
}},
|
||||
},
|
||||
{
|
||||
Status: chain.Allow,
|
||||
Actions: chain.Actions{Names: []string{s3common.Wildcard}},
|
||||
Resources: chain.Resources{Names: []string{native.ResourceFormatAllObjects, native.ResourceFormatAllContainers}},
|
||||
|
@ -705,7 +780,8 @@ func TestIPConditions(t *testing.T) {
|
|||
Key: common.PropertyKeyFrostFSSourceIP,
|
||||
Value: "203.0.113.0/24",
|
||||
}},
|
||||
}},
|
||||
},
|
||||
},
|
||||
}
|
||||
|
||||
nativeChain, err := ConvertToNativeChain(p, newMockUserResolver(nil, nil, ""))
|
||||
|
@ -853,10 +929,12 @@ func TestComplexNativeConditions(t *testing.T) {
|
|||
}
|
||||
|
||||
expectedStatus := chain.Allow
|
||||
expectedActions := chain.Actions{Names: actionToNativeOpMap["s3:"+action]}
|
||||
expectedActions := chain.Actions{Names: actionToNativeOpMap["s3:"+action].operations}
|
||||
expectedResource1 := chain.Resources{Names: []string{nativeResource1, nativeResource1cnr}}
|
||||
expectedResource23 := chain.Resources{Names: []string{nativeResource2, nativeResource2cnr, nativeResource3, nativeResource3cnr}}
|
||||
|
||||
treeCondition := chain.Condition{Op: chain.CondStringNotEquals, Kind: chain.KindResource, Key: native.ProperyKeyTreeID, Value: ""}
|
||||
//notTreeCondition := chain.Condition{Op: chain.CondStringEquals, Kind: chain.KindResource, Key: native.ProperyKeyTreeID, Value: ""} // todo
|
||||
user1Condition := chain.Condition{Op: chain.CondStringEquals, Kind: chain.KindRequest, Key: native.PropertyKeyActorPublicKey, Value: mockResolver.users[user1]}
|
||||
user2Condition := chain.Condition{Op: chain.CondStringEquals, Kind: chain.KindRequest, Key: native.PropertyKeyActorPublicKey, Value: mockResolver.users[user2]}
|
||||
objectName1Condition := chain.Condition{Op: chain.CondStringLike, Kind: chain.KindResource, Key: PropertyKeyFilePath, Value: objName1}
|
||||
|
@ -865,6 +943,24 @@ func TestComplexNativeConditions(t *testing.T) {
|
|||
key2val2Condition := chain.Condition{Op: chain.CondStringLike, Kind: chain.KindRequest, Key: key2, Value: val2}
|
||||
|
||||
expected := &chain.Chain{Rules: []chain.Rule{
|
||||
{
|
||||
Status: expectedStatus,
|
||||
Actions: chain.Actions{Names: []string{native.MethodGetObject, native.MethodPutObject}},
|
||||
Resources: chain.Resources{Names: []string{nativeResource1, nativeResource2, nativeResource3}},
|
||||
Condition: []chain.Condition{
|
||||
user1Condition,
|
||||
treeCondition,
|
||||
},
|
||||
},
|
||||
{
|
||||
Status: expectedStatus,
|
||||
Actions: chain.Actions{Names: []string{native.MethodGetObject, native.MethodPutObject}},
|
||||
Resources: chain.Resources{Names: []string{nativeResource1, nativeResource2, nativeResource3}},
|
||||
Condition: []chain.Condition{
|
||||
user2Condition,
|
||||
treeCondition,
|
||||
},
|
||||
},
|
||||
{
|
||||
Status: expectedStatus,
|
||||
Actions: expectedActions,
|
||||
|
@ -1443,11 +1539,24 @@ func TestWildcardConverters(t *testing.T) {
|
|||
require.Equal(t, s3Expected, s3Chain)
|
||||
|
||||
nativeExpected := &chain.Chain{
|
||||
Rules: []chain.Rule{{
|
||||
Rules: []chain.Rule{
|
||||
{
|
||||
Status: chain.Allow,
|
||||
Actions: chain.Actions{Names: []string{native.MethodGetObject, native.MethodPutObject}},
|
||||
Resources: chain.Resources{Names: []string{native.ResourceFormatAllObjects}},
|
||||
Condition: []chain.Condition{{
|
||||
Op: chain.CondStringNotEquals,
|
||||
Kind: chain.KindResource,
|
||||
Key: native.ProperyKeyTreeID,
|
||||
Value: "",
|
||||
}},
|
||||
},
|
||||
{
|
||||
Status: chain.Allow,
|
||||
Actions: chain.Actions{Names: []string{s3common.Wildcard}},
|
||||
Resources: chain.Resources{Names: []string{native.ResourceFormatAllObjects, native.ResourceFormatAllContainers}},
|
||||
}},
|
||||
},
|
||||
},
|
||||
}
|
||||
|
||||
nativeChain, err := ConvertToNativeChain(p, newMockUserResolver(nil, nil, ""))
|
||||
|
@ -1490,14 +1599,27 @@ func TestWildcardObjectsConverters(t *testing.T) {
|
|||
mockResolver := newMockUserResolver(nil, []string{"bucket"}, "")
|
||||
|
||||
nativeExpected := &chain.Chain{
|
||||
Rules: []chain.Rule{{
|
||||
Rules: []chain.Rule{
|
||||
{
|
||||
Status: chain.Allow,
|
||||
Actions: chain.Actions{Names: []string{native.MethodGetObject, native.MethodPutObject}},
|
||||
Resources: chain.Resources{Names: []string{fmt.Sprintf(native.ResourceFormatRootContainerObjects, mockResolver.containers["bucket"])}},
|
||||
Condition: []chain.Condition{{
|
||||
Op: chain.CondStringNotEquals,
|
||||
Kind: chain.KindResource,
|
||||
Key: native.ProperyKeyTreeID,
|
||||
Value: "",
|
||||
}},
|
||||
},
|
||||
{
|
||||
Status: chain.Allow,
|
||||
Actions: chain.Actions{Names: []string{s3common.Wildcard}},
|
||||
Resources: chain.Resources{Names: []string{
|
||||
fmt.Sprintf(native.ResourceFormatRootContainer, mockResolver.containers["bucket"]),
|
||||
fmt.Sprintf(native.ResourceFormatRootContainerObjects, mockResolver.containers["bucket"]),
|
||||
}},
|
||||
}},
|
||||
},
|
||||
},
|
||||
}
|
||||
|
||||
nativeChain, err := ConvertToNativeChain(p, mockResolver)
|
||||
|
@ -1655,7 +1777,7 @@ func TestFromActions(t *testing.T) {
|
|||
},
|
||||
} {
|
||||
t.Run("", func(t *testing.T) {
|
||||
actions, err := formNativeActionNames([]string{tc.action})
|
||||
actions, _, err := formNativeActionNames([]string{tc.action})
|
||||
if tc.err {
|
||||
require.Error(t, err)
|
||||
} else {
|
||||
|
@ -1776,13 +1898,19 @@ func TestTagsConditions(t *testing.T) {
|
|||
},
|
||||
}
|
||||
|
||||
expectedNativeConditions := []chain.Condition{
|
||||
{
|
||||
expectedNativeConditions := [][]chain.Condition{
|
||||
{{
|
||||
Op: chain.CondStringNotEquals,
|
||||
Kind: chain.KindResource,
|
||||
Key: native.ProperyKeyTreeID,
|
||||
Value: "",
|
||||
}},
|
||||
{{
|
||||
Op: chain.CondStringEquals,
|
||||
Kind: chain.KindRequest,
|
||||
Key: fmt.Sprintf(common.PropertyKeyFormatFrostFSIDUserClaim, "tag-department"),
|
||||
Value: "hr",
|
||||
},
|
||||
}},
|
||||
}
|
||||
|
||||
for _, tc := range []struct {
|
||||
|
@ -1847,8 +1975,9 @@ func TestTagsConditions(t *testing.T) {
|
|||
|
||||
nativeChain, err := ConvertToNativeChain(p, newMockUserResolver(nil, nil, ""))
|
||||
require.NoError(t, err)
|
||||
require.Len(t, nativeChain.Rules, 1)
|
||||
require.ElementsMatch(t, expectedNativeConditions, nativeChain.Rules[0].Condition)
|
||||
require.Len(t, nativeChain.Rules, 2)
|
||||
require.ElementsMatch(t, expectedNativeConditions[0], nativeChain.Rules[0].Condition)
|
||||
require.ElementsMatch(t, expectedNativeConditions[1], nativeChain.Rules[1].Condition)
|
||||
}
|
||||
}
|
||||
|
||||
|
|
Loading…
Add table
Reference in a new issue