diff --git a/src/app/modules/onboarding/controller.nim b/src/app/modules/onboarding/controller.nim index 2c092a96cb..3f6ab3472c 100644 --- a/src/app/modules/onboarding/controller.nim +++ b/src/app/modules/onboarding/controller.nim @@ -72,9 +72,9 @@ proc init*(self: Controller) = self.delegate.onKeycardSetPinFailure(args.error) self.connectionIds.add(handlerId) - handlerId = self.events.onWithUUID(SIGNAL_KEYCARD_AUTHORIZE_FAILURE) do(e: Args): - let args = KeycardErrorArg(e) - self.delegate.onKeycardAuthorizeFailure(args.error) + handlerId = self.events.onWithUUID(SIGNAL_KEYCARD_AUTHORIZE_FINISHED) do(e: Args): + let args = KeycardAuthorizeEvent(e) + self.delegate.onKeycardAuthorizeFinished(args.error, args.authorized) self.connectionIds.add(handlerId) handlerId = self.events.onWithUUID(SIGNAL_KEYCARD_LOAD_MNEMONIC_FAILURE) do(e: Args): diff --git a/src/app/modules/onboarding/io_interface.nim b/src/app/modules/onboarding/io_interface.nim index 855d4c3f34..bfc6f12284 100644 --- a/src/app/modules/onboarding/io_interface.nim +++ b/src/app/modules/onboarding/io_interface.nim @@ -58,7 +58,7 @@ method onKeycardStateUpdated*(self: AccessInterface, keycardEvent: KeycardEventD method onKeycardSetPinFailure*(self: AccessInterface, error: string) {.base.} = raise newException(ValueError, "No implementation available") -method onKeycardAuthorizeFailure*(self: AccessInterface, error: string) {.base.} = +method onKeycardAuthorizeFinished*(self: AccessInterface, error: string, authorized: bool) {.base.} = raise newException(ValueError, "No implementation available") method onKeycardLoadMnemonicFailure*(self: AccessInterface, error: string) {.base.} = diff --git a/src/app/modules/onboarding/module.nim b/src/app/modules/onboarding/module.nim index 081d0f0a66..d5163024fd 100644 --- a/src/app/modules/onboarding/module.nim +++ b/src/app/modules/onboarding/module.nim @@ -99,7 +99,7 @@ method initialize*[T](self: Module[T], pin: string) = self.controller.initialize(pin) method authorize*[T](self: Module[T], pin: string) = - self.view.setAuthorizationState(ProgressState.InProgress.int) + self.view.setAuthorizationState(AuthorizationState.InProgress) self.controller.authorize(pin) method getPasswordStrengthScore*[T](self: Module[T], password, userName: string): int = @@ -287,20 +287,23 @@ method onKeycardStateUpdated*[T](self: Module[T], keycardEvent: KeycardEventDto) if keycardEvent.state == KeycardState.Authorized and self.view.getAuthorizationState() == ProgressState.InProgress.int: # We just finished authorizing - self.view.setAuthorizationState(ProgressState.Success.int) + self.view.setAuthorizationState(AuthorizationState.Authorized) method onKeycardSetPinFailure*[T](self: Module[T], error: string) = self.view.setPinSettingState(ProgressState.Failed.int) -method onKeycardAuthorizeFailure*[T](self: Module[T], error: string) = - self.view.setAuthorizationState(ProgressState.Failed.int) +method onKeycardAuthorizeFinished*[T](self: Module[T], error: string, authorized: bool) = + if error != "": + self.view.setAuthorizationState(AuthorizationState.Error) + elif not authorized: + self.view.setAuthorizationState(AuthorizationState.WrongPIN) + else: + self.view.setAuthorizationState(AuthorizationState.Authorized) + return if self.loginFlow == LoginMethod.Keycard: # We were trying to login and the authorization failed - var wrongPassword = false - if error.contains("wrong pin"): - wrongPassword = true - self.view.accountLoginError(error, wrongPassword) + self.view.accountLoginError(error, not authorized) method onKeycardLoadMnemonicFailure*[T](self: Module[T], error: string) = self.view.setAddKeyPairState(ProgressState.Failed.int) diff --git a/src/app/modules/onboarding/states.nim b/src/app/modules/onboarding/states.nim index 97c633b3f7..f7459acde7 100644 --- a/src/app/modules/onboarding/states.nim +++ b/src/app/modules/onboarding/states.nim @@ -23,3 +23,10 @@ type ProgressState* {.pure.} = enum InProgress, Success, Failed, + +type AuthorizationState* {.pure.} = enum + Idle + InProgress + Authorized + WrongPIN + Error diff --git a/src/app/modules/onboarding/view.nim b/src/app/modules/onboarding/view.nim index e7c59799d1..ecf0ee6c2c 100644 --- a/src/app/modules/onboarding/view.nim +++ b/src/app/modules/onboarding/view.nim @@ -1,5 +1,5 @@ import NimQml -import io_interface +import io_interface, states from app_service/service/keycardV2/dto import KeycardEventDto # TODO move these files to this module when we remove the old onboarding @@ -14,7 +14,7 @@ QtObject: syncState: int addKeyPairState: int pinSettingState: int - authorizationState: int + authorizationState: AuthorizationState restoreKeysExportState: int loginAccountsModel: login_acc_model.Model loginAccountsModelVariant: QVariant @@ -60,11 +60,11 @@ QtObject: proc authorizationStateChanged*(self: View) {.signal.} proc getAuthorizationState*(self: View): int {.slot.} = - return self.authorizationState + return self.authorizationState.int QtProperty[int] authorizationState: read = getAuthorizationState notify = authorizationStateChanged - proc setAuthorizationState*(self: View, authorizationState: int) = + proc setAuthorizationState*(self: View, authorizationState: AuthorizationState) = self.authorizationState = authorizationState self.authorizationStateChanged() diff --git a/src/app_service/service/keycardV2/service.nim b/src/app_service/service/keycardV2/service.nim index c47c6dffed..854b68758c 100644 --- a/src/app_service/service/keycardV2/service.nim +++ b/src/app_service/service/keycardV2/service.nim @@ -19,7 +19,7 @@ const PUKLengthForStatusApp* = 12 const SIGNAL_KEYCARD_STATE_UPDATED* = "keycardStateUpdated" const SIGNAL_KEYCARD_SET_PIN_FAILURE* = "keycardSetPinFailure" -const SIGNAL_KEYCARD_AUTHORIZE_FAILURE* = "keycardAuthorizeFailure" +const SIGNAL_KEYCARD_AUTHORIZE_FINISHED* = "keycardAuthorizeFinished" const SIGNAL_KEYCARD_LOAD_MNEMONIC_FAILURE* = "keycardLoadMnemonicFailure" const SIGNAL_KEYCARD_LOAD_MNEMONIC_SUCCESS* = "keycardLoadMnemonicSuccess" const SIGNAL_KEYCARD_EXPORT_RESTORE_KEYS_FAILURE* = "keycardExportRestoreKeysFailure" @@ -34,6 +34,10 @@ type KeycardErrorArg* = ref object of Args error*: string + KeycardAuthorizeEvent* = ref object of Args + error*: string + authorized*: bool + KeycardKeyUIDArg* = ref object of Args keyUID*: string @@ -140,9 +144,16 @@ QtObject: let rpcResponseObj = responseObj["response"].getStr().parseJson() if rpcResponseObj{"error"}.kind != JNull and rpcResponseObj{"error"}.getStr != "": raise newException(RpcException, rpcResponseObj["error"].getStr) + let resultObj = rpcResponseObj{"result"} + let event = KeycardAuthorizeEvent( + error: "", + authorized: resultObj{"authorized"}.getBool(), + ) + self.events.emit(SIGNAL_KEYCARD_AUTHORIZE_FINISHED, event) except Exception as e: error "error during authorize: ", msg = e.msg - self.events.emit(SIGNAL_KEYCARD_AUTHORIZE_FAILURE, KeycardErrorArg(error: e.msg)) + let event = KeycardAuthorizeEvent(error: e.msg, authorized: false) + self.events.emit(SIGNAL_KEYCARD_AUTHORIZE_FINISHED, event) proc receiveKeycardSignalV2(self: Service, signal: string) {.slot.} = try: diff --git a/storybook/pages/OnboardingLayoutPage.qml b/storybook/pages/OnboardingLayoutPage.qml index 8e0550237d..47f35eef65 100644 --- a/storybook/pages/OnboardingLayoutPage.qml +++ b/storybook/pages/OnboardingLayoutPage.qml @@ -40,7 +40,7 @@ SplitView { store.keycardState = Onboarding.KeycardState.NoPCSCService store.addKeyPairState = Onboarding.ProgressState.Idle store.pinSettingState = Onboarding.ProgressState.Idle - store.authorizationState = Onboarding.ProgressState.Idle + store.authorizationState = Onboarding.AuthorizationState.Idle store.restoreKeysExportState = Onboarding.ProgressState.Idle store.syncState = Onboarding.ProgressState.Idle store.keycardRemainingPinAttempts = Constants.onboarding.defaultPinAttempts @@ -76,7 +76,7 @@ SplitView { property int keycardState: Onboarding.KeycardState.NoPCSCService property int addKeyPairState: Onboarding.ProgressState.Idle property int pinSettingState: Onboarding.ProgressState.Idle - property int authorizationState: Onboarding.ProgressState.Idle + property int authorizationState: Onboarding.AuthorizationState.Idle property int restoreKeysExportState: Onboarding.ProgressState.Idle property int syncState: Onboarding.ProgressState.Idle property var loginAccountsModel: ctrlLoginScreen.checked ? loginAccountsModel : emptyModel @@ -111,9 +111,9 @@ SplitView { function authorize(pin: string) { logs.logEvent("OnboardingStore.authorize", ["pin"], arguments) if (pin === mockDriver.pin) - authorizationState = Onboarding.ProgressState.Success + authorizationState = Onboarding.AuthorizationState.Authorized else - authorizationState = Onboarding.ProgressState.Failed + authorizationState = Onboarding.AuthorizationState.WrongPin } function loadMnemonic(mnemonic: string) { // -> void @@ -578,7 +578,7 @@ SplitView { } Repeater { - model: Onboarding.getModelFromEnum("ProgressState") + model: Onboarding.getModelFromEnum("AuthorizationState") RoundButton { text: modelData.name diff --git a/storybook/qmlTests/tests/tst_OnboardingLayout.qml b/storybook/qmlTests/tests/tst_OnboardingLayout.qml index 3066583c06..857c19f589 100644 --- a/storybook/qmlTests/tests/tst_OnboardingLayout.qml +++ b/storybook/qmlTests/tests/tst_OnboardingLayout.qml @@ -25,7 +25,7 @@ Item { id: mockDriver property int keycardState // enum Onboarding.KeycardState property int pinSettingState // enum Onboarding.ProgressState - property int authorizationState // enum Onboarding.ProgressState + property int authorizationState // enum Onboarding.AuthorizationState property int restoreKeysExportState // enum Onboarding.ProgressState property bool biometricsAvailable property string existingPin @@ -57,7 +57,7 @@ Item { onboardingStore: OnboardingStore { readonly property int keycardState: mockDriver.keycardState // enum Onboarding.KeycardState readonly property int pinSettingState: mockDriver.pinSettingState // enum Onboarding.ProgressState - readonly property int authorizationState: mockDriver.authorizationState // enum Onboarding.ProgressState + readonly property int authorizationState: mockDriver.authorizationState // enum Onboarding.AuthorizationState readonly property int restoreKeysExportState: mockDriver.restoreKeysExportState // enum Onboarding.ProgressState property int keycardRemainingPinAttempts: Constants.onboarding.defaultPinAttempts property int keycardRemainingPukAttempts: Constants.onboarding.defaultPukAttempts @@ -172,7 +172,7 @@ Item { function cleanup() { mockDriver.keycardState = -1 mockDriver.pinSettingState = Onboarding.ProgressState.Idle - mockDriver.authorizationState = Onboarding.ProgressState.Idle + mockDriver.authorizationState = Onboarding.AuthorizationState.Idle mockDriver.restoreKeysExportState = Onboarding.ProgressState.Idle mockDriver.biometricsAvailable = false mockDriver.existingPin = "" @@ -479,7 +479,7 @@ Item { tryCompare(dynamicSpy, "count", 1) compare(dynamicSpy.signalArguments[0][0], newPin) mockDriver.pinSettingState = Onboarding.ProgressState.Success - mockDriver.authorizationState = Onboarding.ProgressState.Success + mockDriver.authorizationState = Onboarding.AuthorizationState.Authorized // PAGE 7: Backup your recovery phrase (intro) dynamicSpy.setup(stack, "currentItemChanged") @@ -637,7 +637,7 @@ Item { keyClickSequence(newPin + newPin) // set and repeat compare(dynamicSpy.signalArguments[0][0], newPin) mockDriver.pinSettingState = Onboarding.ProgressState.Success - mockDriver.authorizationState = Onboarding.ProgressState.Success + mockDriver.authorizationState = Onboarding.AuthorizationState.Authorized // PAGE 8: Adding key pair to Keycard dynamicSpy.setup(stack, "currentItemChanged") @@ -900,7 +900,7 @@ Item { compare(dynamicSpy.signalArguments[0][0], mockDriver.existingPin) dynamicSpy.setup(controlUnderTest.onboardingStore, "exportRecoverKeysCalled") - mockDriver.authorizationState = Onboarding.ProgressState.Success + mockDriver.authorizationState = Onboarding.AuthorizationState.Authorized tryCompare(dynamicSpy, "count", 1) // PAGE 6: Extracting keys from Keycard @@ -1237,7 +1237,7 @@ Item { tryCompare(dynamicSpy, "count", 1) compare(dynamicSpy.signalArguments[0][0], newPin) mockDriver.pinSettingState = Onboarding.ProgressState.Success - mockDriver.authorizationState = Onboarding.ProgressState.Success + mockDriver.authorizationState = Onboarding.AuthorizationState.Authorized // PAGE 6: Adding key pair to Keycard dynamicSpy.setup(stack, "currentItemChanged") diff --git a/ui/StatusQ/src/onboarding/enums.h b/ui/StatusQ/src/onboarding/enums.h index 925c468b6a..d8012ae71d 100644 --- a/ui/StatusQ/src/onboarding/enums.h +++ b/ui/StatusQ/src/onboarding/enums.h @@ -82,10 +82,19 @@ public: Failed }; + enum class AuthorizationState { + Idle, + InProgress, + Authorized, + WrongPin, + Error, + }; + private: Q_ENUM(PrimaryFlow) Q_ENUM(OnboardingFlow) Q_ENUM(LoginMethod) Q_ENUM(KeycardState) Q_ENUM(ProgressState) + Q_ENUM(AuthorizationState) }; diff --git a/ui/app/AppLayouts/Onboarding2/LoginWithKeycardFlow.qml b/ui/app/AppLayouts/Onboarding2/LoginWithKeycardFlow.qml index 3030e6fc82..9a0bc63870 100644 --- a/ui/app/AppLayouts/Onboarding2/LoginWithKeycardFlow.qml +++ b/ui/app/AppLayouts/Onboarding2/LoginWithKeycardFlow.qml @@ -81,12 +81,11 @@ SQUtils.QObject { state: { switch (root.authorizationState) { - case Onboarding.ProgressState.Success: + case Onboarding.AuthorizationState.Authorized: return KeycardEnterPinPage.State.Success - case Onboarding.ProgressState.InProgress: + case Onboarding.AuthorizationState.InProgress: return KeycardEnterPinPage.State.InProgress - // workaround by mapping all failures as wrong pin (#17289) - case Onboarding.ProgressState.Failed: + case Onboarding.AuthorizationState.WrongPin: return KeycardEnterPinPage.State.WrongPin } @@ -105,7 +104,7 @@ SQUtils.QObject { enabled: page.visible function onAuthorizationStateChanged() { - if (root.authorizationState !== Onboarding.ProgressState.Success) + if (root.authorizationState !== Onboarding.AuthorizationState.Authorized) return const doNext = () => { diff --git a/ui/app/AppLayouts/Onboarding2/OnboardingFlow.qml b/ui/app/AppLayouts/Onboarding2/OnboardingFlow.qml index 02e988ed7b..654b0298a4 100644 --- a/ui/app/AppLayouts/Onboarding2/OnboardingFlow.qml +++ b/ui/app/AppLayouts/Onboarding2/OnboardingFlow.qml @@ -99,10 +99,17 @@ SQUtils.QObject { termsOfUsePopup.createObject(root.stackView).open() } - function handleKeycardFailedState(state) { - if (state !== Onboarding.ProgressState.Failed) - return + function handleKeycardProgressFailedState(state) { + if (state === Onboarding.ProgressState.Failed) + handleKeycardFailedState() + } + function handleKeycardAuthorizationErrorState(state) { + if (state === Onboarding.AuthorizationState.Error) + handleKeycardFailedState() + } + + function handleKeycardFailedState() { // find index of first page in the flow let idx = 0 const entryItem = stackView.find((item, index) => { @@ -111,7 +118,7 @@ SQUtils.QObject { return item instanceof Loader }) - // when the initial page is not found, bacause e.g. the flow is not initialized + // when the initial page is not found, because e.g. the flow is not initialized // or the stack was cleared if (!entryItem) return @@ -127,24 +134,19 @@ SQUtils.QObject { !(root.stackView.currentItem instanceof EnableBiometricsPage) function onPinSettingStateChanged() { - d.handleKeycardFailedState(pinSettingState) + d.handleKeycardProgressFailedState(pinSettingState) } function onAuthorizationStateChanged() { - // workaround for entering pin because currently there is not possible - // to distinguish invalid pin and failed pin entering operation (#17289) - if (root.stackView.currentItem instanceof KeycardEnterPinPage) - return - - d.handleKeycardFailedState(authorizationState) + d.handleKeycardAuthorizationErrorState(authorizationState) } function onRestoreKeysExportStateChanged() { - d.handleKeycardFailedState(restoreKeysExportState) + d.handleKeycardProgressFailedState(restoreKeysExportState) } function onAddKeyPairStateChanged() { - d.handleKeycardFailedState(addKeyPairState) + d.handleKeycardProgressFailedState(addKeyPairState) } } diff --git a/ui/app/AppLayouts/Onboarding2/UnblockWithPukFlow.qml b/ui/app/AppLayouts/Onboarding2/UnblockWithPukFlow.qml index b168e1567c..1c5604e997 100644 --- a/ui/app/AppLayouts/Onboarding2/UnblockWithPukFlow.qml +++ b/ui/app/AppLayouts/Onboarding2/UnblockWithPukFlow.qml @@ -75,7 +75,7 @@ SQUtils.QObject { KeycardCreatePinDelayedPage { pinSettingState: root.pinSettingState - authorizationState: Onboarding.ProgressState.Success // authorization not needed + authorizationState: Onboarding.AuthorizationState.Authorized // authorization not needed onSetPinRequested: root.setPinRequested(pin) onFinished: root.stackView.replace(keycardUnblockedPage, diff --git a/ui/app/AppLayouts/Onboarding2/UnblockWithSeedphraseFlow.qml b/ui/app/AppLayouts/Onboarding2/UnblockWithSeedphraseFlow.qml index 3e2a0cd21d..953900fdda 100644 --- a/ui/app/AppLayouts/Onboarding2/UnblockWithSeedphraseFlow.qml +++ b/ui/app/AppLayouts/Onboarding2/UnblockWithSeedphraseFlow.qml @@ -42,7 +42,7 @@ SQUtils.QObject { KeycardCreatePinDelayedPage { pinSettingState: root.pinSettingState - authorizationState: Onboarding.ProgressState.Success // authorization not needed + authorizationState: Onboarding.AuthorizationState.Authorized // authorization not needed onSetPinRequested: root.setPinRequested(pin) onFinished: root.finished() diff --git a/ui/app/AppLayouts/Onboarding2/stores/OnboardingStore.qml b/ui/app/AppLayouts/Onboarding2/stores/OnboardingStore.qml index 702b2e29c6..81b9b161d5 100644 --- a/ui/app/AppLayouts/Onboarding2/stores/OnboardingStore.qml +++ b/ui/app/AppLayouts/Onboarding2/stores/OnboardingStore.qml @@ -24,8 +24,8 @@ QtObject { // keycard readonly property int keycardState: d.onboardingModuleInst.keycardState // cf. enum Onboarding.KeycardState readonly property int pinSettingState: d.onboardingModuleInst.pinSettingState // cf. enum Onboarding.ProgressState - readonly property int authorizationState: d.onboardingModuleInst.authorizationState // cf. enum Onboarding.ProgressState - readonly property int restoreKeysExportState: d.onboardingModuleInst.restoreKeysExportState // cf. enum Onboarding.ProgressState + readonly property int authorizationState: d.onboardingModuleInst.authorizationState // cf. enum Onboarding.AuthorizationState + readonly property int restoreKeysExportState: d.onboardingModuleInst.restoreKeysExportState // cf. enum Onboarding.AuthorizationState readonly property int keycardRemainingPinAttempts: d.onboardingModuleInst.keycardRemainingPinAttempts readonly property int keycardRemainingPukAttempts: d.onboardingModuleInst.keycardRemainingPukAttempts