testing provider screen

This commit is contained in:
Sebastian 2021-11-04 12:37:27 -03:00
parent c87be3707e
commit 4ac0b23793
No known key found for this signature in database
GPG Key ID: BE4FF68352439FC1
5 changed files with 209 additions and 41 deletions

View File

@ -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' });

View File

@ -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>
);
}

View File

@ -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();
}}
>
<div style={{ display: 'flex' }}>
<span class="icon ">
{authMethods[props.method].icon}
</span>
{authAvailableSet.has(props.method) ?
<span>
{authMethods[props.method].label}
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">
<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.
<b>Explain the exclamation marks</b>
<a>Explain how to add providers</a>
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>

View File

@ -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

View File

@ -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,
}
}