exchange

Base system with REST service to issue digital coins, run by the payment service provider
Log | Files | Refs | Submodules | README | LICENSE

commit 71b4c20fdd898c500f2d88d72348125ef7ee7c60
parent 75cac8340a41c5d8b1057d31feeb7877c91a7839
Author: Christian Grothoff <christian@grothoff.org>
Date:   Wed,  1 Apr 2026 14:29:30 +0200

minor consistency fixes, enforce AML officer read-only access semantics

Diffstat:
Msrc/exchange/taler-exchange-httpd.c | 40+++++++++++++++++++++++++++++++++++++++-
Msrc/exchange/taler-exchange-httpd_get-aml-OFFICER_PUB-attributes-H_NORMALIZED_PAYTO.c | 4++--
Msrc/exchange/taler-exchange-httpd_get-aml-OFFICER_PUB-decisions.c | 4++--
Msrc/exchange/taler-exchange-httpd_get-aml-OFFICER_PUB-legitimizations.c | 5++---
Msrc/exchange/taler-exchange-httpd_get-coins-COIN_PUB-history.c | 2--
Msrc/exchangedb/pg_select_aml_measures.c | 2+-
Msrc/exchangedb/pg_test_aml_officer.c | 18+++++++++++++-----
Msrc/exchangedb/pg_test_aml_officer.h | 4+++-
Msrc/include/taler/taler-exchange/get-coins-COIN_PUB-history.h | 1+
Msrc/include/taler/taler_exchangedb_plugin.h | 4+++-
Msrc/lib/exchange_api_get-aml-OFFICER_PUB-attributes-H_NORMALIZED_PAYTO.c | 2--
Msrc/lib/exchange_api_get-coins-COIN_PUB-history.c | 11+++++++++--
Msrc/util/wallet_signatures.c | 2+-
13 files changed, 76 insertions(+), 23 deletions(-)

