commit af12cf24122fe2dba1ff8fed747e7c7813da2aad
parent 16ca230ad87bdb72eb845d29dc9761e86ae31bf2
Author: Florian Dold <florian@dold.me>
Date: Fri, 8 May 2026 16:30:13 +0200
harness: fix and simplify self-provisioning tests (still WIP)
Diffstat:
6 files changed, 361 insertions(+), 360 deletions(-)
diff --git a/packages/taler-harness/src/harness/tan-helper.ts b/packages/taler-harness/src/harness/tan-helper.ts
@@ -14,53 +14,149 @@
GNU Taler; see the file COPYING. If not, see <http://www.gnu.org/licenses/>
*/
-import { Logger } from "@gnu-taler/taler-util";
-import { createServer } from "net";
+import * as fs from "node:fs";
+
+import {
+ ChallengeResponse,
+ Configuration,
+ Logger,
+ succeedOrThrow,
+ TalerMerchantInstanceHttpClient,
+ TanChannel,
+} from "@gnu-taler/taler-util";
+import { GlobalTestState, waitMs } from "./harness.js";
const logger = new Logger("tan-helper.ts");
-export interface TestTanHelperService {
- stop: () => void;
- getLastCodeForAddress(addr: string): string;
+export async function wait2FaCode(
+ path: string,
+): Promise<{ address: string; code: string }> {
+ while (true) {
+ if (!fs.existsSync(path)) {
+ logger.info(`waiting for 2fa code at ${path}`);
+ await waitMs(500);
+ continue;
+ }
+ const res = JSON.parse(fs.readFileSync(path, "utf-8"));
+ if (typeof res.address != "string" || typeof res.code != "string") {
+ throw Error("bad format");
+ }
+ return res;
+ }
}
-/**
- * Tan helper
- */
-export async function startTanHelper(opts: {
- socketFile?: string;
-}): Promise<TestTanHelperService> {
- const lastCodeForAddr: Record<string, string> = {};
-
- const server = createServer((socket) => {
- socket.on("data", (data) => {
- try {
- const jsonData = JSON.parse(data.toString().trim());
- const args: string[] = jsonData.args;
- const idx = args.findIndex(a => a === "--")
- if (idx === -1) throw Error("missing '--' mark")
- const addr = args[idx+1]
- const code = jsonData.stdin;
- lastCodeForAddr[addr] = code;
- } catch (error) {
- console.error("Error parsing JSON:", error);
- }
- });
-
- socket.on("end", () => {
- console.log("Client disconnected");
- });
- });
-
- await new Promise<void>((resolve, reject) => {
- server.listen(opts.socketFile ?? "/tmp/tan-helper.socket", () => resolve());
- });
+export type TestMfaChannelConfig = Partial<
+ Record<TanChannel, { path: string; address: string }>
+>;
+
+export async function solveMFA(
+ t: GlobalTestState,
+ api: TalerMerchantInstanceHttpClient,
+ resp: ChallengeResponse,
+ addrs: TestMfaChannelConfig,
+) {
+ for (const {
+ challenge_id: challenge,
+ tan_channel: type,
+ } of resp.challenges) {
+ const addrUsed = addrs[type];
+ if (!addrUsed) {
+ throw Error(
+ `The MFA is asking unexpected channel: ${type} id ${challenge}`,
+ );
+ }
+ await doChallenge(t, api, challenge, addrUsed.address, addrUsed.path);
+ }
+}
+
+export async function doChallenge(
+ t: GlobalTestState,
+ merchantClient: TalerMerchantInstanceHttpClient,
+ challengeId: string,
+ address: string,
+ mfaHelperResultPath: string,
+): Promise<void> {
+ succeedOrThrow(await merchantClient.sendChallenge(challengeId));
+ const res = await wait2FaCode(mfaHelperResultPath);
+ t.assertDeepEqual(res.address, address);
+ succeedOrThrow(
+ await merchantClient.confirmChallenge(challengeId, {
+ tan: res.code,
+ }),
+ );
+}
+
+export interface TestMfaChannelConfigEmailSms {
+ [TanChannel.EMAIL]: {
+ path: string;
+ address: string;
+ };
+
+ [TanChannel.SMS]: {
+ path: string;
+ address: string;
+ };
+}
+
+export function makeMfaConfigEmailSms(
+ t: GlobalTestState,
+ email: string,
+ sms: string,
+): TestMfaChannelConfigEmailSms {
+ const pathSms = `${t.testDir}/2fa-sms.json`;
+ const pathEmail = `${t.testDir}/2fa-email.json`;
return {
- stop() {
- server.close();
+ [TanChannel.EMAIL]: {
+ address: email,
+ path: pathEmail,
},
- getLastCodeForAddress(address: string): string {
- return lastCodeForAddr[address];
+ [TanChannel.SMS]: {
+ address: sms,
+ path: pathSms,
},
};
}
+
+export interface TestMfaChannelConfigEmailOnly {
+ [TanChannel.EMAIL]: {
+ path: string;
+ address: string;
+ };
+}
+
+export function makeMfaConfigEmailOnly(
+ t: GlobalTestState,
+ email: string,
+): TestMfaChannelConfigEmailOnly {
+ const pathEmail = `${t.testDir}/2fa-email.json`;
+ return {
+ [TanChannel.EMAIL]: {
+ address: email,
+ path: pathEmail,
+ },
+ };
+}
+
+export function configureTestMerchantMfa(
+ cfg: Configuration,
+ config: TestMfaChannelConfig,
+): void {
+ let channels = [];
+ if (config.email != null) {
+ cfg.setString(
+ "merchant",
+ "HELPER_EMAIL",
+ `taler-harness helper-2fa-dump ${config.email.path}`,
+ );
+ channels.push("email");
+ }
+ if (config.sms != null) {
+ cfg.setString(
+ "merchant",
+ "HELPER_SMS",
+ `taler-harness helper-2fa-dump ${config.sms.path}`,
+ );
+ channels.push("sms");
+ }
+ cfg.setString("merchant", "MANDATORY_TAN_CHANNELS", channels.join(" "));
+}
diff --git a/packages/taler-harness/src/index.ts b/packages/taler-harness/src/index.ts
@@ -83,7 +83,6 @@ import { AML_PROGRAM_FROM_ATTRIBUTES_TO_CONTEXT } from "integrationtests/test-ky
import { AML_PROGRAM_NEXT_MEASURE_FORM } from "integrationtests/test-kyc-two-forms.js";
import { execSync } from "node:child_process";
import fs from "node:fs";
-import net from "node:net";
import os from "node:os";
import path from "node:path";
import postgres from "postgres";
@@ -930,13 +929,13 @@ deploymentCli
}
bankAdminAuth = {
type: "bearer",
- token: resp.body.access_token
- }
+ token: resp.body.access_token,
+ };
} else if (bankAdminTokenArg != null) {
bankAdminAuth = {
type: "bearer",
- token: bankAdminTokenArg
- }
+ token: bankAdminTokenArg,
+ };
}
let merchantAdminToken: AccessToken | undefined;
@@ -975,9 +974,9 @@ deploymentCli
contact_data:
email || phone
? {
- email: email,
- phone: phone,
- }
+ email: email,
+ phone: phone,
+ }
: undefined,
});
@@ -1340,10 +1339,10 @@ deploymentCli
credit_facade_credentials:
bankUser && bankPassword
? {
- type: "basic",
- username: bankUser,
- password: bankPassword,
- }
+ type: "basic",
+ username: bankUser,
+ password: bankPassword,
+ }
: undefined,
});
if (createAccountResp.type != "ok") {
@@ -1777,26 +1776,31 @@ export const helpersCli = talerHarnessCli.subcommand(
);
talerHarnessCli
- .subcommand("runHelper", "run-helper")
- .requiredOption("socket", ["-s", "--socket"], clk.STRING)
- .maybeArgument("rest1", clk.STRING)
- .maybeArgument("rest2", clk.STRING)
- .maybeArgument("rest3", clk.STRING)
- .maybeArgument("rest4", clk.STRING)
- .maybeArgument("rest5", clk.STRING)
- .maybeArgument("rest6", clk.STRING)
+ .subcommand("helper2faDump", "helper-2fa-dump", {
+ help: "Helper used in integration tests.",
+ })
+ .requiredArgument("path", clk.STRING)
+ .requiredArgument("address", clk.STRING)
.action(async (args) => {
- const socketPath = args.runHelper.socket;
- const input = fs.readFileSync(0, "utf-8").trim();
-
- const stream = net.createConnection({ path: socketPath });
- stream.write(
+ const path = args.helper2faDump.path;
+ const address = args.helper2faDump.address;
+ const input = await read(process.stdin);
+ logger.info(
+ `started 2fa dump helper with target path ${path} and address ${address}`,
+ );
+ logger.info(`got input: ${input}`);
+ const searchRes = /[0-9-]+/.exec(input);
+ if (!searchRes) {
+ throw Error("could not find code in message");
+ }
+ const code = searchRes[0];
+ fs.writeFileSync(
+ path,
JSON.stringify({
- stdin: input,
- args: process.argv,
+ address,
+ code,
}),
);
- stream.end();
});
export const amlProgramCli = talerHarnessCli.subcommand(
@@ -2001,8 +2005,8 @@ merchantCli
const scope = args.token.scope ?? "all";
const duration = args.token.duration
? Duration.toTalerProtocolDuration(
- Duration.fromPrettyString(args.token.duration),
- )
+ Duration.fromPrettyString(args.token.duration),
+ )
: undefined;
const tokResp = await merchantApi.createAccessToken(instance, password, {
scope: scope as LoginTokenScope,
diff --git a/packages/taler-harness/src/integrationtests/test-merchant-self-provision-activation-two-bank-account.ts b/packages/taler-harness/src/integrationtests/test-merchant-self-provision-activation-two-bank-account.ts
@@ -26,14 +26,18 @@ import {
succeedOrThrow,
TalerMerchantInstanceHttpClient,
TalerMerchantManagementHttpClient,
- TanChannel,
} from "@gnu-taler/taler-util";
import { createSimpleTestkudosEnvironmentV3 } from "harness/environments.js";
-import { startTanHelper } from "harness/tan-helper.js";
-import { randomBytes } from "node:crypto";
-import { chmodSync, writeFileSync } from "node:fs";
-import { getTestHarnessPaytoForLabel, GlobalTestState } from "../harness/harness.js";
-import { solveMFA } from "./test-merchant-self-provision-inactive-account-permissions.js";
+import {
+ getTestHarnessPaytoForLabel,
+ GlobalTestState,
+} from "../harness/harness.js";
+import {
+ configureTestMerchantMfa,
+ doChallenge,
+ makeMfaConfigEmailSms,
+ solveMFA,
+} from "../harness/tan-helper.js";
/**
* Do basic checks on instance management and authentication.
@@ -43,44 +47,6 @@ export async function runMerchantSelfProvisionActivationTwoBankAccountsTest(
) {
// Set up test environment
- // FIXME: maybe merchant can use commands?
- const RND = randomBytes(10).toString("hex");
- const socketFile = `${t.testDir}/tan-helper-${RND}.socket`;
- const helperScript = `${t.testDir}/harness-helper-${RND}.sh`;
- writeFileSync(
- helperScript,
- `#!/bin/bash
-taler-harness run-helper --socket ${socketFile} -- $@
-`,
- );
- chmodSync(helperScript, "777");
-
- const {
- merchant,
- merchantAdminAccessToken,
- } = await createSimpleTestkudosEnvironmentV3(t, undefined, {
- additionalMerchantConfig(m) {
- m.modifyConfig(async (cfg) => {
- cfg.setString("merchant", "ENABLE_SELF_PROVISIONING", "yes");
- cfg.setString("merchant", "HELPER_SMS", helperScript);
- cfg.setString("merchant", "HELPER_EMAIL", helperScript);
- cfg.setString("merchant", "MANDATORY_TAN_CHANNELS", "email sms");
- });
- },
- });
- const helper = await startTanHelper({ socketFile });
-
- const merchantClient = new TalerMerchantManagementHttpClient(
- merchant.makeInstanceBaseUrl(),
- );
-
- {
- const r = succeedOrThrow(
- await merchantClient.listInstances(merchantAdminAccessToken),
- );
- t.assertDeepEqual(r.instances.length, 2);
- }
-
const instanceInfo = {
id: "self-instance",
name: "My instance",
@@ -100,6 +66,34 @@ taler-harness run-helper --socket ${socketFile} -- $@
phone_number: "+1111",
use_stefan: false,
};
+
+ const mfaConfig = makeMfaConfigEmailSms(
+ t,
+ instanceInfo.email,
+ instanceInfo.phone_number,
+ );
+
+ const { merchant, merchantAdminAccessToken } =
+ await createSimpleTestkudosEnvironmentV3(t, undefined, {
+ additionalMerchantConfig(m) {
+ m.modifyConfig(async (cfg) => {
+ cfg.setString("merchant", "ENABLE_SELF_PROVISIONING", "yes");
+ configureTestMerchantMfa(cfg, mfaConfig);
+ });
+ },
+ });
+
+ const merchantClient = new TalerMerchantManagementHttpClient(
+ merchant.makeInstanceBaseUrl(),
+ );
+
+ {
+ const r = succeedOrThrow(
+ await merchantClient.listInstances(merchantAdminAccessToken),
+ );
+ t.assertDeepEqual(r.instances.length, 2);
+ }
+
const signupStart = alternativeOrThrow(
await merchantClient.createInstanceSelfProvision(instanceInfo),
HttpStatusCode.Accepted,
@@ -120,33 +114,21 @@ taler-harness run-helper --socket ${socketFile} -- $@
t.assertDeepEqual(r.instances.length, 2);
}
- {
- succeedOrThrow(
- await merchantClient.sendChallenge(firstChallenge.challenge_id),
- );
-
- const message = helper.getLastCodeForAddress(instanceInfo.phone_number);
- const [tanCode] = message.split("\n");
- succeedOrThrow(
- await merchantClient.confirmChallenge(firstChallenge.challenge_id, {
- tan: tanCode,
- }),
- );
- }
-
- {
- succeedOrThrow(
- await merchantClient.sendChallenge(secondChallenge.challenge_id),
- );
+ await doChallenge(
+ t,
+ merchantClient,
+ firstChallenge.challenge_id,
+ instanceInfo.phone_number,
+ mfaConfig.sms.path,
+ );
- const message = helper.getLastCodeForAddress(instanceInfo.email);
- const [tanCode] = message.split("\n");
- succeedOrThrow(
- await merchantClient.confirmChallenge(secondChallenge.challenge_id, {
- tan: tanCode,
- }),
- );
- }
+ await doChallenge(
+ t,
+ merchantClient,
+ secondChallenge.challenge_id,
+ instanceInfo.email,
+ mfaConfig.email.path,
+ );
const completeSignup = await merchantClient.createInstanceSelfProvision(
instanceInfo,
@@ -181,10 +163,7 @@ taler-harness run-helper --socket ${socketFile} -- $@
HttpStatusCode.Accepted,
);
- await solveMFA(merchantClient, helper, loginChallenge, {
- [TanChannel.EMAIL]: instanceInfo.email,
- [TanChannel.SMS]: instanceInfo.phone_number,
- });
+ await solveMFA(t, merchantClient, loginChallenge, mfaConfig);
const { access_token: token } = succeedOrThrow(
await instanceApi.createAccessToken(
@@ -199,28 +178,37 @@ taler-harness run-helper --socket ${socketFile} -- $@
),
);
- const bankAccount = succeedOrThrow(await instanceApi.addBankAccount(token, {
- payto_uri: getTestHarnessPaytoForLabel("account1")
- }))
-
- const secondBankAccountChallenge = alternativeOrThrow(await instanceApi.addBankAccount(token, {
- payto_uri: getTestHarnessPaytoForLabel("account2")
- }), HttpStatusCode.Accepted)
-
- await solveMFA(instanceApi, helper, secondBankAccountChallenge, {
- [TanChannel.EMAIL]: instanceInfo.email,
- [TanChannel.SMS]: instanceInfo.phone_number,
- });
+ const bankAccount = succeedOrThrow(
+ await instanceApi.addBankAccount(token, {
+ payto_uri: getTestHarnessPaytoForLabel("account1"),
+ }),
+ );
- const secondBankAccount = succeedOrThrow(await instanceApi.addBankAccount(token, {
- payto_uri: getTestHarnessPaytoForLabel("account2")
- }, {
- challengeIds: secondBankAccountChallenge.challenges.map((c) => c.challenge_id)
- }))
+ const secondBankAccountChallenge = alternativeOrThrow(
+ await instanceApi.addBankAccount(token, {
+ payto_uri: getTestHarnessPaytoForLabel("account2"),
+ }),
+ HttpStatusCode.Accepted,
+ );
-
+ await solveMFA(t, instanceApi, secondBankAccountChallenge, mfaConfig);
- helper.stop();
+ const secondBankAccount = succeedOrThrow(
+ await instanceApi.addBankAccount(
+ token,
+ {
+ payto_uri: getTestHarnessPaytoForLabel("account2"),
+ },
+ {
+ challengeIds: secondBankAccountChallenge.challenges.map(
+ (c) => c.challenge_id,
+ ),
+ },
+ ),
+ );
}
-runMerchantSelfProvisionActivationTwoBankAccountsTest.suites = ["merchant", "self-provision"];
+runMerchantSelfProvisionActivationTwoBankAccountsTest.suites = [
+ "merchant",
+ "self-provision",
+];
diff --git a/packages/taler-harness/src/integrationtests/test-merchant-self-provision-activation.ts b/packages/taler-harness/src/integrationtests/test-merchant-self-provision-activation.ts
@@ -21,6 +21,7 @@ import {
alternativeOrThrow,
Duration,
HttpStatusCode,
+ Logger,
LoginTokenScope,
MerchantAuthMethod,
succeedOrThrow,
@@ -29,11 +30,15 @@ import {
TanChannel,
} from "@gnu-taler/taler-util";
import { createSimpleTestkudosEnvironmentV3 } from "harness/environments.js";
-import { startTanHelper } from "harness/tan-helper.js";
-import { randomBytes } from "node:crypto";
-import { chmodSync, writeFileSync } from "node:fs";
import { GlobalTestState } from "../harness/harness.js";
-import { solveMFA } from "./test-merchant-self-provision-inactive-account-permissions.js";
+import {
+ configureTestMerchantMfa,
+ makeMfaConfigEmailSms,
+ solveMFA,
+ wait2FaCode,
+} from "../harness/tan-helper.js";
+
+export const logger = new Logger("test-merchant-self-provision-activation.ts");
/**
* Do basic checks on instance management and authentication.
@@ -43,48 +48,6 @@ export async function runMerchantSelfProvisionActivationTest(
) {
// Set up test environment
- // FIXME: maybe merchant can use commands?
- const RND = randomBytes(10).toString("hex");
- const socketFile = `${t.testDir}/tan-helper-${RND}.socket`;
- const helperScript = `${t.testDir}/harness-helper-${RND}.sh`;
- writeFileSync(
- helperScript,
- `#!/bin/bash
-taler-harness run-helper --socket ${socketFile} -- $@
-`,
- );
- chmodSync(helperScript, "777");
-
- const {
- walletClient,
- bankClient,
- exchange,
- merchant,
- bank,
- merchantAdminAccessToken,
- } = await createSimpleTestkudosEnvironmentV3(t, undefined, {
- additionalMerchantConfig(m) {
- m.modifyConfig(async (cfg) => {
- cfg.setString("merchant", "ENABLE_SELF_PROVISIONING", "yes");
- cfg.setString("merchant", "HELPER_SMS", helperScript);
- cfg.setString("merchant", "HELPER_EMAIL", helperScript);
- cfg.setString("merchant", "MANDATORY_TAN_CHANNELS", "email sms");
- });
- },
- });
- const helper = await startTanHelper({ socketFile });
-
- const merchantClient = new TalerMerchantManagementHttpClient(
- merchant.makeInstanceBaseUrl(),
- );
-
- {
- const r = succeedOrThrow(
- await merchantClient.listInstances(merchantAdminAccessToken),
- );
- t.assertDeepEqual(r.instances.length, 2);
- }
-
const instanceInfo = {
id: "self-instance",
name: "My instance",
@@ -104,6 +67,34 @@ taler-harness run-helper --socket ${socketFile} -- $@
phone_number: "+1111",
use_stefan: false,
};
+
+ const mfaConfig = makeMfaConfigEmailSms(
+ t,
+ instanceInfo.email,
+ instanceInfo.phone_number,
+ );
+
+ const { merchant, merchantAdminAccessToken } =
+ await createSimpleTestkudosEnvironmentV3(t, undefined, {
+ additionalMerchantConfig(m) {
+ m.modifyConfig(async (cfg) => {
+ cfg.setString("merchant", "ENABLE_SELF_PROVISIONING", "yes");
+ configureTestMerchantMfa(cfg, mfaConfig);
+ });
+ },
+ });
+
+ const merchantClient = new TalerMerchantManagementHttpClient(
+ merchant.makeInstanceBaseUrl(),
+ );
+
+ {
+ const r = succeedOrThrow(
+ await merchantClient.listInstances(merchantAdminAccessToken),
+ );
+ t.assertDeepEqual(r.instances.length, 2);
+ }
+
const signupStart = alternativeOrThrow(
await merchantClient.createInstanceSelfProvision(instanceInfo),
HttpStatusCode.Accepted,
@@ -116,7 +107,7 @@ taler-harness run-helper --socket ${socketFile} -- $@
const firstChallenge = signupStart.challenges[0];
const secondChallenge = signupStart.challenges[1];
- //FIXME: check the order
+ // FIXME: check the order
// always first emails since is cheaper
t.assertTrue(firstChallenge.tan_channel === TanChannel.EMAIL);
t.assertTrue(secondChallenge.tan_channel === TanChannel.SMS);
@@ -133,12 +124,11 @@ taler-harness run-helper --socket ${socketFile} -- $@
succeedOrThrow(
await merchantClient.sendChallenge(firstChallenge.challenge_id),
);
-
- const message = helper.getLastCodeForAddress(instanceInfo.phone_number);
- const [tanCode] = message.split("\n");
+ const res = await wait2FaCode(mfaConfig.email.path);
+ t.assertDeepEqual(res.address, instanceInfo.email);
succeedOrThrow(
await merchantClient.confirmChallenge(firstChallenge.challenge_id, {
- tan: tanCode,
+ tan: res.code,
}),
);
}
@@ -147,12 +137,11 @@ taler-harness run-helper --socket ${socketFile} -- $@
succeedOrThrow(
await merchantClient.sendChallenge(secondChallenge.challenge_id),
);
-
- const message = helper.getLastCodeForAddress(instanceInfo.email);
- const [tanCode] = message.split("\n");
+ const res = await wait2FaCode(mfaConfig.sms.path);
+ t.assertDeepEqual(res.address, instanceInfo.phone_number);
succeedOrThrow(
await merchantClient.confirmChallenge(secondChallenge.challenge_id, {
- tan: tanCode,
+ tan: res.code,
}),
);
}
@@ -190,10 +179,7 @@ taler-harness run-helper --socket ${socketFile} -- $@
HttpStatusCode.Accepted,
);
- await solveMFA(merchantClient, helper, loginChallenge, {
- [TanChannel.EMAIL]: instanceInfo.email,
- [TanChannel.SMS]: instanceInfo.phone_number,
- });
+ await solveMFA(t, merchantClient, loginChallenge, mfaConfig);
const { access_token: token } = succeedOrThrow(
await instanceApi.createAccessToken(
@@ -217,8 +203,6 @@ taler-harness run-helper --socket ${socketFile} -- $@
t.assertDeepEqual(det.email_validated, true);
t.assertDeepEqual(det.phone_number, instanceInfo.phone_number);
t.assertDeepEqual(det.phone_validated, true);
-
- helper.stop();
}
runMerchantSelfProvisionActivationTest.suites = ["merchant", "self-provision"];
diff --git a/packages/taler-harness/src/integrationtests/test-merchant-self-provision-forgot-password.ts b/packages/taler-harness/src/integrationtests/test-merchant-self-provision-forgot-password.ts
@@ -26,14 +26,14 @@ import {
succeedOrThrow,
TalerMerchantInstanceHttpClient,
TalerMerchantManagementHttpClient,
- TanChannel,
} from "@gnu-taler/taler-util";
import { createSimpleTestkudosEnvironmentV3 } from "harness/environments.js";
-import { startTanHelper } from "harness/tan-helper.js";
-import { randomBytes } from "node:crypto";
-import { chmodSync, writeFileSync } from "node:fs";
+import {
+ configureTestMerchantMfa,
+ makeMfaConfigEmailOnly,
+ solveMFA,
+} from "harness/tan-helper.js";
import { GlobalTestState } from "../harness/harness.js";
-import { solveMFA } from "./test-merchant-self-provision-inactive-account-permissions.js";
/**
* The merchant should get the TAN code on request to be used to activate the account.
@@ -43,47 +43,6 @@ export async function runMerchantSelfProvisionForgotPasswordTest(
) {
// Set up test environment
- // FIXME: maybe merchant can use commands?
- const RND = randomBytes(10).toString("hex");
- const socketFile = `${t.testDir}/tan-helper-${RND}.socket`;
- const helperScript = `${t.testDir}/harness-helper-${RND}.sh`;
- writeFileSync(
- helperScript,
- `#!/bin/bash
-taler-harness run-helper --socket ${socketFile} -- $@
-`,
- );
- chmodSync(helperScript, "777");
-
- const {
- walletClient,
- bankClient,
- exchange,
- merchant,
- merchantAdminAccessToken,
- bank,
- } = await createSimpleTestkudosEnvironmentV3(t, undefined, {
- additionalMerchantConfig(m) {
- m.modifyConfig(async (cfg) => {
- cfg.setString("merchant", "ENABLE_SELF_PROVISIONING", "yes");
- cfg.setString("merchant", "HELPER_EMAIL", helperScript);
- cfg.setString("merchant", "MANDATORY_TAN_CHANNELS", "email");
- });
- },
- });
- const helper = await startTanHelper({ socketFile });
-
- const merchantClient = new TalerMerchantManagementHttpClient(
- merchant.makeInstanceBaseUrl(),
- );
-
- {
- const r = succeedOrThrow(
- await merchantClient.listInstances(merchantAdminAccessToken),
- );
- t.assertDeepEqual(r.instances.length, 2);
- }
-
const instanceInfo = {
id: "self-instance",
name: "My instance",
@@ -103,14 +62,35 @@ taler-harness run-helper --socket ${socketFile} -- $@
use_stefan: false,
};
+ const mfaConfig = makeMfaConfigEmailOnly(t, instanceInfo.email);
+
+ const { merchant, merchantAdminAccessToken } =
+ await createSimpleTestkudosEnvironmentV3(t, undefined, {
+ additionalMerchantConfig(m) {
+ m.modifyConfig(async (cfg) => {
+ cfg.setString("merchant", "ENABLE_SELF_PROVISIONING", "yes");
+ configureTestMerchantMfa(cfg, mfaConfig);
+ });
+ },
+ });
+
+ const merchantClient = new TalerMerchantManagementHttpClient(
+ merchant.makeInstanceBaseUrl(),
+ );
+
+ {
+ const r = succeedOrThrow(
+ await merchantClient.listInstances(merchantAdminAccessToken),
+ );
+ t.assertDeepEqual(r.instances.length, 2);
+ }
+
const tryCreation = alternativeOrThrow(
await merchantClient.createInstanceSelfProvision(instanceInfo),
HttpStatusCode.Accepted,
);
- await solveMFA(merchantClient, helper, tryCreation, {
- [TanChannel.EMAIL]: instanceInfo.email,
- });
+ await solveMFA(t, merchantClient, tryCreation, mfaConfig);
succeedOrThrow(
await merchantClient.createInstanceSelfProvision(instanceInfo, {
@@ -133,9 +113,7 @@ taler-harness run-helper --socket ${socketFile} -- $@
HttpStatusCode.Accepted,
);
- await solveMFA(instanceApi, helper, mfa, {
- [TanChannel.EMAIL]: instanceInfo.email,
- });
+ await solveMFA(t, instanceApi, mfa, mfaConfig);
succeedOrThrow(
await instanceApi.forgotPasswordSelfProvision(newPassword, {
@@ -150,9 +128,7 @@ taler-harness run-helper --socket ${socketFile} -- $@
HttpStatusCode.Accepted,
);
- await solveMFA(instanceApi, helper, mfa2, {
- [TanChannel.EMAIL]: instanceInfo.email,
- });
+ await solveMFA(t, instanceApi, mfa2, mfaConfig);
const tk = succeedOrThrow(
await instanceApi.createAccessToken(
@@ -166,8 +142,6 @@ taler-harness run-helper --socket ${socketFile} -- $@
},
),
);
-
- helper.stop();
}
runMerchantSelfProvisionForgotPasswordTest.suites = [
diff --git a/packages/taler-harness/src/integrationtests/test-merchant-self-provision-inactive-account-permissions.ts b/packages/taler-harness/src/integrationtests/test-merchant-self-provision-inactive-account-permissions.ts
@@ -19,7 +19,6 @@
*/
import {
alternativeOrThrow,
- ChallengeResponse,
Duration,
HttpStatusCode,
LoginTokenScope,
@@ -27,12 +26,14 @@ import {
succeedOrThrow,
TalerMerchantInstanceHttpClient,
TalerMerchantManagementHttpClient,
- TanChannel,
} from "@gnu-taler/taler-util";
import { createSimpleTestkudosEnvironmentV3 } from "harness/environments.js";
-import { startTanHelper, TestTanHelperService } from "harness/tan-helper.js";
-import { randomBytes } from "node:crypto";
-import { chmodSync, writeFileSync } from "node:fs";
+import {
+ configureTestMerchantMfa,
+ makeMfaConfigEmailOnly,
+ solveMFA,
+ wait2FaCode,
+} from "harness/tan-helper.js";
import { GlobalTestState } from "../harness/harness.js";
/**
@@ -44,47 +45,6 @@ export async function runMerchantSelfProvisionInactiveAccountPermissionsTest(
) {
// Set up test environment
- // FIXME: maybe merchant can use commands?
- const RND = randomBytes(10).toString("hex");
- const socketFile = `${t.testDir}/tan-helper-${RND}.socket`;
- const helperScript = `${t.testDir}/harness-helper-${RND}.sh`;
- writeFileSync(
- helperScript,
- `#!/bin/bash
- taler-harness run-helper --socket ${socketFile} -- $@
- `,
- );
- chmodSync(helperScript, "777");
-
- const {
- walletClient,
- bankClient,
- exchange,
- merchant,
- bank,
- merchantAdminAccessToken,
- } = await createSimpleTestkudosEnvironmentV3(t, undefined, {
- additionalMerchantConfig(m) {
- m.modifyConfig(async (cfg) => {
- cfg.setString("merchant", "ENABLE_SELF_PROVISIONING", "yes");
- cfg.setString("merchant", "HELPER_SMS", helperScript);
- cfg.setString("merchant", "HELPER_EMAIL", helperScript);
- cfg.setString("merchant", "MANDATORY_TAN_CHANNELS", "email");
- });
- },
- });
- const helper = await startTanHelper({ socketFile });
-
- const merchantClient = new TalerMerchantManagementHttpClient(
- merchant.makeInstanceBaseUrl(),
- );
-
- {
- const r = succeedOrThrow(
- await merchantClient.listInstances(merchantAdminAccessToken),
- );
- t.assertDeepEqual(r.instances.length, 2);
- }
const instanceInfo = {
id: "self-instance",
name: "My instance",
@@ -104,6 +64,29 @@ export async function runMerchantSelfProvisionInactiveAccountPermissionsTest(
use_stefan: false,
};
+ const mfaConfig = makeMfaConfigEmailOnly(t, instanceInfo.email);
+
+ const { merchant, merchantAdminAccessToken } =
+ await createSimpleTestkudosEnvironmentV3(t, undefined, {
+ additionalMerchantConfig(m) {
+ m.modifyConfig(async (cfg) => {
+ cfg.setString("merchant", "ENABLE_SELF_PROVISIONING", "yes");
+ configureTestMerchantMfa(cfg, mfaConfig);
+ });
+ },
+ });
+
+ const merchantClient = new TalerMerchantManagementHttpClient(
+ merchant.makeInstanceBaseUrl(),
+ );
+
+ {
+ const r = succeedOrThrow(
+ await merchantClient.listInstances(merchantAdminAccessToken),
+ );
+ t.assertDeepEqual(r.instances.length, 2);
+ }
+
const signupStart = alternativeOrThrow(
await merchantClient.createInstanceSelfProvision(instanceInfo),
HttpStatusCode.Accepted,
@@ -130,7 +113,8 @@ export async function runMerchantSelfProvisionInactiveAccountPermissionsTest(
succeedOrThrow(await merchantClient.sendChallenge(firstChallenge));
- const [tan] = helper.getLastCodeForAddress(instanceInfo.email).split("\n");
+ const res = await wait2FaCode(mfaConfig.email.path);
+ const tan = res.code;
t.assertTrue(tan.length > 0, "the mechant didn't send the code to the email");
@@ -148,9 +132,7 @@ export async function runMerchantSelfProvisionInactiveAccountPermissionsTest(
t.assertDeepEqual(emailChange.challenges.length, 1);
t.assertDeepEqual(emailChange.combi_and, true);
- await solveMFA(merchantClient, helper, emailChange, {
- [TanChannel.EMAIL]: instanceInfo.email,
- });
+ await solveMFA(t, merchantClient, emailChange, mfaConfig);
const wrongInfo = {
...instanceInfo,
@@ -186,9 +168,7 @@ export async function runMerchantSelfProvisionInactiveAccountPermissionsTest(
HttpStatusCode.Accepted,
);
- await solveMFA(merchantClient, helper, loginChallenge, {
- [TanChannel.EMAIL]: instanceInfo.email,
- });
+ await solveMFA(t, merchantClient, loginChallenge, mfaConfig);
const { access_token: token } = succeedOrThrow(
await instanceApi.createAccessToken(
@@ -210,34 +190,9 @@ export async function runMerchantSelfProvisionInactiveAccountPermissionsTest(
// check that the instance has the new email
t.assertDeepEqual(det.email, instanceInfo.email);
t.assertDeepEqual(det.email_validated, true);
- helper.stop();
}
runMerchantSelfProvisionInactiveAccountPermissionsTest.suites = [
"merchant",
"self-provision",
];
-
-export async function solveMFA(
- api: TalerMerchantInstanceHttpClient,
- helper: TestTanHelperService,
- resp: ChallengeResponse,
- addrs: Partial<Record<TanChannel, string>>,
-) {
- for (const {
- challenge_id: challenge,
- tan_channel: type,
- } of resp.challenges) {
- const addrUsed = addrs[type];
- if (!addrUsed) {
- throw Error(
- `The MFA is asking unexpected channel: ${type} id ${challenge}`,
- );
- }
- succeedOrThrow(await api.sendChallenge(challenge));
-
- const [tan] = helper.getLastCodeForAddress(addrUsed).split("\n");
-
- succeedOrThrow(await api.confirmChallenge(challenge, { tan }));
- }
-}