fix(onboarding): show warning when trying to re-encrypt existing account

Fixes #5029
This commit is contained in:
Jonathan Rainville 2022-03-23 15:51:39 -04:00 committed by Iuri Matias
parent c0096453db
commit d35344834e
9 changed files with 110 additions and 38 deletions

View File

@ -37,7 +37,7 @@ proc init*(self: Controller) =
self.events.on(SignalType.NodeLogin.event) do(e:Args):
let signal = NodeSignal(e)
if signal.event.error != "":
self.delegate.setupAccountError()
self.delegate.setupAccountError(signal.event.error)
proc getGeneratedAccounts*(self: Controller): seq[GeneratedAccountDto] =
return self.accountsService.generatedAccounts()
@ -53,18 +53,20 @@ proc setDisplayName*(self: Controller, displayName: string) =
self.displayName = displayName
proc storeSelectedAccountAndLogin*(self: Controller, password: string) =
if(not self.accountsService.setupAccount(self.selectedAccountId, password, self.displayName)):
self.delegate.setupAccountError()
let error = self.accountsService.setupAccount(self.selectedAccountId, password, self.displayName)
if error != "":
self.delegate.setupAccountError(error)
proc validateMnemonic*(self: Controller, mnemonic: string): string =
return self.accountsService.validateMnemonic(mnemonic)
proc importMnemonic*(self: Controller, mnemonic: string) =
if(self.accountsService.importMnemonic(mnemonic)):
let error = self.accountsService.importMnemonic(mnemonic)
if(error == ""):
self.selectedAccountId = self.getImportedAccount().id
self.delegate.importAccountSuccess()
else:
self.delegate.importAccountError()
self.delegate.importAccountError(error)
method getPasswordStrengthScore*(self: Controller, password, userName: string): int =
return self.generalService.getPasswordStrengthScore(password, userName)

View File

@ -12,10 +12,10 @@ method load*(self: AccessInterface) {.base.} =
method isLoaded*(self: AccessInterface): bool {.base.} =
raise newException(ValueError, "No implementation available")
method setupAccountError*(self: AccessInterface) {.base.} =
method setupAccountError*(self: AccessInterface, error: string) {.base.} =
raise newException(ValueError, "No implementation available")
method importAccountError*(self: AccessInterface) {.base.} =
method importAccountError*(self: AccessInterface, error: string) {.base.} =
raise newException(ValueError, "No implementation available")
method importAccountSuccess*(self: AccessInterface) {.base.} =

View File

@ -61,8 +61,8 @@ method setDisplayName*(self: Module, displayName: string) =
method storeSelectedAccountAndLogin*(self: Module, password: string) =
self.controller.storeSelectedAccountAndLogin(password)
method setupAccountError*(self: Module) =
self.view.setupAccountError()
method setupAccountError*(self: Module, error: string) =
self.view.setupAccountError(error)
method getImportedAccount*(self: Module): GeneratedAccountDto =
return self.controller.getImportedAccount()
@ -73,8 +73,8 @@ method validateMnemonic*(self: Module, mnemonic: string): string =
method importMnemonic*(self: Module, mnemonic: string) =
self.controller.importMnemonic(mnemonic)
method importAccountError*(self: Module) =
self.view.importAccountError()
method importAccountError*(self: Module, error: string) =
self.view.importAccountError(error)
method importAccountSuccess*(self: Module) =
self.view.importAccountSuccess()

View File

@ -76,10 +76,10 @@ QtObject:
proc storeSelectedAccountAndLogin*(self: View, password: string) {.slot.} =
self.delegate.storeSelectedAccountAndLogin(password)
proc accountSetupError*(self: View) {.signal.}
proc accountSetupError*(self: View, error: string) {.signal.}
proc setupAccountError*(self: View) =
self.accountSetupError()
proc setupAccountError*(self: View, error: string) =
self.accountSetupError(error)
proc validateMnemonic*(self: View, mnemonic: string): string {.slot.} =
return self.delegate.validateMnemonic(mnemonic)
@ -87,15 +87,18 @@ QtObject:
proc importMnemonic*(self: View, mnemonic: string) {.slot.} =
self.delegate.importMnemonic(mnemonic)
proc accountImportError*(self: View) {.signal.}
proc accountImportError*(self: View, error: string) {.signal.}
proc importAccountError*(self: View) =
proc importAccountError*(self: View, error: string) =
# In QML we can connect to this signal and notify a user
# before refactoring we didn't have this signal
self.accountImportError()
self.accountImportError(error)
proc accountImportSuccess*(self: View) {.signal.}
proc importAccountSuccess*(self: View) =
self.importedAccountChanged()
self.accountImportSuccess()
proc getPasswordStrengthScore*(self: View, password: string, userName: string): int {.slot.} =
return self.delegate.getPasswordStrengthScore(password, userName)

