/* 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_common_kyc.c * @brief shared logic for finishing a KYC process * @author Christian Grothoff */ #include "platform.h" #include "taler-exchange-httpd.h" #include "taler-exchange-httpd_common_kyc.h" #include "taler_attributes.h" #include "taler_error_codes.h" #include "taler_kyclogic_lib.h" #include "taler_exchangedb_plugin.h" #include 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, * non-zero if AML checks are required next * @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 = 0; struct GNUNET_ShortHashCode kyc_prox; struct GNUNET_AsyncScopeSave old_scope; unsigned int num_checks; char **provided_checks; 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)); if ( (TEH_age_restriction_enabled) && (NULL != birthdate) ) { enum GNUNET_GenericReturnValue ret; ret = TALER_parse_coarse_date (birthdate, &TEH_age_restriction_config.mask, &birthday); if (GNUNET_OK != ret) { GNUNET_log (GNUNET_ERROR_TYPE_WARNING, "Failed to parse birthdate `%s' from KYC attributes\n", birthdate); if (NULL != kat->response) MHD_destroy_response (kat->response); kat->http_status = MHD_HTTP_BAD_REQUEST; kat->response = TALER_MHD_make_error ( TALER_EC_GENERIC_PARAMETER_MALFORMED, TALER_ATTRIBUTE_BIRTHDATE); goto RETURN_RESULT; } } TALER_CRYPTO_kyc_attributes_encrypt (&TEH_attribute_key, kat->attributes, &ea, &eas); TALER_KYCLOGIC_lookup_checks (kat->provider_section, &num_checks, &provided_checks); qs = TEH_plugin->insert_kyc_attributes ( TEH_plugin->cls, kat->process_row, &kat->account_id, &kyc_prox, kat->provider_section, num_checks, (const char **) provided_checks, birthday, GNUNET_TIME_timestamp_get (), kat->provider_user_id, kat->provider_legitimization_id, kat->expiration, eas, ea, 0 != code); for (unsigned int i = 0; iprocess_row, qs); 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"); /* Continued below to return the response */ } RETURN_RESULT: /* 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); } bool TEH_kyc_failed (uint64_t process_row, const struct TALER_PaytoHashP *account_id, const char *provider_section, const char *provider_user_id, const char *provider_legitimization_id) { enum GNUNET_DB_QueryStatus qs; qs = TEH_plugin->insert_kyc_failure ( TEH_plugin->cls, process_row, account_id, provider_section, provider_user_id, provider_legitimization_id); GNUNET_break (qs >= 0); return qs >= 0; }