storj.io/minio@v0.0.0-20230509071714-0cbc90f649b1/cmd/signature-v4-parser_test.go (about) 1 /* 2 * MinIO Cloud Storage, (C) 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/url" 21 "strconv" 22 "strings" 23 "testing" 24 "time" 25 ) 26 27 // generates credential string from its fields. 28 func generateCredentialStr(accessKey, date, region, service, requestVersion string) string { 29 return "Credential=" + joinWithSlash(accessKey, date, region, service, requestVersion) 30 } 31 32 // joins the argument strings with a '/' and returns it. 33 func joinWithSlash(accessKey, date, region, service, requestVersion string) string { 34 return strings.Join([]string{ 35 accessKey, 36 date, 37 region, 38 service, 39 requestVersion}, SlashSeparator) 40 } 41 42 // generate CredentialHeader from its fields. 43 func generateCredentials(t *testing.T, accessKey string, date string, region, service, requestVersion string) credentialHeader { 44 cred := credentialHeader{ 45 accessKey: accessKey, 46 } 47 parsedDate, err := time.Parse(yyyymmdd, date) 48 if err != nil { 49 t.Fatalf("Failed to parse date") 50 } 51 cred.scope.date = parsedDate 52 cred.scope.region = region 53 cred.scope.service = service 54 cred.scope.request = requestVersion 55 56 return cred 57 } 58 59 // validates the credential fields against the expected credential. 60 func validateCredentialfields(t *testing.T, testNum int, expectedCredentials credentialHeader, actualCredential credentialHeader) { 61 62 if expectedCredentials.accessKey != actualCredential.accessKey { 63 t.Errorf("Test %d: AccessKey mismatch: Expected \"%s\", got \"%s\"", testNum, expectedCredentials.accessKey, actualCredential.accessKey) 64 } 65 if !expectedCredentials.scope.date.Equal(actualCredential.scope.date) { 66 t.Errorf("Test %d: Date mismatch:Expected \"%s\", got \"%s\"", testNum, expectedCredentials.scope.date, actualCredential.scope.date) 67 } 68 if expectedCredentials.scope.region != actualCredential.scope.region { 69 t.Errorf("Test %d: region mismatch:Expected \"%s\", got \"%s\"", testNum, expectedCredentials.scope.region, actualCredential.scope.region) 70 } 71 if expectedCredentials.scope.service != actualCredential.scope.service { 72 t.Errorf("Test %d: service mismatch:Expected \"%s\", got \"%s\"", testNum, expectedCredentials.scope.service, actualCredential.scope.service) 73 } 74 75 if expectedCredentials.scope.request != actualCredential.scope.request { 76 t.Errorf("Test %d: scope request mismatch:Expected \"%s\", got \"%s\"", testNum, expectedCredentials.scope.request, actualCredential.scope.request) 77 } 78 } 79 80 // TestParseCredentialHeader - validates the format validator and extractor for the Credential header in an aws v4 request. 81 // A valid format of creadential should be of the following format. 82 // Credential = accessKey + SlashSeparator+ scope 83 // where scope = string.Join([]string{ currTime.Format(yyyymmdd), 84 // globalMinioDefaultRegion, 85 // "s3", 86 // "aws4_request", 87 // },SlashSeparator) 88 func TestParseCredentialHeader(t *testing.T) { 89 90 sampleTimeStr := UTCNow().Format(yyyymmdd) 91 92 testCases := []struct { 93 inputCredentialStr string 94 expectedCredentials credentialHeader 95 expectedErrCode APIErrorCode 96 }{ 97 // Test Case - 1. 98 // Test case with no '=' in te inputCredentialStr. 99 { 100 inputCredentialStr: "Credential", 101 expectedCredentials: credentialHeader{}, 102 expectedErrCode: ErrMissingFields, 103 }, 104 // Test Case - 2. 105 // Test case with no "Credential" string in te inputCredentialStr. 106 { 107 inputCredentialStr: "Cred=", 108 expectedCredentials: credentialHeader{}, 109 expectedErrCode: ErrMissingCredTag, 110 }, 111 // Test Case - 3. 112 // Test case with malformed credentials. 113 { 114 inputCredentialStr: "Credential=abc", 115 expectedCredentials: credentialHeader{}, 116 expectedErrCode: ErrCredMalformed, 117 }, 118 // Test Case - 4. 119 // Test case with AccessKey of length 2. 120 { 121 inputCredentialStr: generateCredentialStr( 122 "^#", 123 UTCNow().Format(yyyymmdd), 124 "ABCD", 125 "ABCD", 126 "ABCD"), 127 expectedCredentials: credentialHeader{}, 128 expectedErrCode: ErrInvalidAccessKeyID, 129 }, 130 // Test Case - 5. 131 // Test case with invalid date format date. 132 // a valid date format for credentials is "yyyymmdd". 133 { 134 inputCredentialStr: generateCredentialStr( 135 "Z7IXGOO6BZ0REAN1Q26I", 136 UTCNow().String(), 137 "ABCD", 138 "ABCD", 139 "ABCD"), 140 expectedCredentials: credentialHeader{}, 141 expectedErrCode: ErrMalformedCredentialDate, 142 }, 143 // Test Case - 6. 144 // Test case with invalid service. 145 // "s3" is the valid service string. 146 { 147 inputCredentialStr: generateCredentialStr( 148 "Z7IXGOO6BZ0REAN1Q26I", 149 UTCNow().Format(yyyymmdd), 150 "us-west-1", 151 "ABCD", 152 "ABCD"), 153 expectedCredentials: credentialHeader{}, 154 expectedErrCode: ErrInvalidServiceS3, 155 }, 156 // Test Case - 7. 157 // Test case with invalid region. 158 { 159 inputCredentialStr: generateCredentialStr( 160 "Z7IXGOO6BZ0REAN1Q26I", 161 UTCNow().Format(yyyymmdd), 162 "us-west-2", 163 "s3", 164 "aws4_request"), 165 expectedCredentials: credentialHeader{}, 166 expectedErrCode: ErrAuthorizationHeaderMalformed, 167 }, 168 // Test Case - 8. 169 // Test case with invalid request version. 170 // "aws4_request" is the valid request version. 171 { 172 inputCredentialStr: generateCredentialStr( 173 "Z7IXGOO6BZ0REAN1Q26I", 174 UTCNow().Format(yyyymmdd), 175 "us-west-1", 176 "s3", 177 "ABCD"), 178 expectedCredentials: credentialHeader{}, 179 expectedErrCode: ErrInvalidRequestVersion, 180 }, 181 // Test Case - 9. 182 // Test case with right inputs. Expected to return a valid CredentialHeader. 183 // "aws4_request" is the valid request version. 184 { 185 inputCredentialStr: generateCredentialStr( 186 "Z7IXGOO6BZ0REAN1Q26I", 187 sampleTimeStr, 188 "us-west-1", 189 "s3", 190 "aws4_request"), 191 expectedCredentials: generateCredentials( 192 t, 193 "Z7IXGOO6BZ0REAN1Q26I", 194 sampleTimeStr, 195 "us-west-1", 196 "s3", 197 "aws4_request"), 198 expectedErrCode: ErrNone, 199 }, 200 // Test Case - 10. 201 // Test case with right inputs -> AccessKey contains `/`. See minio/#6443 202 // "aws4_request" is the valid request version. 203 { 204 inputCredentialStr: generateCredentialStr( 205 "LOCALKEY/DEV/1", 206 sampleTimeStr, 207 "us-west-1", 208 "s3", 209 "aws4_request"), 210 expectedCredentials: generateCredentials( 211 t, 212 "LOCALKEY/DEV/1", 213 sampleTimeStr, 214 "us-west-1", 215 "s3", 216 "aws4_request"), 217 expectedErrCode: ErrNone, 218 }, 219 // Test Case - 11. 220 // Test case with right inputs -> AccessKey contains `=`. See minio/#7376 221 // "aws4_request" is the valid request version. 222 { 223 inputCredentialStr: generateCredentialStr( 224 "LOCALKEY/DEV/1=", 225 sampleTimeStr, 226 "us-west-1", 227 "s3", 228 "aws4_request"), 229 expectedCredentials: generateCredentials( 230 t, 231 "LOCALKEY/DEV/1=", 232 sampleTimeStr, 233 "us-west-1", 234 "s3", 235 "aws4_request"), 236 expectedErrCode: ErrNone, 237 }, 238 } 239 240 for i, testCase := range testCases { 241 actualCredential, actualErrCode := parseCredentialHeader(testCase.inputCredentialStr, "us-west-1", "s3") 242 // validating the credential fields. 243 if testCase.expectedErrCode != actualErrCode { 244 t.Fatalf("Test %d: Expected the APIErrCode to be %s, got %s", i+1, errorCodes[testCase.expectedErrCode].Code, errorCodes[actualErrCode].Code) 245 } 246 if actualErrCode == ErrNone { 247 validateCredentialfields(t, i+1, testCase.expectedCredentials, actualCredential) 248 } 249 } 250 } 251 252 // TestParseSignature - validates the logic for extracting the signature string. 253 func TestParseSignature(t *testing.T) { 254 testCases := []struct { 255 inputSignElement string 256 expectedSignStr string 257 expectedErrCode APIErrorCode 258 }{ 259 // Test case - 1. 260 // SignElemenet doesn't have 2 parts on an attempt to split at '='. 261 // ErrMissingFields expected. 262 { 263 inputSignElement: "Signature", 264 expectedSignStr: "", 265 expectedErrCode: ErrMissingFields, 266 }, 267 // Test case - 2. 268 // SignElement does have 2 parts but doesn't have valid signature value. 269 // ErrMissingFields expected. 270 { 271 inputSignElement: "Signature=", 272 expectedSignStr: "", 273 expectedErrCode: ErrMissingFields, 274 }, 275 // Test case - 3. 276 // SignElemenet with missing "SignatureTag",ErrMissingSignTag expected. 277 { 278 inputSignElement: "Sign=", 279 expectedSignStr: "", 280 expectedErrCode: ErrMissingSignTag, 281 }, 282 // Test case - 4. 283 // Test case with valid inputs. 284 { 285 inputSignElement: "Signature=abcd", 286 expectedSignStr: "abcd", 287 expectedErrCode: ErrNone, 288 }, 289 } 290 for i, testCase := range testCases { 291 actualSignStr, actualErrCode := parseSignature(testCase.inputSignElement) 292 if testCase.expectedErrCode != actualErrCode { 293 t.Fatalf("Test %d: Expected the APIErrCode to be %d, got %d", i+1, testCase.expectedErrCode, actualErrCode) 294 } 295 if actualErrCode == ErrNone { 296 if testCase.expectedSignStr != actualSignStr { 297 t.Errorf("Test %d: Expected the result to be \"%s\", but got \"%s\". ", i+1, testCase.expectedSignStr, actualSignStr) 298 299 } 300 } 301 302 } 303 } 304 305 // TestParseSignedHeaders - validates the logic for extracting the signature string. 306 func TestParseSignedHeaders(t *testing.T) { 307 testCases := []struct { 308 inputSignElement string 309 expectedSignedHeaders []string 310 expectedErrCode APIErrorCode 311 }{ 312 // Test case - 1. 313 // SignElemenet doesn't have 2 parts on an attempt to split at '='. 314 // ErrMissingFields expected. 315 { 316 inputSignElement: "SignedHeaders", 317 expectedSignedHeaders: nil, 318 expectedErrCode: ErrMissingFields, 319 }, 320 // Test case - 2. 321 // SignElemenet with missing "SigHeaderTag",ErrMissingSignHeadersTag expected. 322 { 323 inputSignElement: "Sign=", 324 expectedSignedHeaders: nil, 325 expectedErrCode: ErrMissingSignHeadersTag, 326 }, 327 // Test case - 3. 328 // Test case with valid inputs. 329 { 330 inputSignElement: "SignedHeaders=host;x-amz-content-sha256;x-amz-date", 331 expectedSignedHeaders: []string{"host", "x-amz-content-sha256", "x-amz-date"}, 332 expectedErrCode: ErrNone, 333 }, 334 } 335 336 for i, testCase := range testCases { 337 actualSignedHeaders, actualErrCode := parseSignedHeader(testCase.inputSignElement) 338 if testCase.expectedErrCode != actualErrCode { 339 t.Errorf("Test %d: Expected the APIErrCode to be %d, got %d", i+1, testCase.expectedErrCode, actualErrCode) 340 } 341 if actualErrCode == ErrNone { 342 if strings.Join(testCase.expectedSignedHeaders, ",") != strings.Join(actualSignedHeaders, ",") { 343 t.Errorf("Test %d: Expected the result to be \"%v\", but got \"%v\". ", i+1, testCase.expectedSignedHeaders, actualSignedHeaders) 344 345 } 346 } 347 348 } 349 } 350 351 // TestParseSignV4 - Tests Parsing of v4 signature form the authorization string. 352 func TestParseSignV4(t *testing.T) { 353 sampleTimeStr := UTCNow().Format(yyyymmdd) 354 testCases := []struct { 355 inputV4AuthStr string 356 expectedAuthField signValues 357 expectedErrCode APIErrorCode 358 }{ 359 // Test case - 1. 360 // Test case with empty auth string. 361 { 362 inputV4AuthStr: "", 363 expectedAuthField: signValues{}, 364 expectedErrCode: ErrAuthHeaderEmpty, 365 }, 366 // Test case - 2. 367 // Test case with no sign v4 Algorithm prefix. 368 // A valid authorization string should begin(prefix) 369 { 370 inputV4AuthStr: "no-singv4AlgorithmPrefix", 371 expectedAuthField: signValues{}, 372 expectedErrCode: ErrSignatureVersionNotSupported, 373 }, 374 // Test case - 3. 375 // Test case with missing fields. 376 // A valid authorization string should have 3 fields. 377 { 378 inputV4AuthStr: signV4Algorithm, 379 expectedAuthField: signValues{}, 380 expectedErrCode: ErrMissingFields, 381 }, 382 // Test case - 4. 383 // Test case with invalid credential field. 384 { 385 inputV4AuthStr: signV4Algorithm + " Cred=,a,b", 386 expectedAuthField: signValues{}, 387 expectedErrCode: ErrMissingCredTag, 388 }, 389 // Test case - 5. 390 // Auth field with missing "SigHeaderTag",ErrMissingSignHeadersTag expected. 391 // A vaild credential is generated. 392 // Test case with invalid credential field. 393 { 394 inputV4AuthStr: signV4Algorithm + 395 strings.Join([]string{ 396 // generating a valid credential field. 397 generateCredentialStr( 398 "Z7IXGOO6BZ0REAN1Q26I", 399 sampleTimeStr, 400 "us-west-1", 401 "s3", 402 "aws4_request"), 403 // Incorrect SignedHeader field. 404 "SignIncorrectHeader=", 405 "b", 406 }, ","), 407 408 expectedAuthField: signValues{}, 409 expectedErrCode: ErrMissingSignHeadersTag, 410 }, 411 // Test case - 6. 412 // Auth string with missing "SignatureTag",ErrMissingSignTag expected. 413 // A vaild credential is generated. 414 // Test case with invalid credential field. 415 { 416 inputV4AuthStr: signV4Algorithm + 417 strings.Join([]string{ 418 // generating a valid credential. 419 generateCredentialStr( 420 "Z7IXGOO6BZ0REAN1Q26I", 421 sampleTimeStr, 422 "us-west-1", 423 "s3", 424 "aws4_request"), 425 // valid SignedHeader. 426 "SignedHeaders=host;x-amz-content-sha256;x-amz-date", 427 // invalid Signature field. 428 // a valid signature is of form "Signature=" 429 "Sign=", 430 }, ","), 431 432 expectedAuthField: signValues{}, 433 expectedErrCode: ErrMissingSignTag, 434 }, 435 // Test case - 7. 436 { 437 inputV4AuthStr: signV4Algorithm + 438 strings.Join([]string{ 439 // generating a valid credential. 440 generateCredentialStr( 441 "Z7IXGOO6BZ0REAN1Q26I", 442 sampleTimeStr, 443 "us-west-1", 444 "s3", 445 "aws4_request"), 446 // valid SignedHeader. 447 "SignedHeaders=host;x-amz-content-sha256;x-amz-date", 448 // valid Signature field. 449 // a valid signature is of form "Signature=" 450 "Signature=abcd", 451 }, ","), 452 expectedAuthField: signValues{ 453 Credential: generateCredentials( 454 t, 455 "Z7IXGOO6BZ0REAN1Q26I", 456 sampleTimeStr, 457 "us-west-1", 458 "s3", 459 "aws4_request"), 460 SignedHeaders: []string{"host", "x-amz-content-sha256", "x-amz-date"}, 461 Signature: "abcd", 462 }, 463 expectedErrCode: ErrNone, 464 }, 465 // Test case - 8. 466 { 467 inputV4AuthStr: signV4Algorithm + 468 strings.Join([]string{ 469 // generating a valid credential. 470 generateCredentialStr( 471 "access key", 472 sampleTimeStr, 473 "us-west-1", 474 "s3", 475 "aws4_request"), 476 // valid SignedHeader. 477 "SignedHeaders=host;x-amz-content-sha256;x-amz-date", 478 // valid Signature field. 479 // a valid signature is of form "Signature=" 480 "Signature=abcd", 481 }, ","), 482 expectedAuthField: signValues{ 483 Credential: generateCredentials( 484 t, 485 "access key", 486 sampleTimeStr, 487 "us-west-1", 488 "s3", 489 "aws4_request"), 490 SignedHeaders: []string{"host", "x-amz-content-sha256", "x-amz-date"}, 491 Signature: "abcd", 492 }, 493 expectedErrCode: ErrNone, 494 }, 495 } 496 497 for i, testCase := range testCases { 498 parsedAuthField, actualErrCode := parseSignV4(testCase.inputV4AuthStr, "", "s3") 499 500 if testCase.expectedErrCode != actualErrCode { 501 t.Fatalf("Test %d: Expected the APIErrCode to be %d, got %d", i+1, testCase.expectedErrCode, actualErrCode) 502 } 503 504 if actualErrCode == ErrNone { 505 // validating the extracted/parsed credential fields. 506 validateCredentialfields(t, i+1, testCase.expectedAuthField.Credential, parsedAuthField.Credential) 507 508 // validating the extraction/parsing of signature field. 509 if !compareSignatureV4(testCase.expectedAuthField.Signature, parsedAuthField.Signature) { 510 t.Errorf("Test %d: Parsed Signature field mismatch: Expected \"%s\", got \"%s\"", i+1, testCase.expectedAuthField.Signature, parsedAuthField.Signature) 511 } 512 513 // validating the extracted signed headers. 514 if strings.Join(testCase.expectedAuthField.SignedHeaders, ",") != strings.Join(parsedAuthField.SignedHeaders, ",") { 515 t.Errorf("Test %d: Expected the result to be \"%v\", but got \"%v\". ", i+1, testCase.expectedAuthField, parsedAuthField.SignedHeaders) 516 } 517 } 518 519 } 520 521 } 522 523 // TestDoesV4PresignParamsExist - tests validate the logic to 524 func TestDoesV4PresignParamsExist(t *testing.T) { 525 testCases := []struct { 526 inputQueryKeyVals []string 527 expectedErrCode APIErrorCode 528 }{ 529 // Test case - 1. 530 // contains all query param keys which are necessary for v4 presign request. 531 { 532 inputQueryKeyVals: []string{ 533 "X-Amz-Algorithm", "", 534 "X-Amz-Credential", "", 535 "X-Amz-Signature", "", 536 "X-Amz-Date", "", 537 "X-Amz-SignedHeaders", "", 538 "X-Amz-Expires", "", 539 }, 540 expectedErrCode: ErrNone, 541 }, 542 // Test case - 2. 543 // missing "X-Amz-Algorithm" in tdhe query param. 544 // contains all query param keys which are necessary for v4 presign request. 545 { 546 inputQueryKeyVals: []string{ 547 "X-Amz-Credential", "", 548 "X-Amz-Signature", "", 549 "X-Amz-Date", "", 550 "X-Amz-SignedHeaders", "", 551 "X-Amz-Expires", "", 552 }, 553 expectedErrCode: ErrInvalidQueryParams, 554 }, 555 // Test case - 3. 556 // missing "X-Amz-Credential" in the query param. 557 { 558 inputQueryKeyVals: []string{ 559 "X-Amz-Algorithm", "", 560 "X-Amz-Signature", "", 561 "X-Amz-Date", "", 562 "X-Amz-SignedHeaders", "", 563 "X-Amz-Expires", "", 564 }, 565 expectedErrCode: ErrInvalidQueryParams, 566 }, 567 // Test case - 4. 568 // missing "X-Amz-Signature" in the query param. 569 { 570 inputQueryKeyVals: []string{ 571 "X-Amz-Algorithm", "", 572 "X-Amz-Credential", "", 573 "X-Amz-Date", "", 574 "X-Amz-SignedHeaders", "", 575 "X-Amz-Expires", "", 576 }, 577 expectedErrCode: ErrInvalidQueryParams, 578 }, 579 // Test case - 5. 580 // missing "X-Amz-Date" in the query param. 581 { 582 inputQueryKeyVals: []string{ 583 "X-Amz-Algorithm", "", 584 "X-Amz-Credential", "", 585 "X-Amz-Signature", "", 586 "X-Amz-SignedHeaders", "", 587 "X-Amz-Expires", "", 588 }, 589 expectedErrCode: ErrInvalidQueryParams, 590 }, 591 // Test case - 6. 592 // missing "X-Amz-SignedHeaders" in the query param. 593 { 594 inputQueryKeyVals: []string{ 595 "X-Amz-Algorithm", "", 596 "X-Amz-Credential", "", 597 "X-Amz-Signature", "", 598 "X-Amz-Date", "", 599 "X-Amz-Expires", "", 600 }, 601 expectedErrCode: ErrInvalidQueryParams, 602 }, 603 // Test case - 7. 604 // missing "X-Amz-Expires" in the query param. 605 { 606 inputQueryKeyVals: []string{ 607 "X-Amz-Algorithm", "", 608 "X-Amz-Credential", "", 609 "X-Amz-Signature", "", 610 "X-Amz-Date", "", 611 "X-Amz-SignedHeaders", "", 612 }, 613 expectedErrCode: ErrInvalidQueryParams, 614 }, 615 } 616 617 for i, testCase := range testCases { 618 inputQuery := url.Values{} 619 // iterating through input query key value and setting the inputQuery of type url.Values. 620 for j := 0; j < len(testCase.inputQueryKeyVals)-1; j += 2 { 621 622 inputQuery.Set(testCase.inputQueryKeyVals[j], testCase.inputQueryKeyVals[j+1]) 623 } 624 625 actualErrCode := doesV4PresignParamsExist(inputQuery) 626 627 if testCase.expectedErrCode != actualErrCode { 628 t.Fatalf("Test %d: Expected the APIErrCode to be %d, got %d", i+1, testCase.expectedErrCode, actualErrCode) 629 } 630 } 631 632 } 633 634 // TestParsePreSignV4 - Validates the parsing logic of Presignied v4 request from its url query values. 635 func TestParsePreSignV4(t *testing.T) { 636 // converts the duration in seconds into string format. 637 getDurationStr := func(expires int) string { 638 return strconv.Itoa(expires) 639 } 640 // used in expected preSignValues, preSignValues.Date is of type time.Time . 641 queryTime := UTCNow() 642 643 sampleTimeStr := UTCNow().Format(yyyymmdd) 644 645 testCases := []struct { 646 inputQueryKeyVals []string 647 expectedPreSignValues preSignValues 648 expectedErrCode APIErrorCode 649 }{ 650 // Test case - 1. 651 // A Valid v4 presign URL requires the following params to be in the query. 652 // "X-Amz-Algorithm", "X-Amz-Credential", "X-Amz-Signature", " X-Amz-Date", "X-Amz-SignedHeaders", "X-Amz-Expires". 653 // If these params are missing its expected to get ErrInvalidQueryParams . 654 // In the following test case 2 out of 6 query params are missing. 655 { 656 inputQueryKeyVals: []string{ 657 "X-Amz-Algorithm", "", 658 "X-Amz-Credential", "", 659 "X-Amz-Signature", "", 660 "X-Amz-Expires", "", 661 }, 662 expectedPreSignValues: preSignValues{}, 663 expectedErrCode: ErrInvalidQueryParams, 664 }, 665 // Test case - 2. 666 // Test case with invalid "X-Amz-Algorithm" query value. 667 // The other query params should exist, other wise ErrInvalidQueryParams will be returned because of missing fields. 668 { 669 inputQueryKeyVals: []string{ 670 671 "X-Amz-Algorithm", "InvalidValue", 672 "X-Amz-Credential", "", 673 "X-Amz-Signature", "", 674 "X-Amz-Date", "", 675 "X-Amz-SignedHeaders", "", 676 "X-Amz-Expires", "", 677 }, 678 expectedPreSignValues: preSignValues{}, 679 expectedErrCode: ErrInvalidQuerySignatureAlgo, 680 }, 681 // Test case - 3. 682 // Test case with valid "X-Amz-Algorithm" query value, but invalid "X-Amz-Credential" header. 683 // Malformed crenential. 684 { 685 inputQueryKeyVals: []string{ 686 // valid "X-Amz-Algorithm" header. 687 "X-Amz-Algorithm", signV4Algorithm, 688 // valid "X-Amz-Credential" header. 689 "X-Amz-Credential", "invalid-credential", 690 "X-Amz-Signature", "", 691 "X-Amz-Date", "", 692 "X-Amz-SignedHeaders", "", 693 "X-Amz-Expires", "", 694 }, 695 expectedPreSignValues: preSignValues{}, 696 expectedErrCode: ErrCredMalformed, 697 }, 698 699 // Test case - 4. 700 // Test case with valid "X-Amz-Algorithm" query value. 701 // Malformed date. 702 { 703 inputQueryKeyVals: []string{ 704 // valid "X-Amz-Algorithm" header. 705 "X-Amz-Algorithm", signV4Algorithm, 706 // valid "X-Amz-Credential" header. 707 "X-Amz-Credential", joinWithSlash( 708 "Z7IXGOO6BZ0REAN1Q26I", 709 sampleTimeStr, 710 "us-west-1", 711 "s3", 712 "aws4_request"), 713 // invalid "X-Amz-Date" query. 714 "X-Amz-Date", "invalid-time", 715 "X-Amz-SignedHeaders", "", 716 "X-Amz-Expires", "", 717 "X-Amz-Signature", "", 718 }, 719 expectedPreSignValues: preSignValues{}, 720 expectedErrCode: ErrMalformedPresignedDate, 721 }, 722 // Test case - 5. 723 // Test case with valid "X-Amz-Algorithm", "X-Amz-Credential", "X-Amz-Date" query value. 724 // Malformed Expiry, a valid expiry should be of format "<int>s". 725 { 726 inputQueryKeyVals: []string{ 727 // valid "X-Amz-Algorithm" header. 728 "X-Amz-Algorithm", signV4Algorithm, 729 // valid "X-Amz-Credential" header. 730 "X-Amz-Credential", joinWithSlash( 731 "Z7IXGOO6BZ0REAN1Q26I", 732 sampleTimeStr, 733 "us-west-1", 734 "s3", 735 "aws4_request"), 736 // valid "X-Amz-Date" query. 737 "X-Amz-Date", UTCNow().Format(iso8601Format), 738 "X-Amz-Expires", "MalformedExpiry", 739 "X-Amz-SignedHeaders", "", 740 "X-Amz-Signature", "", 741 }, 742 expectedPreSignValues: preSignValues{}, 743 expectedErrCode: ErrMalformedExpires, 744 }, 745 // Test case - 6. 746 // Test case with negative X-Amz-Expires header. 747 { 748 inputQueryKeyVals: []string{ 749 // valid "X-Amz-Algorithm" header. 750 "X-Amz-Algorithm", signV4Algorithm, 751 // valid "X-Amz-Credential" header. 752 "X-Amz-Credential", joinWithSlash( 753 "Z7IXGOO6BZ0REAN1Q26I", 754 sampleTimeStr, 755 "us-west-1", 756 "s3", 757 "aws4_request"), 758 // valid "X-Amz-Date" query. 759 "X-Amz-Date", queryTime.UTC().Format(iso8601Format), 760 "X-Amz-Expires", getDurationStr(-1), 761 "X-Amz-Signature", "abcd", 762 "X-Amz-SignedHeaders", "host;x-amz-content-sha256;x-amz-date", 763 }, 764 expectedPreSignValues: preSignValues{}, 765 expectedErrCode: ErrNegativeExpires, 766 }, 767 // Test case - 7. 768 // Test case with empty X-Amz-SignedHeaders. 769 { 770 inputQueryKeyVals: []string{ 771 // valid "X-Amz-Algorithm" header. 772 "X-Amz-Algorithm", signV4Algorithm, 773 // valid "X-Amz-Credential" header. 774 "X-Amz-Credential", joinWithSlash( 775 "Z7IXGOO6BZ0REAN1Q26I", 776 sampleTimeStr, 777 "us-west-1", 778 "s3", 779 "aws4_request"), 780 // valid "X-Amz-Date" query. 781 "X-Amz-Date", queryTime.UTC().Format(iso8601Format), 782 "X-Amz-Expires", getDurationStr(100), 783 "X-Amz-Signature", "abcd", 784 "X-Amz-SignedHeaders", "", 785 }, 786 expectedPreSignValues: preSignValues{}, 787 expectedErrCode: ErrMissingFields, 788 }, 789 // Test case - 8. 790 // Test case with valid "X-Amz-Algorithm", "X-Amz-Credential", "X-Amz-Date" query value. 791 // Malformed Expiry, a valid expiry should be of format "<int>s". 792 { 793 inputQueryKeyVals: []string{ 794 // valid "X-Amz-Algorithm" header. 795 "X-Amz-Algorithm", signV4Algorithm, 796 // valid "X-Amz-Credential" header. 797 "X-Amz-Credential", joinWithSlash( 798 "Z7IXGOO6BZ0REAN1Q26I", 799 sampleTimeStr, 800 "us-west-1", 801 "s3", 802 "aws4_request"), 803 // valid "X-Amz-Date" query. 804 "X-Amz-Date", queryTime.UTC().Format(iso8601Format), 805 "X-Amz-Expires", getDurationStr(100), 806 "X-Amz-Signature", "abcd", 807 "X-Amz-SignedHeaders", "host;x-amz-content-sha256;x-amz-date", 808 }, 809 expectedPreSignValues: preSignValues{ 810 signValues{ 811 // Credentials. 812 generateCredentials( 813 t, 814 "Z7IXGOO6BZ0REAN1Q26I", 815 sampleTimeStr, 816 "us-west-1", 817 "s3", 818 "aws4_request", 819 ), 820 // SignedHeaders. 821 []string{"host", "x-amz-content-sha256", "x-amz-date"}, 822 // Signature. 823 "abcd", 824 }, 825 // Date 826 queryTime, 827 // Expires. 828 100 * time.Second, 829 }, 830 expectedErrCode: ErrNone, 831 }, 832 833 // Test case - 9. 834 // Test case with value greater than 604800 in X-Amz-Expires header. 835 { 836 inputQueryKeyVals: []string{ 837 // valid "X-Amz-Algorithm" header. 838 "X-Amz-Algorithm", signV4Algorithm, 839 // valid "X-Amz-Credential" header. 840 "X-Amz-Credential", joinWithSlash( 841 "Z7IXGOO6BZ0REAN1Q26I", 842 sampleTimeStr, 843 "us-west-1", 844 "s3", 845 "aws4_request"), 846 // valid "X-Amz-Date" query. 847 "X-Amz-Date", queryTime.UTC().Format(iso8601Format), 848 // Invalid Expiry time greater than 7 days (604800 in seconds). 849 "X-Amz-Expires", getDurationStr(605000), 850 "X-Amz-Signature", "abcd", 851 "X-Amz-SignedHeaders", "host;x-amz-content-sha256;x-amz-date", 852 }, 853 expectedPreSignValues: preSignValues{}, 854 expectedErrCode: ErrMaximumExpires, 855 }, 856 } 857 858 for i, testCase := range testCases { 859 inputQuery := url.Values{} 860 // iterating through input query key value and setting the inputQuery of type url.Values. 861 for j := 0; j < len(testCase.inputQueryKeyVals)-1; j += 2 { 862 inputQuery.Set(testCase.inputQueryKeyVals[j], testCase.inputQueryKeyVals[j+1]) 863 } 864 // call the function under test. 865 parsedPreSign, actualErrCode := parsePreSignV4(inputQuery, "", serviceS3) 866 if testCase.expectedErrCode != actualErrCode { 867 t.Fatalf("Test %d: Expected the APIErrCode to be %d, got %d", i+1, testCase.expectedErrCode, actualErrCode) 868 } 869 if actualErrCode == ErrNone { 870 // validating credentials. 871 validateCredentialfields(t, i+1, testCase.expectedPreSignValues.Credential, parsedPreSign.Credential) 872 // validating signed headers. 873 if strings.Join(testCase.expectedPreSignValues.SignedHeaders, ",") != strings.Join(parsedPreSign.SignedHeaders, ",") { 874 t.Errorf("Test %d: Expected the result to be \"%v\", but got \"%v\". ", i+1, testCase.expectedPreSignValues.SignedHeaders, parsedPreSign.SignedHeaders) 875 } 876 // validating signature field. 877 if !compareSignatureV4(testCase.expectedPreSignValues.Signature, parsedPreSign.Signature) { 878 t.Errorf("Test %d: Signature field mismatch: Expected \"%s\", got \"%s\"", i+1, testCase.expectedPreSignValues.Signature, parsedPreSign.Signature) 879 } 880 // validating expiry duration. 881 if testCase.expectedPreSignValues.Expires != parsedPreSign.Expires { 882 t.Errorf("Test %d: Expected expiry time to be %v, but got %v", i+1, testCase.expectedPreSignValues.Expires, parsedPreSign.Expires) 883 } 884 // validating presign date field. 885 if testCase.expectedPreSignValues.Date.UTC().Format(iso8601Format) != parsedPreSign.Date.UTC().Format(iso8601Format) { 886 t.Errorf("Test %d: Expected date to be %v, but got %v", i+1, testCase.expectedPreSignValues.Date.UTC().Format(iso8601Format), parsedPreSign.Date.UTC().Format(iso8601Format)) 887 } 888 } 889 890 } 891 }