summaryrefslogtreecommitdiff
path: root/src/exchange/taler-exchange-httpd_common_kyc.c
diff options
context:
space:
mode:
Diffstat (limited to 'src/exchange/taler-exchange-httpd_common_kyc.c')
-rw-r--r--src/exchange/taler-exchange-httpd_common_kyc.c302
1 files changed, 302 insertions, 0 deletions
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 000000000..bcee5a0d2
--- /dev/null
+++ b/src/exchange/taler-exchange-httpd_common_kyc.c
@@ -0,0 +1,302 @@
+/*
+ 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.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 <gnunet/gnunet_common.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,
+ * 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; i<num_checks; i++)
+ GNUNET_free (provided_checks[i]);
+ GNUNET_free (provided_checks);
+ GNUNET_free (ea);
+ GNUNET_log (GNUNET_ERROR_TYPE_INFO,
+ "Stored encrypted KYC process #%llu attributes: %d\n",
+ (unsigned long long) kat->process_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;
+}