diff --git a/store/account_test.go b/store/account_test.go index 06ab7c9..f48c8ea 100644 --- a/store/account_test.go +++ b/store/account_test.go @@ -50,8 +50,8 @@ func expectAccountMatch( if verifyTokenString != expectedVerifyTokenString { t.Fatalf( "Verify token string not as expected. Want: %s Got: %s", - verifyTokenString, expectedVerifyTokenString, + verifyTokenString, ) } diff --git a/store/password_test.go b/store/password_test.go index 0c858a9..bde9355 100644 --- a/store/password_test.go +++ b/store/password_test.go @@ -73,6 +73,8 @@ func TestStoreChangePasswordErrors(t *testing.T) { sequence wallet.Sequence emailSuffix auth.Email oldPasswordSuffix auth.Password + verifyToken auth.VerifyTokenString + verifyExpiration *time.Time expectedError error }{ { @@ -82,6 +84,15 @@ func TestStoreChangePasswordErrors(t *testing.T) { emailSuffix: auth.Email("_wrong"), // the email is *incorrect* oldPasswordSuffix: auth.Password(""), // the password is correct expectedError: ErrWrongCredentials, + }, { + name: "unverified account", + hasWallet: true, // we have the requisite wallet (even though it should be impossible for an unverified account) + sequence: wallet.Sequence(2), // sequence is correct + emailSuffix: auth.Email(""), // the email is correct + oldPasswordSuffix: auth.Password(""), // the password is correct + verifyToken: auth.VerifyTokenString("aoeu1234aoeu1234aoeu1234aoeu1234"), + verifyExpiration: &time.Time{}, + expectedError: ErrNotVerified, }, { name: "wrong old password", hasWallet: true, // we have the requisite wallet @@ -114,7 +125,7 @@ func TestStoreChangePasswordErrors(t *testing.T) { s, sqliteTmpFile := StoreTestInit(t) defer StoreTestCleanup(sqliteTmpFile) - userId, email, oldPassword, oldSeed := makeTestUser(t, &s, "", nil) + userId, email, oldPassword, oldSeed := makeTestUser(t, &s, tc.verifyToken, tc.verifyExpiration) expiration := time.Now().UTC().Add(time.Hour * 24 * 14) authToken := auth.AuthToken{ Token: auth.AuthTokenString("my-token"), @@ -161,7 +172,7 @@ func TestStoreChangePasswordErrors(t *testing.T) { // This tests the transaction rollbacks in particular, given the errors // that are at a couple different stages of the txn, triggered by these // tests. - expectAccountMatch(t, &s, email.Normalize(), email, oldPassword, oldSeed, "", nil) + expectAccountMatch(t, &s, email.Normalize(), email, oldPassword, oldSeed, tc.verifyToken, tc.verifyExpiration) if tc.hasWallet { expectWalletExists(t, &s, userId, oldEncryptedWallet, oldSequence, oldHmac) } else { @@ -218,6 +229,8 @@ func TestStoreChangePasswordNoWalletErrors(t *testing.T) { hasWallet bool emailSuffix auth.Email oldPasswordSuffix auth.Password + verifyToken auth.VerifyTokenString + verifyExpiration *time.Time expectedError error }{ { @@ -232,6 +245,14 @@ func TestStoreChangePasswordNoWalletErrors(t *testing.T) { emailSuffix: auth.Email(""), // the email is correct oldPasswordSuffix: auth.Password("_wrong"), // the old password is *incorrect* expectedError: ErrWrongCredentials, + }, { + name: "unverified account", + hasWallet: false, // we don't have the wallet, as expected for this function + emailSuffix: auth.Email(""), // the email is correct + oldPasswordSuffix: auth.Password(""), // the password is correct + verifyToken: auth.VerifyTokenString("aoeu1234aoeu1234aoeu1234aoeu1234"), + verifyExpiration: &time.Time{}, + expectedError: ErrNotVerified, }, { name: "unexpected wallet", hasWallet: true, // we have a wallet which we shouldn't have at this point @@ -246,7 +267,7 @@ func TestStoreChangePasswordNoWalletErrors(t *testing.T) { s, sqliteTmpFile := StoreTestInit(t) defer StoreTestCleanup(sqliteTmpFile) - userId, email, oldPassword, oldSeed := makeTestUser(t, &s, "", nil) + userId, email, oldPassword, oldSeed := makeTestUser(t, &s, tc.verifyToken, tc.verifyExpiration) expiration := time.Now().UTC().Add(time.Hour * 24 * 14) authToken := auth.AuthToken{ Token: auth.AuthTokenString("my-token"), @@ -292,7 +313,7 @@ func TestStoreChangePasswordNoWalletErrors(t *testing.T) { // deleted. This tests the transaction rollbacks in particular, given the // errors that are at a couple different stages of the txn, triggered by // these tests. - expectAccountMatch(t, &s, email.Normalize(), email, oldPassword, oldSeed, "", nil) + expectAccountMatch(t, &s, email.Normalize(), email, oldPassword, oldSeed, tc.verifyToken, tc.verifyExpiration) if tc.hasWallet { expectWalletExists(t, &s, userId, encryptedWallet, sequence, hmac) } else { diff --git a/store/store.go b/store/store.go index 981d22d..5b4c813 100644 --- a/store/store.go +++ b/store/store.go @@ -488,11 +488,12 @@ func (s *Store) changePassword( var oldKey auth.KDFKey var oldSalt auth.ServerSalt + var verified bool err = tx.QueryRow( - `SELECT user_id, key, server_salt from accounts WHERE normalized_email=?`, + `SELECT user_id, key, server_salt, verify_token="" from accounts WHERE normalized_email=?`, email.Normalize(), - ).Scan(&userId, &oldKey, &oldSalt) + ).Scan(&userId, &oldKey, &oldSalt, &verified) if err == sql.ErrNoRows { err = ErrWrongCredentials } @@ -503,6 +504,9 @@ func (s *Store) changePassword( if err == nil && !match { err = ErrWrongCredentials } + if err == nil && !verified { + err = ErrNotVerified + } if err != nil { return } diff --git a/store/store_test.go b/store/store_test.go index 694eda2..e7f235c 100644 --- a/store/store_test.go +++ b/store/store_test.go @@ -51,7 +51,7 @@ func makeTestUser( seed = auth.ClientSaltSeed("abcd1234abcd1234") rows, err := s.db.Query( - "INSERT INTO accounts (normalized_email, email, key, server_salt, client_salt_seed, verify_token) values(?,?,?,?,?,?) returning user_id", + "INSERT INTO accounts (normalized_email, email, key, server_salt, client_salt_seed, verify_token, verify_expiration) values(?,?,?,?,?,?,?) returning user_id", normEmail, email, key, salt, seed, verifyToken, verifyExpiration, ) if err != nil {