From f5771cc7b99dc938fd606dcbee350b66ec8027c9 Mon Sep 17 00:00:00 2001 From: Sebastian Date: Tue, 9 Jan 2024 18:51:49 -0300 Subject: prepare for 2fa impl --- .../demobank-ui/src/components/Cashouts/views.tsx | 6 +- packages/demobank-ui/src/context/config.ts | 21 ++- .../demobank-ui/src/pages/AccountPage/state.ts | 6 +- .../demobank-ui/src/pages/OperationState/state.ts | 6 +- .../demobank-ui/src/pages/OperationState/views.tsx | 30 ++-- .../src/pages/PaytoWireTransferForm.tsx | 14 +- packages/demobank-ui/src/pages/QrCodeSection.tsx | 7 +- .../demobank-ui/src/pages/RegistrationPage.tsx | 16 +- .../demobank-ui/src/pages/WalletWithdrawForm.tsx | 7 +- packages/demobank-ui/src/pages/WireTransfer.tsx | 6 +- .../src/pages/WithdrawalConfirmationQuestion.tsx | 22 +-- .../demobank-ui/src/pages/WithdrawalQRCode.tsx | 5 +- .../src/pages/account/ShowAccountDetails.tsx | 21 +-- .../src/pages/account/UpdateAccountPassword.tsx | 9 +- .../demobank-ui/src/pages/admin/AccountForm.tsx | 6 +- .../demobank-ui/src/pages/admin/AccountList.tsx | 4 +- .../src/pages/admin/CreateNewAccount.tsx | 18 +- .../demobank-ui/src/pages/admin/RemoveAccount.tsx | 14 +- .../src/pages/business/CreateCashout.tsx | 44 +++-- .../src/pages/business/ShowCashoutDetails.tsx | 29 ++-- packages/taler-util/src/http-client/bank-core.ts | 182 +++++++++++---------- packages/taler-util/src/operation.ts | 10 +- 22 files changed, 276 insertions(+), 207 deletions(-) (limited to 'packages') diff --git a/packages/demobank-ui/src/components/Cashouts/views.tsx b/packages/demobank-ui/src/components/Cashouts/views.tsx index 341c43b48..1f1f02e4e 100644 --- a/packages/demobank-ui/src/components/Cashouts/views.tsx +++ b/packages/demobank-ui/src/components/Cashouts/views.tsx @@ -14,7 +14,7 @@ GNU Taler; see the file COPYING. If not, see */ -import { Amounts, TalerError, assertUnreachable } from "@gnu-taler/taler-util"; +import { Amounts, HttpStatusCode, TalerError, assertUnreachable } from "@gnu-taler/taler-util"; import { Attention, Loading, useTranslationContext } from "@gnu-taler/web-util/browser"; import { format } from "date-fns"; import { Fragment, VNode, h } from "preact"; @@ -26,13 +26,13 @@ import { State } from "./index.js"; export function FailedView({ error }: State.Failed) { const { i18n } = useTranslationContext(); switch (error.case) { - case "cashout-not-supported": return
{error.detail.hint}
- case "account-not-found": return
{error.detail.hint} diff --git a/packages/demobank-ui/src/context/config.ts b/packages/demobank-ui/src/context/config.ts index 9908c73a2..2d70cf932 100644 --- a/packages/demobank-ui/src/context/config.ts +++ b/packages/demobank-ui/src/context/config.ts @@ -14,7 +14,7 @@ GNU Taler; see the file COPYING. If not, see */ -import { TalerCorebankApi, TalerCoreBankHttpClient, TalerError } from "@gnu-taler/taler-util"; +import { LibtoolVersion, TalerCorebankApi, TalerCoreBankHttpClient, TalerError } from "@gnu-taler/taler-util"; import { BrowserHttpLib, ErrorLoading, useTranslationContext } from "@gnu-taler/web-util/browser"; import { ComponentChildren, createContext, FunctionComponent, h, VNode } from "preact"; import { useContext, useEffect, useState } from "preact/hooks"; @@ -34,8 +34,15 @@ const Context = createContext(undefined as any); export const useBankCoreApiContext = (): Type => useContext(Context); +enum VersionHint { + /** + * when this flag is on, server is running an old version with cashout before implementing 2fa API + */ + CASHOUT_BEFORE_2FA, +} + export type ConfigResult = undefined - | { type: "ok", config: TalerCorebankApi.Config } + | { type: "ok", config: TalerCorebankApi.Config, hint: VersionHint[] } | { type: "incompatible", result: TalerCorebankApi.Config, supported: string } | { type: "error", error: TalerError } @@ -56,9 +63,15 @@ export const BankCoreApiProvider = ({ api.getConfig() .then((resp) => { if (api.isCompatible(resp.body.version)) { - setChecked({ type: "ok", config: resp.body }); + setChecked({ type: "ok", config: resp.body, hint: [] }); } else { - setChecked({ type: "incompatible", result: resp.body, supported: api.PROTOCOL_VERSION }) + //this API supports version 3.0.3 + const compare = LibtoolVersion.compare("3:0:3", resp.body.version) + if (compare?.compatible ?? false) { + setChecked({ type: "ok", config: resp.body, hint: [VersionHint.CASHOUT_BEFORE_2FA] }); + } else { + setChecked({ type: "incompatible", result: resp.body, supported: api.PROTOCOL_VERSION }) + } } }) .catch((error: unknown) => { diff --git a/packages/demobank-ui/src/pages/AccountPage/state.ts b/packages/demobank-ui/src/pages/AccountPage/state.ts index d28aba7bf..88e8cf747 100644 --- a/packages/demobank-ui/src/pages/AccountPage/state.ts +++ b/packages/demobank-ui/src/pages/AccountPage/state.ts @@ -14,7 +14,7 @@ GNU Taler; see the file COPYING. If not, see */ -import { Amounts, TalerError, parsePaytoUri } from "@gnu-taler/taler-util"; +import { Amounts, HttpStatusCode, TalerError, parsePaytoUri } from "@gnu-taler/taler-util"; import { useTranslationContext } from "@gnu-taler/web-util/browser"; import { useAccountDetails } from "../../hooks/access.js"; import { assertUnreachable } from "../WithdrawalOperationPage.js"; @@ -40,11 +40,11 @@ export function useComponentState({ account, goToConfirmOperation }: Props): Sta if (result.type === "fail") { switch (result.case) { - case "unauthorized": return { + case HttpStatusCode.Unauthorized: return { status: "login", reason: "forbidden" } - case "not-found": return { + case HttpStatusCode.NotFound: return { status: "login", reason: "not-found", } diff --git a/packages/demobank-ui/src/pages/OperationState/state.ts b/packages/demobank-ui/src/pages/OperationState/state.ts index defca6f13..477146d1e 100644 --- a/packages/demobank-ui/src/pages/OperationState/state.ts +++ b/packages/demobank-ui/src/pages/OperationState/state.ts @@ -14,7 +14,7 @@ GNU Taler; see the file COPYING. If not, see */ -import { Amounts, FailCasesByMethod, TalerCoreBankErrorsByMethod, TalerError, TalerErrorDetail, TranslatedString, parsePaytoUri, parseWithdrawUri, stringifyWithdrawUri } from "@gnu-taler/taler-util"; +import { Amounts, FailCasesByMethod, HttpStatusCode, TalerCoreBankErrorsByMethod, TalerError, TalerErrorDetail, TranslatedString, parsePaytoUri, parseWithdrawUri, stringifyWithdrawUri } from "@gnu-taler/taler-util"; import { notify, notifyError, notifyInfo, useTranslationContext, utils } from "@gnu-taler/web-util/browser"; import { useEffect, useState } from "preact/hooks"; import { mutate } from "swr"; @@ -134,8 +134,8 @@ export function useComponentState({ currency, onClose }: Props): utils.Recursive if (result.type === "fail") { switch (result.case) { - case "invalid-id": - case "not-found": { + case HttpStatusCode.BadRequest: + case HttpStatusCode.NotFound: { return { status: "aborted", error: undefined, diff --git a/packages/demobank-ui/src/pages/OperationState/views.tsx b/packages/demobank-ui/src/pages/OperationState/views.tsx index b1f09ba2a..a02bb3bbd 100644 --- a/packages/demobank-ui/src/pages/OperationState/views.tsx +++ b/packages/demobank-ui/src/pages/OperationState/views.tsx @@ -14,7 +14,7 @@ GNU Taler; see the file COPYING. If not, see */ -import { TranslatedString, stringifyWithdrawUri } from "@gnu-taler/taler-util"; +import { HttpStatusCode, TalerErrorCode, TranslatedString, stringifyWithdrawUri } from "@gnu-taler/taler-util"; import { Attention, LocalNotificationBanner, ShowInputErrorLabel, notifyInfo, useLocalNotification, useTranslationContext } from "@gnu-taler/web-util/browser"; import { Fragment, VNode, h } from "preact"; import { useEffect, useMemo, useState } from "preact/hooks"; @@ -70,19 +70,19 @@ export function NeedConfirmationView({ error, onAbort: doAbort, onConfirm: doCon const resp = await doAbort() if (!resp) return; switch (resp.case) { - case "previously-confirmed": return notify({ + case HttpStatusCode.Conflict: return notify({ type: "error", title: i18n.str`The reserve operation has been confirmed previously and can't be aborted`, description: resp.detail.hint as TranslatedString, debug: resp.detail, }) - case "invalid-id": return notify({ + case HttpStatusCode.BadRequest: return notify({ type: "error", title: i18n.str`The operation id is invalid.`, description: resp.detail.hint as TranslatedString, debug: resp.detail, }); - case "not-found": return notify({ + case HttpStatusCode.NotFound: return notify({ type: "error", title: i18n.str`The operation was not found.`, description: resp.detail.hint as TranslatedString, @@ -104,31 +104,31 @@ export function NeedConfirmationView({ error, onAbort: doAbort, onConfirm: doCon return } switch (hasError.case) { - case "previously-aborted": return notify({ + case TalerErrorCode.BANK_CONFIRM_ABORT_CONFLICT: return notify({ type: "error", title: i18n.str`The withdrawal has been aborted previously and can't be confirmed`, description: hasError.detail.hint as TranslatedString, debug: hasError.detail, }) - case "no-exchange-or-reserve-selected": return notify({ + case TalerErrorCode.BANK_CONFIRM_INCOMPLETE: return notify({ type: "error", title: i18n.str`The withdraw operation cannot be confirmed because no exchange and reserve public key selection happened before`, description: hasError.detail.hint as TranslatedString, debug: hasError.detail, }) - case "invalid-id": return notify({ + case HttpStatusCode.BadRequest: return notify({ type: "error", title: i18n.str`The operation id is invalid.`, description: hasError.detail.hint as TranslatedString, debug: hasError.detail, }); - case "not-found": return notify({ + case HttpStatusCode.NotFound: return notify({ type: "error", title: i18n.str`The operation was not found.`, description: hasError.detail.hint as TranslatedString, debug: hasError.detail, }); - case "insufficient-funds": return notify({ + case TalerErrorCode.BANK_UNALLOWED_DEBIT: return notify({ type: "error", title: i18n.str`Your balance is not enough.`, description: hasError.detail.hint as TranslatedString, @@ -215,19 +215,19 @@ export function NeedConfirmationView({ error, onAbort: doAbort, onConfirm: doCon export function FailedView({ error }: State.Failed) { const { i18n } = useTranslationContext(); switch (error.case) { - case "unauthorized": return
{error.detail.hint}
- case "insufficient-funds": return
{error.detail.hint}
- case "account-not-found": return
{error.detail.hint} @@ -322,19 +322,19 @@ export function ReadyView({ uri, onClose: doClose }: State.Ready): VNode<{}> { const hasError = await doClose() if (!hasError) return; switch (hasError.case) { - case "previously-confirmed": return notify({ + case HttpStatusCode.Conflict: return notify({ type: "error", title: i18n.str`The reserve operation has been confirmed previously and can't be aborted`, description: hasError.detail.hint as TranslatedString, debug: hasError.detail, }) - case "invalid-id": return notify({ + case HttpStatusCode.BadRequest: return notify({ type: "error", title: i18n.str`The operation id is invalid.`, description: hasError.detail.hint as TranslatedString, debug: hasError.detail, }); - case "not-found": return notify({ + case HttpStatusCode.NotFound: return notify({ type: "error", title: i18n.str`The operation was not found.`, description: hasError.detail.hint as TranslatedString, diff --git a/packages/demobank-ui/src/pages/PaytoWireTransferForm.tsx b/packages/demobank-ui/src/pages/PaytoWireTransferForm.tsx index c785b24df..7a94f5486 100644 --- a/packages/demobank-ui/src/pages/PaytoWireTransferForm.tsx +++ b/packages/demobank-ui/src/pages/PaytoWireTransferForm.tsx @@ -21,8 +21,10 @@ import { Amounts, CurrencySpecification, FRAC_SEPARATOR, + HttpStatusCode, Logger, PaytoString, + TalerErrorCode, TranslatedString, buildPayto, parsePaytoUri, @@ -151,37 +153,37 @@ export function PaytoWireTransferForm({ mutate(() => true) if (res.type === "fail") { switch (res.case) { - case "invalid-input": return notify({ + case HttpStatusCode.BadRequest: return notify({ type: "error", title: i18n.str`The request was invalid or the payto://-URI used unacceptable features.`, description: res.detail.hint as TranslatedString, debug: res.detail, }) - case "unauthorized": return notify({ + case HttpStatusCode.Unauthorized: return notify({ type: "error", title: i18n.str`Not enough permission to complete the operation.`, description: res.detail.hint as TranslatedString, debug: res.detail, }) - case "creditor-not-found": return notify({ + case TalerErrorCode.BANK_UNKNOWN_CREDITOR: return notify({ type: "error", title: i18n.str`The destination account "${puri}" was not found.`, description: res.detail.hint as TranslatedString, debug: res.detail, }) - case "creditor-same": return notify({ + case TalerErrorCode.BANK_SAME_ACCOUNT: return notify({ type: "error", title: i18n.str`The origin and the destination of the transfer can't be the same.`, description: res.detail.hint as TranslatedString, debug: res.detail, }) - case "insufficient-funds": return notify({ + case TalerErrorCode.BANK_UNALLOWED_DEBIT: return notify({ type: "error", title: i18n.str`Your balance is not enough.`, description: res.detail.hint as TranslatedString, debug: res.detail, }) - case "not-found": return notify({ + case HttpStatusCode.NotFound: return notify({ type: "error", title: i18n.str`The origin account "${puri}" was not found.`, description: res.detail.hint as TranslatedString, diff --git a/packages/demobank-ui/src/pages/QrCodeSection.tsx b/packages/demobank-ui/src/pages/QrCodeSection.tsx index 60beb012e..64ebf7e83 100644 --- a/packages/demobank-ui/src/pages/QrCodeSection.tsx +++ b/packages/demobank-ui/src/pages/QrCodeSection.tsx @@ -15,6 +15,7 @@ */ import { + HttpStatusCode, stringifyWithdrawUri, TranslatedString, WithdrawUriResult @@ -68,17 +69,17 @@ export function QrCodeSection({ onAborted(); } else { switch (resp.case) { - case "previously-confirmed": return notify({ + case HttpStatusCode.Conflict: return notify({ type: "error", title: i18n.str`The reserve operation has been confirmed previously and can't be aborted` }) - case "invalid-id": return notify({ + case HttpStatusCode.BadRequest: return notify({ type: "error", title: i18n.str`The operation id is invalid.`, description: resp.detail.hint as TranslatedString, debug: resp.detail, }) - case "not-found": return notify({ + case HttpStatusCode.NotFound: return notify({ type: "error", title: i18n.str`The operation was not found.`, description: resp.detail.hint as TranslatedString, diff --git a/packages/demobank-ui/src/pages/RegistrationPage.tsx b/packages/demobank-ui/src/pages/RegistrationPage.tsx index d20c402ac..89bfbcb35 100644 --- a/packages/demobank-ui/src/pages/RegistrationPage.tsx +++ b/packages/demobank-ui/src/pages/RegistrationPage.tsx @@ -13,7 +13,7 @@ You should have received a copy of the GNU General Public License along with GNU Taler; see the file COPYING. If not, see */ -import { AccessToken, Logger, TranslatedString } from "@gnu-taler/taler-util"; +import { AccessToken, HttpStatusCode, Logger, TalerErrorCode, TranslatedString } from "@gnu-taler/taler-util"; import { LocalNotificationBanner, ShowInputErrorLabel, @@ -101,43 +101,43 @@ function RegistrationForm({ onComplete, onCancel }: { onComplete: () => void, on const creationResponse = await api.createAccount("" as AccessToken, { name, username, password }); if (creationResponse.type === "fail") { switch (creationResponse.case) { - case "invalid-phone-or-email": return notify({ + case HttpStatusCode.BadRequest: return notify({ type: "error", title: i18n.str`Server replied with invalid phone or email.`, description: creationResponse.detail.hint as TranslatedString, debug: creationResponse.detail, }) - case "insufficient-funds": return notify({ + case TalerErrorCode.BANK_UNALLOWED_DEBIT: return notify({ type: "error", title: i18n.str`Registration is disabled because the bank ran out of bonus credit.`, description: creationResponse.detail.hint as TranslatedString, debug: creationResponse.detail, }) - case "unauthorized": return notify({ + case HttpStatusCode.Unauthorized: return notify({ type: "error", title: i18n.str`No enough permission to create that account.`, description: creationResponse.detail.hint as TranslatedString, debug: creationResponse.detail, }) - case "payto-already-exists": return notify({ + case TalerErrorCode.BANK_REGISTER_PAYTO_URI_REUSE: return notify({ type: "error", title: i18n.str`That account id is already taken.`, description: creationResponse.detail.hint as TranslatedString, debug: creationResponse.detail, }) - case "username-already-exists": return notify({ + case TalerErrorCode.BANK_REGISTER_USERNAME_REUSE: return notify({ type: "error", title: i18n.str`That username is already taken.`, description: creationResponse.detail.hint as TranslatedString, debug: creationResponse.detail, }) - case "username-reserved": return notify({ + case TalerErrorCode.BANK_RESERVED_USERNAME_CONFLICT: return notify({ type: "error", title: i18n.str`That username can't be used because is reserved.`, description: creationResponse.detail.hint as TranslatedString, debug: creationResponse.detail, }) - case "user-cant-set-debt": return notify({ + case TalerErrorCode.BANK_NON_ADMIN_PATCH_DEBT_LIMIT: return notify({ type: "error", title: i18n.str`Only admin is allow to set debt limit.`, description: creationResponse.detail.hint as TranslatedString, diff --git a/packages/demobank-ui/src/pages/WalletWithdrawForm.tsx b/packages/demobank-ui/src/pages/WalletWithdrawForm.tsx index 5eef95f1e..bee36e7ad 100644 --- a/packages/demobank-ui/src/pages/WalletWithdrawForm.tsx +++ b/packages/demobank-ui/src/pages/WalletWithdrawForm.tsx @@ -17,6 +17,7 @@ import { AmountJson, Amounts, + HttpStatusCode, Logger, TranslatedString, parseWithdrawUri @@ -103,7 +104,7 @@ function OldWithdrawalForm({ goToConfirmOperation, limit, onCancel, focus }: { } } else { switch (resp.case) { - case "insufficient-funds": { + case HttpStatusCode.Conflict: { notify({ type: "error", title: i18n.str`The operation was rejected due to insufficient funds`, @@ -112,7 +113,7 @@ function OldWithdrawalForm({ goToConfirmOperation, limit, onCancel, focus }: { }) break; } - case "unauthorized": { + case HttpStatusCode.Unauthorized: { notify({ type: "error", title: i18n.str`The operation was rejected due to insufficient funds`, @@ -121,7 +122,7 @@ function OldWithdrawalForm({ goToConfirmOperation, limit, onCancel, focus }: { }) break; } - case "account-not-found": { + case HttpStatusCode.NotFound: { notify({ type: "error", title: i18n.str`Account not found`, diff --git a/packages/demobank-ui/src/pages/WireTransfer.tsx b/packages/demobank-ui/src/pages/WireTransfer.tsx index 7648df482..d6133b504 100644 --- a/packages/demobank-ui/src/pages/WireTransfer.tsx +++ b/packages/demobank-ui/src/pages/WireTransfer.tsx @@ -1,4 +1,4 @@ -import { Amounts, TalerError } from "@gnu-taler/taler-util"; +import { Amounts, HttpStatusCode, TalerError } from "@gnu-taler/taler-util"; import { Loading, notifyInfo, useTranslationContext } from "@gnu-taler/web-util/browser"; import { Fragment, VNode, h } from "preact"; import { ErrorLoadingWithDebug } from "../components/ErrorLoadingWithDebug.js"; @@ -22,8 +22,8 @@ export function WireTransfer({ toAccount, onRegister, onCancel, onSuccess }: { o } if (result.type === "fail") { switch (result.case) { - case "unauthorized": return - case "not-found": return + case HttpStatusCode.Unauthorized: return + case HttpStatusCode.NotFound: return default: assertUnreachable(result) } } diff --git a/packages/demobank-ui/src/pages/WithdrawalConfirmationQuestion.tsx b/packages/demobank-ui/src/pages/WithdrawalConfirmationQuestion.tsx index 43b88ccd8..ed1db854a 100644 --- a/packages/demobank-ui/src/pages/WithdrawalConfirmationQuestion.tsx +++ b/packages/demobank-ui/src/pages/WithdrawalConfirmationQuestion.tsx @@ -16,11 +16,13 @@ import { AmountJson, + HttpStatusCode, Logger, PaytoUri, PaytoUriIBAN, PaytoUriTalerBank, TalerError, + TalerErrorCode, TranslatedString, WithdrawUriResult } from "@gnu-taler/taler-util"; @@ -81,8 +83,8 @@ export function WithdrawalConfirmationQuestion({ } if (withdrawalInfo.type === "fail") { switch (withdrawalInfo.case) { - case "not-found": return - case "invalid-id": return + case HttpStatusCode.NotFound: return + case HttpStatusCode.BadRequest: return default: assertUnreachable(withdrawalInfo) } } @@ -121,31 +123,31 @@ export function WithdrawalConfirmationQuestion({ } } else { switch (resp.case) { - case "previously-aborted": return notify({ + case TalerErrorCode.BANK_CONFIRM_ABORT_CONFLICT: return notify({ type: "error", title: i18n.str`The withdrawal has been aborted previously and can't be confirmed`, description: resp.detail.hint as TranslatedString, debug: resp.detail, }); - case "no-exchange-or-reserve-selected": return notify({ + case TalerErrorCode.BANK_CONFIRM_INCOMPLETE: return notify({ type: "error", title: i18n.str`The withdraw operation cannot be confirmed because no exchange and reserve public key selection happened before`, description: resp.detail.hint as TranslatedString, debug: resp.detail, }); - case "invalid-id": return notify({ + case HttpStatusCode.BadRequest: return notify({ type: "error", title: i18n.str`The operation id is invalid.`, description: resp.detail.hint as TranslatedString, debug: resp.detail, }) - case "not-found": return notify({ + case HttpStatusCode.NotFound: return notify({ type: "error", title: i18n.str`The operation was not found.`, description: resp.detail.hint as TranslatedString, debug: resp.detail, }) - case "insufficient-funds": return notify({ + case TalerErrorCode.BANK_UNALLOWED_DEBIT: return notify({ type: "error", title: i18n.str`Your balance is not enough for the operation.`, description: resp.detail.hint as TranslatedString, @@ -167,17 +169,17 @@ export function WithdrawalConfirmationQuestion({ onAborted(); } else { switch (resp.case) { - case "previously-confirmed": return notify({ + case HttpStatusCode.Conflict: return notify({ type: "error", title: i18n.str`The reserve operation has been confirmed previously and can't be aborted` }); - case "invalid-id": return notify({ + case HttpStatusCode.BadRequest: return notify({ type: "error", title: i18n.str`The operation id is invalid.`, description: resp.detail.hint as TranslatedString, debug: resp.detail, }) - case "not-found": return notify({ + case HttpStatusCode.NotFound: return notify({ type: "error", title: i18n.str`The operation was not found.`, description: resp.detail.hint as TranslatedString, diff --git a/packages/demobank-ui/src/pages/WithdrawalQRCode.tsx b/packages/demobank-ui/src/pages/WithdrawalQRCode.tsx index c5a558ab8..f05f183d4 100644 --- a/packages/demobank-ui/src/pages/WithdrawalQRCode.tsx +++ b/packages/demobank-ui/src/pages/WithdrawalQRCode.tsx @@ -16,6 +16,7 @@ import { Amounts, + HttpStatusCode, Logger, TalerError, WithdrawUriResult, @@ -55,8 +56,8 @@ export function WithdrawalQRCode({ } if (result.type === "fail") { switch (result.case) { - case "invalid-id": - case "not-found": return + case HttpStatusCode.BadRequest: + case HttpStatusCode.NotFound: return default: assertUnreachable(result) } } diff --git a/packages/demobank-ui/src/pages/account/ShowAccountDetails.tsx b/packages/demobank-ui/src/pages/account/ShowAccountDetails.tsx index 4b66c0d8d..98fb72283 100644 --- a/packages/demobank-ui/src/pages/account/ShowAccountDetails.tsx +++ b/packages/demobank-ui/src/pages/account/ShowAccountDetails.tsx @@ -1,4 +1,4 @@ -import { TalerCorebankApi, TalerError, TranslatedString } from "@gnu-taler/taler-util"; +import { HttpStatusCode, TalerCorebankApi, TalerError, TalerErrorCode, TranslatedString } from "@gnu-taler/taler-util"; import { Loading, LocalNotificationBanner, notifyInfo, useLocalNotification, useTranslationContext } from "@gnu-taler/web-util/browser"; import { Fragment, VNode, h } from "preact"; import { useState } from "preact/hooks"; @@ -40,8 +40,8 @@ export function ShowAccountDetails({ } if (result.type === "fail") { switch (result.case) { - case "not-found": return - case "unauthorized": return + case HttpStatusCode.Unauthorized: + case HttpStatusCode.NotFound: return default: assertUnreachable(result) } } @@ -52,6 +52,7 @@ export function ShowAccountDetails({ const resp = await api.updateAccount({ token: creds.token, username: account, + }, submitAccount); if (resp.type === "ok") { @@ -59,39 +60,39 @@ export function ShowAccountDetails({ onUpdateSuccess(); } else { switch (resp.case) { - case "unauthorized": return notify({ + case HttpStatusCode.Unauthorized: return notify({ type: "error", title: i18n.str`The rights to change the account are not sufficient`, description: resp.detail.hint as TranslatedString, debug: resp.detail, }) - case "not-found": return notify({ + case HttpStatusCode.NotFound: return notify({ type: "error", title: i18n.str`The username was not found`, description: resp.detail.hint as TranslatedString, debug: resp.detail, }) - case "user-cant-change-name": return notify({ + case TalerErrorCode.BANK_NON_ADMIN_PATCH_LEGAL_NAME: return notify({ type: "error", title: i18n.str`You can't change the legal name, please contact the your account administrator.`, description: resp.detail.hint as TranslatedString, debug: resp.detail, }) - case "user-cant-change-debt": return notify({ + case TalerErrorCode.BANK_NON_ADMIN_PATCH_DEBT_LIMIT: return notify({ type: "error", title: i18n.str`You can't change the debt limit, please contact the your account administrator.`, description: resp.detail.hint as TranslatedString, debug: resp.detail, }) - case "user-cant-change-cashout": return notify({ + case TalerErrorCode.BANK_NON_ADMIN_PATCH_CASHOUT: return notify({ type: "error", title: i18n.str`You can't change the cashout address, please contact the your account administrator.`, description: resp.detail.hint as TranslatedString, debug: resp.detail, }) - case "missing-contact-data": return notify({ + case TalerErrorCode.BANK_NON_ADMIN_PATCH_CONTACT: return notify({ type: "error", - title: i18n.str`You need contact data to enable 2FA.`, + title: i18n.str`You can't change the contact data, please contact the your account administrator.`, description: resp.detail.hint as TranslatedString, debug: resp.detail, }) diff --git a/packages/demobank-ui/src/pages/account/UpdateAccountPassword.tsx b/packages/demobank-ui/src/pages/account/UpdateAccountPassword.tsx index ece1f63e7..95c425dc7 100644 --- a/packages/demobank-ui/src/pages/account/UpdateAccountPassword.tsx +++ b/packages/demobank-ui/src/pages/account/UpdateAccountPassword.tsx @@ -9,6 +9,7 @@ import { doAutoFocus } from "../PaytoWireTransferForm.js"; import { ProfileNavigation } from "../ProfileNavigation.js"; import { assertUnreachable } from "../WithdrawalOperationPage.js"; import { LocalNotificationBanner } from "@gnu-taler/web-util/browser"; +import { HttpStatusCode, TalerErrorCode } from "@gnu-taler/taler-util"; export function UpdateAccountPassword({ account: accountName, @@ -57,19 +58,19 @@ export function UpdateAccountPassword({ onUpdateSuccess(); } else { switch (resp.case) { - case "unauthorized": return notify({ + case HttpStatusCode.Unauthorized: return notify({ type: "error", title: i18n.str`Not authorized to change the password, maybe the session is invalid.` }) - case "not-found": return notify({ + case HttpStatusCode.NotFound: return notify({ type: "error", title: i18n.str`Account not found` }) - case "user-require-old-password": return notify({ + case TalerErrorCode.BANK_NON_ADMIN_PATCH_MISSING_OLD_PASSWORD: return notify({ type: "error", title: i18n.str`Old password need to be provided in order to change new one. If you don't have it contact your account administrator.` }) - case "wrong-old-password": return notify({ + case TalerErrorCode.BANK_PATCH_BAD_OLD_PASSWORD: return notify({ type: "error", title: i18n.str`Your current password doesn't match, can't change to a new password.` }) diff --git a/packages/demobank-ui/src/pages/admin/AccountForm.tsx b/packages/demobank-ui/src/pages/admin/AccountForm.tsx index c64431918..859c04396 100644 --- a/packages/demobank-ui/src/pages/admin/AccountForm.tsx +++ b/packages/demobank-ui/src/pages/admin/AccountForm.tsx @@ -66,7 +66,7 @@ export function AccountForm({ const defaultValue: AccountFormData = { - debit_threshold: Amounts.stringifyValue(template?.debit_threshold ??config.default_debit_threshold), + debit_threshold: Amounts.stringifyValue(template?.debit_threshold ?? config.default_debit_threshold), isExchange: template?.is_taler_exchange, isPublic: template?.is_public, name: template?.name ?? "", @@ -418,13 +418,13 @@ export function AccountForm({ Is public -

diff --git a/packages/demobank-ui/src/pages/admin/AccountList.tsx b/packages/demobank-ui/src/pages/admin/AccountList.tsx index 42d3d7fca..dd4586697 100644 --- a/packages/demobank-ui/src/pages/admin/AccountList.tsx +++ b/packages/demobank-ui/src/pages/admin/AccountList.tsx @@ -1,4 +1,4 @@ -import { Amounts, TalerError } from "@gnu-taler/taler-util"; +import { Amounts, HttpStatusCode, TalerError } from "@gnu-taler/taler-util"; import { Loading, useTranslationContext } from "@gnu-taler/web-util/browser"; import { Fragment, VNode, h } from "preact"; import { ErrorLoadingWithDebug } from "../../components/ErrorLoadingWithDebug.js"; @@ -29,7 +29,7 @@ export function AccountList({ onRemoveAccount, onShowAccountDetails, onUpdateAcc } if (result.data.type === "fail") { switch (result.data.case) { - case "unauthorized": return + case HttpStatusCode.Unauthorized: return default: assertUnreachable(result.data.case) } } diff --git a/packages/demobank-ui/src/pages/admin/CreateNewAccount.tsx b/packages/demobank-ui/src/pages/admin/CreateNewAccount.tsx index 3d196973e..d6b7d5b1e 100644 --- a/packages/demobank-ui/src/pages/admin/CreateNewAccount.tsx +++ b/packages/demobank-ui/src/pages/admin/CreateNewAccount.tsx @@ -1,4 +1,4 @@ -import { TalerCorebankApi, TranslatedString } from "@gnu-taler/taler-util"; +import { HttpStatusCode, TalerCorebankApi, TalerErrorCode, TranslatedString } from "@gnu-taler/taler-util"; import { Attention, LocalNotificationBanner, notifyInfo, useLocalNotification, useTranslationContext } from "@gnu-taler/web-util/browser"; import { Fragment, VNode, h } from "preact"; import { useState } from "preact/hooks"; @@ -48,43 +48,43 @@ export function CreateNewAccount({ onCreateSuccess(); } else { switch (resp.case) { - case "invalid-phone-or-email": return notify({ + case HttpStatusCode.BadRequest: return notify({ type: "error", - title: i18n.str`Server replied with invalid phone or email`, + title: i18n.str`Server replied that phone or email is invalid`, description: resp.detail.hint as TranslatedString, debug: resp.detail, }) - case "unauthorized": return notify({ + case HttpStatusCode.Unauthorized: return notify({ type: "error", title: i18n.str`The rights to perform the operation are not sufficient`, description: resp.detail.hint as TranslatedString, debug: resp.detail, }) - case "username-already-exists": return notify({ + case TalerErrorCode.BANK_REGISTER_USERNAME_REUSE: return notify({ type: "error", title: i18n.str`Account username is already taken`, description: resp.detail.hint as TranslatedString, debug: resp.detail, }) - case "payto-already-exists": return notify({ + case TalerErrorCode.BANK_REGISTER_PAYTO_URI_REUSE: return notify({ type: "error", title: i18n.str`Account id is already taken`, description: resp.detail.hint as TranslatedString, debug: resp.detail, }) - case "insufficient-funds": return notify({ + case TalerErrorCode.BANK_UNALLOWED_DEBIT: return notify({ type: "error", title: i18n.str`Bank ran out of bonus credit.`, description: resp.detail.hint as TranslatedString, debug: resp.detail, }) - case "username-reserved": return notify({ + case TalerErrorCode.BANK_RESERVED_USERNAME_CONFLICT: return notify({ type: "error", title: i18n.str`Account username can't be used because is reserved`, description: resp.detail.hint as TranslatedString, debug: resp.detail, }) - case "user-cant-set-debt": return notify({ + case TalerErrorCode.BANK_NON_ADMIN_PATCH_DEBT_LIMIT: return notify({ type: "error", title: i18n.str`Only admin is allow to set debt limit.`, description: resp.detail.hint as TranslatedString, diff --git a/packages/demobank-ui/src/pages/admin/RemoveAccount.tsx b/packages/demobank-ui/src/pages/admin/RemoveAccount.tsx index 0d294a3a6..d47f48dd9 100644 --- a/packages/demobank-ui/src/pages/admin/RemoveAccount.tsx +++ b/packages/demobank-ui/src/pages/admin/RemoveAccount.tsx @@ -1,4 +1,4 @@ -import { Amounts, TalerError, TranslatedString } from "@gnu-taler/taler-util"; +import { Amounts, HttpStatusCode, TalerError, TalerErrorCode, TranslatedString } from "@gnu-taler/taler-util"; import { Attention, Loading, LocalNotificationBanner, ShowInputErrorLabel, notifyInfo, useLocalNotification, useTranslationContext } from "@gnu-taler/web-util/browser"; import { Fragment, VNode, h } from "preact"; import { useState } from "preact/hooks"; @@ -39,8 +39,8 @@ export function RemoveAccount({ } if (result.type === "fail") { switch (result.case) { - case "unauthorized": return - case "not-found": return + case HttpStatusCode.Unauthorized: return + case HttpStatusCode.NotFound: return default: assertUnreachable(result) } } @@ -65,25 +65,25 @@ export function RemoveAccount({ onUpdateSuccess(); } else { switch (resp.case) { - case "unauthorized": return notify({ + case HttpStatusCode.Unauthorized: return notify({ type: "error", title: i18n.str`No enough permission to delete the account.`, description: resp.detail.hint as TranslatedString, debug: resp.detail, }) - case "not-found": return notify({ + case HttpStatusCode.NotFound: return notify({ type: "error", title: i18n.str`The username was not found.`, description: resp.detail.hint as TranslatedString, debug: resp.detail, }) - case "username-reserved": return notify({ + case TalerErrorCode.BANK_RESERVED_USERNAME_CONFLICT: return notify({ type: "error", title: i18n.str`Can't delete a reserved username.`, description: resp.detail.hint as TranslatedString, debug: resp.detail, }) - case "balance-not-zero": return notify({ + case TalerErrorCode.BANK_ACCOUNT_BALANCE_NOT_ZERO: return notify({ type: "error", title: i18n.str`Can't delete an account with balance different than zero.`, description: resp.detail.hint as TranslatedString, diff --git a/packages/demobank-ui/src/pages/business/CreateCashout.tsx b/packages/demobank-ui/src/pages/business/CreateCashout.tsx index 92c80ea38..c3921cbd0 100644 --- a/packages/demobank-ui/src/pages/business/CreateCashout.tsx +++ b/packages/demobank-ui/src/pages/business/CreateCashout.tsx @@ -15,7 +15,9 @@ */ import { Amounts, + HttpStatusCode, TalerError, + TalerErrorCode, TranslatedString, encodeCrock, getRandomBytes, @@ -99,8 +101,8 @@ export function CreateCashout({ } if (resultAccount.type === "fail") { switch (resultAccount.case) { - case "unauthorized": return - case "not-found": return + case HttpStatusCode.Unauthorized: return + case HttpStatusCode.NotFound: return default: assertUnreachable(resultAccount) } } @@ -181,8 +183,9 @@ export function CreateCashout({ async function createCashout() { const request_uid = encodeCrock(getRandomBytes(32)) await handleError(async () => { - if (!creds || !form.subject || !form.channel) return; + const validChannel = config.supported_tan_channels.length === 0 || form.channel + if (!creds || !form.subject || !validChannel) return; const resp = await api.createCashout(creds, { request_uid, amount_credit: Amounts.stringify(calc.credit), @@ -194,47 +197,55 @@ export function CreateCashout({ notifyInfo(i18n.str`Cashout created`) } else { switch (resp.case) { - case "account-not-found": return notify({ + case HttpStatusCode.NotFound: return notify({ type: "error", title: i18n.str`Account not found`, description: resp.detail.hint as TranslatedString, debug: resp.detail, }); - case "request-already-used": return notify({ + case TalerErrorCode.BANK_TRANSFER_REQUEST_UID_REUSED: return notify({ type: "error", title: i18n.str`Duplicated request detected, check if the operation succeded or try again.`, description: resp.detail.hint as TranslatedString, debug: resp.detail, }); - case "incorrect-exchange-rate": return notify({ + case TalerErrorCode.BANK_BAD_CONVERSION: return notify({ type: "error", title: i18n.str`The exchange rate was incorrectly applied`, description: resp.detail.hint as TranslatedString, debug: resp.detail, }); - case "no-enough-balance": return notify({ + case TalerErrorCode.BANK_UNALLOWED_DEBIT: return notify({ type: "error", title: i18n.str`The account does not have sufficient funds`, description: resp.detail.hint as TranslatedString, debug: resp.detail, }); - case "cashout-not-supported": return notify({ + case HttpStatusCode.NotImplemented: return notify({ type: "error", title: i18n.str`Cashouts are not supported`, description: resp.detail.hint as TranslatedString, debug: resp.detail, }); - case "no-cashout-uri": return notify({ + case TalerErrorCode.BANK_CONFIRM_INCOMPLETE: return notify({ type: "error", title: i18n.str`Missing cashout URI in the profile`, description: resp.detail.hint as TranslatedString, debug: resp.detail, }); + case TalerErrorCode.BANK_TAN_CHANNEL_SCRIPT_FAILED: return notify({ + type: "error", + title: i18n.str`Sending the confirmation message failed, retry later or contact the administrator.`, + description: resp.detail.hint as TranslatedString, + debug: resp.detail, + }); } assertUnreachable(resp) } }) } + const cashoutDisabled = config.supported_tan_channels.length < 1 || !resultAccount.body.cashout_payto_uri + console.log("disab", cashoutDisabled) const cashoutAccount = !resultAccount.body.cashout_payto_uri ? undefined : parsePaytoUri(resultAccount.body.cashout_payto_uri); const cashoutAccountName = !cashoutAccount ? undefined : cashoutAccount.targetPath @@ -317,7 +328,7 @@ export function CreateCashout({ class="block w-full rounded-md disabled:bg-gray-200 border-0 py-1.5 text-gray-900 shadow-sm ring-1 ring-inset ring-gray-300 data-[error=true]:ring-red-500 placeholder:text-gray-400 focus:ring-2 focus:ring-inset focus:ring-indigo-600 sm:text-sm sm:leading-6" name="subject" id="subject" - disabled={!resultAccount.body.cashout_payto_uri} + disabled={cashoutDisabled} data-error={!!errors?.subject && form.subject !== undefined} value={form.subject ?? ""} onChange={(e) => { @@ -360,7 +371,7 @@ export function CreateCashout({ left currency={limit.currency} value={trimmedAmountStr} - onChange={!resultAccount.body.cashout_payto_uri ? undefined : (value) => { + onChange={cashoutDisabled ? undefined : (value) => { form.amount = value; updateForm(structuredClone(form)); }} @@ -424,7 +435,15 @@ export function CreateCashout({ )} {/* channel */} - {config.supported_tan_channels.length === 0 ? undefined : + {config.supported_tan_channels.length === 0 ? +

+ + + Before doing a cashout the server need to provide an second channel to confirm the operation + + +
+ :
-
); } diff --git a/packages/demobank-ui/src/pages/business/ShowCashoutDetails.tsx b/packages/demobank-ui/src/pages/business/ShowCashoutDetails.tsx index 3ef835574..5d8db5aee 100644 --- a/packages/demobank-ui/src/pages/business/ShowCashoutDetails.tsx +++ b/packages/demobank-ui/src/pages/business/ShowCashoutDetails.tsx @@ -15,7 +15,9 @@ */ import { Amounts, + HttpStatusCode, TalerError, + TalerErrorCode, TranslatedString } from "@gnu-taler/taler-util"; import { @@ -73,9 +75,9 @@ export function ShowCashoutDetails({ } if (result.type === "fail") { switch (result.case) { - case "not-found": return + case HttpStatusCode.NotFound: return - case "cashout-not-supported": return + case HttpStatusCode.NotImplemented: return default: assertUnreachable(result) } @@ -93,6 +95,7 @@ export function ShowCashoutDetails({ }); const isPending = String(result.body.status).toUpperCase() === "PENDING"; const { fiat_currency_specification, regional_currency_specification } = info.body + // won't implement in retry in old API 3:0:3 since request_uid is missing async function doAbortCashout() { if (!creds) return; await handleError(async () => { @@ -101,19 +104,19 @@ export function ShowCashoutDetails({ onCancel(); } else { switch (resp.case) { - case "not-found": return notify({ + case HttpStatusCode.NotFound: return notify({ type: "error", title: i18n.str`Cashout not found. It may be also mean that it was already aborted.`, description: resp.detail.hint as TranslatedString, debug: resp.detail, }) - case "already-confirmed": return notify({ + case HttpStatusCode.Conflict: return notify({ type: "error", title: i18n.str`Cashout was already confimed.`, description: resp.detail.hint as TranslatedString, debug: resp.detail, }) - case "cashout-not-supported": return notify({ + case HttpStatusCode.NotImplemented: return notify({ type: "error", title: i18n.str`Cashout operation is not supported.`, description: resp.detail.hint as TranslatedString, @@ -136,49 +139,49 @@ export function ShowCashoutDetails({ mutate(() => true)//clean cashout state } else { switch (resp.case) { - case "not-found": return notify({ + case HttpStatusCode.NotFound: return notify({ type: "error", title: i18n.str`Cashout not found. It may be also mean that it was already aborted.`, description: resp.detail.hint as TranslatedString, debug: resp.detail, }) - case "no-enough-balance": return notify({ + case TalerErrorCode.BANK_UNALLOWED_DEBIT: return notify({ type: "error", title: i18n.str`The account does not have sufficient funds`, description: resp.detail.hint as TranslatedString, debug: resp.detail, }); - case "incorrect-exchange-rate": return notify({ + case TalerErrorCode.BANK_BAD_CONVERSION: return notify({ type: "error", title: i18n.str`The exchange rate was incorrectly applied`, description: resp.detail.hint as TranslatedString, debug: resp.detail, }); - case "already-aborted": return notify({ + case TalerErrorCode.BANK_CONFIRM_ABORT_CONFLICT: return notify({ type: "error", title: i18n.str`The cashout operation is already aborted.`, description: resp.detail.hint as TranslatedString, debug: resp.detail, }); - case "no-cashout-payto": return notify({ + case TalerErrorCode.BANK_CONFIRM_INCOMPLETE: return notify({ type: "error", title: i18n.str`Missing destination account.`, description: resp.detail.hint as TranslatedString, debug: resp.detail, }) - case "too-many-attempts": return notify({ + case HttpStatusCode.TooManyRequests: return notify({ type: "error", title: i18n.str`Too many failed attempts.`, description: resp.detail.hint as TranslatedString, debug: resp.detail, }) - case "cashout-not-supported": return notify({ + case HttpStatusCode.NotImplemented: return notify({ type: "error", title: i18n.str`Cashout operation is not supported.`, description: resp.detail.hint as TranslatedString, debug: resp.detail, }) - case "invalid-code": return notify({ + case TalerErrorCode.BANK_TAN_CHALLENGE_FAILED: return notify({ type: "error", title: i18n.str`The code for this cashout is invalid.`, description: resp.detail.hint as TranslatedString, diff --git a/packages/taler-util/src/http-client/bank-core.ts b/packages/taler-util/src/http-client/bank-core.ts index b7e0292bd..dd0948250 100644 --- a/packages/taler-util/src/http-client/bank-core.ts +++ b/packages/taler-util/src/http-client/bank-core.ts @@ -20,7 +20,9 @@ import { TalerErrorCode, codecForChallenge, codecForTalerErrorDetail, - codecForTanTransmission + codecForTanTransmission, + opKnownHttpFailure, + opKnownTalerFailure } from "@gnu-taler/taler-util"; import { HttpRequestLibrary, @@ -45,7 +47,7 @@ export type TalerCoreBankErrorsByMethod = | OperationOk @@ -31,6 +31,14 @@ export function opFixedSuccess(body: T): OperationOk { export function opEmptySuccess(): OperationOk { return { type: "ok" as const, body: void 0 } } +export async function opKnownHttpFailure(s: T, resp: HttpResponse): Promise> { + const detail = await readTalerErrorResponse(resp) + return { type: "fail", case: s, detail } +} +export async function opKnownTalerFailure(s: T, resp: HttpResponse): Promise> { + const detail = await readTalerErrorResponse(resp) + return { type: "fail", case: s, detail } +} export async function opKnownFailure(s: T, resp: HttpResponse): Promise> { const detail = await readTalerErrorResponse(resp) return { type: "fail", case: s, detail } -- cgit v1.2.3