| 
									
										
										
										
											2021-04-19 03:41:13 +08:00
										 |  |  | // Copyright (c) 2015-2021 MinIO, Inc.
 | 
					
						
							|  |  |  | //
 | 
					
						
							|  |  |  | // This file is part of MinIO Object Storage stack
 | 
					
						
							|  |  |  | //
 | 
					
						
							|  |  |  | // This program is free software: you can redistribute it and/or modify
 | 
					
						
							|  |  |  | // it under the terms of the GNU Affero General Public License as published by
 | 
					
						
							|  |  |  | // the Free Software Foundation, either version 3 of the License, or
 | 
					
						
							|  |  |  | // (at your option) any later version.
 | 
					
						
							|  |  |  | //
 | 
					
						
							|  |  |  | // This program is distributed in the hope that it will be useful
 | 
					
						
							|  |  |  | // but WITHOUT ANY WARRANTY; without even the implied warranty of
 | 
					
						
							|  |  |  | // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 | 
					
						
							|  |  |  | // GNU Affero General Public License for more details.
 | 
					
						
							|  |  |  | //
 | 
					
						
							|  |  |  | // You should have received a copy of the GNU Affero General Public License
 | 
					
						
							|  |  |  | // along with this program.  If not, see <http://www.gnu.org/licenses/>.
 | 
					
						
							| 
									
										
										
										
											2017-03-19 02:28:41 +08:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2016-10-01 05:32:13 +08:00
										 |  |  | package cmd | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | import ( | 
					
						
							|  |  |  | 	"fmt" | 
					
						
							|  |  |  | 	"net/http" | 
					
						
							|  |  |  | 	"net/url" | 
					
						
							| 
									
										
										
										
											2017-08-13 10:25:43 +08:00
										 |  |  | 	"os" | 
					
						
							| 
									
										
										
										
											2016-10-01 05:32:13 +08:00
										 |  |  | 	"sort" | 
					
						
							|  |  |  | 	"testing" | 
					
						
							|  |  |  | ) | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | // Tests for 'func TestResourceListSorting(t *testing.T)'.
 | 
					
						
							|  |  |  | func TestResourceListSorting(t *testing.T) { | 
					
						
							|  |  |  | 	sortedResourceList := make([]string, len(resourceList)) | 
					
						
							|  |  |  | 	copy(sortedResourceList, resourceList) | 
					
						
							|  |  |  | 	sort.Strings(sortedResourceList) | 
					
						
							|  |  |  | 	for i := 0; i < len(resourceList); i++ { | 
					
						
							|  |  |  | 		if resourceList[i] != sortedResourceList[i] { | 
					
						
							|  |  |  | 			t.Errorf("Expected resourceList[%d] = \"%s\", resourceList is not correctly sorted.", i, sortedResourceList[i]) | 
					
						
							|  |  |  | 			break | 
					
						
							|  |  |  | 		} | 
					
						
							|  |  |  | 	} | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2016-12-16 06:56:18 +08:00
										 |  |  | // Tests presigned v2 signature.
 | 
					
						
							| 
									
										
										
										
											2016-10-01 05:32:13 +08:00
										 |  |  | func TestDoesPresignedV2SignatureMatch(t *testing.T) { | 
					
						
							| 
									
										
										
										
											2018-08-15 12:41:47 +08:00
										 |  |  | 	obj, fsDir, err := prepareFS() | 
					
						
							| 
									
										
										
										
											2016-10-01 05:32:13 +08:00
										 |  |  | 	if err != nil { | 
					
						
							| 
									
										
										
										
											2018-08-15 12:41:47 +08:00
										 |  |  | 		t.Fatal(err) | 
					
						
							|  |  |  | 	} | 
					
						
							|  |  |  | 	defer os.RemoveAll(fsDir) | 
					
						
							|  |  |  | 	if err = newTestConfig(globalMinioDefaultRegion, obj); err != nil { | 
					
						
							|  |  |  | 		t.Fatal(err) | 
					
						
							| 
									
										
										
										
											2016-10-01 05:32:13 +08:00
										 |  |  | 	} | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2017-03-19 02:28:41 +08:00
										 |  |  | 	now := UTCNow() | 
					
						
							| 
									
										
										
										
											2016-10-01 05:32:13 +08:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2017-09-25 05:20:12 +08:00
										 |  |  | 	var ( | 
					
						
							| 
									
										
										
										
											2019-10-23 13:59:13 +08:00
										 |  |  | 		accessKey = globalActiveCred.AccessKey | 
					
						
							|  |  |  | 		secretKey = globalActiveCred.SecretKey | 
					
						
							| 
									
										
										
										
											2017-09-25 05:20:12 +08:00
										 |  |  | 	) | 
					
						
							| 
									
										
										
										
											2016-10-01 05:32:13 +08:00
										 |  |  | 	testCases := []struct { | 
					
						
							|  |  |  | 		queryParams map[string]string | 
					
						
							|  |  |  | 		expected    APIErrorCode | 
					
						
							|  |  |  | 	}{ | 
					
						
							|  |  |  | 		// (0) Should error without a set URL query.
 | 
					
						
							|  |  |  | 		{ | 
					
						
							|  |  |  | 			expected: ErrInvalidQueryParams, | 
					
						
							|  |  |  | 		}, | 
					
						
							|  |  |  | 		// (1) Should error on an invalid access key.
 | 
					
						
							|  |  |  | 		{ | 
					
						
							|  |  |  | 			queryParams: map[string]string{ | 
					
						
							|  |  |  | 				"Expires":        "60", | 
					
						
							|  |  |  | 				"Signature":      "badsignature", | 
					
						
							|  |  |  | 				"AWSAccessKeyId": "Z7IXGOO6BZ0REAN1Q26I", | 
					
						
							|  |  |  | 			}, | 
					
						
							|  |  |  | 			expected: ErrInvalidAccessKeyID, | 
					
						
							|  |  |  | 		}, | 
					
						
							|  |  |  | 		// (2) Should error with malformed expires.
 | 
					
						
							|  |  |  | 		{ | 
					
						
							|  |  |  | 			queryParams: map[string]string{ | 
					
						
							|  |  |  | 				"Expires":        "60s", | 
					
						
							|  |  |  | 				"Signature":      "badsignature", | 
					
						
							| 
									
										
										
										
											2017-09-25 05:20:12 +08:00
										 |  |  | 				"AWSAccessKeyId": accessKey, | 
					
						
							| 
									
										
										
										
											2016-10-01 05:32:13 +08:00
										 |  |  | 			}, | 
					
						
							|  |  |  | 			expected: ErrMalformedExpires, | 
					
						
							|  |  |  | 		}, | 
					
						
							|  |  |  | 		// (3) Should give an expired request if it has expired.
 | 
					
						
							|  |  |  | 		{ | 
					
						
							|  |  |  | 			queryParams: map[string]string{ | 
					
						
							|  |  |  | 				"Expires":        "60", | 
					
						
							|  |  |  | 				"Signature":      "badsignature", | 
					
						
							| 
									
										
										
										
											2017-09-25 05:20:12 +08:00
										 |  |  | 				"AWSAccessKeyId": accessKey, | 
					
						
							| 
									
										
										
										
											2016-10-01 05:32:13 +08:00
										 |  |  | 			}, | 
					
						
							|  |  |  | 			expected: ErrExpiredPresignRequest, | 
					
						
							|  |  |  | 		}, | 
					
						
							|  |  |  | 		// (4) Should error when the signature does not match.
 | 
					
						
							|  |  |  | 		{ | 
					
						
							|  |  |  | 			queryParams: map[string]string{ | 
					
						
							|  |  |  | 				"Expires":        fmt.Sprintf("%d", now.Unix()+60), | 
					
						
							|  |  |  | 				"Signature":      "badsignature", | 
					
						
							| 
									
										
										
										
											2017-09-25 05:20:12 +08:00
										 |  |  | 				"AWSAccessKeyId": accessKey, | 
					
						
							| 
									
										
										
										
											2016-10-01 05:32:13 +08:00
										 |  |  | 			}, | 
					
						
							|  |  |  | 			expected: ErrSignatureDoesNotMatch, | 
					
						
							|  |  |  | 		}, | 
					
						
							| 
									
										
										
										
											2016-12-16 06:56:18 +08:00
										 |  |  | 		// (5) Should error when the signature does not match.
 | 
					
						
							|  |  |  | 		{ | 
					
						
							|  |  |  | 			queryParams: map[string]string{ | 
					
						
							| 
									
										
										
										
											2017-01-17 09:05:00 +08:00
										 |  |  | 				"Expires":        fmt.Sprintf("%d", now.Unix()+60), | 
					
						
							| 
									
										
										
										
											2016-12-16 06:56:18 +08:00
										 |  |  | 				"Signature":      "zOM2YrY/yAQe15VWmT78OlBrK6g=", | 
					
						
							| 
									
										
										
										
											2017-09-25 05:20:12 +08:00
										 |  |  | 				"AWSAccessKeyId": accessKey, | 
					
						
							| 
									
										
										
										
											2016-12-16 06:56:18 +08:00
										 |  |  | 			}, | 
					
						
							|  |  |  | 			expected: ErrSignatureDoesNotMatch, | 
					
						
							|  |  |  | 		}, | 
					
						
							| 
									
										
										
										
											2017-09-27 02:00:07 +08:00
										 |  |  | 		// (6) Should not error signature matches with extra query params.
 | 
					
						
							| 
									
										
										
										
											2017-09-25 05:20:12 +08:00
										 |  |  | 		{ | 
					
						
							|  |  |  | 			queryParams: map[string]string{ | 
					
						
							|  |  |  | 				"response-content-disposition": "attachment; filename=\"4K%2d4M.txt\"", | 
					
						
							|  |  |  | 			}, | 
					
						
							|  |  |  | 			expected: ErrNone, | 
					
						
							|  |  |  | 		}, | 
					
						
							| 
									
										
										
										
											2017-09-27 02:00:07 +08:00
										 |  |  | 		// (7) Should not error signature matches with no special query params.
 | 
					
						
							|  |  |  | 		{ | 
					
						
							|  |  |  | 			queryParams: map[string]string{}, | 
					
						
							|  |  |  | 			expected:    ErrNone, | 
					
						
							|  |  |  | 		}, | 
					
						
							| 
									
										
										
										
											2016-10-01 05:32:13 +08:00
										 |  |  | 	} | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	// Run each test case individually.
 | 
					
						
							|  |  |  | 	for i, testCase := range testCases { | 
					
						
							|  |  |  | 		// Turn the map[string]string into map[string][]string, because Go.
 | 
					
						
							|  |  |  | 		query := url.Values{} | 
					
						
							|  |  |  | 		for key, value := range testCase.queryParams { | 
					
						
							|  |  |  | 			query.Set(key, value) | 
					
						
							|  |  |  | 		} | 
					
						
							|  |  |  | 		// Create a request to use.
 | 
					
						
							| 
									
										
										
										
											2017-09-25 05:20:12 +08:00
										 |  |  | 		req, err := http.NewRequest(http.MethodGet, "http://host/a/b?"+query.Encode(), nil) | 
					
						
							|  |  |  | 		if err != nil { | 
					
						
							|  |  |  | 			t.Errorf("(%d) failed to create http.Request, got %v", i, err) | 
					
						
							| 
									
										
										
										
											2016-10-01 05:32:13 +08:00
										 |  |  | 		} | 
					
						
							| 
									
										
										
										
											2017-09-25 05:20:12 +08:00
										 |  |  | 		if testCase.expected != ErrNone { | 
					
						
							|  |  |  | 			// Should be set since we are simulating a http server.
 | 
					
						
							|  |  |  | 			req.RequestURI = req.URL.RequestURI() | 
					
						
							|  |  |  | 			// Check if it matches!
 | 
					
						
							|  |  |  | 			errCode := doesPresignV2SignatureMatch(req) | 
					
						
							|  |  |  | 			if errCode != testCase.expected { | 
					
						
							|  |  |  | 				t.Errorf("(%d) expected to get %s, instead got %s", i, niceError(testCase.expected), niceError(errCode)) | 
					
						
							|  |  |  | 			} | 
					
						
							|  |  |  | 		} else { | 
					
						
							|  |  |  | 			err = preSignV2(req, accessKey, secretKey, now.Unix()+60) | 
					
						
							|  |  |  | 			if err != nil { | 
					
						
							|  |  |  | 				t.Fatalf("(%d) failed to preSignV2 http request, got %v", i, err) | 
					
						
							|  |  |  | 			} | 
					
						
							|  |  |  | 			// Should be set since we are simulating a http server.
 | 
					
						
							|  |  |  | 			req.RequestURI = req.URL.RequestURI() | 
					
						
							|  |  |  | 			errCode := doesPresignV2SignatureMatch(req) | 
					
						
							|  |  |  | 			if errCode != testCase.expected { | 
					
						
							|  |  |  | 				t.Errorf("(%d) expected to get success, instead got %s", i, niceError(errCode)) | 
					
						
							|  |  |  | 			} | 
					
						
							| 
									
										
										
										
											2016-10-01 05:32:13 +08:00
										 |  |  | 		} | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	} | 
					
						
							|  |  |  | } | 
					
						
							| 
									
										
										
										
											2016-10-14 00:25:56 +08:00
										 |  |  | 
 | 
					
						
							|  |  |  | // TestValidateV2AuthHeader - Tests validate the logic of V2 Authorization header validator.
 | 
					
						
							|  |  |  | func TestValidateV2AuthHeader(t *testing.T) { | 
					
						
							| 
									
										
										
										
											2018-08-15 12:41:47 +08:00
										 |  |  | 	obj, fsDir, err := prepareFS() | 
					
						
							| 
									
										
										
										
											2017-02-08 04:51:43 +08:00
										 |  |  | 	if err != nil { | 
					
						
							| 
									
										
										
										
											2018-08-15 12:41:47 +08:00
										 |  |  | 		t.Fatal(err) | 
					
						
							|  |  |  | 	} | 
					
						
							|  |  |  | 	defer os.RemoveAll(fsDir) | 
					
						
							|  |  |  | 	if err = newTestConfig(globalMinioDefaultRegion, obj); err != nil { | 
					
						
							|  |  |  | 		t.Fatal(err) | 
					
						
							| 
									
										
										
										
											2016-10-14 00:25:56 +08:00
										 |  |  | 	} | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2019-10-23 13:59:13 +08:00
										 |  |  | 	accessID := globalActiveCred.AccessKey | 
					
						
							| 
									
										
										
										
											2016-10-14 00:25:56 +08:00
										 |  |  | 	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) { | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2018-10-10 05:00:01 +08:00
										 |  |  | 			req := &http.Request{ | 
					
						
							|  |  |  | 				Header: make(http.Header), | 
					
						
							|  |  |  | 				URL:    &url.URL{}, | 
					
						
							|  |  |  | 			} | 
					
						
							|  |  |  | 			req.Header.Set("Authorization", testCase.authString) | 
					
						
							|  |  |  | 			_, actualErrCode := validateV2AuthHeader(req) | 
					
						
							| 
									
										
										
										
											2016-10-14 00:25:56 +08:00
										 |  |  | 
 | 
					
						
							|  |  |  | 			if testCase.expectedError != actualErrCode { | 
					
						
							|  |  |  | 				t.Errorf("Expected the error code to be %v, got %v.", testCase.expectedError, actualErrCode) | 
					
						
							|  |  |  | 			} | 
					
						
							|  |  |  | 		}) | 
					
						
							|  |  |  | 	} | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | } | 
					
						
							| 
									
										
										
										
											2016-10-22 23:57:12 +08:00
										 |  |  | 
 | 
					
						
							|  |  |  | func TestDoesPolicySignatureV2Match(t *testing.T) { | 
					
						
							| 
									
										
										
										
											2018-08-15 12:41:47 +08:00
										 |  |  | 	obj, fsDir, err := prepareFS() | 
					
						
							| 
									
										
										
										
											2017-02-08 04:51:43 +08:00
										 |  |  | 	if err != nil { | 
					
						
							| 
									
										
										
										
											2018-08-15 12:41:47 +08:00
										 |  |  | 		t.Fatal(err) | 
					
						
							| 
									
										
										
										
											2016-10-22 23:57:12 +08:00
										 |  |  | 	} | 
					
						
							| 
									
										
										
										
											2018-08-15 12:41:47 +08:00
										 |  |  | 	defer os.RemoveAll(fsDir) | 
					
						
							|  |  |  | 	if err = newTestConfig(globalMinioDefaultRegion, obj); err != nil { | 
					
						
							|  |  |  | 		t.Fatal(err) | 
					
						
							|  |  |  | 	} | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2019-10-23 13:59:13 +08:00
										 |  |  | 	creds := globalActiveCred | 
					
						
							| 
									
										
										
										
											2016-10-22 23:57:12 +08:00
										 |  |  | 	policy := "policy" | 
					
						
							|  |  |  | 	testCases := []struct { | 
					
						
							|  |  |  | 		accessKey string | 
					
						
							|  |  |  | 		policy    string | 
					
						
							|  |  |  | 		signature string | 
					
						
							|  |  |  | 		errCode   APIErrorCode | 
					
						
							|  |  |  | 	}{ | 
					
						
							| 
									
										
										
										
											2016-12-27 02:21:23 +08:00
										 |  |  | 		{"invalidAccessKey", policy, calculateSignatureV2(policy, creds.SecretKey), ErrInvalidAccessKeyID}, | 
					
						
							|  |  |  | 		{creds.AccessKey, policy, calculateSignatureV2("random", creds.SecretKey), ErrSignatureDoesNotMatch}, | 
					
						
							|  |  |  | 		{creds.AccessKey, policy, calculateSignatureV2(policy, creds.SecretKey), ErrNone}, | 
					
						
							| 
									
										
										
										
											2016-10-22 23:57:12 +08:00
										 |  |  | 	} | 
					
						
							|  |  |  | 	for i, test := range testCases { | 
					
						
							| 
									
										
										
										
											2017-03-14 05:41:13 +08:00
										 |  |  | 		formValues := make(http.Header) | 
					
						
							|  |  |  | 		formValues.Set("Awsaccesskeyid", test.accessKey) | 
					
						
							|  |  |  | 		formValues.Set("Signature", test.signature) | 
					
						
							|  |  |  | 		formValues.Set("Policy", test.policy) | 
					
						
							| 
									
										
										
										
											2021-03-04 00:47:08 +08:00
										 |  |  | 		_, errCode := doesPolicySignatureV2Match(formValues) | 
					
						
							| 
									
										
										
										
											2016-10-22 23:57:12 +08:00
										 |  |  | 		if errCode != test.errCode { | 
					
						
							|  |  |  | 			t.Fatalf("(%d) expected to get %s, instead got %s", i+1, niceError(test.errCode), niceError(errCode)) | 
					
						
							|  |  |  | 		} | 
					
						
							|  |  |  | 	} | 
					
						
							|  |  |  | } |