From 82b8e83ca635bd27954565e2c29c77e0a75b3fc0 Mon Sep 17 00:00:00 2001 From: Sebastian Date: Mon, 22 Apr 2024 12:50:25 -0300 Subject: fix #8398 --- packages/challenger-ui/src/Routing.tsx | 1 + packages/challenger-ui/src/app.tsx | 25 +++- packages/challenger-ui/src/hooks/challenge.ts | 2 +- .../challenger-ui/src/pages/AnswerChallenge.tsx | 44 ++++--- packages/challenger-ui/src/pages/AskChallenge.tsx | 135 ++++++++++++--------- packages/challenger-ui/src/pages/Setup.tsx | 2 +- packages/taler-util/src/http-client/challenger.ts | 18 ++- packages/web-util/src/context/activity.ts | 2 +- packages/web-util/src/context/challenger-api.ts | 14 ++- 9 files changed, 157 insertions(+), 86 deletions(-) diff --git a/packages/challenger-ui/src/Routing.tsx b/packages/challenger-ui/src/Routing.tsx index f1f4d82d2..6711e4cae 100644 --- a/packages/challenger-ui/src/Routing.tsx +++ b/packages/challenger-ui/src/Routing.tsx @@ -234,6 +234,7 @@ function PublicRounting(): VNode { > { navigateTo( publicPages.completed.url({ diff --git a/packages/challenger-ui/src/app.tsx b/packages/challenger-ui/src/app.tsx index d85893c07..2b5c5c815 100644 --- a/packages/challenger-ui/src/app.tsx +++ b/packages/challenger-ui/src/app.tsx @@ -15,6 +15,9 @@ */ import { + CacheEvictor, + ChallengerCacheEviction, + assertUnreachable, canonicalizeBaseUrl, getGlobalLogLevel, setGlobalLogLevelFromString, @@ -33,13 +36,28 @@ import { Routing } from "./Routing.js"; // import { BrowserHashNavigationProvider } from "./context/navigation.js"; import { SettingsProvider } from "./context/settings.js"; // import { TalerWalletIntegrationBrowserProvider } from "./context/wallet-integration.js"; -import { h } from "preact"; +import { VNode, h } from "preact"; import { strings } from "./i18n/strings.js"; import { ChallengerUiSettings, fetchSettings } from "./settings.js"; import { Frame } from "./pages/Frame.js"; +import { revalidateChallengeSession } from "./hooks/challenge.js"; const WITH_LOCAL_STORAGE_CACHE = false; -export function App() { +const evictBankSwrCache: CacheEvictor = { + async notifySuccess(op) { + switch (op) { + case ChallengerCacheEviction.CREATE_CHALLENGE: { + await Promise.all([revalidateChallengeSession()]); + return; + } + default: { + assertUnreachable(op); + } + } + }, +}; + +export function App(): VNode { const [settings, setSettings] = useState(); useEffect(() => { fetchSettings(setSettings); @@ -60,6 +78,9 @@ export function App() { void; + routeAsk: RouteDefinition<{ nonce: string }>; }; -export function AnswerChallenge({ nonce, onComplete }: Props): VNode { +export function AnswerChallenge({ nonce, onComplete, routeAsk }: Props): VNode { const { lib } = useChallengerApiContext(); const { i18n } = useTranslationContext(); const { state, accepted, completed } = useSessionState(); @@ -54,7 +56,9 @@ export function AnswerChallenge({ nonce, onComplete }: Props): VNode { ? undefined : !state.lastStatus ? undefined - : ((state.lastStatus.last_address as any)["email"] as string); + : !state.lastStatus.last_address + ? undefined + : state.lastStatus.last_address["email"]; const onSendAgain = !state || lastEmail === undefined @@ -62,7 +66,7 @@ export function AnswerChallenge({ nonce, onComplete }: Props): VNode { : withErrorHandler( async () => { if (!lastEmail) return; - return await lib.bank.challenge(nonce, { email: lastEmail }); + return await lib.challenger.challenge(nonce, { email: lastEmail }); }, (ok) => { if ("redirectURL" in ok.body) { @@ -93,11 +97,11 @@ export function AnswerChallenge({ nonce, onComplete }: Props): VNode { ); const onCheck = - lastTryError && lastTryError.exhausted + errors !== undefined || (lastTryError && lastTryError.exhausted) ? undefined : withErrorHandler( async () => { - return lib.bank.solve(nonce, { pin: pin! }); + return lib.challenger.solve(nonce, { pin: pin! }); }, (ok) => { completed(ok.body.redirectURL as URL); @@ -141,7 +145,7 @@ export function AnswerChallenge({ nonce, onComplete }: Props): VNode {

