github.com/openshift-online/ocm-sdk-go@v0.1.473/authentication/handler_test.go (about) 1 //go:build linux 2 // +build linux 3 4 /* 5 Copyright (c) 2019 Red Hat, Inc. 6 7 Licensed under the Apache License, Version 2.0 (the "License"); 8 you may not use this file except in compliance with the License. 9 You may obtain a copy of the License at 10 11 http://www.apache.org/licenses/LICENSE-2.0 12 13 Unless required by applicable law or agreed to in writing, software 14 distributed under the License is distributed on an "AS IS" BASIS, 15 WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 16 See the License for the specific language governing permissions and 17 limitations under the License. 18 */ 19 20 // This file contains tests for the authentication handler. 21 22 package authentication 23 24 import ( 25 "net/http" 26 "net/http/httptest" 27 "os" 28 "time" 29 30 "github.com/golang-jwt/jwt/v4" 31 32 . "github.com/onsi/ginkgo/v2/dsl/core" 33 . "github.com/onsi/gomega" // nolint 34 . "github.com/onsi/gomega/ghttp" // nolint 35 . "github.com/openshift-online/ocm-sdk-go/testing" // nolint 36 ) 37 38 var _ = Describe("Handler", func() { 39 It("Can't be built without a logger", func() { 40 // Prepare the next handler: 41 next := http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { 42 w.WriteHeader(http.StatusOK) 43 }) 44 45 // Try to create the handler: 46 _, err := NewHandler(). 47 KeysFile(keysFile). 48 Next(next). 49 Build() 50 Expect(err).To(HaveOccurred()) 51 Expect(err.Error()).To(ContainSubstring("logger")) 52 Expect(err.Error()).To(ContainSubstring("mandatory")) 53 }) 54 55 It("Can't be built without at least one keys source", func() { 56 // Prepare the next handler: 57 next := http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { 58 w.WriteHeader(http.StatusOK) 59 }) 60 61 // Try to create the handler: 62 _, err := NewHandler(). 63 Logger(logger). 64 Next(next). 65 Build() 66 Expect(err).To(HaveOccurred()) 67 }) 68 69 It("Can't be built with a keys file that doesn't exist", func() { 70 // Prepare the next handler: 71 next := http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { 72 w.WriteHeader(http.StatusOK) 73 }) 74 75 // Try to create the handler: 76 _, err := NewHandler(). 77 Logger(logger). 78 KeysFile("/does/not/exist"). 79 Next(next). 80 Build() 81 Expect(err).To(HaveOccurred()) 82 Expect(err.Error()).To(ContainSubstring("/does/not/exist")) 83 }) 84 85 It("Can't be built with a malformed keys URL", func() { 86 // Prepare the next handler: 87 next := http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { 88 w.WriteHeader(http.StatusOK) 89 }) 90 91 // Try to create the handler: 92 _, err := NewHandler(). 93 Logger(logger). 94 KeysURL("junk"). 95 Next(next). 96 Build() 97 Expect(err).To(HaveOccurred()) 98 Expect(err.Error()).To(ContainSubstring("junk")) 99 }) 100 101 It("Can't be built with a URL that isn't HTTPS", func() { 102 // Prepare the next handler: 103 next := http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { 104 w.WriteHeader(http.StatusOK) 105 }) 106 107 // Try to create the handler: 108 _, err := NewHandler(). 109 Logger(logger). 110 KeysURL("http://api.openshift.com/.well-known/jwks.json"). 111 Next(next). 112 Build() 113 Expect(err).To(HaveOccurred()) 114 }) 115 116 It("Can be built with one keys file", func() { 117 // Prepare the next handler: 118 next := http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { 119 w.WriteHeader(http.StatusOK) 120 }) 121 122 // Try to create the handler: 123 _, err := NewHandler(). 124 Logger(logger). 125 KeysFile(keysFile). 126 Next(next). 127 Build() 128 Expect(err).ToNot(HaveOccurred()) 129 }) 130 131 It("Can be built with one keys URL", func() { 132 // Prepare the next handler: 133 next := http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { 134 w.WriteHeader(http.StatusOK) 135 }) 136 137 // Try to create the handler: 138 _, err := NewHandler(). 139 Logger(logger). 140 KeysURL("https://api.openshift.com/.well-known/jwks.json"). 141 Next(next). 142 Build() 143 Expect(err).ToNot(HaveOccurred()) 144 }) 145 146 It("Can't be built without a next handler", func() { 147 _, err := NewHandler(). 148 Logger(logger). 149 KeysFile(keysFile). 150 Build() 151 Expect(err).To(HaveOccurred()) 152 Expect(err.Error()).To(ContainSubstring("next")) 153 Expect(err.Error()).To(ContainSubstring("mandatory")) 154 }) 155 156 It("Rejects request without authorization header or cookie", func() { 157 // Prepare the next handler, which should not be called: 158 next := http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { 159 Expect(true).To(BeFalse()) 160 w.WriteHeader(http.StatusBadRequest) 161 }) 162 163 // Prepare the handler: 164 handler, err := NewHandler(). 165 Logger(logger). 166 KeysFile(keysFile). 167 Next(next). 168 Build() 169 Expect(err).ToNot(HaveOccurred()) 170 171 // Send the request: 172 request := httptest.NewRequest(http.MethodGet, "/api/clusters_mgmt/v1/private", nil) 173 recorder := httptest.NewRecorder() 174 handler.ServeHTTP(recorder, request) 175 176 // Verify that the request is rejected: 177 Expect(recorder.Code).To(Equal(http.StatusUnauthorized)) 178 Expect(recorder.Body).To(MatchJSON(`{ 179 "kind": "Error", 180 "id": "401", 181 "href": "/api/clusters_mgmt/v1/errors/401", 182 "code": "CLUSTERS-MGMT-401", 183 "reason": "Request doesn't contain the 'Authorization' header or the 'cs_jwt' cookie" 184 }`)) 185 }) 186 187 It("Rejects bad authorization type", func() { 188 // Prepare the next handler, which should not be called: 189 next := http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { 190 Expect(true).To(BeFalse()) 191 w.WriteHeader(http.StatusBadRequest) 192 }) 193 194 // Prepare the token: 195 bearer := MakeTokenString("Bearer", 1*time.Minute) 196 197 // Prepare the handler: 198 handler, err := NewHandler(). 199 Logger(logger). 200 KeysFile(keysFile). 201 Next(next). 202 Build() 203 Expect(err).ToNot(HaveOccurred()) 204 205 // Send the request with a bad type and a good token: 206 request := httptest.NewRequest(http.MethodGet, "/api/clusters_mgmt/v1/private", nil) 207 request.Header.Set("Authorization", "Bad "+bearer) 208 recorder := httptest.NewRecorder() 209 handler.ServeHTTP(recorder, request) 210 211 // Verify that the request is rejected: 212 Expect(recorder.Code).To(Equal(http.StatusUnauthorized)) 213 Expect(recorder.Body).To(MatchJSON(`{ 214 "kind": "Error", 215 "id": "401", 216 "href": "/api/clusters_mgmt/v1/errors/401", 217 "code": "CLUSTERS-MGMT-401", 218 "reason": "Authentication type 'Bad' isn't supported" 219 }`)) 220 }) 221 222 It("Rejects bad bearer token", func() { 223 // Prepare the next handler, which should not be called: 224 next := http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { 225 Expect(true).To(BeFalse()) 226 w.WriteHeader(http.StatusBadRequest) 227 }) 228 229 // Prepare the handler: 230 handler, err := NewHandler(). 231 Logger(logger). 232 KeysFile(keysFile). 233 Next(next). 234 Build() 235 Expect(err).ToNot(HaveOccurred()) 236 237 // Send the request with a bad type and a good token: 238 request := httptest.NewRequest(http.MethodGet, "/api/clusters_mgmt/v1/private", nil) 239 request.Header.Set("Authorization", "Bearer bad") 240 recorder := httptest.NewRecorder() 241 handler.ServeHTTP(recorder, request) 242 243 // Verify that the request is rejected: 244 Expect(recorder.Code).To(Equal(http.StatusUnauthorized)) 245 Expect(recorder.Body).To(MatchJSON(`{ 246 "kind": "Error", 247 "id": "401", 248 "href": "/api/clusters_mgmt/v1/errors/401", 249 "code": "CLUSTERS-MGMT-401", 250 "reason": "Bearer token is malformed" 251 }`)) 252 }) 253 254 It("Rejects expired bearer token", func() { 255 // Prepare the next handler, which should not be called: 256 next := http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { 257 Expect(true).To(BeFalse()) 258 w.WriteHeader(http.StatusBadRequest) 259 }) 260 261 // Prepare the expired token: 262 bearer := MakeTokenString("Bearer", -1*time.Hour) 263 264 // Prepare the handler: 265 handler, err := NewHandler(). 266 Logger(logger). 267 KeysFile(keysFile). 268 Next(next). 269 Build() 270 Expect(err).ToNot(HaveOccurred()) 271 272 // Send the request with the expired token: 273 request := httptest.NewRequest(http.MethodGet, "/api/clusters_mgmt/v1/private", nil) 274 request.Header.Set("Authorization", "Bearer "+bearer) 275 recorder := httptest.NewRecorder() 276 handler.ServeHTTP(recorder, request) 277 278 // Verify that the request is rejected: 279 Expect(recorder.Code).To(Equal(http.StatusUnauthorized)) 280 Expect(recorder.Body).To(MatchJSON(`{ 281 "kind": "Error", 282 "id": "401", 283 "href": "/api/clusters_mgmt/v1/errors/401", 284 "code": "CLUSTERS-MGMT-401", 285 "reason": "Bearer token is expired" 286 }`)) 287 }) 288 289 It("Accepts token without `typ` claim", func() { 290 // Prepare the next handler: 291 next := http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { 292 w.WriteHeader(http.StatusOK) 293 }) 294 295 // Prepare a token without the 'typ' claim: 296 bearer := MakeTokenObject(jwt.MapClaims{ 297 "typ": nil, 298 }).Raw 299 300 // Prepare the handler: 301 handler, err := NewHandler(). 302 Logger(logger). 303 KeysFile(keysFile). 304 Next(next). 305 Build() 306 Expect(err).ToNot(HaveOccurred()) 307 308 // Send the request with the bad token: 309 request := httptest.NewRequest(http.MethodGet, "/api/clusters_mgmt/v1/private", nil) 310 request.Header.Set("Authorization", "Bearer "+bearer) 311 recorder := httptest.NewRecorder() 312 handler.ServeHTTP(recorder, request) 313 314 // Verify that the request is accepted: 315 Expect(recorder.Code).To(Equal(http.StatusOK)) 316 }) 317 318 It("Rejects `typ` claim with incorrect type", func() { 319 // Prepare the next handler, which should not be called: 320 next := http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { 321 Expect(true).To(BeFalse()) 322 w.WriteHeader(http.StatusBadRequest) 323 }) 324 325 // Prepare a refresh token: 326 bearer := MakeTokenObject(jwt.MapClaims{ 327 "typ": 123, 328 }).Raw 329 330 // Prepare the handler: 331 handler, err := NewHandler(). 332 Logger(logger). 333 KeysFile(keysFile). 334 Next(next). 335 Build() 336 Expect(err).ToNot(HaveOccurred()) 337 338 // Send the request with the bad token: 339 request := httptest.NewRequest(http.MethodGet, "/api/clusters_mgmt/v1/private", nil) 340 request.Header.Set("Authorization", "Bearer "+bearer) 341 recorder := httptest.NewRecorder() 342 handler.ServeHTTP(recorder, request) 343 344 // Verify that the request is rejected: 345 Expect(recorder.Code).To(Equal(http.StatusUnauthorized)) 346 Expect(recorder.Body).To(MatchJSON(`{ 347 "kind": "Error", 348 "id": "401", 349 "href": "/api/clusters_mgmt/v1/errors/401", 350 "code": "CLUSTERS-MGMT-401", 351 "reason": "Bearer token type claim contains incorrect string value '123'" 352 }`)) 353 }) 354 355 It("Rejects refresh tokens", func() { 356 // Prepare the next handler, which should not be called: 357 next := http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { 358 Expect(true).To(BeFalse()) 359 w.WriteHeader(http.StatusBadRequest) 360 }) 361 362 // Prepare a refresh token: 363 bearer := MakeTokenString("Refresh", 1*time.Hour) 364 365 // Prepare the handler: 366 handler, err := NewHandler(). 367 Logger(logger). 368 KeysFile(keysFile). 369 Next(next). 370 Build() 371 Expect(err).ToNot(HaveOccurred()) 372 373 // Send the request with the bad token: 374 request := httptest.NewRequest(http.MethodGet, "/api/clusters_mgmt/v1/private", nil) 375 request.Header.Set("Authorization", "Bearer "+bearer) 376 recorder := httptest.NewRecorder() 377 handler.ServeHTTP(recorder, request) 378 379 // Verify that the request is rejected: 380 Expect(recorder.Code).To(Equal(http.StatusUnauthorized)) 381 Expect(recorder.Body).To(MatchJSON(`{ 382 "kind": "Error", 383 "id": "401", 384 "href": "/api/clusters_mgmt/v1/errors/401", 385 "code": "CLUSTERS-MGMT-401", 386 "reason": "Bearer token type 'Refresh' isn't allowed" 387 }`)) 388 }) 389 390 It("Rejects offline tokens", func() { 391 // Prepare the next handler, which should not be called: 392 next := http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { 393 Expect(true).To(BeFalse()) 394 w.WriteHeader(http.StatusBadRequest) 395 }) 396 397 // Prepare an offline access token: 398 bearer := MakeTokenString("Offline", 0) 399 400 // Prepare the handler: 401 handler, err := NewHandler(). 402 Logger(logger). 403 KeysFile(keysFile). 404 Next(next). 405 Build() 406 Expect(err).ToNot(HaveOccurred()) 407 408 // Send the request with the bad token: 409 request := httptest.NewRequest(http.MethodGet, "/api/clusters_mgmt/v1/private", nil) 410 request.Header.Set("Authorization", "Bearer "+bearer) 411 recorder := httptest.NewRecorder() 412 handler.ServeHTTP(recorder, request) 413 414 // Verify that the request is rejected: 415 Expect(recorder.Code).To(Equal(http.StatusUnauthorized)) 416 Expect(recorder.Body).To(MatchJSON(`{ 417 "kind": "Error", 418 "id": "401", 419 "href": "/api/clusters_mgmt/v1/errors/401", 420 "code": "CLUSTERS-MGMT-401", 421 "reason": "Bearer token type 'Offline' isn't allowed" 422 }`)) 423 }) 424 425 It("Rejects token without issue date", func() { 426 // Prepare the next handler, which should not be called: 427 next := http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { 428 Expect(true).To(BeFalse()) 429 w.WriteHeader(http.StatusBadRequest) 430 }) 431 432 // Prepare the token without the 'iat' claim: 433 token := MakeTokenObject(jwt.MapClaims{ 434 "typ": "Bearer", 435 "iat": nil, 436 }) 437 bearer := token.Raw 438 439 // Prepare the handler: 440 handler, err := NewHandler(). 441 Logger(logger). 442 KeysFile(keysFile). 443 Next(next). 444 Build() 445 Expect(err).ToNot(HaveOccurred()) 446 447 // Send the request with the bad token: 448 request := httptest.NewRequest(http.MethodGet, "/api/clusters_mgmt/v1/private", nil) 449 request.Header.Set("Authorization", "Bearer "+bearer) 450 recorder := httptest.NewRecorder() 451 handler.ServeHTTP(recorder, request) 452 453 // Verify that the request is rejected: 454 Expect(recorder.Code).To(Equal(http.StatusUnauthorized)) 455 Expect(recorder.Body).To(MatchJSON(`{ 456 "kind": "Error", 457 "id": "401", 458 "href": "/api/clusters_mgmt/v1/errors/401", 459 "code": "CLUSTERS-MGMT-401", 460 "reason": "Bearer token doesn't contain required claim 'iat'" 461 }`)) 462 }) 463 464 It("Rejects token without expiration date", func() { 465 // Prepare the next handler, which should not be called: 466 next := http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { 467 Expect(true).To(BeFalse()) 468 w.WriteHeader(http.StatusBadRequest) 469 }) 470 471 // Prepare the token without the 'exp' claim: 472 token := MakeTokenObject(jwt.MapClaims{ 473 "typ": "Bearer", 474 "exp": nil, 475 }) 476 bearer := token.Raw 477 478 // Prepare the handler: 479 handler, err := NewHandler(). 480 Logger(logger). 481 KeysFile(keysFile). 482 Next(next). 483 Build() 484 Expect(err).ToNot(HaveOccurred()) 485 486 // Send the request with the bad token: 487 request := httptest.NewRequest(http.MethodGet, "/api/clusters_mgmt/v1/private", nil) 488 request.Header.Set("Authorization", "Bearer "+bearer) 489 recorder := httptest.NewRecorder() 490 handler.ServeHTTP(recorder, request) 491 492 // Verify that the request is rejected: 493 Expect(recorder.Code).To(Equal(http.StatusUnauthorized)) 494 Expect(recorder.Body).To(MatchJSON(`{ 495 "kind": "Error", 496 "id": "401", 497 "href": "/api/clusters_mgmt/v1/errors/401", 498 "code": "CLUSTERS-MGMT-401", 499 "reason": "Bearer token doesn't contain required claim 'exp'" 500 }`)) 501 }) 502 503 It("Rejects token issued in the future", func() { 504 // Prepare the next handler, which should not be called: 505 next := http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { 506 Expect(true).To(BeFalse()) 507 w.WriteHeader(http.StatusBadRequest) 508 }) 509 510 // Prepare a token issued in the future: 511 now := time.Now() 512 iat := now.Add(1 * time.Minute) 513 exp := iat.Add(1 * time.Minute) 514 token := MakeTokenObject(jwt.MapClaims{ 515 "typ": "Bearer", 516 "iat": iat.Unix(), 517 "exp": exp.Unix(), 518 }) 519 bearer := token.Raw 520 521 // Prepare the handler: 522 handler, err := NewHandler(). 523 Logger(logger). 524 KeysFile(keysFile). 525 Next(next). 526 Build() 527 Expect(err).ToNot(HaveOccurred()) 528 529 // Send the request with the bad token: 530 request := httptest.NewRequest(http.MethodGet, "/api/clusters_mgmt/v1/private", nil) 531 request.Header.Set("Authorization", "Bearer "+bearer) 532 recorder := httptest.NewRecorder() 533 handler.ServeHTTP(recorder, request) 534 535 // Verify that the request is rejected: 536 Expect(recorder.Code).To(Equal(http.StatusUnauthorized)) 537 Expect(recorder.Body).To(MatchJSON(`{ 538 "kind": "Error", 539 "id": "401", 540 "href": "/api/clusters_mgmt/v1/errors/401", 541 "code": "CLUSTERS-MGMT-401", 542 "reason": "Bearer token was issued in the future" 543 }`)) 544 }) 545 546 It("Rejects token that isn't valid yet", func() { 547 // Prepare the next handler, which should not be called: 548 next := http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { 549 Expect(true).To(BeFalse()) 550 w.WriteHeader(http.StatusBadRequest) 551 }) 552 553 // Prepare the not yet valid token: 554 iat := time.Now() 555 nbf := iat.Add(1 * time.Minute) 556 exp := nbf.Add(1 * time.Minute) 557 token := MakeTokenObject(jwt.MapClaims{ 558 "typ": "Bearer", 559 "iat": iat.Unix(), 560 "nbf": nbf.Unix(), 561 "exp": exp.Unix(), 562 }) 563 bearer := token.Raw 564 565 // Prepare the handler: 566 handler, err := NewHandler(). 567 Logger(logger). 568 KeysFile(keysFile). 569 Next(next). 570 Build() 571 Expect(err).ToNot(HaveOccurred()) 572 573 // Send the request with a bad token: 574 request := httptest.NewRequest(http.MethodGet, "/api/clusters_mgmt/v1/private", nil) 575 request.Header.Set("Authorization", "Bearer "+bearer) 576 recorder := httptest.NewRecorder() 577 handler.ServeHTTP(recorder, request) 578 579 // Verify that the request is rejected: 580 Expect(recorder.Code).To(Equal(http.StatusUnauthorized)) 581 Expect(recorder.Body).To(MatchJSON(`{ 582 "kind": "Error", 583 "id": "401", 584 "href": "/api/clusters_mgmt/v1/errors/401", 585 "code": "CLUSTERS-MGMT-401", 586 "reason": "Bearer token isn't valid yet" 587 }`)) 588 }) 589 590 It("Rejects impersonated token", func() { 591 // Prepare the next handler, which should not be called: 592 next := http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { 593 Expect(true).To(BeFalse()) 594 w.WriteHeader(http.StatusBadRequest) 595 }) 596 597 // Prepare the impersonated token: 598 token := MakeTokenObject(jwt.MapClaims{ 599 "impersonated": true, 600 }) 601 bearer := token.Raw 602 603 // Prepare the handler: 604 handler, err := NewHandler(). 605 Logger(logger). 606 KeysFile(keysFile). 607 Next(next). 608 Build() 609 Expect(err).ToNot(HaveOccurred()) 610 611 // Send the request: 612 request := httptest.NewRequest(http.MethodGet, "/api/clusters_mgmt/v1/private", nil) 613 request.Header.Set("Authorization", "Bearer "+bearer) 614 recorder := httptest.NewRecorder() 615 handler.ServeHTTP(recorder, request) 616 617 // Verify that the request is rejected: 618 Expect(recorder.Code).To(Equal(http.StatusUnauthorized)) 619 Expect(recorder.Body).To(MatchJSON(`{ 620 "kind": "Error", 621 "id": "401", 622 "href": "/api/clusters_mgmt/v1/errors/401", 623 "code": "CLUSTERS-MGMT-401", 624 "reason": "Impersonation isn't allowed" 625 }`)) 626 }) 627 628 It("Rejects bad impersonated claim", func() { 629 // Prepare the next handler, which should not be called: 630 next := http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { 631 Expect(true).To(BeFalse()) 632 w.WriteHeader(http.StatusBadRequest) 633 }) 634 635 // Prepare the impersonated token: 636 token := MakeTokenObject(jwt.MapClaims{ 637 "impersonated": "junk", 638 }) 639 bearer := token.Raw 640 641 // Prepare the handler: 642 handler, err := NewHandler(). 643 Logger(logger). 644 KeysFile(keysFile). 645 Next(next). 646 Build() 647 Expect(err).ToNot(HaveOccurred()) 648 649 // Send the request: 650 request := httptest.NewRequest(http.MethodGet, "/api/clusters_mgmt/v1/private", nil) 651 request.Header.Set("Authorization", "Bearer "+bearer) 652 recorder := httptest.NewRecorder() 653 handler.ServeHTTP(recorder, request) 654 655 // Verify that the request is rejected: 656 Expect(recorder.Code).To(Equal(http.StatusUnauthorized)) 657 Expect(recorder.Body).To(MatchJSON(`{ 658 "kind": "Error", 659 "id": "401", 660 "href": "/api/clusters_mgmt/v1/errors/401", 661 "code": "CLUSTERS-MGMT-401", 662 "reason": "Impersonation claim contains incorrect boolean value 'junk'" 663 }`)) 664 }) 665 666 It("Accepts false impersonated claim", func() { 667 // Prepare the next handler: 668 next := http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { 669 w.WriteHeader(http.StatusOK) 670 }) 671 672 // Prepare the impersonated token: 673 token := MakeTokenObject(jwt.MapClaims{ 674 "impersonated": false, 675 }) 676 bearer := token.Raw 677 678 // Prepare the handler: 679 handler, err := NewHandler(). 680 Logger(logger). 681 KeysFile(keysFile). 682 Next(next). 683 Build() 684 Expect(err).ToNot(HaveOccurred()) 685 686 // Send the request: 687 request := httptest.NewRequest(http.MethodGet, "/api/clusters_mgmt/v1/private", nil) 688 request.Header.Set("Authorization", "Bearer "+bearer) 689 recorder := httptest.NewRecorder() 690 handler.ServeHTTP(recorder, request) 691 692 // Verify the response: 693 Expect(recorder.Code).To(Equal(http.StatusOK)) 694 }) 695 696 It("Loads keys from file", func() { 697 // Prepare the next handler: 698 next := http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { 699 w.WriteHeader(http.StatusOK) 700 }) 701 702 // Prepare the token: 703 bearer := MakeTokenString("Bearer", 1*time.Minute) 704 705 // Prepare the handler: 706 handler, err := NewHandler(). 707 Logger(logger). 708 KeysFile(keysFile). 709 Next(next). 710 Build() 711 Expect(err).ToNot(HaveOccurred()) 712 713 // Send the request: 714 request := httptest.NewRequest(http.MethodGet, "/api/clusters_mgmt/v1/private", nil) 715 request.Header.Set("Authorization", "Bearer "+bearer) 716 recorder := httptest.NewRecorder() 717 handler.ServeHTTP(recorder, request) 718 719 // Verify that the request is rejected: 720 Expect(recorder.Code).To(Equal(http.StatusOK)) 721 }) 722 723 It("Adds token to the request context", func() { 724 // Prepare the token: 725 bearer := MakeTokenString("Bearer", 1*time.Minute) 726 727 // Prepare the next handler: 728 next := http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { 729 actual, err := BearerFromContext(r.Context()) 730 Expect(err).ToNot(HaveOccurred()) 731 Expect(actual).To(Equal(bearer)) 732 w.WriteHeader(http.StatusOK) 733 }) 734 735 // Prepare the handler: 736 handler, err := NewHandler(). 737 Logger(logger). 738 KeysFile(keysFile). 739 Next(next). 740 Build() 741 Expect(err).ToNot(HaveOccurred()) 742 743 // Send the request: 744 request := httptest.NewRequest(http.MethodGet, "/api/clusters_mgmt/v1/private", nil) 745 request.Header.Set("Authorization", "Bearer "+bearer) 746 recorder := httptest.NewRecorder() 747 handler.ServeHTTP(recorder, request) 748 749 // Verify the response: 750 Expect(recorder.Code).To(Equal(http.StatusOK)) 751 }) 752 753 It("Doesn't require authorization header for public URL", func() { 754 // Prepare the next handler: 755 next := http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { 756 actual, err := BearerFromContext(r.Context()) 757 Expect(err).ToNot(HaveOccurred()) 758 Expect(actual).To(BeEmpty()) 759 w.WriteHeader(http.StatusOK) 760 }) 761 762 // Prepare the handler: 763 handler, err := NewHandler(). 764 Logger(logger). 765 KeysFile(keysFile). 766 Public("^/api/clusters_mgmt/v1/public(/.*)?$"). 767 Next(next). 768 Build() 769 Expect(err).ToNot(HaveOccurred()) 770 771 // Send the request without the authorization header: 772 request := httptest.NewRequest(http.MethodGet, "/api/clusters_mgmt/v1/public", nil) 773 recorder := httptest.NewRecorder() 774 handler.ServeHTTP(recorder, request) 775 776 // Verify the response: 777 Expect(recorder.Code).To(Equal(http.StatusOK)) 778 }) 779 780 It("Ignores malformed authorization header for public URL", func() { 781 // Prepare the next handler: 782 next := http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { 783 actual, err := BearerFromContext(r.Context()) 784 Expect(err).ToNot(HaveOccurred()) 785 Expect(actual).To(BeEmpty()) 786 w.WriteHeader(http.StatusOK) 787 }) 788 789 // Prepare the handler: 790 handler, err := NewHandler(). 791 Logger(logger). 792 KeysFile(keysFile). 793 Public("^/api/clusters_mgmt/v1/public(/.*)?$"). 794 Next(next). 795 Build() 796 Expect(err).ToNot(HaveOccurred()) 797 798 // Send the request: 799 request := httptest.NewRequest(http.MethodGet, "/api/clusters_mgmt/v1/public", nil) 800 request.Header.Set("Authorization", "Bad junk") 801 recorder := httptest.NewRecorder() 802 handler.ServeHTTP(recorder, request) 803 }) 804 805 It("Ignores expired token for public URL", func() { 806 // Prepare the expired token: 807 bearer := MakeTokenString("Bearer", -1*time.Minute) 808 809 // Prepare the next handler: 810 next := http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { 811 bearer, err := BearerFromContext(r.Context()) 812 Expect(err).ToNot(HaveOccurred()) 813 Expect(bearer).To(BeEmpty()) 814 w.WriteHeader(http.StatusOK) 815 }) 816 817 // Prepare the handler: 818 handler, err := NewHandler(). 819 Logger(logger). 820 KeysFile(keysFile). 821 Public("^/api/clusters_mgmt/v1/public(/.*)?$"). 822 Next(next). 823 Build() 824 Expect(err).ToNot(HaveOccurred()) 825 826 // Send the request: 827 request := httptest.NewRequest(http.MethodGet, "/api/clusters_mgmt/v1/public", nil) 828 request.Header.Set("Authorization", "Bearer "+bearer) 829 recorder := httptest.NewRecorder() 830 handler.ServeHTTP(recorder, request) 831 }) 832 833 It("Combines multiple public URLs", func() { 834 // Prepare the token: 835 bearer := MakeTokenString("Bearer", 1*time.Minute) 836 837 // Prepare the next handler: 838 next := http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { 839 actual, err := BearerFromContext(r.Context()) 840 Expect(err).ToNot(HaveOccurred()) 841 Expect(actual).To(BeEmpty()) 842 w.WriteHeader(http.StatusOK) 843 }) 844 845 // Prepare the handler: 846 handler, err := NewHandler(). 847 Logger(logger). 848 KeysFile(keysFile). 849 Public("^/api/clusters_mgmt/v1/public(/.*)?$"). 850 Public("^/api/clusters_mgmt/v1/open(/.*)?$"). 851 Next(next). 852 Build() 853 Expect(err).ToNot(HaveOccurred()) 854 855 // Send a request for one of the public URLs: 856 request := httptest.NewRequest(http.MethodGet, "/api/clusters_mgmt/v1/public", nil) 857 request.Header.Set("Authorization", "Bearer "+bearer) 858 recorder := httptest.NewRecorder() 859 handler.ServeHTTP(recorder, request) 860 Expect(recorder.Code).To(Equal(http.StatusOK)) 861 862 // Send a request for another of the public URLs: 863 request = httptest.NewRequest(http.MethodGet, "/api/clusters_mgmt/v1/open", nil) 864 request.Header.Set("Authorization", "Bearer "+bearer) 865 recorder = httptest.NewRecorder() 866 handler.ServeHTTP(recorder, request) 867 Expect(recorder.Code).To(Equal(http.StatusOK)) 868 }) 869 870 It("Doesn't pass ignored token to next handler for public URL", func() { 871 // Prepare the token: 872 bearer := MakeTokenString("Bearer", 1*time.Minute) 873 874 // Prepare the next handler: 875 next := http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { 876 actual, err := BearerFromContext(r.Context()) 877 Expect(err).ToNot(HaveOccurred()) 878 Expect(actual).To(BeEmpty()) 879 w.WriteHeader(http.StatusOK) 880 }) 881 882 // Prepare the handler: 883 handler, err := NewHandler(). 884 Logger(logger). 885 KeysFile(keysFile). 886 Public("^/api/clusters_mgmt/v1/public(/.*)?$"). 887 Next(next). 888 Build() 889 Expect(err).ToNot(HaveOccurred()) 890 891 // Send the request: 892 request := httptest.NewRequest(http.MethodGet, "/api/clusters_mgmt/v1/public", nil) 893 request.Header.Set("Authorization", "Bearer "+bearer) 894 recorder := httptest.NewRecorder() 895 handler.ServeHTTP(recorder, request) 896 897 // Verify the response: 898 Expect(recorder.Code).To(Equal(http.StatusOK)) 899 }) 900 901 It("Doesn't load insecure keys by default", func() { 902 var err error 903 904 // Prepare the server: 905 server, ca := MakeTCPTLSServer() 906 defer func() { 907 server.Close() 908 err = os.Remove(ca) 909 Expect(err).ToNot(HaveOccurred()) 910 }() 911 server.AppendHandlers( 912 RespondWith(http.StatusOK, keysBytes), 913 ) 914 server.SetAllowUnhandledRequests(true) 915 916 // Prepare the next handler: 917 next := http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { 918 w.WriteHeader(http.StatusOK) 919 }) 920 921 // Prepare the token: 922 bearer := MakeTokenString("Bearer", 1*time.Minute) 923 924 // Prepare the handler: 925 handler, err := NewHandler(). 926 Logger(logger). 927 KeysURL(server.URL()). 928 Next(next). 929 Build() 930 Expect(err).ToNot(HaveOccurred()) 931 932 // Send the request: 933 request := httptest.NewRequest(http.MethodGet, "/api/clusters_mgmt/v1/private", nil) 934 request.Header.Set("Authorization", "Bearer "+bearer) 935 recorder := httptest.NewRecorder() 936 handler.ServeHTTP(recorder, request) 937 938 // Verify that the request is rejected: 939 Expect(recorder.Code).To(Equal(http.StatusUnauthorized)) 940 }) 941 942 It("Loads insecure keys in insecure mode", func() { 943 var err error 944 945 // Prepare the server that will return the keys: 946 server, ca := MakeTCPTLSServer() 947 defer func() { 948 server.Close() 949 err = os.Remove(ca) 950 Expect(err).ToNot(HaveOccurred()) 951 }() 952 server.AppendHandlers( 953 RespondWith(http.StatusOK, keysBytes), 954 ) 955 956 // Prepare the next handler: 957 next := http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { 958 w.WriteHeader(http.StatusOK) 959 }) 960 961 // Prepare the token: 962 bearer := MakeTokenString("Bearer", 1*time.Minute) 963 964 // Prepare the handler: 965 handler, err := NewHandler(). 966 Logger(logger). 967 KeysURL(server.URL()). 968 KeysInsecure(true). 969 Next(next). 970 Build() 971 Expect(err).ToNot(HaveOccurred()) 972 973 // Send the request: 974 request := httptest.NewRequest(http.MethodGet, "/api/clusters_mgmt/v1/private", nil) 975 request.Header.Set("Authorization", "Bearer "+bearer) 976 recorder := httptest.NewRecorder() 977 handler.ServeHTTP(recorder, request) 978 979 // Verify that the request is rejected: 980 Expect(recorder.Code).To(Equal(http.StatusOK)) 981 }) 982 983 It("Returns the response of the next handler", func() { 984 // Prepare the token: 985 bearer := MakeTokenString("Bearer", 1*time.Minute) 986 987 // Prepare the next handler: 988 next := http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { 989 w.WriteHeader(http.StatusOK) 990 w.Header().Set("Content-Type", "application/json") 991 _, err := w.Write([]byte(`{ 992 "myfield": "myvalue" 993 }`)) 994 Expect(err).ToNot(HaveOccurred()) 995 }) 996 997 // Prepare the handler: 998 handler, err := NewHandler(). 999 Logger(logger). 1000 KeysFile(keysFile). 1001 Next(next). 1002 Build() 1003 Expect(err).ToNot(HaveOccurred()) 1004 1005 // Send a request: 1006 request := httptest.NewRequest(http.MethodGet, "/api/clusters_mgmt/v1/private", nil) 1007 request.Header.Set("Authorization", "Bearer "+bearer) 1008 recorder := httptest.NewRecorder() 1009 handler.ServeHTTP(recorder, request) 1010 1011 // Verify the response: 1012 Expect(recorder.Code).To(Equal(http.StatusOK)) 1013 Expect(recorder.Header().Get("Content-Type")).To(Equal("application/json")) 1014 Expect(recorder.Body).To(MatchJSON(`{ 1015 "myfield": "myvalue" 1016 }`)) 1017 }) 1018 1019 It("Accepts token if ACL is empty", func() { 1020 // Prepare the ACL: 1021 acl, err := os.CreateTemp("", "acl-*.yml") 1022 Expect(err).ToNot(HaveOccurred()) 1023 _, err = acl.WriteString("") 1024 Expect(err).ToNot(HaveOccurred()) 1025 defer func() { 1026 err := os.Remove(acl.Name()) 1027 Expect(err).ToNot(HaveOccurred()) 1028 }() 1029 err = acl.Close() 1030 Expect(err).ToNot(HaveOccurred()) 1031 1032 // Prepare the next handler: 1033 next := http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { 1034 w.WriteHeader(http.StatusOK) 1035 }) 1036 1037 // Prepare the token: 1038 bearer := MakeTokenString("Bearer", 1*time.Minute) 1039 1040 // Prepare the handler: 1041 handler, err := NewHandler(). 1042 Logger(logger). 1043 KeysFile(keysFile). 1044 Next(next). 1045 ACLFile(acl.Name()). 1046 Build() 1047 Expect(err).ToNot(HaveOccurred()) 1048 1049 // Send the request: 1050 request := httptest.NewRequest(http.MethodGet, "/api/clusters_mgmt/v1/private", nil) 1051 request.Header.Set("Authorization", "Bearer "+bearer) 1052 recorder := httptest.NewRecorder() 1053 handler.ServeHTTP(recorder, request) 1054 1055 // Verify the response: 1056 Expect(recorder.Code).To(Equal(http.StatusOK)) 1057 }) 1058 1059 It("Accepts token that matches first ACL item", func() { 1060 // Prepare the ACL: 1061 acl, err := os.CreateTemp("", "acl-*.yml") 1062 Expect(err).ToNot(HaveOccurred()) 1063 _, err = acl.WriteString(` 1064 - claim: email 1065 pattern: ^.*@example\.com$ 1066 - claim: sub 1067 pattern: ^f:b3f7b485-7184-43c8-8169-37bd6d1fe4aa:myuser$ 1068 `) 1069 Expect(err).ToNot(HaveOccurred()) 1070 defer func() { 1071 err := os.Remove(acl.Name()) 1072 Expect(err).ToNot(HaveOccurred()) 1073 }() 1074 err = acl.Close() 1075 Expect(err).ToNot(HaveOccurred()) 1076 1077 // Prepare the next handler: 1078 next := http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { 1079 w.WriteHeader(http.StatusOK) 1080 }) 1081 1082 // Prepare the token: 1083 token := MakeTokenObject(jwt.MapClaims{ 1084 "typ": "Bearer", 1085 "email": "jdoe@example.com", 1086 }) 1087 bearer := token.Raw 1088 1089 // Prepare the handler: 1090 handler, err := NewHandler(). 1091 Logger(logger). 1092 KeysFile(keysFile). 1093 Next(next). 1094 ACLFile(acl.Name()). 1095 Build() 1096 Expect(err).ToNot(HaveOccurred()) 1097 1098 // Send the request: 1099 request := httptest.NewRequest(http.MethodGet, "/api/clusters_mgmt/v1/private", nil) 1100 request.Header.Set("Authorization", "Bearer "+bearer) 1101 recorder := httptest.NewRecorder() 1102 handler.ServeHTTP(recorder, request) 1103 1104 // Verify the response: 1105 Expect(recorder.Code).To(Equal(http.StatusOK)) 1106 }) 1107 1108 It("Accepts token that matches second ACL item", func() { 1109 // Prepare the ACL: 1110 acl, err := os.CreateTemp("", "acl-*.yml") 1111 Expect(err).ToNot(HaveOccurred()) 1112 _, err = acl.WriteString(` 1113 - claim: email 1114 pattern: ^.*@example\.com$ 1115 - claim: sub 1116 pattern: ^f:b3f7b485-7184-43c8-8169-37bd6d1fe4aa:myuser$ 1117 `) 1118 Expect(err).ToNot(HaveOccurred()) 1119 defer func() { 1120 err := os.Remove(acl.Name()) 1121 Expect(err).ToNot(HaveOccurred()) 1122 }() 1123 err = acl.Close() 1124 Expect(err).ToNot(HaveOccurred()) 1125 1126 // Prepare the next handler: 1127 next := http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { 1128 w.WriteHeader(http.StatusOK) 1129 }) 1130 1131 // Prepare the token: 1132 token := MakeTokenObject(jwt.MapClaims{ 1133 "typ": "Bearer", 1134 "sub": "f:b3f7b485-7184-43c8-8169-37bd6d1fe4aa:myuser", 1135 }) 1136 bearer := token.Raw 1137 1138 // Prepare the handler: 1139 handler, err := NewHandler(). 1140 Logger(logger). 1141 KeysFile(keysFile). 1142 Next(next). 1143 ACLFile(acl.Name()). 1144 Build() 1145 Expect(err).ToNot(HaveOccurred()) 1146 1147 // Send the request: 1148 request := httptest.NewRequest(http.MethodGet, "/api/clusters_mgmt/v1/private", nil) 1149 request.Header.Set("Authorization", "Bearer "+bearer) 1150 recorder := httptest.NewRecorder() 1151 handler.ServeHTTP(recorder, request) 1152 1153 // Verify the response: 1154 Expect(recorder.Code).To(Equal(http.StatusOK)) 1155 }) 1156 1157 It("Accepts token that matches second ACL file", func() { 1158 // Prepare the first ACL: 1159 firstACL, err := os.CreateTemp("", "acl-*.yml") 1160 Expect(err).ToNot(HaveOccurred()) 1161 _, err = firstACL.WriteString(` 1162 - claim: email 1163 pattern: ^.*@example\.com$ 1164 `) 1165 Expect(err).ToNot(HaveOccurred()) 1166 defer func() { 1167 err := os.Remove(firstACL.Name()) 1168 Expect(err).ToNot(HaveOccurred()) 1169 }() 1170 err = firstACL.Close() 1171 Expect(err).ToNot(HaveOccurred()) 1172 1173 // Prepare the second ACL: 1174 secondACL, err := os.CreateTemp("", "acl-*.yml") 1175 Expect(err).ToNot(HaveOccurred()) 1176 _, err = secondACL.WriteString(` 1177 - claim: sub 1178 pattern: ^f:b3f7b485-7184-43c8-8169-37bd6d1fe4aa:myuser$ 1179 `) 1180 Expect(err).ToNot(HaveOccurred()) 1181 defer func() { 1182 err := os.Remove(secondACL.Name()) 1183 Expect(err).ToNot(HaveOccurred()) 1184 }() 1185 err = secondACL.Close() 1186 Expect(err).ToNot(HaveOccurred()) 1187 1188 // Prepare the next handler: 1189 next := http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { 1190 w.WriteHeader(http.StatusOK) 1191 }) 1192 1193 // Prepare the token: 1194 token := MakeTokenObject(jwt.MapClaims{ 1195 "typ": "Bearer", 1196 "sub": "f:b3f7b485-7184-43c8-8169-37bd6d1fe4aa:myuser", 1197 }) 1198 bearer := token.Raw 1199 1200 // Prepare the handler: 1201 handler, err := NewHandler(). 1202 Logger(logger). 1203 KeysFile(keysFile). 1204 Next(next). 1205 ACLFile(firstACL.Name()). 1206 ACLFile(secondACL.Name()). 1207 Build() 1208 Expect(err).ToNot(HaveOccurred()) 1209 1210 // Send the request: 1211 request := httptest.NewRequest(http.MethodGet, "/api/clusters_mgmt/v1/private", nil) 1212 request.Header.Set("Authorization", "Bearer "+bearer) 1213 recorder := httptest.NewRecorder() 1214 handler.ServeHTTP(recorder, request) 1215 1216 // Verify the response: 1217 Expect(recorder.Code).To(Equal(http.StatusOK)) 1218 }) 1219 1220 It("Rejects token that doesn't match the ACL", func() { 1221 // Prepare the ACL: 1222 acl, err := os.CreateTemp("", "acl-*.yml") 1223 Expect(err).ToNot(HaveOccurred()) 1224 _, err = acl.WriteString(` 1225 - claim: email 1226 pattern: ^.*@example\.com$ 1227 - claim: sub 1228 pattern: ^f:b3f7b485-7184-43c8-8169-37bd6d1fe4aa:myuser$ 1229 `) 1230 Expect(err).ToNot(HaveOccurred()) 1231 defer func() { 1232 err := os.Remove(acl.Name()) 1233 Expect(err).ToNot(HaveOccurred()) 1234 }() 1235 err = acl.Close() 1236 Expect(err).ToNot(HaveOccurred()) 1237 1238 // Prepare the next handler, which should never be called: 1239 next := http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { 1240 Expect(true).To(BeFalse()) 1241 w.WriteHeader(http.StatusBadRequest) 1242 }) 1243 1244 // Prepare the token: 1245 token := MakeTokenObject(jwt.MapClaims{ 1246 "typ": "Bearer", 1247 "email": "jdoe@hacker.com", 1248 }) 1249 bearer := token.Raw 1250 1251 // Prepare the handler: 1252 handler, err := NewHandler(). 1253 Logger(logger). 1254 KeysFile(keysFile). 1255 Next(next). 1256 ACLFile(acl.Name()). 1257 Build() 1258 Expect(err).ToNot(HaveOccurred()) 1259 1260 // Send the request: 1261 request := httptest.NewRequest(http.MethodGet, "/api/clusters_mgmt/v1/private", nil) 1262 request.Header.Set("Authorization", "Bearer "+bearer) 1263 recorder := httptest.NewRecorder() 1264 handler.ServeHTTP(recorder, request) 1265 1266 // Verify the response: 1267 Expect(recorder.Code).To(Equal(http.StatusUnauthorized)) 1268 Expect(recorder.Body).To(MatchJSON(`{ 1269 "kind": "Error", 1270 "id": "401", 1271 "href": "/api/clusters_mgmt/v1/errors/401", 1272 "code": "CLUSTERS-MGMT-401", 1273 "reason": "Access denied" 1274 }`)) 1275 }) 1276 1277 It("Returns expected headers", func() { 1278 // Prepare the next handler, which should never be called: 1279 next := http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { 1280 Expect(true).To(BeFalse()) 1281 w.WriteHeader(http.StatusBadRequest) 1282 }) 1283 1284 // Prepare the handler: 1285 handler, err := NewHandler(). 1286 Logger(logger). 1287 KeysFile(keysFile). 1288 Next(next). 1289 Build() 1290 Expect(err).ToNot(HaveOccurred()) 1291 1292 // Send the request: 1293 request := httptest.NewRequest(http.MethodGet, "/api/clusters_mgmt/v1/private", nil) 1294 request.Header.Set("Authorization", "Bearer junk") 1295 recorder := httptest.NewRecorder() 1296 handler.ServeHTTP(recorder, request) 1297 1298 // Verify the response: 1299 Expect(recorder.Code).To(Equal(http.StatusUnauthorized)) 1300 header := recorder.Header().Get("WWW-Authenticate") 1301 Expect(header).To(Equal("Bearer realm=\"clusters_mgmt/v1\"")) 1302 }) 1303 1304 It("Supports multiple services and versions", func() { 1305 // Prepare the next handler, which should never be called: 1306 next := http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { 1307 Expect(true).To(BeFalse()) 1308 w.WriteHeader(http.StatusBadRequest) 1309 }) 1310 1311 // Prepare the handler: 1312 handler, err := NewHandler(). 1313 Logger(logger). 1314 KeysFile(keysFile). 1315 Next(next). 1316 Build() 1317 Expect(err).ToNot(HaveOccurred()) 1318 1319 // Prepare the variables that we will use for the checks: 1320 var ( 1321 request *http.Request 1322 recorder *httptest.ResponseRecorder 1323 ) 1324 1325 // Check a request for 'clusters_mgmt/v1': 1326 request = httptest.NewRequest(http.MethodGet, "/api/clusters_mgmt/v1/private", nil) 1327 request.Header.Set("Authorization", "Bearer junk") 1328 recorder = httptest.NewRecorder() 1329 handler.ServeHTTP(recorder, request) 1330 Expect(recorder.Code).To(Equal(http.StatusUnauthorized)) 1331 Expect(recorder.Body).To(MatchJSON(`{ 1332 "kind": "Error", 1333 "id": "401", 1334 "href": "/api/clusters_mgmt/v1/errors/401", 1335 "code": "CLUSTERS-MGMT-401", 1336 "reason": "Bearer token is malformed" 1337 }`)) 1338 1339 // Check a request for 'clusters_mgmt/v2': 1340 request = httptest.NewRequest(http.MethodGet, "/api/clusters_mgmt/v2/private", nil) 1341 request.Header.Set("Authorization", "Bearer junk") 1342 recorder = httptest.NewRecorder() 1343 handler.ServeHTTP(recorder, request) 1344 Expect(recorder.Code).To(Equal(http.StatusUnauthorized)) 1345 Expect(recorder.Body).To(MatchJSON(`{ 1346 "kind": "Error", 1347 "id": "401", 1348 "href": "/api/clusters_mgmt/v2/errors/401", 1349 "code": "CLUSTERS-MGMT-401", 1350 "reason": "Bearer token is malformed" 1351 }`)) 1352 1353 // Check a request for 'accounts_mgmt/v1': 1354 request = httptest.NewRequest(http.MethodGet, "/api/accounts_mgmt/v1/private", nil) 1355 request.Header.Set("Authorization", "Bearer junk") 1356 recorder = httptest.NewRecorder() 1357 handler.ServeHTTP(recorder, request) 1358 Expect(recorder.Code).To(Equal(http.StatusUnauthorized)) 1359 Expect(recorder.Body).To(MatchJSON(`{ 1360 "kind": "Error", 1361 "id": "401", 1362 "href": "/api/accounts_mgmt/v1/errors/401", 1363 "code": "ACCOUNTS-MGMT-401", 1364 "reason": "Bearer token is malformed" 1365 }`)) 1366 1367 // Check a request for 'accounts_mgmt/v2': 1368 request = httptest.NewRequest(http.MethodGet, "/api/accounts_mgmt/v2/private", nil) 1369 request.Header.Set("Authorization", "Bearer junk") 1370 recorder = httptest.NewRecorder() 1371 handler.ServeHTTP(recorder, request) 1372 Expect(recorder.Code).To(Equal(http.StatusUnauthorized)) 1373 Expect(recorder.Body).To(MatchJSON(`{ 1374 "kind": "Error", 1375 "id": "401", 1376 "href": "/api/accounts_mgmt/v2/errors/401", 1377 "code": "ACCOUNTS-MGMT-401", 1378 "reason": "Bearer token is malformed" 1379 }`)) 1380 }) 1381 1382 It("Honours explicit service identifier", func() { 1383 // Prepare the next handler, which should never be called: 1384 next := http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { 1385 Expect(true).To(BeFalse()) 1386 w.WriteHeader(http.StatusBadRequest) 1387 }) 1388 1389 // Prepare the handler: 1390 handler, err := NewHandler(). 1391 Logger(logger). 1392 KeysFile(keysFile). 1393 Next(next). 1394 Service("my_service"). 1395 Build() 1396 Expect(err).ToNot(HaveOccurred()) 1397 1398 // Check that the response code contains the service identifier: 1399 request := httptest.NewRequest(http.MethodGet, "/api/clusters_mgmt/v1/private", nil) 1400 request.Header.Set("Authorization", "Bearer junk") 1401 recorder := httptest.NewRecorder() 1402 handler.ServeHTTP(recorder, request) 1403 Expect(recorder.Code).To(Equal(http.StatusUnauthorized)) 1404 Expect(recorder.Body).To(MatchJSON(`{ 1405 "kind": "Error", 1406 "id": "401", 1407 "href": "/api/clusters_mgmt/v1/errors/401", 1408 "code": "MY-SERVICE-401", 1409 "reason": "Bearer token is malformed" 1410 }`)) 1411 }) 1412 1413 It("Honours explicit error identifier", func() { 1414 // Prepare the next handler, which should never be called: 1415 next := http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { 1416 Expect(true).To(BeFalse()) 1417 w.WriteHeader(http.StatusBadRequest) 1418 }) 1419 1420 // Prepare the handler: 1421 handler, err := NewHandler(). 1422 Logger(logger). 1423 KeysFile(keysFile). 1424 Next(next). 1425 Error("123"). 1426 Build() 1427 Expect(err).ToNot(HaveOccurred()) 1428 1429 // Check that the response code contains the custom error identifier: 1430 request := httptest.NewRequest(http.MethodGet, "/api/clusters_mgmt/v1/private", nil) 1431 request.Header.Set("Authorization", "Bearer junk") 1432 recorder := httptest.NewRecorder() 1433 handler.ServeHTTP(recorder, request) 1434 Expect(recorder.Code).To(Equal(http.StatusUnauthorized)) 1435 Expect(recorder.Body).To(MatchJSON(`{ 1436 "kind": "Error", 1437 "id": "123", 1438 "href": "/api/clusters_mgmt/v1/errors/123", 1439 "code": "CLUSTERS-MGMT-123", 1440 "reason": "Bearer token is malformed" 1441 }`)) 1442 }) 1443 1444 It("Adds operation identifier", func() { 1445 // Prepare the next handler, which should never be called: 1446 next := http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { 1447 Expect(true).To(BeFalse()) 1448 w.WriteHeader(http.StatusBadRequest) 1449 }) 1450 1451 // Prepare the handler: 1452 handler, err := NewHandler(). 1453 Logger(logger). 1454 KeysFile(keysFile). 1455 Next(next). 1456 OperationID(func(r *http.Request) string { 1457 return r.Header.Get("X-Operation-ID") 1458 }). 1459 Build() 1460 Expect(err).ToNot(HaveOccurred()) 1461 1462 // Check that the response code contains the operation identifier: 1463 request := httptest.NewRequest(http.MethodGet, "/api/clusters_mgmt/v1/private", nil) 1464 request.Header.Set("Authorization", "Bearer junk") 1465 request.Header.Set("x-Operation-ID", "123") 1466 recorder := httptest.NewRecorder() 1467 handler.ServeHTTP(recorder, request) 1468 Expect(recorder.Code).To(Equal(http.StatusUnauthorized)) 1469 Expect(recorder.Body).To(MatchJSON(`{ 1470 "kind": "Error", 1471 "id": "401", 1472 "href": "/api/clusters_mgmt/v1/errors/401", 1473 "code": "CLUSTERS-MGMT-401", 1474 "reason": "Bearer token is malformed", 1475 "operation_id": "123" 1476 }`)) 1477 }) 1478 1479 It("Accepts token expired within the configured tolerance", func() { 1480 // Prepare the next handler: 1481 next := http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { 1482 w.WriteHeader(http.StatusOK) 1483 }) 1484 1485 // Prepare a token that expired 5 minutes ago: 1486 bearer := MakeTokenString("Bearer", -5*time.Minute) 1487 1488 // Prepare a handler that tolerates tokens that expired up to 10 minutes ago: 1489 handler, err := NewHandler(). 1490 Logger(logger). 1491 KeysFile(keysFile). 1492 Tolerance(10 * time.Minute). 1493 Next(next). 1494 Build() 1495 Expect(err).ToNot(HaveOccurred()) 1496 1497 // Send the request: 1498 request := httptest.NewRequest(http.MethodGet, "/api/clusters_mgmt/v1/private", nil) 1499 request.Header.Set("Authorization", "Bearer "+bearer) 1500 recorder := httptest.NewRecorder() 1501 handler.ServeHTTP(recorder, request) 1502 1503 // Verify the response: 1504 Expect(recorder.Code).To(Equal(http.StatusOK)) 1505 }) 1506 1507 It("Rejects token expired outside the configured tolerance", func() { 1508 // Prepare the next handler: 1509 next := http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { 1510 w.WriteHeader(http.StatusOK) 1511 }) 1512 1513 // Prepare a token that expired 15 minutes ago: 1514 bearer := MakeTokenString("Bearer", -15*time.Minute) 1515 1516 // Prepare a handler that tolerates tokens that have expired up to 10 minutes ago: 1517 handler, err := NewHandler(). 1518 Logger(logger). 1519 KeysFile(keysFile). 1520 Tolerance(10 * time.Minute). 1521 Next(next). 1522 Build() 1523 Expect(err).ToNot(HaveOccurred()) 1524 1525 // Send the request: 1526 request := httptest.NewRequest(http.MethodGet, "/api/clusters_mgmt/v1/private", nil) 1527 request.Header.Set("Authorization", "Bearer "+bearer) 1528 recorder := httptest.NewRecorder() 1529 handler.ServeHTTP(recorder, request) 1530 1531 // Verify the response: 1532 Expect(recorder.Code).To(Equal(http.StatusUnauthorized)) 1533 }) 1534 1535 It("Accepts good token from cookie", func() { 1536 // Prepare the next handler: 1537 next := http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { 1538 w.WriteHeader(http.StatusOK) 1539 }) 1540 1541 // Prepare the token: 1542 bearer := MakeTokenString("Bearer", 15*time.Minute) 1543 1544 // Prepare the handler: 1545 handler, err := NewHandler(). 1546 Logger(logger). 1547 KeysFile(keysFile). 1548 Next(next). 1549 Build() 1550 Expect(err).ToNot(HaveOccurred()) 1551 1552 // Send the request: 1553 request := httptest.NewRequest(http.MethodGet, "/api/clusters_mgmt/v1/private", nil) 1554 request.AddCookie(&http.Cookie{ 1555 Name: "cs_jwt", 1556 Value: bearer, 1557 }) 1558 recorder := httptest.NewRecorder() 1559 handler.ServeHTTP(recorder, request) 1560 1561 // Verify the response: 1562 Expect(recorder.Code).To(Equal(http.StatusOK)) 1563 }) 1564 1565 It("Rejects bad token from cookie", func() { 1566 // Prepare the next handler: 1567 next := http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { 1568 w.WriteHeader(http.StatusOK) 1569 }) 1570 1571 // Prepare the handler: 1572 handler, err := NewHandler(). 1573 Logger(logger). 1574 KeysFile(keysFile). 1575 Next(next). 1576 Build() 1577 Expect(err).ToNot(HaveOccurred()) 1578 1579 // Send the request: 1580 request := httptest.NewRequest(http.MethodGet, "/api/clusters_mgmt/v1/private", nil) 1581 request.AddCookie(&http.Cookie{ 1582 Name: "cs_jwt", 1583 Value: "junk", 1584 }) 1585 recorder := httptest.NewRecorder() 1586 handler.ServeHTTP(recorder, request) 1587 1588 // Verify the response: 1589 Expect(recorder.Code).To(Equal(http.StatusUnauthorized)) 1590 }) 1591 1592 It("Ignores cookie when header present", func() { 1593 // Prepare the next handler: 1594 next := http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { 1595 w.WriteHeader(http.StatusOK) 1596 }) 1597 1598 // Prepare the token: 1599 bearer := MakeTokenString("Bearer", 15*time.Minute) 1600 1601 // Prepare the handler: 1602 handler, err := NewHandler(). 1603 Logger(logger). 1604 KeysFile(keysFile). 1605 Next(next). 1606 Build() 1607 Expect(err).ToNot(HaveOccurred()) 1608 1609 // Send the request: 1610 request := httptest.NewRequest(http.MethodGet, "/api/clusters_mgmt/v1/private", nil) 1611 request.Header.Set("Authorization", "Bearer "+bearer) 1612 request.AddCookie(&http.Cookie{ 1613 Name: "cs_jwt", 1614 Value: "junk", 1615 }) 1616 recorder := httptest.NewRecorder() 1617 handler.ServeHTTP(recorder, request) 1618 1619 // Verify the response: 1620 Expect(recorder.Code).To(Equal(http.StatusOK)) 1621 }) 1622 1623 It("Honours custom cookie", func() { 1624 // Prepare the next handler: 1625 next := http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { 1626 w.WriteHeader(http.StatusOK) 1627 }) 1628 1629 // Prepare the token: 1630 bearer := MakeTokenString("Bearer", 15*time.Minute) 1631 1632 // Prepare the handler: 1633 handler, err := NewHandler(). 1634 Logger(logger). 1635 KeysFile(keysFile). 1636 Cookie("my_cookie"). 1637 Next(next). 1638 Build() 1639 Expect(err).ToNot(HaveOccurred()) 1640 1641 // Send the request: 1642 request := httptest.NewRequest(http.MethodGet, "/api/clusters_mgmt/v1/private", nil) 1643 request.AddCookie(&http.Cookie{ 1644 Name: "my_cookie", 1645 Value: bearer, 1646 }) 1647 recorder := httptest.NewRecorder() 1648 handler.ServeHTTP(recorder, request) 1649 1650 // Verify the response: 1651 Expect(recorder.Code).To(Equal(http.StatusOK)) 1652 }) 1653 1654 It("Ignores default cookie when custom cookie is specified", func() { 1655 // Prepare the next handler: 1656 next := http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { 1657 w.WriteHeader(http.StatusOK) 1658 }) 1659 1660 // Prepare the token: 1661 bearer := MakeTokenString("Bearer", 15*time.Minute) 1662 1663 // Prepare the handler: 1664 handler, err := NewHandler(). 1665 Logger(logger). 1666 KeysFile(keysFile). 1667 Cookie("my_cookie"). 1668 Next(next). 1669 Build() 1670 Expect(err).ToNot(HaveOccurred()) 1671 1672 // Send the request: 1673 request := httptest.NewRequest(http.MethodGet, "/api/clusters_mgmt/v1/private", nil) 1674 request.AddCookie(&http.Cookie{ 1675 Name: "cs_jwt", 1676 Value: bearer, 1677 }) 1678 recorder := httptest.NewRecorder() 1679 handler.ServeHTTP(recorder, request) 1680 1681 // Verify the response: 1682 Expect(recorder.Code).To(Equal(http.StatusUnauthorized)) 1683 }) 1684 })