mirror of
https://github.com/minio/minio.git
synced 2025-01-11 23:13:23 -05:00
Construct dynamic XML error responses for postpolicyform validation (#7321)
Fixes #7314
This commit is contained in:
parent
c57159a0fe
commit
c0a1369b73
@ -114,7 +114,6 @@ const (
|
|||||||
ErrInvalidRequestVersion
|
ErrInvalidRequestVersion
|
||||||
ErrMissingSignTag
|
ErrMissingSignTag
|
||||||
ErrMissingSignHeadersTag
|
ErrMissingSignHeadersTag
|
||||||
ErrPolicyAlreadyExpired
|
|
||||||
ErrMalformedDate
|
ErrMalformedDate
|
||||||
ErrMalformedPresignedDate
|
ErrMalformedPresignedDate
|
||||||
ErrMalformedCredentialDate
|
ErrMalformedCredentialDate
|
||||||
@ -625,11 +624,6 @@ var errorCodes = errorCodeMap{
|
|||||||
Description: "Signature header missing SignedHeaders field.",
|
Description: "Signature header missing SignedHeaders field.",
|
||||||
HTTPStatusCode: http.StatusBadRequest,
|
HTTPStatusCode: http.StatusBadRequest,
|
||||||
},
|
},
|
||||||
ErrPolicyAlreadyExpired: {
|
|
||||||
Code: "AccessDenied",
|
|
||||||
Description: "Invalid according to Policy: Policy expired.",
|
|
||||||
HTTPStatusCode: http.StatusBadRequest,
|
|
||||||
},
|
|
||||||
ErrMalformedExpires: {
|
ErrMalformedExpires: {
|
||||||
Code: "AuthorizationQueryParametersError",
|
Code: "AuthorizationQueryParametersError",
|
||||||
Description: "X-Amz-Expires should be a number",
|
Description: "X-Amz-Expires should be a number",
|
||||||
|
@ -640,3 +640,38 @@ func writeCustomErrorResponseJSON(ctx context.Context, w http.ResponseWriter, er
|
|||||||
encodedErrorResponse := encodeResponseJSON(errorResponse)
|
encodedErrorResponse := encodeResponseJSON(errorResponse)
|
||||||
writeResponse(w, err.HTTPStatusCode, encodedErrorResponse, mimeJSON)
|
writeResponse(w, err.HTTPStatusCode, encodedErrorResponse, mimeJSON)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// writeCustomErrorResponseXML - similar to writeErrorResponse,
|
||||||
|
// but accepts the error message directly (this allows messages to be
|
||||||
|
// dynamically generated.)
|
||||||
|
func writeCustomErrorResponseXML(ctx context.Context, w http.ResponseWriter, err APIError, errBody string, reqURL *url.URL, browser bool) {
|
||||||
|
|
||||||
|
switch err.Code {
|
||||||
|
case "SlowDown", "XMinioServerNotInitialized", "XMinioReadQuorum", "XMinioWriteQuorum":
|
||||||
|
// Set retry-after header to indicate user-agents to retry request after 120secs.
|
||||||
|
// https://developer.mozilla.org/en-US/docs/Web/HTTP/Headers/Retry-After
|
||||||
|
w.Header().Set("Retry-After", "120")
|
||||||
|
case "AccessDenied":
|
||||||
|
// The request is from browser and also if browser
|
||||||
|
// is enabled we need to redirect.
|
||||||
|
if browser && globalIsBrowserEnabled {
|
||||||
|
w.Header().Set("Location", minioReservedBucketPath+reqURL.Path)
|
||||||
|
w.WriteHeader(http.StatusTemporaryRedirect)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
reqInfo := logger.GetReqInfo(ctx)
|
||||||
|
errorResponse := APIErrorResponse{
|
||||||
|
Code: err.Code,
|
||||||
|
Message: errBody,
|
||||||
|
Resource: reqURL.Path,
|
||||||
|
BucketName: reqInfo.BucketName,
|
||||||
|
Key: reqInfo.ObjectName,
|
||||||
|
RequestID: w.Header().Get(responseRequestIDKey),
|
||||||
|
HostID: w.Header().Get(responseDeploymentIDKey),
|
||||||
|
}
|
||||||
|
|
||||||
|
encodedErrorResponse := encodeResponse(errorResponse)
|
||||||
|
writeResponse(w, err.HTTPStatusCode, encodedErrorResponse, mimeXML)
|
||||||
|
}
|
||||||
|
@ -595,8 +595,8 @@ func (api objectAPIHandlers) PostPolicyBucketHandler(w http.ResponseWriter, r *h
|
|||||||
}
|
}
|
||||||
|
|
||||||
// Make sure formValues adhere to policy restrictions.
|
// Make sure formValues adhere to policy restrictions.
|
||||||
if errCode = checkPostPolicy(formValues, postPolicyForm); errCode != ErrNone {
|
if err = checkPostPolicy(formValues, postPolicyForm); err != nil {
|
||||||
writeErrorResponse(ctx, w, errorCodes.ToAPIErr(errCode), r.URL, guessIsBrowserReq(r))
|
writeCustomErrorResponseXML(ctx, w, errorCodes.ToAPIErr(ErrAccessDenied), err.Error(), r.URL, guessIsBrowserReq(r))
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -300,7 +300,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{}{curTime.Add(-1 * time.Minute * 5).Format(expirationDateFormat), curTime.Format(iso8601DateFormat), curTime.Format(yyyymmdd)},
|
dates: []interface{}{curTime.Add(-1 * time.Minute * 5).Format(expirationDateFormat), curTime.Format(iso8601DateFormat), curTime.Format(yyyymmdd)},
|
||||||
|
@ -217,10 +217,10 @@ func checkPolicyCond(op string, input1, input2 string) bool {
|
|||||||
|
|
||||||
// checkPostPolicy - apply policy conditions and validate input values.
|
// checkPostPolicy - apply policy conditions and validate input values.
|
||||||
// (http://docs.aws.amazon.com/AmazonS3/latest/API/sigv4-HTTPPOSTConstructPolicy.html)
|
// (http://docs.aws.amazon.com/AmazonS3/latest/API/sigv4-HTTPPOSTConstructPolicy.html)
|
||||||
func checkPostPolicy(formValues http.Header, postPolicyForm PostPolicyForm) APIErrorCode {
|
func checkPostPolicy(formValues http.Header, postPolicyForm PostPolicyForm) error {
|
||||||
// Check if policy document expiry date is still not reached
|
// Check if policy document expiry date is still not reached
|
||||||
if !postPolicyForm.Expiration.After(UTCNow()) {
|
if !postPolicyForm.Expiration.After(UTCNow()) {
|
||||||
return ErrPolicyAlreadyExpired
|
return fmt.Errorf("Invalid according to Policy: Policy expired")
|
||||||
}
|
}
|
||||||
|
|
||||||
// Flag to indicate if all policies conditions are satisfied
|
// Flag to indicate if all policies conditions are satisfied
|
||||||
@ -237,22 +237,23 @@ func checkPostPolicy(formValues http.Header, postPolicyForm PostPolicyForm) APIE
|
|||||||
if startsWithSupported, condFound := startsWithConds[cond]; condFound {
|
if startsWithSupported, condFound := startsWithConds[cond]; 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 ErrAccessDenied
|
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)
|
if !condPassed {
|
||||||
|
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(cond, "$x-amz-meta-") || strings.HasPrefix(cond, "$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), v.Value)
|
||||||
}
|
|
||||||
}
|
|
||||||
// Check if current policy condition is satisfied, quit immediately otherwise
|
|
||||||
if !condPassed {
|
if !condPassed {
|
||||||
return ErrAccessDenied
|
return fmt.Errorf("Invalid according to Policy: Policy Condition failed: [%s, %s, %s]", op, cond, v.Value)
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
return ErrNone
|
return nil
|
||||||
}
|
}
|
||||||
|
@ -18,6 +18,7 @@ package cmd
|
|||||||
|
|
||||||
import (
|
import (
|
||||||
"encoding/base64"
|
"encoding/base64"
|
||||||
|
"fmt"
|
||||||
"net/http"
|
"net/http"
|
||||||
"testing"
|
"testing"
|
||||||
|
|
||||||
@ -45,26 +46,28 @@ func TestPostPolicyForm(t *testing.T) {
|
|||||||
SuccessActionStatus string
|
SuccessActionStatus string
|
||||||
Policy string
|
Policy string
|
||||||
Expired bool
|
Expired bool
|
||||||
ErrCode APIErrorCode
|
expectedErr error
|
||||||
}
|
}
|
||||||
|
|
||||||
testCases := []testCase{
|
testCases := []testCase{
|
||||||
// Everything is fine with this test
|
// Everything is fine with this test
|
||||||
{Bucket: "testbucket", Key: "user/user1/filename/${filename}/myfile.txt", XAmzMetaUUID: "14365123651274", SuccessActionStatus: "201", XAmzCredential: "KVGKMDUQ23TCZXTLTHLP/20160727/us-east-1/s3/aws4_request", XAmzDate: "20160727T000000Z", XAmzAlgorithm: "AWS4-HMAC-SHA256", ContentType: "image/jpeg", ErrCode: ErrNone},
|
{Bucket: "testbucket", Key: "user/user1/filename/${filename}/myfile.txt", XAmzMetaUUID: "14365123651274", SuccessActionStatus: "201", XAmzCredential: "KVGKMDUQ23TCZXTLTHLP/20160727/us-east-1/s3/aws4_request", XAmzDate: "20160727T000000Z", XAmzAlgorithm: "AWS4-HMAC-SHA256", ContentType: "image/jpeg", expectedErr: nil},
|
||||||
// Expired policy document
|
// Expired policy document
|
||||||
{Bucket: "testbucket", Key: "user/user1/filename/${filename}/myfile.txt", XAmzMetaUUID: "14365123651274", SuccessActionStatus: "201", XAmzCredential: "KVGKMDUQ23TCZXTLTHLP/20160727/us-east-1/s3/aws4_request", XAmzDate: "20160727T000000Z", XAmzAlgorithm: "AWS4-HMAC-SHA256", ContentType: "image/jpeg", Expired: true, ErrCode: ErrPolicyAlreadyExpired},
|
{Bucket: "testbucket", Key: "user/user1/filename/${filename}/myfile.txt", XAmzMetaUUID: "14365123651274", SuccessActionStatus: "201", XAmzCredential: "KVGKMDUQ23TCZXTLTHLP/20160727/us-east-1/s3/aws4_request", XAmzDate: "20160727T000000Z", XAmzAlgorithm: "AWS4-HMAC-SHA256", ContentType: "image/jpeg", Expired: true, expectedErr: fmt.Errorf("Invalid according to Policy: Policy expired")},
|
||||||
// Different AMZ date
|
// Different AMZ date
|
||||||
{Bucket: "testbucket", Key: "user/user1/filename/${filename}/myfile.txt", XAmzMetaUUID: "14365123651274", XAmzDate: "20160727T000000Z", XAmzAlgorithm: "AWS4-HMAC-SHA256", ContentType: "image/jpeg", ErrCode: ErrAccessDenied},
|
{Bucket: "testbucket", Key: "user/user1/filename/${filename}/myfile.txt", XAmzMetaUUID: "14365123651274", XAmzDate: "2017T000000Z", XAmzAlgorithm: "AWS4-HMAC-SHA256", ContentType: "image/jpeg", expectedErr: fmt.Errorf("Invalid according to Policy: Policy Condition failed")},
|
||||||
// Key which doesn't start with user/user1/filename
|
// Key which doesn't start with user/user1/filename
|
||||||
{Bucket: "testbucket", Key: "myfile.txt", XAmzDate: "20160727T000000Z", XAmzMetaUUID: "14365123651274", XAmzAlgorithm: "AWS4-HMAC-SHA256", ContentType: "image/jpeg", ErrCode: ErrAccessDenied},
|
{Bucket: "testbucket", Key: "myfile.txt", XAmzDate: "20160727T000000Z", XAmzMetaUUID: "14365123651274", XAmzAlgorithm: "AWS4-HMAC-SHA256", ContentType: "image/jpeg", expectedErr: fmt.Errorf("Invalid according to Policy: Policy Condition failed")},
|
||||||
// Incorrect bucket name.
|
// Incorrect bucket name.
|
||||||
{Bucket: "incorrect", Key: "user/user1/filename/myfile.txt", XAmzMetaUUID: "14365123651274", XAmzDate: "20160727T000000Z", XAmzAlgorithm: "AWS4-HMAC-SHA256", ContentType: "image/jpeg", ErrCode: ErrAccessDenied},
|
{Bucket: "incorrect", Key: "user/user1/filename/myfile.txt", XAmzMetaUUID: "14365123651274", XAmzDate: "20160727T000000Z", XAmzAlgorithm: "AWS4-HMAC-SHA256", ContentType: "image/jpeg", expectedErr: fmt.Errorf("Invalid according to Policy: Policy Condition failed")},
|
||||||
// Incorrect key name
|
// Incorrect key name
|
||||||
{Bucket: "testbucket", Key: "incorrect", XAmzDate: "20160727T000000Z", XAmzMetaUUID: "14365123651274", XAmzAlgorithm: "AWS4-HMAC-SHA256", ContentType: "image/jpeg", ErrCode: ErrAccessDenied},
|
{Bucket: "testbucket", Key: "incorrect", XAmzDate: "20160727T000000Z", XAmzMetaUUID: "14365123651274", XAmzAlgorithm: "AWS4-HMAC-SHA256", ContentType: "image/jpeg", expectedErr: fmt.Errorf("Invalid according to Policy: Policy Condition failed")},
|
||||||
// Incorrect date
|
// Incorrect date
|
||||||
{Bucket: "testbucket", Key: "user/user1/filename/${filename}/myfile.txt", XAmzMetaUUID: "14365123651274", XAmzDate: "incorrect", XAmzAlgorithm: "AWS4-HMAC-SHA256", ContentType: "image/jpeg", ErrCode: ErrAccessDenied},
|
{Bucket: "testbucket", Key: "user/user1/filename/${filename}/myfile.txt", XAmzMetaUUID: "14365123651274", XAmzDate: "incorrect", XAmzAlgorithm: "AWS4-HMAC-SHA256", ContentType: "image/jpeg", expectedErr: fmt.Errorf("Invalid according to Policy: Policy Condition failed")},
|
||||||
// Incorrect ContentType
|
// Incorrect ContentType
|
||||||
{Bucket: "testbucket", Key: "user/user1/filename/${filename}/myfile.txt", XAmzMetaUUID: "14365123651274", XAmzDate: "20160727T000000Z", XAmzAlgorithm: "AWS4-HMAC-SHA256", ContentType: "incorrect", ErrCode: ErrAccessDenied},
|
{Bucket: "testbucket", Key: "user/user1/filename/${filename}/myfile.txt", XAmzMetaUUID: "14365123651274", XAmzDate: "20160727T000000Z", XAmzAlgorithm: "AWS4-HMAC-SHA256", ContentType: "incorrect", expectedErr: fmt.Errorf("Invalid according to Policy: Policy Condition failed")},
|
||||||
|
// Incorrect Metadata
|
||||||
|
{Bucket: "testbucket", Key: "user/user1/filename/${filename}/myfile.txt", XAmzMetaUUID: "151274", SuccessActionStatus: "201", XAmzCredential: "KVGKMDUQ23TCZXTLTHLP/20160727/us-east-1/s3/aws4_request", XAmzDate: "20160727T000000Z", XAmzAlgorithm: "AWS4-HMAC-SHA256", ContentType: "image/jpeg", expectedErr: fmt.Errorf("Invalid according to Policy: Policy Condition failed: [eq, $x-amz-meta-uuid, 14365123651274]")},
|
||||||
}
|
}
|
||||||
// Validate all the test cases.
|
// Validate all the test cases.
|
||||||
for i, tt := range testCases {
|
for i, tt := range testCases {
|
||||||
@ -96,8 +99,9 @@ func TestPostPolicyForm(t *testing.T) {
|
|||||||
t.Fatal(err)
|
t.Fatal(err)
|
||||||
}
|
}
|
||||||
|
|
||||||
if errCode := checkPostPolicy(formValues, postPolicyForm); tt.ErrCode != errCode {
|
err = checkPostPolicy(formValues, postPolicyForm)
|
||||||
t.Fatalf("Test %d:, Expected %d, got %d", i+1, tt.ErrCode, errCode)
|
if err != nil && tt.expectedErr != nil && err.Error() != tt.expectedErr.Error() {
|
||||||
|
t.Fatalf("Test %d:, Expected %s, got %s", i+1, tt.expectedErr.Error(), err.Error())
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
Loading…
Reference in New Issue
Block a user