taler-typescript-core

Wallet core logic and WebUIs for various components
Log | Files | Refs | Submodules | README | LICENSE

commit f8c228464bfcd74e493aa5284d569fcf949a0b58
parent fa7373350d98a06541b16f1f4b9d46229cf39fd6
Author: Sebastian <sebasjm@gmail.com>
Date:   Mon, 10 Nov 2025 07:47:56 -0300

fix #10553

Diffstat:
Mpackages/aml-backoffice-ui/src/components/UnlockAccount.tsx | 18+++++++++++-------
Mpackages/aml-backoffice-ui/src/hooks/officer.ts | 32++++++++++++++++++++------------
Mpackages/aml-backoffice-ui/src/pages/DecisionWizard.tsx | 11++++++++---
3 files changed, 39 insertions(+), 22 deletions(-)

diff --git a/packages/aml-backoffice-ui/src/components/UnlockAccount.tsx b/packages/aml-backoffice-ui/src/components/UnlockAccount.tsx @@ -21,10 +21,11 @@ import { LocalNotificationBanner, useForm, useLocalNotificationBetter, - useTranslationContext + useTranslationContext, } from "@gnu-taler/web-util/browser"; import { VNode, h } from "preact"; import { OfficerLocked } from "../hooks/officer.js"; +import { HttpStatusCode } from "@gnu-taler/taler-util"; type FormType = { password: string; @@ -59,10 +60,13 @@ export function UnlockAccount({ officer }: { officer: OfficerLocked }): VNode { officer.tryUnlock, status.status === "fail" ? undefined : [status.result.password], ); - const forget = safeFunctionHandler( - async () => officer.forget(), - [], - ); + unlock.onFail = (fail) => { + switch (fail.case) { + case HttpStatusCode.Forbidden: + return i18n.str`Couldn't unlock the account, the password may be wrong.`; + } + }; + const forget = safeFunctionHandler(async () => officer.forget(), []); return ( <div class="flex min-h-full flex-col "> @@ -81,7 +85,7 @@ export function UnlockAccount({ officer }: { officer: OfficerLocked }): VNode { </div> <div class="mt-10 sm:mx-auto sm:w-full sm:max-w-[480px] "> - <div class="bg-gray-100 px-6 py-6 shadow sm:rounded-lg sm:px-12"> + <form class="bg-gray-100 px-6 py-6 shadow sm:rounded-lg sm:px-12"> <div class="mb-4"> <InputLine label={i18n.str`Password`} @@ -101,7 +105,7 @@ export function UnlockAccount({ officer }: { officer: OfficerLocked }): VNode { <i18n.Translate>Unlock</i18n.Translate> </ButtonBetter> </div> - </div> + </form> <ButtonBetter type="button" onClick={forget} diff --git a/packages/aml-backoffice-ui/src/hooks/officer.ts b/packages/aml-backoffice-ui/src/hooks/officer.ts @@ -18,9 +18,11 @@ import { Codec, Duration, EddsaPrivP, + HttpStatusCode, LockedAccount, OfficerAccount, OfficerId, + OperationFail, OperationOk, buildCodecForObject, codecForAbsoluteTime, @@ -29,7 +31,8 @@ import { decodeCrock, encodeCrock, opFixedSuccess, - unlockOfficerAccount, + opKnownFailure, + unlockOfficerAccount } from "@gnu-taler/taler-util"; import { buildStorageKey, @@ -41,7 +44,7 @@ import { usePreferences } from "./preferences.js"; const DEFAULT_SESSION_DURATION = Duration.fromSpec({ // seconds: 10, - hours: 1 + hours: 1, }); export interface Officer { @@ -79,7 +82,7 @@ export interface OfficerNotFound { export interface OfficerLocked { state: "locked"; forget: () => OperationOk<void>; - tryUnlock: (password: string) => Promise<OperationOk<void>>; + tryUnlock: (password: string) => Promise<OperationOk<void> | OperationFail<HttpStatusCode.Forbidden>>; } export interface OfficerReady { state: "ready"; @@ -109,7 +112,7 @@ export function useOfficer(): OfficerState { return { id: accountStorage.value.id as OfficerId, signingKey: decodeCrock(accountStorage.value.strKey) as EddsaPrivP, - unlocked: accountStorage.value.unlocked + unlocked: accountStorage.value.unlocked, }; }, [accountStorage.value?.id, accountStorage.value?.strKey]); @@ -152,14 +155,19 @@ export function useOfficer(): OfficerState { return opFixedSuccess(undefined); }, tryUnlock: async (pwd: string) => { - const ac = await unlockOfficerAccount(officer.account, pwd); - // accountStorage.update(ac); - accountStorage.update({ - id: ac.id, - strKey: encodeCrock(ac.signingKey), - unlocked: AbsoluteTime.now(), - }); - return opFixedSuccess(undefined); + try { + const ac = await unlockOfficerAccount(officer.account, pwd); + // accountStorage.update(ac); + accountStorage.update({ + id: ac.id, + strKey: encodeCrock(ac.signingKey), + unlocked: AbsoluteTime.now(), + }); + return opFixedSuccess(undefined); + } catch (e) { + const d = opKnownFailure(HttpStatusCode.Forbidden); + return d; + } }, }; } diff --git a/packages/aml-backoffice-ui/src/pages/DecisionWizard.tsx b/packages/aml-backoffice-ui/src/pages/DecisionWizard.tsx @@ -19,7 +19,7 @@ import { PaytoString, PaytoUri, TalerError, - TranslatedString + TranslatedString, } from "@gnu-taler/taler-util"; import { CopyButton, useTranslationContext } from "@gnu-taler/web-util/browser"; import { h, VNode } from "preact"; @@ -118,7 +118,7 @@ export function DecisionWizard({ newPayto?: string; formId: string | undefined; step?: WizardSteps; - officer: OfficerReady, + officer: OfficerReady; onMove: (n: WizardSteps | undefined) => void; }): VNode { const { i18n } = useTranslationContext(); @@ -139,7 +139,12 @@ export function DecisionWizard({ return <Attributes formId={formId} />; case "summary": return ( - <Summary account={account} onMove={onMove} newPayto={newPayto} officer={officer}/> + <Summary + account={account} + onMove={onMove} + newPayto={newPayto} + officer={officer} + /> ); } assertUnreachable(stepOrDefault);