starts-with policy condition support issue (#7937)

This commit is contained in:
ebozduman 2019-09-22 14:20:49 -07:00 committed by Harshavardhana
parent 26985ac632
commit dbf7b1e573
4 changed files with 34 additions and 27 deletions

View File

@ -315,6 +315,7 @@ const (
ErrAdminProfilerNotEnabled ErrAdminProfilerNotEnabled
ErrInvalidDecompressedSize ErrInvalidDecompressedSize
ErrAddUserInvalidArgument ErrAddUserInvalidArgument
ErrPostPolicyConditionInvalidFormat
) )
type errorCodeMap map[APIErrorCode]APIError type errorCodeMap map[APIErrorCode]APIError
@ -1496,6 +1497,11 @@ var errorCodes = errorCodeMap{
Description: "User is not allowed to be same as admin access key", Description: "User is not allowed to be same as admin access key",
HTTPStatusCode: http.StatusConflict, HTTPStatusCode: http.StatusConflict,
}, },
ErrPostPolicyConditionInvalidFormat: {
Code: "PostPolicyInvalidKeyName",
Description: "Invalid according to Policy: Policy Condition failed",
HTTPStatusCode: http.StatusForbidden,
},
// Add your error structure here. // Add your error structure here.
} }

View File

@ -656,9 +656,10 @@ func (api objectAPIHandlers) PostPolicyBucketHandler(w http.ResponseWriter, r *h
// Handle policy if it is set. // Handle policy if it is set.
if len(policyBytes) > 0 { if len(policyBytes) > 0 {
postPolicyForm, err := parsePostPolicyForm(string(policyBytes)) postPolicyForm, err := parsePostPolicyForm(string(policyBytes))
if err != nil { if err != nil {
writeErrorResponse(ctx, w, errorCodes.ToAPIErr(ErrMalformedPOSTRequest), r.URL, guessIsBrowserReq(r)) writeErrorResponse(ctx, w, errorCodes.ToAPIErr(ErrPostPolicyConditionInvalidFormat), r.URL, guessIsBrowserReq(r))
return return
} }

View File

@ -314,7 +314,7 @@ func testPostPolicyBucketHandler(obj ObjectLayer, instanceType string, t TestErr
{ {
objectName: "test", objectName: "test",
data: []byte("Hello, World"), data: []byte("Hello, World"),
expectedRespStatus: http.StatusBadRequest, expectedRespStatus: http.StatusForbidden,
accessKey: credentials.AccessKey, accessKey: credentials.AccessKey,
secretKey: credentials.SecretKey, secretKey: credentials.SecretKey,
dates: []interface{}{curTimePlus5Min.Format(expirationDateFormat), curTime.Format(iso8601DateFormat), curTime.Format(yyyymmdd)}, dates: []interface{}{curTimePlus5Min.Format(expirationDateFormat), curTime.Format(iso8601DateFormat), curTime.Format(yyyymmdd)},

View File

@ -101,8 +101,9 @@ type contentLengthRange struct {
type PostPolicyForm struct { type PostPolicyForm struct {
Expiration time.Time // Expiration date and time of the POST policy. Expiration time.Time // Expiration date and time of the POST policy.
Conditions struct { // Conditional policy structure. Conditions struct { // Conditional policy structure.
Policies map[string]struct { Policies []struct {
Operator string Operator string
Key string
Value string Value string
} }
ContentLengthRange contentLengthRange ContentLengthRange contentLengthRange
@ -130,10 +131,6 @@ func parsePostPolicyForm(policy string) (ppf PostPolicyForm, e error) {
if err != nil { if err != nil {
return ppf, err return ppf, err
} }
parsedPolicy.Conditions.Policies = make(map[string]struct {
Operator string
Value string
})
// Parse conditions. // Parse conditions.
for _, val := range rawPolicy.Conditions { for _, val := range rawPolicy.Conditions {
@ -146,13 +143,13 @@ func parsePostPolicyForm(policy string) (ppf PostPolicyForm, e error) {
} }
// {"acl": "public-read" } is an alternate way to indicate - [ "eq", "$acl", "public-read" ] // {"acl": "public-read" } is an alternate way to indicate - [ "eq", "$acl", "public-read" ]
// In this case we will just collapse this into "eq" for all use cases. // In this case we will just collapse this into "eq" for all use cases.
parsedPolicy.Conditions.Policies["$"+strings.ToLower(k)] = struct { parsedPolicy.Conditions.Policies = append(parsedPolicy.Conditions.Policies, struct {
Operator string Operator string
Key string
Value string Value string
}{ }{
Operator: policyCondEqual, policyCondEqual, "$" + strings.ToLower(k), toString(v),
Value: toString(v), })
}
} }
case []interface{}: // Handle array types. case []interface{}: // Handle array types.
if len(condt) != 3 { // Return error if we have insufficient elements. if len(condt) != 3 { // Return error if we have insufficient elements.
@ -167,13 +164,16 @@ func parsePostPolicyForm(policy string) (ppf PostPolicyForm, e error) {
} }
} }
operator, matchType, value := toLowerString(condt[0]), toLowerString(condt[1]), toString(condt[2]) operator, matchType, value := toLowerString(condt[0]), toLowerString(condt[1]), toString(condt[2])
parsedPolicy.Conditions.Policies[matchType] = struct { if !strings.HasPrefix(matchType, "$") {
return parsedPolicy, fmt.Errorf("Invalid according to Policy: Policy Condition failed: [%s, %s, %s]", operator, matchType, value)
}
parsedPolicy.Conditions.Policies = append(parsedPolicy.Conditions.Policies, struct {
Operator string Operator string
Key string
Value string Value string
}{ }{
Operator: operator, operator, matchType, value,
Value: value, })
}
case policyCondContentLength: case policyCondContentLength:
min, err := toInteger(condt[1]) min, err := toInteger(condt[1])
if err != nil { if err != nil {
@ -224,10 +224,10 @@ func checkPostPolicy(formValues http.Header, postPolicyForm PostPolicyForm) erro
} }
// map to store the metadata // map to store the metadata
metaMap := make(map[string]string) metaMap := make(map[string]string)
for cond, v := range postPolicyForm.Conditions.Policies { for _, policy := range postPolicyForm.Conditions.Policies {
if strings.HasPrefix(cond, "$x-amz-meta-") { if strings.HasPrefix(policy.Key, "$x-amz-meta-") {
formCanonicalName := http.CanonicalHeaderKey(strings.TrimPrefix(cond, "$")) formCanonicalName := http.CanonicalHeaderKey(strings.TrimPrefix(policy.Key, "$"))
metaMap[formCanonicalName] = v.Value metaMap[formCanonicalName] = policy.Value
} }
} }
// Check if any extra metadata field is passed as input // Check if any extra metadata field is passed as input
@ -243,30 +243,30 @@ func checkPostPolicy(formValues http.Header, postPolicyForm PostPolicyForm) erro
condPassed := true condPassed := true
// Iterate over policy conditions and check them against received form fields // Iterate over policy conditions and check them against received form fields
for cond, v := range postPolicyForm.Conditions.Policies { for _, policy := range postPolicyForm.Conditions.Policies {
// Form fields names are in canonical format, convert conditions names // Form fields names are in canonical format, convert conditions names
// to canonical for simplification purpose, so `$key` will become `Key` // to canonical for simplification purpose, so `$key` will become `Key`
formCanonicalName := http.CanonicalHeaderKey(strings.TrimPrefix(cond, "$")) formCanonicalName := http.CanonicalHeaderKey(strings.TrimPrefix(policy.Key, "$"))
// Operator for the current policy condition // Operator for the current policy condition
op := v.Operator op := policy.Operator
// If the current policy condition is known // If the current policy condition is known
if startsWithSupported, condFound := startsWithConds[cond]; condFound { if startsWithSupported, condFound := startsWithConds[policy.Key]; condFound {
// Check if the current condition supports starts-with operator // Check if the current condition supports starts-with operator
if op == policyCondStartsWith && !startsWithSupported { if op == policyCondStartsWith && !startsWithSupported {
return fmt.Errorf("Invalid according to Policy: Policy Condition failed") return fmt.Errorf("Invalid according to Policy: Policy Condition failed")
} }
// Check if current policy condition is satisfied // Check if current policy condition is satisfied
condPassed = checkPolicyCond(op, formValues.Get(formCanonicalName), v.Value) condPassed = checkPolicyCond(op, formValues.Get(formCanonicalName), policy.Value)
if !condPassed { if !condPassed {
return fmt.Errorf("Invalid according to Policy: Policy Condition failed") return fmt.Errorf("Invalid according to Policy: Policy Condition failed")
} }
} else { } else {
// This covers all conditions X-Amz-Meta-* and X-Amz-* // This covers all conditions X-Amz-Meta-* and X-Amz-*
if strings.HasPrefix(cond, "$x-amz-meta-") || strings.HasPrefix(cond, "$x-amz-") { if strings.HasPrefix(policy.Key, "$x-amz-meta-") || strings.HasPrefix(policy.Key, "$x-amz-") {
// Check if policy condition is satisfied // Check if policy condition is satisfied
condPassed = checkPolicyCond(op, formValues.Get(formCanonicalName), v.Value) condPassed = checkPolicyCond(op, formValues.Get(formCanonicalName), policy.Value)
if !condPassed { if !condPassed {
return fmt.Errorf("Invalid according to Policy: Policy Condition failed: [%s, %s, %s]", op, cond, v.Value) return fmt.Errorf("Invalid according to Policy: Policy Condition failed: [%s, %s, %s]", op, policy.Key, policy.Value)
} }
} }
} }