diff options
5 files changed, 209 insertions, 41 deletions
diff --git a/packages/anastasis-webui/src/pages/home/AddingProviderScreen.stories.tsx b/packages/anastasis-webui/src/pages/home/AddingProviderScreen.stories.tsx new file mode 100644 index 000000000..43807fefe --- /dev/null +++ b/packages/anastasis-webui/src/pages/home/AddingProviderScreen.stories.tsx @@ -0,0 +1,50 @@ +/* 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 { ReducerState } from 'anastasis-core'; +import { createExample, reducerStatesExample } from '../../utils'; +import { AddingProviderScreen as TestedComponent } from './AddingProviderScreen'; + + +export default { + title: 'Pages/backup/AddingProviderScreen', + component: TestedComponent, + args: { + order: 4, + }, + argTypes: { + onUpdate: { action: 'onUpdate' }, + onBack: { action: 'onBack' }, + }, +}; + +export const NewProvider = createExample(TestedComponent, { + ...reducerStatesExample.authEditing, +} as ReducerState); + +export const NewSMSProvider = createExample(TestedComponent, { + ...reducerStatesExample.authEditing, +} as ReducerState, { providerType: 'sms'}); + +export const NewIBANProvider = createExample(TestedComponent, { + ...reducerStatesExample.authEditing, +} as ReducerState, { providerType: 'iban' }); diff --git a/packages/anastasis-webui/src/pages/home/AddingProviderScreen.tsx b/packages/anastasis-webui/src/pages/home/AddingProviderScreen.tsx new file mode 100644 index 000000000..9c83da49e --- /dev/null +++ b/packages/anastasis-webui/src/pages/home/AddingProviderScreen.tsx @@ -0,0 +1,101 @@ +/* eslint-disable @typescript-eslint/camelcase */ +import { + encodeCrock, + stringToBytes +} from "@gnu-taler/taler-util"; +import { h, VNode } from "preact"; +import { useLayoutEffect, useRef, useState } from "preact/hooks"; +import { TextInput } from "../../components/fields/TextInput"; +import { authMethods, KnownAuthMethods } from "./authMethod"; +import { AnastasisClientFrame } from "./index"; + +interface Props { + providerType?: KnownAuthMethods; + cancel: () => void; +} +export function AddingProviderScreen({ providerType, cancel }: Props): VNode { + const [providerURL, setProviderURL] = useState(""); + const [error, setError] = useState<string | undefined>() + const providerLabel = providerType ? authMethods[providerType].label : undefined + + function testProvider(): void { + setError(undefined) + + fetch(`${providerURL}/config`) + .then(r => r.json().catch(d => ({}))) + .then(r => { + if (!("methods" in r) || !Array.isArray(r.methods)) { + setError("This provider doesn't have authentication method. Check the provider URL") + return; + } + if (!providerLabel) { + setError("") + return + } + let found = false + for (let i = 0; i < r.methods.length && !found; i++) { + found = r.methods[i].type !== providerType + } + if (!found) { + setError(`This provider does not support authentication method ${providerLabel}`) + } + }) + .catch(e => { + setError(`There was an error testing this provider, try another one. ${e.message}`) + }) + + } + function addProvider(): void { + // addAuthMethod({ + // authentication_method: { + // type: "sms", + // instructions: `SMS to ${providerURL}`, + // challenge: encodeCrock(stringToBytes(providerURL)), + // }, + // }); + } + const inputRef = useRef<HTMLInputElement>(null); + useLayoutEffect(() => { + inputRef.current?.focus(); + }, []); + + let errors = !providerURL ? 'Add provider URL' : undefined + try { + new URL(providerURL) + } catch { + errors = 'Check the URL' + } + if (!!error && !errors) { + errors = error + } + + return ( + <AnastasisClientFrame hideNav + title={!providerLabel ? `Backup: Adding a provider` : `Backup: Adding a ${providerLabel} provider`} + hideNext={errors}> + <div> + <p> + Add a provider url {errors} + </p> + <div class="container"> + <TextInput + label="Provider URL" + placeholder="https://provider.com" + grabFocus + bind={[providerURL, setProviderURL]} /> + </div> + {!!error && <p class="block has-text-danger">{error}</p>} + {error === "" && <p class="block has-text-success">This provider worked!</p>} + <div style={{ marginTop: '2em', display: 'flex', justifyContent: 'space-between' }}> + <button class="button" onClick={testProvider}>TEST</button> + </div> + <div style={{ marginTop: '2em', display: 'flex', justifyContent: 'space-between' }}> + <button class="button" onClick={cancel}>Cancel</button> + <span data-tooltip={errors}> + <button class="button is-info" disabled={errors !== undefined} onClick={addProvider}>Add</button> + </span> + </div> + </div> + </AnastasisClientFrame> + ); +} diff --git a/packages/anastasis-webui/src/pages/home/AuthenticationEditorScreen.tsx b/packages/anastasis-webui/src/pages/home/AuthenticationEditorScreen.tsx index ab482044f..b95d3f1e3 100644 --- a/packages/anastasis-webui/src/pages/home/AuthenticationEditorScreen.tsx +++ b/packages/anastasis-webui/src/pages/home/AuthenticationEditorScreen.tsx @@ -1,7 +1,8 @@ /* eslint-disable @typescript-eslint/camelcase */ import { AuthMethod } from "anastasis-core"; -import { ComponentChildren, h, VNode } from "preact"; +import { ComponentChildren, Fragment, h, VNode } from "preact"; import { useState } from "preact/hooks"; +import { TextInput } from "../../components/fields/TextInput"; import { useAnastasisContext } from "../../context/anastasis"; import { authMethods, KnownAuthMethods } from "./authMethod"; import { AnastasisClientFrame } from "./index"; @@ -13,6 +14,7 @@ const getKeys = Object.keys as <T extends object>(obj: T) => Array<keyof T> export function AuthenticationEditorScreen(): VNode { const [noProvidersAck, setNoProvidersAck] = useState(false) const [selectedMethod, setSelectedMethod] = useState<KnownAuthMethods | undefined>(undefined); + const [addingProvider, setAddingProvider] = useState<string | undefined>(undefined) const reducer = useAnastasisContext() if (!reducer) { @@ -62,36 +64,58 @@ export function AuthenticationEditorScreen(): VNode { }; const AuthSetup = authMethods[selectedMethod].screen ?? AuthMethodNotImplemented; - return ( + return (<Fragment> <AuthSetup cancel={cancel} configured={camByType[selectedMethod] || []} addAuthMethod={addMethod} method={selectedMethod} /> + + {!authAvailableSet.has(selectedMethod) && <ConfirmModal active + onCancel={cancel} description="No providers founds" label="Add a provider manually" + onConfirm={() => { + null + }} + > + 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) + <p> + <a>More about cloud providers</a> + </p> + </ConfirmModal>} + + </Fragment> ); } + + if (addingProvider !== undefined) { + return <div /> + } + function MethodButton(props: { method: KnownAuthMethods }): VNode { + if (authMethods[props.method].skip) return <div /> + return ( <div class="block"> <button style={{ justifyContent: 'space-between' }} class="button is-fullwidth" onClick={() => { - if (!authAvailableSet.has(props.method)) { - //open add sms dialog - } else { - setSelectedMethod(props.method); - } - if (reducer) reducer.dismissError(); + setSelectedMethod(props.method); }} > <div style={{ display: 'flex' }}> <span class="icon "> {authMethods[props.method].icon} </span> - <span> - {authMethods[props.method].label} - </span> + {authAvailableSet.has(props.method) ? + <span> + Add a {authMethods[props.method].label} challenge + </span> : + <span> + Add a {authMethods[props.method].label} provider + </span> + } </div> {!authAvailableSet.has(props.method) && <span class="icon has-text-danger" > @@ -111,41 +135,34 @@ export function AuthenticationEditorScreen(): VNode { return ( <AnastasisClientFrame title="Backup: Configure Authentication Methods" hideNext={errors}> <div class="columns"> - <div class="column is-half"> + <div class="column one-third"> <div> {getKeys(authMethods).map(method => <MethodButton key={method} method={method} />)} </div> - {authAvailableSet.size === 0 && <ConfirmModal active={!noProvidersAck} onCancel={() => setNoProvidersAck(true)} description="No providers founds" label="Add a provider manually"> + {authAvailableSet.size === 0 && <ConfirmModal active={!noProvidersAck} + onCancel={() => setNoProvidersAck(true)} description="No providers founds" label="Add a provider manually" + onConfirm={() => { + null + }} + > 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) <p> <a>More about cloud providers</a> </p> </ConfirmModal>} - - {/* {haveMethodsConfigured && ( - configuredAuthMethods.map((x, i) => { - return ( - <p key={i}> - {x.type} ({x.instructions}){" "} - <button class="button is-danger is-small" - onClick={() => reducer.transition("delete_authentication", { - authentication_method: i, - })} - > - Remove - </button> - </p> - ); - }) - )} */} </div> - <div class="column is-half"> - When recovering your wallet, you will be asked to verify your identity via the methods you configure here. - - <b>Explain the exclamation marks</b> - - <a>Explain how to add providers</a> + <div class="column two-third"> + <p class="block"> + When recovering your wallet, you will be asked to verify your identity via the methods you configure here. + The list of authentication method is defined by the backup provider list. + </p> + <p class="block"> + <button class="button is-info">Manage the backup provider's list</button> + </p> + {authAvailableSet.size > 0 && <p class="block"> + We couldn't find provider for some of the authentication methods. + </p>} </div> </div> </AnastasisClientFrame> diff --git a/packages/anastasis-webui/src/pages/home/ReviewPoliciesScreen.tsx b/packages/anastasis-webui/src/pages/home/ReviewPoliciesScreen.tsx index b8beb7b47..9441022b8 100644 --- a/packages/anastasis-webui/src/pages/home/ReviewPoliciesScreen.tsx +++ b/packages/anastasis-webui/src/pages/home/ReviewPoliciesScreen.tsx @@ -1,5 +1,4 @@ /* eslint-disable @typescript-eslint/camelcase */ -import { AuthMethod } from "anastasis-core"; import { h, VNode } from "preact"; import { useState } from "preact/hooks"; import { useAnastasisContext } from "../../context/anastasis"; @@ -51,8 +50,8 @@ export function ReviewPoliciesScreen(): VNode { {policies.length < 1 && <p class="block"> No policies had been created. Go back and add more authentication methods. </p>} - <div class="block" onClick={() => setEditingPolicy(policies.length + 1)}> - <button class="button is-success">Add new policy</button> + <div class="block" style={{justifyContent:'flex-end'}} > + <button class="button is-success" onClick={() => setEditingPolicy(policies.length + 1)}>Add new policy</button> </div> {policies.map((p, policy_index) => { const methods = p.methods diff --git a/packages/anastasis-webui/src/pages/home/authMethod/index.tsx b/packages/anastasis-webui/src/pages/home/authMethod/index.tsx index 1e1d7bc03..7b0cce883 100644 --- a/packages/anastasis-webui/src/pages/home/authMethod/index.tsx +++ b/packages/anastasis-webui/src/pages/home/authMethod/index.tsx @@ -17,6 +17,7 @@ interface AuthMethodConfiguration { icon: VNode; label: string; screen: (props: AuthMethodSetupProps) => VNode; + skip?: boolean; } export type KnownAuthMethods = "sms" | "email" | "post" | "question" | "video" | "totp" | "iban"; @@ -62,7 +63,7 @@ export const authMethods: KnowMethodConfig = { video: { icon: <img src={videoIcon} />, label: "Video", - screen: VideScreen - + screen: VideScreen, + skip: true, } }
\ No newline at end of file |