some solve challenge examples, WIP
This commit is contained in:
parent
292d647aa9
commit
6ef5fd21fc
@ -1,4 +1,3 @@
|
||||
/* eslint-disable @typescript-eslint/camelcase */
|
||||
/*
|
||||
This file is part of GNU Taler
|
||||
(C) 2021 Taler Systems S.A.
|
||||
@ -26,7 +25,7 @@ import { AddingProviderScreen as TestedComponent } from './AddingProviderScreen'
|
||||
|
||||
|
||||
export default {
|
||||
title: 'Pages/backup/AddingProviderScreen',
|
||||
title: 'Pages/backup/AuthorizationMethod/AddingProvider',
|
||||
component: TestedComponent,
|
||||
args: {
|
||||
order: 4,
|
||||
|
@ -1,4 +1,3 @@
|
||||
/* eslint-disable @typescript-eslint/camelcase */
|
||||
/*
|
||||
This file is part of GNU Taler
|
||||
(C) 2021 Taler Systems S.A.
|
||||
@ -26,10 +25,10 @@ import { AttributeEntryScreen as TestedComponent } from './AttributeEntryScreen'
|
||||
|
||||
|
||||
export default {
|
||||
title: 'Pages/AttributeEntryScreen',
|
||||
title: 'Pages/PersonalInformation',
|
||||
component: TestedComponent,
|
||||
args: {
|
||||
order: 4,
|
||||
order: 3,
|
||||
},
|
||||
argTypes: {
|
||||
onUpdate: { action: 'onUpdate' },
|
||||
|
@ -1,4 +1,3 @@
|
||||
/* eslint-disable @typescript-eslint/camelcase */
|
||||
/*
|
||||
This file is part of GNU Taler
|
||||
(C) 2021 Taler Systems S.A.
|
||||
@ -26,10 +25,10 @@ import { AuthenticationEditorScreen as TestedComponent } from './AuthenticationE
|
||||
|
||||
|
||||
export default {
|
||||
title: 'Pages/backup/AuthenticationEditorScreen',
|
||||
title: 'Pages/backup/AuthorizationMethod',
|
||||
component: TestedComponent,
|
||||
args: {
|
||||
order: 5,
|
||||
order: 4,
|
||||
},
|
||||
argTypes: {
|
||||
onUpdate: { action: 'onUpdate' },
|
||||
@ -37,7 +36,7 @@ export default {
|
||||
},
|
||||
};
|
||||
|
||||
export const Example = createExample(TestedComponent, reducerStatesExample.authEditing);
|
||||
export const InitialState = createExample(TestedComponent, reducerStatesExample.authEditing);
|
||||
export const OneAuthMethodConfigured = createExample(TestedComponent, {
|
||||
...reducerStatesExample.authEditing,
|
||||
authentication_methods: [{
|
||||
@ -86,8 +85,3 @@ export const NoAuthMethodProvided = createExample(TestedComponent, {
|
||||
authentication_providers: {},
|
||||
authentication_methods: []
|
||||
} as ReducerState);
|
||||
|
||||
// type: string;
|
||||
// instructions: string;
|
||||
// challenge: string;
|
||||
// mime_type?: string;
|
||||
|
@ -1,10 +1,8 @@
|
||||
/* eslint-disable @typescript-eslint/camelcase */
|
||||
import { AuthMethod } from "anastasis-core";
|
||||
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 { authMethods, AuthMethodSetupProps, AuthMethodWithRemove, KnownAuthMethods } from "./authMethod";
|
||||
import { AnastasisClientFrame } from "./index";
|
||||
|
||||
|
||||
@ -14,7 +12,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 [addingProvider, setAddingProvider] = useState<string | undefined>(undefined)
|
||||
|
||||
const reducer = useAnastasisContext()
|
||||
if (!reducer) {
|
||||
@ -63,7 +61,7 @@ export function AuthenticationEditorScreen(): VNode {
|
||||
setSelectedMethod(undefined);
|
||||
};
|
||||
|
||||
const AuthSetup = authMethods[selectedMethod].screen ?? AuthMethodNotImplemented;
|
||||
const AuthSetup = authMethods[selectedMethod].setup ?? AuthMethodNotImplemented;
|
||||
return (<Fragment>
|
||||
<AuthSetup
|
||||
cancel={cancel}
|
||||
@ -88,10 +86,6 @@ export function AuthenticationEditorScreen(): VNode {
|
||||
);
|
||||
}
|
||||
|
||||
if (addingProvider !== undefined) {
|
||||
return <div />
|
||||
}
|
||||
|
||||
function MethodButton(props: { method: KnownAuthMethods }): VNode {
|
||||
if (authMethods[props.method].skip) return <div />
|
||||
|
||||
@ -169,14 +163,6 @@ export function AuthenticationEditorScreen(): VNode {
|
||||
);
|
||||
}
|
||||
|
||||
type AuthMethodWithRemove = AuthMethod & { remove: () => void }
|
||||
export interface AuthMethodSetupProps {
|
||||
method: string;
|
||||
addAuthMethod: (x: any) => void;
|
||||
configured: AuthMethodWithRemove[];
|
||||
cancel: () => void;
|
||||
}
|
||||
|
||||
function AuthMethodNotImplemented(props: AuthMethodSetupProps): VNode {
|
||||
return (
|
||||
<AnastasisClientFrame hideNav title={`Add ${props.method} authentication`}>
|
||||
|
@ -1,4 +1,3 @@
|
||||
/* eslint-disable @typescript-eslint/camelcase */
|
||||
/*
|
||||
This file is part of GNU Taler
|
||||
(C) 2021 Taler Systems S.A.
|
||||
@ -26,10 +25,10 @@ import { BackupFinishedScreen as TestedComponent } from './BackupFinishedScreen'
|
||||
|
||||
|
||||
export default {
|
||||
title: 'Pages/backup/FinishedScreen',
|
||||
title: 'Pages/backup/Finished',
|
||||
component: TestedComponent,
|
||||
args: {
|
||||
order: 9,
|
||||
order: 8,
|
||||
},
|
||||
argTypes: {
|
||||
onUpdate: { action: 'onUpdate' },
|
||||
|
@ -24,7 +24,7 @@ import { createExample, reducerStatesExample } from "../../utils";
|
||||
import { ChallengeOverviewScreen as TestedComponent } from "./ChallengeOverviewScreen";
|
||||
|
||||
export default {
|
||||
title: "Pages/recovery/ChallengeOverviewScreen",
|
||||
title: "Pages/recovery/SolveChallenge/Overview",
|
||||
component: TestedComponent,
|
||||
args: {
|
||||
order: 5,
|
||||
|
@ -24,7 +24,7 @@ import { ChallengePayingScreen as TestedComponent } from './ChallengePayingScree
|
||||
|
||||
|
||||
export default {
|
||||
title: 'Pages/recovery/__ChallengePayingScreen',
|
||||
title: 'Pages/recovery/__ChallengePaying',
|
||||
component: TestedComponent,
|
||||
args: {
|
||||
order: 10,
|
||||
|
@ -26,7 +26,7 @@ import { EditPoliciesScreen as TestedComponent } from './EditPoliciesScreen';
|
||||
|
||||
|
||||
export default {
|
||||
title: 'Pages/backup/ReviewPoliciesScreen/EditPoliciesScreen',
|
||||
title: 'Pages/backup/ReviewPolicies/EditPolicies',
|
||||
args: {
|
||||
order: 6,
|
||||
},
|
||||
|
@ -1,4 +1,3 @@
|
||||
/* eslint-disable @typescript-eslint/camelcase */
|
||||
/*
|
||||
This file is part of GNU Taler
|
||||
(C) 2021 Taler Systems S.A.
|
||||
@ -26,10 +25,10 @@ import { PoliciesPayingScreen as TestedComponent } from './PoliciesPayingScreen'
|
||||
|
||||
|
||||
export default {
|
||||
title: 'Pages/backup/PoliciesPayingScreen',
|
||||
title: 'Pages/backup/__PoliciesPaying',
|
||||
component: TestedComponent,
|
||||
args: {
|
||||
order: 8,
|
||||
order: 9,
|
||||
},
|
||||
argTypes: {
|
||||
onUpdate: { action: 'onUpdate' },
|
||||
|
@ -26,7 +26,7 @@ import { RecoveryFinishedScreen as TestedComponent } from './RecoveryFinishedScr
|
||||
|
||||
|
||||
export default {
|
||||
title: 'Pages/recovery/FinishedScreen',
|
||||
title: 'Pages/recovery/Finished',
|
||||
args: {
|
||||
order: 7,
|
||||
},
|
||||
|
@ -1,4 +1,3 @@
|
||||
/* eslint-disable @typescript-eslint/camelcase */
|
||||
/*
|
||||
This file is part of GNU Taler
|
||||
(C) 2021 Taler Systems S.A.
|
||||
@ -26,7 +25,7 @@ import { ReviewPoliciesScreen as TestedComponent } from './ReviewPoliciesScreen'
|
||||
|
||||
|
||||
export default {
|
||||
title: 'Pages/backup/ReviewPoliciesScreen',
|
||||
title: 'Pages/backup/ReviewPolicies',
|
||||
args: {
|
||||
order: 6,
|
||||
},
|
||||
|
@ -1,4 +1,3 @@
|
||||
/* eslint-disable @typescript-eslint/camelcase */
|
||||
/*
|
||||
This file is part of GNU Taler
|
||||
(C) 2021 Taler Systems S.A.
|
||||
@ -26,7 +25,7 @@ import { SecretEditorScreen as TestedComponent } from './SecretEditorScreen';
|
||||
|
||||
|
||||
export default {
|
||||
title: 'Pages/backup/SecretEditorScreen',
|
||||
title: 'Pages/backup/SecretInput',
|
||||
component: TestedComponent,
|
||||
args: {
|
||||
order: 7,
|
||||
|
@ -25,7 +25,7 @@ import { SecretSelectionScreen as TestedComponent } from './SecretSelectionScree
|
||||
|
||||
|
||||
export default {
|
||||
title: 'Pages/recovery/SecretSelectionScreen',
|
||||
title: 'Pages/recovery/SecretSelection',
|
||||
component: TestedComponent,
|
||||
args: {
|
||||
order: 4,
|
||||
|
@ -25,7 +25,7 @@ import { SolveScreen as TestedComponent } from './SolveScreen';
|
||||
|
||||
|
||||
export default {
|
||||
title: 'Pages/recovery/SolveScreen',
|
||||
title: 'Pages/recovery/SolveChallenge/Solve',
|
||||
component: TestedComponent,
|
||||
args: {
|
||||
order: 6,
|
||||
|
@ -1,17 +1,14 @@
|
||||
import { Fragment, h, VNode } from "preact";
|
||||
import { useState } from "preact/hooks";
|
||||
import { AnastasisClientFrame } from ".";
|
||||
import {
|
||||
ChallengeFeedback,
|
||||
ChallengeFeedbackStatus,
|
||||
ChallengeInfo,
|
||||
ChallengeFeedbackStatus
|
||||
} from "../../../../anastasis-core/lib";
|
||||
import { AsyncButton } from "../../components/AsyncButton";
|
||||
import { TextInput } from "../../components/fields/TextInput";
|
||||
import { Notifications } from "../../components/Notifications";
|
||||
import { useAnastasisContext } from "../../context/anastasis";
|
||||
import { authMethods, AuthMethodSolveProps, KnownAuthMethods } from "./authMethod";
|
||||
|
||||
function SolveOverviewFeedbackDisplay(props: { feedback?: ChallengeFeedback }): VNode {
|
||||
export function SolveOverviewFeedbackDisplay(props: { feedback?: ChallengeFeedback }): VNode {
|
||||
const { feedback } = props;
|
||||
if (!feedback) {
|
||||
return <div />;
|
||||
@ -80,7 +77,6 @@ function SolveOverviewFeedbackDisplay(props: { feedback?: ChallengeFeedback }):
|
||||
|
||||
export function SolveScreen(): VNode {
|
||||
const reducer = useAnastasisContext();
|
||||
const [answer, setAnswer] = useState("");
|
||||
|
||||
if (!reducer) {
|
||||
return (
|
||||
@ -120,162 +116,30 @@ export function SolveScreen(): VNode {
|
||||
</AnastasisClientFrame>
|
||||
);
|
||||
}
|
||||
function SolveNotImplemented(): VNode {
|
||||
return (
|
||||
<AnastasisClientFrame hideNav title="Not implemented">
|
||||
<p>
|
||||
The challenge selected is not supported for this UI. Please update this
|
||||
version or try using another policy.
|
||||
</p>
|
||||
{reducer &&
|
||||
<div style={{ marginTop: '2em', display: 'flex', justifyContent: 'space-between' }}>
|
||||
<button class="button" onClick={() => reducer.back()}>Back</button>
|
||||
</div>
|
||||
}
|
||||
</AnastasisClientFrame>
|
||||
);
|
||||
}
|
||||
|
||||
|
||||
const chArr = reducer.currentReducerState.recovery_information.challenges;
|
||||
const challengeFeedback =
|
||||
reducer.currentReducerState.challenge_feedback ?? {};
|
||||
const selectedUuid = reducer.currentReducerState.selected_challenge_uuid;
|
||||
const challenges: {
|
||||
[uuid: string]: ChallengeInfo;
|
||||
} = {};
|
||||
for (const ch of chArr) {
|
||||
challenges[ch.uuid] = ch;
|
||||
}
|
||||
const selectedChallenge = challenges[selectedUuid];
|
||||
const dialogMap: Record<string, (p: SolveEntryProps) => h.JSX.Element> = {
|
||||
question: SolveQuestionEntry,
|
||||
sms: SolveSmsEntry,
|
||||
email: SolveEmailEntry,
|
||||
post: SolvePostEntry,
|
||||
};
|
||||
const SolveDialog =
|
||||
selectedChallenge === undefined
|
||||
? SolveUndefinedEntry
|
||||
: dialogMap[selectedChallenge.type] ?? SolveUnsupportedEntry;
|
||||
const selectedChallenge = chArr.find(ch => ch.uuid === selectedUuid)
|
||||
|
||||
async function onNext(): Promise<void> {
|
||||
return reducer?.transition("solve_challenge", { answer });
|
||||
}
|
||||
function onCancel(): void {
|
||||
reducer?.back();
|
||||
}
|
||||
const SolveDialog = !selectedChallenge || !authMethods[selectedChallenge.type as KnownAuthMethods] ?
|
||||
SolveNotImplemented :
|
||||
authMethods[selectedChallenge.type as KnownAuthMethods].solve ?? SolveNotImplemented
|
||||
|
||||
const feedback = challengeFeedback[selectedUuid]
|
||||
const shouldHideConfirm = feedback?.state === ChallengeFeedbackStatus.RateLimitExceeded
|
||||
|| feedback?.state === ChallengeFeedbackStatus.Redirect
|
||||
|| feedback?.state === ChallengeFeedbackStatus.Unsupported
|
||||
|| feedback?.state === ChallengeFeedbackStatus.TruthUnknown
|
||||
|
||||
return (
|
||||
<AnastasisClientFrame hideNav title="Recovery: Solve challenge">
|
||||
<SolveOverviewFeedbackDisplay
|
||||
feedback={feedback}
|
||||
/>
|
||||
<SolveDialog
|
||||
id={selectedUuid}
|
||||
answer={answer}
|
||||
setAnswer={setAnswer}
|
||||
challenge={selectedChallenge}
|
||||
feedback={feedback}
|
||||
/>
|
||||
|
||||
<div
|
||||
style={{
|
||||
marginTop: "2em",
|
||||
display: "flex",
|
||||
justifyContent: "space-between",
|
||||
}}
|
||||
>
|
||||
<button class="button" onClick={onCancel}>
|
||||
Cancel
|
||||
</button>
|
||||
{!shouldHideConfirm && <AsyncButton class="button is-info" onClick={onNext}>
|
||||
Confirm
|
||||
</AsyncButton>}
|
||||
</div>
|
||||
</AnastasisClientFrame>
|
||||
);
|
||||
}
|
||||
|
||||
export interface SolveEntryProps {
|
||||
id: string;
|
||||
challenge: ChallengeInfo;
|
||||
feedback?: ChallengeFeedback;
|
||||
answer: string;
|
||||
setAnswer: (s: string) => void;
|
||||
}
|
||||
|
||||
function SolveSmsEntry({
|
||||
challenge,
|
||||
answer,
|
||||
setAnswer,
|
||||
}: SolveEntryProps): VNode {
|
||||
return (
|
||||
<Fragment>
|
||||
|
||||
<p>
|
||||
An sms has been sent to "<b>{challenge.instructions}</b>". Type the code
|
||||
below
|
||||
</p>
|
||||
<TextInput label="Answer" grabFocus bind={[answer, setAnswer]} />
|
||||
</Fragment>
|
||||
);
|
||||
}
|
||||
function SolveQuestionEntry({
|
||||
challenge,
|
||||
answer,
|
||||
setAnswer,
|
||||
}: SolveEntryProps): VNode {
|
||||
return (
|
||||
<Fragment>
|
||||
<p>Type the answer to the following question:</p>
|
||||
<pre>{challenge.instructions}</pre>
|
||||
<TextInput label="Answer" grabFocus bind={[answer, setAnswer]} />
|
||||
</Fragment>
|
||||
);
|
||||
}
|
||||
|
||||
function SolvePostEntry({
|
||||
challenge,
|
||||
answer,
|
||||
setAnswer,
|
||||
}: SolveEntryProps): VNode {
|
||||
return (
|
||||
<Fragment>
|
||||
<p>
|
||||
instruction for post type challenge "<b>{challenge.instructions}</b>"
|
||||
</p>
|
||||
<TextInput label="Answer" grabFocus bind={[answer, setAnswer]} />
|
||||
</Fragment>
|
||||
);
|
||||
}
|
||||
|
||||
function SolveEmailEntry({
|
||||
challenge,
|
||||
answer,
|
||||
setAnswer,
|
||||
}: SolveEntryProps): VNode {
|
||||
return (
|
||||
<Fragment>
|
||||
<p>
|
||||
An email has been sent to "<b>{challenge.instructions}</b>". Type the
|
||||
code below
|
||||
</p>
|
||||
<TextInput label="Answer" grabFocus bind={[answer, setAnswer]} />
|
||||
</Fragment>
|
||||
);
|
||||
}
|
||||
|
||||
function SolveUnsupportedEntry(props: SolveEntryProps): VNode {
|
||||
return (
|
||||
<Fragment>
|
||||
<p>
|
||||
The challenge selected is not supported for this UI. Please update this
|
||||
version or try using another policy.
|
||||
</p>
|
||||
<p>
|
||||
<b>Challenge type:</b> {props.challenge.type}
|
||||
</p>
|
||||
</Fragment>
|
||||
);
|
||||
}
|
||||
function SolveUndefinedEntry(props: SolveEntryProps): VNode {
|
||||
return (
|
||||
<Fragment>
|
||||
<p>
|
||||
There is no challenge information for id <b>"{props.id}"</b>. Try
|
||||
resetting the recovery session.
|
||||
</p>
|
||||
</Fragment>
|
||||
);
|
||||
return <SolveDialog id={selectedUuid} />
|
||||
}
|
||||
|
@ -24,7 +24,7 @@ import { StartScreen as TestedComponent } from './StartScreen';
|
||||
|
||||
|
||||
export default {
|
||||
title: 'Pages/StartScreen',
|
||||
title: 'Pages/Start',
|
||||
component: TestedComponent,
|
||||
args: {
|
||||
order: 1,
|
||||
|
@ -25,7 +25,7 @@ import { TruthsPayingScreen as TestedComponent } from './TruthsPayingScreen';
|
||||
|
||||
|
||||
export default {
|
||||
title: 'Pages/backup/__TruthsPayingScreen',
|
||||
title: 'Pages/backup/__TruthsPaying',
|
||||
component: TestedComponent,
|
||||
args: {
|
||||
order: 10,
|
||||
|
@ -1,4 +1,3 @@
|
||||
/* eslint-disable @typescript-eslint/camelcase */
|
||||
/*
|
||||
This file is part of GNU Taler
|
||||
(C) 2021 Taler Systems S.A.
|
||||
@ -25,7 +24,7 @@ import { authMethods as TestedComponent, KnownAuthMethods } from './index';
|
||||
|
||||
|
||||
export default {
|
||||
title: 'Pages/backup/authMethods/email',
|
||||
title: 'Pages/backup/AuthorizationMethod/AuthMethods/email',
|
||||
component: TestedComponent,
|
||||
args: {
|
||||
order: 5,
|
||||
@ -38,11 +37,11 @@ export default {
|
||||
|
||||
const type: KnownAuthMethods = 'email'
|
||||
|
||||
export const Empty = createExample(TestedComponent[type].screen, reducerStatesExample.authEditing, {
|
||||
export const Empty = createExample(TestedComponent[type].setup, reducerStatesExample.authEditing, {
|
||||
configured: []
|
||||
});
|
||||
|
||||
export const WithOneExample = createExample(TestedComponent[type].screen, reducerStatesExample.authEditing, {
|
||||
export const WithOneExample = createExample(TestedComponent[type].setup, reducerStatesExample.authEditing, {
|
||||
configured: [{
|
||||
challenge: 'qwe',
|
||||
type,
|
||||
@ -51,7 +50,7 @@ export const WithOneExample = createExample(TestedComponent[type].screen, reduce
|
||||
}]
|
||||
});
|
||||
|
||||
export const WithMoreExamples = createExample(TestedComponent[type].screen, reducerStatesExample.authEditing, {
|
||||
export const WithMoreExamples = createExample(TestedComponent[type].setup, reducerStatesExample.authEditing, {
|
||||
configured: [{
|
||||
challenge: 'qwe',
|
||||
type,
|
||||
|
@ -1,14 +1,12 @@
|
||||
/* eslint-disable @typescript-eslint/camelcase */
|
||||
import {
|
||||
encodeCrock,
|
||||
stringToBytes
|
||||
} from "@gnu-taler/taler-util";
|
||||
import { Fragment, h, VNode } from "preact";
|
||||
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 { EmailInput } from "../../../components/fields/EmailInput";
|
||||
import { AnastasisClientFrame } from "../index";
|
||||
import { AuthMethodSetupProps } from "./index";
|
||||
|
||||
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,}))$/
|
||||
|
||||
|
@ -0,0 +1,80 @@
|
||||
/*
|
||||
This file is part of GNU Taler
|
||||
(C) 2021 Taler Systems S.A.
|
||||
|
||||
GNU Taler is free software; you can redistribute it and/or modify it under the
|
||||
terms of the GNU General Public License as published by the Free Software
|
||||
Foundation; either version 3, or (at your option) any later version.
|
||||
|
||||
GNU Taler is distributed in the hope that it will be useful, but WITHOUT ANY
|
||||
WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR
|
||||
A PARTICULAR PURPOSE. See the GNU General Public License for more details.
|
||||
|
||||
You should have received a copy of the GNU General Public License along with
|
||||
GNU Taler; see the file COPYING. If not, see <http://www.gnu.org/licenses/>
|
||||
*/
|
||||
|
||||
/**
|
||||
*
|
||||
* @author Sebastian Javier Marchano (sebasjm)
|
||||
*/
|
||||
|
||||
import { ChallengeFeedbackStatus, ReducerState } from 'anastasis-core';
|
||||
import { createExample, reducerStatesExample } from '../../../utils';
|
||||
import { authMethods as TestedComponent, KnownAuthMethods } from './index';
|
||||
|
||||
|
||||
export default {
|
||||
title: 'Pages/recovery/SolveChallenge/AuthMethods/email',
|
||||
component: TestedComponent,
|
||||
args: {
|
||||
order: 5,
|
||||
},
|
||||
argTypes: {
|
||||
onUpdate: { action: 'onUpdate' },
|
||||
onBack: { action: 'onBack' },
|
||||
},
|
||||
};
|
||||
|
||||
const type: KnownAuthMethods = 'email'
|
||||
|
||||
export const WithoutFeedback = createExample(TestedComponent[type].solve, {
|
||||
...reducerStatesExample.challengeSolving,
|
||||
recovery_information: {
|
||||
challenges: [{
|
||||
cost: 'USD:1',
|
||||
instructions: 'does P equals NP?',
|
||||
type: 'question',
|
||||
uuid: 'uuid-1'
|
||||
}],
|
||||
policies: [],
|
||||
},
|
||||
selected_challenge_uuid: 'uuid-1',
|
||||
} as ReducerState, {
|
||||
id: 'uuid-1',
|
||||
});
|
||||
|
||||
export const PaymentFeedback = createExample(TestedComponent[type].solve, {
|
||||
...reducerStatesExample.challengeSolving,
|
||||
recovery_information: {
|
||||
challenges: [{
|
||||
cost: 'USD:1',
|
||||
instructions: 'does P equals NP?',
|
||||
type: 'question',
|
||||
uuid: 'uuid-1'
|
||||
}],
|
||||
policies: [],
|
||||
},
|
||||
selected_challenge_uuid: 'uuid-1',
|
||||
challenge_feedback: {
|
||||
'uuid-1': {
|
||||
state: ChallengeFeedbackStatus.Payment,
|
||||
taler_pay_uri: "taler://pay/...",
|
||||
provider: "https://localhost:8080/",
|
||||
payment_secret: "3P4561HAMHRRYEYD6CM6J7TS5VTD5SR2K2EXJDZEFSX92XKHR4KG"
|
||||
}
|
||||
}
|
||||
} as ReducerState, {
|
||||
id: 'uuid-1',
|
||||
});
|
||||
|
@ -0,0 +1,106 @@
|
||||
import { ChallengeFeedbackStatus, ChallengeInfo } from "anastasis-core";
|
||||
import { h, VNode } from "preact";
|
||||
import { useState } from "preact/hooks";
|
||||
import { AsyncButton } from "../../../components/AsyncButton";
|
||||
import { TextInput } from "../../../components/fields/TextInput";
|
||||
import { useAnastasisContext } from "../../../context/anastasis";
|
||||
import { AnastasisClientFrame } from "../index";
|
||||
import { SolveOverviewFeedbackDisplay } from "../SolveScreen";
|
||||
import { AuthMethodSolveProps } from "./index";
|
||||
|
||||
export function AuthMethodEmailSolve({ id }: AuthMethodSolveProps): VNode {
|
||||
const [answer, setAnswer] = useState("");
|
||||
|
||||
const reducer = useAnastasisContext();
|
||||
if (!reducer) {
|
||||
return (
|
||||
<AnastasisClientFrame hideNav title="Recovery problem">
|
||||
<div>no reducer in context</div>
|
||||
</AnastasisClientFrame>
|
||||
);
|
||||
}
|
||||
if (
|
||||
!reducer.currentReducerState ||
|
||||
reducer.currentReducerState.recovery_state === undefined
|
||||
) {
|
||||
return (
|
||||
<AnastasisClientFrame hideNav title="Recovery problem">
|
||||
<div>invalid state</div>
|
||||
</AnastasisClientFrame>
|
||||
);
|
||||
}
|
||||
|
||||
if (!reducer.currentReducerState.recovery_information) {
|
||||
return (
|
||||
<AnastasisClientFrame
|
||||
hideNext="Recovery document not found"
|
||||
title="Recovery problem"
|
||||
>
|
||||
<div>no recovery information found</div>
|
||||
</AnastasisClientFrame>
|
||||
);
|
||||
}
|
||||
if (!reducer.currentReducerState.selected_challenge_uuid) {
|
||||
return (
|
||||
<AnastasisClientFrame hideNav title="Recovery problem">
|
||||
<div>invalid state</div>
|
||||
<div style={{ marginTop: '2em', display: 'flex', justifyContent: 'space-between' }}>
|
||||
<button class="button" onClick={() => reducer.back()}>Back</button>
|
||||
</div>
|
||||
</AnastasisClientFrame>
|
||||
);
|
||||
}
|
||||
|
||||
const chArr = reducer.currentReducerState.recovery_information.challenges;
|
||||
const challengeFeedback =
|
||||
reducer.currentReducerState.challenge_feedback ?? {};
|
||||
const selectedUuid = reducer.currentReducerState.selected_challenge_uuid;
|
||||
const challenges: {
|
||||
[uuid: string]: ChallengeInfo;
|
||||
} = {};
|
||||
for (const ch of chArr) {
|
||||
challenges[ch.uuid] = ch;
|
||||
}
|
||||
const selectedChallenge = challenges[selectedUuid];
|
||||
const feedback = challengeFeedback[selectedUuid]
|
||||
|
||||
|
||||
async function onNext(): Promise<void> {
|
||||
return reducer?.transition("solve_challenge", { answer });
|
||||
}
|
||||
function onCancel(): void {
|
||||
reducer?.back();
|
||||
}
|
||||
|
||||
|
||||
const shouldHideConfirm = feedback?.state === ChallengeFeedbackStatus.RateLimitExceeded
|
||||
|| feedback?.state === ChallengeFeedbackStatus.Redirect
|
||||
|| feedback?.state === ChallengeFeedbackStatus.Unsupported
|
||||
|| feedback?.state === ChallengeFeedbackStatus.TruthUnknown
|
||||
|
||||
return (
|
||||
<AnastasisClientFrame hideNav title="Add email authentication">
|
||||
<SolveOverviewFeedbackDisplay feedback={feedback} />
|
||||
<p>
|
||||
An email has been sent to "<b>{selectedChallenge.instructions}</b>". Type the
|
||||
code below
|
||||
</p>
|
||||
<TextInput label="Answer" grabFocus bind={[answer, setAnswer]} />
|
||||
|
||||
<div
|
||||
style={{
|
||||
marginTop: "2em",
|
||||
display: "flex",
|
||||
justifyContent: "space-between",
|
||||
}}
|
||||
>
|
||||
<button class="button" onClick={onCancel}>
|
||||
Cancel
|
||||
</button>
|
||||
{!shouldHideConfirm && <AsyncButton class="button is-info" onClick={onNext}>
|
||||
Confirm
|
||||
</AsyncButton>}
|
||||
</div>
|
||||
</AnastasisClientFrame>
|
||||
);
|
||||
}
|
@ -1,4 +1,3 @@
|
||||
/* eslint-disable @typescript-eslint/camelcase */
|
||||
/*
|
||||
This file is part of GNU Taler
|
||||
(C) 2021 Taler Systems S.A.
|
||||
@ -25,7 +24,7 @@ import { authMethods as TestedComponent, KnownAuthMethods } from './index';
|
||||
|
||||
|
||||
export default {
|
||||
title: 'Pages/backup/authMethods/IBAN',
|
||||
title: 'Pages/backup/AuthorizationMethod/AuthMethods/IBAN',
|
||||
component: TestedComponent,
|
||||
args: {
|
||||
order: 5,
|
||||
@ -38,11 +37,11 @@ export default {
|
||||
|
||||
const type: KnownAuthMethods = 'iban'
|
||||
|
||||
export const Empty = createExample(TestedComponent[type].screen, reducerStatesExample.authEditing, {
|
||||
export const Empty = createExample(TestedComponent[type].setup, reducerStatesExample.authEditing, {
|
||||
configured: []
|
||||
});
|
||||
|
||||
export const WithOneExample = createExample(TestedComponent[type].screen, reducerStatesExample.authEditing, {
|
||||
export const WithOneExample = createExample(TestedComponent[type].setup, reducerStatesExample.authEditing, {
|
||||
configured: [{
|
||||
challenge: 'qwe',
|
||||
type,
|
||||
@ -50,7 +49,7 @@ export const WithOneExample = createExample(TestedComponent[type].screen, reduce
|
||||
remove: () => null
|
||||
}]
|
||||
});
|
||||
export const WithMoreExamples = createExample(TestedComponent[type].screen, reducerStatesExample.authEditing, {
|
||||
export const WithMoreExamples = createExample(TestedComponent[type].setup, reducerStatesExample.authEditing, {
|
||||
configured: [{
|
||||
challenge: 'qwe',
|
||||
type,
|
||||
|
@ -1,4 +1,3 @@
|
||||
/* eslint-disable @typescript-eslint/camelcase */
|
||||
import {
|
||||
canonicalJson,
|
||||
encodeCrock,
|
||||
@ -6,8 +5,8 @@ import {
|
||||
} from "@gnu-taler/taler-util";
|
||||
import { h, VNode } from "preact";
|
||||
import { useState } from "preact/hooks";
|
||||
import { AuthMethodSetupProps } from ".";
|
||||
import { TextInput } from "../../../components/fields/TextInput";
|
||||
import { AuthMethodSetupProps } from "../AuthenticationEditorScreen";
|
||||
import { AnastasisClientFrame } from "../index";
|
||||
|
||||
export function AuthMethodIbanSetup({ addAuthMethod, cancel, configured }: AuthMethodSetupProps): VNode {
|
||||
|
@ -0,0 +1,56 @@
|
||||
/*
|
||||
This file is part of GNU Taler
|
||||
(C) 2021 Taler Systems S.A.
|
||||
|
||||
GNU Taler is free software; you can redistribute it and/or modify it under the
|
||||
terms of the GNU General Public License as published by the Free Software
|
||||
Foundation; either version 3, or (at your option) any later version.
|
||||
|
||||
GNU Taler is distributed in the hope that it will be useful, but WITHOUT ANY
|
||||
WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR
|
||||
A PARTICULAR PURPOSE. See the GNU General Public License for more details.
|
||||
|
||||
You should have received a copy of the GNU General Public License along with
|
||||
GNU Taler; see the file COPYING. If not, see <http://www.gnu.org/licenses/>
|
||||
*/
|
||||
|
||||
/**
|
||||
*
|
||||
* @author Sebastian Javier Marchano (sebasjm)
|
||||
*/
|
||||
|
||||
import { ChallengeFeedbackStatus, ReducerState } from 'anastasis-core';
|
||||
import { createExample, reducerStatesExample } from '../../../utils';
|
||||
import { authMethods as TestedComponent, KnownAuthMethods } from './index';
|
||||
|
||||
|
||||
export default {
|
||||
title: 'Pages/recovery/SolveChallenge/AuthMethods/Iban',
|
||||
component: TestedComponent,
|
||||
args: {
|
||||
order: 5,
|
||||
},
|
||||
argTypes: {
|
||||
onUpdate: { action: 'onUpdate' },
|
||||
onBack: { action: 'onBack' },
|
||||
},
|
||||
};
|
||||
|
||||
const type: KnownAuthMethods = 'iban'
|
||||
|
||||
export const WithoutFeedback = createExample(TestedComponent[type].solve, {
|
||||
...reducerStatesExample.challengeSolving,
|
||||
recovery_information: {
|
||||
challenges: [{
|
||||
cost: 'USD:1',
|
||||
instructions: 'does P equals NP?',
|
||||
type: 'question',
|
||||
uuid: 'uuid-1'
|
||||
}],
|
||||
policies: [],
|
||||
},
|
||||
selected_challenge_uuid: 'uuid-1',
|
||||
} as ReducerState, {
|
||||
id: 'uuid-1',
|
||||
});
|
||||
|
@ -0,0 +1,105 @@
|
||||
import { ChallengeFeedbackStatus, ChallengeInfo } from "anastasis-core";
|
||||
import { h, VNode } from "preact";
|
||||
import { useState } from "preact/hooks";
|
||||
import { AsyncButton } from "../../../components/AsyncButton";
|
||||
import { TextInput } from "../../../components/fields/TextInput";
|
||||
import { useAnastasisContext } from "../../../context/anastasis";
|
||||
import { AnastasisClientFrame } from "../index";
|
||||
import { SolveOverviewFeedbackDisplay } from "../SolveScreen";
|
||||
import { AuthMethodSolveProps } from "./index";
|
||||
|
||||
export function AuthMethodIbanSolve({ id }: AuthMethodSolveProps): VNode {
|
||||
const [answer, setAnswer] = useState("");
|
||||
|
||||
const reducer = useAnastasisContext();
|
||||
if (!reducer) {
|
||||
return (
|
||||
<AnastasisClientFrame hideNav title="Recovery problem">
|
||||
<div>no reducer in context</div>
|
||||
</AnastasisClientFrame>
|
||||
);
|
||||
}
|
||||
if (
|
||||
!reducer.currentReducerState ||
|
||||
reducer.currentReducerState.recovery_state === undefined
|
||||
) {
|
||||
return (
|
||||
<AnastasisClientFrame hideNav title="Recovery problem">
|
||||
<div>invalid state</div>
|
||||
</AnastasisClientFrame>
|
||||
);
|
||||
}
|
||||
|
||||
if (!reducer.currentReducerState.recovery_information) {
|
||||
return (
|
||||
<AnastasisClientFrame
|
||||
hideNext="Recovery document not found"
|
||||
title="Recovery problem"
|
||||
>
|
||||
<div>no recovery information found</div>
|
||||
</AnastasisClientFrame>
|
||||
);
|
||||
}
|
||||
if (!reducer.currentReducerState.selected_challenge_uuid) {
|
||||
return (
|
||||
<AnastasisClientFrame hideNav title="Recovery problem">
|
||||
<div>invalid state</div>
|
||||
<div style={{ marginTop: '2em', display: 'flex', justifyContent: 'space-between' }}>
|
||||
<button class="button" onClick={() => reducer.back()}>Back</button>
|
||||
</div>
|
||||
</AnastasisClientFrame>
|
||||
);
|
||||
}
|
||||
|
||||
const chArr = reducer.currentReducerState.recovery_information.challenges;
|
||||
const challengeFeedback =
|
||||
reducer.currentReducerState.challenge_feedback ?? {};
|
||||
const selectedUuid = reducer.currentReducerState.selected_challenge_uuid;
|
||||
const challenges: {
|
||||
[uuid: string]: ChallengeInfo;
|
||||
} = {};
|
||||
for (const ch of chArr) {
|
||||
challenges[ch.uuid] = ch;
|
||||
}
|
||||
const selectedChallenge = challenges[selectedUuid];
|
||||
const feedback = challengeFeedback[selectedUuid]
|
||||
|
||||
|
||||
async function onNext(): Promise<void> {
|
||||
return reducer?.transition("solve_challenge", { answer });
|
||||
}
|
||||
function onCancel(): void {
|
||||
reducer?.back();
|
||||
}
|
||||
|
||||
|
||||
const shouldHideConfirm = feedback?.state === ChallengeFeedbackStatus.RateLimitExceeded
|
||||
|| feedback?.state === ChallengeFeedbackStatus.Redirect
|
||||
|| feedback?.state === ChallengeFeedbackStatus.Unsupported
|
||||
|| feedback?.state === ChallengeFeedbackStatus.TruthUnknown
|
||||
|
||||
return (
|
||||
<AnastasisClientFrame hideNav title="Add email authentication">
|
||||
<SolveOverviewFeedbackDisplay feedback={feedback} />
|
||||
<p>
|
||||
Send a wire transfer to the address
|
||||
</p>
|
||||
<TextInput label="Answer" grabFocus bind={[answer, setAnswer]} />
|
||||
|
||||
<div
|
||||
style={{
|
||||
marginTop: "2em",
|
||||
display: "flex",
|
||||
justifyContent: "space-between",
|
||||
}}
|
||||
>
|
||||
<button class="button" onClick={onCancel}>
|
||||
Cancel
|
||||
</button>
|
||||
{!shouldHideConfirm && <AsyncButton class="button is-info" onClick={onNext}>
|
||||
Confirm
|
||||
</AsyncButton>}
|
||||
</div>
|
||||
</AnastasisClientFrame>
|
||||
);
|
||||
}
|
@ -25,7 +25,7 @@ import { authMethods as TestedComponent, KnownAuthMethods } from './index';
|
||||
|
||||
|
||||
export default {
|
||||
title: 'Pages/backup/authMethods/Post',
|
||||
title: 'Pages/backup/AuthorizationMethod/AuthMethods/Post',
|
||||
component: TestedComponent,
|
||||
args: {
|
||||
order: 5,
|
||||
@ -38,11 +38,11 @@ export default {
|
||||
|
||||
const type: KnownAuthMethods = 'post'
|
||||
|
||||
export const Empty = createExample(TestedComponent[type].screen, reducerStatesExample.authEditing, {
|
||||
export const Empty = createExample(TestedComponent[type].setup, reducerStatesExample.authEditing, {
|
||||
configured: []
|
||||
});
|
||||
|
||||
export const WithOneExample = createExample(TestedComponent[type].screen, reducerStatesExample.authEditing, {
|
||||
export const WithOneExample = createExample(TestedComponent[type].setup, reducerStatesExample.authEditing, {
|
||||
configured: [{
|
||||
challenge: 'qwe',
|
||||
type,
|
||||
@ -51,7 +51,7 @@ export const WithOneExample = createExample(TestedComponent[type].screen, reduce
|
||||
}]
|
||||
});
|
||||
|
||||
export const WithMoreExamples = createExample(TestedComponent[type].screen, reducerStatesExample.authEditing, {
|
||||
export const WithMoreExamples = createExample(TestedComponent[type].setup, reducerStatesExample.authEditing, {
|
||||
configured: [{
|
||||
challenge: 'qwe',
|
||||
type,
|
||||
|
@ -1,13 +1,12 @@
|
||||
/* eslint-disable @typescript-eslint/camelcase */
|
||||
import {
|
||||
canonicalJson, encodeCrock,
|
||||
stringToBytes
|
||||
} from "@gnu-taler/taler-util";
|
||||
import { Fragment, h, VNode } from "preact";
|
||||
import { h, VNode } from "preact";
|
||||
import { useState } from "preact/hooks";
|
||||
import { AuthMethodSetupProps } from "../AuthenticationEditorScreen";
|
||||
import { TextInput } from "../../../components/fields/TextInput";
|
||||
import { AnastasisClientFrame } from "..";
|
||||
import { TextInput } from "../../../components/fields/TextInput";
|
||||
import { AuthMethodSetupProps } from "./index";
|
||||
|
||||
export function AuthMethodPostSetup({ addAuthMethod, cancel, configured }: AuthMethodSetupProps): VNode {
|
||||
const [fullName, setFullName] = useState("");
|
||||
|
@ -0,0 +1,56 @@
|
||||
/*
|
||||
This file is part of GNU Taler
|
||||
(C) 2021 Taler Systems S.A.
|
||||
|
||||
GNU Taler is free software; you can redistribute it and/or modify it under the
|
||||
terms of the GNU General Public License as published by the Free Software
|
||||
Foundation; either version 3, or (at your option) any later version.
|
||||
|
||||
GNU Taler is distributed in the hope that it will be useful, but WITHOUT ANY
|
||||
WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR
|
||||
A PARTICULAR PURPOSE. See the GNU General Public License for more details.
|
||||
|
||||
You should have received a copy of the GNU General Public License along with
|
||||
GNU Taler; see the file COPYING. If not, see <http://www.gnu.org/licenses/>
|
||||
*/
|
||||
|
||||
/**
|
||||
*
|
||||
* @author Sebastian Javier Marchano (sebasjm)
|
||||
*/
|
||||
|
||||
import { ChallengeFeedbackStatus, ReducerState } from 'anastasis-core';
|
||||
import { createExample, reducerStatesExample } from '../../../utils';
|
||||
import { authMethods as TestedComponent, KnownAuthMethods } from './index';
|
||||
|
||||
|
||||
export default {
|
||||
title: 'Pages/recovery/SolveChallenge/AuthMethods/post',
|
||||
component: TestedComponent,
|
||||
args: {
|
||||
order: 5,
|
||||
},
|
||||
argTypes: {
|
||||
onUpdate: { action: 'onUpdate' },
|
||||
onBack: { action: 'onBack' },
|
||||
},
|
||||
};
|
||||
|
||||
const type: KnownAuthMethods = 'post'
|
||||
|
||||
export const WithoutFeedback = createExample(TestedComponent[type].solve, {
|
||||
...reducerStatesExample.challengeSolving,
|
||||
recovery_information: {
|
||||
challenges: [{
|
||||
cost: 'USD:1',
|
||||
instructions: 'does P equals NP?',
|
||||
type: 'question',
|
||||
uuid: 'uuid-1'
|
||||
}],
|
||||
policies: [],
|
||||
},
|
||||
selected_challenge_uuid: 'uuid-1',
|
||||
} as ReducerState, {
|
||||
id: 'uuid-1',
|
||||
});
|
||||
|
@ -0,0 +1,105 @@
|
||||
import { ChallengeFeedbackStatus, ChallengeInfo } from "anastasis-core";
|
||||
import { h, VNode } from "preact";
|
||||
import { useState } from "preact/hooks";
|
||||
import { AsyncButton } from "../../../components/AsyncButton";
|
||||
import { TextInput } from "../../../components/fields/TextInput";
|
||||
import { useAnastasisContext } from "../../../context/anastasis";
|
||||
import { AnastasisClientFrame } from "../index";
|
||||
import { SolveOverviewFeedbackDisplay } from "../SolveScreen";
|
||||
import { AuthMethodSolveProps } from "./index";
|
||||
|
||||
export function AuthMethodPostSolve({ id }: AuthMethodSolveProps): VNode {
|
||||
const [answer, setAnswer] = useState("");
|
||||
|
||||
const reducer = useAnastasisContext();
|
||||
if (!reducer) {
|
||||
return (
|
||||
<AnastasisClientFrame hideNav title="Recovery problem">
|
||||
<div>no reducer in context</div>
|
||||
</AnastasisClientFrame>
|
||||
);
|
||||
}
|
||||
if (
|
||||
!reducer.currentReducerState ||
|
||||
reducer.currentReducerState.recovery_state === undefined
|
||||
) {
|
||||
return (
|
||||
<AnastasisClientFrame hideNav title="Recovery problem">
|
||||
<div>invalid state</div>
|
||||
</AnastasisClientFrame>
|
||||
);
|
||||
}
|
||||
|
||||
if (!reducer.currentReducerState.recovery_information) {
|
||||
return (
|
||||
<AnastasisClientFrame
|
||||
hideNext="Recovery document not found"
|
||||
title="Recovery problem"
|
||||
>
|
||||
<div>no recovery information found</div>
|
||||
</AnastasisClientFrame>
|
||||
);
|
||||
}
|
||||
if (!reducer.currentReducerState.selected_challenge_uuid) {
|
||||
return (
|
||||
<AnastasisClientFrame hideNav title="Recovery problem">
|
||||
<div>invalid state</div>
|
||||
<div style={{ marginTop: '2em', display: 'flex', justifyContent: 'space-between' }}>
|
||||
<button class="button" onClick={() => reducer.back()}>Back</button>
|
||||
</div>
|
||||
</AnastasisClientFrame>
|
||||
);
|
||||
}
|
||||
|
||||
const chArr = reducer.currentReducerState.recovery_information.challenges;
|
||||
const challengeFeedback =
|
||||
reducer.currentReducerState.challenge_feedback ?? {};
|
||||
const selectedUuid = reducer.currentReducerState.selected_challenge_uuid;
|
||||
const challenges: {
|
||||
[uuid: string]: ChallengeInfo;
|
||||
} = {};
|
||||
for (const ch of chArr) {
|
||||
challenges[ch.uuid] = ch;
|
||||
}
|
||||
const selectedChallenge = challenges[selectedUuid];
|
||||
const feedback = challengeFeedback[selectedUuid]
|
||||
|
||||
|
||||
async function onNext(): Promise<void> {
|
||||
return reducer?.transition("solve_challenge", { answer });
|
||||
}
|
||||
function onCancel(): void {
|
||||
reducer?.back();
|
||||
}
|
||||
|
||||
|
||||
const shouldHideConfirm = feedback?.state === ChallengeFeedbackStatus.RateLimitExceeded
|
||||
|| feedback?.state === ChallengeFeedbackStatus.Redirect
|
||||
|| feedback?.state === ChallengeFeedbackStatus.Unsupported
|
||||
|| feedback?.state === ChallengeFeedbackStatus.TruthUnknown
|
||||
|
||||
return (
|
||||
<AnastasisClientFrame hideNav title="Add email authentication">
|
||||
<SolveOverviewFeedbackDisplay feedback={feedback} />
|
||||
<p>
|
||||
Wait for the answer
|
||||
</p>
|
||||
<TextInput label="Answer" grabFocus bind={[answer, setAnswer]} />
|
||||
|
||||
<div
|
||||
style={{
|
||||
marginTop: "2em",
|
||||
display: "flex",
|
||||
justifyContent: "space-between",
|
||||
}}
|
||||
>
|
||||
<button class="button" onClick={onCancel}>
|
||||
Cancel
|
||||
</button>
|
||||
{!shouldHideConfirm && <AsyncButton class="button is-info" onClick={onNext}>
|
||||
Confirm
|
||||
</AsyncButton>}
|
||||
</div>
|
||||
</AnastasisClientFrame>
|
||||
);
|
||||
}
|
@ -25,7 +25,7 @@ import { authMethods as TestedComponent, KnownAuthMethods } from './index';
|
||||
|
||||
|
||||
export default {
|
||||
title: 'Pages/backup/authMethods/Question',
|
||||
title: 'Pages/backup/AuthorizationMethod/AuthMethods/Question',
|
||||
component: TestedComponent,
|
||||
args: {
|
||||
order: 5,
|
||||
@ -38,11 +38,11 @@ export default {
|
||||
|
||||
const type: KnownAuthMethods = 'question'
|
||||
|
||||
export const Empty = createExample(TestedComponent[type].screen, reducerStatesExample.authEditing, {
|
||||
export const Empty = createExample(TestedComponent[type].setup, reducerStatesExample.authEditing, {
|
||||
configured: []
|
||||
});
|
||||
|
||||
export const WithOneExample = createExample(TestedComponent[type].screen, reducerStatesExample.authEditing, {
|
||||
export const WithOneExample = createExample(TestedComponent[type].setup, reducerStatesExample.authEditing, {
|
||||
configured: [{
|
||||
challenge: 'qwe',
|
||||
type,
|
||||
@ -51,7 +51,7 @@ export const WithOneExample = createExample(TestedComponent[type].screen, reduce
|
||||
}]
|
||||
});
|
||||
|
||||
export const WithMoreExamples = createExample(TestedComponent[type].screen, reducerStatesExample.authEditing, {
|
||||
export const WithMoreExamples = createExample(TestedComponent[type].setup, reducerStatesExample.authEditing, {
|
||||
configured: [{
|
||||
challenge: 'qwe',
|
||||
type,
|
||||
|
@ -1,11 +1,10 @@
|
||||
/* 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 { AuthMethodSetupProps } from "./index";
|
||||
import { AnastasisClientFrame } from "../index";
|
||||
import { TextInput } from "../../../components/fields/TextInput";
|
||||
|
||||
|
@ -0,0 +1,56 @@
|
||||
/*
|
||||
This file is part of GNU Taler
|
||||
(C) 2021 Taler Systems S.A.
|
||||
|
||||
GNU Taler is free software; you can redistribute it and/or modify it under the
|
||||
terms of the GNU General Public License as published by the Free Software
|
||||
Foundation; either version 3, or (at your option) any later version.
|
||||
|
||||
GNU Taler is distributed in the hope that it will be useful, but WITHOUT ANY
|
||||
WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR
|
||||
A PARTICULAR PURPOSE. See the GNU General Public License for more details.
|
||||
|
||||
You should have received a copy of the GNU General Public License along with
|
||||
GNU Taler; see the file COPYING. If not, see <http://www.gnu.org/licenses/>
|
||||
*/
|
||||
|
||||
/**
|
||||
*
|
||||
* @author Sebastian Javier Marchano (sebasjm)
|
||||
*/
|
||||
|
||||
import { ChallengeFeedbackStatus, ReducerState } from 'anastasis-core';
|
||||
import { createExample, reducerStatesExample } from '../../../utils';
|
||||
import { authMethods as TestedComponent, KnownAuthMethods } from './index';
|
||||
|
||||
|
||||
export default {
|
||||
title: 'Pages/recovery/SolveChallenge/AuthMethods/question',
|
||||
component: TestedComponent,
|
||||
args: {
|
||||
order: 5,
|
||||
},
|
||||
argTypes: {
|
||||
onUpdate: { action: 'onUpdate' },
|
||||
onBack: { action: 'onBack' },
|
||||
},
|
||||
};
|
||||
|
||||
const type: KnownAuthMethods = 'question'
|
||||
|
||||
export const WithoutFeedback = createExample(TestedComponent[type].solve, {
|
||||
...reducerStatesExample.challengeSolving,
|
||||
recovery_information: {
|
||||
challenges: [{
|
||||
cost: 'USD:1',
|
||||
instructions: 'does P equals NP?',
|
||||
type: 'question',
|
||||
uuid: 'uuid-1'
|
||||
}],
|
||||
policies: [],
|
||||
},
|
||||
selected_challenge_uuid: 'uuid-1',
|
||||
} as ReducerState, {
|
||||
id: 'uuid-1',
|
||||
});
|
||||
|
@ -0,0 +1,105 @@
|
||||
import { ChallengeFeedbackStatus, ChallengeInfo } from "anastasis-core";
|
||||
import { h, VNode } from "preact";
|
||||
import { useState } from "preact/hooks";
|
||||
import { AsyncButton } from "../../../components/AsyncButton";
|
||||
import { TextInput } from "../../../components/fields/TextInput";
|
||||
import { useAnastasisContext } from "../../../context/anastasis";
|
||||
import { AnastasisClientFrame } from "../index";
|
||||
import { SolveOverviewFeedbackDisplay } from "../SolveScreen";
|
||||
import { AuthMethodSolveProps } from "./index";
|
||||
|
||||
export function AuthMethodQuestionSolve({ id }: AuthMethodSolveProps): VNode {
|
||||
const [answer, setAnswer] = useState("");
|
||||
|
||||
const reducer = useAnastasisContext();
|
||||
if (!reducer) {
|
||||
return (
|
||||
<AnastasisClientFrame hideNav title="Recovery problem">
|
||||
<div>no reducer in context</div>
|
||||
</AnastasisClientFrame>
|
||||
);
|
||||
}
|
||||
if (
|
||||
!reducer.currentReducerState ||
|
||||
reducer.currentReducerState.recovery_state === undefined
|
||||
) {
|
||||
return (
|
||||
<AnastasisClientFrame hideNav title="Recovery problem">
|
||||
<div>invalid state</div>
|
||||
</AnastasisClientFrame>
|
||||
);
|
||||
}
|
||||
|
||||
if (!reducer.currentReducerState.recovery_information) {
|
||||
return (
|
||||
<AnastasisClientFrame
|
||||
hideNext="Recovery document not found"
|
||||
title="Recovery problem"
|
||||
>
|
||||
<div>no recovery information found</div>
|
||||
</AnastasisClientFrame>
|
||||
);
|
||||
}
|
||||
if (!reducer.currentReducerState.selected_challenge_uuid) {
|
||||
return (
|
||||
<AnastasisClientFrame hideNav title="Recovery problem">
|
||||
<div>invalid state</div>
|
||||
<div style={{ marginTop: '2em', display: 'flex', justifyContent: 'space-between' }}>
|
||||
<button class="button" onClick={() => reducer.back()}>Back</button>
|
||||
</div>
|
||||
</AnastasisClientFrame>
|
||||
);
|
||||
}
|
||||
|
||||
const chArr = reducer.currentReducerState.recovery_information.challenges;
|
||||
const challengeFeedback =
|
||||
reducer.currentReducerState.challenge_feedback ?? {};
|
||||
const selectedUuid = reducer.currentReducerState.selected_challenge_uuid;
|
||||
const challenges: {
|
||||
[uuid: string]: ChallengeInfo;
|
||||
} = {};
|
||||
for (const ch of chArr) {
|
||||
challenges[ch.uuid] = ch;
|
||||
}
|
||||
const selectedChallenge = challenges[selectedUuid];
|
||||
const feedback = challengeFeedback[selectedUuid]
|
||||
|
||||
|
||||
async function onNext(): Promise<void> {
|
||||
return reducer?.transition("solve_challenge", { answer });
|
||||
}
|
||||
function onCancel(): void {
|
||||
reducer?.back();
|
||||
}
|
||||
|
||||
|
||||
const shouldHideConfirm = feedback?.state === ChallengeFeedbackStatus.RateLimitExceeded
|
||||
|| feedback?.state === ChallengeFeedbackStatus.Redirect
|
||||
|| feedback?.state === ChallengeFeedbackStatus.Unsupported
|
||||
|| feedback?.state === ChallengeFeedbackStatus.TruthUnknown
|
||||
|
||||
return (
|
||||
<AnastasisClientFrame hideNav title="Add email authentication">
|
||||
<SolveOverviewFeedbackDisplay feedback={feedback} />
|
||||
<p>
|
||||
Answer the question please
|
||||
</p>
|
||||
<TextInput label="Answer" grabFocus bind={[answer, setAnswer]} />
|
||||
|
||||
<div
|
||||
style={{
|
||||
marginTop: "2em",
|
||||
display: "flex",
|
||||
justifyContent: "space-between",
|
||||
}}
|
||||
>
|
||||
<button class="button" onClick={onCancel}>
|
||||
Cancel
|
||||
</button>
|
||||
{!shouldHideConfirm && <AsyncButton class="button is-info" onClick={onNext}>
|
||||
Confirm
|
||||
</AsyncButton>}
|
||||
</div>
|
||||
</AnastasisClientFrame>
|
||||
);
|
||||
}
|
@ -25,7 +25,7 @@ import { authMethods as TestedComponent, KnownAuthMethods } from './index';
|
||||
|
||||
|
||||
export default {
|
||||
title: 'Pages/backup/authMethods/Sms',
|
||||
title: 'Pages/backup/AuthorizationMethod/AuthMethods/Sms',
|
||||
component: TestedComponent,
|
||||
args: {
|
||||
order: 5,
|
||||
@ -38,11 +38,11 @@ export default {
|
||||
|
||||
const type: KnownAuthMethods = 'sms'
|
||||
|
||||
export const Empty = createExample(TestedComponent[type].screen, reducerStatesExample.authEditing, {
|
||||
export const Empty = createExample(TestedComponent[type].setup, reducerStatesExample.authEditing, {
|
||||
configured: []
|
||||
});
|
||||
|
||||
export const WithOneExample = createExample(TestedComponent[type].screen, reducerStatesExample.authEditing, {
|
||||
export const WithOneExample = createExample(TestedComponent[type].setup, reducerStatesExample.authEditing, {
|
||||
configured: [{
|
||||
challenge: 'qwe',
|
||||
type,
|
||||
@ -51,7 +51,7 @@ export const WithOneExample = createExample(TestedComponent[type].screen, reduce
|
||||
}]
|
||||
});
|
||||
|
||||
export const WithMoreExamples = createExample(TestedComponent[type].screen, reducerStatesExample.authEditing, {
|
||||
export const WithMoreExamples = createExample(TestedComponent[type].setup, reducerStatesExample.authEditing, {
|
||||
configured: [{
|
||||
challenge: 'qwe',
|
||||
type,
|
||||
|
@ -1,12 +1,11 @@
|
||||
/* 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 { AuthMethodSetupProps } from ".";
|
||||
import { NumberInput } from "../../../components/fields/NumberInput";
|
||||
import { AuthMethodSetupProps } from "../AuthenticationEditorScreen";
|
||||
import { AnastasisClientFrame } from "../index";
|
||||
|
||||
export function AuthMethodSmsSetup({ addAuthMethod, cancel, configured }: AuthMethodSetupProps): VNode {
|
||||
|
@ -0,0 +1,56 @@
|
||||
/*
|
||||
This file is part of GNU Taler
|
||||
(C) 2021 Taler Systems S.A.
|
||||
|
||||
GNU Taler is free software; you can redistribute it and/or modify it under the
|
||||
terms of the GNU General Public License as published by the Free Software
|
||||
Foundation; either version 3, or (at your option) any later version.
|
||||
|
||||
GNU Taler is distributed in the hope that it will be useful, but WITHOUT ANY
|
||||
WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR
|
||||
A PARTICULAR PURPOSE. See the GNU General Public License for more details.
|
||||
|
||||
You should have received a copy of the GNU General Public License along with
|
||||
GNU Taler; see the file COPYING. If not, see <http://www.gnu.org/licenses/>
|
||||
*/
|
||||
|
||||
/**
|
||||
*
|
||||
* @author Sebastian Javier Marchano (sebasjm)
|
||||
*/
|
||||
|
||||
import { ChallengeFeedbackStatus, ReducerState } from 'anastasis-core';
|
||||
import { createExample, reducerStatesExample } from '../../../utils';
|
||||
import { authMethods as TestedComponent, KnownAuthMethods } from './index';
|
||||
|
||||
|
||||
export default {
|
||||
title: 'Pages/recovery/SolveChallenge/AuthMethods/sms',
|
||||
component: TestedComponent,
|
||||
args: {
|
||||
order: 5,
|
||||
},
|
||||
argTypes: {
|
||||
onUpdate: { action: 'onUpdate' },
|
||||
onBack: { action: 'onBack' },
|
||||
},
|
||||
};
|
||||
|
||||
const type: KnownAuthMethods = 'sms'
|
||||
|
||||
export const WithoutFeedback = createExample(TestedComponent[type].solve, {
|
||||
...reducerStatesExample.challengeSolving,
|
||||
recovery_information: {
|
||||
challenges: [{
|
||||
cost: 'USD:1',
|
||||
instructions: 'does P equals NP?',
|
||||
type: 'question',
|
||||
uuid: 'uuid-1'
|
||||
}],
|
||||
policies: [],
|
||||
},
|
||||
selected_challenge_uuid: 'uuid-1',
|
||||
} as ReducerState, {
|
||||
id: 'uuid-1',
|
||||
});
|
||||
|
@ -0,0 +1,106 @@
|
||||
import { ChallengeFeedbackStatus, ChallengeInfo } from "anastasis-core";
|
||||
import { h, VNode } from "preact";
|
||||
import { useState } from "preact/hooks";
|
||||
import { AsyncButton } from "../../../components/AsyncButton";
|
||||
import { TextInput } from "../../../components/fields/TextInput";
|
||||
import { useAnastasisContext } from "../../../context/anastasis";
|
||||
import { AnastasisClientFrame } from "../index";
|
||||
import { SolveOverviewFeedbackDisplay } from "../SolveScreen";
|
||||
import { AuthMethodSolveProps } from "./index";
|
||||
|
||||
export function AuthMethodSmsSolve({ id }: AuthMethodSolveProps): VNode {
|
||||
const [answer, setAnswer] = useState("");
|
||||
|
||||
const reducer = useAnastasisContext();
|
||||
if (!reducer) {
|
||||
return (
|
||||
<AnastasisClientFrame hideNav title="Recovery problem">
|
||||
<div>no reducer in context</div>
|
||||
</AnastasisClientFrame>
|
||||
);
|
||||
}
|
||||
if (
|
||||
!reducer.currentReducerState ||
|
||||
reducer.currentReducerState.recovery_state === undefined
|
||||
) {
|
||||
return (
|
||||
<AnastasisClientFrame hideNav title="Recovery problem">
|
||||
<div>invalid state</div>
|
||||
</AnastasisClientFrame>
|
||||
);
|
||||
}
|
||||
|
||||
if (!reducer.currentReducerState.recovery_information) {
|
||||
return (
|
||||
<AnastasisClientFrame
|
||||
hideNext="Recovery document not found"
|
||||
title="Recovery problem"
|
||||
>
|
||||
<div>no recovery information found</div>
|
||||
</AnastasisClientFrame>
|
||||
);
|
||||
}
|
||||
if (!reducer.currentReducerState.selected_challenge_uuid) {
|
||||
return (
|
||||
<AnastasisClientFrame hideNav title="Recovery problem">
|
||||
<div>invalid state</div>
|
||||
<div style={{ marginTop: '2em', display: 'flex', justifyContent: 'space-between' }}>
|
||||
<button class="button" onClick={() => reducer.back()}>Back</button>
|
||||
</div>
|
||||
</AnastasisClientFrame>
|
||||
);
|
||||
}
|
||||
|
||||
const chArr = reducer.currentReducerState.recovery_information.challenges;
|
||||
const challengeFeedback =
|
||||
reducer.currentReducerState.challenge_feedback ?? {};
|
||||
const selectedUuid = reducer.currentReducerState.selected_challenge_uuid;
|
||||
const challenges: {
|
||||
[uuid: string]: ChallengeInfo;
|
||||
} = {};
|
||||
for (const ch of chArr) {
|
||||
challenges[ch.uuid] = ch;
|
||||
}
|
||||
const selectedChallenge = challenges[selectedUuid];
|
||||
const feedback = challengeFeedback[selectedUuid]
|
||||
|
||||
|
||||
async function onNext(): Promise<void> {
|
||||
return reducer?.transition("solve_challenge", { answer });
|
||||
}
|
||||
function onCancel(): void {
|
||||
reducer?.back();
|
||||
}
|
||||
|
||||
|
||||
const shouldHideConfirm = feedback?.state === ChallengeFeedbackStatus.RateLimitExceeded
|
||||
|| feedback?.state === ChallengeFeedbackStatus.Redirect
|
||||
|| feedback?.state === ChallengeFeedbackStatus.Unsupported
|
||||
|| feedback?.state === ChallengeFeedbackStatus.TruthUnknown
|
||||
|
||||
return (
|
||||
<AnastasisClientFrame hideNav title="Add email authentication">
|
||||
<SolveOverviewFeedbackDisplay feedback={feedback} />
|
||||
<p>
|
||||
An sms has been sent to "<b>{selectedChallenge.instructions}</b>". Type the code
|
||||
below
|
||||
</p>
|
||||
<TextInput label="Answer" grabFocus bind={[answer, setAnswer]} />
|
||||
|
||||
<div
|
||||
style={{
|
||||
marginTop: "2em",
|
||||
display: "flex",
|
||||
justifyContent: "space-between",
|
||||
}}
|
||||
>
|
||||
<button class="button" onClick={onCancel}>
|
||||
Cancel
|
||||
</button>
|
||||
{!shouldHideConfirm && <AsyncButton class="button is-info" onClick={onNext}>
|
||||
Confirm
|
||||
</AsyncButton>}
|
||||
</div>
|
||||
</AnastasisClientFrame>
|
||||
);
|
||||
}
|
@ -25,7 +25,7 @@ import { authMethods as TestedComponent, KnownAuthMethods } from './index';
|
||||
|
||||
|
||||
export default {
|
||||
title: 'Pages/backup/authMethods/TOTP',
|
||||
title: 'Pages/backup/AuthorizationMethod/AuthMethods/TOTP',
|
||||
component: TestedComponent,
|
||||
args: {
|
||||
order: 5,
|
||||
@ -38,10 +38,10 @@ export default {
|
||||
|
||||
const type: KnownAuthMethods = 'totp'
|
||||
|
||||
export const Empty = createExample(TestedComponent[type].screen, reducerStatesExample.authEditing, {
|
||||
export const Empty = createExample(TestedComponent[type].setup, reducerStatesExample.authEditing, {
|
||||
configured: []
|
||||
});
|
||||
export const WithOneExample = createExample(TestedComponent[type].screen, reducerStatesExample.authEditing, {
|
||||
export const WithOneExample = createExample(TestedComponent[type].setup, reducerStatesExample.authEditing, {
|
||||
configured: [{
|
||||
challenge: 'qwe',
|
||||
type,
|
||||
@ -49,7 +49,7 @@ export const WithOneExample = createExample(TestedComponent[type].screen, reduce
|
||||
remove: () => null
|
||||
}]
|
||||
});
|
||||
export const WithMoreExample = createExample(TestedComponent[type].screen, reducerStatesExample.authEditing, {
|
||||
export const WithMoreExample = createExample(TestedComponent[type].setup, reducerStatesExample.authEditing, {
|
||||
configured: [{
|
||||
challenge: 'qwe',
|
||||
type,
|
||||
|
@ -1,11 +1,10 @@
|
||||
/* eslint-disable @typescript-eslint/camelcase */
|
||||
import {
|
||||
encodeCrock,
|
||||
stringToBytes
|
||||
} from "@gnu-taler/taler-util";
|
||||
import { h, VNode } from "preact";
|
||||
import { useMemo, useState } from "preact/hooks";
|
||||
import { AuthMethodSetupProps } from "../AuthenticationEditorScreen";
|
||||
import { AuthMethodSetupProps } from "./index";
|
||||
import { AnastasisClientFrame } from "../index";
|
||||
import { TextInput } from "../../../components/fields/TextInput";
|
||||
import { QR } from "../../../components/QR";
|
||||
|
@ -0,0 +1,56 @@
|
||||
/*
|
||||
This file is part of GNU Taler
|
||||
(C) 2021 Taler Systems S.A.
|
||||
|
||||
GNU Taler is free software; you can redistribute it and/or modify it under the
|
||||
terms of the GNU General Public License as published by the Free Software
|
||||
Foundation; either version 3, or (at your option) any later version.
|
||||
|
||||
GNU Taler is distributed in the hope that it will be useful, but WITHOUT ANY
|
||||
WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR
|
||||
A PARTICULAR PURPOSE. See the GNU General Public License for more details.
|
||||
|
||||
You should have received a copy of the GNU General Public License along with
|
||||
GNU Taler; see the file COPYING. If not, see <http://www.gnu.org/licenses/>
|
||||
*/
|
||||
|
||||
/**
|
||||
*
|
||||
* @author Sebastian Javier Marchano (sebasjm)
|
||||
*/
|
||||
|
||||
import { ChallengeFeedbackStatus, ReducerState } from 'anastasis-core';
|
||||
import { createExample, reducerStatesExample } from '../../../utils';
|
||||
import { authMethods as TestedComponent, KnownAuthMethods } from './index';
|
||||
|
||||
|
||||
export default {
|
||||
title: 'Pages/recovery/SolveChallenge/AuthMethods/totp',
|
||||
component: TestedComponent,
|
||||
args: {
|
||||
order: 5,
|
||||
},
|
||||
argTypes: {
|
||||
onUpdate: { action: 'onUpdate' },
|
||||
onBack: { action: 'onBack' },
|
||||
},
|
||||
};
|
||||
|
||||
const type: KnownAuthMethods = 'totp'
|
||||
|
||||
export const WithoutFeedback = createExample(TestedComponent[type].solve, {
|
||||
...reducerStatesExample.challengeSolving,
|
||||
recovery_information: {
|
||||
challenges: [{
|
||||
cost: 'USD:1',
|
||||
instructions: 'does P equals NP?',
|
||||
type: 'question',
|
||||
uuid: 'uuid-1'
|
||||
}],
|
||||
policies: [],
|
||||
},
|
||||
selected_challenge_uuid: 'uuid-1',
|
||||
} as ReducerState, {
|
||||
id: 'uuid-1',
|
||||
});
|
||||
|
@ -0,0 +1,105 @@
|
||||
import { ChallengeFeedbackStatus, ChallengeInfo } from "anastasis-core";
|
||||
import { h, VNode } from "preact";
|
||||
import { useState } from "preact/hooks";
|
||||
import { AsyncButton } from "../../../components/AsyncButton";
|
||||
import { TextInput } from "../../../components/fields/TextInput";
|
||||
import { useAnastasisContext } from "../../../context/anastasis";
|
||||
import { AnastasisClientFrame } from "../index";
|
||||
import { SolveOverviewFeedbackDisplay } from "../SolveScreen";
|
||||
import { AuthMethodSolveProps } from "./index";
|
||||
|
||||
export function AuthMethodTotpSolve({ id }: AuthMethodSolveProps): VNode {
|
||||
const [answer, setAnswer] = useState("");
|
||||
|
||||
const reducer = useAnastasisContext();
|
||||
if (!reducer) {
|
||||
return (
|
||||
<AnastasisClientFrame hideNav title="Recovery problem">
|
||||
<div>no reducer in context</div>
|
||||
</AnastasisClientFrame>
|
||||
);
|
||||
}
|
||||
if (
|
||||
!reducer.currentReducerState ||
|
||||
reducer.currentReducerState.recovery_state === undefined
|
||||
) {
|
||||
return (
|
||||
<AnastasisClientFrame hideNav title="Recovery problem">
|
||||
<div>invalid state</div>
|
||||
</AnastasisClientFrame>
|
||||
);
|
||||
}
|
||||
|
||||
if (!reducer.currentReducerState.recovery_information) {
|
||||
return (
|
||||
<AnastasisClientFrame
|
||||
hideNext="Recovery document not found"
|
||||
title="Recovery problem"
|
||||
>
|
||||
<div>no recovery information found</div>
|
||||
</AnastasisClientFrame>
|
||||
);
|
||||
}
|
||||
if (!reducer.currentReducerState.selected_challenge_uuid) {
|
||||
return (
|
||||
<AnastasisClientFrame hideNav title="Recovery problem">
|
||||
<div>invalid state</div>
|
||||
<div style={{ marginTop: '2em', display: 'flex', justifyContent: 'space-between' }}>
|
||||
<button class="button" onClick={() => reducer.back()}>Back</button>
|
||||
</div>
|
||||
</AnastasisClientFrame>
|
||||
);
|
||||
}
|
||||
|
||||
const chArr = reducer.currentReducerState.recovery_information.challenges;
|
||||
const challengeFeedback =
|
||||
reducer.currentReducerState.challenge_feedback ?? {};
|
||||
const selectedUuid = reducer.currentReducerState.selected_challenge_uuid;
|
||||
const challenges: {
|
||||
[uuid: string]: ChallengeInfo;
|
||||
} = {};
|
||||
for (const ch of chArr) {
|
||||
challenges[ch.uuid] = ch;
|
||||
}
|
||||
const selectedChallenge = challenges[selectedUuid];
|
||||
const feedback = challengeFeedback[selectedUuid]
|
||||
|
||||
|
||||
async function onNext(): Promise<void> {
|
||||
return reducer?.transition("solve_challenge", { answer });
|
||||
}
|
||||
function onCancel(): void {
|
||||
reducer?.back();
|
||||
}
|
||||
|
||||
|
||||
const shouldHideConfirm = feedback?.state === ChallengeFeedbackStatus.RateLimitExceeded
|
||||
|| feedback?.state === ChallengeFeedbackStatus.Redirect
|
||||
|| feedback?.state === ChallengeFeedbackStatus.Unsupported
|
||||
|| feedback?.state === ChallengeFeedbackStatus.TruthUnknown
|
||||
|
||||
return (
|
||||
<AnastasisClientFrame hideNav title="Add email authentication">
|
||||
<SolveOverviewFeedbackDisplay feedback={feedback} />
|
||||
<p>
|
||||
enter the totp solution
|
||||
</p>
|
||||
<TextInput label="Answer" grabFocus bind={[answer, setAnswer]} />
|
||||
|
||||
<div
|
||||
style={{
|
||||
marginTop: "2em",
|
||||
display: "flex",
|
||||
justifyContent: "space-between",
|
||||
}}
|
||||
>
|
||||
<button class="button" onClick={onCancel}>
|
||||
Cancel
|
||||
</button>
|
||||
{!shouldHideConfirm && <AsyncButton class="button is-info" onClick={onNext}>
|
||||
Confirm
|
||||
</AsyncButton>}
|
||||
</div>
|
||||
</AnastasisClientFrame>
|
||||
);
|
||||
}
|
@ -25,7 +25,7 @@ import { authMethods as TestedComponent, KnownAuthMethods } from './index';
|
||||
import logoImage from '../../../assets/logo.jpeg'
|
||||
|
||||
export default {
|
||||
title: 'Pages/backup/authMethods/Video',
|
||||
title: 'Pages/backup/AuthorizationMethod/AuthMethods/Video',
|
||||
component: TestedComponent,
|
||||
args: {
|
||||
order: 5,
|
||||
@ -38,11 +38,11 @@ export default {
|
||||
|
||||
const type: KnownAuthMethods = 'video'
|
||||
|
||||
export const Empty = createExample(TestedComponent[type].screen, reducerStatesExample.authEditing, {
|
||||
export const Empty = createExample(TestedComponent[type].setup, reducerStatesExample.authEditing, {
|
||||
configured: []
|
||||
});
|
||||
|
||||
export const WithOneExample = createExample(TestedComponent[type].screen, reducerStatesExample.authEditing, {
|
||||
export const WithOneExample = createExample(TestedComponent[type].setup, reducerStatesExample.authEditing, {
|
||||
configured: [{
|
||||
challenge: 'qwe',
|
||||
type,
|
||||
@ -51,7 +51,7 @@ export const WithOneExample = createExample(TestedComponent[type].screen, reduce
|
||||
}]
|
||||
});
|
||||
|
||||
export const WithMoreExamples = createExample(TestedComponent[type].screen, reducerStatesExample.authEditing, {
|
||||
export const WithMoreExamples = createExample(TestedComponent[type].setup, reducerStatesExample.authEditing, {
|
||||
configured: [{
|
||||
challenge: 'qwe',
|
||||
type,
|
||||
|
@ -1,4 +1,3 @@
|
||||
/* eslint-disable @typescript-eslint/camelcase */
|
||||
import {
|
||||
encodeCrock,
|
||||
stringToBytes
|
||||
@ -6,7 +5,7 @@ import {
|
||||
import { h, VNode } from "preact";
|
||||
import { useState } from "preact/hooks";
|
||||
import { ImageInput } from "../../../components/fields/ImageInput";
|
||||
import { AuthMethodSetupProps } from "../AuthenticationEditorScreen";
|
||||
import { AuthMethodSetupProps } from "./index";
|
||||
import { AnastasisClientFrame } from "../index";
|
||||
|
||||
export function AuthMethodVideoSetup({cancel, addAuthMethod, configured}: AuthMethodSetupProps): VNode {
|
||||
|
@ -0,0 +1,56 @@
|
||||
/*
|
||||
This file is part of GNU Taler
|
||||
(C) 2021 Taler Systems S.A.
|
||||
|
||||
GNU Taler is free software; you can redistribute it and/or modify it under the
|
||||
terms of the GNU General Public License as published by the Free Software
|
||||
Foundation; either version 3, or (at your option) any later version.
|
||||
|
||||
GNU Taler is distributed in the hope that it will be useful, but WITHOUT ANY
|
||||
WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR
|
||||
A PARTICULAR PURPOSE. See the GNU General Public License for more details.
|
||||
|
||||
You should have received a copy of the GNU General Public License along with
|
||||
GNU Taler; see the file COPYING. If not, see <http://www.gnu.org/licenses/>
|
||||
*/
|
||||
|
||||
/**
|
||||
*
|
||||
* @author Sebastian Javier Marchano (sebasjm)
|
||||
*/
|
||||
|
||||
import { ChallengeFeedbackStatus, ReducerState } from 'anastasis-core';
|
||||
import { createExample, reducerStatesExample } from '../../../utils';
|
||||
import { authMethods as TestedComponent, KnownAuthMethods } from './index';
|
||||
|
||||
|
||||
export default {
|
||||
title: 'Pages/recovery/SolveChallenge/AuthMethods/video',
|
||||
component: TestedComponent,
|
||||
args: {
|
||||
order: 5,
|
||||
},
|
||||
argTypes: {
|
||||
onUpdate: { action: 'onUpdate' },
|
||||
onBack: { action: 'onBack' },
|
||||
},
|
||||
};
|
||||
|
||||
const type: KnownAuthMethods = 'video'
|
||||
|
||||
export const WithoutFeedback = createExample(TestedComponent[type].solve, {
|
||||
...reducerStatesExample.challengeSolving,
|
||||
recovery_information: {
|
||||
challenges: [{
|
||||
cost: 'USD:1',
|
||||
instructions: 'does P equals NP?',
|
||||
type: 'question',
|
||||
uuid: 'uuid-1'
|
||||
}],
|
||||
policies: [],
|
||||
},
|
||||
selected_challenge_uuid: 'uuid-1',
|
||||
} as ReducerState, {
|
||||
id: 'uuid-1',
|
||||
});
|
||||
|
@ -0,0 +1,105 @@
|
||||
import { ChallengeFeedbackStatus, ChallengeInfo } from "anastasis-core";
|
||||
import { h, VNode } from "preact";
|
||||
import { useState } from "preact/hooks";
|
||||
import { AsyncButton } from "../../../components/AsyncButton";
|
||||
import { TextInput } from "../../../components/fields/TextInput";
|
||||
import { useAnastasisContext } from "../../../context/anastasis";
|
||||
import { AnastasisClientFrame } from "../index";
|
||||
import { SolveOverviewFeedbackDisplay } from "../SolveScreen";
|
||||
import { AuthMethodSolveProps } from "./index";
|
||||
|
||||
export function AuthMethodVideoSolve({ id }: AuthMethodSolveProps): VNode {
|
||||
const [answer, setAnswer] = useState("");
|
||||
|
||||
const reducer = useAnastasisContext();
|
||||
if (!reducer) {
|
||||
return (
|
||||
<AnastasisClientFrame hideNav title="Recovery problem">
|
||||
<div>no reducer in context</div>
|
||||
</AnastasisClientFrame>
|
||||
);
|
||||
}
|
||||
if (
|
||||
!reducer.currentReducerState ||
|
||||
reducer.currentReducerState.recovery_state === undefined
|
||||
) {
|
||||
return (
|
||||
<AnastasisClientFrame hideNav title="Recovery problem">
|
||||
<div>invalid state</div>
|
||||
</AnastasisClientFrame>
|
||||
);
|
||||
}
|
||||
|
||||
if (!reducer.currentReducerState.recovery_information) {
|
||||
return (
|
||||
<AnastasisClientFrame
|
||||
hideNext="Recovery document not found"
|
||||
title="Recovery problem"
|
||||
>
|
||||
<div>no recovery information found</div>
|
||||
</AnastasisClientFrame>
|
||||
);
|
||||
}
|
||||
if (!reducer.currentReducerState.selected_challenge_uuid) {
|
||||
return (
|
||||
<AnastasisClientFrame hideNav title="Recovery problem">
|
||||
<div>invalid state</div>
|
||||
<div style={{ marginTop: '2em', display: 'flex', justifyContent: 'space-between' }}>
|
||||
<button class="button" onClick={() => reducer.back()}>Back</button>
|
||||
</div>
|
||||
</AnastasisClientFrame>
|
||||
);
|
||||
}
|
||||
|
||||
const chArr = reducer.currentReducerState.recovery_information.challenges;
|
||||
const challengeFeedback =
|
||||
reducer.currentReducerState.challenge_feedback ?? {};
|
||||
const selectedUuid = reducer.currentReducerState.selected_challenge_uuid;
|
||||
const challenges: {
|
||||
[uuid: string]: ChallengeInfo;
|
||||
} = {};
|
||||
for (const ch of chArr) {
|
||||
challenges[ch.uuid] = ch;
|
||||
}
|
||||
const selectedChallenge = challenges[selectedUuid];
|
||||
const feedback = challengeFeedback[selectedUuid]
|
||||
|
||||
|
||||
async function onNext(): Promise<void> {
|
||||
return reducer?.transition("solve_challenge", { answer });
|
||||
}
|
||||
function onCancel(): void {
|
||||
reducer?.back();
|
||||
}
|
||||
|
||||
|
||||
const shouldHideConfirm = feedback?.state === ChallengeFeedbackStatus.RateLimitExceeded
|
||||
|| feedback?.state === ChallengeFeedbackStatus.Redirect
|
||||
|| feedback?.state === ChallengeFeedbackStatus.Unsupported
|
||||
|| feedback?.state === ChallengeFeedbackStatus.TruthUnknown
|
||||
|
||||
return (
|
||||
<AnastasisClientFrame hideNav title="Add email authentication">
|
||||
<SolveOverviewFeedbackDisplay feedback={feedback} />
|
||||
<p>
|
||||
You are gonna be called to check your identity
|
||||
</p>
|
||||
<TextInput label="Answer" grabFocus bind={[answer, setAnswer]} />
|
||||
|
||||
<div
|
||||
style={{
|
||||
marginTop: "2em",
|
||||
display: "flex",
|
||||
justifyContent: "space-between",
|
||||
}}
|
||||
>
|
||||
<button class="button" onClick={onCancel}>
|
||||
Cancel
|
||||
</button>
|
||||
{!shouldHideConfirm && <AsyncButton class="button is-info" onClick={onNext}>
|
||||
Confirm
|
||||
</AsyncButton>}
|
||||
</div>
|
||||
</AnastasisClientFrame>
|
||||
);
|
||||
}
|
@ -1,22 +1,44 @@
|
||||
import { AuthMethod } from "anastasis-core";
|
||||
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';
|
||||
import { AuthMethodEmailSetup as EmailSetup } from "./AuthMethodEmailSetup";
|
||||
import { AuthMethodEmailSolve as EmailSolve } from "./AuthMethodEmailSolve";
|
||||
import { AuthMethodIbanSetup as IbanSetup } from "./AuthMethodIbanSetup";
|
||||
import { AuthMethodPostSetup as PostalSetup } from "./AuthMethodPostSetup";
|
||||
import { AuthMethodQuestionSetup as QuestionSetup } from "./AuthMethodQuestionSetup";
|
||||
import { AuthMethodSmsSetup as SmsSetup } from "./AuthMethodSmsSetup";
|
||||
import { AuthMethodTotpSetup as TotpSetup } from "./AuthMethodTotpSetup";
|
||||
import { AuthMethodVideoSetup as VideoSetup } from "./AuthMethodVideoSetup";
|
||||
|
||||
import { AuthMethodIbanSolve as IbanSolve } from "./AuthMethodIbanSolve";
|
||||
import { AuthMethodPostSolve as PostalSolve } from "./AuthMethodPostSolve";
|
||||
import { AuthMethodQuestionSolve as QuestionSolve } from "./AuthMethodQuestionSolve";
|
||||
import { AuthMethodSmsSolve as SmsSolve } from "./AuthMethodSmsSolve";
|
||||
import { AuthMethodTotpSolve as TotpSolve } from "./AuthMethodTotpSolve";
|
||||
import { AuthMethodVideoSolve as VideoSolve } from "./AuthMethodVideoSolve";
|
||||
|
||||
|
||||
export type AuthMethodWithRemove = AuthMethod & { remove: () => void }
|
||||
|
||||
export interface AuthMethodSetupProps {
|
||||
method: string;
|
||||
addAuthMethod: (x: any) => void;
|
||||
configured: AuthMethodWithRemove[];
|
||||
cancel: () => void;
|
||||
}
|
||||
|
||||
export interface AuthMethodSolveProps {
|
||||
id: string;
|
||||
}
|
||||
|
||||
interface AuthMethodConfiguration {
|
||||
icon: VNode;
|
||||
label: string;
|
||||
screen: (props: AuthMethodSetupProps) => VNode;
|
||||
setup: (props: AuthMethodSetupProps) => VNode;
|
||||
solve: (props: AuthMethodSolveProps) => VNode;
|
||||
skip?: boolean;
|
||||
}
|
||||
export type KnownAuthMethods = "sms" | "email" | "post" | "question" | "video" | "totp" | "iban";
|
||||
@ -29,41 +51,44 @@ export const authMethods: KnowMethodConfig = {
|
||||
question: {
|
||||
icon: <img src={questionIcon} />,
|
||||
label: "Question",
|
||||
screen: QuestionScreen
|
||||
setup: QuestionSetup,
|
||||
solve: QuestionSolve,
|
||||
},
|
||||
sms: {
|
||||
icon: <img src={smsIcon} />,
|
||||
label: "SMS",
|
||||
screen: SmsScreen
|
||||
setup: SmsSetup,
|
||||
solve: SmsSolve,
|
||||
},
|
||||
email: {
|
||||
icon: <i class="mdi mdi-email" />,
|
||||
label: "Email",
|
||||
screen: EmailScreen
|
||||
|
||||
setup: EmailSetup,
|
||||
solve: EmailSolve,
|
||||
},
|
||||
iban: {
|
||||
icon: <i class="mdi mdi-bank" />,
|
||||
label: "IBAN",
|
||||
screen: IbanScreen
|
||||
|
||||
setup: IbanSetup,
|
||||
solve: IbanSolve,
|
||||
},
|
||||
post: {
|
||||
icon: <img src={postalIcon} />,
|
||||
label: "Physical mail",
|
||||
screen: PostalScreen
|
||||
|
||||
setup: PostalSetup,
|
||||
solve: PostalSolve,
|
||||
},
|
||||
totp: {
|
||||
icon: <i class="mdi mdi-devices" />,
|
||||
label: "TOTP",
|
||||
screen: TotpScreen
|
||||
|
||||
setup: TotpSetup,
|
||||
solve: TotpSolve,
|
||||
},
|
||||
video: {
|
||||
icon: <img src={videoIcon} />,
|
||||
label: "Video",
|
||||
screen: VideScreen,
|
||||
skip: true,
|
||||
setup: VideoSetup,
|
||||
solve: VideoSolve,
|
||||
skip: true,
|
||||
}
|
||||
}
|
@ -1,8 +1,6 @@
|
||||
import {
|
||||
BackupStates,
|
||||
RecoveryStates,
|
||||
ReducerStateBackup,
|
||||
ReducerStateRecovery
|
||||
RecoveryStates
|
||||
} from "anastasis-core";
|
||||
import {
|
||||
ComponentChildren, Fragment,
|
||||
|
Loading…
Reference in New Issue
Block a user