refactoring challenge overview to look more like policy reviewing

This commit is contained in:
Sebastian 2021-11-02 12:31:37 -03:00
parent aa78c1105e
commit 1fd337f4fe
No known key found for this signature in database
GPG Key ID: BE4FF68352439FC1
20 changed files with 85 additions and 68 deletions

View File

@ -3,7 +3,7 @@ import { AuthMethod } from "anastasis-core";
import { ComponentChildren, h, VNode } from "preact"; import { ComponentChildren, h, VNode } from "preact";
import { useState } from "preact/hooks"; import { useState } from "preact/hooks";
import { useAnastasisContext } from "../../context/anastasis"; import { useAnastasisContext } from "../../context/anastasis";
import { authMethods, KnownAuthMethods } from "./authMethodSetup"; import { authMethods, KnownAuthMethods } from "./authMethod";
import { AnastasisClientFrame } from "./index"; import { AnastasisClientFrame } from "./index";

View File

@ -56,17 +56,17 @@ export const SomePoliciesOneSolved = createExample(TestedComponent, {
policies: [[{ uuid: '1' }, { uuid: '2' }], [{ uuid: 'uuid-3' }]], policies: [[{ uuid: '1' }, { uuid: '2' }], [{ uuid: 'uuid-3' }]],
challenges: [{ challenges: [{
cost: 'USD:1', cost: 'USD:1',
instructions: 'just go for it', instructions: 'this question cost 1 USD',
type: 'question', type: 'question',
uuid: '1', uuid: '1',
}, { }, {
cost: 'USD:1', cost: 'USD:0',
instructions: 'just go for it', instructions: 'answering this question is free',
type: 'question', type: 'question',
uuid: '2', uuid: '2',
}, { }, {
cost: 'USD:1', cost: 'USD:1',
instructions: 'just go for it', instructions: 'this question is already answered',
type: 'question', type: 'question',
uuid: 'uuid-3', uuid: 'uuid-3',
}] }]
@ -84,8 +84,8 @@ export const OneBadConfiguredPolicy = createExample(TestedComponent, {
policies: [[{ uuid: '1' }, { uuid: '2' }]], policies: [[{ uuid: '1' }, { uuid: '2' }]],
challenges: [{ challenges: [{
cost: 'USD:1', cost: 'USD:1',
instructions: 'just go for it', instructions: 'this policy has a missing uuid (the other auth method)',
type: 'sasd', type: 'totp',
uuid: '1', uuid: '1',
}], }],
}, },
@ -101,35 +101,48 @@ export const OnePolicyWithAllTheChallenges = createExample(TestedComponent, {
{ uuid: '4' }, { uuid: '4' },
{ uuid: '5' }, { uuid: '5' },
{ uuid: '6' }, { uuid: '6' },
{ uuid: '7' },
{ uuid: '8' },
]], ]],
challenges: [{ challenges: [{
cost: 'USD:1', cost: 'USD:1',
instructions: 'answer the a question correctly', instructions: 'Does P equals NP?',
type: 'question', type: 'question',
uuid: '1', uuid: '1',
},{ },{
cost: 'USD:1', cost: 'USD:1',
instructions: 'enter a text received by a sms', instructions: 'SMS to 555-555',
type: 'sms', type: 'sms',
uuid: '2', uuid: '2',
},{ },{
cost: 'USD:1', cost: 'USD:1',
instructions: 'enter a text received by a email', instructions: 'Email to qwe@asd.com',
type: 'email', type: 'email',
uuid: '3', uuid: '3',
},{ },{
cost: 'USD:1', cost: 'USD:1',
instructions: 'enter a code based on a time-based one-time password', instructions: 'Enter 8 digits code for "Anastasis"',
type: 'totp', type: 'totp',
uuid: '4', uuid: '4',
},{ },{//
cost: 'USD:1', cost: 'USD:0',
instructions: 'send a wire transfer to an account', instructions: 'Wire transfer from ASDXCVQWE123123 with holder Florian',
type: 'iban', type: 'iban',
uuid: '5', uuid: '5',
},{ },{
cost: 'USD:1', cost: 'USD:1',
instructions: 'just go for it', 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', type: 'new-type-of-challenge',
uuid: '6', uuid: '6',
}], }],
@ -154,52 +167,52 @@ export const OnePolicyWithAllTheChallengesInDifferentState = createExample(Teste
]], ]],
challenges: [{ challenges: [{
cost: 'USD:1', cost: 'USD:1',
instructions: 'answer the a question correctly', instructions: 'in state "solved"',
type: 'question', type: 'question',
uuid: '1', uuid: '1',
},{ },{
cost: 'USD:1', cost: 'USD:1',
instructions: 'answer the a question correctly', instructions: 'in state "hint"',
type: 'question', type: 'question',
uuid: '2', uuid: '2',
},{ },{
cost: 'USD:1', cost: 'USD:1',
instructions: 'answer the a question correctly', instructions: 'in state "details"',
type: 'question', type: 'question',
uuid: '3', uuid: '3',
},{ },{
cost: 'USD:1', cost: 'USD:1',
instructions: 'answer the a question correctly', instructions: 'in state "body"',
type: 'question', type: 'question',
uuid: '4', uuid: '4',
},{ },{
cost: 'USD:1', cost: 'USD:1',
instructions: 'answer the a question correctly', instructions: 'in state "redirect"',
type: 'question', type: 'question',
uuid: '5', uuid: '5',
},{ },{
cost: 'USD:1', cost: 'USD:1',
instructions: 'answer the a question correctly', instructions: 'in state "server-failure"',
type: 'question', type: 'question',
uuid: '6', uuid: '6',
},{ },{
cost: 'USD:1', cost: 'USD:1',
instructions: 'answer the a question correctly', instructions: 'in state "truth-unknown"',
type: 'question', type: 'question',
uuid: '7', uuid: '7',
},{ },{
cost: 'USD:1', cost: 'USD:1',
instructions: 'answer the a question correctly', instructions: 'in state "rate-limit-exceeded"',
type: 'question', type: 'question',
uuid: '8', uuid: '8',
},{ },{
cost: 'USD:1', cost: 'USD:1',
instructions: 'answer the a question correctly', instructions: 'in state "authentication-timeout"',
type: 'question', type: 'question',
uuid: '9', uuid: '9',
},{ },{
cost: 'USD:1', cost: 'USD:1',
instructions: 'answer the a question correctly', instructions: 'in state "external-instructions"',
type: 'question', type: 'question',
uuid: '10', uuid: '10',
}], }],

