feat: AuthorizationState (#17309)

This commit is contained in:
Igor Sirotin 2025-02-17 21:09:01 +03:00 committed by GitHub
parent ab197d8c4b
commit 4a6543bdca
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
14 changed files with 83 additions and 52 deletions

View File

@ -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):

View File

@ -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.} =

View File

@ -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)

View File

@ -23,3 +23,10 @@ type ProgressState* {.pure.} = enum
InProgress,
Success,
Failed,
type AuthorizationState* {.pure.} = enum
Idle
InProgress
Authorized
WrongPIN
Error

View File

@ -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()

View File

@ -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:

View File

@ -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

View File

@ -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")

View File

@ -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)
};

View File

@ -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 = () => {

View File

@ -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)
}
}

View File

@ -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,

View File

@ -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()

View File

@ -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