merchant

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

commit c983fb533c9a1ad5ab780b932c52a176994c08e1
parent a1cf5ed17a55dd3bb43b97a476dd1d64c646c614
Author: Christian Grothoff <christian@grothoff.org>
Date:   Sat, 27 Dec 2025 22:02:50 +0100

changing to new auth design as discussed with Florian

Diffstat:
Msrc/backend/Makefile.am | 3+++
Msrc/backend/taler-merchant-httpd.c | 8++++++++
Msrc/backend/taler-merchant-report-generator.c | 110+++++++++++++++++++++++++++++++++++++++++++++++++------------------------------
Msrc/backenddb/Makefile.am | 1+
Msrc/backenddb/merchant-0028.sql | 3+++
Asrc/backenddb/pg_check_report.c | 68++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
Asrc/backenddb/pg_check_report.h | 51+++++++++++++++++++++++++++++++++++++++++++++++++++
Msrc/backenddb/pg_insert_report.c | 8+++++++-
Msrc/backenddb/pg_lookup_reports_pending.c | 10+++++-----
Msrc/backenddb/pg_select_report.c | 2+-
Msrc/backenddb/plugin_merchantdb_postgres.c | 3+++
Msrc/include/taler_merchant_util.h | 12++++++++++++
Msrc/include/taler_merchantdb_plugin.h | 32+++++++++++++++++++++++++++-----
13 files changed, 258 insertions(+), 53 deletions(-)

