github.com/decred/politeia@v1.4.0/politeiawww/legacy/user_test.go (about)

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