github.com/dbernstein1/tyk@v2.9.0-beta9-dl-apic+incompatible/gateway/mw_auth_key_test.go (about)

     1  package gateway
     2  
     3  import (
     4  	"encoding/hex"
     5  	"fmt"
     6  	"net/http"
     7  	"net/http/httptest"
     8  	"net/url"
     9  	"testing"
    10  	"time"
    11  
    12  	"github.com/justinas/alice"
    13  	"github.com/lonelycode/go-uuid/uuid"
    14  
    15  	"github.com/TykTechnologies/tyk/config"
    16  	"github.com/TykTechnologies/tyk/signature_validator"
    17  	"github.com/TykTechnologies/tyk/test"
    18  	"github.com/TykTechnologies/tyk/user"
    19  )
    20  
    21  func TestMurmur3CharBug(t *testing.T) {
    22  	defer ResetTestConfig()
    23  	ts := StartTest()
    24  	defer ts.Close()
    25  
    26  	api := BuildAPI(func(spec *APISpec) {
    27  		spec.UseKeylessAccess = false
    28  		spec.Proxy.ListenPath = "/"
    29  	})[0]
    30  
    31  	genTestCase := func(key string, status int) test.TestCase {
    32  		return test.TestCase{Path: "/", Headers: map[string]string{"Authorization": key}, Code: status}
    33  	}
    34  
    35  	t.Run("Without hashing", func(t *testing.T) {
    36  		globalConf := config.Global()
    37  		globalConf.HashKeys = false
    38  		config.SetGlobal(globalConf)
    39  
    40  		LoadAPI(api)
    41  
    42  		key := CreateSession()
    43  
    44  		ts.Run(t, []test.TestCase{
    45  			genTestCase("wrong", 403),
    46  			genTestCase(key+"abc", 403),
    47  			genTestCase(key, 200),
    48  		}...)
    49  	})
    50  
    51  	t.Run("murmur32 hashing, legacy", func(t *testing.T) {
    52  		globalConf := config.Global()
    53  		globalConf.HashKeys = true
    54  		globalConf.HashKeyFunction = ""
    55  		config.SetGlobal(globalConf)
    56  
    57  		LoadAPI(api)
    58  
    59  		key := CreateSession()
    60  
    61  		ts.Run(t, []test.TestCase{
    62  			genTestCase("wrong", 403),
    63  			// Should reject instead, just to show bug
    64  			genTestCase(key+"abc", 200),
    65  			genTestCase(key, 200),
    66  		}...)
    67  	})
    68  
    69  	t.Run("murmur32 hashing, json keys", func(t *testing.T) {
    70  		globalConf := config.Global()
    71  		globalConf.HashKeys = true
    72  		globalConf.HashKeyFunction = "murmur32"
    73  		config.SetGlobal(globalConf)
    74  
    75  		LoadAPI(api)
    76  
    77  		key := CreateSession()
    78  
    79  		ts.Run(t, []test.TestCase{
    80  			genTestCase("wrong", 403),
    81  			// Should reject instead, just to show bug
    82  			genTestCase(key+"abc", 200),
    83  			genTestCase(key, 200),
    84  		}...)
    85  	})
    86  
    87  	t.Run("murmur64 hashing", func(t *testing.T) {
    88  		globalConf := config.Global()
    89  		globalConf.HashKeys = true
    90  		globalConf.HashKeyFunction = "murmur64"
    91  		config.SetGlobal(globalConf)
    92  
    93  		LoadAPI(api)
    94  
    95  		key := CreateSession()
    96  
    97  		ts.Run(t, []test.TestCase{
    98  			genTestCase("wrong", 403),
    99  			// New hashing fixes the bug
   100  			genTestCase(key+"abc", 403),
   101  			genTestCase(key, 200),
   102  		}...)
   103  	})
   104  }
   105  
   106  func TestSignatureValidation(t *testing.T) {
   107  	defer ResetTestConfig()
   108  	ts := StartTest()
   109  	defer ts.Close()
   110  
   111  	api := BuildAPI(func(spec *APISpec) {
   112  		spec.UseKeylessAccess = false
   113  		spec.Proxy.ListenPath = "/"
   114  		spec.Auth.ValidateSignature = true
   115  		spec.Auth.Signature.Algorithm = "MasheryMD5"
   116  		spec.Auth.Signature.Header = "Signature"
   117  		spec.Auth.Signature.Secret = "foobar"
   118  		spec.Auth.Signature.AllowedClockSkew = 1
   119  	})[0]
   120  
   121  	t.Run("Static signature", func(t *testing.T) {
   122  		api.Auth.Signature.Secret = "foobar"
   123  		LoadAPI(api)
   124  
   125  		key := CreateSession()
   126  		hasher := signature_validator.MasheryMd5sum{}
   127  		validHash := hasher.Hash(key, "foobar", time.Now().Unix())
   128  
   129  		validSigHeader := map[string]string{
   130  			"authorization": key,
   131  			"signature":     hex.EncodeToString(validHash),
   132  		}
   133  
   134  		invalidSigHeader := map[string]string{
   135  			"authorization": key,
   136  			"signature":     "junk",
   137  		}
   138  
   139  		emptySigHeader := map[string]string{
   140  			"authorization": key,
   141  		}
   142  
   143  		ts.Run(t, []test.TestCase{
   144  			{Headers: emptySigHeader, Code: 401},
   145  			{Headers: invalidSigHeader, Code: 401},
   146  			{Headers: validSigHeader, Code: 200},
   147  		}...)
   148  	})
   149  
   150  	t.Run("Dynamic signature", func(t *testing.T) {
   151  		api.Auth.Signature.Secret = "$tyk_meta.signature_secret"
   152  		LoadAPI(api)
   153  
   154  		key := CreateSession(func(s *user.SessionState) {
   155  			s.MetaData = map[string]interface{}{
   156  				"signature_secret": "foobar",
   157  			}
   158  		})
   159  
   160  		hasher := signature_validator.MasheryMd5sum{}
   161  		validHash := hasher.Hash(key, "foobar", time.Now().Unix())
   162  
   163  		validSigHeader := map[string]string{
   164  			"authorization": key,
   165  			"signature":     hex.EncodeToString(validHash),
   166  		}
   167  
   168  		invalidSigHeader := map[string]string{
   169  			"authorization": key,
   170  			"signature":     "junk",
   171  		}
   172  
   173  		ts.Run(t, []test.TestCase{
   174  			{Headers: invalidSigHeader, Code: 401},
   175  			{Headers: validSigHeader, Code: 200},
   176  		}...)
   177  	})
   178  }
   179  
   180  func createAuthKeyAuthSession(isBench bool) *user.SessionState {
   181  	session := new(user.SessionState)
   182  	// essentially non-throttled
   183  	session.Rate = 100.0
   184  	session.Allowance = session.Rate
   185  	session.LastCheck = time.Now().Unix()
   186  	session.Per = 1.0
   187  	session.QuotaRenewalRate = 300 // 5 minutes
   188  	session.QuotaRenews = time.Now().Unix()
   189  	if isBench {
   190  		session.QuotaRemaining = 100000000
   191  		session.QuotaMax = 100000000
   192  	} else {
   193  		session.QuotaRemaining = 10
   194  		session.QuotaMax = 10
   195  	}
   196  	session.AccessRights = map[string]user.AccessDefinition{"31": {APIName: "Tyk Auth Key Test", APIID: "31", Versions: []string{"default"}}}
   197  	return session
   198  }
   199  
   200  func getAuthKeyChain(spec *APISpec) http.Handler {
   201  	remote, _ := url.Parse(spec.Proxy.TargetURL)
   202  	proxy := TykNewSingleHostReverseProxy(remote, spec)
   203  	proxyHandler := ProxyHandler(proxy, spec)
   204  	baseMid := BaseMiddleware{Spec: spec, Proxy: proxy}
   205  	chain := alice.New(mwList(
   206  		&IPWhiteListMiddleware{baseMid},
   207  		&IPBlackListMiddleware{BaseMiddleware: baseMid},
   208  		&AuthKey{baseMid},
   209  		&VersionCheck{BaseMiddleware: baseMid},
   210  		&KeyExpired{baseMid},
   211  		&AccessRightsCheck{baseMid},
   212  		&RateLimitAndQuotaCheck{baseMid},
   213  	)...).Then(proxyHandler)
   214  	return chain
   215  }
   216  
   217  func testPrepareAuthKeySession(tb testing.TB, apiDef string, isBench bool) (string, *APISpec) {
   218  	spec := CreateSpecTest(tb, apiDef)
   219  	session := createAuthKeyAuthSession(isBench)
   220  	customToken := ""
   221  	if isBench {
   222  		customToken = uuid.New()
   223  	} else {
   224  		customToken = "54321111"
   225  	}
   226  	// AuthKey sessions are stored by {token}
   227  	spec.SessionManager.UpdateSession(customToken, session, 60, false)
   228  	return customToken, spec
   229  }
   230  
   231  func TestBearerTokenAuthKeySession(t *testing.T) {
   232  	customToken, spec := testPrepareAuthKeySession(t, authKeyDef, false)
   233  
   234  	recorder := httptest.NewRecorder()
   235  	req := TestReq(t, "GET", "/auth_key_test/", nil)
   236  
   237  	req.Header.Set("authorization", "Bearer "+customToken)
   238  
   239  	chain := getAuthKeyChain(spec)
   240  	chain.ServeHTTP(recorder, req)
   241  
   242  	if recorder.Code != 200 {
   243  		t.Error("Initial request failed with non-200 code, should have gone through!: \n", recorder.Code)
   244  		t.Error(recorder.Body.String())
   245  	}
   246  }
   247  
   248  func BenchmarkBearerTokenAuthKeySession(b *testing.B) {
   249  	b.ReportAllocs()
   250  
   251  	customToken, spec := testPrepareAuthKeySession(b, authKeyDef, true)
   252  
   253  	recorder := httptest.NewRecorder()
   254  	req := TestReq(b, "GET", "/auth_key_test/", nil)
   255  
   256  	req.Header.Set("authorization", "Bearer "+customToken)
   257  
   258  	chain := getAuthKeyChain(spec)
   259  
   260  	for i := 0; i < b.N; i++ {
   261  		chain.ServeHTTP(recorder, req)
   262  		if recorder.Code != 200 {
   263  			b.Error("Initial request failed with non-200 code, should have gone through!: \n", recorder.Code)
   264  			b.Error(recorder.Body.String())
   265  		}
   266  	}
   267  }
   268  
   269  const authKeyDef = `{
   270  	"api_id": "31",
   271  	"org_id": "default",
   272  	"auth": {"auth_header_name": "authorization"},
   273  	"version_data": {
   274  		"not_versioned": true,
   275  		"versions": {
   276  			"v1": {"name": "v1"}
   277  		}
   278  	},
   279  	"proxy": {
   280  		"listen_path": "/auth_key_test/",
   281  		"target_url": "` + testHttpAny + `"
   282  	}
   283  }`
   284  
   285  func TestMultiAuthBackwardsCompatibleSession(t *testing.T) {
   286  	customToken, spec := testPrepareAuthKeySession(t, multiAuthBackwardsCompatible, false)
   287  
   288  	recorder := httptest.NewRecorder()
   289  	req := TestReq(t, "GET", fmt.Sprintf("/auth_key_test/?token=%s", customToken), nil)
   290  
   291  	chain := getAuthKeyChain(spec)
   292  	chain.ServeHTTP(recorder, req)
   293  
   294  	if recorder.Code != 200 {
   295  		t.Error("Initial request failed with non-200 code, should have gone through!: \n", recorder.Code)
   296  		t.Error(recorder.Body.String())
   297  	}
   298  }
   299  
   300  func BenchmarkMultiAuthBackwardsCompatibleSession(b *testing.B) {
   301  	b.ReportAllocs()
   302  
   303  	customToken, spec := testPrepareAuthKeySession(b, multiAuthBackwardsCompatible, true)
   304  
   305  	recorder := httptest.NewRecorder()
   306  	req := TestReq(b, "GET", fmt.Sprintf("/auth_key_test/?token=%s", customToken), nil)
   307  
   308  	chain := getAuthKeyChain(spec)
   309  
   310  	for i := 0; i < b.N; i++ {
   311  		chain.ServeHTTP(recorder, req)
   312  		if recorder.Code != 200 {
   313  			b.Error("Initial request failed with non-200 code, should have gone through!: \n", recorder.Code)
   314  			b.Error(recorder.Body.String())
   315  		}
   316  	}
   317  }
   318  
   319  const multiAuthBackwardsCompatible = `{
   320  	"api_id": "31",
   321  	"org_id": "default",
   322  	"auth": {
   323  		"auth_header_name": "token",
   324  		"use_param": true
   325  	},
   326  	"version_data": {
   327  		"not_versioned": true,
   328  		"versions": {
   329  			"v1": {"name": "v1"}
   330  		}
   331  	},
   332  	"proxy": {
   333  		"listen_path": "/auth_key_test/",
   334  		"target_url": "` + testHttpAny + `"
   335  	}
   336  }`
   337  
   338  func TestMultiAuthSession(t *testing.T) {
   339  	spec := CreateSpecTest(t, multiAuthDef)
   340  	session := createAuthKeyAuthSession(false)
   341  	customToken := "54321111"
   342  	// AuthKey sessions are stored by {token}
   343  	spec.SessionManager.UpdateSession(customToken, session, 60, false)
   344  
   345  	// Set the url param
   346  	recorder := httptest.NewRecorder()
   347  	req := TestReq(t, "GET", fmt.Sprintf("/auth_key_test/?token=%s", customToken), nil)
   348  
   349  	chain := getAuthKeyChain(spec)
   350  	chain.ServeHTTP(recorder, req)
   351  
   352  	if recorder.Code != 200 {
   353  		t.Error("First request failed with non-200 code, should have gone through!: \n", recorder.Code)
   354  		t.Error(recorder.Body.String())
   355  	}
   356  
   357  	// Set the header
   358  	recorder = httptest.NewRecorder()
   359  	req = TestReq(t, "GET", "/auth_key_test/?token=", nil)
   360  	req.Header.Set("authorization", customToken)
   361  
   362  	chain.ServeHTTP(recorder, req)
   363  
   364  	if recorder.Code != 200 {
   365  		t.Error("Second request failed with non-200 code, should have gone through!: \n", recorder.Code)
   366  		t.Error(recorder.Body.String())
   367  	}
   368  
   369  	// Set the cookie
   370  	recorder = httptest.NewRecorder()
   371  	req = TestReq(t, "GET", "/auth_key_test/?token=", nil)
   372  	req.AddCookie(&http.Cookie{Name: "oreo", Value: customToken})
   373  
   374  	chain.ServeHTTP(recorder, req)
   375  
   376  	if recorder.Code != 200 {
   377  		t.Error("Third request failed with non-200 code, should have gone through!: \n", recorder.Code)
   378  		t.Error(recorder.Body.String())
   379  	}
   380  
   381  	// No header, param or cookie
   382  	recorder = httptest.NewRecorder()
   383  	req = TestReq(t, "GET", "/auth_key_test/", nil)
   384  
   385  	chain.ServeHTTP(recorder, req)
   386  
   387  	if recorder.Code == 200 {
   388  		t.Error("Request returned 200 code, should NOT have gone through!: \n", recorder.Code)
   389  		t.Error(recorder.Body.String())
   390  	}
   391  }
   392  
   393  const multiAuthDef = `{
   394  	"api_id": "31",
   395  	"org_id": "default",
   396  	"auth": {
   397  		"auth_header_name": "authorization",
   398  		"param_name": "token",
   399  		"cookie_name": "oreo"
   400  	},
   401  	"version_data": {
   402  		"not_versioned": true,
   403  		"versions": {
   404  			"v1": {"name": "v1"}
   405  		}
   406  	},
   407  	"proxy": {
   408  		"listen_path": "/auth_key_test/",
   409  		"target_url": "` + testHttpAny + `"
   410  	}
   411  }`
   412  
   413  func TestStripBearer(t *testing.T) {
   414  	var bearerTests = []struct {
   415  		in  string
   416  		out string
   417  	}{
   418  		{"Bearer abc", "abc"},
   419  		{"bearer abc", "abc"},
   420  		{"bEaReR abc", "abc"},
   421  		{"Bearer: abc", "Bearer: abc"}, // invalid
   422  		{"Basic abc", "Basic abc"},
   423  		{"abc", "abc"},
   424  	}
   425  
   426  	for _, tt := range bearerTests {
   427  		t.Run(tt.in, func(t *testing.T) {
   428  			out := stripBearer(tt.in)
   429  			if out != tt.out {
   430  				t.Errorf("got %q, want %q", out, tt.out)
   431  			}
   432  		})
   433  	}
   434  }
   435  
   436  func BenchmarkStripBearer(b *testing.B) {
   437  	b.ReportAllocs()
   438  
   439  	for i := 0; i < b.N; i++ {
   440  		_ = stripBearer("Bearer abcdefghijklmnopqrstuvwxyz12345678910")
   441  	}
   442  }