aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--packages/anastasis-core/src/index.ts1
-rw-r--r--packages/anastasis-webui/src/pages/home/ChallengeOverviewScreen.stories.tsx339
-rw-r--r--packages/anastasis-webui/src/pages/home/ChallengeOverviewScreen.tsx157
-rw-r--r--packages/anastasis-webui/src/pages/home/CountrySelectionScreen.tsx2
-rw-r--r--packages/anastasis-webui/src/pages/home/TruthsPayingScreen.tsx2
5 files changed, 265 insertions, 236 deletions
diff --git a/packages/anastasis-core/src/index.ts b/packages/anastasis-core/src/index.ts
index 859dd083b..3b73aa002 100644
--- a/packages/anastasis-core/src/index.ts
+++ b/packages/anastasis-core/src/index.ts
@@ -96,6 +96,7 @@ const { fetch } = fetchPonyfill({});
export * from "./reducer-types.js";
export * as validators from "./validators.js";
+export * from "./challenge-feedback-types.js";
const logger = new Logger("anastasis-core:index.ts");
diff --git a/packages/anastasis-webui/src/pages/home/ChallengeOverviewScreen.stories.tsx b/packages/anastasis-webui/src/pages/home/ChallengeOverviewScreen.stories.tsx
index a89b5640c..48115c798 100644
--- a/packages/anastasis-webui/src/pages/home/ChallengeOverviewScreen.stories.tsx
+++ b/packages/anastasis-webui/src/pages/home/ChallengeOverviewScreen.stories.tsx
@@ -16,218 +16,201 @@
*/
/**
-*
-* @author Sebastian Javier Marchano (sebasjm)
-*/
-
-import { RecoveryStates, ReducerState } from 'anastasis-core';
-import { createExample, reducerStatesExample } from '../../utils';
-import { ChallengeOverviewScreen as TestedComponent } from './ChallengeOverviewScreen';
+ *
+ * @author Sebastian Javier Marchano (sebasjm)
+ */
+import { RecoveryStates, ReducerState } from "anastasis-core";
+import { createExample, reducerStatesExample } from "../../utils";
+import { ChallengeOverviewScreen as TestedComponent } from "./ChallengeOverviewScreen";
export default {
- title: 'Pages/recovery/ChallengeOverviewScreen',
+ title: "Pages/recovery/ChallengeOverviewScreen",
component: TestedComponent,
args: {
order: 5,
},
argTypes: {
- onUpdate: { action: 'onUpdate' },
- onBack: { action: 'onBack' },
+ onUpdate: { action: "onUpdate" },
+ onBack: { action: "onBack" },
},
};
export const OneUnsolvedPolicy = createExample(TestedComponent, {
...reducerStatesExample.challengeSelecting,
recovery_information: {
- policies: [[{ uuid: '1' }]],
- challenges: [{
- cost: 'USD:1',
- instructions: 'just go for it',
- type: 'question',
- uuid: '1',
- }]
+ policies: [[{ uuid: "1" }]],
+ challenges: [
+ {
+ cost: "USD:1",
+ instructions: "just go for it",
+ type: "question",
+ uuid: "1",
+ },
+ ],
},
} as ReducerState);
export const SomePoliciesOneSolved = createExample(TestedComponent, {
...reducerStatesExample.challengeSelecting,
recovery_information: {
- policies: [[{ uuid: '1' }, { uuid: '2' }], [{ uuid: 'uuid-3' }]],
- challenges: [{
- cost: 'USD:1',
- instructions: 'this question cost 1 USD',
- type: 'question',
- uuid: '1',
- }, {
- cost: 'USD:0',
- instructions: 'answering this question is free',
- type: 'question',
- uuid: '2',
- }, {
- cost: 'USD:1',
- instructions: 'this question is already answered',
- type: 'question',
- uuid: 'uuid-3',
- }]
+ policies: [[{ uuid: "1" }, { uuid: "2" }], [{ uuid: "uuid-3" }]],
+ challenges: [
+ {
+ cost: "USD:1",
+ instructions: "this question cost 1 USD",
+ type: "question",
+ uuid: "1",
+ },
+ {
+ cost: "USD:0",
+ instructions: "answering this question is free",
+ type: "question",
+ uuid: "2",
+ },
+ {
+ cost: "USD:1",
+ instructions: "this question is already answered",
+ type: "question",
+ uuid: "uuid-3",
+ },
+ ],
},
challenge_feedback: {
- 'uuid-3': {
- state: 'solved'
- }
+ "uuid-3": {
+ state: "solved",
+ },
},
} as ReducerState);
export const OneBadConfiguredPolicy = createExample(TestedComponent, {
...reducerStatesExample.challengeSelecting,
recovery_information: {
- policies: [[{ uuid: '1' }, { uuid: '2' }]],
- challenges: [{
- cost: 'USD:1',
- instructions: 'this policy has a missing uuid (the other auth method)',
- type: 'totp',
- uuid: '1',
- }],
+ policies: [[{ uuid: "1" }, { uuid: "2" }]],
+ challenges: [
+ {
+ cost: "USD:1",
+ instructions: "this policy has a missing uuid (the other auth method)",
+ type: "totp",
+ uuid: "1",
+ },
+ ],
},
} as ReducerState);
export const OnePolicyWithAllTheChallenges = createExample(TestedComponent, {
...reducerStatesExample.challengeSelecting,
recovery_information: {
- policies: [[
- { uuid: '1' },
- { uuid: '2' },
- { uuid: '3' },
- { uuid: '4' },
- { uuid: '5' },
- { uuid: '6' },
- { uuid: '7' },
- { uuid: '8' },
- ]],
- challenges: [{
- cost: 'USD:1',
- instructions: 'Does P equals NP?',
- type: 'question',
- uuid: '1',
- },{
- cost: 'USD:1',
- instructions: 'SMS to 555-555',
- type: 'sms',
- uuid: '2',
- },{
- cost: 'USD:1',
- instructions: 'Email to qwe@asd.com',
- type: 'email',
- uuid: '3',
- },{
- cost: 'USD:1',
- instructions: 'Enter 8 digits code for "Anastasis"',
- type: 'totp',
- uuid: '4',
- },{//
- cost: 'USD:0',
- instructions: 'Wire transfer from ASDXCVQWE123123 with holder Florian',
- type: 'iban',
- uuid: '5',
- },{
- cost: 'USD:1',
- instructions: 'Join a video call',
- type: 'video',//Enter 8 digits code for "Anastasis"
- uuid: '7',
- },{
- },{
- 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',
- }],
+ policies: [
+ [
+ { uuid: "1" },
+ { uuid: "2" },
+ { uuid: "3" },
+ { uuid: "4" },
+ { uuid: "5" },
+ { uuid: "6" },
+ { uuid: "7" },
+ { uuid: "8" },
+ ],
+ ],
+ challenges: [
+ {
+ cost: "USD:1",
+ instructions: "Does P equals NP?",
+ type: "question",
+ uuid: "1",
+ },
+ {
+ cost: "USD:1",
+ instructions: "SMS to 555-555",
+ type: "sms",
+ uuid: "2",
+ },
+ {
+ cost: "USD:1",
+ instructions: "Email to qwe@asd.com",
+ type: "email",
+ uuid: "3",
+ },
+ {
+ cost: "USD:1",
+ instructions: 'Enter 8 digits code for "Anastasis"',
+ type: "totp",
+ uuid: "4",
+ },
+ {
+ //
+ cost: "USD:0",
+ instructions: "Wire transfer from ASDXCVQWE123123 with holder Florian",
+ type: "iban",
+ uuid: "5",
+ },
+ {
+ cost: "USD:1",
+ instructions: "Join a video call",
+ type: "video", //Enter 8 digits code for "Anastasis"
+ uuid: "7",
+ },
+ {},
+ {
+ 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);
-
-export const OnePolicyWithAllTheChallengesInDifferentState = createExample(TestedComponent, {
- ...reducerStatesExample.challengeSelecting,
- recovery_information: {
- policies: [[
- { uuid: '1' },
- { uuid: '2' },
- { uuid: '3' },
- { uuid: '4' },
- { uuid: '5' },
- { uuid: '6' },
- { uuid: '7' },
- { uuid: '8' },
- { uuid: '9' },
- { uuid: '10' },
- ]],
- challenges: [{
- cost: 'USD:1',
- instructions: 'in state "solved"',
- type: 'question',
- uuid: '1',
- },{
- cost: 'USD:1',
- instructions: 'in state "hint"',
- 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: {
- 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);
+export const OnePolicyWithAllTheChallengesInDifferentState = createExample(
+ TestedComponent,
+ {
+ ...reducerStatesExample.challengeSelecting,
+ recovery_state: RecoveryStates.ChallengeSelecting,
+ recovery_information: {
+ policies: [
+ [
+ { uuid: "1" },
+ { uuid: "2" },
+ { uuid: "3" },
+ { uuid: "4" },
+ { uuid: "5" },
+ { uuid: "6" },
+ { uuid: "7" },
+ { uuid: "8" },
+ { uuid: "9" },
+ { uuid: "10" },
+ ],
+ ],
+ challenges: [
+ {
+ cost: "USD:1",
+ instructions: 'in state "solved"',
+ type: "question",
+ uuid: "1",
+ },
+ {
+ cost: "USD:1",
+ instructions: 'in state "message"',
+ type: "question",
+ uuid: "2",
+ },
+ ],
+ },
+ challenge_feedback: {
+ 1: { state: "solved" },
+ 2: { state: "message", message: "Security question was not solved correctly" },
+ // FIXME: add missing feedback states here!
+ },
+ } as ReducerState,
+);
+export const NoPolicies = createExample(
+ TestedComponent,
+ reducerStatesExample.challengeSelecting,
+);
diff --git a/packages/anastasis-webui/src/pages/home/ChallengeOverviewScreen.tsx b/packages/anastasis-webui/src/pages/home/ChallengeOverviewScreen.tsx
index 7b9b060ce..c63f19eb6 100644
--- a/packages/anastasis-webui/src/pages/home/ChallengeOverviewScreen.tsx
+++ b/packages/anastasis-webui/src/pages/home/ChallengeOverviewScreen.tsx
@@ -1,23 +1,29 @@
/* eslint-disable @typescript-eslint/camelcase */
-import { ChallengeFeedback } from "anastasis-core";
+import { ChallengeFeedback, ChallengeFeedbackStatus } from "anastasis-core";
import { h, VNode } from "preact";
import { useAnastasisContext } from "../../context/anastasis";
import { AnastasisClientFrame } from "./index";
import { authMethods, KnownAuthMethods } from "./authMethod";
export function ChallengeOverviewScreen(): VNode {
- const reducer = useAnastasisContext()
+ const reducer = useAnastasisContext();
if (!reducer) {
- return <div>no reducer in context</div>
+ return <div>no reducer in context</div>;
}
- if (!reducer.currentReducerState || reducer.currentReducerState.recovery_state === undefined) {
- return <div>invalid state</div>
+ if (
+ !reducer.currentReducerState ||
+ reducer.currentReducerState.recovery_state === undefined
+ ) {
+ return <div>invalid state</div>;
}
- const policies = reducer.currentReducerState.recovery_information?.policies ?? [];
- const knownChallengesArray = reducer.currentReducerState.recovery_information?.challenges ?? [];
- const challengeFeedback = reducer.currentReducerState?.challenge_feedback ?? {};
+ const policies =
+ reducer.currentReducerState.recovery_information?.policies ?? [];
+ const knownChallengesArray =
+ reducer.currentReducerState.recovery_information?.challenges ?? [];
+ const challengeFeedback =
+ reducer.currentReducerState?.challenge_feedback ?? {};
const knownChallengesMap: {
[uuid: string]: {
@@ -32,51 +38,80 @@ export function ChallengeOverviewScreen(): VNode {
type: ch.type,
cost: ch.cost,
instructions: ch.instructions,
- feedback: challengeFeedback[ch.uuid]
+ feedback: challengeFeedback[ch.uuid],
};
}
- const policiesWithInfo = policies.map(row => {
- let isPolicySolved = true
- const challenges = row.map(({ uuid }) => {
- const info = knownChallengesMap[uuid];
- const isChallengeSolved = info?.feedback?.state === 'solved'
- isPolicySolved = isPolicySolved && isChallengeSolved
- return { info, uuid, isChallengeSolved }
- }).filter(ch => ch.info !== undefined)
+ const policiesWithInfo = policies.map((row) => {
+ let isPolicySolved = true;
+ const challenges = row
+ .map(({ uuid }) => {
+ const info = knownChallengesMap[uuid];
+ const isChallengeSolved = info?.feedback?.state === "solved";
+ isPolicySolved = isPolicySolved && isChallengeSolved;
+ 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 (
<AnastasisClientFrame hideNext={errors} title="Recovery: Solve challenges">
- {!policies.length ? <p class="block">
- No policies found, try with another version of the secret
- </p> : (policies.length === 1 ? <p class="block">
- One policy found for this secret. You need to solve all the challenges 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>)}
+ {!policies.length ? (
+ <p class="block">
+ No policies found, try with another version of the secret
+ </p>
+ ) : policies.length === 1 ? (
+ <p class="block">
+ One policy found for this secret. You need to solve all the challenges
+ 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) => {
const tableBody = policy.challenges.map(({ info, uuid }) => {
- const isFree = !info.cost || info.cost.endsWith(':0')
- const method = authMethods[info.type as KnownAuthMethods]
+ const isFree = !info.cost || info.cost.endsWith(":0");
+ const method = authMethods[info.type as KnownAuthMethods];
return (
- <div key={uuid} class="block" style={{ display: 'flex', justifyContent: 'space-between' }}>
- <div style={{display:'flex', alignItems:'center'}}>
- <span class="icon">
- {method?.icon}
- </span>
- <span>
- {info.instructions}
- </span>
+ <div
+ key={uuid}
+ class="block"
+ style={{ display: "flex", justifyContent: "space-between" }}
+ >
+ <div
+ style={{
+ 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>
{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`}
</a>
) : null}
@@ -86,26 +121,36 @@ export function ChallengeOverviewScreen(): VNode {
</div>
</div>
);
- })
-
- const policyName = policy.challenges.map(x => x.info.type).join(" + ");
- const opa = !atLeastThereIsOnePolicySolved ? undefined : ( policy.isPolicySolved ? undefined : '0.6')
+ });
+
+ const policyName = policy.challenges
+ .map((x) => x.info.type)
+ .join(" + ");
+ const opa = !atLeastThereIsOnePolicySolved
+ ? undefined
+ : policy.isPolicySolved
+ ? undefined
+ : "0.6";
return (
- <div key={policy_index} class="box" style={{
- opacity: opa
- }}>
+ <div
+ key={policy_index}
+ class="box"
+ style={{
+ opacity: opa,
+ }}
+ >
<h3 class="subtitle">
Policy #{policy_index + 1}: {policyName}
</h3>
- {policy.challenges.length === 0 && <p>
- This policy doesn't have challenges.
- </p>}
- {policy.challenges.length === 1 && <p>
- This policy just have one challenge.
- </p>}
- {policy.challenges.length > 1 && <p>
- This policy have {policy.challenges.length} challenges.
- </p>}
+ {policy.challenges.length === 0 && (
+ <p>This policy doesn't have challenges.</p>
+ )}
+ {policy.challenges.length === 1 && (
+ <p>This policy just have one challenge.</p>
+ )}
+ {policy.challenges.length > 1 && (
+ <p>This policy have {policy.challenges.length} challenges.</p>
+ )}
{tableBody}
</div>
);
diff --git a/packages/anastasis-webui/src/pages/home/CountrySelectionScreen.tsx b/packages/anastasis-webui/src/pages/home/CountrySelectionScreen.tsx
index 77329f4fa..b64e1a096 100644
--- a/packages/anastasis-webui/src/pages/home/CountrySelectionScreen.tsx
+++ b/packages/anastasis-webui/src/pages/home/CountrySelectionScreen.tsx
@@ -16,7 +16,7 @@ export function CountrySelectionScreen(): VNode {
currencies: [x.currency],
});
return (
- <AnastasisClientFrame hideNext title={withProcessLabel(reducer, "Select Country")} >
+ <AnastasisClientFrame hideNext={"FIXME"} title={withProcessLabel(reducer, "Select Country")} >
<div style={{ display: 'flex', flexDirection: 'column' }}>
{reducer.currentReducerState.countries!.map((x: any) => (
<div key={x.name}>
diff --git a/packages/anastasis-webui/src/pages/home/TruthsPayingScreen.tsx b/packages/anastasis-webui/src/pages/home/TruthsPayingScreen.tsx
index 098a8ba8d..0b32e0db5 100644
--- a/packages/anastasis-webui/src/pages/home/TruthsPayingScreen.tsx
+++ b/packages/anastasis-webui/src/pages/home/TruthsPayingScreen.tsx
@@ -13,7 +13,7 @@ export function TruthsPayingScreen(): VNode {
const payments = reducer.currentReducerState.payments ?? [];
return (
<AnastasisClientFrame
- hideNext
+ hideNext={"FIXME"}
title="Backup: Truths Paying"
>
<p>