| 
									
										
										
										
											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/>.
 | 
					
						
							| 
									
										
										
										
											2016-07-19 14:56:27 +08:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2016-08-19 07:23:42 +08:00
										 |  |  | package cmd | 
					
						
							| 
									
										
										
										
											2016-07-19 14:56:27 +08:00
										 |  |  | 
 | 
					
						
							|  |  |  | import ( | 
					
						
							|  |  |  | 	"bytes" | 
					
						
							| 
									
										
										
										
											2018-11-13 03:07:43 +08:00
										 |  |  | 	"context" | 
					
						
							| 
									
										
										
										
											2016-07-19 14:56:27 +08:00
										 |  |  | 	"io" | 
					
						
							| 
									
										
										
										
											2018-05-19 02:27:25 +08:00
										 |  |  | 	"io/ioutil" | 
					
						
							| 
									
										
										
										
											2016-07-19 14:56:27 +08:00
										 |  |  | 	"net/http" | 
					
						
							| 
									
										
										
										
											2016-09-17 06:17:49 +08:00
										 |  |  | 	"net/url" | 
					
						
							| 
									
										
										
										
											2017-08-13 10:25:43 +08:00
										 |  |  | 	"os" | 
					
						
							| 
									
										
										
										
											2016-07-19 14:56:27 +08:00
										 |  |  | 	"testing" | 
					
						
							| 
									
										
										
										
											2018-01-18 02:36:25 +08:00
										 |  |  | 	"time" | 
					
						
							| 
									
										
										
										
											2017-11-01 02:54:32 +08:00
										 |  |  | 
 | 
					
						
							|  |  |  | 	"github.com/minio/minio/pkg/auth" | 
					
						
							| 
									
										
										
										
											2019-11-19 18:03:18 +08:00
										 |  |  | 	iampolicy "github.com/minio/minio/pkg/iam/policy" | 
					
						
							| 
									
										
										
										
											2016-07-19 14:56:27 +08:00
										 |  |  | ) | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2016-09-17 06:17:49 +08:00
										 |  |  | // Test get request auth type.
 | 
					
						
							|  |  |  | func TestGetRequestAuthType(t *testing.T) { | 
					
						
							|  |  |  | 	type testCase struct { | 
					
						
							|  |  |  | 		req   *http.Request | 
					
						
							|  |  |  | 		authT authType | 
					
						
							|  |  |  | 	} | 
					
						
							|  |  |  | 	testCases := []testCase{ | 
					
						
							|  |  |  | 		// Test case - 1
 | 
					
						
							|  |  |  | 		// Check for generic signature v4 header.
 | 
					
						
							|  |  |  | 		{ | 
					
						
							|  |  |  | 			req: &http.Request{ | 
					
						
							|  |  |  | 				URL: &url.URL{ | 
					
						
							| 
									
										
										
										
											2017-02-10 15:26:44 +08:00
										 |  |  | 					Host:   "127.0.0.1:9000", | 
					
						
							| 
									
										
										
										
											2017-01-19 04:24:34 +08:00
										 |  |  | 					Scheme: httpScheme, | 
					
						
							| 
									
										
										
										
											2019-08-07 03:08:58 +08:00
										 |  |  | 					Path:   SlashSeparator, | 
					
						
							| 
									
										
										
										
											2016-09-17 06:17:49 +08:00
										 |  |  | 				}, | 
					
						
							|  |  |  | 				Header: http.Header{ | 
					
						
							|  |  |  | 					"Authorization":        []string{"AWS4-HMAC-SHA256 <cred_string>"}, | 
					
						
							|  |  |  | 					"X-Amz-Content-Sha256": []string{streamingContentSHA256}, | 
					
						
							| 
									
										
										
										
											2017-02-21 04:07:03 +08:00
										 |  |  | 					"Content-Encoding":     []string{streamingContentEncoding}, | 
					
						
							| 
									
										
										
										
											2016-09-17 06:17:49 +08:00
										 |  |  | 				}, | 
					
						
							| 
									
										
										
										
											2020-07-21 03:52:49 +08:00
										 |  |  | 				Method: http.MethodPut, | 
					
						
							| 
									
										
										
										
											2016-09-17 06:17:49 +08:00
										 |  |  | 			}, | 
					
						
							|  |  |  | 			authT: authTypeStreamingSigned, | 
					
						
							|  |  |  | 		}, | 
					
						
							|  |  |  | 		// Test case - 2
 | 
					
						
							|  |  |  | 		// Check for JWT header.
 | 
					
						
							|  |  |  | 		{ | 
					
						
							|  |  |  | 			req: &http.Request{ | 
					
						
							|  |  |  | 				URL: &url.URL{ | 
					
						
							| 
									
										
										
										
											2017-02-10 15:26:44 +08:00
										 |  |  | 					Host:   "127.0.0.1:9000", | 
					
						
							| 
									
										
										
										
											2017-01-19 04:24:34 +08:00
										 |  |  | 					Scheme: httpScheme, | 
					
						
							| 
									
										
										
										
											2019-08-07 03:08:58 +08:00
										 |  |  | 					Path:   SlashSeparator, | 
					
						
							| 
									
										
										
										
											2016-09-17 06:17:49 +08:00
										 |  |  | 				}, | 
					
						
							|  |  |  | 				Header: http.Header{ | 
					
						
							|  |  |  | 					"Authorization": []string{"Bearer 12313123"}, | 
					
						
							|  |  |  | 				}, | 
					
						
							|  |  |  | 			}, | 
					
						
							|  |  |  | 			authT: authTypeJWT, | 
					
						
							|  |  |  | 		}, | 
					
						
							|  |  |  | 		// Test case - 3
 | 
					
						
							|  |  |  | 		// Empty authorization header.
 | 
					
						
							|  |  |  | 		{ | 
					
						
							|  |  |  | 			req: &http.Request{ | 
					
						
							|  |  |  | 				URL: &url.URL{ | 
					
						
							| 
									
										
										
										
											2017-02-10 15:26:44 +08:00
										 |  |  | 					Host:   "127.0.0.1:9000", | 
					
						
							| 
									
										
										
										
											2017-01-19 04:24:34 +08:00
										 |  |  | 					Scheme: httpScheme, | 
					
						
							| 
									
										
										
										
											2019-08-07 03:08:58 +08:00
										 |  |  | 					Path:   SlashSeparator, | 
					
						
							| 
									
										
										
										
											2016-09-17 06:17:49 +08:00
										 |  |  | 				}, | 
					
						
							|  |  |  | 				Header: http.Header{ | 
					
						
							|  |  |  | 					"Authorization": []string{""}, | 
					
						
							|  |  |  | 				}, | 
					
						
							|  |  |  | 			}, | 
					
						
							|  |  |  | 			authT: authTypeUnknown, | 
					
						
							|  |  |  | 		}, | 
					
						
							|  |  |  | 		// Test case - 4
 | 
					
						
							|  |  |  | 		// Check for presigned.
 | 
					
						
							|  |  |  | 		{ | 
					
						
							|  |  |  | 			req: &http.Request{ | 
					
						
							|  |  |  | 				URL: &url.URL{ | 
					
						
							| 
									
										
										
										
											2017-02-10 15:26:44 +08:00
										 |  |  | 					Host:     "127.0.0.1:9000", | 
					
						
							| 
									
										
										
										
											2017-01-19 04:24:34 +08:00
										 |  |  | 					Scheme:   httpScheme, | 
					
						
							| 
									
										
										
										
											2019-08-07 03:08:58 +08:00
										 |  |  | 					Path:     SlashSeparator, | 
					
						
							| 
									
										
										
										
											2016-09-17 06:17:49 +08:00
										 |  |  | 					RawQuery: "X-Amz-Credential=EXAMPLEINVALIDEXAMPL%2Fs3%2F20160314%2Fus-east-1", | 
					
						
							|  |  |  | 				}, | 
					
						
							|  |  |  | 			}, | 
					
						
							|  |  |  | 			authT: authTypePresigned, | 
					
						
							|  |  |  | 		}, | 
					
						
							|  |  |  | 		// Test case - 5
 | 
					
						
							|  |  |  | 		// Check for post policy.
 | 
					
						
							|  |  |  | 		{ | 
					
						
							|  |  |  | 			req: &http.Request{ | 
					
						
							|  |  |  | 				URL: &url.URL{ | 
					
						
							| 
									
										
										
										
											2017-02-10 15:26:44 +08:00
										 |  |  | 					Host:   "127.0.0.1:9000", | 
					
						
							| 
									
										
										
										
											2017-01-19 04:24:34 +08:00
										 |  |  | 					Scheme: httpScheme, | 
					
						
							| 
									
										
										
										
											2019-08-07 03:08:58 +08:00
										 |  |  | 					Path:   SlashSeparator, | 
					
						
							| 
									
										
										
										
											2016-09-17 06:17:49 +08:00
										 |  |  | 				}, | 
					
						
							|  |  |  | 				Header: http.Header{ | 
					
						
							|  |  |  | 					"Content-Type": []string{"multipart/form-data"}, | 
					
						
							|  |  |  | 				}, | 
					
						
							| 
									
										
										
										
											2020-07-21 03:52:49 +08:00
										 |  |  | 				Method: http.MethodPost, | 
					
						
							| 
									
										
										
										
											2016-09-17 06:17:49 +08:00
										 |  |  | 			}, | 
					
						
							|  |  |  | 			authT: authTypePostPolicy, | 
					
						
							|  |  |  | 		}, | 
					
						
							|  |  |  | 	} | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	// .. Tests all request auth type.
 | 
					
						
							|  |  |  | 	for i, testc := range testCases { | 
					
						
							|  |  |  | 		authT := getRequestAuthType(testc.req) | 
					
						
							|  |  |  | 		if authT != testc.authT { | 
					
						
							|  |  |  | 			t.Errorf("Test %d: Expected %d, got %d", i+1, testc.authT, authT) | 
					
						
							|  |  |  | 		} | 
					
						
							|  |  |  | 	} | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2016-08-09 11:56:29 +08:00
										 |  |  | // Test all s3 supported auth types.
 | 
					
						
							|  |  |  | func TestS3SupportedAuthType(t *testing.T) { | 
					
						
							|  |  |  | 	type testCase struct { | 
					
						
							|  |  |  | 		authT authType | 
					
						
							|  |  |  | 		pass  bool | 
					
						
							|  |  |  | 	} | 
					
						
							|  |  |  | 	// List of all valid and invalid test cases.
 | 
					
						
							|  |  |  | 	testCases := []testCase{ | 
					
						
							|  |  |  | 		// Test 1 - supported s3 type anonymous.
 | 
					
						
							|  |  |  | 		{ | 
					
						
							|  |  |  | 			authT: authTypeAnonymous, | 
					
						
							|  |  |  | 			pass:  true, | 
					
						
							|  |  |  | 		}, | 
					
						
							|  |  |  | 		// Test 2 - supported s3 type presigned.
 | 
					
						
							|  |  |  | 		{ | 
					
						
							|  |  |  | 			authT: authTypePresigned, | 
					
						
							|  |  |  | 			pass:  true, | 
					
						
							|  |  |  | 		}, | 
					
						
							|  |  |  | 		// Test 3 - supported s3 type signed.
 | 
					
						
							|  |  |  | 		{ | 
					
						
							|  |  |  | 			authT: authTypeSigned, | 
					
						
							|  |  |  | 			pass:  true, | 
					
						
							|  |  |  | 		}, | 
					
						
							|  |  |  | 		// Test 4 - supported s3 type with post policy.
 | 
					
						
							|  |  |  | 		{ | 
					
						
							|  |  |  | 			authT: authTypePostPolicy, | 
					
						
							|  |  |  | 			pass:  true, | 
					
						
							|  |  |  | 		}, | 
					
						
							|  |  |  | 		// Test 5 - supported s3 type with streaming signed.
 | 
					
						
							|  |  |  | 		{ | 
					
						
							|  |  |  | 			authT: authTypeStreamingSigned, | 
					
						
							|  |  |  | 			pass:  true, | 
					
						
							|  |  |  | 		}, | 
					
						
							| 
									
										
										
										
											2016-10-01 05:32:13 +08:00
										 |  |  | 		// Test 6 - supported s3 type with signature v2.
 | 
					
						
							|  |  |  | 		{ | 
					
						
							|  |  |  | 			authT: authTypeSignedV2, | 
					
						
							|  |  |  | 			pass:  true, | 
					
						
							|  |  |  | 		}, | 
					
						
							|  |  |  | 		// Test 7 - supported s3 type with presign v2.
 | 
					
						
							|  |  |  | 		{ | 
					
						
							|  |  |  | 			authT: authTypePresignedV2, | 
					
						
							|  |  |  | 			pass:  true, | 
					
						
							|  |  |  | 		}, | 
					
						
							|  |  |  | 		// Test 8 - JWT is not supported s3 type.
 | 
					
						
							| 
									
										
										
										
											2016-08-09 11:56:29 +08:00
										 |  |  | 		{ | 
					
						
							|  |  |  | 			authT: authTypeJWT, | 
					
						
							|  |  |  | 			pass:  false, | 
					
						
							|  |  |  | 		}, | 
					
						
							| 
									
										
										
										
											2016-10-01 05:32:13 +08:00
										 |  |  | 		// Test 9 - unknown auth header is not supported s3 type.
 | 
					
						
							| 
									
										
										
										
											2016-08-09 11:56:29 +08:00
										 |  |  | 		{ | 
					
						
							|  |  |  | 			authT: authTypeUnknown, | 
					
						
							|  |  |  | 			pass:  false, | 
					
						
							|  |  |  | 		}, | 
					
						
							| 
									
										
										
										
											2016-10-01 05:32:13 +08:00
										 |  |  | 		// Test 10 - some new auth type is not supported s3 type.
 | 
					
						
							| 
									
										
										
										
											2016-08-09 11:56:29 +08:00
										 |  |  | 		{ | 
					
						
							| 
									
										
										
										
											2016-10-01 05:32:13 +08:00
										 |  |  | 			authT: authType(9), | 
					
						
							| 
									
										
										
										
											2016-08-09 11:56:29 +08:00
										 |  |  | 			pass:  false, | 
					
						
							|  |  |  | 		}, | 
					
						
							|  |  |  | 	} | 
					
						
							|  |  |  | 	// Validate all the test cases.
 | 
					
						
							|  |  |  | 	for i, tt := range testCases { | 
					
						
							|  |  |  | 		ok := isSupportedS3AuthType(tt.authT) | 
					
						
							|  |  |  | 		if ok != tt.pass { | 
					
						
							|  |  |  | 			t.Errorf("Test %d:, Expected %t, got %t", i+1, tt.pass, ok) | 
					
						
							|  |  |  | 		} | 
					
						
							|  |  |  | 	} | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2016-10-01 05:32:13 +08:00
										 |  |  | func TestIsRequestPresignedSignatureV2(t *testing.T) { | 
					
						
							|  |  |  | 	testCases := []struct { | 
					
						
							|  |  |  | 		inputQueryKey   string | 
					
						
							|  |  |  | 		inputQueryValue string | 
					
						
							|  |  |  | 		expectedResult  bool | 
					
						
							|  |  |  | 	}{ | 
					
						
							|  |  |  | 		// Test case - 1.
 | 
					
						
							|  |  |  | 		// Test case with query key "AWSAccessKeyId" set.
 | 
					
						
							|  |  |  | 		{"", "", false}, | 
					
						
							|  |  |  | 		// Test case - 2.
 | 
					
						
							|  |  |  | 		{"AWSAccessKeyId", "", true}, | 
					
						
							|  |  |  | 		// Test case - 3.
 | 
					
						
							|  |  |  | 		{"X-Amz-Content-Sha256", "", false}, | 
					
						
							|  |  |  | 	} | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	for i, testCase := range testCases { | 
					
						
							|  |  |  | 		// creating an input HTTP request.
 | 
					
						
							|  |  |  | 		// Only the query parameters are relevant for this particular test.
 | 
					
						
							| 
									
										
										
										
											2020-07-21 03:52:49 +08:00
										 |  |  | 		inputReq, err := http.NewRequest(http.MethodGet, "http://example.com", nil) | 
					
						
							| 
									
										
										
										
											2016-10-01 05:32:13 +08:00
										 |  |  | 		if err != nil { | 
					
						
							|  |  |  | 			t.Fatalf("Error initializing input HTTP request: %v", err) | 
					
						
							|  |  |  | 		} | 
					
						
							|  |  |  | 		q := inputReq.URL.Query() | 
					
						
							|  |  |  | 		q.Add(testCase.inputQueryKey, testCase.inputQueryValue) | 
					
						
							|  |  |  | 		inputReq.URL.RawQuery = q.Encode() | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 		actualResult := isRequestPresignedSignatureV2(inputReq) | 
					
						
							|  |  |  | 		if testCase.expectedResult != actualResult { | 
					
						
							|  |  |  | 			t.Errorf("Test %d: Expected the result to `%v`, but instead got `%v`", i+1, testCase.expectedResult, actualResult) | 
					
						
							|  |  |  | 		} | 
					
						
							|  |  |  | 	} | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2016-08-02 11:54:11 +08:00
										 |  |  | // TestIsRequestPresignedSignatureV4 - Test validates the logic for presign signature verision v4 detection.
 | 
					
						
							|  |  |  | func TestIsRequestPresignedSignatureV4(t *testing.T) { | 
					
						
							|  |  |  | 	testCases := []struct { | 
					
						
							|  |  |  | 		inputQueryKey   string | 
					
						
							|  |  |  | 		inputQueryValue string | 
					
						
							|  |  |  | 		expectedResult  bool | 
					
						
							|  |  |  | 	}{ | 
					
						
							|  |  |  | 		// Test case - 1.
 | 
					
						
							|  |  |  | 		// Test case with query key ""X-Amz-Credential" set.
 | 
					
						
							|  |  |  | 		{"", "", false}, | 
					
						
							|  |  |  | 		// Test case - 2.
 | 
					
						
							|  |  |  | 		{"X-Amz-Credential", "", true}, | 
					
						
							|  |  |  | 		// Test case - 3.
 | 
					
						
							|  |  |  | 		{"X-Amz-Content-Sha256", "", false}, | 
					
						
							|  |  |  | 	} | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	for i, testCase := range testCases { | 
					
						
							|  |  |  | 		// creating an input HTTP request.
 | 
					
						
							|  |  |  | 		// Only the query parameters are relevant for this particular test.
 | 
					
						
							| 
									
										
										
										
											2020-07-21 03:52:49 +08:00
										 |  |  | 		inputReq, err := http.NewRequest(http.MethodGet, "http://example.com", nil) | 
					
						
							| 
									
										
										
										
											2016-08-02 11:54:11 +08:00
										 |  |  | 		if err != nil { | 
					
						
							|  |  |  | 			t.Fatalf("Error initializing input HTTP request: %v", err) | 
					
						
							|  |  |  | 		} | 
					
						
							|  |  |  | 		q := inputReq.URL.Query() | 
					
						
							|  |  |  | 		q.Add(testCase.inputQueryKey, testCase.inputQueryValue) | 
					
						
							|  |  |  | 		inputReq.URL.RawQuery = q.Encode() | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 		actualResult := isRequestPresignedSignatureV4(inputReq) | 
					
						
							|  |  |  | 		if testCase.expectedResult != actualResult { | 
					
						
							|  |  |  | 			t.Errorf("Test %d: Expected the result to `%v`, but instead got `%v`", i+1, testCase.expectedResult, actualResult) | 
					
						
							|  |  |  | 		} | 
					
						
							|  |  |  | 	} | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2016-07-19 14:56:27 +08:00
										 |  |  | // Provides a fully populated http request instance, fails otherwise.
 | 
					
						
							|  |  |  | func mustNewRequest(method string, urlStr string, contentLength int64, body io.ReadSeeker, t *testing.T) *http.Request { | 
					
						
							|  |  |  | 	req, err := newTestRequest(method, urlStr, contentLength, body) | 
					
						
							|  |  |  | 	if err != nil { | 
					
						
							|  |  |  | 		t.Fatalf("Unable to initialize new http request %s", err) | 
					
						
							|  |  |  | 	} | 
					
						
							|  |  |  | 	return req | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | // This is similar to mustNewRequest but additionally the request
 | 
					
						
							|  |  |  | // is signed with AWS Signature V4, fails if not able to do so.
 | 
					
						
							|  |  |  | func mustNewSignedRequest(method string, urlStr string, contentLength int64, body io.ReadSeeker, t *testing.T) *http.Request { | 
					
						
							|  |  |  | 	req := mustNewRequest(method, urlStr, contentLength, body, t) | 
					
						
							| 
									
										
										
										
											2019-10-23 13:59:13 +08:00
										 |  |  | 	cred := globalActiveCred | 
					
						
							| 
									
										
										
										
											2016-12-27 02:21:23 +08:00
										 |  |  | 	if err := signRequestV4(req, cred.AccessKey, cred.SecretKey); err != nil { | 
					
						
							| 
									
										
										
										
											2016-07-19 14:56:27 +08:00
										 |  |  | 		t.Fatalf("Unable to inititalized new signed http request %s", err) | 
					
						
							|  |  |  | 	} | 
					
						
							|  |  |  | 	return req | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2018-01-18 02:36:25 +08:00
										 |  |  | // This is similar to mustNewRequest but additionally the request
 | 
					
						
							|  |  |  | // is signed with AWS Signature V2, fails if not able to do so.
 | 
					
						
							|  |  |  | func mustNewSignedV2Request(method string, urlStr string, contentLength int64, body io.ReadSeeker, t *testing.T) *http.Request { | 
					
						
							|  |  |  | 	req := mustNewRequest(method, urlStr, contentLength, body, t) | 
					
						
							| 
									
										
										
										
											2019-10-23 13:59:13 +08:00
										 |  |  | 	cred := globalActiveCred | 
					
						
							| 
									
										
										
										
											2018-01-18 02:36:25 +08:00
										 |  |  | 	if err := signRequestV2(req, cred.AccessKey, cred.SecretKey); err != nil { | 
					
						
							|  |  |  | 		t.Fatalf("Unable to inititalized new signed http request %s", err) | 
					
						
							|  |  |  | 	} | 
					
						
							|  |  |  | 	return req | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | // This is similar to mustNewRequest but additionally the request
 | 
					
						
							|  |  |  | // is presigned with AWS Signature V2, fails if not able to do so.
 | 
					
						
							|  |  |  | func mustNewPresignedV2Request(method string, urlStr string, contentLength int64, body io.ReadSeeker, t *testing.T) *http.Request { | 
					
						
							|  |  |  | 	req := mustNewRequest(method, urlStr, contentLength, body, t) | 
					
						
							| 
									
										
										
										
											2019-10-23 13:59:13 +08:00
										 |  |  | 	cred := globalActiveCred | 
					
						
							| 
									
										
										
										
											2018-01-18 02:36:25 +08:00
										 |  |  | 	if err := preSignV2(req, cred.AccessKey, cred.SecretKey, time.Now().Add(10*time.Minute).Unix()); err != nil { | 
					
						
							|  |  |  | 		t.Fatalf("Unable to inititalized new signed http request %s", err) | 
					
						
							|  |  |  | 	} | 
					
						
							|  |  |  | 	return req | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | // This is similar to mustNewRequest but additionally the request
 | 
					
						
							|  |  |  | // is presigned with AWS Signature V4, fails if not able to do so.
 | 
					
						
							|  |  |  | func mustNewPresignedRequest(method string, urlStr string, contentLength int64, body io.ReadSeeker, t *testing.T) *http.Request { | 
					
						
							|  |  |  | 	req := mustNewRequest(method, urlStr, contentLength, body, t) | 
					
						
							| 
									
										
										
										
											2019-10-23 13:59:13 +08:00
										 |  |  | 	cred := globalActiveCred | 
					
						
							| 
									
										
										
										
											2018-01-18 02:36:25 +08:00
										 |  |  | 	if err := preSignV4(req, cred.AccessKey, cred.SecretKey, time.Now().Add(10*time.Minute).Unix()); err != nil { | 
					
						
							|  |  |  | 		t.Fatalf("Unable to inititalized new signed http request %s", err) | 
					
						
							|  |  |  | 	} | 
					
						
							|  |  |  | 	return req | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2018-03-17 02:22:34 +08:00
										 |  |  | func mustNewSignedShortMD5Request(method string, urlStr string, contentLength int64, body io.ReadSeeker, t *testing.T) *http.Request { | 
					
						
							|  |  |  | 	req := mustNewRequest(method, urlStr, contentLength, body, t) | 
					
						
							|  |  |  | 	req.Header.Set("Content-Md5", "invalid-digest") | 
					
						
							| 
									
										
										
										
											2019-10-23 13:59:13 +08:00
										 |  |  | 	cred := globalActiveCred | 
					
						
							| 
									
										
										
										
											2018-03-17 02:22:34 +08:00
										 |  |  | 	if err := signRequestV4(req, cred.AccessKey, cred.SecretKey); err != nil { | 
					
						
							|  |  |  | 		t.Fatalf("Unable to initialized new signed http request %s", err) | 
					
						
							|  |  |  | 	} | 
					
						
							|  |  |  | 	return req | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | func mustNewSignedEmptyMD5Request(method string, urlStr string, contentLength int64, body io.ReadSeeker, t *testing.T) *http.Request { | 
					
						
							|  |  |  | 	req := mustNewRequest(method, urlStr, contentLength, body, t) | 
					
						
							|  |  |  | 	req.Header.Set("Content-Md5", "") | 
					
						
							| 
									
										
										
										
											2019-10-23 13:59:13 +08:00
										 |  |  | 	cred := globalActiveCred | 
					
						
							| 
									
										
										
										
											2018-03-17 02:22:34 +08:00
										 |  |  | 	if err := signRequestV4(req, cred.AccessKey, cred.SecretKey); err != nil { | 
					
						
							|  |  |  | 		t.Fatalf("Unable to initialized new signed http request %s", err) | 
					
						
							|  |  |  | 	} | 
					
						
							|  |  |  | 	return req | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2019-10-23 13:59:13 +08:00
										 |  |  | func mustNewSignedBadMD5Request(method string, urlStr string, contentLength int64, | 
					
						
							|  |  |  | 	body io.ReadSeeker, t *testing.T) *http.Request { | 
					
						
							| 
									
										
										
										
											2017-04-11 00:58:08 +08:00
										 |  |  | 	req := mustNewRequest(method, urlStr, contentLength, body, t) | 
					
						
							|  |  |  | 	req.Header.Set("Content-Md5", "YWFhYWFhYWFhYWFhYWFhCg==") | 
					
						
							| 
									
										
										
										
											2019-10-23 13:59:13 +08:00
										 |  |  | 	cred := globalActiveCred | 
					
						
							| 
									
										
										
										
											2017-04-11 00:58:08 +08:00
										 |  |  | 	if err := signRequestV4(req, cred.AccessKey, cred.SecretKey); err != nil { | 
					
						
							|  |  |  | 		t.Fatalf("Unable to initialized new signed http request %s", err) | 
					
						
							|  |  |  | 	} | 
					
						
							|  |  |  | 	return req | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2016-07-19 14:56:27 +08:00
										 |  |  | // Tests is requested authenticated function, tests replies for s3 errors.
 | 
					
						
							|  |  |  | func TestIsReqAuthenticated(t *testing.T) { | 
					
						
							| 
									
										
										
										
											2018-08-15 12:41:47 +08:00
										 |  |  | 	objLayer, fsDir, err := prepareFS() | 
					
						
							| 
									
										
										
										
											2016-07-19 14:56:27 +08:00
										 |  |  | 	if err != nil { | 
					
						
							| 
									
										
										
										
											2018-08-15 12:41:47 +08:00
										 |  |  | 		t.Fatal(err) | 
					
						
							|  |  |  | 	} | 
					
						
							|  |  |  | 	defer os.RemoveAll(fsDir) | 
					
						
							|  |  |  | 	if err = newTestConfig(globalMinioDefaultRegion, objLayer); err != nil { | 
					
						
							| 
									
										
										
										
											2016-07-26 15:01:35 +08:00
										 |  |  | 		t.Fatalf("unable initialize config file, %s", err) | 
					
						
							| 
									
										
										
										
											2016-07-19 14:56:27 +08:00
										 |  |  | 	} | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2017-11-01 02:54:32 +08:00
										 |  |  | 	creds, err := auth.CreateCredentials("myuser", "mypassword") | 
					
						
							| 
									
										
										
										
											2017-03-16 15:16:06 +08:00
										 |  |  | 	if err != nil { | 
					
						
							|  |  |  | 		t.Fatalf("unable create credential, %s", err) | 
					
						
							|  |  |  | 	} | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2019-10-23 13:59:13 +08:00
										 |  |  | 	globalActiveCred = creds | 
					
						
							| 
									
										
										
										
											2016-07-19 14:56:27 +08:00
										 |  |  | 
 | 
					
						
							|  |  |  | 	// List of test cases for validating http request authentication.
 | 
					
						
							|  |  |  | 	testCases := []struct { | 
					
						
							|  |  |  | 		req     *http.Request | 
					
						
							|  |  |  | 		s3Error APIErrorCode | 
					
						
							|  |  |  | 	}{ | 
					
						
							|  |  |  | 		// When request is unsigned, access denied is returned.
 | 
					
						
							| 
									
										
										
										
											2020-07-21 03:52:49 +08:00
										 |  |  | 		{mustNewRequest(http.MethodGet, "http://127.0.0.1:9000", 0, nil, t), ErrAccessDenied}, | 
					
						
							| 
									
										
										
										
											2018-03-17 02:22:34 +08:00
										 |  |  | 		// Empty Content-Md5 header.
 | 
					
						
							| 
									
										
										
										
											2020-07-21 03:52:49 +08:00
										 |  |  | 		{mustNewSignedEmptyMD5Request(http.MethodPut, "http://127.0.0.1:9000/", 5, bytes.NewReader([]byte("hello")), t), ErrInvalidDigest}, | 
					
						
							| 
									
										
										
										
											2018-03-17 02:22:34 +08:00
										 |  |  | 		// Short Content-Md5 header.
 | 
					
						
							| 
									
										
										
										
											2020-07-21 03:52:49 +08:00
										 |  |  | 		{mustNewSignedShortMD5Request(http.MethodPut, "http://127.0.0.1:9000/", 5, bytes.NewReader([]byte("hello")), t), ErrInvalidDigest}, | 
					
						
							| 
									
										
										
										
											2016-07-19 14:56:27 +08:00
										 |  |  | 		// When request is properly signed, but has bad Content-MD5 header.
 | 
					
						
							| 
									
										
										
										
											2020-07-21 03:52:49 +08:00
										 |  |  | 		{mustNewSignedBadMD5Request(http.MethodPut, "http://127.0.0.1:9000/", 5, bytes.NewReader([]byte("hello")), t), ErrBadDigest}, | 
					
						
							| 
									
										
										
										
											2016-07-19 14:56:27 +08:00
										 |  |  | 		// When request is properly signed, error is none.
 | 
					
						
							| 
									
										
										
										
											2020-07-21 03:52:49 +08:00
										 |  |  | 		{mustNewSignedRequest(http.MethodGet, "http://127.0.0.1:9000", 0, nil, t), ErrNone}, | 
					
						
							| 
									
										
										
										
											2016-07-19 14:56:27 +08:00
										 |  |  | 	} | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2018-11-13 03:07:43 +08:00
										 |  |  | 	ctx := context.Background() | 
					
						
							| 
									
										
										
										
											2016-07-19 14:56:27 +08:00
										 |  |  | 	// Validates all testcases.
 | 
					
						
							| 
									
										
										
										
											2018-05-19 02:27:25 +08:00
										 |  |  | 	for i, testCase := range testCases { | 
					
						
							| 
									
										
										
										
											2019-10-23 13:59:13 +08:00
										 |  |  | 		s3Error := isReqAuthenticated(ctx, testCase.req, globalServerRegion, serviceS3) | 
					
						
							| 
									
										
										
										
											2019-02-28 09:46:55 +08:00
										 |  |  | 		if s3Error != testCase.s3Error { | 
					
						
							| 
									
										
										
										
											2018-11-13 03:07:43 +08:00
										 |  |  | 			if _, err := ioutil.ReadAll(testCase.req.Body); toAPIErrorCode(ctx, err) != testCase.s3Error { | 
					
						
							| 
									
										
										
										
											2019-02-12 17:25:52 +08:00
										 |  |  | 				t.Fatalf("Test %d: Unexpected S3 error: want %d - got %d (got after reading request %s)", i, testCase.s3Error, s3Error, toAPIError(ctx, err).Code) | 
					
						
							| 
									
										
										
										
											2018-05-19 02:27:25 +08:00
										 |  |  | 			} | 
					
						
							| 
									
										
										
										
											2016-07-19 14:56:27 +08:00
										 |  |  | 		} | 
					
						
							|  |  |  | 	} | 
					
						
							|  |  |  | } | 
					
						
							| 
									
										
										
										
											2020-04-23 04:42:01 +08:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2018-01-18 02:36:25 +08:00
										 |  |  | func TestCheckAdminRequestAuthType(t *testing.T) { | 
					
						
							| 
									
										
										
										
											2018-08-15 12:41:47 +08:00
										 |  |  | 	objLayer, fsDir, err := prepareFS() | 
					
						
							| 
									
										
										
										
											2018-01-18 02:36:25 +08:00
										 |  |  | 	if err != nil { | 
					
						
							| 
									
										
										
										
											2018-08-15 12:41:47 +08:00
										 |  |  | 		t.Fatal(err) | 
					
						
							|  |  |  | 	} | 
					
						
							|  |  |  | 	defer os.RemoveAll(fsDir) | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	if err = newTestConfig(globalMinioDefaultRegion, objLayer); err != nil { | 
					
						
							| 
									
										
										
										
											2018-01-18 02:36:25 +08:00
										 |  |  | 		t.Fatalf("unable initialize config file, %s", err) | 
					
						
							|  |  |  | 	} | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	creds, err := auth.CreateCredentials("myuser", "mypassword") | 
					
						
							|  |  |  | 	if err != nil { | 
					
						
							|  |  |  | 		t.Fatalf("unable create credential, %s", err) | 
					
						
							|  |  |  | 	} | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2019-10-23 13:59:13 +08:00
										 |  |  | 	globalActiveCred = creds | 
					
						
							| 
									
										
										
										
											2018-01-18 02:36:25 +08:00
										 |  |  | 	testCases := []struct { | 
					
						
							|  |  |  | 		Request *http.Request | 
					
						
							|  |  |  | 		ErrCode APIErrorCode | 
					
						
							|  |  |  | 	}{ | 
					
						
							| 
									
										
										
										
											2020-07-21 03:52:49 +08:00
										 |  |  | 		{Request: mustNewRequest(http.MethodGet, "http://127.0.0.1:9000", 0, nil, t), ErrCode: ErrAccessDenied}, | 
					
						
							|  |  |  | 		{Request: mustNewSignedRequest(http.MethodGet, "http://127.0.0.1:9000", 0, nil, t), ErrCode: ErrNone}, | 
					
						
							|  |  |  | 		{Request: mustNewSignedV2Request(http.MethodGet, "http://127.0.0.1:9000", 0, nil, t), ErrCode: ErrAccessDenied}, | 
					
						
							|  |  |  | 		{Request: mustNewPresignedV2Request(http.MethodGet, "http://127.0.0.1:9000", 0, nil, t), ErrCode: ErrAccessDenied}, | 
					
						
							|  |  |  | 		{Request: mustNewPresignedRequest(http.MethodGet, "http://127.0.0.1:9000", 0, nil, t), ErrCode: ErrAccessDenied}, | 
					
						
							| 
									
										
										
										
											2018-01-18 02:36:25 +08:00
										 |  |  | 	} | 
					
						
							| 
									
										
										
										
											2018-11-13 03:07:43 +08:00
										 |  |  | 	ctx := context.Background() | 
					
						
							| 
									
										
										
										
											2018-01-18 02:36:25 +08:00
										 |  |  | 	for i, testCase := range testCases { | 
					
						
							| 
									
										
										
										
											2020-12-19 03:51:15 +08:00
										 |  |  | 		if _, s3Error := checkAdminRequestAuth(ctx, testCase.Request, iampolicy.AllAdminActions, globalServerRegion); s3Error != testCase.ErrCode { | 
					
						
							| 
									
										
										
										
											2018-01-18 02:36:25 +08:00
										 |  |  | 			t.Errorf("Test %d: Unexpected s3error returned wanted %d, got %d", i, testCase.ErrCode, s3Error) | 
					
						
							|  |  |  | 		} | 
					
						
							|  |  |  | 	} | 
					
						
							|  |  |  | } | 
					
						
							| 
									
										
										
										
											2020-04-23 04:42:01 +08:00
										 |  |  | 
 | 
					
						
							|  |  |  | func TestValidateAdminSignature(t *testing.T) { | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	ctx := context.Background() | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	objLayer, fsDir, err := prepareFS() | 
					
						
							|  |  |  | 	if err != nil { | 
					
						
							|  |  |  | 		t.Fatal(err) | 
					
						
							|  |  |  | 	} | 
					
						
							|  |  |  | 	defer os.RemoveAll(fsDir) | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	if err = newTestConfig(globalMinioDefaultRegion, objLayer); err != nil { | 
					
						
							|  |  |  | 		t.Fatalf("unable initialize config file, %s", err) | 
					
						
							|  |  |  | 	} | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	creds, err := auth.CreateCredentials("admin", "mypassword") | 
					
						
							|  |  |  | 	if err != nil { | 
					
						
							|  |  |  | 		t.Fatalf("unable create credential, %s", err) | 
					
						
							|  |  |  | 	} | 
					
						
							|  |  |  | 	globalActiveCred = creds | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	testCases := []struct { | 
					
						
							|  |  |  | 		AccessKey string | 
					
						
							|  |  |  | 		SecretKey string | 
					
						
							|  |  |  | 		ErrCode   APIErrorCode | 
					
						
							|  |  |  | 	}{ | 
					
						
							|  |  |  | 		{"", "", ErrInvalidAccessKeyID}, | 
					
						
							|  |  |  | 		{"admin", "", ErrSignatureDoesNotMatch}, | 
					
						
							|  |  |  | 		{"admin", "wrongpassword", ErrSignatureDoesNotMatch}, | 
					
						
							|  |  |  | 		{"wronguser", "mypassword", ErrInvalidAccessKeyID}, | 
					
						
							|  |  |  | 		{"", "mypassword", ErrInvalidAccessKeyID}, | 
					
						
							|  |  |  | 		{"admin", "mypassword", ErrNone}, | 
					
						
							|  |  |  | 	} | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	for i, testCase := range testCases { | 
					
						
							| 
									
										
										
										
											2020-07-21 03:52:49 +08:00
										 |  |  | 		req := mustNewRequest(http.MethodGet, "http://localhost:9000/", 0, nil, t) | 
					
						
							| 
									
										
										
										
											2020-04-23 04:42:01 +08:00
										 |  |  | 		if err := signRequestV4(req, testCase.AccessKey, testCase.SecretKey); err != nil { | 
					
						
							|  |  |  | 			t.Fatalf("Unable to inititalized new signed http request %s", err) | 
					
						
							|  |  |  | 		} | 
					
						
							|  |  |  | 		_, _, _, s3Error := validateAdminSignature(ctx, req, globalMinioDefaultRegion) | 
					
						
							|  |  |  | 		if s3Error != testCase.ErrCode { | 
					
						
							|  |  |  | 			t.Errorf("Test %d: Unexpected s3error returned wanted %d, got %d", i+1, testCase.ErrCode, s3Error) | 
					
						
							|  |  |  | 		} | 
					
						
							|  |  |  | 	} | 
					
						
							|  |  |  | } |