tests: tests for signature v4 parser (#2362)

This commit is contained in:
karthic rao 2016-08-05 20:33:18 +05:30 committed by Harshavardhana
parent 5a44c34fd7
commit d63ce9d60d

440
signature-v4-parser_test.go Normal file
View File

@ -0,0 +1,440 @@
/*
* Minio 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 main
import (
"strings"
"testing"
"time"
)
// generates credential string from its fields.
func generateCredentialStr(accessKey, date, region, service, requestVersion string) string {
return "Credential=" + strings.Join([]string{
accessKey,
date,
region,
service,
requestVersion}, "/")
}
// generate CredentialHeader from its fields.
func generateCredentials(t *testing.T, accessKey string, date string, region, service, requestVersion string) credentialHeader {
cred := credentialHeader{
accessKey: accessKey,
}
parsedDate, err := time.Parse(yyyymmdd, date)
if err != nil {
t.Fatalf("Failed to parse date")
}
cred.scope.date = parsedDate
cred.scope.region = region
cred.scope.service = service
cred.scope.request = requestVersion
return cred
}
// validates the credential fields against the expected credential.
func validateCredentialfields(t *testing.T, testNum int, expectedCredentials credentialHeader, actualCredential credentialHeader) {
if expectedCredentials.accessKey != actualCredential.accessKey {
t.Errorf("Test %d: AccessKey mismatch: Expected \"%s\", got \"%s\"", testNum, expectedCredentials.accessKey, actualCredential.accessKey)
}
if expectedCredentials.scope.date != actualCredential.scope.date {
t.Errorf("Test %d: Date mismatch:Expected \"%s\", got \"%s\"", testNum, expectedCredentials.scope.date, actualCredential.scope.date)
}
if expectedCredentials.scope.region != actualCredential.scope.region {
t.Errorf("Test %d: region mismatch:Expected \"%s\", got \"%s\"", testNum, expectedCredentials.scope.region, actualCredential.scope.region)
}
if expectedCredentials.scope.service != actualCredential.scope.service {
t.Errorf("Test %d: service mismatch:Expected \"%s\", got \"%s\"", testNum, expectedCredentials.scope.service, actualCredential.scope.service)
}
if expectedCredentials.scope.request != actualCredential.scope.request {
t.Errorf("Test %d: scope request mismatch:Expected \"%s\", got \"%s\"", testNum, expectedCredentials.scope.request, actualCredential.scope.request)
}
}
// TestParseCredentialHeader - validates the format validator and extractor for the Credential header in an aws v4 request.
// A valid format of creadential should be of the following format.
// Credential = accessKey + "/"+ scope
// where scope = string.Join([]string{ currTime.Format(yyyymmdd),
// "us-east-1",
// "s3",
// "aws4_request",
// },"/")
func TestParseCredentialHeader(t *testing.T) {
sampleTimeStr := time.Now().UTC().Format(yyyymmdd)
testCases := []struct {
inputCredentialStr string
expectedCredentials credentialHeader
expectedErrCode APIErrorCode
}{
// Test Case - 1.
// Test case with no '=' in te inputCredentialStr.
{
inputCredentialStr: "Credential",
expectedCredentials: credentialHeader{},
expectedErrCode: ErrMissingFields,
},
// Test Case - 2.
// Test case with no "Credential" string in te inputCredentialStr.
{
inputCredentialStr: "Cred=",
expectedCredentials: credentialHeader{},
expectedErrCode: ErrMissingCredTag,
},
// Test Case - 3.
// Test case with malformed credentials.
{
inputCredentialStr: "Credential=abc",
expectedCredentials: credentialHeader{},
expectedErrCode: ErrCredMalformed,
},
// Test Case - 4.
// Test case with malformed AccessKey.
{
inputCredentialStr: generateCredentialStr(
"^#@..!23",
time.Now().UTC().Format(yyyymmdd),
"ABCD",
"ABCD",
"ABCD"),
expectedCredentials: credentialHeader{},
expectedErrCode: ErrInvalidAccessKeyID,
},
// Test Case - 5.
// Test case with invalid date format date.
// a valid date format for credentials is "yyyymmdd".
{
inputCredentialStr: generateCredentialStr(
"Z7IXGOO6BZ0REAN1Q26I",
time.Now().UTC().String(),
"ABCD",
"ABCD",
"ABCD"),
expectedCredentials: credentialHeader{},
expectedErrCode: ErrMalformedDate,
},
// Test Case - 6.
// Test case with invalid region.
// region should a non empty string.
{
inputCredentialStr: generateCredentialStr(
"Z7IXGOO6BZ0REAN1Q26I",
time.Now().UTC().Format(yyyymmdd),
"",
"ABCD",
"ABCD"),
expectedCredentials: credentialHeader{},
expectedErrCode: ErrInvalidRegion,
},
// Test Case - 7.
// Test case with invalid service.
// "s3" is the valid service string.
{
inputCredentialStr: generateCredentialStr(
"Z7IXGOO6BZ0REAN1Q26I",
time.Now().UTC().Format(yyyymmdd),
"us-west-1",
"ABCD",
"ABCD"),
expectedCredentials: credentialHeader{},
expectedErrCode: ErrInvalidService,
},
// Test Case - 8.
// Test case with invalid request version.
// "aws4_request" is the valid request version.
{
inputCredentialStr: generateCredentialStr(
"Z7IXGOO6BZ0REAN1Q26I",
time.Now().UTC().Format(yyyymmdd),
"us-west-1",
"s3",
"ABCD"),
expectedCredentials: credentialHeader{},
expectedErrCode: ErrInvalidRequestVersion,
},
// Test Case - 9.
// Test case with right inputs. Expected to return a valid CredentialHeader.
// "aws4_request" is the valid request version.
{
inputCredentialStr: generateCredentialStr(
"Z7IXGOO6BZ0REAN1Q26I",
sampleTimeStr,
"us-west-1",
"s3",
"aws4_request"),
expectedCredentials: generateCredentials(
t,
"Z7IXGOO6BZ0REAN1Q26I",
sampleTimeStr,
"us-west-1",
"s3",
"aws4_request"),
expectedErrCode: ErrNone,
},
}
for i, testCase := range testCases {
actualCredential, actualErrCode := parseCredentialHeader(testCase.inputCredentialStr)
// validating the credential fields.
if testCase.expectedErrCode != actualErrCode {
t.Fatalf("Test %d: Expected the APIErrCode to be %d, got %d", i+1, testCase.expectedErrCode, actualErrCode)
}
if actualErrCode == ErrNone {
validateCredentialfields(t, i+1, testCase.expectedCredentials, actualCredential)
}
}
}
// TestParseSignature - validates the logic for extracting the signature string.
func TestParseSignature(t *testing.T) {
testCases := []struct {
inputSignElement string
expectedSignStr string
expectedErrCode APIErrorCode
}{
// Test case - 1.
// SignElemenet doesn't have 2 parts on an attempt to split at '='.
// ErrMissingFields expected.
{
inputSignElement: "Signature",
expectedSignStr: "",
expectedErrCode: ErrMissingFields,
},
// Test case - 2.
// SignElemenet with missing "SignatureTag",ErrMissingSignTag expected.
{
inputSignElement: "Sign=",
expectedSignStr: "",
expectedErrCode: ErrMissingSignTag,
},
// Test case - 3.
// Test case with valid inputs.
{
inputSignElement: "Signature=abcd",
expectedSignStr: "abcd",
expectedErrCode: ErrNone,
},
}
for i, testCase := range testCases {
actualSignStr, actualErrCode := parseSignature(testCase.inputSignElement)
if testCase.expectedErrCode != actualErrCode {
t.Fatalf("Test %d: Expected the APIErrCode to be %d, got %d", i+1, testCase.expectedErrCode, actualErrCode)
}
if actualErrCode == ErrNone {
if testCase.expectedSignStr != actualSignStr {
t.Errorf("Test %d: Expected the result to be \"%s\", but got \"%s\". ", i+1, testCase.expectedSignStr, actualSignStr)
}
}
}
}
// TestParseSignedHeaders - validates the logic for extracting the signature string.
func TestParseSignedHeaders(t *testing.T) {
testCases := []struct {
inputSignElement string
expectedSignedHeaders []string
expectedErrCode APIErrorCode
}{
// Test case - 1.
// SignElemenet doesn't have 2 parts on an attempt to split at '='.
// ErrMissingFields expected.
{
inputSignElement: "SignedHeaders",
expectedSignedHeaders: nil,
expectedErrCode: ErrMissingFields,
},
// Test case - 2.
// SignElemenet with missing "SigHeaderTag",ErrMissingSignHeadersTag expected.
{
inputSignElement: "Sign=",
expectedSignedHeaders: nil,
expectedErrCode: ErrMissingSignHeadersTag,
},
// Test case - 3.
// Test case with valid inputs.
{
inputSignElement: "SignedHeaders=host;x-amz-content-sha256;x-amz-date",
expectedSignedHeaders: []string{"host", "x-amz-content-sha256", "x-amz-date"},
expectedErrCode: ErrNone,
},
}
for i, testCase := range testCases {
actualSignedHeaders, actualErrCode := parseSignedHeaders(testCase.inputSignElement)
if testCase.expectedErrCode != actualErrCode {
t.Errorf("Test %d: Expected the APIErrCode to be %d, got %d", i+1, testCase.expectedErrCode, actualErrCode)
}
if actualErrCode == ErrNone {
if strings.Join(testCase.expectedSignedHeaders, ",") != strings.Join(actualSignedHeaders, ",") {
t.Errorf("Test %d: Expected the result to be \"%v\", but got \"%v\". ", i+1, testCase.expectedSignedHeaders, actualSignedHeaders)
}
}
}
}
// TestParseSignV4 - Tests Parsing of v4 signature form the authorization string.
func TestParseSignV4(t *testing.T) {
sampleTimeStr := time.Now().UTC().Format(yyyymmdd)
testCases := []struct {
inputV4AuthStr string
expectedAuthField signValues
expectedErrCode APIErrorCode
}{
// Test case - 1.
// Test case with empty auth string.
{
inputV4AuthStr: "",
expectedAuthField: signValues{},
expectedErrCode: ErrAuthHeaderEmpty,
},
// Test case - 2.
// Test case with no sign v4 Algorithm prefix.
// A valid authorization string should begin(prefix)
{
inputV4AuthStr: "no-singv4AlgorithmPrefix",
expectedAuthField: signValues{},
expectedErrCode: ErrSignatureVersionNotSupported,
},
// Test case - 3.
// Test case with missing fields.
// A valid authorization string should have 3 fields.
{
inputV4AuthStr: signV4Algorithm,
expectedAuthField: signValues{},
expectedErrCode: ErrMissingFields,
},
// Test case - 4.
// Test case with invalid credential field.
{
inputV4AuthStr: signV4Algorithm + " Cred=,a,b",
expectedAuthField: signValues{},
expectedErrCode: ErrMissingCredTag,
},
// Test case - 5.
// Auth field with missing "SigHeaderTag",ErrMissingSignHeadersTag expected.
// A vaild credential is generated.
// Test case with invalid credential field.
{
inputV4AuthStr: signV4Algorithm +
strings.Join([]string{
// generating a valid credential field.
generateCredentialStr(
"Z7IXGOO6BZ0REAN1Q26I",
sampleTimeStr,
"us-west-1",
"s3",
"aws4_request"),
// Incorrect SignedHeader field.
"SignIncorrectHeader=",
"b",
}, ","),
expectedAuthField: signValues{},
expectedErrCode: ErrMissingSignHeadersTag,
},
// Test case - 5.
// Auth string with missing "SignatureTag",ErrMissingSignTag expected.
// A vaild credential is generated.
// Test case with invalid credential field.
{
inputV4AuthStr: signV4Algorithm +
strings.Join([]string{
// generating a valid credential.
generateCredentialStr(
"Z7IXGOO6BZ0REAN1Q26I",
sampleTimeStr,
"us-west-1",
"s3",
"aws4_request"),
// valid SignedHeader.
"SignedHeaders=host;x-amz-content-sha256;x-amz-date",
// invalid Signature field.
// a valid signature is of form "Signature="
"Sign=",
}, ","),
expectedAuthField: signValues{},
expectedErrCode: ErrMissingSignTag,
},
{
inputV4AuthStr: signV4Algorithm +
strings.Join([]string{
// generating a valid credential.
generateCredentialStr(
"Z7IXGOO6BZ0REAN1Q26I",
sampleTimeStr,
"us-west-1",
"s3",
"aws4_request"),
// valid SignedHeader.
"SignedHeaders=host;x-amz-content-sha256;x-amz-date",
// valid Signature field.
// a valid signature is of form "Signature="
"Signature=abcd",
}, ","),
expectedAuthField: signValues{
Credential: generateCredentials(
t,
"Z7IXGOO6BZ0REAN1Q26I",
sampleTimeStr,
"us-west-1",
"s3",
"aws4_request"),
SignedHeaders: []string{"host", "x-amz-content-sha256", "x-amz-date"},
Signature: "abcd",
},
expectedErrCode: ErrNone,
},
}
for i, testCase := range testCases {
parsedAuthField, actualErrCode := parseSignV4(testCase.inputV4AuthStr)
if testCase.expectedErrCode != actualErrCode {
t.Fatalf("Test %d: Expected the APIErrCode to be %d, got %d", i+1, testCase.expectedErrCode, actualErrCode)
}
if actualErrCode == ErrNone {
// validating the extracted/parsed credential fields.
validateCredentialfields(t, i+1, testCase.expectedAuthField.Credential, parsedAuthField.Credential)
// validating the extraction/parsing of signature field.
if testCase.expectedAuthField.Signature != parsedAuthField.Signature {
t.Errorf("Test %d: Parsed Signature field mismatch: Expected \"%s\", got \"%s\"", i+1, testCase.expectedAuthField.Signature, parsedAuthField.Signature)
}
// validating the extracted signed headers.
if strings.Join(testCase.expectedAuthField.SignedHeaders, ",") != strings.Join(parsedAuthField.SignedHeaders, ",") {
t.Errorf("Test %d: Expected the result to be \"%v\", but got \"%v\". ", i+1, testCase.expectedAuthField, parsedAuthField.SignedHeaders)
}
}
}
}