aboutsummaryrefslogtreecommitdiff
path: root/packages/taler-harness/src/integrationtests/test-kyc-skip-expiration.ts
diff options
context:
space:
mode:
Diffstat (limited to 'packages/taler-harness/src/integrationtests/test-kyc-skip-expiration.ts')
-rw-r--r--packages/taler-harness/src/integrationtests/test-kyc-skip-expiration.ts291
1 files changed, 291 insertions, 0 deletions
diff --git a/packages/taler-harness/src/integrationtests/test-kyc-skip-expiration.ts b/packages/taler-harness/src/integrationtests/test-kyc-skip-expiration.ts
new file mode 100644
index 000000000..f0e06b10c
--- /dev/null
+++ b/packages/taler-harness/src/integrationtests/test-kyc-skip-expiration.ts
@@ -0,0 +1,291 @@
+/*
+ This file is part of GNU Taler
+ (C) 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/>
+ */
+
+/**
+ * Imports.
+ */
+import {
+ AbsoluteTime,
+ codecForAny,
+ codecForKycProcessClientInformation,
+ codecOptional,
+ Configuration,
+ decodeCrock,
+ encodeCrock,
+ j2s,
+ signAmlQuery,
+ TalerKycAml,
+ TalerProtocolTimestamp,
+ TransactionIdStr,
+ TransactionMajorState,
+ TransactionMinorState,
+} from "@gnu-taler/taler-util";
+import { readResponseJsonOrThrow } from "@gnu-taler/taler-util/http";
+import { WalletApiOperation } from "@gnu-taler/taler-wallet-core";
+import {
+ createKycTestkudosEnvironment,
+ postAmlDecision,
+ withdrawViaBankV3,
+} from "../harness/environments.js";
+import { GlobalTestState, harnessHttpLib, waitMs } from "../harness/harness.js";
+
+export const AML_PROGRAM_FROM_ATTRIBUTES_TO_CONTEXT: TalerKycAml.AmlProgramDefinition =
+ {
+ name: "from-attr-to-context",
+ logic: (_input, config) => {
+ const now = AbsoluteTime.toProtocolTimestamp(AbsoluteTime.now());
+ const outcome: TalerKycAml.AmlOutcome = {
+ to_investigate: false,
+ // pushing to info into properties for testing purposes
+ properties: {
+ "this comes": "from the program",
+ input: _input as any,
+ config,
+ },
+ events: [],
+ new_rules: {
+ expiration_time: now,
+ new_check: "info-oauth-test-passed",
+ rules: [],
+ successor_measure: "ask_more_info",
+ custom_measures: {
+ ask_more_info: {
+ context: {
+ info: _input?.attributes,
+ // this is the context info that the KYC-SPA will see
+ WAT: "REALLY?",
+ },
+ check_name: "C2",
+ prog_name: "P2",
+ },
+ },
+ },
+ };
+ return outcome;
+ },
+ requiredAttributes: [],
+ requiredContext: [],
+ };
+
+function adjustExchangeConfig(config: Configuration) {
+ config.setString("exchange", "enable_kyc", "yes");
+
+ config.setString("KYC-RULE-R1", "operation_type", "withdraw");
+ config.setString("KYC-RULE-R1", "enabled", "yes");
+ config.setString("KYC-RULE-R1", "exposed", "yes");
+ config.setString("KYC-RULE-R1", "is_and_combinator", "no");
+ config.setString("KYC-RULE-R1", "threshold", "TESTKUDOS:5");
+ config.setString("KYC-RULE-R1", "timeframe", "1d");
+ config.setString("KYC-RULE-R1", "next_measures", "M2");
+
+ config.setString("KYC-MEASURE-M1", "check_name", "C1");
+ config.setString("KYC-MEASURE-M1", "context", "{}");
+ config.setString("KYC-MEASURE-M1", "program", "P1");
+
+ config.setString("KYC-MEASURE-M2", "check_name", "C2");
+ config.setString("KYC-MEASURE-M2", "context", "{}");
+ config.setString("KYC-MEASURE-M2", "program", "P2");
+
+ config.setString("KYC-MEASURE-M3", "check_name", "SKIP");
+ config.setString("KYC-MEASURE-M3", "context", "{}");
+ config.setString("KYC-MEASURE-M3", "program", "P1");
+
+ config.setString(
+ "AML-PROGRAM-P1",
+ "command",
+ "taler-harness aml-program run-program --name from-attr-to-context",
+ );
+ config.setString("AML-PROGRAM-P1", "enabled", "true");
+ config.setString("AML-PROGRAM-P1", "description", "remove all rules");
+ config.setString("AML-PROGRAM-P1", "description_i18n", "{}");
+ config.setString("AML-PROGRAM-P1", "fallback", "M1");
+
+ config.setString("AML-PROGRAM-P2", "command", "/bin/true");
+ config.setString("AML-PROGRAM-P2", "enabled", "true");
+ config.setString("AML-PROGRAM-P2", "description", "does nothing");
+ config.setString("AML-PROGRAM-P2", "description_i18n", "{}");
+ config.setString("AML-PROGRAM-P2", "fallback", "M1");
+
+ config.setString("KYC-CHECK-C1", "type", "FORM");
+ config.setString("KYC-CHECK-C1", "form_name", "myform");
+ config.setString("KYC-CHECK-C1", "description", "my check!");
+ config.setString("KYC-CHECK-C1", "description_i18n", "{}");
+ config.setString("KYC-CHECK-C1", "outputs", "full_name birthdate");
+ config.setString("KYC-CHECK-C1", "fallback", "M1");
+
+ config.setString("KYC-CHECK-C2", "type", "FORM");
+ config.setString("KYC-CHECK-C2", "form_name", "dynamicform");
+ config.setString("KYC-CHECK-C2", "description", "my check info!");
+ config.setString("KYC-CHECK-C2", "description_i18n", "{}");
+ config.setString("KYC-CHECK-C1", "outputs", "what_the_officer_asked");
+ config.setString("KYC-CHECK-C2", "fallback", "M2");
+
+ config.setString("KYC-CHECK-C3", "type", "INFO");
+ config.setString("KYC-CHECK-C3", "description", "this is info c3");
+ config.setString("KYC-CHECK-C3", "description_i18n", "{}");
+ config.setString("KYC-CHECK-C3", "fallback", "M2");
+}
+
+/**
+ * Test setting a `new_measure` as the AML officer.
+ */
+export async function runKycSkipExpirationTest(t: GlobalTestState) {
+ // Set up test environment
+
+ const { walletClient, bankClient, exchange, amlKeypair } =
+ await createKycTestkudosEnvironment(t, { adjustExchangeConfig });
+
+ // Withdraw digital cash into the wallet.
+ let kycPaytoHash: string | undefined;
+ let accessToken: string | undefined;
+ let firstTransaction: string | undefined;
+
+ {
+ // step 1) Withdraw to trigger AML
+ const wres = await withdrawViaBankV3(t, {
+ amount: "TESTKUDOS:20",
+ bankClient,
+ exchange,
+ walletClient,
+ });
+
+ await walletClient.call(WalletApiOperation.TestingWaitTransactionState, {
+ transactionId: wres.transactionId as TransactionIdStr,
+ txState: {
+ major: TransactionMajorState.Pending,
+ minor: TransactionMinorState.KycRequired,
+ },
+ });
+
+ const txDetails = await walletClient.call(
+ WalletApiOperation.GetTransactionById,
+ {
+ transactionId: wres.transactionId,
+ },
+ );
+
+ accessToken = txDetails.kycAccessToken;
+ kycPaytoHash = txDetails.kycPaytoHash;
+ firstTransaction = wres.transactionId;
+ }
+
+ t.assertTrue(!!accessToken);
+
+ {
+ // step 2) Check KYC info
+ const infoResp = await harnessHttpLib.fetch(
+ new URL(`kyc-info/${accessToken}`, exchange.baseUrl).href,
+ );
+
+ const clientInfo = await readResponseJsonOrThrow(
+ infoResp,
+ codecOptional(codecForKycProcessClientInformation()),
+ );
+
+ console.log(j2s(clientInfo));
+ t.assertDeepEqual(infoResp.status, 200);
+ }
+
+ const sig = signAmlQuery(decodeCrock(amlKeypair.priv));
+ {
+ // step 3) Apply Measure 3 with SKIP check
+ const decisionsResp = await harnessHttpLib.fetch(
+ new URL(`aml/${amlKeypair.pub}/decisions`, exchange.baseUrl).href,
+ {
+ headers: {
+ "Taler-AML-Officer-Signature": encodeCrock(sig),
+ },
+ },
+ );
+
+ console.log(decisionsResp.status);
+ t.assertDeepEqual(decisionsResp.status, 204);
+
+ t.assertTrue(!!kycPaytoHash);
+
+ await postAmlDecision(t, {
+ amlPriv: amlKeypair.priv,
+ amlPub: amlKeypair.pub,
+ exchangeBaseUrl: exchange.baseUrl,
+ paytoHash: kycPaytoHash,
+ newMeasure: "M3",
+ properties: {
+ form: { name: "string" },
+ },
+ newRules: {
+ expiration_time: TalerProtocolTimestamp.now(),
+ custom_measures: {},
+ rules: [
+ // No rules!
+ ],
+ },
+ });
+ }
+
+ {
+ // step 4) Check KYC info, it should have the result
+ // of running program p1
+ const decisionsResp = await harnessHttpLib.fetch(
+ new URL(`aml/${amlKeypair.pub}/decisions`, exchange.baseUrl).href,
+ {
+ headers: {
+ "Taler-AML-Officer-Signature": encodeCrock(sig),
+ },
+ },
+ );
+
+ const decisions = await readResponseJsonOrThrow(
+ decisionsResp,
+ codecForAny(),
+ );
+ console.log(j2s(decisions));
+
+ t.assertDeepEqual(decisionsResp.status, 200);
+ }
+
+ // Wait for the KYC program to run
+ while (true) {
+ const infoResp = await harnessHttpLib.fetch(
+ new URL(`kyc-info/${accessToken}`, exchange.baseUrl).href,
+ );
+
+ console.log(`kyc-info status: ${infoResp.status}`);
+ if (infoResp.status == 202) {
+ await waitMs(1000);
+ continue;
+ }
+ t.assertDeepEqual(infoResp.status, 200);
+
+ const clientInfo = await readResponseJsonOrThrow(
+ infoResp,
+ codecOptional(codecForKycProcessClientInformation()),
+ );
+
+ console.log(j2s(clientInfo));
+
+ // Finally here we must see the officer defined form
+ t.assertDeepEqual(clientInfo?.requirements[0].context, {
+ // info contains the properties in the aml decision
+ info: { form: { name: "string" } },
+ // this is fixed by the aml program
+ WAT: "REALLY?",
+ });
+
+ break;
+ }
+}
+
+runKycSkipExpirationTest.suites = ["wallet"];