anastasis-webui: make it compile again

This commit is contained in:
Florian Dold 2021-11-03 18:26:57 +01:00
parent 04356cd23f
commit 7d24d2254b
No known key found for this signature in database
GPG Key ID: D2E4F00F29D02A4B
5 changed files with 265 additions and 236 deletions

View File

@ -96,6 +96,7 @@ const { fetch } = fetchPonyfill({});
export * from "./reducer-types.js"; export * from "./reducer-types.js";
export * as validators from "./validators.js"; export * as validators from "./validators.js";
export * from "./challenge-feedback-types.js";
const logger = new Logger("anastasis-core:index.ts"); const logger = new Logger("anastasis-core:index.ts");

View File

@ -16,218 +16,201 @@
*/ */
/** /**
* *
* @author Sebastian Javier Marchano (sebasjm) * @author Sebastian Javier Marchano (sebasjm)
*/ */
import { RecoveryStates, ReducerState } from 'anastasis-core';
import { createExample, reducerStatesExample } from '../../utils';
import { ChallengeOverviewScreen as TestedComponent } from './ChallengeOverviewScreen';
import { RecoveryStates, ReducerState } from "anastasis-core";
import { createExample, reducerStatesExample } from "../../utils";
import { ChallengeOverviewScreen as TestedComponent } from "./ChallengeOverviewScreen";
export default { export default {
title: 'Pages/recovery/ChallengeOverviewScreen', title: "Pages/recovery/ChallengeOverviewScreen",
component: TestedComponent, component: TestedComponent,
args: { args: {
order: 5, order: 5,
}, },
argTypes: { argTypes: {
onUpdate: { action: 'onUpdate' }, onUpdate: { action: "onUpdate" },
onBack: { action: 'onBack' }, onBack: { action: "onBack" },
}, },
}; };
export const OneUnsolvedPolicy = createExample(TestedComponent, { export const OneUnsolvedPolicy = createExample(TestedComponent, {
...reducerStatesExample.challengeSelecting, ...reducerStatesExample.challengeSelecting,
recovery_information: { recovery_information: {
policies: [[{ uuid: '1' }]], policies: [[{ uuid: "1" }]],
challenges: [{ challenges: [
cost: 'USD:1', {
instructions: 'just go for it', cost: "USD:1",
type: 'question', instructions: "just go for it",
uuid: '1', type: "question",
}] uuid: "1",
},
],
}, },
} as ReducerState); } as ReducerState);
export const SomePoliciesOneSolved = createExample(TestedComponent, { export const SomePoliciesOneSolved = createExample(TestedComponent, {
...reducerStatesExample.challengeSelecting, ...reducerStatesExample.challengeSelecting,
recovery_information: { recovery_information: {
policies: [[{ uuid: '1' }, { uuid: '2' }], [{ uuid: 'uuid-3' }]], policies: [[{ uuid: "1" }, { uuid: "2" }], [{ uuid: "uuid-3" }]],
challenges: [{ challenges: [
cost: 'USD:1', {
instructions: 'this question cost 1 USD', cost: "USD:1",
type: 'question', instructions: "this question cost 1 USD",
uuid: '1', type: "question",
}, { uuid: "1",
cost: 'USD:0', },
instructions: 'answering this question is free', {
type: 'question', cost: "USD:0",
uuid: '2', instructions: "answering this question is free",
}, { type: "question",
cost: 'USD:1', uuid: "2",
instructions: 'this question is already answered', },
type: 'question', {
uuid: 'uuid-3', cost: "USD:1",
}] instructions: "this question is already answered",
type: "question",
uuid: "uuid-3",
},
],
}, },
challenge_feedback: { challenge_feedback: {
'uuid-3': { "uuid-3": {
state: 'solved' state: "solved",
} },
}, },
} as ReducerState); } as ReducerState);
export const OneBadConfiguredPolicy = createExample(TestedComponent, { export const OneBadConfiguredPolicy = createExample(TestedComponent, {
...reducerStatesExample.challengeSelecting, ...reducerStatesExample.challengeSelecting,
recovery_information: { recovery_information: {
policies: [[{ uuid: '1' }, { uuid: '2' }]], policies: [[{ uuid: "1" }, { uuid: "2" }]],
challenges: [{ challenges: [
cost: 'USD:1', {
instructions: 'this policy has a missing uuid (the other auth method)', cost: "USD:1",
type: 'totp', instructions: "this policy has a missing uuid (the other auth method)",
uuid: '1', type: "totp",
}], uuid: "1",
},
],
}, },
} as ReducerState); } as ReducerState);
export const OnePolicyWithAllTheChallenges = createExample(TestedComponent, { export const OnePolicyWithAllTheChallenges = createExample(TestedComponent, {
...reducerStatesExample.challengeSelecting, ...reducerStatesExample.challengeSelecting,
recovery_information: { recovery_information: {
policies: [[ policies: [
{ uuid: '1' }, [
{ uuid: '2' }, { uuid: "1" },
{ uuid: '3' }, { uuid: "2" },
{ uuid: '4' }, { uuid: "3" },
{ uuid: '5' }, { uuid: "4" },
{ uuid: '6' }, { uuid: "5" },
{ uuid: '7' }, { uuid: "6" },
{ uuid: '8' }, { uuid: "7" },
]], { uuid: "8" },
challenges: [{ ],
cost: 'USD:1', ],
instructions: 'Does P equals NP?', challenges: [
type: 'question', {
uuid: '1', cost: "USD:1",
},{ instructions: "Does P equals NP?",
cost: 'USD:1', type: "question",
instructions: 'SMS to 555-555', uuid: "1",
type: 'sms', },
uuid: '2', {
},{ cost: "USD:1",
cost: 'USD:1', instructions: "SMS to 555-555",
instructions: 'Email to qwe@asd.com', type: "sms",
type: 'email', uuid: "2",
uuid: '3', },
},{ {
cost: 'USD:1', cost: "USD:1",
instructions: 'Enter 8 digits code for "Anastasis"', instructions: "Email to qwe@asd.com",
type: 'totp', type: "email",
uuid: '4', uuid: "3",
},{// },
cost: 'USD:0', {
instructions: 'Wire transfer from ASDXCVQWE123123 with holder Florian', cost: "USD:1",
type: 'iban', instructions: 'Enter 8 digits code for "Anastasis"',
uuid: '5', type: "totp",
},{ uuid: "4",
cost: 'USD:1', },
instructions: 'Join a video call', {
type: 'video',//Enter 8 digits code for "Anastasis" //
uuid: '7', cost: "USD:0",
},{ instructions: "Wire transfer from ASDXCVQWE123123 with holder Florian",
},{ type: "iban",
cost: 'USD:1', uuid: "5",
instructions: 'Letter to address in postal code DE123123', },
type: 'post',//Enter 8 digits code for "Anastasis" {
uuid: '8', cost: "USD:1",
},{ instructions: "Join a video call",
cost: 'USD:1', type: "video", //Enter 8 digits code for "Anastasis"
instructions: 'instruction for an unknown type of challenge', uuid: "7",
type: 'new-type-of-challenge', },
uuid: '6', {},
}], {
cost: "USD:1",
instructions: "Letter to address in postal code DE123123",
type: "post", //Enter 8 digits code for "Anastasis"
uuid: "8",
},
{
cost: "USD:1",
instructions: "instruction for an unknown type of challenge",
type: "new-type-of-challenge",
uuid: "6",
},
],
}, },
} as ReducerState); } as ReducerState);
export const OnePolicyWithAllTheChallengesInDifferentState = createExample(
export const OnePolicyWithAllTheChallengesInDifferentState = createExample(TestedComponent, { TestedComponent,
...reducerStatesExample.challengeSelecting, {
recovery_information: { ...reducerStatesExample.challengeSelecting,
policies: [[ recovery_state: RecoveryStates.ChallengeSelecting,
{ uuid: '1' }, recovery_information: {
{ uuid: '2' }, policies: [
{ uuid: '3' }, [
{ uuid: '4' }, { uuid: "1" },
{ uuid: '5' }, { uuid: "2" },
{ uuid: '6' }, { uuid: "3" },
{ uuid: '7' }, { uuid: "4" },
{ uuid: '8' }, { uuid: "5" },
{ uuid: '9' }, { uuid: "6" },
{ uuid: '10' }, { uuid: "7" },
]], { uuid: "8" },
challenges: [{ { uuid: "9" },
cost: 'USD:1', { uuid: "10" },
instructions: 'in state "solved"', ],
type: 'question', ],
uuid: '1', challenges: [
},{ {
cost: 'USD:1', cost: "USD:1",
instructions: 'in state "hint"', instructions: 'in state "solved"',
type: 'question', type: "question",
uuid: '2', uuid: "1",
},{ },
cost: 'USD:1', {
instructions: 'in state "details"', cost: "USD:1",
type: 'question', instructions: 'in state "message"',
uuid: '3', type: "question",
},{ uuid: "2",
cost: 'USD:1', },
instructions: 'in state "body"', ],
type: 'question', },
uuid: '4', challenge_feedback: {
},{ 1: { state: "solved" },
cost: 'USD:1', 2: { state: "message", message: "Security question was not solved correctly" },
instructions: 'in state "redirect"', // FIXME: add missing feedback states here!
type: 'question', },
uuid: '5', } as ReducerState,
},{ );
cost: 'USD:1', export const NoPolicies = createExample(
instructions: 'in state "server-failure"', TestedComponent,
type: 'question', reducerStatesExample.challengeSelecting,
uuid: '6', );
},{
cost: 'USD:1',
instructions: 'in state "truth-unknown"',
type: 'question',
uuid: '7',
},{
cost: 'USD:1',
instructions: 'in state "rate-limit-exceeded"',
type: 'question',
uuid: '8',
},{
cost: 'USD:1',
instructions: 'in state "authentication-timeout"',
type: 'question',
uuid: '9',
},{
cost: 'USD:1',
instructions: 'in state "external-instructions"',
type: 'question',
uuid: '10',
}],
},
challenge_feedback: {
1: { state: 'solved' },
2: { state: 'hint' },
3: { state: 'details' },
4: { state: 'body' },
5: { state: 'redirect' },
6: { state: 'server-failure' },
7: { state: 'truth-unknown' },
8: { state: 'rate-limit-exceeded' },
9: { state: 'authentication-timeout' },
10: { state: 'external-instructions' },
}
} as ReducerState);
export const NoPolicies = createExample(TestedComponent, reducerStatesExample.challengeSelecting);

