merchant

Merchant backend to process payments, run by merchants
Log | Files | Refs | Submodules | README | LICENSE

commit 9cf48c76a0d15bb4ca3acdb944d5d40c324428f4
parent d8687409fb60842079eb19fde79a48fb9759f9b1
Author: Christian Grothoff <christian@grothoff.org>
Date:   Wed, 10 Jun 2026 18:08:47 +0200

fix #11213

Diffstat:
Msrc/backend/taler-merchant-httpd_mfa.c | 9++++++++-
Msrc/backend/taler-merchant-httpd_mfa.h | 2++
Msrc/backend/taler-merchant-httpd_patch-management-instances-INSTANCE.c | 3+++
Msrc/backend/taler-merchant-httpd_post-management-instances-INSTANCE-auth.c | 3+++
Msrc/backend/taler-merchant-httpd_post-management-instances.c | 3+++
Msrc/backenddb/create_mfa_challenge.c | 10+++-------
Msrc/backenddb/lookup_mfa_challenge.c | 22++++++++++------------
Asrc/backenddb/sql-schema/merchant-0039.sql | 36++++++++++++++++++++++++++++++++++++
Msrc/backenddb/sql-schema/meson.build | 1+
Msrc/include/merchant-database/create_mfa_challenge.h | 29+++++++++++++++--------------
10 files changed, 84 insertions(+), 34 deletions(-)