View File

@ -1,7 +1,9 @@
/* eslint-disable @typescript-eslint/camelcase */
import { ChallengeFeedback } from "anastasis-core"; import { ChallengeFeedback } 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";
export function ChallengeOverviewScreen(): VNode { export function ChallengeOverviewScreen(): VNode {
const reducer = useAnastasisContext() const reducer = useAnastasisContext()
@ -50,59 +52,61 @@ export function ChallengeOverviewScreen(): VNode {
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> {!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> </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. One policy found for this secret. You need to solve all the challenges in order to recover your secret.
</p> : <p> </p> : <p class="block">
We have found {policies.length} polices. You need to solve all the challenges from one policy in order We have found {policies.length} polices. You need to solve all the challenges from one policy in order
to recover your secret. to recover your secret.
</p>)} </p>)}
{policiesWithInfo.map((row, i) => { {policiesWithInfo.map((policy, policy_index) => {
const tableBody = row.challenges.map(({ info, uuid }) => { const tableBody = policy.challenges.map(({ info, uuid }) => {
const isFree = !info.cost || info.cost.endsWith(':0')
const method = authMethods[info.type as KnownAuthMethods]
return ( return (
<tr key={uuid}> <div key={uuid} class="block" style={{ display: 'flex', justifyContent: 'space-between' }}>
<td>{info.type}</td> <div style={{display:'flex', alignItems:'center'}}>
<td> <span class="icon">
{info.instructions} {method?.icon}
</td> </span>
<td>{info.feedback?.state ?? "unknown"}</td> <span>
<td>{info.cost}</td> {info.instructions}
<td> </span>
{info.feedback?.state !== "solved" ? ( </div>
<a onClick={() => reducer.transition("select_challenge", { uuid })}> <div>
Solve {method && info.feedback?.state !== "solved" ? (
<a class="button" onClick={() => reducer.transition("select_challenge", { uuid })}>
{isFree ? "Solve" : `Pay and Solve`}
</a> </a>
) : null} ) : null}
</td> {info.feedback?.state === "solved" ? (
</tr> <a class="button is-success"> Solved </a>
) : null}
</div>
</div>
); );
}) })
const policyName = policy.challenges.map(x => x.info.type).join(" + ");
const opa = !atLeastThereIsOnePolicySolved ? undefined : ( policy.isPolicySolved ? undefined : '0.6')
return ( return (
<div key={i}> <div key={policy_index} class="box" style={{
<b>Policy #{i + 1}</b> opacity: opa
{row.challenges.length === 0 && <p> }}>
This policy doesn't have challenges <h3 class="subtitle">
Policy #{policy_index + 1}: {policyName}
</h3>
{policy.challenges.length === 0 && <p>
This policy doesn't have challenges.
</p>} </p>}
{row.challenges.length === 1 && <p> {policy.challenges.length === 1 && <p>
This policy just have one challenge to be solved This policy just have one challenge.
</p>} </p>}
{row.challenges.length > 1 && <p> {policy.challenges.length > 1 && <p>
This policy have {row.challenges.length} challenges This policy have {policy.challenges.length} challenges.
</p>} </p>}
<table class="table"> {tableBody}
<thead>
<tr>
<td>Challenge type</td>
<td>Description</td>
<td>Status</td>
<td>Cost</td>
</tr>
</thead>
<tbody>
{tableBody}
</tbody>
</table>
</div> </div>
); );
})} })}