diff --git a/src/exchange/taler-exchange-httpd.c b/src/exchange/taler-exchange-httpd.c @@ -481,6 +481,42 @@ handle_post_aml (struct TEH_RequestContext *rc, TALER_EC_EXCHANGE_GENERIC_AML_OFFICER_PUB_MALFORMED, args[0]); } + + { + enum GNUNET_DB_QueryStatus qs; + bool ro; + + qs = TEH_plugin->test_aml_officer (TEH_plugin->cls, + &officer_pub, + &ro); + switch (qs) + { + case GNUNET_DB_STATUS_HARD_ERROR: + case GNUNET_DB_STATUS_SOFT_ERROR: + GNUNET_break (0); + return TALER_MHD_reply_with_error (rc->connection, + MHD_HTTP_INTERNAL_SERVER_ERROR, + TALER_EC_GENERIC_DB_FETCH_FAILED, + NULL); + case GNUNET_DB_STATUS_SUCCESS_NO_RESULTS: + GNUNET_break_op (0); + return TALER_MHD_reply_with_error (rc->connection, + MHD_HTTP_FORBIDDEN, + TALER_EC_EXCHANGE_GENERIC_AML_OFFICER_ACCESS_DENIED, + "unknown or inactive"); + case GNUNET_DB_STATUS_SUCCESS_ONE_RESULT: + break; + } + if (ro) + { + GNUNET_break_op (0); + return TALER_MHD_reply_with_error (rc->connection, + MHD_HTTP_CONFLICT, + TALER_EC_EXCHANGE_GENERIC_AML_OFFICER_ACCESS_DENIED, + "read-only"); + } + } + for (unsigned int i = 0; NULL != h[i].op; i++) if (0 == strcmp (h[i].op, args[1])) @@ -638,9 +674,11 @@ handle_get_aml (struct TEH_RequestContext *rc, { enum GNUNET_DB_QueryStatus qs; + bool ro; qs = TEH_plugin->test_aml_officer (TEH_plugin->cls, - &officer_pub); + &officer_pub, + &ro); switch (qs) { case GNUNET_DB_STATUS_HARD_ERROR: diff --git a/src/exchange/taler-exchange-httpd_get-aml-OFFICER_PUB-attributes-H_NORMALIZED_PAYTO.c b/src/exchange/taler-exchange-httpd_get-aml-OFFICER_PUB-attributes-H_NORMALIZED_PAYTO.c @@ -372,8 +372,8 @@ detail_cb ( json_array_append_new ( rc->details.json, GNUNET_JSON_PACK ( - GNUNET_JSON_pack_int64 ("rowid", - row_id), + GNUNET_JSON_pack_uint64 ("rowid", + row_id), GNUNET_JSON_pack_bool ("by_aml_officer", by_aml_officer), GNUNET_JSON_pack_allow_null ( diff --git a/src/exchange/taler-exchange-httpd_get-aml-OFFICER_PUB-decisions.c b/src/exchange/taler-exchange-httpd_get-aml-OFFICER_PUB-decisions.c @@ -81,8 +81,8 @@ record_cb ( payto), GNUNET_JSON_pack_bool ("is_wallet", is_wallet), - GNUNET_JSON_pack_int64 ("rowid", - row_id), + GNUNET_JSON_pack_uint64 ("rowid", + row_id), GNUNET_JSON_pack_allow_null ( GNUNET_JSON_pack_string ("justification", justification)), diff --git a/src/exchange/taler-exchange-httpd_get-aml-OFFICER_PUB-legitimizations.c b/src/exchange/taler-exchange-httpd_get-aml-OFFICER_PUB-legitimizations.c @@ -69,9 +69,8 @@ record_cb ( GNUNET_JSON_pack_timestamp ("start_time", GNUNET_TIME_absolute_to_timestamp ( start_time)), - GNUNET_JSON_pack_allow_null ( - GNUNET_JSON_pack_object_incref ("measures", - (json_t *) jmeasures)), + GNUNET_JSON_pack_object_incref ("measures", + (json_t *) jmeasures), GNUNET_JSON_pack_bool ("is_finished", is_finished) ))); diff --git a/src/exchange/taler-exchange-httpd_get-coins-COIN_PUB-history.c b/src/exchange/taler-exchange-httpd_get-coins-COIN_PUB-history.c @@ -382,8 +382,6 @@ compile_transaction_history ( &recoup->coin_blind), // FIXME-9828: spec says we should have h_commitment? // FIXME-9828: spec says we should have coin_index? - GNUNET_JSON_pack_data_auto ("reserve_pub", - &recoup->reserve_pub), GNUNET_JSON_pack_timestamp ("timestamp", recoup->timestamp)))) { diff --git a/src/exchangedb/pg_select_aml_measures.c b/src/exchangedb/pg_select_aml_measures.c @@ -74,7 +74,7 @@ handle_aml_result (void *cls, struct TALER_NormalizedPaytoHashP h_payto; uint64_t rowid; struct GNUNET_TIME_Absolute start_time; - json_t *jmeasures = NULL; + json_t *jmeasures; bool is_finished; struct GNUNET_PQ_ResultSpec rs[] = { GNUNET_PQ_result_spec_uint64 ("legitimization_measure_serial_id", diff --git a/src/exchangedb/pg_test_aml_officer.c b/src/exchangedb/pg_test_aml_officer.c @@ -29,20 +29,28 @@ enum GNUNET_DB_QueryStatus TEH_PG_test_aml_officer ( void *cls, - const struct TALER_AmlOfficerPublicKeyP *decider_pub) + const struct TALER_AmlOfficerPublicKeyP *decider_pub, + bool *read_only) { struct PostgresClosure *pg = cls; struct GNUNET_PQ_QueryParam params[] = { GNUNET_PQ_query_param_auto_from_type (decider_pub), GNUNET_PQ_query_param_end }; + struct GNUNET_PQ_ResultSpec rs[] = { + GNUNET_PQ_result_spec_bool ("read_only", + read_only), + GNUNET_PQ_result_spec_end + }; PREPARE (pg, "test_aml_staff", - "SELECT 1 FROM aml_staff" + "SELECT read_only" + " FROM aml_staff" " WHERE decider_pub=$1" " AND is_active;"); - return GNUNET_PQ_eval_prepared_non_select (pg->conn, - "test_aml_staff", - params); + return GNUNET_PQ_eval_prepared_singleton_select (pg->conn, + "test_aml_staff", + params, + rs); } diff --git a/src/exchangedb/pg_test_aml_officer.h b/src/exchangedb/pg_test_aml_officer.h @@ -32,12 +32,14 @@ * * @param cls closure * @param decider_pub public key of the staff member + * @param[out] read_only set to true if the member is read-only * @return database transaction status, if member is unknown or not active, 1 if member is active */ enum GNUNET_DB_QueryStatus TEH_PG_test_aml_officer ( void *cls, - const struct TALER_AmlOfficerPublicKeyP *decider_pub); + const struct TALER_AmlOfficerPublicKeyP *decider_pub, + bool *read_only); #endif diff --git a/src/include/taler/taler-exchange/get-coins-COIN_PUB-history.h b/src/include/taler/taler-exchange/get-coins-COIN_PUB-history.h @@ -210,6 +210,7 @@ struct TALER_EXCHANGE_CoinHistoryEntry { struct TALER_ReserveSignatureP reserve_sig; struct TALER_CoinSpendSignatureP coin_sig; + struct TALER_Amount coin_contribution; } reserve_open_deposit; } details; diff --git a/src/include/taler/taler_exchangedb_plugin.h b/src/include/taler/taler_exchangedb_plugin.h @@ -7649,12 +7649,14 @@ struct TALER_EXCHANGEDB_Plugin * * @param cls closure * @param decider_pub public key of the staff member + * @param[out] read_only set to true if the member is read-only * @return database transaction status, if member is unknown or not active, 1 if member is active */ enum GNUNET_DB_QueryStatus (*test_aml_officer)( void *cls, - const struct TALER_AmlOfficerPublicKeyP *decider_pub); + const struct TALER_AmlOfficerPublicKeyP *decider_pub, + bool *read_only); /** diff --git a/src/lib/exchange_api_get-aml-OFFICER_PUB-attributes-H_NORMALIZED_PAYTO.c b/src/lib/exchange_api_get-aml-OFFICER_PUB-attributes-H_NORMALIZED_PAYTO.c @@ -440,8 +440,6 @@ TALER_EXCHANGE_get_aml_attributes_start ( job_headers = curl_slist_append (NULL, hdr); GNUNET_free (hdr); - job_headers = curl_slist_append (job_headers, - "Content-type: application/json"); aagh->job = GNUNET_CURL_job_add2 (aagh->ctx, eh, job_headers, diff --git a/src/lib/exchange_api_get-coins-COIN_PUB-history.c b/src/lib/exchange_api_get-coins-COIN_PUB-history.c @@ -722,6 +722,9 @@ help_reserve_open_deposit (struct CoinHistoryParseContext *pc, &rh->details.reserve_open_deposit.reserve_sig), GNUNET_JSON_spec_fixed_auto ("coin_sig", &rh->details.reserve_open_deposit.coin_sig), + TALER_JSON_spec_amount_any ("coin_contribution", + &rh->details.reserve_open_deposit. + coin_contribution), GNUNET_JSON_spec_end () }; @@ -773,13 +776,13 @@ TALER_EXCHANGE_parse_coin_history ( { "REFUND", &help_refund, TALER_EXCHANGE_CTT_REFUND }, - { "RECOUP", + { "RECOUP-WITHDRAW", &help_recoup, TALER_EXCHANGE_CTT_RECOUP }, { "RECOUP-REFRESH", &help_recoup_refresh, TALER_EXCHANGE_CTT_RECOUP_REFRESH }, - { "OLD-COIN-RECOUP", + { "RECOUP-REFRESH-RECEIVER", &help_old_coin_recoup, TALER_EXCHANGE_CTT_OLD_COIN_RECOUP }, { "PURSE-DEPOSIT", @@ -986,6 +989,10 @@ handle_coins_history_finished (void *cls, rs.hr.ec = TALER_EC_GENERIC_REPLY_MALFORMED; } break; + case MHD_HTTP_NO_CONTENT: + break; + case MHD_HTTP_NOT_MODIFIED: + break; case MHD_HTTP_BAD_REQUEST: /* This should never happen, either us or the exchange is buggy (or API version conflict); just pass JSON reply to the application */ diff --git a/src/util/wallet_signatures.c b/src/util/wallet_signatures.c @@ -1759,7 +1759,7 @@ struct TALER_ReserveOpenDepositPS GNUNET_NETWORK_STRUCT_END -// FIXME-#7267: add h_age_commitment, h_denom_pub to have proof! +// FIXME-#11318: add h_age_commitment, h_denom_pub to have proof! void TALER_wallet_reserve_open_deposit_sign ( const struct TALER_Amount *coin_contribution,