github.com/BTBurke/caddy-jwt@v3.7.1+incompatible/jwt_test.go (about) 1 package jwt 2 3 import ( 4 "fmt" 5 "net/http" 6 "net/http/httptest" 7 "os" 8 "strings" 9 "testing" 10 "time" 11 12 "io/ioutil" 13 14 jwt "github.com/dgrijalva/jwt-go" 15 "github.com/caddyserver/caddy/caddyhttp/httpserver" 16 . "github.com/onsi/ginkgo" 17 . "github.com/onsi/gomega" 18 ) 19 20 const ( 21 validToken = "eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJzdWIiOiIxMjM0NTY3ODkwIiwibmFtZSI6IkpvaG4gRG9lIiwiYWRtaW4iOnRydWV9.TJVA95OrM7E2cBab30RMHrHDcEfxjoYZgeFONFh7HgQ" 22 malformedToken = "loremIpsum" 23 rsaPublicKey = `-----BEGIN PUBLIC KEY----- 24 MIGfMA0GCSqGSIb3DQEBAQUAA4GNADCBiQKBgQCx8HkixKMKDI43bBcL5TxhNsTy 25 4qbZW+LMzSazcFmICITg/c3BbDyCS88VO6hqPhfLzQsNbaZeKKqxQfVudhYQI2cX 26 9ID2IuYxw3M8vazffhiJjgKVXnNaGdUCnKVFKVPxklwVztxVE8tYmfN0cvAeNafc 27 KPMSbZEZEqQeFfkafQIDAQAB 28 -----END PUBLIC KEY----- 29 ` 30 rsaPrivateKey = `-----BEGIN RSA PRIVATE KEY----- 31 MIICXgIBAAKBgQCx8HkixKMKDI43bBcL5TxhNsTy4qbZW+LMzSazcFmICITg/c3B 32 bDyCS88VO6hqPhfLzQsNbaZeKKqxQfVudhYQI2cX9ID2IuYxw3M8vazffhiJjgKV 33 XnNaGdUCnKVFKVPxklwVztxVE8tYmfN0cvAeNafcKPMSbZEZEqQeFfkafQIDAQAB 34 AoGBAI1NRDTK6BnTzJ/QUyDcIi2ku5ORTyPuZtVx2FjIUCDJexPcGKeP1yE1KDZZ 35 UK1Fr8nkgvFf8Kx3KM1obokQdwV3QXTtENIaLoq3OTzmDihGmvrSqCvfWPQNF/Wn 36 qxcMedY3z/u4RqHW5Gects0K6RDWNua8QV0W6jazRFzcfcKhAkEA6tSQiOmjUUQz 37 +IKNr0BU+r127uNuly9t5w6Umqd4i9eYzRZRaNeokFCn7qOr/D70hMJynHLYr3sZ 38 KtBQUsFf5QJBAMH6+THDtPfFiB8Qtz67ucQq2DwWWUjCVFLd3rqMiRqZ7mJNEv+C 39 YOusKbw54UHCD5bgORYC5HXVg2hzBYj2trkCQCA/oLmsnCkE3L4774kppIHqkvKr 40 ePx6HvWkIvQ6G2vY57sCXZuwQg3PhcBX6b5yRtIUgfjKLMeseABRKzayJ6ECQQCe 41 KcCdrvETRWBj1AFViUNCi5ycAazzAmA24OkGOihgJDqWtDlVVD0qa8nry1W7hDup 42 zVE+fUVCPsFSnNZagq8hAkEA4tOFUKxqEDg+QXaJbFXiUTj9BMDUlEGTqGS/becS 43 99L5HGoSkzGQazoqD6bA6ZQwF+gUN1LweweK7LLcnZsVFg== 44 -----END RSA PRIVATE KEY----- 45 ` 46 ecdsaPublicKey = `-----BEGIN PUBLIC KEY----- 47 MIGbMBAGByqGSM49AgEGBSuBBAAjA4GGAAQBa7NUN5FTTN0snJpIxpljP3vZ/gQA 48 X7yBZpGBdHxPAKcV1dkxUPZeaqJKS5UsGL+Z5QzaaionFVddNNTiZxFZVmoAJxcF 49 lW5lqXQXg4iJ6yNd7dVrNDSvH6CyVNME9lhu4sDXsYEofjidtnNsSQ4cLIiW3q2J 50 6pF7NtHApTtl/GKDPoY= 51 -----END PUBLIC KEY----- 52 ` 53 ecdsaPrivateKey = `-----BEGIN EC PRIVATE KEY----- 54 MIHcAgEBBEIB1QVyei7HRoi+sTQUj5RrvRiqZ5/xUSzqCm5hm/Xco5B/i2gZID/B 55 J48fw0IFpKcWX4DY8to2wQWI6vYH0Up+ekWgBwYFK4EEACOhgYkDgYYABAFrs1Q3 56 kVNM3SycmkjGmWM/e9n+BABfvIFmkYF0fE8ApxXV2TFQ9l5qokpLlSwYv5nlDNpq 57 KicVV1001OJnEVlWagAnFwWVbmWpdBeDiInrI13t1Ws0NK8foLJU0wT2WG7iwNex 58 gSh+OJ22c2xJDhwsiJberYnqkXs20cClO2X8YoM+hg== 59 -----END EC PRIVATE KEY----- 60 ` 61 ) 62 63 func TestCaddyJwt(t *testing.T) { 64 RegisterFailHandler(Fail) 65 RunSpecs(t, "CaddyJWT Suite") 66 } 67 68 func passThruHandler(w http.ResponseWriter, r *http.Request) (int, error) { 69 // copy received headers back into response so they can be inspected 70 for head, val := range r.Header { 71 w.Header().Add(head, val[0]) 72 } 73 return http.StatusOK, nil 74 } 75 76 func genToken(secret string, claims map[string]interface{}) string { 77 token := jwt.New(jwt.SigningMethodHS256) 78 token.Claims.(jwt.MapClaims)["exp"] = time.Now().Add(time.Hour * 1).Unix() 79 80 for claim, value := range claims { 81 token.Claims.(jwt.MapClaims)[claim] = value 82 } 83 validToken, err := token.SignedString([]byte(secret)) 84 if err != nil { 85 Fail(fmt.Sprintf("unexpected error constructing token: %s", err)) 86 } 87 return validToken 88 } 89 90 func genRSAToken(privatekey string, claims map[string]interface{}) string { 91 token := jwt.New(jwt.SigningMethodRS256) 92 token.Claims.(jwt.MapClaims)["exp"] = time.Now().Add(time.Hour * 1).Unix() 93 94 for claim, value := range claims { 95 token.Claims.(jwt.MapClaims)[claim] = value 96 } 97 pemKey, _ := jwt.ParseRSAPrivateKeyFromPEM([]byte(privatekey)) 98 99 validToken, err := token.SignedString(pemKey) 100 if err != nil { 101 Fail("failed constructing RSA token") 102 } 103 return validToken 104 } 105 106 func setSecretAndGetEnv(value string) *HmacKeyBackend { 107 backend := setSecretAndTryGetEnv(value) 108 if backend == nil { 109 Fail("unexpected error constructing backends") 110 } 111 return backend 112 } 113 114 func setSecretAndTryGetEnv(value string) *HmacKeyBackend { 115 if err := os.Setenv(ENV_SECRET, value); err != nil { 116 Fail("unexpected error setting JWT_SECRET") 117 } 118 os.Unsetenv(ENV_PUBLIC_KEY) 119 backends, err := NewDefaultKeyBackends() 120 if err != nil { 121 Fail(fmt.Sprintf("unexpected error constructing backends: %s", err)) 122 } 123 if len(backends) != 1 { 124 return nil 125 } 126 return backends[0].(*HmacKeyBackend) 127 } 128 129 func setPublicKeyAndGetEnv(value string) *PublicKeyBackend { 130 backend := setPublicKeyAndTryGetEnv(value) 131 if backend == nil { 132 Fail("unexpected error constructing backends") 133 } 134 return backend 135 } 136 137 func setPublicKeyAndTryGetEnv(value string) *PublicKeyBackend { 138 if err := os.Setenv(ENV_PUBLIC_KEY, value); err != nil { 139 Fail("unexpected error setting JWT_PUBLIC_KEY") 140 } 141 os.Unsetenv(ENV_SECRET) 142 backends, err := NewDefaultKeyBackends() 143 if err != nil { 144 Fail(fmt.Sprintf("unexpected error constructing backends: %s", err)) 145 } 146 if len(backends) != 1 { 147 return nil 148 } 149 return backends[0].(*PublicKeyBackend) 150 } 151 152 var _ = Describe("Auth", func() { 153 AfterEach(func() { 154 os.Unsetenv(ENV_PUBLIC_KEY) 155 os.Unsetenv(ENV_SECRET) 156 }) 157 Describe("Use environment to get secrets", func() { 158 159 It("should get the JWT secret from the environment JWT_SECRET", func() { 160 backend := setSecretAndGetEnv("secret") 161 Expect(backend.secret).To(Equal([]byte("secret"))) 162 }) 163 164 It("should return an error JWT_SECRET not set", func() { 165 backend := setSecretAndTryGetEnv("") 166 Expect(backend).To(BeNil()) 167 }) 168 169 It("should find RSA key material stored on disk", func() { 170 pemKey, _ := jwt.ParseRSAPublicKeyFromPEM([]byte(rsaPublicKey)) 171 keyfile, err := ioutil.TempFile(os.TempDir(), "testkey") 172 if err != nil { 173 Fail("Unexpected error creating temporary key file") 174 } 175 defer os.Remove(keyfile.Name()) 176 if _, err := keyfile.Write([]byte(rsaPublicKey)); err != nil { 177 Fail("Unexpected error writing temporary key file") 178 } 179 if err := keyfile.Close(); err != nil { 180 Fail("Unexpected error closing temporary key file") 181 } 182 backend, err := NewLazyPublicKeyFileBackend(keyfile.Name()) 183 if err != nil { 184 Fail(err.Error()) 185 } 186 if err := backend.loadIfRequired(); err != nil { 187 Fail(err.Error()) 188 } 189 Expect(backend.publicKey).To(Equal(pemKey)) 190 Expect(backend.filename).To(Equal(keyfile.Name())) 191 }) 192 193 It("should find ECDSA key material stored on disk", func() { 194 pemKey, _ := jwt.ParseECPublicKeyFromPEM([]byte(ecdsaPublicKey)) 195 keyfile, err := ioutil.TempFile(os.TempDir(), "testkey") 196 if err != nil { 197 Fail("Unexpected error creating temporary key file") 198 } 199 defer os.Remove(keyfile.Name()) 200 if _, err := keyfile.Write([]byte(ecdsaPublicKey)); err != nil { 201 Fail("Unexpected error writing temporary key file") 202 } 203 if err := keyfile.Close(); err != nil { 204 Fail("Unexpected error closing temporary key file") 205 } 206 backend, err := NewLazyPublicKeyFileBackend(keyfile.Name()) 207 if err != nil { 208 Fail(err.Error()) 209 } 210 if err := backend.loadIfRequired(); err != nil { 211 Fail(err.Error()) 212 } 213 Expect(backend.publicKey).To(Equal(pemKey)) 214 Expect(backend.filename).To(Equal(keyfile.Name())) 215 }) 216 217 It("should find HMAC key material stored on disk and invalidate cache if file changes", func() { 218 secret1 := []byte("secret1") 219 secret2 := []byte("secret2") 220 221 keyfile, err := ioutil.TempFile(os.TempDir(), "testkey") 222 if err != nil { 223 Fail("Unexpected error creating temporary key file") 224 } 225 defer os.Remove(keyfile.Name()) 226 227 if _, err := keyfile.Write(secret1); err != nil { 228 Fail("Unexpected error writing temporary key file") 229 } 230 if err := keyfile.Close(); err != nil { 231 Fail("Unexpected error closing temporary key file") 232 } 233 234 backend, err := NewLazyHmacKeyBackend(keyfile.Name()) 235 if err != nil { 236 Fail(err.Error()) 237 } 238 if err := backend.loadIfRequired(); err != nil { 239 Fail(err.Error()) 240 } 241 Expect(backend.secret).To(Equal(secret1)) 242 Expect(backend.filename).To(Equal(keyfile.Name())) 243 244 // write new value and invalidate cache after short timeout to allow modinfo time to change 245 time.Sleep(20 * time.Millisecond) 246 if err := ioutil.WriteFile(keyfile.Name(), secret2, os.ModePerm); err != nil { 247 Fail("Unexpected error overwriting keyfile in cache invalidation test") 248 } 249 250 if err := backend.loadIfRequired(); err != nil { 251 Fail(err.Error()) 252 } 253 Expect(backend.secret).To(Equal(secret2)) 254 Expect(backend.filename).To(Equal(keyfile.Name())) 255 }) 256 257 It("should detect invalid configurations of auth backends", func() { 258 os.Unsetenv("JWT_PUBLIC_KEY") 259 os.Unsetenv("JWT_SECRET") 260 backends, err := NewDefaultKeyBackends() 261 if err != nil { 262 Fail(err.Error()) 263 } 264 Expect(len(backends)).To(Equal(0)) 265 }) 266 }) 267 268 Describe("Validate flatten map function", func() { 269 270 listMap := map[string]interface{}{"context": map[string]interface{}{"user": map[string]interface{}{"roles": []string{"admin", "user"}}}} 271 myMap := map[string]interface{}{"context": map[string]interface{}{"user": map[string]interface{}{"username": "foobar"}}} 272 273 It("Should flatten map with dots", func() { 274 result, err := Flatten(myMap, "", DotStyle) 275 if err != nil { 276 panic(err) 277 } 278 expectedMap := map[string]interface{}{"context.user.username": "foobar"} 279 Expect(result).To(Equal(expectedMap)) 280 }) 281 It("Should flatten map and leave slices as is", func() { 282 result, err := Flatten(listMap, "", DotStyle) 283 if err != nil { 284 panic(err) 285 } 286 expectedMap := map[string]interface{}{"context.user.roles": []string{"admin", "user"}} 287 Expect(result).To(Equal(expectedMap)) 288 }) 289 }) 290 291 Describe("Find tokens in the request with a default token source config", func() { 292 // Empty list should trigger the use of the default config. 293 // This also tests each token source type individually. 294 emptyTssList := []TokenSource{} 295 It("should return the token if set in the Auhorization header", func() { 296 req, _ := http.NewRequest("GET", "/testing", nil) 297 req.Header.Set("Authorization", strings.Join([]string{"Bearer", validToken}, " ")) 298 token, err := ExtractToken(emptyTssList, req) 299 Expect(err).To(BeNil()) 300 Expect(token).To(Equal(validToken)) 301 }) 302 303 It("should return the token if set in a cookie", func() { 304 req, _ := http.NewRequest("GET", "/testing", nil) 305 req.AddCookie(&http.Cookie{Name: "jwt_token", Value: validToken}) 306 token, err := ExtractToken(emptyTssList, req) 307 Expect(err).To(BeNil()) 308 Expect(token).To(Equal(validToken)) 309 }) 310 311 It("should return the token if set as query parameter", func() { 312 url := strings.Join([]string{"/testing?token=", validToken}, "") 313 req, _ := http.NewRequest("GET", url, nil) 314 token, err := ExtractToken(emptyTssList, req) 315 Expect(err).To(BeNil()) 316 Expect(token).To(Equal(validToken)) 317 }) 318 }) 319 320 Describe("Find tokens in the request with a custom token source config", func() { 321 It("should return the token from the first source that finds it in the request", func() { 322 config := []TokenSource{ 323 &QueryTokenSource{ 324 ParamName: "custom_param", 325 }, 326 &CookieTokenSource{ 327 CookieName: "custom_jwt_token", 328 }, 329 &HeaderTokenSource{ 330 HeaderName: "Bearer", 331 }, 332 } 333 // These should be ignored as their names don't match. 334 url := strings.Join([]string{"/testing?token=", malformedToken}, "") 335 req, _ := http.NewRequest("GET", url, nil) 336 req.AddCookie(&http.Cookie{Name: "jwt_token", Value: malformedToken}) 337 // This should be ignored as it is the last in the config list. 338 req.Header.Set("Authorization", strings.Join([]string{"Bearer", malformedToken}, " ")) 339 // This should be extracted. 340 req.AddCookie(&http.Cookie{Name: "custom_jwt_token", Value: validToken}) 341 token, err := ExtractToken(config, req) 342 Expect(err).To(BeNil()) 343 Expect(token).To(Equal(validToken)) 344 }) 345 }) 346 347 Describe("Validate tokens in accordance with the JWT standard", func() { 348 349 It("should validate a correctly formed token", func() { 350 backend := setSecretAndGetEnv("secret") 351 token := jwt.New(jwt.SigningMethodHS256) 352 token.Claims.(jwt.MapClaims)["exp"] = time.Now().Add(time.Hour * 1).Unix() 353 sToken, err := token.SignedString([]byte("secret")) 354 if err != nil { 355 Fail(fmt.Sprintf("unexpected error constructing token: %s", err)) 356 } 357 vToken, err := ValidateToken(sToken, backend) 358 359 Expect(err).To(BeNil()) 360 Expect(vToken.Valid).To(Equal(true)) 361 }) 362 363 It("should validate a correctly formed RSA token", func() { 364 backend := setPublicKeyAndGetEnv(rsaPublicKey) 365 token := jwt.New(jwt.SigningMethodRS256) 366 token.Claims.(jwt.MapClaims)["exp"] = time.Now().Add(time.Hour * 1).Unix() 367 368 secret, err := jwt.ParseRSAPrivateKeyFromPEM([]byte(rsaPrivateKey)) 369 if err != nil { 370 Fail(fmt.Sprintf("unexpected error constructing private key: %s", err)) 371 } 372 sToken, err := token.SignedString(secret) 373 if err != nil { 374 Fail(fmt.Sprintf("unexpected error constructing token: %s", err)) 375 } 376 377 vToken, err := ValidateToken(sToken, backend) 378 379 Expect(err).To(BeNil()) 380 Expect(vToken.Valid).To(Equal(true)) 381 }) 382 383 It("should validate a correctly formed ECDSA token", func() { 384 backend := setPublicKeyAndGetEnv(ecdsaPublicKey) 385 token := jwt.New(jwt.SigningMethodES512) 386 token.Claims.(jwt.MapClaims)["exp"] = time.Now().Add(time.Hour * 1).Unix() 387 388 secret, err := jwt.ParseECPrivateKeyFromPEM([]byte(ecdsaPrivateKey)) 389 if err != nil { 390 Fail(fmt.Sprintf("unexpected error constructing private key: %s", err)) 391 } 392 sToken, err := token.SignedString(secret) 393 if err != nil { 394 Fail(fmt.Sprintf("unexpected error constructing token: %s", err)) 395 } 396 397 vToken, err := ValidateToken(sToken, backend) 398 399 Expect(err).To(BeNil()) 400 Expect(vToken.Valid).To(Equal(true)) 401 }) 402 403 It("should not validate a incorrectly formed token", func() { 404 backend := setSecretAndGetEnv("secret") 405 token := jwt.New(jwt.SigningMethodHS256) 406 token.Claims.(jwt.MapClaims)["exp"] = time.Now().Add(time.Hour * 1).Unix() 407 sToken, err := token.SignedString([]byte("notsecret")) 408 if err != nil { 409 Fail(fmt.Sprintf("unexpected error constructing token: %s", err)) 410 } 411 412 vToken, err := ValidateToken(sToken, backend) 413 414 Expect(err).To(HaveOccurred()) 415 Expect(vToken).To(BeNil()) 416 }) 417 418 It("should not validate a malformed token", func() { 419 backend := setSecretAndGetEnv("secret") 420 421 vToken, err := ValidateToken(malformedToken, backend) 422 423 Expect(err).To(HaveOccurred()) 424 Expect(vToken).To(BeNil()) 425 }) 426 427 It("should not validate a token with an expired timestamp", func() { 428 backend := setSecretAndGetEnv("secret") 429 token := jwt.New(jwt.SigningMethodHS256) 430 token.Claims.(jwt.MapClaims)["exp"] = time.Now().Add(time.Hour * -1).Unix() 431 sToken, err := token.SignedString([]byte("secret")) 432 if err != nil { 433 Fail(fmt.Sprintf("unexpected error constructing token: %s", err)) 434 } 435 436 vToken, err := ValidateToken(sToken, backend) 437 438 Expect(err).To(HaveOccurred()) 439 Expect(vToken).To(BeNil()) 440 }) 441 442 It("should not allow JWT with algorithm none", func() { 443 backend := setSecretAndGetEnv("secret") 444 token := jwt.New(jwt.SigningMethodHS256) 445 token.Header["alg"] = "none" 446 token.Claims.(jwt.MapClaims)["exp"] = time.Now().Add(time.Hour * 1).Unix() 447 sToken, err := token.SignedString([]byte("secret")) 448 if err != nil { 449 Fail(fmt.Sprintf("unexpected error constructing token: %s", err)) 450 } 451 452 vToken, err := ValidateToken(sToken, backend) 453 454 Expect(err).To(HaveOccurred()) 455 Expect(vToken).To(BeNil()) 456 }) 457 }) 458 Describe("Redirect on access deny works", func() { 459 It("return 303 when a redirect is configured and access denied", func() { 460 req, err := http.NewRequest("GET", "/testing", nil) 461 462 rec := httptest.NewRecorder() 463 rw := Auth{ 464 Rules: []Rule{{Path: "/testing", Redirect: "/login"}}, 465 } 466 result, err := rw.ServeHTTP(rec, req) 467 if err != nil { 468 Fail(fmt.Sprintf("unexpected error constructing server: %s", err)) 469 } 470 471 Expect(result).To(Equal(http.StatusSeeOther)) 472 Expect(rec.Result().StatusCode).To(Equal(http.StatusSeeOther)) 473 Expect(rec.Result().Header.Get("Location")).To(Equal("/login")) 474 }) 475 It("variables in location value are replaced", func() { 476 req, err := http.NewRequest("GET", "/testing", nil) 477 478 rec := httptest.NewRecorder() 479 rw := Auth{ 480 Rules: []Rule{{Path: "/testing", Redirect: "/login?backTo={rewrite_uri}"}}, 481 } 482 result, err := rw.ServeHTTP(rec, req) 483 if err != nil { 484 Fail(fmt.Sprintf("unexpected error constructing server: %s", err)) 485 } 486 487 Expect(result).To(Equal(http.StatusSeeOther)) 488 Expect(rec.Result().StatusCode).To(Equal(http.StatusSeeOther)) 489 Expect(rec.Result().Header.Get("Location")).To(Equal("/login?backTo=/testing")) 490 }) 491 }) 492 Describe("Function correctly as an authorization middleware for malformed paths", func() { 493 It("return 401 when no authorization header and the path is protected (malformed path - 1st level)", func() { 494 rw := Auth{ 495 Next: httpserver.HandlerFunc(passThruHandler), 496 Rules: []Rule{ 497 Rule{Path: "/"}, 498 }, 499 Realm: "testing.com", 500 } 501 req, err := http.NewRequest("GET", "//testing", nil) 502 503 rec := httptest.NewRecorder() 504 result, err := rw.ServeHTTP(rec, req) 505 if err != nil { 506 Fail(fmt.Sprintf("unexpected error constructing server: %s", err)) 507 } 508 509 Expect(result).To(Equal(http.StatusUnauthorized)) 510 Expect(rec.Result().Header.Get("WWW-Authenticate")).To(Equal("Bearer realm=\"testing.com\",error=\"invalid_token\"")) 511 }) 512 It("return 401 when no authorization header and the path is protected (malformed path - root level)", func() { 513 rw := Auth{ 514 Next: httpserver.HandlerFunc(passThruHandler), 515 Rules: []Rule{ 516 Rule{Path: "/"}, 517 }, 518 Realm: "testing.com", 519 } 520 req, err := http.NewRequest("GET", "//", nil) 521 522 rec := httptest.NewRecorder() 523 result, err := rw.ServeHTTP(rec, req) 524 if err != nil { 525 Fail(fmt.Sprintf("unexpected error constructing server: %s", err)) 526 } 527 528 Expect(result).To(Equal(http.StatusUnauthorized)) 529 Expect(rec.Result().Header.Get("WWW-Authenticate")).To(Equal("Bearer realm=\"testing.com\",error=\"invalid_token\"")) 530 }) 531 532 It("return 401 when no authorization header and the path is protected (malformed path - 2nd level)", func() { 533 rw := Auth{ 534 Next: httpserver.HandlerFunc(passThruHandler), 535 Rules: []Rule{ 536 Rule{Path: "/testing/test"}, 537 }, 538 Realm: "testing.com", 539 } 540 req, err := http.NewRequest("GET", "/testing//test", nil) 541 542 rec := httptest.NewRecorder() 543 result, err := rw.ServeHTTP(rec, req) 544 if err != nil { 545 Fail(fmt.Sprintf("unexpected error constructing server: %s", err)) 546 } 547 548 Expect(result).To(Equal(http.StatusUnauthorized)) 549 Expect(rec.Result().Header.Get("WWW-Authenticate")).To(Equal("Bearer realm=\"testing.com\",error=\"invalid_token\"")) 550 }) 551 552 It("return 401 when no authorization header and the path is protected (malformed path - 2nd of nested)", func() { 553 rw := Auth{ 554 Next: httpserver.HandlerFunc(passThruHandler), 555 Rules: []Rule{ 556 Rule{Path: "/testing/test/secret"}, 557 }, 558 Realm: "testing.com", 559 } 560 req, err := http.NewRequest("GET", "/testing//test/secret", nil) 561 562 rec := httptest.NewRecorder() 563 result, err := rw.ServeHTTP(rec, req) 564 if err != nil { 565 Fail(fmt.Sprintf("unexpected error constructing server: %s", err)) 566 } 567 568 Expect(result).To(Equal(http.StatusUnauthorized)) 569 Expect(rec.Result().Header.Get("WWW-Authenticate")).To(Equal("Bearer realm=\"testing.com\",error=\"invalid_token\"")) 570 }) 571 It("return 401 when no authorization header and the path is protected (malformed path - 3rd level)", func() { 572 rw := Auth{ 573 Next: httpserver.HandlerFunc(passThruHandler), 574 Rules: []Rule{ 575 Rule{Path: "/testing/test/secret"}, 576 }, 577 Realm: "testing.com", 578 } 579 req, err := http.NewRequest("GET", "/testing/test//secret", nil) 580 581 rec := httptest.NewRecorder() 582 result, err := rw.ServeHTTP(rec, req) 583 if err != nil { 584 Fail(fmt.Sprintf("unexpected error constructing server: %s", err)) 585 } 586 587 Expect(result).To(Equal(http.StatusUnauthorized)) 588 Expect(rec.Result().Header.Get("WWW-Authenticate")).To(Equal("Bearer realm=\"testing.com\",error=\"invalid_token\"")) 589 }) 590 591 }) 592 Describe("Function correctly as an authorization middleware", func() { 593 backend := setSecretAndGetEnv("secret") 594 rw := Auth{ 595 Next: httpserver.HandlerFunc(passThruHandler), 596 Rules: []Rule{ 597 Rule{Path: "/testing", ExceptedPaths: []string{"/testing/excepted"}, KeyBackends: []KeyBackend{backend}}, 598 }, 599 Realm: "testing.com", 600 } 601 token := jwt.New(jwt.SigningMethodHS256) 602 token.Claims.(jwt.MapClaims)["exp"] = time.Now().Add(time.Hour * 1).Unix() 603 token.Claims.(jwt.MapClaims)["user"] = "test" 604 token.Claims.(jwt.MapClaims)["int32"] = int32(10) 605 token.Claims.(jwt.MapClaims)["float32"] = float32(3.14159) 606 token.Claims.(jwt.MapClaims)["float64"] = float64(3.14159) 607 token.Claims.(jwt.MapClaims)["bool"] = true 608 token.Claims.(jwt.MapClaims)["list"] = []string{"foo", "bar", "bazz"} 609 token.Claims.(jwt.MapClaims)["http://test.com/path"] = "true" 610 611 validToken, err := token.SignedString([]byte("secret")) 612 if err != nil { 613 Fail(fmt.Sprintf("unexpected error constructing token: %s", err)) 614 } 615 616 invalidToken, err := token.SignedString([]byte("notsecret")) 617 if err != nil { 618 Fail(fmt.Sprintf("unexpected error constructing token: %s", err)) 619 } 620 621 It("return 401 when no authorization header and the path is protected", func() { 622 req, err := http.NewRequest("GET", "/testing", nil) 623 624 rec := httptest.NewRecorder() 625 result, err := rw.ServeHTTP(rec, req) 626 if err != nil { 627 Fail(fmt.Sprintf("unexpected error constructing server: %s", err)) 628 } 629 630 Expect(result).To(Equal(http.StatusUnauthorized)) 631 Expect(rec.Result().Header.Get("WWW-Authenticate")).To(Equal("Bearer realm=\"testing.com\",error=\"invalid_token\"")) 632 }) 633 634 It("return 401 when no authorization header and the path is protected (malformed path)", func() { 635 req, err := http.NewRequest("GET", "//testing", nil) 636 637 rec := httptest.NewRecorder() 638 result, err := rw.ServeHTTP(rec, req) 639 if err != nil { 640 Fail(fmt.Sprintf("unexpected error constructing server: %s", err)) 641 } 642 643 Expect(result).To(Equal(http.StatusUnauthorized)) 644 Expect(rec.Result().Header.Get("WWW-Authenticate")).To(Equal("Bearer realm=\"testing.com\",error=\"invalid_token\"")) 645 }) 646 647 It("return 401 when no token and the path is protected", func() { 648 req, err := http.NewRequest("GET", "/testing", nil) 649 req.Header.Set("Authorization", strings.Join([]string{"Basic", "QWxhZGRpbjpvcGVuIHNlc2FtZQ=="}, " ")) 650 651 rec := httptest.NewRecorder() 652 result, err := rw.ServeHTTP(rec, req) 653 if err != nil { 654 Fail(fmt.Sprintf("unexpected error constructing server: %s", err)) 655 } 656 657 Expect(result).To(Equal(http.StatusUnauthorized)) 658 Expect(rec.Result().Header.Get("WWW-Authenticate")).To(Equal("Bearer realm=\"testing.com\",error=\"invalid_token\"")) 659 }) 660 661 It("return 401 when the token is not valid and the path is protected", func() { 662 req, err := http.NewRequest("GET", "/testing", nil) 663 req.Header.Set("Authorization", strings.Join([]string{"Bearer", invalidToken}, " ")) 664 665 rec := httptest.NewRecorder() 666 result, err := rw.ServeHTTP(rec, req) 667 if err != nil { 668 Fail(fmt.Sprintf("unexpected error constructing server: %s", err)) 669 } 670 671 Expect(result).To(Equal(http.StatusUnauthorized)) 672 Expect(rec.Result().Header.Get("WWW-Authenticate")).To(Equal("Bearer realm=\"testing.com\",error=\"invalid_token\"")) 673 }) 674 675 It("allow valid requests to continue to next handler", func() { 676 req, err := http.NewRequest("GET", "/testing", nil) 677 req.Header.Set("Authorization", strings.Join([]string{"Bearer", validToken}, " ")) 678 679 rec := httptest.NewRecorder() 680 result, err := rw.ServeHTTP(rec, req) 681 if err != nil { 682 Fail(fmt.Sprintf("unexpected error constructing server: %s", err)) 683 } 684 685 Expect(result).To(Equal(http.StatusOK)) 686 }) 687 688 It("allow OPTIONS requests to continue to next handler", func() { 689 req, err := http.NewRequest("OPTIONS", "/testing", nil) 690 691 rec := httptest.NewRecorder() 692 result, err := rw.ServeHTTP(rec, req) 693 if err != nil { 694 Fail(fmt.Sprintf("unexpected error constructing server: %s", err)) 695 } 696 697 Expect(result).To(Equal(http.StatusOK)) 698 }) 699 700 It("allow unprotected requests to continue to next handler", func() { 701 req, err := http.NewRequest("GET", "/unprotected", nil) 702 703 rec := httptest.NewRecorder() 704 result, err := rw.ServeHTTP(rec, req) 705 if err != nil { 706 Fail(fmt.Sprintf("unexpected error constructing server: %s", err)) 707 } 708 709 Expect(result).To(Equal(http.StatusOK)) 710 }) 711 712 It("allow excepted path requests to continue to next handler", func() { 713 req, err := http.NewRequest("GET", "/testing/excepted", nil) 714 715 rec := httptest.NewRecorder() 716 result, err := rw.ServeHTTP(rec, req) 717 if err != nil { 718 Fail(fmt.Sprintf("unexpected error constructing server: %s", err)) 719 } 720 721 Expect(result).To(Equal(http.StatusOK)) 722 }) 723 724 It("set claims as individual headers", func() { 725 req, err := http.NewRequest("GET", "/testing", nil) 726 req.Header.Set("Authorization", strings.Join([]string{"Bearer", validToken}, " ")) 727 728 rec := httptest.NewRecorder() 729 result, err := rw.ServeHTTP(rec, req) 730 if err != nil { 731 Fail(fmt.Sprintf("unexpected error constructing server: %s", err)) 732 } 733 734 Expect(result).To(Equal(http.StatusOK)) 735 expectedHeaders := map[string]string{ 736 "Token-Claim-User": "test", 737 "Token-Claim-Bool": "true", 738 "Token-Claim-Float32": "3.14159", 739 "Token-Claim-Float64": "3.14159", 740 "Token-Claim-Int32": "10", 741 "Token-Claim-List": "foo,bar,bazz", 742 "Token-Claim-Http:%2F%2Ftest.com%2Fpath": "true", 743 } 744 returnedHeaders := rec.Header() 745 for head, value := range expectedHeaders { 746 val, ok := returnedHeaders[head] 747 if !ok { 748 Fail(fmt.Sprintf("expected header not in response: %v. Have: %v", head, returnedHeaders)) 749 } 750 Expect(val[0]).To(Equal(value)) 751 } 752 753 }) 754 Describe("Strip headers when set", func() { 755 backend := setSecretAndGetEnv("secret") 756 rw := Auth{ 757 Next: httpserver.HandlerFunc(passThruHandler), 758 Rules: []Rule{ 759 Rule{Path: "/testing", ExceptedPaths: []string{"/testing/excepted"}, StripHeader: true, KeyBackends: []KeyBackend{backend}}, 760 }, 761 Realm: "testing.com", 762 } 763 token := jwt.New(jwt.SigningMethodHS256) 764 token.Claims.(jwt.MapClaims)["exp"] = time.Now().Add(time.Hour * 1).Unix() 765 token.Claims.(jwt.MapClaims)["user"] = "test" 766 token.Claims.(jwt.MapClaims)["int32"] = int32(10) 767 token.Claims.(jwt.MapClaims)["float32"] = float32(3.14159) 768 token.Claims.(jwt.MapClaims)["float64"] = float64(3.14159) 769 token.Claims.(jwt.MapClaims)["bool"] = true 770 token.Claims.(jwt.MapClaims)["list"] = []string{"foo", "bar", "bazz"} 771 token.Claims.(jwt.MapClaims)["http://test.com/path.me"] = "true" 772 773 validToken, err := token.SignedString([]byte("secret")) 774 if err != nil { 775 Fail(fmt.Sprintf("unexpected error constructing token: %s", err)) 776 } 777 778 It("set claims as individual headers, and strips if necessary", func() { 779 req, err := http.NewRequest("GET", "/testing", nil) 780 req.Header.Set("Authorization", strings.Join([]string{"Bearer", validToken}, " ")) 781 782 rec := httptest.NewRecorder() 783 result, err := rw.ServeHTTP(rec, req) 784 if err != nil { 785 Fail(fmt.Sprintf("unexpected error constructing server: %s", err)) 786 } 787 788 Expect(result).To(Equal(http.StatusOK)) 789 expectedHeaders := map[string]string{ 790 "Token-Claim-User": "test", 791 "Token-Claim-Bool": "true", 792 "Token-Claim-Float32": "3.14159", 793 "Token-Claim-Float64": "3.14159", 794 "Token-Claim-Int32": "10", 795 "Token-Claim-List": "foo,bar,bazz", 796 "Token-Claim-Path.me": "true", 797 } 798 returnedHeaders := rec.Header() 799 for head, value := range expectedHeaders { 800 val, ok := returnedHeaders[head] 801 if !ok { 802 Fail(fmt.Sprintf("expected header not in response: %v. Have: %v", head, returnedHeaders)) 803 } 804 Expect(val[0]).To(Equal(value)) 805 } 806 807 }) 808 }) 809 Describe("Function correctly as an authorization middleware for complex access rules", func() { 810 backend := setSecretAndGetEnv("secret") 811 tokenUser := genToken("secret", map[string]interface{}{"user": "test", "role": "member"}) 812 tokenNotUser := genToken("secret", map[string]interface{}{"user": "bad"}) 813 tokenAdmin := genToken("secret", map[string]interface{}{"role": "admin"}) 814 accessRuleAllowUser := AccessRule{Authorize: ALLOW, 815 Claim: "user", 816 Value: "test", 817 } 818 accessRuleAllowRole := AccessRule{Authorize: ALLOW, 819 Claim: "role", 820 Value: "admin", 821 } 822 accessRuleDenyRole := AccessRule{Authorize: DENY, 823 Claim: "role", 824 Value: "member", 825 } 826 ruleAllowUser := Rule{Path: "/testing", AccessRules: []AccessRule{accessRuleAllowUser}, KeyBackends: []KeyBackend{backend}} 827 ruleDenyRole := Rule{Path: "/testing", AccessRules: []AccessRule{accessRuleDenyRole}, KeyBackends: []KeyBackend{backend}} 828 ruleAllowRoleAllowUser := []Rule{Rule{Path: "/testing", AccessRules: []AccessRule{accessRuleAllowRole, accessRuleAllowUser}, KeyBackends: []KeyBackend{backend}}} 829 ruleDenyRoleAllowUser := []Rule{Rule{Path: "/testing", AccessRules: []AccessRule{accessRuleDenyRole, accessRuleAllowUser}, KeyBackends: []KeyBackend{backend}}} 830 831 It("should allow authorization based on a specific claim value", func() { 832 rw := Auth{ 833 Next: httpserver.HandlerFunc(passThruHandler), 834 Rules: []Rule{ruleAllowUser}, 835 } 836 837 req, err := http.NewRequest("GET", "/testing", nil) 838 req.Header.Set("Authorization", strings.Join([]string{"Bearer", tokenUser}, " ")) 839 840 rec := httptest.NewRecorder() 841 result, err := rw.ServeHTTP(rec, req) 842 if err != nil { 843 Fail(fmt.Sprintf("unexpected error constructing server: %s", err)) 844 } 845 846 Expect(result).To(Equal(http.StatusOK)) 847 }) 848 It("should deny authorization based on a specific claim value that doesnt match", func() { 849 rw := Auth{ 850 Next: httpserver.HandlerFunc(passThruHandler), 851 Rules: []Rule{ruleAllowUser}, 852 Realm: "testing.com", 853 } 854 855 req, err := http.NewRequest("GET", "/testing", nil) 856 req.Header.Set("Authorization", strings.Join([]string{"Bearer", tokenNotUser}, " ")) 857 858 rec := httptest.NewRecorder() 859 result, err := rw.ServeHTTP(rec, req) 860 if err != nil { 861 Fail(fmt.Sprintf("unexpected error constructing server: %s", err)) 862 } 863 864 Expect(result).To(Equal(http.StatusForbidden)) 865 Expect(rec.Result().Header.Get("WWW-Authenticate")).To(Equal("Bearer realm=\"testing.com\",error=\"insufficient_scope\"")) 866 }) 867 It("should correctly apply rules in order with multiple ALLOWs", func() { 868 // tests situation where user is denied based on wrong role 869 // but subsequent allow based on username is ok 870 rw := Auth{ 871 Next: httpserver.HandlerFunc(passThruHandler), 872 Rules: ruleAllowRoleAllowUser, 873 } 874 875 req, err := http.NewRequest("GET", "/testing", nil) 876 req.Header.Set("Authorization", strings.Join([]string{"Bearer", tokenUser}, " ")) 877 878 rec := httptest.NewRecorder() 879 result, err := rw.ServeHTTP(rec, req) 880 if err != nil { 881 Fail(fmt.Sprintf("unexpected error constructing server: %s", err)) 882 } 883 884 Expect(result).To(Equal(http.StatusOK)) 885 }) 886 It("should correctly apply rules in order with a DENY then ALLOW", func() { 887 // test situation where default deny for a particular role 888 // subsequent rule based on user ok 889 rw := Auth{ 890 Next: httpserver.HandlerFunc(passThruHandler), 891 Rules: ruleDenyRoleAllowUser, 892 } 893 894 req, err := http.NewRequest("GET", "/testing", nil) 895 req.Header.Set("Authorization", strings.Join([]string{"Bearer", tokenUser}, " ")) 896 897 rec := httptest.NewRecorder() 898 result, err := rw.ServeHTTP(rec, req) 899 if err != nil { 900 Fail(fmt.Sprintf("unexpected error constructing server: %s", err)) 901 } 902 903 Expect(result).To(Equal(http.StatusOK)) 904 }) 905 906 It("should correctly deny based on specific match", func() { 907 // tests situation where user is denied based on wrong role 908 // but subsequent allow based on username is ok 909 rw := Auth{ 910 Next: httpserver.HandlerFunc(passThruHandler), 911 Rules: []Rule{ruleDenyRole}, 912 Realm: "testing.com", 913 } 914 915 req, err := http.NewRequest("GET", "/testing", nil) 916 req.Header.Set("Authorization", strings.Join([]string{"Bearer", tokenUser}, " ")) 917 918 rec := httptest.NewRecorder() 919 result, err := rw.ServeHTTP(rec, req) 920 if err != nil { 921 Fail(fmt.Sprintf("unexpected error constructing server: %s", err)) 922 } 923 924 Expect(result).To(Equal(http.StatusForbidden)) 925 Expect(rec.Result().Header.Get("WWW-Authenticate")).To(Equal("Bearer realm=\"testing.com\",error=\"insufficient_scope\"")) 926 }) 927 928 It("should allow based on no match to DENY", func() { 929 // tests situation where user is denied based on wrong role 930 // but subsequent allow based on username is ok 931 rw := Auth{ 932 Next: httpserver.HandlerFunc(passThruHandler), 933 Rules: []Rule{ruleDenyRole}, 934 } 935 936 req, err := http.NewRequest("GET", "/testing", nil) 937 req.Header.Set("Authorization", strings.Join([]string{"Bearer", tokenAdmin}, " ")) 938 939 rec := httptest.NewRecorder() 940 result, err := rw.ServeHTTP(rec, req) 941 if err != nil { 942 Fail(fmt.Sprintf("unexpected error constructing server: %s", err)) 943 } 944 945 Expect(result).To(Equal(http.StatusOK)) 946 }) 947 }) 948 Describe("Function correctly as an authorization middleware for list types", func() { 949 950 tokenGroups := genToken("secret", map[string]interface{}{"group": []string{"admin", "user"}}) 951 tokenGroupsOperator := genToken("secret", map[string]interface{}{"group": []string{"operator"}}) 952 ruleAllowUser := Rule{Path: "/testing", AccessRules: []AccessRule{ 953 AccessRule{Authorize: ALLOW, 954 Claim: "group", 955 Value: "admin", 956 }, 957 AccessRule{Authorize: DENY, 958 Claim: "group", 959 Value: "operator", 960 }, 961 }, KeyBackends: []KeyBackend{backend}} 962 BeforeEach(func() { 963 if err := os.Setenv("JWT_SECRET", "secret"); err != nil { 964 Fail("Could not set environment secret") 965 } 966 if err := os.Setenv("JWT_PUBLIC_KEY", ""); err != nil { 967 Fail("Could not unset secret") 968 } 969 }) 970 971 It("should allow claim values, which are part of a list", func() { 972 rw := Auth{ 973 Next: httpserver.HandlerFunc(passThruHandler), 974 Rules: []Rule{ruleAllowUser}, 975 } 976 977 req, err := http.NewRequest("GET", "/testing", nil) 978 req.Header.Set("Authorization", strings.Join([]string{"Bearer", tokenGroups}, " ")) 979 980 rec := httptest.NewRecorder() 981 result, err := rw.ServeHTTP(rec, req) 982 if err != nil { 983 Fail(fmt.Sprintf("unexpected error constructing server: %s", err)) 984 } 985 986 Expect(result).To(Equal(http.StatusOK)) 987 }) 988 It("should deny claim values, which are part of a list", func() { 989 rw := Auth{ 990 Next: httpserver.HandlerFunc(passThruHandler), 991 Rules: []Rule{ruleAllowUser}, 992 Realm: "testing.com", 993 } 994 995 req, err := http.NewRequest("GET", "/testing", nil) 996 req.Header.Set("Authorization", strings.Join([]string{"Bearer", tokenGroupsOperator}, " ")) 997 998 rec := httptest.NewRecorder() 999 result, err := rw.ServeHTTP(rec, req) 1000 if err != nil { 1001 Fail(fmt.Sprintf("unexpected error constructing server: %s", err)) 1002 } 1003 1004 Expect(result).To(Equal(http.StatusForbidden)) 1005 Expect(rec.Result().Header.Get("WWW-Authenticate")).To(Equal("Bearer realm=\"testing.com\",error=\"insufficient_scope\"")) 1006 }) 1007 }) 1008 1009 Describe("Prevent spoofing of claims headers", func() { 1010 It("should remove spoofed claims with no JWT provided", func() { 1011 rw := Auth{ 1012 Next: httpserver.HandlerFunc(passThruHandler), 1013 Rules: []Rule{{Path: "/testing", Passthrough: true}}, 1014 } 1015 req, err := http.NewRequest("GET", "/testing", nil) 1016 req.Header.Set("Token-Claim-Spoofed", "spoof") 1017 1018 rec := httptest.NewRecorder() 1019 result, err := rw.ServeHTTP(rec, req) 1020 if err != nil { 1021 Fail(fmt.Sprintf("unexpected error constructing server: %s", err)) 1022 } 1023 1024 Expect(result).To(Equal(http.StatusOK)) 1025 Expect(rec.Result().Header.Get("Token-Claim-Spoofed")).To(Equal("")) 1026 }) 1027 1028 It("should remove spoofed claims with valid token provided", func() { 1029 rw := Auth{ 1030 Next: httpserver.HandlerFunc(passThruHandler), 1031 Rules: []Rule{{Path: "/testing", Passthrough: true}}, 1032 } 1033 req, err := http.NewRequest("GET", "/testing", nil) 1034 req.Header.Set("Token-Claim-Spoofed", "spoof") 1035 req.Header.Set("Authorization", strings.Join([]string{"Bearer", validToken}, " ")) 1036 1037 rec := httptest.NewRecorder() 1038 result, err := rw.ServeHTTP(rec, req) 1039 if err != nil { 1040 Fail(fmt.Sprintf("unexpected error constructing server: %s", err)) 1041 } 1042 1043 Expect(result).To(Equal(http.StatusOK)) 1044 Expect(rec.Result().Header.Get("Token-Claim-Spoofed")).To(Equal("")) 1045 }) 1046 1047 It("should remove spoofed claims with invalid token provided", func() { 1048 rw := Auth{ 1049 Next: httpserver.HandlerFunc(passThruHandler), 1050 Rules: []Rule{{Path: "/testing", Passthrough: true}}, 1051 } 1052 req, err := http.NewRequest("GET", "/testing", nil) 1053 req.Header.Set("Token-Claim-Spoofed", "spoof") 1054 req.Header.Set("Authorization", strings.Join([]string{"Bearer", "foo"}, " ")) 1055 1056 rec := httptest.NewRecorder() 1057 result, err := rw.ServeHTTP(rec, req) 1058 if err != nil { 1059 Fail(fmt.Sprintf("unexpected error constructing server: %s", err)) 1060 } 1061 1062 Expect(result).To(Equal(http.StatusOK)) 1063 Expect(rec.Result().Header.Get("Token-Claim-Spoofed")).To(Equal("")) 1064 }) 1065 }) 1066 1067 Describe("Handle multiple keyfiles correctly", func() { 1068 It("should allow access when one of the keyfiles matches", func() { 1069 key1, err := createKeyFile(rsaPublicKey) 1070 if err != nil { 1071 Fail(fmt.Sprintf("unexpected error creating key file: %s", err)) 1072 } 1073 backend1, err := NewLazyPublicKeyFileBackend(key1) 1074 if err != nil { 1075 Fail(err.Error()) 1076 } 1077 defer os.Remove(key1) 1078 key2, err := createKeyFile("notvalidkey") 1079 if err != nil { 1080 Fail(fmt.Sprintf("unexpected error creating key file: %s", err)) 1081 } 1082 backend2, err := NewLazyPublicKeyFileBackend(key2) 1083 if err != nil { 1084 Fail(err.Error()) 1085 } 1086 defer os.Remove(key2) 1087 1088 token := genRSAToken(rsaPrivateKey, map[string]interface{}{"test": "test"}) 1089 1090 rw := Auth{ 1091 Next: httpserver.HandlerFunc(passThruHandler), 1092 Rules: []Rule{{Path: "/testing", KeyBackends: []KeyBackend{backend1, backend2}}}, 1093 } 1094 req, err := http.NewRequest("GET", "/testing", nil) 1095 req.Header.Set("Authorization", strings.Join([]string{"Bearer", token}, " ")) 1096 1097 rec := httptest.NewRecorder() 1098 result, err := rw.ServeHTTP(rec, req) 1099 if err != nil { 1100 Fail(fmt.Sprintf("unexpected error constructing server: %s", err)) 1101 } 1102 1103 Expect(result).To(Equal(http.StatusOK)) 1104 }) 1105 It("should allow access when one of the keyfiles matches in any order", func() { 1106 key1, err := createKeyFile(rsaPublicKey) 1107 if err != nil { 1108 Fail(fmt.Sprintf("unexpected error creating key file: %s", err)) 1109 } 1110 backend1, err := NewLazyPublicKeyFileBackend(key1) 1111 if err != nil { 1112 Fail(err.Error()) 1113 } 1114 defer os.Remove(key1) 1115 key2, err := createKeyFile("notvalidkey") 1116 if err != nil { 1117 Fail(fmt.Sprintf("unexpected error creating key file: %s", err)) 1118 } 1119 backend2, err := NewLazyPublicKeyFileBackend(key2) 1120 if err != nil { 1121 Fail(err.Error()) 1122 } 1123 defer os.Remove(key2) 1124 1125 token := genRSAToken(rsaPrivateKey, map[string]interface{}{"test": "test"}) 1126 1127 rw := Auth{ 1128 Next: httpserver.HandlerFunc(passThruHandler), 1129 Rules: []Rule{{Path: "/testing", KeyBackends: []KeyBackend{backend2, backend2, backend2, backend1}}}, 1130 } 1131 req, err := http.NewRequest("GET", "/testing", nil) 1132 req.Header.Set("Authorization", strings.Join([]string{"Bearer", token}, " ")) 1133 1134 rec := httptest.NewRecorder() 1135 result, err := rw.ServeHTTP(rec, req) 1136 if err != nil { 1137 Fail(fmt.Sprintf("unexpected error constructing server: %s", err)) 1138 } 1139 1140 Expect(result).To(Equal(http.StatusOK)) 1141 }) 1142 It("should deny access when all keyfiles dont validate", func() { 1143 key2, err := createKeyFile("notvalidkey") 1144 if err != nil { 1145 Fail(fmt.Sprintf("unexpected error creating key file: %s", err)) 1146 } 1147 backend2, err := NewLazyPublicKeyFileBackend(key2) 1148 if err != nil { 1149 Fail(err.Error()) 1150 } 1151 defer os.Remove(key2) 1152 1153 token := genRSAToken(rsaPrivateKey, map[string]interface{}{"test": "test"}) 1154 1155 rw := Auth{ 1156 Next: httpserver.HandlerFunc(passThruHandler), 1157 Rules: []Rule{{Path: "/testing", KeyBackends: []KeyBackend{backend2, backend2, backend2}}}, 1158 } 1159 req, err := http.NewRequest("GET", "/testing", nil) 1160 req.Header.Set("Authorization", strings.Join([]string{"Bearer", token}, " ")) 1161 1162 rec := httptest.NewRecorder() 1163 result, err := rw.ServeHTTP(rec, req) 1164 if err != nil { 1165 Fail(fmt.Sprintf("unexpected error constructing server: %s", err)) 1166 } 1167 1168 Expect(result).To(Equal(http.StatusUnauthorized)) 1169 }) 1170 }) 1171 }) 1172 1173 }) 1174 1175 func createKeyFile(key string) (string, error) { 1176 f, err := ioutil.TempFile("", "jwt") 1177 if err != nil { 1178 return "", err 1179 } 1180 if _, err := f.Write([]byte(key)); err != nil { 1181 return "", err 1182 } 1183 if err := f.Close(); err != nil { 1184 return "", err 1185 } 1186 return f.Name(), nil 1187 }