Bucket policies should use minio-go/pkg/policy instead. (#5090)

master
Harshavardhana 7 years ago committed by Dee Koder
parent 8bbfb1b714
commit 203ac8edaa
  1. 2
      cmd/api-errors.go
  2. 18
      cmd/bucket-handlers.go
  3. 44
      cmd/bucket-policy-handlers.go
  4. 23
      cmd/bucket-policy-handlers_test.go
  5. 118
      cmd/bucket-policy-parser.go
  6. 194
      cmd/bucket-policy-parser_test.go
  7. 53
      cmd/bucket-policy.go
  8. 9
      cmd/test-utils_test.go
  9. 6
      cmd/web-handlers.go
  10. 74
      cmd/web-handlers_test.go

@ -763,6 +763,8 @@ func toAPIErrorCode(err error) (apiErr APIErrorCode) {
apiErr = ErrUnsupportedMetadata apiErr = ErrUnsupportedMetadata
case PartsSizeUnequal: case PartsSizeUnequal:
apiErr = ErrPartsSizeUnequal apiErr = ErrPartsSizeUnequal
case BucketPolicyNotFound:
apiErr = ErrNoSuchBucketPolicy
default: default:
apiErr = ErrInternalError apiErr = ErrInternalError
} }

@ -24,10 +24,12 @@ import (
"net/http" "net/http"
"net/url" "net/url"
"path" "path"
"reflect"
"strings" "strings"
"sync" "sync"
mux "github.com/gorilla/mux" mux "github.com/gorilla/mux"
"github.com/minio/minio-go/pkg/policy"
"github.com/minio/minio-go/pkg/set" "github.com/minio/minio-go/pkg/set"
"github.com/minio/minio/pkg/hash" "github.com/minio/minio/pkg/hash"
) )
@ -56,8 +58,8 @@ func enforceBucketPolicy(bucket, action, resource, referer, sourceIP string, que
} }
// Fetch bucket policy, if policy is not set return access denied. // Fetch bucket policy, if policy is not set return access denied.
policy := globalBucketPolicies.GetBucketPolicy(bucket) p := globalBucketPolicies.GetBucketPolicy(bucket)
if policy == nil { if reflect.DeepEqual(p, emptyBucketPolicy) {
return ErrAccessDenied return ErrAccessDenied
} }
@ -65,7 +67,7 @@ func enforceBucketPolicy(bucket, action, resource, referer, sourceIP string, que
arn := bucketARNPrefix + strings.TrimSuffix(strings.TrimPrefix(resource, "/"), "/") arn := bucketARNPrefix + strings.TrimSuffix(strings.TrimPrefix(resource, "/"), "/")
// Get conditions for policy verification. // Get conditions for policy verification.
conditionKeyMap := make(map[string]set.StringSet) conditionKeyMap := make(policy.ConditionKeyMap)
for queryParam := range queryParams { for queryParam := range queryParams {
conditionKeyMap[queryParam] = set.CreateStringSet(queryParams.Get(queryParam)) conditionKeyMap[queryParam] = set.CreateStringSet(queryParams.Get(queryParam))
} }
@ -78,7 +80,7 @@ func enforceBucketPolicy(bucket, action, resource, referer, sourceIP string, que
conditionKeyMap["ip"] = set.CreateStringSet(sourceIP) conditionKeyMap["ip"] = set.CreateStringSet(sourceIP)
// Validate action, resource and conditions with current policy statements. // Validate action, resource and conditions with current policy statements.
if !bucketPolicyEvalStatements(action, arn, conditionKeyMap, policy.Statements) { if !bucketPolicyEvalStatements(action, arn, conditionKeyMap, p.Statements) {
return ErrAccessDenied return ErrAccessDenied
} }
return ErrNone return ErrNone
@ -90,14 +92,14 @@ func isBucketActionAllowed(action, bucket, prefix string) bool {
return false return false
} }
policy := globalBucketPolicies.GetBucketPolicy(bucket) bp := globalBucketPolicies.GetBucketPolicy(bucket)
if policy == nil { if reflect.DeepEqual(bp, emptyBucketPolicy) {
return false return false
} }
resource := bucketARNPrefix + path.Join(bucket, prefix) resource := bucketARNPrefix + path.Join(bucket, prefix)
var conditionKeyMap map[string]set.StringSet var conditionKeyMap map[string]set.StringSet
// Validate action, resource and conditions with current policy statements. // Validate action, resource and conditions with current policy statements.
return bucketPolicyEvalStatements(action, resource, conditionKeyMap, policy.Statements) return bucketPolicyEvalStatements(action, resource, conditionKeyMap, bp.Statements)
} }
// GetBucketLocationHandler - GET Bucket location. // GetBucketLocationHandler - GET Bucket location.
@ -690,7 +692,7 @@ func (api objectAPIHandlers) DeleteBucketHandler(w http.ResponseWriter, r *http.
_ = removeBucketPolicy(bucket, objectAPI) _ = removeBucketPolicy(bucket, objectAPI)
// Notify all peers (including self) to update in-memory state // Notify all peers (including self) to update in-memory state
S3PeersUpdateBucketPolicy(bucket, policyChange{true, nil}) S3PeersUpdateBucketPolicy(bucket, policyChange{true, policy.BucketAccessPolicy{}})
// Delete notification config, if present - ignore any errors. // Delete notification config, if present - ignore any errors.
_ = removeNotificationConfig(bucket, objectAPI) _ = removeNotificationConfig(bucket, objectAPI)

@ -17,7 +17,7 @@
package cmd package cmd
import ( import (
"fmt" "encoding/json"
"io" "io"
"io/ioutil" "io/ioutil"
"net" "net"
@ -27,7 +27,7 @@ import (
humanize "github.com/dustin/go-humanize" humanize "github.com/dustin/go-humanize"
mux "github.com/gorilla/mux" mux "github.com/gorilla/mux"
"github.com/minio/minio-go/pkg/set" "github.com/minio/minio-go/pkg/policy"
"github.com/minio/minio/pkg/wildcard" "github.com/minio/minio/pkg/wildcard"
) )
@ -36,7 +36,8 @@ const maxAccessPolicySize = 20 * humanize.KiByte
// Verify if a given action is valid for the url path based on the // Verify if a given action is valid for the url path based on the
// existing bucket access policy. // existing bucket access policy.
func bucketPolicyEvalStatements(action string, resource string, conditions map[string]set.StringSet, statements []policyStatement) bool { func bucketPolicyEvalStatements(action string, resource string, conditions policy.ConditionKeyMap,
statements []policy.Statement) bool {
for _, statement := range statements { for _, statement := range statements {
if bucketPolicyMatchStatement(action, resource, conditions, statement) { if bucketPolicyMatchStatement(action, resource, conditions, statement) {
if statement.Effect == "Allow" { if statement.Effect == "Allow" {
@ -52,7 +53,8 @@ func bucketPolicyEvalStatements(action string, resource string, conditions map[s
} }
// Verify if action, resource and conditions match input policy statement. // Verify if action, resource and conditions match input policy statement.
func bucketPolicyMatchStatement(action string, resource string, conditions map[string]set.StringSet, statement policyStatement) bool { func bucketPolicyMatchStatement(action string, resource string, conditions policy.ConditionKeyMap,
statement policy.Statement) bool {
// Verify if action, resource and condition match in given statement. // Verify if action, resource and condition match in given statement.
return (bucketPolicyActionMatch(action, statement) && return (bucketPolicyActionMatch(action, statement) &&
bucketPolicyResourceMatch(resource, statement) && bucketPolicyResourceMatch(resource, statement) &&
@ -60,7 +62,7 @@ func bucketPolicyMatchStatement(action string, resource string, conditions map[s
} }
// Verify if given action matches with policy statement. // Verify if given action matches with policy statement.
func bucketPolicyActionMatch(action string, statement policyStatement) bool { func bucketPolicyActionMatch(action string, statement policy.Statement) bool {
return !statement.Actions.FuncMatch(actionMatch, action).IsEmpty() return !statement.Actions.FuncMatch(actionMatch, action).IsEmpty()
} }
@ -95,7 +97,7 @@ func isIPInCIDR(cidr, ip string) bool {
} }
// Verify if given resource matches with policy statement. // Verify if given resource matches with policy statement.
func bucketPolicyResourceMatch(resource string, statement policyStatement) bool { func bucketPolicyResourceMatch(resource string, statement policy.Statement) bool {
// the resource rule for object could contain "*" wild card. // the resource rule for object could contain "*" wild card.
// the requested object can be given access based on the already set bucket policy if // the requested object can be given access based on the already set bucket policy if
// the match is successful. // the match is successful.
@ -104,7 +106,7 @@ func bucketPolicyResourceMatch(resource string, statement policyStatement) bool
} }
// Verify if given condition matches with policy statement. // Verify if given condition matches with policy statement.
func bucketPolicyConditionMatch(conditions map[string]set.StringSet, statement policyStatement) bool { func bucketPolicyConditionMatch(conditions policy.ConditionKeyMap, statement policy.Statement) bool {
// Supports following conditions. // Supports following conditions.
// - StringEquals // - StringEquals
// - StringNotEquals // - StringNotEquals
@ -300,13 +302,11 @@ func (api objectAPIHandlers) DeleteBucketPolicyHandler(w http.ResponseWriter, r
// Delete bucket access policy, by passing an empty policy // Delete bucket access policy, by passing an empty policy
// struct. // struct.
if err := persistAndNotifyBucketPolicyChange(bucket, policyChange{true, nil}, objAPI); err != nil { err = persistAndNotifyBucketPolicyChange(bucket, policyChange{
switch err.(type) { true, policy.BucketAccessPolicy{},
case BucketPolicyNotFound: }, objAPI)
writeErrorResponse(w, ErrNoSuchBucketPolicy, r.URL) if err != nil {
default: writeErrorResponse(w, toAPIErrorCode(err), r.URL)
writeErrorResponse(w, ErrInternalError, r.URL)
}
return return
} }
@ -345,15 +345,17 @@ func (api objectAPIHandlers) GetBucketPolicyHandler(w http.ResponseWriter, r *ht
policy, err := readBucketPolicy(bucket, objAPI) policy, err := readBucketPolicy(bucket, objAPI)
if err != nil { if err != nil {
errorIf(err, "Unable to read bucket policy.") errorIf(err, "Unable to read bucket policy.")
switch err.(type) { writeErrorResponse(w, toAPIErrorCode(err), r.URL)
case BucketPolicyNotFound: return
writeErrorResponse(w, ErrNoSuchBucketPolicy, r.URL) }
default:
writeErrorResponse(w, ErrInternalError, r.URL) policyBytes, err := json.Marshal(&policy)
} if err != nil {
errorIf(err, "Unable to marshal bucket policy.")
writeErrorResponse(w, toAPIErrorCode(err), r.URL)
return return
} }
// Write to client. // Write to client.
fmt.Fprint(w, policy) w.Write(policyBytes)
} }

@ -25,6 +25,7 @@ import (
"net/http/httptest" "net/http/httptest"
"testing" "testing"
"github.com/minio/minio-go/pkg/policy"
"github.com/minio/minio-go/pkg/set" "github.com/minio/minio-go/pkg/set"
) )
@ -32,8 +33,8 @@ import (
func TestBucketPolicyResourceMatch(t *testing.T) { func TestBucketPolicyResourceMatch(t *testing.T) {
// generates statement with given resource.. // generates statement with given resource..
generateStatement := func(resource string) policyStatement { generateStatement := func(resource string) policy.Statement {
statement := policyStatement{} statement := policy.Statement{}
statement.Resources = set.CreateStringSet([]string{resource}...) statement.Resources = set.CreateStringSet([]string{resource}...)
return statement return statement
} }
@ -45,7 +46,7 @@ func TestBucketPolicyResourceMatch(t *testing.T) {
testCases := []struct { testCases := []struct {
resourceToMatch string resourceToMatch string
statement policyStatement statement policy.Statement
expectedResourceMatch bool expectedResourceMatch bool
}{ }{
// Test case 1-4. // Test case 1-4.
@ -85,7 +86,7 @@ func TestBucketPolicyResourceMatch(t *testing.T) {
} }
// TestBucketPolicyActionMatch - Test validates whether given action on the // TestBucketPolicyActionMatch - Test validates whether given action on the
// bucket/object matches the allowed actions in policyStatement. // bucket/object matches the allowed actions in policy.Statement.
// This test preserves the allowed actions for all 3 sets of policies, that is read-write,read-only, write-only. // This test preserves the allowed actions for all 3 sets of policies, that is read-write,read-only, write-only.
// The intention of the test is to catch any changes made to allowed action for on eof the above 3 major policy groups mentioned. // The intention of the test is to catch any changes made to allowed action for on eof the above 3 major policy groups mentioned.
func TestBucketPolicyActionMatch(t *testing.T) { func TestBucketPolicyActionMatch(t *testing.T) {
@ -94,7 +95,7 @@ func TestBucketPolicyActionMatch(t *testing.T) {
testCases := []struct { testCases := []struct {
action string action string
statement policyStatement statement policy.Statement
expectedResult bool expectedResult bool
}{ }{
// s3:GetBucketLocation is the action necessary to be present in the bucket policy to allow // s3:GetBucketLocation is the action necessary to be present in the bucket policy to allow
@ -843,28 +844,28 @@ func testDeleteBucketPolicyHandler(obj ObjectLayer, instanceType, bucketName str
// TestBucketPolicyConditionMatch - Tests to validate whether bucket policy conditions match. // TestBucketPolicyConditionMatch - Tests to validate whether bucket policy conditions match.
func TestBucketPolicyConditionMatch(t *testing.T) { func TestBucketPolicyConditionMatch(t *testing.T) {
// obtain the inner map[string]set.StringSet for policyStatement.Conditions. // obtain the inner map[string]set.StringSet for policy.Statement.Conditions.
getInnerMap := func(key2, value string) map[string]set.StringSet { getInnerMap := func(key2, value string) map[string]set.StringSet {
innerMap := make(map[string]set.StringSet) innerMap := make(map[string]set.StringSet)
innerMap[key2] = set.CreateStringSet(value) innerMap[key2] = set.CreateStringSet(value)
return innerMap return innerMap
} }
// obtain policyStatement with Conditions set. // obtain policy.Statement with Conditions set.
getStatementWithCondition := func(key1, key2, value string) policyStatement { getStatementWithCondition := func(key1, key2, value string) policy.Statement {
innerMap := getInnerMap(key2, value) innerMap := getInnerMap(key2, value)
// to set policyStatment.Conditions . // to set policyStatment.Conditions .
conditions := make(map[string]map[string]set.StringSet) conditions := make(policy.ConditionMap)
conditions[key1] = innerMap conditions[key1] = innerMap
// new policy statement. // new policy statement.
statement := policyStatement{} statement := policy.Statement{}
// set the condition. // set the condition.
statement.Conditions = conditions statement.Conditions = conditions
return statement return statement
} }
testCases := []struct { testCases := []struct {
statementCondition policyStatement statementCondition policy.Statement
condition map[string]set.StringSet condition map[string]set.StringSet
expectedMatch bool expectedMatch bool

@ -26,9 +26,12 @@ import (
"sort" "sort"
"strings" "strings"
"github.com/minio/minio-go/pkg/policy"
"github.com/minio/minio-go/pkg/set" "github.com/minio/minio-go/pkg/set"
) )
var emptyBucketPolicy = policy.BucketAccessPolicy{}
var conditionKeyActionMap = map[string]set.StringSet{ var conditionKeyActionMap = map[string]set.StringSet{
"s3:prefix": set.CreateStringSet("s3:ListBucket"), "s3:prefix": set.CreateStringSet("s3:ListBucket"),
"s3:max-keys": set.CreateStringSet("s3:ListBucket"), "s3:max-keys": set.CreateStringSet("s3:ListBucket"),
@ -49,41 +52,16 @@ var supportedConditionsKey = set.CreateStringSet("s3:prefix", "s3:max-keys", "aw
// supportedEffectMap - supported effects. // supportedEffectMap - supported effects.
var supportedEffectMap = set.CreateStringSet("Allow", "Deny") var supportedEffectMap = set.CreateStringSet("Allow", "Deny")
// Statement - minio policy statement
type policyStatement struct {
Actions set.StringSet `json:"Action"`
Conditions map[string]map[string]set.StringSet `json:"Condition,omitempty"`
Effect string
Principal interface{} `json:"Principal"`
Resources set.StringSet `json:"Resource"`
Sid string
}
// bucketPolicy - collection of various bucket policy statements.
type bucketPolicy struct {
Version string // date in YYYY-MM-DD format
Statements []policyStatement `json:"Statement"`
}
// Stringer implementation for the bucket policies.
func (b bucketPolicy) String() string {
bbytes, err := json.Marshal(&b)
if err != nil {
errorIf(err, "Unable to marshal bucket policy into JSON %#v", b)
return ""
}
return string(bbytes)
}
// isValidActions - are actions valid. // isValidActions - are actions valid.
func isValidActions(actions set.StringSet) (err error) { func isValidActions(actions set.StringSet) (err error) {
// Statement actions cannot be empty. // Statement actions cannot be empty.
if len(actions) == 0 { if actions.IsEmpty() {
err = errors.New("Action list cannot be empty") err = errors.New("Action list cannot be empty")
return err return err
} }
if unsupportedActions := actions.Difference(supportedActionMap); !unsupportedActions.IsEmpty() { if unsupportedActions := actions.Difference(supportedActionMap); !unsupportedActions.IsEmpty() {
err = fmt.Errorf("Unsupported actions found: ‘%#v’, please validate your policy document", unsupportedActions) err = fmt.Errorf("Unsupported actions found: ‘%#v’, please validate your policy document",
unsupportedActions)
return err return err
} }
return nil return nil
@ -106,7 +84,7 @@ func isValidEffect(effect string) (err error) {
// isValidResources - are valid resources. // isValidResources - are valid resources.
func isValidResources(resources set.StringSet) (err error) { func isValidResources(resources set.StringSet) (err error) {
// Statement resources cannot be empty. // Statement resources cannot be empty.
if len(resources) == 0 { if resources.IsEmpty() {
err = errors.New("Resource list cannot be empty") err = errors.New("Resource list cannot be empty")
return err return err
} }
@ -124,60 +102,17 @@ func isValidResources(resources set.StringSet) (err error) {
return nil return nil
} }
// Parse principals parses a incoming json. Handles cases for
// these three combinations.
// - "Principal": "*",
// - "Principal": { "AWS" : "*" }
// - "Principal": { "AWS" : [ "*" ]}
func parsePrincipals(principal interface{}) set.StringSet {
principals, ok := principal.(map[string]interface{})
if !ok {
var principalStr string
principalStr, ok = principal.(string)
if ok {
return set.CreateStringSet(principalStr)
}
} // else {
var principalStrs []string
for _, p := range principals {
principalStr, isStr := p.(string)
if !isStr {
principalsAdd, isInterface := p.([]interface{})
if !isInterface {
principalStrsAddr, isStrs := p.([]string)
if !isStrs {
continue
}
principalStrs = append(principalStrs, principalStrsAddr...)
} else {
for _, pa := range principalsAdd {
var pstr string
pstr, isStr = pa.(string)
if !isStr {
continue
}
principalStrs = append(principalStrs, pstr)
}
}
continue
} // else {
principalStrs = append(principalStrs, principalStr)
}
return set.CreateStringSet(principalStrs...)
}
// isValidPrincipals - are valid principals. // isValidPrincipals - are valid principals.
func isValidPrincipals(principal interface{}) (err error) { func isValidPrincipals(principal policy.User) (err error) {
principals := parsePrincipals(principal) if principal.AWS.IsEmpty() {
// Statement principal should have a value. return errors.New("Principal cannot be empty")
if len(principals) == 0 {
err = errors.New("Principal cannot be empty")
return err
} }
if unsuppPrincipals := principals.Difference(set.CreateStringSet([]string{"*"}...)); !unsuppPrincipals.IsEmpty() { if diff := principal.AWS.Difference(set.CreateStringSet("*")); !diff.IsEmpty() {
// Minio does not support or implement IAM, "*" is the only valid value. // Minio does not support or implement IAM, "*" is the only valid value.
// Amazon s3 doc on principals: http://docs.aws.amazon.com/IAM/latest/UserGuide/reference_policies_elements.html#Principal // Amazon s3 doc on principal:
err = fmt.Errorf("Unsupported principals found: ‘%#v’, please validate your policy document", unsuppPrincipals) // http://docs.aws.amazon.com/IAM/latest/UserGuide/reference_policies_elements.html#Principal
err = fmt.Errorf("Unsupported principals found: ‘%#v’, please validate your policy document",
diff)
return err return err
} }
return nil return nil
@ -185,7 +120,7 @@ func isValidPrincipals(principal interface{}) (err error) {
// isValidConditions - returns nil if the given conditions valid and // isValidConditions - returns nil if the given conditions valid and
// corresponding error otherwise. // corresponding error otherwise.
func isValidConditions(actions set.StringSet, conditions map[string]map[string]set.StringSet) (err error) { func isValidConditions(actions set.StringSet, conditions policy.ConditionMap) (err error) {
// Verify conditions should be valid. Validate if only // Verify conditions should be valid. Validate if only
// supported condition keys are present and return error // supported condition keys are present and return error
// otherwise. // otherwise.
@ -239,7 +174,7 @@ func resourcePrefix(resource string) string {
// checkBucketPolicyResources validates Resources in unmarshalled bucket policy structure. // checkBucketPolicyResources validates Resources in unmarshalled bucket policy structure.
// - Resources are validated against the given set of Actions. // - Resources are validated against the given set of Actions.
// - // -
func checkBucketPolicyResources(bucket string, bucketPolicy *bucketPolicy) APIErrorCode { func checkBucketPolicyResources(bucket string, bucketPolicy policy.BucketAccessPolicy) APIErrorCode {
// Validate statements for special actions and collect resources // Validate statements for special actions and collect resources
// for others to validate nesting. // for others to validate nesting.
var resourceMap = set.NewStringSet() var resourceMap = set.NewStringSet()
@ -294,27 +229,27 @@ func checkBucketPolicyResources(bucket string, bucketPolicy *bucketPolicy) APIEr
// parseBucketPolicy - parses and validates if bucket policy is of // parseBucketPolicy - parses and validates if bucket policy is of
// proper JSON and follows allowed restrictions with policy standards. // proper JSON and follows allowed restrictions with policy standards.
func parseBucketPolicy(bucketPolicyReader io.Reader, policy *bucketPolicy) (err error) { func parseBucketPolicy(bucketPolicyReader io.Reader, bktPolicy *policy.BucketAccessPolicy) (err error) {
// Parse bucket policy reader. // Parse bucket policy reader.
decoder := json.NewDecoder(bucketPolicyReader) decoder := json.NewDecoder(bucketPolicyReader)
if err = decoder.Decode(&policy); err != nil { if err = decoder.Decode(bktPolicy); err != nil {
return err return err
} }
// Policy version cannot be empty. // Policy version cannot be empty.
if len(policy.Version) == 0 { if len(bktPolicy.Version) == 0 {
err = errors.New("Policy version cannot be empty") err = errors.New("Policy version cannot be empty")
return err return err
} }
// Policy statements cannot be empty. // Policy statements cannot be empty.
if len(policy.Statements) == 0 { if len(bktPolicy.Statements) == 0 {
err = errors.New("Policy statement cannot be empty") err = errors.New("Policy statement cannot be empty")
return err return err
} }
// Loop through all policy statements and validate entries. // Loop through all policy statements and validate entries.
for _, statement := range policy.Statements { for _, statement := range bktPolicy.Statements {
// Statement effect should be valid. // Statement effect should be valid.
if err := isValidEffect(statement.Effect); err != nil { if err := isValidEffect(statement.Effect); err != nil {
return err return err
@ -339,19 +274,20 @@ func parseBucketPolicy(bucketPolicyReader io.Reader, policy *bucketPolicy) (err
// Separate deny and allow statements, so that we can apply deny // Separate deny and allow statements, so that we can apply deny
// statements in the beginning followed by Allow statements. // statements in the beginning followed by Allow statements.
var denyStatements []policyStatement var denyStatements []policy.Statement
var allowStatements []policyStatement var allowStatements []policy.Statement
for _, statement := range policy.Statements { for _, statement := range bktPolicy.Statements {
if statement.Effect == "Deny" { if statement.Effect == "Deny" {
denyStatements = append(denyStatements, statement) denyStatements = append(denyStatements, statement)
continue continue
} }
// else if statement.Effect == "Allow" // else if statement.Effect == "Allow"
allowStatements = append(allowStatements, statement) allowStatements = append(allowStatements, statement)
} }
// Deny statements are enforced first once matched. // Deny statements are enforced first once matched.
policy.Statements = append(denyStatements, allowStatements...) bktPolicy.Statements = append(denyStatements, allowStatements...)
// Return successfully parsed policy structure. // Return successfully parsed policy structure.
return nil return nil

@ -20,6 +20,7 @@ import (
"encoding/json" "encoding/json"
"errors" "errors"
"fmt" "fmt"
"reflect"
"testing" "testing"
"github.com/minio/minio-go/pkg/policy" "github.com/minio/minio-go/pkg/policy"
@ -74,11 +75,11 @@ var (
) )
// Obtain bucket statement for read-write bucketPolicy. // Obtain bucket statement for read-write bucketPolicy.
func getReadWriteObjectStatement(bucketName, objectPrefix string) policyStatement { func getReadWriteObjectStatement(bucketName, objectPrefix string) policy.Statement {
objectResourceStatement := policyStatement{} objectResourceStatement := policy.Statement{}
objectResourceStatement.Effect = "Allow" objectResourceStatement.Effect = "Allow"
objectResourceStatement.Principal = map[string]interface{}{ objectResourceStatement.Principal = policy.User{
"AWS": "*", AWS: set.StringSet{"*": struct{}{}},
} }
objectResourceStatement.Resources = set.CreateStringSet([]string{fmt.Sprintf("%s%s", bucketARNPrefix, bucketName+"/"+objectPrefix+"*")}...) objectResourceStatement.Resources = set.CreateStringSet([]string{fmt.Sprintf("%s%s", bucketARNPrefix, bucketName+"/"+objectPrefix+"*")}...)
objectResourceStatement.Actions = set.CreateStringSet(readWriteObjectActions...) objectResourceStatement.Actions = set.CreateStringSet(readWriteObjectActions...)
@ -86,11 +87,11 @@ func getReadWriteObjectStatement(bucketName, objectPrefix string) policyStatemen
} }
// Obtain object statement for read-write bucketPolicy. // Obtain object statement for read-write bucketPolicy.
func getReadWriteBucketStatement(bucketName, objectPrefix string) policyStatement { func getReadWriteBucketStatement(bucketName, objectPrefix string) policy.Statement {
bucketResourceStatement := policyStatement{} bucketResourceStatement := policy.Statement{}
bucketResourceStatement.Effect = "Allow" bucketResourceStatement.Effect = "Allow"
bucketResourceStatement.Principal = map[string]interface{}{ bucketResourceStatement.Principal = policy.User{
"AWS": "*", AWS: set.StringSet{"*": struct{}{}},
} }
bucketResourceStatement.Resources = set.CreateStringSet([]string{fmt.Sprintf("%s%s", bucketARNPrefix, bucketName)}...) bucketResourceStatement.Resources = set.CreateStringSet([]string{fmt.Sprintf("%s%s", bucketARNPrefix, bucketName)}...)
bucketResourceStatement.Actions = set.CreateStringSet(readWriteBucketActions...) bucketResourceStatement.Actions = set.CreateStringSet(readWriteBucketActions...)
@ -98,19 +99,19 @@ func getReadWriteBucketStatement(bucketName, objectPrefix string) policyStatemen
} }
// Obtain statements for read-write bucketPolicy. // Obtain statements for read-write bucketPolicy.
func getReadWriteStatement(bucketName, objectPrefix string) []policyStatement { func getReadWriteStatement(bucketName, objectPrefix string) []policy.Statement {
statements := []policyStatement{} statements := []policy.Statement{}
// Save the read write policy. // Save the read write policy.
statements = append(statements, getReadWriteBucketStatement(bucketName, objectPrefix), getReadWriteObjectStatement(bucketName, objectPrefix)) statements = append(statements, getReadWriteBucketStatement(bucketName, objectPrefix), getReadWriteObjectStatement(bucketName, objectPrefix))
return statements return statements
} }
// Obtain bucket statement for read only bucketPolicy. // Obtain bucket statement for read only bucketPolicy.
func getReadOnlyBucketStatement(bucketName, objectPrefix string) policyStatement { func getReadOnlyBucketStatement(bucketName, objectPrefix string) policy.Statement {
bucketResourceStatement := policyStatement{} bucketResourceStatement := policy.Statement{}
bucketResourceStatement.Effect = "Allow" bucketResourceStatement.Effect = "Allow"
bucketResourceStatement.Principal = map[string]interface{}{ bucketResourceStatement.Principal = policy.User{
"AWS": "*", AWS: set.StringSet{"*": struct{}{}},
} }
bucketResourceStatement.Resources = set.CreateStringSet([]string{fmt.Sprintf("%s%s", bucketARNPrefix, bucketName)}...) bucketResourceStatement.Resources = set.CreateStringSet([]string{fmt.Sprintf("%s%s", bucketARNPrefix, bucketName)}...)
bucketResourceStatement.Actions = set.CreateStringSet(readOnlyBucketActions...) bucketResourceStatement.Actions = set.CreateStringSet(readOnlyBucketActions...)
@ -118,11 +119,11 @@ func getReadOnlyBucketStatement(bucketName, objectPrefix string) policyStatement
} }
// Obtain object statement for read only bucketPolicy. // Obtain object statement for read only bucketPolicy.
func getReadOnlyObjectStatement(bucketName, objectPrefix string) policyStatement { func getReadOnlyObjectStatement(bucketName, objectPrefix string) policy.Statement {
objectResourceStatement := policyStatement{} objectResourceStatement := policy.Statement{}
objectResourceStatement.Effect = "Allow" objectResourceStatement.Effect = "Allow"
objectResourceStatement.Principal = map[string]interface{}{ objectResourceStatement.Principal = policy.User{
"AWS": "*", AWS: set.StringSet{"*": struct{}{}},
} }
objectResourceStatement.Resources = set.CreateStringSet([]string{fmt.Sprintf("%s%s", bucketARNPrefix, bucketName+"/"+objectPrefix+"*")}...) objectResourceStatement.Resources = set.CreateStringSet([]string{fmt.Sprintf("%s%s", bucketARNPrefix, bucketName+"/"+objectPrefix+"*")}...)
objectResourceStatement.Actions = set.CreateStringSet(readOnlyObjectActions...) objectResourceStatement.Actions = set.CreateStringSet(readOnlyObjectActions...)
@ -130,20 +131,20 @@ func getReadOnlyObjectStatement(bucketName, objectPrefix string) policyStatement
} }
// Obtain statements for read only bucketPolicy. // Obtain statements for read only bucketPolicy.
func getReadOnlyStatement(bucketName, objectPrefix string) []policyStatement { func getReadOnlyStatement(bucketName, objectPrefix string) []policy.Statement {
statements := []policyStatement{} statements := []policy.Statement{}
// Save the read only policy. // Save the read only policy.
statements = append(statements, getReadOnlyBucketStatement(bucketName, objectPrefix), getReadOnlyObjectStatement(bucketName, objectPrefix)) statements = append(statements, getReadOnlyBucketStatement(bucketName, objectPrefix), getReadOnlyObjectStatement(bucketName, objectPrefix))
return statements return statements
} }
// Obtain bucket statements for write only bucketPolicy. // Obtain bucket statements for write only bucketPolicy.
func getWriteOnlyBucketStatement(bucketName, objectPrefix string) policyStatement { func getWriteOnlyBucketStatement(bucketName, objectPrefix string) policy.Statement {
bucketResourceStatement := policyStatement{} bucketResourceStatement := policy.Statement{}
bucketResourceStatement.Effect = "Allow" bucketResourceStatement.Effect = "Allow"
bucketResourceStatement.Principal = map[string]interface{}{ bucketResourceStatement.Principal = policy.User{
"AWS": "*", AWS: set.StringSet{"*": struct{}{}},
} }
bucketResourceStatement.Resources = set.CreateStringSet([]string{fmt.Sprintf("%s%s", bucketARNPrefix, bucketName)}...) bucketResourceStatement.Resources = set.CreateStringSet([]string{fmt.Sprintf("%s%s", bucketARNPrefix, bucketName)}...)
bucketResourceStatement.Actions = set.CreateStringSet(writeOnlyBucketActions...) bucketResourceStatement.Actions = set.CreateStringSet(writeOnlyBucketActions...)
@ -151,11 +152,11 @@ func getWriteOnlyBucketStatement(bucketName, objectPrefix string) policyStatemen
} }
// Obtain object statements for write only bucketPolicy. // Obtain object statements for write only bucketPolicy.
func getWriteOnlyObjectStatement(bucketName, objectPrefix string) policyStatement { func getWriteOnlyObjectStatement(bucketName, objectPrefix string) policy.Statement {
objectResourceStatement := policyStatement{} objectResourceStatement := policy.Statement{}
objectResourceStatement.Effect = "Allow" objectResourceStatement.Effect = "Allow"
objectResourceStatement.Principal = map[string]interface{}{ objectResourceStatement.Principal = policy.User{
"AWS": "*", AWS: set.StringSet{"*": struct{}{}},
} }
objectResourceStatement.Resources = set.CreateStringSet([]string{fmt.Sprintf("%s%s", bucketARNPrefix, bucketName+"/"+objectPrefix+"*")}...) objectResourceStatement.Resources = set.CreateStringSet([]string{fmt.Sprintf("%s%s", bucketARNPrefix, bucketName+"/"+objectPrefix+"*")}...)
objectResourceStatement.Actions = set.CreateStringSet(writeOnlyObjectActions...) objectResourceStatement.Actions = set.CreateStringSet(writeOnlyObjectActions...)
@ -163,8 +164,8 @@ func getWriteOnlyObjectStatement(bucketName, objectPrefix string) policyStatemen
} }
// Obtain statements for write only bucketPolicy. // Obtain statements for write only bucketPolicy.
func getWriteOnlyStatement(bucketName, objectPrefix string) []policyStatement { func getWriteOnlyStatement(bucketName, objectPrefix string) []policy.Statement {
statements := []policyStatement{} statements := []policy.Statement{}
// Write only policy. // Write only policy.
// Save the write only policy. // Save the write only policy.
statements = append(statements, getWriteOnlyBucketStatement(bucketName, objectPrefix), getWriteOnlyBucketStatement(bucketName, objectPrefix)) statements = append(statements, getWriteOnlyBucketStatement(bucketName, objectPrefix), getWriteOnlyBucketStatement(bucketName, objectPrefix))
@ -325,9 +326,10 @@ func TestIsValidPrincipals(t *testing.T) {
{[]string{"*"}, nil, true}, {[]string{"*"}, nil, true},
} }
for i, testCase := range testCases { for i, testCase := range testCases {
err := isValidPrincipals(map[string]interface{}{ u := policy.User{
"AWS": testCase.principals, AWS: set.CreateStringSet(testCase.principals...),
}) }
err := isValidPrincipals(u)
if err != nil && testCase.shouldPass { if err != nil && testCase.shouldPass {
t.Errorf("Test %d: Expected to pass, but failed with: <ERROR> %s", i+1, err.Error()) t.Errorf("Test %d: Expected to pass, but failed with: <ERROR> %s", i+1, err.Error())
} }
@ -343,86 +345,86 @@ func TestIsValidPrincipals(t *testing.T) {
} }
} }
// getEmptyConditionKeyMap - returns a function that generates a // getEmptyConditionMap - returns a function that generates a
// condition key map for a given key. // condition key map for a given key.
func getEmptyConditionKeyMap(conditionKey string) func() map[string]map[string]set.StringSet { func getEmptyConditionMap(conditionKey string) func() policy.ConditionMap {
emptyConditonGenerator := func() map[string]map[string]set.StringSet { emptyConditonGenerator := func() policy.ConditionMap {
emptyMap := make(map[string]set.StringSet) emptyMap := make(policy.ConditionKeyMap)
conditions := make(map[string]map[string]set.StringSet) conditions := make(policy.ConditionMap)
conditions[conditionKey] = emptyMap conditions[conditionKey] = emptyMap
return conditions return conditions
} }
return emptyConditonGenerator return emptyConditonGenerator
} }
// Tests validate policyStatement condition validator. // Tests validate policy.Statement condition validator.
func TestIsValidConditions(t *testing.T) { func TestIsValidConditions(t *testing.T) {
// returns empty conditions map. // returns empty conditions map.
setEmptyConditions := func() map[string]map[string]set.StringSet { setEmptyConditions := func() policy.ConditionMap {
return make(map[string]map[string]set.StringSet) return make(policy.ConditionMap)
} }
// returns map with the "StringEquals" set to empty map. // returns map with the "StringEquals" set to empty map.
setEmptyStringEquals := getEmptyConditionKeyMap("StringEquals") setEmptyStringEquals := getEmptyConditionMap("StringEquals")
// returns map with the "StringNotEquals" set to empty map. // returns map with the "StringNotEquals" set to empty map.
setEmptyStringNotEquals := getEmptyConditionKeyMap("StringNotEquals") setEmptyStringNotEquals := getEmptyConditionMap("StringNotEquals")
// returns map with the "StringLike" set to empty map. // returns map with the "StringLike" set to empty map.
setEmptyStringLike := getEmptyConditionKeyMap("StringLike") setEmptyStringLike := getEmptyConditionMap("StringLike")
// returns map with the "StringNotLike" set to empty map. // returns map with the "StringNotLike" set to empty map.
setEmptyStringNotLike := getEmptyConditionKeyMap("StringNotLike") setEmptyStringNotLike := getEmptyConditionMap("StringNotLike")
// returns map with the "IpAddress" set to empty map. // returns map with the "IpAddress" set to empty map.
setEmptyIPAddress := getEmptyConditionKeyMap("IpAddress") setEmptyIPAddress := getEmptyConditionMap("IpAddress")
// returns map with "NotIpAddress" set to empty map. // returns map with "NotIpAddress" set to empty map.
setEmptyNotIPAddress := getEmptyConditionKeyMap("NotIpAddress") setEmptyNotIPAddress := getEmptyConditionMap("NotIpAddress")
// Generate conditions. // Generate conditions.
generateConditions := func(key1, key2, value string) map[string]map[string]set.StringSet { generateConditions := func(key1, key2, value string) policy.ConditionMap {
innerMap := make(map[string]set.StringSet) innerMap := make(policy.ConditionKeyMap)
innerMap[key2] = set.CreateStringSet(value) innerMap[key2] = set.CreateStringSet(value)
conditions := make(map[string]map[string]set.StringSet) conditions := make(policy.ConditionMap)
conditions[key1] = innerMap conditions[key1] = innerMap
return conditions return conditions
} }
// generate ambigious conditions. // generate ambigious conditions.
generateAmbigiousConditions := func() map[string]map[string]set.StringSet { generateAmbigiousConditions := func() policy.ConditionMap {
prefixMap := make(map[string]set.StringSet) prefixMap := make(policy.ConditionKeyMap)
prefixMap["s3:prefix"] = set.CreateStringSet("Asia/") prefixMap["s3:prefix"] = set.CreateStringSet("Asia/")
conditions := make(map[string]map[string]set.StringSet) conditions := make(policy.ConditionMap)
conditions["StringEquals"] = prefixMap conditions["StringEquals"] = prefixMap
conditions["StringNotEquals"] = prefixMap conditions["StringNotEquals"] = prefixMap
return conditions return conditions
} }
// generate valid and non valid type in the condition map. // generate valid and non valid type in the condition map.
generateValidInvalidConditions := func() map[string]map[string]set.StringSet { generateValidInvalidConditions := func() policy.ConditionMap {
innerMap := make(map[string]set.StringSet) innerMap := make(policy.ConditionKeyMap)
innerMap["s3:prefix"] = set.CreateStringSet("Asia/") innerMap["s3:prefix"] = set.CreateStringSet("Asia/")
conditions := make(map[string]map[string]set.StringSet) conditions := make(policy.ConditionMap)
conditions["StringEquals"] = innerMap conditions["StringEquals"] = innerMap
conditions["InvalidType"] = innerMap conditions["InvalidType"] = innerMap
return conditions return conditions
} }
// generate valid and invalid keys for valid types in the same condition map. // generate valid and invalid keys for valid types in the same condition map.
generateValidInvalidConditionKeys := func() map[string]map[string]set.StringSet { generateValidInvalidConditionKeys := func() policy.ConditionMap {
innerMapValid := make(map[string]set.StringSet) innerMapValid := make(policy.ConditionKeyMap)
innerMapValid["s3:prefix"] = set.CreateStringSet("Asia/") innerMapValid["s3:prefix"] = set.CreateStringSet("Asia/")
innerMapInValid := make(map[string]set.StringSet) innerMapInValid := make(map[string]set.StringSet)
innerMapInValid["s3:invalid"] = set.CreateStringSet("Asia/") innerMapInValid["s3:invalid"] = set.CreateStringSet("Asia/")
conditions := make(map[string]map[string]set.StringSet) conditions := make(policy.ConditionMap)
conditions["StringEquals"] = innerMapValid conditions["StringEquals"] = innerMapValid
conditions["StringEquals"] = innerMapInValid conditions["StringEquals"] = innerMapInValid
return conditions return conditions
} }
// List of Conditions used for test cases. // List of Conditions used for test cases.
testConditions := []map[string]map[string]set.StringSet{ testConditions := []policy.ConditionMap{
generateConditions("StringValues", "s3:max-keys", "100"), generateConditions("StringValues", "s3:max-keys", "100"),
generateConditions("StringEquals", "s3:Object", "100"), generateConditions("StringEquals", "s3:Object", "100"),
generateAmbigiousConditions(), generateAmbigiousConditions(),
@ -447,7 +449,7 @@ func TestIsValidConditions(t *testing.T) {
"please validate your policy document", "s3:max-keys", getObjectActionSet) "please validate your policy document", "s3:max-keys", getObjectActionSet)
testCases := []struct { testCases := []struct {
inputActions set.StringSet inputActions set.StringSet
inputCondition map[string]map[string]set.StringSet inputCondition policy.ConditionMap
// expected result. // expected result.
expectedErr error expectedErr error
// flag indicating whether test should pass. // flag indicating whether test should pass.
@ -518,26 +520,26 @@ func TestIsValidConditions(t *testing.T) {
// Tests validate Policy Action and Resource fields. // Tests validate Policy Action and Resource fields.
func TestCheckbucketPolicyResources(t *testing.T) { func TestCheckbucketPolicyResources(t *testing.T) {
// constructing policy statement without invalidPrefixActions (check bucket-policy-parser.go). // constructing policy statement without invalidPrefixActions (check bucket-policy-parser.go).
setValidPrefixActions := func(statements []policyStatement) []policyStatement { setValidPrefixActions := func(statements []policy.Statement) []policy.Statement {
statements[0].Actions = set.CreateStringSet([]string{"s3:DeleteObject", "s3:PutObject"}...) statements[0].Actions = set.CreateStringSet([]string{"s3:DeleteObject", "s3:PutObject"}...)
return statements return statements
} }
// contracting policy statement with recursive resources. // contracting policy statement with recursive resources.
// should result in ErrMalformedPolicy // should result in ErrMalformedPolicy
setRecurseResource := func(statements []policyStatement) []policyStatement { setRecurseResource := func(statements []policy.Statement) []policy.Statement {
statements[0].Resources = set.CreateStringSet([]string{"arn:aws:s3:::minio-bucket/Asia/*", "arn:aws:s3:::minio-bucket/Asia/India/*"}...) statements[0].Resources = set.CreateStringSet([]string{"arn:aws:s3:::minio-bucket/Asia/*", "arn:aws:s3:::minio-bucket/Asia/India/*"}...)
return statements return statements
} }
// constructing policy statement with lexically close characters. // constructing policy statement with lexically close characters.
// should not result in ErrMalformedPolicy // should not result in ErrMalformedPolicy
setResourceLexical := func(statements []policyStatement) []policyStatement { setResourceLexical := func(statements []policy.Statement) []policy.Statement {
statements[0].Resources = set.CreateStringSet([]string{"arn:aws:s3:::minio-bucket/op*", "arn:aws:s3:::minio-bucket/oo*"}...) statements[0].Resources = set.CreateStringSet([]string{"arn:aws:s3:::minio-bucket/op*", "arn:aws:s3:::minio-bucket/oo*"}...)
return statements return statements
} }
// List of bucketPolicy used for tests. // List of bucketPolicy used for tests.
bucketAccessPolicies := []bucketPolicy{ bucketAccessPolicies := []policy.BucketAccessPolicy{
// bucketPolicy - 1. // bucketPolicy - 1.
// Contains valid read only policy statement. // Contains valid read only policy statement.
{Version: "1.0", Statements: getReadOnlyStatement("minio-bucket", "")}, {Version: "1.0", Statements: getReadOnlyStatement("minio-bucket", "")},
@ -568,7 +570,7 @@ func TestCheckbucketPolicyResources(t *testing.T) {
} }
testCases := []struct { testCases := []struct {
inputPolicy bucketPolicy inputPolicy policy.BucketAccessPolicy
// expected results. // expected results.
apiErrCode APIErrorCode apiErrCode APIErrorCode
// Flag indicating whether the test should pass. // Flag indicating whether the test should pass.
@ -599,7 +601,7 @@ func TestCheckbucketPolicyResources(t *testing.T) {
{bucketAccessPolicies[6], ErrNone, true}, {bucketAccessPolicies[6], ErrNone, true},
} }
for i, testCase := range testCases { for i, testCase := range testCases {
apiErrCode := checkBucketPolicyResources("minio-bucket", &testCase.inputPolicy) apiErrCode := checkBucketPolicyResources("minio-bucket", testCase.inputPolicy)
if apiErrCode != ErrNone && testCase.shouldPass { if apiErrCode != ErrNone && testCase.shouldPass {
t.Errorf("Test %d: Expected to pass, but failed with Errocode %v", i+1, apiErrCode) t.Errorf("Test %d: Expected to pass, but failed with Errocode %v", i+1, apiErrCode)
} }
@ -618,39 +620,39 @@ func TestCheckbucketPolicyResources(t *testing.T) {
// Tests validate parsing of BucketAccessPolicy. // Tests validate parsing of BucketAccessPolicy.
func TestParseBucketPolicy(t *testing.T) { func TestParseBucketPolicy(t *testing.T) {
// set Unsupported Actions. // set Unsupported Actions.
setUnsupportedActions := func(statements []policyStatement) []policyStatement { setUnsupportedActions := func(statements []policy.Statement) []policy.Statement {
// "s3:DeleteEverything"" is an Unsupported Action. // "s3:DeleteEverything"" is an Unsupported Action.
statements[0].Actions = set.CreateStringSet([]string{"s3:GetObject", "s3:ListBucket", "s3:PutObject", "s3:DeleteEverything"}...) statements[0].Actions = set.CreateStringSet([]string{"s3:GetObject", "s3:ListBucket", "s3:PutObject", "s3:DeleteEverything"}...)
return statements return statements
} }
// set unsupported Effect. // set unsupported Effect.
setUnsupportedEffect := func(statements []policyStatement) []policyStatement { setUnsupportedEffect := func(statements []policy.Statement) []policy.Statement {
// Effect "Don't allow" is Unsupported. // Effect "Don't allow" is Unsupported.
statements[0].Effect = "DontAllow" statements[0].Effect = "DontAllow"
return statements return statements
} }
// set unsupported principals. // set unsupported principals.
setUnsupportedPrincipals := func(statements []policyStatement) []policyStatement { setUnsupportedPrincipals := func(statements []policy.Statement) []policy.Statement {
// "User1111"" is an Unsupported Principal. // "User1111"" is an Unsupported Principal.
statements[0].Principal = map[string]interface{}{ statements[0].Principal = policy.User{
"AWS": []string{"*", "User1111"}, AWS: set.CreateStringSet([]string{"*", "User1111"}...),
} }
return statements return statements
} }
// set unsupported Resources. // set unsupported Resources.
setUnsupportedResources := func(statements []policyStatement) []policyStatement { setUnsupportedResources := func(statements []policy.Statement) []policy.Statement {
// "s3:DeleteEverything"" is an Unsupported Action. // "s3:DeleteEverything"" is an Unsupported Action.
statements[0].Resources = set.CreateStringSet([]string{"my-resource"}...) statements[0].Resources = set.CreateStringSet([]string{"my-resource"}...)
return statements return statements
} }
// List of bucketPolicy used for test cases. // List of bucketPolicy used for test cases.
bucketAccesPolicies := []bucketPolicy{ bucketAccesPolicies := []policy.BucketAccessPolicy{
// bucketPolicy - 0. // bucketPolicy - 0.
// bucketPolicy statement empty. // bucketPolicy statement empty.
{Version: "1.0"}, {Version: "1.0"},
// bucketPolicy - 1. // bucketPolicy - 1.
// bucketPolicy version empty. // bucketPolicy version empty.
{Version: "", Statements: []policyStatement{}}, {Version: "", Statements: []policy.Statement{}},
// bucketPolicy - 2. // bucketPolicy - 2.
// Readonly bucketPolicy. // Readonly bucketPolicy.
{Version: "1.0", Statements: getReadOnlyStatement("minio-bucket", "")}, {Version: "1.0", Statements: getReadOnlyStatement("minio-bucket", "")},
@ -675,19 +677,19 @@ func TestParseBucketPolicy(t *testing.T) {
} }
testCases := []struct { testCases := []struct {
inputPolicy bucketPolicy inputPolicy policy.BucketAccessPolicy
// expected results. // expected results.
expectedPolicy bucketPolicy expectedPolicy policy.BucketAccessPolicy
err error err error
// Flag indicating whether the test should pass. // Flag indicating whether the test should pass.
shouldPass bool shouldPass bool
}{ }{
// Test case - 1. // Test case - 1.
// bucketPolicy statement empty. // bucketPolicy statement empty.
{bucketAccesPolicies[0], bucketPolicy{}, errors.New("Policy statement cannot be empty"), false}, {bucketAccesPolicies[0], policy.BucketAccessPolicy{}, errors.New("Policy statement cannot be empty"), false},
// Test case - 2. // Test case - 2.
// bucketPolicy version empty. // bucketPolicy version empty.
{bucketAccesPolicies[1], bucketPolicy{}, errors.New("Policy version cannot be empty"), false}, {bucketAccesPolicies[1], policy.BucketAccessPolicy{}, errors.New("Policy version cannot be empty"), false},
// Test case - 3. // Test case - 3.
// Readonly bucketPolicy. // Readonly bucketPolicy.
{bucketAccesPolicies[2], bucketAccesPolicies[2], nil, true}, {bucketAccesPolicies[2], bucketAccesPolicies[2], nil, true},
@ -718,8 +720,8 @@ func TestParseBucketPolicy(t *testing.T) {
t.Fatalf("Test %d: Couldn't Marshal bucket policy %s", i+1, err) t.Fatalf("Test %d: Couldn't Marshal bucket policy %s", i+1, err)
} }
var actualAccessPolicy = &bucketPolicy{} var actualAccessPolicy = policy.BucketAccessPolicy{}
err = parseBucketPolicy(&buffer, actualAccessPolicy) err = parseBucketPolicy(&buffer, &actualAccessPolicy)
if err != nil && testCase.shouldPass { if err != nil && testCase.shouldPass {
t.Errorf("Test %d: Expected to pass, but failed with: <ERROR> %s", i+1, err.Error()) t.Errorf("Test %d: Expected to pass, but failed with: <ERROR> %s", i+1, err.Error())
} }
@ -734,7 +736,7 @@ func TestParseBucketPolicy(t *testing.T) {
} }
// Test passes as expected, but the output values are verified for correctness here. // Test passes as expected, but the output values are verified for correctness here.
if err == nil && testCase.shouldPass { if err == nil && testCase.shouldPass {
if testCase.expectedPolicy.String() != actualAccessPolicy.String() { 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) t.Errorf("Test %d: The expected statements from resource statement generator doesn't match the actual statements", i+1)
} }
} }
@ -751,8 +753,8 @@ func TestAWSRefererCondition(t *testing.T) {
set.CreateStringSet("www.example.com", set.CreateStringSet("www.example.com",
"http://www.example.com")) "http://www.example.com"))
requestConditionKeyMap := make(map[string]set.StringSet) requestConditionMap := make(policy.ConditionKeyMap)
requestConditionKeyMap["referer"] = set.CreateStringSet("www.example.com") requestConditionMap["referer"] = set.CreateStringSet("www.example.com")
testCases := []struct { testCases := []struct {
effect string effect string
@ -782,20 +784,20 @@ func TestAWSRefererCondition(t *testing.T) {
} }
for i, test := range testCases { for i, test := range testCases {
conditions := make(map[string]map[string]set.StringSet) conditions := make(policy.ConditionMap)
conditions[test.conditionKey] = conditionsKeyMap conditions[test.conditionKey] = conditionsKeyMap
allowStatement := policyStatement{ allowStatement := policy.Statement{
Sid: "Testing AWS referer condition", Sid: "Testing AWS referer condition",
Effect: test.effect, Effect: test.effect,
Principal: map[string]interface{}{ Principal: policy.User{
"AWS": "*", AWS: set.CreateStringSet("*"),
}, },
Resources: resource, Resources: resource,
Conditions: conditions, Conditions: conditions,
} }
if result := bucketPolicyConditionMatch(requestConditionKeyMap, allowStatement); result != test.match { if result := bucketPolicyConditionMatch(requestConditionMap, allowStatement); result != test.match {
t.Errorf("Test %d - Expected conditons to evaluate to %v but got %v", t.Errorf("Test %d - Expected conditons to evaluate to %v but got %v",
i+1, test.match, result) i+1, test.match, result)
} }
@ -813,8 +815,8 @@ func TestAWSSourceIPCondition(t *testing.T) {
set.CreateStringSet("54.240.143.0/24", set.CreateStringSet("54.240.143.0/24",
"2001:DB8:1234:5678::/64")) "2001:DB8:1234:5678::/64"))
requestConditionKeyMap := make(map[string]set.StringSet) requestConditionMap := make(policy.ConditionKeyMap)
requestConditionKeyMap["ip"] = set.CreateStringSet("54.240.143.2") requestConditionMap["ip"] = set.CreateStringSet("54.240.143.2")
testCases := []struct { testCases := []struct {
effect string effect string
@ -844,20 +846,20 @@ func TestAWSSourceIPCondition(t *testing.T) {
} }
for i, test := range testCases { for i, test := range testCases {
conditions := make(map[string]map[string]set.StringSet) conditions := make(policy.ConditionMap)
conditions[test.conditionKey] = conditionsKeyMap conditions[test.conditionKey] = conditionsKeyMap
allowStatement := policyStatement{ allowStatement := policy.Statement{
Sid: "Testing AWS referer condition", Sid: "Testing AWS referer condition",
Effect: test.effect, Effect: test.effect,
Principal: map[string]interface{}{ Principal: policy.User{
"AWS": "*", AWS: set.CreateStringSet("*"),
}, },
Resources: resource, Resources: resource,
Conditions: conditions, Conditions: conditions,
} }
if result := bucketPolicyConditionMatch(requestConditionKeyMap, allowStatement); result != test.match { if result := bucketPolicyConditionMatch(requestConditionMap, allowStatement); result != test.match {
t.Errorf("Test %d - Expected conditons to evaluate to %v but got %v", t.Errorf("Test %d - Expected conditons to evaluate to %v but got %v",
i+1, test.match, result) i+1, test.match, result)
} }

@ -20,8 +20,10 @@ import (
"bytes" "bytes"
"encoding/json" "encoding/json"
"io" "io"
"reflect"
"sync" "sync"
"github.com/minio/minio-go/pkg/policy"
"github.com/minio/minio/pkg/hash" "github.com/minio/minio/pkg/hash"
) )
@ -43,7 +45,7 @@ type bucketPolicies struct {
rwMutex *sync.RWMutex rwMutex *sync.RWMutex
// Collection of 'bucket' policies. // Collection of 'bucket' policies.
bucketPolicyConfigs map[string]*bucketPolicy bucketPolicyConfigs map[string]policy.BucketAccessPolicy
} }
// Represent a policy change // Represent a policy change
@ -53,11 +55,11 @@ type policyChange struct {
IsRemove bool IsRemove bool
// represents the new policy for the bucket // represents the new policy for the bucket
BktPolicy *bucketPolicy BktPolicy policy.BucketAccessPolicy
} }
// Fetch bucket policy for a given bucket. // Fetch bucket policy for a given bucket.
func (bp bucketPolicies) GetBucketPolicy(bucket string) *bucketPolicy { func (bp bucketPolicies) GetBucketPolicy(bucket string) policy.BucketAccessPolicy {
bp.rwMutex.RLock() bp.rwMutex.RLock()
defer bp.rwMutex.RUnlock() defer bp.rwMutex.RUnlock()
return bp.bucketPolicyConfigs[bucket] return bp.bucketPolicyConfigs[bucket]
@ -72,7 +74,7 @@ func (bp *bucketPolicies) SetBucketPolicy(bucket string, pCh policyChange) error
if pCh.IsRemove { if pCh.IsRemove {
delete(bp.bucketPolicyConfigs, bucket) delete(bp.bucketPolicyConfigs, bucket)
} else { } else {
if pCh.BktPolicy == nil { if reflect.DeepEqual(pCh.BktPolicy, emptyBucketPolicy) {
return errInvalidArgument return errInvalidArgument
} }
bp.bucketPolicyConfigs[bucket] = pCh.BktPolicy bp.bucketPolicyConfigs[bucket] = pCh.BktPolicy
@ -81,19 +83,19 @@ func (bp *bucketPolicies) SetBucketPolicy(bucket string, pCh policyChange) error
} }
// Loads all bucket policies from persistent layer. // Loads all bucket policies from persistent layer.
func loadAllBucketPolicies(objAPI ObjectLayer) (policies map[string]*bucketPolicy, err error) { func loadAllBucketPolicies(objAPI ObjectLayer) (policies map[string]policy.BucketAccessPolicy, err error) {
// List buckets to proceed loading all notification configuration. // List buckets to proceed loading all notification configuration.
buckets, err := objAPI.ListBuckets() buckets, err := objAPI.ListBuckets()
errorIf(err, "Unable to list buckets.")
if err != nil { if err != nil {
errorIf(err, "Unable to list buckets.")
return nil, errorCause(err) return nil, errorCause(err)
} }
policies = make(map[string]*bucketPolicy) policies = make(map[string]policy.BucketAccessPolicy)
var pErrs []error var pErrs []error
// Loads bucket policy. // Loads bucket policy.
for _, bucket := range buckets { for _, bucket := range buckets {
policy, pErr := readBucketPolicy(bucket.Name, objAPI) bp, pErr := readBucketPolicy(bucket.Name, objAPI)
if pErr != nil { if pErr != nil {
// net.Dial fails for rpc client or any // net.Dial fails for rpc client or any
// other unexpected errors during net.Dial. // other unexpected errors during net.Dial.
@ -105,7 +107,7 @@ func loadAllBucketPolicies(objAPI ObjectLayer) (policies map[string]*bucketPolic
// Continue to load other bucket policies if possible. // Continue to load other bucket policies if possible.
continue continue
} }
policies[bucket.Name] = policy policies[bucket.Name] = bp
} }
// Look for any errors occurred while reading bucket policies. // Look for any errors occurred while reading bucket policies.
@ -168,21 +170,20 @@ func readBucketPolicyJSON(bucket string, objAPI ObjectLayer) (bucketPolicyReader
// readBucketPolicy - reads bucket policy for an input bucket, returns BucketPolicyNotFound // readBucketPolicy - reads bucket policy for an input bucket, returns BucketPolicyNotFound
// if bucket policy is not found. This function also parses the bucket policy into an object. // if bucket policy is not found. This function also parses the bucket policy into an object.
func readBucketPolicy(bucket string, objAPI ObjectLayer) (*bucketPolicy, error) { func readBucketPolicy(bucket string, objAPI ObjectLayer) (policy.BucketAccessPolicy, error) {
// Read bucket policy JSON. // Read bucket policy JSON.
bucketPolicyReader, err := readBucketPolicyJSON(bucket, objAPI) bucketPolicyReader, err := readBucketPolicyJSON(bucket, objAPI)
if err != nil { if err != nil {
return nil, err return emptyBucketPolicy, err
} }
// Parse the saved policy. // Parse the saved policy.
var policy = &bucketPolicy{} var bp policy.BucketAccessPolicy
err = parseBucketPolicy(bucketPolicyReader, policy) if err = parseBucketPolicy(bucketPolicyReader, &bp); err != nil {
if err != nil { return emptyBucketPolicy, err
return nil, err
} }
return policy, nil return bp, nil
} }
// removeBucketPolicy - removes any previously written bucket policy. Returns BucketPolicyNotFound // removeBucketPolicy - removes any previously written bucket policy. Returns BucketPolicyNotFound
@ -195,7 +196,8 @@ func removeBucketPolicy(bucket string, objAPI ObjectLayer) error {
return err return err
} }
defer objLock.Unlock() defer objLock.Unlock()
if err := objAPI.DeleteObject(minioMetaBucket, policyPath); err != nil { err := objAPI.DeleteObject(minioMetaBucket, policyPath)
if err != nil {
errorIf(err, "Unable to remove bucket-policy on bucket %s.", bucket) errorIf(err, "Unable to remove bucket-policy on bucket %s.", bucket)
err = errorCause(err) err = errorCause(err)
if _, ok := err.(ObjectNotFound); ok { if _, ok := err.(ObjectNotFound); ok {
@ -207,10 +209,10 @@ func removeBucketPolicy(bucket string, objAPI ObjectLayer) error {
} }
// writeBucketPolicy - save a bucket policy that is assumed to be validated. // writeBucketPolicy - save a bucket policy that is assumed to be validated.
func writeBucketPolicy(bucket string, objAPI ObjectLayer, bpy *bucketPolicy) error { func writeBucketPolicy(bucket string, objAPI ObjectLayer, bpy policy.BucketAccessPolicy) error {
buf, err := json.Marshal(bpy) buf, err := json.Marshal(bpy)
if err != nil { if err != nil {
errorIf(err, "Unable to marshal bucket policy '%v' to JSON", *bpy) errorIf(err, "Unable to marshal bucket policy '%#v' to JSON", bpy)
return err return err
} }
policyPath := pathJoin(bucketConfigPrefix, bucket, bucketPolicyConfig) policyPath := pathJoin(bucketConfigPrefix, bucket, bucketPolicyConfig)
@ -236,15 +238,15 @@ func writeBucketPolicy(bucket string, objAPI ObjectLayer, bpy *bucketPolicy) err
func parseAndPersistBucketPolicy(bucket string, policyBytes []byte, objAPI ObjectLayer) APIErrorCode { func parseAndPersistBucketPolicy(bucket string, policyBytes []byte, objAPI ObjectLayer) APIErrorCode {
// Parse bucket policy. // Parse bucket policy.
var policy = &bucketPolicy{} var bktPolicy policy.BucketAccessPolicy
err := parseBucketPolicy(bytes.NewReader(policyBytes), policy) err := parseBucketPolicy(bytes.NewReader(policyBytes), &bktPolicy)
if err != nil { if err != nil {
errorIf(err, "Unable to parse bucket policy.") errorIf(err, "Unable to parse bucket policy.")
return ErrInvalidPolicyDocument return ErrInvalidPolicyDocument
} }
// Parse check bucket policy. // Parse check bucket policy.
if s3Error := checkBucketPolicyResources(bucket, policy); s3Error != ErrNone { if s3Error := checkBucketPolicyResources(bucket, bktPolicy); s3Error != ErrNone {
return s3Error return s3Error
} }
@ -257,7 +259,7 @@ func parseAndPersistBucketPolicy(bucket string, policyBytes []byte, objAPI Objec
defer bucketLock.Unlock() defer bucketLock.Unlock()
// Save bucket policy. // Save bucket policy.
if err = persistAndNotifyBucketPolicyChange(bucket, policyChange{false, policy}, objAPI); err != nil { if err = persistAndNotifyBucketPolicyChange(bucket, policyChange{false, bktPolicy}, objAPI); err != nil {
switch err.(type) { switch err.(type) {
case BucketNameInvalid: case BucketNameInvalid:
return ErrInvalidBucketName return ErrInvalidBucketName
@ -276,11 +278,12 @@ func parseAndPersistBucketPolicy(bucket string, policyBytes []byte, objAPI Objec
// change. In-memory state is updated in response to the notification. // change. In-memory state is updated in response to the notification.
func persistAndNotifyBucketPolicyChange(bucket string, pCh policyChange, objAPI ObjectLayer) error { func persistAndNotifyBucketPolicyChange(bucket string, pCh policyChange, objAPI ObjectLayer) error {
if pCh.IsRemove { if pCh.IsRemove {
if err := removeBucketPolicy(bucket, objAPI); err != nil { err := removeBucketPolicy(bucket, objAPI)
if err != nil {
return err return err
} }
} else { } else {
if pCh.BktPolicy == nil { if reflect.DeepEqual(pCh.BktPolicy, emptyBucketPolicy) {
return errInvalidArgument return errInvalidArgument
} }
if err := writeBucketPolicy(bucket, objAPI, pCh.BktPolicy); err != nil { if err := writeBucketPolicy(bucket, objAPI, pCh.BktPolicy); err != nil {

@ -52,6 +52,7 @@ import (
"github.com/fatih/color" "github.com/fatih/color"
router "github.com/gorilla/mux" router "github.com/gorilla/mux"
"github.com/minio/minio-go/pkg/policy"
"github.com/minio/minio-go/pkg/s3signer" "github.com/minio/minio-go/pkg/s3signer"
"github.com/minio/minio/pkg/hash" "github.com/minio/minio/pkg/hash"
) )
@ -1761,7 +1762,7 @@ func prepareTestBackend(instanceType string) (ObjectLayer, []string, error) {
// STEP 2: Set the policy to allow the unsigned request, use the policyFunc to obtain the relevant statement and call // STEP 2: Set the policy to allow the unsigned request, use the policyFunc to obtain the relevant statement and call
// the handler again to verify its success. // the handler again to verify its success.
func ExecObjectLayerAPIAnonTest(t *testing.T, testName, bucketName, objectName, instanceType string, apiRouter http.Handler, func ExecObjectLayerAPIAnonTest(t *testing.T, testName, bucketName, objectName, instanceType string, apiRouter http.Handler,
anonReq *http.Request, policyFunc func(string, string) policyStatement) { anonReq *http.Request, policyFunc func(string, string) policy.Statement) {
anonTestStr := "Anonymous HTTP request test" anonTestStr := "Anonymous HTTP request test"
unknownSignTestStr := "Unknown HTTP signature test" unknownSignTestStr := "Unknown HTTP signature test"
@ -1814,12 +1815,12 @@ func ExecObjectLayerAPIAnonTest(t *testing.T, testName, bucketName, objectName,
} }
// Set write only policy on bucket to allow anonymous HTTP request for the operation under test. // Set write only policy on bucket to allow anonymous HTTP request for the operation under test.
// request to go through. // request to go through.
policy := bucketPolicy{ bp := policy.BucketAccessPolicy{
Version: "1.0", Version: "1.0",
Statements: []policyStatement{policyFunc(bucketName, "")}, Statements: []policy.Statement{policyFunc(bucketName, "")},
} }
globalBucketPolicies.SetBucketPolicy(bucketName, policyChange{false, &policy}) globalBucketPolicies.SetBucketPolicy(bucketName, policyChange{false, bp})
// now call the handler again with the unsigned/anonymous request, it should be accepted. // now call the handler again with the unsigned/anonymous request, it should be accepted.
rec = httptest.NewRecorder() rec = httptest.NewRecorder()

@ -857,12 +857,14 @@ func (web *webAPIHandlers) SetBucketPolicy(r *http.Request, args *SetBucketPolic
} }
if len(policyInfo.Statements) == 0 { if len(policyInfo.Statements) == 0 {
err = persistAndNotifyBucketPolicyChange(args.BucketName, policyChange{true, nil}, objectAPI) if err = persistAndNotifyBucketPolicyChange(args.BucketName, policyChange{
if err != nil { true, policy.BucketAccessPolicy{},
}, objectAPI); err != nil {
return toJSONError(err, args.BucketName) return toJSONError(err, args.BucketName)
} }
return nil return nil
} }
data, err := json.Marshal(policyInfo) data, err := json.Marshal(policyInfo)
if err != nil { if err != nil {
return toJSONError(err) return toJSONError(err)

@ -433,12 +433,12 @@ func testListObjectsWebHandler(obj ObjectLayer, instanceType string, t TestErrHa
t.Fatalf("Expected error `%s`", err) t.Fatalf("Expected error `%s`", err)
} }
policy := bucketPolicy{ policy := policy.BucketAccessPolicy{
Version: "1.0", Version: "1.0",
Statements: []policyStatement{getReadOnlyObjectStatement(bucketName, "")}, Statements: []policy.Statement{getReadOnlyObjectStatement(bucketName, "")},
} }
globalBucketPolicies.SetBucketPolicy(bucketName, policyChange{false, &policy}) globalBucketPolicies.SetBucketPolicy(bucketName, policyChange{false, policy})
// Unauthenticated ListObjects with READ bucket policy should succeed. // Unauthenticated ListObjects with READ bucket policy should succeed.
err, reply = test("") err, reply = test("")
@ -807,12 +807,12 @@ func testUploadWebHandler(obj ObjectLayer, instanceType string, t TestErrHandler
t.Fatalf("Expected the response status to be 403, but instead found `%d`", code) t.Fatalf("Expected the response status to be 403, but instead found `%d`", code)
} }
policy := bucketPolicy{ bp := policy.BucketAccessPolicy{
Version: "1.0", Version: "1.0",
Statements: []policyStatement{getWriteOnlyObjectStatement(bucketName, "")}, Statements: []policy.Statement{getWriteOnlyObjectStatement(bucketName, "")},
} }
globalBucketPolicies.SetBucketPolicy(bucketName, policyChange{false, &policy}) globalBucketPolicies.SetBucketPolicy(bucketName, policyChange{false, bp})
// Unauthenticated upload with WRITE policy should succeed. // Unauthenticated upload with WRITE policy should succeed.
code = test("", true) code = test("", true)
@ -914,12 +914,12 @@ func testDownloadWebHandler(obj ObjectLayer, instanceType string, t TestErrHandl
t.Fatalf("Expected the response status to be 403, but instead found `%d`", code) t.Fatalf("Expected the response status to be 403, but instead found `%d`", code)
} }
policy := bucketPolicy{ bp := policy.BucketAccessPolicy{
Version: "1.0", Version: "1.0",
Statements: []policyStatement{getReadOnlyObjectStatement(bucketName, "")}, Statements: []policy.Statement{getReadOnlyObjectStatement(bucketName, "")},
} }
globalBucketPolicies.SetBucketPolicy(bucketName, policyChange{false, &policy}) globalBucketPolicies.SetBucketPolicy(bucketName, policyChange{false, bp})
// Unauthenticated download with READ policy should succeed. // Unauthenticated download with READ policy should succeed.
code, bodyContent = test("") code, bodyContent = test("")
@ -1142,26 +1142,30 @@ func testWebGetBucketPolicyHandler(obj ObjectLayer, instanceType string, t TestE
t.Fatal("Unexpected error: ", err) t.Fatal("Unexpected error: ", err)
} }
policyVal := bucketPolicy{ policyVal := policy.BucketAccessPolicy{
Version: "2012-10-17", Version: "2012-10-17",
Statements: []policyStatement{ Statements: []policy.Statement{
{ {
Actions: set.CreateStringSet("s3:GetBucketLocation", "s3:ListBucket"), Actions: set.CreateStringSet("s3:GetBucketLocation", "s3:ListBucket"),
Effect: "Allow", Effect: "Allow",
Principal: map[string][]string{"AWS": {"*"}}, Principal: policy.User{
AWS: set.CreateStringSet("*"),
},
Resources: set.CreateStringSet(bucketARNPrefix + bucketName), Resources: set.CreateStringSet(bucketARNPrefix + bucketName),
Sid: "", Sid: "",
}, },
{ {
Actions: set.CreateStringSet("s3:GetObject"), Actions: set.CreateStringSet("s3:GetObject"),
Effect: "Allow", Effect: "Allow",
Principal: map[string][]string{"AWS": {"*"}}, Principal: policy.User{
AWS: set.CreateStringSet("*"),
},
Resources: set.CreateStringSet(bucketARNPrefix + bucketName + "/*"), Resources: set.CreateStringSet(bucketARNPrefix + bucketName + "/*"),
Sid: "", Sid: "",
}, },
}, },
} }
if err := writeBucketPolicy(bucketName, obj, &policyVal); err != nil { if err := writeBucketPolicy(bucketName, obj, policyVal); err != nil {
t.Fatal("Unexpected error: ", err) t.Fatal("Unexpected error: ", err)
} }
@ -1216,32 +1220,32 @@ func testWebListAllBucketPoliciesHandler(obj ObjectLayer, instanceType string, t
t.Fatal("Unexpected error: ", err) t.Fatal("Unexpected error: ", err)
} }
policyVal := bucketPolicy{ stringEqualsConditions := policy.ConditionMap{}
stringEqualsConditions["StringEquals"] = make(policy.ConditionKeyMap)
stringEqualsConditions["StringEquals"].Add("s3:prefix", set.CreateStringSet("hello"))
policyVal := policy.BucketAccessPolicy{
Version: "2012-10-17", Version: "2012-10-17",
Statements: []policyStatement{ Statements: []policy.Statement{
{ {
Actions: set.CreateStringSet("s3:GetBucketLocation"), Actions: set.CreateStringSet("s3:GetBucketLocation"),
Effect: "Allow", Effect: "Allow",
Principal: map[string][]string{"AWS": {"*"}}, Principal: policy.User{AWS: set.CreateStringSet("*")},
Resources: set.CreateStringSet(bucketARNPrefix + bucketName), Resources: set.CreateStringSet(bucketARNPrefix + bucketName),
Sid: "", Sid: "",
}, },
{ {
Actions: set.CreateStringSet("s3:ListBucket"), Actions: set.CreateStringSet("s3:ListBucket"),
Conditions: map[string]map[string]set.StringSet{ Conditions: stringEqualsConditions,
"StringEquals": { Effect: "Allow",
"s3:prefix": set.CreateStringSet("hello"), Principal: policy.User{AWS: set.CreateStringSet("*")},
}, Resources: set.CreateStringSet(bucketARNPrefix + bucketName),
}, Sid: "",
Effect: "Allow",
Principal: map[string][]string{"AWS": {"*"}},
Resources: set.CreateStringSet(bucketARNPrefix + bucketName),
Sid: "",
}, },
{ {
Actions: set.CreateStringSet("s3:ListBucketMultipartUploads"), Actions: set.CreateStringSet("s3:ListBucketMultipartUploads"),
Effect: "Allow", Effect: "Allow",
Principal: map[string][]string{"AWS": {"*"}}, Principal: policy.User{AWS: set.CreateStringSet("*")},
Resources: set.CreateStringSet(bucketARNPrefix + bucketName), Resources: set.CreateStringSet(bucketARNPrefix + bucketName),
Sid: "", Sid: "",
}, },
@ -1249,13 +1253,13 @@ func testWebListAllBucketPoliciesHandler(obj ObjectLayer, instanceType string, t
Actions: set.CreateStringSet("s3:AbortMultipartUpload", "s3:DeleteObject", Actions: set.CreateStringSet("s3:AbortMultipartUpload", "s3:DeleteObject",
"s3:GetObject", "s3:ListMultipartUploadParts", "s3:PutObject"), "s3:GetObject", "s3:ListMultipartUploadParts", "s3:PutObject"),
Effect: "Allow", Effect: "Allow",
Principal: map[string][]string{"AWS": {"*"}}, Principal: policy.User{AWS: set.CreateStringSet("*")},
Resources: set.CreateStringSet(bucketARNPrefix + bucketName + "/hello*"), Resources: set.CreateStringSet(bucketARNPrefix + bucketName + "/hello*"),
Sid: "", Sid: "",
}, },
}, },
} }
if err := writeBucketPolicy(bucketName, obj, &policyVal); err != nil { if err := writeBucketPolicy(bucketName, obj, policyVal); err != nil {
t.Fatal("Unexpected error: ", err) t.Fatal("Unexpected error: ", err)
} }
@ -1348,7 +1352,7 @@ func testWebSetBucketPolicyHandler(obj ObjectLayer, instanceType string, t TestE
// Parse RPC response // Parse RPC response
err = getTestWebRPCResponse(rec, &reply) err = getTestWebRPCResponse(rec, &reply)
if testCase.pass && err != nil { if testCase.pass && err != nil {
t.Fatalf("Test %d: Should succeed but it didn't, %v", i+1, err) t.Fatalf("Test %d: Should succeed but it didn't, %#v", i+1, err)
} }
if !testCase.pass && err == nil { if !testCase.pass && err == nil {
t.Fatalf("Test %d: Should fail it didn't", i+1) t.Fatalf("Test %d: Should fail it didn't", i+1)

Loading…
Cancel
Save