summaryrefslogtreecommitdiff
path: root/packages/anastasis-webui/src/pages/home/SecretSelectionScreen.tsx
diff options
context:
space:
mode:
Diffstat (limited to 'packages/anastasis-webui/src/pages/home/SecretSelectionScreen.tsx')
-rw-r--r--packages/anastasis-webui/src/pages/home/SecretSelectionScreen.tsx497
1 files changed, 431 insertions, 66 deletions
diff --git a/packages/anastasis-webui/src/pages/home/SecretSelectionScreen.tsx b/packages/anastasis-webui/src/pages/home/SecretSelectionScreen.tsx
index 903f57868..ce44b0884 100644
--- a/packages/anastasis-webui/src/pages/home/SecretSelectionScreen.tsx
+++ b/packages/anastasis-webui/src/pages/home/SecretSelectionScreen.tsx
@@ -1,87 +1,452 @@
-/* eslint-disable @typescript-eslint/camelcase */
-import { h, VNode } from "preact";
-import { useState } from "preact/hooks";
-import { useAnastasisContext } from "../../context/anastasis";
-import { AnastasisClientFrame } from "./index";
+/*
+ This file is part of GNU Anastasis
+ (C) 2021-2022 Anastasis SARL
-export function SecretSelectionScreen(): VNode {
- const [selectingVersion, setSelectingVersion] = useState<boolean>(false);
- const [otherProvider, setOtherProvider] = useState<string>("");
- const reducer = useAnastasisContext()
+ GNU Anastasis is free software; you can redistribute it and/or modify it under the
+ terms of the GNU Affero General Public License as published by the Free Software
+ Foundation; either version 3, or (at your option) any later version.
- const currentVersion = reducer?.currentReducerState
- && ("recovery_document" in reducer.currentReducerState)
- && reducer.currentReducerState.recovery_document?.version;
+ GNU Anastasis 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 Affero General Public License for more details.
- const [otherVersion, setOtherVersion] = useState<number>(currentVersion || 0);
+ You should have received a copy of the GNU Affero General Public License along with
+ GNU Anastasis; see the file COPYING. If not, see <http://www.gnu.org/licenses/>
+ */
+import {
+ AggregatedPolicyMetaInfo,
+ AuthenticationProviderStatus,
+ AuthenticationProviderStatusOk,
+} from "@gnu-taler/anastasis-core";
+import { h, VNode } from "preact";
+import { useEffect, useState } from "preact/hooks";
+import { AsyncButton } from "../../components/AsyncButton.js";
+import { PhoneNumberInput } from "../../components/fields/NumberInput.js";
+import { useAnastasisContext } from "../../context/anastasis.js";
+import AddingProviderScreen from "./AddingProviderScreen/index.js";
+import { AnastasisClientFrame } from "./index.js";
+export function SecretSelectionScreenFound({
+ policies,
+ onManageProvider,
+ onNext,
+}: {
+ policies: AggregatedPolicyMetaInfo[];
+ onManageProvider: () => void;
+ onNext: (version: AggregatedPolicyMetaInfo) => void;
+}): VNode {
+ 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.reducer_type !== "recovery"
+ ) {
+ return <div>invalid state</div>;
}
+ return (
+ <AnastasisClientFrame
+ title="Recovery: Select secret"
+ hideNext="Please select version to recover"
+ >
+ <div class="columns">
+ <div class="column">
+ <p class="block">Found versions:</p>
+ {policies.map((version, i) => (
+ <div key={i} class="box">
+ <div
+ class="block"
+ style={{ display: "flex", justifyContent: "space-between" }}
+ >
+ <div
+ style={{
+ display: "flex",
+ flexDirection: "column",
+ }}
+ >
+ <div style={{ display: "flex", alignItems: "center" }}>
+ <b>Name:</b>&nbsp;<span>{version.secret_name}</span>
+ </div>
+ <div style={{ display: "flex", alignItems: "center" }}>
+ <b>Id:</b>&nbsp;
+ <span
+ class="icon has-tooltip-top"
+ data-tooltip={version.policy_hash}
+ >
+ <i class="mdi mdi-information" />
+ </span>
+ <span>{version.policy_hash.substring(0, 22)}...</span>
+ </div>
+ </div>
+
+ <div>
+ <AsyncButton
+ class="button"
+ onClick={async () => onNext(version)}
+ >
+ Recover
+ </AsyncButton>
+ </div>
+ </div>
+ </div>
+ ))}
+ </div>
+ <div class="column">
+ <p>
+ Secret found, you can select another version or continue to the
+ challenges solving
+ </p>
+ <p class="block">
+ <a onClick={onManageProvider}>Manage recovery providers</a>
+ </p>
+ </div>
+ </div>
+ </AnastasisClientFrame>
+ );
+}
+
+export function SecretSelectionScreen(): VNode {
+ const reducer = useAnastasisContext();
+ const [manageProvider, setManageProvider] = useState(false);
+
+ useEffect(() => {
+ async function f() {
+ if (reducer) {
+ await reducer.discoverStart();
+ }
+ }
+ f().catch((e) => console.log(e));
+ }, []);
- function selectVersion(p: string, n: number): void {
- if (!reducer) return;
- reducer.runTransaction(async (tx) => {
- await tx.transition("change_version", {
- version: n,
- provider_url: p,
- });
- setSelectingVersion(false);
- });
+ if (!reducer) {
+ return <div>no reducer in context</div>;
}
- const recoveryDocument = reducer.currentReducerState.recovery_document
- if (!recoveryDocument) {
+ if (
+ !reducer.currentReducerState ||
+ reducer.currentReducerState.reducer_type !== "recovery"
+ ) {
+ return <div>invalid state</div>;
+ }
+
+ if (manageProvider) {
return (
- <AnastasisClientFrame hideNav title="Recovery: Problem">
- <p>No recovery document found</p>
- </AnastasisClientFrame>
- )
+ <AddingProviderScreen onCancel={async () => setManageProvider(false)} />
+ );
+ }
+
+ if (
+ reducer.discoveryState.state === "none" ||
+ reducer.discoveryState.state === "active"
+ ) {
+ // Can this even happen?
+ return <SecretSelectionScreenWaiting />;
}
- if (selectingVersion) {
+
+ const policies = reducer.discoveryState.aggregatedPolicies ?? [];
+
+ if (policies.length === 0) {
return (
- <AnastasisClientFrame hideNav title="Recovery: Select secret">
- <p>Select a different version of the secret</p>
- <select onChange={(e) => setOtherProvider((e.target as any).value)}>
- {Object.keys(reducer.currentReducerState.authentication_providers ?? {}).map(
- (x, i) => (
- <option key={i} selected={x === recoveryDocument.provider_url} value={x}>
- {x}
+ <AddingProviderScreen
+ onCancel={async () => setManageProvider(false)}
+ notifications={[
+ {
+ message: "Secret not found",
+ type: "ERROR",
+ description:
+ "With the information you provided we could not found secret in any of the providers. You can try adding more providers if you think the data is correct.",
+ },
+ ]}
+ />
+ );
+ }
+
+ return (
+ <SecretSelectionScreenFound
+ policies={policies}
+ onNext={(version) => reducer.transition("select_version", version)}
+ onManageProvider={async () => setManageProvider(false)}
+ />
+ );
+}
+
+// export function OldSecretSelectionScreen(): VNode {
+// const [selectingVersion, setSelectingVersion] = useState<boolean>(false);
+// const reducer = useAnastasisContext();
+// const [manageProvider, setManageProvider] = useState(false);
+
+// useEffect(() => {
+// async function f() {
+// if (reducer) {
+// await reducer.discoverStart();
+// }
+// }
+// f().catch((e) => console.log(e));
+// }, []);
+
+// const currentVersion =
+// (reducer?.currentReducerState &&
+// "recovery_document" in reducer.currentReducerState &&
+// reducer.currentReducerState.recovery_document?.version) ||
+// 0;
+
+// if (!reducer) {
+// return <div>no reducer in context</div>;
+// }
+// if (
+// !reducer.currentReducerState ||
+// reducer.currentReducerState.reducer_type !== "recovery"
+// ) {
+// return <div>invalid state</div>;
+// }
+
+// async function doSelectVersion(p: string, n: number): Promise<void> {
+// if (!reducer) return Promise.resolve();
+// return reducer.runTransaction(async (tx) => {
+// await tx.transition("select_version", {
+// version: n,
+// provider_url: p,
+// });
+// setSelectingVersion(false);
+// });
+// }
+
+// const provs = reducer.currentReducerState.authentication_providers ?? {};
+// const recoveryDocument = reducer.currentReducerState.recovery_document;
+
+// if (!recoveryDocument) {
+// return (
+// <ChooseAnotherProviderScreen
+// providers={provs}
+// selected=""
+// onChange={(newProv) => doSelectVersion(newProv, 0)}
+// />
+// );
+// }
+
+// if (selectingVersion) {
+// return (
+// <SelectOtherVersionProviderScreen
+// providers={provs}
+// provider={recoveryDocument.provider_url}
+// version={recoveryDocument.version}
+// onCancel={() => setSelectingVersion(false)}
+// onConfirm={doSelectVersion}
+// />
+// );
+// }
+
+// if (manageProvider) {
+// return (
+// <AddingProviderScreen onCancel={async () => setManageProvider(false)} />
+// );
+// }
+
+// const providerInfo = provs[
+// recoveryDocument.provider_url
+// ] as AuthenticationProviderStatusOk;
+
+// return (
+// <AnastasisClientFrame title="Recovery: Select secret">
+// <div class="columns">
+// <div class="column">
+// <div class="box" style={{ border: "2px solid green" }}>
+// <h1 class="subtitle">{providerInfo.business_name}</h1>
+// <div class="block">
+// {currentVersion === 0 ? (
+// <p>Set to recover the latest version</p>
+// ) : (
+// <p>Set to recover the version number {currentVersion}</p>
+// )}
+// </div>
+// <div class="buttons is-right">
+// <button class="button" onClick={(e) => setSelectingVersion(true)}>
+// Change secret&apos;s version
+// </button>
+// </div>
+// </div>
+// </div>
+// <div class="column">
+// <p>
+// Secret found, you can select another version or continue to the
+// challenges solving
+// </p>
+// <p class="block">
+// <a onClick={() => setManageProvider(true)}>
+// Manage recovery providers
+// </a>
+// </p>
+// </div>
+// </div>
+// </AnastasisClientFrame>
+// );
+// }
+
+function ChooseAnotherProviderScreen({
+ onChange,
+}: {
+ onChange: (prov: string) => void;
+}): VNode {
+ const reducer = useAnastasisContext();
+
+ if (!reducer) {
+ return <div>no reducer in context</div>;
+ }
+
+ if (
+ !reducer.currentReducerState ||
+ reducer.currentReducerState.reducer_type !== "recovery"
+ ) {
+ return <div>invalid state</div>;
+ }
+ const providers = reducer.currentReducerState.authentication_providers ?? {};
+
+ return (
+ <AnastasisClientFrame
+ hideNext="Recovery document not found"
+ title="Recovery: Problem"
+ >
+ <p>No recovery document found, try with another provider</p>
+ <div class="field">
+ <label class="label">Provider</label>
+ <div class="control is-expanded has-icons-left">
+ <div class="select is-fullwidth">
+ <select onChange={(e) => onChange(e.currentTarget.value)} value="">
+ <option key="none" disabled selected value="">
+ Choose a provider
</option>
- )
- )}
- </select>
- <div>
- <input
- value={otherVersion}
- onChange={(e) => setOtherVersion(Number((e.target as HTMLInputElement).value))}
- type="number" />
- <button onClick={() => selectVersion(otherProvider, otherVersion)}>
- Use this version
- </button>
+ {Object.keys(providers).map((url) => {
+ const p = providers[url];
+ if (!("methods" in p)) return null;
+ return (
+ <option key={url} value={url}>
+ {p.business_name}
+ </option>
+ );
+ })}
+ </select>
+ <div class="icon is-small is-left">
+ <i class="mdi mdi-earth" />
+ </div>
+ </div>
</div>
- <div>
- <button onClick={() => selectVersion(otherProvider, 0)}>
- Use latest version
- </button>
- </div>
- <div>
- <button onClick={() => setSelectingVersion(false)}>Cancel</button>
+ </div>
+ </AnastasisClientFrame>
+ );
+}
+
+function SelectOtherVersionProviderScreen({
+ providers,
+ provider,
+ version,
+ onConfirm,
+ onCancel,
+}: {
+ onCancel: () => void;
+ provider: string;
+ version: number;
+ providers: { [url: string]: AuthenticationProviderStatus };
+ onConfirm: (prov: string, v: number) => Promise<void>;
+}): VNode {
+ const [otherProvider, setOtherProvider] = useState<string>(provider);
+ const [otherVersion, setOtherVersion] = useState(
+ version > 0 ? String(version) : "",
+ );
+ const otherProviderInfo = providers[
+ otherProvider
+ ] as AuthenticationProviderStatusOk;
+
+ return (
+ <AnastasisClientFrame hideNav title="Recovery: Select secret">
+ <div class="columns">
+ <div class="column">
+ <div class="box">
+ <h1 class="subtitle">Provider {otherProviderInfo.business_name}</h1>
+ <div class="block">
+ {version === 0 ? (
+ <p>Set to recover the latest version</p>
+ ) : (
+ <p>Set to recover the version number {version}</p>
+ )}
+ <p>Specify other version below or use the latest</p>
+ </div>
+
+ <div class="field">
+ <label class="label">Provider</label>
+ <div class="control is-expanded has-icons-left">
+ <div class="select is-fullwidth">
+ <select
+ onChange={(e) => setOtherProvider(e.currentTarget.value)}
+ value={otherProvider}
+ >
+ <option key="none" disabled selected value="">
+ {" "}
+ Choose a provider{" "}
+ </option>
+ {Object.keys(providers).map((url) => {
+ const p = providers[url];
+ if (!("methods" in p)) return null;
+ return (
+ <option key={url} value={url}>
+ {p.business_name}
+ </option>
+ );
+ })}
+ </select>
+ <div class="icon is-small is-left">
+ <i class="mdi mdi-earth" />
+ </div>
+ </div>
+ </div>
+ </div>
+ <div class="container">
+ <PhoneNumberInput
+ label="Version"
+ placeholder="version number to recover"
+ grabFocus
+ bind={[otherVersion, setOtherVersion]}
+ />
+ </div>
+ </div>
+ <div
+ style={{
+ marginTop: "2em",
+ display: "flex",
+ justifyContent: "space-between",
+ }}
+ >
+ <button class="button" onClick={onCancel}>
+ Cancel
+ </button>
+ <div class="buttons">
+ <AsyncButton
+ class="button"
+ onClick={() => onConfirm(otherProvider, 0)}
+ >
+ Use latest
+ </AsyncButton>
+ <AsyncButton
+ class="button is-info"
+ onClick={() =>
+ onConfirm(otherProvider, parseInt(otherVersion, 10))
+ }
+ >
+ Confirm
+ </AsyncButton>
+ </div>
+ </div>
</div>
- </AnastasisClientFrame>
- );
- }
+ </div>
+ </AnastasisClientFrame>
+ );
+}
+
+function SecretSelectionScreenWaiting(): VNode {
return (
<AnastasisClientFrame title="Recovery: Select secret">
- <p>Provider: {recoveryDocument.provider_url}</p>
- <p>Secret version: {recoveryDocument.version}</p>
- <p>Secret name: {recoveryDocument.secret_name}</p>
- <button onClick={() => setSelectingVersion(true)}>
- Select different secret
- </button>
+ <div>loading secret versions</div>
</AnastasisClientFrame>
);
}