github.com/decred/politeia@v1.4.0/politeiawww/legacy/user_test.go (about) 1 // Copyright (c) 2017-2020 The Decred developers 2 // Use of this source code is governed by an ISC 3 // license that can be found in the LICENSE file. 4 5 package legacy 6 7 import ( 8 "encoding/hex" 9 "errors" 10 "sort" 11 "testing" 12 "time" 13 14 "github.com/davecgh/go-spew/spew" 15 "github.com/decred/politeia/politeiad/api/v1/identity" 16 www "github.com/decred/politeia/politeiawww/api/www/v1" 17 "github.com/decred/politeia/politeiawww/legacy/user" 18 "github.com/decred/politeia/util" 19 "github.com/go-test/deep" 20 "github.com/pquerna/otp/totp" 21 "golang.org/x/crypto/bcrypt" 22 ) 23 24 func TestValidatePubkey(t *testing.T) { 25 id, err := identity.New() 26 if err != nil { 27 t.Fatalf("%v", err) 28 } 29 30 // Valid public key 31 valid := id.Public.String() 32 33 // Invalid hex string. The last character is an 'x'. 34 invalidHex := "62920bbb25dd552c7367677be0abe19f4c11394c82ce4096eabf83469439901x" 35 36 // The private key is 64 bytes so we can use it to test 37 // the invalid size error path. The expected size of the 38 // public key is 32 bytes. 39 invalidSize := hex.EncodeToString(id.PrivateKey[:]) 40 41 // Valid size public key that is all zeros 42 var zeros [identity.PublicKeySize]byte 43 empty := hex.EncodeToString(zeros[:]) 44 45 // Setup tests 46 var tests = []struct { 47 name string 48 pubkey string 49 want error 50 }{ 51 { 52 "valid pubkey", 53 valid, 54 nil, 55 }, 56 { 57 "invalid hexadecimal", 58 invalidHex, 59 www.UserError{ 60 ErrorCode: www.ErrorStatusInvalidPublicKey, 61 }, 62 }, 63 { 64 "invalid size", 65 invalidSize, 66 www.UserError{ 67 ErrorCode: www.ErrorStatusInvalidPublicKey, 68 }, 69 }, 70 { 71 "empty pubkey", 72 empty, 73 www.UserError{ 74 ErrorCode: www.ErrorStatusInvalidPublicKey, 75 }, 76 }, 77 } 78 79 // Run tests 80 for _, v := range tests { 81 t.Run(v.name, func(t *testing.T) { 82 err := validatePubKey(v.pubkey) 83 got := errToStr(err) 84 want := errToStr(v.want) 85 if got != want { 86 t.Errorf("got error %v, want %v", got, want) 87 } 88 }) 89 } 90 } 91 92 func TestValidateUsername(t *testing.T) { 93 // Username under the min length requirement 94 var underMin string 95 for i := 0; i < www.PolicyMinUsernameLength-1; i++ { 96 underMin += "0" 97 } 98 99 // Username over the max length requirement 100 var overMax string 101 for i := 0; i < www.PolicyMaxUsernameLength+1; i++ { 102 overMax += "0" 103 } 104 105 // Setup tests 106 var tests = []struct { 107 name string 108 username string 109 want error 110 }{ 111 { 112 "contains uppercase", 113 "Politeiauser", 114 www.UserError{ 115 ErrorCode: www.ErrorStatusMalformedUsername, 116 }, 117 }, 118 { 119 "leading whitespace", 120 " politeiauser", 121 www.UserError{ 122 ErrorCode: www.ErrorStatusMalformedUsername, 123 }, 124 }, 125 { 126 "trailing whitespace", 127 "politeiauser ", 128 www.UserError{ 129 ErrorCode: www.ErrorStatusMalformedUsername, 130 }, 131 }, 132 { 133 "empty", 134 "", 135 www.UserError{ 136 ErrorCode: www.ErrorStatusMalformedUsername, 137 }, 138 }, 139 { 140 "under min length", 141 underMin, 142 www.UserError{ 143 ErrorCode: www.ErrorStatusMalformedUsername, 144 }, 145 }, 146 { 147 "over max length", 148 overMax, 149 www.UserError{ 150 ErrorCode: www.ErrorStatusMalformedUsername, 151 }, 152 }, 153 { 154 "unsupported character", 155 "politeiauser?", 156 www.UserError{ 157 ErrorCode: www.ErrorStatusMalformedUsername, 158 }, 159 }, 160 { 161 "contains whitespace", 162 "politeia user", 163 www.UserError{ 164 ErrorCode: www.ErrorStatusMalformedUsername, 165 }, 166 }, 167 { 168 "valid username", 169 "politeiauser", 170 nil, 171 }, 172 } 173 174 // Run tests 175 for _, v := range tests { 176 t.Run(v.name, func(t *testing.T) { 177 err := validateUsername(v.username) 178 got := errToStr(err) 179 want := errToStr(v.want) 180 if got != want { 181 t.Errorf("got error %v, want %v", got, want) 182 } 183 }) 184 } 185 } 186 187 func TestValidatePassword(t *testing.T) { 188 // Password under the min length requirement 189 var minPass string 190 for i := 0; i < www.PolicyMinPasswordLength-1; i++ { 191 minPass += "0" 192 } 193 194 // Setup tests 195 var tests = []struct { 196 name string 197 password string 198 want error 199 }{ 200 {"under min length", minPass, 201 www.UserError{ 202 ErrorCode: www.ErrorStatusMalformedPassword, 203 }}, 204 } 205 206 // Run tests 207 for _, v := range tests { 208 t.Run(v.name, func(t *testing.T) { 209 err := validatePassword(v.password) 210 got := errToStr(err) 211 want := errToStr(v.want) 212 if got != want { 213 t.Errorf("got error %v, want %v", 214 got, want) 215 } 216 }) 217 } 218 } 219 220 func TestProcessNewUser(t *testing.T) { 221 p, cleanup := newTestPoliteiawww(t) 222 defer cleanup() 223 224 // Create a verified user 225 usrVerified, _ := newUser(t, p, true, false) 226 227 // Create an unverified user with an unexpired 228 // verification token. 229 usrUnexpired, id := newUser(t, p, false, false) 230 usrUnexpiredPublicKey := id.Public.String() 231 232 // Create two unverified users with expired verification tokens. 233 // The verification tokens are expired manually. A user with an 234 // expired verification token is allowed to use a new pubkey if 235 // they want to update their identity. 236 // 237 // usrExpiredSame will use the same pubkey that is saved to the db. 238 // usrExpiredDiff will use a different pubkey to test the identity 239 // update path. 240 usrExpiredSame, id := newUser(t, p, false, false) 241 usrExpiredSame.NewUserVerificationExpiry = time.Now().Unix() - 1 242 err := p.db.UserUpdate(*usrExpiredSame) 243 if err != nil { 244 t.Fatal(err) 245 } 246 usrExpiredSamePublicKey := id.Public.String() 247 248 usrExpiredDiff, _ := newUser(t, p, false, false) 249 usrExpiredDiff.NewUserVerificationExpiry = time.Now().Unix() - 1 250 err = p.db.UserUpdate(*usrExpiredDiff) 251 if err != nil { 252 t.Fatal(err) 253 } 254 id, err = identity.New() 255 if err != nil { 256 t.Fatal(err) 257 } 258 usrExpiredDiffPublicKey := id.Public.String() 259 260 // Create valid user credentials to use in the tests. 261 id, err = identity.New() 262 if err != nil { 263 t.Fatalf("%v", err) 264 } 265 r, err := util.Random(int(www.PolicyMinPasswordLength)) 266 if err != nil { 267 t.Fatalf("%v", err) 268 } 269 validEmail := hex.EncodeToString(r) + "@example.com" 270 validUsername := hex.EncodeToString(r) 271 validPassword := hex.EncodeToString(r) 272 validPublicKey := id.Public.String() 273 274 // Setup tests 275 var tests = []struct { 276 name string 277 newUser www.NewUser 278 want error 279 }{ 280 { 281 "invalid email", 282 www.NewUser{ 283 Email: "", 284 Password: validPassword, 285 PublicKey: validPublicKey, 286 Username: validUsername, 287 }, 288 www.UserError{ 289 ErrorCode: www.ErrorStatusMalformedEmail, 290 }, 291 }, 292 { 293 "invalid pubkey", 294 www.NewUser{ 295 Email: validEmail, 296 Password: validPassword, 297 PublicKey: "", 298 Username: validUsername, 299 }, 300 www.UserError{ 301 ErrorCode: www.ErrorStatusInvalidPublicKey, 302 }, 303 }, 304 { 305 "invalid username", 306 www.NewUser{ 307 Email: validEmail, 308 Password: validPassword, 309 PublicKey: validPublicKey, 310 Username: "", 311 }, 312 www.UserError{ 313 ErrorCode: www.ErrorStatusMalformedUsername, 314 }, 315 }, 316 { 317 "invalid password", 318 www.NewUser{ 319 Email: validEmail, 320 Password: "", 321 PublicKey: validPublicKey, 322 Username: validUsername, 323 }, 324 www.UserError{ 325 ErrorCode: www.ErrorStatusMalformedPassword, 326 }, 327 }, 328 { 329 "user already verified", 330 www.NewUser{ 331 Email: usrVerified.Email, 332 Password: validPassword, 333 PublicKey: usrVerified.PublicKey(), 334 Username: usrVerified.Username, 335 }, 336 nil, 337 }, 338 { 339 "unverified user unexpired token", 340 www.NewUser{ 341 Email: usrUnexpired.Email, 342 Password: validPassword, 343 PublicKey: usrUnexpiredPublicKey, 344 Username: usrUnexpired.Username, 345 }, 346 nil, 347 }, 348 { 349 "unverified user expired token same pubkey", 350 www.NewUser{ 351 Email: usrExpiredSame.Email, 352 Password: validPassword, 353 PublicKey: usrExpiredSamePublicKey, 354 Username: usrExpiredSame.Username, 355 }, 356 nil, 357 }, 358 { 359 "unverified user expired token different pubkey", 360 www.NewUser{ 361 Email: usrExpiredDiff.Email, 362 Password: validPassword, 363 PublicKey: usrExpiredDiffPublicKey, 364 Username: usrExpiredDiff.Username, 365 }, 366 nil, 367 }, 368 { 369 "duplicate username", 370 www.NewUser{ 371 Email: validEmail, 372 Password: validPassword, 373 PublicKey: validPublicKey, 374 Username: usrVerified.Username, 375 }, 376 www.UserError{ 377 ErrorCode: www.ErrorStatusDuplicateUsername, 378 }, 379 }, 380 { 381 "duplicate pubkey", 382 www.NewUser{ 383 Email: validEmail, 384 Password: validPassword, 385 PublicKey: usrVerified.PublicKey(), 386 Username: validUsername, 387 }, 388 www.UserError{ 389 ErrorCode: www.ErrorStatusDuplicatePublicKey, 390 }, 391 }, 392 { 393 "valid new user", 394 www.NewUser{ 395 Email: validEmail, 396 Password: validPassword, 397 PublicKey: validPublicKey, 398 Username: validUsername, 399 }, 400 nil, 401 }, 402 } 403 404 // Run tests 405 for _, v := range tests { 406 t.Run(v.name, func(t *testing.T) { 407 _, err := p.processNewUser(v.newUser) 408 got := errToStr(err) 409 want := errToStr(v.want) 410 if got != want { 411 t.Errorf("got error %v, want %v", 412 got, want) 413 } 414 }) 415 } 416 } 417 418 func TestProcessVerifyNewUser(t *testing.T) { 419 p, cleanup := newTestPoliteiawww(t) 420 defer cleanup() 421 422 // Create a user with a valid, unexpired verification 423 // token. 424 usr, id := newUser(t, p, false, false) 425 token := hex.EncodeToString(usr.NewUserVerificationToken) 426 s := id.SignMessage([]byte(token)) 427 sig := hex.EncodeToString(s[:]) 428 429 s = id.SignMessage([]byte("intentionally wrong")) 430 wrongSig := hex.EncodeToString(s[:]) 431 432 // Create a token that does not correspond to a user 433 b, _, err := newVerificationTokenAndExpiry() 434 if err != nil { 435 t.Fatalf("%v", err) 436 } 437 wrongToken := hex.EncodeToString(b) 438 439 // Create a user with an expired verification token 440 expiredUsr, id := newUser(t, p, true, false) 441 expiredUsr.NewUserVerificationExpiry = time.Now().Unix() - 1 442 err = p.db.UserUpdate(*expiredUsr) 443 if err != nil { 444 t.Fatalf("%v", err) 445 } 446 expiredToken := hex.EncodeToString(expiredUsr.NewUserVerificationToken) 447 s = id.SignMessage([]byte(expiredToken)) 448 expiredSig := hex.EncodeToString(s[:]) 449 450 // Setup tests 451 var tests = []struct { 452 name string 453 input www.VerifyNewUser 454 want error 455 }{ 456 // An invalid token error is thrown when the user lookup 457 // fails so that info about which email addresses exist 458 // cannot be ascertained. 459 { 460 "user not found", 461 www.VerifyNewUser{ 462 Email: "invalidemail", 463 VerificationToken: token, 464 Signature: sig, 465 }, 466 www.UserError{ 467 ErrorCode: www.ErrorStatusVerificationTokenInvalid, 468 }, 469 }, 470 { 471 "invalid verification token", 472 www.VerifyNewUser{ 473 Email: usr.Email, 474 VerificationToken: "zzz", 475 Signature: sig, 476 }, 477 www.UserError{ 478 ErrorCode: www.ErrorStatusVerificationTokenInvalid, 479 }, 480 }, 481 { 482 "wrong verification token", 483 www.VerifyNewUser{ 484 Email: usr.Email, 485 VerificationToken: wrongToken, 486 Signature: sig, 487 }, 488 www.UserError{ 489 ErrorCode: www.ErrorStatusVerificationTokenInvalid, 490 }, 491 }, 492 { 493 "expired verification token", 494 www.VerifyNewUser{ 495 Email: expiredUsr.Email, 496 VerificationToken: expiredToken, 497 Signature: expiredSig, 498 }, 499 www.UserError{ 500 ErrorCode: www.ErrorStatusVerificationTokenExpired, 501 }, 502 }, 503 { 504 "invalid signature", 505 www.VerifyNewUser{ 506 Email: usr.Email, 507 VerificationToken: token, 508 Signature: "abc", 509 }, 510 www.UserError{ 511 ErrorCode: www.ErrorStatusInvalidSignature, 512 }, 513 }, 514 515 // I didn't test the ErrorStatusNoPublicKey error path because 516 // I don't think it is possible for that error path to be hit. 517 // A user always has an active identity. 518 519 { 520 "wrong signature", 521 www.VerifyNewUser{ 522 Email: usr.Email, 523 VerificationToken: token, 524 Signature: wrongSig, 525 }, 526 www.UserError{ 527 ErrorCode: www.ErrorStatusInvalidSignature, 528 }, 529 }, 530 { 531 "success", 532 www.VerifyNewUser{ 533 Email: usr.Email, 534 VerificationToken: token, 535 Signature: sig, 536 }, 537 nil, 538 }, 539 } 540 541 // Run tests 542 for _, v := range tests { 543 t.Run(v.name, func(t *testing.T) { 544 _, err := p.processVerifyNewUser(v.input) 545 got := errToStr(err) 546 want := errToStr(v.want) 547 if got != want { 548 t.Errorf("got error %v, want %v", got, want) 549 } 550 }) 551 } 552 } 553 554 func TestProcessResendVerification(t *testing.T) { 555 p, cleanup := newTestPoliteiawww(t) 556 defer cleanup() 557 558 // Create a verified user 559 usrVerified, id := newUser(t, p, true, false) 560 usrVerifiedPubkey := id.Public.String() 561 562 // Create two unverified users 563 usr1, id := newUser(t, p, false, false) 564 usr1Pubkey := id.Public.String() 565 usr2, _ := newUser(t, p, false, false) 566 567 // A user is allowed to pass in a different pubkey 568 // than is currently saved in the database. We give 569 // usr2 a new pubkey to test this. 570 id, err := identity.New() 571 if err != nil { 572 t.Fatalf("%v", err) 573 } 574 usr2Pubkey := id.Public.String() 575 576 // Setup tests 577 var tests = []struct { 578 name string 579 rv www.ResendVerification 580 want error 581 }{ 582 { 583 "user not found", 584 www.ResendVerification{ 585 Email: "someuser@example.com", 586 PublicKey: usrVerifiedPubkey, 587 }, 588 www.UserError{ 589 ErrorCode: www.ErrorStatusUserNotFound, 590 }, 591 }, 592 { 593 "user already verified", 594 www.ResendVerification{ 595 Email: usrVerified.Email, 596 PublicKey: usrVerifiedPubkey, 597 }, 598 www.UserError{ 599 ErrorCode: www.ErrorStatusEmailAlreadyVerified, 600 }, 601 }, 602 { 603 "invalid pubkey", 604 www.ResendVerification{ 605 Email: usr1.Email, 606 PublicKey: "", 607 }, 608 www.UserError{ 609 ErrorCode: www.ErrorStatusInvalidPublicKey, 610 }, 611 }, 612 { 613 "duplicate pubkey", 614 www.ResendVerification{ 615 Email: usr1.Email, 616 PublicKey: usrVerifiedPubkey, 617 }, 618 www.UserError{ 619 ErrorCode: www.ErrorStatusDuplicatePublicKey, 620 }, 621 }, 622 // If the user has an unexpired token, they are allowed 623 // to resend the verification email one time. The second 624 // attempt should fail. 625 { 626 "success", 627 www.ResendVerification{ 628 Email: usr1.Email, 629 PublicKey: usr1Pubkey, 630 }, 631 nil, 632 }, 633 { 634 "unexpired token", 635 www.ResendVerification{ 636 Email: usr1.Email, 637 PublicKey: usr1Pubkey, 638 }, 639 www.UserError{ 640 ErrorCode: www.ErrorStatusVerificationTokenUnexpired, 641 }, 642 }, 643 // The user does not have to pass in the same pubkey that 644 // is currently saved in the database. If they do use a 645 // different pubkey, their active identity in the database 646 // is updated to reflect the new pubkey. 647 { 648 "success with new pubkey", 649 www.ResendVerification{ 650 Email: usr2.Email, 651 PublicKey: usr2Pubkey, 652 }, 653 nil, 654 }, 655 } 656 657 // Run tests 658 for _, v := range tests { 659 t.Run(v.name, func(t *testing.T) { 660 _, err := p.processResendVerification(&v.rv) 661 got := errToStr(err) 662 want := errToStr(v.want) 663 if got != want { 664 t.Errorf("got error %v, want %v", 665 got, want) 666 } 667 }) 668 } 669 } 670 671 func TestProcessUpdateUserKey(t *testing.T) { 672 673 p, cleanup := newTestPoliteiawww(t) 674 defer cleanup() 675 676 // Create an user with an unexpired verification token 677 usrUnexpired, id := newUser(t, p, true, false) 678 token, expiry, err := newVerificationTokenAndExpiry() 679 if err != nil { 680 t.Fatal(err) 681 } 682 usrUnexpired.UpdateKeyVerificationToken = token 683 usrUnexpired.UpdateKeyVerificationExpiry = expiry 684 duppk := id.Public.String() 685 686 err = p.db.UserUpdate(*usrUnexpired) 687 if err != nil { 688 t.Fatal(err) 689 } 690 691 // Create a user with an expired verification token. 692 usr, _ := newUser(t, p, true, false) 693 tokenb, _, err := newVerificationTokenAndExpiry() 694 if err != nil { 695 t.Fatal(err) 696 } 697 usr.UpdateKeyVerificationToken = tokenb 698 usr.UpdateKeyVerificationExpiry = time.Now().Unix() - 1 699 err = p.db.UserUpdate(*usr) 700 if err != nil { 701 t.Fatal(err) 702 } 703 704 newid, _ := identity.New() 705 newpk := newid.Public.String() 706 707 var tests = []struct { 708 name string 709 usr *user.User 710 uuk www.UpdateUserKey 711 want error 712 }{ 713 { 714 "user with duplicate pubkey", 715 usrUnexpired, 716 www.UpdateUserKey{ 717 PublicKey: duppk, 718 }, 719 www.UserError{ 720 ErrorCode: www.ErrorStatusDuplicatePublicKey, 721 }, 722 }, 723 { 724 "user with expired verification token", 725 usrUnexpired, 726 www.UpdateUserKey{ 727 PublicKey: newpk, 728 }, 729 www.UserError{ 730 ErrorCode: www.ErrorStatusVerificationTokenUnexpired, 731 }, 732 }, 733 { 734 "success with new pubkey", 735 usr, 736 www.UpdateUserKey{ 737 PublicKey: newpk, 738 }, 739 nil, 740 }, 741 } 742 // Run tests 743 for _, v := range tests { 744 t.Run(v.name, func(t *testing.T) { 745 _, err := p.processUpdateUserKey(v.usr, v.uuk) 746 got := errToStr(err) 747 want := errToStr(v.want) 748 if got != want { 749 t.Errorf("got error %v, want %v", 750 got, want) 751 } 752 }) 753 } 754 } 755 756 func TestProcessVerifyUpdateUserKey(t *testing.T) { 757 p, cleanup := newTestPoliteiawww(t) 758 defer cleanup() 759 760 // Create an user with an unexpired verification token 761 usr, id := newUser(t, p, true, false) 762 token, expiry, err := newVerificationTokenAndExpiry() 763 if err != nil { 764 t.Fatal(err) 765 } 766 usr.UpdateKeyVerificationToken = token 767 usr.UpdateKeyVerificationExpiry = expiry 768 769 pubkey := hex.EncodeToString(id.Public.Key[:]) 770 newid, _ := user.NewIdentity(pubkey) 771 usr.Identities = []user.Identity{ 772 *newid, 773 } 774 775 usrToken := hex.EncodeToString(token) 776 777 s := id.SignMessage([]byte(usrToken)) 778 sig := hex.EncodeToString(s[:]) 779 780 err = p.db.UserUpdate(*usr) 781 if err != nil { 782 t.Fatal(err) 783 } 784 785 // Create a user with an expired verification token. 786 usrExpired, idex := newUser(t, p, true, false) 787 tokenb, _, err := newVerificationTokenAndExpiry() 788 usrExpiredToken := hex.EncodeToString(tokenb) 789 sxp := idex.SignMessage([]byte(usrExpiredToken)) 790 sigxp := hex.EncodeToString(sxp[:]) 791 792 if err != nil { 793 t.Fatal(err) 794 } 795 usrExpired.UpdateKeyVerificationToken = tokenb 796 usrExpired.UpdateKeyVerificationExpiry = time.Now().Unix() - 1 797 err = p.db.UserUpdate(*usrExpired) 798 if err != nil { 799 t.Fatal(err) 800 } 801 802 var tests = []struct { 803 name string 804 usr *user.User 805 vu www.VerifyUpdateUserKey 806 want error 807 }{ 808 { 809 "invalid verification token", 810 usr, 811 www.VerifyUpdateUserKey{ 812 VerificationToken: "", 813 Signature: sig, 814 }, 815 www.UserError{ 816 ErrorCode: www.ErrorStatusVerificationTokenInvalid, 817 }, 818 }, 819 { 820 "unmatching verification tokens", 821 usr, 822 www.VerifyUpdateUserKey{ 823 VerificationToken: usrExpiredToken, 824 Signature: sigxp, 825 }, 826 www.UserError{ 827 ErrorCode: www.ErrorStatusVerificationTokenInvalid, 828 }, 829 }, 830 { 831 "expired verification token", 832 usrExpired, 833 www.VerifyUpdateUserKey{ 834 VerificationToken: usrExpiredToken, 835 Signature: sigxp, 836 }, 837 www.UserError{ 838 ErrorCode: www.ErrorStatusVerificationTokenExpired, 839 }, 840 }, 841 { 842 "invalid signature", 843 usr, 844 www.VerifyUpdateUserKey{ 845 VerificationToken: usrToken, 846 Signature: "", 847 }, 848 www.UserError{ 849 ErrorCode: www.ErrorStatusInvalidSignature, 850 }, 851 }, 852 { 853 "signature not matching pubkey", 854 usr, 855 www.VerifyUpdateUserKey{ 856 VerificationToken: usrToken, 857 Signature: sigxp, 858 }, 859 www.UserError{ 860 ErrorCode: www.ErrorStatusInvalidSignature, 861 }, 862 }, 863 { 864 "verify update user key", 865 usr, 866 www.VerifyUpdateUserKey{ 867 VerificationToken: usrToken, 868 Signature: sig, 869 }, 870 nil, 871 }, 872 } 873 874 for _, v := range tests { 875 t.Run(v.name, func(t *testing.T) { 876 _, err := p.processVerifyUpdateUserKey(v.usr, v.vu) 877 got := errToStr(err) 878 want := errToStr(v.want) 879 if got != want { 880 t.Errorf("got error %v, want %v", 881 got, want) 882 } 883 }) 884 } 885 } 886 887 func TestLogin(t *testing.T) { 888 p, cleanup := newTestPoliteiawww(t) 889 defer cleanup() 890 891 // newUser() sets the password to be the username, which is 892 // why we keep setting the passwords to be the the usernames. 893 894 // Create an unverified user 895 usrUnverified, _ := newUser(t, p, false, false) 896 usrUnverifiedPassword := usrUnverified.Username 897 898 // Create a user and lock their account from failed login 899 // attempts. 900 usrLocked, _ := newUser(t, p, true, false) 901 usrLocked.FailedLoginAttempts = LoginAttemptsToLockUser + 1 902 err := p.db.UserUpdate(*usrLocked) 903 if err != nil { 904 t.Fatal(err) 905 } 906 usrLockedPassword := usrLocked.Username 907 908 // Create a deactivated user 909 usrDeactivated, _ := newUser(t, p, true, false) 910 usrDeactivated.Deactivated = true 911 err = p.db.UserUpdate(*usrDeactivated) 912 if err != nil { 913 t.Fatal(err) 914 } 915 usrDeactivatedPassword := usrDeactivated.Username 916 917 // Create a verified user and the expected login reply 918 // for the success case. 919 usr, id := newUser(t, p, true, false) 920 usrPassword := usr.Username 921 successReply := www.LoginReply{ 922 IsAdmin: false, 923 UserID: usr.ID.String(), 924 Email: usr.Email, 925 Username: usr.Username, 926 PublicKey: id.Public.String(), 927 PaywallAddress: usr.NewUserPaywallAddress, 928 PaywallAmount: usr.NewUserPaywallAmount, 929 PaywallTxNotBefore: usr.NewUserPaywallTxNotBefore, 930 PaywallTxID: "", 931 ProposalCredits: 0, 932 LastLoginTime: 0, 933 } 934 935 // Setup tests 936 var tests = []struct { 937 name string 938 login www.Login 939 wantReply *www.LoginReply 940 wantError error 941 }{ 942 { 943 "wrong email", 944 www.Login{ 945 Email: "", 946 Password: usrPassword, 947 }, 948 nil, 949 www.UserError{ 950 ErrorCode: www.ErrorStatusInvalidLogin, 951 }, 952 }, 953 { 954 "wrong password", 955 www.Login{ 956 Email: usr.Email, 957 Password: "", 958 }, 959 nil, 960 www.UserError{ 961 ErrorCode: www.ErrorStatusInvalidLogin, 962 }, 963 }, 964 { 965 "user not verified", 966 www.Login{ 967 Email: usrUnverified.Email, 968 Password: usrUnverifiedPassword, 969 }, 970 nil, 971 www.UserError{ 972 ErrorCode: www.ErrorStatusEmailNotVerified, 973 }, 974 }, 975 { 976 "user deactivated", 977 www.Login{ 978 Email: usrDeactivated.Email, 979 Password: usrDeactivatedPassword, 980 }, 981 nil, 982 www.UserError{ 983 ErrorCode: www.ErrorStatusUserDeactivated, 984 }, 985 }, 986 { 987 "user account locked", 988 www.Login{ 989 Email: usrLocked.Email, 990 Password: usrLockedPassword, 991 }, 992 nil, 993 www.UserError{ 994 ErrorCode: www.ErrorStatusUserLocked, 995 }, 996 }, 997 { 998 "success", 999 www.Login{ 1000 Email: usr.Email, 1001 Password: usrPassword, 1002 }, 1003 &successReply, 1004 nil, 1005 }, 1006 } 1007 1008 // Run tests 1009 for _, v := range tests { 1010 t.Run(v.name, func(t *testing.T) { 1011 lr := p.login(v.login) 1012 gotErr := errToStr(lr.err) 1013 wantErr := errToStr(v.wantError) 1014 if gotErr != wantErr { 1015 t.Errorf("got error %v, want %v", 1016 gotErr, wantErr) 1017 } 1018 1019 // If there were errors then we're done 1020 if err != nil { 1021 return 1022 } 1023 1024 // Verify reply 1025 diff := deep.Equal(lr.reply, v.wantReply) 1026 if diff != nil { 1027 t.Errorf("got/want diff:\n%v", 1028 spew.Sdump(diff)) 1029 } 1030 }) 1031 } 1032 1033 // Create TOTP Verified user 1034 usrTOTPVerified, idTOTP := newUser(t, p, true, false) 1035 1036 opts := p.totpGenerateOpts(defaultPoliteiaIssuer, usrTOTPVerified.Username) 1037 key, err := totp.Generate(opts) 1038 if err != nil { 1039 t.Errorf("unable to generate secret key %v", err) 1040 } 1041 1042 usrTOTPVerified.TOTPType = int(www.TOTPTypeBasic) 1043 usrTOTPVerified.TOTPSecret = key.Secret() 1044 usrTOTPVerified.TOTPLastUpdated = append(usrTOTPVerified.TOTPLastUpdated, 1045 time.Now().Unix()) 1046 usrTOTPVerified.TOTPVerified = true 1047 err = p.db.UserUpdate(*usrTOTPVerified) 1048 if err != nil { 1049 t.Errorf("unable to update totp verified user %v", err) 1050 } 1051 1052 usrTOTPVerifiedPassword := usrTOTPVerified.Username 1053 1054 // Successful TOTP user reply 1055 successTOTPReply := www.LoginReply{ 1056 IsAdmin: false, 1057 UserID: usrTOTPVerified.ID.String(), 1058 Email: usrTOTPVerified.Email, 1059 Username: usrTOTPVerified.Username, 1060 PublicKey: idTOTP.Public.String(), 1061 PaywallAddress: usrTOTPVerified.NewUserPaywallAddress, 1062 PaywallAmount: usrTOTPVerified.NewUserPaywallAmount, 1063 PaywallTxNotBefore: usrTOTPVerified.NewUserPaywallTxNotBefore, 1064 PaywallTxID: "", 1065 ProposalCredits: 0, 1066 LastLoginTime: 0, 1067 TOTPVerified: true, 1068 } 1069 requestTime := time.Now() 1070 code, err := p.totpGenerateCode(key.Secret(), requestTime) 1071 if err != nil { 1072 t.Errorf("unable to generate code %v", err) 1073 } 1074 1075 // Setup tests 1076 var testsTOTPVerified = []struct { 1077 name string 1078 login www.Login 1079 wantReply *www.LoginReply 1080 wantError error 1081 }{ 1082 { 1083 "totp verified no code", 1084 www.Login{ 1085 Email: usrTOTPVerified.Email, 1086 Password: usrTOTPVerifiedPassword, 1087 Code: "", 1088 }, 1089 nil, 1090 www.UserError{ 1091 ErrorCode: www.ErrorStatusRequiresTOTPCode, 1092 }, 1093 }, 1094 { 1095 "totp verified wrong code", 1096 www.Login{ 1097 Email: usrTOTPVerified.Email, 1098 Password: usrTOTPVerifiedPassword, 1099 Code: "12345", 1100 }, 1101 nil, 1102 www.UserError{ 1103 ErrorCode: www.ErrorStatusTOTPFailedValidation, 1104 }, 1105 }, 1106 { 1107 "success totp verified", 1108 www.Login{ 1109 Email: usrTOTPVerified.Email, 1110 Password: usrTOTPVerifiedPassword, 1111 Code: code, 1112 }, 1113 &successTOTPReply, 1114 nil, 1115 }, 1116 } 1117 1118 // Run verified TOTP tests separate since they are time dependant. 1119 for _, v := range testsTOTPVerified { 1120 t.Run(v.name, func(t *testing.T) { 1121 lr := p.login(v.login) 1122 gotErr := errToStr(lr.err) 1123 wantErr := errToStr(v.wantError) 1124 if gotErr != wantErr { 1125 t.Logf("failed %v %v", requestTime, code) 1126 t.Errorf("got error %v, want %v", 1127 gotErr, wantErr) 1128 } 1129 1130 // If there were errors then we're done 1131 if err != nil { 1132 return 1133 } 1134 1135 // Verify reply 1136 diff := deep.Equal(lr.reply, v.wantReply) 1137 if diff != nil { 1138 t.Errorf("got/want diff:\n%v", 1139 spew.Sdump(diff)) 1140 } 1141 }) 1142 } 1143 1144 // Create TOTP Verified Timed out user 1145 usrTOTPVerifiedTimeout, idTOTPTimeout := newUser(t, p, true, false) 1146 1147 opts = p.totpGenerateOpts(defaultPoliteiaIssuer, 1148 usrTOTPVerifiedTimeout.Username) 1149 key, err = totp.Generate(opts) 1150 if err != nil { 1151 t.Errorf("unable to generate secret key %v", err) 1152 } 1153 1154 // Add a delay based on the set totp test period. This will allow for 1155 // testing weather or not only 2 failed totp attempts in a short period 1156 // of time (as opposed to the default 60s). 1157 futureCodeDelay := totpTestPeriod * time.Second 1158 futureCode, err := p.totpGenerateCode(key.Secret(), 1159 time.Now().Add(futureCodeDelay)) 1160 if err != nil { 1161 t.Errorf("unable to generate future code %v", err) 1162 } 1163 1164 usrTOTPVerifiedTimeout.TOTPType = int(www.TOTPTypeBasic) 1165 usrTOTPVerifiedTimeout.TOTPSecret = key.Secret() 1166 usrTOTPVerifiedTimeout.TOTPLastUpdated = 1167 append(usrTOTPVerified.TOTPLastUpdated, time.Now().Unix()) 1168 usrTOTPVerifiedTimeout.TOTPVerified = true 1169 err = p.db.UserUpdate(*usrTOTPVerifiedTimeout) 1170 if err != nil { 1171 t.Errorf("unable to update totp verified user %v", err) 1172 } 1173 1174 usrTOTPVerifiedTimeoutPassword := usrTOTPVerifiedTimeout.Username 1175 1176 // Successful TOTP user reply 1177 successTOTPTimeoutReply := www.LoginReply{ 1178 IsAdmin: false, 1179 UserID: usrTOTPVerifiedTimeout.ID.String(), 1180 Email: usrTOTPVerifiedTimeout.Email, 1181 Username: usrTOTPVerifiedTimeout.Username, 1182 PublicKey: idTOTPTimeout.Public.String(), 1183 PaywallAddress: usrTOTPVerifiedTimeout.NewUserPaywallAddress, 1184 PaywallAmount: usrTOTPVerifiedTimeout.NewUserPaywallAmount, 1185 PaywallTxNotBefore: usrTOTPVerifiedTimeout.NewUserPaywallTxNotBefore, 1186 PaywallTxID: "", 1187 ProposalCredits: 0, 1188 LastLoginTime: 0, 1189 TOTPVerified: true, 1190 } 1191 1192 // Setup tests 1193 var testsTOTPVerifiedTimeout = []struct { 1194 name string 1195 login www.Login 1196 wantReply *www.LoginReply 1197 wantError error 1198 }{ 1199 { 1200 "totp verified timeout first incorrect", 1201 www.Login{ 1202 Email: usrTOTPVerifiedTimeout.Email, 1203 Password: usrTOTPVerifiedTimeoutPassword, 1204 Code: "12345", 1205 }, 1206 nil, 1207 www.UserError{ 1208 ErrorCode: www.ErrorStatusTOTPFailedValidation, 1209 }, 1210 }, 1211 { 1212 "totp verified timeout second incorrect", 1213 www.Login{ 1214 Email: usrTOTPVerifiedTimeout.Email, 1215 Password: usrTOTPVerifiedTimeoutPassword, 1216 Code: "12345", 1217 }, 1218 nil, 1219 www.UserError{ 1220 ErrorCode: www.ErrorStatusTOTPFailedValidation, 1221 }, 1222 }, 1223 { 1224 "totp verified timeout third incorrect timeout", 1225 www.Login{ 1226 Email: usrTOTPVerifiedTimeout.Email, 1227 Password: usrTOTPVerifiedTimeoutPassword, 1228 Code: "12345", 1229 }, 1230 nil, 1231 www.UserError{ 1232 ErrorCode: www.ErrorStatusTOTPWaitForNewCode, 1233 }, 1234 }, 1235 { 1236 "error after timeout", 1237 www.Login{ 1238 Email: usrTOTPVerifiedTimeout.Email, 1239 Password: usrTOTPVerifiedTimeoutPassword, 1240 Code: "12345", 1241 }, 1242 nil, 1243 www.UserError{ 1244 ErrorCode: www.ErrorStatusTOTPFailedValidation, 1245 }, 1246 }, 1247 { 1248 "success after timeout", 1249 www.Login{ 1250 Email: usrTOTPVerifiedTimeout.Email, 1251 Password: usrTOTPVerifiedTimeoutPassword, 1252 Code: futureCode, 1253 }, 1254 &successTOTPTimeoutReply, 1255 nil, 1256 }, 1257 } 1258 // Run verified TOTP timeout tests separate since they are time dependant. 1259 for _, v := range testsTOTPVerifiedTimeout { 1260 t.Run(v.name, func(t *testing.T) { 1261 if v.name == "error after timeout" { 1262 time.Sleep(futureCodeDelay) 1263 } 1264 lr := p.login(v.login) 1265 gotErr := errToStr(lr.err) 1266 wantErr := errToStr(v.wantError) 1267 if gotErr != wantErr { 1268 t.Errorf("got error %v, want %v", 1269 gotErr, wantErr) 1270 } 1271 1272 // If there were errors then we're done 1273 if err != nil { 1274 return 1275 } 1276 1277 // Verify reply 1278 diff := deep.Equal(lr.reply, v.wantReply) 1279 if diff != nil { 1280 t.Errorf("got/want diff:\n%v", 1281 spew.Sdump(diff)) 1282 } 1283 }) 1284 } 1285 } 1286 1287 func TestProcessLogin(t *testing.T) { 1288 p, cleanup := newTestPoliteiawww(t) 1289 defer cleanup() 1290 1291 // loginMinWaitTime is a global variable that is used to 1292 // prevent timing attacks on login requests. Its normally set 1293 // to 500 milliseconds. We temporarily reduce it to 50ms for 1294 // these tests so that they don't take as long to run. 1295 m := loginMinWaitTime 1296 loginMinWaitTime = 50 * time.Millisecond 1297 defer func() { 1298 loginMinWaitTime = m 1299 }() 1300 1301 // Test the incorrect email error path because it's 1302 // the quickest failure path for the login route. 1303 start := time.Now() 1304 _, err := p.processLogin(www.Login{}) 1305 end := time.Now() 1306 elapsed := end.Sub(start) 1307 1308 got := errToStr(err) 1309 want := www.ErrorStatus[www.ErrorStatusInvalidLogin] 1310 if got != want { 1311 t.Errorf("got error %v, want %v", 1312 got, want) 1313 } 1314 if elapsed < loginMinWaitTime { 1315 t.Errorf("execution time got %v, want >%v", 1316 elapsed, loginMinWaitTime) 1317 } 1318 1319 // Test a successful login. newUser() sets the password 1320 // to be the username, which is why we pass the username 1321 // into the password field. 1322 u, _ := newUser(t, p, true, false) 1323 start = time.Now() 1324 lr, err := p.processLogin(www.Login{ 1325 Email: u.Email, 1326 Password: u.Username, 1327 }) 1328 end = time.Now() 1329 elapsed = end.Sub(start) 1330 got = errToStr(err) 1331 switch { 1332 case got != "nil": 1333 t.Errorf("got error %v, want nil", got) 1334 case lr.UserID != u.ID.String(): 1335 t.Errorf("login reply userID got %v, want %v", 1336 lr.UserID, u.ID.String()) 1337 case elapsed < loginMinWaitTime: 1338 t.Errorf("execution time got %v, want >%v", 1339 elapsed, loginMinWaitTime) 1340 } 1341 } 1342 1343 func TestProcessChangePassword(t *testing.T) { 1344 p, cleanup := newTestPoliteiawww(t) 1345 defer cleanup() 1346 1347 // Create a new user. newUser() sets the password 1348 // as the username, which is why currPass is set 1349 // to be the username. 1350 u, _ := newUser(t, p, true, false) 1351 currPass := u.Username 1352 1353 r, err := util.Random(int(www.PolicyMinPasswordLength)) 1354 if err != nil { 1355 t.Fatalf("%v", err) 1356 } 1357 newPass := hex.EncodeToString(r) 1358 1359 // Setup tests 1360 var tests = []struct { 1361 name string 1362 cp www.ChangePassword 1363 want error 1364 }{ 1365 { 1366 "wrong current password", 1367 www.ChangePassword{ 1368 CurrentPassword: "wrong!", 1369 NewPassword: newPass, 1370 }, 1371 www.UserError{ 1372 ErrorCode: www.ErrorStatusInvalidPassword, 1373 }, 1374 }, 1375 { 1376 "invalid new password", 1377 www.ChangePassword{ 1378 CurrentPassword: currPass, 1379 NewPassword: "", 1380 }, 1381 www.UserError{ 1382 ErrorCode: www.ErrorStatusMalformedPassword, 1383 }, 1384 }, 1385 { 1386 "success", 1387 www.ChangePassword{ 1388 CurrentPassword: currPass, 1389 NewPassword: newPass, 1390 }, 1391 nil, 1392 }, 1393 } 1394 1395 // Run tests 1396 for _, v := range tests { 1397 t.Run(v.name, func(t *testing.T) { 1398 _, err := p.processChangePassword(u.Email, v.cp) 1399 got := errToStr(err) 1400 want := errToStr(v.want) 1401 if got != want { 1402 t.Errorf("got error %v, want %v", 1403 got, want) 1404 } 1405 }) 1406 } 1407 } 1408 1409 func TestProcessResetPassword(t *testing.T) { 1410 p, cleanup := newTestPoliteiawww(t) 1411 defer cleanup() 1412 1413 // Test resetPasswordMinWaitTime 1414 t.Run("minimum wait time", func(t *testing.T) { 1415 usr, _ := newUser(t, p, true, false) 1416 rp := www.ResetPassword{ 1417 Username: usr.Username, 1418 Email: usr.Email, 1419 } 1420 start := time.Now() 1421 rpr, err := p.processResetPassword(rp) 1422 1423 // Ensure the wait time is being adhered to. 1424 if time.Since(start) < resetPasswordMinWaitTime { 1425 t.Fatalf("min wait time violated") 1426 } 1427 1428 // Check reply 1429 got := errToStr(err) 1430 if err != nil { 1431 t.Fatalf("got error %v, want nil", got) 1432 } 1433 if rpr.VerificationToken == "" { 1434 t.Errorf("verification token not sent") 1435 } 1436 }) 1437 1438 // Remove the min wait time requirement so that the 1439 // remaining tests aren't super slow. 1440 wt := resetPasswordMinWaitTime 1441 resetPasswordMinWaitTime = 0 * time.Millisecond 1442 defer func() { 1443 resetPasswordMinWaitTime = wt 1444 }() 1445 1446 // Setup test data 1447 1448 // Create a user with no verification token yet. 1449 usrNoToken, _ := newUser(t, p, true, false) 1450 1451 // Create a user with an unexpired verification token. 1452 usrUnexpired, _ := newUser(t, p, true, false) 1453 tokenb, expiry, err := newVerificationTokenAndExpiry() 1454 if err != nil { 1455 t.Fatal(err) 1456 } 1457 usrUnexpired.ResetPasswordVerificationToken = tokenb 1458 usrUnexpired.ResetPasswordVerificationExpiry = expiry 1459 err = p.db.UserUpdate(*usrUnexpired) 1460 if err != nil { 1461 t.Fatal(err) 1462 } 1463 1464 // Create a user with an expired verification token. 1465 usrExpired, _ := newUser(t, p, true, false) 1466 tokenb, _, err = newVerificationTokenAndExpiry() 1467 if err != nil { 1468 t.Fatal(err) 1469 } 1470 usrExpired.ResetPasswordVerificationToken = tokenb 1471 usrExpired.ResetPasswordVerificationExpiry = time.Now().Unix() - 1 1472 err = p.db.UserUpdate(*usrExpired) 1473 if err != nil { 1474 t.Fatal(err) 1475 } 1476 1477 // Setup tests 1478 var tests = []struct { 1479 name string 1480 rp www.ResetPassword 1481 wantErr error 1482 wantToken bool // Should the verification token have been sent 1483 }{ 1484 { 1485 "invalid username", 1486 www.ResetPassword{ 1487 Username: "wrongusername", 1488 Email: usrNoToken.Email, 1489 }, 1490 www.UserError{ 1491 ErrorCode: www.ErrorStatusUserNotFound, 1492 }, 1493 false, 1494 }, 1495 { 1496 "wrong email", 1497 www.ResetPassword{ 1498 Username: usrNoToken.Username, 1499 Email: "wrongemail", 1500 }, 1501 nil, 1502 false, 1503 }, 1504 // User has already requested a reset password 1505 // verification token and it has not expired. 1506 { 1507 "unexpired verification token", 1508 www.ResetPassword{ 1509 Username: usrUnexpired.Username, 1510 Email: usrUnexpired.Email, 1511 }, 1512 nil, 1513 false, 1514 }, 1515 // User has already requested a reset password 1516 // verification token but the token is expired. 1517 { 1518 "expired verification token", 1519 www.ResetPassword{ 1520 Username: usrExpired.Username, 1521 Email: usrExpired.Email, 1522 }, 1523 nil, 1524 true, 1525 }, 1526 // User has not yet requested a reset password 1527 // verification token. 1528 { 1529 "no token", 1530 www.ResetPassword{ 1531 Username: usrNoToken.Username, 1532 Email: usrNoToken.Email, 1533 }, 1534 nil, 1535 true, 1536 }, 1537 } 1538 1539 // Run tests 1540 for _, v := range tests { 1541 t.Run(v.name, func(t *testing.T) { 1542 rpr, err := p.processResetPassword(v.rp) 1543 got := errToStr(err) 1544 want := errToStr(v.wantErr) 1545 if got != want { 1546 t.Errorf("got error %v, want %v", got, want) 1547 } 1548 1549 // Check if the verification token was successfully 1550 // sent. The email server is disabled for testing so 1551 // if the token is in the reply then it is considered 1552 // to have been successfully sent. 1553 if v.wantToken && rpr.VerificationToken == "" { 1554 t.Errorf("verification token not sent") 1555 } 1556 }) 1557 } 1558 } 1559 1560 func TestProcessVerifyResetPassword(t *testing.T) { 1561 p, cleanup := newTestPoliteiawww(t) 1562 defer cleanup() 1563 1564 // Create a user with an unexpired verification token 1565 usrUnexpired, _ := newUser(t, p, true, false) 1566 token, expiry, err := newVerificationTokenAndExpiry() 1567 if err != nil { 1568 t.Fatal(err) 1569 } 1570 usrUnexpired.ResetPasswordVerificationToken = token 1571 usrUnexpired.ResetPasswordVerificationExpiry = expiry 1572 err = p.db.UserUpdate(*usrUnexpired) 1573 if err != nil { 1574 t.Fatal(err) 1575 } 1576 usrUnexpiredToken := hex.EncodeToString(token) 1577 1578 // Create a user with an exipred verification token 1579 usrExpired, _ := newUser(t, p, true, false) 1580 token, _, err = newVerificationTokenAndExpiry() 1581 if err != nil { 1582 t.Fatal(err) 1583 } 1584 usrExpired.ResetPasswordVerificationToken = token 1585 usrExpired.ResetPasswordVerificationExpiry = time.Now().Unix() - 1 1586 err = p.db.UserUpdate(*usrExpired) 1587 if err != nil { 1588 t.Fatal(err) 1589 } 1590 usrExpiredToken := hex.EncodeToString(token) 1591 1592 // Create a locked user with an unexpired verification token. 1593 usrLocked, _ := newUser(t, p, true, false) 1594 token, expiry, err = newVerificationTokenAndExpiry() 1595 if err != nil { 1596 t.Fatal(err) 1597 } 1598 usrLocked.ResetPasswordVerificationToken = token 1599 usrLocked.ResetPasswordVerificationExpiry = expiry 1600 usrLocked.FailedLoginAttempts = LoginAttemptsToLockUser 1601 err = p.db.UserUpdate(*usrLocked) 1602 if err != nil { 1603 t.Fatal(err) 1604 } 1605 usrLockedToken := hex.EncodeToString(token) 1606 1607 // Create a new password 1608 h, err := p.hashPassword("newpassword") 1609 if err != nil { 1610 t.Fatal(err) 1611 } 1612 newPass := hex.EncodeToString(h) 1613 1614 // Setup tests 1615 var tests = []struct { 1616 name string 1617 vrp www.VerifyResetPassword 1618 wantErr error 1619 }{ 1620 { 1621 "user not found", 1622 www.VerifyResetPassword{ 1623 Username: "badusername", 1624 VerificationToken: usrUnexpiredToken, 1625 NewPassword: newPass, 1626 }, 1627 www.UserError{ 1628 ErrorCode: www.ErrorStatusUserNotFound, 1629 }, 1630 }, 1631 { 1632 "no verification token", 1633 www.VerifyResetPassword{ 1634 Username: usrUnexpired.Username, 1635 VerificationToken: "", 1636 NewPassword: newPass, 1637 }, 1638 www.UserError{ 1639 ErrorCode: www.ErrorStatusVerificationTokenInvalid, 1640 }, 1641 }, 1642 { 1643 "invalid verification token", 1644 www.VerifyResetPassword{ 1645 Username: usrUnexpired.Username, 1646 VerificationToken: "invalidtoken", 1647 NewPassword: newPass, 1648 }, 1649 www.UserError{ 1650 ErrorCode: www.ErrorStatusVerificationTokenInvalid, 1651 }, 1652 }, 1653 { 1654 "wrong verification token", 1655 www.VerifyResetPassword{ 1656 Username: usrUnexpired.Username, 1657 VerificationToken: usrExpiredToken, 1658 NewPassword: newPass, 1659 }, 1660 www.UserError{ 1661 ErrorCode: www.ErrorStatusVerificationTokenInvalid, 1662 }, 1663 }, 1664 { 1665 "expired verification token", 1666 www.VerifyResetPassword{ 1667 Username: usrExpired.Username, 1668 VerificationToken: usrExpiredToken, 1669 NewPassword: newPass, 1670 }, 1671 www.UserError{ 1672 ErrorCode: www.ErrorStatusVerificationTokenExpired, 1673 }, 1674 }, 1675 { 1676 "invalid password", 1677 www.VerifyResetPassword{ 1678 Username: usrUnexpired.Username, 1679 VerificationToken: usrUnexpiredToken, 1680 NewPassword: "", 1681 }, 1682 www.UserError{ 1683 ErrorCode: www.ErrorStatusMalformedPassword, 1684 }, 1685 }, 1686 { 1687 "success", 1688 www.VerifyResetPassword{ 1689 Username: usrUnexpired.Username, 1690 VerificationToken: usrUnexpiredToken, 1691 NewPassword: newPass, 1692 }, 1693 nil, 1694 }, 1695 { 1696 "success with locked account", 1697 www.VerifyResetPassword{ 1698 Username: usrLocked.Username, 1699 VerificationToken: usrLockedToken, 1700 NewPassword: newPass, 1701 }, 1702 nil, 1703 }, 1704 } 1705 1706 // Run tests 1707 for _, v := range tests { 1708 t.Run(v.name, func(t *testing.T) { 1709 _, err := p.processVerifyResetPassword(v.vrp) 1710 got := errToStr(err) 1711 want := errToStr(v.wantErr) 1712 if got != want { 1713 t.Errorf("got error %v, want %v", got, want) 1714 return 1715 } 1716 1717 // If there were no errors, ensure that the user password 1718 // was updated correctly, the user account was unlocked, 1719 // and the verification token fields were cleared out. 1720 if err == nil { 1721 u, err := p.db.UserGetByUsername(v.vrp.Username) 1722 if err != nil { 1723 t.Fatal(err) 1724 } 1725 err = bcrypt.CompareHashAndPassword(u.HashedPassword, 1726 []byte(v.vrp.NewPassword)) 1727 if err != nil { 1728 if errors.Is(err, bcrypt.ErrMismatchedHashAndPassword) { 1729 t.Errorf("user password not updated") 1730 } 1731 t.Fatal(err) 1732 } 1733 switch { 1734 case userIsLocked(u.FailedLoginAttempts): 1735 t.Errorf("user account is still locked") 1736 case u.ResetPasswordVerificationToken != nil: 1737 t.Errorf("verification token not nil") 1738 case u.ResetPasswordVerificationExpiry != 0: 1739 t.Errorf("verification expiry not 0") 1740 } 1741 } 1742 }) 1743 } 1744 } 1745 1746 func TestProcessChangeUsername(t *testing.T) { 1747 p, cleanup := newTestPoliteiawww(t) 1748 defer cleanup() 1749 1750 // Create a new user. newUser() sets 1751 // the password to be the username. 1752 u, _ := newUser(t, p, true, false) 1753 password := u.Username 1754 1755 // Setup tests 1756 var tests = []struct { 1757 name string 1758 email string 1759 cu www.ChangeUsername 1760 want error 1761 }{ 1762 { 1763 "wrong password", 1764 u.Email, 1765 www.ChangeUsername{ 1766 Password: "wrong", 1767 }, 1768 www.UserError{ 1769 ErrorCode: www.ErrorStatusInvalidPassword, 1770 }, 1771 }, 1772 { 1773 "invalid username", 1774 u.Email, 1775 www.ChangeUsername{ 1776 Password: password, 1777 NewUsername: "?", 1778 }, 1779 www.UserError{ 1780 ErrorCode: www.ErrorStatusMalformedUsername, 1781 }, 1782 }, 1783 { 1784 "duplicate username", 1785 u.Email, 1786 www.ChangeUsername{ 1787 Password: password, 1788 NewUsername: u.Username, 1789 }, 1790 www.UserError{ 1791 ErrorCode: www.ErrorStatusDuplicateUsername, 1792 }, 1793 }, 1794 { 1795 "success", 1796 u.Email, 1797 www.ChangeUsername{ 1798 Password: password, 1799 NewUsername: "politeiauser", 1800 }, 1801 nil, 1802 }, 1803 } 1804 1805 // Run tests 1806 for _, v := range tests { 1807 t.Run(v.name, func(t *testing.T) { 1808 _, err := p.processChangeUsername(v.email, v.cu) 1809 got := errToStr(err) 1810 want := errToStr(v.want) 1811 if got != want { 1812 t.Errorf("got error %v, want %v", got, want) 1813 } 1814 }) 1815 } 1816 } 1817 1818 func TestProcessUserDetails(t *testing.T) { 1819 p, cleanup := newTestPoliteiawww(t) 1820 defer cleanup() 1821 1822 // Create a new user. This is the UUID that 1823 // we'll use to test the UserDetails route. 1824 u, _ := newUser(t, p, true, false) 1825 ud := www.UserDetails{} 1826 1827 // Test a valid length UUID that does not belong to a user. 1828 // We can assume that any invalid UUIDs were caught by the 1829 // user details request handler. 1830 t.Run("valid UUID with no user", func(t *testing.T) { 1831 ud.UserID = "aaaaaaaa-aaaa-aaaa-aaaa-aaaaaaaaaaaa" 1832 _, err := p.processUserDetails(&ud, false, false) 1833 got := errToStr(err) 1834 want := www.ErrorStatus[www.ErrorStatusUserNotFound] 1835 if got != want { 1836 t.Errorf("got error %v, want %v", got, want) 1837 } 1838 }) 1839 1840 // UserDetails will either return the full user details 1841 // or just the public user details depending on who is 1842 // requesting the data. The full user details includes 1843 // private data such as email address and payment info. 1844 fullUser := convertWWWUserFromDatabaseUser(u) 1845 fullUserMsg := "full user details" 1846 1847 publicUser := filterUserPublicFields(fullUser) 1848 publicUserMsg := "public user details" 1849 1850 // Use a valid UUID for the remaining tests 1851 ud.UserID = fullUser.ID 1852 1853 // Setup tests 1854 var tests = []struct { 1855 name string // Test name 1856 ud www.UserDetails // User details request 1857 isCurrentUser bool // Is a user requesting their own details 1858 isAdmin bool // Is an admin requesting the user details 1859 wantUsr www.User // Wanted user response 1860 wantMsg string // Description of the wanted user response 1861 }{ 1862 { 1863 "public user details", 1864 ud, false, false, 1865 publicUser, 1866 publicUserMsg, 1867 }, 1868 { 1869 "admin requesting user details", 1870 ud, false, true, 1871 fullUser, 1872 fullUserMsg, 1873 }, 1874 1875 { 1876 "user requesting their own details", 1877 ud, true, false, 1878 fullUser, 1879 fullUserMsg, 1880 }, 1881 { 1882 "admin requesting their own details", 1883 ud, true, true, 1884 fullUser, 1885 fullUserMsg, 1886 }, 1887 } 1888 1889 // Run tests 1890 for _, v := range tests { 1891 t.Run(v.name, func(t *testing.T) { 1892 udr, err := p.processUserDetails(&v.ud, 1893 v.isCurrentUser, v.isAdmin) 1894 if err != nil { 1895 t.Errorf("got error %v, want nil", err) 1896 } 1897 1898 diff := deep.Equal(udr.User, v.wantUsr) 1899 if diff != nil { 1900 t.Errorf("want %v, got/want diff:\n%v", 1901 v.wantMsg, spew.Sdump(diff)) 1902 } 1903 }) 1904 } 1905 } 1906 1907 func TestProcessEditUser(t *testing.T) { 1908 p, cleanup := newTestPoliteiawww(t) 1909 defer cleanup() 1910 1911 // Create a new user. This is the user 1912 // that we will be editing. 1913 user, _ := newUser(t, p, true, false) 1914 1915 // Setup test cases 1916 tests := []struct { 1917 name string 1918 notification uint64 1919 want []www.EmailNotificationT 1920 }{ 1921 { 1922 "single notification setting", 1923 0x1, 1924 []www.EmailNotificationT{ 1925 www.NotificationEmailMyProposalStatusChange, 1926 }, 1927 }, 1928 { 1929 "multiple notification settings", 1930 0x7, 1931 []www.EmailNotificationT{ 1932 www.NotificationEmailMyProposalStatusChange, 1933 www.NotificationEmailMyProposalVoteStarted, 1934 www.NotificationEmailRegularProposalVetted, 1935 }, 1936 }, 1937 { 1938 "no notification settings", 1939 0x0, 1940 []www.EmailNotificationT{}, 1941 }, 1942 { 1943 "invalid notification setting", 1944 0x100000, 1945 []www.EmailNotificationT{}, 1946 }, 1947 } 1948 1949 // Run test cases 1950 for _, test := range tests { 1951 t.Run(test.name, func(t *testing.T) { 1952 _, err := p.processEditUser(&www.EditUser{ 1953 EmailNotifications: &test.notification, 1954 }, user) 1955 if err != nil { 1956 t.Errorf("got error %v, want nil", err) 1957 } 1958 1959 // Ensure database was updated with 1960 // correct notification settings. 1961 u, err := p.db.UserGetById(user.ID) 1962 if err != nil { 1963 t.Fatalf("%v", err) 1964 } 1965 1966 var bitsWant uint64 1967 for _, notification := range test.want { 1968 bitsWant |= uint64(notification) 1969 } 1970 1971 // Apply a mask to ignore invalid bits. The mask 1972 // represents all possible notification settings. 1973 var mask uint64 = 0x1FF 1974 bitsGot := u.EmailNotifications & mask 1975 if !(bitsWant|bitsGot == bitsWant) { 1976 t.Errorf("notification bits got %#x, want %#x", bitsGot, bitsWant) 1977 } 1978 }) 1979 } 1980 } 1981 1982 func TestProcessManageUser(t *testing.T) { 1983 p, cleanup := newTestPoliteiawww(t) 1984 defer cleanup() 1985 1986 // Create a new user. This is the user 1987 // that we will be managing. 1988 usr, _ := newUser(t, p, true, false) 1989 uid := usr.ID.String() 1990 1991 // Create a new admin. This user will be 1992 // used to manage the user 1993 admin, _ := newUser(t, p, true, false) 1994 1995 var tests = []struct { 1996 name string 1997 mu www.ManageUser 1998 admin *user.User 1999 want error 2000 }{ 2001 { 2002 "invalid manage action", 2003 www.ManageUser{ 2004 UserID: uid, 2005 Action: www.UserManageInvalid, 2006 Reason: "reason", 2007 }, 2008 admin, 2009 www.UserError{ 2010 ErrorCode: www.ErrorStatusInvalidUserManageAction, 2011 }, 2012 }, 2013 { 2014 "invalid reason", 2015 www.ManageUser{ 2016 UserID: uid, 2017 Action: www.UserManageExpireNewUserVerification, 2018 Reason: "", 2019 }, 2020 admin, 2021 www.UserError{ 2022 ErrorCode: www.ErrorStatusInvalidInput, 2023 }, 2024 }, 2025 { 2026 "unsupported edit action", 2027 www.ManageUser{ 2028 UserID: uid, 2029 Action: 9, 2030 Reason: "reason", 2031 }, 2032 admin, 2033 www.UserError{ 2034 ErrorCode: www.ErrorStatusInvalidUserManageAction, 2035 }, 2036 }, 2037 } 2038 2039 // Run tests 2040 for _, v := range tests { 2041 t.Run(v.name, func(t *testing.T) { 2042 _, err := p.processManageUser(&v.mu, v.admin) 2043 got := errToStr(err) 2044 want := errToStr(v.want) 2045 if got != want { 2046 t.Errorf("got error %v, want %v", 2047 got, want) 2048 } 2049 }) 2050 } 2051 } 2052 2053 func TestProcessUsers(t *testing.T) { 2054 p, cleanup := newTestPoliteiawww(t) 2055 defer cleanup() 2056 2057 // Create a new user. 2058 usr, id := newUser(t, p, true, false) 2059 // Create an admin user. 2060 adm, _ := newUser(t, p, true, true) 2061 2062 usrpk := id.Public.String() 2063 2064 // empty user list 2065 empty := make([]www.AbridgedUser, 0, www.UserListPageSize) 2066 usrlistpub := append(empty, www.AbridgedUser{ 2067 ID: usr.ID.String(), 2068 Email: "", 2069 Username: usr.Username, 2070 }) 2071 2072 var allusers = []www.AbridgedUser{ 2073 { 2074 ID: adm.ID.String(), 2075 Email: adm.Email, 2076 Username: adm.Username, 2077 }, 2078 { 2079 ID: usr.ID.String(), 2080 Email: usr.Email, 2081 Username: usr.Username, 2082 }, 2083 } 2084 // sorts all users buy username 2085 sort.Slice(allusers, func(i, j int) bool { 2086 return allusers[i].Username < allusers[j].Username 2087 }) 2088 2089 var tests = []struct { 2090 name string 2091 u www.Users 2092 isAdmin bool 2093 wantReply www.UsersReply 2094 }{ 2095 { 2096 "pubkey not found", 2097 www.Users{ 2098 PublicKey: "", 2099 }, 2100 false, 2101 www.UsersReply{ 2102 Users: empty, 2103 }, 2104 }, 2105 { 2106 "email not found", 2107 www.Users{ 2108 Email: "notfound", 2109 }, 2110 false, 2111 www.UsersReply{ 2112 Users: empty, 2113 }, 2114 }, 2115 { 2116 "username not found", 2117 www.Users{ 2118 Username: "notfound", 2119 }, 2120 false, 2121 www.UsersReply{ 2122 Users: empty, 2123 }, 2124 }, 2125 { 2126 "regular users can find users by pubkey", 2127 www.Users{ 2128 PublicKey: usrpk, 2129 }, 2130 false, 2131 www.UsersReply{ 2132 Users: usrlistpub, 2133 TotalMatches: 1, 2134 }, 2135 }, 2136 { 2137 "regular users can find users by username", 2138 www.Users{ 2139 Username: usr.Username, 2140 }, 2141 false, 2142 www.UsersReply{ 2143 Users: usrlistpub, 2144 TotalMatches: 1, 2145 }, 2146 }, 2147 { 2148 "regular users can't find users by email", 2149 www.Users{ 2150 Email: usr.Email, 2151 }, 2152 false, 2153 www.UsersReply{ 2154 Users: empty, 2155 }, 2156 }, 2157 { 2158 "admin can find user by pubkey", 2159 www.Users{ 2160 PublicKey: usrpk, 2161 }, 2162 true, 2163 www.UsersReply{ 2164 Users: []www.AbridgedUser{ 2165 { 2166 Username: usr.Username, 2167 Email: usr.Email, 2168 ID: usr.ID.String(), 2169 }, 2170 }, 2171 TotalMatches: 1, 2172 TotalUsers: 2, 2173 }, 2174 }, 2175 { 2176 "admin can find user by email", 2177 www.Users{ 2178 Email: usr.Email, 2179 }, 2180 true, 2181 www.UsersReply{ 2182 Users: []www.AbridgedUser{ 2183 { 2184 Username: usr.Username, 2185 Email: usr.Email, 2186 ID: usr.ID.String(), 2187 }, 2188 }, 2189 TotalMatches: 1, 2190 TotalUsers: 2, 2191 }, 2192 }, 2193 { 2194 "admin can find user by username", 2195 www.Users{ 2196 Username: usr.Username, 2197 }, 2198 true, 2199 www.UsersReply{ 2200 Users: []www.AbridgedUser{ 2201 { 2202 Username: usr.Username, 2203 Email: usr.Email, 2204 ID: usr.ID.String(), 2205 }, 2206 }, 2207 TotalMatches: 1, 2208 TotalUsers: 2, 2209 }, 2210 }, 2211 { 2212 "admin fetches all users when email is empty", 2213 www.Users{ 2214 Email: "", 2215 }, 2216 true, 2217 www.UsersReply{ 2218 Users: allusers, 2219 TotalMatches: 2, 2220 TotalUsers: 2, 2221 }, 2222 }, 2223 { 2224 "admin fetches all users when username is empty", 2225 www.Users{ 2226 Username: "", 2227 }, 2228 true, 2229 www.UsersReply{ 2230 Users: allusers, 2231 TotalMatches: 2, 2232 TotalUsers: 2, 2233 }, 2234 }, 2235 { 2236 "admin fetches all users when pubkey is empty", 2237 www.Users{ 2238 PublicKey: "", 2239 }, 2240 true, 2241 www.UsersReply{ 2242 Users: allusers, 2243 TotalMatches: 2, 2244 TotalUsers: 2, 2245 }, 2246 }, 2247 } 2248 2249 // Run tests 2250 for _, v := range tests { 2251 t.Run(v.name, func(t *testing.T) { 2252 pur, err := p.processUsers(&v.u, v.isAdmin) 2253 if err != nil { 2254 return 2255 } 2256 // Verify reply 2257 diff := deep.Equal(*pur, v.wantReply) 2258 if diff != nil { 2259 t.Errorf("got/want diff:\n%v", 2260 spew.Sdump(diff)) 2261 } 2262 }) 2263 } 2264 } 2265 2266 func TestProcessSetTOTP(t *testing.T) { 2267 p, cleanup := newTestPoliteiawww(t) 2268 defer cleanup() 2269 2270 basicUser, _ := newUser(t, p, true, false) 2271 2272 var tests = []struct { 2273 name string 2274 params www.SetTOTP 2275 wantError error 2276 user *user.User 2277 }{ 2278 { 2279 "error wrong type", 2280 www.SetTOTP{ 2281 Type: www.TOTPTypeInvalid, 2282 }, 2283 www.UserError{ 2284 ErrorCode: www.ErrorStatusTOTPInvalidType, 2285 }, 2286 basicUser, 2287 }, 2288 { 2289 "success", 2290 www.SetTOTP{ 2291 Type: www.TOTPTypeBasic, 2292 }, 2293 nil, 2294 basicUser, 2295 }, 2296 } 2297 2298 for _, v := range tests { 2299 t.Run(v.name, func(t *testing.T) { 2300 reply, err := p.processSetTOTP(v.params, v.user) 2301 2302 got := errToStr(err) 2303 want := errToStr(v.wantError) 2304 if got != want { 2305 t.Errorf("got %v, want %v", got, want) 2306 return 2307 } 2308 2309 if err != nil { 2310 return 2311 } 2312 userInfo, err := p.userByIDStr(v.user.ID.String()) 2313 if err != nil { 2314 t.Errorf("unable to get update user %v", err) 2315 return 2316 } 2317 if userInfo.TOTPSecret != reply.Key { 2318 t.Error("secret returned does not match saved key") 2319 } 2320 }) 2321 } 2322 2323 // Set up separate tests for testing already set totp key 2324 alreadySetUser, _ := newUser(t, p, true, false) 2325 2326 opts := p.totpGenerateOpts(defaultPoliteiaIssuer, alreadySetUser.Username) 2327 key, err := totp.Generate(opts) 2328 if err != nil { 2329 t.Errorf("unable to generate secret key %v", err) 2330 } 2331 2332 alreadySetUser.TOTPType = int(www.TOTPTypeBasic) 2333 alreadySetUser.TOTPSecret = key.Secret() 2334 alreadySetUser.TOTPVerified = true 2335 alreadySetUser.TOTPLastUpdated = append(alreadySetUser.TOTPLastUpdated, 2336 time.Now().Unix()) 2337 2338 err = p.db.UserUpdate(*alreadySetUser) 2339 if err != nil { 2340 t.Errorf("unable to update user secret key %v", err) 2341 } 2342 requestTime := time.Now() 2343 code, err := p.totpGenerateCode(key.Secret(), requestTime) 2344 if err != nil { 2345 t.Errorf("unable to generate code %v", err) 2346 } 2347 2348 // We run separate tests because these are time dependant because of codes 2349 // generated. 2350 var alreadySetTests = []struct { 2351 name string 2352 params www.SetTOTP 2353 wantError error 2354 user *user.User 2355 }{ 2356 { 2357 "error already set wrong code", 2358 www.SetTOTP{ 2359 Type: www.TOTPTypeBasic, 2360 Code: "12345", 2361 }, 2362 www.UserError{ 2363 ErrorCode: www.ErrorStatusTOTPFailedValidation, 2364 }, 2365 alreadySetUser, 2366 }, 2367 { 2368 "success already set", 2369 www.SetTOTP{ 2370 Type: www.TOTPTypeBasic, 2371 Code: code, 2372 }, 2373 nil, 2374 alreadySetUser, 2375 }, 2376 } 2377 for _, v := range alreadySetTests { 2378 t.Run(v.name, func(t *testing.T) { 2379 reply, err := p.processSetTOTP(v.params, v.user) 2380 2381 // Check to see that expected errors match 2382 got := errToStr(err) 2383 want := errToStr(v.wantError) 2384 if got != want { 2385 t.Errorf("got %v, want %v", got, want) 2386 return 2387 } 2388 2389 if err != nil { 2390 return 2391 } 2392 userInfo, err := p.userByIDStr(v.user.ID.String()) 2393 if err != nil { 2394 t.Errorf("unable to get update user %v", err) 2395 return 2396 } 2397 if userInfo.TOTPSecret != reply.Key { 2398 t.Error("secret returned does not match saved key") 2399 } 2400 }) 2401 } 2402 2403 } 2404 2405 func TestProcessVerifyTOTP(t *testing.T) { 2406 p, cleanup := newTestPoliteiawww(t) 2407 defer cleanup() 2408 2409 usr, _ := newUser(t, p, true, false) 2410 2411 opts := p.totpGenerateOpts(defaultPoliteiaIssuer, usr.Username) 2412 key, err := totp.Generate(opts) 2413 if err != nil { 2414 t.Errorf("unable to generate secret key %v", err) 2415 } 2416 2417 usr.TOTPType = int(www.TOTPTypeBasic) 2418 usr.TOTPSecret = key.Secret() 2419 usr.TOTPVerified = false 2420 usr.TOTPLastUpdated = append(usr.TOTPLastUpdated, time.Now().Unix()) 2421 2422 err = p.db.UserUpdate(*usr) 2423 if err != nil { 2424 t.Errorf("unable to update user secret key %v", err) 2425 } 2426 2427 code, err := p.totpGenerateCode(key.Secret(), time.Now()) 2428 if err != nil { 2429 t.Errorf("unable to generate code %v", err) 2430 } 2431 2432 var tests = []struct { 2433 name string 2434 params www.VerifyTOTP 2435 wantError error 2436 }{ 2437 { 2438 "error wrong code", 2439 www.VerifyTOTP{ 2440 Code: "12345", 2441 }, 2442 www.UserError{ 2443 ErrorCode: www.ErrorStatusTOTPFailedValidation, 2444 }, 2445 }, 2446 { 2447 "success", 2448 www.VerifyTOTP{ 2449 Code: code, 2450 }, 2451 nil, 2452 }, 2453 } 2454 2455 for _, v := range tests { 2456 t.Run(v.name, func(t *testing.T) { 2457 _, err := p.processVerifyTOTP(v.params, usr) 2458 if err != nil { 2459 got := errToStr(err) 2460 want := errToStr(v.wantError) 2461 if got != want { 2462 t.Errorf("got %v, want %v", got, want) 2463 } 2464 return 2465 } 2466 }) 2467 } 2468 }