go.etcd.io/etcd@v3.3.27+incompatible/etcdserver/api/v2http/client_auth_test.go (about) 1 // Copyright 2015 The etcd Authors 2 // 3 // Licensed under the Apache License, Version 2.0 (the "License"); 4 // you may not use this file except in compliance with the License. 5 // You may obtain a copy of the License at 6 // 7 // http://www.apache.org/licenses/LICENSE-2.0 8 // 9 // Unless required by applicable law or agreed to in writing, software 10 // distributed under the License is distributed on an "AS IS" BASIS, 11 // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 // See the License for the specific language governing permissions and 13 // limitations under the License. 14 15 package v2http 16 17 import ( 18 "crypto/tls" 19 "crypto/x509" 20 "encoding/json" 21 "encoding/pem" 22 "errors" 23 "fmt" 24 "io/ioutil" 25 "net/http" 26 "net/http/httptest" 27 "net/url" 28 "path" 29 "sort" 30 "strings" 31 "testing" 32 33 "github.com/coreos/etcd/etcdserver/api" 34 "github.com/coreos/etcd/etcdserver/auth" 35 ) 36 37 const goodPassword = "good" 38 39 func mustJSONRequest(t *testing.T, method string, p string, body string) *http.Request { 40 req, err := http.NewRequest(method, path.Join(authPrefix, p), strings.NewReader(body)) 41 if err != nil { 42 t.Fatalf("Error making JSON request: %s %s %s\n", method, p, body) 43 } 44 req.Header.Set("Content-Type", "application/json") 45 return req 46 } 47 48 type mockAuthStore struct { 49 users map[string]*auth.User 50 roles map[string]*auth.Role 51 err error 52 enabled bool 53 } 54 55 func (s *mockAuthStore) AllUsers() ([]string, error) { 56 var us []string 57 for u := range s.users { 58 us = append(us, u) 59 } 60 sort.Strings(us) 61 return us, s.err 62 } 63 func (s *mockAuthStore) GetUser(name string) (auth.User, error) { 64 u, ok := s.users[name] 65 if !ok { 66 return auth.User{}, s.err 67 } 68 return *u, s.err 69 } 70 func (s *mockAuthStore) CreateOrUpdateUser(user auth.User) (out auth.User, created bool, err error) { 71 if s.users == nil { 72 out, err = s.CreateUser(user) 73 return out, true, err 74 } 75 out, err = s.UpdateUser(user) 76 return out, false, err 77 } 78 func (s *mockAuthStore) CreateUser(user auth.User) (auth.User, error) { return user, s.err } 79 func (s *mockAuthStore) DeleteUser(name string) error { return s.err } 80 func (s *mockAuthStore) UpdateUser(user auth.User) (auth.User, error) { 81 return *s.users[user.User], s.err 82 } 83 func (s *mockAuthStore) AllRoles() ([]string, error) { 84 return []string{"awesome", "guest", "root"}, s.err 85 } 86 func (s *mockAuthStore) GetRole(name string) (auth.Role, error) { 87 r, ok := s.roles[name] 88 if ok { 89 return *r, s.err 90 } 91 return auth.Role{}, fmt.Errorf("%q does not exist (%v)", name, s.err) 92 } 93 func (s *mockAuthStore) CreateRole(role auth.Role) error { return s.err } 94 func (s *mockAuthStore) DeleteRole(name string) error { return s.err } 95 func (s *mockAuthStore) UpdateRole(role auth.Role) (auth.Role, error) { 96 return *s.roles[role.Role], s.err 97 } 98 func (s *mockAuthStore) AuthEnabled() bool { return s.enabled } 99 func (s *mockAuthStore) EnableAuth() error { return s.err } 100 func (s *mockAuthStore) DisableAuth() error { return s.err } 101 102 func (s *mockAuthStore) CheckPassword(user auth.User, password string) bool { 103 return user.Password == password 104 } 105 106 func (s *mockAuthStore) HashPassword(password string) (string, error) { 107 return password, nil 108 } 109 110 func TestAuthFlow(t *testing.T) { 111 api.EnableCapability(api.AuthCapability) 112 var testCases = []struct { 113 req *http.Request 114 store mockAuthStore 115 116 wcode int 117 wbody string 118 }{ 119 { 120 req: mustJSONRequest(t, "PUT", "users/alice", `{{{{{{{`), 121 store: mockAuthStore{}, 122 wcode: http.StatusBadRequest, 123 wbody: `{"message":"Invalid JSON in request body."}`, 124 }, 125 { 126 req: mustJSONRequest(t, "PUT", "users/alice", `{"user": "alice", "password": "goodpassword"}`), 127 store: mockAuthStore{enabled: true}, 128 wcode: http.StatusUnauthorized, 129 wbody: `{"message":"Insufficient credentials"}`, 130 }, 131 // Users 132 { 133 req: mustJSONRequest(t, "GET", "users", ""), 134 store: mockAuthStore{ 135 users: map[string]*auth.User{ 136 "alice": { 137 User: "alice", 138 Roles: []string{"alicerole", "guest"}, 139 Password: "wheeee", 140 }, 141 "bob": { 142 User: "bob", 143 Roles: []string{"guest"}, 144 Password: "wheeee", 145 }, 146 "root": { 147 User: "root", 148 Roles: []string{"root"}, 149 Password: "wheeee", 150 }, 151 }, 152 roles: map[string]*auth.Role{ 153 "alicerole": { 154 Role: "alicerole", 155 }, 156 "guest": { 157 Role: "guest", 158 }, 159 "root": { 160 Role: "root", 161 }, 162 }, 163 }, 164 wcode: http.StatusOK, 165 wbody: `{"users":[` + 166 `{"user":"alice","roles":[` + 167 `{"role":"alicerole","permissions":{"kv":{"read":null,"write":null}}},` + 168 `{"role":"guest","permissions":{"kv":{"read":null,"write":null}}}` + 169 `]},` + 170 `{"user":"bob","roles":[{"role":"guest","permissions":{"kv":{"read":null,"write":null}}}]},` + 171 `{"user":"root","roles":[{"role":"root","permissions":{"kv":{"read":null,"write":null}}}]}]}`, 172 }, 173 { 174 req: mustJSONRequest(t, "GET", "users/alice", ""), 175 store: mockAuthStore{ 176 users: map[string]*auth.User{ 177 "alice": { 178 User: "alice", 179 Roles: []string{"alicerole"}, 180 Password: "wheeee", 181 }, 182 }, 183 roles: map[string]*auth.Role{ 184 "alicerole": { 185 Role: "alicerole", 186 }, 187 }, 188 }, 189 wcode: http.StatusOK, 190 wbody: `{"user":"alice","roles":[{"role":"alicerole","permissions":{"kv":{"read":null,"write":null}}}]}`, 191 }, 192 { 193 req: mustJSONRequest(t, "PUT", "users/alice", `{"user": "alice", "password": "goodpassword"}`), 194 store: mockAuthStore{}, 195 wcode: http.StatusCreated, 196 wbody: `{"user":"alice","roles":null}`, 197 }, 198 { 199 req: mustJSONRequest(t, "DELETE", "users/alice", ``), 200 store: mockAuthStore{}, 201 wcode: http.StatusOK, 202 wbody: ``, 203 }, 204 { 205 req: mustJSONRequest(t, "PUT", "users/alice", `{"user": "alice", "password": "goodpassword"}`), 206 store: mockAuthStore{ 207 users: map[string]*auth.User{ 208 "alice": { 209 User: "alice", 210 Roles: []string{"alicerole", "guest"}, 211 Password: "wheeee", 212 }, 213 }, 214 }, 215 wcode: http.StatusOK, 216 wbody: `{"user":"alice","roles":["alicerole","guest"]}`, 217 }, 218 { 219 req: mustJSONRequest(t, "PUT", "users/alice", `{"user": "alice", "grant": ["alicerole"]}`), 220 store: mockAuthStore{ 221 users: map[string]*auth.User{ 222 "alice": { 223 User: "alice", 224 Roles: []string{"alicerole", "guest"}, 225 Password: "wheeee", 226 }, 227 }, 228 }, 229 wcode: http.StatusOK, 230 wbody: `{"user":"alice","roles":["alicerole","guest"]}`, 231 }, 232 { 233 req: mustJSONRequest(t, "GET", "users/alice", ``), 234 store: mockAuthStore{ 235 users: map[string]*auth.User{}, 236 err: auth.Error{Status: http.StatusNotFound, Errmsg: "auth: User alice doesn't exist."}, 237 }, 238 wcode: http.StatusNotFound, 239 wbody: `{"message":"auth: User alice doesn't exist."}`, 240 }, 241 { 242 req: mustJSONRequest(t, "GET", "roles/manager", ""), 243 store: mockAuthStore{ 244 roles: map[string]*auth.Role{ 245 "manager": { 246 Role: "manager", 247 }, 248 }, 249 }, 250 wcode: http.StatusOK, 251 wbody: `{"role":"manager","permissions":{"kv":{"read":null,"write":null}}}`, 252 }, 253 { 254 req: mustJSONRequest(t, "DELETE", "roles/manager", ``), 255 store: mockAuthStore{}, 256 wcode: http.StatusOK, 257 wbody: ``, 258 }, 259 { 260 req: mustJSONRequest(t, "PUT", "roles/manager", `{"role":"manager","permissions":{"kv":{"read":[],"write":[]}}}`), 261 store: mockAuthStore{}, 262 wcode: http.StatusCreated, 263 wbody: `{"role":"manager","permissions":{"kv":{"read":[],"write":[]}}}`, 264 }, 265 { 266 req: mustJSONRequest(t, "PUT", "roles/manager", `{"role":"manager","revoke":{"kv":{"read":["foo"],"write":[]}}}`), 267 store: mockAuthStore{ 268 roles: map[string]*auth.Role{ 269 "manager": { 270 Role: "manager", 271 }, 272 }, 273 }, 274 wcode: http.StatusOK, 275 wbody: `{"role":"manager","permissions":{"kv":{"read":null,"write":null}}}`, 276 }, 277 { 278 req: mustJSONRequest(t, "GET", "roles", ""), 279 store: mockAuthStore{ 280 roles: map[string]*auth.Role{ 281 "awesome": { 282 Role: "awesome", 283 }, 284 "guest": { 285 Role: "guest", 286 }, 287 "root": { 288 Role: "root", 289 }, 290 }, 291 }, 292 wcode: http.StatusOK, 293 wbody: `{"roles":[{"role":"awesome","permissions":{"kv":{"read":null,"write":null}}},` + 294 `{"role":"guest","permissions":{"kv":{"read":null,"write":null}}},` + 295 `{"role":"root","permissions":{"kv":{"read":null,"write":null}}}]}`, 296 }, 297 { 298 req: mustJSONRequest(t, "GET", "enable", ""), 299 store: mockAuthStore{ 300 enabled: true, 301 }, 302 wcode: http.StatusOK, 303 wbody: `{"enabled":true}`, 304 }, 305 { 306 req: mustJSONRequest(t, "PUT", "enable", ""), 307 store: mockAuthStore{ 308 enabled: false, 309 }, 310 wcode: http.StatusOK, 311 wbody: ``, 312 }, 313 { 314 req: (func() *http.Request { 315 req := mustJSONRequest(t, "DELETE", "enable", "") 316 req.SetBasicAuth("root", "good") 317 return req 318 })(), 319 store: mockAuthStore{ 320 enabled: true, 321 users: map[string]*auth.User{ 322 "root": { 323 User: "root", 324 Password: goodPassword, 325 Roles: []string{"root"}, 326 }, 327 }, 328 roles: map[string]*auth.Role{ 329 "root": { 330 Role: "root", 331 }, 332 }, 333 }, 334 wcode: http.StatusOK, 335 wbody: ``, 336 }, 337 { 338 req: (func() *http.Request { 339 req := mustJSONRequest(t, "DELETE", "enable", "") 340 req.SetBasicAuth("root", "bad") 341 return req 342 })(), 343 store: mockAuthStore{ 344 enabled: true, 345 users: map[string]*auth.User{ 346 "root": { 347 User: "root", 348 Password: goodPassword, 349 Roles: []string{"root"}, 350 }, 351 }, 352 roles: map[string]*auth.Role{ 353 "root": { 354 Role: "guest", 355 }, 356 }, 357 }, 358 wcode: http.StatusUnauthorized, 359 wbody: `{"message":"Insufficient credentials"}`, 360 }, 361 } 362 363 for i, tt := range testCases { 364 mux := http.NewServeMux() 365 h := &authHandler{ 366 sec: &tt.store, 367 cluster: &fakeCluster{id: 1}, 368 } 369 handleAuth(mux, h) 370 rw := httptest.NewRecorder() 371 mux.ServeHTTP(rw, tt.req) 372 if rw.Code != tt.wcode { 373 t.Errorf("#%d: got code=%d, want %d", i, rw.Code, tt.wcode) 374 } 375 g := rw.Body.String() 376 g = strings.TrimSpace(g) 377 if g != tt.wbody { 378 t.Errorf("#%d: got body=%s, want %s", i, g, tt.wbody) 379 } 380 } 381 } 382 383 func TestGetUserGrantedWithNonexistingRole(t *testing.T) { 384 sh := &authHandler{ 385 sec: &mockAuthStore{ 386 users: map[string]*auth.User{ 387 "root": { 388 User: "root", 389 Roles: []string{"root", "foo"}, 390 }, 391 }, 392 roles: map[string]*auth.Role{ 393 "root": { 394 Role: "root", 395 }, 396 }, 397 }, 398 cluster: &fakeCluster{id: 1}, 399 } 400 srv := httptest.NewServer(http.HandlerFunc(sh.baseUsers)) 401 defer srv.Close() 402 403 req, err := http.NewRequest("GET", "", nil) 404 if err != nil { 405 t.Fatal(err) 406 } 407 req.URL, err = url.Parse(srv.URL) 408 if err != nil { 409 t.Fatal(err) 410 } 411 req.Header.Set("Content-Type", "application/json") 412 413 cli := http.DefaultClient 414 resp, err := cli.Do(req) 415 if err != nil { 416 t.Fatal(err) 417 } 418 defer resp.Body.Close() 419 420 var uc usersCollections 421 if err := json.NewDecoder(resp.Body).Decode(&uc); err != nil { 422 t.Fatal(err) 423 } 424 if len(uc.Users) != 1 { 425 t.Fatalf("expected 1 user, got %+v", uc.Users) 426 } 427 if uc.Users[0].User != "root" { 428 t.Fatalf("expected 'root', got %q", uc.Users[0].User) 429 } 430 if len(uc.Users[0].Roles) != 1 { 431 t.Fatalf("expected 1 role, got %+v", uc.Users[0].Roles) 432 } 433 if uc.Users[0].Roles[0].Role != "root" { 434 t.Fatalf("expected 'root', got %q", uc.Users[0].Roles[0].Role) 435 } 436 } 437 438 func mustAuthRequest(method, username, password string) *http.Request { 439 req, err := http.NewRequest(method, "path", strings.NewReader("")) 440 if err != nil { 441 panic("Cannot make auth request: " + err.Error()) 442 } 443 req.SetBasicAuth(username, password) 444 return req 445 } 446 447 func unauthedRequest(method string) *http.Request { 448 req, err := http.NewRequest(method, "path", strings.NewReader("")) 449 if err != nil { 450 panic("Cannot make request: " + err.Error()) 451 } 452 return req 453 } 454 455 func tlsAuthedRequest(req *http.Request, certname string) *http.Request { 456 bytes, err := ioutil.ReadFile(fmt.Sprintf("testdata/%s.pem", certname)) 457 if err != nil { 458 panic(err) 459 } 460 461 block, _ := pem.Decode(bytes) 462 cert, err := x509.ParseCertificate(block.Bytes) 463 if err != nil { 464 panic(err) 465 } 466 467 req.TLS = &tls.ConnectionState{ 468 VerifiedChains: [][]*x509.Certificate{{cert}}, 469 } 470 return req 471 } 472 473 func TestPrefixAccess(t *testing.T) { 474 var table = []struct { 475 key string 476 req *http.Request 477 store *mockAuthStore 478 hasRoot bool 479 hasKeyPrefixAccess bool 480 hasRecursiveAccess bool 481 }{ 482 { 483 key: "/foo", 484 req: mustAuthRequest("GET", "root", "good"), 485 store: &mockAuthStore{ 486 users: map[string]*auth.User{ 487 "root": { 488 User: "root", 489 Password: goodPassword, 490 Roles: []string{"root"}, 491 }, 492 }, 493 roles: map[string]*auth.Role{ 494 "root": { 495 Role: "root", 496 }, 497 }, 498 enabled: true, 499 }, 500 hasRoot: true, 501 hasKeyPrefixAccess: true, 502 hasRecursiveAccess: true, 503 }, 504 { 505 key: "/foo", 506 req: mustAuthRequest("GET", "user", "good"), 507 store: &mockAuthStore{ 508 users: map[string]*auth.User{ 509 "user": { 510 User: "user", 511 Password: goodPassword, 512 Roles: []string{"foorole"}, 513 }, 514 }, 515 roles: map[string]*auth.Role{ 516 "foorole": { 517 Role: "foorole", 518 Permissions: auth.Permissions{ 519 KV: auth.RWPermission{ 520 Read: []string{"/foo"}, 521 Write: []string{"/foo"}, 522 }, 523 }, 524 }, 525 }, 526 enabled: true, 527 }, 528 hasRoot: false, 529 hasKeyPrefixAccess: true, 530 hasRecursiveAccess: false, 531 }, 532 { 533 key: "/foo", 534 req: mustAuthRequest("GET", "user", "good"), 535 store: &mockAuthStore{ 536 users: map[string]*auth.User{ 537 "user": { 538 User: "user", 539 Password: goodPassword, 540 Roles: []string{"foorole"}, 541 }, 542 }, 543 roles: map[string]*auth.Role{ 544 "foorole": { 545 Role: "foorole", 546 Permissions: auth.Permissions{ 547 KV: auth.RWPermission{ 548 Read: []string{"/foo*"}, 549 Write: []string{"/foo*"}, 550 }, 551 }, 552 }, 553 }, 554 enabled: true, 555 }, 556 hasRoot: false, 557 hasKeyPrefixAccess: true, 558 hasRecursiveAccess: true, 559 }, 560 { 561 key: "/foo", 562 req: mustAuthRequest("GET", "user", "bad"), 563 store: &mockAuthStore{ 564 users: map[string]*auth.User{ 565 "user": { 566 User: "user", 567 Password: goodPassword, 568 Roles: []string{"foorole"}, 569 }, 570 }, 571 roles: map[string]*auth.Role{ 572 "foorole": { 573 Role: "foorole", 574 Permissions: auth.Permissions{ 575 KV: auth.RWPermission{ 576 Read: []string{"/foo*"}, 577 Write: []string{"/foo*"}, 578 }, 579 }, 580 }, 581 }, 582 enabled: true, 583 }, 584 hasRoot: false, 585 hasKeyPrefixAccess: false, 586 hasRecursiveAccess: false, 587 }, 588 { 589 key: "/foo", 590 req: mustAuthRequest("GET", "user", "good"), 591 store: &mockAuthStore{ 592 users: map[string]*auth.User{}, 593 err: errors.New("Not the user"), 594 enabled: true, 595 }, 596 hasRoot: false, 597 hasKeyPrefixAccess: false, 598 hasRecursiveAccess: false, 599 }, 600 { 601 key: "/foo", 602 req: mustJSONRequest(t, "GET", "somepath", ""), 603 store: &mockAuthStore{ 604 users: map[string]*auth.User{ 605 "user": { 606 User: "user", 607 Password: goodPassword, 608 Roles: []string{"foorole"}, 609 }, 610 }, 611 roles: map[string]*auth.Role{ 612 "guest": { 613 Role: "guest", 614 Permissions: auth.Permissions{ 615 KV: auth.RWPermission{ 616 Read: []string{"/foo*"}, 617 Write: []string{"/foo*"}, 618 }, 619 }, 620 }, 621 }, 622 enabled: true, 623 }, 624 hasRoot: false, 625 hasKeyPrefixAccess: true, 626 hasRecursiveAccess: true, 627 }, 628 { 629 key: "/bar", 630 req: mustJSONRequest(t, "GET", "somepath", ""), 631 store: &mockAuthStore{ 632 users: map[string]*auth.User{ 633 "user": { 634 User: "user", 635 Password: goodPassword, 636 Roles: []string{"foorole"}, 637 }, 638 }, 639 roles: map[string]*auth.Role{ 640 "guest": { 641 Role: "guest", 642 Permissions: auth.Permissions{ 643 KV: auth.RWPermission{ 644 Read: []string{"/foo*"}, 645 Write: []string{"/foo*"}, 646 }, 647 }, 648 }, 649 }, 650 enabled: true, 651 }, 652 hasRoot: false, 653 hasKeyPrefixAccess: false, 654 hasRecursiveAccess: false, 655 }, 656 // check access for multiple roles 657 { 658 key: "/foo", 659 req: mustAuthRequest("GET", "user", "good"), 660 store: &mockAuthStore{ 661 users: map[string]*auth.User{ 662 "user": { 663 User: "user", 664 Password: goodPassword, 665 Roles: []string{"role1", "role2"}, 666 }, 667 }, 668 roles: map[string]*auth.Role{ 669 "role1": { 670 Role: "role1", 671 }, 672 "role2": { 673 Role: "role2", 674 Permissions: auth.Permissions{ 675 KV: auth.RWPermission{ 676 Read: []string{"/foo"}, 677 Write: []string{"/foo"}, 678 }, 679 }, 680 }, 681 }, 682 enabled: true, 683 }, 684 hasRoot: false, 685 hasKeyPrefixAccess: true, 686 hasRecursiveAccess: false, 687 }, 688 { 689 key: "/foo", 690 req: (func() *http.Request { 691 req := mustJSONRequest(t, "GET", "somepath", "") 692 req.Header.Set("Authorization", "malformedencoding") 693 return req 694 })(), 695 store: &mockAuthStore{ 696 enabled: true, 697 users: map[string]*auth.User{ 698 "root": { 699 User: "root", 700 Password: goodPassword, 701 Roles: []string{"root"}, 702 }, 703 }, 704 roles: map[string]*auth.Role{ 705 "guest": { 706 Role: "guest", 707 Permissions: auth.Permissions{ 708 KV: auth.RWPermission{ 709 Read: []string{"/foo*"}, 710 Write: []string{"/foo*"}, 711 }, 712 }, 713 }, 714 }, 715 }, 716 hasRoot: false, 717 hasKeyPrefixAccess: false, 718 hasRecursiveAccess: false, 719 }, 720 { // guest access in non-TLS mode 721 key: "/foo", 722 req: (func() *http.Request { 723 return mustJSONRequest(t, "GET", "somepath", "") 724 })(), 725 store: &mockAuthStore{ 726 enabled: true, 727 users: map[string]*auth.User{ 728 "root": { 729 User: "root", 730 Password: goodPassword, 731 Roles: []string{"root"}, 732 }, 733 }, 734 roles: map[string]*auth.Role{ 735 "guest": { 736 Role: "guest", 737 Permissions: auth.Permissions{ 738 KV: auth.RWPermission{ 739 Read: []string{"/foo*"}, 740 Write: []string{"/foo*"}, 741 }, 742 }, 743 }, 744 }, 745 }, 746 hasRoot: false, 747 hasKeyPrefixAccess: true, 748 hasRecursiveAccess: true, 749 }, 750 } 751 752 for i, tt := range table { 753 if tt.hasRoot != hasRootAccess(tt.store, tt.req, true) { 754 t.Errorf("#%d: hasRoot doesn't match (expected %v)", i, tt.hasRoot) 755 } 756 if tt.hasKeyPrefixAccess != hasKeyPrefixAccess(tt.store, tt.req, tt.key, false, true) { 757 t.Errorf("#%d: hasKeyPrefixAccess doesn't match (expected %v)", i, tt.hasRoot) 758 } 759 if tt.hasRecursiveAccess != hasKeyPrefixAccess(tt.store, tt.req, tt.key, true, true) { 760 t.Errorf("#%d: hasRecursiveAccess doesn't match (expected %v)", i, tt.hasRoot) 761 } 762 } 763 } 764 765 func TestUserFromClientCertificate(t *testing.T) { 766 witherror := &mockAuthStore{ 767 users: map[string]*auth.User{ 768 "user": { 769 User: "user", 770 Roles: []string{"root"}, 771 Password: "password", 772 }, 773 "basicauth": { 774 User: "basicauth", 775 Roles: []string{"root"}, 776 Password: "password", 777 }, 778 }, 779 roles: map[string]*auth.Role{ 780 "root": { 781 Role: "root", 782 }, 783 }, 784 err: errors.New(""), 785 } 786 787 noerror := &mockAuthStore{ 788 users: map[string]*auth.User{ 789 "user": { 790 User: "user", 791 Roles: []string{"root"}, 792 Password: "password", 793 }, 794 "basicauth": { 795 User: "basicauth", 796 Roles: []string{"root"}, 797 Password: "password", 798 }, 799 }, 800 roles: map[string]*auth.Role{ 801 "root": { 802 Role: "root", 803 }, 804 }, 805 } 806 807 var table = []struct { 808 req *http.Request 809 userExists bool 810 store auth.Store 811 username string 812 }{ 813 { 814 // non tls request 815 req: unauthedRequest("GET"), 816 userExists: false, 817 store: witherror, 818 }, 819 { 820 // cert with cn of existing user 821 req: tlsAuthedRequest(unauthedRequest("GET"), "user"), 822 userExists: true, 823 username: "user", 824 store: noerror, 825 }, 826 { 827 // cert with cn of non-existing user 828 req: tlsAuthedRequest(unauthedRequest("GET"), "otheruser"), 829 userExists: false, 830 store: witherror, 831 }, 832 } 833 834 for i, tt := range table { 835 user := userFromClientCertificate(tt.store, tt.req) 836 userExists := user != nil 837 838 if tt.userExists != userExists { 839 t.Errorf("#%d: userFromClientCertificate doesn't match (expected %v)", i, tt.userExists) 840 } 841 if user != nil && (tt.username != user.User) { 842 t.Errorf("#%d: userFromClientCertificate username doesn't match (expected %s, got %s)", i, tt.username, user.User) 843 } 844 } 845 } 846 847 func TestUserFromBasicAuth(t *testing.T) { 848 sec := &mockAuthStore{ 849 users: map[string]*auth.User{ 850 "user": { 851 User: "user", 852 Roles: []string{"root"}, 853 Password: "password", 854 }, 855 }, 856 roles: map[string]*auth.Role{ 857 "root": { 858 Role: "root", 859 }, 860 }, 861 } 862 863 var table = []struct { 864 username string 865 req *http.Request 866 userExists bool 867 }{ 868 { 869 // valid user, valid pass 870 username: "user", 871 req: mustAuthRequest("GET", "user", "password"), 872 userExists: true, 873 }, 874 { 875 // valid user, bad pass 876 username: "user", 877 req: mustAuthRequest("GET", "user", "badpass"), 878 userExists: false, 879 }, 880 { 881 // valid user, no pass 882 username: "user", 883 req: mustAuthRequest("GET", "user", ""), 884 userExists: false, 885 }, 886 { 887 // missing user 888 username: "missing", 889 req: mustAuthRequest("GET", "missing", "badpass"), 890 userExists: false, 891 }, 892 { 893 // no basic auth 894 req: unauthedRequest("GET"), 895 userExists: false, 896 }, 897 } 898 899 for i, tt := range table { 900 user := userFromBasicAuth(sec, tt.req) 901 userExists := user != nil 902 903 if tt.userExists != userExists { 904 t.Errorf("#%d: userFromBasicAuth doesn't match (expected %v)", i, tt.userExists) 905 } 906 if user != nil && (tt.username != user.User) { 907 t.Errorf("#%d: userFromBasicAuth username doesn't match (expected %s, got %s)", i, tt.username, user.User) 908 } 909 } 910 }