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: "Email to qwe@asd.com",
type: "email",
uuid: "3",
},
{
cost: "USD:1",
instructions: 'Enter 8 digits code for "Anastasis"', instructions: 'Enter 8 digits code for "Anastasis"',
type: 'totp', type: "totp",
uuid: '4', uuid: "4",
},{// },
cost: 'USD:0', {
instructions: 'Wire transfer from ASDXCVQWE123123 with holder Florian', //
type: 'iban', cost: "USD:0",
uuid: '5', instructions: "Wire transfer from ASDXCVQWE123123 with holder Florian",
},{ type: "iban",
cost: 'USD:1', uuid: "5",
instructions: 'Join a video call', },
type: 'video',//Enter 8 digits code for "Anastasis" {
uuid: '7', cost: "USD:1",
},{ instructions: "Join a video call",
},{ type: "video", //Enter 8 digits code for "Anastasis"
cost: 'USD:1', uuid: "7",
instructions: 'Letter to address in postal code DE123123', },
type: 'post',//Enter 8 digits code for "Anastasis" {},
uuid: '8', {
},{ cost: "USD:1",
cost: 'USD:1', instructions: "Letter to address in postal code DE123123",
instructions: 'instruction for an unknown type of challenge', type: "post", //Enter 8 digits code for "Anastasis"
type: 'new-type-of-challenge', uuid: "8",
uuid: '6', },
}], {
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, ...reducerStatesExample.challengeSelecting,
recovery_state: RecoveryStates.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: '9' }, { uuid: "8" },
{ uuid: '10' }, { uuid: "9" },
]], { uuid: "10" },
challenges: [{ ],
cost: 'USD:1', ],
challenges: [
{
cost: "USD:1",
instructions: 'in state "solved"', instructions: 'in state "solved"',
type: 'question', type: "question",
uuid: '1', uuid: "1",
},{ },
cost: 'USD:1', {
instructions: 'in state "hint"', cost: "USD:1",
type: 'question', instructions: 'in state "message"',
uuid: '2', type: "question",
},{ uuid: "2",
cost: 'USD:1', },
instructions: 'in state "details"', ],
type: 'question',
uuid: '3',
},{
cost: 'USD:1',
instructions: 'in state "body"',
type: 'question',
uuid: '4',
},{
cost: 'USD:1',
instructions: 'in state "redirect"',
type: 'question',
uuid: '5',
},{
cost: 'USD:1',
instructions: 'in state "server-failure"',
type: 'question',
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: { challenge_feedback: {
1: { state: 'solved' }, 1: { state: "solved" },
2: { state: 'hint' }, 2: { state: "message", message: "Security question was not solved correctly" },
3: { state: 'details' }, // FIXME: add missing feedback states here!
4: { state: 'body' }, },
5: { state: 'redirect' }, } as ReducerState,
6: { state: 'server-failure' }, );
7: { state: 'truth-unknown' }, export const NoPolicies = createExample(
8: { state: 'rate-limit-exceeded' }, TestedComponent,
9: { state: 'authentication-timeout' }, reducerStatesExample.challengeSelecting,
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
.map(({ uuid }) => {
const info = knownChallengesMap[uuid]; const info = knownChallengesMap[uuid];
const isChallengeSolved = info?.feedback?.state === 'solved' const isChallengeSolved = info?.feedback?.state === "solved";
isPolicySolved = isPolicySolved && isChallengeSolved isPolicySolved = isPolicySolved && isChallengeSolved;
return { info, uuid, isChallengeSolved } return { info, uuid, isChallengeSolved };
}).filter(ch => ch.info !== undefined)
return { isPolicySolved, challenges }
}) })
.filter((ch) => ch.info !== undefined);
const atLeastThereIsOnePolicySolved = policiesWithInfo.find(p => p.isPolicySolved) !== undefined return { isPolicySolved, challenges };
});
const errors = !atLeastThereIsOnePolicySolved ? "Solve one policy before proceeding" : undefined; const atLeastThereIsOnePolicySolved =
policiesWithInfo.find((p) => p.isPolicySolved) !== 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 ? (
<p class="block">
No policies found, try with another version of the secret No policies found, try with another version of the secret
</p> : (policies.length === 1 ? <p class="block"> </p>
One policy found for this secret. You need to solve all the challenges in order to recover your secret. ) : policies.length === 1 ? (
</p> : <p class="block"> <p class="block">
We have found {policies.length} polices. You need to solve all the challenges from one policy in order One policy found for this secret. You need to solve all the challenges
to recover your secret. in order to recover your secret.
</p>)} </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>