diff options
| author | Sebastian <sebasjm@gmail.com> | 2021-11-04 12:37:27 -0300 | 
|---|---|---|
| committer | Sebastian <sebasjm@gmail.com> | 2021-11-04 12:37:58 -0300 | 
| commit | 4ac0b23793417536281f7d3c67c4c954da795f88 (patch) | |
| tree | 8f793d8d8cd6b9f4755d3cdcb3a1fe1f00ec1fba /packages/anastasis-webui/src | |
| parent | c87be3707efe7a675599b37f9327c6810d5fc011 (diff) | |
testing provider screen
Diffstat (limited to 'packages/anastasis-webui/src')
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  | 