View File

@ -47,3 +47,9 @@ proc toAccountDto*(jsonObj: JsonNode): AccountDto =
if(jsonObj.getProp("images", imagesObj) and imagesObj.kind == JArray):
for imgObj in imagesObj:
result.images.add(toImage(imgObj))
proc contains*(accounts: seq[AccountDto], keyUid: string): bool =
for account in accounts:
if (account.keyUid == keyUid):
return true
return false

View File

@ -18,11 +18,13 @@ logScope:
topics = "accounts-service"
const PATHS = @[PATH_WALLET_ROOT, PATH_EIP_1581, PATH_WHISPER, PATH_DEFAULT_WALLET]
const ACCOUNT_ALREADY_EXISTS_ERROR = "account already exists"
type
Service* = ref object of RootObj
fleetConfiguration: FleetConfiguration
generatedAccounts: seq[GeneratedAccountDto]
accounts: seq[AccountDto]
loggedInAccount: AccountDto
importedAccount: GeneratedAccountDto
isFirstTimeAccountLogin: bool
@ -98,21 +100,21 @@ proc openedAccounts*(self: Service): seq[AccountDto] =
try:
let response = status_account.openedAccounts(main_constants.STATUSGODIR)
let accounts = map(response.result.getElems(), proc(x: JsonNode): AccountDto = toAccountDto(x))
self.accounts = map(response.result.getElems(), proc(x: JsonNode): AccountDto = toAccountDto(x))
return accounts
return self.accounts
except Exception as e:
error "error: ", procName="openedAccounts", errName = e.name, errDesription = e.msg
proc storeDerivedAccounts(self: Service, accountId, hashedPassword: string,
paths: seq[string]): DerivedAccounts =
try:
let response = status_account.storeDerivedAccounts(accountId, hashedPassword, paths)
result = toDerivedAccounts(response.result)
except Exception as e:
error "error: ", procName="storeDerivedAccounts", errName = e.name, errDesription = e.msg
if response.result.contains("error"):
raise newException(Exception, response.result["error"].getStr)
result = toDerivedAccounts(response.result)
proc saveAccountAndLogin(self: Service, hashedPassword: string, account,
subaccounts, settings, config: JsonNode): AccountDto =
@ -250,7 +252,7 @@ proc getDefaultNodeConfig*(self: Service, installationId: string): JsonNode =
# TODO: commented since it's not necessary (we do the connections thru C bindings). Enable it thru an option once status-nodes are able to be configured in desktop
# result["ListenAddr"] = if existsEnv("STATUS_PORT"): newJString("0.0.0.0:" & $getEnv("STATUS_PORT")) else: newJString("0.0.0.0:30305")
proc setupAccount*(self: Service, accountId, password, displayName: string): bool =
proc setupAccount*(self: Service, accountId, password, displayName: string): string =
try:
let installationId = $genUUID()
let accountDataJson = self.getAccountDataForAccountId(accountId, displayName)
@ -262,36 +264,41 @@ proc setupAccount*(self: Service, accountId, password, displayName: string): boo
nodeConfigJson.isNil):
let description = "at least one json object is not prepared well"
error "error: ", procName="setupAccount", errDesription = description
return false
return description
let hashedPassword = hashString(password)
discard self.storeDerivedAccounts(accountId, hashedPassword, PATHS)
self.loggedInAccount = self.saveAccountAndLogin(hashedPassword, accountDataJson, subaccountDataJson, settingsJson,
nodeConfigJson)
return self.getLoggedInAccount.isValid()
self.loggedInAccount = self.saveAccountAndLogin(hashedPassword, accountDataJson,
subaccountDataJson, settingsJson, nodeConfigJson)
if self.getLoggedInAccount.isValid():
return ""
else:
return "logged in account is not valid"
except Exception as e:
error "error: ", procName="setupAccount", errName = e.name, errDesription = e.msg
return false
return e.msg
proc importMnemonic*(self: Service, mnemonic: string): bool =
proc importMnemonic*(self: Service, mnemonic: string): string =
try:
let response = status_account.multiAccountImportMnemonic(mnemonic)
self.importedAccount = toGeneratedAccountDto(response.result)
if (self.accounts.contains(self.importedAccount.keyUid)):
return ACCOUNT_ALREADY_EXISTS_ERROR
let responseDerived = status_account.deriveAccounts(self.importedAccount.id, PATHS)
self.importedAccount.derivedAccounts = toDerivedAccounts(responseDerived.result)
self.importedAccount.alias= generateAliasFromPk(self.importedAccount.derivedAccounts.whisper.publicKey)
self.importedAccount.identicon = generateIdenticonFromPk(self.importedAccount.derivedAccounts.whisper.publicKey)
return self.importedAccount.isValid()
if (not self.importedAccount.isValid()):
return "imported account is not valid"
except Exception as e:
error "error: ", procName="importMnemonic", errName = e.name, errDesription = e.msg
return false
return e.msg
proc login*(self: Service, account: AccountDto, password: string): string =
try:

