diff options
Diffstat (limited to 'src/exchange/taler-exchange-httpd_kyc-proof.c')
-rw-r--r-- | src/exchange/taler-exchange-httpd_kyc-proof.c | 344 |
1 files changed, 233 insertions, 111 deletions
diff --git a/src/exchange/taler-exchange-httpd_kyc-proof.c b/src/exchange/taler-exchange-httpd_kyc-proof.c index 6d06f0c82..bad377a2a 100644 --- a/src/exchange/taler-exchange-httpd_kyc-proof.c +++ b/src/exchange/taler-exchange-httpd_kyc-proof.c @@ -1,6 +1,6 @@ /* This file is part of TALER - Copyright (C) 2021-2022 Taler Systems SA + Copyright (C) 2021-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 @@ -23,11 +23,12 @@ #include <gnunet/gnunet_json_lib.h> #include <jansson.h> #include <microhttpd.h> -#include <pthread.h> #include "taler_attributes.h" #include "taler_json_lib.h" #include "taler_kyclogic_lib.h" #include "taler_mhd_lib.h" +#include "taler_templating_lib.h" +#include "taler-exchange-httpd_common_kyc.h" #include "taler-exchange-httpd_kyc-proof.h" #include "taler-exchange-httpd_responses.h" @@ -69,6 +70,11 @@ struct KycProofContext struct TALER_KYCLOGIC_ProofHandle *ph; /** + * KYC AML trigger operation. + */ + struct TEH_KycAmlTrigger *kat; + + /** * Process information about the user for the plugin from the database, can * be NULL. */ @@ -160,6 +166,101 @@ TEH_kyc_proof_cleanup (void) /** + * Function called after the KYC-AML trigger is done. + * + * @param cls closure + * @param http_status final HTTP status to return + * @param[in] response final HTTP ro return + */ +static void +proof_finish ( + void *cls, + unsigned int http_status, + struct MHD_Response *response) +{ + struct KycProofContext *kpc = cls; + + kpc->kat = NULL; + kpc->response_code = http_status; + kpc->response = response; + kpc_resume (kpc); +} + + +/** + * Generate HTML error for @a connection using @a template. + * + * @param connection HTTP client connection + * @param template template to expand + * @param[in,out] http_status HTTP status of the response + * @param ec Taler error code to return + * @param message extended message to return + * @return MHD response object + */ +struct MHD_Response * +make_html_error (struct MHD_Connection *connection, + const char *template, + unsigned int *http_status, + enum TALER_ErrorCode ec, + const char *message) +{ + struct MHD_Response *response = NULL; + json_t *body; + + body = GNUNET_JSON_PACK ( + GNUNET_JSON_pack_allow_null ( + GNUNET_JSON_pack_string ("message", + message)), + TALER_JSON_pack_ec ( + ec)); + GNUNET_break ( + GNUNET_SYSERR != + TALER_TEMPLATING_build (connection, + http_status, + template, + NULL, + NULL, + body, + &response)); + json_decref (body); + return response; +} + + +/** + * Respond with an HTML message on the given @a rc. + * + * @param[in,out] rc request to respond to + * @param http_status HTTP status code to use + * @param template template to fill in + * @param ec error code to use for the template + * @param message additional message to return + * @return MHD result code + */ +static MHD_RESULT +respond_html_ec (struct TEH_RequestContext *rc, + unsigned int http_status, + const char *template, + enum TALER_ErrorCode ec, + const char *message) +{ + struct MHD_Response *response; + MHD_RESULT res; + + response = make_html_error (rc->connection, + template, + &http_status, + ec, + message); + res = MHD_queue_response (rc->connection, + http_status, + response); + MHD_destroy_response (response); + return res; +} + + +/** * Function called with the result of a proof check operation. * * Note that the "decref" for the @a response @@ -192,74 +293,104 @@ proof_cb ( kpc->ph = NULL; GNUNET_async_scope_enter (&rc->async_scope_id, &old_scope); - - if (TALER_KYCLOGIC_STATUS_SUCCESS == status) + switch (status) { - enum GNUNET_DB_QueryStatus qs; - size_t eas; - void *ea; - const char *birthdate; - struct GNUNET_ShortHashCode kyc_prox; - - TALER_CRYPTO_attributes_to_kyc_prox (attributes, - &kyc_prox); - birthdate = json_string_value (json_object_get (attributes, - TALER_ATTRIBUTE_BIRTHDATE)); - TALER_CRYPTO_kyc_attributes_encrypt (&TEH_attribute_key, - attributes, - &ea, - &eas); - qs = TEH_plugin->insert_kyc_attributes ( - TEH_plugin->cls, - &kpc->h_payto, - &kyc_prox, - kpc->provider_section, - birthdate, - GNUNET_TIME_timestamp_get (), - GNUNET_TIME_absolute_to_timestamp (expiration), - eas, - ea); - GNUNET_free (ea); - if (GNUNET_DB_STATUS_HARD_ERROR == qs) + case TALER_KYCLOGIC_STATUS_SUCCESS: + GNUNET_log (GNUNET_ERROR_TYPE_INFO, + "KYC process #%llu succeeded with KYC provider\n", + (unsigned long long) kpc->process_row); + kpc->kat = TEH_kyc_finished (&rc->async_scope_id, + kpc->process_row, + &kpc->h_payto, + kpc->provider_section, + provider_user_id, + provider_legitimization_id, + expiration, + attributes, + http_status, + response, + &proof_finish, + kpc); + if (NULL == kpc->kat) { - GNUNET_break (0); + http_status = MHD_HTTP_INTERNAL_SERVER_ERROR; if (NULL != response) MHD_destroy_response (response); - kpc->response_code = MHD_HTTP_INTERNAL_SERVER_ERROR; - kpc->response = TALER_MHD_make_error (TALER_EC_GENERIC_DB_STORE_FAILED, - "insert_kyc_attributes"); - GNUNET_async_scope_restore (&old_scope); - return; + response = make_html_error (kpc->rc->connection, + "kyc-proof-internal-error", + &http_status, + TALER_EC_EXCHANGE_GENERIC_BAD_CONFIGURATION, + "[exchange] AML_KYC_TRIGGER"); } - qs = TEH_plugin->update_kyc_process_by_row (TEH_plugin->cls, - kpc->process_row, - kpc->provider_section, - &kpc->h_payto, - provider_user_id, - provider_legitimization_id, - expiration); - if (GNUNET_DB_STATUS_HARD_ERROR == qs) + break; + case TALER_KYCLOGIC_STATUS_FAILED: + case TALER_KYCLOGIC_STATUS_PROVIDER_FAILED: + case TALER_KYCLOGIC_STATUS_USER_ABORTED: + case TALER_KYCLOGIC_STATUS_ABORTED: + GNUNET_assert (NULL == kpc->kat); + GNUNET_log (GNUNET_ERROR_TYPE_INFO, + "KYC process %s/%s (Row #%llu) failed: %d\n", + provider_user_id, + provider_legitimization_id, + (unsigned long long) kpc->process_row, + status); + if (5 == http_status / 100) { - GNUNET_break (0); + char *msg; + + /* OAuth2 server had a problem, do NOT log this as a KYC failure */ if (NULL != response) MHD_destroy_response (response); - kpc->response_code = MHD_HTTP_INTERNAL_SERVER_ERROR; - kpc->response = TALER_MHD_make_error (TALER_EC_GENERIC_DB_STORE_FAILED, - "set_kyc_ok"); - GNUNET_async_scope_restore (&old_scope); - return; + GNUNET_asprintf (&msg, + "Failure by KYC provider (HTTP status %u)\n", + http_status); + http_status = MHD_HTTP_BAD_GATEWAY; + response = make_html_error (kpc->rc->connection, + "kyc-proof-internal-error", + &http_status, + TALER_EC_EXCHANGE_KYC_GENERIC_PROVIDER_UNEXPECTED_REPLY, + msg); + GNUNET_free (msg); + } + else + { + if (! TEH_kyc_failed (kpc->process_row, + &kpc->h_payto, + kpc->provider_section, + provider_user_id, + provider_legitimization_id)) + { + GNUNET_break (0); + if (NULL != response) + MHD_destroy_response (response); + http_status = MHD_HTTP_INTERNAL_SERVER_ERROR; + response = make_html_error (kpc->rc->connection, + "kyc-proof-internal-error", + &http_status, + TALER_EC_GENERIC_DB_STORE_FAILED, + "insert_kyc_failure"); + } } + break; + default: + GNUNET_log (GNUNET_ERROR_TYPE_INFO, + "KYC status of %s/%s (Row #%llu) is %d\n", + provider_user_id, + provider_legitimization_id, + (unsigned long long) kpc->process_row, + (int) status); + break; } - else + if (NULL == kpc->kat) { GNUNET_log (GNUNET_ERROR_TYPE_INFO, "KYC process #%llu failed with status %d\n", (unsigned long long) kpc->process_row, status); + proof_finish (kpc, + http_status, + response); } - kpc->response_code = http_status; - kpc->response = response; - kpc_resume (kpc); GNUNET_async_scope_restore (&old_scope); } @@ -279,6 +410,11 @@ clean_kpc (struct TEH_RequestContext *rc) kpc->logic->proof_cancel (kpc->ph); kpc->ph = NULL; } + if (NULL != kpc->kat) + { + TEH_kyc_finished_cancel (kpc->kat); + kpc->kat = NULL; + } if (NULL != kpc->response) { MHD_destroy_response (kpc->response); @@ -297,7 +433,6 @@ TEH_handler_kyc_proof ( { struct KycProofContext *kpc = rc->rh_ctx; const char *provider_section_or_logic = args[0]; - const char *h_payto; if (NULL == kpc) { @@ -305,38 +440,19 @@ TEH_handler_kyc_proof ( if (NULL == provider_section_or_logic) { GNUNET_break_op (0); - return TALER_MHD_reply_with_error (rc->connection, - MHD_HTTP_NOT_FOUND, - TALER_EC_GENERIC_ENDPOINT_UNKNOWN, - "'/kyc-proof/$PROVIDER_SECTION?state=$H_PAYTO' required"); - } - h_payto = MHD_lookup_connection_value (rc->connection, - MHD_GET_ARGUMENT_KIND, - "state"); - if (NULL == h_payto) - { - GNUNET_break_op (0); - return TALER_MHD_reply_with_error (rc->connection, - MHD_HTTP_BAD_REQUEST, - TALER_EC_GENERIC_PARAMETER_MISSING, - "h_payto"); + return respond_html_ec (rc, + MHD_HTTP_NOT_FOUND, + "kyc-proof-endpoint-unknown", + TALER_EC_GENERIC_ENDPOINT_UNKNOWN, + "'/kyc-proof/$PROVIDER_SECTION?state=$H_PAYTO' required"); } kpc = GNUNET_new (struct KycProofContext); kpc->rc = rc; rc->rh_ctx = kpc; rc->rh_cleaner = &clean_kpc; - if (GNUNET_OK != - GNUNET_STRINGS_string_to_data (h_payto, - strlen (h_payto), - &kpc->h_payto, - sizeof (kpc->h_payto))) - { - GNUNET_break_op (0); - return TALER_MHD_reply_with_error (rc->connection, - MHD_HTTP_BAD_REQUEST, - TALER_EC_GENERIC_PARAMETER_MALFORMED, - "h_payto"); - } + TALER_MHD_parse_request_arg_auto_t (rc->connection, + "state", + &kpc->h_payto); if (GNUNET_OK != TALER_KYCLOGIC_lookup_logic (provider_section_or_logic, &kpc->logic, @@ -344,10 +460,11 @@ TEH_handler_kyc_proof ( &kpc->provider_section)) { GNUNET_break_op (0); - return TALER_MHD_reply_with_error (rc->connection, - MHD_HTTP_NOT_FOUND, - TALER_EC_EXCHANGE_KYC_GENERIC_LOGIC_UNKNOWN, - provider_section_or_logic); + return respond_html_ec (rc, + MHD_HTTP_NOT_FOUND, + "kyc-proof-target-unknown", + TALER_EC_EXCHANGE_KYC_GENERIC_LOGIC_UNKNOWN, + provider_section_or_logic); } if (NULL != kpc->provider_section) { @@ -358,10 +475,11 @@ TEH_handler_kyc_proof ( kpc->provider_section)) { GNUNET_break_op (0); - return TALER_MHD_reply_with_error (rc->connection, - MHD_HTTP_BAD_REQUEST, - TALER_EC_GENERIC_PARAMETER_MALFORMED, - "PROVIDER_SECTION"); + return respond_html_ec (rc, + MHD_HTTP_BAD_REQUEST, + "kyc-proof-bad-request", + TALER_EC_GENERIC_PARAMETER_MALFORMED, + "PROVIDER_SECTION"); } qs = TEH_plugin->lookup_kyc_process_by_account ( @@ -376,26 +494,28 @@ TEH_handler_kyc_proof ( { case GNUNET_DB_STATUS_HARD_ERROR: case GNUNET_DB_STATUS_SOFT_ERROR: - return TALER_MHD_reply_with_ec (rc->connection, - TALER_EC_GENERIC_DB_STORE_FAILED, - "lookup_kyc_requirement_by_account"); + return respond_html_ec (rc, + MHD_HTTP_INTERNAL_SERVER_ERROR, + "kyc-proof-internal-error", + TALER_EC_GENERIC_DB_FETCH_FAILED, + "lookup_kyc_process_by_account"); case GNUNET_DB_STATUS_SUCCESS_NO_RESULTS: - return TALER_MHD_reply_with_error (rc->connection, - MHD_HTTP_NOT_FOUND, - TALER_EC_EXCHANGE_KYC_PROOF_REQUEST_UNKNOWN, - kpc->provider_section); + return respond_html_ec (rc, + MHD_HTTP_NOT_FOUND, + "kyc-proof-target-unknown", + TALER_EC_EXCHANGE_KYC_PROOF_REQUEST_UNKNOWN, + kpc->provider_section); case GNUNET_DB_STATUS_SUCCESS_ONE_RESULT: break; } if (GNUNET_TIME_absolute_is_future (expiration)) { /* KYC not required */ - return TALER_MHD_reply_static ( - rc->connection, - MHD_HTTP_NO_CONTENT, - NULL, - NULL, - 0); + return respond_html_ec (rc, + MHD_HTTP_OK, + "kyc-proof-already-done", + TALER_EC_NONE, + NULL); } } kpc->ph = kpc->logic->proof (kpc->logic->cls, @@ -410,10 +530,11 @@ TEH_handler_kyc_proof ( if (NULL == kpc->ph) { GNUNET_break (0); - return TALER_MHD_reply_with_error (rc->connection, - MHD_HTTP_INTERNAL_SERVER_ERROR, - TALER_EC_GENERIC_INTERNAL_INVARIANT_FAILURE, - "could not start proof with KYC logic"); + return respond_html_ec (rc, + MHD_HTTP_INTERNAL_SERVER_ERROR, + "kyc-proof-internal-error", + TALER_EC_GENERIC_INTERNAL_INVARIANT_FAILURE, + "could not start proof with KYC logic"); } @@ -428,10 +549,11 @@ TEH_handler_kyc_proof ( if (NULL == kpc->response) { GNUNET_break (0); - return TALER_MHD_reply_with_error (rc->connection, - MHD_HTTP_INTERNAL_SERVER_ERROR, - TALER_EC_GENERIC_INTERNAL_INVARIANT_FAILURE, - "handler resumed without response"); + return respond_html_ec (rc, + MHD_HTTP_INTERNAL_SERVER_ERROR, + "kyc-proof-internal-error", + TALER_EC_GENERIC_INTERNAL_INVARIANT_FAILURE, + "handler resumed without response"); } /* return response from KYC logic */ |