Skip to content

Commit

Permalink
feat(Onboarding): implement the new UnblockWithPukFlow
Browse files Browse the repository at this point in the history
- integrate the PUK unblock flow into the Onboarding and Login screen
- added a dedicated SB page for it
- remove the `Locked` keycard state everywhere in favor of `BlockedPIN`
and `BlockedPUK`
- fix the various "Locked" buttons, based on the context and the state
of the keycard

Fixes: #17092
  • Loading branch information
caybro committed Jan 24, 2025
1 parent 3537f17 commit 1d374aa
Show file tree
Hide file tree
Showing 23 changed files with 602 additions and 95 deletions.
12 changes: 11 additions & 1 deletion storybook/pages/KeycardEnterPukPagePage.qml
Original file line number Diff line number Diff line change
Expand Up @@ -11,15 +11,25 @@ Item {
KeycardEnterPukPage {
id: page
anchors.fill: parent
remainingAttempts: 3
tryToSetPukFunction: (puk) => {
console.warn("!!! ATTEMPTED PUK:", puk)
return puk === root.existingPuk
const valid = puk === root.existingPuk
if (!valid)
remainingAttempts--
return valid
}
onKeycardPukEntered: (puk) => {
console.warn("!!! CORRECT PUK:", puk)
console.warn("!!! RESETTING FLOW")
state = "entering"
}
onKeycardFactoryResetRequested: {
console.warn("onKeycardFactoryResetRequested")
console.warn("!!! RESETTING FLOW")
state = "entering"
remainingAttempts = 3
}
}

Label {
Expand Down
3 changes: 2 additions & 1 deletion storybook/pages/KeycardIntroPagePage.qml
Original file line number Diff line number Diff line change
Expand Up @@ -80,7 +80,8 @@ Item {
{ value: Onboarding.KeycardState.WrongKeycard, text: "WrongKeycard" },
{ value: Onboarding.KeycardState.NotKeycard, text: "NotKeycard" },
{ value: Onboarding.KeycardState.MaxPairingSlotsReached, text: "MaxPairingSlotsReached" },
{ value: Onboarding.KeycardState.Locked, text: "Locked" },
{ value: Onboarding.KeycardState.BlockedPIN, text: "BlockedPIN" },
{ value: Onboarding.KeycardState.BlockedPUK, text: "BlockedPUK" },
{ value: Onboarding.KeycardState.NotEmpty, text: "NotEmpty" },
{ value: Onboarding.KeycardState.Empty, text: "Empty" }
]
Expand Down
27 changes: 14 additions & 13 deletions storybook/pages/LoginScreenPage.qml
Original file line number Diff line number Diff line change
Expand Up @@ -30,16 +30,16 @@ SplitView {

// keycard
property int keycardState: Onboarding.KeycardState.NoPCSCService
property int keycardRemainingPinAttempts: ctrlUnlockWithPuk.checked ? 1 : 5
property int keycardRemainingPinAttempts: 3

function setPin(pin: string) { // -> bool
logs.logEvent("OnboardingStore.setPin", ["pin"], arguments)
const valid = pin === ctrlPin.text
if (!valid)
keycardRemainingPinAttempts-- // SIMULATION: decrease the remaining PIN attempts
if (keycardRemainingPinAttempts <= 0) { // SIMULATION: "lock" the keycard
keycardState = Onboarding.KeycardState.Locked
keycardRemainingPinAttempts = ctrlUnlockWithPuk.checked ? 1 : 5
if (keycardRemainingPinAttempts <= 0) { // SIMULATION: "block" the keycard
keycardState = Onboarding.KeycardState.BlockedPIN
keycardRemainingPinAttempts = 0
}
return valid
}
Expand Down Expand Up @@ -72,9 +72,10 @@ SplitView {
}
onOnboardingCreateProfileFlowRequested: logs.logEvent("onOnboardingCreateProfileFlowRequested")
onOnboardingLoginFlowRequested: logs.logEvent("onOnboardingLoginFlowRequested")
onUnlockWithSeedphraseRequested: logs.logEvent("onUnlockWithSeedphraseRequested")
onUnlockWithPukRequested: logs.logEvent("onUnlockWithPukRequested")
onUnblockWithSeedphraseRequested: logs.logEvent("onUnblockWithSeedphraseRequested")
onUnblockWithPukRequested: logs.logEvent("onUnblockWithPukRequested")
onLostKeycard: logs.logEvent("onLostKeycard")
onKeycardFactoryResetRequested: logs.logEvent("onKeycardFactoryResetRequested")

// mocks
QtObject {
Expand All @@ -90,6 +91,7 @@ SplitView {
x: root.Window.width - width
password: ctrlPassword.text
pin: ctrlPin.text
selectedProfileIsKeycard: loginScreen.selectedProfileIsKeycard
onAccountLoginError: (error, wrongPassword) => store.accountLoginError(error, wrongPassword)
onObtainingPasswordSuccess: (password) => store.obtainingPasswordSuccess(password)
onObtainingPasswordError: (errorDescription, errorType, wrongFingerprint) => store.obtainingPasswordError(errorDescription, errorType, wrongFingerprint)
Expand Down Expand Up @@ -132,11 +134,6 @@ SplitView {
enabled: ctrlBiometrics.checked
checked: ctrlBiometrics.checked
}
Switch {
id: ctrlUnlockWithPuk
text: "Unlock with PUK available"
checked: true
}
}

RowLayout {
Expand Down Expand Up @@ -167,11 +164,15 @@ SplitView {
{ value: Onboarding.KeycardState.WrongKeycard, text: "WrongKeycard" },
{ value: Onboarding.KeycardState.NotKeycard, text: "NotKeycard" },
{ value: Onboarding.KeycardState.MaxPairingSlotsReached, text: "MaxPairingSlotsReached" },
{ value: Onboarding.KeycardState.Locked, text: "Locked" },
{ value: Onboarding.KeycardState.BlockedPIN, text: "BlockedPIN" },
{ value: Onboarding.KeycardState.BlockedPUK, text: "BlockedPUK" },
{ value: Onboarding.KeycardState.NotEmpty, text: "NotEmpty" },
{ value: Onboarding.KeycardState.Empty, text: "Empty" }
]
onActivated: store.keycardState = currentValue
onActivated: {
store.keycardState = currentValue
store.keycardRemainingPinAttempts = 3
}
Component.onCompleted: currentIndex = Qt.binding(() => indexOfValue(store.keycardState))
}
}
Expand Down
54 changes: 41 additions & 13 deletions storybook/pages/OnboardingLayoutPage.qml
Original file line number Diff line number Diff line change
Expand Up @@ -32,6 +32,7 @@ SplitView {
readonly property string mnemonic: "dog dog dog dog dog dog dog dog dog dog dog dog"
readonly property var seedWords: ["apple", "banana", "cat", "cow", "catalog", "catch", "category", "cattle", "dog", "elephant", "fish", "grape"]
readonly property string pin: "111111"
readonly property string puk: "111111111111"
readonly property string password: "somepassword"

// TODO simulation
Expand Down Expand Up @@ -62,17 +63,30 @@ SplitView {
property int addKeyPairState: Onboarding.AddKeyPairState.InProgress
property int syncState: Onboarding.SyncState.InProgress

property int keycardRemainingPinAttempts: ctrlUnlockWithPuk.checked ? 1 : 5
property int keycardRemainingPinAttempts: 2
property int keycardRemainingPukAttempts: 3

function setPin(pin: string) { // -> bool
logs.logEvent("OnboardingStore.setPin", ["pin"], arguments)
ctrlLoginResult.result = "🯄"
const valid = pin === mockDriver.pin
if (!valid)
keycardRemainingPinAttempts--
if (keycardRemainingPinAttempts <= 0) { // SIMULATION: "lock" the keycard
keycardState = Onboarding.KeycardState.Locked
keycardRemainingPinAttempts = ctrlUnlockWithPuk.checked ? 1 : 5
if (keycardRemainingPinAttempts <= 0) { // SIMULATION: "block" the keycard
keycardState = Onboarding.KeycardState.BlockedPIN
keycardRemainingPinAttempts = 0
}
return valid
}

function setPuk(puk) { // -> bool
logs.logEvent("OnboardingStore.setPuk", ["puk"], arguments)
const valid = puk === mockDriver.puk
if (!valid)
keycardRemainingPukAttempts--
if (keycardRemainingPukAttempts <= 0) { // SIMULATION: "block" the keycard
keycardState = Onboarding.KeycardState.BlockedPUK
keycardRemainingPukAttempts = 0
}
return valid
}
Expand Down Expand Up @@ -153,7 +167,11 @@ SplitView {
}
}

onReloadKeycardRequested: store.keycardState = Onboarding.KeycardState.NoPCSCService
onReloadKeycardRequested: {
store.keycardState = Onboarding.KeycardState.NoPCSCService
store.keycardRemainingPinAttempts = 2
store.keycardRemainingPukAttempts = 3
}

// mocks
QtObject {
Expand Down Expand Up @@ -229,13 +247,25 @@ SplitView {

visible: onboarding.stack.currentItem instanceof KeycardEnterPinPage ||
onboarding.stack.currentItem instanceof KeycardCreatePinPage ||
(onboarding.stack.currentItem instanceof LoginScreen && onboarding.stack.currentItem.selectedProfileIsKeycard)
(onboarding.stack.currentItem instanceof LoginScreen && onboarding.stack.currentItem.selectedProfileIsKeycard && store.keycardState === Onboarding.KeycardState.NotEmpty)

text: "Copy valid PIN (\"%1\")".arg(mockDriver.pin)
focusPolicy: Qt.NoFocus
onClicked: ClipboardUtils.setText(mockDriver.pin)
}

Button {
anchors.bottom: parent.bottom
anchors.right: parent.right
anchors.margins: 10

visible: onboarding.stack.currentItem instanceof KeycardEnterPukPage

text: "Copy valid PUK (\"%1\")".arg(mockDriver.puk)
focusPolicy: Qt.NoFocus
onClicked: ClipboardUtils.setText(mockDriver.puk)
}

Button {
anchors.bottom: parent.bottom
anchors.right: parent.right
Expand Down Expand Up @@ -290,6 +320,7 @@ SplitView {
x: root.Window.width - width
password: mockDriver.password
pin: mockDriver.pin
selectedProfileIsKeycard: onboarding.stack.currentItem instanceof LoginScreen && onboarding.stack.currentItem.selectedProfileIsKeycard
onAccountLoginError: (error, wrongPassword) => store.accountLoginError(error, wrongPassword)
onObtainingPasswordSuccess: (password) => store.obtainingPasswordSuccess(password)
onObtainingPasswordError: (errorDescription, errorType, wrongFingerprint) => store.obtainingPasswordError(errorDescription, errorType, wrongFingerprint)
Expand Down Expand Up @@ -388,19 +419,15 @@ SplitView {
Switch {
id: ctrlTouchIdUser
text: "Touch ID login"
visible: ctrlLoginScreen.checked
enabled: ctrlBiometrics.checked
checked: ctrlBiometrics.checked
}

Switch {
id: ctrlUnlockWithPuk
text: "Unlock with PUK available"
checked: true
}

Text {
id: ctrlLoginResult
property string result: "🯄"
visible: ctrlLoginScreen.checked
text: "Login result: %1".arg(result)
}
}
Expand All @@ -427,7 +454,8 @@ SplitView {
{ value: Onboarding.KeycardState.WrongKeycard, text: "WrongKeycard" },
{ value: Onboarding.KeycardState.NotKeycard, text: "NotKeycard" },
{ value: Onboarding.KeycardState.MaxPairingSlotsReached, text: "MaxPairingSlotsReached" },
{ value: Onboarding.KeycardState.Locked, text: "Locked" },
{ value: Onboarding.KeycardState.BlockedPIN, text: "BlockedPIN" },
{ value: Onboarding.KeycardState.BlockedPUK, text: "BlockedPUK" },
{ value: Onboarding.KeycardState.NotEmpty, text: "NotEmpty" },
{ value: Onboarding.KeycardState.Empty, text: "Empty" }
]
Expand Down
Loading

0 comments on commit 1d374aa

Please sign in to comment.