View File

@ -1,6 +1,7 @@
import QtQuick 2.0
import QtQuick.Controls 2.13
import QtQuick.Layouts 1.12
import QtQuick.Dialogs 1.3
import shared.controls 1.0
import shared 1.0
@ -130,6 +131,35 @@ OnboardingBasePage {
pause.start();
}
}
Connections {
target: onboardingModule
onAccountSetupError: {
if (error === Constants.existingAccountError) {
importLoginError.title = qsTr("Keys for this account already exist")
importLoginError.text = qsTr("Keys for this account already exist and can't be added again. If you've lost your password, passcode or Keycard, uninstall the app, reinstall and access your keys by entering your seed phrase")
} else {
//% "Login failed"
importLoginError.title = qsTrId("login-failed")
//% "Login failed. Please re-enter your password and try again."
importLoginError.text = qsTrId("login-failed.-please-re-enter-your-password-and-try-again.")
}
importLoginError.open()
}
}
MessageDialog {
id: importLoginError
//% "Login failed"
title: qsTrId("login-failed")
//% "Login failed. Please re-enter your password and try again."
text: qsTrId("login-failed.-please-re-enter-your-password-and-try-again.")
icon: StandardIcon.Critical
standardButtons: StandardButton.Ok
onVisibilityChanged: {
submitBtn.loading = false
}
}
}
}

View File

@ -1,5 +1,6 @@
import QtQuick 2.13
import QtQuick.Controls 2.13
import QtQuick.Dialogs 1.3
import utils 1.0
@ -19,14 +20,35 @@ Item {
enterSeedPhraseModal.open()
}
Connections {
target: onboardingModule
onAccountImportError: {
if (error === Constants.existingAccountError) {
importSeedError.title = qsTr("Keys for this account already exist")
importSeedError.text = qsTr("Keys for this account already exist and can't be added again. If you've lost your password, passcode or Keycard, uninstall the app, reinstall and access your keys by entering your seed phrase")
} else {
importSeedError.title = qsTr("Error importing seed")
importSeedError.text = error
}
importSeedError.open()
}
onAccountImportSuccess: {
enterSeedPhraseModal.wentNext = true
enterSeedPhraseModal.close()
recoverySuccessModal.open()
}
}
MessageDialog {
id: importSeedError
icon: StandardIcon.Critical
standardButtons: StandardButton.Ok
}
EnterSeedPhraseModal {
property bool wentNext: false
id: enterSeedPhraseModal
onConfirmSeedClick: function (mnemonic) {
wentNext = true
enterSeedPhraseModal.close()
OnboardingStore.importMnemonic(mnemonic)
recoverySuccessModal.open()
}
onClosed: function () {
if (!wentNext) {

View File

@ -247,4 +247,6 @@ QtObject {
}
readonly property bool isCppApp: typeof cppApp !== "undefined" ? cppApp : false
readonly property string existingAccountError: "account already exists"
}