From b674422840041f8cdba0ad52be17bd0c527ffecb Mon Sep 17 00:00:00 2001 From: Sebastian Date: Tue, 26 Mar 2024 16:54:35 -0300 Subject: fix #8660 --- packages/bank-ui/src/Routing.tsx | 2 +- packages/bank-ui/src/context/config.ts | 23 +- packages/bank-ui/src/pages/LoginForm.tsx | 3 +- packages/bank-ui/src/pages/RegistrationPage.tsx | 2 +- .../src/paths/admin/create/index.tsx | 6 +- .../src/paths/instance/token/index.tsx | 4 +- .../src/paths/login/index.tsx | 6 +- .../create_merchantAndBankAccount_pdf.sh | 23 ++ packages/taler-harness/pdf-template.html | 65 ++++ packages/taler-harness/src/index.ts | 393 ++++++++++++++++++--- .../taler-util/src/http-client/authentication.ts | 12 +- packages/taler-util/src/http-client/bank-core.ts | 10 +- packages/taler-util/src/http-client/merchant.ts | 6 +- packages/taler-util/src/http-common.ts | 1 + packages/taler-util/src/http-impl.node.ts | 9 +- packages/taler-util/src/http-impl.qtart.ts | 8 +- packages/web-util/src/context/bank-api.ts | 1 - packages/web-util/src/context/merchant-api.ts | 2 - 18 files changed, 481 insertions(+), 95 deletions(-) create mode 100644 packages/taler-harness/create_merchantAndBankAccount_pdf.sh create mode 100644 packages/taler-harness/pdf-template.html diff --git a/packages/bank-ui/src/Routing.tsx b/packages/bank-ui/src/Routing.tsx index 489dbe30b..3ec5f0c77 100644 --- a/packages/bank-ui/src/Routing.tsx +++ b/packages/bank-ui/src/Routing.tsx @@ -113,7 +113,7 @@ function PublicRounting({ async function doAutomaticLogin(username: string, password: string) { await handleError(async () => { - const resp = await lib.auth(username).createAccessToken(password, { + const resp = await lib.auth(username).createAccessTokenBasic(username, password, { scope: "readwrite", duration: { d_us: "forever" }, refreshable: true, diff --git a/packages/bank-ui/src/context/config.ts b/packages/bank-ui/src/context/config.ts index 55b21d0da..86b6df5f3 100644 --- a/packages/bank-ui/src/context/config.ts +++ b/packages/bank-ui/src/context/config.ts @@ -218,7 +218,6 @@ function buildApiClient(url: URL) { const authClient = (user: string) => new TalerAuthenticationHttpClient( bankClient.getAuthenticationAPI(user).href, - user, httpLib, ); @@ -305,15 +304,15 @@ const evictBankSwrCache: CacheEvictor = { }; const evictConversionSwrCache: CacheEvictor = - { - async notifySuccess(op) { - switch (op) { - case TalerBankConversionCacheEviction.UPDATE_RATE: { - await revalidateConversionInfo(); - return; - } - default: - assertUnreachable(op); +{ + async notifySuccess(op) { + switch (op) { + case TalerBankConversionCacheEviction.UPDATE_RATE: { + await revalidateConversionInfo(); + return; } - }, - }; + default: + assertUnreachable(op); + } + }, +}; diff --git a/packages/bank-ui/src/pages/LoginForm.tsx b/packages/bank-ui/src/pages/LoginForm.tsx index 904cd39d2..7eed0cd9e 100644 --- a/packages/bank-ui/src/pages/LoginForm.tsx +++ b/packages/bank-ui/src/pages/LoginForm.tsx @@ -79,8 +79,7 @@ export function LoginForm({ ? undefined : withErrorHandler( async () => - authenticator(username).createAccessToken(password, { - // scope: "readwrite" as "write", // FIX: different than merchant + authenticator(username).createAccessTokenBasic(username, password, { scope: "readwrite", duration: { d_us: "forever" }, refreshable: true, diff --git a/packages/bank-ui/src/pages/RegistrationPage.tsx b/packages/bank-ui/src/pages/RegistrationPage.tsx index d7093d973..dc08ce0fa 100644 --- a/packages/bank-ui/src/pages/RegistrationPage.tsx +++ b/packages/bank-ui/src/pages/RegistrationPage.tsx @@ -114,7 +114,7 @@ function RegistrationForm({ onComplete: () => void, ) { await handleError(async (onError) => { - const resp = await api.createAccount("" as AccessToken, { + const resp = await api.createAccount(undefined, { name, username, password, diff --git a/packages/merchant-backoffice-ui/src/paths/admin/create/index.tsx b/packages/merchant-backoffice-ui/src/paths/admin/create/index.tsx index 3db38acc3..0e8ea1f5b 100644 --- a/packages/merchant-backoffice-ui/src/paths/admin/create/index.tsx +++ b/packages/merchant-backoffice-ui/src/paths/admin/create/index.tsx @@ -57,7 +57,7 @@ export default function Create({ onBack, onConfirm, forceId }: Props): VNode { try { await createInstance(d); if (d.auth.token) { - const result = await lib.authenticate.createAccessToken( + const result = await lib.authenticate.createAccessTokenBearer( d.auth.token, { scope: "write", @@ -68,8 +68,8 @@ export default function Create({ onBack, onConfirm, forceId }: Props): VNode { }, ); if (result.type === "ok") { - const { access_token } = result.body; - logIn({ token: access_token }); + const { token } = result.body; + logIn({ token }); } } onConfirm(); diff --git a/packages/merchant-backoffice-ui/src/paths/instance/token/index.tsx b/packages/merchant-backoffice-ui/src/paths/instance/token/index.tsx index 889ef6f5d..13b5c45f1 100644 --- a/packages/merchant-backoffice-ui/src/paths/instance/token/index.tsx +++ b/packages/merchant-backoffice-ui/src/paths/instance/token/index.tsx @@ -85,11 +85,11 @@ export default function Token({ }} onNewToken={async (currentToken, newToken): Promise => { try { - await lib.management.updateInstanceAuthentication(currentToken, { + await lib.management.updateCurrentInstanceAuthentication(currentToken, { token: newToken, method: "token" }) - const resp = await lib.authenticate.createAccessTokenMerchant(newToken, { + const resp = await lib.authenticate.createAccessTokenBearer(newToken, { scope: "write", duration: { d_us: "forever" diff --git a/packages/merchant-backoffice-ui/src/paths/login/index.tsx b/packages/merchant-backoffice-ui/src/paths/login/index.tsx index 14322f079..6a698186a 100644 --- a/packages/merchant-backoffice-ui/src/paths/login/index.tsx +++ b/packages/merchant-backoffice-ui/src/paths/login/index.tsx @@ -34,7 +34,7 @@ import { } from "../../context/session.js"; import { Notification } from "../../utils/types.js"; -interface Props {} +interface Props { } const tokenRequest = { scope: "write", @@ -55,7 +55,7 @@ export function LoginPage(_p: Props): VNode { async function doImpersonateImpl(instanceId: string) { const result = await lib .impersonate(instanceId) - .createAccessTokenMerchant(token, tokenRequest); + .createAccessTokenBearer(token, tokenRequest); if (result.type === "ok") { const { token } = result.body; logIn({ token }); @@ -80,7 +80,7 @@ export function LoginPage(_p: Props): VNode { } } async function doLoginImpl() { - const result = await lib.authenticate.createAccessTokenMerchant( + const result = await lib.authenticate.createAccessTokenBearer( token, tokenRequest, ); diff --git a/packages/taler-harness/create_merchantAndBankAccount_pdf.sh b/packages/taler-harness/create_merchantAndBankAccount_pdf.sh new file mode 100644 index 000000000..4593cc9a0 --- /dev/null +++ b/packages/taler-harness/create_merchantAndBankAccount_pdf.sh @@ -0,0 +1,23 @@ +DATA=$(mktemp) +set -e + +[ -z "$1" ] && echo First parameter must be the json file result from \'taler-harness deployment provision-bank-and-merchant\'. Alternative \'-\' can be used if the file is provided from stdin. && exit 1 + +cat $1 > $DATA + +[ -z "$(jq -r '.bankUser//empty' $DATA)" ] && echo the json file is not complete: missing bankUser && exit 1 +[ -z "$(jq -r '.bankURL//empty' $DATA)" ] && echo the json file is not complete: missing bankURL && exit 1 +[ -z "$(jq -r '.merchantURL//empty' $DATA)" ] && echo the json file is not complete: missing merchantURL && exit 1 +[ -z "$(jq -r '.templateURI//empty' $DATA)" ] && echo the json file is not complete: missing templateURI && exit 1 +[ -z "$(jq -r '.password//empty' $DATA)" ] && echo the json file is not complete: missing password && exit 1 + +add_qr_image(){ + jq -r $1 $DATA | qrencode -l Q -m 2 -s 5 -o - | base64 -w 0 | jq -Rn '{"'$2'":inputs}' | jq -s add - $DATA | sponge $DATA +} + +add_qr_image .templateURI templateQR +add_qr_image .bankURL bankQR +add_qr_image .merchantURL merchantQR + +chevron pdf-template.html -d $DATA | wkhtmltopdf - out.pdf + diff --git a/packages/taler-harness/pdf-template.html b/packages/taler-harness/pdf-template.html new file mode 100644 index 000000000..d308d67c4 --- /dev/null +++ b/packages/taler-harness/pdf-template.html @@ -0,0 +1,65 @@ + + + +

