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

View File

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

View File

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

View File

@ -76,10 +76,10 @@ QtObject:
proc storeSelectedAccountAndLogin*(self: View, password: string) {.slot.} = proc storeSelectedAccountAndLogin*(self: View, password: string) {.slot.} =
self.delegate.storeSelectedAccountAndLogin(password) self.delegate.storeSelectedAccountAndLogin(password)
proc accountSetupError*(self: View) {.signal.} proc accountSetupError*(self: View, error: string) {.signal.}
proc setupAccountError*(self: View) = proc setupAccountError*(self: View, error: string) =
self.accountSetupError() self.accountSetupError(error)
proc validateMnemonic*(self: View, mnemonic: string): string {.slot.} = proc validateMnemonic*(self: View, mnemonic: string): string {.slot.} =
return self.delegate.validateMnemonic(mnemonic) return self.delegate.validateMnemonic(mnemonic)
@ -87,15 +87,18 @@ QtObject:
proc importMnemonic*(self: View, mnemonic: string) {.slot.} = proc importMnemonic*(self: View, mnemonic: string) {.slot.} =
self.delegate.importMnemonic(mnemonic) 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 # In QML we can connect to this signal and notify a user
# before refactoring we didn't have this signal # before refactoring we didn't have this signal
self.accountImportError() self.accountImportError(error)
proc accountImportSuccess*(self: View) {.signal.}
proc importAccountSuccess*(self: View) = proc importAccountSuccess*(self: View) =
self.importedAccountChanged() self.importedAccountChanged()
self.accountImportSuccess()
proc getPasswordStrengthScore*(self: View, password: string, userName: string): int {.slot.} = proc getPasswordStrengthScore*(self: View, password: string, userName: string): int {.slot.} =
return self.delegate.getPasswordStrengthScore(password, userName) 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): if(jsonObj.getProp("images", imagesObj) and imagesObj.kind == JArray):
for imgObj in imagesObj: for imgObj in imagesObj:
result.images.add(toImage(imgObj)) 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" topics = "accounts-service"
const PATHS = @[PATH_WALLET_ROOT, PATH_EIP_1581, PATH_WHISPER, PATH_DEFAULT_WALLET] const PATHS = @[PATH_WALLET_ROOT, PATH_EIP_1581, PATH_WHISPER, PATH_DEFAULT_WALLET]
const ACCOUNT_ALREADY_EXISTS_ERROR = "account already exists"
type type
Service* = ref object of RootObj Service* = ref object of RootObj
fleetConfiguration: FleetConfiguration fleetConfiguration: FleetConfiguration
generatedAccounts: seq[GeneratedAccountDto] generatedAccounts: seq[GeneratedAccountDto]
accounts: seq[AccountDto]
loggedInAccount: AccountDto loggedInAccount: AccountDto
importedAccount: GeneratedAccountDto importedAccount: GeneratedAccountDto
isFirstTimeAccountLogin: bool isFirstTimeAccountLogin: bool
@ -98,21 +100,21 @@ proc openedAccounts*(self: Service): seq[AccountDto] =
try: try:
let response = status_account.openedAccounts(main_constants.STATUSGODIR) 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: except Exception as e:
error "error: ", procName="openedAccounts", errName = e.name, errDesription = e.msg error "error: ", procName="openedAccounts", errName = e.name, errDesription = e.msg
proc storeDerivedAccounts(self: Service, accountId, hashedPassword: string, proc storeDerivedAccounts(self: Service, accountId, hashedPassword: string,
paths: seq[string]): DerivedAccounts = paths: seq[string]): DerivedAccounts =
try: let response = status_account.storeDerivedAccounts(accountId, hashedPassword, paths)
let response = status_account.storeDerivedAccounts(accountId, hashedPassword, paths)
result = toDerivedAccounts(response.result)
except Exception as e: if response.result.contains("error"):
error "error: ", procName="storeDerivedAccounts", errName = e.name, errDesription = e.msg raise newException(Exception, response.result["error"].getStr)
result = toDerivedAccounts(response.result)
proc saveAccountAndLogin(self: Service, hashedPassword: string, account, proc saveAccountAndLogin(self: Service, hashedPassword: string, account,
subaccounts, settings, config: JsonNode): AccountDto = 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 # 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") # 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: try:
let installationId = $genUUID() let installationId = $genUUID()
let accountDataJson = self.getAccountDataForAccountId(accountId, displayName) let accountDataJson = self.getAccountDataForAccountId(accountId, displayName)
@ -262,36 +264,41 @@ proc setupAccount*(self: Service, accountId, password, displayName: string): boo
nodeConfigJson.isNil): nodeConfigJson.isNil):
let description = "at least one json object is not prepared well" let description = "at least one json object is not prepared well"
error "error: ", procName="setupAccount", errDesription = description error "error: ", procName="setupAccount", errDesription = description
return false return description
let hashedPassword = hashString(password) let hashedPassword = hashString(password)
discard self.storeDerivedAccounts(accountId, hashedPassword, PATHS) discard self.storeDerivedAccounts(accountId, hashedPassword, PATHS)
self.loggedInAccount = self.saveAccountAndLogin(hashedPassword, accountDataJson, subaccountDataJson, settingsJson, self.loggedInAccount = self.saveAccountAndLogin(hashedPassword, accountDataJson,
nodeConfigJson) subaccountDataJson, settingsJson, nodeConfigJson)
return self.getLoggedInAccount.isValid()
if self.getLoggedInAccount.isValid():
return ""
else:
return "logged in account is not valid"
except Exception as e: except Exception as e:
error "error: ", procName="setupAccount", errName = e.name, errDesription = e.msg 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: try:
let response = status_account.multiAccountImportMnemonic(mnemonic) let response = status_account.multiAccountImportMnemonic(mnemonic)
self.importedAccount = toGeneratedAccountDto(response.result) 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) let responseDerived = status_account.deriveAccounts(self.importedAccount.id, PATHS)
self.importedAccount.derivedAccounts = toDerivedAccounts(responseDerived.result) self.importedAccount.derivedAccounts = toDerivedAccounts(responseDerived.result)
self.importedAccount.alias= generateAliasFromPk(self.importedAccount.derivedAccounts.whisper.publicKey) self.importedAccount.alias= generateAliasFromPk(self.importedAccount.derivedAccounts.whisper.publicKey)
self.importedAccount.identicon = generateIdenticonFromPk(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: except Exception as e:
error "error: ", procName="importMnemonic", errName = e.name, errDesription = e.msg error "error: ", procName="importMnemonic", errName = e.name, errDesription = e.msg
return false return e.msg
proc login*(self: Service, account: AccountDto, password: string): string = proc login*(self: Service, account: AccountDto, password: string): string =
try: try:

View File

@ -1,6 +1,7 @@
import QtQuick 2.0 import QtQuick 2.0
import QtQuick.Controls 2.13 import QtQuick.Controls 2.13
import QtQuick.Layouts 1.12 import QtQuick.Layouts 1.12
import QtQuick.Dialogs 1.3
import shared.controls 1.0 import shared.controls 1.0
import shared 1.0 import shared 1.0
@ -130,6 +131,35 @@ OnboardingBasePage {
pause.start(); 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 2.13
import QtQuick.Controls 2.13 import QtQuick.Controls 2.13
import QtQuick.Dialogs 1.3
import utils 1.0 import utils 1.0
@ -19,14 +20,35 @@ Item {
enterSeedPhraseModal.open() 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 { EnterSeedPhraseModal {
property bool wentNext: false property bool wentNext: false
id: enterSeedPhraseModal id: enterSeedPhraseModal
onConfirmSeedClick: function (mnemonic) { onConfirmSeedClick: function (mnemonic) {
wentNext = true
enterSeedPhraseModal.close()
OnboardingStore.importMnemonic(mnemonic) OnboardingStore.importMnemonic(mnemonic)
recoverySuccessModal.open()
} }
onClosed: function () { onClosed: function () {
if (!wentNext) { if (!wentNext) {

View File

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