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