commit 9cf48c76a0d15bb4ca3acdb944d5d40c324428f4
parent d8687409fb60842079eb19fde79a48fb9759f9b1
Author: Christian Grothoff <christian@grothoff.org>
Date: Wed, 10 Jun 2026 18:08:47 +0200
fix #11213
Diffstat:
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