github.com/Tyktechnologies/tyk@v2.9.5+incompatible/gateway/api_test.go (about) 1 package gateway 2 3 import ( 4 "bytes" 5 "encoding/json" 6 "net/http" 7 "net/http/httptest" 8 "reflect" 9 "sort" 10 "strconv" 11 "sync" 12 "testing" 13 14 "github.com/go-redis/redis" 15 uuid "github.com/satori/go.uuid" 16 17 "fmt" 18 19 "github.com/TykTechnologies/tyk/config" 20 "github.com/TykTechnologies/tyk/storage" 21 "github.com/TykTechnologies/tyk/test" 22 "github.com/TykTechnologies/tyk/user" 23 ) 24 25 const apiTestDef = `{ 26 "api_id": "1", 27 "definition": { 28 "location": "header", 29 "key": "version" 30 }, 31 "auth": {"auth_header_name": "authorization"}, 32 "version_data": { 33 "versions": { 34 "v1": {"name": "v1"} 35 } 36 }, 37 "proxy": { 38 "listen_path": "/v1", 39 "target_url": "` + TestHttpAny + `" 40 } 41 }` 42 43 func TestHealthCheckEndpoint(t *testing.T) { 44 globalConf := config.Global() 45 globalConf.HealthCheck.EnableHealthChecks = true 46 config.SetGlobal(globalConf) 47 defer ResetTestConfig() 48 49 ts := StartTest() 50 defer ts.Close() 51 52 BuildAndLoadAPI() 53 54 ts.Run(t, []test.TestCase{ 55 {Path: "/tyk/health/?api_id=test", AdminAuth: true, Code: 200}, 56 {Path: "/tyk/health/?api_id=unknown", AdminAuth: true, Code: 404, BodyMatch: `"message":"API ID not found"`}, 57 }...) 58 } 59 60 func TestApiHandlerPostDupPath(t *testing.T) { 61 type testCase struct { 62 APIID, ListenPath string 63 } 64 65 assertListenPaths := func(tests []testCase) { 66 for _, tc := range tests { 67 s := getApiSpec(tc.APIID) 68 if want, got := tc.ListenPath, s.Proxy.ListenPath; want != got { 69 t.Errorf("API spec %s want path %s, got %s", "2", want, got) 70 } 71 } 72 } 73 74 t.Run("Sequentieal order", func(t *testing.T) { 75 // Load initial API 76 BuildAndLoadAPI( 77 func(spec *APISpec) { spec.APIID = "1" }, 78 ) 79 80 BuildAndLoadAPI( 81 func(spec *APISpec) { spec.APIID = "1" }, 82 func(spec *APISpec) { spec.APIID = "2" }, 83 func(spec *APISpec) { spec.APIID = "3" }, 84 ) 85 86 assertListenPaths([]testCase{ 87 // Should retain original API 88 {"1", "/sample"}, 89 {"2", "/sample-2"}, 90 {"3", "/sample-3"}, 91 }) 92 }) 93 94 t.Run("Should re-order", func(t *testing.T) { 95 BuildAndLoadAPI( 96 func(spec *APISpec) { spec.APIID = "2" }, 97 func(spec *APISpec) { spec.APIID = "3" }, 98 ) 99 100 assertListenPaths([]testCase{ 101 {"2", "/sample-2"}, 102 {"3", "/sample-3"}, 103 }) 104 }) 105 106 t.Run("Restore original order", func(t *testing.T) { 107 BuildAndLoadAPI( 108 func(spec *APISpec) { spec.APIID = "1" }, 109 func(spec *APISpec) { spec.APIID = "2" }, 110 func(spec *APISpec) { spec.APIID = "3" }, 111 ) 112 113 assertListenPaths([]testCase{ 114 // Since API was not loaded previously first it has prefixed id 115 {"1", "/sample-1"}, 116 {"2", "/sample-2"}, 117 {"3", "/sample-3"}, 118 }) 119 }) 120 } 121 122 func TestKeyHandler(t *testing.T) { 123 ts := StartTest() 124 defer ts.Close() 125 126 defer ResetTestConfig() 127 128 BuildAndLoadAPI(func(spec *APISpec) { 129 spec.UseKeylessAccess = false 130 spec.Auth.UseParam = true 131 }) 132 133 // Access right not specified 134 masterKey := CreateStandardSession() 135 masterKeyJSON, _ := json.Marshal(masterKey) 136 137 // with access 138 withAccess := CreateStandardSession() 139 withAccess.AccessRights = map[string]user.AccessDefinition{"test": { 140 APIID: "test", Versions: []string{"v1"}, 141 }} 142 withAccessJSON, _ := json.Marshal(withAccess) 143 144 // with policy 145 policiesMu.Lock() 146 policiesByID["abc_policy"] = user.Policy{ 147 Active: true, 148 QuotaMax: 5, 149 QuotaRenewalRate: 300, 150 AccessRights: map[string]user.AccessDefinition{"test": { 151 APIID: "test", Versions: []string{"v1"}, 152 }}, 153 OrgID: "default", 154 } 155 policiesMu.Unlock() 156 withPolicy := CreateStandardSession() 157 withoutPolicyJSON, _ := json.Marshal(withPolicy) 158 159 withPolicy.ApplyPolicies = []string{ 160 "abc_policy", 161 } 162 withPolicyJSON, _ := json.Marshal(withPolicy) 163 164 // with invalid policy 165 withBadPolicy := CreateStandardSession() 166 withBadPolicy.AccessRights = map[string]user.AccessDefinition{"test": { 167 APIID: "test", Versions: []string{"v1"}, 168 }} 169 withBadPolicy.ApplyPolicies = []string{ 170 "xyz_policy", 171 } 172 withBadPolicyJSON, _ := json.Marshal(withBadPolicy) 173 174 t.Run("Create key", func(t *testing.T) { 175 ts.Run(t, []test.TestCase{ 176 // Master keys should be disabled by default 177 {Method: "POST", Path: "/tyk/keys/create", Data: string(masterKeyJSON), AdminAuth: true, Code: 400, BodyMatch: "Failed to create key, keys must have at least one Access Rights record set."}, 178 {Method: "POST", Path: "/tyk/keys/create", Data: string(withAccessJSON), AdminAuth: true, Code: 200}, 179 }...) 180 }) 181 182 t.Run("Create key with policy", func(t *testing.T) { 183 ts.Run(t, []test.TestCase{ 184 { 185 Method: "POST", 186 Path: "/tyk/keys/create", 187 Data: string(withoutPolicyJSON), 188 AdminAuth: true, 189 Code: 400, 190 }, 191 { 192 Method: "POST", 193 Path: "/tyk/keys/create", 194 Data: string(withPolicyJSON), 195 AdminAuth: true, 196 Code: 200, 197 }, 198 { 199 Method: "POST", 200 Path: "/tyk/keys/create", 201 Data: string(withBadPolicyJSON), 202 AdminAuth: true, 203 Code: 500, 204 }, 205 { 206 Method: "POST", 207 Path: "/tyk/keys/my_key_id", 208 Data: string(withPolicyJSON), 209 AdminAuth: true, 210 Code: 200, 211 }, 212 { 213 Method: "GET", 214 Path: "/sample/?authorization=wrong_key_id", 215 Code: 403, 216 }, 217 { 218 Method: "GET", 219 Path: "/sample/?authorization=my_key_id", 220 Code: 200, 221 }, 222 { 223 Method: "GET", 224 Path: "/tyk/keys/my_key_id" + "?api_id=test", 225 AdminAuth: true, 226 Code: 200, 227 BodyMatch: `"quota_max":5`, 228 }, 229 { 230 Method: "GET", 231 Path: "/tyk/keys/my_key_id" + "?api_id=test", 232 AdminAuth: true, 233 Code: 200, 234 BodyMatch: `"quota_remaining":4`, 235 }, 236 }...) 237 238 FallbackKeySesionManager.Store().DeleteAllKeys() 239 }) 240 241 _, knownKey := ts.CreateSession(func(s *user.SessionState) { 242 s.AccessRights = map[string]user.AccessDefinition{"test": { 243 APIID: "test", Versions: []string{"v1"}, 244 }} 245 s.Mutex = &sync.RWMutex{} 246 }) 247 248 _, unknownOrgKey := ts.CreateSession(func(s *user.SessionState) { 249 s.OrgID = "dummy" 250 s.AccessRights = map[string]user.AccessDefinition{"test": { 251 APIID: "test", Versions: []string{"v1"}, 252 }} 253 s.Mutex = &sync.RWMutex{} 254 }) 255 256 t.Run("Get key", func(t *testing.T) { 257 ts.Run(t, []test.TestCase{ 258 {Method: "GET", Path: "/tyk/keys/unknown", AdminAuth: true, Code: 404}, 259 {Method: "GET", Path: "/tyk/keys/" + knownKey, AdminAuth: true, Code: 200}, 260 {Method: "GET", Path: "/tyk/keys/" + knownKey + "?api_id=test", AdminAuth: true, Code: 200}, 261 {Method: "GET", Path: "/tyk/keys/" + knownKey + "?api_id=unknown", AdminAuth: true, Code: 200}, 262 }...) 263 }) 264 265 t.Run("List keys", func(t *testing.T) { 266 ts.Run(t, []test.TestCase{ 267 {Method: "GET", Path: "/tyk/keys/", AdminAuth: true, Code: 200, BodyMatch: knownKey}, 268 {Method: "GET", Path: "/tyk/keys/?api_id=test", AdminAuth: true, Code: 200, BodyMatch: knownKey}, 269 {Method: "GET", Path: "/tyk/keys/?api_id=unknown", AdminAuth: true, Code: 200, BodyMatch: knownKey}, 270 }...) 271 272 globalConf := config.Global() 273 globalConf.HashKeyFunction = "" 274 config.SetGlobal(globalConf) 275 _, keyWithoutHash := ts.CreateSession(func(s *user.SessionState) { 276 s.AccessRights = map[string]user.AccessDefinition{"test": { 277 APIID: "test", Versions: []string{"v1"}, 278 }} 279 s.Mutex = &sync.RWMutex{} 280 }) 281 282 assert := func(response *http.Response, expected []string) { 283 var keys apiAllKeys 284 _ = json.NewDecoder(response.Body).Decode(&keys) 285 actual := keys.APIKeys 286 287 sort.Strings(expected) 288 sort.Strings(actual) 289 290 if !reflect.DeepEqual(expected, actual) { 291 t.Errorf("Expected %v, actual %v", expected, actual) 292 } 293 } 294 295 t.Run(`filter=""`, func(t *testing.T) { 296 resp, _ := ts.Run(t, test.TestCase{Method: "GET", Path: "/tyk/keys/", AdminAuth: true, Code: 200, BodyMatch: knownKey}) 297 expected := []string{knownKey, unknownOrgKey, keyWithoutHash} 298 assert(resp, expected) 299 }) 300 301 t.Run(`filter=orgID`, func(t *testing.T) { 302 resp, _ := ts.Run(t, test.TestCase{Method: "GET", Path: "/tyk/keys/?filter=" + "default", AdminAuth: true, Code: 200, BodyMatch: knownKey}) 303 expected := []string{knownKey, keyWithoutHash} 304 assert(resp, expected) 305 }) 306 }) 307 308 t.Run("Update key", func(t *testing.T) { 309 ts.Run(t, []test.TestCase{ 310 // Without data 311 {Method: "PUT", Path: "/tyk/keys/" + knownKey, AdminAuth: true, Code: 400}, 312 {Method: "PUT", Path: "/tyk/keys/" + knownKey, Data: string(withAccessJSON), AdminAuth: true, Code: 200}, 313 {Method: "PUT", Path: "/tyk/keys/" + knownKey + "?api_id=test", Data: string(withAccessJSON), AdminAuth: true, Code: 200}, 314 {Method: "PUT", Path: "/tyk/keys/" + knownKey + "?api_id=none", Data: string(withAccessJSON), AdminAuth: true, Code: 200}, 315 }...) 316 }) 317 318 t.Run("Delete key", func(t *testing.T) { 319 ts.Run(t, []test.TestCase{ 320 {Method: "DELETE", Path: "/tyk/keys/" + knownKey, AdminAuth: true, Code: 200, BodyMatch: `"action":"deleted"`}, 321 {Method: "GET", Path: "/tyk/keys/" + knownKey, AdminAuth: true, Code: 404}, 322 }...) 323 }) 324 } 325 326 func TestKeyHandler_UpdateKey(t *testing.T) { 327 const testAPIID = "testAPIID" 328 329 ts := StartTest() 330 defer ts.Close() 331 332 BuildAndLoadAPI(func(spec *APISpec) { 333 spec.APIID = testAPIID 334 spec.UseKeylessAccess = false 335 spec.Auth.UseParam = true 336 spec.OrgID = "default" 337 }) 338 339 pID := CreatePolicy(func(p *user.Policy) { 340 p.Partitions.RateLimit = true 341 p.Tags = []string{"p1-tag"} 342 p.MetaData = map[string]interface{}{ 343 "p1-meta": "p1-value", 344 } 345 }) 346 347 pID2 := CreatePolicy(func(p *user.Policy) { 348 p.Partitions.Quota = true 349 p.Tags = []string{"p2-tag"} 350 p.MetaData = map[string]interface{}{ 351 "p2-meta": "p2-value", 352 } 353 }) 354 355 session, key := ts.CreateSession(func(s *user.SessionState) { 356 s.ApplyPolicies = []string{pID} 357 s.Tags = []string{"key-tag1", "key-tag2"} 358 s.MetaData = map[string]interface{}{ 359 "key-meta1": "key-value1", 360 "key-meta2": "key-value2", 361 } 362 s.AccessRights = map[string]user.AccessDefinition{testAPIID: { 363 APIID: testAPIID, Versions: []string{"v1"}, 364 }} 365 s.Mutex = &sync.RWMutex{} 366 }) 367 368 t.Run("Add policy not enforcing acl", func(t *testing.T) { 369 session.ApplyPolicies = append(session.ApplyPolicies, pID2) 370 sessionData, _ := json.Marshal(session) 371 path := fmt.Sprintf("/tyk/keys/%s", key) 372 373 _, _ = ts.Run(t, []test.TestCase{ 374 {Method: http.MethodPut, Path: path, Data: sessionData, AdminAuth: true, Code: 200}, 375 }...) 376 377 sessionState, found := FallbackKeySesionManager.SessionDetail(key, false) 378 accessRight, _ := sessionState.GetAccessRightByAPIID(testAPIID) 379 if !found || accessRight.APIID != testAPIID || len(sessionState.ApplyPolicies) != 2 { 380 t.Fatal("Adding policy to the list failed") 381 } 382 }) 383 384 t.Run("Remove policy not enforcing acl", func(t *testing.T) { 385 session.ApplyPolicies = []string{} 386 sessionData, _ := json.Marshal(session) 387 path := fmt.Sprintf("/tyk/keys/%s", key) 388 389 _, _ = ts.Run(t, []test.TestCase{ 390 {Method: http.MethodPut, Path: path, Data: sessionData, AdminAuth: true, Code: 200}, 391 }...) 392 393 sessionState, found := FallbackKeySesionManager.SessionDetail(key, false) 394 accessRight, _ := sessionState.GetAccessRightByAPIID(testAPIID) 395 if !found || accessRight.APIID != testAPIID || len(sessionState.ApplyPolicies) != 0 { 396 t.Fatal("Removing policy from the list failed") 397 } 398 }) 399 400 t.Run("Tags on key level", func(t *testing.T) { 401 assertTags := func(session *user.SessionState, expected []string) { 402 sessionData, _ := json.Marshal(session) 403 path := fmt.Sprintf("/tyk/keys/%s", key) 404 405 _, _ = ts.Run(t, []test.TestCase{ 406 {Method: http.MethodPut, Path: path, Data: sessionData, AdminAuth: true, Code: 200}, 407 }...) 408 409 sessionState, found := FallbackKeySesionManager.SessionDetail(key, false) 410 411 sort.Strings(sessionState.Tags) 412 sort.Strings(expected) 413 414 if !found || !reflect.DeepEqual(expected, sessionState.Tags) { 415 t.Fatalf("Expected %v, returned %v", expected, sessionState.Tags) 416 } 417 } 418 419 t.Run("Add", func(t *testing.T) { 420 expected := []string{"p1-tag", "p2-tag", "key-tag1", "key-tag2"} 421 session.ApplyPolicies = []string{pID, pID2} 422 assertTags(session, expected) 423 }) 424 425 t.Run("Make unique", func(t *testing.T) { 426 expected := []string{"p1-tag", "p2-tag", "key-tag1", "key-tag2"} 427 session.ApplyPolicies = []string{pID, pID2} 428 session.Tags = append(session.Tags, "p1-tag", "key-tag1") 429 assertTags(session, expected) 430 }) 431 432 t.Run("Remove", func(t *testing.T) { 433 expected := []string{"p1-tag", "p2-tag", "key-tag2"} 434 session.ApplyPolicies = []string{pID, pID2} 435 session.Tags = []string{"key-tag2"} 436 assertTags(session, expected) 437 }) 438 439 }) 440 441 t.Run("MetaData on key level", func(t *testing.T) { 442 assertMetaData := func(session *user.SessionState, expected map[string]interface{}) { 443 sessionData, _ := json.Marshal(session) 444 path := fmt.Sprintf("/tyk/keys/%s", key) 445 446 _, _ = ts.Run(t, []test.TestCase{ 447 {Method: http.MethodPut, Path: path, Data: sessionData, AdminAuth: true, Code: 200}, 448 }...) 449 450 sessionState, found := FallbackKeySesionManager.SessionDetail(key, false) 451 452 if !found || !reflect.DeepEqual(expected, sessionState.GetMetaData()) { 453 t.Fatalf("Expected %v, returned %v", expected, sessionState.GetMetaData()) 454 } 455 } 456 457 t.Run("Add", func(t *testing.T) { 458 expected := map[string]interface{}{ 459 "p1-meta": "p1-value", 460 "p2-meta": "p2-value", 461 "key-meta1": "key-value1", 462 "key-meta2": "key-value2", 463 } 464 session.ApplyPolicies = []string{pID, pID2} 465 assertMetaData(session, expected) 466 }) 467 468 t.Run("Make unique", func(t *testing.T) { 469 expected := map[string]interface{}{ 470 "p1-meta": "p1-value", 471 "p2-meta": "p2-value", 472 "key-meta1": "key-value1", 473 "key-meta2": "key-value2", 474 } 475 session.ApplyPolicies = []string{pID, pID2} 476 assertMetaData(session, expected) 477 }) 478 479 t.Run("Remove", func(t *testing.T) { 480 expected := map[string]interface{}{ 481 "p1-meta": "p1-value", 482 "p2-meta": "p2-value", 483 "key-meta2": "key-value2", 484 } 485 session.ApplyPolicies = []string{pID, pID2} 486 session.SetMetaData(map[string]interface{}{ 487 "key-meta2": "key-value2", 488 }) 489 assertMetaData(session, expected) 490 }) 491 }) 492 } 493 494 func TestKeyHandler_CheckKeysNotDuplicateOnUpdate(t *testing.T) { 495 ts := StartTest() 496 defer ts.Close() 497 498 BuildAndLoadAPI(func(spec *APISpec) { 499 spec.UseKeylessAccess = false 500 spec.Auth.UseParam = true 501 }) 502 503 cases := []struct { 504 Name string 505 IsCustomKey bool 506 KeyName string 507 }{ 508 { 509 Name: "duplicity on update custom key", 510 IsCustomKey: true, 511 KeyName: "my_custom_key", 512 }, 513 { 514 Name: "duplicity on update regular key", 515 IsCustomKey: false, 516 }, 517 } 518 519 for _, tc := range cases { 520 t.Run(tc.Name, func(t *testing.T) { 521 FallbackKeySesionManager.Store().DeleteAllKeys() 522 session := CreateStandardSession() 523 session.AccessRights = map[string]user.AccessDefinition{"test": { 524 APIID: "test", Versions: []string{"v1"}, 525 }} 526 527 keyName := tc.KeyName 528 if !tc.IsCustomKey { 529 keyName = generateToken(session.OrgID, "") 530 } 531 532 if err := doAddOrUpdate(keyName, session, false, true); err != nil { 533 t.Error("Failed to create key, ensure security settings are correct:" + err.Error()) 534 } 535 536 requestByte, _ := json.Marshal(session) 537 r := httptest.NewRequest(http.MethodPut, "/tyk/keys/"+keyName, bytes.NewReader(requestByte)) 538 handleAddOrUpdate(keyName, r, true) 539 540 sessions := FallbackKeySesionManager.Sessions("") 541 if len(sessions) != 1 { 542 t.Errorf("Sessions stored in global manager should be 1. But got: %v", len(sessions)) 543 } 544 545 }) 546 } 547 } 548 549 func TestHashKeyHandler(t *testing.T) { 550 globalConf := config.Global() 551 // make it to use hashes for Redis keys 552 globalConf.HashKeys = true 553 // enable hashed keys listing 554 globalConf.EnableHashedKeysListing = true 555 config.SetGlobal(globalConf) 556 defer ResetTestConfig() 557 558 hashTests := []struct { 559 hashFunction string 560 expectedHashSize int 561 desc string 562 }{ 563 {"", 8, " Legacy tokens, fallback to murmur32"}, 564 {storage.HashMurmur32, 8, ""}, 565 {storage.HashMurmur64, 16, ""}, 566 {storage.HashMurmur128, 32, ""}, 567 {storage.HashSha256, 64, ""}, 568 {"wrong", 16, " Should fallback to murmur64 if wrong alg"}, 569 } 570 571 for _, tc := range hashTests { 572 globalConf.HashKeyFunction = tc.hashFunction 573 config.SetGlobal(globalConf) 574 575 t.Run(fmt.Sprintf("%sHash fn: %s", tc.desc, tc.hashFunction), func(t *testing.T) { 576 testHashKeyHandlerHelper(t, tc.expectedHashSize) 577 }) 578 t.Run(fmt.Sprintf("%sHash fn: %s and Basic Auth", tc.desc, tc.hashFunction), func(t *testing.T) { 579 testHashFuncAndBAHelper(t) 580 }) 581 } 582 } 583 584 func TestHashKeyHandlerLegacyWithHashFunc(t *testing.T) { 585 globalConf := config.Global() 586 587 globalConf.HashKeys = true 588 globalConf.EnableHashedKeysListing = true 589 // settings to create BA session with legacy key format 590 globalConf.HashKeyFunction = "" 591 config.SetGlobal(globalConf) 592 defer ResetTestConfig() 593 594 ts := StartTest() 595 defer ts.Close() 596 597 // create session with legacy key format 598 session := testPrepareBasicAuth(false) 599 600 ts.Run(t, []test.TestCase{ 601 { 602 Method: "POST", 603 Path: "/tyk/keys/defaultuser", 604 Data: session, 605 AdminAuth: true, 606 Code: 200, 607 }, 608 { 609 Method: "GET", 610 Path: "/tyk/keys/defaultuser?username=true&org_id=default", 611 AdminAuth: true, 612 Code: 200, 613 }, 614 }...) 615 616 // set custom hashing function and check if we still can get BA session with legacy key format 617 globalConf.HashKeyFunction = storage.HashMurmur64 618 config.SetGlobal(globalConf) 619 620 ts.Run(t, []test.TestCase{ 621 { 622 Method: "GET", 623 Path: "/tyk/keys/defaultuser?username=true&org_id=default", 624 AdminAuth: true, 625 Code: 200, 626 }, 627 { 628 Method: "DELETE", 629 Path: "/tyk/keys/defaultuser?username=true&org_id=default", 630 AdminAuth: true, 631 Code: 200, 632 BodyMatch: `"action":"deleted"`, 633 }, 634 }...) 635 } 636 637 func testHashKeyHandlerHelper(t *testing.T, expectedHashSize int) { 638 ts := StartTest() 639 defer ts.Close() 640 641 BuildAndLoadAPI() 642 643 withAccess := CreateStandardSession() 644 withAccess.AccessRights = map[string]user.AccessDefinition{"test": { 645 APIID: "test", Versions: []string{"v1"}, 646 }} 647 withAccessJSON, _ := json.Marshal(withAccess) 648 649 myKey := "my_key_id" 650 myKeyHash := storage.HashKey(generateToken("default", myKey)) 651 652 if len(myKeyHash) != expectedHashSize { 653 t.Errorf("Expected hash size: %d, got %d. Hash: %s. Key: %s", expectedHashSize, len(myKeyHash), myKeyHash, myKey) 654 } 655 656 t.Run("Create, get and delete key with key hashing", func(t *testing.T) { 657 ts.Run(t, []test.TestCase{ 658 // create key 659 { 660 Method: "POST", 661 Path: "/tyk/keys/create", 662 Data: string(withAccessJSON), 663 AdminAuth: true, 664 Code: 200, 665 BodyMatch: `"key_hash"`, 666 }, 667 { 668 Method: "POST", 669 Path: "/tyk/keys", 670 Data: string(withAccessJSON), 671 AdminAuth: true, 672 Code: 200, 673 BodyMatch: `"key_hash"`, 674 }, 675 // create key with custom value 676 { 677 Method: "POST", 678 Path: "/tyk/keys/" + myKey, 679 Data: string(withAccessJSON), 680 AdminAuth: true, 681 Code: 200, 682 BodyMatch: fmt.Sprintf(`"key_hash":"%s"`, myKeyHash), 683 }, 684 // Update key by hash value with specifying hashed=true 685 { 686 Method: "PUT", 687 Path: "/tyk/keys/" + myKeyHash + "?hashed=true", 688 Data: string(withAccessJSON), 689 AdminAuth: true, 690 Code: 200, 691 BodyMatch: fmt.Sprintf(`"key":"%s"`, myKeyHash), 692 }, 693 // get one key by key name (API specified) 694 { 695 Method: "GET", 696 Path: "/tyk/keys/" + myKey + "?api_id=test", 697 Data: string(withAccessJSON), 698 AdminAuth: true, 699 Code: 200, 700 }, 701 // get one key by hash value with specifying hashed=true (no API specified) 702 { 703 Method: "GET", 704 Path: "/tyk/keys/" + myKeyHash + "?hashed=true", 705 Data: string(withAccessJSON), 706 AdminAuth: true, 707 Code: 200, 708 }, 709 // get one key by hash value with specifying hashed=true (API specified) 710 { 711 Method: "GET", 712 Path: "/tyk/keys/" + myKeyHash + "?hashed=true&api_id=test", 713 Data: string(withAccessJSON), 714 AdminAuth: true, 715 Code: 200, 716 }, 717 // get one key by hash value without specifying hashed=true 718 { 719 Method: "GET", 720 Path: "/tyk/keys/" + myKeyHash, 721 Data: string(withAccessJSON), 722 AdminAuth: true, 723 Code: 404, 724 }, 725 // get list of keys' hashes, no API specified 726 { 727 Method: "GET", 728 Path: "/tyk/keys", 729 Data: string(withAccessJSON), 730 AdminAuth: true, 731 Code: 200, 732 BodyMatch: myKeyHash, 733 }, 734 // get list of keys' hashes, API specified 735 { 736 Method: "GET", 737 Path: "/tyk/keys?api_id=test", 738 Data: string(withAccessJSON), 739 AdminAuth: true, 740 Code: 200, 741 BodyMatch: myKeyHash, 742 }, 743 // delete one key by hash value with specifying hashed=true 744 { 745 Method: "DELETE", 746 Path: "/tyk/keys/" + myKeyHash + "?hashed=true&api_id=test", 747 Data: string(withAccessJSON), 748 AdminAuth: true, 749 Code: 200, 750 }, 751 // check that key is not present any more 752 { 753 Method: "GET", 754 Path: "/tyk/keys/" + myKeyHash + "?hashed=true&api_id=test", 755 Data: string(withAccessJSON), 756 AdminAuth: true, 757 Code: 404, 758 }, 759 }...) 760 }) 761 } 762 763 func testHashFuncAndBAHelper(t *testing.T) { 764 ts := StartTest() 765 defer ts.Close() 766 767 session := testPrepareBasicAuth(false) 768 769 ts.Run(t, []test.TestCase{ 770 { 771 Method: "POST", 772 Path: "/tyk/keys/defaultuser", 773 Data: session, 774 AdminAuth: true, 775 Code: 200, 776 }, 777 { 778 Method: "GET", 779 Path: "/tyk/keys/defaultuser?username=true&org_id=default", 780 AdminAuth: true, 781 Code: 200, 782 }, 783 { 784 Method: "DELETE", 785 Path: "/tyk/keys/defaultuser?username=true&org_id=default", 786 AdminAuth: true, 787 Code: 200, 788 BodyMatch: `"action":"deleted"`, 789 }, 790 }...) 791 } 792 793 func TestHashKeyListingDisabled(t *testing.T) { 794 globalConf := config.Global() 795 // make it to use hashes for Redis keys 796 globalConf.HashKeys = true 797 // disable hashed keys listing 798 globalConf.EnableHashedKeysListing = false 799 config.SetGlobal(globalConf) 800 801 defer ResetTestConfig() 802 803 ts := StartTest() 804 defer ts.Close() 805 806 BuildAndLoadAPI() 807 808 withAccess := CreateStandardSession() 809 withAccess.AccessRights = map[string]user.AccessDefinition{"test": { 810 APIID: "test", Versions: []string{"v1"}, 811 }} 812 withAccessJSON, _ := json.Marshal(withAccess) 813 814 myKey := "my_key_id" 815 myKeyHash := storage.HashKey(generateToken("default", myKey)) 816 817 t.Run("Create, get and delete key with key hashing", func(t *testing.T) { 818 ts.Run(t, []test.TestCase{ 819 // create key 820 { 821 Method: "POST", 822 Path: "/tyk/keys/create", 823 Data: string(withAccessJSON), 824 AdminAuth: true, 825 Code: 200, 826 BodyMatch: `"key_hash"`, 827 }, 828 { 829 Method: "POST", 830 Path: "/tyk/keys", 831 Data: string(withAccessJSON), 832 AdminAuth: true, 833 Code: 200, 834 BodyMatch: `"key_hash"`, 835 }, 836 // create key with custom value 837 { 838 Method: "POST", 839 Path: "/tyk/keys/" + myKey, 840 Data: string(withAccessJSON), 841 AdminAuth: true, 842 Code: 200, 843 BodyMatch: fmt.Sprintf(`"key_hash":"%s"`, myKeyHash), 844 }, 845 // get one key by key name (API specified) 846 { 847 Method: "GET", 848 Path: "/tyk/keys/" + myKey + "?api_id=test", 849 Data: string(withAccessJSON), 850 AdminAuth: true, 851 Code: 200, 852 }, 853 // get one key by hash value with specifying hashed=true (no API specified) 854 { 855 Method: "GET", 856 Path: "/tyk/keys/" + myKeyHash + "?hashed=true", 857 Data: string(withAccessJSON), 858 AdminAuth: true, 859 Code: 200, 860 }, 861 // get one key by hash value with specifying hashed=true (API specified) 862 { 863 Method: "GET", 864 Path: "/tyk/keys/" + myKeyHash + "?hashed=true&api_id=test", 865 Data: string(withAccessJSON), 866 AdminAuth: true, 867 Code: 200, 868 }, 869 // get one key by hash value without specifying hashed=true 870 { 871 Method: "GET", 872 Path: "/tyk/keys/" + myKeyHash, 873 Data: string(withAccessJSON), 874 AdminAuth: true, 875 Code: 404, 876 }, 877 // get list of keys' hashes, no API specified 878 { 879 Method: "GET", 880 Path: "/tyk/keys", 881 Data: string(withAccessJSON), 882 AdminAuth: true, 883 Code: 404, 884 }, 885 // get list of keys' hashes, API specified 886 { 887 Method: "GET", 888 Path: "/tyk/keys?api_id=test", 889 Data: string(withAccessJSON), 890 AdminAuth: true, 891 Code: 404, 892 }, 893 // delete one key by hash value with specifying hashed=true 894 { 895 Method: "DELETE", 896 Path: "/tyk/keys/" + myKeyHash + "?hashed=true&api_id=test", 897 Data: string(withAccessJSON), 898 AdminAuth: true, 899 Code: 200, 900 }, 901 // check that key is not present any more 902 { 903 Method: "GET", 904 Path: "/tyk/keys/" + myKeyHash + "?hashed=true&api_id=test", 905 Data: string(withAccessJSON), 906 AdminAuth: true, 907 Code: 404, 908 }, 909 }...) 910 }) 911 } 912 913 func TestKeyHandler_HashingDisabled(t *testing.T) { 914 globalConf := config.Global() 915 // make it to NOT use hashes for Redis keys 916 globalConf.HashKeys = false 917 config.SetGlobal(globalConf) 918 919 defer ResetTestConfig() 920 921 ts := StartTest() 922 defer ts.Close() 923 924 BuildAndLoadAPI() 925 926 withAccess := CreateStandardSession() 927 withAccess.AccessRights = map[string]user.AccessDefinition{"test": { 928 APIID: "test", Versions: []string{"v1"}, 929 }} 930 withAccessJSON, _ := json.Marshal(withAccess) 931 932 myKeyID := "my_key_id" 933 token := generateToken("default", myKeyID) 934 myKeyHash := storage.HashKey(token) 935 936 t.Run("Create, get and delete key with key hashing", func(t *testing.T) { 937 _, _ = ts.Run(t, []test.TestCase{ 938 // create key 939 { 940 Method: "POST", 941 Path: "/tyk/keys/create", 942 Data: string(withAccessJSON), 943 AdminAuth: true, 944 Code: 200, 945 BodyNotMatch: `"key_hash"`, 946 }, 947 { 948 Method: "POST", 949 Path: "/tyk/keys", 950 Data: string(withAccessJSON), 951 AdminAuth: true, 952 Code: 200, 953 BodyNotMatch: `"key_hash"`, 954 }, 955 // create key with custom key ID 956 { 957 Method: "POST", 958 Path: "/tyk/keys/" + myKeyID, 959 Data: string(withAccessJSON), 960 AdminAuth: true, 961 Code: 200, 962 BodyMatch: fmt.Sprintf(`"key":"%s"`, token), 963 BodyNotMatch: fmt.Sprintf(`"key_hash":"%s"`, myKeyHash), 964 }, 965 // get one key by generated token 966 { 967 Method: "GET", 968 Path: "/tyk/keys/" + token, 969 AdminAuth: true, 970 Code: 200, 971 }, 972 // get one key by hash value with specifying hashed=true (no API specified) 973 { 974 Method: "GET", 975 Path: "/tyk/keys/" + myKeyHash + "?hashed=true", 976 AdminAuth: true, 977 Code: 400, 978 }, 979 // get one key by hash value with specifying hashed=true (API specified) 980 { 981 Method: "GET", 982 Path: "/tyk/keys/" + myKeyHash + "?hashed=true&api_id=test", 983 AdminAuth: true, 984 Code: 400, 985 }, 986 // delete one key by hash value with specifying hashed=true 987 { 988 Method: "DELETE", 989 Path: "/tyk/keys/" + myKeyHash + "?hashed=true&api_id=test", 990 AdminAuth: true, 991 Code: 200, 992 }, 993 }...) 994 }) 995 } 996 997 func TestInvalidateCache(t *testing.T) { 998 ts := StartTest() 999 defer ts.Close() 1000 1001 BuildAndLoadAPI() 1002 1003 ts.Run(t, []test.TestCase{ 1004 {Method: "DELETE", Path: "/tyk/cache/test", AdminAuth: true, Code: 200}, 1005 {Method: "DELETE", Path: "/tyk/cache/test/", AdminAuth: true, Code: 200}, 1006 }...) 1007 } 1008 1009 func TestGetOAuthClients(t *testing.T) { 1010 ts := StartTest() 1011 defer ts.Close() 1012 1013 BuildAndLoadAPI(func(spec *APISpec) { 1014 spec.UseOauth2 = true 1015 }) 1016 1017 oauthRequest := NewClientRequest{ 1018 ClientID: "test", 1019 ClientRedirectURI: "http://localhost", 1020 APIID: "test", 1021 ClientSecret: "secret", 1022 } 1023 validOauthRequest, _ := json.Marshal(oauthRequest) 1024 1025 ts.Run(t, []test.TestCase{ 1026 {Path: "/tyk/oauth/clients/unknown", AdminAuth: true, Code: 404}, 1027 {Path: "/tyk/oauth/clients/test", AdminAuth: true, Code: 200, BodyMatch: `\[\]`}, 1028 {Method: "POST", Path: "/tyk/oauth/clients/create", AdminAuth: true, Data: string(validOauthRequest), Code: 200}, 1029 {Path: "/tyk/oauth/clients/test", AdminAuth: true, Code: 200, BodyMatch: `\[{"client_id":"test"`}, 1030 }...) 1031 } 1032 1033 func TestCreateOAuthClient(t *testing.T) { 1034 ts := StartTest() 1035 defer ts.Close() 1036 1037 BuildAndLoadAPI( 1038 func(spec *APISpec) { 1039 spec.UseOauth2 = true 1040 }, 1041 func(spec *APISpec) { 1042 spec.APIID = "non_oauth_api" 1043 spec.UseOauth2 = false 1044 }, 1045 ) 1046 1047 CreatePolicy(func(p *user.Policy) { 1048 p.ID = "p1" 1049 p.AccessRights = map[string]user.AccessDefinition{ 1050 "test": { 1051 APIID: "test", 1052 }, 1053 } 1054 }) 1055 CreatePolicy(func(p *user.Policy) { 1056 p.ID = "p2" 1057 p.AccessRights = map[string]user.AccessDefinition{ 1058 "test": { 1059 APIID: "test", 1060 }, 1061 "abc": { 1062 APIID: "abc", 1063 }, 1064 } 1065 }) 1066 1067 tests := map[string]struct { 1068 req NewClientRequest 1069 code int 1070 bodyMatch string 1071 }{ 1072 "no api_id but policy_id provided": { 1073 req: NewClientRequest{ 1074 ClientID: "client_test1", 1075 PolicyID: "p1", 1076 }, 1077 code: http.StatusOK, 1078 bodyMatch: `client_id":"client_test1"`, 1079 }, 1080 "no policy_id but api_id provided": { 1081 req: NewClientRequest{ 1082 ClientID: "client_test2", 1083 APIID: "test", 1084 }, 1085 code: http.StatusOK, 1086 bodyMatch: `client_id":"client_test2"`, 1087 }, 1088 // "both api_id and policy_id provided": { 1089 // req: NewClientRequest{ 1090 // PolicyID: "p1", 1091 // APIID: "test", 1092 // }, 1093 // code: http.StatusBadRequest, 1094 // bodyMatch: "both api_id and policy_id specified", 1095 // }, 1096 "policy does not exist": { 1097 req: NewClientRequest{ 1098 PolicyID: "unknown", 1099 }, 1100 code: http.StatusBadRequest, 1101 bodyMatch: "Policy doesn't exist", 1102 }, 1103 "API does not exist": { 1104 req: NewClientRequest{ 1105 APIID: "unknown", 1106 }, 1107 code: http.StatusBadRequest, 1108 bodyMatch: "API doesn't exist", 1109 }, 1110 // "policy should contain only one API": { 1111 // req: NewClientRequest{ 1112 // PolicyID: "p2", 1113 // }, 1114 // code: http.StatusBadRequest, 1115 // bodyMatch: "should contain only one API", 1116 // }, 1117 "API is not OAuth": { 1118 req: NewClientRequest{ 1119 APIID: "non_oauth_api", 1120 }, 1121 code: http.StatusBadRequest, 1122 bodyMatch: "API is not OAuth2", 1123 }, 1124 } 1125 1126 for testName, testData := range tests { 1127 t.Run(testName, func(t *testing.T) { 1128 requestData, _ := json.Marshal(testData.req) 1129 ts.Run( 1130 t, 1131 test.TestCase{ 1132 Method: http.MethodPost, 1133 Path: "/tyk/oauth/clients/create", 1134 AdminAuth: true, 1135 Data: string(requestData), 1136 Code: testData.code, 1137 BodyMatch: testData.bodyMatch, 1138 }, 1139 ) 1140 }) 1141 } 1142 } 1143 1144 func TestUpdateOauthClientHandler(t *testing.T) { 1145 1146 ts := StartTest() 1147 defer ts.Close() 1148 1149 BuildAndLoadAPI( 1150 func(spec *APISpec) { 1151 spec.UseOauth2 = true 1152 }, 1153 func(spec *APISpec) { 1154 spec.APIID = "non_oauth_api" 1155 spec.UseOauth2 = false 1156 }, 1157 ) 1158 1159 CreatePolicy(func(p *user.Policy) { 1160 p.ID = "p1" 1161 p.AccessRights = map[string]user.AccessDefinition{ 1162 "test": { 1163 APIID: "test", 1164 }, 1165 } 1166 }) 1167 CreatePolicy(func(p *user.Policy) { 1168 p.ID = "p2" 1169 p.AccessRights = map[string]user.AccessDefinition{ 1170 "test": { 1171 APIID: "test", 1172 }, 1173 "abc": { 1174 APIID: "abc", 1175 }, 1176 } 1177 }) 1178 1179 var b bytes.Buffer 1180 1181 json.NewEncoder(&b).Encode(NewClientRequest{ 1182 ClientID: "12345", 1183 APIID: "test", 1184 PolicyID: "p1", 1185 }) 1186 1187 ts.Run( 1188 t, 1189 test.TestCase{ 1190 Method: http.MethodPost, 1191 Path: "/tyk/oauth/clients/create", 1192 AdminAuth: true, 1193 Data: b.String(), 1194 Code: http.StatusOK, 1195 BodyMatch: `"client_id":"12345"`, 1196 }, 1197 ) 1198 1199 tests := map[string]struct { 1200 req NewClientRequest 1201 code int 1202 bodyMatch string 1203 bodyNotMatch string 1204 }{ 1205 "Update description": { 1206 req: NewClientRequest{ 1207 ClientID: "12345", 1208 APIID: "test", 1209 PolicyID: "p1", 1210 Description: "Updated field", 1211 }, 1212 code: http.StatusOK, 1213 bodyMatch: `"description":"Updated field"`, 1214 bodyNotMatch: "", 1215 }, 1216 "Secret cannot be updated": { 1217 req: NewClientRequest{ 1218 ClientID: "12345", 1219 APIID: "test", 1220 PolicyID: "p1", 1221 Description: "Updated field", 1222 ClientSecret: "super-new-secret", 1223 }, 1224 code: http.StatusOK, 1225 bodyNotMatch: `"secret":"super-new-secret"`, 1226 bodyMatch: "", 1227 }, 1228 } 1229 1230 for testName, testData := range tests { 1231 t.Run(testName, func(t *testing.T) { 1232 requestData, _ := json.Marshal(testData.req) 1233 testCase := test.TestCase{ 1234 Method: http.MethodPut, 1235 Path: "/tyk/oauth/clients/test/12345", 1236 AdminAuth: true, 1237 Data: string(requestData), 1238 Code: testData.code, 1239 } 1240 1241 if testData.bodyMatch != "" { 1242 testCase.BodyMatch = testData.bodyMatch 1243 } 1244 1245 if testData.bodyNotMatch != "" { 1246 testCase.BodyNotMatch = testData.bodyNotMatch 1247 } 1248 1249 ts.Run(t, testCase) 1250 }) 1251 } 1252 } 1253 1254 func TestGroupResetHandler(t *testing.T) { 1255 didSubscribe := make(chan bool) 1256 didReload := make(chan bool) 1257 cacheStore := storage.RedisCluster{} 1258 cacheStore.Connect() 1259 1260 go func() { 1261 err := cacheStore.StartPubSubHandler(RedisPubSubChannel, func(v interface{}) { 1262 switch x := v.(type) { 1263 case *redis.Subscription: 1264 didSubscribe <- true 1265 case *redis.Message: 1266 notf := Notification{} 1267 if err := json.Unmarshal([]byte(x.Payload), ¬f); err != nil { 1268 t.Fatal(err) 1269 } 1270 if notf.Command == NoticeGroupReload { 1271 didReload <- true 1272 } 1273 } 1274 }) 1275 if err != nil { 1276 t.Log(err) 1277 t.Fail() 1278 close(didReload) 1279 } 1280 }() 1281 1282 uri := "/tyk/reload/group" 1283 1284 apisMu.Lock() 1285 apisByID = make(map[string]*APISpec) 1286 apisMu.Unlock() 1287 1288 LoadSampleAPI(apiTestDef) 1289 1290 recorder := httptest.NewRecorder() 1291 1292 // If we don't wait for the subscription to be done, we might do 1293 // the reload before pub/sub is in place to receive our message. 1294 <-didSubscribe 1295 req := withAuth(TestReq(t, "GET", uri, nil)) 1296 1297 mainRouter().ServeHTTP(recorder, req) 1298 1299 if recorder.Code != 200 { 1300 t.Fatal("Hot reload (group) failed, response code was: ", recorder.Code) 1301 } 1302 1303 apisMu.RLock() 1304 if len(apisByID) == 0 { 1305 t.Fatal("Hot reload (group) was triggered but no APIs were found.") 1306 } 1307 apisMu.RUnlock() 1308 1309 // We wait for the right notification (NoticeGroupReload), other 1310 // type of notifications may be received during tests, as this 1311 // is the cluster channel: 1312 <-didReload 1313 } 1314 1315 func TestHotReloadSingle(t *testing.T) { 1316 ReloadTestCase.Enable() 1317 defer ReloadTestCase.Disable() 1318 oldRouter := mainRouter() 1319 var wg sync.WaitGroup 1320 wg.Add(1) 1321 reloadURLStructure(wg.Done) 1322 ReloadTestCase.TickOk(t) 1323 wg.Wait() 1324 if mainRouter() == oldRouter { 1325 t.Fatal("router wasn't swapped") 1326 } 1327 } 1328 1329 func BenchmarkApiReload(b *testing.B) { 1330 b.ReportAllocs() 1331 1332 specs := make([]*APISpec, 100) 1333 1334 for i := 0; i < 100; i++ { 1335 specs[i] = BuildAndLoadAPI(func(spec *APISpec) { 1336 spec.APIID = strconv.Itoa(i + 1) 1337 })[0] 1338 } 1339 1340 b.ResetTimer() 1341 for i := 0; i < b.N; i++ { 1342 loadAPIEndpoints(nil) 1343 loadApps(specs) 1344 } 1345 } 1346 1347 func TestContextData(t *testing.T) { 1348 r := new(http.Request) 1349 if ctxGetData(r) != nil { 1350 t.Fatal("expected ctxGetData to return nil") 1351 } 1352 ctxSetData(r, map[string]interface{}{"foo": "bar"}) 1353 if ctxGetData(r) == nil { 1354 t.Fatal("expected ctxGetData to return non-nil") 1355 } 1356 defer func() { 1357 if r := recover(); r == nil { 1358 t.Fatal("expected ctxSetData of zero val to panic") 1359 } 1360 }() 1361 ctxSetData(r, nil) 1362 } 1363 1364 func TestContextSession(t *testing.T) { 1365 r := new(http.Request) 1366 if ctxGetSession(r) != nil { 1367 t.Fatal("expected ctxGetSession to return nil") 1368 } 1369 ctxSetSession(r, 1370 &user.SessionState{ 1371 Mutex: &sync.RWMutex{}, 1372 }, 1373 "", 1374 false) 1375 if ctxGetSession(r) == nil { 1376 t.Fatal("expected ctxGetSession to return non-nil") 1377 } 1378 defer func() { 1379 if r := recover(); r == nil { 1380 t.Fatal("expected ctxSetSession of zero val to panic") 1381 } 1382 }() 1383 ctxSetSession(r, nil, "", false) 1384 } 1385 1386 func TestApiLoaderLongestPathFirst(t *testing.T) { 1387 globalConf := config.Global() 1388 globalConf.EnableCustomDomains = true 1389 config.SetGlobal(globalConf) 1390 1391 defer ResetTestConfig() 1392 1393 type hostAndPath struct { 1394 host, path string 1395 } 1396 inputs := map[hostAndPath]bool{} 1397 hosts := []string{"host1.local", "host2.local", "host3.local"} 1398 paths := []string{"a", "ab", "a/b/c", "ab/c", "abc", "a/b/c"} 1399 // Use a map so that we get a somewhat random order when 1400 // iterating. Would be better to use math/rand.Shuffle once we 1401 // need only support Go 1.10 and later. 1402 for _, host := range hosts { 1403 for _, path := range paths { 1404 inputs[hostAndPath{host, path}] = true 1405 } 1406 } 1407 1408 var apis []*APISpec 1409 1410 for hp := range inputs { 1411 apis = append(apis, BuildAPI(func(spec *APISpec) { 1412 spec.APIID = uuid.NewV4().String() 1413 spec.Domain = hp.host 1414 spec.Proxy.ListenPath = "/" + hp.path 1415 })[0]) 1416 } 1417 1418 ts := StartTest() 1419 defer ts.Close() 1420 LoadAPI(apis...) 1421 1422 var testCases []test.TestCase 1423 1424 for hp := range inputs { 1425 testCases = append(testCases, test.TestCase{ 1426 Path: "/" + hp.path, 1427 Domain: hp.host, 1428 Code: 200, 1429 BodyMatch: `"Url":"/` + hp.path + `"`, 1430 }) 1431 } 1432 1433 ts.Run(t, testCases...) 1434 }