View File

@ -2,7 +2,7 @@
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 "./authMethodSetup"; import { authMethods, KnownAuthMethods } from "./authMethod";
export function ReviewPoliciesScreen(): VNode { export function ReviewPoliciesScreen(): VNode {
const reducer = useAnastasisContext() const reducer = useAnastasisContext()

View File

@ -51,7 +51,7 @@ export function AuthMethodEmailSetup({ cancel, addAuthMethod, configured }: Auth
</div></section>} </div></section>}
<div> <div>
<div style={{ marginTop: '2em', display: 'flex', justifyContent: 'space-between' }}> <div style={{ marginTop: '2em', display: 'flex', justifyContent: 'space-between' }}>
<button class="button" onClick={cancel}>Canceul</button> <button class="button" onClick={cancel}>Cancel</button>
<span data-tooltip={errors}> <span data-tooltip={errors}>
<button class="button is-info" disabled={errors !== undefined} onClick={addEmailAuth}>Add</button> <button class="button is-info" disabled={errors !== undefined} onClick={addEmailAuth}>Add</button>
</span> </span>

View File

@ -25,7 +25,7 @@ export function AuthMethodTotpSetup({ addAuthMethod, cancel, configured }: AuthM
const addTotpAuth = (): void => addAuthMethod({ const addTotpAuth = (): void => addAuthMethod({
authentication_method: { authentication_method: {
type: "totp", type: "totp",
instructions: `Enter ${digits} digits code for ${name}`, instructions: `Enter ${digits} digits code for "${name}"`,
challenge: encodeCrock(stringToBytes(totpURL)), challenge: encodeCrock(stringToBytes(totpURL)),
}, },
}); });

View File

@ -15,7 +15,7 @@ export function AuthMethodVideoSetup({cancel, addAuthMethod, configured}: AuthMe
addAuthMethod({ addAuthMethod({
authentication_method: { authentication_method: {
type: "video", type: "video",
instructions: image, instructions: 'Join a video call',
challenge: encodeCrock(stringToBytes(image)), challenge: encodeCrock(stringToBytes(image)),
}, },
}) })