github.com/anuvu/tyk@v2.9.0-beta9-dl-apic+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  	"strings"
    10  	"testing"
    11  
    12  	"github.com/lonelycode/go-uuid/uuid"
    13  
    14  	"github.com/TykTechnologies/tyk/test"
    15  
    16  	"github.com/TykTechnologies/tyk/apidef"
    17  	"github.com/TykTechnologies/tyk/config"
    18  	"github.com/TykTechnologies/tyk/user"
    19  )
    20  
    21  func TestLoadPoliciesFromDashboardReLogin(t *testing.T) {
    22  	// Test Dashboard
    23  	ts := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
    24  		w.WriteHeader(403)
    25  	}))
    26  	defer ts.Close()
    27  
    28  	globalConf := config.Global()
    29  	oldUseDBAppConfigs := globalConf.UseDBAppConfigs
    30  	globalConf.UseDBAppConfigs = false
    31  	config.SetGlobal(globalConf)
    32  
    33  	defer func() {
    34  		globalConf.UseDBAppConfigs = oldUseDBAppConfigs
    35  		config.SetGlobal(globalConf)
    36  	}()
    37  
    38  	allowExplicitPolicyID := config.Global().Policies.AllowExplicitPolicyID
    39  
    40  	policyMap := LoadPoliciesFromDashboard(ts.URL, "", allowExplicitPolicyID)
    41  
    42  	if policyMap != nil {
    43  		t.Error("Should be nil because got back 403 from Dashboard")
    44  	}
    45  }
    46  
    47  type dummySessionManager struct {
    48  	DefaultSessionManager
    49  }
    50  
    51  func (*dummySessionManager) UpdateSession(key string, sess *user.SessionState, ttl int64, hashed bool) error {
    52  	return nil
    53  }
    54  
    55  type testApplyPoliciesData struct {
    56  	name      string
    57  	policies  []string
    58  	errMatch  string                               // substring
    59  	sessMatch func(*testing.T, *user.SessionState) // ignored if nil
    60  }
    61  
    62  func testPrepareApplyPolicies() (*BaseMiddleware, []testApplyPoliciesData) {
    63  	policiesMu.RLock()
    64  	policiesByID = map[string]user.Policy{
    65  		"nonpart1": {},
    66  		"nonpart2": {},
    67  		"difforg":  {OrgID: "different"},
    68  		"tags1": {
    69  			Partitions: user.PolicyPartitions{Quota: true},
    70  			Tags:       []string{"tagA"},
    71  		},
    72  		"tags2": {
    73  			Partitions: user.PolicyPartitions{RateLimit: true},
    74  			Tags:       []string{"tagX", "tagY"},
    75  		},
    76  		"inactive1": {
    77  			Partitions: user.PolicyPartitions{RateLimit: true},
    78  			IsInactive: true,
    79  		},
    80  		"inactive2": {
    81  			Partitions: user.PolicyPartitions{Quota: true},
    82  			IsInactive: true,
    83  		},
    84  		"quota1": {
    85  			Partitions: user.PolicyPartitions{Quota: true},
    86  			QuotaMax:   2,
    87  		},
    88  		"quota2": {Partitions: user.PolicyPartitions{Quota: true}},
    89  		"rate1": {
    90  			Partitions: user.PolicyPartitions{RateLimit: true},
    91  			Rate:       3,
    92  		},
    93  		"rate2": {Partitions: user.PolicyPartitions{RateLimit: true}},
    94  		"acl1": {
    95  			Partitions:   user.PolicyPartitions{Acl: true},
    96  			AccessRights: map[string]user.AccessDefinition{"a": {}},
    97  		},
    98  		"acl2": {
    99  			Partitions:   user.PolicyPartitions{Acl: true},
   100  			AccessRights: map[string]user.AccessDefinition{"b": {}},
   101  		},
   102  		"acl3": {
   103  			AccessRights: map[string]user.AccessDefinition{"c": {}},
   104  		},
   105  		"per_api_and_partitions": {
   106  			ID: "per_api_and_partitions",
   107  			Partitions: user.PolicyPartitions{
   108  				PerAPI:    true,
   109  				Quota:     true,
   110  				RateLimit: true,
   111  				Acl:       true,
   112  			},
   113  			AccessRights: map[string]user.AccessDefinition{"d": {
   114  				Limit: &user.APILimit{
   115  					QuotaMax:         1000,
   116  					QuotaRenewalRate: 3600,
   117  					Rate:             20,
   118  					Per:              1,
   119  				},
   120  			}},
   121  		},
   122  		"per_api_and_some_partitions": {
   123  			ID: "per_api_and_some_partitions",
   124  			Partitions: user.PolicyPartitions{
   125  				PerAPI:    true,
   126  				Quota:     false,
   127  				RateLimit: true,
   128  				Acl:       false,
   129  			},
   130  			AccessRights: map[string]user.AccessDefinition{"d": {
   131  				Limit: &user.APILimit{
   132  					QuotaMax:         1000,
   133  					QuotaRenewalRate: 3600,
   134  					Rate:             20,
   135  					Per:              1,
   136  				},
   137  			}},
   138  		},
   139  		"per_api_and_no_other_partitions": {
   140  			ID: "per_api_and_no_other_partitions",
   141  			Partitions: user.PolicyPartitions{
   142  				PerAPI:    true,
   143  				Quota:     false,
   144  				RateLimit: false,
   145  				Acl:       false,
   146  			},
   147  			AccessRights: map[string]user.AccessDefinition{
   148  				"d": {
   149  					Limit: &user.APILimit{
   150  						QuotaMax:         1000,
   151  						QuotaRenewalRate: 3600,
   152  						Rate:             20,
   153  						Per:              1,
   154  					},
   155  				},
   156  				"c": {
   157  					Limit: &user.APILimit{
   158  						QuotaMax: -1,
   159  						Rate:     2000,
   160  						Per:      60,
   161  					},
   162  				},
   163  			},
   164  		},
   165  		"per_api_with_the_same_api": {
   166  			ID: "per_api_with_the_same_api",
   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:         5000,
   177  						QuotaRenewalRate: 3600,
   178  						Rate:             200,
   179  						Per:              10,
   180  					},
   181  				},
   182  			},
   183  		},
   184  		"per_api_with_limit_set_from_policy": {
   185  			ID:       "per_api_with_limit_set_from_policy",
   186  			QuotaMax: -1,
   187  			Rate:     300,
   188  			Per:      1,
   189  			Partitions: user.PolicyPartitions{
   190  				PerAPI:    true,
   191  				Quota:     false,
   192  				RateLimit: false,
   193  				Acl:       false,
   194  			},
   195  			AccessRights: map[string]user.AccessDefinition{
   196  				"d": {
   197  					Limit: &user.APILimit{
   198  						QuotaMax:         5000,
   199  						QuotaRenewalRate: 3600,
   200  						Rate:             200,
   201  						Per:              10,
   202  					},
   203  				},
   204  				"e": {},
   205  			},
   206  		},
   207  	}
   208  	policiesMu.RUnlock()
   209  	bmid := &BaseMiddleware{Spec: &APISpec{
   210  		APIDefinition:  &apidef.APIDefinition{},
   211  		SessionManager: &dummySessionManager{},
   212  	}}
   213  	tests := []testApplyPoliciesData{
   214  		{
   215  			"Empty", nil,
   216  			"", nil,
   217  		},
   218  		{
   219  			"Single", []string{"nonpart1"},
   220  			"", nil,
   221  		},
   222  		{
   223  			"Missing", []string{"nonexistent"},
   224  			"not found", nil,
   225  		},
   226  		{
   227  			"DiffOrg", []string{"difforg"},
   228  			"different org", nil,
   229  		},
   230  		{
   231  			"MultiNonPart", []string{"nonpart1", "nonpart2"},
   232  			"any are non-part", nil,
   233  		},
   234  		{
   235  			"NonpartAndPart", []string{"nonpart1", "quota1"},
   236  			"any are non-part", nil,
   237  		},
   238  		{
   239  			"TagMerge", []string{"tags1", "tags2"},
   240  			"", func(t *testing.T, s *user.SessionState) {
   241  				want := []string{"tagA", "tagX", "tagY"}
   242  				sort.Strings(s.Tags)
   243  				if !reflect.DeepEqual(want, s.Tags) {
   244  					t.Fatalf("want Tags %v, got %v", want, s.Tags)
   245  				}
   246  			},
   247  		},
   248  		{
   249  			"InactiveMergeOne", []string{"tags1", "inactive1"},
   250  			"", func(t *testing.T, s *user.SessionState) {
   251  				if !s.IsInactive {
   252  					t.Fatalf("want IsInactive to be true")
   253  				}
   254  			},
   255  		},
   256  		{
   257  			"InactiveMergeAll", []string{"inactive1", "inactive2"},
   258  			"", func(t *testing.T, s *user.SessionState) {
   259  				if !s.IsInactive {
   260  					t.Fatalf("want IsInactive to be true")
   261  				}
   262  			},
   263  		},
   264  		{
   265  			"QuotaPart", []string{"quota1"},
   266  			"", func(t *testing.T, s *user.SessionState) {
   267  				if s.QuotaMax != 2 {
   268  					t.Fatalf("want QuotaMax to be 2")
   269  				}
   270  			},
   271  		},
   272  		{
   273  			"QuotaParts", []string{"quota1", "quota2"},
   274  			"multiple quota policies", nil,
   275  		},
   276  		{
   277  			"RatePart", []string{"rate1"},
   278  			"", func(t *testing.T, s *user.SessionState) {
   279  				if s.Rate != 3 {
   280  					t.Fatalf("want Rate to be 3")
   281  				}
   282  			},
   283  		},
   284  		{
   285  			"RateParts", []string{"rate1", "rate2"},
   286  			"multiple rate limit policies", nil,
   287  		},
   288  		{
   289  			"AclPart", []string{"acl1"},
   290  			"", func(t *testing.T, s *user.SessionState) {
   291  				want := map[string]user.AccessDefinition{"a": {}}
   292  				if !reflect.DeepEqual(want, s.AccessRights) {
   293  					t.Fatalf("want %v got %v", want, s.AccessRights)
   294  				}
   295  			},
   296  		},
   297  		{
   298  			"AclPart", []string{"acl1", "acl2"},
   299  			"", func(t *testing.T, s *user.SessionState) {
   300  				want := map[string]user.AccessDefinition{"a": {}, "b": {}}
   301  				if !reflect.DeepEqual(want, s.AccessRights) {
   302  					t.Fatalf("want %v got %v", want, s.AccessRights)
   303  				}
   304  			},
   305  		},
   306  		{
   307  			"RightsUpdate", []string{"acl3"},
   308  			"", func(t *testing.T, s *user.SessionState) {
   309  				newPolicy := user.Policy{
   310  					AccessRights: map[string]user.AccessDefinition{"a": {}, "b": {}, "c": {}},
   311  				}
   312  				policiesMu.Lock()
   313  				policiesByID["acl3"] = newPolicy
   314  				policiesMu.Unlock()
   315  				err := bmid.ApplyPolicies(s)
   316  				if err != nil {
   317  					t.Fatalf("couldn't apply policy: %s", err.Error())
   318  				}
   319  				want := newPolicy.AccessRights
   320  				if !reflect.DeepEqual(want, s.AccessRights) {
   321  					t.Fatalf("want %v got %v", want, s.AccessRights)
   322  				}
   323  			},
   324  		},
   325  		{
   326  			name:     "Per API is set with other partitions to true",
   327  			policies: []string{"per_api_and_partitions"},
   328  			errMatch: "cannot apply policy per_api_and_partitions which has per_api and any of partitions set",
   329  		},
   330  		{
   331  			name:     "Per API is set to true with some partitions set to true",
   332  			policies: []string{"per_api_and_some_partitions"},
   333  			errMatch: "cannot apply policy per_api_and_some_partitions which has per_api and any of partitions set",
   334  		},
   335  		{
   336  			name:     "Per API is set to true with no other partitions set to true",
   337  			policies: []string{"per_api_and_no_other_partitions"},
   338  			sessMatch: func(t *testing.T, s *user.SessionState) {
   339  				want := map[string]user.AccessDefinition{
   340  					"d": {
   341  						Limit: &user.APILimit{
   342  							QuotaMax:         1000,
   343  							QuotaRenewalRate: 3600,
   344  							Rate:             20,
   345  							Per:              1,
   346  						},
   347  					},
   348  					"c": {
   349  						Limit: &user.APILimit{
   350  							QuotaMax: -1,
   351  							Rate:     2000,
   352  							Per:      60,
   353  						},
   354  					},
   355  				}
   356  				if !reflect.DeepEqual(want, s.AccessRights) {
   357  					t.Fatalf("want %v got %v", want, s.AccessRights)
   358  				}
   359  			},
   360  		},
   361  		{
   362  			name:     "several policies with Per API set to true but specifying limit for the same API",
   363  			policies: []string{"per_api_and_no_other_partitions", "per_api_with_the_same_api"},
   364  			errMatch: "cannot apply multiple policies for API: d",
   365  		},
   366  		{
   367  			name:     "several policies, mixed the one which has Per API set to true and partitioned ones",
   368  			policies: []string{"per_api_and_no_other_partitions", "quota1"},
   369  			errMatch: "cannot apply multiple policies when some are partitioned and some have per_api set",
   370  		},
   371  		{
   372  			name:     "several policies, mixed the one which has Per API set to true and partitioned ones (different order)",
   373  			policies: []string{"rate1", "per_api_and_no_other_partitions"},
   374  			errMatch: "cannot apply multiple policies when some have per_api set and some are partitioned",
   375  		},
   376  		{
   377  			name:     "Per API is set to true and some API gets limit set from policy's fields",
   378  			policies: []string{"per_api_with_limit_set_from_policy"},
   379  			sessMatch: func(t *testing.T, s *user.SessionState) {
   380  				want := map[string]user.AccessDefinition{
   381  					"d": {
   382  						Limit: &user.APILimit{
   383  							QuotaMax:         5000,
   384  							QuotaRenewalRate: 3600,
   385  							Rate:             200,
   386  							Per:              10,
   387  						},
   388  					},
   389  					"e": {
   390  						Limit: &user.APILimit{
   391  							QuotaMax:    -1,
   392  							Rate:        300,
   393  							Per:         1,
   394  							SetByPolicy: true,
   395  						},
   396  					},
   397  				}
   398  				if !reflect.DeepEqual(want, s.AccessRights) {
   399  					t.Fatalf("want %v got %v", want, s.AccessRights)
   400  				}
   401  			},
   402  		},
   403  	}
   404  
   405  	return bmid, tests
   406  }
   407  
   408  func TestApplyPolicies(t *testing.T) {
   409  	bmid, tests := testPrepareApplyPolicies()
   410  
   411  	for _, tc := range tests {
   412  		t.Run(tc.name, func(t *testing.T) {
   413  			sess := &user.SessionState{}
   414  			sess.SetPolicies(tc.policies...)
   415  			errStr := ""
   416  			if err := bmid.ApplyPolicies(sess); err != nil {
   417  				errStr = err.Error()
   418  			}
   419  			if tc.errMatch == "" && errStr != "" {
   420  				t.Fatalf("didn't want err but got %s", errStr)
   421  			} else if !strings.Contains(errStr, tc.errMatch) {
   422  				t.Fatalf("error %q doesn't match %q",
   423  					errStr, tc.errMatch)
   424  			}
   425  			if tc.sessMatch != nil {
   426  				tc.sessMatch(t, sess)
   427  			}
   428  		})
   429  	}
   430  }
   431  
   432  func BenchmarkApplyPolicies(b *testing.B) {
   433  	b.ReportAllocs()
   434  
   435  	bmid, tests := testPrepareApplyPolicies()
   436  
   437  	for i := 0; i < b.N; i++ {
   438  		for _, tc := range tests {
   439  			sess := &user.SessionState{}
   440  			sess.SetPolicies(tc.policies...)
   441  			bmid.ApplyPolicies(sess)
   442  		}
   443  	}
   444  }
   445  
   446  func TestApplyPoliciesQuotaAPILimit(t *testing.T) {
   447  	policiesMu.RLock()
   448  	policy := user.Policy{
   449  		ID:               "two_of_three_with_api_limit",
   450  		Per:              1,
   451  		Rate:             1000,
   452  		QuotaMax:         50,
   453  		QuotaRenewalRate: 3600,
   454  		OrgID:            "default",
   455  		Partitions: user.PolicyPartitions{
   456  			PerAPI:    true,
   457  			Quota:     false,
   458  			RateLimit: false,
   459  			Acl:       false,
   460  		},
   461  		AccessRights: map[string]user.AccessDefinition{
   462  			"api1": {
   463  				Versions: []string{"v1"},
   464  				Limit: &user.APILimit{
   465  					QuotaMax:         100,
   466  					QuotaRenewalRate: 3600,
   467  					Rate:             1000,
   468  					Per:              1,
   469  				},
   470  			},
   471  			"api2": {
   472  				Versions: []string{"v1"},
   473  				Limit: &user.APILimit{
   474  					QuotaMax:         200,
   475  					QuotaRenewalRate: 3600,
   476  					Rate:             1000,
   477  					Per:              1,
   478  				},
   479  			},
   480  			"api3": {
   481  				Versions: []string{"v1"},
   482  			},
   483  		},
   484  	}
   485  	policiesByID = map[string]user.Policy{
   486  		"two_of_three_with_api_limit": policy,
   487  	}
   488  	policiesMu.RUnlock()
   489  
   490  	ts := StartTest()
   491  	defer ts.Close()
   492  
   493  	// load APIs
   494  	BuildAndLoadAPI(
   495  		func(spec *APISpec) {
   496  			spec.Name = "api 1"
   497  			spec.APIID = "api1"
   498  			spec.UseKeylessAccess = false
   499  			spec.Proxy.ListenPath = "/api1"
   500  			spec.OrgID = "default"
   501  		},
   502  		func(spec *APISpec) {
   503  			spec.Name = "api 2"
   504  			spec.APIID = "api2"
   505  			spec.UseKeylessAccess = false
   506  			spec.Proxy.ListenPath = "/api2"
   507  			spec.OrgID = "default"
   508  		},
   509  		func(spec *APISpec) {
   510  			spec.Name = "api 3"
   511  			spec.APIID = "api3"
   512  			spec.UseKeylessAccess = false
   513  			spec.Proxy.ListenPath = "/api3"
   514  			spec.OrgID = "default"
   515  		},
   516  	)
   517  
   518  	// create test session
   519  	session := &user.SessionState{
   520  		ApplyPolicies: []string{"two_of_three_with_api_limit"},
   521  		OrgID:         "default",
   522  		AccessRights: map[string]user.AccessDefinition{
   523  			"api1": {
   524  				APIID:    "api1",
   525  				Versions: []string{"v1"},
   526  			},
   527  			"api2": {
   528  				APIID:    "api2",
   529  				Versions: []string{"v1"},
   530  			},
   531  			"api3": {
   532  				APIID:    "api3",
   533  				Versions: []string{"v1"},
   534  			},
   535  		},
   536  	}
   537  
   538  	// create key
   539  	key := uuid.New()
   540  	ts.Run(t, []test.TestCase{
   541  		{Method: http.MethodPost, Path: "/tyk/keys/" + key, Data: session, AdminAuth: true, Code: 200},
   542  	}...)
   543  
   544  	// run requests to different APIs
   545  	authHeader := map[string]string{"Authorization": key}
   546  	ts.Run(t, []test.TestCase{
   547  		// 2 requests to api1, API limit quota remaining should be 98
   548  		{Method: http.MethodGet, Path: "/api1", Headers: authHeader, Code: http.StatusOK,
   549  			HeadersMatch: map[string]string{XRateLimitRemaining: "99"}},
   550  		{Method: http.MethodGet, Path: "/api1", Headers: authHeader, Code: http.StatusOK,
   551  			HeadersMatch: map[string]string{XRateLimitRemaining: "98"}},
   552  		// 3 requests to api2, API limit quota remaining should be 197
   553  		{Method: http.MethodGet, Path: "/api2", Headers: authHeader, Code: http.StatusOK,
   554  			HeadersMatch: map[string]string{XRateLimitRemaining: "199"}},
   555  		{Method: http.MethodGet, Path: "/api2", Headers: authHeader, Code: http.StatusOK,
   556  			HeadersMatch: map[string]string{XRateLimitRemaining: "198"}},
   557  		{Method: http.MethodGet, Path: "/api2", Headers: authHeader, Code: http.StatusOK,
   558  			HeadersMatch: map[string]string{XRateLimitRemaining: "197"}},
   559  		// 5 requests to api3, API limit quota remaining should be 45
   560  		{Method: http.MethodGet, Path: "/api3", Headers: authHeader, Code: http.StatusOK,
   561  			HeadersMatch: map[string]string{XRateLimitRemaining: "49"}},
   562  		{Method: http.MethodGet, Path: "/api3", Headers: authHeader, Code: http.StatusOK,
   563  			HeadersMatch: map[string]string{XRateLimitRemaining: "48"}},
   564  		{Method: http.MethodGet, Path: "/api3", Headers: authHeader, Code: http.StatusOK,
   565  			HeadersMatch: map[string]string{XRateLimitRemaining: "47"}},
   566  		{Method: http.MethodGet, Path: "/api3", Headers: authHeader, Code: http.StatusOK,
   567  			HeadersMatch: map[string]string{XRateLimitRemaining: "46"}},
   568  		{Method: http.MethodGet, Path: "/api3", Headers: authHeader, Code: http.StatusOK,
   569  			HeadersMatch: map[string]string{XRateLimitRemaining: "45"}},
   570  	}...)
   571  
   572  	// check key session
   573  	ts.Run(t, []test.TestCase{
   574  		{
   575  			Method:    http.MethodGet,
   576  			Path:      "/tyk/keys/" + key,
   577  			AdminAuth: true,
   578  			Code:      http.StatusOK,
   579  			BodyMatchFunc: func(data []byte) bool {
   580  				sessionData := user.SessionState{}
   581  				if err := json.Unmarshal(data, &sessionData); err != nil {
   582  					t.Log(err.Error())
   583  					return false
   584  				}
   585  				api1Limit := sessionData.AccessRights["api1"].Limit
   586  				if api1Limit == nil {
   587  					t.Log("api1 limit is not set")
   588  					return false
   589  				}
   590  				api1LimitExpected := user.APILimit{
   591  					Rate:             1000,
   592  					Per:              1,
   593  					QuotaMax:         100,
   594  					QuotaRenewalRate: 3600,
   595  					QuotaRenews:      api1Limit.QuotaRenews,
   596  					QuotaRemaining:   98,
   597  				}
   598  				if !reflect.DeepEqual(*api1Limit, api1LimitExpected) {
   599  					t.Log("api1 limit received:", *api1Limit, "expected:", api1LimitExpected)
   600  					return false
   601  				}
   602  				api2Limit := sessionData.AccessRights["api2"].Limit
   603  				if api2Limit == nil {
   604  					t.Log("api2 limit is not set")
   605  					return false
   606  				}
   607  				api2LimitExpected := user.APILimit{
   608  					Rate:             1000,
   609  					Per:              1,
   610  					QuotaMax:         200,
   611  					QuotaRenewalRate: 3600,
   612  					QuotaRenews:      api2Limit.QuotaRenews,
   613  					QuotaRemaining:   197,
   614  				}
   615  				if !reflect.DeepEqual(*api2Limit, api2LimitExpected) {
   616  					t.Log("api2 limit received:", *api2Limit, "expected:", api2LimitExpected)
   617  					return false
   618  				}
   619  				api3Limit := sessionData.AccessRights["api3"].Limit
   620  				if api3Limit == nil {
   621  					t.Log("api3 limit is not set")
   622  					return false
   623  				}
   624  				api3LimitExpected := user.APILimit{
   625  					Rate:             1000,
   626  					Per:              1,
   627  					QuotaMax:         50,
   628  					QuotaRenewalRate: 3600,
   629  					QuotaRenews:      api3Limit.QuotaRenews,
   630  					QuotaRemaining:   45,
   631  					SetByPolicy:      true,
   632  				}
   633  				if !reflect.DeepEqual(*api3Limit, api3LimitExpected) {
   634  					t.Log("api3 limit received:", *api3Limit, "expected:", api3LimitExpected)
   635  					return false
   636  				}
   637  				return true
   638  			},
   639  		},
   640  	}...)
   641  
   642  	// Reset quota
   643  	ts.Run(t, []test.TestCase{
   644  		{
   645  			Method:    http.MethodPut,
   646  			Path:      "/tyk/keys/" + key,
   647  			AdminAuth: true,
   648  			Code:      http.StatusOK,
   649  			Data:      session,
   650  		},
   651  		{
   652  			Method:    http.MethodGet,
   653  			Path:      "/tyk/keys/" + key,
   654  			AdminAuth: true,
   655  			Code:      http.StatusOK,
   656  			BodyMatchFunc: func(data []byte) bool {
   657  				sessionData := user.SessionState{}
   658  				if err := json.Unmarshal(data, &sessionData); err != nil {
   659  					t.Log(err.Error())
   660  					return false
   661  				}
   662  				api1Limit := sessionData.AccessRights["api1"].Limit
   663  				if api1Limit == nil {
   664  					t.Error("api1 limit is not set")
   665  					return false
   666  				}
   667  
   668  				if api1Limit.QuotaRemaining != 100 {
   669  					t.Error("Should reset quota:", api1Limit.QuotaRemaining)
   670  					return false
   671  				}
   672  
   673  				return true
   674  			},
   675  		},
   676  	}...)
   677  }
   678  
   679  func TestPerAPIPolicyUpdate(t *testing.T) {
   680  	policiesMu.RLock()
   681  	policy := user.Policy{
   682  		ID:    "per_api_policy_with_two_apis",
   683  		OrgID: "default",
   684  		Partitions: user.PolicyPartitions{
   685  			PerAPI:    true,
   686  			Quota:     false,
   687  			RateLimit: false,
   688  			Acl:       false,
   689  		},
   690  		AccessRights: map[string]user.AccessDefinition{
   691  			"api1": {
   692  				Versions: []string{"v1"},
   693  			},
   694  			"api2": {
   695  				Versions: []string{"v1"},
   696  			},
   697  		},
   698  	}
   699  	policiesByID = map[string]user.Policy{
   700  		"per_api_policy_with_two_apis": policy,
   701  	}
   702  	policiesMu.RUnlock()
   703  
   704  	ts := StartTest()
   705  	defer ts.Close()
   706  
   707  	// load APIs
   708  	BuildAndLoadAPI(
   709  		func(spec *APISpec) {
   710  			spec.Name = "api 1"
   711  			spec.APIID = "api1"
   712  			spec.UseKeylessAccess = false
   713  			spec.Proxy.ListenPath = "/api1"
   714  			spec.OrgID = "default"
   715  		},
   716  		func(spec *APISpec) {
   717  			spec.Name = "api 2"
   718  			spec.APIID = "api2"
   719  			spec.UseKeylessAccess = false
   720  			spec.Proxy.ListenPath = "/api2"
   721  			spec.OrgID = "default"
   722  		},
   723  	)
   724  
   725  	// create test session
   726  	session := &user.SessionState{
   727  		ApplyPolicies: []string{"per_api_policy_with_two_apis"},
   728  		OrgID:         "default",
   729  		AccessRights: map[string]user.AccessDefinition{
   730  			"api1": {
   731  				APIID:    "api1",
   732  				Versions: []string{"v1"},
   733  			},
   734  			"api2": {
   735  				APIID:    "api2",
   736  				Versions: []string{"v1"},
   737  			},
   738  		},
   739  	}
   740  
   741  	// create key
   742  	key := uuid.New()
   743  	ts.Run(t, []test.TestCase{
   744  		{Method: http.MethodPost, Path: "/tyk/keys/" + key, Data: session, AdminAuth: true, Code: 200},
   745  	}...)
   746  
   747  	// check key session
   748  	ts.Run(t, []test.TestCase{
   749  		{
   750  			Method:    http.MethodGet,
   751  			Path:      "/tyk/keys/" + key,
   752  			AdminAuth: true,
   753  			Code:      http.StatusOK,
   754  			BodyMatchFunc: func(data []byte) bool {
   755  				sessionData := user.SessionState{}
   756  				if err := json.Unmarshal(data, &sessionData); err != nil {
   757  					t.Log(err.Error())
   758  					return false
   759  				}
   760  
   761  				if len(sessionData.AccessRights) != 2 {
   762  					t.Fatalf("expected 2 entries in AccessRights found %d", len(sessionData.AccessRights))
   763  				}
   764  
   765  				_, ok1 := sessionData.AccessRights["api1"]
   766  				_, ok2 := sessionData.AccessRights["api2"]
   767  
   768  				if !ok1 || !ok2 {
   769  					t.Fatalf("expected api1 and api2 in AccessRights found %v", sessionData.AccessRights)
   770  				}
   771  
   772  				return true
   773  			},
   774  		},
   775  	}...)
   776  
   777  	//Update policy
   778  	policiesMu.RLock()
   779  	policy = user.Policy{
   780  		ID:    "per_api_policy_with_two_apis",
   781  		OrgID: "default",
   782  		Partitions: user.PolicyPartitions{
   783  			PerAPI:    true,
   784  			Quota:     false,
   785  			RateLimit: false,
   786  			Acl:       false,
   787  		},
   788  		AccessRights: map[string]user.AccessDefinition{
   789  			"api1": {
   790  				Versions: []string{"v1"},
   791  			},
   792  		},
   793  	}
   794  	policiesByID = map[string]user.Policy{
   795  		"per_api_policy_with_two_apis": policy,
   796  	}
   797  	policiesMu.RUnlock()
   798  
   799  	ts.Run(t, []test.TestCase{
   800  		{
   801  			Method:    http.MethodGet,
   802  			Path:      "/tyk/keys/" + key,
   803  			AdminAuth: true,
   804  			Code:      http.StatusOK,
   805  			BodyMatchFunc: func(data []byte) bool {
   806  				sessionData := user.SessionState{}
   807  				if err := json.Unmarshal(data, &sessionData); err != nil {
   808  					t.Log(err.Error())
   809  					return false
   810  				}
   811  
   812  				if len(sessionData.AccessRights) != 1 {
   813  					t.Fatalf("expected only 1 entry in AccessRights found %d", len(sessionData.AccessRights))
   814  				}
   815  
   816  				_, ok1 := sessionData.AccessRights["api1"]
   817  
   818  				if !ok1 {
   819  					t.Fatalf("expected api1 in AccessRights found %v", sessionData.AccessRights)
   820  				}
   821  
   822  				return true
   823  			},
   824  		},
   825  	}...)
   826  }