[#680] Drop GetContainer operation from policy converter

Signed-off-by: Denis Kirillov <d.kirillov@yadro.com>
This commit is contained in:
Denis Kirillov 2025-04-07 17:15:55 +03:00 committed by Alexey Vanin
parent 4d305d0d0f
commit 526ddc1243
2 changed files with 103 additions and 101 deletions

View file

@ -1,7 +1,6 @@
package iam package iam
import ( import (
"errors"
"fmt" "fmt"
"strings" "strings"
@ -14,61 +13,61 @@ const PropertyKeyFilePath = "FilePath"
type nativeOperationInfo struct { type nativeOperationInfo struct {
operations []string operations []string
needTreeWrite bool treeOperations []string
} }
var actionToNativeOpMap = map[string]nativeOperationInfo{ var actionToNativeOpMap = map[string]nativeOperationInfo{
s3common.S3ActionAbortMultipartUpload: {operations: []string{native.MethodGetContainer, native.MethodDeleteObject, native.MethodHeadObject, native.MethodGetObject, native.MethodPutObject}, needTreeWrite: true}, s3common.S3ActionAbortMultipartUpload: {treeOperations: []string{native.MethodGetObject, native.MethodPutObject}, operations: []string{native.MethodDeleteObject, native.MethodHeadObject, native.MethodGetObject, native.MethodPutObject}},
s3common.S3ActionCreateBucket: {operations: []string{native.MethodGetContainer, native.MethodPutContainer, native.MethodSetContainerEACL}, needTreeWrite: true}, s3common.S3ActionCreateBucket: {treeOperations: []string{native.MethodGetObject, native.MethodPutObject}, operations: []string{native.MethodPutContainer, native.MethodSetContainerEACL}},
s3common.S3ActionDeleteBucket: {operations: []string{native.MethodGetContainer, native.MethodDeleteContainer, native.MethodSearchObject, native.MethodHeadObject, native.MethodGetObject, native.MethodPutObject}, needTreeWrite: true}, s3common.S3ActionDeleteBucket: {treeOperations: []string{native.MethodGetObject, native.MethodPutObject}, operations: []string{native.MethodDeleteContainer, native.MethodSearchObject, native.MethodHeadObject, native.MethodGetObject, native.MethodPutObject}},
s3common.S3ActionDeleteBucketPolicy: {operations: []string{native.MethodGetContainer}}, s3common.S3ActionDeleteBucketPolicy: {treeOperations: []string{native.MethodGetObject}},
s3common.S3ActionDeleteObject: {operations: []string{native.MethodGetContainer, native.MethodDeleteObject, native.MethodPutObject, native.MethodHeadObject, native.MethodGetObject, native.MethodRangeObject}, needTreeWrite: true}, s3common.S3ActionDeleteObject: {treeOperations: []string{native.MethodGetObject, native.MethodPutObject}, operations: []string{native.MethodDeleteObject, native.MethodPutObject, native.MethodHeadObject, native.MethodGetObject, native.MethodRangeObject}},
s3common.S3ActionDeleteObjectTagging: {operations: []string{native.MethodGetContainer}, needTreeWrite: true}, s3common.S3ActionDeleteObjectTagging: {treeOperations: []string{native.MethodGetObject, native.MethodPutObject}},
s3common.S3ActionDeleteObjectVersion: {operations: []string{native.MethodGetContainer, native.MethodDeleteObject, native.MethodPutObject, native.MethodHeadObject, native.MethodGetObject, native.MethodRangeObject}, needTreeWrite: true}, s3common.S3ActionDeleteObjectVersion: {treeOperations: []string{native.MethodGetObject, native.MethodPutObject}, operations: []string{native.MethodDeleteObject, native.MethodPutObject, native.MethodHeadObject, native.MethodGetObject, native.MethodRangeObject}},
s3common.S3ActionDeleteObjectVersionTagging: {operations: []string{native.MethodGetContainer}, needTreeWrite: true}, s3common.S3ActionDeleteObjectVersionTagging: {treeOperations: []string{native.MethodGetObject, native.MethodPutObject}},
s3common.S3ActionGetBucketACL: {operations: []string{native.MethodGetContainer}}, s3common.S3ActionGetBucketACL: {treeOperations: []string{native.MethodGetObject}},
s3common.S3ActionGetBucketCORS: {operations: []string{native.MethodGetContainer, native.MethodGetObject, native.MethodHeadObject}}, s3common.S3ActionGetBucketCORS: {treeOperations: []string{native.MethodGetObject}, operations: []string{native.MethodGetObject, native.MethodHeadObject}},
s3common.S3ActionGetBucketLocation: {operations: []string{native.MethodGetContainer}}, s3common.S3ActionGetBucketLocation: {treeOperations: []string{native.MethodGetObject}},
s3common.S3ActionGetBucketNotification: {operations: []string{native.MethodGetContainer, native.MethodGetObject, native.MethodHeadObject}}, // not supported s3common.S3ActionGetBucketNotification: {treeOperations: []string{native.MethodGetObject}, operations: []string{native.MethodGetObject, native.MethodHeadObject}}, // not supported
s3common.S3ActionGetBucketObjectLockConfiguration: {operations: []string{native.MethodGetContainer}}, s3common.S3ActionGetBucketObjectLockConfiguration: {treeOperations: []string{native.MethodGetObject}},
s3common.S3ActionGetBucketPolicy: {operations: []string{native.MethodGetContainer}}, s3common.S3ActionGetBucketPolicy: {treeOperations: []string{native.MethodGetObject}},
s3common.S3ActionGetBucketPolicyStatus: {operations: []string{native.MethodGetContainer}}, s3common.S3ActionGetBucketPolicyStatus: {treeOperations: []string{native.MethodGetObject}},
s3common.S3ActionGetBucketTagging: {operations: []string{native.MethodGetContainer}}, s3common.S3ActionGetBucketTagging: {treeOperations: []string{native.MethodGetObject}},
s3common.S3ActionGetBucketVersioning: {operations: []string{native.MethodGetContainer}}, s3common.S3ActionGetBucketVersioning: {treeOperations: []string{native.MethodGetObject}},
s3common.S3ActionGetLifecycleConfiguration: {operations: []string{native.MethodGetContainer, native.MethodGetObject, native.MethodHeadObject}}, s3common.S3ActionGetLifecycleConfiguration: {treeOperations: []string{native.MethodGetObject}, operations: []string{native.MethodGetObject, native.MethodHeadObject}},
s3common.S3ActionGetObject: {operations: []string{native.MethodGetContainer, native.MethodGetObject, native.MethodHeadObject, native.MethodSearchObject, native.MethodRangeObject, native.MethodHashObject}}, s3common.S3ActionGetObject: {treeOperations: []string{native.MethodGetObject}, operations: []string{native.MethodGetObject, native.MethodHeadObject, native.MethodSearchObject, native.MethodRangeObject, native.MethodHashObject}},
s3common.S3ActionGetObjectACL: {operations: []string{native.MethodGetContainer}}, s3common.S3ActionGetObjectACL: {treeOperations: []string{native.MethodGetObject}},
s3common.S3ActionGetObjectAttributes: {operations: []string{native.MethodGetContainer, native.MethodHeadObject}}, s3common.S3ActionGetObjectAttributes: {treeOperations: []string{native.MethodGetObject}, operations: []string{native.MethodHeadObject}},
s3common.S3ActionGetObjectLegalHold: {operations: []string{native.MethodGetContainer}}, s3common.S3ActionGetObjectLegalHold: {treeOperations: []string{native.MethodGetObject}},
s3common.S3ActionGetObjectRetention: {operations: []string{native.MethodGetContainer}}, s3common.S3ActionGetObjectRetention: {treeOperations: []string{native.MethodGetObject}},
s3common.S3ActionGetObjectTagging: {operations: []string{native.MethodGetContainer}}, s3common.S3ActionGetObjectTagging: {treeOperations: []string{native.MethodGetObject}},
s3common.S3ActionGetObjectVersion: {operations: []string{native.MethodGetContainer, native.MethodGetObject, native.MethodHeadObject, native.MethodSearchObject, native.MethodRangeObject, native.MethodHashObject}}, s3common.S3ActionGetObjectVersion: {treeOperations: []string{native.MethodGetObject}, operations: []string{native.MethodGetObject, native.MethodHeadObject, native.MethodSearchObject, native.MethodRangeObject, native.MethodHashObject}},
s3common.S3ActionGetObjectVersionACL: {operations: []string{native.MethodGetContainer}}, s3common.S3ActionGetObjectVersionACL: {treeOperations: []string{native.MethodGetObject}},
s3common.S3ActionGetObjectVersionAttributes: {operations: []string{native.MethodGetContainer, native.MethodHeadObject}}, s3common.S3ActionGetObjectVersionAttributes: {treeOperations: []string{native.MethodGetObject}, operations: []string{native.MethodHeadObject}},
s3common.S3ActionGetObjectVersionTagging: {operations: []string{native.MethodGetContainer}}, s3common.S3ActionGetObjectVersionTagging: {treeOperations: []string{native.MethodGetObject}},
s3common.S3ActionListAllMyBuckets: {operations: []string{native.MethodListContainers, native.MethodGetContainer}}, s3common.S3ActionListAllMyBuckets: {treeOperations: []string{native.MethodGetObject}, operations: []string{native.MethodListContainers}},
s3common.S3ActionListBucket: {operations: []string{native.MethodGetContainer, native.MethodGetObject, native.MethodHeadObject, native.MethodSearchObject, native.MethodRangeObject, native.MethodHashObject}}, s3common.S3ActionListBucket: {treeOperations: []string{native.MethodGetObject}, operations: []string{native.MethodGetObject, native.MethodHeadObject, native.MethodSearchObject, native.MethodRangeObject, native.MethodHashObject}},
s3common.S3ActionListBucketMultipartUploads: {operations: []string{native.MethodGetContainer}}, s3common.S3ActionListBucketMultipartUploads: {treeOperations: []string{native.MethodGetObject}},
s3common.S3ActionListBucketVersions: {operations: []string{native.MethodGetContainer, native.MethodGetObject, native.MethodHeadObject, native.MethodSearchObject, native.MethodRangeObject, native.MethodHashObject}}, s3common.S3ActionListBucketVersions: {treeOperations: []string{native.MethodGetObject}, operations: []string{native.MethodGetObject, native.MethodHeadObject, native.MethodSearchObject, native.MethodRangeObject, native.MethodHashObject}},
s3common.S3ActionListMultipartUploadParts: {operations: []string{native.MethodGetContainer}}, s3common.S3ActionListMultipartUploadParts: {treeOperations: []string{native.MethodGetObject}},
s3common.S3ActionPutBucketACL: {operations: []string{native.MethodGetContainer}, needTreeWrite: true}, s3common.S3ActionPutBucketACL: {treeOperations: []string{native.MethodGetObject, native.MethodPutObject}},
s3common.S3ActionPutBucketCORS: {operations: []string{native.MethodGetContainer, native.MethodGetObject, native.MethodPutObject}, needTreeWrite: true}, s3common.S3ActionPutBucketCORS: {treeOperations: []string{native.MethodGetObject, native.MethodPutObject}, operations: []string{native.MethodGetObject, native.MethodPutObject}},
s3common.S3ActionPutBucketNotification: {operations: []string{native.MethodGetContainer, native.MethodHeadObject, native.MethodDeleteObject, native.MethodGetObject, native.MethodPutObject}, needTreeWrite: true}, // not supported s3common.S3ActionPutBucketNotification: {treeOperations: []string{native.MethodGetObject, native.MethodPutObject}, operations: []string{native.MethodHeadObject, native.MethodDeleteObject, native.MethodGetObject, native.MethodPutObject}}, // not supported
s3common.S3ActionPutBucketObjectLockConfiguration: {operations: []string{native.MethodGetContainer}, needTreeWrite: true}, s3common.S3ActionPutBucketObjectLockConfiguration: {treeOperations: []string{native.MethodGetObject, native.MethodPutObject}},
s3common.S3ActionPutBucketPolicy: {operations: []string{native.MethodGetContainer}}, s3common.S3ActionPutBucketPolicy: {treeOperations: []string{native.MethodGetObject}},
s3common.S3ActionPutBucketTagging: {operations: []string{native.MethodGetContainer}, needTreeWrite: true}, s3common.S3ActionPutBucketTagging: {treeOperations: []string{native.MethodGetObject, native.MethodPutObject}},
s3common.S3ActionPutBucketVersioning: {operations: []string{native.MethodGetContainer}, needTreeWrite: true}, s3common.S3ActionPutBucketVersioning: {treeOperations: []string{native.MethodGetObject, native.MethodPutObject}},
s3common.S3ActionPutLifecycleConfiguration: {operations: []string{native.MethodGetContainer, native.MethodGetObject, native.MethodHeadObject, native.MethodPutObject, native.MethodDeleteObject}, needTreeWrite: true}, s3common.S3ActionPutLifecycleConfiguration: {treeOperations: []string{native.MethodGetObject, native.MethodPutObject}, operations: []string{native.MethodGetObject, native.MethodHeadObject, native.MethodPutObject, native.MethodDeleteObject}},
s3common.S3ActionPutObject: {operations: []string{native.MethodGetContainer, native.MethodPutObject, native.MethodGetObject, native.MethodHeadObject, native.MethodRangeObject}, needTreeWrite: true}, s3common.S3ActionPutObject: {treeOperations: []string{native.MethodGetObject, native.MethodPutObject}, operations: []string{native.MethodPutObject, native.MethodGetObject, native.MethodHeadObject, native.MethodRangeObject}},
s3common.S3ActionPutObjectACL: {operations: []string{native.MethodGetContainer}}, // not supported s3common.S3ActionPutObjectACL: {}, // not supported
s3common.S3ActionPutObjectLegalHold: {operations: []string{native.MethodGetContainer, native.MethodHeadObject, native.MethodGetObject, native.MethodPutObject}, needTreeWrite: true}, s3common.S3ActionPutObjectLegalHold: {treeOperations: []string{native.MethodGetObject, native.MethodPutObject}, operations: []string{native.MethodHeadObject, native.MethodGetObject, native.MethodPutObject}},
s3common.S3ActionPutObjectRetention: {operations: []string{native.MethodGetContainer, native.MethodHeadObject, native.MethodGetObject, native.MethodPutObject}, needTreeWrite: true}, s3common.S3ActionPutObjectRetention: {treeOperations: []string{native.MethodGetObject, native.MethodPutObject}, operations: []string{native.MethodHeadObject, native.MethodGetObject, native.MethodPutObject}},
s3common.S3ActionPutObjectTagging: {operations: []string{native.MethodGetContainer}, needTreeWrite: true}, s3common.S3ActionPutObjectTagging: {treeOperations: []string{native.MethodGetObject, native.MethodPutObject}},
s3common.S3ActionPutObjectVersionACL: {operations: []string{native.MethodGetContainer}}, // not supported s3common.S3ActionPutObjectVersionACL: {}, // not supported
s3common.S3ActionPutObjectVersionTagging: {operations: []string{native.MethodGetContainer}, needTreeWrite: true}, s3common.S3ActionPutObjectVersionTagging: {treeOperations: []string{native.MethodGetObject, native.MethodPutObject}},
s3common.S3ActionPatchObject: {operations: []string{native.MethodGetContainer, native.MethodGetObject, native.MethodHeadObject, native.MethodPatchObject, native.MethodPutObject, native.MethodRangeObject}, needTreeWrite: true}, s3common.S3ActionPatchObject: {treeOperations: []string{native.MethodGetObject, native.MethodPutObject}, operations: []string{native.MethodGetObject, native.MethodHeadObject, native.MethodPatchObject, native.MethodPutObject, native.MethodRangeObject}},
s3common.S3ActionPutBucketPublicAccessBlock: {operations: []string{native.MethodGetContainer}, needTreeWrite: true}, s3common.S3ActionPutBucketPublicAccessBlock: {treeOperations: []string{native.MethodGetObject, native.MethodPutObject}},
s3common.S3ActionGetBucketPublicAccessBlock: {operations: []string{native.MethodGetContainer}}, s3common.S3ActionGetBucketPublicAccessBlock: {treeOperations: []string{native.MethodGetObject}},
} }
var containerNativeOperations = map[string]struct{}{ var containerNativeOperations = map[string]struct{}{
@ -90,7 +89,7 @@ var objectNativeOperations = map[string]struct{}{
native.MethodHashObject: {}, native.MethodHashObject: {},
} }
var errConditionKeyNotApplicable = errors.New("condition key is not applicable") var treeNativeOperations = []string{native.MethodGetObject, native.MethodPutObject}
func ConvertToNativeChain(p s3common.Policy, resolver s3common.NativeResolver) (*chain.Chain, error) { func ConvertToNativeChain(p s3common.Policy, resolver s3common.NativeResolver) (*chain.Chain, error) {
if err := p.Validate(s3common.ResourceBasedPolicyType); err != nil { if err := p.Validate(s3common.ResourceBasedPolicyType); err != nil {
@ -109,37 +108,37 @@ func ConvertToNativeChain(p s3common.Policy, resolver s3common.NativeResolver) (
} }
action, actionInverted := statement.GetAction() action, actionInverted := statement.GetAction()
nativeActions, treeWrite, err := formNativeActionNames(action) nativeActions, nativeTreeActions, err := formNativeActionNames(action)
if err != nil { if err != nil {
return nil, err return nil, err
} }
ruleAction := chain.Actions{Inverted: actionInverted, Names: nativeActions}
if len(ruleAction.Names) == 0 {
continue
}
resource, resourceInverted := statement.GetResource() resource, resourceInverted := statement.GetResource()
groupedResources, treeRes, err := formNativeResourceNamesAndConditions(resource, resolver, getActionTypes(nativeActions)) groupedResources, treeRes, err := formNativeResourceNamesAndConditions(resource, resolver, getActionTypes(nativeActions, nativeTreeActions))
if err != nil { if err != nil {
return nil, err return nil, err
} }
groupedConditions, err := convertToNativeChainCondition(statement.Conditions, resolver)
if err != nil {
if errors.Is(err, errConditionKeyNotApplicable) {
continue
}
return nil, err
}
splitConditions := s3common.SplitGroupedConditions(groupedConditions)
principals, principalCondFn, err := getNativePrincipalsAndConditionFunc(statement, resolver) principals, principalCondFn, err := getNativePrincipalsAndConditionFunc(statement, resolver)
if err != nil { if err != nil {
return nil, err return nil, err
} }
engineChain.Rules = append(engineChain.Rules, getTreeRule(treeRes, principals, principalCondFn, treeWrite)...) if len(treeRes) != 0 {
engineChain.Rules = append(engineChain.Rules, getTreeRule(treeRes, principals, principalCondFn, nativeTreeActions)...)
}
if len(groupedResources) == 0 {
continue
}
groupedConditions, err := convertToNativeChainCondition(statement.Conditions, resolver)
if err != nil {
return nil, err
}
splitConditions := s3common.SplitGroupedConditions(groupedConditions)
ruleAction := chain.Actions{Inverted: actionInverted, Names: nativeActions}
for _, groupedResource := range groupedResources { for _, groupedResource := range groupedResources {
for _, principal := range principals { for _, principal := range principals {
for _, conditions := range splitConditions { for _, conditions := range splitConditions {
@ -172,12 +171,7 @@ func ConvertToNativeChain(p s3common.Policy, resolver s3common.NativeResolver) (
return &engineChain, nil return &engineChain, nil
} }
func getTreeRule(resources []string, principals []string, principalCondFn s3common.FormPrincipalConditionFunc, needWrite bool) []chain.Rule { func getTreeRule(resources []string, principals []string, principalCondFn s3common.FormPrincipalConditionFunc, treeActions []string) []chain.Rule {
ops := []string{native.MethodGetObject}
if needWrite {
ops = append(ops, native.MethodPutObject)
}
treeCondition := chain.Condition{ treeCondition := chain.Condition{
Op: chain.CondStringNotEquals, Op: chain.CondStringNotEquals,
Kind: chain.KindResource, Kind: chain.KindResource,
@ -197,7 +191,7 @@ func getTreeRule(resources []string, principals []string, principalCondFn s3comm
if len(principalTreeConditions) == 0 { if len(principalTreeConditions) == 0 {
return []chain.Rule{{ return []chain.Rule{{
Status: chain.Allow, Status: chain.Allow,
Actions: chain.Actions{Names: ops}, Actions: chain.Actions{Names: treeActions},
Resources: chain.Resources{Names: resources}, Resources: chain.Resources{Names: resources},
Condition: []chain.Condition{treeCondition}, Condition: []chain.Condition{treeCondition},
}} }}
@ -207,7 +201,7 @@ func getTreeRule(resources []string, principals []string, principalCondFn s3comm
for i, condition := range principalTreeConditions { for i, condition := range principalTreeConditions {
res[i] = chain.Rule{ res[i] = chain.Rule{
Status: chain.Allow, Status: chain.Allow,
Actions: chain.Actions{Names: ops}, Actions: chain.Actions{Names: treeActions},
Resources: chain.Resources{Names: resources}, Resources: chain.Resources{Names: resources},
Condition: []chain.Condition{treeCondition, condition}, Condition: []chain.Condition{treeCondition, condition},
} }
@ -216,8 +210,8 @@ func getTreeRule(resources []string, principals []string, principalCondFn s3comm
return res return res
} }
func getActionTypes(nativeActions []string) ActionTypes { func getActionTypes(nativeActions []string, treeActions []string) ActionTypes {
var res ActionTypes res := ActionTypes{Tree: len(treeActions) != 0}
for _, action := range nativeActions { for _, action := range nativeActions {
if res.Object && res.Container { if res.Object && res.Container {
break break
@ -278,7 +272,8 @@ func convertToNativeChainCondition(c s3common.Conditions, resolver s3common.Nati
for i := range gr.Conditions { for i := range gr.Conditions {
switch { switch {
case gr.Conditions[i].Key == condKeyAWSMFAPresent: case gr.Conditions[i].Key == condKeyAWSMFAPresent:
return s3common.GroupedConditions{}, errConditionKeyNotApplicable // MFA property exist only in S3 request (in AccessBox), so native protocol should not process such conditions.
continue
case gr.Conditions[i].Key == s3common.CondKeyAWSPrincipalARN: case gr.Conditions[i].Key == s3common.CondKeyAWSPrincipalARN:
gr.Conditions[i].Key = native.PropertyKeyActorPublicKey gr.Conditions[i].Key = native.PropertyKeyActorPublicKey
val, err := formPrincipalKey(gr.Conditions[i].Value, resolver) val, err := formPrincipalKey(gr.Conditions[i].Value, resolver)
@ -308,10 +303,11 @@ type GroupedResources struct {
type ActionTypes struct { type ActionTypes struct {
Object bool Object bool
Container bool Container bool
Tree bool
} }
func formNativeResourceNamesAndConditions(names []string, resolver s3common.NativeResolver, actionTypes ActionTypes) ([]GroupedResources, []string, error) { func formNativeResourceNamesAndConditions(names []string, resolver s3common.NativeResolver, actionTypes ActionTypes) ([]GroupedResources, []string, error) {
if !actionTypes.Object && !actionTypes.Container { if !actionTypes.Object && !actionTypes.Container && !actionTypes.Tree {
return nil, nil, s3common.ErrActionsNotApplicable return nil, nil, s3common.ErrActionsNotApplicable
} }
@ -356,7 +352,12 @@ func formNativeResourceNamesAndConditions(names []string, resolver s3common.Nati
return nil, nil, err return nil, nil, err
} }
if actionTypes.Tree {
treeResMap[fmt.Sprintf(native.ResourceFormatNamespaceContainerObjects, bktInfo.Namespace, bktInfo.Container)] = struct{}{} treeResMap[fmt.Sprintf(native.ResourceFormatNamespaceContainerObjects, bktInfo.Namespace, bktInfo.Container)] = struct{}{}
if !actionTypes.Object && !actionTypes.Container {
continue
}
}
if obj == s3common.Wildcard && actionTypes.Object { // this corresponds to arn:aws:s3:::BUCKET/ or arn:aws:s3:::BUCKET/* 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.ResourceFormatNamespaceContainerObjects, bktInfo.Namespace, bktInfo.Container)] = struct{}{}
@ -440,18 +441,18 @@ func formPrincipalKey(principal string, resolver s3common.NativeResolver) (strin
return key, nil return key, nil
} }
func formNativeActionNames(names []string) ([]string, bool, error) { func formNativeActionNames(names []string) ([]string, []string, error) {
uniqueActions := make(map[string]struct{}, len(names)) uniqueActions := make(map[string]struct{}, len(names))
var treeWrite bool var treeActions []string
for _, action := range names { for _, action := range names {
if action == s3common.Wildcard { if action == s3common.Wildcard {
return []string{s3common.Wildcard}, true, nil return []string{s3common.Wildcard}, treeNativeOperations, nil
} }
isIAM, err := s3common.ValidateAction(action) isIAM, err := s3common.ValidateAction(action)
if err != nil { if err != nil {
return nil, false, err return nil, nil, err
} }
if isIAM { if isIAM {
@ -459,15 +460,16 @@ func formNativeActionNames(names []string) ([]string, bool, error) {
} }
if action[len(s3common.S3ActionPrefix):] == s3common.Wildcard { if action[len(s3common.S3ActionPrefix):] == s3common.Wildcard {
return []string{s3common.Wildcard}, true, nil return []string{s3common.Wildcard}, treeNativeOperations, nil
} }
nativeActions := actionToNativeOpMap[action] nativeActions := actionToNativeOpMap[action]
if nativeActions.needTreeWrite { if len(nativeActions.operations) == 0 && len(nativeActions.treeOperations) == 0 {
treeWrite = true return nil, nil, s3common.ErrActionsNotApplicable
} }
if len(nativeActions.operations) == 0 {
return nil, false, s3common.ErrActionsNotApplicable if len(nativeActions.treeOperations) > len(treeActions) {
treeActions = nativeActions.treeOperations
} }
for _, nativeAction := range nativeActions.operations { for _, nativeAction := range nativeActions.operations {
@ -480,5 +482,5 @@ func formNativeActionNames(names []string) ([]string, bool, error) {
res = append(res, key) res = append(res, key)
} }
return res, treeWrite, nil return res, treeActions, nil
} }

View file

@ -160,7 +160,7 @@ func TestConverters(t *testing.T) {
}, },
{ {
Status: chain.Allow, Status: chain.Allow,
Actions: chain.Actions{Names: []string{native.MethodGetContainer, native.MethodPutObject, Actions: chain.Actions{Names: []string{native.MethodPutObject,
native.MethodGetObject, native.MethodHeadObject, native.MethodRangeObject}}, native.MethodGetObject, native.MethodHeadObject, native.MethodRangeObject}},
Resources: chain.Resources{Names: []string{ Resources: chain.Resources{Names: []string{
fmt.Sprintf(native.ResourceFormatNamespaceContainerObjects, namespace, mockResolver.containers[bktName]), fmt.Sprintf(native.ResourceFormatNamespaceContainerObjects, namespace, mockResolver.containers[bktName]),
@ -257,7 +257,7 @@ func TestConverters(t *testing.T) {
{ {
Status: chain.Allow, Status: chain.Allow,
Actions: chain.Actions{Names: []string{ Actions: chain.Actions{Names: []string{
native.MethodGetContainer, native.MethodDeleteContainer, native.MethodDeleteContainer,
native.MethodSearchObject, native.MethodHeadObject, native.MethodSearchObject, native.MethodHeadObject,
native.MethodDeleteObject, native.MethodPutObject, native.MethodDeleteObject, native.MethodPutObject,
native.MethodGetObject, native.MethodRangeObject, native.MethodGetObject, native.MethodRangeObject,
@ -284,7 +284,7 @@ func TestConverters(t *testing.T) {
{ {
Status: chain.Allow, Status: chain.Allow,
Actions: chain.Actions{Names: []string{ Actions: chain.Actions{Names: []string{
native.MethodGetContainer, native.MethodDeleteContainer, native.MethodDeleteContainer,
native.MethodSearchObject, native.MethodHeadObject, native.MethodSearchObject, native.MethodHeadObject,
native.MethodDeleteObject, native.MethodPutObject, native.MethodDeleteObject, native.MethodPutObject,
native.MethodGetObject, native.MethodRangeObject, native.MethodGetObject, native.MethodRangeObject,
@ -421,8 +421,8 @@ func TestConverters(t *testing.T) {
}, },
{ {
Status: chain.Allow, Status: chain.Allow,
Actions: chain.Actions{Names: []string{native.MethodGetContainer, native.MethodDeleteObject, native.MethodPutObject, native.MethodHeadObject, native.MethodGetObject, native.MethodRangeObject}}, Actions: chain.Actions{Names: []string{native.MethodDeleteObject, native.MethodPutObject, native.MethodHeadObject, native.MethodGetObject, native.MethodRangeObject}},
Resources: chain.Resources{Names: []string{native.ResourceFormatAllObjects, native.ResourceFormatAllContainers}}, Resources: chain.Resources{Names: []string{native.ResourceFormatAllObjects}},
Condition: []chain.Condition{{ Condition: []chain.Condition{{
Op: chain.CondStringEquals, Op: chain.CondStringEquals,
Kind: chain.KindRequest, Kind: chain.KindRequest,
@ -1752,7 +1752,7 @@ func TestFromActions(t *testing.T) {
}, },
{ {
action: "s3:PutObject", action: "s3:PutObject",
res: []string{native.MethodGetContainer, native.MethodPutObject, res: []string{native.MethodPutObject,
native.MethodGetObject, native.MethodHeadObject, native.MethodRangeObject}, native.MethodGetObject, native.MethodHeadObject, native.MethodRangeObject},
}, },
{ {
@ -2095,7 +2095,7 @@ func TestMFACondition(t *testing.T) {
require.ElementsMatch(t, expectedConditions, s3Chain.Rules[0].Condition) require.ElementsMatch(t, expectedConditions, s3Chain.Rules[0].Condition)
_, err = ConvertToNativeChain(p, newMockUserResolver(nil, nil, "")) _, err = ConvertToNativeChain(p, newMockUserResolver(nil, nil, ""))
require.ErrorIs(t, err, s3common.ErrActionsNotApplicable) require.NoError(t, err)
} }
} }