Merge pull request #880 from harshavardhana/implement-presigned-policy
Implement presigned policymaster
commit
6c7543d49b
@ -0,0 +1,32 @@ |
||||
package signature |
||||
|
||||
// MissingDateHeader date header missing
|
||||
type MissingDateHeader struct{} |
||||
|
||||
func (e MissingDateHeader) Error() string { |
||||
return "Missing date header" |
||||
} |
||||
|
||||
// MissingExpiresQuery expires query string missing
|
||||
type MissingExpiresQuery struct{} |
||||
|
||||
func (e MissingExpiresQuery) Error() string { |
||||
return "Missing expires query string" |
||||
} |
||||
|
||||
// ExpiredPresignedRequest request already expired
|
||||
type ExpiredPresignedRequest struct{} |
||||
|
||||
func (e ExpiredPresignedRequest) Error() string { |
||||
return "Presigned request already expired" |
||||
} |
||||
|
||||
// DoesNotMatch invalid signature
|
||||
type DoesNotMatch struct { |
||||
SignatureSent string |
||||
SignatureCalculated string |
||||
} |
||||
|
||||
func (e DoesNotMatch) Error() string { |
||||
return "The request signature we calculated does not match the signature you provided" |
||||
} |
@ -0,0 +1,141 @@ |
||||
package signature |
||||
|
||||
import ( |
||||
"encoding/json" |
||||
"fmt" |
||||
"reflect" |
||||
"time" |
||||
|
||||
"github.com/minio/minio/pkg/probe" |
||||
) |
||||
|
||||
// toString - Safely convert interface to string without causing panic.
|
||||
func toString(val interface{}) string { |
||||
switch v := val.(type) { |
||||
case string: |
||||
return v |
||||
} |
||||
return "" |
||||
} |
||||
|
||||
// toInteger _ Safely convert interface to integer without causing panic.
|
||||
func toInteger(val interface{}) int { |
||||
switch v := val.(type) { |
||||
case int: |
||||
return v |
||||
} |
||||
return 0 |
||||
} |
||||
|
||||
// isString - Safely check if val is of type string without causing panic.
|
||||
func isString(val interface{}) bool { |
||||
switch val.(type) { |
||||
case string: |
||||
return true |
||||
} |
||||
return false |
||||
} |
||||
|
||||
// PostPolicyForm provides strict static type conversion and validation for Amazon S3's POST policy JSON string.
|
||||
type PostPolicyForm struct { |
||||
Expiration time.Time // Expiration date and time of the POST policy.
|
||||
Conditions struct { // Conditional policy structure.
|
||||
Policies map[string]struct { |
||||
Operator string |
||||
Value string |
||||
} |
||||
ContentLengthRange struct { |
||||
Min int |
||||
Max int |
||||
} |
||||
} |
||||
} |
||||
|
||||
// ParsePostPolicyForm - Parse JSON policy string into typed POostPolicyForm structure.
|
||||
func ParsePostPolicyForm(policy string) (PostPolicyForm, *probe.Error) { |
||||
// Convert po into interfaces and
|
||||
// perform strict type conversion using reflection.
|
||||
var rawPolicy struct { |
||||
Expiration string `json:"expiration"` |
||||
Conditions []interface{} `json:"conditions"` |
||||
} |
||||
|
||||
e := json.Unmarshal([]byte(policy), &rawPolicy) |
||||
if e != nil { |
||||
return PostPolicyForm{}, probe.NewError(e) |
||||
} |
||||
|
||||
parsedPolicy := PostPolicyForm{} |
||||
|
||||
// Parse expiry time.
|
||||
parsedPolicy.Expiration, e = time.Parse(time.RFC3339Nano, rawPolicy.Expiration) |
||||
if e != nil { |
||||
return PostPolicyForm{}, probe.NewError(e) |
||||
} |
||||
parsedPolicy.Conditions.Policies = make(map[string]struct { |
||||
Operator string |
||||
Value string |
||||
}) |
||||
|
||||
// Parse conditions.
|
||||
for _, val := range rawPolicy.Conditions { |
||||
switch condt := val.(type) { |
||||
case map[string]interface{}: // Handle key:value map types.
|
||||
for k, v := range condt { |
||||
if !isString(v) { // Pre-check value type.
|
||||
// All values must be of type string.
|
||||
return parsedPolicy, probe.NewError(fmt.Errorf("Unknown type ‘%s’ of conditional field value ‘%s’ found in POST policy form.", |
||||
reflect.TypeOf(condt).String(), condt)) |
||||
} |
||||
// {"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.
|
||||
parsedPolicy.Conditions.Policies["$"+k] = struct { |
||||
Operator string |
||||
Value string |
||||
}{ |
||||
Operator: "eq", |
||||
Value: toString(v), |
||||
} |
||||
} |
||||
case []interface{}: // Handle array types.
|
||||
if len(condt) != 3 { // Return error if we have insufficient elements.
|
||||
return parsedPolicy, probe.NewError(fmt.Errorf("Malformed conditional fields ‘%s’ of type ‘%s’ found in POST policy form.", |
||||
condt, reflect.TypeOf(condt).String())) |
||||
} |
||||
switch toString(condt[0]) { |
||||
case "eq", "starts-with": |
||||
for _, v := range condt { // Pre-check all values for type.
|
||||
if !isString(v) { |
||||
// All values must be of type string.
|
||||
return parsedPolicy, probe.NewError(fmt.Errorf("Unknown type ‘%s’ of conditional field value ‘%s’ found in POST policy form.", |
||||
reflect.TypeOf(condt).String(), condt)) |
||||
} |
||||
} |
||||
operator, matchType, value := toString(condt[0]), toString(condt[1]), toString(condt[2]) |
||||
parsedPolicy.Conditions.Policies[matchType] = struct { |
||||
Operator string |
||||
Value string |
||||
}{ |
||||
Operator: operator, |
||||
Value: value, |
||||
} |
||||
case "content-length-range": |
||||
parsedPolicy.Conditions.ContentLengthRange = struct { |
||||
Min int |
||||
Max int |
||||
}{ |
||||
Min: toInteger(condt[1]), |
||||
Max: toInteger(condt[2]), |
||||
} |
||||
default: |
||||
// Condition should be valid.
|
||||
return parsedPolicy, probe.NewError(fmt.Errorf("Unknown type ‘%s’ of conditional field value ‘%s’ found in POST policy form.", |
||||
reflect.TypeOf(condt).String(), condt)) |
||||
} |
||||
default: |
||||
return parsedPolicy, probe.NewError(fmt.Errorf("Unknown field ‘%s’ of type ‘%s’ found in POST policy form.", |
||||
condt, reflect.TypeOf(condt).String())) |
||||
} |
||||
} |
||||
return parsedPolicy, nil |
||||
} |
Loading…
Reference in new issue