From 2a0e4b6f5800a2a63e6c1efd6012c9de209c995c Mon Sep 17 00:00:00 2001 From: Harshavardhana Date: Fri, 28 Dec 2018 12:18:58 -0800 Subject: [PATCH] Add boolean function condition support (#7027) --- pkg/iam/policy/action.go | 141 ++++------------ pkg/policy/action.go | 141 +++------------- pkg/policy/condition/binaryequalsfunc.go | 4 +- pkg/policy/condition/binaryequalsfunc_test.go | 5 - pkg/policy/condition/boolfunc.go | 105 ++++++++++++ pkg/policy/condition/boolfunc_test.go | 152 ++++++++++++++++++ pkg/policy/condition/func.go | 1 + pkg/policy/condition/func_test.go | 14 +- pkg/policy/condition/key.go | 24 ++- pkg/policy/condition/key_test.go | 2 +- pkg/policy/condition/name.go | 30 ++-- pkg/policy/condition/stringequalsfunc.go | 4 +- pkg/policy/condition/stringequalsfunc_test.go | 10 -- .../stringequalsignorecasefunc_test.go | 10 -- pkg/policy/condition/stringlikefunc_test.go | 12 -- pkg/policy/statement_test.go | 4 +- 16 files changed, 364 insertions(+), 295 deletions(-) create mode 100644 pkg/policy/condition/boolfunc.go create mode 100644 pkg/policy/condition/boolfunc_test.go diff --git a/pkg/iam/policy/action.go b/pkg/iam/policy/action.go index 1dd30fb9c..df49739f3 100644 --- a/pkg/iam/policy/action.go +++ b/pkg/iam/policy/action.go @@ -176,133 +176,54 @@ func parseAction(s string) (Action, error) { var actionConditionKeyMap = map[Action]condition.KeySet{ AllActions: condition.NewKeySet(condition.AllSupportedKeys...), - AbortMultipartUploadAction: condition.NewKeySet( - condition.AWSReferer, - condition.AWSSourceIP, - condition.AWSUserAgent, - condition.AWSSecureTransport, - ), + AbortMultipartUploadAction: condition.NewKeySet(condition.CommonKeys...), - CreateBucketAction: condition.NewKeySet( - condition.AWSReferer, - condition.AWSSourceIP, - condition.AWSUserAgent, - condition.AWSSecureTransport, - ), + CreateBucketAction: condition.NewKeySet(condition.CommonKeys...), - DeleteBucketPolicyAction: condition.NewKeySet( - condition.AWSReferer, - condition.AWSSourceIP, - condition.AWSUserAgent, - condition.AWSSecureTransport, - ), + DeleteBucketPolicyAction: condition.NewKeySet(condition.CommonKeys...), - DeleteObjectAction: condition.NewKeySet( - condition.AWSReferer, - condition.AWSSourceIP, - condition.AWSUserAgent, - condition.AWSSecureTransport, - ), + DeleteObjectAction: condition.NewKeySet(condition.CommonKeys...), - GetBucketLocationAction: condition.NewKeySet( - condition.AWSReferer, - condition.AWSSourceIP, - condition.AWSUserAgent, - condition.AWSSecureTransport, - ), + GetBucketLocationAction: condition.NewKeySet(condition.CommonKeys...), - GetBucketNotificationAction: condition.NewKeySet( - condition.AWSReferer, - condition.AWSSourceIP, - condition.AWSUserAgent, - condition.AWSSecureTransport, - ), + GetBucketNotificationAction: condition.NewKeySet(condition.CommonKeys...), - GetBucketPolicyAction: condition.NewKeySet( - condition.AWSReferer, - condition.AWSSourceIP, - condition.AWSUserAgent, - condition.AWSSecureTransport, - ), + GetBucketPolicyAction: condition.NewKeySet(condition.CommonKeys...), GetObjectAction: condition.NewKeySet( - condition.S3XAmzServerSideEncryption, - condition.S3XAmzServerSideEncryptionAwsKMSKeyID, - condition.S3XAmzStorageClass, - condition.AWSReferer, - condition.AWSSourceIP, - condition.AWSUserAgent, - condition.AWSSecureTransport, - ), + append([]condition.Key{ + condition.S3XAmzServerSideEncryption, + condition.S3XAmzServerSideEncryptionCustomerAlgorithm, + condition.S3XAmzStorageClass, + }, condition.CommonKeys...)...), - HeadBucketAction: condition.NewKeySet( - condition.AWSReferer, - condition.AWSSourceIP, - condition.AWSUserAgent, - condition.AWSSecureTransport, - ), + HeadBucketAction: condition.NewKeySet(condition.CommonKeys...), - ListAllMyBucketsAction: condition.NewKeySet( - condition.AWSReferer, - condition.AWSSourceIP, - condition.AWSUserAgent, - condition.AWSSecureTransport, - ), + ListAllMyBucketsAction: condition.NewKeySet(condition.CommonKeys...), ListBucketAction: condition.NewKeySet( - condition.S3Prefix, - condition.S3Delimiter, - condition.S3MaxKeys, - condition.AWSReferer, - condition.AWSSourceIP, - condition.AWSUserAgent, - condition.AWSSecureTransport, - ), + append([]condition.Key{ + condition.S3Prefix, + condition.S3Delimiter, + condition.S3MaxKeys, + }, condition.CommonKeys...)...), - ListBucketMultipartUploadsAction: condition.NewKeySet( - condition.AWSReferer, - condition.AWSSourceIP, - condition.AWSUserAgent, - condition.AWSSecureTransport, - ), + ListBucketMultipartUploadsAction: condition.NewKeySet(condition.CommonKeys...), - ListenBucketNotificationAction: condition.NewKeySet( - condition.AWSReferer, - condition.AWSSourceIP, - condition.AWSUserAgent, - condition.AWSSecureTransport, - ), + ListenBucketNotificationAction: condition.NewKeySet(condition.CommonKeys...), - ListMultipartUploadPartsAction: condition.NewKeySet( - condition.AWSReferer, - condition.AWSSourceIP, - condition.AWSUserAgent, - condition.AWSSecureTransport, - ), + ListMultipartUploadPartsAction: condition.NewKeySet(condition.CommonKeys...), - PutBucketNotificationAction: condition.NewKeySet( - condition.AWSReferer, - condition.AWSSourceIP, - condition.AWSUserAgent, - condition.AWSSecureTransport, - ), + PutBucketNotificationAction: condition.NewKeySet(condition.CommonKeys...), - PutBucketPolicyAction: condition.NewKeySet( - condition.AWSReferer, - condition.AWSSourceIP, - condition.AWSUserAgent, - condition.AWSSecureTransport, - ), + PutBucketPolicyAction: condition.NewKeySet(condition.CommonKeys...), PutObjectAction: condition.NewKeySet( - condition.S3XAmzCopySource, - condition.S3XAmzServerSideEncryption, - condition.S3XAmzServerSideEncryptionAwsKMSKeyID, - condition.S3XAmzMetadataDirective, - condition.S3XAmzStorageClass, - condition.AWSReferer, - condition.AWSSourceIP, - condition.AWSUserAgent, - condition.AWSSecureTransport, - ), + append([]condition.Key{ + condition.S3XAmzCopySource, + condition.S3XAmzServerSideEncryption, + condition.S3XAmzServerSideEncryptionCustomerAlgorithm, + condition.S3XAmzMetadataDirective, + condition.S3XAmzStorageClass, + }, condition.CommonKeys...)...), } diff --git a/pkg/policy/action.go b/pkg/policy/action.go index c95010224..bb41a97d4 100644 --- a/pkg/policy/action.go +++ b/pkg/policy/action.go @@ -158,133 +158,42 @@ func parseAction(s string) (Action, error) { // actionConditionKeyMap - holds mapping of supported condition key for an action. var actionConditionKeyMap = map[Action]condition.KeySet{ - AbortMultipartUploadAction: condition.NewKeySet( - condition.AWSReferer, - condition.AWSSourceIP, - condition.AWSUserAgent, - condition.AWSSecureTransport, - ), + AbortMultipartUploadAction: condition.NewKeySet(condition.CommonKeys...), - CreateBucketAction: condition.NewKeySet( - condition.AWSReferer, - condition.AWSSourceIP, - condition.AWSUserAgent, - condition.AWSSecureTransport, - ), + CreateBucketAction: condition.NewKeySet(condition.CommonKeys...), - DeleteBucketPolicyAction: condition.NewKeySet( - condition.AWSReferer, - condition.AWSSourceIP, - condition.AWSUserAgent, - condition.AWSSecureTransport, - ), + DeleteObjectAction: condition.NewKeySet(condition.CommonKeys...), - DeleteObjectAction: condition.NewKeySet( - condition.AWSReferer, - condition.AWSSourceIP, - condition.AWSUserAgent, - condition.AWSSecureTransport, - ), - - GetBucketLocationAction: condition.NewKeySet( - condition.AWSReferer, - condition.AWSSourceIP, - condition.AWSUserAgent, - condition.AWSSecureTransport, - ), - - GetBucketNotificationAction: condition.NewKeySet( - condition.AWSReferer, - condition.AWSSourceIP, - condition.AWSUserAgent, - condition.AWSSecureTransport, - ), - - GetBucketPolicyAction: condition.NewKeySet( - condition.AWSReferer, - condition.AWSSourceIP, - condition.AWSUserAgent, - condition.AWSSecureTransport, - ), + GetBucketLocationAction: condition.NewKeySet(condition.CommonKeys...), GetObjectAction: condition.NewKeySet( - condition.S3XAmzServerSideEncryption, - condition.S3XAmzServerSideEncryptionAwsKMSKeyID, - condition.S3XAmzStorageClass, - condition.AWSReferer, - condition.AWSSourceIP, - condition.AWSUserAgent, - condition.AWSSecureTransport, - ), + append([]condition.Key{ + condition.S3XAmzServerSideEncryption, + condition.S3XAmzServerSideEncryptionCustomerAlgorithm, + condition.S3XAmzStorageClass, + }, condition.CommonKeys...)...), - HeadBucketAction: condition.NewKeySet( - condition.AWSReferer, - condition.AWSSourceIP, - condition.AWSUserAgent, - condition.AWSSecureTransport, - ), + HeadBucketAction: condition.NewKeySet(condition.CommonKeys...), - ListAllMyBucketsAction: condition.NewKeySet( - condition.AWSReferer, - condition.AWSSourceIP, - condition.AWSUserAgent, - condition.AWSSecureTransport, - ), + ListAllMyBucketsAction: condition.NewKeySet(condition.CommonKeys...), ListBucketAction: condition.NewKeySet( - condition.S3Prefix, - condition.S3Delimiter, - condition.S3MaxKeys, - condition.AWSReferer, - condition.AWSSourceIP, - condition.AWSUserAgent, - condition.AWSSecureTransport, - ), + append([]condition.Key{ + condition.S3Prefix, + condition.S3Delimiter, + condition.S3MaxKeys, + }, condition.CommonKeys...)...), - ListBucketMultipartUploadsAction: condition.NewKeySet( - condition.AWSReferer, - condition.AWSSourceIP, - condition.AWSUserAgent, - condition.AWSSecureTransport, - ), + ListBucketMultipartUploadsAction: condition.NewKeySet(condition.CommonKeys...), - ListenBucketNotificationAction: condition.NewKeySet( - condition.AWSReferer, - condition.AWSSourceIP, - condition.AWSUserAgent, - condition.AWSSecureTransport, - ), - - ListMultipartUploadPartsAction: condition.NewKeySet( - condition.AWSReferer, - condition.AWSSourceIP, - condition.AWSUserAgent, - condition.AWSSecureTransport, - ), - - PutBucketNotificationAction: condition.NewKeySet( - condition.AWSReferer, - condition.AWSSourceIP, - condition.AWSUserAgent, - condition.AWSSecureTransport, - ), - - PutBucketPolicyAction: condition.NewKeySet( - condition.AWSReferer, - condition.AWSSourceIP, - condition.AWSUserAgent, - condition.AWSSecureTransport, - ), + ListMultipartUploadPartsAction: condition.NewKeySet(condition.CommonKeys...), PutObjectAction: condition.NewKeySet( - condition.S3XAmzCopySource, - condition.S3XAmzServerSideEncryption, - condition.S3XAmzServerSideEncryptionAwsKMSKeyID, - condition.S3XAmzMetadataDirective, - condition.S3XAmzStorageClass, - condition.AWSReferer, - condition.AWSSourceIP, - condition.AWSUserAgent, - condition.AWSSecureTransport, - ), + append([]condition.Key{ + condition.S3XAmzCopySource, + condition.S3XAmzServerSideEncryption, + condition.S3XAmzServerSideEncryptionCustomerAlgorithm, + condition.S3XAmzMetadataDirective, + condition.S3XAmzStorageClass, + }, condition.CommonKeys...)...), } diff --git a/pkg/policy/condition/binaryequalsfunc.go b/pkg/policy/condition/binaryequalsfunc.go index 89239ae13..9f74c5b09 100644 --- a/pkg/policy/condition/binaryequalsfunc.go +++ b/pkg/policy/condition/binaryequalsfunc.go @@ -102,8 +102,8 @@ func validateBinaryEqualsValues(n name, key Key, values set.StringSet) error { if err = s3utils.CheckValidBucketName(bucket); err != nil { return err } - case S3XAmzServerSideEncryption: - if s != "aws:kms" && s != "AES256" { + case S3XAmzServerSideEncryption, S3XAmzServerSideEncryptionCustomerAlgorithm: + if s != "AES256" { return fmt.Errorf("invalid value '%v' for '%v' for %v condition", s, S3XAmzServerSideEncryption, n) } case S3XAmzMetadataDirective: diff --git a/pkg/policy/condition/binaryequalsfunc_test.go b/pkg/policy/condition/binaryequalsfunc_test.go index beb6250d8..8a925bb1a 100644 --- a/pkg/policy/condition/binaryequalsfunc_test.go +++ b/pkg/policy/condition/binaryequalsfunc_test.go @@ -58,7 +58,6 @@ func TestBinaryEqualsFuncEvaluate(t *testing.T) { {case1Function, map[string][]string{"delimiter": {"/"}}, false}, {case2Function, map[string][]string{"x-amz-server-side-encryption": {"AES256"}}, true}, - {case2Function, map[string][]string{"x-amz-server-side-encryption": {"aws:kms"}}, false}, {case2Function, map[string][]string{}, false}, {case2Function, map[string][]string{"delimiter": {"/"}}, false}, @@ -167,7 +166,6 @@ func TestBinaryEqualsFuncToMap(t *testing.T) { case4Function, err := newBinaryEqualsFunc(S3XAmzServerSideEncryption, NewValueSet( NewStringValue(base64.StdEncoding.EncodeToString([]byte("AES256"))), - NewStringValue(base64.StdEncoding.EncodeToString([]byte("aws:kms"))), ), ) if err != nil { @@ -177,7 +175,6 @@ func TestBinaryEqualsFuncToMap(t *testing.T) { case4Result := map[Key]ValueSet{ S3XAmzServerSideEncryption: NewValueSet( NewStringValue(base64.StdEncoding.EncodeToString([]byte("AES256"))), - NewStringValue(base64.StdEncoding.EncodeToString([]byte("aws:kms"))), ), } @@ -285,7 +282,6 @@ func TestNewBinaryEqualsFunc(t *testing.T) { case4Function, err := newBinaryEqualsFunc(S3XAmzServerSideEncryption, NewValueSet( NewStringValue(base64.StdEncoding.EncodeToString([]byte("AES256"))), - NewStringValue(base64.StdEncoding.EncodeToString([]byte("aws:kms"))), ), ) if err != nil { @@ -341,7 +337,6 @@ func TestNewBinaryEqualsFunc(t *testing.T) { {S3XAmzServerSideEncryption, NewValueSet( NewStringValue(base64.StdEncoding.EncodeToString([]byte("AES256"))), - NewStringValue(base64.StdEncoding.EncodeToString([]byte("aws:kms"))), ), case4Function, false}, {S3XAmzMetadataDirective, NewValueSet(NewStringValue(base64.StdEncoding.EncodeToString([]byte("REPLACE")))), case5Function, false}, diff --git a/pkg/policy/condition/boolfunc.go b/pkg/policy/condition/boolfunc.go new file mode 100644 index 000000000..9253e0252 --- /dev/null +++ b/pkg/policy/condition/boolfunc.go @@ -0,0 +1,105 @@ +/* + * Minio Cloud Storage, (C) 2018 Minio, Inc. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package condition + +import ( + "fmt" + "net/http" + "reflect" + "strconv" +) + +// booleanFunc - Bool condition function. It checks whether Key is true or false. +// https://docs.aws.amazon.com/IAM/latest/UserGuide/reference_policies_elements_condition_operators.html#Conditions_Boolean +type booleanFunc struct { + k Key + value string +} + +// evaluate() - evaluates to check whether Key is present in given values or not. +// Depending on condition boolean value, this function returns true or false. +func (f booleanFunc) evaluate(values map[string][]string) bool { + requestValue, ok := values[http.CanonicalHeaderKey(f.k.Name())] + if !ok { + requestValue = values[f.k.Name()] + } + + return f.value == requestValue[0] +} + +// key() - returns condition key which is used by this condition function. +func (f booleanFunc) key() Key { + return f.k +} + +// name() - returns "Bool" condition name. +func (f booleanFunc) name() name { + return boolean +} + +func (f booleanFunc) String() string { + return fmt.Sprintf("%v:%v:%v", boolean, f.k, f.value) +} + +// toMap - returns map representation of this function. +func (f booleanFunc) toMap() map[Key]ValueSet { + if !f.k.IsValid() { + return nil + } + + return map[Key]ValueSet{ + f.k: NewValueSet(NewStringValue(f.value)), + } +} + +func newBooleanFunc(key Key, values ValueSet) (Function, error) { + if key != AWSSecureTransport { + return nil, fmt.Errorf("only %v key is allowed for %v condition", AWSSecureTransport, boolean) + } + + if len(values) != 1 { + return nil, fmt.Errorf("only one value is allowed for boolean condition") + } + + var value Value + for v := range values { + value = v + switch v.GetType() { + case reflect.Bool: + if _, err := v.GetBool(); err != nil { + return nil, err + } + case reflect.String: + s, err := v.GetString() + if err != nil { + return nil, err + } + if _, err = strconv.ParseBool(s); err != nil { + return nil, fmt.Errorf("value must be a boolean string for boolean condition") + } + default: + return nil, fmt.Errorf("value must be a boolean for boolean condition") + } + } + + return &booleanFunc{key, value.String()}, nil +} + +// NewBoolFunc - returns new Bool function. +func NewBoolFunc(key Key, value string) (Function, error) { + return &booleanFunc{key, value}, nil +} diff --git a/pkg/policy/condition/boolfunc_test.go b/pkg/policy/condition/boolfunc_test.go new file mode 100644 index 000000000..01c49d9e0 --- /dev/null +++ b/pkg/policy/condition/boolfunc_test.go @@ -0,0 +1,152 @@ +/* + * Minio Cloud Storage, (C) 2018 Minio, Inc. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package condition + +import ( + "reflect" + "testing" +) + +func TestBooleanFuncEvaluate(t *testing.T) { + case1Function, err := newBooleanFunc(AWSSecureTransport, NewValueSet(NewBoolValue(true))) + if err != nil { + t.Fatalf("unexpected error. %v\n", err) + } + + case2Function, err := newBooleanFunc(AWSSecureTransport, NewValueSet(NewBoolValue(false))) + if err != nil { + t.Fatalf("unexpected error. %v\n", err) + } + + testCases := []struct { + function Function + values map[string][]string + expectedResult bool + }{ + {case1Function, map[string][]string{"SecureTransport": {"true"}}, true}, + {case2Function, map[string][]string{"SecureTransport": {"false"}}, true}, + } + + for i, testCase := range testCases { + result := testCase.function.evaluate(testCase.values) + + if result != testCase.expectedResult { + t.Errorf("case %v: expected: %v, got: %v\n", i+1, testCase.expectedResult, result) + } + } +} + +func TestBooleanFuncKey(t *testing.T) { + case1Function, err := newBooleanFunc(AWSSecureTransport, NewValueSet(NewBoolValue(true))) + if err != nil { + t.Fatalf("unexpected error. %v\n", err) + } + + testCases := []struct { + function Function + expectedResult Key + }{ + {case1Function, AWSSecureTransport}, + } + + for i, testCase := range testCases { + result := testCase.function.key() + + if result != testCase.expectedResult { + t.Fatalf("case %v: expected: %v, got: %v\n", i+1, testCase.expectedResult, result) + } + } +} + +func TestBooleanFuncToMap(t *testing.T) { + case1Function, err := newBooleanFunc(AWSSecureTransport, NewValueSet(NewBoolValue(true))) + if err != nil { + t.Fatalf("unexpected error. %v\n", err) + } + + case1Result := map[Key]ValueSet{ + AWSSecureTransport: NewValueSet(NewStringValue("true")), + } + + case2Function, err := newBooleanFunc(AWSSecureTransport, NewValueSet(NewBoolValue(false))) + if err != nil { + t.Fatalf("unexpected error. %v\n", err) + } + + case2Result := map[Key]ValueSet{ + AWSSecureTransport: NewValueSet(NewStringValue("false")), + } + + testCases := []struct { + f Function + expectedResult map[Key]ValueSet + }{ + {case1Function, case1Result}, + {case2Function, case2Result}, + } + + for i, testCase := range testCases { + result := testCase.f.toMap() + + if !reflect.DeepEqual(result, testCase.expectedResult) { + t.Fatalf("case %v: result: expected: %v, got: %v\n", i+1, testCase.expectedResult, result) + } + } +} + +func TestNewBooleanFunc(t *testing.T) { + case1Function, err := newBooleanFunc(AWSSecureTransport, NewValueSet(NewBoolValue(true))) + if err != nil { + t.Fatalf("unexpected error. %v\n", err) + } + + case2Function, err := newBooleanFunc(AWSSecureTransport, NewValueSet(NewBoolValue(false))) + if err != nil { + t.Fatalf("unexpected error. %v\n", err) + } + + testCases := []struct { + key Key + values ValueSet + expectedResult Function + expectErr bool + }{ + {AWSSecureTransport, NewValueSet(NewBoolValue(true)), case1Function, false}, + {AWSSecureTransport, NewValueSet(NewStringValue("false")), case2Function, false}, + // Multiple values error. + {AWSSecureTransport, NewValueSet(NewStringValue("true"), NewStringValue("false")), nil, true}, + // Invalid boolean string error. + {AWSSecureTransport, NewValueSet(NewStringValue("foo")), nil, true}, + // Invalid value error. + {AWSSecureTransport, NewValueSet(NewIntValue(7)), nil, true}, + } + + for i, testCase := range testCases { + result, err := newBooleanFunc(testCase.key, testCase.values) + expectErr := (err != nil) + + if expectErr != testCase.expectErr { + t.Fatalf("case %v: error: expected: %v, got: %v\n", i+1, testCase.expectErr, expectErr) + } + + if !testCase.expectErr { + if !reflect.DeepEqual(result, testCase.expectedResult) { + t.Fatalf("case %v: result: expected: %v, got: %v\n", i+1, testCase.expectedResult, result) + } + } + } +} diff --git a/pkg/policy/condition/func.go b/pkg/policy/condition/func.go index b525b74fa..9ae7d311e 100644 --- a/pkg/policy/condition/func.go +++ b/pkg/policy/condition/func.go @@ -99,6 +99,7 @@ var conditionFuncMap = map[name]func(Key, ValueSet) (Function, error){ ipAddress: newIPAddressFunc, notIPAddress: newNotIPAddressFunc, null: newNullFunc, + boolean: newBooleanFunc, // Add new conditions here. } diff --git a/pkg/policy/condition/func_test.go b/pkg/policy/condition/func_test.go index 3a9121f3f..a2ba9c47f 100644 --- a/pkg/policy/condition/func_test.go +++ b/pkg/policy/condition/func_test.go @@ -148,7 +148,7 @@ func TestFunctionsMarshalJSON(t *testing.T) { t.Fatalf("unexpected error. %v\n", err) } - func6, err := newNullFunc(S3XAmzServerSideEncryptionAwsKMSKeyID, NewValueSet(NewBoolValue(true))) + func6, err := newNullFunc(S3XAmzServerSideEncryptionCustomerAlgorithm, NewValueSet(NewBoolValue(true))) if err != nil { t.Fatalf("unexpected error. %v\n", err) } @@ -159,9 +159,9 @@ func TestFunctionsMarshalJSON(t *testing.T) { t.Fatalf("unexpected error. %v\n", err) } - case1Result := []byte(`{"IpAddress":{"aws:SourceIp":["192.168.1.0/24"]},"NotIpAddress":{"aws:SourceIp":["10.1.10.0/24"]},"Null":{"s3:x-amz-server-side-encryption-aws-kms-key-id":[true]},"StringEquals":{"s3:x-amz-copy-source":["mybucket/myobject"]},"StringLike":{"s3:x-amz-metadata-directive":["REPL*"]},"StringNotEquals":{"s3:x-amz-server-side-encryption":["AES256"]},"StringNotLike":{"s3:x-amz-storage-class":["STANDARD"]}}`) + case1Result := []byte(`{"IpAddress":{"aws:SourceIp":["192.168.1.0/24"]},"NotIpAddress":{"aws:SourceIp":["10.1.10.0/24"]},"Null":{"s3:x-amz-server-side-encryption-customer-algorithm":[true]},"StringEquals":{"s3:x-amz-copy-source":["mybucket/myobject"]},"StringLike":{"s3:x-amz-metadata-directive":["REPL*"]},"StringNotEquals":{"s3:x-amz-server-side-encryption":["AES256"]},"StringNotLike":{"s3:x-amz-storage-class":["STANDARD"]}}`) - case2Result := []byte(`{"Null":{"s3:x-amz-server-side-encryption-aws-kms-key-id":[true]}}`) + case2Result := []byte(`{"Null":{"s3:x-amz-server-side-encryption-customer-algorithm":[true]}}`) testCases := []struct { functions Functions @@ -211,7 +211,7 @@ func TestFunctionsUnmarshalJSON(t *testing.T) { "s3:x-amz-storage-class": "STANDARD" }, "Null": { - "s3:x-amz-server-side-encryption-aws-kms-key-id": true + "s3:x-amz-server-side-encryption-customer-algorithm": true }, "IpAddress": { "aws:SourceIp": [ @@ -246,7 +246,7 @@ func TestFunctionsUnmarshalJSON(t *testing.T) { t.Fatalf("unexpected error. %v\n", err) } - func6, err := newNullFunc(S3XAmzServerSideEncryptionAwsKMSKeyID, NewValueSet(NewBoolValue(true))) + func6, err := newNullFunc(S3XAmzServerSideEncryptionCustomerAlgorithm, NewValueSet(NewBoolValue(true))) if err != nil { t.Fatalf("unexpected error. %v\n", err) } @@ -259,10 +259,10 @@ func TestFunctionsUnmarshalJSON(t *testing.T) { case2Data := []byte(`{ "Null": { - "s3:x-amz-server-side-encryption-aws-kms-key-id": true + "s3:x-amz-server-side-encryption-customer-algorithm": true }, "Null": { - "s3:x-amz-server-side-encryption-aws-kms-key-id": "true" + "s3:x-amz-server-side-encryption-customer-algorithm": "true" } }`) diff --git a/pkg/policy/condition/key.go b/pkg/policy/condition/key.go index d57ddd620..83acb86af 100644 --- a/pkg/policy/condition/key.go +++ b/pkg/policy/condition/key.go @@ -35,10 +35,6 @@ const ( // to PutObject API only. S3XAmzServerSideEncryption = "s3:x-amz-server-side-encryption" - // S3XAmzServerSideEncryptionAwsKMSKeyID - key representing x-amz-server-side-encryption-aws-kms-key-id - // HTTP header applicable to PutObject API only. - S3XAmzServerSideEncryptionAwsKMSKeyID = "s3:x-amz-server-side-encryption-aws-kms-key-id" - // S3XAmzServerSideEncryptionCustomerAlgorithm - key representing // x-amz-server-side-encryption-customer-algorithm HTTP header applicable to PutObject API only. S3XAmzServerSideEncryptionCustomerAlgorithm = "s3:x-amz-server-side-encryption-customer-algorithm" @@ -74,13 +70,19 @@ const ( // AWSSecureTransport - key representing if the clients request is authenticated or not. AWSSecureTransport = "aws:SecureTransport" + + // AWSCurrentTime - key representing the current time. + AWSCurrentTime = "aws:CurrentTime" + + // AWSEpochTime - key representing the current epoch time. + AWSEpochTime = "aws:EpochTime" ) // AllSupportedKeys - is list of all all supported keys. var AllSupportedKeys = []Key{ S3XAmzCopySource, S3XAmzServerSideEncryption, - S3XAmzServerSideEncryptionAwsKMSKeyID, + S3XAmzServerSideEncryptionCustomerAlgorithm, S3XAmzMetadataDirective, S3XAmzStorageClass, S3LocationConstraint, @@ -91,9 +93,21 @@ var AllSupportedKeys = []Key{ AWSSourceIP, AWSUserAgent, AWSSecureTransport, + AWSCurrentTime, + AWSEpochTime, // Add new supported condition keys. } +// CommonKeys - is list of all common condition keys. +var CommonKeys = []Key{ + AWSReferer, + AWSSourceIP, + AWSUserAgent, + AWSSecureTransport, + AWSCurrentTime, + AWSEpochTime, +} + // IsValid - checks if key is valid or not. func (key Key) IsValid() bool { for _, supKey := range AllSupportedKeys { diff --git a/pkg/policy/condition/key_test.go b/pkg/policy/condition/key_test.go index b1ba03413..bafae0472 100644 --- a/pkg/policy/condition/key_test.go +++ b/pkg/policy/condition/key_test.go @@ -29,7 +29,7 @@ func TestKeyIsValid(t *testing.T) { }{ {S3XAmzCopySource, true}, {S3XAmzServerSideEncryption, true}, - {S3XAmzServerSideEncryptionAwsKMSKeyID, true}, + {S3XAmzServerSideEncryptionCustomerAlgorithm, true}, {S3XAmzMetadataDirective, true}, {S3XAmzStorageClass, true}, {S3LocationConstraint, true}, diff --git a/pkg/policy/condition/name.go b/pkg/policy/condition/name.go index 7d7a6a8d3..7baa78d16 100644 --- a/pkg/policy/condition/name.go +++ b/pkg/policy/condition/name.go @@ -34,23 +34,27 @@ const ( ipAddress = "IpAddress" notIPAddress = "NotIpAddress" null = "Null" + boolean = "Bool" ) +var supportedConditions = []name{ + stringEquals, + stringNotEquals, + stringEqualsIgnoreCase, + stringNotEqualsIgnoreCase, + binaryEquals, + stringLike, + stringNotLike, + ipAddress, + notIPAddress, + null, + boolean, + // Add new conditions here. +} + // IsValid - checks if name is valid or not. func (n name) IsValid() bool { - for _, supn := range []name{ - stringEquals, - stringNotEquals, - stringEqualsIgnoreCase, - stringNotEqualsIgnoreCase, - binaryEquals, - stringLike, - stringNotLike, - ipAddress, - notIPAddress, - null, - // Add new conditions here. - } { + for _, supn := range supportedConditions { if n == supn { return true } diff --git a/pkg/policy/condition/stringequalsfunc.go b/pkg/policy/condition/stringequalsfunc.go index b988917ec..795a0fe19 100644 --- a/pkg/policy/condition/stringequalsfunc.go +++ b/pkg/policy/condition/stringequalsfunc.go @@ -134,8 +134,8 @@ func validateStringEqualsValues(n name, key Key, values set.StringSet) error { if err := s3utils.CheckValidBucketName(bucket); err != nil { return err } - case S3XAmzServerSideEncryption: - if s != "aws:kms" && s != "AES256" { + case S3XAmzServerSideEncryption, S3XAmzServerSideEncryptionCustomerAlgorithm: + if s != "AES256" { return fmt.Errorf("invalid value '%v' for '%v' for %v condition", s, S3XAmzServerSideEncryption, n) } case S3XAmzMetadataDirective: diff --git a/pkg/policy/condition/stringequalsfunc_test.go b/pkg/policy/condition/stringequalsfunc_test.go index 2344bb34d..ad6e46f70 100644 --- a/pkg/policy/condition/stringequalsfunc_test.go +++ b/pkg/policy/condition/stringequalsfunc_test.go @@ -53,7 +53,6 @@ func TestStringEqualsFuncEvaluate(t *testing.T) { {case1Function, map[string][]string{"delimiter": {"/"}}, false}, {case2Function, map[string][]string{"x-amz-server-side-encryption": {"AES256"}}, true}, - {case2Function, map[string][]string{"x-amz-server-side-encryption": {"aws:kms"}}, false}, {case2Function, map[string][]string{}, false}, {case2Function, map[string][]string{"delimiter": {"/"}}, false}, @@ -156,7 +155,6 @@ func TestStringEqualsFuncToMap(t *testing.T) { case4Function, err := newStringEqualsFunc(S3XAmzServerSideEncryption, NewValueSet( NewStringValue("AES256"), - NewStringValue("aws:kms"), ), ) if err != nil { @@ -166,7 +164,6 @@ func TestStringEqualsFuncToMap(t *testing.T) { case4Result := map[Key]ValueSet{ S3XAmzServerSideEncryption: NewValueSet( NewStringValue("AES256"), - NewStringValue("aws:kms"), ), } @@ -278,7 +275,6 @@ func TestStringNotEqualsFuncEvaluate(t *testing.T) { {case1Function, map[string][]string{"delimiter": {"/"}}, true}, {case2Function, map[string][]string{"x-amz-server-side-encryption": {"AES256"}}, false}, - {case2Function, map[string][]string{"x-amz-server-side-encryption": {"aws:kms"}}, true}, {case2Function, map[string][]string{}, true}, {case2Function, map[string][]string{"delimiter": {"/"}}, true}, @@ -381,7 +377,6 @@ func TestStringNotEqualsFuncToMap(t *testing.T) { case4Function, err := newStringNotEqualsFunc(S3XAmzServerSideEncryption, NewValueSet( NewStringValue("AES256"), - NewStringValue("aws:kms"), ), ) if err != nil { @@ -391,7 +386,6 @@ func TestStringNotEqualsFuncToMap(t *testing.T) { case4Result := map[Key]ValueSet{ S3XAmzServerSideEncryption: NewValueSet( NewStringValue("AES256"), - NewStringValue("aws:kms"), ), } @@ -495,7 +489,6 @@ func TestNewStringEqualsFunc(t *testing.T) { case4Function, err := newStringEqualsFunc(S3XAmzServerSideEncryption, NewValueSet( NewStringValue("AES256"), - NewStringValue("aws:kms"), ), ) if err != nil { @@ -549,7 +542,6 @@ func TestNewStringEqualsFunc(t *testing.T) { {S3XAmzServerSideEncryption, NewValueSet( NewStringValue("AES256"), - NewStringValue("aws:kms"), ), case4Function, false}, {S3XAmzMetadataDirective, NewValueSet(NewStringValue("REPLACE")), case5Function, false}, @@ -618,7 +610,6 @@ func TestNewStringNotEqualsFunc(t *testing.T) { case4Function, err := newStringNotEqualsFunc(S3XAmzServerSideEncryption, NewValueSet( NewStringValue("AES256"), - NewStringValue("aws:kms"), ), ) if err != nil { @@ -672,7 +663,6 @@ func TestNewStringNotEqualsFunc(t *testing.T) { {S3XAmzServerSideEncryption, NewValueSet( NewStringValue("AES256"), - NewStringValue("aws:kms"), ), case4Function, false}, {S3XAmzMetadataDirective, NewValueSet(NewStringValue("REPLACE")), case5Function, false}, diff --git a/pkg/policy/condition/stringequalsignorecasefunc_test.go b/pkg/policy/condition/stringequalsignorecasefunc_test.go index c28fd4aee..744170655 100644 --- a/pkg/policy/condition/stringequalsignorecasefunc_test.go +++ b/pkg/policy/condition/stringequalsignorecasefunc_test.go @@ -54,7 +54,6 @@ func TestStringEqualsIgnoreCaseFuncEvaluate(t *testing.T) { {case2Function, map[string][]string{"x-amz-server-side-encryption": {"AES256"}}, true}, {case2Function, map[string][]string{"x-amz-server-side-encryption": {"aes256"}}, true}, - {case2Function, map[string][]string{"x-amz-server-side-encryption": {"aws:kms"}}, false}, {case2Function, map[string][]string{}, false}, {case2Function, map[string][]string{"delimiter": {"/"}}, false}, @@ -158,7 +157,6 @@ func TestStringEqualsIgnoreCaseFuncToMap(t *testing.T) { case4Function, err := newStringEqualsIgnoreCaseFunc(S3XAmzServerSideEncryption, NewValueSet( NewStringValue("AES256"), - NewStringValue("aws:kms"), ), ) if err != nil { @@ -168,7 +166,6 @@ func TestStringEqualsIgnoreCaseFuncToMap(t *testing.T) { case4Result := map[Key]ValueSet{ S3XAmzServerSideEncryption: NewValueSet( NewStringValue("AES256"), - NewStringValue("aws:kms"), ), } @@ -280,7 +277,6 @@ func TestStringNotEqualsIgnoreCaseFuncEvaluate(t *testing.T) { {case1Function, map[string][]string{"delimiter": {"/"}}, true}, {case2Function, map[string][]string{"x-amz-server-side-encryption": {"AES256"}}, false}, - {case2Function, map[string][]string{"x-amz-server-side-encryption": {"aws:kms"}}, true}, {case2Function, map[string][]string{}, true}, {case2Function, map[string][]string{"delimiter": {"/"}}, true}, @@ -383,7 +379,6 @@ func TestStringNotEqualsIgnoreCaseFuncToMap(t *testing.T) { case4Function, err := newStringNotEqualsIgnoreCaseFunc(S3XAmzServerSideEncryption, NewValueSet( NewStringValue("AES256"), - NewStringValue("aws:kms"), ), ) if err != nil { @@ -393,7 +388,6 @@ func TestStringNotEqualsIgnoreCaseFuncToMap(t *testing.T) { case4Result := map[Key]ValueSet{ S3XAmzServerSideEncryption: NewValueSet( NewStringValue("AES256"), - NewStringValue("aws:kms"), ), } @@ -497,7 +491,6 @@ func TestNewStringEqualsIgnoreCaseFunc(t *testing.T) { case4Function, err := newStringEqualsIgnoreCaseFunc(S3XAmzServerSideEncryption, NewValueSet( NewStringValue("AES256"), - NewStringValue("aws:kms"), ), ) if err != nil { @@ -551,7 +544,6 @@ func TestNewStringEqualsIgnoreCaseFunc(t *testing.T) { {S3XAmzServerSideEncryption, NewValueSet( NewStringValue("AES256"), - NewStringValue("aws:kms"), ), case4Function, false}, {S3XAmzMetadataDirective, NewValueSet(NewStringValue("REPLACE")), case5Function, false}, @@ -620,7 +612,6 @@ func TestNewStringNotEqualsIgnoreCaseFunc(t *testing.T) { case4Function, err := newStringNotEqualsIgnoreCaseFunc(S3XAmzServerSideEncryption, NewValueSet( NewStringValue("AES256"), - NewStringValue("aws:kms"), ), ) if err != nil { @@ -674,7 +665,6 @@ func TestNewStringNotEqualsIgnoreCaseFunc(t *testing.T) { {S3XAmzServerSideEncryption, NewValueSet( NewStringValue("AES256"), - NewStringValue("aws:kms"), ), case4Function, false}, {S3XAmzMetadataDirective, NewValueSet(NewStringValue("REPLACE")), case5Function, false}, diff --git a/pkg/policy/condition/stringlikefunc_test.go b/pkg/policy/condition/stringlikefunc_test.go index 94275e58a..d4e17dcbc 100644 --- a/pkg/policy/condition/stringlikefunc_test.go +++ b/pkg/policy/condition/stringlikefunc_test.go @@ -81,13 +81,11 @@ func TestStringLikeFuncEvaluate(t *testing.T) { {case3Function, map[string][]string{"x-amz-server-side-encryption": {"AES256"}}, true}, {case3Function, map[string][]string{"x-amz-server-side-encryption": {"AES512"}}, true}, - {case3Function, map[string][]string{"x-amz-server-side-encryption": {"aws:kms"}}, false}, {case3Function, map[string][]string{}, false}, {case3Function, map[string][]string{"delimiter": {"/"}}, false}, {case4Function, map[string][]string{"x-amz-server-side-encryption": {"AES256"}}, true}, {case4Function, map[string][]string{"x-amz-server-side-encryption": {"AES512"}}, false}, - {case4Function, map[string][]string{"x-amz-server-side-encryption": {"aws:kms"}}, false}, {case4Function, map[string][]string{}, false}, {case4Function, map[string][]string{"delimiter": {"/"}}, false}, @@ -204,7 +202,6 @@ func TestStringLikeFuncToMap(t *testing.T) { case4Function, err := newStringLikeFunc(S3XAmzServerSideEncryption, NewValueSet( NewStringValue("AES*"), - NewStringValue("aws:*"), ), ) if err != nil { @@ -214,7 +211,6 @@ func TestStringLikeFuncToMap(t *testing.T) { case4Result := map[Key]ValueSet{ S3XAmzServerSideEncryption: NewValueSet( NewStringValue("AES*"), - NewStringValue("aws:*"), ), } @@ -354,13 +350,11 @@ func TestStringNotLikeFuncEvaluate(t *testing.T) { {case3Function, map[string][]string{"x-amz-server-side-encryption": {"AES256"}}, false}, {case3Function, map[string][]string{"x-amz-server-side-encryption": {"AES512"}}, false}, - {case3Function, map[string][]string{"x-amz-server-side-encryption": {"aws:kms"}}, true}, {case3Function, map[string][]string{}, true}, {case3Function, map[string][]string{"delimiter": {"/"}}, true}, {case4Function, map[string][]string{"x-amz-server-side-encryption": {"AES256"}}, false}, {case4Function, map[string][]string{"x-amz-server-side-encryption": {"AES512"}}, true}, - {case4Function, map[string][]string{"x-amz-server-side-encryption": {"aws:kms"}}, true}, {case4Function, map[string][]string{}, true}, {case4Function, map[string][]string{"delimiter": {"/"}}, true}, @@ -477,7 +471,6 @@ func TestStringNotLikeFuncToMap(t *testing.T) { case4Function, err := newStringNotLikeFunc(S3XAmzServerSideEncryption, NewValueSet( NewStringValue("AES*"), - NewStringValue("aws:*"), ), ) if err != nil { @@ -487,7 +480,6 @@ func TestStringNotLikeFuncToMap(t *testing.T) { case4Result := map[Key]ValueSet{ S3XAmzServerSideEncryption: NewValueSet( NewStringValue("AES*"), - NewStringValue("aws:*"), ), } @@ -591,7 +583,6 @@ func TestNewStringLikeFunc(t *testing.T) { case4Function, err := newStringLikeFunc(S3XAmzServerSideEncryption, NewValueSet( NewStringValue("AES*"), - NewStringValue("aws:*"), ), ) if err != nil { @@ -645,7 +636,6 @@ func TestNewStringLikeFunc(t *testing.T) { {S3XAmzServerSideEncryption, NewValueSet( NewStringValue("AES*"), - NewStringValue("aws:*"), ), case4Function, false}, {S3XAmzMetadataDirective, NewValueSet(NewStringValue("REPL*")), case5Function, false}, @@ -712,7 +702,6 @@ func TestNewStringNotLikeFunc(t *testing.T) { case4Function, err := newStringNotLikeFunc(S3XAmzServerSideEncryption, NewValueSet( NewStringValue("AES*"), - NewStringValue("aws:*"), ), ) if err != nil { @@ -766,7 +755,6 @@ func TestNewStringNotLikeFunc(t *testing.T) { {S3XAmzServerSideEncryption, NewValueSet( NewStringValue("AES*"), - NewStringValue("aws:*"), ), case4Function, false}, {S3XAmzMetadataDirective, NewValueSet(NewStringValue("REPL*")), case5Function, false}, diff --git a/pkg/policy/statement_test.go b/pkg/policy/statement_test.go index ac2e475ae..8c833f64e 100644 --- a/pkg/policy/statement_test.go +++ b/pkg/policy/statement_test.go @@ -305,11 +305,11 @@ func TestStatementMarshalJSON(t *testing.T) { case3Statement := NewStatement( Deny, NewPrincipal("*"), - NewActionSet(GetObjectAction), + NewActionSet(PutObjectAction), NewResourceSet(NewResource("mybucket", "/myobject*")), condition.NewFunctions(func2), ) - case3Data := []byte(`{"Effect":"Deny","Principal":{"AWS":["*"]},"Action":["s3:GetObject"],"Resource":["arn:aws:s3:::mybucket/myobject*"],"Condition":{"Null":{"s3:x-amz-server-side-encryption":[false]}}}`) + case3Data := []byte(`{"Effect":"Deny","Principal":{"AWS":["*"]},"Action":["s3:PutObject"],"Resource":["arn:aws:s3:::mybucket/myobject*"],"Condition":{"Null":{"s3:x-amz-server-side-encryption":[false]}}}`) case4Statement := NewStatement( Allow,