| 
									
										
										
										
											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-09-14 10:00:01 +08:00
										 |  |  | 
 | 
					
						
							|  |  |  | package cmd | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | import ( | 
					
						
							|  |  |  | 	"bytes" | 
					
						
							| 
									
										
										
										
											2018-03-16 04:27:16 +08:00
										 |  |  | 	"context" | 
					
						
							| 
									
										
										
										
											2016-09-14 10:00:01 +08:00
										 |  |  | 	"encoding/base64" | 
					
						
							|  |  |  | 	"fmt" | 
					
						
							| 
									
										
										
										
											2016-09-15 14:53:42 +08:00
										 |  |  | 	"io/ioutil" | 
					
						
							| 
									
										
										
										
											2016-09-14 10:00:01 +08:00
										 |  |  | 	"mime/multipart" | 
					
						
							|  |  |  | 	"net/http" | 
					
						
							|  |  |  | 	"net/http/httptest" | 
					
						
							| 
									
										
										
										
											2017-03-14 05:41:13 +08:00
										 |  |  | 	"net/url" | 
					
						
							| 
									
										
										
										
											2016-09-14 10:00:01 +08:00
										 |  |  | 	"testing" | 
					
						
							|  |  |  | 	"time" | 
					
						
							| 
									
										
										
										
											2016-11-23 10:18:22 +08:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2021-10-28 22:35:28 +08:00
										 |  |  | 	"github.com/dustin/go-humanize" | 
					
						
							| 
									
										
										
										
											2016-09-14 10:00:01 +08:00
										 |  |  | ) | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | const ( | 
					
						
							| 
									
										
										
										
											2020-05-26 07:51:32 +08:00
										 |  |  | 	iso8601DateFormat = "20060102T150405Z" | 
					
						
							| 
									
										
										
										
											2016-09-14 10:00:01 +08:00
										 |  |  | ) | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2016-11-21 20:15:26 +08:00
										 |  |  | func newPostPolicyBytesV4WithContentRange(credential, bucketName, objectKey string, expiration time.Time) []byte { | 
					
						
							| 
									
										
										
										
											2017-03-19 02:28:41 +08:00
										 |  |  | 	t := UTCNow() | 
					
						
							| 
									
										
										
										
											2016-11-21 20:15:26 +08:00
										 |  |  | 	// Add the expiration date.
 | 
					
						
							| 
									
										
										
										
											2020-05-26 07:51:32 +08:00
										 |  |  | 	expirationStr := fmt.Sprintf(`"expiration": "%s"`, expiration.Format(iso8601TimeFormat)) | 
					
						
							| 
									
										
										
										
											2016-11-21 20:15:26 +08:00
										 |  |  | 	// Add the bucket condition, only accept buckets equal to the one passed.
 | 
					
						
							|  |  |  | 	bucketConditionStr := fmt.Sprintf(`["eq", "$bucket", "%s"]`, bucketName) | 
					
						
							|  |  |  | 	// Add the key condition, only accept keys equal to the one passed.
 | 
					
						
							| 
									
										
										
										
											2016-12-05 04:23:19 +08:00
										 |  |  | 	keyConditionStr := fmt.Sprintf(`["eq", "$key", "%s/upload.txt"]`, objectKey) | 
					
						
							| 
									
										
										
										
											2016-11-21 20:15:26 +08:00
										 |  |  | 	// Add content length condition, only accept content sizes of a given length.
 | 
					
						
							|  |  |  | 	contentLengthCondStr := `["content-length-range", 1024, 1048576]` | 
					
						
							|  |  |  | 	// Add the algorithm condition, only accept AWS SignV4 Sha256.
 | 
					
						
							|  |  |  | 	algorithmConditionStr := `["eq", "$x-amz-algorithm", "AWS4-HMAC-SHA256"]` | 
					
						
							|  |  |  | 	// Add the date condition, only accept the current date.
 | 
					
						
							|  |  |  | 	dateConditionStr := fmt.Sprintf(`["eq", "$x-amz-date", "%s"]`, t.Format(iso8601DateFormat)) | 
					
						
							|  |  |  | 	// Add the credential string, only accept the credential passed.
 | 
					
						
							|  |  |  | 	credentialConditionStr := fmt.Sprintf(`["eq", "$x-amz-credential", "%s"]`, credential) | 
					
						
							| 
									
										
										
										
											2019-04-02 14:45:32 +08:00
										 |  |  | 	// Add the meta-uuid string, set to 1234
 | 
					
						
							|  |  |  | 	uuidConditionStr := fmt.Sprintf(`["eq", "$x-amz-meta-uuid", "%s"]`, "1234") | 
					
						
							| 
									
										
										
										
											2016-11-21 20:15:26 +08:00
										 |  |  | 
 | 
					
						
							|  |  |  | 	// Combine all conditions into one string.
 | 
					
						
							| 
									
										
										
										
											2019-04-02 14:45:32 +08:00
										 |  |  | 	conditionStr := fmt.Sprintf(`"conditions":[%s, %s, %s, %s, %s, %s, %s]`, bucketConditionStr, | 
					
						
							|  |  |  | 		keyConditionStr, contentLengthCondStr, algorithmConditionStr, dateConditionStr, credentialConditionStr, uuidConditionStr) | 
					
						
							| 
									
										
										
										
											2016-11-21 20:15:26 +08:00
										 |  |  | 	retStr := "{" | 
					
						
							|  |  |  | 	retStr = retStr + expirationStr + "," | 
					
						
							| 
									
										
										
										
											2021-11-17 01:28:29 +08:00
										 |  |  | 	retStr += conditionStr | 
					
						
							|  |  |  | 	retStr += "}" | 
					
						
							| 
									
										
										
										
											2016-11-21 20:15:26 +08:00
										 |  |  | 
 | 
					
						
							|  |  |  | 	return []byte(retStr) | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2016-10-22 23:57:12 +08:00
										 |  |  | // newPostPolicyBytesV4 - creates a bare bones postpolicy string with key and bucket matches.
 | 
					
						
							|  |  |  | func newPostPolicyBytesV4(credential, bucketName, objectKey string, expiration time.Time) []byte { | 
					
						
							| 
									
										
										
										
											2017-03-19 02:28:41 +08:00
										 |  |  | 	t := UTCNow() | 
					
						
							| 
									
										
										
										
											2016-09-14 10:00:01 +08:00
										 |  |  | 	// Add the expiration date.
 | 
					
						
							| 
									
										
										
										
											2020-05-26 07:51:32 +08:00
										 |  |  | 	expirationStr := fmt.Sprintf(`"expiration": "%s"`, expiration.Format(iso8601TimeFormat)) | 
					
						
							| 
									
										
										
										
											2016-09-14 10:00:01 +08:00
										 |  |  | 	// Add the bucket condition, only accept buckets equal to the one passed.
 | 
					
						
							|  |  |  | 	bucketConditionStr := fmt.Sprintf(`["eq", "$bucket", "%s"]`, bucketName) | 
					
						
							|  |  |  | 	// Add the key condition, only accept keys equal to the one passed.
 | 
					
						
							| 
									
										
										
										
											2016-12-05 04:23:19 +08:00
										 |  |  | 	keyConditionStr := fmt.Sprintf(`["eq", "$key", "%s/upload.txt"]`, objectKey) | 
					
						
							| 
									
										
										
										
											2016-09-14 10:00:01 +08:00
										 |  |  | 	// Add the algorithm condition, only accept AWS SignV4 Sha256.
 | 
					
						
							|  |  |  | 	algorithmConditionStr := `["eq", "$x-amz-algorithm", "AWS4-HMAC-SHA256"]` | 
					
						
							|  |  |  | 	// Add the date condition, only accept the current date.
 | 
					
						
							|  |  |  | 	dateConditionStr := fmt.Sprintf(`["eq", "$x-amz-date", "%s"]`, t.Format(iso8601DateFormat)) | 
					
						
							|  |  |  | 	// Add the credential string, only accept the credential passed.
 | 
					
						
							|  |  |  | 	credentialConditionStr := fmt.Sprintf(`["eq", "$x-amz-credential", "%s"]`, credential) | 
					
						
							| 
									
										
										
										
											2019-04-02 14:45:32 +08:00
										 |  |  | 	// Add the meta-uuid string, set to 1234
 | 
					
						
							|  |  |  | 	uuidConditionStr := fmt.Sprintf(`["eq", "$x-amz-meta-uuid", "%s"]`, "1234") | 
					
						
							| 
									
										
										
										
											2016-09-14 10:00:01 +08:00
										 |  |  | 
 | 
					
						
							|  |  |  | 	// Combine all conditions into one string.
 | 
					
						
							| 
									
										
										
										
											2019-04-02 14:45:32 +08:00
										 |  |  | 	conditionStr := fmt.Sprintf(`"conditions":[%s, %s, %s, %s, %s, %s]`, bucketConditionStr, keyConditionStr, algorithmConditionStr, dateConditionStr, credentialConditionStr, uuidConditionStr) | 
					
						
							| 
									
										
										
										
											2016-09-14 10:00:01 +08:00
										 |  |  | 	retStr := "{" | 
					
						
							|  |  |  | 	retStr = retStr + expirationStr + "," | 
					
						
							| 
									
										
										
										
											2021-11-17 01:28:29 +08:00
										 |  |  | 	retStr += conditionStr | 
					
						
							|  |  |  | 	retStr += "}" | 
					
						
							| 
									
										
										
										
											2016-09-14 10:00:01 +08:00
										 |  |  | 
 | 
					
						
							|  |  |  | 	return []byte(retStr) | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2016-10-22 23:57:12 +08:00
										 |  |  | // newPostPolicyBytesV2 - creates a bare bones postpolicy string with key and bucket matches.
 | 
					
						
							|  |  |  | func newPostPolicyBytesV2(bucketName, objectKey string, expiration time.Time) []byte { | 
					
						
							|  |  |  | 	// Add the expiration date.
 | 
					
						
							| 
									
										
										
										
											2020-05-26 07:51:32 +08:00
										 |  |  | 	expirationStr := fmt.Sprintf(`"expiration": "%s"`, expiration.Format(iso8601TimeFormat)) | 
					
						
							| 
									
										
										
										
											2016-10-22 23:57:12 +08:00
										 |  |  | 	// Add the bucket condition, only accept buckets equal to the one passed.
 | 
					
						
							|  |  |  | 	bucketConditionStr := fmt.Sprintf(`["eq", "$bucket", "%s"]`, bucketName) | 
					
						
							|  |  |  | 	// Add the key condition, only accept keys equal to the one passed.
 | 
					
						
							| 
									
										
										
										
											2016-12-05 04:23:19 +08:00
										 |  |  | 	keyConditionStr := fmt.Sprintf(`["starts-with", "$key", "%s/upload.txt"]`, objectKey) | 
					
						
							| 
									
										
										
										
											2016-10-22 23:57:12 +08:00
										 |  |  | 
 | 
					
						
							|  |  |  | 	// Combine all conditions into one string.
 | 
					
						
							|  |  |  | 	conditionStr := fmt.Sprintf(`"conditions":[%s, %s]`, bucketConditionStr, keyConditionStr) | 
					
						
							|  |  |  | 	retStr := "{" | 
					
						
							|  |  |  | 	retStr = retStr + expirationStr + "," | 
					
						
							| 
									
										
										
										
											2021-11-17 01:28:29 +08:00
										 |  |  | 	retStr += conditionStr | 
					
						
							|  |  |  | 	retStr += "}" | 
					
						
							| 
									
										
										
										
											2016-10-22 23:57:12 +08:00
										 |  |  | 
 | 
					
						
							|  |  |  | 	return []byte(retStr) | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2020-06-13 11:04:01 +08:00
										 |  |  | // Wrapper for calling TestPostPolicyBucketHandler tests for both Erasure multiple disks and single node setup.
 | 
					
						
							| 
									
										
										
										
											2016-12-05 04:23:19 +08:00
										 |  |  | func TestPostPolicyBucketHandler(t *testing.T) { | 
					
						
							|  |  |  | 	ExecObjectLayerTest(t, testPostPolicyBucketHandler) | 
					
						
							| 
									
										
										
										
											2016-09-14 10:00:01 +08:00
										 |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2016-12-05 04:23:19 +08:00
										 |  |  | // testPostPolicyBucketHandler - Tests validate post policy handler uploading objects.
 | 
					
						
							|  |  |  | func testPostPolicyBucketHandler(obj ObjectLayer, instanceType string, t TestErrHandler) { | 
					
						
							| 
									
										
										
										
											2018-08-15 12:41:47 +08:00
										 |  |  | 	if err := newTestConfig(globalMinioDefaultRegion, obj); err != nil { | 
					
						
							| 
									
										
										
										
											2016-10-06 03:48:07 +08:00
										 |  |  | 		t.Fatalf("Initializing config.json failed") | 
					
						
							|  |  |  | 	} | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2016-09-14 10:00:01 +08:00
										 |  |  | 	// get random bucket name.
 | 
					
						
							|  |  |  | 	bucketName := getRandomBucketName() | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2018-09-11 00:42:43 +08:00
										 |  |  | 	var opts ObjectOptions | 
					
						
							| 
									
										
										
										
											2020-06-13 11:04:01 +08:00
										 |  |  | 	// Register the API end points with Erasure/FS object layer.
 | 
					
						
							| 
									
										
										
										
											2016-09-14 10:00:01 +08:00
										 |  |  | 	apiRouter := initTestAPIEndPoints(obj, []string{"PostPolicy"}) | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2019-10-23 13:59:13 +08:00
										 |  |  | 	credentials := globalActiveCred | 
					
						
							| 
									
										
										
										
											2016-09-15 14:53:42 +08:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2017-03-19 02:28:41 +08:00
										 |  |  | 	curTime := UTCNow() | 
					
						
							| 
									
										
										
										
											2016-12-05 04:23:19 +08:00
										 |  |  | 	curTimePlus5Min := curTime.Add(time.Minute * 5) | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2016-09-14 10:00:01 +08:00
										 |  |  | 	// bucketnames[0].
 | 
					
						
							|  |  |  | 	// objectNames[0].
 | 
					
						
							|  |  |  | 	// uploadIds [0].
 | 
					
						
							|  |  |  | 	// Create bucket before initiating NewMultipartUpload.
 | 
					
						
							| 
									
										
										
										
											2020-06-13 11:04:01 +08:00
										 |  |  | 	err := obj.MakeBucketWithLocation(context.Background(), bucketName, BucketOptions{}) | 
					
						
							| 
									
										
										
										
											2016-09-14 10:00:01 +08:00
										 |  |  | 	if err != nil { | 
					
						
							|  |  |  | 		// Failed to create newbucket, abort.
 | 
					
						
							|  |  |  | 		t.Fatalf("%s : %s", instanceType, err.Error()) | 
					
						
							|  |  |  | 	} | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2016-10-22 23:57:12 +08:00
										 |  |  | 	// Test cases for signature-V2.
 | 
					
						
							|  |  |  | 	testCasesV2 := []struct { | 
					
						
							|  |  |  | 		expectedStatus int | 
					
						
							|  |  |  | 		accessKey      string | 
					
						
							|  |  |  | 		secretKey      string | 
					
						
							|  |  |  | 	}{ | 
					
						
							| 
									
										
										
										
											2016-12-27 02:21:23 +08:00
										 |  |  | 		{http.StatusForbidden, "invalidaccesskey", credentials.SecretKey}, | 
					
						
							|  |  |  | 		{http.StatusForbidden, credentials.AccessKey, "invalidsecretkey"}, | 
					
						
							|  |  |  | 		{http.StatusNoContent, credentials.AccessKey, credentials.SecretKey}, | 
					
						
							| 
									
										
										
										
											2016-10-22 23:57:12 +08:00
										 |  |  | 	} | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	for i, test := range testCasesV2 { | 
					
						
							|  |  |  | 		// initialize HTTP NewRecorder, this records any mutations to response writer inside the handler.
 | 
					
						
							|  |  |  | 		rec := httptest.NewRecorder() | 
					
						
							|  |  |  | 		req, perr := newPostRequestV2("", bucketName, "testobject", test.accessKey, test.secretKey) | 
					
						
							|  |  |  | 		if perr != nil { | 
					
						
							|  |  |  | 			t.Fatalf("Test %d: %s: Failed to create HTTP request for PostPolicyHandler: <ERROR> %v", i+1, instanceType, perr) | 
					
						
							|  |  |  | 		} | 
					
						
							|  |  |  | 		// Since `apiRouter` satisfies `http.Handler` it has a ServeHTTP to execute the logic ofthe handler.
 | 
					
						
							|  |  |  | 		// Call the ServeHTTP to execute the handler.
 | 
					
						
							|  |  |  | 		apiRouter.ServeHTTP(rec, req) | 
					
						
							|  |  |  | 		if rec.Code != test.expectedStatus { | 
					
						
							|  |  |  | 			t.Fatalf("Test %d: %s: Expected the response status to be `%d`, but instead found `%d`", i+1, instanceType, test.expectedStatus, rec.Code) | 
					
						
							|  |  |  | 		} | 
					
						
							|  |  |  | 	} | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	// Test cases for signature-V4.
 | 
					
						
							|  |  |  | 	testCasesV4 := []struct { | 
					
						
							| 
									
										
										
										
											2016-09-14 10:00:01 +08:00
										 |  |  | 		objectName         string | 
					
						
							|  |  |  | 		data               []byte | 
					
						
							| 
									
										
										
										
											2016-12-20 08:14:04 +08:00
										 |  |  | 		expectedHeaders    map[string]string | 
					
						
							| 
									
										
										
										
											2016-09-14 10:00:01 +08:00
										 |  |  | 		expectedRespStatus int | 
					
						
							| 
									
										
										
										
											2016-09-15 14:53:42 +08:00
										 |  |  | 		accessKey          string | 
					
						
							|  |  |  | 		secretKey          string | 
					
						
							|  |  |  | 		malformedBody      bool | 
					
						
							| 
									
										
										
										
											2016-09-14 10:00:01 +08:00
										 |  |  | 	}{ | 
					
						
							|  |  |  | 		// Success case.
 | 
					
						
							|  |  |  | 		{ | 
					
						
							|  |  |  | 			objectName:         "test", | 
					
						
							|  |  |  | 			data:               []byte("Hello, World"), | 
					
						
							|  |  |  | 			expectedRespStatus: http.StatusNoContent, | 
					
						
							| 
									
										
										
										
											2016-12-20 08:14:04 +08:00
										 |  |  | 			expectedHeaders:    map[string]string{"X-Amz-Meta-Uuid": "1234"}, | 
					
						
							| 
									
										
										
										
											2016-12-27 02:21:23 +08:00
										 |  |  | 			accessKey:          credentials.AccessKey, | 
					
						
							|  |  |  | 			secretKey:          credentials.SecretKey, | 
					
						
							| 
									
										
										
										
											2016-09-15 14:53:42 +08:00
										 |  |  | 			malformedBody:      false, | 
					
						
							| 
									
										
										
										
											2016-09-14 10:00:01 +08:00
										 |  |  | 		}, | 
					
						
							| 
									
										
										
										
											2016-09-15 14:53:42 +08:00
										 |  |  | 		// Bad case invalid request.
 | 
					
						
							| 
									
										
										
										
											2016-09-14 10:00:01 +08:00
										 |  |  | 		{ | 
					
						
							|  |  |  | 			objectName:         "test", | 
					
						
							|  |  |  | 			data:               []byte("Hello, World"), | 
					
						
							| 
									
										
										
										
											2020-08-24 13:06:22 +08:00
										 |  |  | 			expectedRespStatus: http.StatusForbidden, | 
					
						
							| 
									
										
										
										
											2016-09-15 14:53:42 +08:00
										 |  |  | 			accessKey:          "", | 
					
						
							|  |  |  | 			secretKey:          "", | 
					
						
							|  |  |  | 			malformedBody:      false, | 
					
						
							|  |  |  | 		}, | 
					
						
							|  |  |  | 		// Bad case malformed input.
 | 
					
						
							|  |  |  | 		{ | 
					
						
							|  |  |  | 			objectName:         "test", | 
					
						
							|  |  |  | 			data:               []byte("Hello, World"), | 
					
						
							|  |  |  | 			expectedRespStatus: http.StatusBadRequest, | 
					
						
							| 
									
										
										
										
											2016-12-27 02:21:23 +08:00
										 |  |  | 			accessKey:          credentials.AccessKey, | 
					
						
							|  |  |  | 			secretKey:          credentials.SecretKey, | 
					
						
							| 
									
										
										
										
											2016-09-15 14:53:42 +08:00
										 |  |  | 			malformedBody:      true, | 
					
						
							| 
									
										
										
										
											2016-09-14 10:00:01 +08:00
										 |  |  | 		}, | 
					
						
							|  |  |  | 	} | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2016-10-22 23:57:12 +08:00
										 |  |  | 	for i, testCase := range testCasesV4 { | 
					
						
							| 
									
										
										
										
											2016-09-14 10:00:01 +08:00
										 |  |  | 		// initialize HTTP NewRecorder, this records any mutations to response writer inside the handler.
 | 
					
						
							|  |  |  | 		rec := httptest.NewRecorder() | 
					
						
							| 
									
										
										
										
											2016-10-22 23:57:12 +08:00
										 |  |  | 		req, perr := newPostRequestV4("", bucketName, testCase.objectName, testCase.data, testCase.accessKey, testCase.secretKey) | 
					
						
							| 
									
										
										
										
											2016-09-14 10:00:01 +08:00
										 |  |  | 		if perr != nil { | 
					
						
							|  |  |  | 			t.Fatalf("Test %d: %s: Failed to create HTTP request for PostPolicyHandler: <ERROR> %v", i+1, instanceType, perr) | 
					
						
							|  |  |  | 		} | 
					
						
							| 
									
										
										
										
											2016-09-15 14:53:42 +08:00
										 |  |  | 		if testCase.malformedBody { | 
					
						
							|  |  |  | 			// Change the request body.
 | 
					
						
							|  |  |  | 			req.Body = ioutil.NopCloser(bytes.NewReader([]byte("Hello,"))) | 
					
						
							|  |  |  | 		} | 
					
						
							| 
									
										
										
										
											2016-09-14 10:00:01 +08:00
										 |  |  | 		// Since `apiRouter` satisfies `http.Handler` it has a ServeHTTP to execute the logic ofthe handler.
 | 
					
						
							|  |  |  | 		// Call the ServeHTTP to execute the handler.
 | 
					
						
							|  |  |  | 		apiRouter.ServeHTTP(rec, req) | 
					
						
							|  |  |  | 		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) | 
					
						
							|  |  |  | 		} | 
					
						
							| 
									
										
										
										
											2016-12-20 08:14:04 +08:00
										 |  |  | 		// When the operation is successful, check if sending metadata is successful too
 | 
					
						
							|  |  |  | 		if rec.Code == http.StatusNoContent { | 
					
						
							| 
									
										
										
										
											2018-09-11 00:42:43 +08:00
										 |  |  | 			objInfo, err := obj.GetObjectInfo(context.Background(), bucketName, testCase.objectName+"/upload.txt", opts) | 
					
						
							| 
									
										
										
										
											2016-12-20 08:14:04 +08:00
										 |  |  | 			if err != nil { | 
					
						
							|  |  |  | 				t.Error("Unexpected error: ", err) | 
					
						
							|  |  |  | 			} | 
					
						
							|  |  |  | 			for k, v := range testCase.expectedHeaders { | 
					
						
							|  |  |  | 				if objInfo.UserDefined[k] != v { | 
					
						
							|  |  |  | 					t.Errorf("Expected to have header %s with value %s, but found value `%s` instead", k, v, objInfo.UserDefined[k]) | 
					
						
							|  |  |  | 				} | 
					
						
							|  |  |  | 			} | 
					
						
							|  |  |  | 		} | 
					
						
							| 
									
										
										
										
											2016-09-14 10:00:01 +08:00
										 |  |  | 	} | 
					
						
							| 
									
										
										
										
											2016-11-21 20:15:26 +08:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2017-05-16 09:17:02 +08:00
										 |  |  | 	region := "us-east-1" | 
					
						
							| 
									
										
										
										
											2016-12-05 04:23:19 +08:00
										 |  |  | 	// Test cases for signature-V4.
 | 
					
						
							|  |  |  | 	testCasesV4BadData := []struct { | 
					
						
							|  |  |  | 		objectName         string | 
					
						
							|  |  |  | 		data               []byte | 
					
						
							|  |  |  | 		expectedRespStatus int | 
					
						
							|  |  |  | 		accessKey          string | 
					
						
							|  |  |  | 		secretKey          string | 
					
						
							|  |  |  | 		dates              []interface{} | 
					
						
							|  |  |  | 		policy             string | 
					
						
							|  |  |  | 		corruptedBase64    bool | 
					
						
							|  |  |  | 		corruptedMultipart bool | 
					
						
							|  |  |  | 	}{ | 
					
						
							|  |  |  | 		// Success case.
 | 
					
						
							|  |  |  | 		{ | 
					
						
							|  |  |  | 			objectName:         "test", | 
					
						
							|  |  |  | 			data:               []byte("Hello, World"), | 
					
						
							|  |  |  | 			expectedRespStatus: http.StatusNoContent, | 
					
						
							| 
									
										
										
										
											2016-12-27 02:21:23 +08:00
										 |  |  | 			accessKey:          credentials.AccessKey, | 
					
						
							|  |  |  | 			secretKey:          credentials.SecretKey, | 
					
						
							| 
									
										
										
										
											2020-05-26 07:51:32 +08:00
										 |  |  | 			dates:              []interface{}{curTimePlus5Min.Format(iso8601TimeFormat), curTime.Format(iso8601DateFormat), curTime.Format(yyyymmdd)}, | 
					
						
							| 
									
										
										
										
											2019-04-02 14:45:32 +08:00
										 |  |  | 			policy:             `{"expiration": "%s","conditions":[["eq", "$bucket", "` + bucketName + `"], ["starts-with", "$key", "test/"], ["eq", "$x-amz-algorithm", "AWS4-HMAC-SHA256"], ["eq", "$x-amz-date", "%s"], ["eq", "$x-amz-credential", "` + credentials.AccessKey + `/%s/us-east-1/s3/aws4_request"],["eq", "$x-amz-meta-uuid", "1234"]]}`, | 
					
						
							| 
									
										
										
										
											2021-10-28 22:35:28 +08:00
										 |  |  | 		}, | 
					
						
							|  |  |  | 		// Success case, big body.
 | 
					
						
							|  |  |  | 		{ | 
					
						
							|  |  |  | 			objectName:         "test", | 
					
						
							|  |  |  | 			data:               bytes.Repeat([]byte("a"), 10<<20), | 
					
						
							|  |  |  | 			expectedRespStatus: http.StatusNoContent, | 
					
						
							|  |  |  | 			accessKey:          credentials.AccessKey, | 
					
						
							|  |  |  | 			secretKey:          credentials.SecretKey, | 
					
						
							|  |  |  | 			dates:              []interface{}{curTimePlus5Min.Format(iso8601TimeFormat), curTime.Format(iso8601DateFormat), curTime.Format(yyyymmdd)}, | 
					
						
							|  |  |  | 			policy:             `{"expiration": "%s","conditions":[["eq", "$bucket", "` + bucketName + `"], ["starts-with", "$key", "test/"], ["eq", "$x-amz-algorithm", "AWS4-HMAC-SHA256"], ["eq", "$x-amz-date", "%s"], ["eq", "$x-amz-credential", "` + credentials.AccessKey + `/%s/us-east-1/s3/aws4_request"],["eq", "$x-amz-meta-uuid", "1234"]]}`, | 
					
						
							| 
									
										
										
										
											2016-12-05 04:23:19 +08:00
										 |  |  | 		}, | 
					
						
							|  |  |  | 		// Corrupted Base 64 result
 | 
					
						
							|  |  |  | 		{ | 
					
						
							|  |  |  | 			objectName:         "test", | 
					
						
							|  |  |  | 			data:               []byte("Hello, World"), | 
					
						
							|  |  |  | 			expectedRespStatus: http.StatusBadRequest, | 
					
						
							| 
									
										
										
										
											2016-12-27 02:21:23 +08:00
										 |  |  | 			accessKey:          credentials.AccessKey, | 
					
						
							|  |  |  | 			secretKey:          credentials.SecretKey, | 
					
						
							| 
									
										
										
										
											2020-05-26 07:51:32 +08:00
										 |  |  | 			dates:              []interface{}{curTimePlus5Min.Format(iso8601TimeFormat), curTime.Format(iso8601DateFormat), curTime.Format(yyyymmdd)}, | 
					
						
							| 
									
										
										
										
											2016-12-27 02:21:23 +08:00
										 |  |  | 			policy:             `{"expiration": "%s","conditions":[["eq", "$bucket", "` + bucketName + `"], ["starts-with", "$key", "test/"], ["eq", "$x-amz-algorithm", "AWS4-HMAC-SHA256"], ["eq", "$x-amz-date", "%s"], ["eq", "$x-amz-credential", "` + credentials.AccessKey + `/%s/us-east-1/s3/aws4_request"]]}`, | 
					
						
							| 
									
										
										
										
											2016-12-05 04:23:19 +08:00
										 |  |  | 			corruptedBase64:    true, | 
					
						
							|  |  |  | 		}, | 
					
						
							|  |  |  | 		// Corrupted Multipart body
 | 
					
						
							|  |  |  | 		{ | 
					
						
							|  |  |  | 			objectName:         "test", | 
					
						
							|  |  |  | 			data:               []byte("Hello, World"), | 
					
						
							|  |  |  | 			expectedRespStatus: http.StatusBadRequest, | 
					
						
							| 
									
										
										
										
											2016-12-27 02:21:23 +08:00
										 |  |  | 			accessKey:          credentials.AccessKey, | 
					
						
							|  |  |  | 			secretKey:          credentials.SecretKey, | 
					
						
							| 
									
										
										
										
											2020-05-26 07:51:32 +08:00
										 |  |  | 			dates:              []interface{}{curTimePlus5Min.Format(iso8601TimeFormat), curTime.Format(iso8601DateFormat), curTime.Format(yyyymmdd)}, | 
					
						
							| 
									
										
										
										
											2016-12-27 02:21:23 +08:00
										 |  |  | 			policy:             `{"expiration": "%s","conditions":[["eq", "$bucket", "` + bucketName + `"], ["starts-with", "$key", "test/"], ["eq", "$x-amz-algorithm", "AWS4-HMAC-SHA256"], ["eq", "$x-amz-date", "%s"], ["eq", "$x-amz-credential", "` + credentials.AccessKey + `/%s/us-east-1/s3/aws4_request"]]}`, | 
					
						
							| 
									
										
										
										
											2016-12-05 04:23:19 +08:00
										 |  |  | 			corruptedMultipart: true, | 
					
						
							|  |  |  | 		}, | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 		// Bad case invalid request.
 | 
					
						
							|  |  |  | 		{ | 
					
						
							|  |  |  | 			objectName:         "test", | 
					
						
							|  |  |  | 			data:               []byte("Hello, World"), | 
					
						
							| 
									
										
										
										
											2020-08-24 13:06:22 +08:00
										 |  |  | 			expectedRespStatus: http.StatusForbidden, | 
					
						
							| 
									
										
										
										
											2016-12-05 04:23:19 +08:00
										 |  |  | 			accessKey:          "", | 
					
						
							|  |  |  | 			secretKey:          "", | 
					
						
							|  |  |  | 			dates:              []interface{}{}, | 
					
						
							|  |  |  | 			policy:             ``, | 
					
						
							|  |  |  | 		}, | 
					
						
							|  |  |  | 		// Expired document
 | 
					
						
							|  |  |  | 		{ | 
					
						
							|  |  |  | 			objectName:         "test", | 
					
						
							|  |  |  | 			data:               []byte("Hello, World"), | 
					
						
							| 
									
										
										
										
											2019-03-06 04:10:47 +08:00
										 |  |  | 			expectedRespStatus: http.StatusForbidden, | 
					
						
							| 
									
										
										
										
											2016-12-27 02:21:23 +08:00
										 |  |  | 			accessKey:          credentials.AccessKey, | 
					
						
							|  |  |  | 			secretKey:          credentials.SecretKey, | 
					
						
							| 
									
										
										
										
											2020-05-26 07:51:32 +08:00
										 |  |  | 			dates:              []interface{}{curTime.Add(-1 * time.Minute * 5).Format(iso8601TimeFormat), curTime.Format(iso8601DateFormat), curTime.Format(yyyymmdd)}, | 
					
						
							| 
									
										
										
										
											2016-12-27 02:21:23 +08:00
										 |  |  | 			policy:             `{"expiration": "%s","conditions":[["eq", "$bucket", "` + bucketName + `"], ["starts-with", "$key", "test/"], ["eq", "$x-amz-algorithm", "AWS4-HMAC-SHA256"], ["eq", "$x-amz-date", "%s"], ["eq", "$x-amz-credential", "` + credentials.AccessKey + `/%s/us-east-1/s3/aws4_request"]]}`, | 
					
						
							| 
									
										
										
										
											2016-12-05 04:23:19 +08:00
										 |  |  | 		}, | 
					
						
							|  |  |  | 		// Corrupted policy document
 | 
					
						
							|  |  |  | 		{ | 
					
						
							|  |  |  | 			objectName:         "test", | 
					
						
							|  |  |  | 			data:               []byte("Hello, World"), | 
					
						
							| 
									
										
										
										
											2019-09-23 05:20:49 +08:00
										 |  |  | 			expectedRespStatus: http.StatusForbidden, | 
					
						
							| 
									
										
										
										
											2016-12-27 02:21:23 +08:00
										 |  |  | 			accessKey:          credentials.AccessKey, | 
					
						
							|  |  |  | 			secretKey:          credentials.SecretKey, | 
					
						
							| 
									
										
										
										
											2020-05-26 07:51:32 +08:00
										 |  |  | 			dates:              []interface{}{curTimePlus5Min.Format(iso8601TimeFormat), curTime.Format(iso8601DateFormat), curTime.Format(yyyymmdd)}, | 
					
						
							| 
									
										
										
										
											2016-12-05 04:23:19 +08:00
										 |  |  | 			policy:             `{"3/aws4_request"]]}`, | 
					
						
							|  |  |  | 		}, | 
					
						
							|  |  |  | 	} | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	for i, testCase := range testCasesV4BadData { | 
					
						
							|  |  |  | 		// initialize HTTP NewRecorder, this records any mutations to response writer inside the handler.
 | 
					
						
							|  |  |  | 		rec := httptest.NewRecorder() | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 		testCase.policy = fmt.Sprintf(testCase.policy, testCase.dates...) | 
					
						
							| 
									
										
										
										
											2016-12-21 01:32:17 +08:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2016-12-05 04:23:19 +08:00
										 |  |  | 		req, perr := newPostRequestV4Generic("", bucketName, testCase.objectName, testCase.data, testCase.accessKey, | 
					
						
							| 
									
										
										
										
											2017-05-16 09:17:02 +08:00
										 |  |  | 			testCase.secretKey, region, curTime, []byte(testCase.policy), nil, testCase.corruptedBase64, testCase.corruptedMultipart) | 
					
						
							| 
									
										
										
										
											2016-12-05 04:23:19 +08:00
										 |  |  | 		if perr != nil { | 
					
						
							|  |  |  | 			t.Fatalf("Test %d: %s: Failed to create HTTP request for PostPolicyHandler: <ERROR> %v", i+1, instanceType, perr) | 
					
						
							|  |  |  | 		} | 
					
						
							|  |  |  | 		// Since `apiRouter` satisfies `http.Handler` it has a ServeHTTP to execute the logic ofthe handler.
 | 
					
						
							|  |  |  | 		// Call the ServeHTTP to execute the handler.
 | 
					
						
							|  |  |  | 		apiRouter.ServeHTTP(rec, req) | 
					
						
							|  |  |  | 		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) | 
					
						
							|  |  |  | 		} | 
					
						
							|  |  |  | 	} | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2016-11-21 20:15:26 +08:00
										 |  |  | 	testCases2 := []struct { | 
					
						
							| 
									
										
										
										
											2017-02-03 02:45:00 +08:00
										 |  |  | 		objectName          string | 
					
						
							|  |  |  | 		data                []byte | 
					
						
							|  |  |  | 		expectedRespStatus  int | 
					
						
							|  |  |  | 		accessKey           string | 
					
						
							|  |  |  | 		secretKey           string | 
					
						
							|  |  |  | 		malformedBody       bool | 
					
						
							|  |  |  | 		ignoreContentLength bool | 
					
						
							| 
									
										
										
										
											2016-11-21 20:15:26 +08:00
										 |  |  | 	}{ | 
					
						
							|  |  |  | 		// Success case.
 | 
					
						
							|  |  |  | 		{ | 
					
						
							| 
									
										
										
										
											2017-02-03 02:45:00 +08:00
										 |  |  | 			objectName:          "test", | 
					
						
							|  |  |  | 			data:                bytes.Repeat([]byte("a"), 1025), | 
					
						
							|  |  |  | 			expectedRespStatus:  http.StatusNoContent, | 
					
						
							|  |  |  | 			accessKey:           credentials.AccessKey, | 
					
						
							|  |  |  | 			secretKey:           credentials.SecretKey, | 
					
						
							|  |  |  | 			malformedBody:       false, | 
					
						
							|  |  |  | 			ignoreContentLength: false, | 
					
						
							|  |  |  | 		}, | 
					
						
							|  |  |  | 		// Failed with Content-Length not specified.
 | 
					
						
							|  |  |  | 		{ | 
					
						
							|  |  |  | 			objectName:          "test", | 
					
						
							|  |  |  | 			data:                bytes.Repeat([]byte("a"), 1025), | 
					
						
							|  |  |  | 			expectedRespStatus:  http.StatusNoContent, | 
					
						
							|  |  |  | 			accessKey:           credentials.AccessKey, | 
					
						
							|  |  |  | 			secretKey:           credentials.SecretKey, | 
					
						
							|  |  |  | 			malformedBody:       false, | 
					
						
							|  |  |  | 			ignoreContentLength: true, | 
					
						
							| 
									
										
										
										
											2016-11-21 20:15:26 +08:00
										 |  |  | 		}, | 
					
						
							|  |  |  | 		// Failed with entity too small.
 | 
					
						
							|  |  |  | 		{ | 
					
						
							| 
									
										
										
										
											2017-02-03 02:45:00 +08:00
										 |  |  | 			objectName:          "test", | 
					
						
							|  |  |  | 			data:                bytes.Repeat([]byte("a"), 1023), | 
					
						
							|  |  |  | 			expectedRespStatus:  http.StatusBadRequest, | 
					
						
							|  |  |  | 			accessKey:           credentials.AccessKey, | 
					
						
							|  |  |  | 			secretKey:           credentials.SecretKey, | 
					
						
							|  |  |  | 			malformedBody:       false, | 
					
						
							|  |  |  | 			ignoreContentLength: false, | 
					
						
							| 
									
										
										
										
											2016-11-21 20:15:26 +08:00
										 |  |  | 		}, | 
					
						
							|  |  |  | 		// Failed with entity too large.
 | 
					
						
							|  |  |  | 		{ | 
					
						
							| 
									
										
										
										
											2017-02-03 02:45:00 +08:00
										 |  |  | 			objectName:          "test", | 
					
						
							|  |  |  | 			data:                bytes.Repeat([]byte("a"), (1*humanize.MiByte)+1), | 
					
						
							|  |  |  | 			expectedRespStatus:  http.StatusBadRequest, | 
					
						
							|  |  |  | 			accessKey:           credentials.AccessKey, | 
					
						
							|  |  |  | 			secretKey:           credentials.SecretKey, | 
					
						
							|  |  |  | 			malformedBody:       false, | 
					
						
							|  |  |  | 			ignoreContentLength: false, | 
					
						
							| 
									
										
										
										
											2016-11-21 20:15:26 +08:00
										 |  |  | 		}, | 
					
						
							|  |  |  | 	} | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	for i, testCase := range testCases2 { | 
					
						
							|  |  |  | 		// initialize HTTP NewRecorder, this records any mutations to response writer inside the handler.
 | 
					
						
							|  |  |  | 		rec := httptest.NewRecorder() | 
					
						
							| 
									
										
										
										
											2017-02-03 02:45:00 +08:00
										 |  |  | 		var req *http.Request | 
					
						
							|  |  |  | 		var perr error | 
					
						
							|  |  |  | 		if testCase.ignoreContentLength { | 
					
						
							|  |  |  | 			req, perr = newPostRequestV4("", bucketName, testCase.objectName, testCase.data, testCase.accessKey, testCase.secretKey) | 
					
						
							|  |  |  | 		} else { | 
					
						
							|  |  |  | 			req, perr = newPostRequestV4WithContentLength("", bucketName, testCase.objectName, testCase.data, testCase.accessKey, testCase.secretKey) | 
					
						
							|  |  |  | 		} | 
					
						
							| 
									
										
										
										
											2016-11-21 20:15:26 +08:00
										 |  |  | 		if perr != nil { | 
					
						
							|  |  |  | 			t.Fatalf("Test %d: %s: Failed to create HTTP request for PostPolicyHandler: <ERROR> %v", i+1, instanceType, perr) | 
					
						
							|  |  |  | 		} | 
					
						
							|  |  |  | 		// Since `apiRouter` satisfies `http.Handler` it has a ServeHTTP to execute the logic ofthe handler.
 | 
					
						
							|  |  |  | 		// Call the ServeHTTP to execute the handler.
 | 
					
						
							|  |  |  | 		apiRouter.ServeHTTP(rec, req) | 
					
						
							|  |  |  | 		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) | 
					
						
							|  |  |  | 		} | 
					
						
							|  |  |  | 	} | 
					
						
							| 
									
										
										
										
											2016-09-14 10:00:01 +08:00
										 |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2020-06-13 11:04:01 +08:00
										 |  |  | // Wrapper for calling TestPostPolicyBucketHandlerRedirect tests for both Erasure multiple disks and single node setup.
 | 
					
						
							| 
									
										
										
										
											2016-12-21 01:32:17 +08:00
										 |  |  | func TestPostPolicyBucketHandlerRedirect(t *testing.T) { | 
					
						
							|  |  |  | 	ExecObjectLayerTest(t, testPostPolicyBucketHandlerRedirect) | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | // testPostPolicyBucketHandlerRedirect tests POST Object when success_action_redirect is specified
 | 
					
						
							|  |  |  | func testPostPolicyBucketHandlerRedirect(obj ObjectLayer, instanceType string, t TestErrHandler) { | 
					
						
							| 
									
										
										
										
											2018-08-15 12:41:47 +08:00
										 |  |  | 	if err := newTestConfig(globalMinioDefaultRegion, obj); err != nil { | 
					
						
							| 
									
										
										
										
											2016-12-21 01:32:17 +08:00
										 |  |  | 		t.Fatalf("Initializing config.json failed") | 
					
						
							|  |  |  | 	} | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	// get random bucket name.
 | 
					
						
							|  |  |  | 	bucketName := getRandomBucketName() | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	// Key specified in Form data
 | 
					
						
							|  |  |  | 	keyName := "test/object" | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2018-09-11 00:42:43 +08:00
										 |  |  | 	var opts ObjectOptions | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2016-12-21 01:32:17 +08:00
										 |  |  | 	// The final name of the upload object
 | 
					
						
							|  |  |  | 	targetObj := keyName + "/upload.txt" | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	// The url of success_action_redirect field
 | 
					
						
							| 
									
										
										
										
											2017-03-14 05:41:13 +08:00
										 |  |  | 	redirectURL, err := url.Parse("http://www.google.com") | 
					
						
							|  |  |  | 	if err != nil { | 
					
						
							|  |  |  | 		t.Fatal(err) | 
					
						
							|  |  |  | 	} | 
					
						
							| 
									
										
										
										
											2016-12-21 01:32:17 +08:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2020-06-13 11:04:01 +08:00
										 |  |  | 	// Register the API end points with Erasure/FS object layer.
 | 
					
						
							| 
									
										
										
										
											2016-12-21 01:32:17 +08:00
										 |  |  | 	apiRouter := initTestAPIEndPoints(obj, []string{"PostPolicy"}) | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2019-10-23 13:59:13 +08:00
										 |  |  | 	credentials := globalActiveCred | 
					
						
							| 
									
										
										
										
											2016-12-21 01:32:17 +08:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2017-03-19 02:28:41 +08:00
										 |  |  | 	curTime := UTCNow() | 
					
						
							| 
									
										
										
										
											2016-12-21 01:32:17 +08:00
										 |  |  | 	curTimePlus5Min := curTime.Add(time.Minute * 5) | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2020-06-13 11:04:01 +08:00
										 |  |  | 	err = obj.MakeBucketWithLocation(context.Background(), bucketName, BucketOptions{}) | 
					
						
							| 
									
										
										
										
											2016-12-21 01:32:17 +08:00
										 |  |  | 	if err != nil { | 
					
						
							|  |  |  | 		// Failed to create newbucket, abort.
 | 
					
						
							|  |  |  | 		t.Fatalf("%s : %s", instanceType, err.Error()) | 
					
						
							|  |  |  | 	} | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	// initialize HTTP NewRecorder, this records any mutations to response writer inside the handler.
 | 
					
						
							|  |  |  | 	rec := httptest.NewRecorder() | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2020-05-26 07:51:32 +08:00
										 |  |  | 	dates := []interface{}{curTimePlus5Min.Format(iso8601TimeFormat), curTime.Format(iso8601DateFormat), curTime.Format(yyyymmdd)} | 
					
						
							| 
									
										
										
										
											2019-04-02 14:45:32 +08:00
										 |  |  | 	policy := `{"expiration": "%s","conditions":[["eq", "$bucket", "` + bucketName + `"], {"success_action_redirect":"` + redirectURL.String() + `"},["starts-with", "$key", "test/"], ["eq", "$x-amz-meta-uuid", "1234"], ["eq", "$x-amz-algorithm", "AWS4-HMAC-SHA256"], ["eq", "$x-amz-date", "%s"], ["eq", "$x-amz-credential", "` + credentials.AccessKey + `/%s/us-east-1/s3/aws4_request"]]}` | 
					
						
							| 
									
										
										
										
											2016-12-21 01:32:17 +08:00
										 |  |  | 
 | 
					
						
							|  |  |  | 	// Generate the final policy document
 | 
					
						
							|  |  |  | 	policy = fmt.Sprintf(policy, dates...) | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2017-05-16 09:17:02 +08:00
										 |  |  | 	region := "us-east-1" | 
					
						
							| 
									
										
										
										
											2016-12-21 01:32:17 +08:00
										 |  |  | 	// Create a new POST request with success_action_redirect field specified
 | 
					
						
							|  |  |  | 	req, perr := newPostRequestV4Generic("", bucketName, keyName, []byte("objData"), | 
					
						
							| 
									
										
										
										
											2017-05-16 09:17:02 +08:00
										 |  |  | 		credentials.AccessKey, credentials.SecretKey, region, curTime, | 
					
						
							| 
									
										
										
										
											2017-03-14 05:41:13 +08:00
										 |  |  | 		[]byte(policy), map[string]string{"success_action_redirect": redirectURL.String()}, false, false) | 
					
						
							| 
									
										
										
										
											2016-12-21 01:32:17 +08:00
										 |  |  | 
 | 
					
						
							|  |  |  | 	if perr != nil { | 
					
						
							|  |  |  | 		t.Fatalf("%s: Failed to create HTTP request for PostPolicyHandler: <ERROR> %v", instanceType, perr) | 
					
						
							|  |  |  | 	} | 
					
						
							|  |  |  | 	// Since `apiRouter` satisfies `http.Handler` it has a ServeHTTP to execute the logic ofthe handler.
 | 
					
						
							|  |  |  | 	// Call the ServeHTTP to execute the handler.
 | 
					
						
							|  |  |  | 	apiRouter.ServeHTTP(rec, req) | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	// Check the status code, which must be 303 because success_action_redirect is specified
 | 
					
						
							|  |  |  | 	if rec.Code != http.StatusSeeOther { | 
					
						
							|  |  |  | 		t.Errorf("%s: Expected the response status to be `%d`, but instead found `%d`", instanceType, http.StatusSeeOther, rec.Code) | 
					
						
							|  |  |  | 	} | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	// Get the uploaded object info
 | 
					
						
							| 
									
										
										
										
											2018-09-11 00:42:43 +08:00
										 |  |  | 	info, err := obj.GetObjectInfo(context.Background(), bucketName, targetObj, opts) | 
					
						
							| 
									
										
										
										
											2016-12-21 01:32:17 +08:00
										 |  |  | 	if err != nil { | 
					
						
							|  |  |  | 		t.Error("Unexpected error: ", err) | 
					
						
							|  |  |  | 	} | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2017-03-14 05:41:13 +08:00
										 |  |  | 	redirectURL.RawQuery = getRedirectPostRawQuery(info) | 
					
						
							|  |  |  | 	expectedLocation := redirectURL.String() | 
					
						
							| 
									
										
										
										
											2016-12-21 01:32:17 +08:00
										 |  |  | 
 | 
					
						
							|  |  |  | 	// Check the new location url
 | 
					
						
							| 
									
										
										
										
											2020-05-19 00:59:45 +08:00
										 |  |  | 	if rec.Header().Get("Location") != expectedLocation { | 
					
						
							|  |  |  | 		t.Errorf("Unexpected location, expected = %s, found = `%s`", rec.Header().Get("Location"), expectedLocation) | 
					
						
							| 
									
										
										
										
											2016-12-21 01:32:17 +08:00
										 |  |  | 	} | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2016-09-14 10:00:01 +08:00
										 |  |  | // postPresignSignatureV4 - presigned signature for PostPolicy requests.
 | 
					
						
							|  |  |  | func postPresignSignatureV4(policyBase64 string, t time.Time, secretAccessKey, location string) string { | 
					
						
							|  |  |  | 	// Get signining key.
 | 
					
						
							| 
									
										
										
										
											2019-02-28 09:46:55 +08:00
										 |  |  | 	signingkey := getSigningKey(secretAccessKey, t, location, "s3") | 
					
						
							| 
									
										
										
										
											2016-09-14 10:00:01 +08:00
										 |  |  | 	// Calculate signature.
 | 
					
						
							|  |  |  | 	signature := getSignature(signingkey, policyBase64) | 
					
						
							|  |  |  | 	return signature | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2016-10-22 23:57:12 +08:00
										 |  |  | func newPostRequestV2(endPoint, bucketName, objectName string, accessKey, secretKey string) (*http.Request, error) { | 
					
						
							|  |  |  | 	// Expire the request five minutes from now.
 | 
					
						
							| 
									
										
										
										
											2017-03-19 02:28:41 +08:00
										 |  |  | 	expirationTime := UTCNow().Add(time.Minute * 5) | 
					
						
							| 
									
										
										
										
											2016-10-22 23:57:12 +08:00
										 |  |  | 	// Create a new post policy.
 | 
					
						
							|  |  |  | 	policy := newPostPolicyBytesV2(bucketName, objectName, expirationTime) | 
					
						
							|  |  |  | 	// Only need the encoding.
 | 
					
						
							|  |  |  | 	encodedPolicy := base64.StdEncoding.EncodeToString(policy) | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	// Presign with V4 signature based on the policy.
 | 
					
						
							|  |  |  | 	signature := calculateSignatureV2(encodedPolicy, secretKey) | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	formData := map[string]string{ | 
					
						
							|  |  |  | 		"AWSAccessKeyId": accessKey, | 
					
						
							|  |  |  | 		"bucket":         bucketName, | 
					
						
							| 
									
										
										
										
											2016-12-05 04:23:19 +08:00
										 |  |  | 		"key":            objectName + "/${filename}", | 
					
						
							| 
									
										
										
										
											2016-10-22 23:57:12 +08:00
										 |  |  | 		"policy":         encodedPolicy, | 
					
						
							|  |  |  | 		"signature":      signature, | 
					
						
							|  |  |  | 	} | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	// Create the multipart form.
 | 
					
						
							|  |  |  | 	var buf bytes.Buffer | 
					
						
							|  |  |  | 	w := multipart.NewWriter(&buf) | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	// Set the normal formData
 | 
					
						
							|  |  |  | 	for k, v := range formData { | 
					
						
							|  |  |  | 		w.WriteField(k, v) | 
					
						
							|  |  |  | 	} | 
					
						
							|  |  |  | 	// Set the File formData
 | 
					
						
							| 
									
										
										
										
											2016-12-05 04:23:19 +08:00
										 |  |  | 	writer, err := w.CreateFormFile("file", "upload.txt") | 
					
						
							| 
									
										
										
										
											2016-10-22 23:57:12 +08:00
										 |  |  | 	if err != nil { | 
					
						
							|  |  |  | 		// return nil, err
 | 
					
						
							|  |  |  | 		return nil, err | 
					
						
							|  |  |  | 	} | 
					
						
							|  |  |  | 	writer.Write([]byte("hello world")) | 
					
						
							|  |  |  | 	// Close before creating the new request.
 | 
					
						
							|  |  |  | 	w.Close() | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	// Set the body equal to the created policy.
 | 
					
						
							|  |  |  | 	reader := bytes.NewReader(buf.Bytes()) | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2020-07-21 03:52:49 +08:00
										 |  |  | 	req, err := http.NewRequest(http.MethodPost, makeTestTargetURL(endPoint, bucketName, "", nil), reader) | 
					
						
							| 
									
										
										
										
											2016-10-22 23:57:12 +08:00
										 |  |  | 	if err != nil { | 
					
						
							|  |  |  | 		return nil, err | 
					
						
							|  |  |  | 	} | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	// Set form content-type.
 | 
					
						
							|  |  |  | 	req.Header.Set("Content-Type", w.FormDataContentType()) | 
					
						
							|  |  |  | 	return req, nil | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2017-05-16 09:17:02 +08:00
										 |  |  | func buildGenericPolicy(t time.Time, accessKey, region, bucketName, objectName string, contentLengthRange bool) []byte { | 
					
						
							| 
									
										
										
										
											2016-09-14 10:00:01 +08:00
										 |  |  | 	// Expire the request five minutes from now.
 | 
					
						
							|  |  |  | 	expirationTime := t.Add(time.Minute * 5) | 
					
						
							| 
									
										
										
										
											2016-12-05 04:23:19 +08:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2017-05-16 09:17:02 +08:00
										 |  |  | 	credStr := getCredentialString(accessKey, region, t) | 
					
						
							| 
									
										
										
										
											2016-09-14 10:00:01 +08:00
										 |  |  | 	// Create a new post policy.
 | 
					
						
							| 
									
										
										
										
											2016-10-22 23:57:12 +08:00
										 |  |  | 	policy := newPostPolicyBytesV4(credStr, bucketName, objectName, expirationTime) | 
					
						
							| 
									
										
										
										
											2016-11-21 20:15:26 +08:00
										 |  |  | 	if contentLengthRange { | 
					
						
							|  |  |  | 		policy = newPostPolicyBytesV4WithContentRange(credStr, bucketName, objectName, expirationTime) | 
					
						
							|  |  |  | 	} | 
					
						
							| 
									
										
										
										
											2016-12-05 04:23:19 +08:00
										 |  |  | 	return policy | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2017-05-16 09:17:02 +08:00
										 |  |  | func newPostRequestV4Generic(endPoint, bucketName, objectName string, objData []byte, accessKey, secretKey string, region string, | 
					
						
							| 
									
										
										
										
											2022-04-14 03:00:11 +08:00
										 |  |  | 	t time.Time, policy []byte, addFormData map[string]string, corruptedB64 bool, corruptedMultipart bool, | 
					
						
							|  |  |  | ) (*http.Request, error) { | 
					
						
							| 
									
										
										
										
											2016-12-05 04:23:19 +08:00
										 |  |  | 	// Get the user credential.
 | 
					
						
							| 
									
										
										
										
											2017-05-16 09:17:02 +08:00
										 |  |  | 	credStr := getCredentialString(accessKey, region, t) | 
					
						
							| 
									
										
										
										
											2016-12-05 04:23:19 +08:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2016-09-14 10:00:01 +08:00
										 |  |  | 	// Only need the encoding.
 | 
					
						
							|  |  |  | 	encodedPolicy := base64.StdEncoding.EncodeToString(policy) | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2016-12-05 04:23:19 +08:00
										 |  |  | 	if corruptedB64 { | 
					
						
							|  |  |  | 		encodedPolicy = "%!~&" + encodedPolicy | 
					
						
							|  |  |  | 	} | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2016-09-15 14:53:42 +08:00
										 |  |  | 	// Presign with V4 signature based on the policy.
 | 
					
						
							| 
									
										
										
										
											2017-05-16 09:17:02 +08:00
										 |  |  | 	signature := postPresignSignatureV4(encodedPolicy, t, secretKey, region) | 
					
						
							| 
									
										
										
										
											2016-09-15 14:53:42 +08:00
										 |  |  | 
 | 
					
						
							|  |  |  | 	formData := map[string]string{ | 
					
						
							|  |  |  | 		"bucket":           bucketName, | 
					
						
							| 
									
										
										
										
											2016-12-05 04:23:19 +08:00
										 |  |  | 		"key":              objectName + "/${filename}", | 
					
						
							| 
									
										
										
										
											2016-09-15 14:53:42 +08:00
										 |  |  | 		"x-amz-credential": credStr, | 
					
						
							|  |  |  | 		"policy":           encodedPolicy, | 
					
						
							|  |  |  | 		"x-amz-signature":  signature, | 
					
						
							|  |  |  | 		"x-amz-date":       t.Format(iso8601DateFormat), | 
					
						
							|  |  |  | 		"x-amz-algorithm":  "AWS4-HMAC-SHA256", | 
					
						
							| 
									
										
										
										
											2016-12-20 08:14:04 +08:00
										 |  |  | 		"x-amz-meta-uuid":  "1234", | 
					
						
							|  |  |  | 		"Content-Encoding": "gzip", | 
					
						
							| 
									
										
										
										
											2016-09-14 10:00:01 +08:00
										 |  |  | 	} | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2016-12-21 01:32:17 +08:00
										 |  |  | 	// Add form data
 | 
					
						
							|  |  |  | 	for k, v := range addFormData { | 
					
						
							|  |  |  | 		formData[k] = v | 
					
						
							|  |  |  | 	} | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2016-09-14 10:00:01 +08:00
										 |  |  | 	// Create the multipart form.
 | 
					
						
							|  |  |  | 	var buf bytes.Buffer | 
					
						
							|  |  |  | 	w := multipart.NewWriter(&buf) | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	// Set the normal formData
 | 
					
						
							|  |  |  | 	for k, v := range formData { | 
					
						
							|  |  |  | 		w.WriteField(k, v) | 
					
						
							|  |  |  | 	} | 
					
						
							| 
									
										
										
										
											2016-12-05 04:23:19 +08:00
										 |  |  | 	// Set the File formData but don't if we want send an incomplete multipart request
 | 
					
						
							|  |  |  | 	if !corruptedMultipart { | 
					
						
							|  |  |  | 		writer, err := w.CreateFormFile("file", "upload.txt") | 
					
						
							|  |  |  | 		if err != nil { | 
					
						
							|  |  |  | 			// return nil, err
 | 
					
						
							|  |  |  | 			return nil, err | 
					
						
							|  |  |  | 		} | 
					
						
							|  |  |  | 		writer.Write(objData) | 
					
						
							|  |  |  | 		// Close before creating the new request.
 | 
					
						
							|  |  |  | 		w.Close() | 
					
						
							| 
									
										
										
										
											2016-09-14 10:00:01 +08:00
										 |  |  | 	} | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	// Set the body equal to the created policy.
 | 
					
						
							|  |  |  | 	reader := bytes.NewReader(buf.Bytes()) | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2020-07-21 03:52:49 +08:00
										 |  |  | 	req, err := http.NewRequest(http.MethodPost, makeTestTargetURL(endPoint, bucketName, "", nil), reader) | 
					
						
							| 
									
										
										
										
											2016-09-14 10:00:01 +08:00
										 |  |  | 	if err != nil { | 
					
						
							|  |  |  | 		return nil, err | 
					
						
							|  |  |  | 	} | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	// Set form content-type.
 | 
					
						
							|  |  |  | 	req.Header.Set("Content-Type", w.FormDataContentType()) | 
					
						
							|  |  |  | 	return req, nil | 
					
						
							|  |  |  | } | 
					
						
							| 
									
										
										
										
											2016-11-21 20:15:26 +08:00
										 |  |  | 
 | 
					
						
							|  |  |  | func newPostRequestV4WithContentLength(endPoint, bucketName, objectName string, objData []byte, accessKey, secretKey string) (*http.Request, error) { | 
					
						
							| 
									
										
										
										
											2017-03-19 02:28:41 +08:00
										 |  |  | 	t := UTCNow() | 
					
						
							| 
									
										
										
										
											2017-05-16 09:17:02 +08:00
										 |  |  | 	region := "us-east-1" | 
					
						
							|  |  |  | 	policy := buildGenericPolicy(t, accessKey, region, bucketName, objectName, true) | 
					
						
							|  |  |  | 	return newPostRequestV4Generic(endPoint, bucketName, objectName, objData, accessKey, secretKey, region, t, policy, nil, false, false) | 
					
						
							| 
									
										
										
										
											2016-11-21 20:15:26 +08:00
										 |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | func newPostRequestV4(endPoint, bucketName, objectName string, objData []byte, accessKey, secretKey string) (*http.Request, error) { | 
					
						
							| 
									
										
										
										
											2017-03-19 02:28:41 +08:00
										 |  |  | 	t := UTCNow() | 
					
						
							| 
									
										
										
										
											2017-05-16 09:17:02 +08:00
										 |  |  | 	region := "us-east-1" | 
					
						
							|  |  |  | 	policy := buildGenericPolicy(t, accessKey, region, bucketName, objectName, false) | 
					
						
							|  |  |  | 	return newPostRequestV4Generic(endPoint, bucketName, objectName, objData, accessKey, secretKey, region, t, policy, nil, false, false) | 
					
						
							| 
									
										
										
										
											2016-11-21 20:15:26 +08:00
										 |  |  | } |