2021-12-25 02:16:58 +01:00
|
|
|
package server
|
|
|
|
|
|
|
|
import (
|
|
|
|
"bytes"
|
2022-07-15 21:36:11 +02:00
|
|
|
"encoding/base64"
|
2021-12-25 02:16:58 +01:00
|
|
|
"encoding/json"
|
|
|
|
"fmt"
|
|
|
|
"io/ioutil"
|
|
|
|
"net/http"
|
|
|
|
"net/http/httptest"
|
2022-06-29 17:10:29 +02:00
|
|
|
"os"
|
|
|
|
"reflect"
|
2022-07-31 22:43:36 +02:00
|
|
|
"strings"
|
2022-06-29 17:10:29 +02:00
|
|
|
"testing"
|
2022-08-27 17:37:09 +02:00
|
|
|
"time"
|
2022-06-29 17:10:29 +02:00
|
|
|
|
2022-08-22 18:05:53 +02:00
|
|
|
"lbryio/wallet-sync-server/auth"
|
|
|
|
"lbryio/wallet-sync-server/server/paths"
|
|
|
|
"lbryio/wallet-sync-server/store"
|
|
|
|
"lbryio/wallet-sync-server/wallet"
|
2021-12-25 02:16:58 +01:00
|
|
|
)
|
|
|
|
|
|
|
|
// Whereas sever_test.go stubs out auth store and wallet, these will use the real thing, but test fewer paths.
|
|
|
|
|
2022-06-29 17:10:29 +02:00
|
|
|
// Integration test requires a real sqlite database
|
|
|
|
func storeTestInit(t *testing.T) (s store.Store, tmpFile *os.File) {
|
|
|
|
s = store.Store{}
|
|
|
|
|
|
|
|
tmpFile, err := ioutil.TempFile(os.TempDir(), "sqlite-test-")
|
|
|
|
if err != nil {
|
|
|
|
t.Fatalf("DB setup failure: %+v", err)
|
|
|
|
return
|
|
|
|
}
|
|
|
|
|
|
|
|
s.Init(tmpFile.Name())
|
|
|
|
|
|
|
|
err = s.Migrate()
|
|
|
|
if err != nil {
|
|
|
|
t.Fatalf("DB setup failure: %+v", err)
|
|
|
|
}
|
|
|
|
|
|
|
|
return
|
|
|
|
}
|
|
|
|
|
|
|
|
func storeTestCleanup(tmpFile *os.File) {
|
|
|
|
if tmpFile != nil {
|
|
|
|
os.Remove(tmpFile.Name())
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2021-12-25 02:16:58 +01:00
|
|
|
func checkStatusCode(t *testing.T, statusCode int, responseBody []byte, expectedStatusCodeSlice ...int) {
|
|
|
|
var expectedStatusCode int
|
|
|
|
if len(expectedStatusCodeSlice) == 1 {
|
|
|
|
expectedStatusCode = expectedStatusCodeSlice[0]
|
|
|
|
} else {
|
|
|
|
expectedStatusCode = http.StatusOK
|
|
|
|
}
|
|
|
|
|
|
|
|
if want, got := expectedStatusCode, statusCode; want != got {
|
|
|
|
t.Errorf("StatusCode: expected %s (%d), got %s (%d)", http.StatusText(want), want, http.StatusText(got), got)
|
|
|
|
var errorResponse ErrorResponse
|
|
|
|
err := json.Unmarshal(responseBody, &errorResponse)
|
|
|
|
if err == nil {
|
2022-06-09 23:04:49 +02:00
|
|
|
t.Errorf("http response: %+v", errorResponse)
|
2021-12-25 02:16:58 +01:00
|
|
|
} else {
|
2022-06-09 23:04:49 +02:00
|
|
|
t.Errorf("%s", err)
|
2021-12-25 02:16:58 +01:00
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2022-06-29 17:15:00 +02:00
|
|
|
// TODO - make this a real request some day. For now it still passes in the
|
|
|
|
// handler. Probably close enough for now.
|
2021-12-25 02:16:58 +01:00
|
|
|
func request(t *testing.T, method string, handler func(http.ResponseWriter, *http.Request), path string, jsonResult interface{}, requestBody string) ([]byte, int) {
|
|
|
|
req := httptest.NewRequest(
|
|
|
|
method,
|
|
|
|
path,
|
|
|
|
bytes.NewBuffer([]byte(requestBody)),
|
|
|
|
)
|
|
|
|
w := httptest.NewRecorder()
|
|
|
|
|
|
|
|
handler(w, req)
|
|
|
|
responseBody, _ := ioutil.ReadAll(w.Body)
|
|
|
|
|
2022-07-31 22:43:36 +02:00
|
|
|
if jsonResult != nil {
|
|
|
|
err := json.Unmarshal(responseBody, &jsonResult)
|
|
|
|
if err != nil {
|
|
|
|
t.Errorf("Error unmarshalling response body err: %+v body: %s", err, responseBody)
|
|
|
|
}
|
2021-12-25 02:16:58 +01:00
|
|
|
}
|
|
|
|
|
|
|
|
return responseBody, w.Result().StatusCode
|
|
|
|
}
|
|
|
|
|
|
|
|
// Test some flows with syncing two devices that have the wallet locally.
|
|
|
|
func TestIntegrationWalletUpdates(t *testing.T) {
|
2022-06-29 17:10:29 +02:00
|
|
|
st, tmpFile := storeTestInit(t)
|
|
|
|
defer storeTestCleanup(tmpFile)
|
2021-12-25 02:16:58 +01:00
|
|
|
|
2022-07-31 22:43:36 +02:00
|
|
|
// Excluding env and email from the integration
|
2022-07-26 17:21:55 +02:00
|
|
|
env := map[string]string{
|
2022-07-31 22:43:36 +02:00
|
|
|
"ACCOUNT_WHITELIST": "abc@example.com",
|
2022-07-26 17:21:55 +02:00
|
|
|
}
|
2022-08-27 17:37:09 +02:00
|
|
|
s := Init(&auth.Auth{}, &st, &TestEnv{env}, &TestMail{}, TestPort)
|
2021-12-25 02:16:58 +01:00
|
|
|
|
2022-06-07 19:25:14 +02:00
|
|
|
////////////////////
|
2022-07-06 22:45:50 +02:00
|
|
|
t.Log("Request: Register email address - any device")
|
2022-06-07 19:25:14 +02:00
|
|
|
////////////////////
|
|
|
|
|
|
|
|
var registerResponse struct{}
|
|
|
|
responseBody, statusCode := request(
|
|
|
|
t,
|
|
|
|
http.MethodPost,
|
|
|
|
s.register,
|
2022-08-01 17:50:16 +02:00
|
|
|
paths.PathRegister,
|
2022-06-07 19:25:14 +02:00
|
|
|
®isterResponse,
|
2022-08-23 01:41:30 +02:00
|
|
|
`{"email": "abc@example.com", "password": "12345678", "clientSaltSeed": "1234abcd1234abcd1234abcd1234abcd1234abcd1234abcd1234abcd1234abcd"}`,
|
2022-06-07 19:25:14 +02:00
|
|
|
)
|
|
|
|
|
2022-06-23 21:22:31 +02:00
|
|
|
checkStatusCode(t, statusCode, responseBody, http.StatusCreated)
|
|
|
|
|
2021-12-25 02:16:58 +01:00
|
|
|
////////////////////
|
2022-07-06 22:45:50 +02:00
|
|
|
t.Log("Request: Get auth token - device 1")
|
2021-12-25 02:16:58 +01:00
|
|
|
////////////////////
|
|
|
|
|
|
|
|
var authToken1 auth.AuthToken
|
2022-06-07 19:25:14 +02:00
|
|
|
responseBody, statusCode = request(
|
2021-12-25 02:16:58 +01:00
|
|
|
t,
|
|
|
|
http.MethodPost,
|
2022-06-08 00:24:01 +02:00
|
|
|
s.getAuthToken,
|
2022-08-01 17:50:16 +02:00
|
|
|
paths.PathAuthToken,
|
2021-12-25 02:16:58 +01:00
|
|
|
&authToken1,
|
2022-08-23 01:41:30 +02:00
|
|
|
`{"deviceId": "dev-1", "email": "abc@example.com", "password": "12345678"}`,
|
2021-12-25 02:16:58 +01:00
|
|
|
)
|
|
|
|
|
|
|
|
checkStatusCode(t, statusCode, responseBody)
|
|
|
|
|
2022-07-28 01:45:09 +02:00
|
|
|
// result.Token is in hex, auth.TokenLength is bytes in the original
|
|
|
|
expectedTokenLength := auth.TokenLength * 2
|
2021-12-25 02:16:58 +01:00
|
|
|
if len(authToken1.Token) != expectedTokenLength {
|
2022-07-31 22:43:36 +02:00
|
|
|
t.Fatalf("Expected auth response to contain token length %d: result: %+v", auth.TokenLength, string(responseBody))
|
2021-12-25 02:16:58 +01:00
|
|
|
}
|
2022-06-07 19:25:14 +02:00
|
|
|
if authToken1.DeviceId != "dev-1" {
|
|
|
|
t.Fatalf("Unexpected response DeviceId. want: %+v got: %+v", "dev-1", authToken1.DeviceId)
|
2021-12-25 02:16:58 +01:00
|
|
|
}
|
|
|
|
if authToken1.Scope != auth.ScopeFull {
|
|
|
|
t.Fatalf("Unexpected response Scope. want: %+v got: %+v", auth.ScopeFull, authToken1.Scope)
|
|
|
|
}
|
|
|
|
|
|
|
|
////////////////////
|
2022-07-06 22:45:50 +02:00
|
|
|
t.Log("Request: Get auth token - device 2")
|
2021-12-25 02:16:58 +01:00
|
|
|
////////////////////
|
|
|
|
|
|
|
|
var authToken2 auth.AuthToken
|
|
|
|
responseBody, statusCode = request(
|
|
|
|
t,
|
|
|
|
http.MethodPost,
|
2022-06-08 00:24:01 +02:00
|
|
|
s.getAuthToken,
|
2022-08-01 17:50:16 +02:00
|
|
|
paths.PathAuthToken,
|
2021-12-25 02:16:58 +01:00
|
|
|
&authToken2,
|
2022-08-23 01:41:30 +02:00
|
|
|
`{"deviceId": "dev-2", "email": "abc@example.com", "password": "12345678"}`,
|
2021-12-25 02:16:58 +01:00
|
|
|
)
|
|
|
|
|
|
|
|
checkStatusCode(t, statusCode, responseBody)
|
|
|
|
|
2022-06-07 19:25:14 +02:00
|
|
|
if authToken2.DeviceId != "dev-2" {
|
|
|
|
t.Fatalf("Unexpected response DeviceId. want: %+v got: %+v", "dev-2", authToken2.DeviceId)
|
2021-12-25 02:16:58 +01:00
|
|
|
}
|
|
|
|
|
|
|
|
////////////////////
|
2022-07-06 22:45:50 +02:00
|
|
|
t.Log("Request: Put first wallet - device 1")
|
2021-12-25 02:16:58 +01:00
|
|
|
////////////////////
|
|
|
|
|
2022-06-23 21:22:31 +02:00
|
|
|
var walletPostResponse struct{}
|
2021-12-25 02:16:58 +01:00
|
|
|
responseBody, statusCode = request(
|
|
|
|
t,
|
|
|
|
http.MethodPost,
|
2022-06-09 23:04:49 +02:00
|
|
|
s.postWallet,
|
2022-08-01 17:50:16 +02:00
|
|
|
paths.PathWallet,
|
2022-06-23 21:22:31 +02:00
|
|
|
&walletPostResponse,
|
2021-12-25 02:16:58 +01:00
|
|
|
fmt.Sprintf(`{
|
|
|
|
"token": "%s",
|
2022-06-09 23:04:49 +02:00
|
|
|
"encryptedWallet": "my-encrypted-wallet-1",
|
|
|
|
"sequence": 1,
|
2022-06-07 19:25:14 +02:00
|
|
|
"hmac": "my-hmac-1"
|
2021-12-25 02:16:58 +01:00
|
|
|
}`, authToken1.Token),
|
|
|
|
)
|
|
|
|
|
|
|
|
checkStatusCode(t, statusCode, responseBody)
|
|
|
|
|
|
|
|
////////////////////
|
2022-07-06 22:45:50 +02:00
|
|
|
t.Log("Request: Get wallet - device 2")
|
2021-12-25 02:16:58 +01:00
|
|
|
////////////////////
|
|
|
|
|
2022-06-23 21:22:31 +02:00
|
|
|
var walletGetResponse WalletResponse
|
2021-12-25 02:16:58 +01:00
|
|
|
responseBody, statusCode = request(
|
|
|
|
t,
|
|
|
|
http.MethodGet,
|
2022-06-09 23:04:49 +02:00
|
|
|
s.getWallet,
|
2022-08-01 17:50:16 +02:00
|
|
|
fmt.Sprintf("%s?token=%s", paths.PathWallet, authToken2.Token),
|
2022-06-23 21:22:31 +02:00
|
|
|
&walletGetResponse,
|
2021-12-25 02:16:58 +01:00
|
|
|
"",
|
|
|
|
)
|
|
|
|
|
|
|
|
checkStatusCode(t, statusCode, responseBody)
|
|
|
|
|
2022-06-23 21:22:31 +02:00
|
|
|
expectedResponse := WalletResponse{
|
|
|
|
EncryptedWallet: wallet.EncryptedWallet("my-encrypted-wallet-1"),
|
|
|
|
Sequence: wallet.Sequence(1),
|
|
|
|
Hmac: wallet.WalletHmac("my-hmac-1"),
|
|
|
|
}
|
|
|
|
|
|
|
|
if !reflect.DeepEqual(walletGetResponse, expectedResponse) {
|
|
|
|
t.Fatalf("Unexpected response values. want: %+v got: %+v", expectedResponse, walletGetResponse)
|
2021-12-25 02:16:58 +01:00
|
|
|
}
|
|
|
|
|
|
|
|
////////////////////
|
2022-07-06 22:45:50 +02:00
|
|
|
t.Log("Request: Put second wallet - device 2")
|
2021-12-25 02:16:58 +01:00
|
|
|
////////////////////
|
|
|
|
|
|
|
|
responseBody, statusCode = request(
|
|
|
|
t,
|
|
|
|
http.MethodPost,
|
2022-06-09 23:04:49 +02:00
|
|
|
s.postWallet,
|
2022-08-01 17:50:16 +02:00
|
|
|
paths.PathWallet,
|
2022-06-23 21:22:31 +02:00
|
|
|
&walletPostResponse,
|
2021-12-25 02:16:58 +01:00
|
|
|
fmt.Sprintf(`{
|
|
|
|
"token": "%s",
|
2022-06-09 23:04:49 +02:00
|
|
|
"encryptedWallet": "my-encrypted-wallet-2",
|
|
|
|
"sequence": 2,
|
2022-06-07 19:25:14 +02:00
|
|
|
"hmac": "my-hmac-2"
|
2021-12-25 02:16:58 +01:00
|
|
|
}`, authToken2.Token),
|
|
|
|
)
|
|
|
|
|
|
|
|
checkStatusCode(t, statusCode, responseBody)
|
|
|
|
|
|
|
|
////////////////////
|
2022-07-06 22:45:50 +02:00
|
|
|
t.Log("Request: Get wallet - device 1")
|
2021-12-25 02:16:58 +01:00
|
|
|
////////////////////
|
|
|
|
|
|
|
|
responseBody, statusCode = request(
|
|
|
|
t,
|
|
|
|
http.MethodGet,
|
2022-06-09 23:04:49 +02:00
|
|
|
s.getWallet,
|
2022-08-01 17:50:16 +02:00
|
|
|
fmt.Sprintf("%s?token=%s", paths.PathWallet, authToken1.Token),
|
2022-06-23 21:22:31 +02:00
|
|
|
&walletGetResponse,
|
2021-12-25 02:16:58 +01:00
|
|
|
"",
|
|
|
|
)
|
|
|
|
|
|
|
|
checkStatusCode(t, statusCode, responseBody)
|
|
|
|
|
2022-06-23 21:22:31 +02:00
|
|
|
expectedResponse = WalletResponse{
|
|
|
|
EncryptedWallet: wallet.EncryptedWallet("my-encrypted-wallet-2"),
|
|
|
|
Sequence: wallet.Sequence(2),
|
|
|
|
Hmac: wallet.WalletHmac("my-hmac-2"),
|
|
|
|
}
|
|
|
|
|
2022-06-09 23:04:49 +02:00
|
|
|
// Expect the same response getting from device 2 as when posting from device 1
|
2022-06-23 21:22:31 +02:00
|
|
|
if !reflect.DeepEqual(walletGetResponse, expectedResponse) {
|
|
|
|
t.Fatalf("Unexpected response values. want: %+v got: %+v", expectedResponse, walletGetResponse)
|
2021-12-25 02:16:58 +01:00
|
|
|
}
|
|
|
|
}
|
2022-07-06 22:45:50 +02:00
|
|
|
|
|
|
|
// Test one device that registers and changes password. Check that wallet
|
|
|
|
// updates and that tokens get deleted.
|
|
|
|
func TestIntegrationChangePassword(t *testing.T) {
|
|
|
|
st, tmpFile := storeTestInit(t)
|
|
|
|
defer storeTestCleanup(tmpFile)
|
|
|
|
|
2022-07-31 22:43:36 +02:00
|
|
|
// Excluding env and email from the integration
|
2022-07-26 17:21:55 +02:00
|
|
|
env := map[string]string{
|
2022-07-31 22:43:36 +02:00
|
|
|
"ACCOUNT_WHITELIST": "abc@example.com",
|
2022-07-26 17:21:55 +02:00
|
|
|
}
|
2022-08-27 17:37:09 +02:00
|
|
|
s := Init(&auth.Auth{}, &st, &TestEnv{env}, &TestMail{}, TestPort)
|
|
|
|
|
|
|
|
// Still need to mock this until we're doing a real integration test
|
|
|
|
// where we call Serve(), which brings up the real websocket manager.
|
|
|
|
// Note that in the integration test, we're only using this for requests
|
|
|
|
// that would get blocked without it.
|
|
|
|
wsmm := wsMockManager{s: s, done: make(chan bool)}
|
2022-07-06 22:45:50 +02:00
|
|
|
|
|
|
|
////////////////////
|
|
|
|
t.Log("Request: Register email address")
|
|
|
|
////////////////////
|
|
|
|
|
|
|
|
var registerResponse struct{}
|
|
|
|
responseBody, statusCode := request(
|
|
|
|
t,
|
|
|
|
http.MethodPost,
|
|
|
|
s.register,
|
2022-08-01 17:50:16 +02:00
|
|
|
paths.PathRegister,
|
2022-07-06 22:45:50 +02:00
|
|
|
®isterResponse,
|
2022-08-23 01:41:30 +02:00
|
|
|
`{"email": "abc@example.com", "password": "12345678", "clientSaltSeed": "1234abcd1234abcd1234abcd1234abcd1234abcd1234abcd1234abcd1234abcd"}`,
|
2022-07-06 22:45:50 +02:00
|
|
|
)
|
|
|
|
|
|
|
|
checkStatusCode(t, statusCode, responseBody, http.StatusCreated)
|
|
|
|
|
2022-07-15 21:36:11 +02:00
|
|
|
////////////////////
|
|
|
|
t.Log("Request: Get client salt seed")
|
|
|
|
////////////////////
|
|
|
|
|
|
|
|
var clientSaltSeedResponse ClientSaltSeedResponse
|
|
|
|
responseBody, statusCode = request(
|
|
|
|
t,
|
|
|
|
http.MethodGet,
|
|
|
|
s.getClientSaltSeed,
|
2022-08-01 17:50:16 +02:00
|
|
|
fmt.Sprintf("%s?email=%s", paths.PathClientSaltSeed, base64.StdEncoding.EncodeToString([]byte("abc@example.com"))),
|
2022-07-15 21:36:11 +02:00
|
|
|
&clientSaltSeedResponse,
|
|
|
|
"",
|
|
|
|
)
|
|
|
|
|
|
|
|
checkStatusCode(t, statusCode, responseBody)
|
|
|
|
|
|
|
|
if clientSaltSeedResponse.ClientSaltSeed != "1234abcd1234abcd1234abcd1234abcd1234abcd1234abcd1234abcd1234abcd" {
|
|
|
|
t.Fatalf("Unexpected client salt seed. want: %+v got: %+v",
|
|
|
|
"1234abcd1234abcd1234abcd1234abcd1234abcd1234abcd1234abcd1234abcd",
|
|
|
|
clientSaltSeedResponse.ClientSaltSeed)
|
|
|
|
}
|
|
|
|
|
2022-07-06 22:45:50 +02:00
|
|
|
////////////////////
|
|
|
|
t.Log("Request: Get auth token")
|
|
|
|
////////////////////
|
|
|
|
|
|
|
|
var authToken auth.AuthToken
|
|
|
|
responseBody, statusCode = request(
|
|
|
|
t,
|
|
|
|
http.MethodPost,
|
|
|
|
s.getAuthToken,
|
2022-08-01 17:50:16 +02:00
|
|
|
paths.PathAuthToken,
|
2022-07-06 22:45:50 +02:00
|
|
|
&authToken,
|
2022-08-23 01:41:30 +02:00
|
|
|
`{"deviceId": "dev-1", "email": "abc@example.com", "password": "12345678"}`,
|
2022-07-06 22:45:50 +02:00
|
|
|
)
|
|
|
|
|
|
|
|
checkStatusCode(t, statusCode, responseBody)
|
|
|
|
|
2022-07-28 01:45:09 +02:00
|
|
|
// result.Token is in hex, auth.TokenLength is bytes in the original
|
|
|
|
expectedTokenLength := auth.TokenLength * 2
|
2022-07-06 22:45:50 +02:00
|
|
|
if len(authToken.Token) != expectedTokenLength {
|
2022-07-31 22:43:36 +02:00
|
|
|
t.Fatalf("Expected auth response to contain token length %d: result: %+v", auth.TokenLength, string(responseBody))
|
2022-07-06 22:45:50 +02:00
|
|
|
}
|
|
|
|
if authToken.DeviceId != "dev-1" {
|
|
|
|
t.Fatalf("Unexpected response DeviceId. want: %+v got: %+v", "dev-1", authToken.DeviceId)
|
|
|
|
}
|
|
|
|
if authToken.Scope != auth.ScopeFull {
|
|
|
|
t.Fatalf("Unexpected response Scope. want: %+v got: %+v", auth.ScopeFull, authToken.Scope)
|
|
|
|
}
|
|
|
|
|
|
|
|
////////////////////
|
|
|
|
t.Log("Request: Change password")
|
|
|
|
////////////////////
|
|
|
|
|
2022-08-27 17:37:09 +02:00
|
|
|
// Giving it a whole second of timeout because this request seems to be a bit
|
|
|
|
// slow.
|
|
|
|
go wsmm.getOneMessage(time.Second)
|
2022-07-06 22:45:50 +02:00
|
|
|
var changePasswordResponse struct{}
|
|
|
|
responseBody, statusCode = request(
|
|
|
|
t,
|
|
|
|
http.MethodPost,
|
|
|
|
s.changePassword,
|
2022-08-01 17:50:16 +02:00
|
|
|
paths.PathPassword,
|
2022-07-06 22:45:50 +02:00
|
|
|
&changePasswordResponse,
|
2022-08-23 01:41:30 +02:00
|
|
|
`{"email": "abc@example.com", "oldPassword": "12345678", "newPassword": "45678901", "clientSaltSeed": "8678def95678def98678def95678def98678def95678def98678def95678def9"}`,
|
2022-07-06 22:45:50 +02:00
|
|
|
)
|
2022-08-27 17:37:09 +02:00
|
|
|
<-wsmm.done
|
2022-07-06 22:45:50 +02:00
|
|
|
|
|
|
|
checkStatusCode(t, statusCode, responseBody)
|
|
|
|
|
2022-07-15 21:36:11 +02:00
|
|
|
////////////////////
|
|
|
|
t.Log("Request: Get client salt seed")
|
|
|
|
////////////////////
|
|
|
|
|
|
|
|
responseBody, statusCode = request(
|
|
|
|
t,
|
|
|
|
http.MethodGet,
|
|
|
|
s.getClientSaltSeed,
|
2022-08-01 17:50:16 +02:00
|
|
|
fmt.Sprintf("%s?email=%s", paths.PathClientSaltSeed, base64.StdEncoding.EncodeToString([]byte("abc@example.com"))),
|
2022-07-15 21:36:11 +02:00
|
|
|
&clientSaltSeedResponse,
|
|
|
|
"",
|
|
|
|
)
|
|
|
|
|
|
|
|
checkStatusCode(t, statusCode, responseBody)
|
|
|
|
|
|
|
|
if clientSaltSeedResponse.ClientSaltSeed != "8678def95678def98678def95678def98678def95678def98678def95678def9" {
|
|
|
|
t.Fatalf("Unexpected client salt seed. want: %+v got: %+v", "8678def95678def98678def95678def98678def95678def98678def95678def9", clientSaltSeedResponse.ClientSaltSeed)
|
|
|
|
}
|
|
|
|
|
2022-07-06 22:45:50 +02:00
|
|
|
////////////////////
|
|
|
|
t.Log("Request: Put first wallet - fail because token invalidated on password change")
|
|
|
|
////////////////////
|
|
|
|
|
|
|
|
var walletPostResponse struct{}
|
|
|
|
responseBody, statusCode = request(
|
|
|
|
t,
|
|
|
|
http.MethodPost,
|
|
|
|
s.postWallet,
|
2022-08-01 17:50:16 +02:00
|
|
|
paths.PathWallet,
|
2022-07-06 22:45:50 +02:00
|
|
|
&walletPostResponse,
|
|
|
|
fmt.Sprintf(`{
|
|
|
|
"token": "%s",
|
|
|
|
"encryptedWallet": "my-encrypted-wallet-1",
|
|
|
|
"sequence": 1,
|
|
|
|
"hmac": "my-hmac-1"
|
|
|
|
}`, authToken.Token),
|
|
|
|
)
|
|
|
|
|
|
|
|
checkStatusCode(t, statusCode, responseBody, http.StatusUnauthorized)
|
|
|
|
|
|
|
|
////////////////////
|
|
|
|
t.Log("Request: Get another auth token")
|
|
|
|
////////////////////
|
|
|
|
|
|
|
|
responseBody, statusCode = request(
|
|
|
|
t,
|
|
|
|
http.MethodPost,
|
|
|
|
s.getAuthToken,
|
2022-08-01 17:50:16 +02:00
|
|
|
paths.PathAuthToken,
|
2022-07-06 22:45:50 +02:00
|
|
|
&authToken,
|
2022-08-23 01:41:30 +02:00
|
|
|
`{"deviceId": "dev-1", "email": "abc@example.com", "password": "45678901"}`,
|
2022-07-06 22:45:50 +02:00
|
|
|
)
|
|
|
|
|
|
|
|
checkStatusCode(t, statusCode, responseBody)
|
|
|
|
|
2022-07-28 01:45:09 +02:00
|
|
|
// result.Token is in hex, auth.TokenLength is bytes in the original
|
|
|
|
expectedTokenLength = auth.TokenLength * 2
|
2022-07-06 22:45:50 +02:00
|
|
|
if len(authToken.Token) != expectedTokenLength {
|
2022-07-31 22:43:36 +02:00
|
|
|
t.Fatalf("Expected auth response to contain token length %d: result: %+v", auth.TokenLength, string(responseBody))
|
2022-07-06 22:45:50 +02:00
|
|
|
}
|
|
|
|
if authToken.DeviceId != "dev-1" {
|
|
|
|
t.Fatalf("Unexpected response DeviceId. want: %+v got: %+v", "dev-1", authToken.DeviceId)
|
|
|
|
}
|
|
|
|
if authToken.Scope != auth.ScopeFull {
|
|
|
|
t.Fatalf("Unexpected response Scope. want: %+v got: %+v", auth.ScopeFull, authToken.Scope)
|
|
|
|
}
|
|
|
|
|
|
|
|
////////////////////
|
|
|
|
t.Log("Request: Put first wallet - success")
|
|
|
|
////////////////////
|
|
|
|
|
|
|
|
responseBody, statusCode = request(
|
|
|
|
t,
|
|
|
|
http.MethodPost,
|
|
|
|
s.postWallet,
|
2022-08-01 17:50:16 +02:00
|
|
|
paths.PathWallet,
|
2022-07-06 22:45:50 +02:00
|
|
|
&walletPostResponse,
|
|
|
|
fmt.Sprintf(`{
|
|
|
|
"token": "%s",
|
|
|
|
"encryptedWallet": "my-encrypted-wallet-1",
|
|
|
|
"sequence": 1,
|
|
|
|
"hmac": "my-hmac-1"
|
|
|
|
}`, authToken.Token),
|
|
|
|
)
|
|
|
|
|
|
|
|
checkStatusCode(t, statusCode, responseBody)
|
|
|
|
|
|
|
|
////////////////////
|
|
|
|
t.Log("Request: Change password again, this time including a wallet (since there is a wallet to update)")
|
|
|
|
////////////////////
|
|
|
|
|
2022-08-27 17:37:09 +02:00
|
|
|
// Giving it a whole second of timeout because this request seems to be a bit
|
|
|
|
// slow.
|
|
|
|
go wsmm.getOneMessage(time.Second)
|
2022-07-06 22:45:50 +02:00
|
|
|
responseBody, statusCode = request(
|
|
|
|
t,
|
|
|
|
http.MethodPost,
|
|
|
|
s.changePassword,
|
2022-08-01 17:50:16 +02:00
|
|
|
paths.PathPassword,
|
2022-07-06 22:45:50 +02:00
|
|
|
&changePasswordResponse,
|
|
|
|
fmt.Sprintf(`{
|
|
|
|
"encryptedWallet": "my-encrypted-wallet-2",
|
|
|
|
"sequence": 2,
|
|
|
|
"hmac": "my-hmac-2",
|
|
|
|
"email": "abc@example.com",
|
2022-08-23 01:41:30 +02:00
|
|
|
"oldPassword": "45678901",
|
|
|
|
"newPassword": "78901234",
|
2022-07-15 21:36:11 +02:00
|
|
|
"clientSaltSeed": "0000ffff0000ffff0000ffff0000ffff0000ffff0000ffff0000ffff0000ffff"
|
2022-07-06 22:45:50 +02:00
|
|
|
}`),
|
|
|
|
)
|
2022-08-27 17:37:09 +02:00
|
|
|
<-wsmm.done
|
2022-07-06 22:45:50 +02:00
|
|
|
|
|
|
|
checkStatusCode(t, statusCode, responseBody)
|
|
|
|
|
2022-07-15 21:36:11 +02:00
|
|
|
////////////////////
|
|
|
|
t.Log("Request: Get client salt seed")
|
|
|
|
////////////////////
|
|
|
|
|
|
|
|
responseBody, statusCode = request(
|
|
|
|
t,
|
|
|
|
http.MethodGet,
|
|
|
|
s.getClientSaltSeed,
|
2022-08-01 17:50:16 +02:00
|
|
|
fmt.Sprintf("%s?email=%s", paths.PathClientSaltSeed, base64.StdEncoding.EncodeToString([]byte("abc@example.com"))),
|
2022-07-15 21:36:11 +02:00
|
|
|
&clientSaltSeedResponse,
|
|
|
|
"",
|
|
|
|
)
|
|
|
|
|
|
|
|
checkStatusCode(t, statusCode, responseBody)
|
|
|
|
|
|
|
|
if clientSaltSeedResponse.ClientSaltSeed != "0000ffff0000ffff0000ffff0000ffff0000ffff0000ffff0000ffff0000ffff" {
|
|
|
|
t.Fatalf("Unexpected client salt seed. want: %+v got: %+v", "0000ffff0000ffff0000ffff0000ffff0000ffff0000ffff0000ffff0000ffff", clientSaltSeedResponse.ClientSaltSeed)
|
|
|
|
}
|
|
|
|
|
2022-07-06 22:45:50 +02:00
|
|
|
////////////////////
|
|
|
|
t.Log("Request: Get wallet - fail because token invalidated on password change")
|
|
|
|
////////////////////
|
|
|
|
|
|
|
|
var walletGetResponse WalletResponse
|
|
|
|
responseBody, statusCode = request(
|
|
|
|
t,
|
|
|
|
http.MethodGet,
|
|
|
|
s.getWallet,
|
2022-08-01 17:50:16 +02:00
|
|
|
fmt.Sprintf("%s?token=%s", paths.PathWallet, authToken.Token),
|
2022-07-06 22:45:50 +02:00
|
|
|
&walletGetResponse,
|
|
|
|
"",
|
|
|
|
)
|
|
|
|
|
|
|
|
checkStatusCode(t, statusCode, responseBody, http.StatusUnauthorized)
|
|
|
|
|
|
|
|
////////////////////
|
|
|
|
t.Log("Request: Get another auth token")
|
|
|
|
////////////////////
|
|
|
|
|
|
|
|
responseBody, statusCode = request(
|
|
|
|
t,
|
|
|
|
http.MethodPost,
|
|
|
|
s.getAuthToken,
|
2022-08-01 17:50:16 +02:00
|
|
|
paths.PathAuthToken,
|
2022-07-06 22:45:50 +02:00
|
|
|
&authToken,
|
2022-08-23 01:41:30 +02:00
|
|
|
`{"deviceId": "dev-1", "email": "abc@example.com", "password": "78901234"}`,
|
2022-07-06 22:45:50 +02:00
|
|
|
)
|
|
|
|
|
|
|
|
checkStatusCode(t, statusCode, responseBody)
|
|
|
|
|
2022-07-28 01:45:09 +02:00
|
|
|
// result.Token is in hex, auth.TokenLength is bytes in the original
|
|
|
|
expectedTokenLength = auth.TokenLength * 2
|
2022-07-06 22:45:50 +02:00
|
|
|
if len(authToken.Token) != expectedTokenLength {
|
2022-07-31 22:43:36 +02:00
|
|
|
t.Fatalf("Expected auth response to contain token length %d: result: %+v", auth.TokenLength, string(responseBody))
|
2022-07-06 22:45:50 +02:00
|
|
|
}
|
|
|
|
if authToken.DeviceId != "dev-1" {
|
|
|
|
t.Fatalf("Unexpected response DeviceId. want: %+v got: %+v", "dev-1", authToken.DeviceId)
|
|
|
|
}
|
|
|
|
if authToken.Scope != auth.ScopeFull {
|
|
|
|
t.Fatalf("Unexpected response Scope. want: %+v got: %+v", auth.ScopeFull, authToken.Scope)
|
|
|
|
}
|
|
|
|
|
|
|
|
////////////////////
|
|
|
|
t.Log("Request: Get wallet")
|
|
|
|
////////////////////
|
|
|
|
|
|
|
|
responseBody, statusCode = request(
|
|
|
|
t,
|
|
|
|
http.MethodGet,
|
|
|
|
s.getWallet,
|
2022-08-01 17:50:16 +02:00
|
|
|
fmt.Sprintf("%s?token=%s", paths.PathWallet, authToken.Token),
|
2022-07-06 22:45:50 +02:00
|
|
|
&walletGetResponse,
|
|
|
|
"",
|
|
|
|
)
|
|
|
|
|
|
|
|
checkStatusCode(t, statusCode, responseBody)
|
|
|
|
|
|
|
|
expectedResponse := WalletResponse{
|
|
|
|
EncryptedWallet: wallet.EncryptedWallet("my-encrypted-wallet-2"),
|
|
|
|
Sequence: wallet.Sequence(2),
|
|
|
|
Hmac: wallet.WalletHmac("my-hmac-2"),
|
|
|
|
}
|
|
|
|
|
|
|
|
if !reflect.DeepEqual(walletGetResponse, expectedResponse) {
|
|
|
|
t.Fatalf("Unexpected response values. want: %+v got: %+v", expectedResponse, walletGetResponse)
|
|
|
|
}
|
|
|
|
}
|
2022-07-31 22:43:36 +02:00
|
|
|
|
|
|
|
func TestIntegrationVerifyAccount(t *testing.T) {
|
|
|
|
st, tmpFile := storeTestInit(t)
|
|
|
|
defer storeTestCleanup(tmpFile)
|
|
|
|
|
|
|
|
// Excluding env and email from the integration. We will spy on emails sent.
|
|
|
|
env := map[string]string{
|
|
|
|
"ACCOUNT_VERIFICATION_MODE": "EmailVerify",
|
|
|
|
}
|
|
|
|
testMail := TestMail{}
|
2022-08-27 17:37:09 +02:00
|
|
|
s := Init(&auth.Auth{}, &st, &TestEnv{env}, &testMail, TestPort)
|
2022-07-31 22:43:36 +02:00
|
|
|
|
|
|
|
////////////////////
|
|
|
|
t.Log("Request: Register email address")
|
|
|
|
////////////////////
|
|
|
|
|
|
|
|
var registerResponse struct{}
|
|
|
|
responseBody, statusCode := request(
|
|
|
|
t,
|
|
|
|
http.MethodPost,
|
|
|
|
s.register,
|
2022-08-01 17:50:16 +02:00
|
|
|
paths.PathRegister,
|
2022-07-31 22:43:36 +02:00
|
|
|
®isterResponse,
|
2022-08-23 01:41:30 +02:00
|
|
|
`{"email": "abc@example.com", "password": "12345678", "clientSaltSeed": "1234abcd1234abcd1234abcd1234abcd1234abcd1234abcd1234abcd1234abcd"}`,
|
2022-07-31 22:43:36 +02:00
|
|
|
)
|
|
|
|
|
|
|
|
checkStatusCode(t, statusCode, responseBody, http.StatusCreated)
|
|
|
|
|
|
|
|
// result.Token is in hex, auth.TokenLength is bytes in the original
|
|
|
|
expectedTokenLength := auth.TokenLength * 2
|
|
|
|
if len(testMail.SendVerificationEmailCall.Token) != expectedTokenLength {
|
|
|
|
t.Fatalf("Expected account verify email to contain token length %d: result: %+v", auth.TokenLength, string(responseBody))
|
|
|
|
}
|
|
|
|
|
|
|
|
////////////////////
|
|
|
|
t.Log("Request: Resend verify email")
|
|
|
|
////////////////////
|
|
|
|
|
|
|
|
var resendVerifyResponse struct{}
|
|
|
|
responseBody, statusCode = request(
|
|
|
|
t,
|
|
|
|
http.MethodPost,
|
|
|
|
s.resendVerifyEmail,
|
2022-08-01 17:50:16 +02:00
|
|
|
paths.PathResendVerify,
|
2022-07-31 22:43:36 +02:00
|
|
|
&resendVerifyResponse,
|
|
|
|
`{"email": "abc@example.com"}`,
|
|
|
|
)
|
|
|
|
|
|
|
|
checkStatusCode(t, statusCode, responseBody)
|
|
|
|
|
|
|
|
// result.Token is in hex, auth.TokenLength is bytes in the original
|
|
|
|
expectedTokenLength = auth.TokenLength * 2
|
|
|
|
if len(testMail.SendVerificationEmailCall.Token) != expectedTokenLength {
|
|
|
|
t.Fatalf("Expected account verify email to contain token length %d: result: %+v", auth.TokenLength, string(responseBody))
|
|
|
|
}
|
|
|
|
|
|
|
|
////////////////////
|
|
|
|
t.Log("Request: Get auth token and fail for not being verified")
|
|
|
|
////////////////////
|
|
|
|
|
|
|
|
var authToken auth.AuthToken
|
|
|
|
responseBody, statusCode = request(
|
|
|
|
t,
|
|
|
|
http.MethodPost,
|
|
|
|
s.getAuthToken,
|
2022-08-01 17:50:16 +02:00
|
|
|
paths.PathAuthToken,
|
2022-07-31 22:43:36 +02:00
|
|
|
&authToken,
|
2022-08-23 01:41:30 +02:00
|
|
|
`{"deviceId": "dev-1", "email": "abc@example.com", "password": "12345678"}`,
|
2022-07-31 22:43:36 +02:00
|
|
|
)
|
|
|
|
|
|
|
|
checkStatusCode(t, statusCode, responseBody, http.StatusUnauthorized)
|
|
|
|
|
|
|
|
////////////////////
|
|
|
|
t.Log("Request: Verify account")
|
|
|
|
////////////////////
|
|
|
|
|
|
|
|
responseBody, statusCode = request(
|
|
|
|
t,
|
|
|
|
http.MethodGet,
|
|
|
|
s.verify,
|
2022-08-01 17:50:16 +02:00
|
|
|
paths.PathVerify+"?verifyToken="+string(testMail.SendVerificationEmailCall.Token),
|
2022-07-31 22:43:36 +02:00
|
|
|
nil,
|
|
|
|
``,
|
|
|
|
)
|
|
|
|
|
|
|
|
checkStatusCode(t, statusCode, responseBody)
|
|
|
|
if strings.TrimSpace(string(responseBody)) != "Your account has been verified." {
|
|
|
|
t.Fatalf("Unexpected resonse from verify account endpoint. Got: '" + string(responseBody) + "'")
|
|
|
|
}
|
|
|
|
|
|
|
|
////////////////////
|
|
|
|
t.Log("Request: Get auth token")
|
|
|
|
////////////////////
|
|
|
|
|
|
|
|
responseBody, statusCode = request(
|
|
|
|
t,
|
|
|
|
http.MethodPost,
|
|
|
|
s.getAuthToken,
|
2022-08-01 17:50:16 +02:00
|
|
|
paths.PathAuthToken,
|
2022-07-31 22:43:36 +02:00
|
|
|
&authToken,
|
2022-08-23 01:41:30 +02:00
|
|
|
`{"deviceId": "dev-1", "email": "abc@example.com", "password": "12345678"}`,
|
2022-07-31 22:43:36 +02:00
|
|
|
)
|
|
|
|
|
|
|
|
checkStatusCode(t, statusCode, responseBody)
|
|
|
|
|
|
|
|
// result.Token is in hex, auth.TokenLength is bytes in the original
|
|
|
|
expectedTokenLength = auth.TokenLength * 2
|
|
|
|
if len(authToken.Token) != expectedTokenLength {
|
|
|
|
t.Fatalf("Expected auth response to contain token length %d: result: %+v", auth.TokenLength, string(responseBody))
|
|
|
|
}
|
|
|
|
if authToken.DeviceId != "dev-1" {
|
|
|
|
t.Fatalf("Unexpected response DeviceId. want: %+v got: %+v", "dev-1", authToken.DeviceId)
|
|
|
|
}
|
|
|
|
if authToken.Scope != auth.ScopeFull {
|
|
|
|
t.Fatalf("Unexpected response Scope. want: %+v got: %+v", auth.ScopeFull, authToken.Scope)
|
|
|
|
}
|
|
|
|
}
|