mirror of https://github.com/minio/minio.git
				
				
				
			
		
			
	
	
		
			213 lines
		
	
	
		
			7.7 KiB
		
	
	
	
		
			Go
		
	
	
	
		
		
			
		
	
	
			213 lines
		
	
	
		
			7.7 KiB
		
	
	
	
		
			Go
		
	
	
	
|  | /* | ||
|  |  * Minio Cloud Storage, (C) 2015, 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 cmd | ||
|  | 
 | ||
|  | import ( | ||
|  | 	"net/http" | ||
|  | 	"testing" | ||
|  | 	"time" | ||
|  | ) | ||
|  | 
 | ||
|  | // TestSkipContentSha256Cksum - Test validate the logic which decides whether to skip checksum validation based on the request header.
 | ||
|  | func TestSkipContentSha256Cksum(t *testing.T) { | ||
|  | 	testCases := []struct { | ||
|  | 		inputHeaderKey   string | ||
|  | 		inputHeaderValue string | ||
|  | 
 | ||
|  | 		inputQueryKey   string | ||
|  | 		inputQueryValue string | ||
|  | 
 | ||
|  | 		expectedResult bool | ||
|  | 	}{ | ||
|  | 		// Test case - 1.
 | ||
|  | 		// Test case with "X-Amz-Content-Sha256" header set to empty value.
 | ||
|  | 		{"X-Amz-Content-Sha256", "", "", "", false}, | ||
|  | 		// Test case - 2.
 | ||
|  | 		// Test case with "X-Amz-Content-Sha256" header set to  "UNSIGNED-PAYLOAD"
 | ||
|  | 		// When "X-Amz-Content-Sha256" header is set to  "UNSIGNED-PAYLOAD", validation of content sha256 has to be skipped.
 | ||
|  | 		{"X-Amz-Content-Sha256", "UNSIGNED-PAYLOAD", "", "", true}, | ||
|  | 		// Test case - 3.
 | ||
|  | 		// Enabling PreSigned Signature v4.
 | ||
|  | 		{"", "", "X-Amz-Credential", "", true}, | ||
|  | 		// Test case - 4.
 | ||
|  | 		// "X-Amz-Content-Sha256" not set and  PreSigned Signature v4 not enabled, sha256 checksum calculation is not skipped.
 | ||
|  | 		{"", "", "X-Amz-Credential", "", true}, | ||
|  | 	} | ||
|  | 
 | ||
|  | 	for i, testCase := range testCases { | ||
|  | 		// creating an input HTTP request.
 | ||
|  | 		// Only the headers are relevant for this particular test.
 | ||
|  | 		inputReq, err := http.NewRequest("GET", "http://example.com", nil) | ||
|  | 		if err != nil { | ||
|  | 			t.Fatalf("Error initializing input HTTP request: %v", err) | ||
|  | 		} | ||
|  | 		if testCase.inputHeaderKey != "" { | ||
|  | 			inputReq.Header.Set(testCase.inputHeaderKey, testCase.inputHeaderValue) | ||
|  | 		} | ||
|  | 		if testCase.inputQueryKey != "" { | ||
|  | 			q := inputReq.URL.Query() | ||
|  | 			q.Add(testCase.inputQueryKey, testCase.inputQueryValue) | ||
|  | 			inputReq.URL.RawQuery = q.Encode() | ||
|  | 		} | ||
|  | 
 | ||
|  | 		actualResult := skipContentSha256Cksum(inputReq) | ||
|  | 		if testCase.expectedResult != actualResult { | ||
|  | 			t.Errorf("Test %d: Expected the result to `%v`, but instead got `%v`", i+1, testCase.expectedResult, actualResult) | ||
|  | 		} | ||
|  | 	} | ||
|  | } | ||
|  | 
 | ||
|  | // TestIsValidRegion - Tests validate the comparison logic for asserting whether the region from http request is valid.
 | ||
|  | func TestIsValidRegion(t *testing.T) { | ||
|  | 	testCases := []struct { | ||
|  | 		inputReqRegion  string | ||
|  | 		inputConfRegion string | ||
|  | 
 | ||
|  | 		expectedResult bool | ||
|  | 	}{ | ||
|  | 
 | ||
|  | 		{"", "", false}, | ||
|  | 		{"us-east-1", "", true}, | ||
|  | 		{"us-east-1", "US", true}, | ||
|  | 		{"us-west-1", "US", false}, | ||
|  | 		{"us-west-1", "us-west-1", true}, | ||
|  | 	} | ||
|  | 
 | ||
|  | 	for i, testCase := range testCases { | ||
|  | 
 | ||
|  | 		actualResult := isValidRegion(testCase.inputReqRegion, testCase.inputConfRegion) | ||
|  | 		if testCase.expectedResult != actualResult { | ||
|  | 			t.Errorf("Test %d: Expected the result to `%v`, but instead got `%v`", i+1, testCase.expectedResult, actualResult) | ||
|  | 		} | ||
|  | 	} | ||
|  | } | ||
|  | 
 | ||
|  | // Tests validate the URL path encoder.
 | ||
|  | func TestGetURLEncodedName(t *testing.T) { | ||
|  | 	testCases := []struct { | ||
|  | 		// Input.
 | ||
|  | 		inputStr string | ||
|  | 		// Expected result.
 | ||
|  | 		result string | ||
|  | 	}{ | ||
|  | 		// % should be encoded as %25
 | ||
|  | 		{"thisisthe%url", "thisisthe%25url"}, | ||
|  | 		// UTF-8 encoding.
 | ||
|  | 		{"本語", "%E6%9C%AC%E8%AA%9E"}, | ||
|  | 		// UTF-8 encoding with ASCII.
 | ||
|  | 		{"本語.1", "%E6%9C%AC%E8%AA%9E.1"}, | ||
|  | 		// Unusual ASCII characters.
 | ||
|  | 		{">123", "%3E123"}, | ||
|  | 		// Fragment path characters.
 | ||
|  | 		{"myurl#link", "myurl%23link"}, | ||
|  | 		// Space should be set to %20 not '+'.
 | ||
|  | 		{"space in url", "space%20in%20url"}, | ||
|  | 		// '+' shouldn't be treated as space.
 | ||
|  | 		{"url+path", "url%2Bpath"}, | ||
|  | 	} | ||
|  | 
 | ||
|  | 	// Tests generated values from url encoded name.
 | ||
|  | 	for i, testCase := range testCases { | ||
|  | 		result := getURLEncodedName(testCase.inputStr) | ||
|  | 		if testCase.result != result { | ||
|  | 			t.Errorf("Test %d: Expected queryEncode result to be \"%s\", but found it to be \"%s\" instead", i+1, testCase.result, result) | ||
|  | 		} | ||
|  | 	} | ||
|  | } | ||
|  | 
 | ||
|  | // TestExtractSignedHeaders - Tests validate extraction of signed headers using list of signed header keys.
 | ||
|  | func TestExtractSignedHeaders(t *testing.T) { | ||
|  | 	signedHeaders := []string{"host", "x-amz-content-sha256", "x-amz-date"} | ||
|  | 
 | ||
|  | 	// If the `expect` key exists in the signed headers then golang server would have stripped out the value, expecting the `expect` header set to `100-continue` in the result.
 | ||
|  | 	signedHeaders = append(signedHeaders, "expect") | ||
|  | 	// expected header values.
 | ||
|  | 	expectedHost := "play.minio.io:9000" | ||
|  | 	expectedContentSha256 := "1234abcd" | ||
|  | 	expectedTime := time.Now().UTC().Format(iso8601Format) | ||
|  | 
 | ||
|  | 	// Creating input http header.
 | ||
|  | 	inputHeader := make(http.Header) | ||
|  | 	inputHeader.Set(signedHeaders[0], expectedHost) | ||
|  | 	inputHeader.Set(signedHeaders[1], expectedContentSha256) | ||
|  | 	inputHeader.Set(signedHeaders[2], expectedTime) | ||
|  | 	// calling the function being tested.
 | ||
|  | 	extractedSignedHeaders, errCode := extractSignedHeaders(signedHeaders, inputHeader) | ||
|  | 	if errCode != ErrNone { | ||
|  | 		t.Fatalf("Expected the APIErrorCode to be %d, but got %d", ErrNone, errCode) | ||
|  | 	} | ||
|  | 
 | ||
|  | 	// "x-amz-content-sha256" header value from the extracted result.
 | ||
|  | 	extractedContentSha256 := extractedSignedHeaders[signedHeaders[1]] | ||
|  | 	// "host" header value from the extracted result.
 | ||
|  | 	extractedHost := extractedSignedHeaders[signedHeaders[0]] | ||
|  | 	//  "x-amz-date" header from the extracted result.
 | ||
|  | 	extractedDate := extractedSignedHeaders[signedHeaders[2]] | ||
|  | 	// extracted `expect` header.
 | ||
|  | 	extractedExpect := extractedSignedHeaders["expect"][0] | ||
|  | 	if expectedHost != extractedHost[0] { | ||
|  | 		t.Errorf("host header mismatch: expected `%s`, got `%s`", expectedHost, extractedHost) | ||
|  | 	} | ||
|  | 	// assert the result with the expected value.
 | ||
|  | 	if expectedContentSha256 != extractedContentSha256[0] { | ||
|  | 		t.Errorf("x-amz-content-sha256 header mismatch: expected `%s`, got `%s`", expectedContentSha256, extractedContentSha256) | ||
|  | 	} | ||
|  | 	if expectedTime != extractedDate[0] { | ||
|  | 		t.Errorf("x-amz-date header mismatch: expected `%s`, got `%s`", expectedTime, extractedDate) | ||
|  | 	} | ||
|  | 
 | ||
|  | 	// Since the list of signed headers value contained `expect`, the default value of `100-continue` will be added to extracted signed headers.
 | ||
|  | 	if extractedExpect != "100-continue" { | ||
|  | 		t.Errorf("expect header incorrect value: expected `%s`, got `%s`", "100-continue", extractedExpect) | ||
|  | 	} | ||
|  | 
 | ||
|  | 	// case where the headers doesn't contain the one of the signed header in the signed headers list.
 | ||
|  | 	signedHeaders = append(signedHeaders, " X-Amz-Credential") | ||
|  | 	// expected to fail with `ErrUnsignedHeaders`.
 | ||
|  | 	_, errCode = extractSignedHeaders(signedHeaders, inputHeader) | ||
|  | 	if errCode != ErrUnsignedHeaders { | ||
|  | 		t.Fatalf("Expected the APIErrorCode to %d, but got %d", ErrUnsignedHeaders, errCode) | ||
|  | 	} | ||
|  | 
 | ||
|  | 	// case where the list of signed headers doesn't contain the host field.
 | ||
|  | 	signedHeaders = signedHeaders[1:] | ||
|  | 	// expected to fail with `ErrUnsignedHeaders`.
 | ||
|  | 	_, errCode = extractSignedHeaders(signedHeaders, inputHeader) | ||
|  | 	if errCode != ErrUnsignedHeaders { | ||
|  | 		t.Fatalf("Expected the APIErrorCode to %d, but got %d", ErrUnsignedHeaders, errCode) | ||
|  | 	} | ||
|  | } | ||
|  | 
 | ||
|  | // TestFindHost - tests the logic to find whether "host" is part of signed headers.
 | ||
|  | func TestFindHost(t *testing.T) { | ||
|  | 	// doesn't contain "host".
 | ||
|  | 	signedHeaders := []string{"x-amz-content-sha256", "x-amz-date"} | ||
|  | 	errCode := findHost(signedHeaders) | ||
|  | 	// expected to error out with code ErrUnsignedHeaders .
 | ||
|  | 	if errCode != ErrUnsignedHeaders { | ||
|  | 		t.Fatalf("Expected the APIErrorCode to be %d, but got %d", ErrUnsignedHeaders, errCode) | ||
|  | 	} | ||
|  | 
 | ||
|  | 	// adding "host".
 | ||
|  | 	signedHeaders = append(signedHeaders, "host") | ||
|  | 	// epxected to pass.
 | ||
|  | 	errCode = findHost(signedHeaders) | ||
|  | 	if errCode != ErrNone { | ||
|  | 		t.Fatalf("Expected the APIErrorCode to be %d, but got %d", ErrNone, errCode) | ||
|  | 	} | ||
|  | } |