
committed by
Harshavardhana

4 changed files with 696 additions and 38 deletions
-
2bucket-handlers.go
-
2bucket-policy-handlers.go
-
85bucket-policy-parser.go
-
645bucket-policy-parser_test.go
@ -0,0 +1,645 @@ |
|||
/* |
|||
* Minio Cloud Storage, (C) 2015, 2016 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 main |
|||
|
|||
import ( |
|||
"encoding/json" |
|||
"errors" |
|||
"fmt" |
|||
"reflect" |
|||
"testing" |
|||
) |
|||
|
|||
var ( |
|||
readWriteBucketActions = []string{ |
|||
"s3:GetBucketLocation", |
|||
"s3:ListBucket", |
|||
"s3:ListBucketMultipartUploads", |
|||
// Add more bucket level read-write actions here.
|
|||
} |
|||
readWriteObjectActions = []string{ |
|||
"s3:AbortMultipartUpload", |
|||
"s3:DeleteObject", |
|||
"s3:GetObject", |
|||
"s3:ListMultipartUploadParts", |
|||
"s3:PutObject", |
|||
// Add more object level read-write actions here.
|
|||
} |
|||
) |
|||
|
|||
// Write only actions.
|
|||
var ( |
|||
writeOnlyBucketActions = []string{ |
|||
"s3:GetBucketLocation", |
|||
"s3:ListBucketMultipartUploads", |
|||
// Add more bucket level write actions here.
|
|||
} |
|||
writeOnlyObjectActions = []string{ |
|||
"s3:AbortMultipartUpload", |
|||
"s3:DeleteObject", |
|||
"s3:ListMultipartUploadParts", |
|||
"s3:PutObject", |
|||
// Add more object level write actions here.
|
|||
} |
|||
) |
|||
|
|||
// Read only actions.
|
|||
var ( |
|||
readOnlyBucketActions = []string{ |
|||
"s3:GetBucketLocation", |
|||
"s3:ListBucket", |
|||
// Add more bucket level read actions here.
|
|||
} |
|||
readOnlyObjectActions = []string{ |
|||
"s3:GetObject", |
|||
// Add more object level read actions here.
|
|||
} |
|||
) |
|||
|
|||
// Obtain statements for read-write BucketPolicy.
|
|||
func setReadWriteStatement(bucketName, objectPrefix string) []policyStatement { |
|||
bucketResourceStatement := policyStatement{} |
|||
objectResourceStatement := policyStatement{} |
|||
statements := []policyStatement{} |
|||
|
|||
bucketResourceStatement.Effect = "Allow" |
|||
bucketResourceStatement.Principal.AWS = []string{"*"} |
|||
bucketResourceStatement.Resources = []string{fmt.Sprintf("%s%s", AWSResourcePrefix, bucketName)} |
|||
bucketResourceStatement.Actions = readWriteBucketActions |
|||
objectResourceStatement.Effect = "Allow" |
|||
objectResourceStatement.Principal.AWS = []string{"*"} |
|||
objectResourceStatement.Resources = []string{fmt.Sprintf("%s%s", AWSResourcePrefix, bucketName+"/"+objectPrefix+"*")} |
|||
objectResourceStatement.Actions = readWriteObjectActions |
|||
// Save the read write policy.
|
|||
statements = append(statements, bucketResourceStatement, objectResourceStatement) |
|||
return statements |
|||
} |
|||
|
|||
// Obtain statements for read only BucketPolicy.
|
|||
func setReadOnlyStatement(bucketName, objectPrefix string) []policyStatement { |
|||
bucketResourceStatement := policyStatement{} |
|||
objectResourceStatement := policyStatement{} |
|||
statements := []policyStatement{} |
|||
|
|||
bucketResourceStatement.Effect = "Allow" |
|||
bucketResourceStatement.Principal.AWS = []string{"*"} |
|||
bucketResourceStatement.Resources = []string{fmt.Sprintf("%s%s", AWSResourcePrefix, bucketName)} |
|||
bucketResourceStatement.Actions = readOnlyBucketActions |
|||
objectResourceStatement.Effect = "Allow" |
|||
objectResourceStatement.Principal.AWS = []string{"*"} |
|||
objectResourceStatement.Resources = []string{fmt.Sprintf("%s%s", AWSResourcePrefix, bucketName+"/"+objectPrefix+"*")} |
|||
objectResourceStatement.Actions = readOnlyObjectActions |
|||
// Save the read only policy.
|
|||
statements = append(statements, bucketResourceStatement, objectResourceStatement) |
|||
return statements |
|||
} |
|||
|
|||
// Obtain statements for write only BucketPolicy.
|
|||
func setWriteOnlyStatement(bucketName, objectPrefix string) []policyStatement { |
|||
bucketResourceStatement := policyStatement{} |
|||
objectResourceStatement := policyStatement{} |
|||
statements := []policyStatement{} |
|||
// Write only policy.
|
|||
bucketResourceStatement.Effect = "Allow" |
|||
bucketResourceStatement.Principal.AWS = []string{"*"} |
|||
bucketResourceStatement.Resources = []string{fmt.Sprintf("%s%s", AWSResourcePrefix, bucketName)} |
|||
bucketResourceStatement.Actions = writeOnlyBucketActions |
|||
objectResourceStatement.Effect = "Allow" |
|||
objectResourceStatement.Principal.AWS = []string{"*"} |
|||
objectResourceStatement.Resources = []string{fmt.Sprintf("%s%s", AWSResourcePrefix, bucketName+"/"+objectPrefix+"*")} |
|||
objectResourceStatement.Actions = writeOnlyObjectActions |
|||
// Save the write only policy.
|
|||
statements = append(statements, bucketResourceStatement, objectResourceStatement) |
|||
return statements |
|||
} |
|||
|
|||
// Tests validate Action validator.
|
|||
func TestIsValidActions(t *testing.T) { |
|||
testCases := []struct { |
|||
// input.
|
|||
actions []string |
|||
// expected output.
|
|||
err error |
|||
// flag indicating whether the test should pass.
|
|||
shouldPass bool |
|||
}{ |
|||
// Inputs with unsupported Action.
|
|||
// Test case - 1.
|
|||
// "s3:ListObject" is an invalid Action.
|
|||
{[]string{"s3:GetObject", "s3:ListObject", "s3:RemoveObject"}, errors.New("Unsupported action found: ‘s3:ListObject’, please validate your policy document."), false}, |
|||
// Test case - 2.
|
|||
// Empty Actions.
|
|||
{[]string{}, errors.New("Action list cannot be empty."), false}, |
|||
// Test case - 3.
|
|||
// "s3:DeleteEverything"" is an invalid Action.
|
|||
{[]string{"s3:GetObject", "s3:ListBucket", "s3:PutObject", "s3:DeleteEverything"}, errors.New("Unsupported action found: ‘s3:DeleteEverything’, please validate your policy document."), false}, |
|||
|
|||
// Inputs with valid Action.
|
|||
// Test Case - 4.
|
|||
{[]string{"s3:GetObject", "s3:ListBucket", "s3:PutObject", "s3:GetBucketLocation", "s3:DeleteObject", "s3:AbortMultipartUpload", "s3:ListBucketMultipartUploads", "s3:ListMultipartUploadParts"}, nil, true}, |
|||
} |
|||
for i, testCase := range testCases { |
|||
err := isValidActions(testCase.actions) |
|||
if err != nil && testCase.shouldPass { |
|||
t.Errorf("Test %d: Expected to pass, but failed with: <ERROR> %s", i+1, err.Error()) |
|||
} |
|||
if err == nil && !testCase.shouldPass { |
|||
t.Errorf("Test %d: Expected to fail with <ERROR> \"%s\", but passed instead", i+1, testCase.err.Error()) |
|||
} |
|||
// Failed as expected, but does it fail for the expected reason.
|
|||
if err != nil && !testCase.shouldPass { |
|||
if err.Error() != testCase.err.Error() { |
|||
t.Errorf("Test %d: Expected to fail with error \"%s\", but instead failed with error \"%s\"", i+1, testCase.err.Error(), err.Error()) |
|||
} |
|||
} |
|||
|
|||
} |
|||
} |
|||
|
|||
// Tests validate Effect validator.
|
|||
func TestIsValidEffect(t *testing.T) { |
|||
testCases := []struct { |
|||
// input.
|
|||
effect string |
|||
// expected output.
|
|||
err error |
|||
// flag indicating whether the test should pass.
|
|||
shouldPass bool |
|||
}{ |
|||
// Inputs with unsupported Effect.
|
|||
// Test case - 1.
|
|||
{"DontAllow", errors.New("Unsupported Effect found: ‘DontAllow’, please validate your policy document."), false}, |
|||
// Test case - 2.
|
|||
{"NeverAllow", errors.New("Unsupported Effect found: ‘NeverAllow’, please validate your policy document."), false}, |
|||
// Test case - 3.
|
|||
{"AllowAlways", errors.New("Unsupported Effect found: ‘AllowAlways’, please validate your policy document."), false}, |
|||
|
|||
// Inputs with valid Effect.
|
|||
// Test Case - 4.
|
|||
{"Allow", nil, true}, |
|||
// Test Case - 5.
|
|||
{"Deny", nil, true}, |
|||
} |
|||
for i, testCase := range testCases { |
|||
err := isValidEffect(testCase.effect) |
|||
if err != nil && testCase.shouldPass { |
|||
t.Errorf("Test %d: Expected to pass, but failed with: <ERROR> %s", i+1, err.Error()) |
|||
} |
|||
if err == nil && !testCase.shouldPass { |
|||
t.Errorf("Test %d: Expected to fail with <ERROR> \"%s\", but passed instead", i+1, testCase.err.Error()) |
|||
} |
|||
// Failed as expected, but does it fail for the expected reason.
|
|||
if err != nil && !testCase.shouldPass { |
|||
if err.Error() != testCase.err.Error() { |
|||
t.Errorf("Test %d: Expected to fail with error \"%s\", but instead failed with error \"%s\"", i+1, testCase.err.Error(), err.Error()) |
|||
} |
|||
} |
|||
} |
|||
} |
|||
|
|||
// Tests validate Resources validator.
|
|||
func TestIsValidResources(t *testing.T) { |
|||
testCases := []struct { |
|||
// input.
|
|||
resources []string |
|||
// expected output.
|
|||
err error |
|||
// flag indicating whether the test should pass.
|
|||
shouldPass bool |
|||
}{ |
|||
// Inputs with unsupported Action.
|
|||
// Test case - 1.
|
|||
// Empty Resources.
|
|||
{[]string{}, errors.New("Resource list cannot be empty."), false}, |
|||
// Test case - 2.
|
|||
// A valid resource should have prefix "arn:aws:s3:::".
|
|||
{[]string{"my-resource"}, errors.New("Unsupported resource style found: ‘my-resource’, please validate your policy document."), false}, |
|||
// Test case - 3.
|
|||
// A Valid resource should have bucket name followed by "arn:aws:s3:::".
|
|||
{[]string{"arn:aws:s3:::"}, errors.New("Invalid resource style found: ‘arn:aws:s3:::’, please validate your policy document."), false}, |
|||
// Test Case - 4.
|
|||
// Valid resource shouldn't have slash('/') followed by "arn:aws:s3:::".
|
|||
{[]string{"arn:aws:s3:::/"}, errors.New("Invalid resource style found: ‘arn:aws:s3:::/’, please validate your policy document."), false}, |
|||
|
|||
// Test cases with valid Resources.
|
|||
{[]string{"arn:aws:s3:::my-bucket"}, nil, true}, |
|||
{[]string{"arn:aws:s3:::my-bucket/Asia/*"}, nil, true}, |
|||
{[]string{"arn:aws:s3:::my-bucket/Asia/India/*"}, nil, true}, |
|||
} |
|||
for i, testCase := range testCases { |
|||
err := isValidResources(testCase.resources) |
|||
if err != nil && testCase.shouldPass { |
|||
t.Errorf("Test %d: Expected to pass, but failed with: <ERROR> %s", i+1, err.Error()) |
|||
} |
|||
if err == nil && !testCase.shouldPass { |
|||
t.Errorf("Test %d: Expected to fail with <ERROR> \"%s\", but passed instead", i+1, testCase.err.Error()) |
|||
} |
|||
// Failed as expected, but does it fail for the expected reason.
|
|||
if err != nil && !testCase.shouldPass { |
|||
if err.Error() != testCase.err.Error() { |
|||
t.Errorf("Test %d: Expected to fail with error \"%s\", but instead failed with error \"%s\"", i+1, testCase.err.Error(), err.Error()) |
|||
} |
|||
} |
|||
} |
|||
} |
|||
|
|||
// Tests validate principals validator.
|
|||
func TestIsValidPrincipals(t *testing.T) { |
|||
testCases := []struct { |
|||
// input.
|
|||
principals []string |
|||
// expected output.
|
|||
err error |
|||
// flag indicating whether the test should pass.
|
|||
shouldPass bool |
|||
}{ |
|||
// Inputs with unsupported Principals.
|
|||
// Test case - 1.
|
|||
// Empty Principals list.
|
|||
{[]string{}, errors.New("Principal cannot be empty."), false}, |
|||
// Test case - 2.
|
|||
// "*" is the only valid principal.
|
|||
{[]string{"my-principal"}, errors.New("Unsupported principal style found: ‘my-principal’, please validate your policy document."), false}, |
|||
// Test case - 3.
|
|||
{[]string{"*", "111122233"}, errors.New("Unsupported principal style found: ‘111122233’, please validate your policy document."), false}, |
|||
|
|||
// Test case - 4.
|
|||
// Test case with valid principal value.
|
|||
{[]string{"*"}, nil, true}, |
|||
} |
|||
for i, testCase := range testCases { |
|||
err := isValidPrincipals(testCase.principals) |
|||
if err != nil && testCase.shouldPass { |
|||
t.Errorf("Test %d: Expected to pass, but failed with: <ERROR> %s", i+1, err.Error()) |
|||
} |
|||
if err == nil && !testCase.shouldPass { |
|||
t.Errorf("Test %d: Expected to fail with <ERROR> \"%s\", but passed instead", i+1, testCase.err.Error()) |
|||
} |
|||
// Failed as expected, but does it fail for the expected reason.
|
|||
if err != nil && !testCase.shouldPass { |
|||
if err.Error() != testCase.err.Error() { |
|||
t.Errorf("Test %d: Expected to fail with error \"%s\", but instead failed with error \"%s\"", i+1, testCase.err.Error(), err.Error()) |
|||
} |
|||
} |
|||
} |
|||
} |
|||
|
|||
// Tests validate policyStatement condition validator.
|
|||
func TestIsValidConditions(t *testing.T) { |
|||
// returns empty conditions map.
|
|||
setEmptyConditions := func() map[string]map[string]string { |
|||
return make(map[string]map[string]string) |
|||
} |
|||
|
|||
// returns map with the "StringEquals" set to empty map.
|
|||
setEmptyStringEquals := func() map[string]map[string]string { |
|||
emptyMap := make(map[string]string) |
|||
conditions := make(map[string]map[string]string) |
|||
conditions["StringEquals"] = emptyMap |
|||
return conditions |
|||
|
|||
} |
|||
|
|||
// returns map with the "StringNotEquals" set to empty map.
|
|||
setEmptyStringNotEquals := func() map[string]map[string]string { |
|||
emptyMap := make(map[string]string) |
|||
conditions := make(map[string]map[string]string) |
|||
conditions["StringNotEquals"] = emptyMap |
|||
return conditions |
|||
|
|||
} |
|||
// Generate conditions.
|
|||
generateConditions := func(key1, key2, value string) map[string]map[string]string { |
|||
innerMap := make(map[string]string) |
|||
innerMap[key2] = value |
|||
conditions := make(map[string]map[string]string) |
|||
conditions[key1] = innerMap |
|||
return conditions |
|||
} |
|||
|
|||
// generate ambigious conditions.
|
|||
generateAmbigiousConditions := func() map[string]map[string]string { |
|||
innerMap := make(map[string]string) |
|||
innerMap["s3:prefix"] = "Asia/" |
|||
conditions := make(map[string]map[string]string) |
|||
conditions["StringEquals"] = innerMap |
|||
conditions["StringNotEquals"] = innerMap |
|||
return conditions |
|||
} |
|||
|
|||
// generate valid and non valid type in the condition map.
|
|||
generateValidInvalidConditions := func() map[string]map[string]string { |
|||
innerMap := make(map[string]string) |
|||
innerMap["s3:prefix"] = "Asia/" |
|||
conditions := make(map[string]map[string]string) |
|||
conditions["StringEquals"] = innerMap |
|||
conditions["InvalidType"] = innerMap |
|||
return conditions |
|||
} |
|||
|
|||
// generate valid and invalid keys for valid types in the same condition map.
|
|||
generateValidInvalidConditionKeys := func() map[string]map[string]string { |
|||
innerMapValid := make(map[string]string) |
|||
innerMapValid["s3:prefix"] = "Asia/" |
|||
innerMapInValid := make(map[string]string) |
|||
innerMapInValid["s3:invalid"] = "Asia/" |
|||
conditions := make(map[string]map[string]string) |
|||
conditions["StringEquals"] = innerMapValid |
|||
conditions["StringEquals"] = innerMapInValid |
|||
return conditions |
|||
} |
|||
|
|||
// List of Conditions used for test cases.
|
|||
testConditions := []map[string]map[string]string{ |
|||
generateConditions("StringValues", "s3:max-keys", "100"), |
|||
generateConditions("StringEquals", "s3:Object", "100"), |
|||
generateAmbigiousConditions(), |
|||
generateValidInvalidConditions(), |
|||
generateValidInvalidConditionKeys(), |
|||
setEmptyConditions(), |
|||
setEmptyStringEquals(), |
|||
setEmptyStringNotEquals(), |
|||
generateConditions("StringEquals", "s3:prefix", "Asia/"), |
|||
generateConditions("StringEquals", "s3:max-keys", "100"), |
|||
generateConditions("StringNotEquals", "s3:prefix", "Asia/"), |
|||
generateConditions("StringNotEquals", "s3:max-keys", "100"), |
|||
} |
|||
|
|||
testCases := []struct { |
|||
inputCondition map[string]map[string]string |
|||
// expected result.
|
|||
expectedErr error |
|||
// flag indicating whether test should pass.
|
|||
shouldPass bool |
|||
}{ |
|||
// Malformed conditions.
|
|||
// Test case - 1.
|
|||
// "StringValues" is an invalid type.
|
|||
{testConditions[0], fmt.Errorf("Unsupported condition type 'StringValues', " + |
|||
"please validate your policy document."), false}, |
|||
// Test case - 2.
|
|||
// "s3:Object" is an invalid key.
|
|||
{testConditions[1], fmt.Errorf("Unsupported condition key " + |
|||
"'StringEquals', please validate your policy document."), false}, |
|||
// Test case - 3.
|
|||
// Test case with Ambigious conditions set.
|
|||
{testConditions[2], fmt.Errorf("Ambigious condition values for key 's3:prefix', " + |
|||
"please validate your policy document."), false}, |
|||
// Test case - 4.
|
|||
// Test case with valid and invalid condition types.
|
|||
{testConditions[3], fmt.Errorf("Unsupported condition type 'InvalidType', " + |
|||
"please validate your policy document."), false}, |
|||
// Test case - 5.
|
|||
// Test case with valid and invalid condition keys.
|
|||
{testConditions[4], fmt.Errorf("Unsupported condition key 'StringEquals', " + |
|||
"please validate your policy document."), false}, |
|||
// Test cases with valid conditions.
|
|||
// Test case - 6.
|
|||
{testConditions[5], nil, true}, |
|||
// Test case - 7.
|
|||
{testConditions[6], nil, true}, |
|||
// Test case - 8.
|
|||
{testConditions[7], nil, true}, |
|||
// Test case - 9.
|
|||
{testConditions[8], nil, true}, |
|||
// Test case - 10.
|
|||
{testConditions[9], nil, true}, |
|||
// Test case - 11.
|
|||
{testConditions[10], nil, true}, |
|||
// Test case 10.
|
|||
{testConditions[11], nil, true}, |
|||
} |
|||
for i, testCase := range testCases { |
|||
actualErr := isValidConditions(testCase.inputCondition) |
|||
if actualErr != nil && testCase.shouldPass { |
|||
t.Errorf("Test %d: Expected to pass, but failed with: <ERROR> %s", i+1, actualErr.Error()) |
|||
} |
|||
if actualErr == nil && !testCase.shouldPass { |
|||
t.Errorf("Test %d: Expected to fail with <ERROR> \"%s\", but passed instead", i+1, testCase.expectedErr.Error()) |
|||
} |
|||
// Failed as expected, but does it fail for the expected reason.
|
|||
if actualErr != nil && !testCase.shouldPass { |
|||
if actualErr.Error() != testCase.expectedErr.Error() { |
|||
t.Errorf("Test %d: Expected to fail with error \"%s\", but instead failed with error \"%s\"", i+1, testCase.expectedErr.Error(), actualErr.Error()) |
|||
} |
|||
} |
|||
} |
|||
} |
|||
|
|||
// Tests validate Policy Action and Resource fields.
|
|||
func TestCheckBucketPolicyResources(t *testing.T) { |
|||
// constructing policy statement without invalidPrefixActions (check bucket-policy-parser.go).
|
|||
setValidPrefixActions := func(statements []policyStatement) []policyStatement { |
|||
statements[0].Actions = []string{"s3:DeleteObject", "s3:PutObject"} |
|||
return statements |
|||
} |
|||
// contructing policy statement with recursive resources.
|
|||
// should result in ErrMalformedPolicy
|
|||
setRecurseResource := func(statements []policyStatement) []policyStatement { |
|||
statements[0].Resources = []string{"arn:aws:s3:::minio-bucket/Asia/*", "arn:aws:s3:::minio-bucket/Asia/India/*"} |
|||
return statements |
|||
} |
|||
|
|||
// List of BucketPolicy used for tests.
|
|||
bucketAccessPolicies := []BucketPolicy{ |
|||
// BucketPolicy - 0.
|
|||
// Contains valid read only policy statement.
|
|||
{Version: "1.0", Statements: setReadOnlyStatement("minio-bucket", "")}, |
|||
// BucketPolicy - 1.
|
|||
// Contains valid read-write only policy statement.
|
|||
{Version: "1.0", Statements: setReadWriteStatement("minio-bucket", "Asia/")}, |
|||
// BucketPolicy - 2.
|
|||
// Contains valid write only policy statement.
|
|||
{Version: "1.0", Statements: setWriteOnlyStatement("minio-bucket", "Asia/India/")}, |
|||
// BucketPolicy - 3.
|
|||
// Contains invalidPrefixActions.
|
|||
// Since resourcePrefix is not to the bucket-name, it return ErrMalformedPolicy.
|
|||
{Version: "1.0", Statements: setReadOnlyStatement("minio-bucket-fail", "Asia/India/")}, |
|||
// BucketPolicy - 4.
|
|||
// constructing policy statement without invalidPrefixActions (check bucket-policy-parser.go).
|
|||
// but bucket part of the resource is not equal to the bucket name.
|
|||
// this results in return of ErrMalformedPolicy.
|
|||
{Version: "1.0", Statements: setValidPrefixActions(setWriteOnlyStatement("minio-bucket-fail", "Asia/India/"))}, |
|||
// BucketPolicy - 5.
|
|||
// constructing policy statement without invalidPrefixActions (check bucket-policy-parser.go).
|
|||
// contructing policy statement with recursive resources.
|
|||
// should result in ErrMalformedPolicy
|
|||
{Version: "1.0", Statements: setRecurseResource(setValidPrefixActions(setWriteOnlyStatement("minio-bucket", "")))}, |
|||
} |
|||
|
|||
testCases := []struct { |
|||
inputPolicy BucketPolicy |
|||
// expected results.
|
|||
apiErrCode APIErrorCode |
|||
// Flag indicating whether the test should pass.
|
|||
shouldPass bool |
|||
}{ |
|||
// Test case - 1.
|
|||
{bucketAccessPolicies[0], ErrNone, true}, |
|||
// Test case - 2.
|
|||
{bucketAccessPolicies[1], ErrNone, true}, |
|||
// Test case - 3.
|
|||
{bucketAccessPolicies[2], ErrNone, true}, |
|||
// Test case - 4.
|
|||
// contains invalidPrefixActions (check bucket-policy-parser.go).
|
|||
// Resource prefix will not be equal to the bucket name in this case.
|
|||
{bucketAccessPolicies[3], ErrMalformedPolicy, false}, |
|||
// Test case - 5.
|
|||
// actions contain invalidPrefixActions (check bucket-policy-parser.go).
|
|||
// Resource prefix bucket part is not equal to the bucket name in this case.
|
|||
{bucketAccessPolicies[4], ErrMalformedPolicy, false}, |
|||
// Test case - 6.
|
|||
// contructing policy statement with recursive resources.
|
|||
// should result in ErrMalformedPolicy.
|
|||
{bucketAccessPolicies[5], ErrMalformedPolicy, false}, |
|||
} |
|||
for i, testCase := range testCases { |
|||
apiErrCode := checkBucketPolicyResources("minio-bucket", testCase.inputPolicy) |
|||
if apiErrCode != ErrNone && testCase.shouldPass { |
|||
t.Errorf("Test %d: Expected to pass, but failed with Errocode %v", i+1, apiErrCode) |
|||
} |
|||
if apiErrCode == ErrNone && !testCase.shouldPass { |
|||
t.Errorf("Test %d: Expected to fail with ErrCode %v, but passed instead", i+1, testCase.apiErrCode) |
|||
} |
|||
// Failed as expected, but does it fail for the expected reason.
|
|||
if apiErrCode != ErrNone && !testCase.shouldPass { |
|||
if testCase.apiErrCode != apiErrCode { |
|||
t.Errorf("Test %d: Expected to fail with error code %v, but instead failed with error code %v", i+1, testCase.apiErrCode, apiErrCode) |
|||
} |
|||
} |
|||
} |
|||
} |
|||
|
|||
// Tests validate parsing of BucketAccessPolicy.
|
|||
func TestParseBucketPolicy(t *testing.T) { |
|||
// set Unsupported Actions.
|
|||
setUnsupportedActions := func(statements []policyStatement) []policyStatement { |
|||
// "s3:DeleteEverything"" is an Unsupported Action.
|
|||
statements[0].Actions = []string{"s3:GetObject", "s3:ListBucket", "s3:PutObject", "s3:DeleteEverything"} |
|||
return statements |
|||
} |
|||
// set unsupported Effect.
|
|||
setUnsupportedEffect := func(statements []policyStatement) []policyStatement { |
|||
// Effect "Don't allow" is Unsupported.
|
|||
statements[0].Effect = "DontAllow" |
|||
return statements |
|||
} |
|||
// set unsupported principals.
|
|||
setUnsupportedPrincipals := func(statements []policyStatement) []policyStatement { |
|||
// "User1111"" is an Unsupported Principal.
|
|||
statements[0].Principal.AWS = []string{"*", "User1111"} |
|||
return statements |
|||
} |
|||
// set unsupported Resources.
|
|||
setUnsupportedResources := func(statements []policyStatement) []policyStatement { |
|||
// "s3:DeleteEverything"" is an Unsupported Action.
|
|||
statements[0].Resources = []string{"my-resource"} |
|||
return statements |
|||
} |
|||
// List of BucketPolicy used for test cases.
|
|||
bucketAccesPolicies := []BucketPolicy{ |
|||
// BucketPolicy - 0.
|
|||
// BucketPolicy statement empty.
|
|||
{Version: "1.0"}, |
|||
// BucketPolicy - 1.
|
|||
// BucketPolicy version empty.
|
|||
{Version: "", Statements: []policyStatement{}}, |
|||
// BucketPolicy - 2.
|
|||
// Readonly BucketPolicy.
|
|||
{Version: "1.0", Statements: setReadOnlyStatement("minio-bucket", "")}, |
|||
// BucketPolicy - 3.
|
|||
// Read-Write bucket policy.
|
|||
{Version: "1.0", Statements: setReadWriteStatement("minio-bucket", "Asia/")}, |
|||
// BucketPolicy - 4.
|
|||
// Write only bucket policy.
|
|||
{Version: "1.0", Statements: setWriteOnlyStatement("minio-bucket", "Asia/India/")}, |
|||
// BucketPolicy - 5.
|
|||
// BucketPolicy statement contains unsupported action.
|
|||
{Version: "1.0", Statements: setUnsupportedActions(setReadOnlyStatement("minio-bucket", ""))}, |
|||
// BucketPolicy - 6.
|
|||
// BucketPolicy statement contains unsupported Effect.
|
|||
{Version: "1.0", Statements: setUnsupportedEffect(setReadWriteStatement("minio-bucket", "Asia/"))}, |
|||
// BucketPolicy - 7.
|
|||
// BucketPolicy statement contains unsupported Principal.
|
|||
{Version: "1.0", Statements: setUnsupportedPrincipals(setWriteOnlyStatement("minio-bucket", "Asia/India/"))}, |
|||
// BucketPolicy - 8.
|
|||
// BucketPolicy statement contains unsupported Resource.
|
|||
{Version: "1.0", Statements: setUnsupportedResources(setWriteOnlyStatement("minio-bucket", "Asia/India/"))}, |
|||
} |
|||
|
|||
testCases := []struct { |
|||
inputPolicy BucketPolicy |
|||
// expected results.
|
|||
expectedPolicy BucketPolicy |
|||
err error |
|||
// Flag indicating whether the test should pass.
|
|||
shouldPass bool |
|||
}{ |
|||
// Test case - 1.
|
|||
// BucketPolicy statement empty.
|
|||
{bucketAccesPolicies[0], BucketPolicy{}, errors.New("Policy statement cannot be empty."), false}, |
|||
// Test case - 2.
|
|||
// BucketPolicy version empty.
|
|||
{bucketAccesPolicies[1], BucketPolicy{}, errors.New("Policy version cannot be empty."), false}, |
|||
// Test case - 3.
|
|||
// Readonly BucketPolicy.
|
|||
{bucketAccesPolicies[2], bucketAccesPolicies[2], nil, true}, |
|||
// Test case - 4.
|
|||
// Read-Write bucket policy.
|
|||
{bucketAccesPolicies[3], bucketAccesPolicies[3], nil, true}, |
|||
// Test case - 5.
|
|||
// Write only bucket policy.
|
|||
{bucketAccesPolicies[4], bucketAccesPolicies[4], nil, true}, |
|||
// Test case - 6.
|
|||
// BucketPolicy statement contains unsupported action.
|
|||
{bucketAccesPolicies[5], bucketAccesPolicies[5], fmt.Errorf("Unsupported action found: ‘s3:DeleteEverything’, please validate your policy document."), false}, |
|||
// Test case - 7.
|
|||
// BucketPolicy statement contains unsupported Effect.
|
|||
{bucketAccesPolicies[6], bucketAccesPolicies[6], fmt.Errorf("Unsupported Effect found: ‘DontAllow’, please validate your policy document."), false}, |
|||
// Test case - 8.
|
|||
// BucketPolicy statement contains unsupported Principal.
|
|||
{bucketAccesPolicies[7], bucketAccesPolicies[7], fmt.Errorf("Unsupported principal style found: ‘User1111’, please validate your policy document."), false}, |
|||
// Test case - 9.
|
|||
// BucketPolicy statement contains unsupported Resource.
|
|||
{bucketAccesPolicies[8], bucketAccesPolicies[8], fmt.Errorf("Unsupported resource style found: ‘my-resource’, please validate your policy document."), false}, |
|||
} |
|||
for i, testCase := range testCases { |
|||
inputPolicyBytes, e := json.Marshal(testCase.inputPolicy) |
|||
if e != nil { |
|||
t.Fatalf("Test %d: Couldn't Marshal bucket policy", i+1) |
|||
} |
|||
|
|||
actualAccessPolicy, err := parseBucketPolicy(inputPolicyBytes) |
|||
if err != nil && testCase.shouldPass { |
|||
t.Errorf("Test %d: Expected to pass, but failed with: <ERROR> %s", i+1, err.Error()) |
|||
} |
|||
if err == nil && !testCase.shouldPass { |
|||
t.Errorf("Test %d: Expected to fail with <ERROR> \"%s\", but passed instead", i+1, testCase.err.Error()) |
|||
} |
|||
// Failed as expected, but does it fail for the expected reason.
|
|||
if err != nil && !testCase.shouldPass { |
|||
if err.Error() != testCase.err.Error() { |
|||
t.Errorf("Test %d: Expected to fail with error \"%s\", but instead failed with error \"%s\"", i+1, testCase.err.Error(), err.Error()) |
|||
} |
|||
} |
|||
// Test passes as expected, but the output values are verified for correctness here.
|
|||
if err == nil && testCase.shouldPass { |
|||
if !reflect.DeepEqual(testCase.expectedPolicy, actualAccessPolicy) { |
|||
t.Errorf("Test %d: The expected statements from resource statement generator doesn't match the actual statements", i+1) |
|||
} |
|||
} |
|||
} |
|||
} |
Write
Preview
Loading…
Cancel
Save
Reference in new issue