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 }