diff options
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.ts | 291 |
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"]; |