fix(@desktop/keycard): updates to the onboarding/login flow

- login states updated so they can resolve enter pin state from each state
that flow may be in
- not a keycard state added
- login plugin state added
This commit is contained in:
Sale Djenic 2022-09-13 11:55:19 +02:00 committed by saledjenic
parent 3042a0cffa
commit 405171dad7
16 changed files with 210 additions and 26 deletions

View File

@ -10,10 +10,7 @@ proc delete*(self: LoginKeycardEmptyState) =
method executePrimaryCommand*(self: LoginKeycardEmptyState, controller: Controller) =
if self.flowType == FlowType.AppLogin:
if not controller.isSelectedLoginAccountKeycardAccount():
controller.login()
else:
controller.runLoadAccountFlow(factoryReset = true)
controller.runLoadAccountFlow(factoryReset = true)
method getNextSecondaryState*(self: LoginKeycardEmptyState, controller: Controller): State =
controller.cancelCurrentFlow()

View File

@ -12,6 +12,12 @@ method executePrimaryCommand*(self: LoginKeycardInsertKeycardState, controller:
if self.flowType == FlowType.AppLogin:
if not controller.isSelectedLoginAccountKeycardAccount():
controller.login()
elif not controller.keychainErrorOccurred() and controller.getPin().len == PINLengthForStatusApp:
controller.enterKeycardPin(controller.getPin())
method getNextPrimaryState*(self: LoginKeycardInsertKeycardState, controller: Controller): State =
if controller.keychainErrorOccurred():
return createState(StateType.LoginKeycardEnterPin, self.flowType, nil)
method getNextSecondaryState*(self: LoginKeycardInsertKeycardState, controller: Controller): State =
controller.cancelCurrentFlow()

View File

@ -12,11 +12,6 @@ method executeBackCommand*(self: LoginKeycardMaxPinRetriesReachedState, controll
if self.flowType == FlowType.AppLogin and controller.isKeycardCreatedAccountSelectedOne():
controller.runLoginFlow()
method executePrimaryCommand*(self: LoginKeycardMaxPinRetriesReachedState, controller: Controller) =
if self.flowType == FlowType.AppLogin:
if not controller.isSelectedLoginAccountKeycardAccount():
controller.login()
method getNextPrimaryState*(self: LoginKeycardMaxPinRetriesReachedState, controller: Controller): State =
return createState(StateType.KeycardRecover, self.flowType, self)
@ -26,4 +21,8 @@ method getNextSecondaryState*(self: LoginKeycardMaxPinRetriesReachedState, contr
method getNextTertiaryState*(self: LoginKeycardMaxPinRetriesReachedState, controller: Controller): State =
controller.cancelCurrentFlow()
return createState(StateType.WelcomeOldStatusUser, self.flowType, self)
return createState(StateType.WelcomeOldStatusUser, self.flowType, self)
method resolveKeycardNextState*(self: LoginKeycardMaxPinRetriesReachedState, keycardFlowType: string, keycardEvent: KeycardEvent,
controller: Controller): State =
return ensureReaderAndCardPresenceAndResolveNextLoginState(self, keycardFlowType, keycardEvent, controller)

View File

@ -11,4 +11,8 @@ proc delete*(self: LoginKeycardMaxPukRetriesReachedState) =
method getNextPrimaryState*(self: LoginKeycardMaxPukRetriesReachedState, controller: Controller): State =
if self.flowType == FlowType.AppLogin:
controller.setRecoverUsingSeedPhraseWhileLogin(true)
return createState(StateType.UserProfileEnterSeedPhrase, self.flowType, nil)
return createState(StateType.UserProfileEnterSeedPhrase, self.flowType, nil)
method resolveKeycardNextState*(self: LoginKeycardMaxPukRetriesReachedState, keycardFlowType: string, keycardEvent: KeycardEvent,
controller: Controller): State =
return ensureReaderAndCardPresenceAndResolveNextLoginState(self, keycardFlowType, keycardEvent, controller)

View File

@ -14,7 +14,7 @@ method executePrimaryCommand*(self: LoginKeycardReadingKeycardState, controller:
if self.flowType == FlowType.AppLogin:
if not controller.isSelectedLoginAccountKeycardAccount():
controller.login()
elif not controller.keychainErrorOccurred():
elif not controller.keychainErrorOccurred() and controller.getPin().len == PINLengthForStatusApp:
controller.enterKeycardPin(controller.getPin())
method getNextPrimaryState*(self: LoginKeycardReadingKeycardState, controller: Controller): State =

View File

@ -12,6 +12,12 @@ method executePrimaryCommand*(self: LoginKeycardWrongKeycardState, controller: C
if self.flowType == FlowType.AppLogin:
if not controller.isSelectedLoginAccountKeycardAccount():
controller.login()
elif not controller.keychainErrorOccurred() and controller.getPin().len == PINLengthForStatusApp:
controller.enterKeycardPin(controller.getPin())
method getNextPrimaryState*(self: LoginKeycardWrongKeycardState, controller: Controller): State =
if controller.keychainErrorOccurred():
return createState(StateType.LoginKeycardEnterPin, self.flowType, nil)
method getNextSecondaryState*(self: LoginKeycardWrongKeycardState, controller: Controller): State =
controller.cancelCurrentFlow()
@ -19,4 +25,8 @@ method getNextSecondaryState*(self: LoginKeycardWrongKeycardState, controller: C
method getNextTertiaryState*(self: LoginKeycardWrongKeycardState, controller: Controller): State =
controller.cancelCurrentFlow()
return createState(StateType.WelcomeOldStatusUser, self.flowType, self)
return createState(StateType.WelcomeOldStatusUser, self.flowType, self)
method resolveKeycardNextState*(self: LoginKeycardWrongKeycardState, keycardFlowType: string, keycardEvent: KeycardEvent,
controller: Controller): State =
return ensureReaderAndCardPresenceAndResolveNextLoginState(self, keycardFlowType, keycardEvent, controller)

View File

@ -14,9 +14,13 @@ method executePrimaryCommand*(self: LoginKeycardWrongPinState, controller: Contr
if self.flowType == FlowType.AppLogin:
if not controller.isSelectedLoginAccountKeycardAccount():
controller.login()
elif controller.getPin().len == PINLengthForStatusApp:
elif not controller.keychainErrorOccurred() and controller.getPin().len == PINLengthForStatusApp:
controller.enterKeycardPin(controller.getPin())
method getNextPrimaryState*(self: LoginKeycardWrongPinState, controller: Controller): State =
if controller.keychainErrorOccurred():
return createState(StateType.LoginKeycardEnterPin, self.flowType, nil)
method getNextSecondaryState*(self: LoginKeycardWrongPinState, controller: Controller): State =
controller.cancelCurrentFlow()
return createState(StateType.WelcomeNewStatusUser, self.flowType, self)

View File

@ -0,0 +1,25 @@
type
LoginNotKeycardState* = ref object of State
proc newLoginNotKeycardState*(flowType: FlowType, backState: State): LoginNotKeycardState =
result = LoginNotKeycardState()
result.setup(flowType, StateType.LoginNotKeycard, backState)
proc delete*(self: LoginNotKeycardState) =
self.State.delete
method executePrimaryCommand*(self: LoginNotKeycardState, controller: Controller) =
if self.flowType == FlowType.AppLogin:
controller.runLoadAccountFlow(factoryReset = true)
method getNextSecondaryState*(self: LoginNotKeycardState, controller: Controller): State =
controller.cancelCurrentFlow()
return createState(StateType.WelcomeNewStatusUser, self.flowType, self)
method getNextTertiaryState*(self: LoginNotKeycardState, controller: Controller): State =
controller.cancelCurrentFlow()
return createState(StateType.WelcomeOldStatusUser, self.flowType, self)
method resolveKeycardNextState*(self: LoginNotKeycardState, keycardFlowType: string, keycardEvent: KeycardEvent,
controller: Controller): State =
return ensureReaderAndCardPresenceAndResolveNextLoginState(self, keycardFlowType, keycardEvent, controller)

View File

@ -0,0 +1,32 @@
type
LoginPluginState* = ref object of State
proc newLoginPluginState*(flowType: FlowType, backState: State): LoginPluginState =
result = LoginPluginState()
result.setup(flowType, StateType.LoginPlugin, backState)
proc delete*(self: LoginPluginState) =
self.State.delete
method executePrimaryCommand*(self: LoginPluginState, controller: Controller) =
if self.flowType == FlowType.AppLogin:
if not controller.isSelectedLoginAccountKeycardAccount():
controller.login()
elif not controller.keychainErrorOccurred() and controller.getPin().len == PINLengthForStatusApp:
controller.enterKeycardPin(controller.getPin())
method getNextPrimaryState*(self: LoginPluginState, controller: Controller): State =
if controller.keychainErrorOccurred():
return createState(StateType.LoginKeycardEnterPin, self.flowType, nil)
method getNextSecondaryState*(self: LoginPluginState, controller: Controller): State =
controller.cancelCurrentFlow()
return createState(StateType.WelcomeNewStatusUser, self.flowType, self)
method getNextTertiaryState*(self: LoginPluginState, controller: Controller): State =
controller.cancelCurrentFlow()
return createState(StateType.WelcomeOldStatusUser, self.flowType, self)
method resolveKeycardNextState*(self: LoginPluginState, keycardFlowType: string, keycardEvent: KeycardEvent,
controller: Controller): State =
return ensureReaderAndCardPresenceAndResolveNextLoginState(self, keycardFlowType, keycardEvent, controller)

View File

@ -12,6 +12,12 @@ method executePrimaryCommand*(self: LoginState, controller: Controller) =
if self.flowType == FlowType.AppLogin:
if not controller.isSelectedLoginAccountKeycardAccount():
controller.login()
elif not controller.keychainErrorOccurred() and controller.getPin().len == PINLengthForStatusApp:
controller.enterKeycardPin(controller.getPin())
method getNextPrimaryState*(self: LoginState, controller: Controller): State =
if controller.keychainErrorOccurred():
return createState(StateType.LoginKeycardEnterPin, self.flowType, nil)
method getNextSecondaryState*(self: LoginState, controller: Controller): State =
controller.cancelCurrentFlow()

View File

@ -47,6 +47,7 @@ type StateType* {.pure.} = enum
KeycardMaxPinRetriesReached = "KeycardMaxPinRetriesReached"
KeycardMaxPukRetriesReached = "KeycardMaxPukRetriesReached"
Login = "Login"
LoginPlugin = "LoginPlugin"
LoginKeycardInsertKeycard = "LoginKeycardInsertKeycard"
LoginKeycardReadingKeycard = "LoginKeycardReadingKeycard"
LoginKeycardEnterPin = "LoginKeycardEnterPin"
@ -55,6 +56,7 @@ type StateType* {.pure.} = enum
LoginKeycardMaxPinRetriesReached = "LoginKeycardMaxPinRetriesReached"
LoginKeycardMaxPukRetriesReached = "LoginKeycardMaxPukRetriesReached"
LoginKeycardEmpty = "LoginKeycardEmpty"
LoginNotKeycard = "LoginNotKeycard"
## This is the base class for all state we may have in onboarding/login flow.

View File

@ -47,6 +47,7 @@ include welcome_state_new_user
include welcome_state_old_user
include welcome_state
include login_state
include login_plugin_state
include login_keycard_insert_keycard_state
include login_keycard_reading_keycard_state
include login_keycard_enter_pin_state
@ -55,6 +56,7 @@ include login_keycard_wrong_pin_state
include login_keycard_max_pin_retries_reached_state
include login_keycard_max_puk_retries_reached_state
include login_keycard_empty_state
include login_not_keycard_state
proc createState*(stateToBeCreated: StateType, flowType: FlowType, backState: State): State =
if stateToBeCreated == StateType.AllowNotifications:
@ -119,6 +121,8 @@ proc createState*(stateToBeCreated: StateType, flowType: FlowType, backState: St
return newKeycardMaxPukRetriesReachedState(flowType, backState)
if stateToBeCreated == StateType.Login:
return newLoginState(flowType, backState)
if stateToBeCreated == StateType.LoginPlugin:
return newLoginPluginState(flowType, backState)
if stateToBeCreated == StateType.LoginKeycardInsertKeycard:
return newLoginKeycardInsertKeycardState(flowType, backState)
if stateToBeCreated == StateType.LoginKeycardReadingKeycard:
@ -135,6 +139,8 @@ proc createState*(stateToBeCreated: StateType, flowType: FlowType, backState: St
return newLoginKeycardMaxPukRetriesReachedState(flowType, backState)
if stateToBeCreated == StateType.LoginKeycardEmpty:
return newLoginKeycardEmptyState(flowType, backState)
if stateToBeCreated == StateType.LoginNotKeycard:
return newLoginNotKeycardState(flowType, backState)
error "No implementation available for state ", state=stateToBeCreated
@ -161,9 +167,9 @@ proc ensureReaderAndCardPresenceLogin*(state: State, keycardFlowType: string, ke
keycardEvent.error.len > 0 and
keycardEvent.error == ErrorConnection:
controller.resumeCurrentFlowLater()
if state.stateType == StateType.Login:
if state.stateType == StateType.LoginPlugin:
return nil
return createState(StateType.Login, state.flowType, state)
return createState(StateType.LoginPlugin, state.flowType, nil)
if keycardFlowType == ResponseTypeValueInsertCard and
keycardEvent.error.len > 0 and
keycardEvent.error == ErrorConnection:
@ -279,10 +285,10 @@ proc ensureReaderAndCardPresenceAndResolveNextLoginState*(state: State, keycardF
if keycardEvent.pinRetries == 0 and keycardEvent.pukRetries > 0:
return createState(StateType.LoginKeycardMaxPinRetriesReached, state.flowType, nil)
if keycardFlowType == ResponseTypeValueSwapCard and
keycardEvent.error.len > 0 and
keycardEvent.error == ErrorNoKeys:
return createState(StateType.LoginKeycardEmpty, state.flowType, nil)
if keycardFlowType == ResponseTypeValueSwapCard and
keycardEvent.error.len > 0 and
keycardEvent.error == RequestParamPUKRetries:
return createState(StateType.LoginKeycardMaxPukRetriesReached, state.flowType, nil)
keycardEvent.error.len > 0:
if keycardEvent.error == ErrorNoKeys:
return createState(StateType.LoginKeycardEmpty, state.flowType, nil)
if keycardEvent.error == ErrorNotAKeycard:
return createState(StateType.LoginNotKeycard, state.flowType, nil)
if keycardEvent.error == RequestParamPUKRetries:
return createState(StateType.LoginKeycardMaxPukRetriesReached, state.flowType, nil)

View File

@ -99,7 +99,6 @@ method load*[T](self: Module[T]) =
error "cannot run the app in login flow cause list of login accounts is empty"
quit() # quit the app
self.setSelectedLoginAccount(items[0])
self.view.setCurrentStartupState(newLoginState(FlowType.AppLogin, nil))
self.delegate.startupDidLoad()
method getKeycardSharedModule*[T](self: Module[T]): QVariant =
@ -228,8 +227,8 @@ method importAccountSuccess*[T](self: Module[T]) =
method setSelectedLoginAccount*[T](self: Module[T], item: login_acc_item.Item) =
self.controller.cancelCurrentFlow()
self.controller.setSelectedLoginAccount(item.getKeyUid(), item.getKeycardCreatedAccount())
self.view.setCurrentStartupState(newLoginState(FlowType.AppLogin, nil))
if item.getKeycardCreatedAccount():
self.view.setCurrentStartupState(newLoginState(FlowType.AppLogin, nil)) # nim garbage collector will handle all abandoned state objects
self.controller.runLoginFlow()
else:
self.controller.tryToObtainDataFromKeychain()

View File

@ -64,6 +64,7 @@ OnboardingBasePage {
return seedPhraseInputViewComponent
}
if (root.startupStore.currentStartupState.stateType === Constants.startupState.login ||
root.startupStore.currentStartupState.stateType === Constants.startupState.loginPlugin ||
root.startupStore.currentStartupState.stateType === Constants.startupState.loginKeycardInsertKeycard ||
root.startupStore.currentStartupState.stateType === Constants.startupState.loginKeycardReadingKeycard ||
root.startupStore.currentStartupState.stateType === Constants.startupState.loginKeycardEnterPin ||
@ -71,7 +72,8 @@ OnboardingBasePage {
root.startupStore.currentStartupState.stateType === Constants.startupState.loginKeycardWrongPin ||
root.startupStore.currentStartupState.stateType === Constants.startupState.loginKeycardMaxPinRetriesReached ||
root.startupStore.currentStartupState.stateType === Constants.startupState.loginKeycardMaxPukRetriesReached ||
root.startupStore.currentStartupState.stateType === Constants.startupState.loginKeycardEmpty)
root.startupStore.currentStartupState.stateType === Constants.startupState.loginKeycardEmpty ||
root.startupStore.currentStartupState.stateType === Constants.startupState.loginNotKeycard)
{
return loginViewComponent
}

View File

@ -518,6 +518,49 @@ Item {
target: pinSection
visible: false
}
PropertyChanges {
target: info
visible: false
}
PropertyChanges {
target: message
text: ""
visible: false
}
PropertyChanges {
target: button
text: ""
visible: false
}
PropertyChanges {
target: link
text: ""
visible: false
}
},
State {
name: Constants.startupState.loginPlugin
when: root.startupStore.selectedLoginAccount.keycardCreatedAccount &&
root.startupStore.currentStartupState.stateType === Constants.startupState.loginPlugin
PropertyChanges {
target: image
source: Style.svg("keycard/card3@2x")
Layout.preferredHeight: sourceSize.height
Layout.preferredWidth: sourceSize.width
}
PropertyChanges {
target: title
text: ""
visible: false
}
PropertyChanges {
target: passwordSection
visible: false
}
PropertyChanges {
target: pinSection
visible: false
}
PropertyChanges {
target: info
text: qsTr("Plug in Keycard reader...")
@ -908,6 +951,53 @@ Item {
text: qsTr("Generate keys for a new Keycard")
visible: true
}
},
State {
name: Constants.startupState.loginNotKeycard
when: root.startupStore.currentStartupState.stateType === Constants.startupState.loginNotKeycard
PropertyChanges {
target: image
source: Style.svg("keycard/card-wrong3@2x")
Layout.preferredHeight: sourceSize.height
Layout.preferredWidth: sourceSize.width
}
PropertyChanges {
target: title
text: ""
visible: false
}
PropertyChanges {
target: passwordSection
visible: false
}
PropertyChanges {
target: pinSection
visible: false
}
PropertyChanges {
target: info
text: qsTr("This is not a Keycard")
visible: true
font.pixelSize: Constants.keycard.general.fontSize2
color: Theme.palette.dangerColor1
}
PropertyChanges {
target: message
text: qsTr("The card inserted is not a recognised Keycard,\nplease remove and try and again")
visible: true
font.pixelSize: Constants.keycard.general.fontSize2
color: Theme.palette.baseColor1
}
PropertyChanges {
target: button
text: ""
visible: false
}
PropertyChanges {
target: link
text: ""
visible: false
}
}
]
}

View File

@ -57,6 +57,7 @@ QtObject {
readonly property string keycardMaxPinRetriesReached: "KeycardMaxPinRetriesReached"
readonly property string keycardMaxPukRetriesReached: "KeycardMaxPukRetriesReached"
readonly property string login: "Login"
readonly property string loginPlugin: "LoginPlugin"
readonly property string loginKeycardInsertKeycard: "LoginKeycardInsertKeycard"
readonly property string loginKeycardReadingKeycard: "LoginKeycardReadingKeycard"
readonly property string loginKeycardEnterPin: "LoginKeycardEnterPin"
@ -65,6 +66,7 @@ QtObject {
readonly property string loginKeycardMaxPinRetriesReached: "LoginKeycardMaxPinRetriesReached"
readonly property string loginKeycardMaxPukRetriesReached: "LoginKeycardMaxPukRetriesReached"
readonly property string loginKeycardEmpty: "LoginKeycardEmpty"
readonly property string loginNotKeycard: "LoginNotKeycard"
}
readonly property QtObject predefinedKeycardData: QtObject {