View File

@ -1,23 +1,29 @@
/* eslint-disable @typescript-eslint/camelcase */ /* eslint-disable @typescript-eslint/camelcase */
import { ChallengeFeedback } from "anastasis-core"; import { ChallengeFeedback, ChallengeFeedbackStatus } from "anastasis-core";
import { h, VNode } from "preact"; import { h, VNode } from "preact";
import { useAnastasisContext } from "../../context/anastasis"; import { useAnastasisContext } from "../../context/anastasis";
import { AnastasisClientFrame } from "./index"; import { AnastasisClientFrame } from "./index";
import { authMethods, KnownAuthMethods } from "./authMethod"; import { authMethods, KnownAuthMethods } from "./authMethod";
export function ChallengeOverviewScreen(): VNode { export function ChallengeOverviewScreen(): VNode {
const reducer = useAnastasisContext() const reducer = useAnastasisContext();
if (!reducer) { if (!reducer) {
return <div>no reducer in context</div> return <div>no reducer in context</div>;
} }
if (!reducer.currentReducerState || reducer.currentReducerState.recovery_state === undefined) { if (
return <div>invalid state</div> !reducer.currentReducerState ||
reducer.currentReducerState.recovery_state === undefined
) {
return <div>invalid state</div>;
} }
const policies = reducer.currentReducerState.recovery_information?.policies ?? []; const policies =
const knownChallengesArray = reducer.currentReducerState.recovery_information?.challenges ?? []; reducer.currentReducerState.recovery_information?.policies ?? [];
const challengeFeedback = reducer.currentReducerState?.challenge_feedback ?? {}; const knownChallengesArray =
reducer.currentReducerState.recovery_information?.challenges ?? [];
const challengeFeedback =
reducer.currentReducerState?.challenge_feedback ?? {};
const knownChallengesMap: { const knownChallengesMap: {
[uuid: string]: { [uuid: string]: {
@ -32,51 +38,80 @@ export function ChallengeOverviewScreen(): VNode {
type: ch.type, type: ch.type,
cost: ch.cost, cost: ch.cost,
instructions: ch.instructions, instructions: ch.instructions,
feedback: challengeFeedback[ch.uuid] feedback: challengeFeedback[ch.uuid],
}; };
} }
const policiesWithInfo = policies.map(row => { const policiesWithInfo = policies.map((row) => {
let isPolicySolved = true let isPolicySolved = true;
const challenges = row.map(({ uuid }) => { const challenges = row
const info = knownChallengesMap[uuid]; .map(({ uuid }) => {
const isChallengeSolved = info?.feedback?.state === 'solved' const info = knownChallengesMap[uuid];
isPolicySolved = isPolicySolved && isChallengeSolved const isChallengeSolved = info?.feedback?.state === "solved";
return { info, uuid, isChallengeSolved } isPolicySolved = isPolicySolved && isChallengeSolved;
}).filter(ch => ch.info !== undefined) return { info, uuid, isChallengeSolved };
})
.filter((ch) => ch.info !== undefined);
return { isPolicySolved, challenges } return { isPolicySolved, challenges };
}) });
const atLeastThereIsOnePolicySolved = policiesWithInfo.find(p => p.isPolicySolved) !== undefined const atLeastThereIsOnePolicySolved =
policiesWithInfo.find((p) => p.isPolicySolved) !== undefined;
const errors = !atLeastThereIsOnePolicySolved ? "Solve one policy before proceeding" : undefined; const errors = !atLeastThereIsOnePolicySolved
? "Solve one policy before proceeding"
: undefined;
return ( return (
<AnastasisClientFrame hideNext={errors} title="Recovery: Solve challenges"> <AnastasisClientFrame hideNext={errors} title="Recovery: Solve challenges">
{!policies.length ? <p class="block"> {!policies.length ? (
No policies found, try with another version of the secret <p class="block">
</p> : (policies.length === 1 ? <p class="block"> No policies found, try with another version of the secret
One policy found for this secret. You need to solve all the challenges in order to recover your secret. </p>
</p> : <p class="block"> ) : policies.length === 1 ? (
We have found {policies.length} polices. You need to solve all the challenges from one policy in order <p class="block">
to recover your secret. One policy found for this secret. You need to solve all the challenges
</p>)} in order to recover your secret.
</p>
) : (
<p class="block">
We have found {policies.length} polices. You need to solve all the
challenges from one policy in order to recover your secret.
</p>
)}
{policiesWithInfo.map((policy, policy_index) => { {policiesWithInfo.map((policy, policy_index) => {
const tableBody = policy.challenges.map(({ info, uuid }) => { const tableBody = policy.challenges.map(({ info, uuid }) => {
const isFree = !info.cost || info.cost.endsWith(':0') const isFree = !info.cost || info.cost.endsWith(":0");
const method = authMethods[info.type as KnownAuthMethods] const method = authMethods[info.type as KnownAuthMethods];
return ( return (
<div key={uuid} class="block" style={{ display: 'flex', justifyContent: 'space-between' }}> <div
<div style={{display:'flex', alignItems:'center'}}> key={uuid}
<span class="icon"> class="block"
{method?.icon} style={{ display: "flex", justifyContent: "space-between" }}
</span> >
<span> <div
{info.instructions} style={{
</span> display: "flex",
flexDirection: "column",
}}
>
<div style={{ display: "flex", alignItems: "center" }}>
<span class="icon">{method?.icon}</span>
<span>{info.instructions}</span>
</div>
{info.feedback?.state === ChallengeFeedbackStatus.Message ? (
<div>
<p>{info.feedback.message}</p>
</div>
) : null}
</div> </div>
<div> <div>
{method && info.feedback?.state !== "solved" ? ( {method && info.feedback?.state !== "solved" ? (
<a class="button" onClick={() => reducer.transition("select_challenge", { uuid })}> <a
class="button"
onClick={() =>
reducer.transition("select_challenge", { uuid })
}
>
{isFree ? "Solve" : `Pay and Solve`} {isFree ? "Solve" : `Pay and Solve`}
</a> </a>
) : null} ) : null}
@ -86,26 +121,36 @@ export function ChallengeOverviewScreen(): VNode {
</div> </div>
</div> </div>
); );
}) });
const policyName = policy.challenges.map(x => x.info.type).join(" + "); const policyName = policy.challenges
const opa = !atLeastThereIsOnePolicySolved ? undefined : ( policy.isPolicySolved ? undefined : '0.6') .map((x) => x.info.type)
.join(" + ");
const opa = !atLeastThereIsOnePolicySolved
? undefined
: policy.isPolicySolved
? undefined
: "0.6";
return ( return (
<div key={policy_index} class="box" style={{ <div
opacity: opa key={policy_index}
}}> class="box"
style={{
opacity: opa,
}}
>
<h3 class="subtitle"> <h3 class="subtitle">
Policy #{policy_index + 1}: {policyName} Policy #{policy_index + 1}: {policyName}
</h3> </h3>
{policy.challenges.length === 0 && <p> {policy.challenges.length === 0 && (
This policy doesn't have challenges. <p>This policy doesn't have challenges.</p>
</p>} )}
{policy.challenges.length === 1 && <p> {policy.challenges.length === 1 && (
This policy just have one challenge. <p>This policy just have one challenge.</p>
</p>} )}
{policy.challenges.length > 1 && <p> {policy.challenges.length > 1 && (
This policy have {policy.challenges.length} challenges. <p>This policy have {policy.challenges.length} challenges.</p>
</p>} )}
{tableBody} {tableBody}
</div> </div>
); );

View File

@ -16,7 +16,7 @@ export function CountrySelectionScreen(): VNode {
currencies: [x.currency], currencies: [x.currency],
}); });
return ( return (
<AnastasisClientFrame hideNext title={withProcessLabel(reducer, "Select Country")} > <AnastasisClientFrame hideNext={"FIXME"} title={withProcessLabel(reducer, "Select Country")} >
<div style={{ display: 'flex', flexDirection: 'column' }}> <div style={{ display: 'flex', flexDirection: 'column' }}>
{reducer.currentReducerState.countries!.map((x: any) => ( {reducer.currentReducerState.countries!.map((x: any) => (
<div key={x.name}> <div key={x.name}>

View File

@ -13,7 +13,7 @@ export function TruthsPayingScreen(): VNode {
const payments = reducer.currentReducerState.payments ?? []; const payments = reducer.currentReducerState.payments ?? [];
return ( return (
<AnastasisClientFrame <AnastasisClientFrame
hideNext hideNext={"FIXME"}
title="Backup: Truths Paying" title="Backup: Truths Paying"
> >
<p> <p>