diff --git a/iam/converter_native.go b/iam/converter_native.go index 293d966..05f90da 100644 --- a/iam/converter_native.go +++ b/iam/converter_native.go @@ -1,6 +1,7 @@ package iam import ( + "errors" "fmt" "strings" @@ -80,6 +81,8 @@ var objectNativeOperations = map[string]struct{}{ native.MethodHashObject: {}, } +var errConditionKeyNotApplicable = errors.New("condition key is not applicable") + type NativeResolver interface { GetUserKey(account, name string) (string, error) GetBucketInfo(bucket string) (*BucketInfo, error) @@ -123,6 +126,9 @@ func ConvertToNativeChain(p Policy, resolver NativeResolver) (*chain.Chain, erro groupedConditions, err := convertToNativeChainCondition(statement.Conditions, resolver) if err != nil { + if errors.Is(err, errConditionKeyNotApplicable) { + continue + } return nil, err } splitConditions := splitGroupedConditions(groupedConditions) @@ -219,6 +225,9 @@ func getNativePrincipalsAndConditionFunc(statement Statement, resolver NativeRes func convertToNativeChainCondition(c Conditions, resolver NativeResolver) ([]GroupedConditions, error) { return convertToChainConditions(c, func(gr GroupedConditions) (GroupedConditions, error) { for i := range gr.Conditions { + if gr.Conditions[i].Key == condKeyAWSMFAPresent { + return GroupedConditions{}, errConditionKeyNotApplicable + } if gr.Conditions[i].Key == condKeyAWSPrincipalARN { gr.Conditions[i].Key = native.PropertyKeyActorPublicKey val, err := formPrincipalKey(gr.Conditions[i].Value, resolver) diff --git a/iam/converter_s3.go b/iam/converter_s3.go index add55b3..d8774d0 100644 --- a/iam/converter_s3.go +++ b/iam/converter_s3.go @@ -7,6 +7,8 @@ import ( "git.frostfs.info/TrueCloudLab/policy-engine/schema/s3" ) +const condKeyAWSMFAPresent = "aws:MultiFactorAuthPresent" + var actionToS3OpMap = map[string][]string{ s3ActionAbortMultipartUpload: {s3ActionAbortMultipartUpload}, s3ActionCreateBucket: {s3ActionCreateBucket}, @@ -175,6 +177,9 @@ func convertToS3ChainCondition(c Conditions, resolver S3Resolver) ([]GroupedCond } gr.Conditions[i].Value = val } + if gr.Conditions[i].Key == condKeyAWSMFAPresent { + gr.Conditions[i].Key = s3.PropertyKeyAccessBoxAttrMFA + } } return gr, nil diff --git a/iam/converter_test.go b/iam/converter_test.go index aa7b889..7ad04b4 100644 --- a/iam/converter_test.go +++ b/iam/converter_test.go @@ -1732,6 +1732,80 @@ func TestTagsConditions(t *testing.T) { require.ElementsMatch(t, expectedConditions, nativeChain.Rules[0].Condition) } +func TestMFACondition(t *testing.T) { + policy := ` +{ + "Version": "2012-10-17", + "Statement": [ + { + "Effect": "Allow", + "Principal": "*", + "Action": "s3:PutObject", + "Resource": "*", + "Condition": { + "Bool": { + "aws:MultiFactorAuthPresent": "true" + } + } + } + ] +} +` + + expectedConditions := []chain.Condition{ + { + Op: chain.CondStringEqualsIgnoreCase, + Object: chain.ObjectRequest, + Key: s3.PropertyKeyAccessBoxAttrMFA, + Value: "true", + }, + } + + var p Policy + err := json.Unmarshal([]byte(policy), &p) + require.NoError(t, err) + + s3Chain, err := ConvertToS3Chain(p, newMockUserResolver(nil, nil, "")) + require.NoError(t, err) + require.Len(t, s3Chain.Rules, 1) + require.ElementsMatch(t, expectedConditions, s3Chain.Rules[0].Condition) + + _, err = ConvertToNativeChain(p, newMockUserResolver(nil, nil, "")) + require.ErrorIs(t, err, ErrActionsNotApplicable) + + policy = ` +{ + "Version": "2012-10-17", + "Statement": [ + { + "Effect": "Allow", + "Principal": "*", + "Action": "s3:GetObject", + "Resource": "*", + "Condition": { + "Bool": { + "aws:MultiFactorAuthPresent": "false" + } + } + } + ] +} +` + + expectedConditions[0].Value = "false" + + err = json.Unmarshal([]byte(policy), &p) + require.NoError(t, err) + + s3Chain, err = ConvertToS3Chain(p, newMockUserResolver(nil, nil, "")) + require.NoError(t, err) + require.Len(t, s3Chain.Rules, 1) + require.ElementsMatch(t, expectedConditions, s3Chain.Rules[0].Condition) + + _, err = ConvertToNativeChain(p, newMockUserResolver(nil, nil, "")) + require.ErrorIs(t, err, ErrActionsNotApplicable) +} + func requireChainRulesMatch(t *testing.T, expected, actual []chain.Rule) { require.Equal(t, len(expected), len(actual), "length of chain rules differ") diff --git a/schema/s3/consts.go b/schema/s3/consts.go index a374fa4..d90b7e8 100644 --- a/schema/s3/consts.go +++ b/schema/s3/consts.go @@ -11,6 +11,9 @@ const ( PropertyKeyFormatResourceTag = "aws:ResourceTag/%s" PropertyKeyFormatRequestTag = "aws:RequestTag/%s" + PropertyKeyAccessBoxAttrMFA = "AccessBox-Attribute/IAM-MFA" + PropertyKeyFormatAccessBoxAttr = "AccessBox-Attribute/%s" + ResourceFormatS3All = "arn:aws:s3:::*" ResourceFormatS3Bucket = "arn:aws:s3:::%s" ResourceFormatS3BucketObjects = "arn:aws:s3:::%s/*"