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  }