policies: Parser should handle Principals with various forms. (#2733)

Handles cases for these three combinations

  - "Principal": "*",
  - "Principal": { "AWS" : "*" }
  - "Principal": { "AWS" : [ "*" ]}

Fixes #2732
This commit is contained in:
Harshavardhana 2016-09-19 13:52:28 -07:00 committed by GitHub
parent 113b93346b
commit ef3c807b4a
2 changed files with 70 additions and 17 deletions

View File

@ -50,18 +50,12 @@ var supportedConditionsKey = set.CreateStringSet("s3:prefix", "s3:max-keys")
// supportedEffectMap - supported effects. // supportedEffectMap - supported effects.
var supportedEffectMap = set.CreateStringSet("Allow", "Deny") var supportedEffectMap = set.CreateStringSet("Allow", "Deny")
// policyUser - canonical users list.
type policyUser struct {
AWS set.StringSet `json:"AWS,omitempty"`
CanonicalUser set.StringSet `json:"CanonicalUser,omitempty"`
}
// Statement - minio policy statement // Statement - minio policy statement
type policyStatement struct { type policyStatement struct {
Actions set.StringSet `json:"Action"` Actions set.StringSet `json:"Action"`
Conditions map[string]map[string]set.StringSet `json:"Condition,omitempty"` Conditions map[string]map[string]set.StringSet `json:"Condition,omitempty"`
Effect string Effect string
Principal policyUser `json:"Principal"` Principal interface{} `json:"Principal"`
Resources set.StringSet `json:"Resource"` Resources set.StringSet `json:"Resource"`
Sid string Sid string
} }
@ -131,8 +125,51 @@ 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(principals set.StringSet) (err error) { func isValidPrincipals(principal interface{}) (err error) {
principals := parsePrincipals(principal)
// Statement principal should have a value. // Statement principal should have a value.
if len(principals) == 0 { if len(principals) == 0 {
err = errors.New("Principal cannot be empty.") err = errors.New("Principal cannot be empty.")
@ -274,7 +311,7 @@ func parseBucketPolicy(bucketPolicyReader io.Reader, policy *bucketPolicy) (err
return err return err
} }
// Statement principal should be supported format. // Statement principal should be supported format.
if err := isValidPrincipals(statement.Principal.AWS); err != nil { if err := isValidPrincipals(statement.Principal); err != nil {
return err return err
} }
// Statement actions should be valid. // Statement actions should be valid.

View File

@ -76,7 +76,9 @@ var (
func getReadWriteObjectStatement(bucketName, objectPrefix string) policyStatement { func getReadWriteObjectStatement(bucketName, objectPrefix string) policyStatement {
objectResourceStatement := policyStatement{} objectResourceStatement := policyStatement{}
objectResourceStatement.Effect = "Allow" objectResourceStatement.Effect = "Allow"
objectResourceStatement.Principal.AWS = set.CreateStringSet([]string{"*"}...) objectResourceStatement.Principal = map[string]interface{}{
"AWS": "*",
}
objectResourceStatement.Resources = set.CreateStringSet([]string{fmt.Sprintf("%s%s", AWSResourcePrefix, bucketName+"/"+objectPrefix+"*")}...) objectResourceStatement.Resources = set.CreateStringSet([]string{fmt.Sprintf("%s%s", AWSResourcePrefix, bucketName+"/"+objectPrefix+"*")}...)
objectResourceStatement.Actions = set.CreateStringSet(readWriteObjectActions...) objectResourceStatement.Actions = set.CreateStringSet(readWriteObjectActions...)
return objectResourceStatement return objectResourceStatement
@ -86,7 +88,9 @@ func getReadWriteObjectStatement(bucketName, objectPrefix string) policyStatemen
func getReadWriteBucketStatement(bucketName, objectPrefix string) policyStatement { func getReadWriteBucketStatement(bucketName, objectPrefix string) policyStatement {
bucketResourceStatement := policyStatement{} bucketResourceStatement := policyStatement{}
bucketResourceStatement.Effect = "Allow" bucketResourceStatement.Effect = "Allow"
bucketResourceStatement.Principal.AWS = set.CreateStringSet([]string{"*"}...) bucketResourceStatement.Principal = map[string]interface{}{
"AWS": "*",
}
bucketResourceStatement.Resources = set.CreateStringSet([]string{fmt.Sprintf("%s%s", AWSResourcePrefix, bucketName)}...) bucketResourceStatement.Resources = set.CreateStringSet([]string{fmt.Sprintf("%s%s", AWSResourcePrefix, bucketName)}...)
bucketResourceStatement.Actions = set.CreateStringSet(readWriteBucketActions...) bucketResourceStatement.Actions = set.CreateStringSet(readWriteBucketActions...)
return bucketResourceStatement return bucketResourceStatement
@ -104,7 +108,9 @@ func getReadWriteStatement(bucketName, objectPrefix string) []policyStatement {
func getReadOnlyBucketStatement(bucketName, objectPrefix string) policyStatement { func getReadOnlyBucketStatement(bucketName, objectPrefix string) policyStatement {
bucketResourceStatement := policyStatement{} bucketResourceStatement := policyStatement{}
bucketResourceStatement.Effect = "Allow" bucketResourceStatement.Effect = "Allow"
bucketResourceStatement.Principal.AWS = set.CreateStringSet([]string{"*"}...) bucketResourceStatement.Principal = map[string]interface{}{
"AWS": "*",
}
bucketResourceStatement.Resources = set.CreateStringSet([]string{fmt.Sprintf("%s%s", AWSResourcePrefix, bucketName)}...) bucketResourceStatement.Resources = set.CreateStringSet([]string{fmt.Sprintf("%s%s", AWSResourcePrefix, bucketName)}...)
bucketResourceStatement.Actions = set.CreateStringSet(readOnlyBucketActions...) bucketResourceStatement.Actions = set.CreateStringSet(readOnlyBucketActions...)
return bucketResourceStatement return bucketResourceStatement
@ -114,7 +120,9 @@ func getReadOnlyBucketStatement(bucketName, objectPrefix string) policyStatement
func getReadOnlyObjectStatement(bucketName, objectPrefix string) policyStatement { func getReadOnlyObjectStatement(bucketName, objectPrefix string) policyStatement {
objectResourceStatement := policyStatement{} objectResourceStatement := policyStatement{}
objectResourceStatement.Effect = "Allow" objectResourceStatement.Effect = "Allow"
objectResourceStatement.Principal.AWS = set.CreateStringSet([]string{"*"}...) objectResourceStatement.Principal = map[string]interface{}{
"AWS": "*",
}
objectResourceStatement.Resources = set.CreateStringSet([]string{fmt.Sprintf("%s%s", AWSResourcePrefix, bucketName+"/"+objectPrefix+"*")}...) objectResourceStatement.Resources = set.CreateStringSet([]string{fmt.Sprintf("%s%s", AWSResourcePrefix, bucketName+"/"+objectPrefix+"*")}...)
objectResourceStatement.Actions = set.CreateStringSet(readOnlyObjectActions...) objectResourceStatement.Actions = set.CreateStringSet(readOnlyObjectActions...)
return objectResourceStatement return objectResourceStatement
@ -133,7 +141,9 @@ func getWriteOnlyBucketStatement(bucketName, objectPrefix string) policyStatemen
bucketResourceStatement := policyStatement{} bucketResourceStatement := policyStatement{}
bucketResourceStatement.Effect = "Allow" bucketResourceStatement.Effect = "Allow"
bucketResourceStatement.Principal.AWS = set.CreateStringSet([]string{"*"}...) bucketResourceStatement.Principal = map[string]interface{}{
"AWS": "*",
}
bucketResourceStatement.Resources = set.CreateStringSet([]string{fmt.Sprintf("%s%s", AWSResourcePrefix, bucketName)}...) bucketResourceStatement.Resources = set.CreateStringSet([]string{fmt.Sprintf("%s%s", AWSResourcePrefix, bucketName)}...)
bucketResourceStatement.Actions = set.CreateStringSet(writeOnlyBucketActions...) bucketResourceStatement.Actions = set.CreateStringSet(writeOnlyBucketActions...)
return bucketResourceStatement return bucketResourceStatement
@ -143,7 +153,9 @@ func getWriteOnlyBucketStatement(bucketName, objectPrefix string) policyStatemen
func getWriteOnlyObjectStatement(bucketName, objectPrefix string) policyStatement { func getWriteOnlyObjectStatement(bucketName, objectPrefix string) policyStatement {
objectResourceStatement := policyStatement{} objectResourceStatement := policyStatement{}
objectResourceStatement.Effect = "Allow" objectResourceStatement.Effect = "Allow"
objectResourceStatement.Principal.AWS = set.CreateStringSet([]string{"*"}...) objectResourceStatement.Principal = map[string]interface{}{
"AWS": "*",
}
objectResourceStatement.Resources = set.CreateStringSet([]string{fmt.Sprintf("%s%s", AWSResourcePrefix, bucketName+"/"+objectPrefix+"*")}...) objectResourceStatement.Resources = set.CreateStringSet([]string{fmt.Sprintf("%s%s", AWSResourcePrefix, bucketName+"/"+objectPrefix+"*")}...)
objectResourceStatement.Actions = set.CreateStringSet(writeOnlyObjectActions...) objectResourceStatement.Actions = set.CreateStringSet(writeOnlyObjectActions...)
return objectResourceStatement return objectResourceStatement
@ -312,7 +324,9 @@ func TestIsValidPrincipals(t *testing.T) {
{[]string{"*"}, nil, true}, {[]string{"*"}, nil, true},
} }
for i, testCase := range testCases { for i, testCase := range testCases {
err := isValidPrincipals(set.CreateStringSet(testCase.principals...)) err := isValidPrincipals(map[string]interface{}{
"AWS": testCase.principals,
})
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())
} }
@ -587,7 +601,9 @@ func TestParseBucketPolicy(t *testing.T) {
// set unsupported principals. // set unsupported principals.
setUnsupportedPrincipals := func(statements []policyStatement) []policyStatement { setUnsupportedPrincipals := func(statements []policyStatement) []policyStatement {
// "User1111"" is an Unsupported Principal. // "User1111"" is an Unsupported Principal.
statements[0].Principal.AWS = set.CreateStringSet([]string{"*", "User1111"}...) statements[0].Principal = map[string]interface{}{
"AWS": []string{"*", "User1111"},
}
return statements return statements
} }
// set unsupported Resources. // set unsupported Resources.