github.com/Tyktechnologies/tyk@v2.9.5+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 "sync" 10 "testing" 11 "time" 12 13 "github.com/justinas/alice" 14 "github.com/lonelycode/go-uuid/uuid" 15 16 "github.com/TykTechnologies/tyk/config" 17 "github.com/TykTechnologies/tyk/signature_validator" 18 "github.com/TykTechnologies/tyk/test" 19 "github.com/TykTechnologies/tyk/user" 20 ) 21 22 func TestMurmur3CharBug(t *testing.T) { 23 defer ResetTestConfig() 24 ts := StartTest() 25 defer ts.Close() 26 27 api := BuildAPI(func(spec *APISpec) { 28 spec.UseKeylessAccess = false 29 spec.Proxy.ListenPath = "/" 30 })[0] 31 32 genTestCase := func(key string, status int) test.TestCase { 33 return test.TestCase{Path: "/", Headers: map[string]string{"Authorization": key}, Code: status} 34 } 35 36 t.Run("Without hashing", func(t *testing.T) { 37 globalConf := config.Global() 38 globalConf.HashKeys = false 39 config.SetGlobal(globalConf) 40 41 LoadAPI(api) 42 43 key := CreateSession() 44 45 ts.Run(t, []test.TestCase{ 46 genTestCase("wrong", 403), 47 genTestCase(key+"abc", 403), 48 genTestCase(key, 200), 49 }...) 50 }) 51 52 t.Run("murmur32 hashing, legacy", func(t *testing.T) { 53 globalConf := config.Global() 54 globalConf.HashKeys = true 55 globalConf.HashKeyFunction = "" 56 config.SetGlobal(globalConf) 57 58 LoadAPI(api) 59 60 key := CreateSession() 61 62 ts.Run(t, []test.TestCase{ 63 genTestCase("wrong", 403), 64 genTestCase(key+"abc", 403), 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 s.Mutex = &sync.RWMutex{} 159 }) 160 161 hasher := signature_validator.MasheryMd5sum{} 162 validHash := hasher.Hash(key, "foobar", time.Now().Unix()) 163 164 validSigHeader := map[string]string{ 165 "authorization": key, 166 "signature": hex.EncodeToString(validHash), 167 } 168 169 invalidSigHeader := map[string]string{ 170 "authorization": key, 171 "signature": "junk", 172 } 173 174 ts.Run(t, []test.TestCase{ 175 {Headers: invalidSigHeader, Code: 401}, 176 {Headers: validSigHeader, Code: 200}, 177 }...) 178 }) 179 } 180 181 func createAuthKeyAuthSession(isBench bool) *user.SessionState { 182 session := new(user.SessionState) 183 // essentially non-throttled 184 session.Rate = 100.0 185 session.Allowance = session.Rate 186 session.LastCheck = time.Now().Unix() 187 session.Per = 1.0 188 session.QuotaRenewalRate = 300 // 5 minutes 189 session.QuotaRenews = time.Now().Unix() 190 if isBench { 191 session.QuotaRemaining = 100000000 192 session.QuotaMax = 100000000 193 } else { 194 session.QuotaRemaining = 10 195 session.QuotaMax = 10 196 } 197 session.AccessRights = map[string]user.AccessDefinition{"31": {APIName: "Tyk Auth Key Test", APIID: "31", Versions: []string{"default"}}} 198 session.Mutex = &sync.RWMutex{} 199 return session 200 } 201 202 func getAuthKeyChain(spec *APISpec) http.Handler { 203 remote, _ := url.Parse(spec.Proxy.TargetURL) 204 proxy := TykNewSingleHostReverseProxy(remote, spec, nil) 205 proxyHandler := ProxyHandler(proxy, spec) 206 baseMid := BaseMiddleware{Spec: spec, Proxy: proxy} 207 chain := alice.New(mwList( 208 &IPWhiteListMiddleware{baseMid}, 209 &IPBlackListMiddleware{BaseMiddleware: baseMid}, 210 &AuthKey{baseMid}, 211 &VersionCheck{BaseMiddleware: baseMid}, 212 &KeyExpired{baseMid}, 213 &AccessRightsCheck{baseMid}, 214 &RateLimitAndQuotaCheck{baseMid}, 215 )...).Then(proxyHandler) 216 return chain 217 } 218 219 func testPrepareAuthKeySession(apiDef string, isBench bool) (string, *APISpec) { 220 spec := LoadSampleAPI(apiDef) 221 222 session := createAuthKeyAuthSession(isBench) 223 customToken := "" 224 if isBench { 225 customToken = uuid.New() 226 } else { 227 customToken = "54321111" 228 } 229 // AuthKey sessions are stored by {token} 230 spec.SessionManager.UpdateSession(customToken, session, 60, false) 231 return customToken, spec 232 } 233 234 func TestBearerTokenAuthKeySession(t *testing.T) { 235 customToken, spec := testPrepareAuthKeySession(authKeyDef, false) 236 237 recorder := httptest.NewRecorder() 238 req := TestReq(t, "GET", "/auth_key_test/", nil) 239 240 req.Header.Set("authorization", "Bearer "+customToken) 241 242 chain := getAuthKeyChain(spec) 243 chain.ServeHTTP(recorder, req) 244 245 if recorder.Code != 200 { 246 t.Error("Initial request failed with non-200 code, should have gone through!: \n", recorder.Code) 247 t.Error(recorder.Body.String()) 248 } 249 } 250 251 func BenchmarkBearerTokenAuthKeySession(b *testing.B) { 252 b.ReportAllocs() 253 254 customToken, spec := testPrepareAuthKeySession(authKeyDef, true) 255 256 recorder := httptest.NewRecorder() 257 req := TestReq(b, "GET", "/auth_key_test/", nil) 258 259 req.Header.Set("authorization", "Bearer "+customToken) 260 261 chain := getAuthKeyChain(spec) 262 263 for i := 0; i < b.N; i++ { 264 chain.ServeHTTP(recorder, req) 265 if recorder.Code != 200 { 266 b.Error("Initial request failed with non-200 code, should have gone through!: \n", recorder.Code) 267 b.Error(recorder.Body.String()) 268 } 269 } 270 } 271 272 const authKeyDef = `{ 273 "api_id": "31", 274 "org_id": "default", 275 "auth": {"auth_header_name": "authorization"}, 276 "version_data": { 277 "not_versioned": true, 278 "versions": { 279 "v1": {"name": "v1"} 280 } 281 }, 282 "proxy": { 283 "listen_path": "/auth_key_test/", 284 "target_url": "` + TestHttpAny + `" 285 } 286 }` 287 288 func TestMultiAuthBackwardsCompatibleSession(t *testing.T) { 289 customToken, spec := testPrepareAuthKeySession(multiAuthBackwardsCompatible, false) 290 291 recorder := httptest.NewRecorder() 292 req := TestReq(t, "GET", fmt.Sprintf("/auth_key_test/?token=%s", customToken), nil) 293 294 chain := getAuthKeyChain(spec) 295 chain.ServeHTTP(recorder, req) 296 297 if recorder.Code != 200 { 298 t.Error("Initial request failed with non-200 code, should have gone through!: \n", recorder.Code) 299 t.Error(recorder.Body.String()) 300 } 301 } 302 303 func BenchmarkMultiAuthBackwardsCompatibleSession(b *testing.B) { 304 b.ReportAllocs() 305 306 customToken, spec := testPrepareAuthKeySession(multiAuthBackwardsCompatible, true) 307 308 recorder := httptest.NewRecorder() 309 req := TestReq(b, "GET", fmt.Sprintf("/auth_key_test/?token=%s", customToken), nil) 310 311 chain := getAuthKeyChain(spec) 312 313 for i := 0; i < b.N; i++ { 314 chain.ServeHTTP(recorder, req) 315 if recorder.Code != 200 { 316 b.Error("Initial request failed with non-200 code, should have gone through!: \n", recorder.Code) 317 b.Error(recorder.Body.String()) 318 } 319 } 320 } 321 322 const multiAuthBackwardsCompatible = `{ 323 "api_id": "31", 324 "org_id": "default", 325 "auth": { 326 "auth_header_name": "token", 327 "use_param": true 328 }, 329 "version_data": { 330 "not_versioned": true, 331 "versions": { 332 "v1": {"name": "v1"} 333 } 334 }, 335 "proxy": { 336 "listen_path": "/auth_key_test/", 337 "target_url": "` + TestHttpAny + `" 338 } 339 }` 340 341 func TestMultiAuthSession(t *testing.T) { 342 spec := LoadSampleAPI(multiAuthDef) 343 session := createAuthKeyAuthSession(false) 344 customToken := "54321111" 345 // AuthKey sessions are stored by {token} 346 spec.SessionManager.UpdateSession(customToken, session, 60, false) 347 348 // Set the url param 349 recorder := httptest.NewRecorder() 350 req := TestReq(t, "GET", fmt.Sprintf("/auth_key_test/?token=%s", customToken), nil) 351 352 chain := getAuthKeyChain(spec) 353 chain.ServeHTTP(recorder, req) 354 355 if recorder.Code != 200 { 356 t.Error("First request failed with non-200 code, should have gone through!: \n", recorder.Code) 357 t.Error(recorder.Body.String()) 358 } 359 360 // Set the header 361 recorder = httptest.NewRecorder() 362 req = TestReq(t, "GET", "/auth_key_test/?token=", nil) 363 req.Header.Set("authorization", customToken) 364 365 chain.ServeHTTP(recorder, req) 366 367 if recorder.Code != 200 { 368 t.Error("Second request failed with non-200 code, should have gone through!: \n", recorder.Code) 369 t.Error(recorder.Body.String()) 370 } 371 372 // Set the cookie 373 recorder = httptest.NewRecorder() 374 req = TestReq(t, "GET", "/auth_key_test/?token=", nil) 375 req.AddCookie(&http.Cookie{Name: "oreo", Value: customToken}) 376 377 chain.ServeHTTP(recorder, req) 378 379 if recorder.Code != 200 { 380 t.Error("Third request failed with non-200 code, should have gone through!: \n", recorder.Code) 381 t.Error(recorder.Body.String()) 382 } 383 384 // No header, param or cookie 385 recorder = httptest.NewRecorder() 386 req = TestReq(t, "GET", "/auth_key_test/", nil) 387 388 chain.ServeHTTP(recorder, req) 389 390 if recorder.Code == 200 { 391 t.Error("Request returned 200 code, should NOT have gone through!: \n", recorder.Code) 392 t.Error(recorder.Body.String()) 393 } 394 } 395 396 const multiAuthDef = `{ 397 "api_id": "31", 398 "org_id": "default", 399 "auth": { 400 "auth_header_name": "authorization", 401 "param_name": "token", 402 "cookie_name": "oreo" 403 }, 404 "version_data": { 405 "not_versioned": true, 406 "versions": { 407 "v1": {"name": "v1"} 408 } 409 }, 410 "proxy": { 411 "listen_path": "/auth_key_test/", 412 "target_url": "` + TestHttpAny + `" 413 } 414 }` 415 416 func TestStripBearer(t *testing.T) { 417 var bearerTests = []struct { 418 in string 419 out string 420 }{ 421 {"Bearer abc", "abc"}, 422 {"bearer abc", "abc"}, 423 {"bEaReR abc", "abc"}, 424 {"Bearer: abc", "Bearer: abc"}, // invalid 425 {"Basic abc", "Basic abc"}, 426 {"abc", "abc"}, 427 } 428 429 for _, tt := range bearerTests { 430 t.Run(tt.in, func(t *testing.T) { 431 out := stripBearer(tt.in) 432 if out != tt.out { 433 t.Errorf("got %q, want %q", out, tt.out) 434 } 435 }) 436 } 437 } 438 439 func BenchmarkStripBearer(b *testing.B) { 440 b.ReportAllocs() 441 442 for i := 0; i < b.N; i++ { 443 _ = stripBearer("Bearer abcdefghijklmnopqrstuvwxyz12345678910") 444 } 445 }