aboutsummaryrefslogtreecommitdiff
path: root/packages/exchange-backoffice-ui/src/pages/Officer.tsx
diff options
context:
space:
mode:
Diffstat (limited to 'packages/exchange-backoffice-ui/src/pages/Officer.tsx')
-rw-r--r--packages/exchange-backoffice-ui/src/pages/Officer.tsx204
1 files changed, 104 insertions, 100 deletions
diff --git a/packages/exchange-backoffice-ui/src/pages/Officer.tsx b/packages/exchange-backoffice-ui/src/pages/Officer.tsx
index 4d8b90228..79dd8bace 100644
--- a/packages/exchange-backoffice-ui/src/pages/Officer.tsx
+++ b/packages/exchange-backoffice-ui/src/pages/Officer.tsx
@@ -4,69 +4,60 @@ import {
notifyInfo,
useLocalStorage,
useMemoryStorage,
+ useTranslationContext,
} from "@gnu-taler/web-util/browser";
import { VNode, h } from "preact";
import { useEffect, useState } from "preact/hooks";
import {
+ Account,
UnwrapKeyError,
createNewAccount,
- createNewSessionId,
unlockAccount,
} from "../account.js";
import { createNewForm } from "../handlers/forms.js";
+import { Officer, codecForOfficer } from "../Dashboard.js";
export function Officer() {
const password = useMemoryStorage("password");
- const session = useLocalStorage("session");
- const officer = useLocalStorage("officer");
- const [keys, setKeys] = useState({ accountId: "", pub: "" });
+ const officer = useLocalStorage("officer", {
+ codec: codecForOfficer(),
+ });
+ const [keys, setKeys] = useState<Account>();
useEffect(() => {
- if (
- officer.value === undefined ||
- session.value === undefined ||
- password.value === undefined
- ) {
+ if (officer.value === undefined || password.value === undefined) {
return;
}
- unlockAccount(session.value, officer.value, password.value)
+
+ unlockAccount(officer.value.salt, officer.value.key, password.value)
.then((keys) => setKeys(keys ?? { accountId: "", pub: "" }))
.catch((e) => {
if (e instanceof UnwrapKeyError) {
console.log(e);
}
});
- }, [officer.value, session.value, password.value]);
-
- useEffect(() => {
- if (!session.value) {
- session.update(createNewSessionId());
- }
- }, []);
-
- const { value: sessionId } = session;
- if (!sessionId) {
- return <div>loading...</div>;
- }
+ }, [officer.value, password.value]);
- if (officer.value === undefined) {
+ if (
+ officer.value === undefined ||
+ !officer.value.key ||
+ !officer.value.salt
+ ) {
return (
<CreateAccount
- sessionId={sessionId}
- onNewAccount={(id) => {
- password.reset();
- officer.update(id);
+ onNewAccount={(salt, key, pwd) => {
+ password.update(pwd);
+ officer.update({ salt, when: { t_ms: Date.now() }, key });
}}
/>
);
}
- console.log("pwd", password.value);
if (password.value === undefined) {
return (
<UnlockAccount
- sessionId={sessionId}
- accountId={officer.value}
+ salt={officer.value.salt}
+ sealedKey={officer.value.key}
onAccountUnlocked={(pwd) => {
password.update(pwd);
}}
@@ -76,42 +67,59 @@ export function Officer() {
return (
<div>
- <div>Officer</div>
- <h1>{sessionId}</h1>
<h1 class="my-2 text-3xl font-bold tracking-tight text-gray-900 ">
Public key
</h1>
- <div>
- <p class="mt-6 leading-8 text-gray-700 break-all">
- -----BEGIN PUBLIC KEY-----
- <div>{keys.pub}</div>
- -----END PUBLIC KEY-----
- </p>
- </div>
- <h1 class="my-2 text-3xl font-bold tracking-tight text-gray-900 ">
- Private key
- </h1>
- <div>
- <p class="mt-6 leading-8 text-gray-700 break-all">
- -----BEGIN PRIVATE KEY-----
- <div>{keys.accountId}</div>
- -----END PRIVATE KEY-----
- </p>
+ <div class="max-w-xl text-base leading-7 text-gray-700 lg:max-w-lg">
+ <p class="mt-6 font-mono break-all">{keys?.accountId}</p>
</div>
+ <p>
+ <a
+ href={`mailto:aml@exchange.taler.net?body=${encodeURIComponent(
+ `I want my AML account\n\n\nPubKey: ${keys?.accountId}`,
+ )}`}
+ target="_blank"
+ rel="noreferrer"
+ class="m-4 block rounded-md w-fit border-0 px-3 py-2 text-center text-sm bg-indigo-700 text-white shadow-sm hover:bg-indigo-700"
+ >
+ Request account activation
+ </a>
+ </p>
+ <p>
+ <button
+ type="button"
+ onClick={() => {
+ password.reset();
+ }}
+ class="m-4 block rounded-md border-0 bg-gray-200 px-3 py-2 text-center text-sm text-black shadow-sm "
+ >
+ Lock account
+ </button>
+ </p>
+ <p>
+ <button
+ type="button"
+ onClick={() => {
+ officer.reset();
+ }}
+ class="m-4 block rounded-md bg-red-600 px-3 py-2 text-center text-sm text-white shadow-sm hover:bg-red-500 "
+ >
+ Remove account
+ </button>
+ </p>
</div>
);
}
function CreateAccount({
- sessionId,
onNewAccount,
}: {
- sessionId: string;
- onNewAccount: (accountId: string) => void;
+ onNewAccount: (salt: string, accountId: string, password: string) => void;
}): VNode {
+ const { i18n } = useTranslationContext();
const Form = createNewForm<{
- email: string;
password: string;
+ repeat: string;
}>();
return (
@@ -125,24 +133,50 @@ function CreateAccount({
<div class="mt-10 sm:mx-auto sm:w-full sm:max-w-[480px] ">
<div class="bg-gray-100 px-6 py-6 shadow sm:rounded-lg sm:px-12">
<Form.Provider
+ computeFormState={(v) => {
+ return {
+ password: {
+ error: !v.password
+ ? i18n.str`required`
+ : v.password.length < 8
+ ? i18n.str`should have at least 8 characters`
+ : !v.password.match(/[a-z]/) && v.password.match(/[A-Z]/)
+ ? i18n.str`should have lowercase and uppercase characters`
+ : !v.password.match(/\d/)
+ ? i18n.str`should have numbers`
+ : !v.password.match(/[^a-zA-Z\d]/)
+ ? i18n.str`should have at least one character which is not a number or letter`
+ : undefined,
+ },
+ repeat: {
+ // error: !v.repeat
+ // ? i18n.str`required`
+ // // : v.repeat !== v.password
+ // // ? i18n.str`doesn't match`
+ // : undefined,
+ },
+ };
+ }}
onSubmit={async (v) => {
- const keys = await createNewAccount(sessionId, v.password);
- onNewAccount(keys.accountId);
+ const keys = await createNewAccount(v.password);
+ onNewAccount(keys.salt, keys.accountId, v.password);
}}
>
<div class="mb-4">
<Form.InputLine
- label={"Email" as TranslatedString}
- name="email"
- type="email"
+ label={"Password" as TranslatedString}
+ name="password"
+ type="password"
+ help={
+ "lower and upper case letters, number and special character" as TranslatedString
+ }
required
/>
</div>
-
<div class="mb-4">
<Form.InputLine
- label={"Password" as TranslatedString}
- name="password"
+ label={"Repeat password" as TranslatedString}
+ name="repeat"
type="password"
required
/>
@@ -164,17 +198,15 @@ function CreateAccount({
}
function UnlockAccount({
- sessionId,
- accountId,
+ salt,
+ sealedKey,
onAccountUnlocked,
}: {
- sessionId: string;
- accountId: string;
+ salt: string;
+ sealedKey: string;
onAccountUnlocked: (password: string) => void;
}): VNode {
const Form = createNewForm<{
- sessionId: string;
- accountId: string;
password: string;
}>();
@@ -182,34 +214,21 @@ function UnlockAccount({
<div class="flex min-h-full flex-col ">
<div class="sm:mx-auto sm:w-full sm:max-w-md">
<h2 class="mt-6 text-center text-2xl font-bold leading-9 tracking-tight text-gray-900">
- Unlock account
+ Account locked
</h2>
+ <p class="mt-6 text-lg leading-8 text-gray-600">
+ Your account is normally locked anytime you reload. To unlock type
+ your password again.
+ </p>
</div>
<div class="mt-10 sm:mx-auto sm:w-full sm:max-w-[480px] ">
<div class="bg-gray-100 px-6 py-6 shadow sm:rounded-lg sm:px-12">
<Form.Provider
- initialValue={{
- sessionId,
- accountId:
- accountId.substring(0, 6) +
- "..." +
- accountId.substring(accountId.length - 6),
- }}
- computeFormState={(v) => {
- return {
- accountId: {
- disabled: true,
- },
- sessionId: {
- disabled: true,
- },
- };
- }}
onSubmit={async (v) => {
try {
// test login
- await unlockAccount(sessionId, accountId, v.password);
+ await unlockAccount(salt, sealedKey, v.password);
onAccountUnlocked(v.password ?? "");
notifyInfo("Account unlocked" as TranslatedString);
@@ -227,21 +246,6 @@ function UnlockAccount({
>
<div class="mb-4">
<Form.InputLine
- label={"Session" as TranslatedString}
- name="sessionId"
- type="text"
- />
- </div>
- <div class="mb-4">
- <Form.InputLine
- label={"AccountId" as TranslatedString}
- name="accountId"
- type="text"
- />
- </div>
-
- <div class="mb-4">
- <Form.InputLine
label={"Password" as TranslatedString}
name="password"
type="password"