mirror of
https://github.com/minio/minio.git
synced 2024-12-24 06:05:55 -05:00
api/bucketPolicy: Use minio-go/pkg/set and fix bucket policy regression. (#2506)
Current master has a regression 'mc policy <policy-type> alias/bucket/prefix' does not work anymore, due to the way new minio-go changes do json marshalling. This led to a regression on server side when a ``prefix`` is provided policy is rejected as malformed from th server which is not the case with AWS S3. This patch uses the new ``minio-go/pkg/set`` package to address the unmarshalling problems. Fixes #2503
This commit is contained in:
parent
a3c509fd23
commit
975eb31973
@ -25,6 +25,7 @@ import (
|
|||||||
"strings"
|
"strings"
|
||||||
|
|
||||||
mux "github.com/gorilla/mux"
|
mux "github.com/gorilla/mux"
|
||||||
|
"github.com/minio/minio-go/pkg/set"
|
||||||
)
|
)
|
||||||
|
|
||||||
// http://docs.aws.amazon.com/AmazonS3/latest/dev/using-with-s3-actions.html
|
// http://docs.aws.amazon.com/AmazonS3/latest/dev/using-with-s3-actions.html
|
||||||
@ -43,13 +44,13 @@ func enforceBucketPolicy(bucket string, action string, reqURL *url.URL) (s3Error
|
|||||||
resource := AWSResourcePrefix + strings.TrimPrefix(reqURL.Path, "/")
|
resource := AWSResourcePrefix + strings.TrimPrefix(reqURL.Path, "/")
|
||||||
|
|
||||||
// Get conditions for policy verification.
|
// Get conditions for policy verification.
|
||||||
conditions := make(map[string]string)
|
conditionKeyMap := make(map[string]set.StringSet)
|
||||||
for queryParam := range reqURL.Query() {
|
for queryParam := range reqURL.Query() {
|
||||||
conditions[queryParam] = reqURL.Query().Get(queryParam)
|
conditionKeyMap[queryParam] = set.CreateStringSet(reqURL.Query().Get(queryParam))
|
||||||
}
|
}
|
||||||
|
|
||||||
// Validate action, resource and conditions with current policy statements.
|
// Validate action, resource and conditions with current policy statements.
|
||||||
if !bucketPolicyEvalStatements(action, resource, conditions, policy.Statements) {
|
if !bucketPolicyEvalStatements(action, resource, conditionKeyMap, policy.Statements) {
|
||||||
return ErrAccessDenied
|
return ErrAccessDenied
|
||||||
}
|
}
|
||||||
return ErrNone
|
return ErrNone
|
||||||
|
@ -24,6 +24,7 @@ import (
|
|||||||
"net/http"
|
"net/http"
|
||||||
|
|
||||||
mux "github.com/gorilla/mux"
|
mux "github.com/gorilla/mux"
|
||||||
|
"github.com/minio/minio-go/pkg/set"
|
||||||
"github.com/minio/minio/pkg/wildcard"
|
"github.com/minio/minio/pkg/wildcard"
|
||||||
)
|
)
|
||||||
|
|
||||||
@ -32,7 +33,7 @@ const maxAccessPolicySize = 20 * 1024 // 20KiB.
|
|||||||
|
|
||||||
// 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]string, statements []policyStatement) bool {
|
func bucketPolicyEvalStatements(action string, resource string, conditions map[string]set.StringSet, statements []policyStatement) 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" {
|
||||||
@ -48,7 +49,7 @@ 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]string, statement policyStatement) bool {
|
func bucketPolicyMatchStatement(action string, resource string, conditions map[string]set.StringSet, statement policyStatement) bool {
|
||||||
// Verify if action matches.
|
// Verify if action matches.
|
||||||
if bucketPolicyActionMatch(action, statement) {
|
if bucketPolicyActionMatch(action, statement) {
|
||||||
// Verify if resource matches.
|
// Verify if resource matches.
|
||||||
@ -64,12 +65,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 policyStatement) bool {
|
||||||
for _, policyAction := range statement.Actions {
|
return !statement.Actions.FuncMatch(actionMatch, action).IsEmpty()
|
||||||
if matched := actionMatch(policyAction, action); matched {
|
|
||||||
return true
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return false
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// Match function matches wild cards in 'pattern' for resource.
|
// Match function matches wild cards in 'pattern' for resource.
|
||||||
@ -84,20 +80,15 @@ func actionMatch(pattern, action 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 policyStatement) bool {
|
||||||
for _, resourcep := range statement.Resources {
|
// 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.
|
// 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 .
|
return !statement.Resources.FuncMatch(resourceMatch, resource).IsEmpty()
|
||||||
if matched := resourceMatch(resourcep, resource); matched {
|
|
||||||
return true
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return false
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// Verify if given condition matches with policy statement.
|
// Verify if given condition matches with policy statement.
|
||||||
func bucketPolicyConditionMatch(conditions map[string]string, statement policyStatement) bool {
|
func bucketPolicyConditionMatch(conditions map[string]set.StringSet, statement policyStatement) bool {
|
||||||
// Supports following conditions.
|
// Supports following conditions.
|
||||||
// - StringEquals
|
// - StringEquals
|
||||||
// - StringNotEquals
|
// - StringNotEquals
|
||||||
@ -106,22 +97,22 @@ func bucketPolicyConditionMatch(conditions map[string]string, statement policySt
|
|||||||
// - s3:prefix
|
// - s3:prefix
|
||||||
// - s3:max-keys
|
// - s3:max-keys
|
||||||
var conditionMatches = true
|
var conditionMatches = true
|
||||||
for condition, conditionKeys := range statement.Conditions {
|
for condition, conditionKeyVal := range statement.Conditions {
|
||||||
if condition == "StringEquals" {
|
if condition == "StringEquals" {
|
||||||
if conditionKeys["s3:prefix"] != conditions["prefix"] {
|
if !conditionKeyVal["s3:prefix"].Equals(conditions["prefix"]) {
|
||||||
conditionMatches = false
|
conditionMatches = false
|
||||||
break
|
break
|
||||||
}
|
}
|
||||||
if conditionKeys["s3:max-keys"] != conditions["max-keys"] {
|
if !conditionKeyVal["s3:max-keys"].Equals(conditions["max-keys"]) {
|
||||||
conditionMatches = false
|
conditionMatches = false
|
||||||
break
|
break
|
||||||
}
|
}
|
||||||
} else if condition == "StringNotEquals" {
|
} else if condition == "StringNotEquals" {
|
||||||
if conditionKeys["s3:prefix"] == conditions["prefix"] {
|
if !conditionKeyVal["s3:prefix"].Equals(conditions["prefix"]) {
|
||||||
conditionMatches = false
|
conditionMatches = false
|
||||||
break
|
break
|
||||||
}
|
}
|
||||||
if conditionKeys["s3:max-keys"] == conditions["max-keys"] {
|
if !conditionKeyVal["s3:max-keys"].Equals(conditions["max-keys"]) {
|
||||||
conditionMatches = false
|
conditionMatches = false
|
||||||
break
|
break
|
||||||
}
|
}
|
||||||
|
@ -23,6 +23,8 @@ import (
|
|||||||
"net/http"
|
"net/http"
|
||||||
"net/http/httptest"
|
"net/http/httptest"
|
||||||
"testing"
|
"testing"
|
||||||
|
|
||||||
|
"github.com/minio/minio-go/pkg/set"
|
||||||
)
|
)
|
||||||
|
|
||||||
// Tests validate Bucket policy resource matcher.
|
// Tests validate Bucket policy resource matcher.
|
||||||
@ -31,7 +33,7 @@ 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 = set.CreateStringSet([]string{resource}...)
|
||||||
return statement
|
return statement
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -336,7 +338,7 @@ func testGetBucketPolicyHandler(obj ObjectLayer, instanceType string, t TestErrH
|
|||||||
credentials := serverConfig.GetCredential()
|
credentials := serverConfig.GetCredential()
|
||||||
|
|
||||||
// template for constructing HTTP request body for PUT bucket policy.
|
// template for constructing HTTP request body for PUT bucket policy.
|
||||||
bucketPolicyTemplate := `{"Version":"2012-10-17","Statement":[{"Sid":"","Effect":"Allow","Principal":{"AWS":["*"]},"Action":["s3:GetBucketLocation","s3:ListBucket"],"Resource":["arn:aws:s3:::%s"]},{"Sid":"","Effect":"Allow","Principal":{"AWS":["*"]},"Action":["s3:GetObject"],"Resource":["arn:aws:s3:::%s/this*"]}]}`
|
bucketPolicyTemplate := `{"Version":"2012-10-17","Statement":[{"Action":["s3:GetBucketLocation","s3:ListBucket"],"Effect":"Allow","Principal":{"AWS":["*"]},"Resource":["arn:aws:s3:::%s"],"Sid":""},{"Action":["s3:GetObject"],"Effect":"Allow","Principal":{"AWS":["*"]},"Resource":["arn:aws:s3:::%s/this*"],"Sid":""}]}`
|
||||||
|
|
||||||
// Writing bucket policy before running test on GetBucketPolicy.
|
// Writing bucket policy before running test on GetBucketPolicy.
|
||||||
putTestPolicies := []struct {
|
putTestPolicies := []struct {
|
||||||
|
@ -26,6 +26,8 @@ import (
|
|||||||
"path"
|
"path"
|
||||||
"sort"
|
"sort"
|
||||||
"strings"
|
"strings"
|
||||||
|
|
||||||
|
"github.com/minio/minio-go/pkg/set"
|
||||||
)
|
)
|
||||||
|
|
||||||
const (
|
const (
|
||||||
@ -34,50 +36,39 @@ const (
|
|||||||
)
|
)
|
||||||
|
|
||||||
// supportedActionMap - lists all the actions supported by minio.
|
// supportedActionMap - lists all the actions supported by minio.
|
||||||
var supportedActionMap = map[string]struct{}{
|
var supportedActionMap = set.CreateStringSet("*", "*", "s3:*", "s3:GetObject",
|
||||||
"*": {},
|
"s3:ListBucket", "s3:PutObject", "s3:GetBucketLocation", "s3:DeleteObject",
|
||||||
"s3:*": {},
|
"s3:AbortMultipartUpload", "s3:ListBucketMultipartUploads", "s3:ListMultipartUploadParts")
|
||||||
"s3:GetObject": {},
|
|
||||||
"s3:ListBucket": {},
|
|
||||||
"s3:PutObject": {},
|
|
||||||
"s3:GetBucketLocation": {},
|
|
||||||
"s3:DeleteObject": {},
|
|
||||||
"s3:AbortMultipartUpload": {},
|
|
||||||
"s3:ListBucketMultipartUploads": {},
|
|
||||||
"s3:ListMultipartUploadParts": {},
|
|
||||||
}
|
|
||||||
|
|
||||||
// supported Conditions type.
|
// supported Conditions type.
|
||||||
var supportedConditionsType = map[string]struct{}{
|
var supportedConditionsType = set.CreateStringSet("StringEquals", "StringNotEquals")
|
||||||
"StringEquals": {},
|
|
||||||
"StringNotEquals": {},
|
|
||||||
}
|
|
||||||
|
|
||||||
// Validate s3:prefix, s3:max-keys are present if not
|
// Validate s3:prefix, s3:max-keys are present if not
|
||||||
// supported keys for the conditions.
|
// supported keys for the conditions.
|
||||||
var supportedConditionsKey = map[string]struct{}{
|
var supportedConditionsKey = set.CreateStringSet("s3:prefix", "s3:max-keys")
|
||||||
"s3:prefix": {},
|
|
||||||
"s3:max-keys": {},
|
|
||||||
}
|
|
||||||
|
|
||||||
// User - canonical users list.
|
// supportedEffectMap - supported effects.
|
||||||
|
var supportedEffectMap = set.CreateStringSet("Allow", "Deny")
|
||||||
|
|
||||||
|
// policyUser - canonical users list.
|
||||||
type policyUser struct {
|
type policyUser struct {
|
||||||
AWS []string
|
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 {
|
||||||
Sid string
|
Actions set.StringSet `json:"Action"`
|
||||||
|
Conditions map[string]map[string]set.StringSet `json:"Condition,omitempty"`
|
||||||
Effect string
|
Effect string
|
||||||
Principal policyUser `json:"Principal"`
|
Principal policyUser `json:"Principal"`
|
||||||
Actions []string `json:"Action"`
|
Resources set.StringSet `json:"Resource"`
|
||||||
Resources []string `json:"Resource"`
|
Sid string
|
||||||
Conditions map[string]map[string]string `json:"Condition,omitempty"`
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// bucketPolicy - collection of various bucket policy statements.
|
// bucketPolicy - collection of various bucket policy statements.
|
||||||
type bucketPolicy struct {
|
type bucketPolicy struct {
|
||||||
Version string // date in 0000-00-00 format
|
Version string // date in YYYY-MM-DD format
|
||||||
Statements []policyStatement `json:"Statement"`
|
Statements []policyStatement `json:"Statement"`
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -91,51 +82,42 @@ func (b bucketPolicy) String() string {
|
|||||||
return string(bbytes)
|
return string(bbytes)
|
||||||
}
|
}
|
||||||
|
|
||||||
// supportedEffectMap - supported effects.
|
|
||||||
var supportedEffectMap = map[string]struct{}{
|
|
||||||
"Allow": {},
|
|
||||||
"Deny": {},
|
|
||||||
}
|
|
||||||
|
|
||||||
// isValidActions - are actions valid.
|
// isValidActions - are actions valid.
|
||||||
func isValidActions(actions []string) (err error) {
|
func isValidActions(actions set.StringSet) (err error) {
|
||||||
// Statement actions cannot be empty.
|
// Statement actions cannot be empty.
|
||||||
if len(actions) == 0 {
|
if len(actions) == 0 {
|
||||||
err = errors.New("Action list cannot be empty.")
|
err = errors.New("Action list cannot be empty.")
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
for _, action := range actions {
|
if unsupportedActions := actions.Difference(supportedActionMap); !unsupportedActions.IsEmpty() {
|
||||||
if _, ok := supportedActionMap[action]; !ok {
|
err = fmt.Errorf("Unsupported actions found: ‘%#v’, please validate your policy document.", unsupportedActions)
|
||||||
err = errors.New("Unsupported action found: ‘" + action + "’, please validate your policy document.")
|
return err
|
||||||
return err
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
// isValidEffect - is effect valid.
|
// isValidEffect - is effect valid.
|
||||||
func isValidEffect(effect string) error {
|
func isValidEffect(effect string) (err error) {
|
||||||
// Statement effect cannot be empty.
|
// Statement effect cannot be empty.
|
||||||
if len(effect) == 0 {
|
if effect == "" {
|
||||||
err := errors.New("Policy effect cannot be empty.")
|
err = errors.New("Policy effect cannot be empty.")
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
_, ok := supportedEffectMap[effect]
|
if !supportedEffectMap.Contains(effect) {
|
||||||
if !ok {
|
err = errors.New("Unsupported Effect found: ‘" + effect + "’, please validate your policy document.")
|
||||||
err := errors.New("Unsupported Effect found: ‘" + effect + "’, please validate your policy document.")
|
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
// isValidResources - are valid resources.
|
// isValidResources - are valid resources.
|
||||||
func isValidResources(resources []string) (err error) {
|
func isValidResources(resources set.StringSet) (err error) {
|
||||||
// Statement resources cannot be empty.
|
// Statement resources cannot be empty.
|
||||||
if len(resources) == 0 {
|
if len(resources) == 0 {
|
||||||
err = errors.New("Resource list cannot be empty.")
|
err = errors.New("Resource list cannot be empty.")
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
for _, resource := range resources {
|
for resource := range resources {
|
||||||
if !strings.HasPrefix(resource, AWSResourcePrefix) {
|
if !strings.HasPrefix(resource, AWSResourcePrefix) {
|
||||||
err = errors.New("Unsupported resource style found: ‘" + resource + "’, please validate your policy document.")
|
err = errors.New("Unsupported resource style found: ‘" + resource + "’, please validate your policy document.")
|
||||||
return err
|
return err
|
||||||
@ -150,63 +132,50 @@ func isValidResources(resources []string) (err error) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// isValidPrincipals - are valid principals.
|
// isValidPrincipals - are valid principals.
|
||||||
func isValidPrincipals(principals []string) (err error) {
|
func isValidPrincipals(principals set.StringSet) (err error) {
|
||||||
// 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.")
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
for _, principal := range principals {
|
if unsuppPrincipals := principals.Difference(set.CreateStringSet([]string{"*"}...)); !unsuppPrincipals.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 principals: http://docs.aws.amazon.com/IAM/latest/UserGuide/reference_policies_elements.html#Principal
|
||||||
if principal != "*" {
|
err = fmt.Errorf("Unsupported principals found: ‘%#v’, please validate your policy document.", unsuppPrincipals)
|
||||||
err = fmt.Errorf("Unsupported principal style found: ‘%s’, please validate your policy document.", principal)
|
return err
|
||||||
return err
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
// isValidConditions - are valid conditions.
|
// isValidConditions - are valid conditions.
|
||||||
func isValidConditions(conditions map[string]map[string]string) (err error) {
|
func isValidConditions(conditions map[string]map[string]set.StringSet) (err error) {
|
||||||
// Returns true if string 'a' is found in the list.
|
|
||||||
findString := func(a string, list []string) bool {
|
|
||||||
for _, b := range list {
|
|
||||||
if b == a {
|
|
||||||
return true
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return false
|
|
||||||
}
|
|
||||||
conditionKeyVal := make(map[string][]string)
|
|
||||||
// Verify conditions should be valid.
|
// Verify conditions should be valid.
|
||||||
// Validate if stringEquals, stringNotEquals are present
|
// Validate if stringEquals, stringNotEquals are present
|
||||||
// if not throw an error.
|
// if not throw an error.
|
||||||
|
conditionKeyVal := make(map[string]set.StringSet)
|
||||||
for conditionType := range conditions {
|
for conditionType := range conditions {
|
||||||
_, validType := supportedConditionsType[conditionType]
|
if !supportedConditionsType.Contains(conditionType) {
|
||||||
if !validType {
|
|
||||||
err = fmt.Errorf("Unsupported condition type '%s', please validate your policy document.", conditionType)
|
err = fmt.Errorf("Unsupported condition type '%s', please validate your policy document.", conditionType)
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
for key := range conditions[conditionType] {
|
for key, value := range conditions[conditionType] {
|
||||||
_, validKey := supportedConditionsKey[key]
|
if !supportedConditionsKey.Contains(key) {
|
||||||
if !validKey {
|
|
||||||
err = fmt.Errorf("Unsupported condition key '%s', please validate your policy document.", conditionType)
|
err = fmt.Errorf("Unsupported condition key '%s', please validate your policy document.", conditionType)
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
conditionArray, ok := conditionKeyVal[key]
|
conditionVal, ok := conditionKeyVal[key]
|
||||||
if ok && findString(conditions[conditionType][key], conditionArray) {
|
if ok && !value.Intersection(conditionVal).IsEmpty() {
|
||||||
err = fmt.Errorf("Ambigious condition values for key '%s', please validate your policy document.", key)
|
err = fmt.Errorf("Ambigious condition values for key '%s', please validate your policy document.", key)
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
conditionKeyVal[key] = append(conditionKeyVal[key], conditions[conditionType][key])
|
conditionKeyVal[key] = value
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
// List of actions for which prefixes are not allowed.
|
// List of actions for which prefixes are not allowed.
|
||||||
var invalidPrefixActions = map[string]struct{}{
|
var invalidPrefixActions = set.StringSet{
|
||||||
"s3:GetBucketLocation": {},
|
"s3:GetBucketLocation": {},
|
||||||
"s3:ListBucket": {},
|
"s3:ListBucket": {},
|
||||||
"s3:ListBucketMultipartUploads": {},
|
"s3:ListBucketMultipartUploads": {},
|
||||||
@ -227,10 +196,10 @@ func resourcePrefix(resource string) string {
|
|||||||
func checkBucketPolicyResources(bucket string, bucketPolicy *bucketPolicy) APIErrorCode {
|
func checkBucketPolicyResources(bucket string, bucketPolicy *bucketPolicy) 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 = make(map[string]struct{})
|
var resourceMap = set.NewStringSet()
|
||||||
for _, statement := range bucketPolicy.Statements {
|
for _, statement := range bucketPolicy.Statements {
|
||||||
for _, action := range statement.Actions {
|
for action := range statement.Actions {
|
||||||
for _, resource := range statement.Resources {
|
for resource := range statement.Resources {
|
||||||
resourcePrefix := strings.SplitAfter(resource, AWSResourcePrefix)[1]
|
resourcePrefix := strings.SplitAfter(resource, AWSResourcePrefix)[1]
|
||||||
if _, ok := invalidPrefixActions[action]; ok {
|
if _, ok := invalidPrefixActions[action]; ok {
|
||||||
// Resource prefix is not equal to bucket for
|
// Resource prefix is not equal to bucket for
|
||||||
@ -245,7 +214,7 @@ func checkBucketPolicyResources(bucket string, bucketPolicy *bucketPolicy) APIEr
|
|||||||
return ErrMalformedPolicy
|
return ErrMalformedPolicy
|
||||||
}
|
}
|
||||||
// All valid resources collect them separately to verify nesting.
|
// All valid resources collect them separately to verify nesting.
|
||||||
resourceMap[resourcePrefix] = struct{}{}
|
resourceMap.Add(resourcePrefix)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -21,8 +21,11 @@ import (
|
|||||||
"errors"
|
"errors"
|
||||||
"fmt"
|
"fmt"
|
||||||
"testing"
|
"testing"
|
||||||
|
|
||||||
|
"github.com/minio/minio-go/pkg/set"
|
||||||
)
|
)
|
||||||
|
|
||||||
|
// Common bucket actions for both read and write policies.
|
||||||
var (
|
var (
|
||||||
readWriteBucketActions = []string{
|
readWriteBucketActions = []string{
|
||||||
"s3:GetBucketLocation",
|
"s3:GetBucketLocation",
|
||||||
@ -73,9 +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 = []string{"*"}
|
objectResourceStatement.Principal.AWS = set.CreateStringSet([]string{"*"}...)
|
||||||
objectResourceStatement.Resources = []string{fmt.Sprintf("%s%s", AWSResourcePrefix, bucketName+"/"+objectPrefix+"*")}
|
objectResourceStatement.Resources = set.CreateStringSet([]string{fmt.Sprintf("%s%s", AWSResourcePrefix, bucketName+"/"+objectPrefix+"*")}...)
|
||||||
objectResourceStatement.Actions = readWriteObjectActions
|
objectResourceStatement.Actions = set.CreateStringSet(readWriteObjectActions...)
|
||||||
return objectResourceStatement
|
return objectResourceStatement
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -83,9 +86,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 = []string{"*"}
|
bucketResourceStatement.Principal.AWS = set.CreateStringSet([]string{"*"}...)
|
||||||
bucketResourceStatement.Resources = []string{fmt.Sprintf("%s%s", AWSResourcePrefix, bucketName)}
|
bucketResourceStatement.Resources = set.CreateStringSet([]string{fmt.Sprintf("%s%s", AWSResourcePrefix, bucketName)}...)
|
||||||
bucketResourceStatement.Actions = readWriteBucketActions
|
bucketResourceStatement.Actions = set.CreateStringSet(readWriteBucketActions...)
|
||||||
return bucketResourceStatement
|
return bucketResourceStatement
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -101,9 +104,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 = []string{"*"}
|
bucketResourceStatement.Principal.AWS = set.CreateStringSet([]string{"*"}...)
|
||||||
bucketResourceStatement.Resources = []string{fmt.Sprintf("%s%s", AWSResourcePrefix, bucketName)}
|
bucketResourceStatement.Resources = set.CreateStringSet([]string{fmt.Sprintf("%s%s", AWSResourcePrefix, bucketName)}...)
|
||||||
bucketResourceStatement.Actions = readOnlyBucketActions
|
bucketResourceStatement.Actions = set.CreateStringSet(readOnlyBucketActions...)
|
||||||
return bucketResourceStatement
|
return bucketResourceStatement
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -111,9 +114,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 = []string{"*"}
|
objectResourceStatement.Principal.AWS = set.CreateStringSet([]string{"*"}...)
|
||||||
objectResourceStatement.Resources = []string{fmt.Sprintf("%s%s", AWSResourcePrefix, bucketName+"/"+objectPrefix+"*")}
|
objectResourceStatement.Resources = set.CreateStringSet([]string{fmt.Sprintf("%s%s", AWSResourcePrefix, bucketName+"/"+objectPrefix+"*")}...)
|
||||||
objectResourceStatement.Actions = readOnlyObjectActions
|
objectResourceStatement.Actions = set.CreateStringSet(readOnlyObjectActions...)
|
||||||
return objectResourceStatement
|
return objectResourceStatement
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -130,9 +133,9 @@ func getWriteOnlyBucketStatement(bucketName, objectPrefix string) policyStatemen
|
|||||||
|
|
||||||
bucketResourceStatement := policyStatement{}
|
bucketResourceStatement := policyStatement{}
|
||||||
bucketResourceStatement.Effect = "Allow"
|
bucketResourceStatement.Effect = "Allow"
|
||||||
bucketResourceStatement.Principal.AWS = []string{"*"}
|
bucketResourceStatement.Principal.AWS = set.CreateStringSet([]string{"*"}...)
|
||||||
bucketResourceStatement.Resources = []string{fmt.Sprintf("%s%s", AWSResourcePrefix, bucketName)}
|
bucketResourceStatement.Resources = set.CreateStringSet([]string{fmt.Sprintf("%s%s", AWSResourcePrefix, bucketName)}...)
|
||||||
bucketResourceStatement.Actions = writeOnlyBucketActions
|
bucketResourceStatement.Actions = set.CreateStringSet(writeOnlyBucketActions...)
|
||||||
return bucketResourceStatement
|
return bucketResourceStatement
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -140,9 +143,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 = []string{"*"}
|
objectResourceStatement.Principal.AWS = set.CreateStringSet([]string{"*"}...)
|
||||||
objectResourceStatement.Resources = []string{fmt.Sprintf("%s%s", AWSResourcePrefix, bucketName+"/"+objectPrefix+"*")}
|
objectResourceStatement.Resources = set.CreateStringSet([]string{fmt.Sprintf("%s%s", AWSResourcePrefix, bucketName+"/"+objectPrefix+"*")}...)
|
||||||
objectResourceStatement.Actions = writeOnlyObjectActions
|
objectResourceStatement.Actions = set.CreateStringSet(writeOnlyObjectActions...)
|
||||||
return objectResourceStatement
|
return objectResourceStatement
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -159,7 +162,7 @@ func getWriteOnlyStatement(bucketName, objectPrefix string) []policyStatement {
|
|||||||
func TestIsValidActions(t *testing.T) {
|
func TestIsValidActions(t *testing.T) {
|
||||||
testCases := []struct {
|
testCases := []struct {
|
||||||
// input.
|
// input.
|
||||||
actions []string
|
actions set.StringSet
|
||||||
// expected output.
|
// expected output.
|
||||||
err error
|
err error
|
||||||
// flag indicating whether the test should pass.
|
// flag indicating whether the test should pass.
|
||||||
@ -168,19 +171,22 @@ func TestIsValidActions(t *testing.T) {
|
|||||||
// Inputs with unsupported Action.
|
// Inputs with unsupported Action.
|
||||||
// Test case - 1.
|
// Test case - 1.
|
||||||
// "s3:ListObject" is an invalid Action.
|
// "s3:ListObject" is an invalid Action.
|
||||||
{[]string{"s3:GetObject", "s3:ListObject", "s3:RemoveObject"}, errors.New("Unsupported action found: ‘s3:ListObject’, please validate your policy document."), false},
|
{set.CreateStringSet([]string{"s3:GetObject", "s3:ListObject", "s3:RemoveObject"}...),
|
||||||
|
errors.New("Unsupported actions found: ‘set.StringSet{\"s3:RemoveObject\":struct {}{}, \"s3:ListObject\":struct {}{}}’, please validate your policy document."), false},
|
||||||
// Test case - 2.
|
// Test case - 2.
|
||||||
// Empty Actions.
|
// Empty Actions.
|
||||||
{[]string{}, errors.New("Action list cannot be empty."), false},
|
{set.CreateStringSet([]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"},
|
{set.CreateStringSet([]string{"s3:GetObject", "s3:ListBucket", "s3:PutObject", "s3:DeleteEverything"}...),
|
||||||
errors.New("Unsupported action found: ‘s3:DeleteEverything’, please validate your policy document."), false},
|
errors.New("Unsupported actions found: ‘set.StringSet{\"s3:DeleteEverything\":struct {}{}}’, please validate your policy document."), false},
|
||||||
|
|
||||||
// Inputs with valid Action.
|
// Inputs with valid Action.
|
||||||
// Test Case - 4.
|
// Test Case - 4.
|
||||||
{[]string{"s3:*", "*", "s3:GetObject", "s3:ListBucket",
|
{set.CreateStringSet([]string{
|
||||||
"s3:PutObject", "s3:GetBucketLocation", "s3:DeleteObject", "s3:AbortMultipartUpload", "s3:ListBucketMultipartUploads", "s3:ListMultipartUploadParts"}, nil, true},
|
"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)
|
||||||
@ -190,13 +196,6 @@ func TestIsValidActions(t *testing.T) {
|
|||||||
if err == nil && !testCase.shouldPass {
|
if err == nil && !testCase.shouldPass {
|
||||||
t.Errorf("Test %d: Expected to fail with <ERROR> \"%s\", but passed instead", i+1, testCase.err.Error())
|
t.Errorf("Test %d: Expected to fail with <ERROR> \"%s\", but passed instead", i+1, testCase.err.Error())
|
||||||
}
|
}
|
||||||
// Failed as expected, but does it fail for the expected reason.
|
|
||||||
if err != nil && !testCase.shouldPass {
|
|
||||||
if err.Error() != testCase.err.Error() {
|
|
||||||
t.Errorf("Test %d: Expected to fail with error \"%s\", but instead failed with error \"%s\"", i+1, testCase.err.Error(), err.Error())
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -212,16 +211,18 @@ func TestIsValidEffect(t *testing.T) {
|
|||||||
}{
|
}{
|
||||||
// Inputs with unsupported Effect.
|
// Inputs with unsupported Effect.
|
||||||
// Test case - 1.
|
// Test case - 1.
|
||||||
{"DontAllow", errors.New("Unsupported Effect found: ‘DontAllow’, please validate your policy document."), false},
|
{"", errors.New("Policy effect cannot be empty."), false},
|
||||||
// Test case - 2.
|
// Test case - 2.
|
||||||
{"NeverAllow", errors.New("Unsupported Effect found: ‘NeverAllow’, please validate your policy document."), false},
|
{"DontAllow", errors.New("Unsupported Effect found: ‘DontAllow’, please validate your policy document."), false},
|
||||||
// Test case - 3.
|
// Test case - 3.
|
||||||
|
{"NeverAllow", errors.New("Unsupported Effect found: ‘NeverAllow’, please validate your policy document."), false},
|
||||||
|
// Test case - 4.
|
||||||
{"AllowAlways", errors.New("Unsupported Effect found: ‘AllowAlways’, please validate your policy document."), false},
|
{"AllowAlways", errors.New("Unsupported Effect found: ‘AllowAlways’, please validate your policy document."), false},
|
||||||
|
|
||||||
// Inputs with valid Effect.
|
// Inputs with valid Effect.
|
||||||
// Test Case - 4.
|
|
||||||
{"Allow", nil, true},
|
|
||||||
// Test Case - 5.
|
// Test Case - 5.
|
||||||
|
{"Allow", nil, true},
|
||||||
|
// Test Case - 6.
|
||||||
{"Deny", nil, true},
|
{"Deny", nil, true},
|
||||||
}
|
}
|
||||||
for i, testCase := range testCases {
|
for i, testCase := range testCases {
|
||||||
@ -271,7 +272,7 @@ func TestIsValidResources(t *testing.T) {
|
|||||||
{[]string{"arn:aws:s3:::my-bucket/Asia/India/*"}, nil, true},
|
{[]string{"arn:aws:s3:::my-bucket/Asia/India/*"}, nil, true},
|
||||||
}
|
}
|
||||||
for i, testCase := range testCases {
|
for i, testCase := range testCases {
|
||||||
err := isValidResources(testCase.resources)
|
err := isValidResources(set.CreateStringSet(testCase.resources...))
|
||||||
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())
|
||||||
}
|
}
|
||||||
@ -303,16 +304,15 @@ func TestIsValidPrincipals(t *testing.T) {
|
|||||||
{[]string{}, errors.New("Principal cannot be empty."), false},
|
{[]string{}, errors.New("Principal cannot be empty."), false},
|
||||||
// Test case - 2.
|
// Test case - 2.
|
||||||
// "*" is the only valid principal.
|
// "*" is the only valid principal.
|
||||||
{[]string{"my-principal"}, errors.New("Unsupported principal style found: ‘my-principal’, please validate your policy document."), false},
|
{[]string{"my-principal"}, errors.New("Unsupported principals found: ‘set.StringSet{\"my-principal\":struct {}{}}’, please validate your policy document."), false},
|
||||||
// Test case - 3.
|
// Test case - 3.
|
||||||
{[]string{"*", "111122233"}, errors.New("Unsupported principal style found: ‘111122233’, please validate your policy document."), false},
|
{[]string{"*", "111122233"}, errors.New("Unsupported principals found: ‘set.StringSet{\"111122233\":struct {}{}}’, please validate your policy document."), false},
|
||||||
|
|
||||||
// Test case - 4.
|
// Test case - 4.
|
||||||
// Test case with valid principal value.
|
// Test case with valid principal value.
|
||||||
{[]string{"*"}, nil, true},
|
{[]string{"*"}, nil, true},
|
||||||
}
|
}
|
||||||
for i, testCase := range testCases {
|
for i, testCase := range testCases {
|
||||||
err := isValidPrincipals(testCase.principals)
|
err := isValidPrincipals(set.CreateStringSet(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())
|
||||||
}
|
}
|
||||||
@ -331,70 +331,70 @@ func TestIsValidPrincipals(t *testing.T) {
|
|||||||
// Tests validate policyStatement condition validator.
|
// Tests validate policyStatement 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]string {
|
setEmptyConditions := func() map[string]map[string]set.StringSet {
|
||||||
return make(map[string]map[string]string)
|
return make(map[string]map[string]set.StringSet)
|
||||||
}
|
}
|
||||||
|
|
||||||
// returns map with the "StringEquals" set to empty map.
|
// returns map with the "StringEquals" set to empty map.
|
||||||
setEmptyStringEquals := func() map[string]map[string]string {
|
setEmptyStringEquals := func() map[string]map[string]set.StringSet {
|
||||||
emptyMap := make(map[string]string)
|
emptyMap := make(map[string]set.StringSet)
|
||||||
conditions := make(map[string]map[string]string)
|
conditions := make(map[string]map[string]set.StringSet)
|
||||||
conditions["StringEquals"] = emptyMap
|
conditions["StringEquals"] = emptyMap
|
||||||
return conditions
|
return conditions
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// returns map with the "StringNotEquals" set to empty map.
|
// returns map with the "StringNotEquals" set to empty map.
|
||||||
setEmptyStringNotEquals := func() map[string]map[string]string {
|
setEmptyStringNotEquals := func() map[string]map[string]set.StringSet {
|
||||||
emptyMap := make(map[string]string)
|
emptyMap := make(map[string]set.StringSet)
|
||||||
conditions := make(map[string]map[string]string)
|
conditions := make(map[string]map[string]set.StringSet)
|
||||||
conditions["StringNotEquals"] = emptyMap
|
conditions["StringNotEquals"] = emptyMap
|
||||||
return conditions
|
return conditions
|
||||||
|
|
||||||
}
|
}
|
||||||
// Generate conditions.
|
// Generate conditions.
|
||||||
generateConditions := func(key1, key2, value string) map[string]map[string]string {
|
generateConditions := func(key1, key2, value string) map[string]map[string]set.StringSet {
|
||||||
innerMap := make(map[string]string)
|
innerMap := make(map[string]set.StringSet)
|
||||||
innerMap[key2] = value
|
innerMap[key2] = set.CreateStringSet(value)
|
||||||
conditions := make(map[string]map[string]string)
|
conditions := make(map[string]map[string]set.StringSet)
|
||||||
conditions[key1] = innerMap
|
conditions[key1] = innerMap
|
||||||
return conditions
|
return conditions
|
||||||
}
|
}
|
||||||
|
|
||||||
// generate ambigious conditions.
|
// generate ambigious conditions.
|
||||||
generateAmbigiousConditions := func() map[string]map[string]string {
|
generateAmbigiousConditions := func() map[string]map[string]set.StringSet {
|
||||||
innerMap := make(map[string]string)
|
innerMap := make(map[string]set.StringSet)
|
||||||
innerMap["s3:prefix"] = "Asia/"
|
innerMap["s3:prefix"] = set.CreateStringSet("Asia/")
|
||||||
conditions := make(map[string]map[string]string)
|
conditions := make(map[string]map[string]set.StringSet)
|
||||||
conditions["StringEquals"] = innerMap
|
conditions["StringEquals"] = innerMap
|
||||||
conditions["StringNotEquals"] = innerMap
|
conditions["StringNotEquals"] = innerMap
|
||||||
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]string {
|
generateValidInvalidConditions := func() map[string]map[string]set.StringSet {
|
||||||
innerMap := make(map[string]string)
|
innerMap := make(map[string]set.StringSet)
|
||||||
innerMap["s3:prefix"] = "Asia/"
|
innerMap["s3:prefix"] = set.CreateStringSet("Asia/")
|
||||||
conditions := make(map[string]map[string]string)
|
conditions := make(map[string]map[string]set.StringSet)
|
||||||
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]string {
|
generateValidInvalidConditionKeys := func() map[string]map[string]set.StringSet {
|
||||||
innerMapValid := make(map[string]string)
|
innerMapValid := make(map[string]set.StringSet)
|
||||||
innerMapValid["s3:prefix"] = "Asia/"
|
innerMapValid["s3:prefix"] = set.CreateStringSet("Asia/")
|
||||||
innerMapInValid := make(map[string]string)
|
innerMapInValid := make(map[string]set.StringSet)
|
||||||
innerMapInValid["s3:invalid"] = "Asia/"
|
innerMapInValid["s3:invalid"] = set.CreateStringSet("Asia/")
|
||||||
conditions := make(map[string]map[string]string)
|
conditions := make(map[string]map[string]set.StringSet)
|
||||||
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]string{
|
testConditions := []map[string]map[string]set.StringSet{
|
||||||
generateConditions("StringValues", "s3:max-keys", "100"),
|
generateConditions("StringValues", "s3:max-keys", "100"),
|
||||||
generateConditions("StringEquals", "s3:Object", "100"),
|
generateConditions("StringEquals", "s3:Object", "100"),
|
||||||
generateAmbigiousConditions(),
|
generateAmbigiousConditions(),
|
||||||
@ -410,7 +410,7 @@ func TestIsValidConditions(t *testing.T) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
testCases := []struct {
|
testCases := []struct {
|
||||||
inputCondition map[string]map[string]string
|
inputCondition map[string]map[string]set.StringSet
|
||||||
// expected result.
|
// expected result.
|
||||||
expectedErr error
|
expectedErr error
|
||||||
// flag indicating whether test should pass.
|
// flag indicating whether test should pass.
|
||||||
@ -474,20 +474,20 @@ func TestIsValidConditions(t *testing.T) {
|
|||||||
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 []policyStatement) []policyStatement {
|
||||||
statements[0].Actions = []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 []policyStatement) []policyStatement {
|
||||||
statements[0].Resources = []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 []policyStatement) []policyStatement {
|
||||||
statements[0].Resources = []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
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -575,7 +575,7 @@ func TestParseBucketPolicy(t *testing.T) {
|
|||||||
// set Unsupported Actions.
|
// set Unsupported Actions.
|
||||||
setUnsupportedActions := func(statements []policyStatement) []policyStatement {
|
setUnsupportedActions := func(statements []policyStatement) []policyStatement {
|
||||||
// "s3:DeleteEverything"" is an Unsupported Action.
|
// "s3:DeleteEverything"" is an Unsupported Action.
|
||||||
statements[0].Actions = []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.
|
||||||
@ -587,13 +587,13 @@ 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 = []string{"*", "User1111"}
|
statements[0].Principal.AWS = set.CreateStringSet([]string{"*", "User1111"}...)
|
||||||
return statements
|
return statements
|
||||||
}
|
}
|
||||||
// set unsupported Resources.
|
// set unsupported Resources.
|
||||||
setUnsupportedResources := func(statements []policyStatement) []policyStatement {
|
setUnsupportedResources := func(statements []policyStatement) []policyStatement {
|
||||||
// "s3:DeleteEverything"" is an Unsupported Action.
|
// "s3:DeleteEverything"" is an Unsupported Action.
|
||||||
statements[0].Resources = []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.
|
||||||
@ -652,13 +652,13 @@ func TestParseBucketPolicy(t *testing.T) {
|
|||||||
{bucketAccesPolicies[4], bucketAccesPolicies[4], nil, true},
|
{bucketAccesPolicies[4], bucketAccesPolicies[4], nil, true},
|
||||||
// Test case - 6.
|
// Test case - 6.
|
||||||
// bucketPolicy statement contains unsupported action.
|
// bucketPolicy statement contains unsupported action.
|
||||||
{bucketAccesPolicies[5], bucketAccesPolicies[5], fmt.Errorf("Unsupported action found: ‘s3:DeleteEverything’, please validate your policy document."), false},
|
{bucketAccesPolicies[5], bucketAccesPolicies[5], fmt.Errorf("Unsupported actions found: ‘set.StringSet{\"s3:DeleteEverything\":struct {}{}}’, please validate your policy document."), false},
|
||||||
// Test case - 7.
|
// Test case - 7.
|
||||||
// bucketPolicy statement contains unsupported Effect.
|
// bucketPolicy statement contains unsupported Effect.
|
||||||
{bucketAccesPolicies[6], bucketAccesPolicies[6], fmt.Errorf("Unsupported Effect found: ‘DontAllow’, please validate your policy document."), false},
|
{bucketAccesPolicies[6], bucketAccesPolicies[6], fmt.Errorf("Unsupported Effect found: ‘DontAllow’, please validate your policy document."), false},
|
||||||
// Test case - 8.
|
// Test case - 8.
|
||||||
// bucketPolicy statement contains unsupported Principal.
|
// bucketPolicy statement contains unsupported Principal.
|
||||||
{bucketAccesPolicies[7], bucketAccesPolicies[7], fmt.Errorf("Unsupported principal style found: ‘User1111’, please validate your policy document."), false},
|
{bucketAccesPolicies[7], bucketAccesPolicies[7], fmt.Errorf("Unsupported principals found: ‘set.StringSet{\"User1111\":struct {}{}}’, please validate your policy document."), false},
|
||||||
// Test case - 9.
|
// Test case - 9.
|
||||||
// bucketPolicy statement contains unsupported Resource.
|
// bucketPolicy statement contains unsupported Resource.
|
||||||
{bucketAccesPolicies[8], bucketAccesPolicies[8], fmt.Errorf("Unsupported resource style found: ‘my-resource’, please validate your policy document."), false},
|
{bucketAccesPolicies[8], bucketAccesPolicies[8], fmt.Errorf("Unsupported resource style found: ‘my-resource’, please validate your policy document."), false},
|
||||||
|
@ -162,7 +162,7 @@ func (s *TestSuiteCommon) TestBucketNotification(c *C) {
|
|||||||
// Deletes the policy and verifies the deletion by fetching it back.
|
// Deletes the policy and verifies the deletion by fetching it back.
|
||||||
func (s *TestSuiteCommon) TestBucketPolicy(c *C) {
|
func (s *TestSuiteCommon) TestBucketPolicy(c *C) {
|
||||||
// Sample bucket policy.
|
// Sample bucket policy.
|
||||||
bucketPolicyBuf := `{"Version":"2012-10-17","Statement":[{"Sid":"","Effect":"Allow","Principal":{"AWS":["*"]},"Action":["s3:GetBucketLocation","s3:ListBucket"],"Resource":["arn:aws:s3:::%s"]},{"Sid":"","Effect":"Allow","Principal":{"AWS":["*"]},"Action":["s3:GetObject"],"Resource":["arn:aws:s3:::%s/this*"]}]}`
|
bucketPolicyBuf := `{"Version":"2012-10-17","Statement":[{"Action":["s3:GetBucketLocation","s3:ListBucket"],"Effect":"Allow","Principal":{"AWS":["*"]},"Resource":["arn:aws:s3:::%s"],"Sid":""},{"Action":["s3:GetObject"],"Effect":"Allow","Principal":{"AWS":["*"]},"Resource":["arn:aws:s3:::%s/this*"],"Sid":""}]}`
|
||||||
|
|
||||||
// generate a random bucket Name.
|
// generate a random bucket Name.
|
||||||
bucketName := getRandomBucketName()
|
bucketName := getRandomBucketName()
|
||||||
|
202
vendor/github.com/minio/minio-go/LICENSE
generated
vendored
Normal file
202
vendor/github.com/minio/minio-go/LICENSE
generated
vendored
Normal file
@ -0,0 +1,202 @@
|
|||||||
|
|
||||||
|
Apache License
|
||||||
|
Version 2.0, January 2004
|
||||||
|
http://www.apache.org/licenses/
|
||||||
|
|
||||||
|
TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION
|
||||||
|
|
||||||
|
1. Definitions.
|
||||||
|
|
||||||
|
"License" shall mean the terms and conditions for use, reproduction,
|
||||||
|
and distribution as defined by Sections 1 through 9 of this document.
|
||||||
|
|
||||||
|
"Licensor" shall mean the copyright owner or entity authorized by
|
||||||
|
the copyright owner that is granting the License.
|
||||||
|
|
||||||
|
"Legal Entity" shall mean the union of the acting entity and all
|
||||||
|
other entities that control, are controlled by, or are under common
|
||||||
|
control with that entity. For the purposes of this definition,
|
||||||
|
"control" means (i) the power, direct or indirect, to cause the
|
||||||
|
direction or management of such entity, whether by contract or
|
||||||
|
otherwise, or (ii) ownership of fifty percent (50%) or more of the
|
||||||
|
outstanding shares, or (iii) beneficial ownership of such entity.
|
||||||
|
|
||||||
|
"You" (or "Your") shall mean an individual or Legal Entity
|
||||||
|
exercising permissions granted by this License.
|
||||||
|
|
||||||
|
"Source" form shall mean the preferred form for making modifications,
|
||||||
|
including but not limited to software source code, documentation
|
||||||
|
source, and configuration files.
|
||||||
|
|
||||||
|
"Object" form shall mean any form resulting from mechanical
|
||||||
|
transformation or translation of a Source form, including but
|
||||||
|
not limited to compiled object code, generated documentation,
|
||||||
|
and conversions to other media types.
|
||||||
|
|
||||||
|
"Work" shall mean the work of authorship, whether in Source or
|
||||||
|
Object form, made available under the License, as indicated by a
|
||||||
|
copyright notice that is included in or attached to the work
|
||||||
|
(an example is provided in the Appendix below).
|
||||||
|
|
||||||
|
"Derivative Works" shall mean any work, whether in Source or Object
|
||||||
|
form, that is based on (or derived from) the Work and for which the
|
||||||
|
editorial revisions, annotations, elaborations, or other modifications
|
||||||
|
represent, as a whole, an original work of authorship. For the purposes
|
||||||
|
of this License, Derivative Works shall not include works that remain
|
||||||
|
separable from, or merely link (or bind by name) to the interfaces of,
|
||||||
|
the Work and Derivative Works thereof.
|
||||||
|
|
||||||
|
"Contribution" shall mean any work of authorship, including
|
||||||
|
the original version of the Work and any modifications or additions
|
||||||
|
to that Work or Derivative Works thereof, that is intentionally
|
||||||
|
submitted to Licensor for inclusion in the Work by the copyright owner
|
||||||
|
or by an individual or Legal Entity authorized to submit on behalf of
|
||||||
|
the copyright owner. For the purposes of this definition, "submitted"
|
||||||
|
means any form of electronic, verbal, or written communication sent
|
||||||
|
to the Licensor or its representatives, including but not limited to
|
||||||
|
communication on electronic mailing lists, source code control systems,
|
||||||
|
and issue tracking systems that are managed by, or on behalf of, the
|
||||||
|
Licensor for the purpose of discussing and improving the Work, but
|
||||||
|
excluding communication that is conspicuously marked or otherwise
|
||||||
|
designated in writing by the copyright owner as "Not a Contribution."
|
||||||
|
|
||||||
|
"Contributor" shall mean Licensor and any individual or Legal Entity
|
||||||
|
on behalf of whom a Contribution has been received by Licensor and
|
||||||
|
subsequently incorporated within the Work.
|
||||||
|
|
||||||
|
2. Grant of Copyright License. Subject to the terms and conditions of
|
||||||
|
this License, each Contributor hereby grants to You a perpetual,
|
||||||
|
worldwide, non-exclusive, no-charge, royalty-free, irrevocable
|
||||||
|
copyright license to reproduce, prepare Derivative Works of,
|
||||||
|
publicly display, publicly perform, sublicense, and distribute the
|
||||||
|
Work and such Derivative Works in Source or Object form.
|
||||||
|
|
||||||
|
3. Grant of Patent License. Subject to the terms and conditions of
|
||||||
|
this License, each Contributor hereby grants to You a perpetual,
|
||||||
|
worldwide, non-exclusive, no-charge, royalty-free, irrevocable
|
||||||
|
(except as stated in this section) patent license to make, have made,
|
||||||
|
use, offer to sell, sell, import, and otherwise transfer the Work,
|
||||||
|
where such license applies only to those patent claims licensable
|
||||||
|
by such Contributor that are necessarily infringed by their
|
||||||
|
Contribution(s) alone or by combination of their Contribution(s)
|
||||||
|
with the Work to which such Contribution(s) was submitted. If You
|
||||||
|
institute patent litigation against any entity (including a
|
||||||
|
cross-claim or counterclaim in a lawsuit) alleging that the Work
|
||||||
|
or a Contribution incorporated within the Work constitutes direct
|
||||||
|
or contributory patent infringement, then any patent licenses
|
||||||
|
granted to You under this License for that Work shall terminate
|
||||||
|
as of the date such litigation is filed.
|
||||||
|
|
||||||
|
4. Redistribution. You may reproduce and distribute copies of the
|
||||||
|
Work or Derivative Works thereof in any medium, with or without
|
||||||
|
modifications, and in Source or Object form, provided that You
|
||||||
|
meet the following conditions:
|
||||||
|
|
||||||
|
(a) You must give any other recipients of the Work or
|
||||||
|
Derivative Works a copy of this License; and
|
||||||
|
|
||||||
|
(b) You must cause any modified files to carry prominent notices
|
||||||
|
stating that You changed the files; and
|
||||||
|
|
||||||
|
(c) You must retain, in the Source form of any Derivative Works
|
||||||
|
that You distribute, all copyright, patent, trademark, and
|
||||||
|
attribution notices from the Source form of the Work,
|
||||||
|
excluding those notices that do not pertain to any part of
|
||||||
|
the Derivative Works; and
|
||||||
|
|
||||||
|
(d) If the Work includes a "NOTICE" text file as part of its
|
||||||
|
distribution, then any Derivative Works that You distribute must
|
||||||
|
include a readable copy of the attribution notices contained
|
||||||
|
within such NOTICE file, excluding those notices that do not
|
||||||
|
pertain to any part of the Derivative Works, in at least one
|
||||||
|
of the following places: within a NOTICE text file distributed
|
||||||
|
as part of the Derivative Works; within the Source form or
|
||||||
|
documentation, if provided along with the Derivative Works; or,
|
||||||
|
within a display generated by the Derivative Works, if and
|
||||||
|
wherever such third-party notices normally appear. The contents
|
||||||
|
of the NOTICE file are for informational purposes only and
|
||||||
|
do not modify the License. You may add Your own attribution
|
||||||
|
notices within Derivative Works that You distribute, alongside
|
||||||
|
or as an addendum to the NOTICE text from the Work, provided
|
||||||
|
that such additional attribution notices cannot be construed
|
||||||
|
as modifying the License.
|
||||||
|
|
||||||
|
You may add Your own copyright statement to Your modifications and
|
||||||
|
may provide additional or different license terms and conditions
|
||||||
|
for use, reproduction, or distribution of Your modifications, or
|
||||||
|
for any such Derivative Works as a whole, provided Your use,
|
||||||
|
reproduction, and distribution of the Work otherwise complies with
|
||||||
|
the conditions stated in this License.
|
||||||
|
|
||||||
|
5. Submission of Contributions. Unless You explicitly state otherwise,
|
||||||
|
any Contribution intentionally submitted for inclusion in the Work
|
||||||
|
by You to the Licensor shall be under the terms and conditions of
|
||||||
|
this License, without any additional terms or conditions.
|
||||||
|
Notwithstanding the above, nothing herein shall supersede or modify
|
||||||
|
the terms of any separate license agreement you may have executed
|
||||||
|
with Licensor regarding such Contributions.
|
||||||
|
|
||||||
|
6. Trademarks. This License does not grant permission to use the trade
|
||||||
|
names, trademarks, service marks, or product names of the Licensor,
|
||||||
|
except as required for reasonable and customary use in describing the
|
||||||
|
origin of the Work and reproducing the content of the NOTICE file.
|
||||||
|
|
||||||
|
7. Disclaimer of Warranty. Unless required by applicable law or
|
||||||
|
agreed to in writing, Licensor provides the Work (and each
|
||||||
|
Contributor provides its Contributions) on an "AS IS" BASIS,
|
||||||
|
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or
|
||||||
|
implied, including, without limitation, any warranties or conditions
|
||||||
|
of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A
|
||||||
|
PARTICULAR PURPOSE. You are solely responsible for determining the
|
||||||
|
appropriateness of using or redistributing the Work and assume any
|
||||||
|
risks associated with Your exercise of permissions under this License.
|
||||||
|
|
||||||
|
8. Limitation of Liability. In no event and under no legal theory,
|
||||||
|
whether in tort (including negligence), contract, or otherwise,
|
||||||
|
unless required by applicable law (such as deliberate and grossly
|
||||||
|
negligent acts) or agreed to in writing, shall any Contributor be
|
||||||
|
liable to You for damages, including any direct, indirect, special,
|
||||||
|
incidental, or consequential damages of any character arising as a
|
||||||
|
result of this License or out of the use or inability to use the
|
||||||
|
Work (including but not limited to damages for loss of goodwill,
|
||||||
|
work stoppage, computer failure or malfunction, or any and all
|
||||||
|
other commercial damages or losses), even if such Contributor
|
||||||
|
has been advised of the possibility of such damages.
|
||||||
|
|
||||||
|
9. Accepting Warranty or Additional Liability. While redistributing
|
||||||
|
the Work or Derivative Works thereof, You may choose to offer,
|
||||||
|
and charge a fee for, acceptance of support, warranty, indemnity,
|
||||||
|
or other liability obligations and/or rights consistent with this
|
||||||
|
License. However, in accepting such obligations, You may act only
|
||||||
|
on Your own behalf and on Your sole responsibility, not on behalf
|
||||||
|
of any other Contributor, and only if You agree to indemnify,
|
||||||
|
defend, and hold each Contributor harmless for any liability
|
||||||
|
incurred by, or claims asserted against, such Contributor by reason
|
||||||
|
of your accepting any such warranty or additional liability.
|
||||||
|
|
||||||
|
END OF TERMS AND CONDITIONS
|
||||||
|
|
||||||
|
APPENDIX: How to apply the Apache License to your work.
|
||||||
|
|
||||||
|
To apply the Apache License to your work, attach the following
|
||||||
|
boilerplate notice, with the fields enclosed by brackets "[]"
|
||||||
|
replaced with your own identifying information. (Don't include
|
||||||
|
the brackets!) The text should be enclosed in the appropriate
|
||||||
|
comment syntax for the file format. We also recommend that a
|
||||||
|
file or class name and description of purpose be included on the
|
||||||
|
same "printed page" as the copyright notice for easier
|
||||||
|
identification within third-party archives.
|
||||||
|
|
||||||
|
Copyright [yyyy] [name of copyright owner]
|
||||||
|
|
||||||
|
Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
|
you may not use this file except in compliance with the License.
|
||||||
|
You may obtain a copy of the License at
|
||||||
|
|
||||||
|
http://www.apache.org/licenses/LICENSE-2.0
|
||||||
|
|
||||||
|
Unless required by applicable law or agreed to in writing, software
|
||||||
|
distributed under the License is distributed on an "AS IS" BASIS,
|
||||||
|
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||||
|
See the License for the specific language governing permissions and
|
||||||
|
limitations under the License.
|
196
vendor/github.com/minio/minio-go/pkg/set/stringset.go
generated
vendored
Normal file
196
vendor/github.com/minio/minio-go/pkg/set/stringset.go
generated
vendored
Normal file
@ -0,0 +1,196 @@
|
|||||||
|
/*
|
||||||
|
* Minio Go Library for Amazon S3 Compatible Cloud Storage (C) 2016 Minio, Inc.
|
||||||
|
*
|
||||||
|
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
|
* you may not use this file except in compliance with the License.
|
||||||
|
* You may obtain a copy of the License at
|
||||||
|
*
|
||||||
|
* http://www.apache.org/licenses/LICENSE-2.0
|
||||||
|
*
|
||||||
|
* Unless required by applicable law or agreed to in writing, software
|
||||||
|
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||||
|
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||||
|
* See the License for the specific language governing permissions and
|
||||||
|
* limitations under the License.
|
||||||
|
*/
|
||||||
|
|
||||||
|
package set
|
||||||
|
|
||||||
|
import (
|
||||||
|
"encoding/json"
|
||||||
|
"fmt"
|
||||||
|
"sort"
|
||||||
|
)
|
||||||
|
|
||||||
|
// StringSet - uses map as set of strings.
|
||||||
|
type StringSet map[string]struct{}
|
||||||
|
|
||||||
|
// keys - returns StringSet keys.
|
||||||
|
func (set StringSet) keys() []string {
|
||||||
|
keys := make([]string, 0, len(set))
|
||||||
|
for k := range set {
|
||||||
|
keys = append(keys, k)
|
||||||
|
}
|
||||||
|
sort.Strings(keys)
|
||||||
|
return keys
|
||||||
|
}
|
||||||
|
|
||||||
|
// IsEmpty - returns whether the set is empty or not.
|
||||||
|
func (set StringSet) IsEmpty() bool {
|
||||||
|
return len(set) == 0
|
||||||
|
}
|
||||||
|
|
||||||
|
// Add - adds string to the set.
|
||||||
|
func (set StringSet) Add(s string) {
|
||||||
|
set[s] = struct{}{}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Remove - removes string in the set. It does nothing if string does not exist in the set.
|
||||||
|
func (set StringSet) Remove(s string) {
|
||||||
|
delete(set, s)
|
||||||
|
}
|
||||||
|
|
||||||
|
// Contains - checks if string is in the set.
|
||||||
|
func (set StringSet) Contains(s string) bool {
|
||||||
|
_, ok := set[s]
|
||||||
|
return ok
|
||||||
|
}
|
||||||
|
|
||||||
|
// FuncMatch - returns new set containing each value who passes match function.
|
||||||
|
// A 'matchFn' should accept element in a set as first argument and
|
||||||
|
// 'matchString' as second argument. The function can do any logic to
|
||||||
|
// compare both the arguments and should return true to accept element in
|
||||||
|
// a set to include in output set else the element is ignored.
|
||||||
|
func (set StringSet) FuncMatch(matchFn func(string, string) bool, matchString string) StringSet {
|
||||||
|
nset := NewStringSet()
|
||||||
|
for k := range set {
|
||||||
|
if matchFn(k, matchString) {
|
||||||
|
nset.Add(k)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return nset
|
||||||
|
}
|
||||||
|
|
||||||
|
// ApplyFunc - returns new set containing each value processed by 'applyFn'.
|
||||||
|
// A 'applyFn' should accept element in a set as a argument and return
|
||||||
|
// a processed string. The function can do any logic to return a processed
|
||||||
|
// string.
|
||||||
|
func (set StringSet) ApplyFunc(applyFn func(string) string) StringSet {
|
||||||
|
nset := NewStringSet()
|
||||||
|
for k := range set {
|
||||||
|
nset.Add(applyFn(k))
|
||||||
|
}
|
||||||
|
return nset
|
||||||
|
}
|
||||||
|
|
||||||
|
// Equals - checks whether given set is equal to current set or not.
|
||||||
|
func (set StringSet) Equals(sset StringSet) bool {
|
||||||
|
// If length of set is not equal to length of given set, the
|
||||||
|
// set is not equal to given set.
|
||||||
|
if len(set) != len(sset) {
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
|
||||||
|
// As both sets are equal in length, check each elements are equal.
|
||||||
|
for k := range set {
|
||||||
|
if _, ok := sset[k]; !ok {
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return true
|
||||||
|
}
|
||||||
|
|
||||||
|
// Intersection - returns the intersection with given set as new set.
|
||||||
|
func (set StringSet) Intersection(sset StringSet) StringSet {
|
||||||
|
nset := NewStringSet()
|
||||||
|
for k := range set {
|
||||||
|
if _, ok := sset[k]; ok {
|
||||||
|
nset.Add(k)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return nset
|
||||||
|
}
|
||||||
|
|
||||||
|
// Difference - returns the difference with given set as new set.
|
||||||
|
func (set StringSet) Difference(sset StringSet) StringSet {
|
||||||
|
nset := NewStringSet()
|
||||||
|
for k := range set {
|
||||||
|
if _, ok := sset[k]; !ok {
|
||||||
|
nset.Add(k)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return nset
|
||||||
|
}
|
||||||
|
|
||||||
|
// Union - returns the union with given set as new set.
|
||||||
|
func (set StringSet) Union(sset StringSet) StringSet {
|
||||||
|
nset := NewStringSet()
|
||||||
|
for k := range set {
|
||||||
|
nset.Add(k)
|
||||||
|
}
|
||||||
|
|
||||||
|
for k := range sset {
|
||||||
|
nset.Add(k)
|
||||||
|
}
|
||||||
|
|
||||||
|
return nset
|
||||||
|
}
|
||||||
|
|
||||||
|
// MarshalJSON - converts to JSON data.
|
||||||
|
func (set StringSet) MarshalJSON() ([]byte, error) {
|
||||||
|
return json.Marshal(set.keys())
|
||||||
|
}
|
||||||
|
|
||||||
|
// UnmarshalJSON - parses JSON data and creates new set with it.
|
||||||
|
// If 'data' contains JSON string array, the set contains each string.
|
||||||
|
// If 'data' contains JSON string, the set contains the string as one element.
|
||||||
|
// If 'data' contains Other JSON types, JSON parse error is returned.
|
||||||
|
func (set *StringSet) UnmarshalJSON(data []byte) error {
|
||||||
|
sl := []string{}
|
||||||
|
var err error
|
||||||
|
if err = json.Unmarshal(data, &sl); err == nil {
|
||||||
|
*set = make(StringSet)
|
||||||
|
for _, s := range sl {
|
||||||
|
set.Add(s)
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
var s string
|
||||||
|
if err = json.Unmarshal(data, &s); err == nil {
|
||||||
|
*set = make(StringSet)
|
||||||
|
set.Add(s)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
// String - returns printable string of the set.
|
||||||
|
func (set StringSet) String() string {
|
||||||
|
return fmt.Sprintf("%s", set.keys())
|
||||||
|
}
|
||||||
|
|
||||||
|
// NewStringSet - creates new string set.
|
||||||
|
func NewStringSet() StringSet {
|
||||||
|
return make(StringSet)
|
||||||
|
}
|
||||||
|
|
||||||
|
// CreateStringSet - creates new string set with given string values.
|
||||||
|
func CreateStringSet(sl ...string) StringSet {
|
||||||
|
set := make(StringSet)
|
||||||
|
for _, k := range sl {
|
||||||
|
set.Add(k)
|
||||||
|
}
|
||||||
|
return set
|
||||||
|
}
|
||||||
|
|
||||||
|
// CopyStringSet - returns copy of given set.
|
||||||
|
func CopyStringSet(set StringSet) StringSet {
|
||||||
|
nset := NewStringSet()
|
||||||
|
for k, v := range set {
|
||||||
|
nset[k] = v
|
||||||
|
}
|
||||||
|
return nset
|
||||||
|
}
|
6
vendor/vendor.json
vendored
6
vendor/vendor.json
vendored
@ -107,6 +107,12 @@
|
|||||||
"revision": "db6b4f13442b26995f04b3b2b31b006cae7786e6",
|
"revision": "db6b4f13442b26995f04b3b2b31b006cae7786e6",
|
||||||
"revisionTime": "2016-02-29T08:42:30-08:00"
|
"revisionTime": "2016-02-29T08:42:30-08:00"
|
||||||
},
|
},
|
||||||
|
{
|
||||||
|
"checksumSHA1": "A8QOw1aWwc+RtjGozY0XeS5varo=",
|
||||||
|
"path": "github.com/minio/minio-go/pkg/set",
|
||||||
|
"revision": "9e734013294ab153b0bdbe182738bcddd46f1947",
|
||||||
|
"revisionTime": "2016-08-18T00:31:20Z"
|
||||||
|
},
|
||||||
{
|
{
|
||||||
"path": "github.com/minio/miniobrowser",
|
"path": "github.com/minio/miniobrowser",
|
||||||
"revision": "2e74c097f04fd7927d46fee6482d41e7b5cbf830",
|
"revision": "2e74c097f04fd7927d46fee6482d41e7b5cbf830",
|
||||||
|
Loading…
Reference in New Issue
Block a user