generated from TrueCloudLab/basic
[#70] iam: Support aws:MultiFactorAuthPresent key #70
4 changed files with 91 additions and 0 deletions
|
@ -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)
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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")
|
||||
|
||||
|
|
|
@ -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/*"
|
||||
|
|
Loading…
Reference in a new issue