github.com/Tyktechnologies/tyk@v2.9.5+incompatible/gateway/multiauth_test.go (about) 1 package gateway 2 3 import ( 4 "encoding/base64" 5 "fmt" 6 "net/http" 7 "net/http/httptest" 8 "net/url" 9 "strings" 10 "sync" 11 "testing" 12 "time" 13 14 jwt "github.com/dgrijalva/jwt-go" 15 "github.com/justinas/alice" 16 "github.com/lonelycode/go-uuid/uuid" 17 18 "github.com/TykTechnologies/tyk/apidef" 19 "github.com/TykTechnologies/tyk/test" 20 "github.com/TykTechnologies/tyk/user" 21 ) 22 23 const multiAuthDev = `{ 24 "api_id": "55", 25 "org_id": "default", 26 "use_basic_auth": true, 27 "use_standard_auth": true, 28 "base_identity_provided_by": "auth_token", 29 "auth_configs": { 30 "basic": {"auth_header_name": "Authorization"}, 31 "authToken": {"auth_header_name": "x-standard-auth"} 32 }, 33 "version_data": { 34 "not_versioned": true, 35 "versions": { 36 "v1": {"name": "v1"} 37 } 38 }, 39 "proxy": { 40 "listen_path": "/v1", 41 "target_url": "` + TestHttpAny + `" 42 } 43 }` 44 45 func createMultiAuthKeyAuthSession(isBench bool) *user.SessionState { 46 session := new(user.SessionState) 47 session.Rate = 100.0 48 session.Allowance = session.Rate 49 session.LastCheck = time.Now().Unix() 50 session.Per = 1.0 51 session.QuotaRenewalRate = 300 // 5 minutes 52 session.QuotaRenews = time.Now().Unix() 53 if isBench { 54 session.QuotaRemaining = 100000000 55 session.QuotaMax = 100000000 56 } else { 57 session.QuotaRemaining = 900 58 session.QuotaMax = 10 59 } 60 session.AccessRights = map[string]user.AccessDefinition{"55": {APIName: "Tyk Multi Key Test", APIID: "55", Versions: []string{"default"}}} 61 session.Mutex = &sync.RWMutex{} 62 return session 63 } 64 65 func createMultiBasicAuthSession(isBench bool) *user.SessionState { 66 session := new(user.SessionState) 67 session.Rate = 8.0 68 session.Allowance = session.Rate 69 session.LastCheck = time.Now().Unix() 70 session.Per = 1.0 71 session.QuotaRenewalRate = 300 // 5 minutes 72 session.QuotaRenews = time.Now().Unix() + 20 73 session.QuotaRemaining = 1 74 session.QuotaMax = -1 75 session.BasicAuthData = user.BasicAuthData{Password: "TEST"} 76 session.AccessRights = map[string]user.AccessDefinition{"55": {APIName: "Tyk Multi Key Test", APIID: "55", Versions: []string{"default"}}} 77 session.Mutex = &sync.RWMutex{} 78 return session 79 } 80 81 func getMultiAuthStandardAndBasicAuthChain(spec *APISpec) http.Handler { 82 remote, _ := url.Parse(TestHttpAny) 83 proxy := TykNewSingleHostReverseProxy(remote, spec, nil) 84 proxyHandler := ProxyHandler(proxy, spec) 85 baseMid := BaseMiddleware{Spec: spec, Proxy: proxy} 86 chain := alice.New(mwList( 87 &IPWhiteListMiddleware{baseMid}, 88 &IPBlackListMiddleware{BaseMiddleware: baseMid}, 89 &BasicAuthKeyIsValid{baseMid, nil, nil}, 90 &AuthKey{baseMid}, 91 &VersionCheck{BaseMiddleware: baseMid}, 92 &KeyExpired{baseMid}, 93 &AccessRightsCheck{baseMid}, 94 &RateLimitAndQuotaCheck{baseMid}, 95 )...).Then(proxyHandler) 96 return chain 97 } 98 99 func testPrepareMultiSessionBA(t testing.TB, isBench bool) (*APISpec, *http.Request) { 100 spec := LoadSampleAPI(multiAuthDev) 101 102 // Create BA 103 baSession := createMultiBasicAuthSession(isBench) 104 username := "" 105 if isBench { 106 username = uuid.New() 107 } else { 108 username = "0987876" 109 } 110 password := "TEST" 111 keyName := generateToken("default", username) 112 // Basic auth sessions are stored as {org-id}{username}, so we need to append it here when we create the session. 113 spec.SessionManager.UpdateSession(keyName, baSession, 60, false) 114 115 // Create key 116 session := createMultiAuthKeyAuthSession(isBench) 117 customToken := "" 118 if isBench { 119 customToken = uuid.New() 120 } else { 121 customToken = "84573485734587384888723487243" 122 } 123 // AuthKey sessions are stored by {token} 124 spec.SessionManager.UpdateSession(customToken, session, 60, false) 125 126 toEncode := strings.Join([]string{username, password}, ":") 127 encodedPass := base64.StdEncoding.EncodeToString([]byte(toEncode)) 128 129 req := TestReq(t, "GET", "/", nil) 130 req.Header.Set("Authorization", fmt.Sprintf("Basic %s", encodedPass)) 131 req.Header.Set("x-standard-auth", fmt.Sprintf("Bearer %s", customToken)) 132 133 return spec, req 134 } 135 136 func TestMultiSession_BA_Standard_OK(t *testing.T) { 137 spec, req := testPrepareMultiSessionBA(t, false) 138 139 recorder := httptest.NewRecorder() 140 chain := getMultiAuthStandardAndBasicAuthChain(spec) 141 chain.ServeHTTP(recorder, req) 142 143 if recorder.Code != 200 { 144 t.Error("Initial request failed with non-200 code, should have gone through!: \n", recorder.Code) 145 } 146 } 147 148 func BenchmarkMultiSession_BA_Standard_OK(b *testing.B) { 149 b.ReportAllocs() 150 151 spec, req := testPrepareMultiSessionBA(b, true) 152 153 recorder := httptest.NewRecorder() 154 chain := getMultiAuthStandardAndBasicAuthChain(spec) 155 156 for i := 0; i < b.N; i++ { 157 chain.ServeHTTP(recorder, req) 158 if recorder.Code != 200 { 159 b.Error("Initial request failed with non-200 code, should have gone through!: \n", recorder.Code) 160 } 161 } 162 } 163 164 func TestMultiSession_BA_Standard_Identity(t *testing.T) { 165 spec := LoadSampleAPI(multiAuthDev) 166 167 // Create BA 168 baSession := createMultiBasicAuthSession(false) 169 username := "0987876" 170 password := "TEST" 171 // Basic auth sessions are stored as {org-id}{username}, so we need to append it here when we create the session. 172 spec.SessionManager.UpdateSession("default0987876", baSession, 60, false) 173 174 // Create key 175 session := createMultiAuthKeyAuthSession(false) 176 customToken := "84573485734587384888723487243" 177 // AuthKey sessions are stored by {token} 178 spec.SessionManager.UpdateSession(customToken, session, 60, false) 179 180 to_encode := strings.Join([]string{username, password}, ":") 181 encodedPass := base64.StdEncoding.EncodeToString([]byte(to_encode)) 182 183 recorder := httptest.NewRecorder() 184 req := TestReq(t, "GET", "/", nil) 185 req.Header.Set("Authorization", fmt.Sprintf("Basic %s", encodedPass)) 186 req.Header.Set("x-standard-auth", fmt.Sprintf("Bearer %s", customToken)) 187 188 chain := getMultiAuthStandardAndBasicAuthChain(spec) 189 chain.ServeHTTP(recorder, req) 190 191 if recorder.Code != 200 { 192 t.Error("Initial request failed with non-200 code, should have gone through!: \n", recorder.Code) 193 } 194 195 if recorder.Header().Get("X-Ratelimit-Remaining") == "-1" { 196 t.Error("Expected quota limit but found -1, wrong base identity became context") 197 t.Error(recorder.Header().Get("X-Ratelimit-Remaining")) 198 } 199 } 200 201 func TestMultiSession_BA_Standard_FAILBA(t *testing.T) { 202 spec := LoadSampleAPI(multiAuthDev) 203 204 // Create BA 205 baSession := createMultiBasicAuthSession(false) 206 username := "0987876" 207 password := "WRONG" 208 // Basic auth sessions are stored as {org-id}{username}, so we need to append it here when we create the session. 209 spec.SessionManager.UpdateSession("default0987876", baSession, 60, false) 210 211 // Create key 212 session := createMultiAuthKeyAuthSession(false) 213 customToken := "84573485734587384888723487243" 214 // AuthKey sessions are stored by {token} 215 spec.SessionManager.UpdateSession(customToken, session, 60, false) 216 217 to_encode := strings.Join([]string{username, password}, ":") 218 encodedPass := base64.StdEncoding.EncodeToString([]byte(to_encode)) 219 220 recorder := httptest.NewRecorder() 221 req := TestReq(t, "GET", "/", nil) 222 req.Header.Set("Authorization", fmt.Sprintf("Basic %s", encodedPass)) 223 req.Header.Set("x-standard-auth", fmt.Sprintf("Bearer %s", customToken)) 224 225 chain := getMultiAuthStandardAndBasicAuthChain(spec) 226 chain.ServeHTTP(recorder, req) 227 228 if recorder.Code != 401 { 229 t.Error("Wrong response code received, expected 401: \n", recorder.Code) 230 } 231 } 232 233 func TestMultiSession_BA_Standard_FAILAuth(t *testing.T) { 234 spec := LoadSampleAPI(multiAuthDev) 235 236 // Create BA 237 baSession := createMultiBasicAuthSession(false) 238 username := "0987876" 239 password := "TEST" 240 // Basic auth sessions are stored as {org-id}{username}, so we need to append it here when we create the session. 241 spec.SessionManager.UpdateSession("default0987876", baSession, 60, false) 242 243 // Create key 244 session := createMultiAuthKeyAuthSession(false) 245 customToken := "84573485734587384888723487243" 246 // AuthKey sessions are stored by {token} 247 spec.SessionManager.UpdateSession(customToken, session, 60, false) 248 249 to_encode := strings.Join([]string{username, password}, ":") 250 encodedPass := base64.StdEncoding.EncodeToString([]byte(to_encode)) 251 252 recorder := httptest.NewRecorder() 253 req := TestReq(t, "GET", "/", nil) 254 req.Header.Set("Authorization", fmt.Sprintf("Basic %s", encodedPass)) 255 req.Header.Set("x-standard-auth", fmt.Sprintf("Bearer %s", "WRONGTOKEN")) 256 257 chain := getMultiAuthStandardAndBasicAuthChain(spec) 258 chain.ServeHTTP(recorder, req) 259 260 if recorder.Code != 403 { 261 t.Error("Wrong response code received, expected 403: \n", recorder.Code) 262 } 263 } 264 265 func TestJWTAuthKeyMultiAuth(t *testing.T) { 266 ts := StartTest() 267 defer ts.Close() 268 269 pID := CreatePolicy() 270 271 spec := BuildAndLoadAPI(func(spec *APISpec) { 272 spec.UseKeylessAccess = false 273 274 spec.AuthConfigs = make(map[string]apidef.AuthConfig) 275 276 spec.UseStandardAuth = true 277 authConfig := spec.AuthConfigs["authToken"] 278 authConfig.AuthHeaderName = "Auth-Token" 279 spec.AuthConfigs["authToken"] = authConfig 280 spec.BaseIdentityProvidedBy = apidef.AuthToken 281 282 spec.EnableJWT = true 283 spec.JWTSigningMethod = RSASign 284 spec.JWTSource = base64.StdEncoding.EncodeToString([]byte(jwtRSAPubKey)) 285 jwtConfig := spec.AuthConfigs["jwt"] 286 jwtConfig.AuthHeaderName = "Auth-JWT" 287 spec.AuthConfigs["jwt"] = jwtConfig 288 spec.JWTIdentityBaseField = "user_id" 289 spec.JWTPolicyFieldName = "policy_id" 290 spec.JWTDefaultPolicies = []string{pID} 291 292 spec.Proxy.ListenPath = "/" 293 })[0] 294 295 LoadAPI(spec) 296 297 jwtToken := CreateJWKToken(func(t *jwt.Token) { 298 t.Claims.(jwt.MapClaims)["user_id"] = "user" 299 t.Claims.(jwt.MapClaims)["exp"] = time.Now().Add(time.Hour * 72).Unix() 300 }) 301 302 key := CreateSession() 303 304 ts.Run(t, []test.TestCase{ 305 { 306 Headers: map[string]string{"Auth-JWT": jwtToken, "Auth-Token": key}, 307 Code: http.StatusOK, 308 }, 309 { 310 Headers: map[string]string{"Auth-JWT": jwtToken, "Auth-Token": key}, 311 Code: http.StatusOK, 312 }, 313 { 314 Headers: map[string]string{"Auth-JWT": jwtToken}, 315 Code: http.StatusUnauthorized, 316 BodyMatch: "Authorization field missing", 317 }, 318 { 319 Headers: map[string]string{"Auth-Token": key}, 320 Code: http.StatusBadRequest, 321 BodyMatch: "Authorization field missing", 322 }, 323 { 324 Headers: map[string]string{"Auth-JWT": "junk", "Auth-Token": key}, 325 Code: http.StatusForbidden, 326 BodyMatch: "Key not authorized", 327 }, 328 }...) 329 }