feat(@desktop/onboarding): `Lost Keycard` - start using account without keycard

This commit introduces:
- `Start using account without keycard` flow

Closes: #7642
This commit is contained in:
Sale Djenic 2023-01-26 12:52:01 +01:00 committed by Anthony Laibe
parent b00f0a80b5
commit 841a37e930
29 changed files with 334 additions and 86 deletions

View File

@ -373,8 +373,7 @@ proc convertSelectedKeyPairToKeycardAccount*(self: Controller, password: string)
return
let acc = self.accountsService.createAccountFromMnemonic(self.getSeedPhrase(), includeEncryption = true)
singletonInstance.localAccountSettings.setStoreToKeychainValue(LS_VALUE_NOT_NOW)
self.accountsService.convertToKeycardAccount(self.tmpSelectedKeyPairDto.keyUid,
currentPassword = password,
self.accountsService.convertToKeycardAccount(currentPassword = password,
newPassword = acc.derivedAccounts.encryption.publicKey)
proc getConvertingProfileSuccess*(self: Controller): bool =

View File

@ -338,7 +338,7 @@ proc importMnemonic*(self: Controller): bool =
self.delegate.importAccountSuccess()
return true
else:
self.delegate.importAccountError(error)
self.delegate.emitStartupError(error, StartupErrorType.ImportAccError)
return false
proc setupKeychain(self: Controller, store: bool) =
@ -352,7 +352,7 @@ proc setupAccount(self: Controller, accountId: string, storeToKeychain: bool) =
self.delegate.moveToLoadingAppState()
let error = self.accountsService.setupAccount(accountId, self.tmpPassword, self.tmpDisplayName)
if error != "":
self.delegate.setupAccountError(error)
self.delegate.emitStartupError(error, StartupErrorType.SetupAccError)
else:
self.setupKeychain(storeToKeychain)
@ -388,7 +388,7 @@ proc setupKeycardAccount*(self: Controller, storeToKeychain: bool, newKeycard: b
else:
if self.tmpKeycardEvent.keyUid.len == 0 or
self.accountsService.openedAccountsContainsKeyUid(self.tmpKeycardEvent.keyUid):
self.delegate.importAccountError(ACCOUNT_ALREADY_EXISTS_ERROR)
self.delegate.emitStartupError(ACCOUNT_ALREADY_EXISTS_ERROR, StartupErrorType.ImportAccError)
return
self.delegate.moveToLoadingAppState()
if newKeycard:
@ -441,19 +441,43 @@ proc login*(self: Controller) =
if(error.len > 0):
self.delegate.emitAccountLoginError(error)
proc loginAccountKeycard*(self: Controller, storeToKeychain = false, syncWalletAfterLogin = false) =
proc loginAccountKeycard*(self: Controller, storeToKeychainValue: string, syncWalletAfterLogin = false) =
if syncWalletAfterLogin:
self.syncKeycardBasedOnAppWalletStateAfterLogin()
singletonInstance.localAccountSettings.setStoreToKeychainValue(storeToKeychainValue)
self.delegate.moveToLoadingAppState()
let selAcc = self.getSelectedLoginAccount()
let error = self.accountsService.loginAccountKeycard(selAcc, self.tmpKeycardEvent)
if(error.len > 0):
self.delegate.emitAccountLoginError(error)
proc loginAccountKeycardUsingSeedPhrase*(self: Controller, storeToKeychain: bool) =
let acc = self.accountsService.createAccountFromMnemonic(self.getSeedPhrase(), includeEncryption = true, includeWhisper = true)
let selAcc = self.getSelectedLoginAccount()
var kcData = KeycardEvent(
keyUid: acc.keyUid,
masterKey: KeyDetails(address: acc.address),
whisperKey: KeyDetails(privateKey: acc.derivedAccounts.whisper.privateKey),
encryptionKey: KeyDetails(publicKey: acc.derivedAccounts.encryption.publicKey)
)
if acc.derivedAccounts.whisper.privateKey.startsWith("0x"):
kcData.whisperKey.privateKey = acc.derivedAccounts.whisper.privateKey[2..^1]
if storeToKeychain:
## storing not now, user will be asked to store the pin once he is logged in
singletonInstance.localAccountSettings.setStoreToKeychainValue(LS_VALUE_NOT_NOW)
else:
singletonInstance.localAccountSettings.setStoreToKeychainValue(LS_VALUE_NEVER)
self.delegate.moveToLoadingAppState()
let error = self.accountsService.loginAccountKeycard(self.tmpKeycardEvent)
let error = self.accountsService.loginAccountKeycard(selAcc, kcData)
if(error.len > 0):
self.delegate.emitAccountLoginError(error)
proc convertToRegularAccount*(self: Controller): string =
let acc = self.accountsService.createAccountFromMnemonic(self.getSeedPhrase(), includeEncryption = true)
return self.accountsService.convertToRegularAccount(self.getSeedPhrase(), acc.derivedAccounts.encryption.publicKey, self.getPassword())
proc getKeyUidForSeedPhrase*(self: Controller, seedPhrase: string): string =
let acc = self.accountsService.createAccountFromMnemonic(seedPhrase)
return acc.keyUid

View File

@ -29,6 +29,8 @@ method executePrimaryCommand*(self: BiometricsState, controller: Controller) =
elif self.flowType == FlowType.LostKeycardReplacement:
self.storeToKeychain = storeToKeychain
controller.startLoginFlowAutomatically(controller.getPin())
elif self.flowType == FlowType.LostKeycardConvertToRegularAccount:
controller.loginAccountKeycardUsingSeedPhrase(storeToKeychain)
method executeSecondaryCommand*(self: BiometricsState, controller: Controller) =
let storeToKeychain = false # false, cause we don't have keychain support for other than mac os
@ -49,6 +51,8 @@ method executeSecondaryCommand*(self: BiometricsState, controller: Controller) =
elif self.flowType == FlowType.LostKeycardReplacement:
self.storeToKeychain = storeToKeychain
controller.startLoginFlowAutomatically(controller.getPin())
elif self.flowType == FlowType.LostKeycardConvertToRegularAccount:
controller.loginAccountKeycardUsingSeedPhrase(storeToKeychain)
method resolveKeycardNextState*(self: BiometricsState, keycardFlowType: string, keycardEvent: KeycardEvent,
controller: Controller): State =
@ -56,4 +60,7 @@ method resolveKeycardNextState*(self: BiometricsState, keycardFlowType: string,
if keycardFlowType == ResponseTypeValueKeycardFlowResult and
keycardEvent.error.len == 0:
controller.setKeycardEvent(keycardEvent)
controller.loginAccountKeycard(self.storeToKeychain, syncWalletAfterLogin = true)
var storeToKeychainValue = LS_VALUE_NEVER
if self.storeToKeychain:
storeToKeychainValue = LS_VALUE_NOT_NOW
controller.loginAccountKeycard(storeToKeychainValue, syncWalletAfterLogin = true)

View File

@ -56,5 +56,6 @@ method resolveKeycardNextState*(self: KeycardEnterPukState, keycardFlowType: str
if keycardFlowType == ResponseTypeValueKeycardFlowResult:
controller.setKeycardEvent(keycardEvent)
controller.setPukValid(true)
controller.loginAccountKeycard()
let storeToKeychainValue = singletonInstance.localAccountSettings.getStoreToKeychainValue()
controller.loginAccountKeycard(storeToKeychainValue)
return nil

View File

@ -44,7 +44,8 @@ method executePrimaryCommand*(self: KeycardPinSetState, controller: Controller)
controller.startLoginFlowAutomatically(controller.getPin())
return
if controller.getValidPuk():
controller.loginAccountKeycard()
let storeToKeychainValue = singletonInstance.localAccountSettings.getStoreToKeychainValue()
controller.loginAccountKeycard(storeToKeychainValue)
if self.flowType == FlowType.LostKeycardReplacement:
if main_constants.IS_MACOS:
return
@ -56,10 +57,10 @@ method resolveKeycardNextState*(self: KeycardPinSetState, keycardFlowType: strin
if keycardFlowType == ResponseTypeValueKeycardFlowResult and
keycardEvent.error.len == 0:
controller.setKeycardEvent(keycardEvent)
controller.loginAccountKeycard(storeToKeychain = true, syncWalletAfterLogin = true)
controller.loginAccountKeycard(storeToKeychainValue = LS_VALUE_NOT_NOW, syncWalletAfterLogin = true)
if self.flowType == FlowType.AppLogin:
if keycardFlowType == ResponseTypeValueKeycardFlowResult and
keycardEvent.error.len == 0:
# we are here in case of recover account from the login flow using seed phrase
controller.setKeycardEvent(keycardEvent)
controller.loginAccountKeycard(storeToKeychain = false, syncWalletAfterLogin = false)
controller.loginAccountKeycard(storeToKeychainValue = LS_VALUE_NEVER, syncWalletAfterLogin = false)

View File

@ -10,6 +10,5 @@ proc delete*(self: KeycardWrongKeycardState) =
method executeBackCommand*(self: KeycardWrongKeycardState, controller: Controller) =
if self.flowType == FlowType.FirstRunOldUserKeycardImport or
self.flowType == FlowType.AppLogin or
self.flowType == FlowType.LostKeycardReplacement:
self.flowType == FlowType.AppLogin:
controller.setKeycardData(updatePredefinedKeycardData(controller.getKeycardData(), PredefinedKeycardData.WrongSeedPhrase, add = false))

View File

@ -59,5 +59,6 @@ method resolveKeycardNextState*(self: KeycardWrongPukState, keycardFlowType: str
if keycardFlowType == ResponseTypeValueKeycardFlowResult:
controller.setKeycardEvent(keycardEvent)
controller.setPukValid(true)
controller.loginAccountKeycard()
let storeToKeychainValue = singletonInstance.localAccountSettings.getStoreToKeychainValue()
controller.loginAccountKeycard(storeToKeychainValue)
return nil

View File

@ -0,0 +1,14 @@
type
LoginKeycardConvertedToRegularAccountState* = ref object of State
proc newLoginKeycardConvertedToRegularAccountState*(flowType: FlowType, backState: State): LoginKeycardConvertedToRegularAccountState =
result = LoginKeycardConvertedToRegularAccountState()
result.setup(flowType, StateType.LoginKeycardConvertedToRegularAccount, backState)
proc delete*(self: LoginKeycardConvertedToRegularAccountState) =
self.State.delete
method executePrimaryCommand*(self: LoginKeycardConvertedToRegularAccountState, controller: Controller) =
if self.flowType == FlowType.LostKeycardConvertToRegularAccount:
info "restart the app because of successfully converted keycard account to regular account"
quit() # quit the app

View File

@ -10,4 +10,5 @@ proc delete*(self: LoginKeycardPinVerifiedState) =
method executePrimaryCommand*(self: LoginKeycardPinVerifiedState, controller: Controller) =
if self.flowType == FlowType.AppLogin:
controller.loginAccountKeycard()
let storeToKeychainValue = singletonInstance.localAccountSettings.getStoreToKeychainValue()
controller.loginAccountKeycard(storeToKeychainValue)

View File

@ -19,8 +19,9 @@ method executePrimaryCommand*(self: LostKeycardOptionsState, controller: Control
self.setFlowType(FlowType.LostKeycardReplacement)
controller.runLoadAccountFlow()
method executeSecondaryCommand*(self: LostKeycardOptionsState, controller: Controller) =
echo "TODO: start using account without keycard..."
method getNextSecondaryState*(self: LostKeycardOptionsState, controller: Controller): State =
if controller.isSelectedAccountAKeycardAccount():
return createState(StateType.UserProfileEnterSeedPhrase, FlowType.LostKeycardConvertToRegularAccount, self)
method resolveKeycardNextState*(self: LostKeycardOptionsState, keycardFlowType: string, keycardEvent: KeycardEvent,
controller: Controller): State =

View File

@ -14,6 +14,7 @@ type FlowType* {.pure.} = enum
FirstRunOldUserImportSeedPhrase = "FirstRunOldUserImportSeedPhrase"
AppLogin = "AppLogin"
LostKeycardReplacement = "LostKeycardReplacement"
LostKeycardConvertToRegularAccount = "LostKeycardConvertToRegularAccount"
type StateType* {.pure.} = enum
NoState = "NoState"
@ -29,6 +30,7 @@ type StateType* {.pure.} = enum
UserProfileConfirmPassword = "UserProfileConfirmPassword"
UserProfileImportSeedPhrase = "UserProfileImportSeedPhrase"
UserProfileEnterSeedPhrase = "UserProfileEnterSeedPhrase"
UserProfileWrongSeedPhrase = "UserProfileWrongSeedPhrase"
Biometrics = "Biometrics"
KeycardPluginReader = "KeycardPluginReader"
KeycardInsertKeycard = "KeycardInsertKeycard"
@ -69,6 +71,7 @@ type StateType* {.pure.} = enum
LoginKeycardMaxPairingSlotsReached = "LoginKeycardMaxPairingSlotsReached"
LoginKeycardEmpty = "LoginKeycardEmpty"
LoginNotKeycard = "LoginNotKeycard"
LoginKeycardConvertedToRegularAccount = "LoginKeycardConvertedToRegularAccount"
ProfileFetching = "ProfileFetching"
ProfileFetchingSuccess = "ProfileFetchingSuccess"
ProfileFetchingTimeout = "ProfileFetchingTimeout"

View File

@ -1,4 +1,5 @@
import sequtils, sugar, chronicles
import ../../../global/global_singleton
import ../../../../constants as main_constants
import ../../../../app_service/service/keycard/constants
import ../controller
@ -54,12 +55,14 @@ include user_profile_create_password_state
include user_profile_create_state
include user_profile_create_same_chat_key_state
include user_profile_enter_seed_phrase_state
include user_profile_wrong_seed_phrase_state
include user_profile_import_seed_phrase_state
include welcome_state_new_user
include welcome_state_old_user
include welcome_state
include login_state
include login_plugin_state
include login_keycard_converted_to_regular_account_state
include login_keycard_insert_keycard_state
include login_keycard_inserted_keycard_state
include login_keycard_reading_keycard_state

View File

@ -21,6 +21,8 @@ proc createState*(stateToBeCreated: StateType, flowType: FlowType, backState: St
return newUserProfileImportSeedPhraseState(flowType, backState)
if stateToBeCreated == StateType.UserProfileEnterSeedPhrase:
return newUserProfileEnterSeedPhraseState(flowType, backState)
if stateToBeCreated == StateType.UserProfileWrongSeedPhrase:
return newUserProfileWrongSeedPhraseState(flowType, backState)
if stateToBeCreated == StateType.Biometrics:
return newBiometricsState(flowType, backState)
if stateToBeCreated == StateType.KeycardPluginReader:
@ -73,6 +75,8 @@ proc createState*(stateToBeCreated: StateType, flowType: FlowType, backState: St
return newLoginState(flowType, backState)
if stateToBeCreated == StateType.LoginPlugin:
return newLoginPluginState(flowType, backState)
if stateToBeCreated == StateType.LoginKeycardConvertedToRegularAccount:
return newLoginKeycardConvertedToRegularAccountState(flowType, backState)
if stateToBeCreated == StateType.LoginKeycardInsertKeycard:
return newLoginKeycardInsertKeycardState(flowType, backState)
if stateToBeCreated == StateType.LoginKeycardInsertedKeycard:

View File

@ -24,6 +24,8 @@ method executePrimaryCommand*(self: UserProfileConfirmPasswordState, controller:
elif self.flowType == FlowType.FirstRunNewUserNewKeycardKeys:
controller.storeKeycardAccountAndLogin(storeToKeychain, newKeycard = true)
elif self.flowType == FlowType.FirstRunOldUserImportSeedPhrase:
controller.storeImportedAccountAndLogin(storeToKeychain = false)
controller.storeImportedAccountAndLogin(storeToKeychain)
elif self.flowType == FlowType.LostKeycardConvertToRegularAccount:
controller.loginAccountKeycardUsingSeedPhrase(storeToKeychain)

View File

@ -32,6 +32,14 @@ method getNextPrimaryState*(self: UserProfileEnterSeedPhraseState, controller: C
if self.enteredMnemonicMatchTargetedKeyUid:
return createState(StateType.KeycardCreatePin, self.flowType, self)
return createState(StateType.KeycardWrongKeycard, self.flowType, self)
if self.flowType == FlowType.LostKeycardReplacement:
if self.enteredMnemonicMatchTargetedKeyUid:
return createState(StateType.KeycardCreatePin, self.flowType, self)
return createState(StateType.UserProfileWrongSeedPhrase, self.flowType, self)
if self.flowType == FlowType.LostKeycardConvertToRegularAccount:
if self.enteredMnemonicMatchTargetedKeyUid:
return createState(StateType.UserProfileCreatePassword, self.flowType, self)
return createState(StateType.UserProfileWrongSeedPhrase, self.flowType, self)
method executePrimaryCommand*(self: UserProfileEnterSeedPhraseState, controller: Controller) =
if self.flowType == FlowType.FirstRunNewUserImportSeedPhrase or
@ -59,6 +67,10 @@ method executePrimaryCommand*(self: UserProfileEnterSeedPhraseState, controller:
controller.storeSeedPhraseToKeycard(controller.getSeedPhraseLength(), controller.getSeedPhrase())
else:
controller.setKeycardData(updatePredefinedKeycardData(controller.getKeycardData(), PredefinedKeycardData.WrongSeedPhrase, add = true))
if self.flowType == FlowType.LostKeycardConvertToRegularAccount:
self.enteredMnemonicMatchTargetedKeyUid = controller.keyUidMatchSelectedLoginAccount(keyUid)
if not self.enteredMnemonicMatchTargetedKeyUid:
controller.setKeycardData(updatePredefinedKeycardData(controller.getKeycardData(), PredefinedKeycardData.WrongSeedPhrase, add = true))
method resolveKeycardNextState*(self: UserProfileEnterSeedPhraseState, keycardFlowType: string, keycardEvent: KeycardEvent,
controller: Controller): State =

View File

@ -0,0 +1,22 @@
type
UserProfileWrongSeedPhraseState* = ref object of State
proc newUserProfileWrongSeedPhraseState*(flowType: FlowType, backState: State): UserProfileWrongSeedPhraseState =
result = UserProfileWrongSeedPhraseState()
result.setup(flowType, StateType.UserProfileWrongSeedPhrase, backState)
proc delete*(self: UserProfileWrongSeedPhraseState) =
self.State.delete
method executeBackCommand*(self: UserProfileWrongSeedPhraseState, controller: Controller) =
if self.flowType == FlowType.LostKeycardReplacement or
self.flowType == FlowType.LostKeycardConvertToRegularAccount:
controller.setKeycardData(updatePredefinedKeycardData(controller.getKeycardData(), PredefinedKeycardData.WrongSeedPhrase, add = false))
method executePrimaryCommand*(self: UserProfileWrongSeedPhraseState, controller: Controller) =
self.executeBackCommand(controller)
method getNextPrimaryState*(self: UserProfileWrongSeedPhraseState, controller: Controller): State =
if self.flowType == FlowType.LostKeycardReplacement or
self.flowType == FlowType.LostKeycardConvertToRegularAccount:
return self.getBackState()

View File

@ -5,6 +5,13 @@ from ../../../app_service/service/keycard/service import KeycardEvent, KeyDetail
const UNIQUE_STARTUP_MODULE_IDENTIFIER* = "SartupModule"
type
StartupErrorType* {.pure.} = enum
UnknownType = 0
ImportAccError
SetupAccError
ConvertToRegularAccError
type
AccessInterface* {.pure inheritable.} = ref object of RootObj
@ -92,15 +99,12 @@ method getPin*(self: AccessInterface): string {.base.} =
method getPasswordStrengthScore*(self: AccessInterface, password: string, userName: string): int {.base.} =
raise newException(ValueError, "No implementation available")
method setupAccountError*(self: AccessInterface, error: string) {.base.} =
method emitStartupError*(self: AccessInterface, error: string, errType: StartupErrorType) {.base.} =
raise newException(ValueError, "No implementation available")
method validMnemonic*(self: AccessInterface, mnemonic: string): bool {.base.} =
raise newException(ValueError, "No implementation available")
method importAccountError*(self: AccessInterface, error: string) {.base.} =
raise newException(ValueError, "No implementation available")
method importAccountSuccess*(self: AccessInterface) {.base.} =
raise newException(ValueError, "No implementation available")

View File

@ -262,15 +262,12 @@ method getPin*[T](self: Module[T]): string =
method getPasswordStrengthScore*[T](self: Module[T], password, userName: string): int =
return self.controller.getPasswordStrengthScore(password, userName)
method setupAccountError*[T](self: Module[T], error: string) =
self.view.setupAccountError(error)
method emitStartupError*[T](self: Module[T], error: string, errType: StartupErrorType) =
self.view.emitStartupError(error, errType)
method validMnemonic*[T](self: Module[T], mnemonic: string): bool =
return self.controller.validMnemonic(mnemonic)
method importAccountError*[T](self: Module[T], error: string) =
self.view.importAccountError(error)
method importAccountSuccess*[T](self: Module[T]) =
self.view.importAccountSuccess()
@ -358,8 +355,15 @@ method startAppAfterDelay*[T](self: Module[T]) =
self.view.setCurrentStartupState(newProfileFetchingState(currStateObj.flowType(), nil))
self.moveToStartupState()
proc logoutAndDisplayError[T](self: Module[T], error: string) =
proc logoutAndDisplayError[T](self: Module[T], error: string, errType: StartupErrorType) =
self.delegate.logout()
if self.controller.isSelectedLoginAccountKeycardAccount() and
errType == StartupErrorType.ConvertToRegularAccError:
self.view.setCurrentStartupState(newLoginState(FlowType.AppLogin, nil))
self.controller.runLoginFlow()
self.moveToStartupState()
self.emitStartupError(error, errType)
return
self.moveToStartupState()
self.emitAccountLoginError(error)
@ -377,12 +381,20 @@ method onNodeLogin*[T](self: Module[T], error: string) =
self.delayStartingApp()
let err = self.delegate.userLoggedIn()
if err.len > 0:
self.logoutAndDisplayError(err)
self.logoutAndDisplayError(err, StartupErrorType.UnknownType)
return
elif currStateObj.flowType() == FlowType.LostKeycardConvertToRegularAccount:
let err = self.controller.convertToRegularAccount()
if err.len > 0:
self.logoutAndDisplayError(err, StartupErrorType.ConvertToRegularAccError)
return
self.delegate.logout()
self.view.setCurrentStartupState(newLoginKeycardConvertedToRegularAccountState(currStateObj.flowType(), nil))
self.moveToStartupState()
else:
let err = self.delegate.userLoggedIn()
if err.len > 0:
self.logoutAndDisplayError(err)
self.logoutAndDisplayError(err, StartupErrorType.UnknownType)
return
self.delegate.finishAppLoading()
if currStateObj.flowType() != FlowType.AppLogin:
@ -393,7 +405,7 @@ method onNodeLogin*[T](self: Module[T], error: string) =
if currStateObj.flowType() == FlowType.AppLogin:
self.emitAccountLoginError(error)
else:
self.setupAccountError(error)
self.emitStartupError(error, StartupErrorType.SetupAccError)
error "login error", methodName="onNodeLogin", errDesription =error
method onKeycardResponse*[T](self: Module[T], keycardFlowType: string, keycardEvent: KeycardEvent) =

View File

@ -192,18 +192,13 @@ QtObject:
proc getPasswordStrengthScore*(self: View, password: string, userName: string): int {.slot.} =
return self.delegate.getPasswordStrengthScore(password, userName)
proc accountSetupError*(self: View, error: string) {.signal.}
proc setupAccountError*(self: View, error: string) =
self.accountSetupError(error)
proc startupError*(self: View, error: string, errType: int) {.signal.}
proc emitStartupError*(self: View, error: string, errType: StartupErrorType) =
self.startupError(error, errType.int)
proc validMnemonic*(self: View, mnemonic: string): bool {.slot.} =
return self.delegate.validMnemonic(mnemonic)
proc accountImportError*(self: View, error: string) {.signal.}
proc importAccountError*(self: View, error: string) =
# In QML we can connect to this signal and notify user, before refactoring we didn't have this signal
self.accountImportError(error)
proc accountImportSuccess*(self: View) {.signal.}
proc importAccountSuccess*(self: View) =
self.importedAccountChanged()

View File

@ -8,12 +8,11 @@ type
settingsJson: JsonNode
hashedCurrentPassword: string
newPassword: string
keyStoreDir: string
const convertToKeycardAccountTask*: Task = proc(argEncoded: string) {.gcsafe, nimcall.} =
let arg = decode[ConvertToKeycardAccountTaskArg](argEncoded)
try:
let response = status_account.convertToKeycardAccount(arg.keyStoreDir, arg.accountDataJson, arg.settingsJson,
let response = status_account.convertToKeycardAccount(arg.accountDataJson, arg.settingsJson,
arg.hashedCurrentPassword, arg.newPassword)
arg.finish(response)
except Exception as e:

View File

@ -462,7 +462,7 @@ QtObject:
"public-key": whisperPublicKey,
"name": alias,
"display-name": displayName,
"address": whisperAddress,
"address": address,
"eip1581-address": eip1581Address,
"dapps-address": walletAddress,
"wallet-root-address": walletRootAddress,
@ -631,21 +631,15 @@ QtObject:
error "error: ", procName="login", errName = e.name, errDesription = e.msg
return e.msg
proc loginAccountKeycard*(self: Service, keycardData: KeycardEvent): string =
proc loginAccountKeycard*(self: Service, accToBeLoggedIn: AccountDto, keycardData: KeycardEvent): string =
try:
self.setKeyStoreDir(keycardData.keyUid)
let openedAccounts = self.openedAccounts()
var accToBeLoggedIn: AccountDto
for acc in openedAccounts:
if acc.keyUid == keycardData.keyUid:
accToBeLoggedIn = acc
break
var accountDataJson = %* {
"name": accToBeLoggedIn.name,
"address": keycardData.masterKey.address,
"key-uid": keycardData.keyUid
"key-uid": keycardData.keyUid,
"kdfIterations": KDF_ITERATIONS,
}
var settingsJson: JsonNode
self.addKeycardDetails(settingsJson, accountDataJson)
@ -681,14 +675,12 @@ QtObject:
error "error: ", procName="verifyAccountPassword", errName = e.name, errDesription = e.msg
proc convertToKeycardAccount*(self: Service, keyUid: string, currentPassword: string, newPassword: string) =
proc convertToKeycardAccount*(self: Service, currentPassword: string, newPassword: string) =
var accountDataJson = %* {
"name": self.getLoggedInAccount().name,
"key-uid": keyUid
}
var settingsJson = %* {
"display-name": self.getLoggedInAccount().name
"key-uid": self.getLoggedInAccount().keyUid,
"kdfIterations": KDF_ITERATIONS
}
var settingsJson = %* { }
self.addKeycardDetails(settingsJson, accountDataJson)
@ -704,7 +696,6 @@ QtObject:
slot: "onConvertToKeycardAccount",
accountDataJson: accountDataJson,
settingsJson: settingsJson,
keyStoreDir: self.keyStoreDir,
hashedCurrentPassword: hashedCurrentPassword,
newPassword: newPassword
)
@ -726,6 +717,20 @@ QtObject:
error "error handilng migrated keypair response", errDesription=e.msg
self.events.emit(SIGNAL_CONVERTING_PROFILE_KEYPAIR, ResultArgs(success: result))
proc convertToRegularAccount*(self: Service, mnemonic: string, currentPassword: string, newPassword: string): string =
let hashedPassword = hashString(newPassword)
try:
let response = status_account.convertToRegularAccount(mnemonic, currentPassword, hashedPassword)
var errMsg = ""
if(response.result.contains("error")):
errMsg = response.result["error"].getStr
if errMsg.len > 0:
error "error: ", procName="convertToRegularAccount", errDesription = errMsg
return errMsg
except Exception as e:
error "error converting to regular account: ", message = e.msg
return e.msg
proc verifyPassword*(self: Service, password: string): bool =
try:
let hashedPassword = hashString(password)

View File

@ -243,16 +243,25 @@ proc saveAccountAndLoginWithKeycard*(chatKey, password: string, account, subacco
error "error doing rpc request", methodName = "saveAccountAndLogin", exception=e.msg
raise newException(RpcException, e.msg)
proc convertToKeycardAccount*(keyStoreDir: string, account: JsonNode, settings: JsonNode, password: string, newPassword: string):
proc convertToKeycardAccount*(account: JsonNode, settings: JsonNode, password: string, newPassword: string):
RpcResponse[JsonNode] {.raises: [Exception].} =
try:
let response = status_go.convertToKeycardAccount(keyStoreDir, $account, $settings, password, newPassword)
let response = status_go.convertToKeycardAccount($account, $settings, password, newPassword)
result.result = Json.decode(response, JsonNode)
except RpcException as e:
error "error doing rpc request", methodName = "convertToKeycardAccount", exception=e.msg
raise newException(RpcException, e.msg)
proc convertToRegularAccount*(mnemonic: string, currPassword: string, newPassword: string):
RpcResponse[JsonNode] {.raises: [Exception].} =
try:
let response = status_go.convertToRegularAccount(mnemonic, currPassword, newPassword)
result.result = Json.decode(response, JsonNode)
except RpcException as e:
error "error doing rpc request", methodName = "convertToRegularAccount", exception=e.msg
raise newException(RpcException, e.msg)
proc login*(name, keyUid: string, kdfIterations: int, hashedPassword, thumbnail, large: string, nodeCfgObj: string):
RpcResponse[JsonNode]
{.raises: [Exception].} =

View File

@ -2,6 +2,10 @@ import QtQuick 2.14
import QtQuick.Controls 2.14
import QtQuick.Dialogs 1.3
import StatusQ.Core 0.1
import StatusQ.Popups.Dialog 0.1
import StatusQ.Core.Theme 0.1
import utils 1.0
import shared.popups.keycard 1.0
@ -77,6 +81,7 @@ OnboardingBasePage {
case Constants.startupState.loginKeycardMaxPairingSlotsReached:
case Constants.startupState.loginKeycardEmpty:
case Constants.startupState.loginNotKeycard:
case Constants.startupState.loginKeycardConvertedToRegularAccount:
return loginViewComponent
case Constants.startupState.keycardPluginReader:
@ -108,6 +113,7 @@ OnboardingBasePage {
case Constants.startupState.keycardMaxPairingSlotsReached:
case Constants.startupState.keycardMaxPinRetriesReached:
case Constants.startupState.keycardMaxPukRetriesReached:
case Constants.startupState.userProfileWrongSeedPhrase:
return keycardStateViewComponent
case Constants.startupState.keycardEnterPuk:
@ -132,29 +138,40 @@ OnboardingBasePage {
Connections {
target: root.startupStore.startupModuleInst
onAccountSetupError: {
if (error === Constants.existingAccountError) {
msgDialog.title = qsTr("Keys for this account already exist")
msgDialog.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 {
msgDialog.title = qsTr("Login failed")
msgDialog.text = qsTr("Login failed. Please re-enter your password and try again.")
onStartupError: {
msgDialog.errType = errType
if (errType === Constants.startupErrorType.setupAccError) {
if (error === Constants.existingAccountError) {
msgDialog.title = qsTr("Keys for this account already exist")
msgDialog.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 {
msgDialog.title = qsTr("Login failed")
msgDialog.text = qsTr("Login failed. Please re-enter your password and try again.")
}
}
else if (errType === Constants.startupErrorType.importAccError) {
if (error === Constants.existingAccountError) {
msgDialog.title = qsTr("Keys for this account already exist")
msgDialog.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. In\
case of Keycard try recovering using PUK or reinstall the app and try login with the Keycard option.")
} else {
msgDialog.title = qsTr("Error importing seed")
msgDialog.text = error
}
}
else if (errType === Constants.startupErrorType.convertToRegularAccError) {
msgDialog.title = qsTr("Converting account")
msgDialog.text = qsTr("Really sorry about this inconvenience.\n\
Most likely that your account is damaged while converting to a regular Status account.\n\
First try to login after app restart, if that doesn't work, you can alway recover your accout\n\
following the \"Add existing Status user\" flow, using your seed phrase.")
}
msgDialog.open()
}
onAccountImportError: {
if (error === Constants.existingAccountError) {
msgDialog.title = qsTr("Keys for this account already exist")
msgDialog.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. In \
case of Keycard try recovering using PUK or reinstall the app and try login with the Keycard option.")
} else {
msgDialog.title = qsTr("Error importing seed")
msgDialog.text = error
}
msgDialog.open()
}
onDisplayKeycardSharedModuleFlow: {
keycardPopup.active = true
}
@ -163,13 +180,26 @@ case of Keycard try recovering using PUK or reinstall the app and try login with
}
}
MessageDialog {
StatusDialog {
id: msgDialog
title: qsTr("Login failed")
text: qsTr("Login failed. Please re-enter your password and try again.")
icon: StandardIcon.Critical
standardButtons: StandardButton.Ok
property int errType: Constants.startupErrorType.unknownType
property string text: qsTr("Login failed. Please re-enter your password and try again.")
StatusBaseText {
anchors.fill: parent
font.pixelSize: 15
color: Theme.palette.directColor1
text: msgDialog.text
wrapMode: Text.WordWrap
}
standardButtons: Dialog.Ok
onAccepted: {
if (msgDialog.errType == Constants.startupErrorType.convertToRegularAccError) {
Qt.quit();
}
console.log("TODO: restart flow...")
}
}

View File

@ -365,6 +365,44 @@ Item {
color: Theme.palette.baseColor1
font.pixelSize: Constants.keycard.general.fontSize3
}
},
State {
name: Constants.startupState.userProfileWrongSeedPhrase
when: root.startupStore.currentStartupState.stateType === Constants.startupState.userProfileWrongSeedPhrase
PropertyChanges {
target: image
pattern: Constants.keycardAnimations.strongError.pattern
source: ""
startImgIndexForTheFirstLoop: Constants.keycardAnimations.strongError.startImgIndexForTheFirstLoop
startImgIndexForOtherLoops: Constants.keycardAnimations.strongError.startImgIndexForOtherLoops
endImgIndex: Constants.keycardAnimations.strongError.endImgIndex
duration: Constants.keycardAnimations.strongError.duration
loops: Constants.keycardAnimations.strongError.loops
}
PropertyChanges {
target: title
text: qsTr("Seed phrase doesnt match any user")
color: Theme.palette.directColor1
font.pixelSize: Constants.keycard.general.fontSize1
}
PropertyChanges {
target: info
text: qsTr("The seed phrase you enter needs to match the seed phrase of an existing user on this device")
color: Theme.palette.directColor1
font.pixelSize: Constants.keycard.general.fontSize2
}
PropertyChanges {
target: button
text: qsTr("Try entering seed phrase again")
}
PropertyChanges {
target: link
text: ""
}
PropertyChanges {
target: message
text: ""
}
}
]
}

View File

@ -1265,6 +1265,56 @@ Item {
text: ""
visible: false
}
},
State {
name: Constants.startupState.loginKeycardConvertedToRegularAccount
when: root.startupStore.currentStartupState.stateType === Constants.startupState.loginKeycardConvertedToRegularAccount
PropertyChanges {
target: image
pattern: Constants.keycardAnimations.strongSuccess.pattern
source: ""
startImgIndexForTheFirstLoop: Constants.keycardAnimations.strongSuccess.startImgIndexForTheFirstLoop
startImgIndexForOtherLoops: Constants.keycardAnimations.strongSuccess.startImgIndexForOtherLoops
endImgIndex: Constants.keycardAnimations.strongSuccess.endImgIndex
duration: Constants.keycardAnimations.strongSuccess.duration
loops: Constants.keycardAnimations.strongSuccess.loops
}
PropertyChanges {
target: userInfo
visible: false
}
PropertyChanges {
target: title
text: qsTr("Your account has been successfully converted to a non Keycard account")
visible: true
}
PropertyChanges {
target: passwordSection
visible: false
}
PropertyChanges {
target: pinSection
visible: false
}
PropertyChanges {
target: info
visible: false
}
PropertyChanges {
target: message
text: qsTr("To complete the process close Status and log in with your password")
visible: true
}
PropertyChanges {
target: button
text: qsTr("Restart app & sign in using your password")
visible: true
}
PropertyChanges {
target: link
text: ""
visible: false
}
}
]
}

View File

@ -316,7 +316,8 @@ Item {
return qsTr("Recover Keycard")
}
else if (root.startupStore.currentStartupState.flowType === Constants.startupFlow.firstRunNewUserImportSeedPhraseIntoKeycard ||
root.startupStore.currentStartupState.flowType === Constants.startupFlow.lostKeycardReplacement) {
root.startupStore.currentStartupState.flowType === Constants.startupFlow.lostKeycardReplacement ||
root.startupStore.currentStartupState.flowType === Constants.startupFlow.lostKeycardConvertToRegularAccount) {
return qsTr("Next")
}
return ""

View File

@ -6,6 +6,7 @@ import QtQml.Models 2.14
import StatusQ.Core 0.1
import StatusQ.Controls 0.1
import StatusQ.Popups.Dialog 0.1
import StatusQ.Core.Theme 0.1
import utils 1.0
import shared.popups.keycard 1.0

View File

@ -24,6 +24,7 @@ QtObject {
readonly property string firstRunOldUserImportSeedPhrase: "FirstRunOldUserImportSeedPhrase"
readonly property string appLogin: "AppLogin"
readonly property string lostKeycardReplacement: "LostKeycardReplacement"
readonly property string lostKeycardConvertToRegularAccount: "LostKeycardConvertToRegularAccount"
}
readonly property QtObject startupState: QtObject {
@ -40,6 +41,7 @@ QtObject {
readonly property string userProfileConfirmPassword: "UserProfileConfirmPassword"
readonly property string userProfileImportSeedPhrase: "UserProfileImportSeedPhrase"
readonly property string userProfileEnterSeedPhrase: "UserProfileEnterSeedPhrase"
readonly property string userProfileWrongSeedPhrase: "UserProfileWrongSeedPhrase"
readonly property string biometrics: "Biometrics"
readonly property string keycardPluginReader: "KeycardPluginReader"
readonly property string keycardInsertKeycard: "KeycardInsertKeycard"
@ -80,6 +82,7 @@ QtObject {
readonly property string loginKeycardMaxPairingSlotsReached: "LoginKeycardMaxPairingSlotsReached"
readonly property string loginKeycardEmpty: "LoginKeycardEmpty"
readonly property string loginNotKeycard: "LoginNotKeycard"
readonly property string loginKeycardConvertedToRegularAccount: "LoginKeycardConvertedToRegularAccount"
readonly property string profileFetching: "ProfileFetching"
readonly property string profileFetchingSuccess: "ProfileFetchingSuccess"
readonly property string profileFetchingTimeout: "ProfileFetchingTimeout"
@ -731,6 +734,13 @@ QtObject {
readonly property bool isCppApp: typeof cppApp !== "undefined" ? cppApp : false
readonly property QtObject startupErrorType: QtObject {
readonly property int unknownType: 0
readonly property int importAccError: 1
readonly property int setupAccError: 2
readonly property int convertToRegularAccError: 3
}
readonly property string existingAccountError: "account already exists"
readonly property string wrongDerivationPathError: "error parsing derivation path"

@ -1 +1 @@
Subproject commit 1d8c32e8557a55c86c35e5d6eaccb321995851a3
Subproject commit 8fed87fe272c4b49008cd7cc5950a96cd0751966