mirror of
https://github.com/minio/minio.git
synced 2025-04-16 17:00:07 -04:00
api/bucket-policy: Add unit tests for more coverage, fixes couple of bugs. (#2055)
Changes to ResourceMatch logic. Test for action match function.
This commit is contained in:
parent
bcb822c390
commit
48aa5f2199
@ -21,7 +21,6 @@ import (
|
|||||||
"io"
|
"io"
|
||||||
"io/ioutil"
|
"io/ioutil"
|
||||||
"net/http"
|
"net/http"
|
||||||
"regexp"
|
|
||||||
"strings"
|
"strings"
|
||||||
|
|
||||||
mux "github.com/gorilla/mux"
|
mux "github.com/gorilla/mux"
|
||||||
@ -65,41 +64,48 @@ 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 policyStatement) bool {
|
||||||
for _, policyAction := range statement.Actions {
|
for _, policyAction := range statement.Actions {
|
||||||
// Policy action can be a regex, validate the action with matching string.
|
if matched := actionMatch(policyAction, action); matched {
|
||||||
matched, err := regexp.MatchString(policyAction, action)
|
|
||||||
fatalIf(err, "Invalid action \"%s\" in bucket policy.", action)
|
|
||||||
if matched {
|
|
||||||
return true
|
return true
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
return false
|
return false
|
||||||
}
|
}
|
||||||
|
|
||||||
// Match function matches wild cards in 'pattern' for resource.
|
// Match function matches wild cards in 'pattern' for 'text'.
|
||||||
func resourceMatch(pattern, resource string) bool {
|
func wildCardMatch(pattern, text string) bool {
|
||||||
if pattern == "" {
|
if pattern == "" {
|
||||||
return resource == pattern
|
return text == pattern
|
||||||
}
|
}
|
||||||
if pattern == "*" {
|
if pattern == "*" {
|
||||||
return true
|
return true
|
||||||
}
|
}
|
||||||
parts := strings.Split(pattern, "*")
|
parts := strings.Split(pattern, "*")
|
||||||
if len(parts) == 1 {
|
if len(parts) == 1 {
|
||||||
return resource == pattern
|
return text == pattern
|
||||||
}
|
}
|
||||||
tGlob := strings.HasSuffix(pattern, "*")
|
tGlob := strings.HasSuffix(pattern, "*")
|
||||||
end := len(parts) - 1
|
end := len(parts) - 1
|
||||||
if !strings.HasPrefix(resource, parts[0]) {
|
if !strings.HasPrefix(text, parts[0]) {
|
||||||
return false
|
return false
|
||||||
}
|
}
|
||||||
for i := 1; i < end; i++ {
|
for i := 1; i < end; i++ {
|
||||||
if !strings.Contains(resource, parts[i]) {
|
if !strings.Contains(text, parts[i]) {
|
||||||
return false
|
return false
|
||||||
}
|
}
|
||||||
idx := strings.Index(resource, parts[i]) + len(parts[i])
|
idx := strings.Index(text, parts[i]) + len(parts[i])
|
||||||
resource = resource[idx:]
|
text = text[idx:]
|
||||||
}
|
}
|
||||||
return tGlob || strings.HasSuffix(resource, parts[end])
|
return tGlob || strings.HasSuffix(text, parts[end])
|
||||||
|
}
|
||||||
|
|
||||||
|
// Match function matches wild cards in 'pattern' for resource.
|
||||||
|
func resourceMatch(pattern, resource string) bool {
|
||||||
|
return wildCardMatch(pattern, resource)
|
||||||
|
}
|
||||||
|
|
||||||
|
// Match function matches wild cards in 'pattern' for action.
|
||||||
|
func actionMatch(pattern, action string) bool {
|
||||||
|
return wildCardMatch(pattern, action)
|
||||||
}
|
}
|
||||||
|
|
||||||
// Verify if given resource matches with policy statement.
|
// Verify if given resource matches with policy statement.
|
||||||
@ -109,11 +115,11 @@ func bucketPolicyResourceMatch(resource string, statement policyStatement) bool
|
|||||||
// 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.
|
||||||
// More info: http://docs.aws.amazon.com/AmazonS3/latest/dev/s3-arn-format.html .
|
// More info: http://docs.aws.amazon.com/AmazonS3/latest/dev/s3-arn-format.html .
|
||||||
if matched := resourceMatch(resourcep, resource); !matched {
|
if matched := resourceMatch(resourcep, resource); matched {
|
||||||
return false
|
return true
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
return true
|
return false
|
||||||
}
|
}
|
||||||
|
|
||||||
// Verify if given condition matches with policy statement.
|
// Verify if given condition matches with policy statement.
|
||||||
|
@ -24,7 +24,7 @@ import (
|
|||||||
// Tests validate Bucket policy resource matcher.
|
// Tests validate Bucket policy resource matcher.
|
||||||
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) policyStatement {
|
||||||
statement := policyStatement{}
|
statement := policyStatement{}
|
||||||
statement.Resources = []string{resource}
|
statement.Resources = []string{resource}
|
||||||
@ -76,3 +76,160 @@ func TestBucketPolicyResourceMatch(t *testing.T) {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// TestBucketPolicyActionMatch - Test validates whether given action on the
|
||||||
|
// bucket/object matches the allowed actions in policyStatement.
|
||||||
|
// 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.
|
||||||
|
func TestBucketPolicyActionMatch(t *testing.T) {
|
||||||
|
bucketName := "test-bucket"
|
||||||
|
objectPrefix := "test-object"
|
||||||
|
|
||||||
|
testCases := []struct {
|
||||||
|
action string
|
||||||
|
statement policyStatement
|
||||||
|
expectedResult bool
|
||||||
|
}{
|
||||||
|
// s3:GetBucketLocation is the action necessary to be present in the bucket policy to allow
|
||||||
|
// fetching of bucket location on an Anonymous/unsigned request.
|
||||||
|
|
||||||
|
//r ead-write bucket policy is expected to allow GetBucketLocation operation on an anonymous request (Test case - 1).
|
||||||
|
{"s3:GetBucketLocation", getReadWriteBucketStatement(bucketName, objectPrefix), true},
|
||||||
|
// write-only bucket policy is expected to allow GetBucketLocation operation on an anonymous request (Test case - 2).
|
||||||
|
{"s3:GetBucketLocation", getWriteOnlyBucketStatement(bucketName, objectPrefix), true},
|
||||||
|
// read-only bucket policy is expected to allow GetBucketLocation operation on an anonymous request (Test case - 3).
|
||||||
|
{"s3:GetBucketLocation", getReadOnlyBucketStatement(bucketName, objectPrefix), true},
|
||||||
|
|
||||||
|
// Any of the Object level access permissions shouldn't allow for GetBucketLocation operation on an Anonymous/unsigned request (Test cases 4-6).
|
||||||
|
{"s3:GetBucketLocation", getReadWriteObjectStatement(bucketName, objectPrefix), false},
|
||||||
|
{"s3:GetBucketLocation", getWriteOnlyObjectStatement(bucketName, objectPrefix), false},
|
||||||
|
{"s3:GetBucketLocation", getReadOnlyObjectStatement(bucketName, objectPrefix), false},
|
||||||
|
|
||||||
|
// s3:ListBucketMultipartUploads is the action necessary to be present in the bucket policy to allow
|
||||||
|
// Listing of multipart uploads in a given bucket for an Anonymous/unsigned request.
|
||||||
|
|
||||||
|
//read-write bucket policy is expected to allow ListBucketMultipartUploads operation on an anonymous request (Test case 7).
|
||||||
|
{"s3:ListBucketMultipartUploads", getReadWriteBucketStatement(bucketName, objectPrefix), true},
|
||||||
|
// write-only bucket policy is expected to allow ListBucketMultipartUploads operation on an anonymous request (Test case 8).
|
||||||
|
{"s3:ListBucketMultipartUploads", getWriteOnlyBucketStatement(bucketName, objectPrefix), true},
|
||||||
|
// read-only bucket policy is expected to not allow ListBucketMultipartUploads operation on an anonymous request (Test case 9).
|
||||||
|
// the allowed actions in read-only bucket statement are "s3:GetBucketLocation","s3:ListBucket",
|
||||||
|
// this shouldnot allow for ListBucketMultipartUploads operations.
|
||||||
|
{"s3:ListBucketMultipartUploads", getReadOnlyBucketStatement(bucketName, objectPrefix), false},
|
||||||
|
|
||||||
|
// Any of the object level policy will not allow for s3:ListBucketMultipartUploads (Test cases 10-12).
|
||||||
|
{"s3:ListBucketMultipartUploads", getReadWriteObjectStatement(bucketName, objectPrefix), false},
|
||||||
|
{"s3:ListBucketMultipartUploads", getWriteOnlyObjectStatement(bucketName, objectPrefix), false},
|
||||||
|
{"s3:ListBucketMultipartUploads", getReadOnlyObjectStatement(bucketName, objectPrefix), false},
|
||||||
|
|
||||||
|
// s3:ListBucket is the action necessary to be present in the bucket policy to allow
|
||||||
|
// listing of all objects inside a given bucket on an Anonymous/unsigned request.
|
||||||
|
|
||||||
|
// Cases for testing ListBucket access for different Bucket level access permissions.
|
||||||
|
// read-only bucket policy is expected to allow ListBucket operation on an anonymous request (Test case 13).
|
||||||
|
{"s3:ListBucket", getReadOnlyBucketStatement(bucketName, objectPrefix), true},
|
||||||
|
// read-write bucket policy is expected to allow ListBucket operation on an anonymous request (Test case 14).
|
||||||
|
{"s3:ListBucket", getReadWriteBucketStatement(bucketName, objectPrefix), true},
|
||||||
|
// write-only bucket policy is expected to not allow ListBucket operation on an anonymous request (Test case 15).
|
||||||
|
// the allowed actions in write-only bucket statement are "s3:GetBucketLocation", "s3:ListBucketMultipartUploads",
|
||||||
|
// this shouldnot allow for ListBucket operations.
|
||||||
|
{"s3:ListBucket", getWriteOnlyBucketStatement(bucketName, objectPrefix), false},
|
||||||
|
|
||||||
|
// Cases for testing ListBucket access for different Object level access permissions (Test cases 16-18).
|
||||||
|
// Any of the Object level access permissions shouldn't allow for ListBucket operation on an Anonymous/unsigned request.
|
||||||
|
{"s3:ListBucket", getReadOnlyObjectStatement(bucketName, objectPrefix), false},
|
||||||
|
{"s3:ListBucket", getReadWriteObjectStatement(bucketName, objectPrefix), false},
|
||||||
|
{"s3:ListBucket", getWriteOnlyObjectStatement(bucketName, objectPrefix), false},
|
||||||
|
|
||||||
|
// s3:DeleteObject is the action necessary to be present in the bucket policy to allow
|
||||||
|
// deleting/removal of objects inside a given bucket for an Anonymous/unsigned request.
|
||||||
|
|
||||||
|
// Cases for testing DeleteObject access for different Bucket level access permissions (Test cases 19-21).
|
||||||
|
// Any of the Bucket level access permissions shouldn't allow for DeleteObject operation on an Anonymous/unsigned request.
|
||||||
|
{"s3:DeleteObject", getReadOnlyBucketStatement(bucketName, objectPrefix), false},
|
||||||
|
{"s3:DeleteObject", getReadWriteBucketStatement(bucketName, objectPrefix), false},
|
||||||
|
{"s3:DeleteObject", getWriteOnlyBucketStatement(bucketName, objectPrefix), false},
|
||||||
|
|
||||||
|
// Cases for testing DeleteObject access for different Object level access permissions (Test cases 22).
|
||||||
|
// read-only bucket policy is expected to not allow Delete Object operation on an anonymous request.
|
||||||
|
{"s3:DeleteObject", getReadOnlyObjectStatement(bucketName, objectPrefix), false},
|
||||||
|
// read-write bucket policy is expected to allow Delete Bucket operation on an anonymous request (Test cases 23).
|
||||||
|
{"s3:DeleteObject", getReadWriteObjectStatement(bucketName, objectPrefix), true},
|
||||||
|
// write-only bucket policy is expected to allow Delete Object operation on an anonymous request (Test cases 24).
|
||||||
|
{"s3:DeleteObject", getWriteOnlyObjectStatement(bucketName, objectPrefix), true},
|
||||||
|
|
||||||
|
// s3:AbortMultipartUpload is the action necessary to be present in the bucket policy to allow
|
||||||
|
// cancelling or abortion of an already initiated multipart upload operation for an Anonymous/unsigned request.
|
||||||
|
|
||||||
|
// Cases for testing AbortMultipartUpload access for different Bucket level access permissions (Test cases 25-27).
|
||||||
|
// Any of the Bucket level access permissions shouldn't allow for AbortMultipartUpload operation on an Anonymous/unsigned request.
|
||||||
|
{"s3:AbortMultipartUpload", getReadOnlyBucketStatement(bucketName, objectPrefix), false},
|
||||||
|
{"s3:AbortMultipartUpload", getReadWriteBucketStatement(bucketName, objectPrefix), false},
|
||||||
|
{"s3:AbortMultipartUpload", getWriteOnlyBucketStatement(bucketName, objectPrefix), false},
|
||||||
|
|
||||||
|
// Cases for testing AbortMultipartUpload access for different Object level access permissions.
|
||||||
|
// read-only object policy is expected to not allow AbortMultipartUpload operation on an anonymous request (Test case 28).
|
||||||
|
{"s3:AbortMultipartUpload", getReadOnlyObjectStatement(bucketName, objectPrefix), false},
|
||||||
|
// read-write object policy is expected to allow AbortMultipartUpload operation on an anonymous request (Test case 29).
|
||||||
|
{"s3:AbortMultipartUpload", getReadWriteObjectStatement(bucketName, objectPrefix), true},
|
||||||
|
// write-only object policy is expected to allow AbortMultipartUpload operation on an anonymous request (Test case 30).
|
||||||
|
{"s3:AbortMultipartUpload", getWriteOnlyObjectStatement(bucketName, objectPrefix), true},
|
||||||
|
|
||||||
|
// s3:PutObject is the action necessary to be present in the bucket policy to allow
|
||||||
|
// uploading of an object for an Anonymous/unsigned request.
|
||||||
|
|
||||||
|
// Cases for testing PutObject access for different Bucket level access permissions (Test cases 31-33).
|
||||||
|
// Any of the Bucket level access permissions shouldn't allow for PutObject operation on an Anonymous/unsigned request.
|
||||||
|
{"s3:PutObject", getReadOnlyBucketStatement(bucketName, objectPrefix), false},
|
||||||
|
{"s3:PutObject", getReadWriteBucketStatement(bucketName, objectPrefix), false},
|
||||||
|
{"s3:PutObject", getWriteOnlyBucketStatement(bucketName, objectPrefix), false},
|
||||||
|
|
||||||
|
// Cases for testing PutObject access for different Object level access permissions.
|
||||||
|
// read-only object policy is expected to not allow PutObject operation on an anonymous request (Test case 34).
|
||||||
|
{"s3:PutObject", getReadOnlyObjectStatement(bucketName, objectPrefix), false},
|
||||||
|
// read-write object policy is expected to allow PutObject operation on an anonymous request (Test case 35).
|
||||||
|
{"s3:PutObject", getReadWriteObjectStatement(bucketName, objectPrefix), true},
|
||||||
|
// write-only object policy is expected to allow PutObject operation on an anonymous request (Test case 36).
|
||||||
|
{"s3:PutObject", getWriteOnlyObjectStatement(bucketName, objectPrefix), true},
|
||||||
|
|
||||||
|
// s3:GetObject is the action necessary to be present in the bucket policy to allow
|
||||||
|
// downloading of an object for an Anonymous/unsigned request.
|
||||||
|
|
||||||
|
// Cases for testing GetObject access for different Bucket level access permissions (Test cases 37-39).
|
||||||
|
// Any of the Bucket level access permissions shouldn't allow for GetObject operation on an Anonymous/unsigned request.
|
||||||
|
{"s3:GetObject", getReadOnlyBucketStatement(bucketName, objectPrefix), false},
|
||||||
|
{"s3:GetObject", getReadWriteBucketStatement(bucketName, objectPrefix), false},
|
||||||
|
{"s3:GetObject", getWriteOnlyBucketStatement(bucketName, objectPrefix), false},
|
||||||
|
|
||||||
|
// Cases for testing GetObject access for different Object level access permissions.
|
||||||
|
// read-only bucket policy is expected to allow downloading of an Object on an anonymous request (Test case 40).
|
||||||
|
{"s3:GetObject", getReadOnlyObjectStatement(bucketName, objectPrefix), true},
|
||||||
|
// read-write bucket policy is expected to allow downloading of an Object on an anonymous request (Test case 41).
|
||||||
|
{"s3:GetObject", getReadWriteObjectStatement(bucketName, objectPrefix), true},
|
||||||
|
// write-only bucket policy is expected to not allow downloading of an Object on an anonymous request (Test case 42).
|
||||||
|
{"s3:GetObject", getWriteOnlyObjectStatement(bucketName, objectPrefix), false},
|
||||||
|
|
||||||
|
// s3:ListMultipartUploadParts is the action necessary to be present in the bucket policy to allow
|
||||||
|
// Listing of uploaded parts for an Anonymous/unsigned request.
|
||||||
|
|
||||||
|
// Any of the Bucket level access permissions shouldn't allow for ListMultipartUploadParts operation on an Anonymous/unsigned request.
|
||||||
|
// read-only bucket policy is expected to not allow ListMultipartUploadParts operation on an anonymous request (Test cases 43-45).
|
||||||
|
{"s3:ListMultipartUploadParts", getReadOnlyBucketStatement(bucketName, objectPrefix), false},
|
||||||
|
{"s3:ListMultipartUploadParts", getReadWriteBucketStatement(bucketName, objectPrefix), false},
|
||||||
|
{"s3:ListMultipartUploadParts", getWriteOnlyBucketStatement(bucketName, objectPrefix), false},
|
||||||
|
|
||||||
|
// read-only object policy is expected to not allow ListMultipartUploadParts operation on an anonymous request (Test case 46).
|
||||||
|
{"s3:ListMultipartUploadParts", getReadOnlyObjectStatement(bucketName, objectPrefix), false},
|
||||||
|
// read-write object policy is expected to allow ListMultipartUploadParts operation on an anonymous request (Test case 47).
|
||||||
|
{"s3:ListMultipartUploadParts", getReadWriteObjectStatement(bucketName, objectPrefix), true},
|
||||||
|
// write-only object policy is expected to allow ListMultipartUploadParts operation on an anonymous request (Test case 48).
|
||||||
|
{"s3:ListMultipartUploadParts", getWriteOnlyObjectStatement(bucketName, objectPrefix), true},
|
||||||
|
}
|
||||||
|
for i, testCase := range testCases {
|
||||||
|
actualResult := bucketPolicyActionMatch(testCase.action, testCase.statement)
|
||||||
|
if testCase.expectedResult != actualResult {
|
||||||
|
t.Errorf("Test %d: Expected the result to be `%v`, but instead found it to be `%v`", i+1, testCase.expectedResult, actualResult)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
@ -34,6 +34,8 @@ const (
|
|||||||
|
|
||||||
// supportedActionMap - lists all the actions supported by minio.
|
// supportedActionMap - lists all the actions supported by minio.
|
||||||
var supportedActionMap = map[string]struct{}{
|
var supportedActionMap = map[string]struct{}{
|
||||||
|
"*": {},
|
||||||
|
"s3:*": {},
|
||||||
"s3:GetObject": {},
|
"s3:GetObject": {},
|
||||||
"s3:ListBucket": {},
|
"s3:ListBucket": {},
|
||||||
"s3:PutObject": {},
|
"s3:PutObject": {},
|
||||||
|
@ -69,60 +69,89 @@ var (
|
|||||||
}
|
}
|
||||||
)
|
)
|
||||||
|
|
||||||
// Obtain statements for read-write BucketPolicy.
|
// Obtain bucket statement for read-write BucketPolicy.
|
||||||
func setReadWriteStatement(bucketName, objectPrefix string) []policyStatement {
|
func getReadWriteObjectStatement(bucketName, objectPrefix string) policyStatement {
|
||||||
bucketResourceStatement := policyStatement{}
|
|
||||||
objectResourceStatement := 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.Effect = "Allow"
|
||||||
objectResourceStatement.Principal.AWS = []string{"*"}
|
objectResourceStatement.Principal.AWS = []string{"*"}
|
||||||
objectResourceStatement.Resources = []string{fmt.Sprintf("%s%s", AWSResourcePrefix, bucketName+"/"+objectPrefix+"*")}
|
objectResourceStatement.Resources = []string{fmt.Sprintf("%s%s", AWSResourcePrefix, bucketName+"/"+objectPrefix+"*")}
|
||||||
objectResourceStatement.Actions = readWriteObjectActions
|
objectResourceStatement.Actions = readWriteObjectActions
|
||||||
|
return objectResourceStatement
|
||||||
|
}
|
||||||
|
|
||||||
|
// Obtain object statement for read-write BucketPolicy.
|
||||||
|
func getReadWriteBucketStatement(bucketName, objectPrefix string) policyStatement {
|
||||||
|
bucketResourceStatement := policyStatement{}
|
||||||
|
bucketResourceStatement.Effect = "Allow"
|
||||||
|
bucketResourceStatement.Principal.AWS = []string{"*"}
|
||||||
|
bucketResourceStatement.Resources = []string{fmt.Sprintf("%s%s", AWSResourcePrefix, bucketName)}
|
||||||
|
bucketResourceStatement.Actions = readWriteBucketActions
|
||||||
|
return bucketResourceStatement
|
||||||
|
}
|
||||||
|
|
||||||
|
// Obtain statements for read-write BucketPolicy.
|
||||||
|
func getReadWriteStatement(bucketName, objectPrefix string) []policyStatement {
|
||||||
|
statements := []policyStatement{}
|
||||||
// Save the read write policy.
|
// Save the read write policy.
|
||||||
statements = append(statements, bucketResourceStatement, objectResourceStatement)
|
statements = append(statements, getReadWriteBucketStatement(bucketName, objectPrefix), getReadWriteObjectStatement(bucketName, objectPrefix))
|
||||||
return statements
|
return statements
|
||||||
}
|
}
|
||||||
|
|
||||||
// Obtain statements for read only BucketPolicy.
|
// Obtain bucket statement for read only BucketPolicy.
|
||||||
func setReadOnlyStatement(bucketName, objectPrefix string) []policyStatement {
|
func getReadOnlyBucketStatement(bucketName, objectPrefix string) policyStatement {
|
||||||
bucketResourceStatement := policyStatement{}
|
bucketResourceStatement := policyStatement{}
|
||||||
objectResourceStatement := policyStatement{}
|
|
||||||
statements := []policyStatement{}
|
|
||||||
|
|
||||||
bucketResourceStatement.Effect = "Allow"
|
bucketResourceStatement.Effect = "Allow"
|
||||||
bucketResourceStatement.Principal.AWS = []string{"*"}
|
bucketResourceStatement.Principal.AWS = []string{"*"}
|
||||||
bucketResourceStatement.Resources = []string{fmt.Sprintf("%s%s", AWSResourcePrefix, bucketName)}
|
bucketResourceStatement.Resources = []string{fmt.Sprintf("%s%s", AWSResourcePrefix, bucketName)}
|
||||||
bucketResourceStatement.Actions = readOnlyBucketActions
|
bucketResourceStatement.Actions = readOnlyBucketActions
|
||||||
|
return bucketResourceStatement
|
||||||
|
}
|
||||||
|
|
||||||
|
// Obtain object statement for read only BucketPolicy.
|
||||||
|
func getReadOnlyObjectStatement(bucketName, objectPrefix string) policyStatement {
|
||||||
|
objectResourceStatement := policyStatement{}
|
||||||
objectResourceStatement.Effect = "Allow"
|
objectResourceStatement.Effect = "Allow"
|
||||||
objectResourceStatement.Principal.AWS = []string{"*"}
|
objectResourceStatement.Principal.AWS = []string{"*"}
|
||||||
objectResourceStatement.Resources = []string{fmt.Sprintf("%s%s", AWSResourcePrefix, bucketName+"/"+objectPrefix+"*")}
|
objectResourceStatement.Resources = []string{fmt.Sprintf("%s%s", AWSResourcePrefix, bucketName+"/"+objectPrefix+"*")}
|
||||||
objectResourceStatement.Actions = readOnlyObjectActions
|
objectResourceStatement.Actions = readOnlyObjectActions
|
||||||
|
return objectResourceStatement
|
||||||
|
}
|
||||||
|
|
||||||
|
// Obtain statements for read only BucketPolicy.
|
||||||
|
func getReadOnlyStatement(bucketName, objectPrefix string) []policyStatement {
|
||||||
|
statements := []policyStatement{}
|
||||||
// Save the read only policy.
|
// Save the read only policy.
|
||||||
statements = append(statements, bucketResourceStatement, objectResourceStatement)
|
statements = append(statements, getReadOnlyBucketStatement(bucketName, objectPrefix), getReadOnlyObjectStatement(bucketName, objectPrefix))
|
||||||
return statements
|
return statements
|
||||||
}
|
}
|
||||||
|
|
||||||
// Obtain statements for write only BucketPolicy.
|
// Obtain bucket statements for write only BucketPolicy.
|
||||||
func setWriteOnlyStatement(bucketName, objectPrefix string) []policyStatement {
|
func getWriteOnlyBucketStatement(bucketName, objectPrefix string) policyStatement {
|
||||||
|
|
||||||
bucketResourceStatement := policyStatement{}
|
bucketResourceStatement := policyStatement{}
|
||||||
objectResourceStatement := policyStatement{}
|
|
||||||
statements := []policyStatement{}
|
|
||||||
// Write only policy.
|
|
||||||
bucketResourceStatement.Effect = "Allow"
|
bucketResourceStatement.Effect = "Allow"
|
||||||
bucketResourceStatement.Principal.AWS = []string{"*"}
|
bucketResourceStatement.Principal.AWS = []string{"*"}
|
||||||
bucketResourceStatement.Resources = []string{fmt.Sprintf("%s%s", AWSResourcePrefix, bucketName)}
|
bucketResourceStatement.Resources = []string{fmt.Sprintf("%s%s", AWSResourcePrefix, bucketName)}
|
||||||
bucketResourceStatement.Actions = writeOnlyBucketActions
|
bucketResourceStatement.Actions = writeOnlyBucketActions
|
||||||
|
return bucketResourceStatement
|
||||||
|
}
|
||||||
|
|
||||||
|
// Obtain object statements for write only BucketPolicy.
|
||||||
|
func getWriteOnlyObjectStatement(bucketName, objectPrefix string) policyStatement {
|
||||||
|
objectResourceStatement := policyStatement{}
|
||||||
objectResourceStatement.Effect = "Allow"
|
objectResourceStatement.Effect = "Allow"
|
||||||
objectResourceStatement.Principal.AWS = []string{"*"}
|
objectResourceStatement.Principal.AWS = []string{"*"}
|
||||||
objectResourceStatement.Resources = []string{fmt.Sprintf("%s%s", AWSResourcePrefix, bucketName+"/"+objectPrefix+"*")}
|
objectResourceStatement.Resources = []string{fmt.Sprintf("%s%s", AWSResourcePrefix, bucketName+"/"+objectPrefix+"*")}
|
||||||
objectResourceStatement.Actions = writeOnlyObjectActions
|
objectResourceStatement.Actions = writeOnlyObjectActions
|
||||||
|
return objectResourceStatement
|
||||||
|
}
|
||||||
|
|
||||||
|
// Obtain statements for write only BucketPolicy.
|
||||||
|
func getWriteOnlyStatement(bucketName, objectPrefix string) []policyStatement {
|
||||||
|
statements := []policyStatement{}
|
||||||
|
// Write only policy.
|
||||||
// Save the write only policy.
|
// Save the write only policy.
|
||||||
statements = append(statements, bucketResourceStatement, objectResourceStatement)
|
statements = append(statements, getWriteOnlyBucketStatement(bucketName, objectPrefix), getWriteOnlyBucketStatement(bucketName, objectPrefix))
|
||||||
return statements
|
return statements
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -145,11 +174,13 @@ func TestIsValidActions(t *testing.T) {
|
|||||||
{[]string{}, errors.New("Action list cannot be empty."), false},
|
{[]string{}, errors.New("Action list cannot be empty."), false},
|
||||||
// Test case - 3.
|
// Test case - 3.
|
||||||
// "s3:DeleteEverything"" is an invalid Action.
|
// "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},
|
{[]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.
|
// Inputs with valid Action.
|
||||||
// Test Case - 4.
|
// Test Case - 4.
|
||||||
{[]string{"s3:GetObject", "s3:ListBucket", "s3:PutObject", "s3:GetBucketLocation", "s3:DeleteObject", "s3:AbortMultipartUpload", "s3:ListBucketMultipartUploads", "s3:ListMultipartUploadParts"}, nil, true},
|
{[]string{"s3:*", "*", "s3:GetObject", "s3:ListBucket",
|
||||||
|
"s3:PutObject", "s3:GetBucketLocation", "s3:DeleteObject", "s3:AbortMultipartUpload", "s3:ListBucketMultipartUploads", "s3:ListMultipartUploadParts"}, nil, true},
|
||||||
}
|
}
|
||||||
for i, testCase := range testCases {
|
for i, testCase := range testCases {
|
||||||
err := isValidActions(testCase.actions)
|
err := isValidActions(testCase.actions)
|
||||||
@ -464,31 +495,31 @@ func TestCheckBucketPolicyResources(t *testing.T) {
|
|||||||
bucketAccessPolicies := []BucketPolicy{
|
bucketAccessPolicies := []BucketPolicy{
|
||||||
// BucketPolicy - 1.
|
// BucketPolicy - 1.
|
||||||
// Contains valid read only policy statement.
|
// Contains valid read only policy statement.
|
||||||
{Version: "1.0", Statements: setReadOnlyStatement("minio-bucket", "")},
|
{Version: "1.0", Statements: getReadOnlyStatement("minio-bucket", "")},
|
||||||
// BucketPolicy - 2.
|
// BucketPolicy - 2.
|
||||||
// Contains valid read-write only policy statement.
|
// Contains valid read-write only policy statement.
|
||||||
{Version: "1.0", Statements: setReadWriteStatement("minio-bucket", "Asia/")},
|
{Version: "1.0", Statements: getReadWriteStatement("minio-bucket", "Asia/")},
|
||||||
// BucketPolicy - 3.
|
// BucketPolicy - 3.
|
||||||
// Contains valid write only policy statement.
|
// Contains valid write only policy statement.
|
||||||
{Version: "1.0", Statements: setWriteOnlyStatement("minio-bucket", "Asia/India/")},
|
{Version: "1.0", Statements: getWriteOnlyStatement("minio-bucket", "Asia/India/")},
|
||||||
// BucketPolicy - 4.
|
// BucketPolicy - 4.
|
||||||
// Contains invalidPrefixActions.
|
// Contains invalidPrefixActions.
|
||||||
// Since resourcePrefix is not to the bucket-name, it return ErrMalformedPolicy.
|
// Since resourcePrefix is not to the bucket-name, it return ErrMalformedPolicy.
|
||||||
{Version: "1.0", Statements: setReadOnlyStatement("minio-bucket-fail", "Asia/India/")},
|
{Version: "1.0", Statements: getReadOnlyStatement("minio-bucket-fail", "Asia/India/")},
|
||||||
// BucketPolicy - 5.
|
// BucketPolicy - 5.
|
||||||
// constructing policy statement without invalidPrefixActions (check bucket-policy-parser.go).
|
// constructing policy statement without invalidPrefixActions (check bucket-policy-parser.go).
|
||||||
// but bucket part of the resource is not equal to the bucket name.
|
// but bucket part of the resource is not equal to the bucket name.
|
||||||
// this results in return of ErrMalformedPolicy.
|
// this results in return of ErrMalformedPolicy.
|
||||||
{Version: "1.0", Statements: setValidPrefixActions(setWriteOnlyStatement("minio-bucket-fail", "Asia/India/"))},
|
{Version: "1.0", Statements: setValidPrefixActions(getWriteOnlyStatement("minio-bucket-fail", "Asia/India/"))},
|
||||||
// BucketPolicy - 6.
|
// BucketPolicy - 6.
|
||||||
// contructing policy statement with recursive resources.
|
// contructing policy statement with recursive resources.
|
||||||
// should result in ErrMalformedPolicy
|
// should result in ErrMalformedPolicy
|
||||||
{Version: "1.0", Statements: setRecurseResource(setValidPrefixActions(setWriteOnlyStatement("minio-bucket", "")))},
|
{Version: "1.0", Statements: setRecurseResource(setValidPrefixActions(getWriteOnlyStatement("minio-bucket", "")))},
|
||||||
// BucketPolciy - 7.
|
// BucketPolciy - 7.
|
||||||
// constructing policy statement with non recursive but
|
// constructing policy statement with non recursive but
|
||||||
// lexically close resources.
|
// lexically close resources.
|
||||||
// should result in ErrNone.
|
// should result in ErrNone.
|
||||||
{Version: "1.0", Statements: setResourceLexical(setValidPrefixActions(setWriteOnlyStatement("minio-bucket", "oo")))},
|
{Version: "1.0", Statements: setResourceLexical(setValidPrefixActions(getWriteOnlyStatement("minio-bucket", "oo")))},
|
||||||
}
|
}
|
||||||
|
|
||||||
testCases := []struct {
|
testCases := []struct {
|
||||||
@ -575,25 +606,25 @@ func TestParseBucketPolicy(t *testing.T) {
|
|||||||
{Version: "", Statements: []policyStatement{}},
|
{Version: "", Statements: []policyStatement{}},
|
||||||
// BucketPolicy - 2.
|
// BucketPolicy - 2.
|
||||||
// Readonly BucketPolicy.
|
// Readonly BucketPolicy.
|
||||||
{Version: "1.0", Statements: setReadOnlyStatement("minio-bucket", "")},
|
{Version: "1.0", Statements: getReadOnlyStatement("minio-bucket", "")},
|
||||||
// BucketPolicy - 3.
|
// BucketPolicy - 3.
|
||||||
// Read-Write bucket policy.
|
// Read-Write bucket policy.
|
||||||
{Version: "1.0", Statements: setReadWriteStatement("minio-bucket", "Asia/")},
|
{Version: "1.0", Statements: getReadWriteStatement("minio-bucket", "Asia/")},
|
||||||
// BucketPolicy - 4.
|
// BucketPolicy - 4.
|
||||||
// Write only bucket policy.
|
// Write only bucket policy.
|
||||||
{Version: "1.0", Statements: setWriteOnlyStatement("minio-bucket", "Asia/India/")},
|
{Version: "1.0", Statements: getWriteOnlyStatement("minio-bucket", "Asia/India/")},
|
||||||
// BucketPolicy - 5.
|
// BucketPolicy - 5.
|
||||||
// BucketPolicy statement contains unsupported action.
|
// BucketPolicy statement contains unsupported action.
|
||||||
{Version: "1.0", Statements: setUnsupportedActions(setReadOnlyStatement("minio-bucket", ""))},
|
{Version: "1.0", Statements: setUnsupportedActions(getReadOnlyStatement("minio-bucket", ""))},
|
||||||
// BucketPolicy - 6.
|
// BucketPolicy - 6.
|
||||||
// BucketPolicy statement contains unsupported Effect.
|
// BucketPolicy statement contains unsupported Effect.
|
||||||
{Version: "1.0", Statements: setUnsupportedEffect(setReadWriteStatement("minio-bucket", "Asia/"))},
|
{Version: "1.0", Statements: setUnsupportedEffect(getReadWriteStatement("minio-bucket", "Asia/"))},
|
||||||
// BucketPolicy - 7.
|
// BucketPolicy - 7.
|
||||||
// BucketPolicy statement contains unsupported Principal.
|
// BucketPolicy statement contains unsupported Principal.
|
||||||
{Version: "1.0", Statements: setUnsupportedPrincipals(setWriteOnlyStatement("minio-bucket", "Asia/India/"))},
|
{Version: "1.0", Statements: setUnsupportedPrincipals(getWriteOnlyStatement("minio-bucket", "Asia/India/"))},
|
||||||
// BucketPolicy - 8.
|
// BucketPolicy - 8.
|
||||||
// BucketPolicy statement contains unsupported Resource.
|
// BucketPolicy statement contains unsupported Resource.
|
||||||
{Version: "1.0", Statements: setUnsupportedResources(setWriteOnlyStatement("minio-bucket", "Asia/India/"))},
|
{Version: "1.0", Statements: setUnsupportedResources(getWriteOnlyStatement("minio-bucket", "Asia/India/"))},
|
||||||
}
|
}
|
||||||
|
|
||||||
testCases := []struct {
|
testCases := []struct {
|
||||||
|
Loading…
x
Reference in New Issue
Block a user