summaryrefslogtreecommitdiff
path: root/src/exchange/taler-exchange-httpd_kyc-check.c
diff options
context:
space:
mode:
Diffstat (limited to 'src/exchange/taler-exchange-httpd_kyc-check.c')
-rw-r--r--src/exchange/taler-exchange-httpd_kyc-check.c274
1 files changed, 189 insertions, 85 deletions
diff --git a/src/exchange/taler-exchange-httpd_kyc-check.c b/src/exchange/taler-exchange-httpd_kyc-check.c
index 61d1abf8e..362c20a2e 100644
--- a/src/exchange/taler-exchange-httpd_kyc-check.c
+++ b/src/exchange/taler-exchange-httpd_kyc-check.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
@@ -72,9 +72,14 @@ struct KycPoller
struct GNUNET_DB_EventHandler *eh;
/**
- * UUID being checked.
+ * Row of the requirement being checked.
*/
- uint64_t legitimization_uuid;
+ uint64_t requirement_row;
+
+ /**
+ * Row of KYC process being initiated.
+ */
+ uint64_t process_row;
/**
* Hash of the payto:// URI we are confirming to
@@ -88,9 +93,9 @@ struct KycPoller
struct GNUNET_TIME_Absolute timeout;
/**
- * Type of KYC check required for this client.
+ * If the KYC complete, what kind of data was collected?
*/
- char *required;
+ json_t *kyc_details;
/**
* Set to starting URL of KYC process if KYC is required.
@@ -103,19 +108,34 @@ struct KycPoller
char *hint;
/**
+ * Name of the section of the provider in the configuration.
+ */
+ const char *section_name;
+
+ /**
+ * Set to AML status of the account.
+ */
+ enum TALER_AmlDecisionState aml_status;
+
+ /**
* Set to error encountered with KYC logic, if any.
*/
enum TALER_ErrorCode ec;
/**
+ * What kind of entity is doing the KYC check?
+ */
+ enum TALER_KYCLOGIC_KycUserType ut;
+
+ /**
* True if we are still suspended.
*/
bool suspended;
/**
- * True if KYC was required but is fully satisfied.
+ * False if KYC is not required.
*/
- bool found;
+ bool kyc_required;
/**
* True if we once tried the KYC initiation.
@@ -185,9 +205,9 @@ kyp_cleanup (struct TEH_RequestContext *rc)
kyp->ih_logic->initiate_cancel (kyp->ih);
kyp->ih = NULL;
}
+ json_decref (kyp->kyc_details);
GNUNET_free (kyp->kyc_url);
GNUNET_free (kyp->hint);
- GNUNET_free (kyp->required);
GNUNET_free (kyp);
}
@@ -218,7 +238,8 @@ initiate_cb (
kyp->ih = NULL;
kyp->ih_done = true;
GNUNET_log (GNUNET_ERROR_TYPE_INFO,
- "KYC initiation completed with status %d (%s)\n",
+ "KYC initiation `%s' completed with ec=%d (%s)\n",
+ provider_legitimization_id,
ec,
(TALER_EC_NONE == ec)
? redirect_url
@@ -232,15 +253,16 @@ initiate_cb (
{
kyp->hint = GNUNET_strdup (error_msg_hint);
}
- qs = TEH_plugin->update_kyc_requirement_by_row (
+ qs = TEH_plugin->update_kyc_process_by_row (
TEH_plugin->cls,
- kyp->legitimization_uuid,
- kyp->required,
+ kyp->process_row,
+ kyp->section_name,
&kyp->h_payto,
provider_user_id,
provider_legitimization_id,
+ redirect_url,
GNUNET_TIME_UNIT_ZERO_ABS);
- if (qs < 0)
+ if (qs <= 0)
GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
"KYC requirement update failed for %s with status %d at %s:%u\n",
TALER_B2S (&kyp->h_payto),
@@ -281,23 +303,21 @@ kyc_check (void *cls,
struct TALER_KYCLOGIC_ProviderDetails *pd;
enum GNUNET_GenericReturnValue ret;
struct TALER_PaytoHashP h_payto;
- struct GNUNET_TIME_Absolute expiration;
- char *provider_account_id;
- char *provider_legitimization_id;
+ char *requirements;
+ char *redirect_url;
+ bool satisfied;
qs = TEH_plugin->lookup_kyc_requirement_by_row (
TEH_plugin->cls,
- kyp->legitimization_uuid,
- &kyp->required,
- &h_payto,
- &expiration,
- &provider_account_id,
- &provider_legitimization_id);
+ kyp->requirement_row,
+ &requirements,
+ &kyp->aml_status,
+ &h_payto);
if (GNUNET_DB_STATUS_SUCCESS_NO_RESULTS == qs)
{
GNUNET_log (GNUNET_ERROR_TYPE_INFO,
"No KYC requirements open for %llu\n",
- (unsigned long long) kyp->legitimization_uuid);
+ (unsigned long long) kyp->requirement_row);
return qs;
}
if (qs < 0)
@@ -305,52 +325,122 @@ kyc_check (void *cls,
GNUNET_break (GNUNET_DB_STATUS_HARD_ERROR != qs);
return qs;
}
- GNUNET_free (provider_account_id);
- GNUNET_free (provider_legitimization_id);
if (0 !=
GNUNET_memcmp (&kyp->h_payto,
&h_payto))
{
GNUNET_log (GNUNET_ERROR_TYPE_WARNING,
- "Account %llu provided, but h_payto does not match\n",
- (unsigned long long) kyp->legitimization_uuid);
+ "Requirement %llu provided, but h_payto does not match\n",
+ (unsigned long long) kyp->requirement_row);
GNUNET_break_op (0);
*mhd_ret = TALER_MHD_reply_with_error (connection,
MHD_HTTP_FORBIDDEN,
TALER_EC_EXCHANGE_KYC_CHECK_AUTHORIZATION_FAILED,
"h_payto");
+ GNUNET_free (requirements);
return GNUNET_DB_STATUS_HARD_ERROR;
}
- kyp->found = true;
- if (GNUNET_TIME_absolute_is_future (expiration))
+ qs = TALER_KYCLOGIC_check_satisfied (
+ &requirements,
+ &h_payto,
+ &kyp->kyc_details,
+ TEH_plugin->select_satisfied_kyc_processes,
+ TEH_plugin->cls,
+ &satisfied);
+ if (qs < 0)
{
- /* kyc not required, we are done */
- return qs;
+ if (GNUNET_DB_STATUS_SOFT_ERROR == qs)
+ return qs;
+ GNUNET_break (0);
+ *mhd_ret = TALER_MHD_reply_with_error (connection,
+ MHD_HTTP_INTERNAL_SERVER_ERROR,
+ TALER_EC_GENERIC_DB_FETCH_FAILED,
+ "kyc_test_required");
+ GNUNET_free (requirements);
+ return GNUNET_DB_STATUS_HARD_ERROR;
+ }
+ if (satisfied)
+ {
+ GNUNET_log (GNUNET_ERROR_TYPE_INFO,
+ "KYC requirements `%s' already satisfied\n",
+ requirements);
+ GNUNET_free (requirements);
+ return GNUNET_DB_STATUS_SUCCESS_NO_RESULTS;
}
- ret = TALER_KYCLOGIC_kyc_get_logic (kyp->required,
- &kyp->ih_logic,
- &pd);
+ kyp->kyc_required = true;
+ ret = TALER_KYCLOGIC_requirements_to_logic (requirements,
+ kyp->ut,
+ &kyp->ih_logic,
+ &pd,
+ &kyp->section_name);
if (GNUNET_OK != ret)
{
- GNUNET_log (GNUNET_ERROR_TYPE_INFO,
- "KYC logic for `%s' not configured but used in database!\n",
- kyp->required);
+ GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
+ "KYC requirements `%s' cannot be checked, but are set as required in database!\n",
+ requirements);
*mhd_ret = TALER_MHD_reply_with_error (connection,
MHD_HTTP_INTERNAL_SERVER_ERROR,
TALER_EC_EXCHANGE_KYC_GENERIC_LOGIC_GONE,
- kyp->required);
+ requirements);
+ GNUNET_free (requirements);
return GNUNET_DB_STATUS_HARD_ERROR;
}
+ GNUNET_free (requirements);
+
if (kyp->ih_done)
return qs;
+ qs = TEH_plugin->get_pending_kyc_requirement_process (
+ TEH_plugin->cls,
+ &h_payto,
+ kyp->section_name,
+ &redirect_url);
+ if (qs < 0)
+ {
+ if (GNUNET_DB_STATUS_SOFT_ERROR == qs)
+ return qs;
+ GNUNET_break (0);
+ *mhd_ret = TALER_MHD_reply_with_error (connection,
+ MHD_HTTP_INTERNAL_SERVER_ERROR,
+ TALER_EC_GENERIC_DB_STORE_FAILED,
+ "insert_kyc_requirement_process");
+ return GNUNET_DB_STATUS_HARD_ERROR;
+ }
+ if ( (qs > 0) &&
+ (NULL != redirect_url) )
+ {
+ kyp->kyc_url = redirect_url;
+ return qs;
+ }
+ if (GNUNET_DB_STATUS_SUCCESS_NO_RESULTS == qs)
+ {
+ /* set up new requirement process */
+ qs = TEH_plugin->insert_kyc_requirement_process (
+ TEH_plugin->cls,
+ &h_payto,
+ kyp->section_name,
+ NULL,
+ NULL,
+ &kyp->process_row);
+ if (qs < 0)
+ {
+ if (GNUNET_DB_STATUS_SOFT_ERROR == qs)
+ return qs;
+ GNUNET_break (0);
+ *mhd_ret = TALER_MHD_reply_with_error (connection,
+ MHD_HTTP_INTERNAL_SERVER_ERROR,
+ TALER_EC_GENERIC_DB_STORE_FAILED,
+ "insert_kyc_requirement_process");
+ return GNUNET_DB_STATUS_HARD_ERROR;
+ }
+ }
GNUNET_log (GNUNET_ERROR_TYPE_INFO,
"Initiating KYC check with logic %s\n",
- kyp->required);
+ kyp->ih_logic->name);
kyp->ih = kyp->ih_logic->initiate (kyp->ih_logic->cls,
pd,
&h_payto,
- kyp->legitimization_uuid,
+ kyp->process_row,
&initiate_cb,
kyp);
GNUNET_break (NULL != kyp->ih);
@@ -401,7 +491,7 @@ db_event_cb (void *cls,
MHD_RESULT
TEH_handler_kyc_check (
struct TEH_RequestContext *rc,
- const char *const args[2])
+ const char *const args[3])
{
struct KycPoller *kyp = rc->rh_ctx;
MHD_RESULT res;
@@ -416,22 +506,22 @@ TEH_handler_kyc_check (
rc->rh_cleaner = &kyp_cleanup;
{
- unsigned long long legitimization_uuid;
+ unsigned long long requirement_row;
char dummy;
if (1 !=
sscanf (args[0],
"%llu%c",
- &legitimization_uuid,
+ &requirement_row,
&dummy))
{
GNUNET_break_op (0);
return TALER_MHD_reply_with_error (rc->connection,
MHD_HTTP_BAD_REQUEST,
TALER_EC_GENERIC_PARAMETER_MALFORMED,
- "legitimization_uuid");
+ "requirement_row");
}
- kyp->legitimization_uuid = (uint64_t) legitimization_uuid;
+ kyp->requirement_row = (uint64_t) requirement_row;
}
if (GNUNET_OK !=
@@ -447,34 +537,30 @@ TEH_handler_kyc_check (
"h_payto");
}
+ if (GNUNET_OK !=
+ TALER_KYCLOGIC_kyc_user_type_from_string (args[2],
+ &kyp->ut))
{
- const char *ts;
-
- ts = MHD_lookup_connection_value (rc->connection,
- MHD_GET_ARGUMENT_KIND,
- "timeout_ms");
- if (NULL != ts)
- {
- char dummy;
- unsigned long long tms;
-
- if (1 !=
- sscanf (ts,
- "%llu%c",
- &tms,
- &dummy))
- {
- GNUNET_break_op (0);
- return TALER_MHD_reply_with_error (rc->connection,
- MHD_HTTP_BAD_REQUEST,
- TALER_EC_GENERIC_PARAMETER_MALFORMED,
- "timeout_ms");
- }
- kyp->timeout = GNUNET_TIME_relative_to_absolute (
- GNUNET_TIME_relative_multiply (GNUNET_TIME_UNIT_MILLISECONDS,
- tms));
- }
+ GNUNET_break_op (0);
+ return TALER_MHD_reply_with_error (rc->connection,
+ MHD_HTTP_BAD_REQUEST,
+ TALER_EC_GENERIC_PARAMETER_MALFORMED,
+ "usertype");
}
+
+ TALER_MHD_parse_request_timeout (rc->connection,
+ &kyp->timeout);
+ }
+ /* KYC plugin generated reply? */
+ if (NULL != kyp->kyc_url)
+ {
+ return TALER_MHD_REPLY_JSON_PACK (
+ rc->connection,
+ MHD_HTTP_ACCEPTED,
+ GNUNET_JSON_pack_uint64 ("aml_status",
+ kyp->aml_status),
+ GNUNET_JSON_pack_string ("kyc_url",
+ kyp->kyc_url));
}
if ( (NULL == kyp->eh) &&
@@ -509,14 +595,36 @@ TEH_handler_kyc_check (
"Transaction failed.\n");
return res;
}
+ /* KYC plugin generated reply? */
+ if (NULL != kyp->kyc_url)
+ {
+ return TALER_MHD_REPLY_JSON_PACK (
+ rc->connection,
+ MHD_HTTP_ACCEPTED,
+ GNUNET_JSON_pack_uint64 ("aml_status",
+ kyp->aml_status),
+ GNUNET_JSON_pack_string ("kyc_url",
+ kyp->kyc_url));
+ }
if ( (NULL == kyp->ih) &&
- (! kyp->found) )
+ (! kyp->kyc_required) )
{
+ if (TALER_AML_NORMAL != kyp->aml_status)
+ {
+ GNUNET_log (GNUNET_ERROR_TYPE_INFO,
+ "KYC is OK, but AML active: %d\n",
+ (int) kyp->aml_status);
+ return TALER_MHD_REPLY_JSON_PACK (
+ rc->connection,
+ MHD_HTTP_UNAVAILABLE_FOR_LEGAL_REASONS,
+ GNUNET_JSON_pack_uint64 ("aml_status",
+ kyp->aml_status));
+ }
/* KYC not required */
GNUNET_log (GNUNET_ERROR_TYPE_INFO,
"KYC not required %llu\n",
- (unsigned long long) kyp->legitimization_uuid);
+ (unsigned long long) kyp->requirement_row);
return TALER_MHD_reply_static (
rc->connection,
MHD_HTTP_NO_CONTENT,
@@ -538,16 +646,17 @@ TEH_handler_kyc_check (
}
/* long polling? */
- if ( (NULL != kyp->required) &&
+ if ( (NULL != kyp->section_name) &&
GNUNET_TIME_absolute_is_future (kyp->timeout))
{
GNUNET_log (GNUNET_ERROR_TYPE_INFO,
"Suspending HTTP request on timeout (%s) now...\n",
- GNUNET_TIME_relative2s (GNUNET_TIME_absolute_get_duration (
+ GNUNET_TIME_relative2s (GNUNET_TIME_absolute_get_remaining (
kyp->timeout),
true));
GNUNET_assert (NULL != kyp->eh);
kyp->suspended = true;
+ kyp->section_name = NULL;
GNUNET_CONTAINER_DLL_insert (kyp_head,
kyp_tail,
kyp);
@@ -555,16 +664,6 @@ TEH_handler_kyc_check (
return MHD_YES;
}
- /* KYC plugin generated reply? */
- if (NULL != kyp->kyc_url)
- {
- return TALER_MHD_REPLY_JSON_PACK (
- rc->connection,
- MHD_HTTP_ACCEPTED,
- GNUNET_JSON_pack_string ("kyc_url",
- kyp->kyc_url));
- }
-
if (TALER_EC_NONE != kyp->ec)
{
return TALER_MHD_reply_with_ec (rc->connection,
@@ -582,6 +681,7 @@ TEH_handler_kyc_check (
(ec = TALER_exchange_online_account_setup_success_sign (
&TEH_keys_exchange_sign_,
&kyp->h_payto,
+ kyp->kyc_details,
now,
&pub,
&sig)))
@@ -597,6 +697,10 @@ TEH_handler_kyc_check (
&sig),
GNUNET_JSON_pack_data_auto ("exchange_pub",
&pub),
+ GNUNET_JSON_pack_uint64 ("aml_status",
+ kyp->aml_status),
+ GNUNET_JSON_pack_object_incref ("kyc_details",
+ kyp->kyc_details),
GNUNET_JSON_pack_timestamp ("now",
now));
}