diff options
author | Florian Dold <florian@dold.me> | 2022-04-13 21:40:56 +0200 |
---|---|---|
committer | Florian Dold <florian@dold.me> | 2022-04-13 23:46:34 +0200 |
commit | 4e1fe5eb10a5db44c86becbcef66daacc408239d (patch) | |
tree | 66c619c46a2228dc00ed78b200db6d1be395125b /packages | |
parent | bd76b5d76f0e03d2d511b6cc0aae4636a2ec52c9 (diff) |
anastasis-webui: updated challenge feedback
Diffstat (limited to 'packages')
25 files changed, 361 insertions, 685 deletions
diff --git a/packages/anastasis-core/src/challenge-feedback-types.ts b/packages/anastasis-core/src/challenge-feedback-types.ts index 0770d9296..30f42288f 100644 --- a/packages/anastasis-core/src/challenge-feedback-types.ts +++ b/packages/anastasis-core/src/challenge-feedback-types.ts @@ -1,29 +1,48 @@ +/* + This file is part of GNU Anastasis + (C) 2021-2022 Anastasis SARL + + GNU Anastasis is free software; you can redistribute it and/or modify it under the + terms of the GNU Affero General Public License as published by the Free Software + Foundation; either version 3, or (at your option) any later version. + + GNU Anastasis is distributed in the hope that it will be useful, but WITHOUT ANY + WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR + A PARTICULAR PURPOSE. See the GNU Affero General Public License for more details. + + You should have received a copy of the GNU Affero General Public License along with + GNU Anastasis; see the file COPYING. If not, see <http://www.gnu.org/licenses/> + */ + +/** + * Imports. + */ import { AmountString, HttpStatusCode } from "@gnu-taler/taler-util"; export enum ChallengeFeedbackStatus { Solved = "solved", + CodeInFile = "code-in-file", + CodeSent = "code-sent", ServerFailure = "server-failure", TruthUnknown = "truth-unknown", - Redirect = "redirect", - Payment = "payment", - Pending = "pending", - Message = "message", + TalerPayment = "taler-payment", Unsupported = "unsupported", RateLimitExceeded = "rate-limit-exceeded", AuthIban = "auth-iban", + IncorrectAnswer = "incorrect-answer", } export type ChallengeFeedback = | ChallengeFeedbackSolved - | ChallengeFeedbackPending - | ChallengeFeedbackPayment + | ChallengeFeedbackCodeInFile + | ChallengeFeedbackCodeSent + | ChallengeFeedbackIncorrectAnswer + | ChallengeFeedbackTalerPaymentRequired | ChallengeFeedbackServerFailure | ChallengeFeedbackRateLimitExceeded | ChallengeFeedbackTruthUnknown - | ChallengeFeedbackRedirect - | ChallengeFeedbackMessage | ChallengeFeedbackUnsupported - | ChallengeFeedbackAuthIban; + | ChallengeFeedbackBankTransferRequired; /** * Challenge has been solved and the key share has @@ -33,13 +52,29 @@ export interface ChallengeFeedbackSolved { state: ChallengeFeedbackStatus.Solved; } +export interface ChallengeFeedbackIncorrectAnswer { + state: ChallengeFeedbackStatus.IncorrectAnswer; +} + +export interface ChallengeFeedbackCodeInFile { + state: ChallengeFeedbackStatus.CodeInFile; + filename: string; + display_hint: string; +} + +export interface ChallengeFeedbackCodeSent { + state: ChallengeFeedbackStatus.CodeSent; + display_hint: string; + address_hint: string; +} + /** * The challenge given by the server is unsupported * by the current anastasis client. */ export interface ChallengeFeedbackUnsupported { state: ChallengeFeedbackStatus.Unsupported; - http_status: HttpStatusCode; + /** * Human-readable identifier of the unsupported method. */ @@ -57,7 +92,7 @@ export interface ChallengeFeedbackRateLimitExceeded { * Instructions for performing authentication via an * IBAN bank transfer. */ -export interface ChallengeFeedbackAuthIban { +export interface ChallengeFeedbackBankTransferRequired { state: ChallengeFeedbackStatus.AuthIban; /** @@ -68,12 +103,12 @@ export interface ChallengeFeedbackAuthIban { /** * Account that should be credited. */ - credit_iban: string; + target_iban: string; /** * Creditor name. */ - business_name: string; + target_business_name: string; /** * Unstructured remittance information that should @@ -81,41 +116,7 @@ export interface ChallengeFeedbackAuthIban { */ wire_transfer_subject: string; - /** - * FIXME: This field is only present for compatibility with - * the C reducer test suite. - */ - method: "iban"; - answer_code: number; - - /** - * FIXME: This field is only present for compatibility with - * the C reducer test suite. - */ - details: { - challenge_amount: AmountString; - credit_iban: string; - business_name: string; - wire_transfer_subject: string; - }; -} - -/** - * Challenge still needs to be solved. - */ -export interface ChallengeFeedbackPending { - state: ChallengeFeedbackStatus.Pending; -} - -/** - * Human-readable response from the provider - * after the user failed to solve the challenge - * correctly. - */ -export interface ChallengeFeedbackMessage { - state: ChallengeFeedbackStatus.Message; - message: string; } /** @@ -141,21 +142,11 @@ export interface ChallengeFeedbackTruthUnknown { } /** - * The user should be asked to go to a URL - * to complete the authentication there. - */ -export interface ChallengeFeedbackRedirect { - state: ChallengeFeedbackStatus.Redirect; - http_status: number; - redirect_url: string; -} - -/** * A payment is required before the user can * even attempt to solve the challenge. */ -export interface ChallengeFeedbackPayment { - state: ChallengeFeedbackStatus.Payment; +export interface ChallengeFeedbackTalerPaymentRequired { + state: ChallengeFeedbackStatus.TalerPayment; taler_pay_uri: string; diff --git a/packages/anastasis-core/src/index.ts b/packages/anastasis-core/src/index.ts index 98aba2ce6..055f3fb62 100644 --- a/packages/anastasis-core/src/index.ts +++ b/packages/anastasis-core/src/index.ts @@ -25,6 +25,7 @@ import { } from "@gnu-taler/taler-util"; import { anastasisData } from "./anastasis-data.js"; import { + codecForChallengeInstructionMessage, EscrowConfigurationResponse, RecoveryMetaResponse, TruthUploadRequest, @@ -363,9 +364,10 @@ async function getTruthValue( case "email": case "totp": case "iban": + case "post": return authMethod.challenge; default: - throw Error("unknown auth type"); + throw Error(`unknown auth type '${authMethod.type}'`); } } @@ -947,17 +949,27 @@ async function requestTruth( const hresp = await getResponseHash(truth, solveRequest); - const resp = await fetch(url.href, { - method: "POST", - headers: { - Accept: "application/json", - "Content-Type": "application/json", - }, - body: JSON.stringify({ - truth_decryption_key: truth.truth_key, - h_response: hresp, - }), - }); + let resp: Response; + + try { + resp = await fetch(url.href, { + method: "POST", + headers: { + Accept: "application/json", + "Content-Type": "application/json", + }, + body: JSON.stringify({ + truth_decryption_key: truth.truth_key, + h_response: hresp, + }), + }); + } catch (e) { + return { + reducer_type: "error", + code: TalerErrorCode.ANASTASIS_TRUTH_CHALLENGE_FAILED, + hint: "network error", + } as ReducerStateError; + } logger.info( `got POST /truth/.../solve response from ${truth.url}, http status ${resp.status}`, @@ -1007,6 +1019,19 @@ async function requestTruth( return tryRecoverSecret(newState); } + if (resp.status === HttpStatusCode.Forbidden) { + const challengeFeedback: { [x: string]: ChallengeFeedback } = { + ...state.challenge_feedback, + [truth.uuid]: { + state: ChallengeFeedbackStatus.IncorrectAnswer, + }, + }; + return { + ...state, + challenge_feedback: challengeFeedback, + }; + } + return { reducer_type: "error", code: TalerErrorCode.ANASTASIS_TRUTH_CHALLENGE_FAILED, @@ -1072,6 +1097,9 @@ async function selectChallenge( const url = new URL(`/truth/${truth.uuid}/challenge`, truth.url); + const newFeedback = { ...state.challenge_feedback }; + delete newFeedback[truth.uuid]; + switch (truth.escrow_type) { case ChallengeType.Question: case ChallengeType.Totp: { @@ -1079,51 +1107,93 @@ async function selectChallenge( ...state, recovery_state: RecoveryStates.ChallengeSolving, selected_challenge_uuid: truth.uuid, - challenge_feedback: { - ...state.challenge_feedback, - [truth.uuid]: { - state: ChallengeFeedbackStatus.Pending, - }, - }, + challenge_feedback: newFeedback, }; } } - const resp = await fetch(url.href, { - method: "POST", - headers: { - Accept: "application/json", - "Content-Type": "application/json", - }, - body: JSON.stringify({ - truth_decryption_key: truth.truth_key, - }), - }); + let resp: Response; + + try { + resp = await fetch(url.href, { + method: "POST", + headers: { + Accept: "application/json", + "Content-Type": "application/json", + }, + body: JSON.stringify({ + truth_decryption_key: truth.truth_key, + }), + }); + } catch (e) { + const feedback: ChallengeFeedback = { + state: ChallengeFeedbackStatus.ServerFailure, + http_status: 0, + }; + return { + ...state, + recovery_state: RecoveryStates.ChallengeSelecting, + selected_challenge_uuid: truth.uuid, + challenge_feedback: { + ...state.challenge_feedback, + [truth.uuid]: feedback, + }, + }; + } logger.info( `got GET /truth/.../challenge response from ${truth.url}, http status ${resp.status}`, ); if (resp.status === HttpStatusCode.Ok) { + const respBodyJson = await resp.json(); + const instr = codecForChallengeInstructionMessage().decode(respBodyJson); + let feedback: ChallengeFeedback; + switch (instr.method) { + case "FILE_WRITTEN": { + feedback = { + state: ChallengeFeedbackStatus.CodeInFile, + display_hint: "TAN code is in file (for debugging)", + filename: instr.filename, + }; + break; + } + case "IBAN_WIRE": { + feedback = { + state: ChallengeFeedbackStatus.AuthIban, + answer_code: instr.answer_code, + target_business_name: instr.business_name, + challenge_amount: instr.amount, + target_iban: instr.credit_iban, + wire_transfer_subject: instr.wire_transfer_subject, + }; + break; + } + case "TAN_SENT": { + feedback = { + state: ChallengeFeedbackStatus.CodeSent, + address_hint: instr.tan_address_hint, + display_hint: "Code sent to address", + }; + } + } return { ...state, recovery_state: RecoveryStates.ChallengeSolving, selected_challenge_uuid: truth.uuid, challenge_feedback: { ...state.challenge_feedback, - [truth.uuid]: { - state: ChallengeFeedbackStatus.Pending, - }, + [truth.uuid]: feedback, }, }; } - // FIXME: look at response, include in challenge_feedback! + // FIXME: look at more error codes in response return { reducer_type: "error", code: TalerErrorCode.ANASTASIS_TRUTH_CHALLENGE_FAILED, - hint: "got unexpected /truth/.../challenge response status", + hint: `got unexpected /truth/.../challenge response status (${resp.status})`, http_status: resp.status, } as ReducerStateError; } @@ -1727,8 +1797,9 @@ export async function reduceAction( } try { return await h.handler(state, parsedArgs); - } catch (e) { + } catch (e: any) { logger.error("action handler failed"); + logger.error(`${e?.stack ?? e}`); if (e instanceof ReducerError) { return { reducer_type: "error", diff --git a/packages/anastasis-core/src/provider-types.ts b/packages/anastasis-core/src/provider-types.ts index 72f2dc6e5..4da62fc04 100644 --- a/packages/anastasis-core/src/provider-types.ts +++ b/packages/anastasis-core/src/provider-types.ts @@ -16,6 +16,13 @@ import { AmountString, + buildCodecForObject, + buildCodecForUnion, + Codec, + codecForAmountString, + codecForConstString, + codecForNumber, + codecForString, TalerProtocolTimestamp, } from "@gnu-taler/taler-util"; @@ -122,3 +129,86 @@ export interface RecoveryMetaDataItem { // document was uploaded. upload_time: TalerProtocolTimestamp; } + +export type ChallengeInstructionMessage = + | FileChallengeInstructionMessage + | IbanChallengeInstructionMessage + | PinChallengeInstructionMessage; + +export interface IbanChallengeInstructionMessage { + // What kind of challenge is this? + method: "IBAN_WIRE"; + + // How much should be wired? + amount: AmountString; + + // What is the target IBAN? + credit_iban: string; + + // What is the receiver name? + business_name: string; + + // What is the expected wire transfer subject? + wire_transfer_subject: string; + + // What is the numeric code (also part of the + // wire transfer subject) to be hashed when + // solving the challenge? + answer_code: number; + + // Hint about the origin account that must be used. + debit_account_hint: string; +} + +export interface PinChallengeInstructionMessage { + // What kind of challenge is this? + method: "TAN_SENT"; + + // Where was the PIN code sent? Note that this + // address will most likely have been obscured + // to improve privacy. + tan_address_hint: string; +} + +export interface FileChallengeInstructionMessage { + // What kind of challenge is this? + method: "FILE_WRITTEN"; + + // Name of the file where the PIN code was written. + filename: string; +} + +export const codecForFileChallengeInstructionMessage = + (): Codec<FileChallengeInstructionMessage> => + buildCodecForObject<FileChallengeInstructionMessage>() + .property("method", codecForConstString("FILE_WRITTEN")) + .property("filename", codecForString()) + .build("FileChallengeInstructionMessage"); + +export const codecForPinChallengeInstructionMessage = + (): Codec<PinChallengeInstructionMessage> => + buildCodecForObject<PinChallengeInstructionMessage>() + .property("method", codecForConstString("TAN_SENT")) + .property("tan_address_hint", codecForString()) + .build("PinChallengeInstructionMessage"); + +export const codecForIbanChallengeInstructionMessage = + (): Codec<IbanChallengeInstructionMessage> => + buildCodecForObject<IbanChallengeInstructionMessage>() + .property("method", codecForConstString("IBAN_WIRE")) + .property("amount", codecForAmountString()) + .property("business_name", codecForString()) + .property("credit_iban", codecForString()) + .property("wire_transfer_subject", codecForString()) + .property("answer_code", codecForNumber()) + .property("debit_account_hint", codecForString()) + .build("IbanChallengeInstructionMessage"); + +export const codecForChallengeInstructionMessage = + (): Codec<ChallengeInstructionMessage> => + buildCodecForUnion<ChallengeInstructionMessage>() + .discriminateOn("method") + .alternative("FILE_WRITTEN", codecForFileChallengeInstructionMessage()) + .alternative("IBAN_WIRE", codecForIbanChallengeInstructionMessage()) + .alternative("TAN_SENT", codecForPinChallengeInstructionMessage()) + .build("ChallengeInstructionMessage"); diff --git a/packages/anastasis-core/src/reducer-types.ts b/packages/anastasis-core/src/reducer-types.ts index 5b5f40297..b7e3148cb 100644 --- a/packages/anastasis-core/src/reducer-types.ts +++ b/packages/anastasis-core/src/reducer-types.ts @@ -220,8 +220,6 @@ export interface ReducerStateRecovery { /** * Explicitly selected version by the user. - * FIXME: In the C reducer this is called "version". - * FIXME: rename to selected_secret / selected_policy? */ selected_version?: AggregatedPolicyMetaInfo; diff --git a/packages/anastasis-webui/package.json b/packages/anastasis-webui/package.json index 2327b5e12..a855ffa94 100644 --- a/packages/anastasis-webui/package.json +++ b/packages/anastasis-webui/package.json @@ -15,7 +15,6 @@ "pretty": "prettier --write src", "storybook": "start-storybook -p 6006" }, - "type": "module", "eslintConfig": { "parser": "@typescript-eslint/parser", "extends": [ @@ -62,4 +61,4 @@ "sirv-cli": "^1.0.14", "typescript": "^4.5.4" } -}
\ No newline at end of file +} diff --git a/packages/anastasis-webui/src/hooks/use-anastasis-reducer.ts b/packages/anastasis-webui/src/hooks/use-anastasis-reducer.ts index 7274c3d03..434e5fb09 100644 --- a/packages/anastasis-webui/src/hooks/use-anastasis-reducer.ts +++ b/packages/anastasis-webui/src/hooks/use-anastasis-reducer.ts @@ -1,3 +1,22 @@ +/* + This file is part of GNU Anastasis + (C) 2021-2022 Anastasis SARL + + GNU Anastasis is free software; you can redistribute it and/or modify it under the + terms of the GNU Affero General Public License as published by the Free Software + Foundation; either version 3, or (at your option) any later version. + + GNU Anastasis is distributed in the hope that it will be useful, but WITHOUT ANY + WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR + A PARTICULAR PURPOSE. See the GNU Affero General Public License for more details. + + You should have received a copy of the GNU Affero General Public License along with + GNU Anastasis; see the file COPYING. If not, see <http://www.gnu.org/licenses/> + */ + +/** + * Imports. + */ import { TalerErrorCode } from "@gnu-taler/taler-util"; import { AggregatedPolicyMetaInfo, @@ -7,7 +26,6 @@ import { getBackupStartState, getRecoveryStartState, mergeDiscoveryAggregate, - PolicyMetaInfo, RecoveryStates, reduceAction, ReducerState, diff --git a/packages/anastasis-webui/src/pages/home/AddingProviderScreen.stories.tsx b/packages/anastasis-webui/src/pages/home/AddingProviderScreen.stories.tsx index e26ba706f..49cddc8b7 100644 --- a/packages/anastasis-webui/src/pages/home/AddingProviderScreen.stories.tsx +++ b/packages/anastasis-webui/src/pages/home/AddingProviderScreen.stories.tsx @@ -44,14 +44,6 @@ export const NewProviderWithoutProviderList = createExample(TestedComponent, { authentication_providers: {}, } as ReducerState); -export const NewVideoProvider = createExample( - TestedComponent, - { - ...reducerStatesExample.authEditing, - } as ReducerState, - { providerType: "video" }, -); - export const NewSmsProvider = createExample( TestedComponent, { diff --git a/packages/anastasis-webui/src/pages/home/ChallengeOverviewScreen.stories.tsx b/packages/anastasis-webui/src/pages/home/ChallengeOverviewScreen.stories.tsx index 3bd6a0c17..3d765aa86 100644 --- a/packages/anastasis-webui/src/pages/home/ChallengeOverviewScreen.stories.tsx +++ b/packages/anastasis-webui/src/pages/home/ChallengeOverviewScreen.stories.tsx @@ -249,19 +249,15 @@ export const OnePolicyWithAllTheChallengesInDifferentState = createExample( }, challenge_feedback: { "uuid-1": { state: ChallengeFeedbackStatus.Solved.toString() }, - "uuid-2": { - state: ChallengeFeedbackStatus.Message.toString(), - message: "Challenge should be solved", - }, "uuid-3": { state: ChallengeFeedbackStatus.AuthIban.toString(), challenge_amount: "EUR:1", - credit_iban: "DE12345789000", - business_name: "Data Loss Incorporated", + target_iban: "DE12345789000", + target_business_name: "Data Loss Incorporated", wire_transfer_subject: "Anastasis 987654321", }, "uuid-4": { - state: ChallengeFeedbackStatus.Payment.toString(), + state: ChallengeFeedbackStatus.TalerPayment.toString(), taler_pay_uri: "taler://pay/...", provider: "https://localhost:8080/", payment_secret: "3P4561HAMHRRYEYD6CM6J7TS5VTD5SR2K2EXJDZEFSX92XKHR4KG", @@ -270,11 +266,6 @@ export const OnePolicyWithAllTheChallengesInDifferentState = createExample( state: ChallengeFeedbackStatus.RateLimitExceeded.toString(), // "error_code": 8121 }, - "uuid-6": { - state: ChallengeFeedbackStatus.Redirect.toString(), - redirect_url: "https://videoconf.example.com/", - http_status: 303, - }, "uuid-7": { state: ChallengeFeedbackStatus.ServerFailure.toString(), http_status: 500, diff --git a/packages/anastasis-webui/src/pages/home/ChallengeOverviewScreen.tsx b/packages/anastasis-webui/src/pages/home/ChallengeOverviewScreen.tsx index c4047f0b3..6660e63de 100644 --- a/packages/anastasis-webui/src/pages/home/ChallengeOverviewScreen.tsx +++ b/packages/anastasis-webui/src/pages/home/ChallengeOverviewScreen.tsx @@ -14,11 +14,8 @@ function OverviewFeedbackDisplay(props: { feedback?: ChallengeFeedback }) { return null; } switch (feedback.state) { - case ChallengeFeedbackStatus.Message: - return <div class="block has-text-danger">{feedback.message}</div>; case ChallengeFeedbackStatus.Solved: return <div />; - case ChallengeFeedbackStatus.Pending: case ChallengeFeedbackStatus.AuthIban: return null; case ChallengeFeedbackStatus.ServerFailure: @@ -43,7 +40,6 @@ function OverviewFeedbackDisplay(props: { feedback?: ChallengeFeedback }) { provider for further information. </div> ); - case ChallengeFeedbackStatus.Redirect: default: return <div />; } @@ -178,7 +174,7 @@ export function ChallengeOverviewScreen(): VNode { case ChallengeFeedbackStatus.RateLimitExceeded: return <div />; case ChallengeFeedbackStatus.AuthIban: - case ChallengeFeedbackStatus.Payment: + case ChallengeFeedbackStatus.TalerPayment: return ( <div> <AsyncButton @@ -192,20 +188,6 @@ export function ChallengeOverviewScreen(): VNode { </AsyncButton> </div> ); - case ChallengeFeedbackStatus.Redirect: - return ( - <div> - <AsyncButton - class="button" - disabled={ - atLeastThereIsOnePolicySolved && !policy.isPolicySolved - } - onClick={selectChallenge} - > - Go to {feedback.redirect_url} - </AsyncButton> - </div> - ); case ChallengeFeedbackStatus.Solved: return ( <div> diff --git a/packages/anastasis-webui/src/pages/home/SolveScreen.tsx b/packages/anastasis-webui/src/pages/home/SolveScreen.tsx index 1070cf8a9..3691d1416 100644 --- a/packages/anastasis-webui/src/pages/home/SolveScreen.tsx +++ b/packages/anastasis-webui/src/pages/home/SolveScreen.tsx @@ -16,19 +16,7 @@ export function SolveOverviewFeedbackDisplay(props: { return <div />; } switch (feedback.state) { - case ChallengeFeedbackStatus.Message: - return ( - <Notifications - notifications={[ - { - type: "INFO", - message: `Message from provider`, - description: feedback.message, - }, - ]} - /> - ); - case ChallengeFeedbackStatus.Payment: + case ChallengeFeedbackStatus.TalerPayment: return ( <Notifications notifications={[ @@ -51,7 +39,7 @@ export function SolveOverviewFeedbackDisplay(props: { { type: "INFO", message: `Message from provider`, - description: `Need to send a wire transfer to "${feedback.business_name}"`, + description: `Need to send a wire transfer to "${feedback.target_business_name}"`, }, ]} /> @@ -80,22 +68,6 @@ export function SolveOverviewFeedbackDisplay(props: { ]} /> ); - case ChallengeFeedbackStatus.Redirect: - return ( - <Notifications - notifications={[ - { - type: "INFO", - message: `Message from provider`, - description: ( - <span> - Please visit this link: <a>{feedback.redirect_url}</a> - </span> - ), - }, - ]} - /> - ); case ChallengeFeedbackStatus.Unsupported: return ( <Notifications @@ -121,6 +93,9 @@ export function SolveOverviewFeedbackDisplay(props: { /> ); default: + console.warn( + `unknown challenge feedback status ${JSON.stringify(feedback)}`, + ); return <div />; } } diff --git a/packages/anastasis-webui/src/pages/home/authMethod/AuthMethodEmailSolve.stories.tsx b/packages/anastasis-webui/src/pages/home/authMethod/AuthMethodEmailSolve.stories.tsx index 1e7053df5..d82111979 100644 --- a/packages/anastasis-webui/src/pages/home/authMethod/AuthMethodEmailSolve.stories.tsx +++ b/packages/anastasis-webui/src/pages/home/authMethod/AuthMethodEmailSolve.stories.tsx @@ -80,7 +80,7 @@ export const PaymentFeedback = createExample( selected_challenge_uuid: "uuid-1", challenge_feedback: { "uuid-1": { - state: ChallengeFeedbackStatus.Payment, + state: ChallengeFeedbackStatus.TalerPayment, taler_pay_uri: "taler://pay/...", provider: "https://localhost:8080/", payment_secret: "3P4561HAMHRRYEYD6CM6J7TS5VTD5SR2K2EXJDZEFSX92XKHR4KG", diff --git a/packages/anastasis-webui/src/pages/home/authMethod/AuthMethodEmailSolve.tsx b/packages/anastasis-webui/src/pages/home/authMethod/AuthMethodEmailSolve.tsx index 4f7f21324..935b45a77 100644 --- a/packages/anastasis-webui/src/pages/home/authMethod/AuthMethodEmailSolve.tsx +++ b/packages/anastasis-webui/src/pages/home/authMethod/AuthMethodEmailSolve.tsx @@ -9,6 +9,7 @@ import { TextInput } from "../../../components/fields/TextInput"; import { useAnastasisContext } from "../../../context/anastasis"; import { AnastasisClientFrame } from "../index"; import { SolveOverviewFeedbackDisplay } from "../SolveScreen"; +import { shouldHideConfirm } from "./helpers"; import { AuthMethodSolveProps } from "./index"; export function AuthMethodEmailSolve({ id }: AuthMethodSolveProps): VNode { @@ -103,12 +104,6 @@ export function AuthMethodEmailSolve({ id }: AuthMethodSolveProps): VNode { reducer?.back(); } - const shouldHideConfirm = - feedback?.state === ChallengeFeedbackStatus.RateLimitExceeded || - feedback?.state === ChallengeFeedbackStatus.Redirect || - feedback?.state === ChallengeFeedbackStatus.Unsupported || - feedback?.state === ChallengeFeedbackStatus.TruthUnknown; - return ( <AnastasisClientFrame hideNav title="Email challenge"> <SolveOverviewFeedbackDisplay feedback={feedback} /> @@ -160,7 +155,7 @@ export function AuthMethodEmailSolve({ id }: AuthMethodSolveProps): VNode { <button class="button" onClick={onCancel}> Cancel </button> - {!shouldHideConfirm && ( + {!shouldHideConfirm(feedback) && ( <AsyncButton class="button is-info" onClick={onNext}> Confirm </AsyncButton> diff --git a/packages/anastasis-webui/src/pages/home/authMethod/AuthMethodIbanSolve.tsx b/packages/anastasis-webui/src/pages/home/authMethod/AuthMethodIbanSolve.tsx index b58952feb..39788b538 100644 --- a/packages/anastasis-webui/src/pages/home/authMethod/AuthMethodIbanSolve.tsx +++ b/packages/anastasis-webui/src/pages/home/authMethod/AuthMethodIbanSolve.tsx @@ -5,10 +5,10 @@ import { import { h, VNode } from "preact"; import { useState } from "preact/hooks"; import { AsyncButton } from "../../../components/AsyncButton"; -import { TextInput } from "../../../components/fields/TextInput"; import { useAnastasisContext } from "../../../context/anastasis"; import { AnastasisClientFrame } from "../index"; import { SolveOverviewFeedbackDisplay } from "../SolveScreen"; +import { shouldHideConfirm } from "./helpers"; import { AuthMethodSolveProps } from "./index"; export function AuthMethodIbanSolve({ id }: AuthMethodSolveProps): VNode { @@ -79,12 +79,6 @@ export function AuthMethodIbanSolve({ id }: AuthMethodSolveProps): VNode { reducer?.back(); } - const shouldHideConfirm = - feedback?.state === ChallengeFeedbackStatus.RateLimitExceeded || - feedback?.state === ChallengeFeedbackStatus.Redirect || - feedback?.state === ChallengeFeedbackStatus.Unsupported || - feedback?.state === ChallengeFeedbackStatus.TruthUnknown; - return ( <AnastasisClientFrame hideNav title="IBAN Challenge"> <SolveOverviewFeedbackDisplay feedback={feedback} /> @@ -101,7 +95,7 @@ export function AuthMethodIbanSolve({ id }: AuthMethodSolveProps): VNode { <button class="button" onClick={onCancel}> Cancel </button> - {!shouldHideConfirm && ( + {!shouldHideConfirm(feedback) && ( <AsyncButton class="button is-info" onClick={onNext}> Confirm </AsyncButton> diff --git a/packages/anastasis-webui/src/pages/home/authMethod/AuthMethodPostSolve.tsx b/packages/anastasis-webui/src/pages/home/authMethod/AuthMethodPostSolve.tsx index fcff0b498..382ffa00a 100644 --- a/packages/anastasis-webui/src/pages/home/authMethod/AuthMethodPostSolve.tsx +++ b/packages/anastasis-webui/src/pages/home/authMethod/AuthMethodPostSolve.tsx @@ -9,6 +9,7 @@ import { TextInput } from "../../../components/fields/TextInput"; import { useAnastasisContext } from "../../../context/anastasis"; import { AnastasisClientFrame } from "../index"; import { SolveOverviewFeedbackDisplay } from "../SolveScreen"; +import { shouldHideConfirm } from "./helpers"; import { AuthMethodSolveProps } from "./index"; export function AuthMethodPostSolve({ id }: AuthMethodSolveProps): VNode { @@ -102,12 +103,6 @@ export function AuthMethodPostSolve({ id }: AuthMethodSolveProps): VNode { reducer?.back(); } - const shouldHideConfirm = - feedback?.state === ChallengeFeedbackStatus.RateLimitExceeded || - feedback?.state === ChallengeFeedbackStatus.Redirect || - feedback?.state === ChallengeFeedbackStatus.Unsupported || - feedback?.state === ChallengeFeedbackStatus.TruthUnknown; - return ( <AnastasisClientFrame hideNav title="Postal Challenge"> <SolveOverviewFeedbackDisplay feedback={feedback} /> @@ -130,7 +125,7 @@ export function AuthMethodPostSolve({ id }: AuthMethodSolveProps): VNode { <button class="button" onClick={onCancel}> Cancel </button> - {!shouldHideConfirm && ( + {!shouldHideConfirm(feedback) && ( <AsyncButton class="button is-info" onClick={onNext}> Confirm </AsyncButton> diff --git a/packages/anastasis-webui/src/pages/home/authMethod/AuthMethodQuestionSolve.stories.tsx b/packages/anastasis-webui/src/pages/home/authMethod/AuthMethodQuestionSolve.stories.tsx index c24ab0503..51d0a9993 100644 --- a/packages/anastasis-webui/src/pages/home/authMethod/AuthMethodQuestionSolve.stories.tsx +++ b/packages/anastasis-webui/src/pages/home/authMethod/AuthMethodQuestionSolve.stories.tsx @@ -20,6 +20,7 @@ */ import { + ChallengeFeedbackBankTransferRequired, ChallengeFeedbackStatus, ReducerState, } from "@gnu-taler/anastasis-core"; @@ -62,28 +63,6 @@ export const WithoutFeedback = createExample( }, ); -export const MessageFeedback = createExample(TestedComponent[type].solve, { - ...reducerStatesExample.challengeSolving, - recovery_information: { - challenges: [ - { - cost: "USD:1", - instructions: "does P equals NP?", - type: "question", - uuid: "ASDASDSAD!1", - }, - ], - policies: [], - }, - selected_challenge_uuid: "ASDASDSAD!1", - challenge_feedback: { - "ASDASDSAD!1": { - state: ChallengeFeedbackStatus.Message, - message: "Challenge should be solved", - }, - }, -} as ReducerState); - export const ServerFailureFeedback = createExample( TestedComponent[type].solve, { @@ -92,7 +71,7 @@ export const ServerFailureFeedback = createExample( challenges: [ { cost: "USD:1", - instructions: "does P equals NP?", + instructions: "does P equal NP?", type: "question", uuid: "ASDASDSAD!1", }, @@ -110,29 +89,6 @@ export const ServerFailureFeedback = createExample( } as ReducerState, ); -export const RedirectFeedback = createExample(TestedComponent[type].solve, { - ...reducerStatesExample.challengeSolving, - recovery_information: { - challenges: [ - { - cost: "USD:1", - instructions: "does P equals NP?", - type: "question", - uuid: "ASDASDSAD!1", - }, - ], - policies: [], - }, - selected_challenge_uuid: "ASDASDSAD!1", - challenge_feedback: { - "ASDASDSAD!1": { - state: ChallengeFeedbackStatus.Redirect, - http_status: 302, - redirect_url: "http://video.taler.net", - }, - }, -} as ReducerState); - export const MessageRateLimitExceededFeedback = createExample( TestedComponent[type].solve, { @@ -201,6 +157,15 @@ export const TruthUnknownFeedback = createExample(TestedComponent[type].solve, { }, } as ReducerState); +const ibanFeedback: ChallengeFeedbackBankTransferRequired = { + state: ChallengeFeedbackStatus.AuthIban, + challenge_amount: "EUR:1", + target_iban: "DE12345789000", + target_business_name: "Data Loss Incorporated", + wire_transfer_subject: "Anastasis 987654321", + answer_code: 987654321, +}; + export const AuthIbanFeedback = createExample(TestedComponent[type].solve, { ...reducerStatesExample.challengeSolving, recovery_information: { @@ -216,23 +181,7 @@ export const AuthIbanFeedback = createExample(TestedComponent[type].solve, { }, selected_challenge_uuid: "ASDASDSAD!1", challenge_feedback: { - "ASDASDSAD!1": { - state: ChallengeFeedbackStatus.AuthIban, - challenge_amount: "EUR:1", - credit_iban: "DE12345789000", - business_name: "Data Loss Incorporated", - wire_transfer_subject: "Anastasis 987654321", - answer_code: 987654321, - // Fields that follow are only for compatibility with C reducer, - // will be removed eventually, - details: { - business_name: "foo", - challenge_amount: "foo", - credit_iban: "foo", - wire_transfer_subject: "foo", - }, - method: "iban", - }, + "ASDASDSAD!1": ibanFeedback, }, } as ReducerState); @@ -252,7 +201,7 @@ export const PaymentFeedback = createExample(TestedComponent[type].solve, { selected_challenge_uuid: "ASDASDSAD!1", challenge_feedback: { "ASDASDSAD!1": { - state: ChallengeFeedbackStatus.Payment, + state: ChallengeFeedbackStatus.TalerPayment, taler_pay_uri: "taler://pay/...", provider: "https://localhost:8080/", payment_secret: "3P4561HAMHRRYEYD6CM6J7TS5VTD5SR2K2EXJDZEFSX92XKHR4KG", diff --git a/packages/anastasis-webui/src/pages/home/authMethod/AuthMethodQuestionSolve.tsx b/packages/anastasis-webui/src/pages/home/authMethod/AuthMethodQuestionSolve.tsx index 058efe009..bc0b67dcb 100644 --- a/packages/anastasis-webui/src/pages/home/authMethod/AuthMethodQuestionSolve.tsx +++ b/packages/anastasis-webui/src/pages/home/authMethod/AuthMethodQuestionSolve.tsx @@ -9,6 +9,7 @@ import { TextInput } from "../../../components/fields/TextInput"; import { useAnastasisContext } from "../../../context/anastasis"; import { AnastasisClientFrame } from "../index"; import { SolveOverviewFeedbackDisplay } from "../SolveScreen"; +import { shouldHideConfirm } from "./helpers"; import { AuthMethodSolveProps } from "./index"; export function AuthMethodQuestionSolve({ id }: AuthMethodSolveProps): VNode { @@ -79,12 +80,6 @@ export function AuthMethodQuestionSolve({ id }: AuthMethodSolveProps): VNode { reducer?.back(); } - const shouldHideConfirm = - feedback?.state === ChallengeFeedbackStatus.RateLimitExceeded || - feedback?.state === ChallengeFeedbackStatus.Redirect || - feedback?.state === ChallengeFeedbackStatus.Unsupported || - feedback?.state === ChallengeFeedbackStatus.TruthUnknown; - return ( <AnastasisClientFrame hideNav title="Question challenge"> <SolveOverviewFeedbackDisplay feedback={feedback} /> @@ -110,7 +105,7 @@ export function AuthMethodQuestionSolve({ id }: AuthMethodSolveProps): VNode { <button class="button" onClick={onCancel}> Cancel </button> - {!shouldHideConfirm && ( + {!shouldHideConfirm(feedback) && ( <AsyncButton class="button is-info" onClick={onNext}> Confirm </AsyncButton> diff --git a/packages/anastasis-webui/src/pages/home/authMethod/AuthMethodSmsSolve.tsx b/packages/anastasis-webui/src/pages/home/authMethod/AuthMethodSmsSolve.tsx index 3b00f6f2a..f3d304c74 100644 --- a/packages/anastasis-webui/src/pages/home/authMethod/AuthMethodSmsSolve.tsx +++ b/packages/anastasis-webui/src/pages/home/authMethod/AuthMethodSmsSolve.tsx @@ -9,6 +9,7 @@ import { TextInput } from "../../../components/fields/TextInput"; import { useAnastasisContext } from "../../../context/anastasis"; import { AnastasisClientFrame } from "../index"; import { SolveOverviewFeedbackDisplay } from "../SolveScreen"; +import { shouldHideConfirm } from "./helpers"; import { AuthMethodSolveProps } from "./index"; export function AuthMethodSmsSolve({ id }: AuthMethodSolveProps): VNode { @@ -103,12 +104,6 @@ export function AuthMethodSmsSolve({ id }: AuthMethodSolveProps): VNode { reducer?.back(); } - const shouldHideConfirm = - feedback?.state === ChallengeFeedbackStatus.RateLimitExceeded || - feedback?.state === ChallengeFeedbackStatus.Redirect || - feedback?.state === ChallengeFeedbackStatus.Unsupported || - feedback?.state === ChallengeFeedbackStatus.TruthUnknown; - return ( <AnastasisClientFrame hideNav title="SMS Challenge"> <SolveOverviewFeedbackDisplay feedback={feedback} /> @@ -160,7 +155,7 @@ export function AuthMethodSmsSolve({ id }: AuthMethodSolveProps): VNode { <button class="button" onClick={onCancel}> Cancel </button> - {!shouldHideConfirm && ( + {!shouldHideConfirm(feedback) && ( <AsyncButton class="button is-info" onClick={onNext}> Confirm </AsyncButton> diff --git a/packages/anastasis-webui/src/pages/home/authMethod/AuthMethodTotpSolve.tsx b/packages/anastasis-webui/src/pages/home/authMethod/AuthMethodTotpSolve.tsx index ee4937441..6b98f8ece 100644 --- a/packages/anastasis-webui/src/pages/home/authMethod/AuthMethodTotpSolve.tsx +++ b/packages/anastasis-webui/src/pages/home/authMethod/AuthMethodTotpSolve.tsx @@ -9,6 +9,7 @@ import { TextInput } from "../../../components/fields/TextInput"; import { useAnastasisContext } from "../../../context/anastasis"; import { AnastasisClientFrame } from "../index"; import { SolveOverviewFeedbackDisplay } from "../SolveScreen"; +import { shouldHideConfirm } from "./helpers"; import { AuthMethodSolveProps } from "./index"; export function AuthMethodTotpSolve(props: AuthMethodSolveProps): VNode { @@ -81,12 +82,6 @@ export function AuthMethodTotpSolve(props: AuthMethodSolveProps): VNode { reducer?.back(); } - const shouldHideConfirm = - feedback?.state === ChallengeFeedbackStatus.RateLimitExceeded || - feedback?.state === ChallengeFeedbackStatus.Redirect || - feedback?.state === ChallengeFeedbackStatus.Unsupported || - feedback?.state === ChallengeFeedbackStatus.TruthUnknown; - return ( <AnastasisClientFrame hideNav title="TOTP Challenge"> <SolveOverviewFeedbackDisplay feedback={feedback} /> @@ -108,7 +103,7 @@ export function AuthMethodTotpSolve(props: AuthMethodSolveProps): VNode { <button class="button" onClick={onCancel}> Cancel </button> - {!shouldHideConfirm && ( + {!shouldHideConfirm(feedback) && ( <AsyncButton class="button is-info" onClick={onNext}> Confirm </AsyncButton> diff --git a/packages/anastasis-webui/src/pages/home/authMethod/AuthMethodVideoSetup.stories.tsx b/packages/anastasis-webui/src/pages/home/authMethod/AuthMethodVideoSetup.stories.tsx deleted file mode 100644 index 4aad0a097..000000000 --- a/packages/anastasis-webui/src/pages/home/authMethod/AuthMethodVideoSetup.stories.tsx +++ /dev/null @@ -1,83 +0,0 @@ -/* eslint-disable @typescript-eslint/camelcase */ -/* - This file is part of GNU Taler - (C) 2021 Taler Systems S.A. - - GNU Taler is free software; you can redistribute it and/or modify it under the - terms of the GNU General Public License as published by the Free Software - Foundation; either version 3, or (at your option) any later version. - - GNU Taler is distributed in the hope that it will be useful, but WITHOUT ANY - WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR - A PARTICULAR PURPOSE. See the GNU General Public License for more details. - - You should have received a copy of the GNU General Public License along with - GNU Taler; see the file COPYING. If not, see <http://www.gnu.org/licenses/> - */ - -/** - * - * @author Sebastian Javier Marchano (sebasjm) - */ - -import { createExample, reducerStatesExample } from "../../../utils"; -import { authMethods as TestedComponent, KnownAuthMethods } from "./index"; -import logoImage from "../../../assets/logo.jpeg"; - -export default { - title: "Pages/backup/AuthorizationMethod/AuthMethods/Video", - component: TestedComponent, - args: { - order: 5, - }, - argTypes: { - onUpdate: { action: "onUpdate" }, - onBack: { action: "onBack" }, - }, -}; - -const type: KnownAuthMethods = "video"; - -export const Empty = createExample( - TestedComponent[type].setup, - reducerStatesExample.authEditing, - { - configured: [], - }, -); - -export const WithOneExample = createExample( - TestedComponent[type].setup, - reducerStatesExample.authEditing, - { - configured: [ - { - challenge: "qwe", - type, - instructions: logoImage, - remove: () => null, - }, - ], - }, -); - -export const WithMoreExamples = createExample( - TestedComponent[type].setup, - reducerStatesExample.authEditing, - { - configured: [ - { - challenge: "qwe", - type, - instructions: logoImage, - remove: () => null, - }, - { - challenge: "qwe", - type, - instructions: logoImage, - remove: () => null, - }, - ], - }, -); diff --git a/packages/anastasis-webui/src/pages/home/authMethod/AuthMethodVideoSetup.tsx b/packages/anastasis-webui/src/pages/home/authMethod/AuthMethodVideoSetup.tsx deleted file mode 100644 index 04a129c4a..000000000 --- a/packages/anastasis-webui/src/pages/home/authMethod/AuthMethodVideoSetup.tsx +++ /dev/null @@ -1,92 +0,0 @@ -import { encodeCrock, stringToBytes } from "@gnu-taler/taler-util"; -import { h, VNode } from "preact"; -import { useState } from "preact/hooks"; -import { ImageInput } from "../../../components/fields/ImageInput"; -import { AuthMethodSetupProps } from "./index"; -import { AnastasisClientFrame } from "../index"; - -export function AuthMethodVideoSetup({ - cancel, - addAuthMethod, - configured, -}: AuthMethodSetupProps): VNode { - const [image, setImage] = useState(""); - const addVideoAuth = (): void => { - addAuthMethod({ - authentication_method: { - type: "video", - instructions: "Join a video call", - challenge: encodeCrock(stringToBytes(image)), - }, - }); - }; - function goNextIfNoErrors(): void { - addVideoAuth(); - } - return ( - <AnastasisClientFrame hideNav title="Add video authentication"> - <p> - For video identification, you need to provide a passport-style - photograph. When recovering your secret, you will be asked to join a - video call. During that call, a human will use the photograph to verify - your identity. - </p> - <div style={{ textAlign: "center" }}> - <ImageInput - label="Choose photograph" - grabFocus - onConfirm={goNextIfNoErrors} - bind={[image, setImage]} - /> - </div> - {configured.length > 0 && ( - <section class="section"> - <div class="block">Your photographs:</div> - <div class="block"> - {configured.map((c, i) => { - return ( - <div - key={i} - class="box" - style={{ display: "flex", justifyContent: "space-between" }} - > - <img - style={{ - marginTop: "auto", - marginBottom: "auto", - width: 100, - height: 100, - border: "solid 1px black", - }} - src={c.instructions} - /> - <div style={{ marginTop: "auto", marginBottom: "auto" }}> - <button class="button is-danger" onClick={c.remove}> - Delete - </button> - </div> - </div> - ); - })} - </div> - </section> - )} - <div> - <div - style={{ - marginTop: "2em", - display: "flex", - justifyContent: "space-between", - }} - > - <button class="button" onClick={cancel}> - Cancel - </button> - <button class="button is-info" onClick={addVideoAuth}> - Add - </button> - </div> - </div> - </AnastasisClientFrame> - ); -} diff --git a/packages/anastasis-webui/src/pages/home/authMethod/AuthMethodVideoSolve.stories.tsx b/packages/anastasis-webui/src/pages/home/authMethod/AuthMethodVideoSolve.stories.tsx deleted file mode 100644 index 0e454dd73..000000000 --- a/packages/anastasis-webui/src/pages/home/authMethod/AuthMethodVideoSolve.stories.tsx +++ /dev/null @@ -1,63 +0,0 @@ -/* - This file is part of GNU Taler - (C) 2021 Taler Systems S.A. - - GNU Taler is free software; you can redistribute it and/or modify it under the - terms of the GNU General Public License as published by the Free Software - Foundation; either version 3, or (at your option) any later version. - - GNU Taler is distributed in the hope that it will be useful, but WITHOUT ANY - WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR - A PARTICULAR PURPOSE. See the GNU General Public License for more details. - - You should have received a copy of the GNU General Public License along with - GNU Taler; see the file COPYING. If not, see <http://www.gnu.org/licenses/> - */ - -/** - * - * @author Sebastian Javier Marchano (sebasjm) - */ - -import { - ChallengeFeedbackStatus, - ReducerState, -} from "@gnu-taler/anastasis-core"; -import { createExample, reducerStatesExample } from "../../../utils"; -import { authMethods as TestedComponent, KnownAuthMethods } from "./index"; - -export default { - title: "Pages/recovery/SolveChallenge/AuthMethods/video", - component: TestedComponent, - args: { - order: 5, - }, - argTypes: { - onUpdate: { action: "onUpdate" }, - onBack: { action: "onBack" }, - }, -}; - -const type: KnownAuthMethods = "video"; - -export const WithoutFeedback = createExample( - TestedComponent[type].solve, - { - ...reducerStatesExample.challengeSolving, - recovery_information: { - challenges: [ - { - cost: "USD:1", - instructions: "does P equals NP?", - type: "question", - uuid: "uuid-1", - }, - ], - policies: [], - }, - selected_challenge_uuid: "uuid-1", - } as ReducerState, - { - id: "uuid-1", - }, -); diff --git a/packages/anastasis-webui/src/pages/home/authMethod/AuthMethodVideoSolve.tsx b/packages/anastasis-webui/src/pages/home/authMethod/AuthMethodVideoSolve.tsx deleted file mode 100644 index e0ebdce76..000000000 --- a/packages/anastasis-webui/src/pages/home/authMethod/AuthMethodVideoSolve.tsx +++ /dev/null @@ -1,114 +0,0 @@ -import { - ChallengeFeedbackStatus, - ChallengeInfo, -} from "@gnu-taler/anastasis-core"; -import { h, VNode } from "preact"; -import { useState } from "preact/hooks"; -import { AsyncButton } from "../../../components/AsyncButton"; -import { TextInput } from "../../../components/fields/TextInput"; -import { useAnastasisContext } from "../../../context/anastasis"; -import { AnastasisClientFrame } from "../index"; -import { SolveOverviewFeedbackDisplay } from "../SolveScreen"; -import { AuthMethodSolveProps } from "./index"; - -export function AuthMethodVideoSolve({ id }: AuthMethodSolveProps): VNode { - const [answer, setAnswer] = useState(""); - - const reducer = useAnastasisContext(); - if (!reducer) { - return ( - <AnastasisClientFrame hideNav title="Recovery problem"> - <div>no reducer in context</div> - </AnastasisClientFrame> - ); - } - if ( - reducer.currentReducerState?.reducer_type !== "recovery" - ) { - return ( - <AnastasisClientFrame hideNav title="Recovery problem"> - <div>invalid state</div> - </AnastasisClientFrame> - ); - } - - if (!reducer.currentReducerState.recovery_information) { - return ( - <AnastasisClientFrame - hideNext="Recovery document not found" - title="Recovery problem" - > - <div>no recovery information found</div> - </AnastasisClientFrame> - ); - } - if (!reducer.currentReducerState.selected_challenge_uuid) { - return ( - <AnastasisClientFrame hideNav title="Recovery problem"> - <div>invalid state</div> - <div - style={{ - marginTop: "2em", - display: "flex", - justifyContent: "space-between", - }} - > - <button class="button" onClick={() => reducer.back()}> - Back - </button> - </div> - </AnastasisClientFrame> - ); - } - - const chArr = reducer.currentReducerState.recovery_information.challenges; - const challengeFeedback = - reducer.currentReducerState.challenge_feedback ?? {}; - const selectedUuid = reducer.currentReducerState.selected_challenge_uuid; - const challenges: { - [uuid: string]: ChallengeInfo; - } = {}; - for (const ch of chArr) { - challenges[ch.uuid] = ch; - } - const selectedChallenge = challenges[selectedUuid]; - const feedback = challengeFeedback[selectedUuid]; - - async function onNext(): Promise<void> { - return reducer?.transition("solve_challenge", { answer }); - } - function onCancel(): void { - reducer?.back(); - } - - const shouldHideConfirm = - feedback?.state === ChallengeFeedbackStatus.RateLimitExceeded || - feedback?.state === ChallengeFeedbackStatus.Redirect || - feedback?.state === ChallengeFeedbackStatus.Unsupported || - feedback?.state === ChallengeFeedbackStatus.TruthUnknown; - - return ( - <AnastasisClientFrame hideNav title="Add email authentication"> - <SolveOverviewFeedbackDisplay feedback={feedback} /> - <p>You are gonna be called to check your identity</p> - <TextInput label="Answer" grabFocus bind={[answer, setAnswer]} /> - - <div - style={{ - marginTop: "2em", - display: "flex", - justifyContent: "space-between", - }} - > - <button class="button" onClick={onCancel}> - Cancel - </button> - {!shouldHideConfirm && ( - <AsyncButton class="button is-info" onClick={onNext}> - Confirm - </AsyncButton> - )} - </div> - </AnastasisClientFrame> - ); -} diff --git a/packages/anastasis-webui/src/pages/home/authMethod/helpers.ts b/packages/anastasis-webui/src/pages/home/authMethod/helpers.ts new file mode 100644 index 000000000..2f5e3773e --- /dev/null +++ b/packages/anastasis-webui/src/pages/home/authMethod/helpers.ts @@ -0,0 +1,12 @@ +import { + ChallengeFeedback, + ChallengeFeedbackStatus, +} from "@gnu-taler/anastasis-core"; + +export function shouldHideConfirm(feedback: ChallengeFeedback): boolean { + return ( + feedback?.state === ChallengeFeedbackStatus.RateLimitExceeded || + feedback?.state === ChallengeFeedbackStatus.Unsupported || + feedback?.state === ChallengeFeedbackStatus.TruthUnknown + ); +} diff --git a/packages/anastasis-webui/src/pages/home/authMethod/index.tsx b/packages/anastasis-webui/src/pages/home/authMethod/index.tsx index 64cf07cd6..a1ab9cd28 100644 --- a/packages/anastasis-webui/src/pages/home/authMethod/index.tsx +++ b/packages/anastasis-webui/src/pages/home/authMethod/index.tsx @@ -3,22 +3,18 @@ import { h, VNode } from "preact"; import postalIcon from "../../../assets/icons/auth_method/postal.svg"; import questionIcon from "../../../assets/icons/auth_method/question.svg"; import smsIcon from "../../../assets/icons/auth_method/sms.svg"; -import videoIcon from "../../../assets/icons/auth_method/video.svg"; import { AuthMethodEmailSetup as EmailSetup } from "./AuthMethodEmailSetup"; import { AuthMethodEmailSolve as EmailSolve } from "./AuthMethodEmailSolve"; import { AuthMethodIbanSetup as IbanSetup } from "./AuthMethodIbanSetup"; -import { AuthMethodPostSetup as PostalSetup } from "./AuthMethodPostSetup"; -import { AuthMethodQuestionSetup as QuestionSetup } from "./AuthMethodQuestionSetup"; -import { AuthMethodSmsSetup as SmsSetup } from "./AuthMethodSmsSetup"; -import { AuthMethodTotpSetup as TotpSetup } from "./AuthMethodTotpSetup"; -import { AuthMethodVideoSetup as VideoSetup } from "./AuthMethodVideoSetup"; - import { AuthMethodIbanSolve as IbanSolve } from "./AuthMethodIbanSolve"; +import { AuthMethodPostSetup as PostalSetup } from "./AuthMethodPostSetup"; import { AuthMethodPostSolve as PostalSolve } from "./AuthMethodPostSolve"; +import { AuthMethodQuestionSetup as QuestionSetup } from "./AuthMethodQuestionSetup"; import { AuthMethodQuestionSolve as QuestionSolve } from "./AuthMethodQuestionSolve"; +import { AuthMethodSmsSetup as SmsSetup } from "./AuthMethodSmsSetup"; import { AuthMethodSmsSolve as SmsSolve } from "./AuthMethodSmsSolve"; +import { AuthMethodTotpSetup as TotpSetup } from "./AuthMethodTotpSetup"; import { AuthMethodTotpSolve as TotpSolve } from "./AuthMethodTotpSolve"; -import { AuthMethodVideoSolve as VideoSolve } from "./AuthMethodVideoSolve"; export type AuthMethodWithRemove = AuthMethod & { remove: () => void }; @@ -40,14 +36,12 @@ interface AuthMethodConfiguration { solve: (props: AuthMethodSolveProps) => VNode; skip?: boolean; } -// export type KnownAuthMethods = "sms" | "email" | "post" | "question" | "video" | "totp" | "iban"; const ALL_METHODS = [ "sms", "email", "post", "question", - "video", "totp", "iban", ] as const; @@ -97,11 +91,4 @@ export const authMethods: KnowMethodConfig = { setup: TotpSetup, solve: TotpSolve, }, - video: { - icon: <img src={videoIcon} />, - label: "Video", - setup: VideoSetup, - solve: VideoSolve, - skip: true, - }, }; diff --git a/packages/taler-util/src/payto.ts b/packages/taler-util/src/payto.ts index c9889160a..09d6856ca 100644 --- a/packages/taler-util/src/payto.ts +++ b/packages/taler-util/src/payto.ts @@ -14,10 +14,14 @@ GNU Taler; see the file COPYING. If not, see <http://www.gnu.org/licenses/> */ -import { generateFakeSegwitAddress } from "./index.js"; +import { generateFakeSegwitAddress } from "./bitcoin.js"; import { URLSearchParams } from "./url.js"; -export type PaytoUri = PaytoUriUnknown | PaytoUriIBAN | PaytoUriTalerBank | PaytoUriBitcoin; +export type PaytoUri = + | PaytoUriUnknown + | PaytoUriIBAN + | PaytoUriTalerBank + | PaytoUriBitcoin; interface PaytoUriGeneric { targetType: string; @@ -31,38 +35,41 @@ interface PaytoUriUnknown extends PaytoUriGeneric { interface PaytoUriIBAN extends PaytoUriGeneric { isKnown: true; - targetType: 'iban', + targetType: "iban"; iban: string; } interface PaytoUriTalerBank extends PaytoUriGeneric { isKnown: true; - targetType: 'x-taler-bank', + targetType: "x-taler-bank"; host: string; account: string; } interface PaytoUriBitcoin extends PaytoUriGeneric { isKnown: true; - targetType: 'bitcoin', - generateSegwitAddress: (r: string) => { addr1: string, addr2: string }; - addr1?: string, addr2?: string, + targetType: "bitcoin"; + generateSegwitAddress: (r: string) => { addr1: string; addr2: string }; + addr1?: string; + addr2?: string; } const paytoPfx = "payto://"; - - function buildSegwitGenerator(result: PaytoUriBitcoin, targetPath: string) { //generate segwit address just once, save addr in payto object //and use it as cache - return function generateSegwitAddress(reserve: string): { addr1: string, addr2: string } { - if (result.addr1 && result.addr2) return { addr1: result.addr1, addr2: result.addr2 }; - const { addr1, addr2 } = generateFakeSegwitAddress(reserve, targetPath) - result.addr1 = addr1 - result.addr2 = addr2 - return { addr1, addr2 } - } + return function generateSegwitAddress(reserve: string): { + addr1: string; + addr2: string; + } { + if (result.addr1 && result.addr2) + return { addr1: result.addr1, addr2: result.addr2 }; + const { addr1, addr2 } = generateFakeSegwitAddress(reserve, targetPath); + result.addr1 = addr1; + result.addr2 = addr2; + return { addr1, addr2 }; + }; } /** * Add query parameters to a payto URI @@ -81,27 +88,27 @@ export function addPaytoQueryParams( /** * Serialize a PaytoURI into a valid payto:// string - * - * @param p - * @returns + * + * @param p + * @returns */ export function stringifyPaytoUri(p: PaytoUri): string { - const url = `${paytoPfx}${p.targetType}//${p.targetPath}` + const url = `${paytoPfx}${p.targetType}//${p.targetPath}`; if (p.params) { const search = Object.entries(p.params) .map(([key, value]) => `${key}=${value}`) .join("&"); - return `${url}?${search}` + return `${url}?${search}`; } - return url + return url; } /** * Parse a valid payto:// uri into a PaytoUri object * RFC 8905 - * - * @param s - * @returns + * + * @param s + * @returns */ export function parsePaytoUri(s: string): PaytoUri | undefined { if (!s.startsWith(paytoPfx)) { @@ -127,47 +134,44 @@ export function parsePaytoUri(s: string): PaytoUri | undefined { params[v] = k; }); - if (targetType === 'x-taler-bank') { - const parts = targetPath.split('/') - const host = parts[0] - const account = parts[1] + if (targetType === "x-taler-bank") { + const parts = targetPath.split("/"); + const host = parts[0]; + const account = parts[1]; return { targetPath, targetType, params, isKnown: true, - host, account, + host, + account, }; - } - if (targetType === 'iban') { + if (targetType === "iban") { return { isKnown: true, targetPath, targetType, params, - iban: targetPath + iban: targetPath, }; - } - if (targetType === 'bitcoin') { - + if (targetType === "bitcoin") { const result: PaytoUriBitcoin = { isKnown: true, targetPath, targetType, params, - generateSegwitAddress: (): any => null - } + generateSegwitAddress: (): any => null, + }; - result.generateSegwitAddress = buildSegwitGenerator(result, targetPath) + result.generateSegwitAddress = buildSegwitGenerator(result, targetPath); return result; - } return { targetPath, targetType, params, - isKnown: false + isKnown: false, }; } |