signature-v2 fix. (#2918)

- Return errors similar to V4 Sign processsing.
- Return ErrMissing fields when Auth Header fields are missing.
- Return InvalidAccessID when accessID doesn't match.

* tests: Adding V2 signature tests for bucket handler API's.
This commit is contained in:
Karthic Rao 2016-10-13 21:55:56 +05:30 committed by Harshavardhana
parent 0aabc1d8d9
commit 17e49a9ed2
3 changed files with 367 additions and 49 deletions

View File

@ -44,34 +44,39 @@ func testGetBucketLocationHandler(obj ObjectLayer, instanceType, bucketName stri
errorResponse APIErrorResponse errorResponse APIErrorResponse
shouldPass bool shouldPass bool
}{ }{
// Test case - 1.
// Tests for authenticated request and proper response. // Tests for authenticated request and proper response.
{ {
bucketName, bucketName: bucketName,
credentials.AccessKeyID, accessKey: credentials.AccessKeyID,
credentials.SecretAccessKey, secretKey: credentials.SecretAccessKey,
http.StatusOK, expectedRespStatus: http.StatusOK,
[]byte(`<?xml version="1.0" encoding="UTF-8"?> locationResponse: []byte(`<?xml version="1.0" encoding="UTF-8"?>
<LocationConstraint xmlns="http://s3.amazonaws.com/doc/2006-03-01/"></LocationConstraint>`), <LocationConstraint xmlns="http://s3.amazonaws.com/doc/2006-03-01/"></LocationConstraint>`),
APIErrorResponse{}, errorResponse: APIErrorResponse{},
true, shouldPass: true,
}, },
// Tests for anonymous requests. // Test case - 2.
// Tests for signature mismatch error.
{ {
bucketName, bucketName: bucketName,
"", accessKey: "abcd",
"", secretKey: "abcd",
http.StatusForbidden, expectedRespStatus: http.StatusForbidden,
[]byte(""), locationResponse: []byte(""),
APIErrorResponse{ errorResponse: APIErrorResponse{
Resource: "/" + bucketName + "/", Resource: "/" + bucketName + "/",
Code: "AccessDenied", Code: "InvalidAccessKeyID",
Message: "Access Denied.", Message: "The access key ID you provided does not exist in our records.",
}, },
false, shouldPass: false,
}, },
} }
for i, testCase := range testCases { for i, testCase := range testCases {
if i != 1 {
continue
}
// initialize httptest Recorder, this records any mutations to response writer inside the handler. // initialize httptest Recorder, this records any mutations to response writer inside the handler.
rec := httptest.NewRecorder() rec := httptest.NewRecorder()
// construct HTTP request for Get bucket location. // construct HTTP request for Get bucket location.
@ -79,7 +84,7 @@ func testGetBucketLocationHandler(obj ObjectLayer, instanceType, bucketName stri
if err != nil { if err != nil {
t.Fatalf("Test %d: %s: Failed to create HTTP request for GetBucketLocationHandler: <ERROR> %v", i+1, instanceType, err) t.Fatalf("Test %d: %s: Failed to create HTTP request for GetBucketLocationHandler: <ERROR> %v", i+1, instanceType, err)
} }
// Since `apiRouter` satisfies `http.Handler` it has a ServeHTTP to execute the logic ofthe handler. // Since `apiRouter` satisfies `http.Handler` it has a ServeHTTP to execute the logic of the handler.
// Call the ServeHTTP to execute the handler. // Call the ServeHTTP to execute the handler.
apiRouter.ServeHTTP(rec, req) apiRouter.ServeHTTP(rec, req)
if rec.Code != testCase.expectedRespStatus { if rec.Code != testCase.expectedRespStatus {
@ -102,6 +107,38 @@ func testGetBucketLocationHandler(obj ObjectLayer, instanceType, bucketName stri
if errorResponse.Code != testCase.errorResponse.Code { if errorResponse.Code != testCase.errorResponse.Code {
t.Errorf("Test %d: %s: Expected the error code to be `%s`, but instead found `%s`", i+1, instanceType, testCase.errorResponse.Code, errorResponse.Code) t.Errorf("Test %d: %s: Expected the error code to be `%s`, but instead found `%s`", i+1, instanceType, testCase.errorResponse.Code, errorResponse.Code)
} }
// Verify response of the V2 signed HTTP request.
// initialize HTTP NewRecorder, this records any mutations to response writer inside the handler.
recV2 := httptest.NewRecorder()
// construct HTTP request for PUT bucket policy endpoint.
reqV2, err := newTestSignedRequestV2("GET", getBucketLocationURL("", testCase.bucketName), 0, nil, testCase.accessKey, testCase.secretKey)
if err != nil {
t.Fatalf("Test %d: %s: Failed to create HTTP request for PutBucketPolicyHandler: <ERROR> %v", i+1, instanceType, err)
}
// Since `apiRouter` satisfies `http.Handler` it has a ServeHTTP to execute the logic of the handler.
// Call the ServeHTTP to execute the handler.
apiRouter.ServeHTTP(recV2, reqV2)
if recV2.Code != testCase.expectedRespStatus {
t.Errorf("Test %d: %s: Expected the response status to be `%d`, but instead found `%d`", i+1, instanceType, testCase.expectedRespStatus, recV2.Code)
}
errorResponse = APIErrorResponse{}
err = xml.Unmarshal(recV2.Body.Bytes(), &errorResponse)
if err != nil && !testCase.shouldPass {
t.Fatalf("Test %d: %s: Unable to marshal response body %s", i+1, instanceType, string(recV2.Body.Bytes()))
}
if errorResponse.Resource != testCase.errorResponse.Resource {
t.Errorf("Test %d: %s: Expected the error resource to be `%s`, but instead found `%s`", i+1, instanceType, testCase.errorResponse.Resource, errorResponse.Resource)
}
if errorResponse.Message != testCase.errorResponse.Message {
t.Errorf("Test %d: %s: Expected the error message to be `%s`, but instead found `%s`", i+1, instanceType, testCase.errorResponse.Message, errorResponse.Message)
}
if errorResponse.Code != testCase.errorResponse.Code {
t.Errorf("Test %d: %s: Expected the error code to be `%s`, but instead found `%s`", i+1, instanceType, testCase.errorResponse.Code, errorResponse.Code)
}
} }
// Test for Anonymous/unsigned http request. // Test for Anonymous/unsigned http request.
@ -149,6 +186,7 @@ func testHeadBucketHandler(obj ObjectLayer, instanceType, bucketName string, api
// expected Response. // expected Response.
expectedRespStatus int expectedRespStatus int
}{ }{
// Test case - 1.
// Bucket exists. // Bucket exists.
{ {
bucketName: bucketName, bucketName: bucketName,
@ -156,6 +194,7 @@ func testHeadBucketHandler(obj ObjectLayer, instanceType, bucketName string, api
secretKey: credentials.SecretAccessKey, secretKey: credentials.SecretAccessKey,
expectedRespStatus: http.StatusOK, expectedRespStatus: http.StatusOK,
}, },
// Test case - 2.
// Non-existent bucket name. // Non-existent bucket name.
{ {
bucketName: "2333", bucketName: "2333",
@ -163,11 +202,13 @@ func testHeadBucketHandler(obj ObjectLayer, instanceType, bucketName string, api
secretKey: credentials.SecretAccessKey, secretKey: credentials.SecretAccessKey,
expectedRespStatus: http.StatusNotFound, expectedRespStatus: http.StatusNotFound,
}, },
// Un-authenticated request. // Test case - 3.
// Testing for signature mismatch error.
// setting invalid acess and secret key.
{ {
bucketName: bucketName, bucketName: bucketName,
accessKey: "", accessKey: "abcd",
secretKey: "", secretKey: "abcd",
expectedRespStatus: http.StatusForbidden, expectedRespStatus: http.StatusForbidden,
}, },
} }
@ -180,12 +221,29 @@ func testHeadBucketHandler(obj ObjectLayer, instanceType, bucketName string, api
if err != nil { if err != nil {
t.Fatalf("Test %d: %s: Failed to create HTTP request for HeadBucketHandler: <ERROR> %v", i+1, instanceType, err) t.Fatalf("Test %d: %s: Failed to create HTTP request for HeadBucketHandler: <ERROR> %v", i+1, instanceType, err)
} }
// Since `apiRouter` satisfies `http.Handler` it has a ServeHTTP to execute the logic ofthe handler. // Since `apiRouter` satisfies `http.Handler` it has a ServeHTTP to execute the logic of the handler.
// Call the ServeHTTP to execute the handler. // Call the ServeHTTP to execute the handler.
apiRouter.ServeHTTP(rec, req) apiRouter.ServeHTTP(rec, req)
if rec.Code != testCase.expectedRespStatus { if rec.Code != testCase.expectedRespStatus {
t.Errorf("Test %d: %s: Expected the response status to be `%d`, but instead found `%d`", i+1, instanceType, testCase.expectedRespStatus, rec.Code) t.Errorf("Test %d: %s: Expected the response status to be `%d`, but instead found `%d`", i+1, instanceType, testCase.expectedRespStatus, rec.Code)
} }
// Verify response the V2 signed HTTP request.
// initialize HTTP NewRecorder, this records any mutations to response writer inside the handler.
recV2 := httptest.NewRecorder()
// construct HTTP request for PUT bucket policy endpoint.
reqV2, err := newTestSignedRequestV2("HEAD", getHEADBucketURL("", testCase.bucketName), 0, nil, testCase.accessKey, testCase.secretKey)
if err != nil {
t.Fatalf("Test %d: %s: Failed to create HTTP request for PutBucketPolicyHandler: <ERROR> %v", i+1, instanceType, err)
}
// Since `apiRouter` satisfies `http.Handler` it has a ServeHTTP to execute the logic of the handler.
// Call the ServeHTTP to execute the handler.
apiRouter.ServeHTTP(recV2, reqV2)
if recV2.Code != testCase.expectedRespStatus {
t.Errorf("Test %d: %s: Expected the response status to be `%d`, but instead found `%d`", i+1, instanceType, testCase.expectedRespStatus, recV2.Code)
}
} }
// Test for Anonymous/unsigned http request. // Test for Anonymous/unsigned http request.
@ -237,25 +295,138 @@ func testListMultipartUploadsHandler(obj ObjectLayer, instanceType, bucketName s
uploadIDMarker string uploadIDMarker string
delimiter string delimiter string
maxUploads string maxUploads string
accessKey string
secretKey string
expectedRespStatus int expectedRespStatus int
shouldPass bool shouldPass bool
}{ }{
// 1 - invalid bucket name. // Test case - 1.
{".test", "", "", "", "", "0", http.StatusBadRequest, false}, // Setting invalid bucket name.
// 2 - bucket not found. {
{"volatile-bucket-1", "", "", "", "", "0", http.StatusNotFound, false}, bucket: ".test",
// 3 - invalid delimiter. prefix: "",
{bucketName, "", "", "", "-", "0", http.StatusNotImplemented, false}, keyMarker: "",
// 4 - invalid prefix and marker combination. uploadIDMarker: "",
{bucketName, "asia", "europe-object", "", "", "0", http.StatusNotImplemented, false}, delimiter: "",
// 5 - invalid upload id and marker combination. maxUploads: "0",
{bucketName, "asia", "asia/europe/", "abc", "", "0", http.StatusNotImplemented, false}, accessKey: credentials.AccessKeyID,
// 6 - invalid max uploads. secretKey: credentials.SecretAccessKey,
{bucketName, "", "", "", "", "-1", http.StatusBadRequest, false}, expectedRespStatus: http.StatusBadRequest,
// 7 - good case delimiter. shouldPass: false,
{bucketName, "", "", "", "/", "100", http.StatusOK, true}, },
// 8 - good case without delimiter. // Test case - 2.
{bucketName, "", "", "", "", "100", http.StatusOK, true}, // Setting a non-existent bucket.
{
bucket: "volatile-bucket-1",
prefix: "",
keyMarker: "",
uploadIDMarker: "",
delimiter: "",
maxUploads: "0",
accessKey: credentials.AccessKeyID,
secretKey: credentials.SecretAccessKey,
expectedRespStatus: http.StatusNotFound,
shouldPass: false,
},
// Test case -3.
// Setting invalid delimiter, expecting the HTTP response status to be http.StatusNotImplemented.
{
bucket: bucketName,
prefix: "",
keyMarker: "",
uploadIDMarker: "",
delimiter: "-",
maxUploads: "0",
accessKey: credentials.AccessKeyID,
secretKey: credentials.SecretAccessKey,
expectedRespStatus: http.StatusNotImplemented,
shouldPass: false,
},
// Test case - 4.
// Setting Invalid prefix and marker combination.
{
bucket: bucketName,
prefix: "asia",
keyMarker: "europe-object",
uploadIDMarker: "",
delimiter: "",
maxUploads: "0",
accessKey: credentials.AccessKeyID,
secretKey: credentials.SecretAccessKey,
expectedRespStatus: http.StatusNotImplemented,
shouldPass: false,
},
// Test case - 5.
// Invalid upload id and marker combination.
{
bucket: bucketName,
prefix: "asia",
keyMarker: "asia/europe/",
uploadIDMarker: "abc",
delimiter: "",
maxUploads: "0",
accessKey: credentials.AccessKeyID,
secretKey: credentials.SecretAccessKey,
expectedRespStatus: http.StatusNotImplemented,
shouldPass: false,
},
// Test case - 6.
// Setting a negative value to max-uploads paramater, should result in http.StatusBadRequest.
{
bucket: bucketName,
prefix: "",
keyMarker: "",
uploadIDMarker: "",
delimiter: "",
maxUploads: "-1",
accessKey: credentials.AccessKeyID,
secretKey: credentials.SecretAccessKey,
expectedRespStatus: http.StatusBadRequest,
shouldPass: false,
},
// Test case - 7.
// Case with right set of parameters,
// should result in success 200OK.
{
bucket: bucketName,
prefix: "",
keyMarker: "",
uploadIDMarker: "",
delimiter: "/",
maxUploads: "100",
accessKey: credentials.AccessKeyID,
secretKey: credentials.SecretAccessKey,
expectedRespStatus: http.StatusOK,
shouldPass: true,
},
// Test case - 8.
// Good case without delimiter.
{
bucket: bucketName,
prefix: "",
keyMarker: "",
uploadIDMarker: "",
delimiter: "",
maxUploads: "100",
accessKey: credentials.AccessKeyID,
secretKey: credentials.SecretAccessKey,
expectedRespStatus: http.StatusOK,
shouldPass: true,
},
// Test case - 9.
// Setting Invalid AccessKey and SecretKey to induce and verify Signature Mismatch error.
{
bucket: bucketName,
prefix: "",
keyMarker: "",
uploadIDMarker: "",
delimiter: "",
maxUploads: "100",
accessKey: "abcd",
secretKey: "abcd",
expectedRespStatus: http.StatusForbidden,
shouldPass: true,
},
} }
for i, testCase := range testCases { for i, testCase := range testCases {
@ -264,16 +435,34 @@ func testListMultipartUploadsHandler(obj ObjectLayer, instanceType, bucketName s
// construct HTTP request for List multipart uploads endpoint. // construct HTTP request for List multipart uploads endpoint.
u := getListMultipartUploadsURLWithParams("", testCase.bucket, testCase.prefix, testCase.keyMarker, testCase.uploadIDMarker, testCase.delimiter, testCase.maxUploads) u := getListMultipartUploadsURLWithParams("", testCase.bucket, testCase.prefix, testCase.keyMarker, testCase.uploadIDMarker, testCase.delimiter, testCase.maxUploads)
req, gerr := newTestSignedRequestV4("GET", u, 0, nil, credentials.AccessKeyID, credentials.SecretAccessKey) req, gerr := newTestSignedRequestV4("GET", u, 0, nil, testCase.accessKey, testCase.secretKey)
if gerr != nil { if gerr != nil {
t.Fatalf("Test %d: %s: Failed to create HTTP request for ListMultipartUploadsHandler: <ERROR> %v", i+1, instanceType, gerr) t.Fatalf("Test %d: %s: Failed to create HTTP request for ListMultipartUploadsHandler: <ERROR> %v", i+1, instanceType, gerr)
} }
// Since `apiRouter` satisfies `http.Handler` it has a ServeHTTP to execute the logic ofthe handler. // Since `apiRouter` satisfies `http.Handler` it has a ServeHTTP to execute the logic of the handler.
// Call the ServeHTTP to execute the handler. // Call the ServeHTTP to execute the handler.
apiRouter.ServeHTTP(rec, req) apiRouter.ServeHTTP(rec, req)
if rec.Code != testCase.expectedRespStatus { if rec.Code != testCase.expectedRespStatus {
t.Errorf("Test %d: %s: Expected the response status to be `%d`, but instead found `%d`", i+1, instanceType, testCase.expectedRespStatus, rec.Code) t.Errorf("Test %d: %s: Expected the response status to be `%d`, but instead found `%d`", i+1, instanceType, testCase.expectedRespStatus, rec.Code)
} }
// Verify response the V2 signed HTTP request.
// initialize HTTP NewRecorder, this records any mutations to response writer inside the handler.
recV2 := httptest.NewRecorder()
// construct HTTP request for PUT bucket policy endpoint.
// verify response for V2 signed HTTP request.
reqV2, err := newTestSignedRequestV2("GET", u, 0, nil, testCase.accessKey, testCase.secretKey)
if err != nil {
t.Fatalf("Test %d: %s: Failed to create HTTP request for PutBucketPolicyHandler: <ERROR> %v", i+1, instanceType, err)
}
// Since `apiRouter` satisfies `http.Handler` it has a ServeHTTP to execute the logic of the handler.
// Call the ServeHTTP to execute the handler.
apiRouter.ServeHTTP(recV2, reqV2)
if recV2.Code != testCase.expectedRespStatus {
t.Errorf("Test %d: %s: Expected the response status to be `%d`, but instead found `%d`", i+1, instanceType, testCase.expectedRespStatus, recV2.Code)
}
} }
// initialize HTTP NewRecorder, this records any mutations to response writer inside the handler. // initialize HTTP NewRecorder, this records any mutations to response writer inside the handler.
@ -285,7 +474,7 @@ func testListMultipartUploadsHandler(obj ObjectLayer, instanceType, bucketName s
if err != nil { if err != nil {
t.Fatalf("Test %s: Failed to create HTTP request for ListMultipartUploadsHandler: <ERROR> %v", instanceType, err) t.Fatalf("Test %s: Failed to create HTTP request for ListMultipartUploadsHandler: <ERROR> %v", instanceType, err)
} }
// Since `apiRouter` satisfies `http.Handler` it has a ServeHTTP to execute the logic ofthe handler. // Since `apiRouter` satisfies `http.Handler` it has a ServeHTTP to execute the logic of the handler.
// Call the ServeHTTP to execute the handler. // Call the ServeHTTP to execute the handler.
apiRouter.ServeHTTP(rec, req) apiRouter.ServeHTTP(rec, req)
if rec.Code != http.StatusForbidden { if rec.Code != http.StatusForbidden {
@ -340,6 +529,7 @@ func testListBucketsHandler(obj ObjectLayer, instanceType, bucketName string, ap
secretKey string secretKey string
expectedRespStatus int expectedRespStatus int
}{ }{
// Test case - 1.
// Validate a good case request succeeds. // Validate a good case request succeeds.
{ {
bucketName: bucketName, bucketName: bucketName,
@ -347,11 +537,12 @@ func testListBucketsHandler(obj ObjectLayer, instanceType, bucketName string, ap
secretKey: credentials.SecretAccessKey, secretKey: credentials.SecretAccessKey,
expectedRespStatus: http.StatusOK, expectedRespStatus: http.StatusOK,
}, },
// Validate a bad case request fails with http.StatusForbidden. // Test case - 2.
// Test case with invalid accessKey to produce and validate Signature MisMatch error.
{ {
bucketName: bucketName, bucketName: bucketName,
accessKey: "", accessKey: "abcd",
secretKey: "", secretKey: "abcd",
expectedRespStatus: http.StatusForbidden, expectedRespStatus: http.StatusForbidden,
}, },
} }
@ -363,12 +554,30 @@ func testListBucketsHandler(obj ObjectLayer, instanceType, bucketName string, ap
if lerr != nil { if lerr != nil {
t.Fatalf("Test %d: %s: Failed to create HTTP request for ListBucketsHandler: <ERROR> %v", i+1, instanceType, lerr) t.Fatalf("Test %d: %s: Failed to create HTTP request for ListBucketsHandler: <ERROR> %v", i+1, instanceType, lerr)
} }
// Since `apiRouter` satisfies `http.Handler` it has a ServeHTTP to execute the logic ofthe handler. // Since `apiRouter` satisfies `http.Handler` it has a ServeHTTP to execute the logic of the handler.
// Call the ServeHTTP to execute the handler. // Call the ServeHTTP to execute the handler.
apiRouter.ServeHTTP(rec, req) apiRouter.ServeHTTP(rec, req)
if rec.Code != testCase.expectedRespStatus { if rec.Code != testCase.expectedRespStatus {
t.Errorf("Test %d: %s: Expected the response status to be `%d`, but instead found `%d`", i+1, instanceType, testCase.expectedRespStatus, rec.Code) t.Errorf("Test %d: %s: Expected the response status to be `%d`, but instead found `%d`", i+1, instanceType, testCase.expectedRespStatus, rec.Code)
} }
// Verify response of the V2 signed HTTP request.
// initialize HTTP NewRecorder, this records any mutations to response writer inside the handler.
recV2 := httptest.NewRecorder()
// construct HTTP request for PUT bucket policy endpoint.
// verify response for V2 signed HTTP request.
reqV2, err := newTestSignedRequestV2("GET", getListBucketURL(""), 0, nil, testCase.accessKey, testCase.secretKey)
if err != nil {
t.Fatalf("Test %d: %s: Failed to create HTTP request for PutBucketPolicyHandler: <ERROR> %v", i+1, instanceType, err)
}
// Since `apiRouter` satisfies `http.Handler` it has a ServeHTTP to execute the logic of the handler.
// Call the ServeHTTP to execute the handler.
apiRouter.ServeHTTP(recV2, reqV2)
if recV2.Code != testCase.expectedRespStatus {
t.Errorf("Test %d: %s: Expected the response status to be `%d`, but instead found `%d`", i+1, instanceType, testCase.expectedRespStatus, recV2.Code)
}
} }
// Test for Anonymous/unsigned http request. // Test for Anonymous/unsigned http request.

View File

@ -140,11 +140,44 @@ func doesPresignV2SignatureMatch(r *http.Request) APIErrorCode {
// doesSignV2Match - Verify authorization header with calculated header in accordance with // doesSignV2Match - Verify authorization header with calculated header in accordance with
// - http://docs.aws.amazon.com/AmazonS3/latest/dev/auth-request-sig-v2.html // - http://docs.aws.amazon.com/AmazonS3/latest/dev/auth-request-sig-v2.html
// returns true if matches, false otherwise. if error is not nil then it is always false // returns true if matches, false otherwise. if error is not nil then it is always false
func doesSignV2Match(r *http.Request) APIErrorCode {
gotAuth := r.Header.Get("Authorization") func validateV2AuthHeader(v2Auth string) APIErrorCode {
if gotAuth == "" { if v2Auth == "" {
return ErrAuthHeaderEmpty return ErrAuthHeaderEmpty
} }
// Verify if the header algorithm is supported or not.
if !strings.HasPrefix(v2Auth, signV2Algorithm) {
return ErrSignatureVersionNotSupported
}
// below is V2 Signed Auth header format, splitting on `space` (after the `AWS` string).
// Authorization = "AWS" + " " + AWSAccessKeyId + ":" + Signature
authFields := strings.Split(v2Auth, " ")
if len(authFields) != 2 {
return ErrMissingFields
}
// Then will be splitting on ":", this will seprate `AWSAccessKeyId` and `Signature` string.
keySignFields := strings.Split(strings.TrimSpace(authFields[1]), ":")
if len(keySignFields) != 2 {
return ErrMissingFields
}
// Access credentials.
cred := serverConfig.GetCredential()
if keySignFields[0] != cred.AccessKeyID {
return ErrInvalidAccessKeyID
}
return ErrNone
}
func doesSignV2Match(r *http.Request) APIErrorCode {
v2Auth := r.Header.Get("Authorization")
if apiError := validateV2AuthHeader(v2Auth); apiError != ErrNone {
return apiError
}
// url.RawPath will be valid if path has any encoded characters, if not it will // url.RawPath will be valid if path has any encoded characters, if not it will
// be empty - in which case we need to consider url.Path (bug in net/http?) // be empty - in which case we need to consider url.Path (bug in net/http?)
@ -158,7 +191,7 @@ func doesSignV2Match(r *http.Request) APIErrorCode {
} }
expectedAuth := signatureV2(r.Method, encodedResource, encodedQuery, r.Header) expectedAuth := signatureV2(r.Method, encodedResource, encodedQuery, r.Header)
if gotAuth != expectedAuth { if v2Auth != expectedAuth {
return ErrSignatureDoesNotMatch return ErrSignatureDoesNotMatch
} }

View File

@ -104,3 +104,79 @@ func TestDoesPresignedV2SignatureMatch(t *testing.T) {
} }
} }
} }
// TestValidateV2AuthHeader - Tests validate the logic of V2 Authorization header validator.
func TestValidateV2AuthHeader(t *testing.T) {
// Initialize server config.
if err := initConfig(); err != nil {
t.Fatal(err)
}
// Save config.
if err := serverConfig.Save(); err != nil {
t.Fatal(err)
}
accessID := serverConfig.GetCredential().AccessKeyID
testCases := []struct {
authString string
expectedError APIErrorCode
}{
// Test case - 1.
// Case with empty V2AuthString.
{
authString: "",
expectedError: ErrAuthHeaderEmpty,
},
// Test case - 2.
// Test case with `signV2Algorithm` ("AWS") not being the prefix.
{
authString: "NoV2Prefix",
expectedError: ErrSignatureVersionNotSupported,
},
// Test case - 3.
// Test case with missing parts in the Auth string.
// below is the correct format of V2 Authorization header.
// Authorization = "AWS" + " " + AWSAccessKeyId + ":" + Signature
{
authString: signV2Algorithm,
expectedError: ErrMissingFields,
},
// Test case - 4.
// Test case with signature part missing.
{
authString: fmt.Sprintf("%s %s", signV2Algorithm, accessID),
expectedError: ErrMissingFields,
},
// Test case - 5.
// Test case with wrong accessID.
{
authString: fmt.Sprintf("%s %s:%s", signV2Algorithm, "InvalidAccessID", "signature"),
expectedError: ErrInvalidAccessKeyID,
},
// Test case - 6.
// Case with right accessID and format.
{
authString: fmt.Sprintf("%s %s:%s", signV2Algorithm, accessID, "signature"),
expectedError: ErrNone,
},
}
for i, testCase := range testCases {
t.Run(fmt.Sprintf("Case %d AuthStr \"%s\".", i+1, testCase.authString), func(t *testing.T) {
actualErrCode := validateV2AuthHeader(testCase.authString)
if testCase.expectedError != actualErrCode {
t.Errorf("Expected the error code to be %v, got %v.", testCase.expectedError, actualErrCode)
}
})
}
}