wallet-sync-server/store/password_test.go
Daniel Krol aefda1245b Make emails case insensitive (for now).
Prevents duplicate accounts. Also allows case insensitive search (user id, salt seed, etc) while still having an index. This is done by storing normalized as a separate field from originally formated (which we'll use for sending emails, etc).
2022-07-22 16:29:03 -04:00

304 lines
12 KiB
Go

package store
import (
"strings"
"testing"
"time"
"lbryio/lbry-id/auth"
"lbryio/lbry-id/wallet"
)
// It involves both wallet and account tables. Should it go in wallet_test.go
// or account_test.go? Decided to just make it its own file.
func TestStoreChangePasswordSuccess(t *testing.T) {
s, sqliteTmpFile := StoreTestInit(t)
defer StoreTestCleanup(sqliteTmpFile)
userId, email, oldPassword, _ := makeTestUser(t, &s)
token := auth.TokenString("my-token")
_, err := s.db.Exec(
"INSERT INTO auth_tokens (token, user_id, device_id, scope, expiration) VALUES(?,?,?,?,?)",
token, userId, "my-dev-id", "*", time.Now().UTC().Add(time.Hour*24*14),
)
if err != nil {
t.Fatalf("Error creating token")
}
_, err = s.db.Exec(
"INSERT INTO wallets (user_id, encrypted_wallet, sequence, hmac) VALUES(?,?,?,?)",
userId, "my-enc-wallet", 1, "my-hmac",
)
if err != nil {
t.Fatalf("Error creating test wallet")
}
newPassword := oldPassword + auth.Password("_new")
newSeed := auth.ClientSaltSeed("edf98765edf98765edf98765edf98765edf98765edf98765edf98765edf98765")
encryptedWallet := wallet.EncryptedWallet("my-enc-wallet-2")
sequence := wallet.Sequence(2)
hmac := wallet.WalletHmac("my-hmac-2")
lowerEmail := auth.Email(strings.ToLower(string(email)))
if err := s.ChangePasswordWithWallet(lowerEmail, oldPassword, newPassword, newSeed, encryptedWallet, sequence, hmac); err != nil {
t.Errorf("ChangePasswordWithWallet (lower case email): unexpected error: %+v", err)
}
expectAccountMatch(t, &s, email.Normalize(), email, newPassword, newSeed)
expectWalletExists(t, &s, userId, encryptedWallet, sequence, hmac)
expectTokenNotExists(t, &s, token)
newNewPassword := newPassword + auth.Password("_new")
newNewSeed := auth.ClientSaltSeed("00008765edf98765edf98765edf98765edf98765edf98765edf98765edf98765")
newEncryptedWallet := wallet.EncryptedWallet("my-enc-wallet-3")
newSequence := wallet.Sequence(3)
newHmac := wallet.WalletHmac("my-hmac-3")
upperEmail := auth.Email(strings.ToUpper(string(email)))
if err := s.ChangePasswordWithWallet(upperEmail, newPassword, newNewPassword, newNewSeed, newEncryptedWallet, newSequence, newHmac); err != nil {
t.Errorf("ChangePasswordWithWallet (upper case email): unexpected error: %+v", err)
}
expectAccountMatch(t, &s, email.Normalize(), email, newNewPassword, newNewSeed)
}
func TestStoreChangePasswordErrors(t *testing.T) {
tt := []struct {
name string
hasWallet bool
sequence wallet.Sequence
emailSuffix auth.Email
oldPasswordSuffix auth.Password
expectedError error
}{
{
name: "wrong email",
hasWallet: true, // we have the requisite wallet
sequence: wallet.Sequence(2), // sequence is correct
emailSuffix: auth.Email("_wrong"), // the email is *incorrect*
oldPasswordSuffix: auth.Password(""), // the password is correct
expectedError: ErrWrongCredentials,
}, {
name: "wrong old password",
hasWallet: true, // we have the requisite wallet
sequence: wallet.Sequence(2), // sequence is correct
emailSuffix: auth.Email(""), // the email is correct
oldPasswordSuffix: auth.Password("_wrong"), // the old password is *incorrect*
expectedError: ErrWrongCredentials,
}, {
name: "wrong sequence",
hasWallet: true, // we have the requisite wallet
sequence: wallet.Sequence(3), // sequence is *incorrect*
emailSuffix: auth.Email(""), // the email is correct
oldPasswordSuffix: auth.Password(""), // the password is correct
expectedError: ErrWrongSequence,
}, {
name: "no wallet to replace",
hasWallet: false, // we have the requisite wallet
sequence: wallet.Sequence(1), // sequence is correct (for there being no wallets)
emailSuffix: auth.Email(""), // the email is correct
oldPasswordSuffix: auth.Password(""), // the password is correct
// Sequence=1 always ends up being wrong for this endpoint since we
// should never be creating a wallet here.
expectedError: ErrWrongSequence,
},
}
for _, tc := range tt {
t.Run(tc.name, func(t *testing.T) {
s, sqliteTmpFile := StoreTestInit(t)
defer StoreTestCleanup(sqliteTmpFile)
userId, email, oldPassword, oldSeed := makeTestUser(t, &s)
expiration := time.Now().UTC().Add(time.Hour * 24 * 14)
authToken := auth.AuthToken{
Token: auth.TokenString("my-token"),
DeviceId: auth.DeviceId("my-dev-id"),
UserId: userId,
Scope: auth.AuthScope("*"),
Expiration: &expiration,
}
_, err := s.db.Exec(
"INSERT INTO auth_tokens (token, user_id, device_id, scope, expiration) VALUES(?,?,?,?,?)",
authToken.Token, authToken.UserId, authToken.DeviceId, authToken.Scope, authToken.Expiration,
)
if err != nil {
t.Fatalf("Error creating token")
}
oldEncryptedWallet := wallet.EncryptedWallet("my-enc-wallet-old")
newEncryptedWallet := wallet.EncryptedWallet("my-enc-wallet-new")
oldHmac := wallet.WalletHmac("my-hmac-old")
newHmac := wallet.WalletHmac("my-hmac-new")
oldSequence := wallet.Sequence(1)
if tc.hasWallet {
_, err := s.db.Exec(
"INSERT INTO wallets (user_id, encrypted_wallet, sequence, hmac) VALUES(?,?,?,?)",
userId, oldEncryptedWallet, oldSequence, oldHmac,
)
if err != nil {
t.Fatalf("Error creating test wallet")
}
}
submittedEmail := email + tc.emailSuffix // Possibly make it the wrong email
submittedOldPassword := oldPassword + tc.oldPasswordSuffix // Possibly make it the wrong password
newPassword := oldPassword + auth.Password("_new") // Make the new password different (as it should be)
newSeed := auth.ClientSaltSeed("edf98765edf98765edf98765edf98765edf98765edf98765edf98765edf98765")
if err := s.ChangePasswordWithWallet(submittedEmail, submittedOldPassword, newPassword, newSeed, newEncryptedWallet, tc.sequence, newHmac); err != tc.expectedError {
t.Errorf("ChangePasswordWithWallet: unexpected value for err. want: %+v, got: %+v", tc.expectedError, err)
}
// The password and wallet didn't change, the token didn't get 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)
if tc.hasWallet {
expectWalletExists(t, &s, userId, oldEncryptedWallet, oldSequence, oldHmac)
} else {
expectWalletNotExists(t, &s, userId)
}
expectTokenExists(t, &s, authToken)
})
}
}
func TestStoreChangePasswordNoWalletSuccess(t *testing.T) {
s, sqliteTmpFile := StoreTestInit(t)
defer StoreTestCleanup(sqliteTmpFile)
userId, email, oldPassword, _ := makeTestUser(t, &s)
token := auth.TokenString("my-token")
_, err := s.db.Exec(
"INSERT INTO auth_tokens (token, user_id, device_id, scope, expiration) VALUES(?,?,?,?,?)",
token, userId, "my-dev-id", "*", time.Now().UTC().Add(time.Hour*24*14),
)
if err != nil {
t.Fatalf("Error creating token")
}
newPassword := oldPassword + auth.Password("_new")
newSeed := auth.ClientSaltSeed("edf98765edf98765edf98765edf98765edf98765edf98765edf98765edf98765")
lowerEmail := auth.Email(strings.ToLower(string(email)))
if err := s.ChangePasswordNoWallet(lowerEmail, oldPassword, newPassword, newSeed); err != nil {
t.Errorf("ChangePasswordNoWallet (lower case email): unexpected error: %+v", err)
}
expectAccountMatch(t, &s, email.Normalize(), email, newPassword, newSeed)
expectWalletNotExists(t, &s, userId)
expectTokenNotExists(t, &s, token)
newNewPassword := newPassword + auth.Password("_new")
newNewSeed := auth.ClientSaltSeed("00008765edf98765edf98765edf98765edf98765edf98765edf98765edf98765")
upperEmail := auth.Email(strings.ToUpper(string(email)))
if err := s.ChangePasswordNoWallet(upperEmail, newPassword, newNewPassword, newNewSeed); err != nil {
t.Errorf("ChangePasswordNoWallet (upper case email): unexpected error: %+v", err)
}
expectAccountMatch(t, &s, email.Normalize(), email, newNewPassword, newNewSeed)
}
func TestStoreChangePasswordNoWalletErrors(t *testing.T) {
tt := []struct {
name string
hasWallet bool
emailSuffix auth.Email
oldPasswordSuffix auth.Password
expectedError error
}{
{
name: "wrong email",
hasWallet: false, // we don't have the wallet, as expected for this function
emailSuffix: auth.Email("_wrong"), // the email is *incorrect*
oldPasswordSuffix: auth.Password(""), // the password is correct
expectedError: ErrWrongCredentials,
}, {
name: "wrong old password",
hasWallet: false, // we don't have the wallet, as expected for this function
emailSuffix: auth.Email(""), // the email is correct
oldPasswordSuffix: auth.Password("_wrong"), // the old password is *incorrect*
expectedError: ErrWrongCredentials,
}, {
name: "unexpected wallet",
hasWallet: true, // we have a wallet which we shouldn't have at this point
emailSuffix: auth.Email(""), // the email is correct
oldPasswordSuffix: auth.Password(""), // the password is correct
expectedError: ErrUnexpectedWallet,
},
}
for _, tc := range tt {
t.Run(tc.name, func(t *testing.T) {
s, sqliteTmpFile := StoreTestInit(t)
defer StoreTestCleanup(sqliteTmpFile)
userId, email, oldPassword, oldSeed := makeTestUser(t, &s)
expiration := time.Now().UTC().Add(time.Hour * 24 * 14)
authToken := auth.AuthToken{
Token: auth.TokenString("my-token"),
DeviceId: auth.DeviceId("my-dev-id"),
UserId: userId,
Scope: auth.AuthScope("*"),
Expiration: &expiration,
}
_, err := s.db.Exec(
"INSERT INTO auth_tokens (token, user_id, device_id, scope, expiration) VALUES(?,?,?,?,?)",
authToken.Token, authToken.UserId, authToken.DeviceId, authToken.Scope, authToken.Expiration,
)
if err != nil {
t.Fatalf("Error creating token")
}
// Only for error case
encryptedWallet := wallet.EncryptedWallet("my-enc-wallet-old")
hmac := wallet.WalletHmac("my-hmac-old")
sequence := wallet.Sequence(1)
if tc.hasWallet {
_, err := s.db.Exec(
"INSERT INTO wallets (user_id, encrypted_wallet, sequence, hmac) VALUES(?,?,?,?)",
userId, encryptedWallet, sequence, hmac,
)
if err != nil {
t.Fatalf("Error creating test wallet")
}
}
submittedEmail := email + tc.emailSuffix // Possibly make it the wrong email
submittedOldPassword := oldPassword + tc.oldPasswordSuffix // Possibly make it the wrong password
newPassword := oldPassword + auth.Password("_new") // Possibly make the new password different (as it should be)
newSeed := auth.ClientSaltSeed("edf98765edf98765edf98765edf98765edf98765edf98765edf98765edf98765")
if err := s.ChangePasswordNoWallet(submittedEmail, submittedOldPassword, newPassword, newSeed); err != tc.expectedError {
t.Errorf("ChangePasswordNoWallet: unexpected value for err. want: %+v, got: %+v", tc.expectedError, err)
}
// The password and wallet (if any) didn't change, the token didn't get
// 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)
if tc.hasWallet {
expectWalletExists(t, &s, userId, encryptedWallet, sequence, hmac)
} else {
expectWalletNotExists(t, &s, userId)
}
expectTokenExists(t, &s, authToken)
})
}
}