Verify token db fields, CreateAccount changes
This commit is contained in:
parent
3b44a55f5a
commit
dee2882fe9
4 changed files with 84 additions and 20 deletions
|
@ -4,21 +4,33 @@ import (
|
||||||
"errors"
|
"errors"
|
||||||
"strings"
|
"strings"
|
||||||
"testing"
|
"testing"
|
||||||
|
"time"
|
||||||
|
|
||||||
"github.com/mattn/go-sqlite3"
|
"github.com/mattn/go-sqlite3"
|
||||||
|
|
||||||
"lbryio/lbry-id/auth"
|
"lbryio/lbry-id/auth"
|
||||||
)
|
)
|
||||||
|
|
||||||
func expectAccountMatch(t *testing.T, s *Store, normEmail auth.NormalizedEmail, expectedEmail auth.Email, password auth.Password, seed auth.ClientSaltSeed) {
|
func expectAccountMatch(
|
||||||
|
t *testing.T,
|
||||||
|
s *Store,
|
||||||
|
normEmail auth.NormalizedEmail,
|
||||||
|
expectedEmail auth.Email,
|
||||||
|
password auth.Password,
|
||||||
|
seed auth.ClientSaltSeed,
|
||||||
|
expectedVerifyTokenString auth.VerifyTokenString,
|
||||||
|
approxVerifyExpiration *time.Time,
|
||||||
|
) {
|
||||||
var key auth.KDFKey
|
var key auth.KDFKey
|
||||||
var salt auth.ServerSalt
|
var salt auth.ServerSalt
|
||||||
var email auth.Email
|
var email auth.Email
|
||||||
|
var verifyExpiration *time.Time
|
||||||
|
var verifyTokenString auth.VerifyTokenString
|
||||||
|
|
||||||
err := s.db.QueryRow(
|
err := s.db.QueryRow(
|
||||||
`SELECT key, server_salt, email from accounts WHERE normalized_email=? AND client_salt_seed=?`,
|
`SELECT key, server_salt, email, verify_token, verify_expiration from accounts WHERE normalized_email=? AND client_salt_seed=?`,
|
||||||
normEmail, seed,
|
normEmail, seed,
|
||||||
).Scan(&key, &salt, &email)
|
).Scan(&key, &salt, &email, &verifyTokenString, &verifyExpiration)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
t.Fatalf("Error finding account for: %s %s - %+v", normEmail, password, err)
|
t.Fatalf("Error finding account for: %s %s - %+v", normEmail, password, err)
|
||||||
}
|
}
|
||||||
|
@ -34,6 +46,31 @@ func expectAccountMatch(t *testing.T, s *Store, normEmail auth.NormalizedEmail,
|
||||||
if email != expectedEmail {
|
if email != expectedEmail {
|
||||||
t.Fatalf("Email case not as expected. Want: %s Got: %s", email, expectedEmail)
|
t.Fatalf("Email case not as expected. Want: %s Got: %s", email, expectedEmail)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if verifyTokenString != expectedVerifyTokenString {
|
||||||
|
t.Fatalf(
|
||||||
|
"Verify token string not as expected. Want: %s Got: %s",
|
||||||
|
verifyTokenString,
|
||||||
|
expectedVerifyTokenString,
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
if approxVerifyExpiration != nil {
|
||||||
|
if verifyExpiration == nil {
|
||||||
|
t.Fatalf("Expected verify expiration to not be nil")
|
||||||
|
}
|
||||||
|
expDiff := approxVerifyExpiration.Sub(*verifyExpiration)
|
||||||
|
if time.Second < expDiff || expDiff < -time.Second {
|
||||||
|
t.Fatalf(
|
||||||
|
"Verify expiration not as expected. Want approximately: %s Got: %s",
|
||||||
|
verifyExpiration,
|
||||||
|
approxVerifyExpiration,
|
||||||
|
)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if approxVerifyExpiration == nil && verifyExpiration != nil {
|
||||||
|
t.Fatalf("Expected verify expiration to be nil. Got: %+v", verifyExpiration)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
func expectAccountNotExists(t *testing.T, s *Store, normEmail auth.NormalizedEmail) {
|
func expectAccountNotExists(t *testing.T, s *Store, normEmail auth.NormalizedEmail) {
|
||||||
|
@ -65,19 +102,20 @@ func TestStoreCreateAccount(t *testing.T) {
|
||||||
// Get an account, come back empty
|
// Get an account, come back empty
|
||||||
expectAccountNotExists(t, &s, normEmail)
|
expectAccountNotExists(t, &s, normEmail)
|
||||||
|
|
||||||
// Create an account
|
// Create an account. Make it verified (i.e. no token) for the usual
|
||||||
if err := s.CreateAccount(email, password, seed); err != nil {
|
// case. We'll test unverified (with token) separately.
|
||||||
|
if err := s.CreateAccount(email, password, seed, ""); err != nil {
|
||||||
t.Fatalf("Unexpected error in CreateAccount: %+v", err)
|
t.Fatalf("Unexpected error in CreateAccount: %+v", err)
|
||||||
}
|
}
|
||||||
|
|
||||||
// Get and confirm the account we just put in
|
// Get and confirm the account we just put in
|
||||||
expectAccountMatch(t, &s, normEmail, email, password, seed)
|
expectAccountMatch(t, &s, normEmail, email, password, seed, "", nil)
|
||||||
|
|
||||||
newPassword := auth.Password("xyz")
|
newPassword := auth.Password("xyz")
|
||||||
|
|
||||||
// Try to create a new account with the same email and different password,
|
// Try to create a new account with the same email and different password,
|
||||||
// fail because email already exists
|
// fail because email already exists
|
||||||
if err := s.CreateAccount(email, newPassword, seed); err != ErrDuplicateAccount {
|
if err := s.CreateAccount(email, newPassword, seed, ""); err != ErrDuplicateAccount {
|
||||||
t.Fatalf(`CreateAccount err: wanted "%+v", got "%+v"`, ErrDuplicateAccount, err)
|
t.Fatalf(`CreateAccount err: wanted "%+v", got "%+v"`, ErrDuplicateAccount, err)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -85,12 +123,30 @@ func TestStoreCreateAccount(t *testing.T) {
|
||||||
|
|
||||||
// Try to create a new account with the same email different capitalization.
|
// Try to create a new account with the same email different capitalization.
|
||||||
// fail because email already exists
|
// fail because email already exists
|
||||||
if err := s.CreateAccount(differentCaseEmail, password, seed); err != ErrDuplicateAccount {
|
if err := s.CreateAccount(differentCaseEmail, password, seed, ""); err != ErrDuplicateAccount {
|
||||||
t.Fatalf(`CreateAccount err (for case insensitivity check): wanted "%+v", got "%+v"`, ErrDuplicateAccount, err)
|
t.Fatalf(`CreateAccount err (for case insensitivity check): wanted "%+v", got "%+v"`, ErrDuplicateAccount, err)
|
||||||
}
|
}
|
||||||
|
|
||||||
// Get the email and same *first* password we successfully put in
|
// Get the email and same *first* password we successfully put in
|
||||||
expectAccountMatch(t, &s, normEmail, email, password, seed)
|
expectAccountMatch(t, &s, normEmail, email, password, seed, "", nil)
|
||||||
|
}
|
||||||
|
|
||||||
|
// Try CreateAccount with a verification string, thus unverified
|
||||||
|
func TestStoreCreateAccountUnverified(t *testing.T) {
|
||||||
|
s, sqliteTmpFile := StoreTestInit(t)
|
||||||
|
defer StoreTestCleanup(sqliteTmpFile)
|
||||||
|
|
||||||
|
email, normEmail := auth.Email("Abc@Example.Com"), auth.NormalizedEmail("abc@example.com")
|
||||||
|
password, seed := auth.Password("123"), auth.ClientSaltSeed("abcd1234abcd1234")
|
||||||
|
|
||||||
|
// Create an account
|
||||||
|
if err := s.CreateAccount(email, password, seed, "abcd1234abcd1234abcd1234abcd1234"); err != nil {
|
||||||
|
t.Fatalf("Unexpected error in CreateAccount: %+v", err)
|
||||||
|
}
|
||||||
|
|
||||||
|
// Get and confirm the account we just put in
|
||||||
|
approxVerifyExpiration := time.Now().Add(time.Hour * 24 * 2).UTC()
|
||||||
|
expectAccountMatch(t, &s, normEmail, email, password, seed, "abcd1234abcd1234abcd1234abcd1234", &approxVerifyExpiration)
|
||||||
}
|
}
|
||||||
|
|
||||||
// Test GetUserId for nonexisting email
|
// Test GetUserId for nonexisting email
|
||||||
|
@ -165,7 +221,7 @@ func TestStoreAccountEmptyFields(t *testing.T) {
|
||||||
|
|
||||||
var sqliteErr sqlite3.Error
|
var sqliteErr sqlite3.Error
|
||||||
|
|
||||||
err := s.CreateAccount(tc.email, tc.password, tc.clientSaltSeed)
|
err := s.CreateAccount(tc.email, tc.password, tc.clientSaltSeed, "")
|
||||||
if errors.As(err, &sqliteErr) {
|
if errors.As(err, &sqliteErr) {
|
||||||
if errors.Is(sqliteErr.ExtendedCode, sqlite3.ErrConstraintCheck) {
|
if errors.Is(sqliteErr.ExtendedCode, sqlite3.ErrConstraintCheck) {
|
||||||
return // We got the error we expected
|
return // We got the error we expected
|
||||||
|
|
|
@ -47,7 +47,7 @@ func TestStoreChangePasswordSuccess(t *testing.T) {
|
||||||
t.Errorf("ChangePasswordWithWallet (lower case email): unexpected error: %+v", err)
|
t.Errorf("ChangePasswordWithWallet (lower case email): unexpected error: %+v", err)
|
||||||
}
|
}
|
||||||
|
|
||||||
expectAccountMatch(t, &s, email.Normalize(), email, newPassword, newSeed)
|
expectAccountMatch(t, &s, email.Normalize(), email, newPassword, newSeed, "", nil)
|
||||||
expectWalletExists(t, &s, userId, encryptedWallet, sequence, hmac)
|
expectWalletExists(t, &s, userId, encryptedWallet, sequence, hmac)
|
||||||
expectTokenNotExists(t, &s, token)
|
expectTokenNotExists(t, &s, token)
|
||||||
|
|
||||||
|
@ -63,7 +63,7 @@ func TestStoreChangePasswordSuccess(t *testing.T) {
|
||||||
t.Errorf("ChangePasswordWithWallet (upper case email): unexpected error: %+v", err)
|
t.Errorf("ChangePasswordWithWallet (upper case email): unexpected error: %+v", err)
|
||||||
}
|
}
|
||||||
|
|
||||||
expectAccountMatch(t, &s, email.Normalize(), email, newNewPassword, newNewSeed)
|
expectAccountMatch(t, &s, email.Normalize(), email, newNewPassword, newNewSeed, "", nil)
|
||||||
}
|
}
|
||||||
|
|
||||||
func TestStoreChangePasswordErrors(t *testing.T) {
|
func TestStoreChangePasswordErrors(t *testing.T) {
|
||||||
|
@ -161,7 +161,7 @@ func TestStoreChangePasswordErrors(t *testing.T) {
|
||||||
// This tests the transaction rollbacks in particular, given the errors
|
// This tests the transaction rollbacks in particular, given the errors
|
||||||
// that are at a couple different stages of the txn, triggered by these
|
// that are at a couple different stages of the txn, triggered by these
|
||||||
// tests.
|
// tests.
|
||||||
expectAccountMatch(t, &s, email.Normalize(), email, oldPassword, oldSeed)
|
expectAccountMatch(t, &s, email.Normalize(), email, oldPassword, oldSeed, "", nil)
|
||||||
if tc.hasWallet {
|
if tc.hasWallet {
|
||||||
expectWalletExists(t, &s, userId, oldEncryptedWallet, oldSequence, oldHmac)
|
expectWalletExists(t, &s, userId, oldEncryptedWallet, oldSequence, oldHmac)
|
||||||
} else {
|
} else {
|
||||||
|
@ -196,7 +196,7 @@ func TestStoreChangePasswordNoWalletSuccess(t *testing.T) {
|
||||||
t.Errorf("ChangePasswordNoWallet (lower case email): unexpected error: %+v", err)
|
t.Errorf("ChangePasswordNoWallet (lower case email): unexpected error: %+v", err)
|
||||||
}
|
}
|
||||||
|
|
||||||
expectAccountMatch(t, &s, email.Normalize(), email, newPassword, newSeed)
|
expectAccountMatch(t, &s, email.Normalize(), email, newPassword, newSeed, "", nil)
|
||||||
expectWalletNotExists(t, &s, userId)
|
expectWalletNotExists(t, &s, userId)
|
||||||
expectTokenNotExists(t, &s, token)
|
expectTokenNotExists(t, &s, token)
|
||||||
|
|
||||||
|
@ -209,7 +209,7 @@ func TestStoreChangePasswordNoWalletSuccess(t *testing.T) {
|
||||||
t.Errorf("ChangePasswordNoWallet (upper case email): unexpected error: %+v", err)
|
t.Errorf("ChangePasswordNoWallet (upper case email): unexpected error: %+v", err)
|
||||||
}
|
}
|
||||||
|
|
||||||
expectAccountMatch(t, &s, email.Normalize(), email, newNewPassword, newNewSeed)
|
expectAccountMatch(t, &s, email.Normalize(), email, newNewPassword, newNewSeed, "", nil)
|
||||||
}
|
}
|
||||||
|
|
||||||
func TestStoreChangePasswordNoWalletErrors(t *testing.T) {
|
func TestStoreChangePasswordNoWalletErrors(t *testing.T) {
|
||||||
|
@ -292,7 +292,7 @@ func TestStoreChangePasswordNoWalletErrors(t *testing.T) {
|
||||||
// deleted. This tests the transaction rollbacks in particular, given the
|
// deleted. This tests the transaction rollbacks in particular, given the
|
||||||
// errors that are at a couple different stages of the txn, triggered by
|
// errors that are at a couple different stages of the txn, triggered by
|
||||||
// these tests.
|
// these tests.
|
||||||
expectAccountMatch(t, &s, email.Normalize(), email, oldPassword, oldSeed)
|
expectAccountMatch(t, &s, email.Normalize(), email, oldPassword, oldSeed, "", nil)
|
||||||
if tc.hasWallet {
|
if tc.hasWallet {
|
||||||
expectWalletExists(t, &s, userId, encryptedWallet, sequence, hmac)
|
expectWalletExists(t, &s, userId, encryptedWallet, sequence, hmac)
|
||||||
} else {
|
} else {
|
||||||
|
|
|
@ -120,6 +120,8 @@ func (s *Store) Migrate() error {
|
||||||
key TEXT NOT NULL,
|
key TEXT NOT NULL,
|
||||||
client_salt_seed TEXT NOT NULL,
|
client_salt_seed TEXT NOT NULL,
|
||||||
server_salt TEXT NOT NULL,
|
server_salt TEXT NOT NULL,
|
||||||
|
verify_token TEXT NOT NULL,
|
||||||
|
verify_expiration DATETIME,
|
||||||
user_id INTEGER PRIMARY KEY AUTOINCREMENT,
|
user_id INTEGER PRIMARY KEY AUTOINCREMENT,
|
||||||
CHECK (
|
CHECK (
|
||||||
email <> '' AND
|
email <> '' AND
|
||||||
|
@ -369,10 +371,16 @@ func (s *Store) CreateAccount(email auth.Email, password auth.Password, seed aut
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
|
var verifyExpiration *time.Time
|
||||||
|
if len(verifyToken) > 0 {
|
||||||
|
verifyExpiration = new(time.Time)
|
||||||
|
*verifyExpiration = time.Now().UTC().Add(time.Hour * 24 * 2)
|
||||||
|
}
|
||||||
|
|
||||||
// userId auto-increments
|
// userId auto-increments
|
||||||
_, err = s.db.Exec(
|
_, err = s.db.Exec(
|
||||||
"INSERT INTO accounts (normalized_email, email, key, server_salt, client_salt_seed) VALUES(?,?,?,?,?)",
|
"INSERT INTO accounts (normalized_email, email, key, server_salt, client_salt_seed, verify_token, verify_expiration) VALUES(?,?,?,?,?,?,?)",
|
||||||
email.Normalize(), email, key, salt, seed,
|
email.Normalize(), email, key, salt, seed, verifyToken, verifyExpiration,
|
||||||
)
|
)
|
||||||
var sqliteErr sqlite3.Error
|
var sqliteErr sqlite3.Error
|
||||||
if errors.As(err, &sqliteErr) {
|
if errors.As(err, &sqliteErr) {
|
||||||
|
|
|
@ -45,8 +45,8 @@ func makeTestUser(t *testing.T, s *Store) (userId auth.UserId, email auth.Email,
|
||||||
seed = auth.ClientSaltSeed("abcd1234abcd1234")
|
seed = auth.ClientSaltSeed("abcd1234abcd1234")
|
||||||
|
|
||||||
rows, err := s.db.Query(
|
rows, err := s.db.Query(
|
||||||
"INSERT INTO accounts (normalized_email, email, key, server_salt, client_salt_seed) values(?,?,?,?,?) returning user_id",
|
"INSERT INTO accounts (normalized_email, email, key, server_salt, client_salt_seed, verify_token) values(?,?,?,?,?,?) returning user_id",
|
||||||
normEmail, email, key, salt, seed,
|
normEmail, email, key, salt, seed, "",
|
||||||
)
|
)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
t.Fatalf("Error setting up account: %+v", err)
|
t.Fatalf("Error setting up account: %+v", err)
|
||||||
|
|
Loading…
Add table
Reference in a new issue