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  }