From 88d142d2098ad87613222e9a0c6df478a78f6528 Mon Sep 17 00:00:00 2001 From: Sebastian Date: Mon, 1 Nov 2021 16:10:49 -0300 Subject: [PATCH] more styling added placeholders for inputs import declaration for png next button now has tooltip providing info about whats missing a lot more of examples for UI testing added qr dependency for totp rendering added email and field input types added all auth method setup screens added modal when there is not auth provider merge continent and country into location section others improvements as well... --- packages/anastasis-core/src/reducer-types.ts | 6 +- packages/anastasis-webui/package.json | 3 +- packages/anastasis-webui/src/assets/empty.png | Bin 0 -> 2785 bytes .../src/assets/example/id1.jpg | Bin 0 -> 103558 bytes .../src/assets/icons/auth_method/email.svg | 1 + .../src/assets/icons/auth_method/postal.svg | 1 + .../src/assets/icons/auth_method/question.svg | 1 + .../src/assets/icons/auth_method/sms.svg | 1 + .../src/assets/icons/auth_method/video.svg | 1 + .../anastasis-webui/src/components/QR.tsx | 35 +++ .../src/components/fields/DateInput.tsx | 5 +- .../src/components/fields/EmailInput.tsx | 44 ++++ .../src/components/fields/FileInput.tsx | 81 +++++++ .../src/components/fields/ImageInput.tsx | 81 +++++++ .../src/components/fields/NumberInput.tsx | 2 + .../src/components/fields/TextInput.tsx | 2 + .../src/components/menu/SideBar.tsx | 5 +- packages/anastasis-webui/src/declaration.d.ts | 4 + .../src/pages/home/AttributeEntryScreen.tsx | 84 +++---- .../src/pages/home/AuthMethodEmailSetup.tsx | 43 ---- .../src/pages/home/AuthMethodPostSetup.tsx | 69 ------ .../pages/home/AuthMethodQuestionSetup.tsx | 47 ---- .../src/pages/home/AuthMethodSmsSetup.tsx | 51 ----- .../AuthenticationEditorScreen.stories.tsx | 55 +++++ .../pages/home/AuthenticationEditorScreen.tsx | 199 ++++++++++++----- .../home/BackupFinishedScreen.stories.tsx | 2 +- .../src/pages/home/BackupFinishedScreen.tsx | 32 ++- .../pages/home/ChallengeOverviewScreen.tsx | 3 +- .../src/pages/home/ChallengePayingScreen.tsx | 2 +- .../pages/home/ContinentSelectionScreen.tsx | 102 ++++++++- .../src/pages/home/CountrySelectionScreen.tsx | 2 +- .../src/pages/home/PoliciesPayingScreen.tsx | 2 +- .../src/pages/home/RecoveryFinishedScreen.tsx | 4 +- .../home/ReviewPoliciesScreen.stories.tsx | 208 ++++++++++++++++-- .../src/pages/home/ReviewPoliciesScreen.tsx | 52 +++-- .../src/pages/home/SecretEditorScreen.tsx | 5 + .../src/pages/home/SecretSelectionScreen.tsx | 2 +- .../src/pages/home/SolveScreen.stories.tsx | 12 +- .../src/pages/home/SolveScreen.tsx | 26 +-- .../AuthMethodEmailSetup.stories.tsx | 66 ++++++ .../authMethodSetup/AuthMethodEmailSetup.tsx | 62 ++++++ .../AuthMethodIbanSetup.stories.tsx | 65 ++++++ .../authMethodSetup/AuthMethodIbanSetup.tsx | 68 ++++++ .../AuthMethodPostSetup.stories.tsx | 66 ++++++ .../authMethodSetup/AuthMethodPostSetup.tsx | 102 +++++++++ .../AuthMethodQuestionSetup.stories.tsx | 66 ++++++ .../AuthMethodQuestionSetup.tsx | 70 ++++++ .../AuthMethodSmsSetup.stories.tsx | 66 ++++++ .../authMethodSetup/AuthMethodSmsSetup.tsx | 63 ++++++ .../AuthMethodTotpSetup.stories.tsx | 64 ++++++ .../authMethodSetup/AuthMethodTotpSetup.tsx | 47 ++++ .../AuthMethodVideoSetup.stories.tsx | 66 ++++++ .../authMethodSetup/AuthMethodVideoSetup.tsx | 56 +++++ .../src/pages/home/authMethodSetup/index.tsx | 68 ++++++ .../anastasis-webui/src/pages/home/index.tsx | 23 +- packages/anastasis-webui/src/scss/main.scss | 8 +- packages/anastasis-webui/src/utils/index.tsx | 8 +- pnpm-lock.yaml | 2 + 58 files changed, 1883 insertions(+), 428 deletions(-) create mode 100644 packages/anastasis-webui/src/assets/empty.png create mode 100644 packages/anastasis-webui/src/assets/example/id1.jpg create mode 100644 packages/anastasis-webui/src/assets/icons/auth_method/email.svg create mode 100644 packages/anastasis-webui/src/assets/icons/auth_method/postal.svg create mode 100644 packages/anastasis-webui/src/assets/icons/auth_method/question.svg create mode 100644 packages/anastasis-webui/src/assets/icons/auth_method/sms.svg create mode 100644 packages/anastasis-webui/src/assets/icons/auth_method/video.svg create mode 100644 packages/anastasis-webui/src/components/QR.tsx create mode 100644 packages/anastasis-webui/src/components/fields/EmailInput.tsx create mode 100644 packages/anastasis-webui/src/components/fields/FileInput.tsx create mode 100644 packages/anastasis-webui/src/components/fields/ImageInput.tsx delete mode 100644 packages/anastasis-webui/src/pages/home/AuthMethodEmailSetup.tsx delete mode 100644 packages/anastasis-webui/src/pages/home/AuthMethodPostSetup.tsx delete mode 100644 packages/anastasis-webui/src/pages/home/AuthMethodQuestionSetup.tsx delete mode 100644 packages/anastasis-webui/src/pages/home/AuthMethodSmsSetup.tsx create mode 100644 packages/anastasis-webui/src/pages/home/authMethodSetup/AuthMethodEmailSetup.stories.tsx create mode 100644 packages/anastasis-webui/src/pages/home/authMethodSetup/AuthMethodEmailSetup.tsx create mode 100644 packages/anastasis-webui/src/pages/home/authMethodSetup/AuthMethodIbanSetup.stories.tsx create mode 100644 packages/anastasis-webui/src/pages/home/authMethodSetup/AuthMethodIbanSetup.tsx create mode 100644 packages/anastasis-webui/src/pages/home/authMethodSetup/AuthMethodPostSetup.stories.tsx create mode 100644 packages/anastasis-webui/src/pages/home/authMethodSetup/AuthMethodPostSetup.tsx create mode 100644 packages/anastasis-webui/src/pages/home/authMethodSetup/AuthMethodQuestionSetup.stories.tsx create mode 100644 packages/anastasis-webui/src/pages/home/authMethodSetup/AuthMethodQuestionSetup.tsx create mode 100644 packages/anastasis-webui/src/pages/home/authMethodSetup/AuthMethodSmsSetup.stories.tsx create mode 100644 packages/anastasis-webui/src/pages/home/authMethodSetup/AuthMethodSmsSetup.tsx create mode 100644 packages/anastasis-webui/src/pages/home/authMethodSetup/AuthMethodTotpSetup.stories.tsx create mode 100644 packages/anastasis-webui/src/pages/home/authMethodSetup/AuthMethodTotpSetup.tsx create mode 100644 packages/anastasis-webui/src/pages/home/authMethodSetup/AuthMethodVideoSetup.stories.tsx create mode 100644 packages/anastasis-webui/src/pages/home/authMethodSetup/AuthMethodVideoSetup.tsx create mode 100644 packages/anastasis-webui/src/pages/home/authMethodSetup/index.tsx diff --git a/packages/anastasis-core/src/reducer-types.ts b/packages/anastasis-core/src/reducer-types.ts index d70712709..57f67f0d0 100644 --- a/packages/anastasis-core/src/reducer-types.ts +++ b/packages/anastasis-core/src/reducer-types.ts @@ -47,7 +47,7 @@ export interface ReducerStateBackup { code?: undefined; currencies?: string[]; continents?: ContinentInfo[]; - countries?: any; + countries?: CountryInfo[]; identity_attributes?: { [n: string]: string }; authentication_providers?: { [url: string]: AuthenticationProviderStatus }; authentication_methods?: AuthMethod[]; @@ -129,8 +129,8 @@ export interface ReducerStateRecovery { identity_attributes?: { [n: string]: string }; - continents?: any; - countries?: any; + continents?: ContinentInfo[]; + countries?: CountryInfo[]; selected_continent?: string; selected_country?: string; diff --git a/packages/anastasis-webui/package.json b/packages/anastasis-webui/package.json index 4cdb00243..2f2577a92 100644 --- a/packages/anastasis-webui/package.json +++ b/packages/anastasis-webui/package.json @@ -29,7 +29,8 @@ "jed": "1.1.1", "preact": "^10.3.1", "preact-render-to-string": "^5.1.4", - "preact-router": "^3.2.1" + "preact-router": "^3.2.1", + "qrcode-generator": "^1.4.4" }, "devDependencies": { "@creativebulma/bulma-tooltip": "^1.2.0", diff --git a/packages/anastasis-webui/src/assets/empty.png b/packages/anastasis-webui/src/assets/empty.png new file mode 100644 index 0000000000000000000000000000000000000000..5120d3138f63109a3433e6eec6059e5e8494d032 GIT binary patch literal 2785 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≪s@k}TW46IbXy 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 whgOdBTE2$oU71~M;RGT?lm \ No newline at end of file diff --git a/packages/anastasis-webui/src/assets/icons/auth_method/postal.svg b/packages/anastasis-webui/src/assets/icons/auth_method/postal.svg new file mode 100644 index 000000000..3787b8350 --- /dev/null +++ b/packages/anastasis-webui/src/assets/icons/auth_method/postal.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/packages/anastasis-webui/src/assets/icons/auth_method/question.svg b/packages/anastasis-webui/src/assets/icons/auth_method/question.svg new file mode 100644 index 000000000..a346556b2 --- /dev/null +++ b/packages/anastasis-webui/src/assets/icons/auth_method/question.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/packages/anastasis-webui/src/assets/icons/auth_method/sms.svg b/packages/anastasis-webui/src/assets/icons/auth_method/sms.svg new file mode 100644 index 000000000..ed15679bf --- /dev/null +++ b/packages/anastasis-webui/src/assets/icons/auth_method/sms.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/packages/anastasis-webui/src/assets/icons/auth_method/video.svg b/packages/anastasis-webui/src/assets/icons/auth_method/video.svg new file mode 100644 index 000000000..69de5e0b4 --- /dev/null +++ b/packages/anastasis-webui/src/assets/icons/auth_method/video.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/packages/anastasis-webui/src/components/QR.tsx b/packages/anastasis-webui/src/components/QR.tsx new file mode 100644 index 000000000..48f1a7c12 --- /dev/null +++ b/packages/anastasis-webui/src/components/QR.tsx @@ -0,0 +1,35 @@ +/* + 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 + */ + +import { h, VNode } from "preact"; +import { useEffect, useRef } from "preact/hooks"; +import qrcode from "qrcode-generator"; + +export function QR({ text }: { text: string }): VNode { + const divRef = useRef(null); + useEffect(() => { + const qr = qrcode(0, 'L'); + qr.addData(text); + qr.make(); + if (divRef.current) divRef.current.innerHTML = qr.createSvgTag({ + scalable: true, + }); + }); + + return

    +
    +
    ; +} diff --git a/packages/anastasis-webui/src/components/fields/DateInput.tsx b/packages/anastasis-webui/src/components/fields/DateInput.tsx index e1c354f7b..69a05fcf3 100644 --- a/packages/anastasis-webui/src/components/fields/DateInput.tsx +++ b/packages/anastasis-webui/src/components/fields/DateInput.tsx @@ -25,7 +25,7 @@ export function DateInput(props: DateInputProps): VNode { setOpened2(v) } - const value = props.bind[0]; + const value = props.bind[0] || ""; const [dirty, setDirty] = useState(false) const showError = dirty && props.error @@ -40,7 +40,8 @@ export function DateInput(props: DateInputProps): VNode { { setOpened(true) }} + readonly + onFocus={() => { setOpened(true) } } value={value} ref={inputRef} /> diff --git a/packages/anastasis-webui/src/components/fields/EmailInput.tsx b/packages/anastasis-webui/src/components/fields/EmailInput.tsx new file mode 100644 index 000000000..e0fca0f46 --- /dev/null +++ b/packages/anastasis-webui/src/components/fields/EmailInput.tsx @@ -0,0 +1,44 @@ +import { h, VNode } from "preact"; +import { useLayoutEffect, useRef, useState } from "preact/hooks"; + +export interface TextInputProps { + label: string; + grabFocus?: boolean; + error?: string; + placeholder?: string; + tooltip?: string; + bind: [string, (x: string) => void]; +} + +export function EmailInput(props: TextInputProps): VNode { + const inputRef = useRef(null); + useLayoutEffect(() => { + if (props.grabFocus) { + inputRef.current?.focus(); + } + }, [props.grabFocus]); + const value = props.bind[0]; + const [dirty, setDirty] = useState(false) + const showError = dirty && props.error + return (
    + +
    + {setDirty(true); props.bind[1]((e.target as HTMLInputElement).value)}} + ref={inputRef} + style={{ display: "block" }} /> +
    + {showError &&

    {props.error}

    } +
    + ); +} diff --git a/packages/anastasis-webui/src/components/fields/FileInput.tsx b/packages/anastasis-webui/src/components/fields/FileInput.tsx new file mode 100644 index 000000000..8b144ea43 --- /dev/null +++ b/packages/anastasis-webui/src/components/fields/FileInput.tsx @@ -0,0 +1,81 @@ +/* + 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 + */ + +/** +* +* @author Sebastian Javier Marchano (sebasjm) +*/ +import { h, VNode } from "preact"; +import { useLayoutEffect, useRef, useState } from "preact/hooks"; +import { TextInputProps } from "./TextInput"; + +const MAX_IMAGE_UPLOAD_SIZE = 1024 * 1024 + +export function FileInput(props: TextInputProps): VNode { + const inputRef = useRef(null); + useLayoutEffect(() => { + if (props.grabFocus) { + inputRef.current?.focus(); + } + }, [props.grabFocus]); + + const value = props.bind[0]; + // const [dirty, setDirty] = useState(false) + const image = useRef(null) + const [sizeError, setSizeError] = useState(false) + function onChange(v: string): void { + // setDirty(true); + props.bind[1](v); + } + return
    + +
    + { + const f: FileList | null = e.currentTarget.files + if (!f || f.length != 1) { + return onChange("") + } + if (f[0].size > MAX_IMAGE_UPLOAD_SIZE) { + setSizeError(true) + return onChange("") + } + setSizeError(false) + return f[0].arrayBuffer().then(b => { + const b64 = btoa( + new Uint8Array(b) + .reduce((data, byte) => data + String.fromCharCode(byte), '') + ) + return onChange(`data:${f[0].type};base64,${b64}` as any) + }) + }} /> + {props.error &&

    {props.error}

    } + {sizeError &&

    + File should be smaller than 1 MB +

    } +
    +
    +} + diff --git a/packages/anastasis-webui/src/components/fields/ImageInput.tsx b/packages/anastasis-webui/src/components/fields/ImageInput.tsx new file mode 100644 index 000000000..d5bf643d4 --- /dev/null +++ b/packages/anastasis-webui/src/components/fields/ImageInput.tsx @@ -0,0 +1,81 @@ +/* + 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 + */ + +/** +* +* @author Sebastian Javier Marchano (sebasjm) +*/ +import { h, VNode } from "preact"; +import { useLayoutEffect, useRef, useState } from "preact/hooks"; +import emptyImage from "../../assets/empty.png"; +import { TextInputProps } from "./TextInput"; + +const MAX_IMAGE_UPLOAD_SIZE = 1024 * 1024 + +export function ImageInput(props: TextInputProps): VNode { + const inputRef = useRef(null); + useLayoutEffect(() => { + if (props.grabFocus) { + inputRef.current?.focus(); + } + }, [props.grabFocus]); + + const value = props.bind[0]; + // const [dirty, setDirty] = useState(false) + const image = useRef(null) + const [sizeError, setSizeError] = useState(false) + function onChange(v: string): void { + // setDirty(true); + props.bind[1](v); + } + return
    + +
    + image.current?.click()} /> + { + const f: FileList | null = e.currentTarget.files + if (!f || f.length != 1) { + return onChange(emptyImage) + } + if (f[0].size > MAX_IMAGE_UPLOAD_SIZE) { + setSizeError(true) + return onChange(emptyImage) + } + setSizeError(false) + return f[0].arrayBuffer().then(b => { + const b64 = btoa( + new Uint8Array(b) + .reduce((data, byte) => data + String.fromCharCode(byte), '') + ) + return onChange(`data:${f[0].type};base64,${b64}` as any) + }) + }} /> + {props.error &&

    {props.error}

    } + {sizeError &&

    + Image should be smaller than 1 MB +

    } +
    +
    +} + diff --git a/packages/anastasis-webui/src/components/fields/NumberInput.tsx b/packages/anastasis-webui/src/components/fields/NumberInput.tsx index af9bbe66b..2b6cdcd2c 100644 --- a/packages/anastasis-webui/src/components/fields/NumberInput.tsx +++ b/packages/anastasis-webui/src/components/fields/NumberInput.tsx @@ -5,6 +5,7 @@ export interface TextInputProps { label: string; grabFocus?: boolean; error?: string; + placeholder?: string; tooltip?: string; bind: [string, (x: string) => void]; } @@ -30,6 +31,7 @@ export function NumberInput(props: TextInputProps): VNode { {setDirty(true); props.bind[1]((e.target as HTMLInputElement).value)}} ref={inputRef} diff --git a/packages/anastasis-webui/src/components/fields/TextInput.tsx b/packages/anastasis-webui/src/components/fields/TextInput.tsx index fa6fd9792..4bb785cd3 100644 --- a/packages/anastasis-webui/src/components/fields/TextInput.tsx +++ b/packages/anastasis-webui/src/components/fields/TextInput.tsx @@ -5,6 +5,7 @@ export interface TextInputProps { label: string; grabFocus?: boolean; error?: string; + placeholder?: string; tooltip?: string; bind: [string, (x: string) => void]; } @@ -29,6 +30,7 @@ export function TextInput(props: TextInputProps): VNode {
    {setDirty(true); props.bind[1]((e.target as HTMLInputElement).value)}} ref={inputRef} diff --git a/packages/anastasis-webui/src/components/menu/SideBar.tsx b/packages/anastasis-webui/src/components/menu/SideBar.tsx index 87e771009..35720e0f1 100644 --- a/packages/anastasis-webui/src/components/menu/SideBar.tsx +++ b/packages/anastasis-webui/src/components/menu/SideBar.tsx @@ -33,6 +33,7 @@ interface Props { export function Sidebar({ mobile }: Props): VNode { // const config = useConfigContext(); const config = { version: 'none' } + // FIXME: add replacement for __VERSION__ with the current version const process = { env: { __VERSION__: '0.0.0' } } const reducer = useAnastasisContext()! @@ -105,12 +106,12 @@ export function Sidebar({ mobile }: Props): VNode { Backup completed
    -
  1. + {/*
  2. Truth Paying
    -
  3. + */} : (reducer.currentReducerState && reducer.currentReducerState?.recovery_state &&
  4. diff --git a/packages/anastasis-webui/src/declaration.d.ts b/packages/anastasis-webui/src/declaration.d.ts index b32fb70fc..edd3a07a3 100644 --- a/packages/anastasis-webui/src/declaration.d.ts +++ b/packages/anastasis-webui/src/declaration.d.ts @@ -10,6 +10,10 @@ declare module '*.jpeg' { const content: any; export default content; } +declare module '*.png' { + const content: any; + export default content; +} declare module 'jed' { const x: any; export = x; diff --git a/packages/anastasis-webui/src/pages/home/AttributeEntryScreen.tsx b/packages/anastasis-webui/src/pages/home/AttributeEntryScreen.tsx index f74dcefba..2c7f54c5b 100644 --- a/packages/anastasis-webui/src/pages/home/AttributeEntryScreen.tsx +++ b/packages/anastasis-webui/src/pages/home/AttributeEntryScreen.tsx @@ -1,6 +1,6 @@ /* eslint-disable @typescript-eslint/camelcase */ import { UserAttributeSpec, validators } from "anastasis-core"; -import { h, VNode } from "preact"; +import { Fragment, h, VNode } from "preact"; import { useState } from "preact/hooks"; import { useAnastasisContext } from "../../context/anastasis"; import { AnastasisClientFrame, withProcessLabel } from "./index"; @@ -20,53 +20,38 @@ export function AttributeEntryScreen(): VNode { if (!reducer.currentReducerState || !("required_attributes" in reducer.currentReducerState)) { return
    invalid state
    } + const reqAttr = reducer.currentReducerState.required_attributes || [] + let hasErrors = false; + const fieldList: VNode[] = reqAttr.map((spec, i: number) => { + const value = attrs[spec.name] + const error = checkIfValid(value, spec) + hasErrors = hasErrors || error !== undefined + return ( + setAttrs({ ...attrs, [spec.name]: v })} + spec={spec} + errorMessage={error} + value={value} /> + ); + }) return ( reducer.transition("enter_user_attributes", { identity_attributes: attrs, })} >
    - - {reducer.currentReducerState.required_attributes?.map((x, i: number) => { - const value = attrs[x.name] - function checkIfValid(): string | undefined { - const pattern = x['validation-regex'] - if (pattern) { - const re = new RegExp(pattern) - if (!re.test(value)) return 'The value is invalid' - } - const logic = x['validation-logic'] - if (logic) { - const func = (validators as any)[logic]; - if (func && typeof func === 'function' && !func(value)) return 'Please check the value' - } - const optional = x.optional - console.log('optiona', optional) - if (!optional && !value) { - return 'This value is required' - } - return undefined - } - - return ( - setAttrs({ ...attrs, [x.name]: v })} - spec={x} - isValid={checkIfValid} - value={value} /> - ); - })} - + {fieldList}
    -

    This personal information will help to locate your secret in the first place

    +

    This personal information will help to locate your secret.

    This stay private

    The information you have entered here:

    @@ -92,14 +77,13 @@ interface AttributeEntryFieldProps { value: string; setValue: (newValue: string) => void; spec: UserAttributeSpec; - isValid: () => string | undefined; + errorMessage: string | undefined; } const possibleBirthdayYear: Array = [] -for (let i = 0; i < 100; i++ ) { +for (let i = 0; i < 100; i++) { possibleBirthdayYear.push(2020 - i) } function AttributeEntryField(props: AttributeEntryFieldProps): VNode { - const errorMessage = props.isValid() return (
    @@ -108,14 +92,14 @@ function AttributeEntryField(props: AttributeEntryFieldProps): VNode { grabFocus={props.isFirst} label={props.spec.label} years={possibleBirthdayYear} - error={errorMessage} + error={props.errorMessage} bind={[props.value, props.setValue]} />} {props.spec.type === 'number' && } @@ -123,7 +107,7 @@ function AttributeEntryField(props: AttributeEntryFieldProps): VNode { } @@ -136,3 +120,21 @@ function AttributeEntryField(props: AttributeEntryFieldProps): VNode {
    ); } + +function checkIfValid(value: string, spec: UserAttributeSpec): string | undefined { + const pattern = spec['validation-regex'] + if (pattern) { + const re = new RegExp(pattern) + if (!re.test(value)) return 'The value is invalid' + } + const logic = spec['validation-logic'] + if (logic) { + const func = (validators as any)[logic]; + if (func && typeof func === 'function' && !func(value)) return 'Please check the value' + } + const optional = spec.optional + if (!optional && !value) { + return 'This value is required' + } + return undefined +} diff --git a/packages/anastasis-webui/src/pages/home/AuthMethodEmailSetup.tsx b/packages/anastasis-webui/src/pages/home/AuthMethodEmailSetup.tsx deleted file mode 100644 index c3783ea6c..000000000 --- a/packages/anastasis-webui/src/pages/home/AuthMethodEmailSetup.tsx +++ /dev/null @@ -1,43 +0,0 @@ -/* eslint-disable @typescript-eslint/camelcase */ -import { - encodeCrock, - stringToBytes -} from "@gnu-taler/taler-util"; -import { h, VNode } from "preact"; -import { useState } from "preact/hooks"; -import { AuthMethodSetupProps } from "./AuthenticationEditorScreen"; -import { AnastasisClientFrame } from "./index"; -import { TextInput } from "../../components/fields/TextInput"; - -export function AuthMethodEmailSetup(props: AuthMethodSetupProps): VNode { - const [email, setEmail] = useState(""); - return ( - -

    - For email authentication, you need to provide an email address. When - recovering your secret, you will need to enter the code you receive by - email. -

    -
    - -
    -
    - - -
    -
    - ); -} diff --git a/packages/anastasis-webui/src/pages/home/AuthMethodPostSetup.tsx b/packages/anastasis-webui/src/pages/home/AuthMethodPostSetup.tsx deleted file mode 100644 index c4ddeff91..000000000 --- a/packages/anastasis-webui/src/pages/home/AuthMethodPostSetup.tsx +++ /dev/null @@ -1,69 +0,0 @@ -/* eslint-disable @typescript-eslint/camelcase */ -import { - canonicalJson, encodeCrock, - stringToBytes -} from "@gnu-taler/taler-util"; -import { h, VNode } from "preact"; -import { useState } from "preact/hooks"; -import { AuthMethodSetupProps } from "./AuthenticationEditorScreen"; -import { TextInput } from "../../components/fields/TextInput"; - -export function AuthMethodPostSetup(props: AuthMethodSetupProps): VNode { - const [fullName, setFullName] = useState(""); - const [street, setStreet] = useState(""); - const [city, setCity] = useState(""); - const [postcode, setPostcode] = useState(""); - const [country, setCountry] = useState(""); - - const addPostAuth = () => { - const challengeJson = { - full_name: fullName, - street, - city, - postcode, - country, - }; - props.addAuthMethod({ - authentication_method: { - type: "email", - instructions: `Letter to address in postal code ${postcode}`, - challenge: encodeCrock(stringToBytes(canonicalJson(challengeJson))), - }, - }); - }; - - return ( -
    -

    Add {props.method} authentication

    -
    -

    - For postal letter authentication, you need to provide a postal - address. When recovering your secret, you will be asked to enter a - code that you will receive in a letter to that address. -

    -
    - -
    -
    - -
    -
    - -
    -
    - -
    -
    - -
    -
    - - -
    -
    -
    - ); -} diff --git a/packages/anastasis-webui/src/pages/home/AuthMethodQuestionSetup.tsx b/packages/anastasis-webui/src/pages/home/AuthMethodQuestionSetup.tsx deleted file mode 100644 index f1bab94ab..000000000 --- a/packages/anastasis-webui/src/pages/home/AuthMethodQuestionSetup.tsx +++ /dev/null @@ -1,47 +0,0 @@ -/* eslint-disable @typescript-eslint/camelcase */ -import { - encodeCrock, - stringToBytes -} from "@gnu-taler/taler-util"; -import { h, VNode } from "preact"; -import { useState } from "preact/hooks"; -import { AuthMethodSetupProps } from "./AuthenticationEditorScreen"; -import { AnastasisClientFrame } from "./index"; -import { TextInput } from "../../components/fields/TextInput"; - -export function AuthMethodQuestionSetup(props: AuthMethodSetupProps): VNode { - const [questionText, setQuestionText] = useState(""); - const [answerText, setAnswerText] = useState(""); - const addQuestionAuth = (): void => props.addAuthMethod({ - authentication_method: { - type: "question", - instructions: questionText, - challenge: encodeCrock(stringToBytes(answerText)), - }, - }); - return ( - -
    -

    - For security question authentication, you need to provide a question - and its answer. When recovering your secret, you will be shown the - question and you will need to type the answer exactly as you typed it - here. -

    -
    - -
    -
    - -
    -
    - - -
    -
    -
    - ); -} diff --git a/packages/anastasis-webui/src/pages/home/AuthMethodSmsSetup.tsx b/packages/anastasis-webui/src/pages/home/AuthMethodSmsSetup.tsx deleted file mode 100644 index 6f4797275..000000000 --- a/packages/anastasis-webui/src/pages/home/AuthMethodSmsSetup.tsx +++ /dev/null @@ -1,51 +0,0 @@ -/* eslint-disable @typescript-eslint/camelcase */ -import { - encodeCrock, - stringToBytes -} from "@gnu-taler/taler-util"; -import { h, VNode } from "preact"; -import { useState, useRef, useLayoutEffect } from "preact/hooks"; -import { AuthMethodSetupProps } from "./AuthenticationEditorScreen"; -import { AnastasisClientFrame } from "./index"; - -export function AuthMethodSmsSetup(props: AuthMethodSetupProps): VNode { - const [mobileNumber, setMobileNumber] = useState(""); - const addSmsAuth = (): void => { - props.addAuthMethod({ - authentication_method: { - type: "sms", - instructions: `SMS to ${mobileNumber}`, - challenge: encodeCrock(stringToBytes(mobileNumber)), - }, - }); - }; - const inputRef = useRef(null); - useLayoutEffect(() => { - inputRef.current?.focus(); - }, []); - return ( - -
    -

    - For SMS authentication, you need to provide a mobile number. When - recovering your secret, you will be asked to enter the code you - receive via SMS. -

    - -
    - - -
    -
    -
    - ); -} diff --git a/packages/anastasis-webui/src/pages/home/AuthenticationEditorScreen.stories.tsx b/packages/anastasis-webui/src/pages/home/AuthenticationEditorScreen.stories.tsx index 8f86831a9..5077c3eb0 100644 --- a/packages/anastasis-webui/src/pages/home/AuthenticationEditorScreen.stories.tsx +++ b/packages/anastasis-webui/src/pages/home/AuthenticationEditorScreen.stories.tsx @@ -1,3 +1,4 @@ +/* eslint-disable @typescript-eslint/camelcase */ /* This file is part of GNU Taler (C) 2021 Taler Systems S.A. @@ -19,6 +20,7 @@ * @author Sebastian Javier Marchano (sebasjm) */ +import { ReducerState } from 'anastasis-core'; import { createExample, reducerStatesExample } from '../../utils'; import { AuthenticationEditorScreen as TestedComponent } from './AuthenticationEditorScreen'; @@ -36,3 +38,56 @@ export default { }; export const Example = createExample(TestedComponent, reducerStatesExample.authEditing); +export const OneAuthMethodConfigured = createExample(TestedComponent, { + ...reducerStatesExample.authEditing, + authentication_methods: [{ + type: 'question', + instructions: 'what time is it?', + challenge: 'asd', + }] +} as ReducerState); + + +export const SomeMoreAuthMethodConfigured = createExample(TestedComponent, { + ...reducerStatesExample.authEditing, + authentication_methods: [{ + type: 'question', + instructions: 'what time is it?', + challenge: 'asd', + },{ + type: 'question', + instructions: 'what time is it?', + challenge: 'qwe', + },{ + type: 'sms', + instructions: 'what time is it?', + challenge: 'asd', + },{ + type: 'email', + instructions: 'what time is it?', + challenge: 'asd', + },{ + type: 'email', + instructions: 'what time is it?', + challenge: 'asd', + },{ + type: 'email', + instructions: 'what time is it?', + challenge: 'asd', + },{ + type: 'email', + instructions: 'what time is it?', + challenge: 'asd', + }] +} as ReducerState); + +export const NoAuthMethodProvided = createExample(TestedComponent, { + ...reducerStatesExample.authEditing, + authentication_providers: {}, + authentication_methods: [] +} as ReducerState); + + // type: string; + // instructions: string; + // challenge: string; + // mime_type?: string; diff --git a/packages/anastasis-webui/src/pages/home/AuthenticationEditorScreen.tsx b/packages/anastasis-webui/src/pages/home/AuthenticationEditorScreen.tsx index e9ffccbac..f4d2aee58 100644 --- a/packages/anastasis-webui/src/pages/home/AuthenticationEditorScreen.tsx +++ b/packages/anastasis-webui/src/pages/home/AuthenticationEditorScreen.tsx @@ -1,19 +1,19 @@ /* eslint-disable @typescript-eslint/camelcase */ -import { AuthMethod, ReducerStateBackup } from "anastasis-core"; -import { h, VNode } from "preact"; +import { AuthMethod } from "anastasis-core"; +import { ComponentChildren, h, VNode } from "preact"; import { useState } from "preact/hooks"; import { useAnastasisContext } from "../../context/anastasis"; -import { AnastasisReducerApi } from "../../hooks/use-anastasis-reducer"; -import { AuthMethodEmailSetup } from "./AuthMethodEmailSetup"; -import { AuthMethodPostSetup } from "./AuthMethodPostSetup"; -import { AuthMethodQuestionSetup } from "./AuthMethodQuestionSetup"; -import { AuthMethodSmsSetup } from "./AuthMethodSmsSetup"; +import { authMethods, KnownAuthMethods } from "./authMethodSetup"; import { AnastasisClientFrame } from "./index"; + + +const getKeys = Object.keys as (obj: T) => Array + export function AuthenticationEditorScreen(): VNode { - const [selectedMethod, setSelectedMethod] = useState( - undefined - ); + const [noProvidersAck, setNoProvidersAck] = useState(false) + const [selectedMethod, setSelectedMethod] = useState(undefined); + const reducer = useAnastasisContext() if (!reducer) { return
    no reducer in context
    @@ -21,7 +21,29 @@ export function AuthenticationEditorScreen(): VNode { if (!reducer.currentReducerState || reducer.currentReducerState.backup_state === undefined) { return
    invalid state
    } + const configuredAuthMethods: AuthMethod[] = reducer.currentReducerState.authentication_methods ?? []; + const haveMethodsConfigured = configuredAuthMethods.length > 0; + + function removeByIndex(index: number): void { + if (reducer) reducer.transition("delete_authentication", { + authentication_method: index, + }) + } + + const camByType: { [s: string]: AuthMethodWithRemove[] } = {} + for (let index = 0; index < configuredAuthMethods.length; index++) { + const cam = { + ...configuredAuthMethods[index], + remove: () => removeByIndex(index) + } + const prevValue = camByType[cam.type] || [] + prevValue.push(cam) + camByType[cam.type] = prevValue; + } + + const providers = reducer.currentReducerState.authentication_providers!; + const authAvailableSet = new Set(); for (const provKey of Object.keys(providers)) { const p = providers[provKey]; @@ -31,79 +53,106 @@ export function AuthenticationEditorScreen(): VNode { } } } + if (selectedMethod) { const cancel = (): void => setSelectedMethod(undefined); const addMethod = (args: any): void => { reducer.transition("add_authentication", args); setSelectedMethod(undefined); }; - const methodMap: Record< - string, (props: AuthMethodSetupProps) => h.JSX.Element - > = { - sms: AuthMethodSmsSetup, - question: AuthMethodQuestionSetup, - email: AuthMethodEmailSetup, - post: AuthMethodPostSetup, - }; - const AuthSetup = methodMap[selectedMethod] ?? AuthMethodNotImplemented; + + const AuthSetup = authMethods[selectedMethod].screen ?? AuthMethodNotImplemented; return ( ); } - function MethodButton(props: { method: string; label: string }): VNode { + function MethodButton(props: { method: KnownAuthMethods }): VNode { return ( - +
    + +
    ); } - const configuredAuthMethods: AuthMethod[] = reducer.currentReducerState.authentication_methods ?? []; - const haveMethodsConfigured = configuredAuthMethods.length; + const errors = !haveMethodsConfigured ? "There is not enough authentication methods." : undefined; return ( - -
    - - - - - - -
    -

    Configured authentication methods

    - {haveMethodsConfigured ? ( - configuredAuthMethods.map((x, i) => { - return ( -

    - {x.type} ({x.instructions}){" "} - + +

    +
    +
    + {getKeys(authMethods).map(method => )} +
    + {authAvailableSet.size === 0 && setNoProvidersAck(true)} description="No providers founds" label="Add a provider manually"> + We have found no trusted cloud providers for your recovery secret. You can add a provider manually. + To add a provider you must know the provider URL (e.g. https://provider.com) +

    + More about cloud providers

    - ); - }) - ) : ( -

    No authentication methods configured yet.

    - )} +
    } + + {/* {haveMethodsConfigured && ( + configuredAuthMethods.map((x, i) => { + return ( +

    + {x.type} ({x.instructions}){" "} + +

    + ); + }) + )} */} +
    +
    + When recovering your wallet, you will be asked to verify your identity via the methods you configure here. +
    +
    ); } +type AuthMethodWithRemove = AuthMethod & { remove: () => void } export interface AuthMethodSetupProps { method: string; addAuthMethod: (x: any) => void; + configured: AuthMethodWithRemove[]; cancel: () => void; } @@ -116,8 +165,36 @@ function AuthMethodNotImplemented(props: AuthMethodSetupProps): VNode { ); } -interface AuthenticationEditorProps { - reducer: AnastasisReducerApi; - backupState: ReducerStateBackup; + +function ConfirmModal({ active, description, onCancel, onConfirm, children, danger, disabled, label = 'Confirm' }: Props): VNode { + return
    + } +interface Props { + active?: boolean; + description?: string; + onCancel?: () => void; + onConfirm?: () => void; + label?: string; + children?: ComponentChildren; + danger?: boolean; + disabled?: boolean; +} diff --git a/packages/anastasis-webui/src/pages/home/BackupFinishedScreen.stories.tsx b/packages/anastasis-webui/src/pages/home/BackupFinishedScreen.stories.tsx index 0c9d007bc..b71a79727 100644 --- a/packages/anastasis-webui/src/pages/home/BackupFinishedScreen.stories.tsx +++ b/packages/anastasis-webui/src/pages/home/BackupFinishedScreen.stories.tsx @@ -37,7 +37,7 @@ export default { }, }; -export const Simple = createExample(TestedComponent, reducerStatesExample.backupFinished); +export const WithoutName = createExample(TestedComponent, reducerStatesExample.backupFinished); export const WithName = createExample(TestedComponent, {...reducerStatesExample.backupFinished, secret_name: 'super_secret', diff --git a/packages/anastasis-webui/src/pages/home/BackupFinishedScreen.tsx b/packages/anastasis-webui/src/pages/home/BackupFinishedScreen.tsx index 218f1d1fd..70ac8157d 100644 --- a/packages/anastasis-webui/src/pages/home/BackupFinishedScreen.tsx +++ b/packages/anastasis-webui/src/pages/home/BackupFinishedScreen.tsx @@ -1,3 +1,4 @@ +import { format } from "date-fns"; import { h, VNode } from "preact"; import { useAnastasisContext } from "../../context/anastasis"; import { AnastasisClientFrame } from "./index"; @@ -11,23 +12,30 @@ export function BackupFinishedScreen(): VNode { return
    invalid state
    } const details = reducer.currentReducerState.success_details - return ( -

    - Your backup of secret "{reducer.currentReducerState.secret_name ?? "??"}" was - successful. -

    -

    The backup is stored by the following providers:

    - {details &&
      + return ( + {reducer.currentReducerState.secret_name ?

      + Your backup of secret "{reducer.currentReducerState.secret_name}" was + successful. +

      : +

      + Your secret was successfully backed up. +

      } + + {details &&
      +

      The backup is stored by the following providers:

      {Object.keys(details).map((x, i) => { const sd = details[x]; return ( -
    • - {x} (Policy version {sd.policy_version}) -
    • +
      + {x} +

      + version {sd.policy_version} + {sd.policy_expiration.t_ms !== 'never' ? ` expires at: ${format(sd.policy_expiration.t_ms, 'dd/MM/yyyy')}` : ' without expiration date'} +

      +
      ); })} -
    } - +
    } ); } diff --git a/packages/anastasis-webui/src/pages/home/ChallengeOverviewScreen.tsx b/packages/anastasis-webui/src/pages/home/ChallengeOverviewScreen.tsx index 3bb3fb837..cf44d5bf4 100644 --- a/packages/anastasis-webui/src/pages/home/ChallengeOverviewScreen.tsx +++ b/packages/anastasis-webui/src/pages/home/ChallengeOverviewScreen.tsx @@ -47,8 +47,9 @@ export function ChallengeOverviewScreen(): VNode { const atLeastThereIsOnePolicySolved = policiesWithInfo.find(p => p.isPolicySolved) !== undefined + const errors = !atLeastThereIsOnePolicySolved ? "Solve one policy before proceeding" : undefined; return ( - + {!policies.length ?

    No policies found, try with another version of the secret

    : (policies.length === 1 ?

    diff --git a/packages/anastasis-webui/src/pages/home/ChallengePayingScreen.tsx b/packages/anastasis-webui/src/pages/home/ChallengePayingScreen.tsx index d87afdf46..84896a2ec 100644 --- a/packages/anastasis-webui/src/pages/home/ChallengePayingScreen.tsx +++ b/packages/anastasis-webui/src/pages/home/ChallengePayingScreen.tsx @@ -13,7 +13,7 @@ export function ChallengePayingScreen(): VNode { const payments = ['']; //reducer.currentReducerState.payments ?? return (

    diff --git a/packages/anastasis-webui/src/pages/home/ContinentSelectionScreen.tsx b/packages/anastasis-webui/src/pages/home/ContinentSelectionScreen.tsx index 94c0409da..713655625 100644 --- a/packages/anastasis-webui/src/pages/home/ContinentSelectionScreen.tsx +++ b/packages/anastasis-webui/src/pages/home/ContinentSelectionScreen.tsx @@ -1,20 +1,108 @@ +/* eslint-disable @typescript-eslint/camelcase */ +import { BackupStates, ContinentInfo, RecoveryStates } from "anastasis-core"; import { h, VNode } from "preact"; +import { useState } from "preact/hooks"; import { useAnastasisContext } from "../../context/anastasis"; import { AnastasisClientFrame, withProcessLabel } from "./index"; export function ContinentSelectionScreen(): VNode { const reducer = useAnastasisContext() + + //FIXME: remove this when #7056 is fixed + const [countryCode, setCountryCode] = useState("") + if (!reducer || !reducer.currentReducerState || !("continents" in reducer.currentReducerState)) { return

    } - const select = (continent: string) => (): void => reducer.transition("select_continent", { continent }); + const selectContinent = (continent: string): void => { + reducer.transition("select_continent", { continent }) + }; + const selectCountry = (country: string): void => { + setCountryCode(country) + }; + + + const continentList = reducer.currentReducerState.continents || []; + const countryList = reducer.currentReducerState.countries || []; + const theContinent = reducer.currentReducerState.selected_continent || "" + // const cc = reducer.currentReducerState.selected_country || ""; + const theCountry = countryList.find(c => c.code === countryCode) + const selectCountryAction = () => { + //selection should be when the select box changes it value + if (!theCountry) return; + reducer.transition("select_country", { + country_code: countryCode, + currencies: [theCountry.currency], + }) + } + + const step1 = reducer.currentReducerState.backup_state === BackupStates.ContinentSelecting || + reducer.currentReducerState.recovery_state === RecoveryStates.ContinentSelecting; + + const errors = !theCountry ? "Select a country" : undefined + return ( - - {reducer.currentReducerState.continents.map((x: any) => ( - - ))} + +
    +
    +
    + +
    +
    + +
    + +
    +
    + {!step1 && + reducer.back()}> + X + + } +
    +
    + +
    + +
    +
    + +
    + +
    +
    +
    +
    + + {theCountry &&
    + +
    + +
    +
    } +
    +
    +

    + A location will help to define a common information that will be use to locate your secret and a currency + for payments if needed. +

    +
    +
    +
    ); } diff --git a/packages/anastasis-webui/src/pages/home/CountrySelectionScreen.tsx b/packages/anastasis-webui/src/pages/home/CountrySelectionScreen.tsx index 417c08633..77329f4fa 100644 --- a/packages/anastasis-webui/src/pages/home/CountrySelectionScreen.tsx +++ b/packages/anastasis-webui/src/pages/home/CountrySelectionScreen.tsx @@ -18,7 +18,7 @@ export function CountrySelectionScreen(): VNode { return (
    - {reducer.currentReducerState.countries.map((x: any) => ( + {reducer.currentReducerState.countries!.map((x: any) => (
    +
    ); })} diff --git a/packages/anastasis-webui/src/pages/home/SecretEditorScreen.tsx b/packages/anastasis-webui/src/pages/home/SecretEditorScreen.tsx index 79a46761c..915465c3f 100644 --- a/packages/anastasis-webui/src/pages/home/SecretEditorScreen.tsx +++ b/packages/anastasis-webui/src/pages/home/SecretEditorScreen.tsx @@ -6,6 +6,7 @@ import { useAnastasisContext } from "../../context/anastasis"; import { AnastasisClientFrame} from "./index"; import { TextInput } from "../../components/fields/TextInput"; +import { FileInput } from "../../components/fields/FileInput"; export function SecretEditorScreen(): VNode { const reducer = useAnastasisContext() @@ -57,6 +58,10 @@ export function SecretEditorScreen(): VNode { or import a file +
    diff --git a/packages/anastasis-webui/src/pages/home/SecretSelectionScreen.tsx b/packages/anastasis-webui/src/pages/home/SecretSelectionScreen.tsx index 5d67ee472..d0b83bda5 100644 --- a/packages/anastasis-webui/src/pages/home/SecretSelectionScreen.tsx +++ b/packages/anastasis-webui/src/pages/home/SecretSelectionScreen.tsx @@ -37,7 +37,7 @@ export function SecretSelectionScreen(): VNode { const recoveryDocument = reducer.currentReducerState.recovery_document if (!recoveryDocument) { return ( - +

    No recovery document found, try with another provider

    diff --git a/packages/anastasis-webui/src/pages/home/SolveScreen.stories.tsx b/packages/anastasis-webui/src/pages/home/SolveScreen.stories.tsx index c05c36b07..cb6561b3f 100644 --- a/packages/anastasis-webui/src/pages/home/SolveScreen.stories.tsx +++ b/packages/anastasis-webui/src/pages/home/SolveScreen.stories.tsx @@ -44,7 +44,7 @@ export const NotSupportedChallenge = createExample(TestedComponent, { recovery_information: { challenges: [{ cost: 'USD:1', - instructions: 'follow htis instructions', + instructions: 'does P equals NP?', type: 'chall-type', uuid: 'ASDASDSAD!1' }], @@ -58,7 +58,7 @@ export const MismatchedChallengeId = createExample(TestedComponent, { recovery_information: { challenges: [{ cost: 'USD:1', - instructions: 'follow htis instructions', + instructions: 'does P equals NP?', type: 'chall-type', uuid: 'ASDASDSAD!1' }], @@ -72,7 +72,7 @@ export const SmsChallenge = createExample(TestedComponent, { recovery_information: { challenges: [{ cost: 'USD:1', - instructions: 'follow htis instructions', + instructions: 'SMS to 555-5555', type: 'sms', uuid: 'ASDASDSAD!1' }], @@ -86,7 +86,7 @@ export const QuestionChallenge = createExample(TestedComponent, { recovery_information: { challenges: [{ cost: 'USD:1', - instructions: 'follow htis instructions', + instructions: 'does P equals NP?', type: 'question', uuid: 'ASDASDSAD!1' }], @@ -100,7 +100,7 @@ export const EmailChallenge = createExample(TestedComponent, { recovery_information: { challenges: [{ cost: 'USD:1', - instructions: 'follow htis instructions', + instructions: 'Email to sebasjm@some-domain.com', type: 'email', uuid: 'ASDASDSAD!1' }], @@ -114,7 +114,7 @@ export const PostChallenge = createExample(TestedComponent, { recovery_information: { challenges: [{ cost: 'USD:1', - instructions: 'follow htis instructions', + instructions: 'Letter to address in postal code ABC123', type: 'post', uuid: 'ASDASDSAD!1' }], diff --git a/packages/anastasis-webui/src/pages/home/SolveScreen.tsx b/packages/anastasis-webui/src/pages/home/SolveScreen.tsx index 077726e02..b0cfa9bb0 100644 --- a/packages/anastasis-webui/src/pages/home/SolveScreen.tsx +++ b/packages/anastasis-webui/src/pages/home/SolveScreen.tsx @@ -8,26 +8,26 @@ import { useAnastasisContext } from "../../context/anastasis"; export function SolveScreen(): VNode { const reducer = useAnastasisContext() const [answer, setAnswer] = useState(""); - + if (!reducer) { - return + return
    no reducer in context
    } if (!reducer.currentReducerState || reducer.currentReducerState.recovery_state === undefined) { - return + return
    invalid state
    } if (!reducer.currentReducerState.recovery_information) { - return + return
    no recovery information found
    } if (!reducer.currentReducerState.selected_challenge_uuid) { - return -
    no selected uuid
    + return +
    invalid state
    } @@ -55,7 +55,7 @@ export function SolveScreen(): VNode { function onCancel(): void { reducer?.back() } - + return (
    - - -
    + + +
    ); } @@ -82,13 +82,13 @@ export interface SolveEntryProps { challenge: ChallengeInfo; feedback?: ChallengeFeedback; answer: string; - setAnswer: (s:string) => void; + setAnswer: (s: string) => void; } function SolveSmsEntry({ challenge, answer, setAnswer }: SolveEntryProps): VNode { return ( -

    An sms has been sent to "{challenge.instructions}". Type the code below

    - +

    An sms has been sent to "{challenge.instructions}". Type the code below

    +
    ); } diff --git a/packages/anastasis-webui/src/pages/home/authMethodSetup/AuthMethodEmailSetup.stories.tsx b/packages/anastasis-webui/src/pages/home/authMethodSetup/AuthMethodEmailSetup.stories.tsx new file mode 100644 index 000000000..e178a4955 --- /dev/null +++ b/packages/anastasis-webui/src/pages/home/authMethodSetup/AuthMethodEmailSetup.stories.tsx @@ -0,0 +1,66 @@ +/* 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 + */ + +/** +* +* @author Sebastian Javier Marchano (sebasjm) +*/ + +import { createExample, reducerStatesExample } from '../../../utils'; +import { authMethods as TestedComponent, KnownAuthMethods } from './index'; + + +export default { + title: 'Pages/backup/authMethods/email', + component: TestedComponent, + args: { + order: 5, + }, + argTypes: { + onUpdate: { action: 'onUpdate' }, + onBack: { action: 'onBack' }, + }, +}; + +const type: KnownAuthMethods = 'email' + +export const Empty = createExample(TestedComponent[type].screen, reducerStatesExample.authEditing, { + configured: [] +}); + +export const WithOneExample = createExample(TestedComponent[type].screen, reducerStatesExample.authEditing, { + configured: [{ + challenge: 'qwe', + type, + instructions: 'Email to sebasjm@email.com ', + remove: () => null + }] +}); + +export const WithMoreExamples = createExample(TestedComponent[type].screen, reducerStatesExample.authEditing, { + configured: [{ + challenge: 'qwe', + type, + instructions: 'Email to sebasjm@email.com', + remove: () => null + },{ + challenge: 'qwe', + type, + instructions: 'Email to someone@sebasjm.com', + remove: () => null + }] +}); diff --git a/packages/anastasis-webui/src/pages/home/authMethodSetup/AuthMethodEmailSetup.tsx b/packages/anastasis-webui/src/pages/home/authMethodSetup/AuthMethodEmailSetup.tsx new file mode 100644 index 000000000..e8cee9cb4 --- /dev/null +++ b/packages/anastasis-webui/src/pages/home/authMethodSetup/AuthMethodEmailSetup.tsx @@ -0,0 +1,62 @@ +/* eslint-disable @typescript-eslint/camelcase */ +import { + encodeCrock, + stringToBytes +} from "@gnu-taler/taler-util"; +import { Fragment, h, VNode } from "preact"; +import { useState } from "preact/hooks"; +import { AuthMethodSetupProps } from "../AuthenticationEditorScreen"; +import { AnastasisClientFrame } from "../index"; +import { TextInput } from "../../../components/fields/TextInput"; +import { EmailInput } from "../../../components/fields/EmailInput"; + +const EMAIL_PATTERN = /^(([^<>()\[\]\\.,;:\s@"]+(\.[^<>()\[\]\\.,;:\s@"]+)*)|(".+"))@((\[[0-9]{1,3}\.[0-9]{1,3}\.[0-9]{1,3}\.[0-9]{1,3}])|(([a-zA-Z\-0-9]+\.)+[a-zA-Z]{2,}))$/ + +export function AuthMethodEmailSetup({ cancel, addAuthMethod, configured }: AuthMethodSetupProps): VNode { + const [email, setEmail] = useState(""); + const addEmailAuth = (): void => addAuthMethod({ + authentication_method: { + type: "email", + instructions: `Email to ${email}`, + challenge: encodeCrock(stringToBytes(email)), + }, + }); + const emailError = !EMAIL_PATTERN.test(email) ? 'Email address is not valid' : undefined + const errors = !email ? 'Add your email' : emailError + + return ( + +

    + For email authentication, you need to provide an email address. When + recovering your secret, you will need to enter the code you receive by + email. +

    +
    + +
    + {configured.length > 0 &&
    +
    + Your emails: +
    + {configured.map((c, i) => { + return
    +

    {c.instructions}

    +
    +
    + })} +
    } +
    +
    + + + + +
    +
    +
    + ); +} diff --git a/packages/anastasis-webui/src/pages/home/authMethodSetup/AuthMethodIbanSetup.stories.tsx b/packages/anastasis-webui/src/pages/home/authMethodSetup/AuthMethodIbanSetup.stories.tsx new file mode 100644 index 000000000..71f618646 --- /dev/null +++ b/packages/anastasis-webui/src/pages/home/authMethodSetup/AuthMethodIbanSetup.stories.tsx @@ -0,0 +1,65 @@ +/* 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 + */ + +/** +* +* @author Sebastian Javier Marchano (sebasjm) +*/ + +import { createExample, reducerStatesExample } from '../../../utils'; +import { authMethods as TestedComponent, KnownAuthMethods } from './index'; + + +export default { + title: 'Pages/backup/authMethods/IBAN', + component: TestedComponent, + args: { + order: 5, + }, + argTypes: { + onUpdate: { action: 'onUpdate' }, + onBack: { action: 'onBack' }, + }, +}; + +const type: KnownAuthMethods = 'iban' + +export const Empty = createExample(TestedComponent[type].screen, reducerStatesExample.authEditing, { + configured: [] +}); + +export const WithOneExample = createExample(TestedComponent[type].screen, reducerStatesExample.authEditing, { + configured: [{ + challenge: 'qwe', + type, + instructions: 'Wire transfer from QWEASD123123 with holder Sebastian', + remove: () => null + }] +}); +export const WithMoreExamples = createExample(TestedComponent[type].screen, reducerStatesExample.authEditing, { + configured: [{ + challenge: 'qwe', + type, + instructions: 'Wire transfer from QWEASD123123 with holder Javier', + remove: () => null + },{ + challenge: 'qwe', + type, + instructions: 'Wire transfer from QWEASD123123 with holder Sebastian', + remove: () => null + }] +},); diff --git a/packages/anastasis-webui/src/pages/home/authMethodSetup/AuthMethodIbanSetup.tsx b/packages/anastasis-webui/src/pages/home/authMethodSetup/AuthMethodIbanSetup.tsx new file mode 100644 index 000000000..c9edbfa07 --- /dev/null +++ b/packages/anastasis-webui/src/pages/home/authMethodSetup/AuthMethodIbanSetup.tsx @@ -0,0 +1,68 @@ +/* eslint-disable @typescript-eslint/camelcase */ +import { + canonicalJson, + encodeCrock, + stringToBytes +} from "@gnu-taler/taler-util"; +import { h, VNode } from "preact"; +import { useState } from "preact/hooks"; +import { TextInput } from "../../../components/fields/TextInput"; +import { AuthMethodSetupProps } from "../AuthenticationEditorScreen"; +import { AnastasisClientFrame } from "../index"; + +export function AuthMethodIbanSetup({ addAuthMethod, cancel, configured }: AuthMethodSetupProps): VNode { + const [name, setName] = useState(""); + const [account, setAccount] = useState(""); + const addIbanAuth = (): void => addAuthMethod({ + authentication_method: { + type: "iban", + instructions: `Wire transfer from ${account} with holder ${name}`, + challenge: encodeCrock(stringToBytes(canonicalJson({ + name, account + }))), + }, + }); + const errors = !name ? 'Add an account name' : ( + !account ? 'Add an account IBAN number' : undefined + ) + return ( + +

    + For bank transfer authentication, you need to provide a bank + account (account holder name and IBAN). When recovering your + secret, you will be asked to pay the recovery fee via bank + transfer from the account you provided here. +

    +
    + + +
    + {configured.length > 0 &&
    +
    + Your bank accounts: +
    + {configured.map((c, i) => { + return
    +

    {c.instructions}

    +
    +
    + })} +
    } +
    +
    + + + + +
    +
    +
    + ); +} diff --git a/packages/anastasis-webui/src/pages/home/authMethodSetup/AuthMethodPostSetup.stories.tsx b/packages/anastasis-webui/src/pages/home/authMethodSetup/AuthMethodPostSetup.stories.tsx new file mode 100644 index 000000000..0f1c17495 --- /dev/null +++ b/packages/anastasis-webui/src/pages/home/authMethodSetup/AuthMethodPostSetup.stories.tsx @@ -0,0 +1,66 @@ +/* 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 + */ + +/** +* +* @author Sebastian Javier Marchano (sebasjm) +*/ + +import { createExample, reducerStatesExample } from '../../../utils'; +import { authMethods as TestedComponent, KnownAuthMethods } from './index'; + + +export default { + title: 'Pages/backup/authMethods/Post', + component: TestedComponent, + args: { + order: 5, + }, + argTypes: { + onUpdate: { action: 'onUpdate' }, + onBack: { action: 'onBack' }, + }, +}; + +const type: KnownAuthMethods = 'post' + +export const Empty = createExample(TestedComponent[type].screen, reducerStatesExample.authEditing, { + configured: [] +}); + +export const WithOneExample = createExample(TestedComponent[type].screen, reducerStatesExample.authEditing, { + configured: [{ + challenge: 'qwe', + type, + instructions: 'Letter to address in postal code QWE456', + remove: () => null + }] +}); + +export const WithMoreExamples = createExample(TestedComponent[type].screen, reducerStatesExample.authEditing, { + configured: [{ + challenge: 'qwe', + type, + instructions: 'Letter to address in postal code QWE456', + remove: () => null + },{ + challenge: 'qwe', + type, + instructions: 'Letter to address in postal code ABC123', + remove: () => null + }] +}); diff --git a/packages/anastasis-webui/src/pages/home/authMethodSetup/AuthMethodPostSetup.tsx b/packages/anastasis-webui/src/pages/home/authMethodSetup/AuthMethodPostSetup.tsx new file mode 100644 index 000000000..bfeaaa832 --- /dev/null +++ b/packages/anastasis-webui/src/pages/home/authMethodSetup/AuthMethodPostSetup.tsx @@ -0,0 +1,102 @@ +/* eslint-disable @typescript-eslint/camelcase */ +import { + canonicalJson, encodeCrock, + stringToBytes +} from "@gnu-taler/taler-util"; +import { Fragment, h, VNode } from "preact"; +import { useState } from "preact/hooks"; +import { AuthMethodSetupProps } from "../AuthenticationEditorScreen"; +import { TextInput } from "../../../components/fields/TextInput"; +import { AnastasisClientFrame } from ".."; + +export function AuthMethodPostSetup({ addAuthMethod, cancel, configured }: AuthMethodSetupProps): VNode { + const [fullName, setFullName] = useState(""); + const [street, setStreet] = useState(""); + const [city, setCity] = useState(""); + const [postcode, setPostcode] = useState(""); + const [country, setCountry] = useState(""); + + const addPostAuth = () => { + const challengeJson = { + full_name: fullName, + street, + city, + postcode, + country, + }; + addAuthMethod({ + authentication_method: { + type: "post", + instructions: `Letter to address in postal code ${postcode}`, + challenge: encodeCrock(stringToBytes(canonicalJson(challengeJson))), + }, + }); + }; + + const errors = !fullName ? 'The full name is missing' : ( + !street ? 'The street is missing' : ( + !city ? 'The city is missing' : ( + !postcode ? 'The postcode is missing' : ( + !country ? 'The country is missing' : undefined + ) + ) + ) + ) + return ( + +

    + For postal letter authentication, you need to provide a postal + address. When recovering your secret, you will be asked to enter a + code that you will receive in a letter to that address. +

    +
    + +
    +
    + +
    +
    + +
    +
    + +
    +
    + +
    + + {configured.length > 0 &&
    +
    + Your postal code: +
    + {configured.map((c, i) => { + return
    +

    {c.instructions}

    +
    +
    + })} +
    +
    } +
    + + + + +
    +
    + ); +} diff --git a/packages/anastasis-webui/src/pages/home/authMethodSetup/AuthMethodQuestionSetup.stories.tsx b/packages/anastasis-webui/src/pages/home/authMethodSetup/AuthMethodQuestionSetup.stories.tsx new file mode 100644 index 000000000..3ba4a84ca --- /dev/null +++ b/packages/anastasis-webui/src/pages/home/authMethodSetup/AuthMethodQuestionSetup.stories.tsx @@ -0,0 +1,66 @@ +/* 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 + */ + +/** +* +* @author Sebastian Javier Marchano (sebasjm) +*/ + +import { createExample, reducerStatesExample } from '../../../utils'; +import { authMethods as TestedComponent, KnownAuthMethods } from './index'; + + +export default { + title: 'Pages/backup/authMethods/Question', + component: TestedComponent, + args: { + order: 5, + }, + argTypes: { + onUpdate: { action: 'onUpdate' }, + onBack: { action: 'onBack' }, + }, +}; + +const type: KnownAuthMethods = 'question' + +export const Empty = createExample(TestedComponent[type].screen, reducerStatesExample.authEditing, { + configured: [] +}); + +export const WithOneExample = createExample(TestedComponent[type].screen, reducerStatesExample.authEditing, { + configured: [{ + challenge: 'qwe', + type, + instructions: 'Is integer factorization polynomial? (non-quantum computer)', + remove: () => null + }] +}); + +export const WithMoreExamples = createExample(TestedComponent[type].screen, reducerStatesExample.authEditing, { + configured: [{ + challenge: 'qwe', + type, + instructions: 'Does P equal NP?', + remove: () => null + },{ + challenge: 'asd', + type, + instructions: 'Are continuous groups automatically differential groups?', + remove: () => null + }] +}); diff --git a/packages/anastasis-webui/src/pages/home/authMethodSetup/AuthMethodQuestionSetup.tsx b/packages/anastasis-webui/src/pages/home/authMethodSetup/AuthMethodQuestionSetup.tsx new file mode 100644 index 000000000..eab800e35 --- /dev/null +++ b/packages/anastasis-webui/src/pages/home/authMethodSetup/AuthMethodQuestionSetup.tsx @@ -0,0 +1,70 @@ +/* eslint-disable @typescript-eslint/camelcase */ +import { + encodeCrock, + stringToBytes +} from "@gnu-taler/taler-util"; +import { Fragment, h, VNode } from "preact"; +import { useState } from "preact/hooks"; +import { AuthMethodSetupProps } from "../AuthenticationEditorScreen"; +import { AnastasisClientFrame } from "../index"; +import { TextInput } from "../../../components/fields/TextInput"; + +export function AuthMethodQuestionSetup({ cancel, addAuthMethod, configured }: AuthMethodSetupProps): VNode { + const [questionText, setQuestionText] = useState(""); + const [answerText, setAnswerText] = useState(""); + const addQuestionAuth = (): void => addAuthMethod({ + authentication_method: { + type: "question", + instructions: questionText, + challenge: encodeCrock(stringToBytes(answerText)), + }, + }); + + const errors = !questionText ? "Add your security question" : ( + !answerText ? 'Add the answer to your question' : undefined + ) + return ( + +
    +

    + For security question authentication, you need to provide a question + and its answer. When recovering your secret, you will be shown the + question and you will need to type the answer exactly as you typed it + here. +

    +
    + +
    +
    + +
    + + {configured.length > 0 &&
    +
    + Your security questions: +
    + {configured.map((c, i) => { + return
    +

    {c.instructions}

    +
    +
    + })} +
    } +
    + + + + +
    +
    +
    + ); +} diff --git a/packages/anastasis-webui/src/pages/home/authMethodSetup/AuthMethodSmsSetup.stories.tsx b/packages/anastasis-webui/src/pages/home/authMethodSetup/AuthMethodSmsSetup.stories.tsx new file mode 100644 index 000000000..ae8297ef7 --- /dev/null +++ b/packages/anastasis-webui/src/pages/home/authMethodSetup/AuthMethodSmsSetup.stories.tsx @@ -0,0 +1,66 @@ +/* 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 + */ + +/** +* +* @author Sebastian Javier Marchano (sebasjm) +*/ + +import { createExample, reducerStatesExample } from '../../../utils'; +import { authMethods as TestedComponent, KnownAuthMethods } from './index'; + + +export default { + title: 'Pages/backup/authMethods/Sms', + component: TestedComponent, + args: { + order: 5, + }, + argTypes: { + onUpdate: { action: 'onUpdate' }, + onBack: { action: 'onBack' }, + }, +}; + +const type: KnownAuthMethods = 'sms' + +export const Empty = createExample(TestedComponent[type].screen, reducerStatesExample.authEditing, { + configured: [] +}); + +export const WithOneExample = createExample(TestedComponent[type].screen, reducerStatesExample.authEditing, { + configured: [{ + challenge: 'qwe', + type, + instructions: 'SMS to +11-1234-2345', + remove: () => null + }] +}); + +export const WithMoreExamples = createExample(TestedComponent[type].screen, reducerStatesExample.authEditing, { + configured: [{ + challenge: 'qwe', + type, + instructions: 'SMS to +11-1234-2345', + remove: () => null + },{ + challenge: 'qwe', + type, + instructions: 'SMS to +11-5555-2345', + remove: () => null + }] +}); diff --git a/packages/anastasis-webui/src/pages/home/authMethodSetup/AuthMethodSmsSetup.tsx b/packages/anastasis-webui/src/pages/home/authMethodSetup/AuthMethodSmsSetup.tsx new file mode 100644 index 000000000..9e85af2b2 --- /dev/null +++ b/packages/anastasis-webui/src/pages/home/authMethodSetup/AuthMethodSmsSetup.tsx @@ -0,0 +1,63 @@ +/* eslint-disable @typescript-eslint/camelcase */ +import { + encodeCrock, + stringToBytes +} from "@gnu-taler/taler-util"; +import { Fragment, h, VNode } from "preact"; +import { useLayoutEffect, useRef, useState } from "preact/hooks"; +import { NumberInput } from "../../../components/fields/NumberInput"; +import { AuthMethodSetupProps } from "../AuthenticationEditorScreen"; +import { AnastasisClientFrame } from "../index"; + +export function AuthMethodSmsSetup({ addAuthMethod, cancel, configured }: AuthMethodSetupProps): VNode { + const [mobileNumber, setMobileNumber] = useState(""); + const addSmsAuth = (): void => { + addAuthMethod({ + authentication_method: { + type: "sms", + instructions: `SMS to ${mobileNumber}`, + challenge: encodeCrock(stringToBytes(mobileNumber)), + }, + }); + }; + const inputRef = useRef(null); + useLayoutEffect(() => { + inputRef.current?.focus(); + }, []); + const errors = !mobileNumber ? 'Add a mobile number' : undefined + return ( + +
    +

    + For SMS authentication, you need to provide a mobile number. When + recovering your secret, you will be asked to enter the code you + receive via SMS. +

    +
    + +
    + {configured.length > 0 &&
    +
    + Your mobile numbers: +
    + {configured.map((c, i) => { + return
    +

    {c.instructions}

    +
    +
    + })} +
    } +
    + + + + +
    +
    +
    + ); +} diff --git a/packages/anastasis-webui/src/pages/home/authMethodSetup/AuthMethodTotpSetup.stories.tsx b/packages/anastasis-webui/src/pages/home/authMethodSetup/AuthMethodTotpSetup.stories.tsx new file mode 100644 index 000000000..3447e3d61 --- /dev/null +++ b/packages/anastasis-webui/src/pages/home/authMethodSetup/AuthMethodTotpSetup.stories.tsx @@ -0,0 +1,64 @@ +/* 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 + */ + +/** +* +* @author Sebastian Javier Marchano (sebasjm) +*/ + +import { createExample, reducerStatesExample } from '../../../utils'; +import { authMethods as TestedComponent, KnownAuthMethods } from './index'; + + +export default { + title: 'Pages/backup/authMethods/TOTP', + component: TestedComponent, + args: { + order: 5, + }, + argTypes: { + onUpdate: { action: 'onUpdate' }, + onBack: { action: 'onBack' }, + }, +}; + +const type: KnownAuthMethods = 'totp' + +export const Empty = createExample(TestedComponent[type].screen, reducerStatesExample.authEditing, { + configured: [] +}); +export const WithOneExample = createExample(TestedComponent[type].screen, reducerStatesExample.authEditing, { + configured: [{ + challenge: 'qwe', + type, + instructions: 'instr', + remove: () => null + }] +}); +export const WithMoreExample = createExample(TestedComponent[type].screen, reducerStatesExample.authEditing, { + configured: [{ + challenge: 'qwe', + type, + instructions: 'instr', + remove: () => null + },{ + challenge: 'qwe', + type, + instructions: 'instr', + remove: () => null + }] +}); diff --git a/packages/anastasis-webui/src/pages/home/authMethodSetup/AuthMethodTotpSetup.tsx b/packages/anastasis-webui/src/pages/home/authMethodSetup/AuthMethodTotpSetup.tsx new file mode 100644 index 000000000..bbffedad6 --- /dev/null +++ b/packages/anastasis-webui/src/pages/home/authMethodSetup/AuthMethodTotpSetup.tsx @@ -0,0 +1,47 @@ +/* eslint-disable @typescript-eslint/camelcase */ +import { + encodeCrock, + stringToBytes +} from "@gnu-taler/taler-util"; +import { h, VNode } from "preact"; +import { useState } from "preact/hooks"; +import { AuthMethodSetupProps } from "../AuthenticationEditorScreen"; +import { AnastasisClientFrame } from "../index"; +import { TextInput } from "../../../components/fields/TextInput"; +import { QR } from "../../../components/QR"; + +export function AuthMethodTotpSetup({addAuthMethod, cancel, configured}: AuthMethodSetupProps): VNode { + const [name, setName] = useState(""); + const addTotpAuth = (): void => addAuthMethod({ + authentication_method: { + type: "totp", + instructions: `Enter code for ${name}`, + challenge: encodeCrock(stringToBytes(name)), + }, + }); + const errors = !name ? 'The TOTP name is missing' : undefined; + return ( + +

    + For Time-based One-Time Password (TOTP) authentication, you need to set + a name for the TOTP secret. Then, you must scan the generated QR code + with your TOTP App to import the TOTP secret into your TOTP App. +

    +
    + +
    + +
    +
    + + + + +
    +
    +
    + ); +} diff --git a/packages/anastasis-webui/src/pages/home/authMethodSetup/AuthMethodVideoSetup.stories.tsx b/packages/anastasis-webui/src/pages/home/authMethodSetup/AuthMethodVideoSetup.stories.tsx new file mode 100644 index 000000000..3c4c7bf39 --- /dev/null +++ b/packages/anastasis-webui/src/pages/home/authMethodSetup/AuthMethodVideoSetup.stories.tsx @@ -0,0 +1,66 @@ +/* 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 + */ + +/** +* +* @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/authMethods/Video', + component: TestedComponent, + args: { + order: 5, + }, + argTypes: { + onUpdate: { action: 'onUpdate' }, + onBack: { action: 'onBack' }, + }, +}; + +const type: KnownAuthMethods = 'video' + +export const Empty = createExample(TestedComponent[type].screen, reducerStatesExample.authEditing, { + configured: [] +}); + +export const WithOneExample = createExample(TestedComponent[type].screen, reducerStatesExample.authEditing, { + configured: [{ + challenge: 'qwe', + type, + instructions: logoImage, + remove: () => null + }] +}); + +export const WithMoreExamples = createExample(TestedComponent[type].screen, 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/authMethodSetup/AuthMethodVideoSetup.tsx b/packages/anastasis-webui/src/pages/home/authMethodSetup/AuthMethodVideoSetup.tsx new file mode 100644 index 000000000..d292a9d24 --- /dev/null +++ b/packages/anastasis-webui/src/pages/home/authMethodSetup/AuthMethodVideoSetup.tsx @@ -0,0 +1,56 @@ +/* eslint-disable @typescript-eslint/camelcase */ +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 "../AuthenticationEditorScreen"; +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: image, + challenge: encodeCrock(stringToBytes(image)), + }, + }) + }; + return ( + +

    + 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. +

    +
    + +
    + {configured.length > 0 &&
    +
    + Your photographs: +
    + {configured.map((c, i) => { + return
    + +
    +
    + })} +
    } +
    +
    + + +
    +
    +
    + ); +} diff --git a/packages/anastasis-webui/src/pages/home/authMethodSetup/index.tsx b/packages/anastasis-webui/src/pages/home/authMethodSetup/index.tsx new file mode 100644 index 000000000..1e1d7bc03 --- /dev/null +++ b/packages/anastasis-webui/src/pages/home/authMethodSetup/index.tsx @@ -0,0 +1,68 @@ +import { h, VNode } from "preact"; +import { AuthMethodSetupProps } from "../AuthenticationEditorScreen"; + +import { AuthMethodEmailSetup as EmailScreen } from "./AuthMethodEmailSetup"; +import { AuthMethodIbanSetup as IbanScreen } from "./AuthMethodIbanSetup"; +import { AuthMethodPostSetup as PostalScreen } from "./AuthMethodPostSetup"; +import { AuthMethodQuestionSetup as QuestionScreen } from "./AuthMethodQuestionSetup"; +import { AuthMethodSmsSetup as SmsScreen } from "./AuthMethodSmsSetup"; +import { AuthMethodTotpSetup as TotpScreen } from "./AuthMethodTotpSetup"; +import { AuthMethodVideoSetup as VideScreen } from "./AuthMethodVideoSetup"; +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'; + +interface AuthMethodConfiguration { + icon: VNode; + label: string; + screen: (props: AuthMethodSetupProps) => VNode; +} +export type KnownAuthMethods = "sms" | "email" | "post" | "question" | "video" | "totp" | "iban"; + +type KnowMethodConfig = { + [name in KnownAuthMethods]: AuthMethodConfiguration; +}; + +export const authMethods: KnowMethodConfig = { + question: { + icon: , + label: "Question", + screen: QuestionScreen + }, + sms: { + icon: , + label: "SMS", + screen: SmsScreen + }, + email: { + icon: , + label: "Email", + screen: EmailScreen + + }, + iban: { + icon: , + label: "IBAN", + screen: IbanScreen + + }, + post: { + icon: , + label: "Physical mail", + screen: PostalScreen + + }, + totp: { + icon: , + label: "TOTP", + screen: TotpScreen + + }, + video: { + icon: , + label: "Video", + screen: VideScreen + + } +} \ No newline at end of file diff --git a/packages/anastasis-webui/src/pages/home/index.tsx b/packages/anastasis-webui/src/pages/home/index.tsx index 5cef4ee9c..fefaa184c 100644 --- a/packages/anastasis-webui/src/pages/home/index.tsx +++ b/packages/anastasis-webui/src/pages/home/index.tsx @@ -11,7 +11,8 @@ import { VNode } from "preact"; import { - useErrorBoundary} from "preact/hooks"; + useErrorBoundary +} from "preact/hooks"; import { Menu } from "../../components/menu"; import { AnastasisProvider, useAnastasisContext } from "../../context/anastasis"; import { @@ -59,7 +60,7 @@ interface AnastasisClientFrameProps { /** * Hide only the "next" button. */ - hideNext?: boolean; + hideNext?: string; } function ErrorBoundary(props: { @@ -112,13 +113,15 @@ export function AnastasisClientFrame(props: AnastasisClientFrameProps): VNode {
    handleKeyPress(e)}> -

    {props.title}

    +

    {props.title}

    {props.children} {!props.hideNav ? ( -
    +
    - {!props.hideNext ? : null} + + +
    ) : null}
    @@ -151,18 +154,12 @@ const AnastasisClientImpl: FunctionalComponent = () => { if ( state.backup_state === BackupStates.ContinentSelecting || - state.recovery_state === RecoveryStates.ContinentSelecting - ) { - return ( - - ); - } - if ( + state.recovery_state === RecoveryStates.ContinentSelecting || state.backup_state === BackupStates.CountrySelecting || state.recovery_state === RecoveryStates.CountrySelecting ) { return ( - + ); } if ( diff --git a/packages/anastasis-webui/src/scss/main.scss b/packages/anastasis-webui/src/scss/main.scss index 2e60bf6f9..1e0d3fded 100644 --- a/packages/anastasis-webui/src/scss/main.scss +++ b/packages/anastasis-webui/src/scss/main.scss @@ -198,10 +198,10 @@ div[data-tooltip]::before { max-width: 40em; } -.home div { - margin-top: 0.5em; - margin-bottom: 0.5em; -} +// .home div { +// margin-top: 0.5em; +// margin-bottom: 0.5em; +// } .policy { padding: 0.5em; diff --git a/packages/anastasis-webui/src/utils/index.tsx b/packages/anastasis-webui/src/utils/index.tsx index 670e229cd..48ac47544 100644 --- a/packages/anastasis-webui/src/utils/index.tsx +++ b/packages/anastasis-webui/src/utils/index.tsx @@ -86,7 +86,13 @@ const base = { { type: "question", usage_fee: "COL:0" - } + },{ + type: "sms", + usage_fee: "COL:0" + },{ + type: "email", + usage_fee: "COL:0" + }, ], salt: "WBMDD76BR1E90YQ5AHBMKPH7GW", storage_limit_in_megabytes: 16, diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml index 283dafaf2..e38b0e856 100644 --- a/pnpm-lock.yaml +++ b/pnpm-lock.yaml @@ -61,6 +61,7 @@ importers: preact-cli: ^3.2.2 preact-render-to-string: ^5.1.4 preact-router: ^3.2.1 + qrcode-generator: ^1.4.4 sass: ^1.32.13 sass-loader: ^10.1.1 sirv-cli: ^1.0.0-next.3 @@ -73,6 +74,7 @@ importers: preact: 10.5.14 preact-render-to-string: 5.1.19_preact@10.5.14 preact-router: 3.2.1_preact@10.5.14 + qrcode-generator: 1.4.4 devDependencies: '@creativebulma/bulma-tooltip': 1.2.0 '@storybook/addon-a11y': 6.3.7