diff --git a/src/backend/taler-merchant-httpd_mfa.c b/src/backend/taler-merchant-httpd_mfa.c @@ -228,6 +228,7 @@ mfa_challenge_check ( * is returned to the client of @a hc. * * @param[in,out] hc handler context of the connection to authorize + * @param instance_name instance name to use in the message to the customer * @param op operation for which we are performing * @param channel TAN channel to try * @param expiration_date when should the challenge expire @@ -242,6 +243,7 @@ mfa_challenge_check ( static enum GNUNET_GenericReturnValue mfa_challenge_start ( struct TMH_HandlerContext *hc, + const char *instance_name, enum TALER_MERCHANT_MFA_CriticalOperation op, enum TALER_MERCHANT_MFA_Channel channel, struct GNUNET_TIME_Absolute expiration_date, @@ -273,6 +275,7 @@ mfa_challenge_start ( challenge_num / 10000, challenge_num % 10000); qs = TALER_MERCHANTDB_create_mfa_challenge (TMH_db, + instance_name, op, &h_body, &salt, @@ -281,7 +284,6 @@ mfa_challenge_start ( GNUNET_TIME_UNIT_ZERO_ABS, channel, required_address, - hc->instance->settings.id, &challenge_serial); GNUNET_free (code); switch (qs) @@ -414,6 +416,7 @@ get_hint (enum TALER_MERCHANT_MFA_Channel channel, * client for the request in @a hc. * * @param[in,out] hc handler context with the connection to the client + * @param instance_name instance name to use in the message to the customer * @param op operation for which we should check challenges for * @param combi_and true to tell the client to solve all challenges (AND), * false means that any of the challenges will do (OR) @@ -426,6 +429,7 @@ get_hint (enum TALER_MERCHANT_MFA_Channel channel, enum GNUNET_GenericReturnValue TMH_mfa_challenges_do ( struct TMH_HandlerContext *hc, + const char *instance_name, enum TALER_MERCHANT_MFA_CriticalOperation op, bool combi_and, ...) @@ -562,6 +566,7 @@ TMH_mfa_challenges_do ( GNUNET_assert (! challenges[i].solved); GNUNET_assert (challenges[i].solvable); ret = mfa_challenge_start (hc, + instance_name, op, challenges[i].channel, expiration_date, @@ -686,6 +691,7 @@ TMH_mfa_check_simple ( if (have_email) { ret = TMH_mfa_challenges_do (hc, + mi->settings.id, op, false, TALER_MERCHANT_MFA_CHANNEL_EMAIL, @@ -699,6 +705,7 @@ TMH_mfa_check_simple ( else if (have_sms) { ret = TMH_mfa_challenges_do (hc, + mi->settings.id, op, false, TALER_MERCHANT_MFA_CHANNEL_SMS, diff --git a/src/backend/taler-merchant-httpd_mfa.h b/src/backend/taler-merchant-httpd_mfa.h @@ -52,6 +52,7 @@ TMH_mfa_parse_challenge_id (struct TMH_HandlerContext *hc, * client for the request in @a hc. * * @param[in,out] hc handler context with the connection to the client + * @param instance_name instance name to use in the message to the customer * @param op operation for which we should check challenges for * @param combi_and true to tell the client to solve all challenges (AND), * false means that any of the challenges will do (OR) @@ -64,6 +65,7 @@ TMH_mfa_parse_challenge_id (struct TMH_HandlerContext *hc, enum GNUNET_GenericReturnValue TMH_mfa_challenges_do ( struct TMH_HandlerContext *hc, + const char *instance_name, enum TALER_MERCHANT_MFA_CriticalOperation op, bool combi_and, ...); diff --git a/src/backend/taler-merchant-httpd_patch-management-instances-INSTANCE.c b/src/backend/taler-merchant-httpd_patch-management-instances-INSTANCE.c @@ -299,6 +299,7 @@ patch_instances_ID (struct TMH_MerchantInstance *mi, /* validate new phone number, if possible require old e-mail address for authorization */ ret = TMH_mfa_challenges_do (hc, + mi->settings.id, TALER_MERCHANT_MFA_CO_ACCOUNT_CONFIGURATION, true, TALER_MERCHANT_MFA_CHANNEL_SMS, @@ -316,6 +317,7 @@ patch_instances_ID (struct TMH_MerchantInstance *mi, /* validate new e-mail address, if possible require old phone address for authorization */ ret = TMH_mfa_challenges_do (hc, + mi->settings.id, TALER_MERCHANT_MFA_CO_ACCOUNT_CONFIGURATION, true, TALER_MERCHANT_MFA_CHANNEL_EMAIL, @@ -335,6 +337,7 @@ patch_instances_ID (struct TMH_MerchantInstance *mi, /* To change both, we require both old and both new addresses to consent */ ret = TMH_mfa_challenges_do (hc, + mi->settings.id, TALER_MERCHANT_MFA_CO_ACCOUNT_CONFIGURATION, true, TALER_MERCHANT_MFA_CHANNEL_EMAIL, diff --git a/src/backend/taler-merchant-httpd_post-management-instances-INSTANCE-auth.c b/src/backend/taler-merchant-httpd_post-management-instances-INSTANCE-auth.c @@ -110,6 +110,7 @@ post_instances_ID_auth (struct TMH_MerchantInstance *mi, break; case TMH_TCS_SMS: ret = TMH_mfa_challenges_do (hc, + mi->settings.id, TALER_MERCHANT_MFA_CO_AUTH_CONFIGURATION, true, TALER_MERCHANT_MFA_CHANNEL_SMS, @@ -118,6 +119,7 @@ post_instances_ID_auth (struct TMH_MerchantInstance *mi, break; case TMH_TCS_EMAIL: ret = TMH_mfa_challenges_do (hc, + mi->settings.id, TALER_MERCHANT_MFA_CO_AUTH_CONFIGURATION, true, TALER_MERCHANT_MFA_CHANNEL_EMAIL, @@ -126,6 +128,7 @@ post_instances_ID_auth (struct TMH_MerchantInstance *mi, break; case TMH_TCS_EMAIL_AND_SMS: ret = TMH_mfa_challenges_do (hc, + mi->settings.id, TALER_MERCHANT_MFA_CO_AUTH_CONFIGURATION, true, TALER_MERCHANT_MFA_CHANNEL_EMAIL, diff --git a/src/backend/taler-merchant-httpd_post-management-instances.c b/src/backend/taler-merchant-httpd_post-management-instances.c @@ -400,6 +400,7 @@ post_instances (const struct TMH_RequestHandler *rh, case TMH_TCS_SMS: is.phone_validated = true; ret = TMH_mfa_challenges_do (hc, + is.id, TALER_MERCHANT_MFA_CO_INSTANCE_PROVISION, true, TALER_MERCHANT_MFA_CHANNEL_SMS, @@ -409,6 +410,7 @@ post_instances (const struct TMH_RequestHandler *rh, case TMH_TCS_EMAIL: is.email_validated = true; ret = TMH_mfa_challenges_do (hc, + is.id, TALER_MERCHANT_MFA_CO_INSTANCE_PROVISION, true, TALER_MERCHANT_MFA_CHANNEL_EMAIL, @@ -419,6 +421,7 @@ post_instances (const struct TMH_RequestHandler *rh, is.phone_validated = true; is.email_validated = true; ret = TMH_mfa_challenges_do (hc, + is.id, TALER_MERCHANT_MFA_CO_INSTANCE_PROVISION, true, TALER_MERCHANT_MFA_CHANNEL_EMAIL, diff --git a/src/backenddb/create_mfa_challenge.c b/src/backenddb/create_mfa_challenge.c @@ -29,6 +29,7 @@ enum GNUNET_DB_QueryStatus TALER_MERCHANTDB_create_mfa_challenge ( struct TALER_MERCHANTDB_PostgresContext *pg, + const char *instance_name, enum TALER_MERCHANT_MFA_CriticalOperation op, const struct TALER_MERCHANT_MFA_BodyHash *h_body, const struct TALER_MERCHANT_MFA_BodySalt *salt, @@ -37,7 +38,6 @@ TALER_MERCHANTDB_create_mfa_challenge ( struct GNUNET_TIME_Absolute retransmission_date, enum TALER_MERCHANT_MFA_Channel tan_channel, const char *required_address, - const char *instance_id, uint64_t *challenge_id) { struct GNUNET_TIME_Absolute now = GNUNET_TIME_absolute_get (); @@ -53,7 +53,7 @@ TALER_MERCHANTDB_create_mfa_challenge ( GNUNET_PQ_query_param_absolute_time (&retransmission_date), GNUNET_PQ_query_param_string (channel_str), GNUNET_PQ_query_param_string (required_address), /* $9 */ - GNUNET_PQ_query_param_uint64 (&pg->current_merchant_serial), /* $10 */ + GNUNET_PQ_query_param_string (instance_name), GNUNET_PQ_query_param_end }; struct GNUNET_PQ_ResultSpec rs[] = { @@ -62,10 +62,6 @@ TALER_MERCHANTDB_create_mfa_challenge ( GNUNET_PQ_result_spec_end }; - GNUNET_assert (NULL != pg->current_merchant_id); - GNUNET_assert (0 == strcmp (instance_id, - pg->current_merchant_id)); - GNUNET_assert (0 != pg->current_merchant_serial); PREPARE (pg, "create_mfa_challenge", "INSERT INTO merchant.tan_challenges" @@ -79,7 +75,7 @@ TALER_MERCHANTDB_create_mfa_challenge ( " ,retry_counter" /* always set to 3 */ " ,tan_channel" " ,required_address" - " ,merchant_serial)" + " ,instance_name)" " VALUES" " ($1, $2, $3, $4, $5, $6, $7, 3, $8, $9, $10)" " RETURNING challenge_id;"); diff --git a/src/backenddb/lookup_mfa_challenge.c b/src/backenddb/lookup_mfa_challenge.c @@ -67,7 +67,7 @@ TALER_MERCHANTDB_lookup_mfa_challenge ( &chan_str), GNUNET_PQ_result_spec_string ("required_address", required_address), - GNUNET_PQ_result_spec_string ("merchant_id", + GNUNET_PQ_result_spec_string ("instance_name", instance_name), GNUNET_PQ_result_spec_end }; @@ -76,17 +76,15 @@ TALER_MERCHANTDB_lookup_mfa_challenge ( PREPARE (pg, "lookup_mfa_challenge", "SELECT " - " tc.op::TEXT" - " ,tc.salt" - " ,tc.confirmation_date" - " ,tc.retransmission_date" - " ,tc.retry_counter" - " ,tc.required_address" - " ,tc.tan_channel::TEXT" - " ,mi.merchant_id" - " FROM merchant.tan_challenges tc" - " JOIN merchant.merchant_instances mi" - " USING (merchant_serial)" + " op::TEXT" + " ,salt" + " ,confirmation_date" + " ,retransmission_date" + " ,retry_counter" + " ,required_address" + " ,tan_channel::TEXT" + " ,instance_name" + " FROM merchant.tan_challenges" " WHERE (challenge_id = $1)" " AND (h_body = $2)" " AND (expiration_date > $3)"); diff --git a/src/backenddb/sql-schema/merchant-0039.sql b/src/backenddb/sql-schema/merchant-0039.sql @@ -0,0 +1,36 @@ +-- +-- This file is part of TALER +-- Copyright (C) 2026 Taler Systems SA +-- +-- 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. +-- +-- 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 +-- TALER; see the file COPYING. If not, see <http://www.gnu.org/licenses/> + +-- @file merchant-0039.sql +-- @brief change tan_challenges.merchant_serial to instance_name::TEXT +-- @author Christian Grothoff + +BEGIN; + +SELECT _v.register_patch('merchant-0039', NULL, NULL); + +SET search_path TO merchant; + +-- void all existing challenges, migration is not possible +-- and unnecessary, just breaks attempts crossing migration. +DELETE FROM tan_challenges; +ALTER TABLE tan_challenges + DROP COLUMN merchant_serial, + ADD COLUMN instance_name TEXT NOT NULL; + +COMMENT ON COLUMN tan_challenges.instance_name + IS 'name of the instance impacted by the operation; note that the instance may not yet exist if it is being created, so this is not a foreign key'; + +COMMIT; diff --git a/src/backenddb/sql-schema/meson.build b/src/backenddb/sql-schema/meson.build @@ -113,6 +113,7 @@ generated_sql = [ ['merchant-0035.sql'], ['merchant-0037.sql'], ['merchant-0038.sql'], + ['merchant-0039.sql'], ] foreach g : generated_sql diff --git a/src/include/merchant-database/create_mfa_challenge.h b/src/include/merchant-database/create_mfa_challenge.h @@ -26,11 +26,13 @@ #include "merchantdb_lib.h" -struct TALER_MERCHANTDB_PostgresContext; /** * Create new multi-factor authorization (MFA) challenge in the database. * * @param pg database context + * @param instance_name name of the instance impacted by the challenge; + * used when communicating with the user; instance does not yet + * need to exist (as it might be we are creating it!) * @param op operation that triggered the MFA request * @param h_body hash of the request body * @param salt salt used to compute @a h_body @@ -41,22 +43,21 @@ struct TALER_MERCHANTDB_PostgresContext; * @param tan_channel which channel was used * @param required_address address * where the challenge is to be sent - * @param instance_id name of the instance for which the challenge - * is being created * @param[out] challenge_id set to the ID of the new challenge * @return database result code */ enum GNUNET_DB_QueryStatus -TALER_MERCHANTDB_create_mfa_challenge (struct TALER_MERCHANTDB_PostgresContext *pg, - enum TALER_MERCHANT_MFA_CriticalOperation op, - const struct TALER_MERCHANT_MFA_BodyHash *h_body, - const struct TALER_MERCHANT_MFA_BodySalt *salt, - const char *code, - struct GNUNET_TIME_Absolute expiration_date, - struct GNUNET_TIME_Absolute retransmission_date, - enum TALER_MERCHANT_MFA_Channel tan_channel, - const char *required_address, - const char *instance_id, - uint64_t *challenge_id); +TALER_MERCHANTDB_create_mfa_challenge ( + struct TALER_MERCHANTDB_PostgresContext *pg, + const char *instance_name, + enum TALER_MERCHANT_MFA_CriticalOperation op, + const struct TALER_MERCHANT_MFA_BodyHash *h_body, + const struct TALER_MERCHANT_MFA_BodySalt *salt, + const char *code, + struct GNUNET_TIME_Absolute expiration_date, + struct GNUNET_TIME_Absolute retransmission_date, + enum TALER_MERCHANT_MFA_Channel tan_channel, + const char *required_address, + uint64_t *challenge_id); #endif