diff --git a/src/backend/Makefile.am b/src/backend/Makefile.am @@ -232,6 +232,8 @@ taler_merchant_httpd_SOURCES = \ taler-merchant-httpd_post-orders-ID-refund.h \ taler-merchant-httpd_post-using-templates.c \ taler-merchant-httpd_post-using-templates.h \ + taler-merchant-httpd_post-reports-ID.c \ + taler-merchant-httpd_post-reports-ID.h \ taler-merchant-httpd_private-get-statistics-amount-SLUG.c \ taler-merchant-httpd_private-get-statistics-amount-SLUG.h \ taler-merchant-httpd_private-get-statistics-counter-SLUG.c \ @@ -354,6 +356,7 @@ taler_merchant_report_generator_LDADD = \ $(top_builddir)/src/util/libtalermerchantutil.la \ -ltalerexchange \ -ltalerjson \ + -ltalercurl \ -ltalerutil \ -ltalerpq \ -lgnunetpq \ diff --git a/src/backend/taler-merchant-httpd.c b/src/backend/taler-merchant-httpd.c @@ -112,6 +112,7 @@ #include "taler-merchant-httpd_spa.h" #include "taler-merchant-httpd_statics.h" #include "taler-merchant-httpd_terms.h" +#include "taler-merchant-httpd_post-reports-ID.h" #include "taler-merchant-httpd_private-delete-report-ID.h" #include "taler-merchant-httpd_private-get-report-ID.h" #include "taler-merchant-httpd_private-get-reports.h" @@ -2110,6 +2111,13 @@ url_handler (void *cls, .have_id_segment = true, .handler = &TMH_return_static }, + /* POST /reports/$ID/ */ + { + .url_prefix = "/reports", + .method = MHD_HTTP_METHOD_POST, + .have_id_segment = true, + .handler = &TMH_post_reports_ID, + }, /* GET /templates/$ID/: */ { .url_prefix = "/templates/", diff --git a/src/backend/taler-merchant-report-generator.c b/src/backend/taler-merchant-report-generator.c @@ -23,6 +23,8 @@ #include <gnunet/gnunet_util_lib.h> #include <gnunet/gnunet_db_lib.h> #include <gnunet/gnunet_curl_lib.h> +#include <taler_merchant_util.h> +#include <taler/taler_curl_lib.h> #include <taler/taler_merchantdb_plugin.h> #include <taler/taler_merchantdb_lib.h> #include <taler/taler_dbevents.h> @@ -58,6 +60,11 @@ struct ReportActivity struct GNUNET_ChildWaitHandle *cwh; /** + * Minor context that holds body and headers. + */ + struct TALER_CURL_PostContext post_ctx; + + /** * CURL easy handle for the HTTP request. */ CURL *eh; @@ -68,14 +75,14 @@ struct ReportActivity struct GNUNET_CURL_Job *job; /** - * URL of the request. + * ID of the instance we are working on. */ - char *url; + char *instance_id; /** - * ID of the instance we are working on. + * URL where we request the report from. */ - char *instance_id; + char *url; /** * Report program section. @@ -192,6 +199,7 @@ free_ra (struct ReportActivity *ra) GNUNET_OS_process_destroy (ra->proc); ra->proc = NULL; } + TALER_curl_easy_post_finished (&ra->post_ctx); if (NULL != ra->eh) { curl_easy_cleanup (ra->eh); @@ -458,21 +466,18 @@ curl_completed_cb (void *cls, * * @param[in,out] ra which report activity are we working on * @param mime_type mime type to request from @a data_source - * @param data_source relative URL path to request data from + * @param report_token token to get access to the report */ static void -fetch_and_transmit (struct ReportActivity *ra, - const char *mime_type, - const char *data_source) +fetch_and_transmit ( + struct ReportActivity *ra, + const char *mime_type, + const struct TALER_MERCHANT_ReportToken *report_token) { - struct curl_slist *headers = NULL; - char *accept_header; - GNUNET_asprintf (&ra->url, - "%sinstances/%s%s", + "%sreports/%llu", base_url, - ra->instance_id, - data_source); + (unsigned long long) ra->report_id); GNUNET_log (GNUNET_ERROR_TYPE_INFO, "Fetching report from %s\n", ra->url); @@ -487,28 +492,46 @@ fetch_and_transmit (struct ReportActivity *ra, return; } - GNUNET_asprintf (&accept_header, - "Accept: %s", - mime_type); - headers = curl_slist_append (headers, - accept_header); - GNUNET_free (accept_header); + { + char *accept_header; + + GNUNET_asprintf (&accept_header, + "Accept: %s", + mime_type); + ra->post_ctx.headers = curl_slist_append (ra->post_ctx.headers, + accept_header); + GNUNET_free (accept_header); + } GNUNET_assert (CURLE_OK == curl_easy_setopt (eh, CURLOPT_URL, ra->url)); - GNUNET_assert (CURLE_OK == - curl_easy_setopt (eh, - CURLOPT_HTTPHEADER, - headers)); - // FIXME: need to set Authorization header!!! + { + json_t *req; + + req = GNUNET_JSON_PACK ( + GNUNET_JSON_pack_data_auto ("report_token", + report_token)); + if (GNUNET_OK != + TALER_curl_easy_post (&ra->post_ctx, + ra->eh, + req)) + { + GNUNET_break (0); + json_decref (req); + finish_transmission (ra, + TALER_EC_GENERIC_CURL_ALLOCATION_FAILURE, + "TALER_curl_easy_post"); + return; + } + json_decref (req); + } ra->job = GNUNET_CURL_job_add_raw (curl_ctx, ra->eh, - headers, + ra->post_ctx.headers, &curl_completed_cb, ra); ra->eh = NULL; - curl_slist_free_all (headers); } @@ -519,25 +542,30 @@ fetch_and_transmit (struct ReportActivity *ra, * @param instance_id name of the instance * @param report_id serial number of the report * @param report_program_section configuration section of program + * for report generation * @param report_description text describing the report * @param mime_type mime type to request - * @param data_source relative URL to request report data from + * @param report_token token to authorize access to the data source * @param target_address where to send report data * @param frequency report frequency - * @param frequency_shift time shift from frequency multiple + * @param frequency_shift how much to shift the report time from a + * multiple of the report @a frequency + * @param next_transmission when is the next transmission of this report + * due */ static void -process_pending_report (void *cls, - const char *instance_id, - uint64_t report_id, - const char *report_program_section, - const char *report_description, - const char *mime_type, - const char *data_source, - const char *target_address, - struct GNUNET_TIME_Relative frequency, - struct GNUNET_TIME_Relative frequency_shift, - struct GNUNET_TIME_Absolute next_transmission) +process_pending_report ( + void *cls, + const char *instance_id, + uint64_t report_id, + const char *report_program_section, + const char *report_description, + const char *mime_type, + const struct TALER_MERCHANT_ReportToken *report_token, + const char *target_address, + struct GNUNET_TIME_Relative frequency, + struct GNUNET_TIME_Relative frequency_shift, + struct GNUNET_TIME_Absolute next_transmission) { struct GNUNET_TIME_Absolute *next = cls; struct ReportActivity *ra; @@ -572,7 +600,7 @@ process_pending_report (void *cls, ra); fetch_and_transmit (ra, mime_type, - data_source); + report_token); } diff --git a/src/backenddb/Makefile.am b/src/backenddb/Makefile.am @@ -151,6 +151,7 @@ libtaler_plugin_merchantdb_postgres_la_SOURCES = \ pg_insert_report.h pg_insert_report.c \ pg_delete_report.h pg_delete_report.c \ pg_update_report.h pg_update_report.c \ + pg_check_report.h pg_check_report.c \ pg_select_reports.h pg_select_reports.c \ pg_lookup_reports_pending.h pg_lookup_reports_pending.c \ pg_update_report_status.h pg_update_report_status.c \ diff --git a/src/backenddb/merchant-0028.sql b/src/backenddb/merchant-0028.sql @@ -31,6 +31,7 @@ CREATE TABLE merchant_reports ,report_program_section TEXT NOT NULL ,report_description TEXT NOT NULL ,mime_type TEXT NOT NULL + ,report_token BYTEA NOT NULL CHECK (LENGTH(report_token)=32) ,data_source TEXT NOT NULL ,target_address TEXT NOT NULL ,frequency INT8 NOT NULL @@ -49,6 +50,8 @@ COMMENT ON COLUMN merchant_reports.report_program_section IS 'Which helper program (configuration section) to use to transmit the report'; COMMENT ON COLUMN merchant_reports.mime_type IS 'Mime-type to request from the backend for the transmission'; +COMMENT ON COLUMN merchant_reports.report_token + IS 'Token clients requesting the report must include in the /report request'; COMMENT ON COLUMN merchant_reports.data_source IS 'Relative URL of the instance for a GET request to request data to send'; COMMENT ON COLUMN merchant_reports.target_address diff --git a/src/backenddb/pg_check_report.c b/src/backenddb/pg_check_report.c @@ -0,0 +1,68 @@ +/* + This file is part of TALER + Copyright (C) 2025 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 backenddb/pg_check_report.c + * @brief Implementation of the check_report function for Postgres + * @author Christian Grothoff + */ +#include "platform.h" +#include <taler/taler_error_codes.h> +#include <taler/taler_dbevents.h> +#include <taler/taler_pq_lib.h> +#include "pg_check_report.h" +#include "pg_helper.h" + +enum GNUNET_DB_QueryStatus +TMH_PG_check_report (void *cls, + uint64_t report_id, + const struct TALER_MERCHANT_ReportToken *report_token, + const char *mime_type, + char **instance_id, + char **data_source) +{ + struct PostgresClosure *pg = cls; + struct GNUNET_PQ_QueryParam params[] = { + GNUNET_PQ_query_param_uint64 (&report_id), + GNUNET_PQ_query_param_auto_from_type (report_token), + GNUNET_PQ_query_param_string (mime_type), + GNUNET_PQ_query_param_end + }; + struct GNUNET_PQ_ResultSpec rs[] = { + GNUNET_PQ_result_spec_string ("instance_id", + instance_id), + GNUNET_PQ_result_spec_string ("data_source", + data_source), + GNUNET_PQ_result_spec_end + }; + + check_connection (pg); + PREPARE (pg, + "check_report", + "SELECT" + " mi.merchant_id" + " ,mr.data_source" + " FROM merchant_reports mr" + " JOIN merchant_instances mi" + " USING (merchant_serial)" + " WHERE mr.report_serial=$1" + " AND mr.report_token=$2" + " AND mr.mime_type=$3;"); + return GNUNET_PQ_eval_prepared_singleton_select ( + pg->conn, + "check_report", + params, + rs); +} diff --git a/src/backenddb/pg_check_report.h b/src/backenddb/pg_check_report.h @@ -0,0 +1,51 @@ +/* + This file is part of TALER + Copyright (C) 2025 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 backenddb/pg_check_report.h + * @brief implementation of the check_report function for Postgres + * @author Christian Grothoff + */ +#ifndef PG_CHECK_REPORT_H +#define PG_CHECK_REPORT_H + +#include <taler/taler_util.h> +#include <taler/taler_json_lib.h> +#include "taler_merchantdb_plugin.h" + + +/** + * Check that a particular report is scheduled under the + * given @a report_id and @a report_token. Does not + * validate that the report is actually due. + * + * @param cls closure + * @param report_id serial number of the report to lookup + * @param report_token report token to check + * @param mime_type mime type to request from the @a data_source + * @param[out] instance_id instance to lookup reports for + * @param[out] data_source relative URL (to instance base URL) + * to request report data from + * @return database result code + */ +enum GNUNET_DB_QueryStatus +TMH_PG_check_report (void *cls, + uint64_t report_id, + const struct TALER_MERCHANT_ReportToken *report_token, + const char *mime_type, + char **instance_id, + char **data_source); + +#endif diff --git a/src/backenddb/pg_insert_report.c b/src/backenddb/pg_insert_report.c @@ -40,12 +40,14 @@ TMH_PG_insert_report ( uint64_t *report_id) { struct PostgresClosure *pg = cls; + struct TALER_MERCHANT_ReportToken report_token; struct GNUNET_TIME_Timestamp start; struct GNUNET_PQ_QueryParam params[] = { GNUNET_PQ_query_param_string (instance_id), GNUNET_PQ_query_param_string (report_program_section), GNUNET_PQ_query_param_string (report_description), GNUNET_PQ_query_param_string (mime_type), + GNUNET_PQ_query_param_auto_from_type (&report_token), GNUNET_PQ_query_param_string (data_source), GNUNET_PQ_query_param_string (target_address), GNUNET_PQ_query_param_relative_time (&frequency), @@ -59,6 +61,9 @@ TMH_PG_insert_report ( GNUNET_PQ_result_spec_end }; + GNUNET_CRYPTO_random_block (GNUNET_CRYPTO_QUALITY_NONCE, + &report_token, + sizeof (report_token)); start = GNUNET_TIME_absolute_to_timestamp ( GNUNET_TIME_absolute_add ( @@ -75,13 +80,14 @@ TMH_PG_insert_report ( ",report_program_section" ",report_description" ",mime_type" + ",report_token" ",data_source" ",target_address" ",frequency" ",frequency_shift" ",next_transmission)" " SELECT merchant_serial, $2, $3, $4, $5," - " $6, $7, $8, $9, $10, $11, $12" + " $6, $7, $8, $9, $10, $11, $12, $13" " FROM merchant_instances" " WHERE merchant_id=$1" " ON CONFLICT DO NOTHING;"); diff --git a/src/backenddb/pg_lookup_reports_pending.c b/src/backenddb/pg_lookup_reports_pending.c @@ -70,7 +70,7 @@ select_pending_reports_cb (void *cls, char *report_program_section; char *report_description; char *mime_type; - char *data_source; + struct TALER_MERCHANT_ReportToken report_token; char *target_address; struct GNUNET_TIME_Relative frequency; struct GNUNET_TIME_Relative frequency_shift; @@ -86,8 +86,8 @@ select_pending_reports_cb (void *cls, &report_description), GNUNET_PQ_result_spec_string ("mime_type", &mime_type), - GNUNET_PQ_result_spec_string ("data_source", - &data_source), + GNUNET_PQ_result_spec_auto_from_type ("report_token", + &report_token), GNUNET_PQ_result_spec_string ("target_address", &target_address), GNUNET_PQ_result_spec_relative_time ("frequency", @@ -114,7 +114,7 @@ select_pending_reports_cb (void *cls, report_program_section, report_description, mime_type, - data_source, + &report_token, target_address, frequency, frequency_shift, @@ -150,7 +150,7 @@ TMH_PG_lookup_reports_pending (void *cls, " ,mr.report_program_section" " ,mr.report_description" " ,mr.mime_type" - " ,mr.data_source" + " ,mr.report_token" " ,mr.target_address" " ,mr.frequency" " ,mr.frequency_shift" diff --git a/src/backenddb/pg_select_report.c b/src/backenddb/pg_select_report.c @@ -80,7 +80,7 @@ TMH_PG_select_report (void *cls, code = TALER_EC_NONE; check_connection (pg); PREPARE (pg, - "select_reports", + "select_report", "SELECT" " mr.report_program_section" " ,mr.report_description" diff --git a/src/backenddb/plugin_merchantdb_postgres.c b/src/backenddb/plugin_merchantdb_postgres.c @@ -62,6 +62,7 @@ #include "pg_insert_report.h" #include "pg_delete_report.h" #include "pg_update_report.h" +#include "pg_check_report.h" #include "pg_select_reports.h" #include "pg_lookup_reports_pending.h" #include "pg_update_report_status.h" @@ -694,6 +695,8 @@ libtaler_plugin_merchantdb_postgres_init (void *cls) = &TMH_PG_delete_report; plugin->update_report = &TMH_PG_update_report; + plugin->check_report + = &TMH_PG_check_report; plugin->select_reports = &TMH_PG_select_reports; plugin->lookup_reports_pending diff --git a/src/include/taler_merchant_util.h b/src/include/taler_merchant_util.h @@ -188,6 +188,18 @@ struct TALER_MERCHANT_MFA_BodySalt /** + * @brief Token used to authorize report generation. + */ +struct TALER_MERCHANT_ReportToken +{ + /** + * Salt. + */ + uint64_t salt[256 / 64]; +}; + + +/** * Hash the given request @a body with the given @a salt to * produce @a h_body for MFA checks. * diff --git a/src/include/taler_merchantdb_plugin.h b/src/include/taler_merchantdb_plugin.h @@ -27,8 +27,8 @@ #include <gnunet/gnunet_time_lib.h> #include <gnunet/gnunet_util_lib.h> #include <gnunet/gnunet_db_lib.h> -#include <taler/taler_exchange_service.h> #include <taler_merchant_util.h> +#include <taler/taler_exchange_service.h> #include <jansson.h> #ifdef HAVE_DONAU_DONAU_SERVICE_H @@ -1372,9 +1372,8 @@ typedef void * @param report_program_section configuration section of program * for report generation * @param report_description text describing the report - * @param mime_type mime type to request from the @a data_source - * @param data_source relative URL (to instance base URL) - * to request report data from + * @param mime_type mime type to request + * @param report_token token to authorize access to the data source * @param target_address where to send report data * @param frequency report frequency * @param frequency_shift how much to shift the report time from a @@ -1390,7 +1389,7 @@ typedef void const char *report_program_section, const char *report_description, const char *mime_type, - const char *data_source, + const struct TALER_MERCHANT_ReportToken *report_token, const char *target_address, struct GNUNET_TIME_Relative frequency, struct GNUNET_TIME_Relative frequency_shift, @@ -5086,6 +5085,29 @@ struct TALER_MERCHANTDB_Plugin char **last_error_detail); /** + * Check that a particular report is scheduled under the + * given @a report_id and @a report_token. Does not + * validate that the report is actually due. + * + * @param cls closure + * @param report_id serial number of the report to lookup + * @param report_token report token to check + * @param mime_type mime type to request from the @a data_source + * @param[out] instance_id instance to lookup reports for + * @param[out] data_source relative URL (to instance base URL) + * to request report data from + * @return database result code + */ + enum GNUNET_DB_QueryStatus + (*check_report)(void *cls, + uint64_t report_id, + const struct TALER_MERCHANT_ReportToken *report_token, + const char *mime_type, + char **instance_id, + char **data_source); + + + /** * Delete information about a report. * * @param cls closure