[Date Prev][Date Next][Thread Prev][Thread Next][Date Index][Thread Index]
[taler-wallet-core] branch master updated: accept tos kyc/aml form
From: |
gnunet |
Subject: |
[taler-wallet-core] branch master updated: accept tos kyc/aml form |
Date: |
Fri, 29 Nov 2024 13:32:14 +0100 |
This is an automated email from the git hooks/post-receive script.
sebasjm pushed a commit to branch master
in repository wallet-core.
The following commit(s) were added to refs/heads/master by this push:
new d396aba3a accept tos kyc/aml form
d396aba3a is described below
commit d396aba3a5815258abbce13c256438a96b040ff6
Author: Sebastian <sebasjm@gmail.com>
AuthorDate: Thu Nov 28 15:44:33 2024 -0300
accept tos kyc/aml form
---
packages/aml-backoffice-ui/src/pages/Measures.tsx | 13 +-
packages/kyc-ui/src/Routing.tsx | 38 ++-
packages/kyc-ui/src/forms/accept-tos.ts | 57 +++++
packages/kyc-ui/src/forms/index.ts | 12 +-
packages/kyc-ui/src/pages/FillForm.tsx | 5 +-
packages/kyc-ui/src/pages/Frame.tsx | 5 +-
packages/kyc-ui/src/pages/Start.tsx | 16 +-
packages/kyc-ui/src/pages/TriggerKyc.tsx | 272 +++++++++++++++++++++
packages/taler-harness/src/index.ts | 1 +
.../integrationtests/test-kyc-skip-expiration.ts | 1 +
packages/taler-util/src/http-client/exchange.ts | 14 +-
.../taler-util/src/http-client/officer-account.ts | 26 +-
packages/web-util/src/forms/HtmlIframe.tsx | 43 ++++
packages/web-util/src/forms/forms.ts | 14 ++
packages/web-util/src/forms/index.ts | 1 +
packages/web-util/src/forms/ui-form.ts | 14 ++
16 files changed, 488 insertions(+), 44 deletions(-)
diff --git a/packages/aml-backoffice-ui/src/pages/Measures.tsx
b/packages/aml-backoffice-ui/src/pages/Measures.tsx
index 0330d09c9..1e6a633d5 100644
--- a/packages/aml-backoffice-ui/src/pages/Measures.tsx
+++ b/packages/aml-backoffice-ui/src/pages/Measures.tsx
@@ -14,23 +14,16 @@
GNU Taler; see the file COPYING. If not, see <http://www.gnu.org/licenses/>
*/
import {
- AbsoluteTime,
- AmlDecisionRequest,
- TalerError,
- TalerExchangeApi,
+ TalerError
} from "@gnu-taler/taler-util";
import {
- FormMetadata,
- InternationalizationAPI,
Loading,
useExchangeApiContext,
- useTranslationContext,
+ useTranslationContext
} from "@gnu-taler/web-util/browser";
import { Fragment, h } from "preact";
-import { useUiFormsContext } from "../context/ui-forms.js";
-import { preloadedForms } from "../forms/index.js";
-import { useServerMeasures } from "../hooks/account.js";
import { ErrorLoadingWithDebug } from "../components/ErrorLoadingWithDebug.js";
+import { useServerMeasures } from "../hooks/account.js";
export function Measures({}: {}) {
const { config } = useExchangeApiContext();
diff --git a/packages/kyc-ui/src/Routing.tsx b/packages/kyc-ui/src/Routing.tsx
index 846a16d2a..be95f4862 100644
--- a/packages/kyc-ui/src/Routing.tsx
+++ b/packages/kyc-ui/src/Routing.tsx
@@ -14,7 +14,7 @@
GNU Taler; see the file COPYING. If not, see <http://www.gnu.org/licenses/>
*/
- import {
+import {
Loading,
urlPattern,
useCurrentLocation,
@@ -28,6 +28,7 @@ import { CallengeCompleted } from
"./pages/CallengeCompleted.js";
import { Frame } from "./pages/Frame.js";
import { Start } from "./pages/Start.js";
import { useSessionState } from "./hooks/session.js";
+import { TriggerKyc } from "./pages/TriggerKyc.js";
export function Routing(): VNode {
// check session and defined if this is
@@ -39,9 +40,10 @@ export function Routing(): VNode {
);
}
-const publicPages = {
+export const publicPages = {
completed: urlPattern(/\/completed/, () => `#/completed`),
start: urlPattern(/\/start/, () => `#/start`),
+ triggerKyc: urlPattern(/\/test\/trigger-kyc/, () => `#/test/trigger-kyc`),
};
function safeGetParam(
@@ -61,12 +63,12 @@ export function safeToURL(s: string | undefined): URL |
undefined {
}
}
-const ACCESS_TOKEN_REGEX = new RegExp("[A-Z0-9]{52}")
+const ACCESS_TOKEN_REGEX = new RegExp("[A-Z0-9]{52}");
/**
* by how the exchange serves the SPA
* /kyc-spa/KXAFXEWM7E3EJSYD9GJ30FYK1C17AKZWV119ZJA3XGPBBMZFJ2C0#/start
- *
+ *
* by how dev.mjs serves the SPA
* /app/?token=KXAFXEWM7E3EJSYD9GJ30FYK1C17AKZWV119ZJA3XGPBBMZFJ2C0#/start
* @returns
@@ -75,11 +77,11 @@ function getAccessTokenFromURL(): AccessToken | undefined {
if (typeof window === "undefined") return undefined;
const paths = window.location.pathname.split("/");
if (paths.length < 3) return undefined;
- const path = paths[2]
+ const path = paths[2];
if (path && ACCESS_TOKEN_REGEX.test(path)) {
return path as AccessToken;
}
- const param = new URLSearchParams(window.location.search).get("token")
+ const param = new URLSearchParams(window.location.search).get("token");
if (param && ACCESS_TOKEN_REGEX.test(param)) {
return param as AccessToken;
}
@@ -93,7 +95,7 @@ function PublicRounting(): VNode {
useErrorBoundary((e) => {
console.log("error", e);
});
- const sessionToken = state?.accessToken
+ const sessionToken = state?.accessToken;
const urlToken = getAccessTokenFromURL();
useEffect(() => {
if (!urlToken) {
@@ -102,13 +104,9 @@ function PublicRounting(): VNode {
}
// loading a new session
if (urlToken !== sessionToken) {
- start(urlToken)
+ start(urlToken);
}
- },[sessionToken, urlToken])
-
- if (!sessionToken) {
- return <div>No access token</div>;
- }
+ }, [sessionToken, urlToken]);
switch (location.name) {
case undefined: {
@@ -116,6 +114,10 @@ function PublicRounting(): VNode {
return <Loading />;
}
case "start": {
+ if (!sessionToken) {
+ return <div>No access token</div>;
+ }
+
return (
<Start
token={sessionToken}
@@ -129,6 +131,16 @@ function PublicRounting(): VNode {
case "completed": {
return <CallengeCompleted />;
}
+ case "triggerKyc": {
+ return (
+ <TriggerKyc
+ onKycStarted={(token) => {
+ start(token)
+ navigateTo(publicPages.start.url({}));
+ }}
+ />
+ );
+ }
default:
assertUnreachable(location);
}
diff --git a/packages/kyc-ui/src/forms/accept-tos.ts
b/packages/kyc-ui/src/forms/accept-tos.ts
new file mode 100644
index 000000000..0545efd8c
--- /dev/null
+++ b/packages/kyc-ui/src/forms/accept-tos.ts
@@ -0,0 +1,57 @@
+/*
+ This file is part of GNU Taler
+ (C) 2022-2024 Taler Systems S.A.
+
+ GNU Taler is free software; you can redistribute it and/or modify it under the
+ terms of the GNU General Public License as published by the Free Software
+ Foundation; either version 3, or (at your option) any later version.
+
+ GNU Taler 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 General Public License for more details.
+
+ You should have received a copy of the GNU General Public License along with
+ GNU Taler; see the file COPYING. If not, see <http://www.gnu.org/licenses/>
+ */
+
+import type {
+ DoubleColumnForm,
+ InternationalizationAPI,
+ UIHandlerId,
+} from "@gnu-taler/web-util/browser";
+
+function filterUndefined<T>(ar: Array<T|undefined>): Array<T> {
+ return ar.filter((a):a is T => !!a)
+}
+
+export const acceptTos = (i18n: InternationalizationAPI, context?: any):
DoubleColumnForm => ({
+ type: "double-column" as const,
+ design: [
+ {
+ title: i18n.str`Accept Term of Service`,
+ fields: filterUndefined([
+ context?.tos_url ? {
+ type: "htmlIframe",
+ label: context?.provider_name ?? `Link`,
+ url: context.tos_url
+ } : undefined,
+ {
+ type: "choiceHorizontal",
+ id: "asd" as UIHandlerId,
+ required: true,
+ label: i18n.str`Do you accept terms of service`,
+ choices: [
+ {
+ label: i18n.str`Yes`,
+ value: "yes",
+ },
+ {
+ label: i18n.str`No`,
+ value: "no",
+ },
+ ],
+ },
+ ]),
+ },
+ ],
+});
diff --git a/packages/kyc-ui/src/forms/index.ts
b/packages/kyc-ui/src/forms/index.ts
index a8803ec9f..ca8b1d49d 100644
--- a/packages/kyc-ui/src/forms/index.ts
+++ b/packages/kyc-ui/src/forms/index.ts
@@ -18,12 +18,14 @@ import {
InternationalizationAPI,
} from "@gnu-taler/web-util/browser";
import { simplest } from "./simplest.js";
-import { personalInfo } from "./personal-info.js";
+import { acceptTos } from "./accept-tos.js";
import { nameAndDob } from "./nameAndBirthdate.js";
+import { personalInfo } from "./personal-info.js";
export const preloadedForms: (
i18n: InternationalizationAPI,
-) => Array<FormMetadata> = (i18n) => [
+ context: object | undefined,
+) => Array<FormMetadata> = (i18n, context) => [
{
label: i18n.str`Simple comment`,
id: "__simple_comment",
@@ -36,6 +38,12 @@ export const preloadedForms: (
version: 1,
config: personalInfo(i18n),
},
+ {
+ label: i18n.str`Accept Terms of Service`,
+ id: "accept-tos",
+ version: 1,
+ config: acceptTos(i18n, context),
+ },
{
label: i18n.str`Name and birthdate`,
id: "name_and_dob",
diff --git a/packages/kyc-ui/src/pages/FillForm.tsx
b/packages/kyc-ui/src/pages/FillForm.tsx
index 459186240..0b691dc11 100644
--- a/packages/kyc-ui/src/pages/FillForm.tsx
+++ b/packages/kyc-ui/src/pages/FillForm.tsx
@@ -87,7 +87,7 @@ export function FillForm({
} as FormMetadata)
: undefined;
- const theForm = searchForm(i18n, customForm ? [customForm] : [], formId);
+ const theForm = searchForm(i18n, customForm ? [customForm] : [], formId,
requirement.context);
if (!theForm) {
return <div>form with id {formId} not found</div>;
}
@@ -274,13 +274,14 @@ function searchForm(
i18n: InternationalizationAPI,
forms: FormMetadata[],
formId: string,
+ context: object | undefined,
): FormMetadata | undefined {
{
const found = forms.find((v) => v.id === formId);
if (found) return found;
}
{
- const pf = preloadedForms(i18n);
+ const pf = preloadedForms(i18n, context);
const found = pf.find((v) => v.id === formId);
if (found) return found;
}
diff --git a/packages/kyc-ui/src/pages/Frame.tsx
b/packages/kyc-ui/src/pages/Frame.tsx
index a45fd129b..7cfdc7ad0 100644
--- a/packages/kyc-ui/src/pages/Frame.tsx
+++ b/packages/kyc-ui/src/pages/Frame.tsx
@@ -31,6 +31,7 @@ import {
usePreferences,
} from "../context/preferences.js";
import { useSettingsContext } from "../context/settings.js";
+import { publicPages } from "../Routing.js";
const GIT_HASH = typeof __GIT_HASH__ !== "undefined" ? __GIT_HASH__ :
undefined;
const VERSION = typeof __VERSION__ !== "undefined" ? __VERSION__ : undefined;
@@ -66,7 +67,9 @@ export function Frame({ children }: { children:
ComponentChildren }): VNode {
title="KYC"
onLogout={undefined}
iconLinkURL="#"
- sites={[]}
+ sites={!preferences.showDebugInfo ? [] : [
+ ["Test kyc", publicPages.triggerKyc.url({})]
+ ]}
supportedLangs={["en"]}
>
<li>
diff --git a/packages/kyc-ui/src/pages/Start.tsx
b/packages/kyc-ui/src/pages/Start.tsx
index 3dbecc093..3c0bb78b3 100644
--- a/packages/kyc-ui/src/pages/Start.tsx
+++ b/packages/kyc-ui/src/pages/Start.tsx
@@ -30,7 +30,7 @@ import {
useTranslationContext,
} from "@gnu-taler/web-util/browser";
import { Fragment, VNode, h } from "preact";
-import { useState } from "preact/hooks";
+import { useEffect, useState } from "preact/hooks";
import { ErrorLoadingWithDebug } from "../components/ErrorLoadingWithDebug.js";
import { useKycInfo } from "../hooks/kyc.js";
import { FillForm } from "./FillForm.js";
@@ -40,7 +40,7 @@ type Props = {
token: AccessToken;
};
-function ShowReqList({
+export function ShowReqList({
token,
onFormSelected,
}: {
@@ -52,6 +52,18 @@ function ShowReqList({
// const { lib } = useExchangeApiContext();
// const { state, start } = useSessionState();
const result = useKycInfo(token);
+
+ const firstAccount =
+ result &&
+ !(result instanceof TalerError) &&
+ result.type === "ok" &&
+ result.body.requirements.length > 0
+ ? result.body.requirements[0]
+ : undefined;
+ useEffect(() => {
+ if (firstAccount) onFormSelected(firstAccount);
+ }, [firstAccount]);
+
if (!result) {
return <Loading />;
}
diff --git a/packages/kyc-ui/src/pages/TriggerKyc.tsx
b/packages/kyc-ui/src/pages/TriggerKyc.tsx
new file mode 100644
index 000000000..c7d2fa4cb
--- /dev/null
+++ b/packages/kyc-ui/src/pages/TriggerKyc.tsx
@@ -0,0 +1,272 @@
+/*
+ This file is part of GNU Taler
+ (C) 2022-2024 Taler Systems S.A.
+
+ GNU Taler is free software; you can redistribute it and/or modify it under the
+ terms of the GNU General Public License as published by the Free Software
+ Foundation; either version 3, or (at your option) any later version.
+
+ GNU Taler 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 General Public License for more details.
+
+ You should have received a copy of the GNU General Public License along with
+ GNU Taler; see the file COPYING. If not, see <http://www.gnu.org/licenses/>
+ */
+import {
+ Attention,
+ Button,
+ convertUiField,
+ FormMetadata,
+ getConverterById,
+ LocalNotificationBanner,
+ RenderAllFieldsByUiConfig,
+ UIHandlerId,
+ useExchangeApiContext,
+ useLocalNotificationHandler,
+ useTranslationContext,
+} from "@gnu-taler/web-util/browser";
+import { Fragment, VNode, h } from "preact";
+import {
+ FormErrors,
+ getRequiredFields,
+ getShapeFromFields,
+ useFormState,
+ validateRequiredFields,
+} from "../hooks/form.js";
+import { undefinedIfEmpty } from "./Start.js";
+import {
+ AbsoluteTime,
+ AccessToken,
+ AmountJson,
+ Amounts,
+ AmountString,
+ assertUnreachable,
+ createNewOfficerAccount,
+ createNewWalletKycAccount,
+ HttpStatusCode,
+} from "@gnu-taler/taler-util";
+import { useEffect, useMemo, useState } from "preact/hooks";
+
+type FormType = {
+ amount: AmountJson;
+};
+type Props = {
+ onKycStarted: (token: AccessToken) => void;
+};
+export function TriggerKyc({ onKycStarted }: Props): VNode {
+ const { i18n } = useTranslationContext();
+ const [notification, withErrorHandler, notify] =
+ useLocalNotificationHandler();
+ const { config, lib } = useExchangeApiContext();
+ const [kycAccount, setKycAccount] = useState<string>();
+
+ const theForm: FormMetadata = {
+ id: "asd",
+ version: 1,
+ label: i18n.str`Trigger KYC balance`,
+ config: {
+ type: "double-column",
+ design: [
+ {
+ title: i18n.str`Trigger KYC Balance`,
+ fields: [
+ {
+ id: ".amount" as UIHandlerId,
+ type: "amount",
+ currency: config.currency,
+ label: i18n.str`Amount`,
+ required: true,
+ converterId: "Taler.Amount",
+ },
+ ],
+ },
+ ],
+ },
+ };
+
+ const shape: Array<UIHandlerId> = [];
+ const requiredFields: Array<UIHandlerId> = [];
+
+ theForm.config.design.forEach((section) => {
+ Array.prototype.push.apply(shape, getShapeFromFields(section.fields));
+ Array.prototype.push.apply(
+ requiredFields,
+ getRequiredFields(section.fields),
+ );
+ });
+ const [form, state] = useFormState<FormType>(
+ shape,
+ {
+ amount: Amounts.parseOrThrow(`${config.currency}:1000000`),
+ },
+ (st) => {
+ const partialErrors = undefinedIfEmpty<FormErrors<FormType>>({});
+
+ const errors = undefinedIfEmpty<FormErrors<FormType> | undefined>(
+ validateRequiredFields(partialErrors, st, requiredFields),
+ );
+
+ if (errors === undefined) {
+ return {
+ status: "ok",
+ result: st as any,
+ errors: undefined,
+ };
+ }
+
+ return {
+ status: "fail",
+ result: st as any,
+ errors,
+ };
+ },
+ );
+
+ const accountPromise = useMemo(async () => {
+ const resp = await lib.exchange.getSeed();
+ const extraEntropy = resp.type === "ok" ? resp.body : new Uint8Array();
+ return createNewWalletKycAccount(extraEntropy);
+ }, [1]);
+
+ useEffect(() => {
+ if (!kycAccount) return;
+ const paytoHash = kycAccount
+ async function check() {
+ const {signingKey} = await accountPromise;
+ const result = await lib.exchange.checkKycStatus(signingKey, paytoHash);
+ if (result.type === "ok") {
+ if (result.body) {
+ onKycStarted(result.body.access_token)
+ } else {
+ console.log("empty body")
+ }
+ } else {
+ switch(result.case) {
+ case HttpStatusCode.Forbidden:{
+ notify({
+ type: "error",
+ title: i18n.str`could not create token`,
+ description: i18n.str`access denied`,
+ when: AbsoluteTime.now(),
+ })
+ }
+ case HttpStatusCode.NotFound: {
+ notify({
+ type: "error",
+ title: i18n.str`could not create token`,
+ description: i18n.str`not found`,
+ when: AbsoluteTime.now(),
+ })
+ }
+ case HttpStatusCode.Conflict: {
+ notify({
+ type: "error",
+ title: i18n.str`could not create token`,
+ description: i18n.str`conflict`,
+ when: AbsoluteTime.now(),
+ })
+
+ }
+ }
+ }
+ }
+ check()
+ }, [kycAccount]);
+
+ const submitHandler =
+ theForm === undefined || state.status === "fail"
+ ? undefined
+ : withErrorHandler(
+ async () => {
+ const account = await accountPromise;
+
+ return lib.exchange.notifyKycBalanceLimit(
+ account,
+ Amounts.stringify(state.result.amount),
+ );
+ },
+ (res) => {
+ notify({
+ type: "info",
+ title: i18n.str`No kyc required`,
+ when: AbsoluteTime.now(),
+ });
+ },
+ (fail) => {
+ switch (fail.case) {
+ case HttpStatusCode.Forbidden:
+ return i18n.str`Access denied trying to test balance.`;
+ case HttpStatusCode.UnavailableForLegalReasons:
+ setKycAccount(fail.body.h_payto);
+ return i18n.str`Unavailable For Legal Reasons`;
+ default:
+ assertUnreachable(fail);
+ }
+ },
+ );
+
+ if (kycAccount) {
+ return <div>loading...</div>
+ }
+
+ return (
+ <div class="rounded-lg bg-white px-5 py-6 shadow m-4">
+ <LocalNotificationBanner notification={notification} />
+ <div class="space-y-10 divide-y -mt-5 divide-gray-900/10">
+ {theForm.config.design.map((section, i) => {
+ if (!section) return <Fragment />;
+ return (
+ <div
+ key={i}
+ class="grid grid-cols-1 gap-x-8 gap-y-8 pt-5 md:grid-cols-3"
+ >
+ <div class="px-4 sm:px-0">
+ <h2 class="text-base font-semibold leading-7 text-gray-900">
+ {section.title}
+ </h2>
+ {section.description && (
+ <p class="mt-1 text-sm leading-6 text-gray-600">
+ {section.description}
+ </p>
+ )}
+ </div>
+ <div class="bg-white shadow-sm ring-1 ring-gray-900/5 rounded-md
md:col-span-2">
+ <div class="p-3">
+ <div class="grid max-w-2xl grid-cols-1 gap-x-6 gap-y-8
sm:grid-cols-6">
+ <RenderAllFieldsByUiConfig
+ key={i}
+ fields={convertUiField(
+ i18n,
+ section.fields,
+ form,
+ getConverterById,
+ )}
+ />
+ </div>
+ </div>
+ </div>
+ </div>
+ );
+ })}
+ </div>
+
+ <div class="mt-6 flex items-center justify-end gap-x-6">
+ <button
+ onClick={() => {}}
+ class="text-sm font-semibold leading-6 text-gray-900"
+ >
+ <i18n.Translate>Cancel</i18n.Translate>
+ </button>
+ <Button
+ type="submit"
+ handler={submitHandler}
+ // disabled={!submitHandler}
+ class="disabled:opacity-50 disabled:cursor-default rounded-md
bg-indigo-600 px-3 py-2 text-sm font-semibold text-white shadow-sm
hover:bg-indigo-500 focus-visible:outline focus-visible:outline-2
focus-visible:outline-offset-2 focus-visible:outline-indigo-600"
+ >
+ <i18n.Translate>Confirm</i18n.Translate>
+ </Button>
+ </div>
+ </div>
+ );
+}
diff --git a/packages/taler-harness/src/index.ts
b/packages/taler-harness/src/index.ts
index f209c1906..4de5b1527 100644
--- a/packages/taler-harness/src/index.ts
+++ b/packages/taler-harness/src/index.ts
@@ -1447,6 +1447,7 @@ const allAmlPrograms: TalerKycAml.AmlProgramDefinition[]
= [
return outcome;
},
requiredAttributes: [],
+ requiredInputs: [],
requiredContext: [],
},
AML_PROGRAM_FROM_ATTRIBUTES_TO_CONTEXT,
diff --git
a/packages/taler-harness/src/integrationtests/test-kyc-skip-expiration.ts
b/packages/taler-harness/src/integrationtests/test-kyc-skip-expiration.ts
index b3405bf02..0ced6a697 100644
--- a/packages/taler-harness/src/integrationtests/test-kyc-skip-expiration.ts
+++ b/packages/taler-harness/src/integrationtests/test-kyc-skip-expiration.ts
@@ -75,6 +75,7 @@ export const AML_PROGRAM_FROM_ATTRIBUTES_TO_CONTEXT:
TalerKycAml.AmlProgramDefin
return outcome;
},
requiredAttributes: [],
+ requiredInputs: [],
requiredContext: [],
};
diff --git a/packages/taler-util/src/http-client/exchange.ts
b/packages/taler-util/src/http-client/exchange.ts
index 7526fd8a0..319ed47d8 100644
--- a/packages/taler-util/src/http-client/exchange.ts
+++ b/packages/taler-util/src/http-client/exchange.ts
@@ -38,6 +38,7 @@ import {
import { Codec, codecForAny } from "../codec.js";
import {
TalerSignaturePurpose,
+ amountToBuffer,
bufferForUint64,
buildSigPS,
decodeCrock,
@@ -616,7 +617,7 @@ export class TalerExchangeHttpClient {
const body: WalletKycRequest = {
balance,
reserve_pub: account.id,
- reserve_sig: encodeCrock(account.signingKey),
+ reserve_sig: buildKYCWalletBalanceSignature(account.signingKey, balance),
};
const resp = await this.httpLib.fetch(url.href, {
@@ -1049,6 +1050,17 @@ export class TalerExchangeHttpClient {
}
}
+function buildKYCWalletBalanceSignature(
+ key: SigningKey,
+ balance: AmountString,
+): string {
+ const sigBlob = buildSigPS(TalerSignaturePurpose.WALLET_ACCOUNT_SETUP)
+ .put(amountToBuffer(balance))
+ .build();
+
+ return encodeCrock(eddsaSign(sigBlob, key));
+}
+
function buildKYCQuerySignature(key: SigningKey): string {
const sigBlob = buildSigPS(TalerSignaturePurpose.KYC_AUTH).build();
diff --git a/packages/taler-util/src/http-client/officer-account.ts
b/packages/taler-util/src/http-client/officer-account.ts
index f5f55ea1b..6a2663155 100644
--- a/packages/taler-util/src/http-client/officer-account.ts
+++ b/packages/taler-util/src/http-client/officer-account.ts
@@ -19,6 +19,8 @@ import {
LockedAccount,
OfficerAccount,
OfficerId,
+ ReserveAccount,
+ ReservePub,
SigningKey,
createEddsaKeyPair,
decodeCrock,
@@ -106,27 +108,25 @@ export async function createNewOfficerAccount(
*/
export async function createNewWalletKycAccount(
extraNonce: EncryptionNonce,
- password: string,
-): Promise<OfficerAccount & { safe: LockedAccount }> {
+ password?: string,
+): Promise<ReserveAccount & { safe?: LockedAccount }> {
const { eddsaPriv, eddsaPub } = createEddsaKeyPair();
- const key = stringToBytes(password);
+ const mergedRnd: EncryptionNonce = extraNonce && password
+ ? kdf(24, stringToBytes("aml-officer"), extraNonce, getRandomBytesF(24))
+ : getRandomBytesF(24);
- const localRnd = getRandomBytesF(24);
- const mergedRnd: EncryptionNonce = extraNonce
- ? kdf(24, stringToBytes("aml-officer"), extraNonce, localRnd)
- : localRnd;
-
- const protectedPrivKey = await encryptWithDerivedKey(
+
+ const protectedPrivKey = password ? await encryptWithDerivedKey(
mergedRnd,
- key,
+ stringToBytes(password),
eddsaPriv,
password,
- );
+ ) : undefined;
const signingKey = eddsaPriv as SigningKey;
- const accountId = encodeCrock(eddsaPub) as OfficerId;
- const safe = encodeCrock(protectedPrivKey) as LockedAccount;
+ const accountId = encodeCrock(eddsaPub) as ReservePub;
+ const safe = protectedPrivKey ? encodeCrock(protectedPrivKey) as
LockedAccount : undefined;
return { id: accountId, signingKey, safe };
}
diff --git a/packages/web-util/src/forms/HtmlIframe.tsx
b/packages/web-util/src/forms/HtmlIframe.tsx
new file mode 100644
index 000000000..4569011a6
--- /dev/null
+++ b/packages/web-util/src/forms/HtmlIframe.tsx
@@ -0,0 +1,43 @@
+import { TranslatedString } from "@gnu-taler/taler-util";
+import { VNode, h } from "preact";
+import { LabelWithTooltipMaybeRequired, RenderAddon } from "./InputLine.js";
+import { Addon } from "./FormProvider.js";
+
+interface Props {
+ label: TranslatedString;
+ url: string;
+ tooltip?: TranslatedString;
+ help?: TranslatedString;
+ before?: Addon;
+ after?: Addon;
+}
+
+export function HtmlIframe({
+ before,
+ url,
+ after,
+ label,
+ tooltip,
+ help,
+}: Props): VNode {
+ return (
+ <div class="sm:col-span-6">
+ {before !== undefined && <RenderAddon addon={before} />}
+ <a
+ href={url}
+ target="_blank"
+ rel="noreferrer"
+ class="underline cursor-pointer"
+ >
+ <p>{label}</p>
+ </a>
+ <iframe src={url} title={label} class="w-full"></iframe>
+ {after !== undefined && <RenderAddon addon={after} />}
+ {help && (
+ <p class="mt-2 text-sm text-gray-500" id="email-description">
+ {help}
+ </p>
+ )}
+ </div>
+ );
+}
diff --git a/packages/web-util/src/forms/forms.ts
b/packages/web-util/src/forms/forms.ts
index 2c789b9a3..70c090fb1 100644
--- a/packages/web-util/src/forms/forms.ts
+++ b/packages/web-util/src/forms/forms.ts
@@ -20,12 +20,14 @@ import {
} from "../index.browser.js";
import { assertUnreachable, TranslatedString } from "@gnu-taler/taler-util";
import { UIFormFieldBaseConfig, UIFormElementConfig } from "./ui-form.js";
+import { HtmlIframe } from "./HtmlIframe.js";
/**
* Constrain the type with the ui props
*/
type FieldType<T extends object = any, K extends keyof T = any> = {
group: Parameters<typeof Group>[0];
caption: Parameters<typeof Caption>[0];
+ htmlIframe: Parameters<typeof HtmlIframe>[0];
array: Parameters<typeof InputArray<T, K>>[0];
file: Parameters<typeof InputFile<T, K>>[0];
selectOne: Parameters<typeof InputSelectOne<T, K>>[0];
@@ -46,6 +48,7 @@ type FieldType<T extends object = any, K extends keyof T =
any> = {
export type UIFormField =
| { type: "group"; properties: FieldType["group"] }
| { type: "caption"; properties: FieldType["caption"] }
+ | { type: "htmlIframe"; properties: FieldType["htmlIframe"] }
| { type: "array"; properties: FieldType["array"] }
| { type: "file"; properties: FieldType["file"] }
| { type: "amount"; properties: FieldType["amount"] }
@@ -85,6 +88,7 @@ type UIFormFieldMap = {
const UIFormConfiguration: UIFormFieldMap = {
group: Group,
caption: Caption,
+ htmlIframe: HtmlIframe,
//@ts-ignore
array: InputArray,
text: InputText,
@@ -173,6 +177,16 @@ export function convertUiField(
};
return resp;
}
+ case "htmlIframe": {
+ const resp: UIFormField = {
+ type: config.type,
+ properties: {
+ ...converBaseFieldsProps(i18n_, config),
+ url: config.url
+ },
+ };
+ return resp;
+ }
case "group": {
const resp: UIFormField = {
type: config.type,
diff --git a/packages/web-util/src/forms/index.ts
b/packages/web-util/src/forms/index.ts
index 7320c70d0..5e3f4e1d3 100644
--- a/packages/web-util/src/forms/index.ts
+++ b/packages/web-util/src/forms/index.ts
@@ -1,5 +1,6 @@
export * from "./Calendar.js"
export * from "./Caption.js"
+export * from "./HtmlIframe.js"
export * from "./DefaultForm.js"
export * from "./Dialog.js"
export * from "./FormProvider.js"
diff --git a/packages/web-util/src/forms/ui-form.ts
b/packages/web-util/src/forms/ui-form.ts
index f26e08f3b..22ef0bdfa 100644
--- a/packages/web-util/src/forms/ui-form.ts
+++ b/packages/web-util/src/forms/ui-form.ts
@@ -3,11 +3,13 @@ import {
buildCodecForUnion,
Codec,
codecForBoolean,
+ codecForCanonBaseUrl,
codecForConstString,
codecForLazy,
codecForList,
codecForNumber,
codecForString,
+ codecForStringURL,
codecForTimestamp,
codecOptional,
Integer,
@@ -38,6 +40,7 @@ export type DoubleColumnFormSection = {
export type UIFormElementConfig =
| UIFormElementGroup
| UIFormElementCaption
+ | UIFormElementHtmlIframe
| UIFormFieldAbsoluteTime
| UIFormFieldAmount
| UIFormFieldArray
@@ -73,6 +76,10 @@ type UIFormFieldArray = {
} & UIFormFieldBaseConfig;
type UIFormElementCaption = { type: "caption" } & UIFieldElementDescription;
+type UIFormElementHtmlIframe = {
+ type: "htmlIframe";
+ url: string;
+} & UIFieldElementDescription;
type UIFormElementGroup = {
type: "group";
@@ -227,6 +234,12 @@ const codecForUiFormFieldCaption = ():
Codec<UIFormElementCaption> =>
.property("type", codecForConstString("caption"))
.build("UIFormFieldCaption");
+const codecForUiFormFieldHtmlIFrame = (): Codec<UIFormElementHtmlIframe> =>
+ codecForUIFormFieldBaseDescriptionTemplate<UIFormElementHtmlIframe>()
+ .property("type", codecForConstString("htmlIframe"))
+ .property("url", codecForStringURL())
+ .build("codecForUiFormFieldHtmlIFrame");
+
const codecForUiFormSelectUiChoice = (): Codec<SelectUiChoice> =>
buildCodecForObject<SelectUiChoice>()
.property("description", codecOptional(codecForString()))
@@ -309,6 +322,7 @@ const codecForUiFormField = (): Codec<UIFormElementConfig>
=>
.alternative("absoluteTimeText", codecForUiFormFieldAbsoluteTime())
.alternative("amount", codecForUiFormFieldAmount())
.alternative("caption", codecForUiFormFieldCaption())
+ .alternative("htmlIframe", codecForUiFormFieldHtmlIFrame())
.alternative("choiceHorizontal", codecForUiFormFieldChoiceHorizontal())
.alternative("choiceStacked", codecForUiFormFieldChoiceStacked())
.alternative("file", codecForUiFormFieldFile())
--
To stop receiving notification emails like this one, please contact
gnunet@gnunet.org.
[Prev in Thread] |
Current Thread |
[Next in Thread] |
- [taler-wallet-core] branch master updated: accept tos kyc/aml form,
gnunet <=