github.com/Tyktechnologies/tyk@v2.9.5+incompatible/gateway/policy_test.go (about) 1 package gateway 2 3 import ( 4 "encoding/json" 5 "net/http" 6 "net/http/httptest" 7 "reflect" 8 "sort" 9 "strconv" 10 "strings" 11 "sync" 12 "testing" 13 "time" 14 15 "github.com/lonelycode/go-uuid/uuid" 16 "github.com/stretchr/testify/assert" 17 18 "github.com/TykTechnologies/tyk/apidef" 19 "github.com/TykTechnologies/tyk/config" 20 "github.com/TykTechnologies/tyk/headers" 21 "github.com/TykTechnologies/tyk/test" 22 "github.com/TykTechnologies/tyk/user" 23 ) 24 25 func TestLoadPoliciesFromDashboardReLogin(t *testing.T) { 26 // Test Dashboard 27 ts := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { 28 w.WriteHeader(403) 29 })) 30 defer ts.Close() 31 32 globalConf := config.Global() 33 oldUseDBAppConfigs := globalConf.UseDBAppConfigs 34 globalConf.UseDBAppConfigs = false 35 config.SetGlobal(globalConf) 36 37 defer func() { 38 globalConf.UseDBAppConfigs = oldUseDBAppConfigs 39 config.SetGlobal(globalConf) 40 }() 41 42 allowExplicitPolicyID := config.Global().Policies.AllowExplicitPolicyID 43 44 policyMap := LoadPoliciesFromDashboard(ts.URL, "", allowExplicitPolicyID) 45 46 if policyMap != nil { 47 t.Error("Should be nil because got back 403 from Dashboard") 48 } 49 } 50 51 type dummySessionManager struct { 52 DefaultSessionManager 53 } 54 55 func (*dummySessionManager) UpdateSession(key string, sess *user.SessionState, ttl int64, hashed bool) error { 56 return nil 57 } 58 59 type testApplyPoliciesData struct { 60 name string 61 policies []string 62 errMatch string // substring 63 sessMatch func(*testing.T, *user.SessionState) // ignored if nil 64 session *user.SessionState 65 } 66 67 func testPrepareApplyPolicies() (*BaseMiddleware, []testApplyPoliciesData) { 68 policiesMu.RLock() 69 policiesByID = map[string]user.Policy{ 70 "nonpart1": { 71 ID: "p1", 72 AccessRights: map[string]user.AccessDefinition{"a": {}}, 73 }, 74 "nonpart2": { 75 ID: "p2", 76 AccessRights: map[string]user.AccessDefinition{"b": {}}, 77 }, 78 "nonpart3": { 79 ID: "p3", 80 AccessRights: map[string]user.AccessDefinition{"a": {}, "b": {}}, 81 }, 82 "difforg": {OrgID: "different"}, 83 "tags1": { 84 Partitions: user.PolicyPartitions{Quota: true}, 85 Tags: []string{"tagA"}, 86 }, 87 "tags2": { 88 Partitions: user.PolicyPartitions{RateLimit: true}, 89 Tags: []string{"tagX", "tagY"}, 90 }, 91 "inactive1": { 92 Partitions: user.PolicyPartitions{RateLimit: true}, 93 IsInactive: true, 94 }, 95 "inactive2": { 96 Partitions: user.PolicyPartitions{Quota: true}, 97 IsInactive: true, 98 }, 99 "quota1": { 100 Partitions: user.PolicyPartitions{Quota: true}, 101 QuotaMax: 2, 102 }, 103 "quota2": { 104 Partitions: user.PolicyPartitions{Quota: true}, 105 QuotaMax: 3, 106 }, 107 "rate1": { 108 Partitions: user.PolicyPartitions{RateLimit: true}, 109 Rate: 3, 110 }, 111 "rate2": { 112 Partitions: user.PolicyPartitions{RateLimit: true}, 113 Rate: 4, 114 }, 115 "rate3": { 116 Partitions: user.PolicyPartitions{RateLimit: true}, 117 Rate: 4, 118 Per: 4, 119 }, 120 "acl1": { 121 Partitions: user.PolicyPartitions{Acl: true}, 122 AccessRights: map[string]user.AccessDefinition{"a": {}}, 123 }, 124 "acl2": { 125 Partitions: user.PolicyPartitions{Acl: true}, 126 AccessRights: map[string]user.AccessDefinition{"b": {}}, 127 }, 128 "acl3": { 129 AccessRights: map[string]user.AccessDefinition{"c": {}}, 130 }, 131 "per_api_and_partitions": { 132 ID: "per_api_and_partitions", 133 Partitions: user.PolicyPartitions{ 134 PerAPI: true, 135 Quota: true, 136 RateLimit: true, 137 Acl: true, 138 }, 139 AccessRights: map[string]user.AccessDefinition{"d": { 140 Limit: &user.APILimit{ 141 QuotaMax: 1000, 142 QuotaRenewalRate: 3600, 143 Rate: 20, 144 Per: 1, 145 }, 146 }}, 147 }, 148 "per_api_and_some_partitions": { 149 ID: "per_api_and_some_partitions", 150 Partitions: user.PolicyPartitions{ 151 PerAPI: true, 152 Quota: false, 153 RateLimit: true, 154 Acl: false, 155 }, 156 AccessRights: map[string]user.AccessDefinition{"d": { 157 Limit: &user.APILimit{ 158 QuotaMax: 1000, 159 QuotaRenewalRate: 3600, 160 Rate: 20, 161 Per: 1, 162 }, 163 }}, 164 }, 165 "per_api_and_no_other_partitions": { 166 ID: "per_api_and_no_other_partitions", 167 Partitions: user.PolicyPartitions{ 168 PerAPI: true, 169 Quota: false, 170 RateLimit: false, 171 Acl: false, 172 }, 173 AccessRights: map[string]user.AccessDefinition{ 174 "d": { 175 Limit: &user.APILimit{ 176 QuotaMax: 1000, 177 QuotaRenewalRate: 3600, 178 Rate: 20, 179 Per: 1, 180 }, 181 }, 182 "c": { 183 Limit: &user.APILimit{ 184 QuotaMax: -1, 185 Rate: 2000, 186 Per: 60, 187 }, 188 }, 189 }, 190 }, 191 "per_api_with_the_same_api": { 192 ID: "per_api_with_the_same_api", 193 Partitions: user.PolicyPartitions{ 194 PerAPI: true, 195 Quota: false, 196 RateLimit: false, 197 Acl: false, 198 }, 199 AccessRights: map[string]user.AccessDefinition{ 200 "d": { 201 Limit: &user.APILimit{ 202 QuotaMax: 5000, 203 QuotaRenewalRate: 3600, 204 Rate: 200, 205 Per: 10, 206 }, 207 }, 208 }, 209 }, 210 "per_api_with_limit_set_from_policy": { 211 ID: "per_api_with_limit_set_from_policy", 212 QuotaMax: -1, 213 Rate: 300, 214 Per: 1, 215 Partitions: user.PolicyPartitions{ 216 PerAPI: true, 217 Quota: false, 218 RateLimit: false, 219 Acl: false, 220 }, 221 AccessRights: map[string]user.AccessDefinition{ 222 "d": { 223 Limit: &user.APILimit{ 224 QuotaMax: 5000, 225 QuotaRenewalRate: 3600, 226 Rate: 200, 227 Per: 10, 228 }, 229 }, 230 "e": {}, 231 }, 232 }, 233 "per-path1": { 234 ID: "per_path_1", 235 AccessRights: map[string]user.AccessDefinition{"a": { 236 AllowedURLs: []user.AccessSpec{ 237 {URL: "/user", Methods: []string{"GET"}}, 238 }, 239 }, "b": { 240 AllowedURLs: []user.AccessSpec{ 241 {URL: "/", Methods: []string{"PUT"}}, 242 }, 243 }}, 244 }, 245 "per-path2": { 246 ID: "per_path_2", 247 AccessRights: map[string]user.AccessDefinition{"a": { 248 AllowedURLs: []user.AccessSpec{ 249 {URL: "/user", Methods: []string{"GET", "POST"}}, 250 {URL: "/companies", Methods: []string{"GET", "POST"}}, 251 }, 252 }}, 253 }, 254 } 255 policiesMu.RUnlock() 256 bmid := &BaseMiddleware{Spec: &APISpec{ 257 APIDefinition: &apidef.APIDefinition{}, 258 SessionManager: &dummySessionManager{}, 259 }} 260 tests := []testApplyPoliciesData{ 261 { 262 "Empty", nil, 263 "", nil, nil, 264 }, 265 { 266 "Single", []string{"nonpart1"}, 267 "", nil, nil, 268 }, 269 { 270 "Missing", []string{"nonexistent"}, 271 "not found", nil, nil, 272 }, 273 { 274 "DiffOrg", []string{"difforg"}, 275 "different org", nil, nil, 276 }, 277 { 278 name: "MultiNonPart", 279 policies: []string{"nonpart1", "nonpart2"}, 280 sessMatch: func(t *testing.T, s *user.SessionState) { 281 want := map[string]user.AccessDefinition{ 282 "a": { 283 Limit: &user.APILimit{}, 284 AllowanceScope: "p1", 285 }, 286 "b": { 287 Limit: &user.APILimit{}, 288 AllowanceScope: "p2", 289 }, 290 } 291 292 assert.Equal(t, want, s.AccessRights) 293 }, 294 }, 295 { 296 name: "MultiACLPolicy", 297 policies: []string{"nonpart3"}, 298 sessMatch: func(t *testing.T, s *user.SessionState) { 299 want := map[string]user.AccessDefinition{ 300 "a": { 301 Limit: &user.APILimit{}, 302 }, 303 "b": { 304 Limit: &user.APILimit{}, 305 }, 306 } 307 308 assert.Equal(t, want, s.AccessRights) 309 }, 310 }, 311 { 312 "NonpartAndPart", []string{"nonpart1", "quota1"}, 313 "", nil, nil, 314 }, 315 { 316 "TagMerge", []string{"tags1", "tags2"}, 317 "", func(t *testing.T, s *user.SessionState) { 318 want := []string{"key-tag", "tagA", "tagX", "tagY"} 319 sort.Strings(s.Tags) 320 321 assert.Equal(t, want, s.Tags) 322 }, &user.SessionState{ 323 Mutex: &sync.RWMutex{}, 324 Tags: []string{"key-tag"}, 325 }, 326 }, 327 { 328 "InactiveMergeOne", []string{"tags1", "inactive1"}, 329 "", func(t *testing.T, s *user.SessionState) { 330 if !s.IsInactive { 331 t.Fatalf("want IsInactive to be true") 332 } 333 }, nil, 334 }, 335 { 336 "InactiveMergeAll", []string{"inactive1", "inactive2"}, 337 "", func(t *testing.T, s *user.SessionState) { 338 if !s.IsInactive { 339 t.Fatalf("want IsInactive to be true") 340 } 341 }, nil, 342 }, 343 { 344 "InactiveWithSession", []string{"tags1", "tags2"}, 345 "", func(t *testing.T, s *user.SessionState) { 346 if !s.IsInactive { 347 t.Fatalf("want IsInactive to be true") 348 } 349 }, &user.SessionState{ 350 IsInactive: true, 351 Mutex: &sync.RWMutex{}, 352 }, 353 }, 354 { 355 "QuotaPart", []string{"quota1"}, 356 "", func(t *testing.T, s *user.SessionState) { 357 if s.QuotaMax != 2 { 358 t.Fatalf("want QuotaMax to be 2") 359 } 360 }, nil, 361 }, 362 { 363 "QuotaParts", []string{"quota1", "quota2"}, 364 "", func(t *testing.T, s *user.SessionState) { 365 if s.QuotaMax != 3 { 366 t.Fatalf("Should pick bigger value") 367 } 368 }, nil, 369 }, 370 { 371 "RatePart", []string{"rate1"}, 372 "", func(t *testing.T, s *user.SessionState) { 373 if s.Rate != 3 { 374 t.Fatalf("want Rate to be 3") 375 } 376 }, nil, 377 }, 378 { 379 "RateParts", []string{"rate1", "rate2"}, 380 "", func(t *testing.T, s *user.SessionState) { 381 if s.Rate != 4 { 382 t.Fatalf("Should pick bigger value") 383 } 384 }, nil, 385 }, 386 { 387 "AclPart", []string{"acl1"}, 388 "", func(t *testing.T, s *user.SessionState) { 389 want := map[string]user.AccessDefinition{"a": {Limit: &user.APILimit{}}} 390 391 assert.Equal(t, want, s.AccessRights) 392 }, nil, 393 }, 394 { 395 "AclPart", []string{"acl1", "acl2"}, 396 "", func(t *testing.T, s *user.SessionState) { 397 want := map[string]user.AccessDefinition{"a": {Limit: &user.APILimit{}}, "b": {Limit: &user.APILimit{}}} 398 assert.Equal(t, want, s.AccessRights) 399 }, nil, 400 }, 401 { 402 "RightsUpdate", []string{"acl3"}, 403 "", func(t *testing.T, s *user.SessionState) { 404 newPolicy := user.Policy{ 405 AccessRights: map[string]user.AccessDefinition{"a": {Limit: &user.APILimit{}}, "b": {Limit: &user.APILimit{}}, "c": {Limit: &user.APILimit{}}}, 406 } 407 policiesMu.Lock() 408 policiesByID["acl3"] = newPolicy 409 policiesMu.Unlock() 410 err := bmid.ApplyPolicies(s) 411 if err != nil { 412 t.Fatalf("couldn't apply policy: %s", err.Error()) 413 } 414 assert.Equal(t, newPolicy.AccessRights, s.AccessRights) 415 }, nil, 416 }, 417 { 418 name: "Per API is set with other partitions to true", 419 policies: []string{"per_api_and_partitions"}, 420 errMatch: "cannot apply policy per_api_and_partitions which has per_api and any of partitions set", 421 }, 422 { 423 name: "Per API is set to true with some partitions set to true", 424 policies: []string{"per_api_and_some_partitions"}, 425 errMatch: "cannot apply policy per_api_and_some_partitions which has per_api and any of partitions set", 426 }, 427 { 428 name: "Per API is set to true with no other partitions set to true", 429 policies: []string{"per_api_and_no_other_partitions"}, 430 sessMatch: func(t *testing.T, s *user.SessionState) { 431 want := map[string]user.AccessDefinition{ 432 "d": { 433 Limit: &user.APILimit{ 434 QuotaMax: 1000, 435 QuotaRenewalRate: 3600, 436 Rate: 20, 437 Per: 1, 438 }, 439 AllowanceScope: "d", 440 }, 441 "c": { 442 Limit: &user.APILimit{ 443 QuotaMax: -1, 444 Rate: 2000, 445 Per: 60, 446 }, 447 AllowanceScope: "c", 448 }, 449 } 450 451 assert.Equal(t, want, s.AccessRights) 452 }, 453 }, 454 { 455 name: "several policies with Per API set to true but specifying limit for the same API", 456 policies: []string{"per_api_and_no_other_partitions", "per_api_with_the_same_api"}, 457 errMatch: "cannot apply multiple policies when some have per_api set and some are partitioned", 458 }, 459 { 460 name: "several policies, mixed the one which has Per API set to true and partitioned ones", 461 policies: []string{"per_api_and_no_other_partitions", "quota1"}, 462 errMatch: "", 463 }, 464 { 465 name: "several policies, mixed the one which has Per API set to true and partitioned ones (different order)", 466 policies: []string{"rate1", "per_api_and_no_other_partitions"}, 467 errMatch: "", 468 }, 469 { 470 name: "Per API is set to true and some API gets limit set from policy's fields", 471 policies: []string{"per_api_with_limit_set_from_policy"}, 472 sessMatch: func(t *testing.T, s *user.SessionState) { 473 want := map[string]user.AccessDefinition{ 474 "e": { 475 Limit: &user.APILimit{ 476 QuotaMax: -1, 477 Rate: 300, 478 Per: 1, 479 }, 480 AllowanceScope: "e", 481 }, 482 "d": { 483 Limit: &user.APILimit{ 484 QuotaMax: 5000, 485 QuotaRenewalRate: 3600, 486 Rate: 200, 487 Per: 10, 488 }, 489 AllowanceScope: "d", 490 }, 491 } 492 493 assert.Equal(t, want, s.AccessRights) 494 }, 495 }, 496 { 497 name: "Merge per path rules for the same API", 498 policies: []string{"per-path2", "per-path1"}, 499 sessMatch: func(t *testing.T, s *user.SessionState) { 500 want := map[string]user.AccessDefinition{ 501 "a": { 502 AllowedURLs: []user.AccessSpec{ 503 {URL: "/user", Methods: []string{"GET", "POST", "GET"}}, 504 {URL: "/companies", Methods: []string{"GET", "POST"}}, 505 }, 506 Limit: &user.APILimit{}, 507 }, 508 "b": { 509 AllowedURLs: []user.AccessSpec{ 510 {URL: "/", Methods: []string{"PUT"}}, 511 }, 512 Limit: &user.APILimit{}, 513 }, 514 } 515 516 assert.Equal(t, want, s.AccessRights) 517 }, 518 }, 519 { 520 name: "inherit quota and rate from partitioned policies", 521 policies: []string{"quota1", "rate3"}, 522 sessMatch: func(t *testing.T, s *user.SessionState) { 523 if s.QuotaMax != 2 { 524 t.Fatalf("quota should be the same as quota policy") 525 } 526 if s.Rate != 4 { 527 t.Fatalf("rate should be the same as rate policy") 528 } 529 if s.Per != 4 { 530 t.Fatalf("Rate per seconds should be the same as rate policy") 531 } 532 }, 533 }, 534 { 535 name: "inherit quota and rate from partitioned policies applied in different order", 536 policies: []string{"rate3", "quota1"}, 537 sessMatch: func(t *testing.T, s *user.SessionState) { 538 if s.QuotaMax != 2 { 539 t.Fatalf("quota should be the same as quota policy") 540 } 541 if s.Rate != 4 { 542 t.Fatalf("rate should be the same as rate policy") 543 } 544 if s.Per != 4 { 545 t.Fatalf("Rate per seconds should be the same as rate policy") 546 } 547 }, 548 }, 549 } 550 551 return bmid, tests 552 } 553 554 func TestApplyPolicies(t *testing.T) { 555 bmid, tests := testPrepareApplyPolicies() 556 557 for _, tc := range tests { 558 t.Run(tc.name, func(t *testing.T) { 559 sess := tc.session 560 if sess == nil { 561 sess = &user.SessionState{Mutex: &sync.RWMutex{}} 562 } 563 sess.SetPolicies(tc.policies...) 564 errStr := "" 565 if err := bmid.ApplyPolicies(sess); err != nil { 566 errStr = err.Error() 567 } 568 if tc.errMatch == "" && errStr != "" { 569 t.Fatalf("didn't want err but got %s", errStr) 570 } else if !strings.Contains(errStr, tc.errMatch) { 571 t.Fatalf("error %q doesn't match %q", 572 errStr, tc.errMatch) 573 } 574 if tc.sessMatch != nil { 575 tc.sessMatch(t, sess) 576 } 577 }) 578 } 579 } 580 581 func BenchmarkApplyPolicies(b *testing.B) { 582 b.ReportAllocs() 583 584 bmid, tests := testPrepareApplyPolicies() 585 586 for i := 0; i < b.N; i++ { 587 for _, tc := range tests { 588 sess := &user.SessionState{Mutex: &sync.RWMutex{}} 589 sess.SetPolicies(tc.policies...) 590 bmid.ApplyPolicies(sess) 591 } 592 } 593 } 594 595 func TestApplyPoliciesQuotaAPILimit(t *testing.T) { 596 policiesMu.RLock() 597 policy := user.Policy{ 598 ID: "two_of_three_with_api_limit", 599 Per: 1, 600 Rate: 1000, 601 QuotaMax: 50, 602 QuotaRenewalRate: 3600, 603 OrgID: "default", 604 Partitions: user.PolicyPartitions{ 605 PerAPI: true, 606 Quota: false, 607 RateLimit: false, 608 Acl: false, 609 }, 610 AccessRights: map[string]user.AccessDefinition{ 611 "api1": { 612 Versions: []string{"v1"}, 613 Limit: &user.APILimit{ 614 QuotaMax: 100, 615 QuotaRenewalRate: 3600, 616 Rate: 1000, 617 Per: 1, 618 }, 619 }, 620 "api2": { 621 Versions: []string{"v1"}, 622 Limit: &user.APILimit{ 623 QuotaMax: 200, 624 QuotaRenewalRate: 3600, 625 Rate: 1000, 626 Per: 1, 627 }, 628 }, 629 "api3": { 630 Versions: []string{"v1"}, 631 }, 632 }, 633 } 634 policiesByID = map[string]user.Policy{ 635 "two_of_three_with_api_limit": policy, 636 } 637 policiesMu.RUnlock() 638 639 ts := StartTest() 640 defer ts.Close() 641 642 // load APIs 643 BuildAndLoadAPI( 644 func(spec *APISpec) { 645 spec.Name = "api 1" 646 spec.APIID = "api1" 647 spec.UseKeylessAccess = false 648 spec.Proxy.ListenPath = "/api1" 649 spec.OrgID = "default" 650 }, 651 func(spec *APISpec) { 652 spec.Name = "api 2" 653 spec.APIID = "api2" 654 spec.UseKeylessAccess = false 655 spec.Proxy.ListenPath = "/api2" 656 spec.OrgID = "default" 657 }, 658 func(spec *APISpec) { 659 spec.Name = "api 3" 660 spec.APIID = "api3" 661 spec.UseKeylessAccess = false 662 spec.Proxy.ListenPath = "/api3" 663 spec.OrgID = "default" 664 }, 665 ) 666 667 // create test session 668 session := &user.SessionState{ 669 Mutex: &sync.RWMutex{}, 670 ApplyPolicies: []string{"two_of_three_with_api_limit"}, 671 OrgID: "default", 672 AccessRights: map[string]user.AccessDefinition{ 673 "api1": { 674 APIID: "api1", 675 Versions: []string{"v1"}, 676 }, 677 "api2": { 678 APIID: "api2", 679 Versions: []string{"v1"}, 680 }, 681 "api3": { 682 APIID: "api3", 683 Versions: []string{"v1"}, 684 }, 685 }, 686 } 687 688 // create key 689 key := uuid.New() 690 ts.Run(t, []test.TestCase{ 691 {Method: http.MethodPost, Path: "/tyk/keys/" + key, Data: session, AdminAuth: true, Code: 200}, 692 }...) 693 694 // run requests to different APIs 695 authHeader := map[string]string{"Authorization": key} 696 ts.Run(t, []test.TestCase{ 697 // 2 requests to api1, API limit quota remaining should be 98 698 {Method: http.MethodGet, Path: "/api1", Headers: authHeader, Code: http.StatusOK, 699 HeadersMatch: map[string]string{headers.XRateLimitRemaining: "99"}}, 700 {Method: http.MethodGet, Path: "/api1", Headers: authHeader, Code: http.StatusOK, 701 HeadersMatch: map[string]string{headers.XRateLimitRemaining: "98"}}, 702 // 3 requests to api2, API limit quota remaining should be 197 703 {Method: http.MethodGet, Path: "/api2", Headers: authHeader, Code: http.StatusOK, 704 HeadersMatch: map[string]string{headers.XRateLimitRemaining: "199"}}, 705 {Method: http.MethodGet, Path: "/api2", Headers: authHeader, Code: http.StatusOK, 706 HeadersMatch: map[string]string{headers.XRateLimitRemaining: "198"}}, 707 {Method: http.MethodGet, Path: "/api2", Headers: authHeader, Code: http.StatusOK, 708 HeadersMatch: map[string]string{headers.XRateLimitRemaining: "197"}}, 709 // 5 requests to api3, API limit quota remaining should be 45 710 {Method: http.MethodGet, Path: "/api3", Headers: authHeader, Code: http.StatusOK, 711 HeadersMatch: map[string]string{headers.XRateLimitRemaining: "49"}}, 712 {Method: http.MethodGet, Path: "/api3", Headers: authHeader, Code: http.StatusOK, 713 HeadersMatch: map[string]string{headers.XRateLimitRemaining: "48"}}, 714 {Method: http.MethodGet, Path: "/api3", Headers: authHeader, Code: http.StatusOK, 715 HeadersMatch: map[string]string{headers.XRateLimitRemaining: "47"}}, 716 {Method: http.MethodGet, Path: "/api3", Headers: authHeader, Code: http.StatusOK, 717 HeadersMatch: map[string]string{headers.XRateLimitRemaining: "46"}}, 718 {Method: http.MethodGet, Path: "/api3", Headers: authHeader, Code: http.StatusOK, 719 HeadersMatch: map[string]string{headers.XRateLimitRemaining: "45"}}, 720 }...) 721 722 // check key session 723 ts.Run(t, []test.TestCase{ 724 { 725 Method: http.MethodGet, 726 Path: "/tyk/keys/" + key, 727 AdminAuth: true, 728 Code: http.StatusOK, 729 BodyMatchFunc: func(data []byte) bool { 730 sessionData := user.SessionState{Mutex: &sync.RWMutex{}} 731 if err := json.Unmarshal(data, &sessionData); err != nil { 732 t.Log(err.Error()) 733 return false 734 } 735 api1Limit := sessionData.AccessRights["api1"].Limit 736 if api1Limit == nil { 737 t.Log("api1 limit is not set") 738 return false 739 } 740 api1LimitExpected := user.APILimit{ 741 Rate: 1000, 742 Per: 1, 743 QuotaMax: 100, 744 QuotaRenewalRate: 3600, 745 QuotaRenews: api1Limit.QuotaRenews, 746 QuotaRemaining: 98, 747 } 748 if !reflect.DeepEqual(*api1Limit, api1LimitExpected) { 749 t.Log("api1 limit received:", *api1Limit, "expected:", api1LimitExpected) 750 return false 751 } 752 api2Limit := sessionData.AccessRights["api2"].Limit 753 if api2Limit == nil { 754 t.Log("api2 limit is not set") 755 return false 756 } 757 api2LimitExpected := user.APILimit{ 758 Rate: 1000, 759 Per: 1, 760 QuotaMax: 200, 761 QuotaRenewalRate: 3600, 762 QuotaRenews: api2Limit.QuotaRenews, 763 QuotaRemaining: 197, 764 } 765 if !reflect.DeepEqual(*api2Limit, api2LimitExpected) { 766 t.Log("api2 limit received:", *api2Limit, "expected:", api2LimitExpected) 767 return false 768 } 769 api3Limit := sessionData.AccessRights["api3"].Limit 770 if api3Limit == nil { 771 t.Log("api3 limit is not set") 772 return false 773 } 774 api3LimitExpected := user.APILimit{ 775 Rate: 1000, 776 Per: 1, 777 QuotaMax: 50, 778 QuotaRenewalRate: 3600, 779 QuotaRenews: api3Limit.QuotaRenews, 780 QuotaRemaining: 45, 781 } 782 783 if !reflect.DeepEqual(*api3Limit, api3LimitExpected) { 784 t.Log("api3 limit received:", *api3Limit, "expected:", api3LimitExpected) 785 return false 786 } 787 return true 788 }, 789 }, 790 }...) 791 792 // Reset quota 793 ts.Run(t, []test.TestCase{ 794 { 795 Method: http.MethodPut, 796 Path: "/tyk/keys/" + key, 797 AdminAuth: true, 798 Code: http.StatusOK, 799 Data: session, 800 }, 801 { 802 Method: http.MethodGet, 803 Path: "/tyk/keys/" + key, 804 AdminAuth: true, 805 Code: http.StatusOK, 806 BodyMatchFunc: func(data []byte) bool { 807 sessionData := user.SessionState{Mutex: &sync.RWMutex{}} 808 if err := json.Unmarshal(data, &sessionData); err != nil { 809 t.Log(err.Error()) 810 return false 811 } 812 api1Limit := sessionData.AccessRights["api1"].Limit 813 if api1Limit == nil { 814 t.Error("api1 limit is not set") 815 return false 816 } 817 818 if api1Limit.QuotaRemaining != 100 { 819 t.Error("Should reset quota:", api1Limit.QuotaRemaining) 820 return false 821 } 822 823 return true 824 }, 825 }, 826 }...) 827 } 828 829 func TestApplyMultiPolicies(t *testing.T) { 830 policiesMu.RLock() 831 policy1 := user.Policy{ 832 ID: "policy1", 833 Rate: 1000, 834 Per: 1, 835 QuotaMax: 50, 836 QuotaRenewalRate: 3600, 837 OrgID: "default", 838 AccessRights: map[string]user.AccessDefinition{ 839 "api1": { 840 Versions: []string{"v1"}, 841 }, 842 }, 843 } 844 845 policy2 := user.Policy{ 846 ID: "policy2", 847 Rate: 100, 848 Per: 1, 849 QuotaMax: 100, 850 QuotaRenewalRate: 3600, 851 OrgID: "default", 852 AccessRights: map[string]user.AccessDefinition{ 853 "api2": { 854 Versions: []string{"v1"}, 855 }, 856 "api3": { 857 Versions: []string{"v1"}, 858 }, 859 }, 860 } 861 862 policiesByID = map[string]user.Policy{ 863 "policy1": policy1, 864 "policy2": policy2, 865 } 866 policiesMu.RUnlock() 867 868 ts := StartTest() 869 defer ts.Close() 870 871 // load APIs 872 BuildAndLoadAPI( 873 func(spec *APISpec) { 874 spec.Name = "api 1" 875 spec.APIID = "api1" 876 spec.UseKeylessAccess = false 877 spec.Proxy.ListenPath = "/api1" 878 spec.OrgID = "default" 879 }, 880 func(spec *APISpec) { 881 spec.Name = "api 2" 882 spec.APIID = "api2" 883 spec.UseKeylessAccess = false 884 spec.Proxy.ListenPath = "/api2" 885 spec.OrgID = "default" 886 }, 887 func(spec *APISpec) { 888 spec.Name = "api 3" 889 spec.APIID = "api3" 890 spec.UseKeylessAccess = false 891 spec.Proxy.ListenPath = "/api3" 892 spec.OrgID = "default" 893 }, 894 ) 895 896 // create test session 897 session := &user.SessionState{ 898 ApplyPolicies: []string{"policy1", "policy2"}, 899 OrgID: "default", 900 Mutex: &sync.RWMutex{}, 901 } 902 903 // create key 904 key := uuid.New() 905 ts.Run(t, []test.TestCase{ 906 {Method: http.MethodPost, Path: "/tyk/keys/" + key, Data: session, AdminAuth: true, Code: 200}, 907 }...) 908 909 // run requests to different APIs 910 authHeader := map[string]string{"Authorization": key} 911 ts.Run(t, []test.TestCase{ 912 // 2 requests to api1, API limit quota remaining should be 48 913 {Path: "/api1", Headers: authHeader, Code: http.StatusOK, 914 HeadersMatch: map[string]string{headers.XRateLimitRemaining: "49"}}, 915 {Path: "/api1", Headers: authHeader, Code: http.StatusOK, 916 HeadersMatch: map[string]string{headers.XRateLimitRemaining: "48"}}, 917 918 // 3 requests to api2, API limit quota remaining should be 197 919 {Path: "/api2", Headers: authHeader, Code: http.StatusOK, 920 HeadersMatch: map[string]string{headers.XRateLimitRemaining: "99"}}, 921 {Path: "/api2", Headers: authHeader, Code: http.StatusOK, 922 HeadersMatch: map[string]string{headers.XRateLimitRemaining: "98"}}, 923 {Path: "/api2", Headers: authHeader, Code: http.StatusOK, 924 HeadersMatch: map[string]string{headers.XRateLimitRemaining: "97"}}, 925 926 // 3 requests to api3, should consume policy2 quota, same as for api2 927 {Path: "/api3", Headers: authHeader, Code: http.StatusOK, 928 HeadersMatch: map[string]string{headers.XRateLimitRemaining: "96"}}, 929 {Path: "/api3", Headers: authHeader, Code: http.StatusOK, 930 HeadersMatch: map[string]string{headers.XRateLimitRemaining: "95"}}, 931 {Path: "/api3", Headers: authHeader, Code: http.StatusOK, 932 HeadersMatch: map[string]string{headers.XRateLimitRemaining: "94"}}, 933 }...) 934 935 // check key session 936 ts.Run(t, []test.TestCase{ 937 { 938 Method: http.MethodGet, 939 Path: "/tyk/keys/" + key, 940 AdminAuth: true, 941 Code: http.StatusOK, 942 BodyMatchFunc: func(data []byte) bool { 943 sessionData := user.SessionState{Mutex: &sync.RWMutex{}} 944 json.Unmarshal(data, &sessionData) 945 946 policy1Expected := user.APILimit{ 947 Rate: 1000, 948 Per: 1, 949 QuotaMax: 50, 950 QuotaRenewalRate: 3600, 951 QuotaRenews: sessionData.AccessRights["api1"].Limit.QuotaRenews, 952 QuotaRemaining: 48, 953 } 954 assert.Equal(t, policy1Expected, *sessionData.AccessRights["api1"].Limit, "API1 limit do not match") 955 956 policy2Expected := user.APILimit{ 957 Rate: 100, 958 Per: 1, 959 QuotaMax: 100, 960 QuotaRenewalRate: 3600, 961 QuotaRenews: sessionData.AccessRights["api2"].Limit.QuotaRenews, 962 QuotaRemaining: 94, 963 } 964 965 assert.Equal(t, policy2Expected, *sessionData.AccessRights["api2"].Limit, "API2 limit do not match") 966 assert.Equal(t, policy2Expected, *sessionData.AccessRights["api3"].Limit, "API3 limit do not match") 967 968 return true 969 }, 970 }, 971 }...) 972 973 // Reset quota 974 ts.Run(t, []test.TestCase{ 975 { 976 Method: http.MethodPut, 977 Path: "/tyk/keys/" + key, 978 AdminAuth: true, 979 Code: http.StatusOK, 980 Data: session, 981 }, 982 { 983 Method: http.MethodGet, 984 Path: "/tyk/keys/" + key, 985 AdminAuth: true, 986 Code: http.StatusOK, 987 BodyMatchFunc: func(data []byte) bool { 988 sessionData := user.SessionState{Mutex: &sync.RWMutex{}} 989 json.Unmarshal(data, &sessionData) 990 991 assert.EqualValues(t, 50, sessionData.AccessRights["api1"].Limit.QuotaRemaining, "should reset policy1 quota") 992 assert.EqualValues(t, 100, sessionData.AccessRights["api2"].Limit.QuotaRemaining, "should reset policy2 quota") 993 assert.EqualValues(t, 100, sessionData.AccessRights["api3"].Limit.QuotaRemaining, "should reset policy2 quota") 994 995 return true 996 }, 997 }, 998 }...) 999 1000 // Rate limits before 1001 ts.Run(t, []test.TestCase{ 1002 // 2 requests to api1, API limit quota remaining should be 48 1003 {Path: "/api1", Headers: authHeader, Code: http.StatusOK, 1004 HeadersMatch: map[string]string{headers.XRateLimitRemaining: "49"}}, 1005 {Path: "/api1", Headers: authHeader, Code: http.StatusOK, 1006 HeadersMatch: map[string]string{headers.XRateLimitRemaining: "48"}}, 1007 }...) 1008 1009 policiesMu.RLock() 1010 policy1.Rate = 1 1011 policy1.LastUpdated = strconv.Itoa(int(time.Now().Unix() + 1)) 1012 DRLManager.SetCurrentTokenValue(100) 1013 defer DRLManager.SetCurrentTokenValue(0) 1014 1015 policiesByID = map[string]user.Policy{ 1016 "policy1": policy1, 1017 "policy2": policy2, 1018 } 1019 policiesMu.RUnlock() 1020 1021 // Rate limits after policy update 1022 ts.Run(t, []test.TestCase{ 1023 {Path: "/api1", Headers: authHeader, Code: http.StatusOK, 1024 HeadersMatch: map[string]string{headers.XRateLimitRemaining: "47"}}, 1025 {Path: "/api1", Headers: authHeader, Code: http.StatusTooManyRequests}, 1026 }...) 1027 1028 } 1029 1030 func TestPerAPIPolicyUpdate(t *testing.T) { 1031 policiesMu.RLock() 1032 policy := user.Policy{ 1033 ID: "per_api_policy_with_two_apis", 1034 OrgID: "default", 1035 Partitions: user.PolicyPartitions{ 1036 PerAPI: true, 1037 Quota: false, 1038 RateLimit: false, 1039 Acl: false, 1040 }, 1041 AccessRights: map[string]user.AccessDefinition{ 1042 "api1": { 1043 Versions: []string{"v1"}, 1044 }, 1045 "api2": { 1046 Versions: []string{"v1"}, 1047 }, 1048 }, 1049 } 1050 policiesByID = map[string]user.Policy{ 1051 "per_api_policy_with_two_apis": policy, 1052 } 1053 policiesMu.RUnlock() 1054 1055 ts := StartTest() 1056 defer ts.Close() 1057 1058 // load APIs 1059 BuildAndLoadAPI( 1060 func(spec *APISpec) { 1061 spec.Name = "api 1" 1062 spec.APIID = "api1" 1063 spec.UseKeylessAccess = false 1064 spec.Proxy.ListenPath = "/api1" 1065 spec.OrgID = "default" 1066 }, 1067 func(spec *APISpec) { 1068 spec.Name = "api 2" 1069 spec.APIID = "api2" 1070 spec.UseKeylessAccess = false 1071 spec.Proxy.ListenPath = "/api2" 1072 spec.OrgID = "default" 1073 }, 1074 ) 1075 1076 // create test session 1077 session := &user.SessionState{ 1078 Mutex: &sync.RWMutex{}, 1079 ApplyPolicies: []string{"per_api_policy_with_two_apis"}, 1080 OrgID: "default", 1081 AccessRights: map[string]user.AccessDefinition{ 1082 "api1": { 1083 APIID: "api1", 1084 Versions: []string{"v1"}, 1085 }, 1086 "api2": { 1087 APIID: "api2", 1088 Versions: []string{"v1"}, 1089 }, 1090 }, 1091 } 1092 1093 // create key 1094 key := uuid.New() 1095 ts.Run(t, []test.TestCase{ 1096 {Method: http.MethodPost, Path: "/tyk/keys/" + key, Data: session, AdminAuth: true, Code: 200}, 1097 }...) 1098 1099 // check key session 1100 ts.Run(t, []test.TestCase{ 1101 { 1102 Method: http.MethodGet, 1103 Path: "/tyk/keys/" + key + "?api_id=api1", 1104 AdminAuth: true, 1105 Code: http.StatusOK, 1106 BodyMatchFunc: func(data []byte) bool { 1107 sessionData := user.SessionState{Mutex: &sync.RWMutex{}} 1108 if err := json.Unmarshal(data, &sessionData); err != nil { 1109 t.Log(err.Error()) 1110 return false 1111 } 1112 1113 if len(sessionData.AccessRights) != 2 { 1114 t.Fatalf("expected 2 entries in AccessRights found %d", len(sessionData.AccessRights)) 1115 } 1116 1117 _, ok1 := sessionData.AccessRights["api1"] 1118 _, ok2 := sessionData.AccessRights["api2"] 1119 1120 if !ok1 || !ok2 { 1121 t.Fatalf("expected api1 and api2 in AccessRights found %v", sessionData.AccessRights) 1122 } 1123 1124 return true 1125 }, 1126 }, 1127 }...) 1128 1129 //Update policy 1130 policiesMu.RLock() 1131 policy = user.Policy{ 1132 ID: "per_api_policy_with_two_apis", 1133 OrgID: "default", 1134 Partitions: user.PolicyPartitions{ 1135 PerAPI: true, 1136 Quota: false, 1137 RateLimit: false, 1138 Acl: false, 1139 }, 1140 AccessRights: map[string]user.AccessDefinition{ 1141 "api1": { 1142 Versions: []string{"v1"}, 1143 }, 1144 }, 1145 } 1146 policiesByID = map[string]user.Policy{ 1147 "per_api_policy_with_two_apis": policy, 1148 } 1149 policiesMu.RUnlock() 1150 1151 ts.Run(t, []test.TestCase{ 1152 { 1153 Method: http.MethodGet, 1154 Path: "/tyk/keys/" + key + "?api_id=api1", 1155 AdminAuth: true, 1156 Code: http.StatusOK, 1157 BodyMatchFunc: func(data []byte) bool { 1158 sessionData := user.SessionState{Mutex: &sync.RWMutex{}} 1159 if err := json.Unmarshal(data, &sessionData); err != nil { 1160 t.Log(err.Error()) 1161 return false 1162 } 1163 1164 if len(sessionData.AccessRights) != 1 { 1165 t.Fatalf("expected only 1 entry in AccessRights found %d", len(sessionData.AccessRights)) 1166 } 1167 1168 _, ok1 := sessionData.AccessRights["api1"] 1169 1170 if !ok1 { 1171 t.Fatalf("expected api1 in AccessRights found %v", sessionData.AccessRights) 1172 } 1173 1174 return true 1175 }, 1176 }, 1177 }...) 1178 }