storj.io/minio@v0.0.0-20230509071714-0cbc90f649b1/cmd/signature-v4-utils_test.go (about) 1 /* 2 * MinIO Cloud Storage, (C) 2015, 2016, 2017 MinIO, Inc. 3 * 4 * Licensed under the Apache License, Version 2.0 (the "License"); 5 * you may not use this file except in compliance with the License. 6 * You may obtain a copy of the License at 7 * 8 * http://www.apache.org/licenses/LICENSE-2.0 9 * 10 * Unless required by applicable law or agreed to in writing, software 11 * distributed under the License is distributed on an "AS IS" BASIS, 12 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 * See the License for the specific language governing permissions and 14 * limitations under the License. 15 */ 16 17 package cmd 18 19 import ( 20 "net/http" 21 "testing" 22 23 xhttp "storj.io/minio/cmd/http" 24 ) 25 26 // TestSkipContentSha256Cksum - Test validate the logic which decides whether 27 // to skip checksum validation based on the request header. 28 func TestSkipContentSha256Cksum(t *testing.T) { 29 testCases := []struct { 30 inputHeaderKey string 31 inputHeaderValue string 32 33 inputQueryKey string 34 inputQueryValue string 35 36 expectedResult bool 37 }{ 38 // Test case - 1. 39 // Test case with "X-Amz-Content-Sha256" header set, but to empty value but we can't skip. 40 {"X-Amz-Content-Sha256", "", "", "", false}, 41 42 // Test case - 2. 43 // Test case with "X-Amz-Content-Sha256" not set so we can skip. 44 {"", "", "", "", true}, 45 46 // Test case - 3. 47 // Test case with "X-Amz-Content-Sha256" header set to "UNSIGNED-PAYLOAD" 48 // When "X-Amz-Content-Sha256" header is set to "UNSIGNED-PAYLOAD", validation of content sha256 has to be skipped. 49 {"X-Amz-Content-Sha256", unsignedPayload, "X-Amz-Credential", "", true}, 50 51 // Test case - 4. 52 // Enabling PreSigned Signature v4, but X-Amz-Content-Sha256 not set has to be skipped. 53 {"", "", "X-Amz-Credential", "", true}, 54 55 // Test case - 5. 56 // Enabling PreSigned Signature v4, but X-Amz-Content-Sha256 set and its not UNSIGNED-PAYLOAD, we shouldn't skip. 57 {"X-Amz-Content-Sha256", "somevalue", "X-Amz-Credential", "", false}, 58 59 // Test case - 6. 60 // Test case with "X-Amz-Content-Sha256" header set to "UNSIGNED-PAYLOAD" and its not presigned, we should skip. 61 {"X-Amz-Content-Sha256", unsignedPayload, "", "", true}, 62 63 // Test case - 7. 64 // "X-Amz-Content-Sha256" not set and PreSigned Signature v4 not enabled, sha256 checksum calculation is not skipped. 65 {"", "", "X-Amz-Credential", "", true}, 66 67 // Test case - 8. 68 // "X-Amz-Content-Sha256" has a proper value cannot skip. 69 {"X-Amz-Content-Sha256", "somevalue", "", "", false}, 70 } 71 72 for i, testCase := range testCases { 73 // creating an input HTTP request. 74 // Only the headers are relevant for this particular test. 75 inputReq, err := http.NewRequest(http.MethodGet, "http://example.com", nil) 76 if err != nil { 77 t.Fatalf("Error initializing input HTTP request: %v", err) 78 } 79 if testCase.inputQueryKey != "" { 80 q := inputReq.URL.Query() 81 q.Add(testCase.inputQueryKey, testCase.inputQueryValue) 82 if testCase.inputHeaderKey != "" { 83 q.Add(testCase.inputHeaderKey, testCase.inputHeaderValue) 84 } 85 inputReq.URL.RawQuery = q.Encode() 86 } else { 87 if testCase.inputHeaderKey != "" { 88 inputReq.Header.Set(testCase.inputHeaderKey, testCase.inputHeaderValue) 89 } 90 } 91 92 actualResult := skipContentSha256Cksum(inputReq) 93 if testCase.expectedResult != actualResult { 94 t.Errorf("Test %d: Expected the result to `%v`, but instead got `%v`", i+1, testCase.expectedResult, actualResult) 95 } 96 } 97 } 98 99 // TestIsValidRegion - Tests validate the comparison logic for asserting whether the region from http request is valid. 100 func TestIsValidRegion(t *testing.T) { 101 testCases := []struct { 102 inputReqRegion string 103 inputConfRegion string 104 105 expectedResult bool 106 }{ 107 108 {"", "", true}, 109 {globalMinioDefaultRegion, "", true}, 110 {globalMinioDefaultRegion, "US", true}, 111 {"us-west-1", "US", false}, 112 {"us-west-1", "us-west-1", true}, 113 // "US" was old naming convention for 'us-east-1'. 114 {"US", "US", true}, 115 } 116 117 for i, testCase := range testCases { 118 actualResult := isValidRegion(testCase.inputReqRegion, testCase.inputConfRegion) 119 if testCase.expectedResult != actualResult { 120 t.Errorf("Test %d: Expected the result to `%v`, but instead got `%v`", i+1, testCase.expectedResult, actualResult) 121 } 122 } 123 } 124 125 // TestExtractSignedHeaders - Tests validate extraction of signed headers using list of signed header keys. 126 func TestExtractSignedHeaders(t *testing.T) { 127 signedHeaders := []string{"host", "x-amz-content-sha256", "x-amz-date", "transfer-encoding"} 128 129 // If the `expect` key exists in the signed headers then golang server would have stripped out the value, expecting the `expect` header set to `100-continue` in the result. 130 signedHeaders = append(signedHeaders, "expect") 131 // expected header values. 132 expectedHost := "play.min.io:9000" 133 expectedContentSha256 := "1234abcd" 134 expectedTime := UTCNow().Format(iso8601Format) 135 expectedTransferEncoding := "gzip" 136 expectedExpect := "100-continue" 137 138 r, err := http.NewRequest(http.MethodGet, "http://play.min.io:9000", nil) 139 if err != nil { 140 t.Fatal("Unable to create http.Request :", err) 141 } 142 r.TransferEncoding = []string{expectedTransferEncoding} 143 144 // Creating input http header. 145 inputHeader := r.Header 146 inputHeader.Set("x-amz-content-sha256", expectedContentSha256) 147 inputHeader.Set("x-amz-date", expectedTime) 148 // calling the function being tested. 149 extractedSignedHeaders, errCode := extractSignedHeaders(signedHeaders, r) 150 if errCode != ErrNone { 151 t.Fatalf("Expected the APIErrorCode to be %d, but got %d", ErrNone, errCode) 152 } 153 154 inputQuery := r.URL.Query() 155 // case where some headers need to get from request query 156 signedHeaders = append(signedHeaders, "x-amz-server-side-encryption") 157 // expect to fail with `ErrUnsignedHeaders` because couldn't find some header 158 _, errCode = extractSignedHeaders(signedHeaders, r) 159 if errCode != ErrUnsignedHeaders { 160 t.Fatalf("Expected the APIErrorCode to %d, but got %d", ErrUnsignedHeaders, errCode) 161 } 162 // set headers value through Get parameter 163 inputQuery.Add("x-amz-server-side-encryption", xhttp.AmzEncryptionAES) 164 r.URL.RawQuery = inputQuery.Encode() 165 _, errCode = extractSignedHeaders(signedHeaders, r) 166 if errCode != ErrNone { 167 t.Fatalf("Expected the APIErrorCode to be %d, but got %d", ErrNone, errCode) 168 } 169 170 // "x-amz-content-sha256" header value from the extracted result. 171 extractedContentSha256 := extractedSignedHeaders.Get("x-amz-content-sha256") 172 // "host" header value from the extracted result. 173 extractedHost := extractedSignedHeaders.Get("host") 174 // "x-amz-date" header from the extracted result. 175 extractedDate := extractedSignedHeaders.Get("x-amz-date") 176 // extracted `expect` header. 177 extractedExpect := extractedSignedHeaders.Get("expect") 178 179 extractedTransferEncoding := extractedSignedHeaders.Get("transfer-encoding") 180 181 if expectedHost != extractedHost { 182 t.Errorf("host header mismatch: expected `%s`, got `%s`", expectedHost, extractedHost) 183 } 184 // assert the result with the expected value. 185 if expectedContentSha256 != extractedContentSha256 { 186 t.Errorf("x-amz-content-sha256 header mismatch: expected `%s`, got `%s`", expectedContentSha256, extractedContentSha256) 187 } 188 if expectedTime != extractedDate { 189 t.Errorf("x-amz-date header mismatch: expected `%s`, got `%s`", expectedTime, extractedDate) 190 } 191 if extractedTransferEncoding != expectedTransferEncoding { 192 t.Errorf("transfer-encoding mismatch: expected %s, got %s", expectedTransferEncoding, extractedTransferEncoding) 193 } 194 195 // Since the list of signed headers value contained `expect`, the default value of `100-continue` will be added to extracted signed headers. 196 if extractedExpect != expectedExpect { 197 t.Errorf("expect header incorrect value: expected `%s`, got `%s`", expectedExpect, extractedExpect) 198 } 199 200 // case where the headers don't contain the one of the signed header in the signed headers list. 201 signedHeaders = append(signedHeaders, "X-Amz-Credential") 202 // expected to fail with `ErrUnsignedHeaders`. 203 _, errCode = extractSignedHeaders(signedHeaders, r) 204 if errCode != ErrUnsignedHeaders { 205 t.Fatalf("Expected the APIErrorCode to %d, but got %d", ErrUnsignedHeaders, errCode) 206 } 207 208 // case where the list of signed headers doesn't contain the host field. 209 signedHeaders = signedHeaders[2:5] 210 // expected to fail with `ErrUnsignedHeaders`. 211 _, errCode = extractSignedHeaders(signedHeaders, r) 212 if errCode != ErrUnsignedHeaders { 213 t.Fatalf("Expected the APIErrorCode to %d, but got %d", ErrUnsignedHeaders, errCode) 214 } 215 } 216 217 // TestSignV4TrimAll - tests the logic of TrimAll() function 218 func TestSignV4TrimAll(t *testing.T) { 219 testCases := []struct { 220 // Input. 221 inputStr string 222 // Expected result. 223 result string 224 }{ 225 {"本語", "本語"}, 226 {" abc ", "abc"}, 227 {" a b ", "a b"}, 228 {"a b ", "a b"}, 229 {"a b", "a b"}, 230 {"a b", "a b"}, 231 {" a b c ", "a b c"}, 232 {"a \t b c ", "a b c"}, 233 {"\"a \t b c ", "\"a b c"}, 234 {" \t\n\u000b\r\fa \t\n\u000b\r\f b \t\n\u000b\r\f c \t\n\u000b\r\f", "a b c"}, 235 } 236 237 // Tests generated values from url encoded name. 238 for i, testCase := range testCases { 239 result := signV4TrimAll(testCase.inputStr) 240 if testCase.result != result { 241 t.Errorf("Test %d: Expected signV4TrimAll result to be \"%s\", but found it to be \"%s\" instead", i+1, testCase.result, result) 242 } 243 } 244 } 245 246 // Test getContentSha256Cksum 247 func TestGetContentSha256Cksum(t *testing.T) { 248 testCases := []struct { 249 h string // header SHA256 250 q string // query SHA256 251 expected string // expected SHA256 252 }{ 253 {"shastring", "", "shastring"}, 254 {emptySHA256, "", emptySHA256}, 255 {"", "", emptySHA256}, 256 {"", "X-Amz-Credential=random", unsignedPayload}, 257 {"", "X-Amz-Credential=random&X-Amz-Content-Sha256=" + unsignedPayload, unsignedPayload}, 258 {"", "X-Amz-Credential=random&X-Amz-Content-Sha256=shastring", "shastring"}, 259 } 260 261 for i, testCase := range testCases { 262 r, err := http.NewRequest(http.MethodGet, "http://localhost/?"+testCase.q, nil) 263 if err != nil { 264 t.Fatal(err) 265 } 266 if testCase.h != "" { 267 r.Header.Set("x-amz-content-sha256", testCase.h) 268 } 269 got := getContentSha256Cksum(r, serviceS3) 270 if got != testCase.expected { 271 t.Errorf("Test %d: got:%s expected:%s", i+1, got, testCase.expected) 272 } 273 } 274 }