Account information

+ +

The information in this page is confidentail, do not share with others.

+ +

Bank

+

+ In your bank account you will be able to see how much revenue + has been consolidated. +

+
+ +
+

URL: {{bankURL}}

+

accounts id: {{bankUser}}

+

password: {{password}}

+
+ +
+
+ +
bank URL
+
+
+
+ +
+ +

Backoffice

+

+ In this site you will be able to see how much are you selling, + make refunds or create new QR codes. +

+ +
+
+

URL: {{merchantURL}}

+

password: {{password}}

+
+
+
+ +
merchant URL
+
+
+
+ +
+
+ +
+

Payme QR code

+

+ The following QR code can be utilized in + public settings to request payments. +

+
+ +
{{templateURI}}
+
+ + + \ No newline at end of file diff --git a/packages/taler-harness/src/index.ts b/packages/taler-harness/src/index.ts index 3bec1698a..4b0319a3e 100644 --- a/packages/taler-harness/src/index.ts +++ b/packages/taler-harness/src/index.ts @@ -18,6 +18,7 @@ * Imports. */ import { + AccessToken, AmountString, Amounts, BalancesResponse, @@ -25,14 +26,23 @@ import { Duration, HttpStatusCode, Logger, - MerchantInstanceConfig, - RegisterAccountRequest, + PaytoString, + TalerAuthenticationHttpClient, + TalerBankConversionHttpClient, + TalerCoreBankHttpClient, + TalerErrorCode, + TalerMerchantInstanceHttpClient, + TalerMerchantManagementHttpClient, TransactionsResponse, decodeCrock, + encodeCrock, generateIban, j2s, + randomBytes, rsaBlind, setGlobalLogLevelFromString, + setPrintHttpRequestAsCurl, + stringifyPayTemplateUri } from "@gnu-taler/taler-util"; import { clk } from "@gnu-taler/taler-util/clk"; import { @@ -69,6 +79,7 @@ import { } from "./harness/helpers.js"; import { getTestInfo, runTests } from "./integrationtests/testrunner.js"; import { lintExchangeDeployment } from "./lint.js"; +import { randomUUID } from "crypto"; const logger = new Logger("taler-harness:index.ts"); @@ -585,6 +596,291 @@ deploymentCli console.log(generateIban(args.genIban.countryCode, args.genIban.length)); }); +deploymentCli + .subcommand("provisionBankMerchant", "provision-bank-and-merchant", { + help: "Provision a bank account, merchant instance and link them together.", + }) + .requiredArgument("merchantApiBaseUrl", clk.STRING, { + help: "URL location of the merchant backend" + }) + .requiredArgument("corebankApiBaseUrl", clk.STRING, { + help: "URL location of the libeufin bank backend" + }) + .requiredOption("merchantToken", ["--merchant-management-token"], clk.STRING, { + help: "acces token of the default instance in the merchant backend" + }) + .maybeOption("bankToken", ["--bank-admin-token"], clk.STRING, { + help: "libeufin bank admin's password if the account creation is restricted" + }) + .requiredOption("name", ["--legal-name"], clk.STRING, { + help: "legal name of the merchant" + }) + .maybeOption("email", ["--email"], clk.STRING, { + help: "email contact of the merchant" + }) + .maybeOption("phone", ["--phone"], clk.STRING, { + help: "phone contact of the merchant" + }) + .requiredOption("id", ["--id"], clk.STRING, { + help: "login id for the bank account and instance id of the merchant backend" + }) + .flag("template", ["--create-template"], { + help: "use this flag to create a default template for the merchant with fixed summary" + }) + .requiredOption("password", ["--password"], clk.STRING, { + help: "password of the accounts in libeufin bank and merchant backend" + }) + .flag("randomPassword", ["--set-random-password"], { + help: "if everything worked ok, change the password of the accounts at the end" + }) + .action(async (args) => { + const managementToken = args.provisionBankMerchant.merchantToken as AccessToken; + const bankAdminPassword = args.provisionBankMerchant.bankToken as AccessToken; + const id = args.provisionBankMerchant.id; + const name = args.provisionBankMerchant.name; + const email = args.provisionBankMerchant.email; + const phone = args.provisionBankMerchant.phone; + const password = args.provisionBankMerchant.password; + + + const httpLib = createPlatformHttpLib({}); + const merchantManager = new TalerMerchantManagementHttpClient(args.provisionBankMerchant.merchantApiBaseUrl, httpLib); + const bank = new TalerCoreBankHttpClient(args.provisionBankMerchant.corebankApiBaseUrl, httpLib); + const instanceURL = merchantManager.getSubInstanceAPI(id).href + const merchantInstance = new TalerMerchantInstanceHttpClient(instanceURL, httpLib); + const conv = new TalerBankConversionHttpClient(bank.getConversionInfoAPI().href, httpLib) + const bankAuth = new TalerAuthenticationHttpClient(bank.getAuthenticationAPI(id).href, httpLib) + + + const bc = await bank.getConfig() + if (!bank.isCompatible(bc.body.version)) { + logger.error( + `bank server version is not compatible: ${bc.body.version}, client version: ${bank.PROTOCOL_VERSION}`, + ); + return; + } + const mc = await merchantManager.getConfig() + if (!merchantManager.isCompatible(mc.body.version)) { + logger.error( + `merchant server version is not compatible: ${mc.body.version}, client version: ${merchantManager.PROTOCOL_VERSION}`, + ); + return; + } + + /** + * create bank account + */ + let accountPayto: PaytoString; + { + const resp = await bank.createAccount(bankAdminPassword, { + name: name, + password: password, + username: id, + contact_data: email || phone ? { + email: email, + phone: phone, + } : undefined, + }) + + if (resp.type === "fail") { + logger.error(`unable to provision bank account, HTTP response status ${resp.case}`); + process.exit(2); + } + logger.info(`account ${id} successfully provisioned`); + accountPayto = resp.body.internal_payto_uri + } + + /** + * create merchant account + */ + { + const resp = await merchantManager.createInstance(managementToken, { + address: {}, + auth: { + method: "token", + token: `secret-token:${password}`, + }, + default_pay_delay: Duration.toTalerProtocolDuration( + Duration.fromSpec({ hours: 1 }), + ), + default_wire_transfer_delay: Duration.toTalerProtocolDuration( + Duration.fromSpec({ hours: 1 }), + ), + id: id, + jurisdiction: {}, + name: name, + use_stefan: true, + }) + + if (resp.type === "ok") { + logger.info(`instance ${id} created successfully`); + } else if (resp.case === HttpStatusCode.Conflict) { + logger.info(`instance ${id} already exists`); + } else { + logger.error( + `unable to create instance ${id}, HTTP status ${resp.case}`, + ); + process.exit(2); + } + } + + let wireAccount: string; + /** + * link bank account and merchant + */ + { + const resp = await merchantInstance.addAccount(password as AccessToken, { + payto_uri: accountPayto, + credit_facade_url: bank.getRevenueAPI(id).href, + credit_facade_credentials: { + type: "basic", + username: id, + password: password, + } + }) + if (resp.type === "fail") { + console.error(`unable to configure bank account for instance ${id}, status ${resp.case}`) + console.error(j2s(resp.detail)); + process.exit(2); + } + wireAccount = resp.body.h_wire + } + + logger.info(`successfully configured bank account for ${id}`); + + let templateURI; + /** + * create template + */ + if (args.provisionBankMerchant.template) { + let currency = bc.body.currency; + if (bc.body.allow_conversion) { + const cc = await conv.getConfig(); + if (cc.type === "ok") { + currency = cc.body.fiat_currency + } else { + console.error( + `could not get fiat currency status ${cc.case}`, + ); + console.error(j2s(cc.detail)); + } + } else { + console.log(`conversion is disabled, using bank currency`) + } + + { + const resp = await merchantInstance.addTemplate(password as AccessToken, { + template_id: "default", + template_description: "First template", + template_contract: { + pay_duration: Duration.toTalerProtocolDuration( + Duration.fromSpec({ hours: 1 }), + ), + minimum_age: 0, + currency, + summary: "Pay me!" + } + }) + if (resp.type === "fail") { + console.error(`unable to create template for insntaince ${id}, status ${resp.case}`) + console.error(j2s(resp.detail)); + process.exit(2); + } + } + + logger.info(`template default successfully created`); + templateURI = stringifyPayTemplateUri({ + merchantBaseUrl: instanceURL, + templateId: "default", + templateParams: { + amount: currency + } + }) + } + + let finalPassword = password; + if (args.provisionBankMerchant.randomPassword) { + const prevPassword = password as AccessToken + const randomPassword = encodeCrock(randomBytes(16)); + logger.info("random password: ", randomPassword) + let token: AccessToken; + { + const resp = await bankAuth.createAccessTokenBasic(id, prevPassword, { + scope: "readwrite", + duration: Duration.toTalerProtocolDuration(Duration.fromSpec({ minutes: 1 })), + refreshable: false, + }) + if (resp.type === "fail") { + console.error(`unable to login into bank accountfor user ${id}, status ${resp.case}`) + console.error(j2s(resp.detail)); + process.exit(2); + } + token = resp.body.access_token; + } + + { + const resp = await bank.updatePassword({ username: id, token }, { + old_password: prevPassword, + new_password: randomPassword, + }); + if (resp.type === "fail") { + console.error(`unable to change bank pasword for user ${id}, status ${resp.case}`) + if (resp.case !== HttpStatusCode.Accepted) { + console.error(j2s(resp.detail)); + } else { + console.error("2FA required") + } + process.exit(2); + } + } + + { + const resp = await merchantInstance.updateCurrentInstanceAuthentication(prevPassword, { + method: "token", + token: `secret-token:${randomPassword}` as AccessToken + }) + if (resp.type === "fail") { + console.error(`unable to change merchant password for instance ${id}, status ${resp.case}`) + console.error(j2s(resp.detail)); + process.exit(2); + } + } + + { + const resp = await merchantInstance.updateAccount(randomPassword as AccessToken, wireAccount, { + credit_facade_url: bank.getRevenueAPI(id).href, + credit_facade_credentials: { + type: "basic", + username: id, + password: randomPassword, + } + }) + if (resp.type != "ok") { + console.error( + `unable to update bank account for instance ${id}, status ${resp.case}`, + ); + console.error(j2s(resp.detail)); + process.exit(2); + } + } + finalPassword = randomPassword; + } + logger.info(`successfully configured bank account for ${id}`); + + /** + * show result + */ + console.log(JSON.stringify({ + bankUser: id, + bankURL: args.provisionBankMerchant.corebankApiBaseUrl, + merchantURL: instanceURL, + templateURI, + password: finalPassword, + }, undefined, 2)) + + }); + + deploymentCli .subcommand("provisionMerchantInstance", "provision-merchant-instance", { help: "Provision a merchant backend instance.", @@ -595,17 +891,27 @@ deploymentCli .requiredOption("name", ["--name"], clk.STRING) .requiredOption("id", ["--id"], clk.STRING) .requiredOption("payto", ["--payto"], clk.STRING) + .maybeOption("bankURL", ["--bankURL"], clk.STRING) + .maybeOption("bankUser", ["--bankUser"], clk.STRING) + .maybeOption("bankPassword", ["--bankPassword"], clk.STRING) .action(async (args) => { - const httpLib = createPlatformHttpLib(); + const httpLib = createPlatformHttpLib({}); const baseUrl = args.provisionMerchantInstance.merchantApiBaseUrl; - const managementToken = args.provisionMerchantInstance.managementToken; - const instanceToken = args.provisionMerchantInstance.instanceToken; + const api = new TalerMerchantManagementHttpClient(baseUrl, httpLib) + const managementToken = args.provisionMerchantInstance.managementToken as AccessToken; + const instanceToken = args.provisionMerchantInstance.instanceToken as AccessToken; const instanceId = args.provisionMerchantInstance.id; - const body: MerchantInstanceConfig = { + const instancceName = args.provisionMerchantInstance.name; + const bankURL = args.provisionMerchantInstance.bankURL; + const bankUser = args.provisionMerchantInstance.bankUser; + const bankPassword = args.provisionMerchantInstance.bankPassword; + const accountPayto = args.provisionMerchantInstance.payto as PaytoString; + + const createResp = await api.createInstance(managementToken, { address: {}, auth: { method: "token", - token: args.provisionMerchantInstance.instanceToken, + token: `secret-token:${instanceToken}`, }, default_pay_delay: Duration.toTalerProtocolDuration( Duration.fromSpec({ hours: 1 }), @@ -613,48 +919,35 @@ deploymentCli default_wire_transfer_delay: { d_us: 1 }, id: instanceId, jurisdiction: {}, - name: args.provisionMerchantInstance.name, + name: instancceName, use_stefan: true, - }; - const url = new URL("management/instances", baseUrl); - const createResp = await httpLib.fetch(url.href, { - method: "POST", - body, - headers: { - Authorization: `Bearer ${managementToken}`, - }, - }); - if (createResp.status >= 200 && createResp.status <= 299) { + }) + + if (createResp.type === "ok") { logger.info(`instance ${instanceId} created successfully`); - } else if (createResp.status === HttpStatusCode.Conflict) { + } else if (createResp.case === HttpStatusCode.Conflict) { logger.info(`instance ${instanceId} already exists`); } else { logger.error( - `unable to create instance ${instanceId}, HTTP status ${createResp.status}`, + `unable to create instance ${instanceId}, HTTP status ${createResp.case}`, ); process.exit(2); } - const accountsUrl = new URL( - `instances/${instanceId}/private/accounts`, - baseUrl, - ); - const accountBody = { - payto_uri: args.provisionMerchantInstance.payto, - }; - const createAccountResp = await httpLib.fetch(accountsUrl.href, { - method: "POST", - body: accountBody, - headers: { - Authorization: `Bearer ${instanceToken}`, - }, - }); - if (createAccountResp.status != 200) { + const createAccountResp = await api.addAccount(instanceToken, { + payto_uri: accountPayto, + credit_facade_url: bankURL, + credit_facade_credentials: bankUser && bankPassword ? { + type: "basic", + username: bankUser, + password: bankPassword, + } : undefined + }) + if (createAccountResp.type != "ok") { console.error( - `unable to configure bank account for instance ${instanceId}, status ${createAccountResp.status}`, + `unable to configure bank account for instance ${instanceId}, status ${createAccountResp.case}`, ); - const resp = await createAccountResp.json(); - console.error(j2s(resp)); + console.error(j2s(createAccountResp.detail)); process.exit(2); } logger.info(`successfully configured bank account for ${instanceId}`); @@ -673,31 +966,25 @@ deploymentCli .maybeOption("internalPayto", ["--payto"], clk.STRING) .action(async (args) => { const httpLib = createPlatformHttpLib(); - const corebankApiBaseUrl = args.provisionBankAccount.corebankApiBaseUrl; - const url = new URL("accounts", corebankApiBaseUrl); + const baseUrl = args.provisionBankAccount.corebankApiBaseUrl; + const api = new TalerCoreBankHttpClient(baseUrl, httpLib); + const accountLogin = args.provisionBankAccount.login; - const body: RegisterAccountRequest = { + const resp = await api.createAccount(undefined, { name: args.provisionBankAccount.name, password: args.provisionBankAccount.password, username: accountLogin, is_public: !!args.provisionBankAccount.public, is_taler_exchange: !!args.provisionBankAccount.exchange, - payto_uri: args.provisionBankAccount.internalPayto, - }; - const resp = await httpLib.fetch(url.href, { - method: "POST", - body, - }); - if (resp.status >= 200 && resp.status <= 299) { + payto_uri: args.provisionBankAccount.internalPayto as PaytoString, + }) + + if (resp.type === "ok") { logger.info(`account ${accountLogin} successfully provisioned`); return; } - if (resp.status === HttpStatusCode.Conflict) { - logger.info(`account ${accountLogin} already provisioned`); - return; - } logger.error( - `unable to provision bank account, HTTP response status ${resp.status}`, + `unable to provision bank account, HTTP response status ${resp.case}`, ); process.exit(2); }); diff --git a/packages/taler-util/src/http-client/authentication.ts b/packages/taler-util/src/http-client/authentication.ts index 00ef21a06..f77df2ed0 100644 --- a/packages/taler-util/src/http-client/authentication.ts +++ b/packages/taler-util/src/http-client/authentication.ts @@ -45,7 +45,6 @@ export class TalerAuthenticationHttpClient { constructor( readonly baseUrl: string, - readonly username: string, httpClient?: HttpRequestLibrary, ) { this.httpLib = httpClient ?? createPlatformHttpLib(); @@ -61,7 +60,8 @@ export class TalerAuthenticationHttpClient { * * @returns */ - async createAccessToken( + async createAccessTokenBasic( + username: string, password: string, body: TalerAuthentication.TokenRequest, ) { @@ -69,7 +69,7 @@ export class TalerAuthenticationHttpClient { const resp = await this.httpLib.fetch(url.href, { method: "POST", headers: { - Authorization: makeBasicAuthHeader(this.username, password), + Authorization: makeBasicAuthHeader(username, password), }, body, }); @@ -90,15 +90,15 @@ export class TalerAuthenticationHttpClient { * * @returns */ - async createAccessTokenMerchant( - password: string, + async createAccessTokenBearer( + token: string, body: TalerAuthentication.TokenRequest, ) { const url = new URL(`token`, this.baseUrl); const resp = await this.httpLib.fetch(url.href, { method: "POST", headers: { - Authorization: makeBearerTokenAuthHeader(password as AccessToken), + Authorization: makeBearerTokenAuthHeader(token as AccessToken), }, body, }); diff --git a/packages/taler-util/src/http-client/bank-core.ts b/packages/taler-util/src/http-client/bank-core.ts index b544d56fe..7a98b6281 100644 --- a/packages/taler-util/src/http-client/bank-core.ts +++ b/packages/taler-util/src/http-client/bank-core.ts @@ -140,16 +140,18 @@ export class TalerCoreBankHttpClient { * */ async createAccount( - auth: AccessToken, + auth: AccessToken | undefined, body: TalerCorebankApi.RegisterAccountRequest, ) { const url = new URL(`accounts`, this.baseUrl); + const headers: Record = {} + if (auth) { + headers.Authorization = makeBearerTokenAuthHeader(auth) + } const resp = await this.httpLib.fetch(url.href, { method: "POST", body, - headers: { - Authorization: makeBearerTokenAuthHeader(auth), - }, + headers: headers, }); switch (resp.status) { case HttpStatusCode.Ok: { diff --git a/packages/taler-util/src/http-client/merchant.ts b/packages/taler-util/src/http-client/merchant.ts index 1b1a7b4a8..394625e38 100644 --- a/packages/taler-util/src/http-client/merchant.ts +++ b/packages/taler-util/src/http-client/merchant.ts @@ -354,10 +354,11 @@ export class TalerMerchantInstanceHttpClient { headers, }); - // switch (resp.status) { case HttpStatusCode.Ok: return opEmptySuccess(resp); + case HttpStatusCode.NoContent: // FIXME: missing in docs + return opEmptySuccess(resp); case HttpStatusCode.NotFound: return opKnownHttpFailure(resp.status, resp); default: @@ -1778,9 +1779,10 @@ export class TalerMerchantManagementHttpClient extends TalerMerchantInstanceHttp */ async updateInstanceAuthentication( token: AccessToken | undefined, + instanceId: string, body: TalerMerchantApi.InstanceAuthConfigurationMessage, ) { - const url = new URL(`management/instances`, this.baseUrl); + const url = new URL(`management/instances/${instanceId}/auth`, this.baseUrl); const headers: Record = {} if (token) { diff --git a/packages/taler-util/src/http-common.ts b/packages/taler-util/src/http-common.ts index 3973e66fb..cc75debd5 100644 --- a/packages/taler-util/src/http-common.ts +++ b/packages/taler-util/src/http-common.ts @@ -440,6 +440,7 @@ export interface HttpLibArgs { * Only allow HTTPS connections, not plain http. */ requireTls?: boolean; + printAsCurl?: boolean; } export function encodeBody(body: any): ArrayBuffer { diff --git a/packages/taler-util/src/http-impl.node.ts b/packages/taler-util/src/http-impl.node.ts index b5c87843f..8606bc451 100644 --- a/packages/taler-util/src/http-impl.node.ts +++ b/packages/taler-util/src/http-impl.node.ts @@ -119,8 +119,13 @@ export class HttpLibImpl implements HttpRequestLibrary { timeoutMs = DEFAULT_REQUEST_TIMEOUT_MS; } - const requestHeadersMap = { ...getDefaultHeaders(method), ...opt?.headers }; - + const requestHeadersMap = getDefaultHeaders(method); + if (opt?.headers) { + Object.entries(opt?.headers).forEach(([key, value]) => { + if (value === undefined) return; + requestHeadersMap[key] = value + }) + } logger.trace(`request timeout ${timeoutMs} ms`); let reqBody: ArrayBuffer | undefined; diff --git a/packages/taler-util/src/http-impl.qtart.ts b/packages/taler-util/src/http-impl.qtart.ts index 0be9f2c23..b4e4ebbe7 100644 --- a/packages/taler-util/src/http-impl.qtart.ts +++ b/packages/taler-util/src/http-impl.qtart.ts @@ -98,7 +98,13 @@ export class HttpLibImpl implements HttpRequestLibrary { } let data: ArrayBuffer | undefined = undefined; - const requestHeadersMap = { ...getDefaultHeaders(method), ...opt?.headers }; + const requestHeadersMap = getDefaultHeaders(method); + if (opt?.headers) { + Object.entries(opt?.headers).forEach(([key, value]) => { + if (value === undefined) return; + requestHeadersMap[key] = value + }) + } let headersList: string[] = []; for (let headerName of Object.keys(requestHeadersMap)) { headersList.push(`${headerName}: ${requestHeadersMap[headerName]}`); diff --git a/packages/web-util/src/context/bank-api.ts b/packages/web-util/src/context/bank-api.ts index 645eda183..bd0653451 100644 --- a/packages/web-util/src/context/bank-api.ts +++ b/packages/web-util/src/context/bank-api.ts @@ -167,7 +167,6 @@ function buildBankApiClient(url: URL, evictors: Evictors, const auth = (user: string) => new TalerAuthenticationHttpClient( bank.getAuthenticationAPI(user).href, - user, httpLib, ); diff --git a/packages/web-util/src/context/merchant-api.ts b/packages/web-util/src/context/merchant-api.ts index 79c79ee9c..a531a5958 100644 --- a/packages/web-util/src/context/merchant-api.ts +++ b/packages/web-util/src/context/merchant-api.ts @@ -183,13 +183,11 @@ function buildMerchantApiClient( ); const authenticate = new TalerAuthenticationHttpClient( management.getAuthenticationAPI().href, - "default", httpLib, ); const impersonate = (instanceId: string) => new TalerAuthenticationHttpClient( instance(instanceId).getAuthenticationAPI().href, - instanceId, httpLib, ); -- cgit v1.2.3