- Please enter the TAN you received to authenticate. + Enter the TAN you received to authenticate.

@@ -220,15 +224,25 @@ export function AnswerChallenge({ nonce, onComplete }: Props): VNode { Check

-
- +
+ +
+ +
diff --git a/packages/challenger-ui/src/pages/AskChallenge.tsx b/packages/challenger-ui/src/pages/AskChallenge.tsx index 76fe6f00a..aaca51db7 100644 --- a/packages/challenger-ui/src/pages/AskChallenge.tsx +++ b/packages/challenger-ui/src/pages/AskChallenge.tsx @@ -49,27 +49,26 @@ export function AskChallenge({ const prevEmail = !status || !status.last_address ? undefined : status.last_address["email"]; const regexEmail = - !status || !status.restrictions - ? undefined - : status.restrictions["email"]; + !status || !status.restrictions ? undefined : status.restrictions["email"]; const { lib } = useChallengerApiContext(); const { i18n } = useTranslationContext(); const [notification, withErrorHandler] = useLocalNotificationHandler(); - const [email, setEmail] = useState(prevEmail); + const [email, setEmail] = useState(); const [repeat, setRepeat] = useState(); + const regexTest = + regexEmail && regexEmail.regex ? new RegExp(regexEmail.regex) : EMAIL_REGEX; + const regexHint = + regexEmail && regexEmail.hint ? regexEmail.hint : i18n.str`invalid email`; + const errors = undefinedIfEmpty({ email: !email ? i18n.str`required` - : regexEmail && regexEmail.regex - ? !new RegExp(regexEmail.regex).test(email) - ? regexEmail.hint - ? regexEmail.hint - : `invalid` - : undefined - : !EMAIL_REGEX.test(email) - ? i18n.str`invalid email` + : !regexTest.test(email) + ? regexHint + : prevEmail !== undefined && email === prevEmail + ? i18n.str`email should be different` : undefined, repeat: !repeat ? i18n.str`required` @@ -82,7 +81,7 @@ export function AskChallenge({ ? undefined : withErrorHandler( async () => { - return lib.bank.challenge(nonce, { email: email! }); + return lib.challenger.challenge(nonce, { email: email! }); }, (ok) => { if ("redirectURL" in ok.body) { @@ -136,11 +135,9 @@ export function AskChallenge({ - You can change the destination or{" "} - - complete the challenge here + + Complete the challenge here. - . @@ -171,8 +168,9 @@ export function AskChallenge({ onChange={(e) => { setEmail(e.currentTarget.value); }} + placeholder={prevEmail} readOnly={status.fix_address} - class="block w-full rounded-md border-0 px-3.5 py-2 text-gray-900 shadow-sm ring-1 ring-inset ring-gray-300 placeholder:text-gray-400 focus:ring-2 focus:ring-inset focus:ring-indigo-600 sm:text-sm sm:leading-6" + class="block w-full read-only:bg-slate-200 rounded-md border-0 px-3.5 py-2 text-gray-900 shadow-sm ring-1 ring-inset ring-gray-300 placeholder:text-gray-400 focus:ring-2 focus:ring-inset focus:ring-indigo-600 sm:text-sm sm:leading-6" /> -
- -
- { - setRepeat(e.currentTarget.value); - }} - autocomplete="email" - class="block w-full rounded-md border-0 px-3.5 py-2 text-gray-900 shadow-sm ring-1 ring-inset ring-gray-300 placeholder:text-gray-400 focus:ring-2 focus:ring-inset focus:ring-indigo-600 sm:text-sm sm:leading-6" - /> - + {status.fix_address ? undefined : ( +
+ +
+ { + setRepeat(e.currentTarget.value); + }} + autocomplete="email" + class="block w-full rounded-md border-0 px-3.5 py-2 text-gray-900 shadow-sm ring-1 ring-inset ring-gray-300 placeholder:text-gray-400 focus:ring-2 focus:ring-inset focus:ring-indigo-600 sm:text-sm sm:leading-6" + /> + +
-
+ )} -

- - You can change your email address another {status.changes_left}{" "} - times. - -

+ {!status.changes_left ? ( +

+ No more changes left +

+ ) : ( +

+ + You can change your email address another{" "} + {status.changes_left} times. + +

+ )}
-
- -
+ {!prevEmail ? ( +
+ +
+ ) : ( +
+ +
+ )} diff --git a/packages/challenger-ui/src/pages/Setup.tsx b/packages/challenger-ui/src/pages/Setup.tsx index 400c9b780..f431835aa 100644 --- a/packages/challenger-ui/src/pages/Setup.tsx +++ b/packages/challenger-ui/src/pages/Setup.tsx @@ -36,7 +36,7 @@ export function Setup({ clientId, onCreated }: Props): VNode { const onStart = withErrorHandler( async () => { - return lib.bank.setup(clientId, "secret-token:chal-secret" as AccessToken); + return lib.challenger.setup(clientId, "secret-token:chal-secret" as AccessToken); }, (ok) => { start({ diff --git a/packages/taler-util/src/http-client/challenger.ts b/packages/taler-util/src/http-client/challenger.ts index 8d23ed273..aa530570d 100644 --- a/packages/taler-util/src/http-client/challenger.ts +++ b/packages/taler-util/src/http-client/challenger.ts @@ -1,6 +1,7 @@ import { HttpRequestLibrary, readTalerErrorResponse } from "../http-common.js"; import { HttpStatusCode } from "../http-status-codes.js"; import { createPlatformHttpLib } from "../http.js"; +import { TalerCoreBankCacheEviction } from "../index.node.js"; import { LibtoolVersion } from "../libtool-version.js"; import { FailCasesByMethod, @@ -22,24 +23,31 @@ import { codecForChallengerTermsOfServiceResponse, codecForInvalidPinResponse, } from "./types.js"; -import { makeBearerTokenAuthHeader } from "./utils.js"; +import { CacheEvictor, makeBearerTokenAuthHeader, nullEvictor } from "./utils.js"; export type ChallengerResultByMethod = ResultByMethod; export type ChallengerErrorsByMethod = FailCasesByMethod; +export enum ChallengerCacheEviction { + CREATE_CHALLENGE, +} + /** */ export class ChallengerHttpClient { httpLib: HttpRequestLibrary; + cacheEvictor: CacheEvictor; public readonly PROTOCOL_VERSION = "1:0:0"; constructor( readonly baseUrl: string, httpClient?: HttpRequestLibrary, - ) { + cacheEvictor?: CacheEvictor, + ) { this.httpLib = httpClient ?? createPlatformHttpLib(); + this.cacheEvictor = cacheEvictor ?? nullEvictor; } isCompatible(version: string): boolean { @@ -146,8 +154,12 @@ export class ChallengerHttpClient { redirect: "manual", }); switch (resp.status) { - case HttpStatusCode.Ok: + case HttpStatusCode.Ok: { + await this.cacheEvictor.notifySuccess( + ChallengerCacheEviction.CREATE_CHALLENGE, + ); return opSuccessFromHttp(resp, codecForChallengeCreateResponse()); + } case HttpStatusCode.Found: const redirect = resp.headers.get("Location")!; return opFixedSuccess({ diff --git a/packages/web-util/src/context/activity.ts b/packages/web-util/src/context/activity.ts index 3cfd83bda..fd366cbe5 100644 --- a/packages/web-util/src/context/activity.ts +++ b/packages/web-util/src/context/activity.ts @@ -67,6 +67,6 @@ export interface BankLib { } export interface ChallengerLib { - bank: ChallengerHttpClient; + challenger: ChallengerHttpClient; } diff --git a/packages/web-util/src/context/challenger-api.ts b/packages/web-util/src/context/challenger-api.ts index 960f1db0d..8748f5f69 100644 --- a/packages/web-util/src/context/challenger-api.ts +++ b/packages/web-util/src/context/challenger-api.ts @@ -15,7 +15,9 @@ */ import { + CacheEvictor, ChallengerApi, + ChallengerCacheEviction, ChallengerHttpClient, LibtoolVersion, ObservabilityEvent, @@ -63,7 +65,9 @@ enum VersionHint { NONE, } -type Evictors = Record; +type Evictors = { + challenger?: CacheEvictor; +} type ConfigResult = | undefined @@ -174,10 +178,10 @@ function buildChallengerApiClient( }, }); - const bank = new ChallengerHttpClient(url.href, httpLib); + const challenger = new ChallengerHttpClient(url.href, httpLib, evictors.challenger); async function getRemoteConfig(): Promise { - const resp = await bank.getConfig(); + const resp = await challenger.getConfig(); if (resp.type === "fail") { throw TalerError.fromUncheckedDetail(resp.detail); } @@ -186,9 +190,9 @@ function buildChallengerApiClient( return { getRemoteConfig, - VERSION: bank.PROTOCOL_VERSION, + VERSION: challenger.PROTOCOL_VERSION, lib: { - bank, + challenger, }, onActivity: tracker.subscribe, cancelRequest: httpLib.cancelRequest, -- cgit v1.2.3