diff options
author | Özgür Kesim <oec-taler@kesim.org> | 2023-06-03 10:45:31 +0200 |
---|---|---|
committer | Özgür Kesim <oec-taler@kesim.org> | 2023-06-03 10:45:31 +0200 |
commit | 80a1b8f5240e8307e1c73862fb8810d7c2bdc874 (patch) | |
tree | 5661068ec9976aa8711dc931355f6bd87ab35fd6 /src/exchange | |
parent | 4a31a180a4595aa040060e91ccde4f839aa02f72 (diff) | |
parent | 2ea3ae1008020589b43a51663c45556a08547212 (diff) |
Merge branch 'master' into age-withdraw
Diffstat (limited to 'src/exchange')
-rw-r--r-- | src/exchange/Makefile.am | 4 | ||||
-rw-r--r-- | src/exchange/exchange.conf | 13 | ||||
-rw-r--r-- | src/exchange/taler-exchange-aggregator.c | 6 | ||||
-rw-r--r-- | src/exchange/taler-exchange-httpd.c | 38 | ||||
-rw-r--r-- | src/exchange/taler-exchange-httpd.h | 10 | ||||
-rw-r--r-- | src/exchange/taler-exchange-httpd_aml-decision-get.c | 3 | ||||
-rw-r--r-- | src/exchange/taler-exchange-httpd_common_kyc.c | 239 | ||||
-rw-r--r-- | src/exchange/taler-exchange-httpd_common_kyc.h | 99 | ||||
-rw-r--r-- | src/exchange/taler-exchange-httpd_keys.c | 2 | ||||
-rw-r--r-- | src/exchange/taler-exchange-httpd_kyc-proof.c | 112 | ||||
-rw-r--r-- | src/exchange/taler-exchange-httpd_kyc-webhook.c | 113 | ||||
-rw-r--r-- | src/exchange/taler-exchange-httpd_reserves_attest.c | 3 | ||||
-rw-r--r-- | src/exchange/taler-exchange-httpd_reserves_get_attest.c | 3 | ||||
-rwxr-xr-x | src/exchange/taler-exchange-kyc-aml-pep-trigger.sh | 7 | ||||
-rw-r--r-- | src/exchange/test_taler_exchange_httpd.conf | 3 |
15 files changed, 527 insertions, 128 deletions
diff --git a/src/exchange/Makefile.am b/src/exchange/Makefile.am index 3d87a2a5..ba74a10f 100644 --- a/src/exchange/Makefile.am +++ b/src/exchange/Makefile.am @@ -15,6 +15,8 @@ pkgcfg_DATA = \ exchange.conf # Programs +bin_SCRIPTS = \ + taler-exchange-kyc-aml-pep-trigger.sh bin_PROGRAMS = \ taler-exchange-aggregator \ @@ -131,6 +133,7 @@ taler_exchange_httpd_SOURCES = \ taler-exchange-httpd_age-withdraw.c taler-exchange-httpd_age-withdraw.h \ taler-exchange-httpd_age-withdraw_reveal.c taler-exchange-httpd_age-withdraw_reveal.h \ taler-exchange-httpd_common_deposit.c taler-exchange-httpd_common_deposit.h \ + taler-exchange-httpd_common_kyc.c taler-exchange-httpd_common_kyc.h \ taler-exchange-httpd_config.c taler-exchange-httpd_config.h \ taler-exchange-httpd_contract.c taler-exchange-httpd_contract.h \ taler-exchange-httpd_csr.c taler-exchange-httpd_csr.h \ @@ -227,4 +230,5 @@ EXTRA_DIST = \ test_taler_exchange_httpd.get \ test_taler_exchange_httpd.post \ exchange.conf \ + $(bin_SCRIPTS) \ $(check_SCRIPTS) diff --git a/src/exchange/exchange.conf b/src/exchange/exchange.conf index 38e5816f..d1558a49 100644 --- a/src/exchange/exchange.conf +++ b/src/exchange/exchange.conf @@ -6,10 +6,23 @@ # This must be adjusted to your actual installation. # MASTER_PUBLIC_KEY = 98NJW3CQHZQGQXTY3K85K531XKPAPAVV4Q5V8PYYRR00NJGZWNVG +# Must be set to the threshold above which transactions +# are flagged for AML review. +# AML_THRESHOLD = + +# Specifies a program (binary) to run on KYC attribute data to decide +# whether we should immediately flag an account for AML review. +# The KYC attribute data will be passed on standard-input. +# Return non-zero to trigger AML review of the new user. +KYC_AML_TRIGGER = true + # Attribute encryption key for storing attributes encrypted # in the database. Should be a high-entropy nonce. ATTRIBUTE_ENCRYPTION_KEY = SET_ME_PLEASE +# Set to NO to disable tipping. +ENABLE_TIPPING = YES + # How long do we allow /keys to be cached at most? The actual # limit is the minimum of this value and the first expected # significant change in /keys based on the expiration times. diff --git a/src/exchange/taler-exchange-aggregator.c b/src/exchange/taler-exchange-aggregator.c index 38110a5e..0073d85e 100644 --- a/src/exchange/taler-exchange-aggregator.c +++ b/src/exchange/taler-exchange-aggregator.c @@ -303,17 +303,17 @@ parse_aggregator_config (void) (TALER_amount_is_zero (¤cy_round_unit)) ) { GNUNET_log (GNUNET_ERROR_TYPE_ERROR, - "Need non-zero amount in section `TALER' under `CURRENCY_ROUND_UNIT'\n"); + "Need non-zero amount in section `taler' under `CURRENCY_ROUND_UNIT'\n"); return GNUNET_SYSERR; } if (GNUNET_OK != TALER_config_get_amount (cfg, - "taler", + "exchange", "AML_THRESHOLD", &aml_threshold)) { GNUNET_log (GNUNET_ERROR_TYPE_ERROR, - "Need amount in section `TALER' under `AML_THRESHOLD'\n"); + "Need amount in section `exchange' under `AML_THRESHOLD'\n"); return GNUNET_SYSERR; } diff --git a/src/exchange/taler-exchange-httpd.c b/src/exchange/taler-exchange-httpd.c index 7e11655c..348967f7 100644 --- a/src/exchange/taler-exchange-httpd.c +++ b/src/exchange/taler-exchange-httpd.c @@ -154,6 +154,16 @@ struct TALER_EXCHANGEDB_Plugin *TEH_plugin; char *TEH_currency; /** + * Name of the KYC-AML-trigger evaluation binary. + */ +char *TEH_kyc_aml_trigger; + +/** + * Option set to #GNUNET_YES if tipping is enabled. + */ +int TEH_enable_tipping; + +/** * What is the largest amount we allow a peer to * merge into a reserve before always triggering * an AML check? @@ -1845,6 +1855,17 @@ exchange_serve_process_config (void) return GNUNET_SYSERR; } if (GNUNET_OK != + GNUNET_CONFIGURATION_get_value_string (TEH_cfg, + "exchange", + "KYC_AML_TRIGGER", + &TEH_kyc_aml_trigger)) + { + GNUNET_log_config_missing (GNUNET_ERROR_TYPE_ERROR, + "exchange", + "KYC_AML_TRIGGER"); + return GNUNET_SYSERR; + } + if (GNUNET_OK != TALER_config_get_currency (TEH_cfg, &TEH_currency)) { @@ -1855,19 +1876,30 @@ exchange_serve_process_config (void) } if (GNUNET_OK != TALER_config_get_amount (TEH_cfg, - "taler", + "exchange", "AML_THRESHOLD", &TEH_aml_threshold)) { GNUNET_log (GNUNET_ERROR_TYPE_ERROR, - "Need amount in section `TALER' under `AML_THRESHOLD'\n"); + "Need amount in section `exchange' under `AML_THRESHOLD'\n"); return GNUNET_SYSERR; } if (0 != strcmp (TEH_currency, TEH_aml_threshold.currency)) { GNUNET_log (GNUNET_ERROR_TYPE_ERROR, - "Amount in section `TALER' under `AML_THRESHOLD' uses the wrong currency!\n"); + "Amount in section `exchange' under `AML_THRESHOLD' uses the wrong currency!\n"); + return GNUNET_SYSERR; + } + TEH_enable_tipping + = GNUNET_CONFIGURATION_get_value_yesno ( + TEH_cfg, + "exchange", + "ENABLE_TIPPING"); + if (GNUNET_SYSERR == TEH_enable_tipping) + { + GNUNET_log (GNUNET_ERROR_TYPE_ERROR, + "Need YES or NO in section `exchange' under `ENABLE_TIPPING'\n"); return GNUNET_SYSERR; } if (GNUNET_OK != diff --git a/src/exchange/taler-exchange-httpd.h b/src/exchange/taler-exchange-httpd.h index 5ab0ea92..24e08772 100644 --- a/src/exchange/taler-exchange-httpd.h +++ b/src/exchange/taler-exchange-httpd.h @@ -65,6 +65,11 @@ extern int TEH_check_invariants_flag; extern int TEH_allow_keys_timetravel; /** + * Option set to #GNUNET_YES if tipping is enabled. + */ +extern int TEH_enable_tipping; + +/** * Main directory with revocation data. */ extern char *TEH_revocation_directory; @@ -98,6 +103,11 @@ extern struct TALER_EXCHANGEDB_Plugin *TEH_plugin; extern char *TEH_currency; /** + * Name of the KYC-AML-trigger evaluation binary. + */ +extern char *TEH_kyc_aml_trigger; + +/** * What is the largest amount we allow a peer to * merge into a reserve before always triggering * an AML check? diff --git a/src/exchange/taler-exchange-httpd_aml-decision-get.c b/src/exchange/taler-exchange-httpd_aml-decision-get.c index 6b36fe27..b4f337db 100644 --- a/src/exchange/taler-exchange-httpd_aml-decision-get.c +++ b/src/exchange/taler-exchange-httpd_aml-decision-get.c @@ -43,8 +43,6 @@ * @param[in,out] cls closure with a `json_t *` array to update * @param h_payto account for which the attribute data is stored * @param provider_section provider that must be checked - * @param birthdate birthdate of user, in format YYYY-MM-DD; can be NULL; - * digits can be 0 if exact day, month or year are unknown * @param collection_time when was the data collected * @param expiration_time when does the data expire * @param enc_attributes_size number of bytes in @a enc_attributes @@ -55,7 +53,6 @@ kyc_attribute_cb ( void *cls, const struct TALER_PaytoHashP *h_payto, const char *provider_section, - const char *birthdate, struct GNUNET_TIME_Timestamp collection_time, struct GNUNET_TIME_Timestamp expiration_time, size_t enc_attributes_size, diff --git a/src/exchange/taler-exchange-httpd_common_kyc.c b/src/exchange/taler-exchange-httpd_common_kyc.c new file mode 100644 index 00000000..ef917a55 --- /dev/null +++ b/src/exchange/taler-exchange-httpd_common_kyc.c @@ -0,0 +1,239 @@ +/* + 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_common_kyc.c + * @brief shared logic for finishing a KYC process + * @author Christian Grothoff + */ +#include "platform.h" +#include "taler-exchange-httpd_common_kyc.h" +#include "taler_attributes.h" +#include "taler_exchangedb_plugin.h" + +struct TEH_KycAmlTrigger +{ + + /** + * Our logging scope. + */ + struct GNUNET_AsyncScopeId scope; + + /** + * account the operation is about + */ + struct TALER_PaytoHashP account_id; + + /** + * until when is the KYC data valid + */ + struct GNUNET_TIME_Absolute expiration; + + /** + * legitimization process the KYC data is about + */ + uint64_t process_row; + + /** + * name of the configuration section of the logic that was run + */ + char *provider_section; + + /** + * set to user ID at the provider, or NULL if not supported or unknown + */ + char *provider_user_id; + + /** + * provider_legitimization_id set to legitimization process ID at the provider, or NULL if not supported or unknown + */ + char *provider_legitimization_id; + + /** + * function to call with the result + */ + TEH_KycAmlTriggerCallback cb; + + /** + * closure for @e cb + */ + void *cb_cls; + + /** + * user attributes returned by the provider + */ + json_t *attributes; + + /** + * response to return to the HTTP client + */ + struct MHD_Response *response; + + /** + * Handle to an external process that evaluates the + * need to run AML on the account. + */ + struct TALER_JSON_ExternalConversion *kyc_aml; + + /** + * HTTP status code of @e response + */ + unsigned int http_status; + +}; + + +/** + * Type of a callback that receives a JSON @a result. + * + * @param cls closure of type `struct TEH_KycAmlTrigger *` + * @param status_type how did the process die + * @param code termination status code from the process + * @param result some JSON result, NULL if we failed to get an JSON output + */ +static void +kyc_aml_finished (void *cls, + enum GNUNET_OS_ProcessStatusType status_type, + unsigned long code, + const json_t *result) +{ + struct TEH_KycAmlTrigger *kat = cls; + enum GNUNET_DB_QueryStatus qs; + size_t eas; + void *ea; + const char *birthdate; + unsigned int birthday; + struct GNUNET_ShortHashCode kyc_prox; + struct GNUNET_AsyncScopeSave old_scope; + + kat->kyc_aml = NULL; + GNUNET_async_scope_enter (&kat->scope, + &old_scope); + TALER_CRYPTO_attributes_to_kyc_prox (kat->attributes, + &kyc_prox); + birthdate = json_string_value (json_object_get (kat->attributes, + TALER_ATTRIBUTE_BIRTHDATE)); + birthday = 0; (void) birthdate; // FIXME-Oec: calculate birthday here... + // Convert 'birthdate' to time after 1970, then compute days. + // Then compare against max age-restriction, and if before, set to 0. + TALER_CRYPTO_kyc_attributes_encrypt (&TEH_attribute_key, + kat->attributes, + &ea, + &eas); + qs = TEH_plugin->insert_kyc_attributes ( + TEH_plugin->cls, + kat->process_row, + &kat->account_id, + &kyc_prox, + kat->provider_section, + birthday, + GNUNET_TIME_timestamp_get (), + kat->provider_user_id, + kat->provider_legitimization_id, + kat->expiration, + eas, + ea, + 0 != code); + GNUNET_free (ea); + if (GNUNET_DB_STATUS_HARD_ERROR == qs) + { + GNUNET_break (0); + if (NULL != kat->response) + MHD_destroy_response (kat->response); + kat->http_status = MHD_HTTP_INTERNAL_SERVER_ERROR; + kat->response = TALER_MHD_make_error (TALER_EC_GENERIC_DB_STORE_FAILED, + "do_insert_kyc_attributes"); + } + /* Finally, return result to main handler */ + kat->cb (kat->cb_cls, + kat->http_status, + kat->response); + kat->response = NULL; + TEH_kyc_finished_cancel (kat); + GNUNET_async_scope_restore (&old_scope); +} + + +struct TEH_KycAmlTrigger * +TEH_kyc_finished (const struct GNUNET_AsyncScopeId *scope, + uint64_t process_row, + const struct TALER_PaytoHashP *account_id, + const char *provider_section, + 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, + TEH_KycAmlTriggerCallback cb, + void *cb_cls) +{ + struct TEH_KycAmlTrigger *kat; + + kat = GNUNET_new (struct TEH_KycAmlTrigger); + kat->scope = *scope; + kat->process_row = process_row; + kat->account_id = *account_id; + kat->provider_section + = GNUNET_strdup (provider_section); + if (NULL != provider_user_id) + kat->provider_user_id + = GNUNET_strdup (provider_user_id); + if (NULL != provider_legitimization_id) + kat->provider_legitimization_id + = GNUNET_strdup (provider_legitimization_id); + kat->expiration = expiration; + kat->attributes = json_incref ((json_t*) attributes); + kat->http_status = http_status; + kat->response = response; + kat->cb = cb; + kat->cb_cls = cb_cls; + kat->kyc_aml + = TALER_JSON_external_conversion_start ( + attributes, + &kyc_aml_finished, + kat, + TEH_kyc_aml_trigger, + TEH_kyc_aml_trigger, + NULL); + if (NULL == kat->kyc_aml) + { + GNUNET_break (0); + TEH_kyc_finished_cancel (kat); + return NULL; + } + return kat; +} + + +void +TEH_kyc_finished_cancel (struct TEH_KycAmlTrigger *kat) +{ + if (NULL != kat->kyc_aml) + { + TALER_JSON_external_conversion_stop (kat->kyc_aml); + kat->kyc_aml = NULL; + } + GNUNET_free (kat->provider_section); + GNUNET_free (kat->provider_user_id); + GNUNET_free (kat->provider_legitimization_id); + json_decref (kat->attributes); + if (NULL != kat->response) + { + MHD_destroy_response (kat->response); + kat->response = NULL; + } + GNUNET_free (kat); +} diff --git a/src/exchange/taler-exchange-httpd_common_kyc.h b/src/exchange/taler-exchange-httpd_common_kyc.h new file mode 100644 index 00000000..57276604 --- /dev/null +++ b/src/exchange/taler-exchange-httpd_common_kyc.h @@ -0,0 +1,99 @@ +/* + 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_common_kyc.h + * @brief shared logic for finishing a KYC process + * @author Christian Grothoff + */ +#ifndef TALER_EXCHANGE_HTTPD_COMMON_KYC_H +#define TALER_EXCHANGE_HTTPD_COMMON_KYC_H + +#include <gnunet/gnunet_util_lib.h> +#include <gnunet/gnunet_json_lib.h> +#include <jansson.h> +#include <microhttpd.h> +#include "taler_json_lib.h" +#include "taler_mhd_lib.h" +#include "taler-exchange-httpd.h" + + +/** + * 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 + */ +typedef void +(*TEH_KycAmlTriggerCallback) ( + void *cls, + unsigned int http_status, + struct MHD_Response *response); + + +/** + * Handle for an asynchronous operation to finish + * a KYC process after running the AML trigger. + */ +struct TEH_KycAmlTrigger; + +// FIXME: also pass async log context and set it! +/** + * We have finished a KYC process and obtained new + * @a attributes for a given @a account_id. + * Check with the KYC-AML trigger to see if we need + * to initiate an AML process, and store the attributes + * in the database. Then call @a cb. + * + * @param scope the HTTP request logging scope + * @param process_row legitimization process the webhook was about + * @param account_id account the webhook was about + * @param provider_section name of the configuration section of the logic that was run + * @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 + * @param cb function to call with the result + * @param cb_cls closure for @a cb + * @return handle to cancel the operation + */ +struct TEH_KycAmlTrigger * +TEH_kyc_finished (const struct GNUNET_AsyncScopeId *scope, + uint64_t process_row, + const struct TALER_PaytoHashP *account_id, + const char *provider_section, + 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, + TEH_KycAmlTriggerCallback cb, + void *cb_cls); + + +/** + * Cancel KYC finish operation. + * + * @param[in] kat operation to abort + */ +void +TEH_kyc_finished_cancel (struct TEH_KycAmlTrigger *kat); + + +#endif diff --git a/src/exchange/taler-exchange-httpd_keys.c b/src/exchange/taler-exchange-httpd_keys.c index fa87a3b8..b39093ec 100644 --- a/src/exchange/taler-exchange-httpd_keys.c +++ b/src/exchange/taler-exchange-httpd_keys.c @@ -1857,6 +1857,8 @@ create_krd (struct TEH_KeyStateHandle *ksh, TEH_currency), GNUNET_JSON_pack_string ("asset_type", asset_type), + GNUNET_JSON_pack_bool ("tipping_allowed", + GNUNET_YES == TEH_enable_tipping), GNUNET_JSON_pack_data_auto ("master_public_key", &TEH_master_public_key), GNUNET_JSON_pack_time_rel ("reserve_closing_delay", diff --git a/src/exchange/taler-exchange-httpd_kyc-proof.c b/src/exchange/taler-exchange-httpd_kyc-proof.c index 9668ee54..b3175bd2 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,11 @@ #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-exchange-httpd_common_kyc.h" #include "taler-exchange-httpd_kyc-proof.h" #include "taler-exchange-httpd_responses.h" @@ -69,6 +69,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 +165,28 @@ 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); +} + + +/** * Function called with the result of a proof check operation. * * Note that the "decref" for the @a response @@ -192,74 +219,40 @@ proof_cb ( kpc->ph = NULL; GNUNET_async_scope_enter (&rc->async_scope_id, &old_scope); - if (TALER_KYCLOGIC_STATUS_SUCCESS == 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) - { - GNUNET_break (0); - 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; - } - 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) + 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, - "set_kyc_ok"); - GNUNET_async_scope_restore (&old_scope); - return; + response = TALER_MHD_make_error ( + TALER_EC_EXCHANGE_GENERIC_BAD_CONFIGURATION, + "[exchange] AML_KYC_TRIGGER"); } } - 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 +272,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); diff --git a/src/exchange/taler-exchange-httpd_kyc-webhook.c b/src/exchange/taler-exchange-httpd_kyc-webhook.c index f8fe711d..415e5de9 100644 --- a/src/exchange/taler-exchange-httpd_kyc-webhook.c +++ b/src/exchange/taler-exchange-httpd_kyc-webhook.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 Affero General Public License as published by the Free Software @@ -28,6 +28,7 @@ #include "taler_json_lib.h" #include "taler_mhd_lib.h" #include "taler_kyclogic_lib.h" +#include "taler-exchange-httpd_common_kyc.h" #include "taler-exchange-httpd_kyc-webhook.h" #include "taler-exchange-httpd_responses.h" @@ -54,6 +55,11 @@ struct KycWebhookContext struct TEH_RequestContext *rc; /** + * Handle for the KYC-AML trigger interaction. + */ + struct TEH_KycAmlTrigger *kat; + + /** * Plugin responsible for the webhook. */ struct TALER_KYCLOGIC_Plugin *plugin; @@ -141,6 +147,28 @@ TEH_kyc_webhook_cleanup (void) /** + * Function called after the KYC-AML trigger is done. + * + * @param cls closure with a `struct KycWebhookContext *` + * @param http_status final HTTP status to return + * @param[in] response final HTTP ro return + */ +static void +kyc_aml_webhook_finished ( + void *cls, + unsigned int http_status, + struct MHD_Response *response) +{ + struct KycWebhookContext *kwh = cls; + + kwh->kat = NULL; + kwh->response = response; + kwh->response_code = http_status; + kwh_resume (kwh); +} + + +/** * Function called with the result of a KYC webhook operation. * * Note that the "decref" for the @a response @@ -178,58 +206,27 @@ webhook_finished_cb ( switch (status) { case TALER_KYCLOGIC_STATUS_SUCCESS: - /* _successfully_ resumed case */ + kwh->kat = TEH_kyc_finished ( + &kwh->rc->async_scope_id, + process_row, + account_id, + provider_section, + provider_user_id, + provider_legitimization_id, + expiration, + attributes, + http_status, + response, + &kyc_aml_webhook_finished, + kwh); + if (NULL == kwh->kat) { - 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, - account_id, - &kyc_prox, - provider_section, - birthdate, - GNUNET_TIME_timestamp_get (), - GNUNET_TIME_absolute_to_timestamp (expiration), - eas, - ea); - GNUNET_free (ea); - if (qs < 0) - { - GNUNET_break (0); - kwh->response = TALER_MHD_make_error (TALER_EC_GENERIC_DB_STORE_FAILED, - "insert_kyc_attributes"); - kwh->response_code = MHD_HTTP_INTERNAL_SERVER_ERROR; - kwh_resume (kwh); - return; - } - qs = TEH_plugin->update_kyc_process_by_row (TEH_plugin->cls, - process_row, - provider_section, - account_id, - provider_user_id, - provider_legitimization_id, - expiration); - if (qs < 0) - { - GNUNET_break (0); - kwh->response = TALER_MHD_make_error (TALER_EC_GENERIC_DB_STORE_FAILED, - "set_kyc_ok"); - kwh->response_code = MHD_HTTP_INTERNAL_SERVER_ERROR; - kwh_resume (kwh); - return; - } + http_status = MHD_HTTP_INTERNAL_SERVER_ERROR; + if (NULL != response) + MHD_destroy_response (response); + response = TALER_MHD_make_error ( + TALER_EC_EXCHANGE_GENERIC_BAD_CONFIGURATION, + "[exchange] AML_KYC_TRIGGER"); } break; default: @@ -241,9 +238,10 @@ webhook_finished_cb ( status); break; } - kwh->response = response; - kwh->response_code = http_status; - kwh_resume (kwh); + if (NULL == kwh->kat) + kyc_aml_webhook_finished (kwh, + http_status, + response); } @@ -262,6 +260,11 @@ clean_kwh (struct TEH_RequestContext *rc) kwh->plugin->webhook_cancel (kwh->wh); kwh->wh = NULL; } + if (NULL != kwh->kat) + { + TEH_kyc_finished_cancel (kwh->kat); + kwh->kat = NULL; + } if (NULL != kwh->response) { MHD_destroy_response (kwh->response); diff --git a/src/exchange/taler-exchange-httpd_reserves_attest.c b/src/exchange/taler-exchange-httpd_reserves_attest.c index 297d8cee..d0f3614e 100644 --- a/src/exchange/taler-exchange-httpd_reserves_attest.c +++ b/src/exchange/taler-exchange-httpd_reserves_attest.c @@ -158,8 +158,6 @@ reply_reserve_attest_success (struct MHD_Connection *connection, * @param cls our `struct ReserveAttestContext *` * @param h_payto account for which the attribute data is stored * @param provider_section provider that must be checked - * @param birthdate birthdate of user, in format YYYY-MM-DD; can be NULL; - * digits can be 0 if exact day, month or year are unknown * @param collection_time when was the data collected * @param expiration_time when does the data expire * @param enc_attributes_size number of bytes in @a enc_attributes @@ -169,7 +167,6 @@ static void kyc_process_cb (void *cls, const struct TALER_PaytoHashP *h_payto, const char *provider_section, - const char *birthdate, struct GNUNET_TIME_Timestamp collection_time, struct GNUNET_TIME_Timestamp expiration_time, size_t enc_attributes_size, diff --git a/src/exchange/taler-exchange-httpd_reserves_get_attest.c b/src/exchange/taler-exchange-httpd_reserves_get_attest.c index b53a8641..ae983682 100644 --- a/src/exchange/taler-exchange-httpd_reserves_get_attest.c +++ b/src/exchange/taler-exchange-httpd_reserves_get_attest.c @@ -64,8 +64,6 @@ struct ReserveAttestContext * @param cls our `struct ReserveAttestContext *` * @param h_payto account for which the attribute data is stored * @param provider_section provider that must be checked - * @param birthdate birthdate of user, in format YYYY-MM-DD; can be NULL; - * digits can be 0 if exact day, month or year are unknown * @param collection_time when was the data collected * @param expiration_time when does the data expire * @param enc_attributes_size number of bytes in @a enc_attributes @@ -75,7 +73,6 @@ static void kyc_process_cb (void *cls, const struct TALER_PaytoHashP *h_payto, const char *provider_section, - const char *birthdate, struct GNUNET_TIME_Timestamp collection_time, struct GNUNET_TIME_Timestamp expiration_time, size_t enc_attributes_size, diff --git a/src/exchange/taler-exchange-kyc-aml-pep-trigger.sh b/src/exchange/taler-exchange-kyc-aml-pep-trigger.sh new file mode 100755 index 00000000..9baa32ba --- /dev/null +++ b/src/exchange/taler-exchange-kyc-aml-pep-trigger.sh @@ -0,0 +1,7 @@ +#!/bin/sh +# This file is in the public domain. +# This is an example of how to trigger AML if the +# KYC attributes include '{"pep":true}' +# +# To be used as a script for the KYC_AML_TRIGGER. +test "false" = $(jq .pep -) diff --git a/src/exchange/test_taler_exchange_httpd.conf b/src/exchange/test_taler_exchange_httpd.conf index 0af23b9d..80cf6230 100644 --- a/src/exchange/test_taler_exchange_httpd.conf +++ b/src/exchange/test_taler_exchange_httpd.conf @@ -7,13 +7,14 @@ TALER_RUNTIME_DIR = ${TMPDIR:-${TMP:-/tmp}}/${USER:-}/taler-system-runtime/ # Currency supported by the exchange (can only be one) CURRENCY = EUR CURRENCY_ROUND_UNIT = EUR:0.01 -AML_THRESHOLD = EUR:1000000 [auditor] TINY_AMOUNT = EUR:0.01 [exchange] +AML_THRESHOLD = EUR:1000000 + # Directory with our terms of service. TERMS_DIR = ../../contrib/tos |