diff options
Diffstat (limited to 'src/exchange')
-rw-r--r-- | src/exchange/Makefile.am | 3 | ||||
-rw-r--r-- | src/exchange/taler-exchange-httpd.c | 226 | ||||
-rw-r--r-- | src/exchange/taler-exchange-httpd.h | 4 | ||||
-rw-r--r-- | src/exchange/taler-exchange-httpd_aml-decision.c | 65 | ||||
-rw-r--r-- | src/exchange/taler-exchange-httpd_aml-decision.h | 45 | ||||
-rw-r--r-- | src/exchange/taler-exchange-httpd_kyc-proof.c | 3 | ||||
-rw-r--r-- | src/exchange/taler-exchange-httpd_management.h | 26 | ||||
-rw-r--r-- | src/exchange/taler-exchange-httpd_management_aml-officers.c | 29 | ||||
-rw-r--r-- | src/exchange/taler-exchange-httpd_management_partners.c | 29 |
9 files changed, 378 insertions, 52 deletions
diff --git a/src/exchange/Makefile.am b/src/exchange/Makefile.am index 29596c38..ffcfc5e9 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 def4fd4a..5501687f 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" @@ -323,6 +324,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. * * @param rc request context @@ -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 2be26f14..7715444a 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 c93c1066..ae2667c1 100644 --- a/src/exchange/taler-exchange-httpd_aml-decision.c +++ b/src/exchange/taler-exchange-httpd_aml-decision.c @@ -30,28 +30,33 @@ #include "taler-exchange-httpd_responses.h" +/** + * How often do we try the DB operation at most? + */ +#define MAX_RETRIES 10 + + 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 +81,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 ( @@ -95,27 +100,29 @@ TEH_handler_management_post_aml_decision ( enum GNUNET_DB_QueryStatus qs; struct GNUNET_TIME_Timestamp last_date; bool invalid_officer; + unsigned int retries_left = MAX_RETRIES; 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); + 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); + if (0 == --retries_left) + break; } 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 +134,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 00000000..8dd3bb3f --- /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 <http://www.gnu.org/licenses/> +*/ +/** + * @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 <microhttpd.h> +#include "taler-exchange-httpd.h" + + +/** + * Handle an "/aml/$OFFICER_PUB/decision" request. Parses the decision + * details, checks the signatures and if appropriately authorized executes + * 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_kyc-proof.c b/src/exchange/taler-exchange-httpd_kyc-proof.c index d3716498..1904c4ac 100644 --- a/src/exchange/taler-exchange-httpd_kyc-proof.c +++ b/src/exchange/taler-exchange-httpd_kyc-proof.c @@ -169,6 +169,7 @@ TEH_kyc_proof_cleanup (void) * @param provider_user_id set to user ID at the provider, or NULL if not supported or unknown * @param provider_legitimization_id set to legitimization process ID at the provider, or NULL if not supported or unknown * @param expiration until when is the KYC check valid + * @param attributes user attributes returned by the provider * @param http_status HTTP status code of @a response * @param[in] response to return to the HTTP client */ @@ -179,6 +180,7 @@ proof_cb ( const char *provider_user_id, const char *provider_legitimization_id, struct GNUNET_TIME_Absolute expiration, + const json_t *attributes, unsigned int http_status, struct MHD_Response *response) { @@ -194,6 +196,7 @@ proof_cb ( { enum GNUNET_DB_QueryStatus qs; + // FIXME: also store 'attributes' in DB! qs = TEH_plugin->update_kyc_process_by_row (TEH_plugin->cls, kpc->process_row, kpc->provider_section, diff --git a/src/exchange/taler-exchange-httpd_management.h b/src/exchange/taler-exchange-httpd_management.h index a5a8b0e7..2fc1fe8d 100644 --- a/src/exchange/taler-exchange-httpd_management.h +++ b/src/exchange/taler-exchange-httpd_management.h @@ -175,6 +175,32 @@ TEH_handler_management_post_drain ( /** + * 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. * * @return #GNUNET_OK on success diff --git a/src/exchange/taler-exchange-httpd_management_aml-officers.c b/src/exchange/taler-exchange-httpd_management_aml-officers.c index 139ccdb2..abc7c3d8 100644 --- a/src/exchange/taler-exchange-httpd_management_aml-officers.c +++ b/src/exchange/taler-exchange-httpd_management_aml-officers.c @@ -31,6 +31,12 @@ #include "taler-exchange-httpd_responses.h" +/** + * How often do we try the DB operation at most? + */ +#define MAX_RETRIES 10 + + MHD_RESULT TEH_handler_management_aml_officers ( struct MHD_Connection *connection, @@ -90,16 +96,19 @@ TEH_handler_management_aml_officers ( { enum GNUNET_DB_QueryStatus qs; struct GNUNET_TIME_Timestamp last_date; + unsigned int retries_left = MAX_RETRIES; do { - qs = TEH_plugin->set_aml_officer (TEH_plugin->cls, - &officer_pub, - officer_name, - change_date, - is_active, - read_only, - &master_sig, - &last_date); + qs = TEH_plugin->insert_aml_officer (TEH_plugin->cls, + &officer_pub, + &master_sig, + officer_name, + is_active, + read_only, + change_date, + &last_date); + if (0 == --retries_left) + break; } while (GNUNET_DB_STATUS_SOFT_ERROR == qs); if (qs < 0) { @@ -107,13 +116,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 5d860120..1c0d4a9a 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, |