github.com/decred/politeia@v1.4.0/politeiawww/legacy/user.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 "bytes" 9 "encoding/base64" 10 "encoding/hex" 11 "errors" 12 "image/png" 13 "regexp" 14 "sort" 15 "strconv" 16 "strings" 17 "sync" 18 "time" 19 20 "github.com/decred/politeia/politeiad/api/v1/identity" 21 www "github.com/decred/politeia/politeiawww/api/www/v1" 22 "github.com/decred/politeia/politeiawww/config" 23 "github.com/decred/politeia/politeiawww/legacy/user" 24 "github.com/decred/politeia/util" 25 "github.com/google/uuid" 26 "github.com/pquerna/otp/totp" 27 "golang.org/x/crypto/bcrypt" 28 ) 29 30 const ( 31 LoginAttemptsToLockUser = 5 32 33 // Number of attempts until totp locks until the next window 34 totpFailedAttempts = 2 35 36 // Route to reset password at GUI 37 ResetPasswordGuiRoute = "/password" // XXX what is this doing here? 38 39 emailRegex = `^[a-zA-Z0-9.!#$%&'*+/=?^_` + 40 "`{|}~-]+@[a-zA-Z0-9](?:[a-zA-Z0-9-]{0,61}[a-zA-Z0-9])?" + 41 "(?:\\.[a-zA-Z0-9](?:[a-zA-Z0-9-]{0,61}[a-zA-Z0-9])?)*$" 42 ) 43 44 var ( 45 validUsername = regexp.MustCompile(createUsernameRegex()) 46 validEmail = regexp.MustCompile(emailRegex) 47 48 // resetPasswordMinWaitTime is the minimum amount of time to wait 49 // before sending a response back to the client for the reset 50 // password route. This is done to prevent an attacker from being 51 // able to execute a timing attack to determine if the provided 52 // email address is the user's valid email address. 53 resetPasswordMinWaitTime = 500 * time.Millisecond 54 55 // loginMinWaitTime is the minimum amount of time to wait before 56 // the server sends a response to the client for the login route. 57 // This is done to prevent an attacker from being able to execute 58 // a timing attack to determine whether the ErrorStatusInvalidLogin 59 // response is specific to a bad email or a bad password. 60 loginMinWaitTime = 500 * time.Millisecond 61 ) 62 63 // processNewUser creates a new user in the db if it doesn't already 64 // exist and sets a verification token and expiry; the token must be 65 // verified before it expires. If the user already exists in the db 66 // and its token is expired, it generates a new one. 67 // 68 // Note that this function always returns a NewUserReply. The caller shall 69 // verify error and determine how to return this information upstream. 70 func (p *Politeiawww) processNewUser(nu www.NewUser) (*www.NewUserReply, error) { 71 log.Tracef("processNewUser: %v", nu.Username) 72 73 // Format and validate user credentials 74 nu.Email = strings.ToLower(nu.Email) 75 if !validEmail.MatchString(nu.Email) { 76 log.Debugf("processNewUser: invalid email '%v'", nu.Email) 77 return nil, www.UserError{ 78 ErrorCode: www.ErrorStatusMalformedEmail, 79 } 80 } 81 err := validatePubKey(nu.PublicKey) 82 if err != nil { 83 return nil, err 84 } 85 86 nu.Username = formatUsername(nu.Username) 87 err = validateUsername(nu.Username) 88 if err != nil { 89 return nil, err 90 } 91 92 // The client should be hashing the password before 93 // sending it to politeiawww. This validation is only 94 // relevant if the client failed to hash the password 95 // or does not include a password in the request. 96 err = validatePassword(nu.Password) 97 if err != nil { 98 return nil, err 99 } 100 101 // Check if user already exists 102 u, err := p.userByEmail(nu.Email) 103 switch err { 104 case nil: 105 // User exists 106 107 // Return if the user is already verified 108 if u.NewUserVerificationToken == nil { 109 log.Debugf("processNewUser: '%v' already verified", 110 nu.Username) 111 return &www.NewUserReply{}, nil 112 } 113 114 // User is not already verified. Check if the verification token 115 // has expired. If the token has not expired yet, we simply return. 116 // The user will have to wait until the token expires before a 117 // new one will be sent. If the token has expired, we update the 118 // user in the database and send the user a new token. The user 119 // identity will be updated if the request specifies a new public 120 // key. 121 if u.NewUserVerificationExpiry > time.Now().Unix() { 122 log.Debugf("processNewUser: '%v' not verified, "+ 123 "token not expired", nu.Username) 124 return &www.NewUserReply{}, nil 125 } 126 127 // Ensure public key is unique 128 usr, err := p.db.UserGetByPubKey(nu.PublicKey) 129 if err != nil { 130 if errors.Is(err, user.ErrUserNotFound) { 131 // Pubkey is unique, but is not the same pubkey that 132 // the user originally signed up with. This is fine. 133 // The user's identity just needs to be updated. 134 id, err := user.NewIdentity(nu.PublicKey) 135 if err != nil { 136 return nil, err 137 } 138 err = u.AddIdentity(*id) 139 if err != nil { 140 return nil, err 141 } 142 } 143 } else { 144 switch { 145 case usr.ID.String() == u.ID.String(): 146 // Pubkey exists and belongs to this user. This is 147 // ok. It just means that the user is requesting a 148 // new verification token using the same identity 149 // that they signed up with. Continue. 150 default: 151 // Pubkey exists and belongs to another user 152 return nil, www.UserError{ 153 ErrorCode: www.ErrorStatusDuplicatePublicKey, 154 } 155 } 156 } 157 158 // Generate a new verification token 159 tokenb, expiry, err := newVerificationTokenAndExpiry() 160 if err != nil { 161 return nil, err 162 } 163 164 // Email the verification token before updating the 165 // database. If the email fails, the database won't 166 // be updated. 167 err = p.emailUserEmailVerify(u.Email, 168 hex.EncodeToString(tokenb), u.Username) 169 if err != nil { 170 log.Errorf("processNewUser: mail verification "+ 171 "token failed for '%v': %v", u.Email, err) 172 return &www.NewUserReply{}, nil 173 } 174 175 // Update user record with the verification token and 176 // the new identity if one was set. 177 u.NewUserVerificationToken = tokenb 178 u.NewUserVerificationExpiry = expiry 179 err = p.db.UserUpdate(*u) 180 if err != nil { 181 return nil, err 182 } 183 184 // Send reply. Only return the verification token in 185 // the reply if the mail server has been disabled. 186 var t string 187 if !p.mail.IsEnabled() { 188 t = hex.EncodeToString(u.NewUserVerificationToken) 189 } 190 return &www.NewUserReply{ 191 VerificationToken: t, 192 }, nil 193 case user.ErrUserNotFound: 194 // User doesn't exist; continue 195 default: 196 // All other errors 197 return nil, err 198 } 199 200 // User does not exist. Create a new user. 201 202 // Ensure username is unique 203 _, err = p.db.UserGetByUsername(nu.Username) 204 switch err { 205 case nil: 206 // Duplicate username 207 return nil, www.UserError{ 208 ErrorCode: www.ErrorStatusDuplicateUsername, 209 } 210 case user.ErrUserNotFound: 211 // Username does not exist; continue 212 default: 213 return nil, err 214 } 215 216 // Ensure public key is unique 217 _, err = p.db.UserGetByPubKey(nu.PublicKey) 218 switch err { 219 case user.ErrUserNotFound: 220 // Pubkey is unique; continue 221 case nil: 222 // Duplicate pubkey 223 return nil, www.UserError{ 224 ErrorCode: www.ErrorStatusDuplicatePublicKey, 225 } 226 default: 227 // All other errors 228 return nil, err 229 } 230 231 // Create user 232 hashedPass, err := p.hashPassword(nu.Password) 233 if err != nil { 234 return nil, err 235 } 236 tokenb, expiry, err := newVerificationTokenAndExpiry() 237 if err != nil { 238 return nil, err 239 } 240 newUser := user.User{ 241 Email: nu.Email, 242 Username: nu.Username, 243 HashedPassword: hashedPass, 244 NewUserVerificationToken: tokenb, 245 NewUserVerificationExpiry: expiry, 246 } 247 id, err := user.NewIdentity(nu.PublicKey) 248 if err != nil { 249 return nil, err 250 } 251 err = newUser.AddIdentity(*id) 252 if err != nil { 253 return nil, err 254 } 255 256 // Try to email the verification link first; if it fails, 257 // then the new user won't be created. 258 // 259 // This is conditional on the email server being setup. 260 err = p.emailUserEmailVerify(newUser.Email, 261 hex.EncodeToString(tokenb), newUser.Username) 262 if err != nil { 263 log.Errorf("processNewUser: mail verification token "+ 264 "failed for '%v': %v", newUser.Email, err) 265 return &www.NewUserReply{}, nil 266 } 267 268 // Save new user to the database 269 err = p.db.UserNew(newUser) 270 if err != nil { 271 return nil, err 272 } 273 274 // Set paywall info for the user. This had to wait until after the 275 // user was already created in the db because the db sets the 276 // paywall address index when the user is created, and the paywall 277 // address index is used to generate the paywall info. Lookup the 278 // user from the db to get the paywall address index. 279 u, err = p.db.UserGetByUsername(newUser.Username) 280 if err != nil { 281 return nil, err 282 } 283 err = p.generateNewUserPaywall(u) 284 if err != nil { 285 return nil, err 286 } 287 288 // Update memory cache 289 p.setUserEmailsCache(u.Email, u.ID) 290 291 log.Infof("New user created: %v", u.Username) 292 293 // Only return the verification token in the reply 294 // if the mail server has been disabled. 295 var t string 296 if !p.mail.IsEnabled() { 297 t = hex.EncodeToString(u.NewUserVerificationToken) 298 } 299 return &www.NewUserReply{ 300 VerificationToken: t, 301 }, nil 302 } 303 304 // processVerifyNewUser verifies the token generated for a recently created 305 // user. It ensures that the token matches with the input and that the token 306 // hasn't expired. On success it returns database user record. 307 func (p *Politeiawww) processVerifyNewUser(usr www.VerifyNewUser) (*user.User, error) { 308 // Check that the user already exists. 309 u, err := p.userByEmail(usr.Email) 310 if err != nil { 311 if errors.Is(err, user.ErrUserNotFound) { 312 log.Debugf("VerifyNewUser failure for %v: user not found", 313 usr.Email) 314 return nil, www.UserError{ 315 ErrorCode: www.ErrorStatusVerificationTokenInvalid, 316 } 317 } 318 return nil, err 319 } 320 321 // Decode the verification token. 322 token, err := hex.DecodeString(usr.VerificationToken) 323 if err != nil { 324 log.Debugf("VerifyNewUser failure for %v: verification token could "+ 325 "not be decoded: %v", u.Email, err) 326 return nil, www.UserError{ 327 ErrorCode: www.ErrorStatusVerificationTokenInvalid, 328 } 329 } 330 331 // Check that the verification token matches. 332 if !bytes.Equal(token, u.NewUserVerificationToken) { 333 log.Debugf("VerifyNewUser: wrong token for user %v "+ 334 "got %v, want %v", u.Email, hex.EncodeToString(token), 335 hex.EncodeToString(u.NewUserVerificationToken)) 336 return nil, www.UserError{ 337 ErrorCode: www.ErrorStatusVerificationTokenInvalid, 338 } 339 } 340 341 // Check that the token hasn't expired. 342 if time.Now().Unix() > u.NewUserVerificationExpiry { 343 log.Debugf("VerifyNewUser failure for %v: verification token expired", 344 u.Email) 345 return nil, www.UserError{ 346 ErrorCode: www.ErrorStatusVerificationTokenExpired, 347 } 348 } 349 350 // Check signature 351 sig, err := util.ConvertSignature(usr.Signature) 352 if err != nil { 353 log.Debugf("VerifyNewUser failure for %v: signature could not be "+ 354 "decoded: %v", u.Email, err) 355 return nil, www.UserError{ 356 ErrorCode: www.ErrorStatusInvalidSignature, 357 } 358 } 359 id := u.InactiveIdentity() 360 if id == nil { 361 log.Debugf("VerifyNewUser failure for %v: no public key", 362 u.Email) 363 return nil, www.UserError{ 364 ErrorCode: www.ErrorStatusNoPublicKey, 365 } 366 } 367 pi, err := identity.PublicIdentityFromBytes(id.Key[:]) 368 if err != nil { 369 return nil, err 370 } 371 if !pi.VerifyMessage([]byte(usr.VerificationToken), sig) { 372 log.Debugf("VerifyNewUser failure for %v: signature doesn't match "+ 373 "(pubkey: %v)", u.Email, pi.String()) 374 return nil, www.UserError{ 375 ErrorCode: www.ErrorStatusInvalidSignature, 376 } 377 } 378 379 // Clear out the verification token fields 380 // and activate the identity for the user. 381 u.NewUserVerificationToken = nil 382 u.NewUserVerificationExpiry = 0 383 u.ResendNewUserVerificationExpiry = 0 384 err = u.ActivateIdentity(id.Key[:]) 385 if err != nil { 386 return nil, err 387 } 388 err = p.db.UserUpdate(*u) 389 if err != nil { 390 return nil, err 391 } 392 393 p.addUserToPaywallPoolLock(u, paywallTypeUser) 394 395 return u, nil 396 } 397 398 // processResendVerification resends a new user verification email if the 399 // user exists and his verification token is expired. 400 func (p *Politeiawww) processResendVerification(rv *www.ResendVerification) (*www.ResendVerificationReply, error) { 401 rvr := www.ResendVerificationReply{} 402 403 // Get user from db. 404 u, err := p.userByEmail(rv.Email) 405 if err != nil { 406 if errors.Is(err, user.ErrUserNotFound) { 407 log.Debugf("ResendVerification failure for %v: user not found", 408 rv.Email) 409 return nil, www.UserError{ 410 ErrorCode: www.ErrorStatusUserNotFound, 411 } 412 } 413 return nil, err 414 } 415 416 // Don't do anything if the user is already verified or the token hasn't 417 // expired yet. 418 if u.NewUserVerificationToken == nil { 419 log.Debugf("ResendVerification failure for %v: user already verified", 420 rv.Email) 421 return nil, www.UserError{ 422 ErrorCode: www.ErrorStatusEmailAlreadyVerified, 423 } 424 } 425 426 if u.ResendNewUserVerificationExpiry > time.Now().Unix() { 427 log.Debugf("ResendVerification failure for %v: verification token "+ 428 "not expired yet", rv.Email) 429 return nil, www.UserError{ 430 ErrorCode: www.ErrorStatusVerificationTokenUnexpired, 431 } 432 } 433 434 // Ensure we got a proper pubkey. 435 err = validatePubKey(rv.PublicKey) 436 if err != nil { 437 return nil, err 438 } 439 440 // Ensure the pubkey is unique 441 _, err = p.db.UserGetByPubKey(rv.PublicKey) 442 switch err { 443 case user.ErrUserNotFound: 444 // Pubkey is unique; continue 445 case nil: 446 // Pubkey is not unique. The user is allowed to use the 447 // same pubkey that they originally signed up with, so 448 // only throw an error if the pubkey is not the user's 449 // inactive identity pubkey. 450 if u.InactiveIdentity().String() != rv.PublicKey { 451 return nil, www.UserError{ 452 ErrorCode: www.ErrorStatusDuplicatePublicKey, 453 } 454 } 455 default: 456 // All other errors 457 return nil, err 458 } 459 460 // Set a new verificaton token and identity. 461 token, expiry, err := newVerificationTokenAndExpiry() 462 if err != nil { 463 return nil, err 464 } 465 u.NewUserVerificationToken = token 466 u.NewUserVerificationExpiry = expiry 467 u.ResendNewUserVerificationExpiry = expiry 468 id, err := user.NewIdentity(rv.PublicKey) 469 if err != nil { 470 return nil, err 471 } 472 err = u.AddIdentity(*id) 473 if err != nil { 474 return nil, err 475 } 476 477 // Try to email the verification link first; if it fails, then 478 // the user won't be updated. 479 // 480 // This is conditional on the email server being setup. 481 err = p.emailUserEmailVerify(u.Email, 482 hex.EncodeToString(token), u.Username) 483 if err != nil { 484 log.Errorf("processResendVerification: email verification "+ 485 "token failed for '%v': %v", u.Email, err) 486 return nil, err 487 } 488 489 // Update the user in the db. 490 err = p.db.UserUpdate(*u) 491 if err != nil { 492 return nil, err 493 } 494 495 // Only set the token if email verification is disabled. 496 if !p.mail.IsEnabled() { 497 rvr.VerificationToken = hex.EncodeToString(token) 498 } 499 return &rvr, nil 500 } 501 502 // processLogin logs the provided user into politeia. This is done using go 503 // routines in order to prevent timing attacks. 504 func (p *Politeiawww) processLogin(l www.Login) (*www.LoginReply, error) { 505 log.Tracef("processLogin: %v", l.Email) 506 507 var ( 508 wg sync.WaitGroup 509 ch = make(chan loginResult) 510 ) 511 512 // Wait for both go routines to finish before returning the 513 // reply. This is done to prevent an attacker from being able 514 // to execute a timing attack to determine if the provided 515 // email address is the user's valid email address. 516 wg.Add(2) 517 go func() { 518 defer wg.Done() 519 ch <- p.login(l) 520 }() 521 go func() { 522 defer wg.Done() 523 time.Sleep(loginMinWaitTime) 524 }() 525 lr := <-ch 526 wg.Wait() 527 528 return lr.reply, lr.err 529 } 530 531 // processResetPassword is used to perform a password change when the user is 532 // not logged in. The provided email address must match the email address 533 // or the user record that corresponds to the provided username. 534 func (p *Politeiawww) processResetPassword(rp www.ResetPassword) (*www.ResetPasswordReply, error) { 535 log.Tracef("processResetPassword: %v", rp.Username) 536 var ( 537 wg sync.WaitGroup 538 ch = make(chan resetPasswordResult) 539 ) 540 541 // Wait for both go routines to finish before returning the 542 // reply. This is done to prevent an attacker from being able 543 // to execute a timing attack to determine if the provided 544 // email address is the user's valid email address. 545 wg.Add(2) 546 go func() { 547 defer wg.Done() 548 ch <- p.resetPassword(rp) 549 }() 550 go func() { 551 defer wg.Done() 552 time.Sleep(resetPasswordMinWaitTime) 553 }() 554 rpr := <-ch 555 wg.Wait() 556 557 return &rpr.reply, rpr.err 558 } 559 560 // processVerifyResetPassword verifies the token that was sent to the user 561 // during the reset password command. If everything checks out, the user's 562 // password is updated with the provided new password and the user's account 563 // is unlocked if it had previously been locked. 564 func (p *Politeiawww) processVerifyResetPassword(vrp www.VerifyResetPassword) (*www.VerifyResetPasswordReply, error) { 565 log.Tracef("processVerifyResetPassword: %v %v", 566 vrp.Username, vrp.VerificationToken) 567 568 // Lookup user 569 u, err := p.db.UserGetByUsername(vrp.Username) 570 if err != nil { 571 if errors.Is(err, user.ErrUserNotFound) { 572 err = www.UserError{ 573 ErrorCode: www.ErrorStatusUserNotFound, 574 } 575 } 576 return nil, err 577 } 578 579 // Validate verification token 580 token, err := hex.DecodeString(vrp.VerificationToken) 581 if err != nil { 582 log.Debugf("processVerifyResetPassword: decode hex '%v': %v", 583 vrp.VerificationToken, err) 584 return nil, www.UserError{ 585 ErrorCode: www.ErrorStatusVerificationTokenInvalid, 586 } 587 } 588 if !bytes.Equal(token, u.ResetPasswordVerificationToken) { 589 log.Debugf("processVerifyResetPassword: wrong token: %v %v", 590 hex.EncodeToString(token), 591 hex.EncodeToString(u.ResetPasswordVerificationToken)) 592 return nil, www.UserError{ 593 ErrorCode: www.ErrorStatusVerificationTokenInvalid, 594 } 595 } 596 if u.ResetPasswordVerificationExpiry < time.Now().Unix() { 597 log.Debugf("processVerifyResetPassword: token expired: %v %v", 598 u.ResetPasswordVerificationExpiry, time.Now().Unix()) 599 return nil, www.UserError{ 600 ErrorCode: www.ErrorStatusVerificationTokenExpired, 601 } 602 } 603 604 // The client should be hashing the password before sending 605 // it to politeiawww. This validation is only relevant if the 606 // client failed to hash the password or does not include a 607 // password in the request. 608 err = validatePassword(vrp.NewPassword) 609 if err != nil { 610 return nil, err 611 } 612 613 // Hash the new password 614 hashedPassword, err := p.hashPassword(vrp.NewPassword) 615 if err != nil { 616 return nil, err 617 } 618 619 // Update the user 620 u.ResetPasswordVerificationToken = nil 621 u.ResetPasswordVerificationExpiry = 0 622 u.HashedPassword = hashedPassword 623 u.FailedLoginAttempts = 0 624 625 err = p.db.UserUpdate(*u) 626 if err != nil { 627 return nil, err 628 } 629 630 return &www.VerifyResetPasswordReply{}, nil 631 } 632 633 // processUserDetails return the requested user's details. Some fields can be 634 // omitted or blank depending on the requester's access level. 635 func (p *Politeiawww) processUserDetails(ud *www.UserDetails, isCurrentUser bool, isAdmin bool) (*www.UserDetailsReply, error) { 636 // Fetch the database user. 637 user, err := p.userByIDStr(ud.UserID) 638 if err != nil { 639 return nil, err 640 } 641 642 // Convert the database user into a proper response. 643 var udr www.UserDetailsReply 644 wwwUser := convertWWWUserFromDatabaseUser(user) 645 646 // Filter returned fields in case the user isn't the admin or the current user 647 if !isAdmin && !isCurrentUser { 648 udr.User = filterUserPublicFields(wwwUser) 649 } else { 650 udr.User = wwwUser 651 } 652 653 return &udr, nil 654 } 655 656 // processEditUser edits a user's preferences. 657 func (p *Politeiawww) processEditUser(eu *www.EditUser, user *user.User) (*www.EditUserReply, error) { 658 if eu.EmailNotifications != nil { 659 user.EmailNotifications = *eu.EmailNotifications 660 } 661 662 // Update the user in the database. 663 err := p.db.UserUpdate(*user) 664 if err != nil { 665 return nil, err 666 } 667 668 return &www.EditUserReply{}, nil 669 } 670 671 // processUpdateUserKey sets a verification token and expiry to allow the user 672 // to update his key pair; the token must be verified before it expires. If the 673 // token is already set and is expired, it generates a new one. 674 func (p *Politeiawww) processUpdateUserKey(usr *user.User, uuk www.UpdateUserKey) (*www.UpdateUserKeyReply, error) { 675 // Ensure we got a proper pubkey that is unique. 676 err := validatePubKey(uuk.PublicKey) 677 if err != nil { 678 return nil, err 679 } 680 _, err = p.db.UserGetByPubKey(uuk.PublicKey) 681 switch err { 682 case user.ErrUserNotFound: 683 // Pubkey is unique; continue 684 case nil: 685 // Pubkey is not unique 686 return nil, www.UserError{ 687 ErrorCode: www.ErrorStatusDuplicatePublicKey, 688 } 689 default: 690 // All other errors 691 return nil, err 692 } 693 694 // Check if the verification token hasn't expired yet. 695 if usr.UpdateKeyVerificationToken != nil { 696 if usr.UpdateKeyVerificationExpiry > time.Now().Unix() { 697 return nil, www.UserError{ 698 ErrorCode: www.ErrorStatusVerificationTokenUnexpired, 699 ErrorContext: []string{ 700 strconv.FormatInt(usr.UpdateKeyVerificationExpiry, 10), 701 }, 702 } 703 } 704 } 705 706 // Generate a new verification token and expiry. 707 tokenb, expiry, err := newVerificationTokenAndExpiry() 708 if err != nil { 709 return nil, err 710 } 711 usr.UpdateKeyVerificationToken = tokenb 712 usr.UpdateKeyVerificationExpiry = expiry 713 714 // Add inactive identity to the user 715 id, err := user.NewIdentity(uuk.PublicKey) 716 if err != nil { 717 return nil, err 718 } 719 err = usr.AddIdentity(*id) 720 if err != nil { 721 return nil, err 722 } 723 724 // Email the user a verification link. The database does not get 725 // updated if this fails. 726 // 727 // This is conditional on the email server being setup. 728 token := hex.EncodeToString(tokenb) 729 recipient := map[uuid.UUID]string{ 730 usr.ID: usr.Email, 731 } 732 err = p.emailUserKeyUpdate(usr.Username, uuk.PublicKey, token, recipient) 733 if err != nil { 734 return nil, err 735 } 736 737 // Save user changes to the database 738 err = p.db.UserUpdate(*usr) 739 if err != nil { 740 return nil, err 741 } 742 743 // Only set the token if email verification is disabled. 744 var t string 745 if !p.mail.IsEnabled() { 746 t = token 747 } 748 return &www.UpdateUserKeyReply{ 749 VerificationToken: t, 750 }, nil 751 } 752 753 // processVerifyUpdateUserKey verifies the token generated for the recently 754 // generated key pair. It ensures that the token matches with the input and 755 // that the token hasn't expired. 756 func (p *Politeiawww) processVerifyUpdateUserKey(u *user.User, vu www.VerifyUpdateUserKey) (*user.User, error) { 757 // Decode the verification token. 758 token, err := hex.DecodeString(vu.VerificationToken) 759 if err != nil { 760 log.Debugf("VerifyUpdateUserKey failure for %v: verification "+ 761 "token could not be decoded: %v", u.Email, err) 762 return nil, www.UserError{ 763 ErrorCode: www.ErrorStatusVerificationTokenInvalid, 764 } 765 } 766 767 // Check that the verification token matches. 768 if !bytes.Equal(token, u.UpdateKeyVerificationToken) { 769 log.Debugf("VerifyUpdateUserKey failure for %v: verification "+ 770 "token doesn't match, expected %v", u.Email, 771 u.UpdateKeyVerificationToken, token) 772 return nil, www.UserError{ 773 ErrorCode: www.ErrorStatusVerificationTokenInvalid, 774 } 775 } 776 777 // Check that the token hasn't expired. 778 if u.UpdateKeyVerificationExpiry < time.Now().Unix() { 779 log.Debugf("VerifyUpdateUserKey failure for %v: verification "+ 780 "token not expired yet", u.Email) 781 return nil, www.UserError{ 782 ErrorCode: www.ErrorStatusVerificationTokenExpired, 783 } 784 } 785 786 // Check signature 787 sig, err := util.ConvertSignature(vu.Signature) 788 if err != nil { 789 log.Debugf("VerifyUpdateUserKey failure for %v: signature "+ 790 "could not be decoded: %v", u.Email, err) 791 return nil, www.UserError{ 792 ErrorCode: www.ErrorStatusInvalidSignature, 793 } 794 } 795 796 id := u.InactiveIdentity() 797 if id == nil { 798 return nil, www.UserError{ 799 ErrorCode: www.ErrorStatusNoPublicKey, 800 } 801 } 802 pi, err := identity.PublicIdentityFromBytes(id.Key[:]) 803 if err != nil { 804 return nil, err 805 } 806 807 if !pi.VerifyMessage([]byte(vu.VerificationToken), sig) { 808 log.Debugf("VerifyUpdateUserKey failure for %v: signature did"+ 809 " not match (pubkey: %v)", u.Email, pi.String()) 810 return nil, www.UserError{ 811 ErrorCode: www.ErrorStatusInvalidSignature, 812 } 813 } 814 815 // Clear out the verification token fields in the db and activate 816 // the key and deactivate the one it's replacing. 817 u.UpdateKeyVerificationToken = nil 818 u.UpdateKeyVerificationExpiry = 0 819 err = u.ActivateIdentity(id.Key[:]) 820 if err != nil { 821 return nil, err 822 } 823 824 return u, p.db.UserUpdate(*u) 825 } 826 827 // processChangeUsername checks that the password matches the one 828 // in the database, then checks that the username is valid and not 829 // already taken, then changes the user record in the database to 830 // the new username. 831 func (p *Politeiawww) processChangeUsername(email string, cu www.ChangeUsername) (*www.ChangeUsernameReply, error) { 832 var reply www.ChangeUsernameReply 833 834 // Get user from db. 835 u, err := p.userByEmail(email) 836 if err != nil { 837 return nil, err 838 } 839 840 // Check the user's password. 841 err = bcrypt.CompareHashAndPassword(u.HashedPassword, 842 []byte(cu.Password)) 843 if err != nil { 844 return nil, www.UserError{ 845 ErrorCode: www.ErrorStatusInvalidPassword, 846 } 847 } 848 849 // Format and validate the new username. 850 newUsername := formatUsername(cu.NewUsername) 851 err = validateUsername(newUsername) 852 if err != nil { 853 return nil, err 854 } 855 856 // Check for duplicate username 857 _, err = p.db.UserGetByUsername(newUsername) 858 switch err { 859 case nil: 860 // Duplicate 861 return nil, www.UserError{ 862 ErrorCode: www.ErrorStatusDuplicateUsername, 863 } 864 case user.ErrUserNotFound: 865 // Doesn't exist, update username. 866 default: 867 // All other errors 868 return nil, err 869 } 870 871 // Add the updated user information to the db. 872 u.Username = newUsername 873 err = p.db.UserUpdate(*u) 874 if err != nil { 875 return nil, err 876 } 877 878 return &reply, nil 879 } 880 881 // processChangePassword checks that the current password matches the one 882 // in the database, then changes it to the new password. 883 func (p *Politeiawww) processChangePassword(email string, cp www.ChangePassword) (*www.ChangePasswordReply, error) { 884 var reply www.ChangePasswordReply 885 886 // Get user from db. 887 u, err := p.userByEmail(email) 888 if err != nil { 889 return nil, err 890 } 891 892 // Check the user's password. 893 err = bcrypt.CompareHashAndPassword(u.HashedPassword, 894 []byte(cp.CurrentPassword)) 895 if err != nil { 896 return nil, www.UserError{ 897 ErrorCode: www.ErrorStatusInvalidPassword, 898 } 899 } 900 901 // Validate the new password. 902 err = validatePassword(cp.NewPassword) 903 if err != nil { 904 return nil, err 905 } 906 907 // Hash the user's password. 908 hashedPassword, err := p.hashPassword(cp.NewPassword) 909 if err != nil { 910 return nil, err 911 } 912 913 // Add the updated user information to the db. 914 u.HashedPassword = hashedPassword 915 916 // We will also reset any possibly issued verification token to avoid 917 // a small chance of one having been issued by a potential attacker. 918 // Any update to the password by a logged in user, should be seen as 919 // an authorized request and therefore override any potential request. 920 u.ResetPasswordVerificationToken = nil 921 u.ResetPasswordVerificationExpiry = 0 922 923 err = p.db.UserUpdate(*u) 924 if err != nil { 925 return nil, err 926 } 927 928 recipient := map[uuid.UUID]string{ 929 u.ID: u.Email, 930 } 931 err = p.emailUserPasswordChanged(u.Username, recipient) 932 if err != nil { 933 return nil, err 934 } 935 936 return &reply, nil 937 } 938 939 // processUsers returns a list of users given a set of filters. Admins can 940 // search by pubkey, username or email. Username and email searches will 941 // return partial matches. Pubkey searches must be an exact match. Non admins 942 // can search by pubkey or username. Non admin searches will only return exact 943 // matches. 944 func (p *Politeiawww) processUsers(users *www.Users, isAdmin bool) (*www.UsersReply, error) { 945 log.Tracef("processUsers: %v", isAdmin) 946 947 emailQuery := strings.ToLower(users.Email) 948 usernameQuery := formatUsername(users.Username) 949 pubkeyQuery := users.PublicKey 950 951 var u *user.User 952 var totalUsers uint64 953 var totalMatches uint64 954 var pubkeyMatchID string 955 matchedUsers := make([]www.AbridgedUser, 0, www.UserListPageSize) 956 957 if pubkeyQuery != "" { 958 // Search by pubkey. Only exact matches are returned. 959 // Validate pubkey 960 err := validatePubKey(pubkeyQuery) 961 if err != nil { 962 return nil, err 963 } 964 965 u, err = p.db.UserGetByPubKey(pubkeyQuery) 966 if err != nil { 967 if errors.Is(err, user.ErrUserNotFound) { 968 // Pubkey searches require an exact match. If no 969 // match was found, we can go ahead and return. 970 return &www.UsersReply{}, nil 971 } 972 return nil, err 973 } 974 975 pubkeyMatchID = u.ID.String() 976 } 977 978 switch { 979 case isAdmin: 980 // Admins can search by username and/or email with 981 // partial matches being returned. 982 err := p.db.AllUsers(func(user *user.User) { 983 totalUsers++ 984 userMatches := true 985 986 // If both emailQuery and usernameQuery are non-empty, the 987 // user must match both to be included in the results. 988 if emailQuery != "" { 989 if !strings.Contains(strings.ToLower(user.Email), 990 emailQuery) { 991 userMatches = false 992 } 993 } 994 995 if usernameQuery != "" && userMatches { 996 if !strings.Contains(strings.ToLower(user.Username), 997 usernameQuery) { 998 userMatches = false 999 } 1000 } 1001 1002 if pubkeyQuery != "" && userMatches { 1003 if user.ID.String() != pubkeyMatchID { 1004 userMatches = false 1005 } 1006 } 1007 1008 if userMatches { 1009 totalMatches++ 1010 if totalMatches < www.UserListPageSize { 1011 matchedUsers = append(matchedUsers, www.AbridgedUser{ 1012 ID: user.ID.String(), 1013 Email: user.Email, 1014 Username: user.Username, 1015 }) 1016 } 1017 } 1018 }) 1019 if err != nil { 1020 return nil, err 1021 } 1022 1023 // Sort results alphabetically. 1024 sort.Slice(matchedUsers, func(i, j int) bool { 1025 return matchedUsers[i].Username < matchedUsers[j].Username 1026 }) 1027 1028 default: 1029 // Non-admins can search by username and the search 1030 // must be an exact match. 1031 if usernameQuery != "" { 1032 // Validate username 1033 err := validateUsername(usernameQuery) 1034 if err != nil { 1035 return nil, err 1036 } 1037 1038 u, err = p.db.UserGetByUsername(usernameQuery) 1039 if err != nil { 1040 // ErrUserNotFound is ok. Empty search results 1041 // will be returned. 1042 if !errors.Is(err, user.ErrUserNotFound) { 1043 return nil, err 1044 } 1045 } 1046 1047 // If both pubkeyQuery and usernameQuery are non-empty, the 1048 // user must match both to be included in the results. 1049 if (u != nil) && (pubkeyQuery != "") && 1050 (u.ID.String() != pubkeyMatchID) { 1051 // User doesn't match both 1052 u = nil 1053 } 1054 } 1055 1056 if u != nil { 1057 totalMatches++ 1058 matchedUsers = append(matchedUsers, www.AbridgedUser{ 1059 ID: u.ID.String(), 1060 Username: u.Username}) 1061 } 1062 } 1063 1064 return &www.UsersReply{ 1065 TotalUsers: totalUsers, 1066 TotalMatches: totalMatches, 1067 Users: matchedUsers, 1068 }, nil 1069 } 1070 1071 // processManageUser processes the admin ManageUser command. 1072 func (p *Politeiawww) processManageUser(mu *www.ManageUser, adminUser *user.User) (*www.ManageUserReply, error) { 1073 // Fetch the database user. 1074 user, err := p.userByIDStr(mu.UserID) 1075 if err != nil { 1076 return nil, err 1077 } 1078 1079 // Validate that the action is valid. 1080 if mu.Action == www.UserManageInvalid { 1081 return nil, www.UserError{ 1082 ErrorCode: www.ErrorStatusInvalidUserManageAction, 1083 } 1084 } 1085 1086 // Validate that the reason is supplied. 1087 mu.Reason = strings.TrimSpace(mu.Reason) 1088 if len(mu.Reason) == 0 { 1089 return nil, www.UserError{ 1090 ErrorCode: www.ErrorStatusInvalidInput, 1091 } 1092 } 1093 1094 // -168 hours is 7 days in the past 1095 expiredTime := time.Now().Add(-168 * time.Hour).Unix() 1096 1097 switch mu.Action { 1098 case www.UserManageExpireNewUserVerification: 1099 user.NewUserVerificationExpiry = expiredTime 1100 user.ResendNewUserVerificationExpiry = expiredTime 1101 case www.UserManageExpireUpdateKeyVerification: 1102 user.UpdateKeyVerificationExpiry = expiredTime 1103 case www.UserManageExpireResetPasswordVerification: 1104 user.ResetPasswordVerificationExpiry = expiredTime 1105 case www.UserManageClearUserPaywall: 1106 p.removeUsersFromPool([]uuid.UUID{user.ID}, paywallTypeUser) 1107 user.NewUserPaywallAmount = 0 1108 user.NewUserPaywallTx = "cleared_by_admin" 1109 user.NewUserPaywallPollExpiry = 0 1110 case www.UserManageUnlock: 1111 user.FailedLoginAttempts = 0 1112 case www.UserManageDeactivate: 1113 user.Deactivated = true 1114 case www.UserManageReactivate: 1115 user.Deactivated = false 1116 default: 1117 return nil, www.UserError{ 1118 ErrorCode: www.ErrorStatusInvalidUserManageAction, 1119 } 1120 } 1121 1122 // Update the user in the database. 1123 err = p.db.UserUpdate(*user) 1124 if err != nil { 1125 return nil, err 1126 } 1127 1128 return &www.ManageUserReply{}, nil 1129 } 1130 1131 // processSetTOTP attempts to set a new TOTP key based on the given TOTP type. 1132 func (p *Politeiawww) processSetTOTP(st www.SetTOTP, u *user.User) (*www.SetTOTPReply, error) { 1133 log.Tracef("processSetTOTP: %v", u.ID.String()) 1134 // if the user already has a TOTP secret set, check the code that was given 1135 // as well to see if it matches to update. 1136 if u.TOTPSecret != "" && u.TOTPVerified { 1137 valid, err := p.totpValidate(st.Code, u.TOTPSecret, time.Now()) 1138 if err != nil { 1139 log.Debugf("Error valdiating totp code %v", err) 1140 return nil, www.UserError{ 1141 ErrorCode: www.ErrorStatusTOTPFailedValidation, 1142 } 1143 } 1144 if !valid { 1145 return nil, www.UserError{ 1146 ErrorCode: www.ErrorStatusTOTPFailedValidation, 1147 } 1148 } 1149 } 1150 1151 // Validate TOTP type that was selected. 1152 if _, ok := validTOTPTypes[st.Type]; !ok { 1153 return nil, www.UserError{ 1154 ErrorCode: www.ErrorStatusTOTPInvalidType, 1155 } 1156 } 1157 1158 issuer := defaultPoliteiaIssuer 1159 if p.cfg.Mode == config.CMSWWWMode { 1160 issuer = defaultCMSIssuer 1161 } 1162 opts := p.totpGenerateOpts(issuer, u.Username) 1163 key, err := totp.Generate(opts) 1164 if err != nil { 1165 return nil, err 1166 } 1167 // Convert TOTP key into a PNG 1168 var buf bytes.Buffer 1169 img, err := key.Image(200, 200) 1170 if err != nil { 1171 return nil, err 1172 } 1173 png.Encode(&buf, img) 1174 1175 u.TOTPType = int(st.Type) 1176 u.TOTPSecret = key.Secret() 1177 u.TOTPVerified = false 1178 u.TOTPLastUpdated = append(u.TOTPLastUpdated, time.Now().Unix()) 1179 1180 err = p.db.UserUpdate(*u) 1181 if err != nil { 1182 return nil, err 1183 } 1184 1185 return &www.SetTOTPReply{ 1186 Key: key.Secret(), 1187 Image: base64.StdEncoding.EncodeToString(buf.Bytes()), 1188 }, nil 1189 } 1190 1191 // processVerifyTOTP attempts to confirm a newly set TOTP key based on the 1192 // given TOTP type. 1193 func (p *Politeiawww) processVerifyTOTP(vt www.VerifyTOTP, u *user.User) (*www.VerifyTOTPReply, error) { 1194 valid, err := p.totpValidate(vt.Code, u.TOTPSecret, time.Now()) 1195 if err != nil { 1196 log.Debugf("Error valdiating totp code %v", err) 1197 return nil, www.UserError{ 1198 ErrorCode: www.ErrorStatusTOTPFailedValidation, 1199 } 1200 } 1201 if !valid { 1202 return nil, www.UserError{ 1203 ErrorCode: www.ErrorStatusTOTPFailedValidation, 1204 } 1205 } 1206 1207 u.TOTPVerified = true 1208 u.TOTPLastUpdated = append(u.TOTPLastUpdated, time.Now().Unix()) 1209 1210 err = p.db.UserUpdate(*u) 1211 if err != nil { 1212 return nil, err 1213 } 1214 1215 return nil, nil 1216 } 1217 1218 // loginReply is used to pass the results of the login command between go 1219 // routines. 1220 type loginResult struct { 1221 reply *www.LoginReply 1222 err error 1223 } 1224 1225 func (p *Politeiawww) login(l www.Login) loginResult { 1226 // Get user record 1227 u, err := p.userByEmail(l.Email) 1228 if err != nil { 1229 if errors.Is(err, user.ErrUserNotFound) { 1230 log.Debugf("login: user not found for email '%v'", 1231 l.Email) 1232 err = www.UserError{ 1233 ErrorCode: www.ErrorStatusInvalidLogin, 1234 } 1235 } 1236 return loginResult{ 1237 reply: nil, 1238 err: err, 1239 } 1240 } 1241 1242 // First check if TOTP is enabled and verified. 1243 if u.TOTPVerified { 1244 err := p.totpCheck(l.Code, u) 1245 if err != nil { 1246 return loginResult{ 1247 reply: nil, 1248 err: err, 1249 } 1250 } 1251 } 1252 1253 // Verify password 1254 err = bcrypt.CompareHashAndPassword(u.HashedPassword, 1255 []byte(l.Password)) 1256 if err != nil { 1257 // Wrong password. Update user record with failed attempt. 1258 log.Debugf("login: wrong password") 1259 if !userIsLocked(u.FailedLoginAttempts) { 1260 u.FailedLoginAttempts++ 1261 u.TOTPLastFailedCodeTime = make([]int64, 0, 2) 1262 err := p.db.UserUpdate(*u) 1263 if err != nil { 1264 return loginResult{ 1265 reply: nil, 1266 err: err, 1267 } 1268 } 1269 // If the failed attempt puts the user over the limit, 1270 // send them an email informing them their account is 1271 // now locked. 1272 if userIsLocked(u.FailedLoginAttempts) { 1273 recipient := map[uuid.UUID]string{ 1274 u.ID: u.Email, 1275 } 1276 err := p.emailUserAccountLocked(u.Username, recipient) 1277 if err != nil { 1278 return loginResult{ 1279 reply: nil, 1280 err: err, 1281 } 1282 } 1283 } 1284 } 1285 return loginResult{ 1286 reply: nil, 1287 err: www.UserError{ 1288 ErrorCode: www.ErrorStatusInvalidLogin, 1289 }, 1290 } 1291 } 1292 1293 // Verify user account is in good standing 1294 if u.NewUserVerificationToken != nil { 1295 return loginResult{ 1296 reply: nil, 1297 err: www.UserError{ 1298 ErrorCode: www.ErrorStatusEmailNotVerified, 1299 }, 1300 } 1301 } 1302 if u.Deactivated { 1303 return loginResult{ 1304 reply: nil, 1305 err: www.UserError{ 1306 ErrorCode: www.ErrorStatusUserDeactivated, 1307 }, 1308 } 1309 } 1310 if userIsLocked(u.FailedLoginAttempts) { 1311 return loginResult{ 1312 reply: nil, 1313 err: www.UserError{ 1314 ErrorCode: www.ErrorStatusUserLocked, 1315 }, 1316 } 1317 } 1318 1319 // Update user record with successful login 1320 lastLoginTime := u.LastLoginTime 1321 u.FailedLoginAttempts = 0 1322 u.LastLoginTime = time.Now().Unix() 1323 u.TOTPLastFailedCodeTime = make([]int64, 0, 2) 1324 err = p.db.UserUpdate(*u) 1325 if err != nil { 1326 return loginResult{ 1327 reply: nil, 1328 err: err, 1329 } 1330 } 1331 1332 reply, err := p.createLoginReply(u, lastLoginTime) 1333 return loginResult{ 1334 reply: reply, 1335 err: err, 1336 } 1337 } 1338 1339 // createLoginReply creates a login reply. 1340 func (p *Politeiawww) createLoginReply(u *user.User, lastLoginTime int64) (*www.LoginReply, error) { 1341 reply := www.LoginReply{ 1342 IsAdmin: u.Admin, 1343 UserID: u.ID.String(), 1344 Email: u.Email, 1345 Username: u.Username, 1346 PublicKey: u.PublicKey(), 1347 PaywallAddress: u.NewUserPaywallAddress, 1348 PaywallAmount: u.NewUserPaywallAmount, 1349 PaywallTxNotBefore: u.NewUserPaywallTxNotBefore, 1350 PaywallTxID: u.NewUserPaywallTx, 1351 ProposalCredits: uint64(len(u.UnspentProposalCredits)), 1352 LastLoginTime: lastLoginTime, 1353 TOTPVerified: u.TOTPVerified, 1354 } 1355 1356 if !p.userHasPaid(*u) { 1357 err := p.generateNewUserPaywall(u) 1358 if err != nil { 1359 return nil, err 1360 } 1361 1362 reply.PaywallAddress = u.NewUserPaywallAddress 1363 reply.PaywallAmount = u.NewUserPaywallAmount 1364 reply.PaywallTxNotBefore = u.NewUserPaywallTxNotBefore 1365 } 1366 1367 return &reply, nil 1368 } 1369 1370 // resetPassword is used to pass the results of the reset password command 1371 // between go routines. 1372 type resetPasswordResult struct { 1373 reply www.ResetPasswordReply 1374 err error 1375 } 1376 1377 func (p *Politeiawww) resetPassword(rp www.ResetPassword) resetPasswordResult { 1378 // Lookup user 1379 u, err := p.db.UserGetByUsername(rp.Username) 1380 if err != nil { 1381 if errors.Is(err, user.ErrUserNotFound) { 1382 err = www.UserError{ 1383 ErrorCode: www.ErrorStatusUserNotFound, 1384 } 1385 } 1386 return resetPasswordResult{ 1387 err: err, 1388 } 1389 } 1390 1391 // Ensure the provided email address matches the user record 1392 // email address. If the addresses does't match, return so 1393 // that the verification token doesn't get sent. 1394 if rp.Email != u.Email { 1395 log.Debugf("resetPassword: wrong email: %v %v", 1396 rp.Email, u.Email) 1397 return resetPasswordResult{} 1398 } 1399 1400 // If the user already has a verification token that has not 1401 // yet expired, do nothing. 1402 t := time.Now().Unix() 1403 if t < u.ResetPasswordVerificationExpiry { 1404 log.Debugf("resetPassword: unexpired verification token: %v %v", 1405 t, u.ResetPasswordVerificationExpiry) 1406 return resetPasswordResult{} 1407 } 1408 1409 // The verification token is not present or is present but has expired. 1410 1411 // Generate a new verification token and expiry. 1412 tokenb, expiry, err := newVerificationTokenAndExpiry() 1413 if err != nil { 1414 return resetPasswordResult{ 1415 err: err, 1416 } 1417 } 1418 1419 // Try to email the verification link first. If it fails, the 1420 // user record won't be updated in the database. 1421 recipient := map[uuid.UUID]string{ 1422 u.ID: u.Email, 1423 } 1424 err = p.emailUserPasswordReset(rp.Username, hex.EncodeToString(tokenb), 1425 recipient) 1426 if err != nil { 1427 return resetPasswordResult{ 1428 err: err, 1429 } 1430 } 1431 1432 // Update the user record 1433 u.ResetPasswordVerificationToken = tokenb 1434 u.ResetPasswordVerificationExpiry = expiry 1435 err = p.db.UserUpdate(*u) 1436 if err != nil { 1437 return resetPasswordResult{ 1438 err: err, 1439 } 1440 } 1441 1442 // Only include the verification token in the reply if the 1443 // email server has been disabled. 1444 var reply www.ResetPasswordReply 1445 if !p.mail.IsEnabled() { 1446 reply.VerificationToken = hex.EncodeToString(tokenb) 1447 } 1448 1449 return resetPasswordResult{ 1450 reply: reply, 1451 } 1452 } 1453 1454 // userByIDStr converts the provided userIDStr to a uuid and returns the 1455 // corresponding user, if one it exists. 1456 func (p *Politeiawww) userByIDStr(userIDStr string) (*user.User, error) { 1457 userID, err := uuid.Parse(userIDStr) 1458 if err != nil { 1459 return nil, www.UserError{ 1460 ErrorCode: www.ErrorStatusInvalidUUID, 1461 } 1462 } 1463 1464 usr, err := p.db.UserGetById(userID) 1465 if err != nil { 1466 if errors.Is(err, user.ErrUserNotFound) { 1467 return nil, www.UserError{ 1468 ErrorCode: www.ErrorStatusUserNotFound, 1469 } 1470 } 1471 return nil, err 1472 } 1473 1474 return usr, nil 1475 } 1476 1477 // hashPassword hashes the given password string with the default bcrypt cost 1478 // or the minimum cost if the test flag is set to speed up running tests. 1479 func (p *Politeiawww) hashPassword(password string) ([]byte, error) { 1480 if p.test { 1481 return bcrypt.GenerateFromPassword([]byte(password), 1482 bcrypt.MinCost) 1483 } 1484 return bcrypt.GenerateFromPassword([]byte(password), 1485 bcrypt.DefaultCost) 1486 } 1487 1488 // createUsernameRegex generates a regex based on the policy supplied valid 1489 // characters in a user name. 1490 func createUsernameRegex() string { 1491 var buf bytes.Buffer 1492 buf.WriteString("^[") 1493 1494 for _, supportedChar := range www.PolicyUsernameSupportedChars { 1495 if len(supportedChar) > 1 { 1496 buf.WriteString(supportedChar) 1497 } else { 1498 buf.WriteString(`\` + supportedChar) 1499 } 1500 } 1501 buf.WriteString("]{") 1502 buf.WriteString(strconv.Itoa(www.PolicyMinUsernameLength) + ",") 1503 buf.WriteString(strconv.Itoa(www.PolicyMaxUsernameLength) + "}$") 1504 1505 return buf.String() 1506 } 1507 1508 // validatePubKey verifies that the provided public key is a valid ed25519 1509 // public key. 1510 func validatePubKey(publicKey string) error { 1511 pk, err := hex.DecodeString(publicKey) 1512 if err != nil { 1513 log.Debugf("validatePubKey: decode hex string "+ 1514 "failed for '%v': %v", publicKey, err) 1515 return www.UserError{ 1516 ErrorCode: www.ErrorStatusInvalidPublicKey, 1517 } 1518 } 1519 1520 var emptyPK [identity.PublicKeySize]byte 1521 switch { 1522 case len(pk) != len(emptyPK): 1523 log.Debugf("validatePubKey: invalid size: %v", 1524 publicKey) 1525 return www.UserError{ 1526 ErrorCode: www.ErrorStatusInvalidPublicKey, 1527 } 1528 case bytes.Equal(pk, emptyPK[:]): 1529 log.Debugf("validatePubKey: key is empty: %v", 1530 publicKey) 1531 return www.UserError{ 1532 ErrorCode: www.ErrorStatusInvalidPublicKey, 1533 } 1534 } 1535 1536 return nil 1537 } 1538 1539 // validateSignature validates an incoming signature against the specified 1540 // public key and message. This function assumes the provided public key is 1541 // valid. 1542 func validateSignature(pubKey string, signature string, elements ...string) error { 1543 sig, err := util.ConvertSignature(signature) 1544 if err != nil { 1545 return www.UserError{ 1546 ErrorCode: www.ErrorStatusInvalidSignature, 1547 } 1548 } 1549 b, err := hex.DecodeString(pubKey) 1550 if err != nil { 1551 return www.UserError{ 1552 ErrorCode: www.ErrorStatusInvalidPublicKey, 1553 } 1554 } 1555 pk, err := identity.PublicIdentityFromBytes(b) 1556 if err != nil { 1557 return err 1558 } 1559 var msg string 1560 for _, v := range elements { 1561 msg += v 1562 } 1563 if !pk.VerifyMessage([]byte(msg), sig) { 1564 return www.UserError{ 1565 ErrorCode: www.ErrorStatusInvalidSignature, 1566 } 1567 } 1568 return nil 1569 } 1570 1571 // formatUsername normalizes a username to lowercase without leading and 1572 // trailing spaces. 1573 func formatUsername(username string) string { 1574 return strings.ToLower(strings.TrimSpace(username)) 1575 } 1576 1577 // validateUsername verifies that a username adheres to required policy. 1578 func validateUsername(username string) error { 1579 if username != formatUsername(username) { 1580 log.Tracef("validateUsername: not normalized: %s %s", 1581 username, formatUsername(username)) 1582 return www.UserError{ 1583 ErrorCode: www.ErrorStatusMalformedUsername, 1584 } 1585 } 1586 if len(username) < www.PolicyMinUsernameLength || 1587 len(username) > www.PolicyMaxUsernameLength { 1588 log.Tracef("validateUsername: not within bounds: %s", 1589 username) 1590 return www.UserError{ 1591 ErrorCode: www.ErrorStatusMalformedUsername, 1592 } 1593 } 1594 if !validUsername.MatchString(username) { 1595 log.Tracef("validateUsername: not valid: %s %s", 1596 username, validUsername.String()) 1597 return www.UserError{ 1598 ErrorCode: www.ErrorStatusMalformedUsername, 1599 } 1600 } 1601 return nil 1602 } 1603 1604 // validatePassword verifies that a password adheres to required policy. 1605 func validatePassword(password string) error { 1606 if len(password) < www.PolicyMinPasswordLength { 1607 return www.UserError{ 1608 ErrorCode: www.ErrorStatusMalformedPassword, 1609 } 1610 } 1611 1612 return nil 1613 } 1614 1615 // userIsLocked returns whether the user's account has been locked due to 1616 // failed login attempts. 1617 func userIsLocked(failedLoginAttempts uint64) bool { 1618 return failedLoginAttempts >= LoginAttemptsToLockUser 1619 } 1620 1621 // newVerificationTokenAndExpiry returns a byte slice of random data that is 1622 // the size of a verification token and a UNIX timestamp that represents the 1623 // expiration of the token. 1624 func newVerificationTokenAndExpiry() ([]byte, int64, error) { 1625 tokenb, err := util.Random(www.VerificationTokenSize) 1626 if err != nil { 1627 return nil, 0, err 1628 } 1629 d := time.Duration(www.VerificationExpiryHours) * time.Hour 1630 expiry := time.Now().Add(d).Unix() 1631 return tokenb, expiry, nil 1632 } 1633 1634 // filterUserPublicFields creates a filtered copy of a www User that only 1635 // contains public information. 1636 func filterUserPublicFields(user www.User) www.User { 1637 return www.User{ 1638 ID: user.ID, 1639 Admin: user.Admin, 1640 Username: user.Username, 1641 Identities: user.Identities, 1642 } 1643 } 1644 1645 // convertWWWUserFromDatabaseUser converts a user User to a www User. 1646 func convertWWWUserFromDatabaseUser(user *user.User) www.User { 1647 return www.User{ 1648 ID: user.ID.String(), 1649 Admin: user.Admin, 1650 Email: user.Email, 1651 Username: user.Username, 1652 NewUserPaywallAddress: user.NewUserPaywallAddress, 1653 NewUserPaywallAmount: user.NewUserPaywallAmount, 1654 NewUserPaywallTx: user.NewUserPaywallTx, 1655 NewUserPaywallTxNotBefore: user.NewUserPaywallTxNotBefore, 1656 NewUserPaywallPollExpiry: user.NewUserPaywallPollExpiry, 1657 NewUserVerificationToken: user.NewUserVerificationToken, 1658 NewUserVerificationExpiry: user.NewUserVerificationExpiry, 1659 UpdateKeyVerificationToken: user.UpdateKeyVerificationToken, 1660 UpdateKeyVerificationExpiry: user.UpdateKeyVerificationExpiry, 1661 ResetPasswordVerificationToken: user.ResetPasswordVerificationToken, 1662 ResetPasswordVerificationExpiry: user.ResetPasswordVerificationExpiry, 1663 LastLoginTime: user.LastLoginTime, 1664 FailedLoginAttempts: user.FailedLoginAttempts, 1665 Deactivated: user.Deactivated, 1666 Locked: userIsLocked(user.FailedLoginAttempts), 1667 Identities: convertWWWIdentitiesFromDatabaseIdentities(user.Identities), 1668 ProposalCredits: uint64(len(user.UnspentProposalCredits)), 1669 EmailNotifications: user.EmailNotifications, 1670 } 1671 } 1672 1673 // convertWWWIdentitiesFromDatabaseIdentities converts a user Identity to a www 1674 // Identity. 1675 func convertWWWIdentitiesFromDatabaseIdentities(identities []user.Identity) []www.UserIdentity { 1676 userIdentities := make([]www.UserIdentity, 0, len(identities)) 1677 for _, v := range identities { 1678 userIdentities = append(userIdentities, 1679 convertWWWIdentityFromDatabaseIdentity(v)) 1680 } 1681 return userIdentities 1682 } 1683 1684 // convertWWWIdentityFromDatabaseIdentity converts a user Identity to a www 1685 // Identity. 1686 func convertWWWIdentityFromDatabaseIdentity(id user.Identity) www.UserIdentity { 1687 return www.UserIdentity{ 1688 Pubkey: id.String(), 1689 Active: id.IsActive(), 1690 } 1691 }