From d131951fbe09b3415c9976acd11f660d51493086 Mon Sep 17 00:00:00 2001 From: Christian Grothoff Date: Sat, 21 Jan 2023 23:08:29 +0100 Subject: [PATCH] add new endpoints to main dispatcher --- contrib/gana | 2 +- src/exchange/Makefile.am | 3 + src/exchange/taler-exchange-httpd.c | 226 +++++++++++++++++- src/exchange/taler-exchange-httpd.h | 4 +- .../taler-exchange-httpd_aml-decision.c | 57 +++-- .../taler-exchange-httpd_aml-decision.h | 45 ++++ .../taler-exchange-httpd_management.h | 26 ++ ...r-exchange-httpd_management_aml-officers.c | 21 +- ...taler-exchange-httpd_management_partners.c | 29 ++- src/exchangedb/0002-partners.sql | 1 + src/exchangedb/pg_insert_aml_decision.c | 12 +- src/exchangedb/pg_insert_aml_decision.h | 9 +- src/exchangedb/pg_insert_aml_officer.c | 8 +- src/exchangedb/pg_insert_aml_officer.h | 9 +- src/exchangedb/pg_insert_partner.c | 19 +- src/include/taler_exchangedb_plugin.h | 13 +- src/lib/exchange_api_add_aml_decision.c | 2 +- src/lib/exchange_api_management_add_partner.c | 26 +- ...change_api_management_update_aml_officer.c | 24 +- 19 files changed, 419 insertions(+), 117 deletions(-) create mode 100644 src/exchange/taler-exchange-httpd_aml-decision.h diff --git a/contrib/gana b/contrib/gana index 66228b8a4..7884adf99 160000 --- a/contrib/gana +++ b/contrib/gana @@ -1 +1 @@ -Subproject commit 66228b8a4306f028d843d78fbfcca54260539ff9 +Subproject commit 7884adf99ec4d5ccf52b1a5a251b99fb6ab9c2f6 diff --git a/src/exchange/Makefile.am b/src/exchange/Makefile.am index 29596c381..ffcfc5e99 100644 --- a/src/exchange/Makefile.am +++ b/src/exchange/Makefile.am @@ -123,6 +123,7 @@ taler_exchange_wirewatch_LDADD = \ taler_exchange_httpd_SOURCES = \ taler-exchange-httpd.c taler-exchange-httpd.h \ taler-exchange-httpd_auditors.c taler-exchange-httpd_auditors.h \ + taler-exchange-httpd_aml-decision.c taler-exchange-httpd_aml-decision.h \ taler-exchange-httpd_batch-deposit.c taler-exchange-httpd_batch-deposit.h \ taler-exchange-httpd_batch-withdraw.c taler-exchange-httpd_batch-withdraw.h \ taler-exchange-httpd_common_deposit.c taler-exchange-httpd_common_deposit.h \ @@ -139,12 +140,14 @@ taler_exchange_httpd_SOURCES = \ taler-exchange-httpd_kyc-webhook.c taler-exchange-httpd_kyc-webhook.h \ taler-exchange-httpd_link.c taler-exchange-httpd_link.h \ taler-exchange-httpd_management.h \ + taler-exchange-httpd_management_aml-officers.c \ taler-exchange-httpd_management_auditors.c \ taler-exchange-httpd_management_auditors_AP_disable.c \ taler-exchange-httpd_management_denominations_HDP_revoke.c \ taler-exchange-httpd_management_drain.c \ taler-exchange-httpd_management_extensions.c \ taler-exchange-httpd_management_global_fees.c \ + taler-exchange-httpd_management_partners.c \ taler-exchange-httpd_management_post_keys.c \ taler-exchange-httpd_management_signkey_EP_revoke.c \ taler-exchange-httpd_management_wire_enable.c \ diff --git a/src/exchange/taler-exchange-httpd.c b/src/exchange/taler-exchange-httpd.c index def4fd4af..5501687fa 100644 --- a/src/exchange/taler-exchange-httpd.c +++ b/src/exchange/taler-exchange-httpd.c @@ -1,6 +1,6 @@ /* This file is part of TALER - Copyright (C) 2014-2022 Taler Systems SA + Copyright (C) 2014-2023 Taler Systems SA TALER is free software; you can redistribute it and/or modify it under the terms of the GNU Affero General Public License as published by the Free Software @@ -30,6 +30,7 @@ #include "taler_kyclogic_lib.h" #include "taler_templating_lib.h" #include "taler_mhd_lib.h" +#include "taler-exchange-httpd_aml-decision.h" #include "taler-exchange-httpd_auditors.h" #include "taler-exchange-httpd_batch-deposit.h" #include "taler-exchange-httpd_batch-withdraw.h" @@ -322,6 +323,187 @@ handle_post_coins (struct TEH_RequestContext *rc, } +/** + * Signature of functions that handle operations + * authorized by AML officers. + * + * @param rc request context + * @param officer_pub the public key of the AML officer + * @param root uploaded JSON data + * @return MHD result code + */ +typedef MHD_RESULT +(*AmlOpPostHandler)(struct TEH_RequestContext *rc, + const struct TALER_AmlOfficerPublicKeyP *officer_pub, + const json_t *root); + + +/** + * Handle a "/aml/$OFFICER_PUB/$OP" POST request. Parses the "officer_pub" + * EdDSA key of the officer and demultiplexes based on $OP. + * + * @param rc request context + * @param root uploaded JSON data + * @param args array of additional options + * @return MHD result code + */ +static MHD_RESULT +handle_post_aml (struct TEH_RequestContext *rc, + const json_t *root, + const char *const args[2]) +{ + struct TALER_AmlOfficerPublicKeyP officer_pub; + static const struct + { + /** + * Name of the operation (args[1]) + */ + const char *op; + + /** + * Function to call to perform the operation. + */ + AmlOpPostHandler handler; + + } h[] = { + { + .op = "decision", + .handler = &TEH_handler_post_aml_decision + }, + { + .op = NULL, + .handler = NULL + }, + }; + + if (GNUNET_OK != + GNUNET_STRINGS_string_to_data (args[0], + strlen (args[0]), + &officer_pub, + sizeof (officer_pub))) + { + GNUNET_break_op (0); + return TALER_MHD_reply_with_error (rc->connection, + MHD_HTTP_BAD_REQUEST, + TALER_EC_EXCHANGE_GENERIC_AML_OFFICER_PUB_MALFORMED, + args[0]); + } + for (unsigned int i = 0; NULL != h[i].op; i++) + if (0 == strcmp (h[i].op, + args[1])) + return h[i].handler (rc, + &officer_pub, + root); + return r404 (rc->connection, + args[1]); +} + + +/** + * Signature of functions that handle operations + * authorized by AML officers. + * + * @param rc request context + * @param officer_pub the public key of the AML officer + * @param args remaining arguments + * @return MHD result code + */ +typedef MHD_RESULT +(*AmlOpGetHandler)(struct TEH_RequestContext *rc, + const struct TALER_AmlOfficerPublicKeyP *officer_pub, + const char *const args[]); + + +/** + * Handle a "/aml/$OFFICER_PUB/$OP" GET request. Parses the "officer_pub" + * EdDSA key of the officer, checks the authentication signature, and + * demultiplexes based on $OP. + * + * @param rc request context + * @param args array of additional options + * @return MHD result code + */ +static MHD_RESULT +handle_get_aml (struct TEH_RequestContext *rc, + const char *const args[]) +{ + struct TALER_AmlOfficerPublicKeyP officer_pub; + static const struct + { + /** + * Name of the operation (args[1]) + */ + const char *op; + + /** + * Function to call to perform the operation. + */ + AmlOpGetHandler handler; + + } h[] = { +#if FIXME_AML_GET_DECISIONS_NOT_IMPLEMENTED + { + .op = "decisions", + .handler = &TEH_handler_get_aml_decisions + }, + { + .op = "decision", + .handler = &TEH_handler_get_aml_decision + }, +#endif + { + .op = NULL, + .handler = NULL + }, + }; + + if (NULL == args[0]) + { + GNUNET_break_op (0); + return TALER_MHD_reply_with_error (rc->connection, + MHD_HTTP_BAD_REQUEST, + TALER_EC_EXCHANGE_GENERIC_AML_OFFICER_PUB_MALFORMED, + "argument missing"); + } + if (GNUNET_OK != + GNUNET_STRINGS_string_to_data (args[0], + strlen (args[0]), + &officer_pub, + sizeof (officer_pub))) + { + GNUNET_break_op (0); + return TALER_MHD_reply_with_error (rc->connection, + MHD_HTTP_BAD_REQUEST, + TALER_EC_EXCHANGE_GENERIC_AML_OFFICER_PUB_MALFORMED, + args[0]); + } + if (NULL == args[1]) + { + GNUNET_break_op (0); + return TALER_MHD_reply_with_error (rc->connection, + MHD_HTTP_NOT_FOUND, + TALER_EC_EXCHANGE_GENERIC_WRONG_NUMBER_OF_SEGMENTS, + "AML GET operations must specify an operation identifier"); + } + if (1) // FIXME: check AML officer GET signature! + { + GNUNET_break_op (0); + return TALER_MHD_reply_with_error (rc->connection, + MHD_HTTP_FORBIDDEN, + TALER_EC_EXCHANGE_GENERIC_AML_OFFICER_GET_SIGNATURE_INVALID, + NULL); + } + for (unsigned int i = 0; NULL != h[i].op; i++) + if (0 == strcmp (h[i].op, + args[1])) + return h[i].handler (rc, + &officer_pub, + &args[2]); + return r404 (rc->connection, + args[1]); +} + + /** * Signature of functions that handle operations on reserves. * @@ -890,6 +1072,8 @@ handle_post_management (struct TEH_RequestContext *rc, &exchange_pub, root); } + /* FIXME-STYLE: all of the following can likely be nicely combined + into an array-based dispatcher to deduplicate the logic... */ if (0 == strcmp (args[0], "keys")) { @@ -967,6 +1151,30 @@ handle_post_management (struct TEH_RequestContext *rc, return TEH_handler_management_post_drain (rc->connection, root); } + if (0 == strcmp (args[0], + "aml-officers")) + { + if (NULL != args[1]) + { + GNUNET_break_op (0); + return r404 (rc->connection, + "/management/aml-officers/*"); + } + return TEH_handler_management_aml_officers (rc->connection, + root); + } + if (0 == strcmp (args[0], + "partners")) + { + if (NULL != args[1]) + { + GNUNET_break_op (0); + return r404 (rc->connection, + "/management/partners/*"); + } + return TEH_handler_management_partners (rc->connection, + root); + } GNUNET_break_op (0); return r404 (rc->connection, "/management/*"); @@ -1289,6 +1497,22 @@ handle_mhd_request (void *cls, .nargs = 4, .nargs_is_upper_bound = true }, + /* AML endpoints */ + { + .url = "aml", + .method = MHD_HTTP_METHOD_GET, + .handler.get = &handle_get_aml, + .nargs = 4, + .nargs_is_upper_bound = true + }, + { + .url = "aml", + .method = MHD_HTTP_METHOD_POST, + .handler.post = &handle_post_aml, + .nargs = 2 + }, + + /* mark end of list */ { .url = NULL diff --git a/src/exchange/taler-exchange-httpd.h b/src/exchange/taler-exchange-httpd.h index 2be26f14d..7715444a3 100644 --- a/src/exchange/taler-exchange-httpd.h +++ b/src/exchange/taler-exchange-httpd.h @@ -213,7 +213,7 @@ struct TEH_RequestHandler * * @param rc context for the request * @param json uploaded JSON data - * @param args array of arguments, needs to be of length @e args_expected + * @param args array of arguments, needs to be of length @e nargs * @return MHD result code */ MHD_RESULT @@ -225,7 +225,7 @@ struct TEH_RequestHandler * Function to call to handle DELETE requests. * * @param rc context for the request - * @param args array of arguments, needs to be of length @e args_expected + * @param args array of arguments, needs to be of length @e nargs * @return MHD result code */ MHD_RESULT diff --git a/src/exchange/taler-exchange-httpd_aml-decision.c b/src/exchange/taler-exchange-httpd_aml-decision.c index c93c10668..4589c8eb9 100644 --- a/src/exchange/taler-exchange-httpd_aml-decision.c +++ b/src/exchange/taler-exchange-httpd_aml-decision.c @@ -31,27 +31,26 @@ MHD_RESULT -TEH_handler_management_post_aml_decision ( - struct MHD_Connection *connection, +TEH_handler_post_aml_decision ( + struct TEH_RequestContext *rc, + const struct TALER_AmlOfficerPublicKeyP *officer_pub, const json_t *root) { + struct MHD_Connection *connection = rc->connection; const char *justification; struct GNUNET_TIME_Timestamp decision_time; struct TALER_Amount new_threshold; struct TALER_PaytoHashP h_payto; uint32_t new_state32; enum TALER_AmlDecisionState new_state; - struct TALER_AmlOfficerPublicKeyP officer_pub; struct TALER_AmlOfficerSignatureP officer_sig; struct GNUNET_JSON_Specification spec[] = { - // FIXME: officer_pub is in URL path, not in JSON body! - GNUNET_JSON_spec_fixed_auto ("officer_pub", - &officer_pub), GNUNET_JSON_spec_fixed_auto ("officer_sig", &officer_sig), GNUNET_JSON_spec_fixed_auto ("h_payto", &h_payto), TALER_JSON_spec_amount ("new_threshold", + TEH_currency, &new_threshold), GNUNET_JSON_spec_string ("justification", &justification), @@ -76,13 +75,13 @@ TEH_handler_management_post_aml_decision ( new_state = (enum TALER_AmlDecisionState) new_state32; TEH_METRICS_num_verifications[TEH_MT_SIGNATURE_EDDSA]++; if (GNUNET_OK != - TALER_exchange_aml_decision_verify (justification, - decision_time, - &new_threshold, - &h_payto, - new_state, - &officer_pub, - &officer_sig)) + TALER_officer_aml_decision_verify (justification, + decision_time, + &new_threshold, + &h_payto, + new_state, + &officer_pub, + &officer_sig)) { GNUNET_break_op (0); return TALER_MHD_reply_with_error ( @@ -97,25 +96,25 @@ TEH_handler_management_post_aml_decision ( bool invalid_officer; do { - qs = TEH_plugin->add_aml_decision (TEH_plugin->cls, - justification, - decision_time, - &new_threshold, - &h_payto, - new_state, - &officer_pub, - &officer_sig, - &invalid_officer, - &last_date); + // FIXME: bound loop? + qs = TEH_plugin->insert_aml_decision (TEH_plugin->cls, + &h_payto, + &new_threshold, + new_state, + decision_time, + justification, + &officer_pub, + &officer_sig, + &invalid_officer, + &last_date); } while (GNUNET_DB_STATUS_SOFT_ERROR == qs); if (qs < 0) { GNUNET_break (0); - *mhd_ret = TALER_MHD_reply_with_error (connection, - MHD_HTTP_INTERNAL_SERVER_ERROR, - TALER_EC_GENERIC_DB_STORE_FAILED, - "add aml_decision"); - return qs; + return TALER_MHD_reply_with_error (connection, + MHD_HTTP_INTERNAL_SERVER_ERROR, + TALER_EC_GENERIC_DB_STORE_FAILED, + "add aml_decision"); } if (invalid_officer) { @@ -127,7 +126,7 @@ TEH_handler_management_post_aml_decision ( } if (GNUNET_TIME_timestamp_cmp (last_date, >, - validity_start)) + decision_time)) { GNUNET_break_op (0); return TALER_MHD_reply_with_error ( diff --git a/src/exchange/taler-exchange-httpd_aml-decision.h b/src/exchange/taler-exchange-httpd_aml-decision.h new file mode 100644 index 000000000..e6e355524 --- /dev/null +++ b/src/exchange/taler-exchange-httpd_aml-decision.h @@ -0,0 +1,45 @@ +/* + This file is part of TALER + Copyright (C) 2023 Taler Systems SA + + TALER is free software; you can redistribute it and/or modify it under the + terms of the GNU Affero 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 Affero General Public License for more details. + + You should have received a copy of the GNU Affero General Public License along with + TALER; see the file COPYING. If not, see +*/ +/** + * @file taler-exchange-httpd_aml_decision.h + * @brief Handle /aml/$OFFICER_PUB/decision requests + * @author Christian Grothoff + */ +#ifndef TALER_EXCHANGE_HTTPD_AML_DECISION_H +#define TALER_EXCHANGE_HTTPD_AML_DECISION_H + +#include +#include "taler-exchange-httpd.h" + + +/** + * Handle an "/aml/$OFFICER_PUB/decision" request. Parses the decision + * details, checks the signatures and if appropriately authorized excecutes + * the decision. + * + * @param rc request context + * @param officer_pub public key of the AML officer who made the request + * @param root uploaded JSON data + * @return MHD result code + */ +MHD_RESULT +TEH_handler_post_aml_decision ( + struct TEH_RequestContext *rc, + const struct TALER_AmlOfficerPublicKeyP *officer_pub, + const json_t *root); + + +#endif diff --git a/src/exchange/taler-exchange-httpd_management.h b/src/exchange/taler-exchange-httpd_management.h index a5a8b0e72..2fc1fe8db 100644 --- a/src/exchange/taler-exchange-httpd_management.h +++ b/src/exchange/taler-exchange-httpd_management.h @@ -174,6 +174,32 @@ TEH_handler_management_post_drain ( const json_t *root); +/** + * Handle a POST "/management/aml-officers" request. + * + * @param connection the MHD connection to handle + * @param root uploaded JSON data + * @return MHD result code + */ +MHD_RESULT +TEH_handler_management_aml_officers ( + struct MHD_Connection *connection, + const json_t *root); + + +/** + * Handle a POST "/management/partners" request. + * + * @param connection the MHD connection to handle + * @param root uploaded JSON data + * @return MHD result code + */ +MHD_RESULT +TEH_handler_management_partners ( + struct MHD_Connection *connection, + const json_t *root); + + /** * Initialize extension configuration handling. * diff --git a/src/exchange/taler-exchange-httpd_management_aml-officers.c b/src/exchange/taler-exchange-httpd_management_aml-officers.c index 139ccdb25..b82c18d47 100644 --- a/src/exchange/taler-exchange-httpd_management_aml-officers.c +++ b/src/exchange/taler-exchange-httpd_management_aml-officers.c @@ -92,14 +92,15 @@ TEH_handler_management_aml_officers ( struct GNUNET_TIME_Timestamp last_date; do { - qs = TEH_plugin->set_aml_officer (TEH_plugin->cls, - &officer_pub, - officer_name, - change_date, - is_active, - read_only, - &master_sig, - &last_date); + // FIXME: bound loop! + qs = TEH_plugin->insert_aml_officer (TEH_plugin->cls, + &officer_pub, + &master_sig, + officer_name, + is_active, + read_only, + change_date, + &last_date); } while (GNUNET_DB_STATUS_SOFT_ERROR == qs); if (qs < 0) { @@ -107,13 +108,13 @@ TEH_handler_management_aml_officers ( return TALER_MHD_reply_with_error (connection, MHD_HTTP_INTERNAL_SERVER_ERROR, TALER_EC_GENERIC_DB_STORE_FAILED, - "XXX"); + "insert_aml_officer"); } if (GNUNET_TIME_timestamp_cmp (last_date, >, change_date)) { - GNUNER_break_op (0); + GNUNET_break_op (0); return TALER_MHD_reply_with_error ( connection, MHD_HTTP_CONFLICT, diff --git a/src/exchange/taler-exchange-httpd_management_partners.c b/src/exchange/taler-exchange-httpd_management_partners.c index 5d860120a..1c0d4a9a1 100644 --- a/src/exchange/taler-exchange-httpd_management_partners.c +++ b/src/exchange/taler-exchange-httpd_management_partners.c @@ -51,13 +51,14 @@ TEH_handler_management_partners ( GNUNET_JSON_spec_string ("partner_base_url", &partner_base_url), TALER_JSON_spec_amount ("wad_fee", + TEH_currency, &wad_fee), GNUNET_JSON_spec_timestamp ("start_date", &start_date), GNUNET_JSON_spec_timestamp ("end_date", &start_date), - GNUNET_JSON_spec_time_rel ("wad_frequency", - &wad_frequency), + GNUNET_JSON_spec_relative_time ("wad_frequency", + &wad_frequency), GNUNET_JSON_spec_end () }; @@ -94,14 +95,14 @@ TEH_handler_management_partners ( { enum GNUNET_DB_QueryStatus qs; - qs = TEH_plugin->add_partner (TEH_plugin->cls, - &partner_pub, - start_date, - end_date, - wad_frequency, - &wad_fee, - partner_base_url, - &master_sig); + qs = TEH_plugin->insert_partner (TEH_plugin->cls, + &partner_pub, + start_date, + end_date, + wad_frequency, + &wad_fee, + partner_base_url, + &master_sig); if (qs < 0) { GNUNET_break (0); @@ -110,6 +111,14 @@ TEH_handler_management_partners ( TALER_EC_GENERIC_DB_STORE_FAILED, "add_partner"); } + if (GNUNET_DB_STATUS_SUCCESS_NO_RESULTS == qs) + { + /* FIXME: check for idempotency! */ + return TALER_MHD_reply_with_error (connection, + MHD_HTTP_CONFLICT, + TALER_EC_EXCHANGE_MANAGEMENT_ADD_PARTNER_DATA_CONFLICT, + NULL); + } } return TALER_MHD_reply_static ( connection, diff --git a/src/exchangedb/0002-partners.sql b/src/exchangedb/0002-partners.sql index ff57f8fc1..c80f2d745 100644 --- a/src/exchangedb/0002-partners.sql +++ b/src/exchangedb/0002-partners.sql @@ -25,6 +25,7 @@ CREATE TABLE partners ,wad_fee_frac INT4 NOT NULL ,master_sig BYTEA NOT NULL CHECK (LENGTH(master_sig)=64) ,partner_base_url TEXT NOT NULL + ,PRIMARY KEY (partner_master_pub, start_date) ); COMMENT ON TABLE partners IS 'exchanges we do wad transfers to'; diff --git a/src/exchangedb/pg_insert_aml_decision.c b/src/exchangedb/pg_insert_aml_decision.c index d046c87f9..421628f65 100644 --- a/src/exchangedb/pg_insert_aml_decision.c +++ b/src/exchangedb/pg_insert_aml_decision.c @@ -1,6 +1,6 @@ /* This file is part of TALER - Copyright (C) 2022 Taler Systems SA + Copyright (C) 2022, 2023 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 @@ -32,10 +32,12 @@ TEH_PG_insert_aml_decision ( const struct TALER_PaytoHashP *h_payto, const struct TALER_Amount *new_threshold, enum TALER_AmlDecisionState new_status, - struct GNUNET_TIME_Absolute decision_time, + struct GNUNET_TIME_Timestamp decision_time, const char *justification, const struct TALER_AmlOfficerPublicKeyP *decider_pub, - const struct TALER_AmlOfficerSignatureP *decider_sig) + const struct TALER_AmlOfficerSignatureP *decider_sig, + bool *invalid_officer, + struct GNUNET_TIME_Timestamp *last_date) { struct PostgresClosure *pg = cls; uint32_t ns = (uint32_t) new_status; @@ -43,13 +45,15 @@ TEH_PG_insert_aml_decision ( GNUNET_PQ_query_param_auto_from_type (h_payto), TALER_PQ_query_param_amount (new_threshold), GNUNET_PQ_query_param_uint32 (&ns), - GNUNET_PQ_query_param_absolute_time (&decision_time), + GNUNET_PQ_query_param_timestamp (&decision_time), GNUNET_PQ_query_param_string (justification), GNUNET_PQ_query_param_auto_from_type (decider_pub), GNUNET_PQ_query_param_auto_from_type (decider_sig), GNUNET_PQ_query_param_end }; + // FIXME: set invalid_officer + // FIXME: set last_date! PREPARE (pg, "insert_aml_decision", "INSERT INTO aml_history " diff --git a/src/exchangedb/pg_insert_aml_decision.h b/src/exchangedb/pg_insert_aml_decision.h index 205c1c74b..94f648fb1 100644 --- a/src/exchangedb/pg_insert_aml_decision.h +++ b/src/exchangedb/pg_insert_aml_decision.h @@ -38,6 +38,9 @@ * @param justification human-readable text justifying the decision * @param decider_pub public key of the staff member * @param decider_sig signature of the staff member + * @param[out] invalid_officer set to TRUE if @a decider_pub is not allowed to make decisions right now + * @param[out] last_date set to the previous decision time; + * the INSERT is not performed if @a last_date is not before @a decision_time * @return database transaction status */ enum GNUNET_DB_QueryStatus @@ -46,10 +49,12 @@ TEH_PG_insert_aml_decision ( const struct TALER_PaytoHashP *h_payto, const struct TALER_Amount *new_threshold, enum TALER_AmlDecisionState new_status, - struct GNUNET_TIME_Absolute decision_time, + struct GNUNET_TIME_Timestamp decision_time, const char *justification, const struct TALER_AmlOfficerPublicKeyP *decider_pub, - const struct TALER_AmlOfficerSignatureP *decider_sig); + const struct TALER_AmlOfficerSignatureP *decider_sig, + bool *invalid_officer, + struct GNUNET_TIME_Timestamp *last_date); #endif diff --git a/src/exchangedb/pg_insert_aml_officer.c b/src/exchangedb/pg_insert_aml_officer.c index fc2cadef2..33e6c86f7 100644 --- a/src/exchangedb/pg_insert_aml_officer.c +++ b/src/exchangedb/pg_insert_aml_officer.c @@ -1,6 +1,6 @@ /* This file is part of TALER - Copyright (C) 2022 Taler Systems SA + Copyright (C) 2022, 2023 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 @@ -34,7 +34,8 @@ TEH_PG_insert_aml_officer ( const char *decider_name, bool is_active, bool read_only, - struct GNUNET_TIME_Absolute last_change) + struct GNUNET_TIME_Timestamp last_change, + struct GNUNET_TIME_Timestamp *previous_change) { struct PostgresClosure *pg = cls; struct GNUNET_PQ_QueryParam params[] = { @@ -43,10 +44,11 @@ TEH_PG_insert_aml_officer ( GNUNET_PQ_query_param_string (decider_name), GNUNET_PQ_query_param_bool (is_active), GNUNET_PQ_query_param_bool (read_only), - GNUNET_PQ_query_param_absolute_time (&last_change), + GNUNET_PQ_query_param_timestamp (&last_change), GNUNET_PQ_query_param_end }; + // FIXME: need to check for previous record! PREPARE (pg, "insert_aml_staff", "INSERT INTO aml_staff " diff --git a/src/exchangedb/pg_insert_aml_officer.h b/src/exchangedb/pg_insert_aml_officer.h index be034d9ff..3c6f5d82e 100644 --- a/src/exchangedb/pg_insert_aml_officer.h +++ b/src/exchangedb/pg_insert_aml_officer.h @@ -27,7 +27,10 @@ /** - * Insert AML staff record. + * Insert AML staff record. If the time given in + * @a last_change is before the previous change in the + * database, only @e previous_change is returned and + * no actual change is committed to the database. * * @param cls closure * @param decider_pub public key of the staff member @@ -36,6 +39,7 @@ * @param is_active true to enable, false to set as inactive * @param read_only true to set read-only access * @param last_change when was the change made effective + * @param[out] previous_change set to the time of the previous change * @return database transaction status */ enum GNUNET_DB_QueryStatus @@ -46,6 +50,7 @@ TEH_PG_insert_aml_officer ( const char *decider_name, bool is_active, bool read_only, - struct GNUNET_TIME_Absolute last_change); + struct GNUNET_TIME_Timestamp last_change, + struct GNUNET_TIME_Timestamp *previous_change); #endif diff --git a/src/exchangedb/pg_insert_partner.c b/src/exchangedb/pg_insert_partner.c index 567f37763..5abb2c910 100644 --- a/src/exchangedb/pg_insert_partner.c +++ b/src/exchangedb/pg_insert_partner.c @@ -28,13 +28,13 @@ enum GNUNET_DB_QueryStatus TEH_PG_insert_partner (void *cls, - const struct TALER_MasterPublicKeyP *master_pub, - struct GNUNET_TIME_Timestamp start_date, - struct GNUNET_TIME_Timestamp end_date, - struct GNUNET_TIME_Relative wad_frequency, - const struct TALER_Amount *wad_fee, - const char *partner_base_url, - const struct TALER_MasterSignatureP *master_sig) + const struct TALER_MasterPublicKeyP *master_pub, + struct GNUNET_TIME_Timestamp start_date, + struct GNUNET_TIME_Timestamp end_date, + struct GNUNET_TIME_Relative wad_frequency, + const struct TALER_Amount *wad_fee, + const char *partner_base_url, + const struct TALER_MasterSignatureP *master_sig) { struct PostgresClosure *pg = cls; struct GNUNET_PQ_QueryParam params[] = { @@ -61,10 +61,9 @@ TEH_PG_insert_partner (void *cls, " ,master_sig" " ,partner_base_url" " ) VALUES " - " ($1, $2, $3, $4, $5, $6, $7, $8);"); + " ($1, $2, $3, $4, $5, $6, $7, $8)" + " ON CONFLICT DO NOTHING;"); return GNUNET_PQ_eval_prepared_non_select (pg->conn, "insert_partner", params); } - - diff --git a/src/include/taler_exchangedb_plugin.h b/src/include/taler_exchangedb_plugin.h index 146321eab..d6bf798c5 100644 --- a/src/include/taler_exchangedb_plugin.h +++ b/src/include/taler_exchangedb_plugin.h @@ -6602,6 +6602,7 @@ struct TALER_EXCHANGEDB_Plugin * @param is_active true to enable, false to set as inactive * @param read_only true to set read-only access * @param last_change when was the change made effective + * @param[out] previous_change when was the previous change made * @return database transaction status */ enum GNUNET_DB_QueryStatus @@ -6612,7 +6613,8 @@ struct TALER_EXCHANGEDB_Plugin const char *decider_name, bool is_active, bool read_only, - struct GNUNET_TIME_Absolute last_change); + struct GNUNET_TIME_Timestamp last_change, + struct GNUNET_TIME_Timestamp *previous_change); /** @@ -6727,6 +6729,9 @@ struct TALER_EXCHANGEDB_Plugin * @param justification human-readable text justifying the decision * @param decider_pub public key of the staff member * @param decider_sig signature of the staff member + * @param[out] invalid_officer set to TRUE if @a decider_pub is not allowed to make decisions right now + * @param[out] last_date set to the previous decision time; + * the INSERT is not performed if @a last_date is not before @a decision_time * @return database transaction status */ enum GNUNET_DB_QueryStatus @@ -6735,10 +6740,12 @@ struct TALER_EXCHANGEDB_Plugin const struct TALER_PaytoHashP *h_payto, const struct TALER_Amount *new_threshold, enum TALER_AmlDecisionState new_status, - struct GNUNET_TIME_Absolute decision_time, + struct GNUNET_TIME_Timestamp decision_time, const char *justification, const struct TALER_AmlOfficerPublicKeyP *decider_pub, - const struct TALER_AmlOfficerSignatureP *decider_sig); + const struct TALER_AmlOfficerSignatureP *decider_sig, + bool *invalid_officer, + struct GNUNET_TIME_Timestamp *last_date); }; diff --git a/src/lib/exchange_api_add_aml_decision.c b/src/lib/exchange_api_add_aml_decision.c index d4b0d1250..c6283532b 100644 --- a/src/lib/exchange_api_add_aml_decision.c +++ b/src/lib/exchange_api_add_aml_decision.c @@ -167,7 +167,7 @@ TALER_EXCHANGE_add_aml_decision ( sizeof (opus)); *end = '\0'; GNUNET_asprintf (&path, - "aml-decision/%s", + "aml/%s/decision", opus); wh->url = TALER_url_join (url, path, diff --git a/src/lib/exchange_api_management_add_partner.c b/src/lib/exchange_api_management_add_partner.c index 264fd664e..75fb8aa6f 100644 --- a/src/lib/exchange_api_management_add_partner.c +++ b/src/lib/exchange_api_management_add_partner.c @@ -66,7 +66,7 @@ struct TALER_EXCHANGE_ManagementAddPartner /** * Function called when we're done processing the - * HTTP POST /aml-decision/$OFFICER_PUB request. + * HTTP POST /management/partners request. * * @param cls the `struct TALER_EXCHANGE_ManagementAddPartner *` * @param response_code HTTP response code, 0 on error @@ -145,25 +145,9 @@ TALER_EXCHANGE_management_add_partner ( wh->cb = cb; wh->cb_cls = cb_cls; wh->ctx = ctx; - { - char *path; - char opus[sizeof (*partner_pub) * 2]; - char *end; - - end = GNUNET_STRINGS_data_to_string ( - partner_pub, - sizeof (*partner_pub), - opus, - sizeof (opus)); - *end = '\0'; - GNUNET_asprintf (&path, - "management/partners/%s", - opus); - wh->url = TALER_url_join (url, - path, - NULL); - GNUNET_free (path); - } + wh->url = TALER_url_join (url, + "management/partners", + NULL); if (NULL == wh->url) { GNUNET_log (GNUNET_ERROR_TYPE_ERROR, @@ -180,6 +164,8 @@ TALER_EXCHANGE_management_add_partner ( end_date), GNUNET_JSON_pack_time_rel ("wad_frequency", wad_frequency), + GNUNET_JSON_pack_data_auto ("partner_pub", + &partner_pub), GNUNET_JSON_pack_data_auto ("master_sig", &master_sig), TALER_JSON_pack_amount ("wad_fee", diff --git a/src/lib/exchange_api_management_update_aml_officer.c b/src/lib/exchange_api_management_update_aml_officer.c index 96ff5a4c7..6e1669465 100644 --- a/src/lib/exchange_api_management_update_aml_officer.c +++ b/src/lib/exchange_api_management_update_aml_officer.c @@ -144,25 +144,9 @@ TALER_EXCHANGE_management_update_aml_officer ( wh->cb = cb; wh->cb_cls = cb_cls; wh->ctx = ctx; - { - char *path; - char opus[sizeof (*officer_pub) * 2]; - char *end; - - end = GNUNET_STRINGS_data_to_string ( - officer_pub, - sizeof (*officer_pub), - opus, - sizeof (opus)); - *end = '\0'; - GNUNET_asprintf (&path, - "management/aml-officers/%s", - opus); - wh->url = TALER_url_join (url, - path, - NULL); - GNUNET_free (path); - } + wh->url = TALER_url_join (url, + "management/aml-officers", + NULL); if (NULL == wh->url) { GNUNET_log (GNUNET_ERROR_TYPE_ERROR, @@ -173,6 +157,8 @@ TALER_EXCHANGE_management_update_aml_officer ( body = GNUNET_JSON_PACK ( GNUNET_JSON_pack_string ("officer_name", officer_name), + GNUNET_JSON_pack_data_auto ("officer_pub", + officer_pub), GNUNET_JSON_pack_data_auto ("master_sig", master_sig), GNUNET_JSON_pack_bool ("is_active",