commit e5b2109c9d3996a849cb464e857b01254d1f7f18
parent e36697ce0c17ee364225901697700b70f2ef6a08
Author: Christian Grothoff <christian@grothoff.org>
Date: Wed, 5 Aug 2026 22:33:11 +0200
fail on update if OTP device does not exist instead of silently unlinking
Diffstat:
2 files changed, 64 insertions(+), 0 deletions(-)
diff --git a/src/backend/taler-merchant-httpd_patch-private-templates-TEMPLATE_ID.c b/src/backend/taler-merchant-httpd_patch-private-templates-TEMPLATE_ID.c
@@ -26,6 +26,7 @@
#include "taler-merchant-httpd_patch-private-templates-TEMPLATE_ID.h"
#include "taler-merchant-httpd_helper.h"
#include <taler/taler_json_lib.h>
+#include "merchant-database/get_otp_device_serial.h"
#include "merchant-database/update_template.h"
@@ -115,6 +116,38 @@ TMH_private_patch_templates_ID (const struct TMH_RequestHandler *rh,
}
}
+ if (NULL != tp.otp_id)
+ {
+ uint64_t otp_serial;
+
+ /* Note: without this pre-check, an unknown @e otp_id would
+ simply resolve to NULL in the UPDATE and thus *silently*
+ remove the OTP device binding of the template. */
+ qs = TALER_MERCHANTDB_get_otp_device_serial (TMH_db,
+ mi->settings.id,
+ tp.otp_id,
+ &otp_serial);
+ switch (qs)
+ {
+ case GNUNET_DB_STATUS_HARD_ERROR:
+ case GNUNET_DB_STATUS_SOFT_ERROR:
+ GNUNET_break (0);
+ GNUNET_JSON_parse_free (spec);
+ return TALER_MHD_reply_with_error (connection,
+ MHD_HTTP_INTERNAL_SERVER_ERROR,
+ TALER_EC_GENERIC_DB_FETCH_FAILED,
+ "get_otp_device_serial");
+ case GNUNET_DB_STATUS_SUCCESS_NO_RESULTS:
+ GNUNET_JSON_parse_free (spec);
+ return TALER_MHD_reply_with_error (connection,
+ MHD_HTTP_NOT_FOUND,
+ TALER_EC_MERCHANT_GENERIC_OTP_DEVICE_UNKNOWN,
+ tp.otp_id);
+ case GNUNET_DB_STATUS_SUCCESS_ONE_RESULT:
+ break;
+ }
+ }
+
qs = TALER_MERCHANTDB_update_template (TMH_db,
mi->settings.id,
template_id,
diff --git a/src/testing/test_merchant_api.c b/src/testing/test_merchant_api.c
@@ -1819,6 +1819,37 @@ run (void *cls,
GNUNET_JSON_pack_time_rel ("pay_duration",
GNUNET_TIME_UNIT_MINUTES)),
MHD_HTTP_NOT_FOUND),
+ /* Bind template-2 to an existing OTP device ... */
+ TALER_TESTING_cmd_merchant_patch_template (
+ "patch-templates-t2-otp",
+ merchant_url,
+ "template-2",
+ "a template with an OTP device",
+ "otp-dev",
+ GNUNET_JSON_PACK (
+ GNUNET_JSON_pack_uint64 ("minimum_age", 0),
+ GNUNET_JSON_pack_time_rel ("pay_duration",
+ GNUNET_TIME_UNIT_MINUTES)),
+ MHD_HTTP_NO_CONTENT),
+ /* ... a PATCH naming an unknown OTP device must be refused
+ with 404 and must NOT clear the existing binding (#5) */
+ TALER_TESTING_cmd_merchant_patch_template (
+ "patch-templates-t2-otp-nx",
+ merchant_url,
+ "template-2",
+ "a template that must not be stored",
+ "otp-dev-nx",
+ GNUNET_JSON_PACK (
+ GNUNET_JSON_pack_uint64 ("minimum_age", 0),
+ GNUNET_JSON_pack_time_rel ("pay_duration",
+ GNUNET_TIME_UNIT_MINUTES)),
+ MHD_HTTP_NOT_FOUND),
+ /* ... hence the template must still match the successful PATCH */
+ TALER_TESTING_cmd_merchant_get_template ("get-template-t2-otp",
+ merchant_url,
+ "template-2",
+ MHD_HTTP_OK,
+ "patch-templates-t2-otp"),
TALER_TESTING_cmd_merchant_post_templates2 (
"post-templates-t3-amount",
merchant_url,