aboutsummaryrefslogtreecommitdiff
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.c355
1 files changed, 257 insertions, 98 deletions
diff --git a/src/exchange/taler-exchange-httpd_kyc-check.c b/src/exchange/taler-exchange-httpd_kyc-check.c
index b4ec4197d..61d1abf8e 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 Taler Systems SA
+ Copyright (C) 2021-2022 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
@@ -25,6 +25,7 @@
#include <microhttpd.h>
#include <pthread.h>
#include "taler_json_lib.h"
+#include "taler_kyclogic_lib.h"
#include "taler_mhd_lib.h"
#include "taler_signatures.h"
#include "taler_dbevents.h"
@@ -54,6 +55,17 @@ struct KycPoller
struct MHD_Connection *connection;
/**
+ * Logic for @e ih
+ */
+ struct TALER_KYCLOGIC_Plugin *ih_logic;
+
+ /**
+ * Handle to asynchronously running KYC initiation
+ * request.
+ */
+ struct TALER_KYCLOGIC_InitiateHandle *ih;
+
+ /**
* Subscription for the database event we are
* waiting for.
*/
@@ -62,12 +74,7 @@ struct KycPoller
/**
* UUID being checked.
*/
- uint64_t auth_payment_target_uuid;
-
- /**
- * Current KYC status.
- */
- struct TALER_EXCHANGEDB_KycStatus kyc;
+ uint64_t legitimization_uuid;
/**
* Hash of the payto:// URI we are confirming to
@@ -76,20 +83,45 @@ struct KycPoller
struct TALER_PaytoHashP h_payto;
/**
- * Payto URL as a string, as given to us by t
+ * When will this request time out?
*/
- const char *hps;
+ struct GNUNET_TIME_Absolute timeout;
/**
- * When will this request time out?
+ * Type of KYC check required for this client.
*/
- struct GNUNET_TIME_Absolute timeout;
+ char *required;
+
+ /**
+ * Set to starting URL of KYC process if KYC is required.
+ */
+ char *kyc_url;
+
+ /**
+ * Set to error details, on error (@ec not TALER_EC_NONE).
+ */
+ char *hint;
+
+ /**
+ * Set to error encountered with KYC logic, if any.
+ */
+ enum TALER_ErrorCode ec;
/**
* True if we are still suspended.
*/
bool suspended;
+ /**
+ * True if KYC was required but is fully satisfied.
+ */
+ bool found;
+
+ /**
+ * True if we once tried the KYC initiation.
+ */
+ bool ih_done;
+
};
@@ -114,6 +146,11 @@ TEH_kyc_check_cleanup ()
GNUNET_CONTAINER_DLL_remove (kyp_head,
kyp_tail,
kyp);
+ if (NULL != kyp->ih)
+ {
+ kyp->ih_logic->initiate_cancel (kyp->ih);
+ kyp->ih = NULL;
+ }
if (kyp->suspended)
{
kyp->suspended = false;
@@ -143,11 +180,84 @@ kyp_cleanup (struct TEH_RequestContext *rc)
kyp->eh);
kyp->eh = NULL;
}
+ if (NULL != kyp->ih)
+ {
+ kyp->ih_logic->initiate_cancel (kyp->ih);
+ kyp->ih = NULL;
+ }
+ GNUNET_free (kyp->kyc_url);
+ GNUNET_free (kyp->hint);
+ GNUNET_free (kyp->required);
GNUNET_free (kyp);
}
/**
+ * Function called with the result of a KYC initiation
+ * operation.
+ *
+ * @param cls closure with our `struct KycPoller *`
+ * @param ec #TALER_EC_NONE on success
+ * @param redirect_url set to where to redirect the user on success, NULL on failure
+ * @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 error_msg_hint set to additional details to return to user, NULL on success
+ */
+static void
+initiate_cb (
+ void *cls,
+ enum TALER_ErrorCode ec,
+ const char *redirect_url,
+ const char *provider_user_id,
+ const char *provider_legitimization_id,
+ const char *error_msg_hint)
+{
+ struct KycPoller *kyp = cls;
+ enum GNUNET_DB_QueryStatus qs;
+
+ kyp->ih = NULL;
+ kyp->ih_done = true;
+ GNUNET_log (GNUNET_ERROR_TYPE_INFO,
+ "KYC initiation completed with status %d (%s)\n",
+ ec,
+ (TALER_EC_NONE == ec)
+ ? redirect_url
+ : error_msg_hint);
+ kyp->ec = ec;
+ if (TALER_EC_NONE == ec)
+ {
+ kyp->kyc_url = GNUNET_strdup (redirect_url);
+ }
+ else
+ {
+ kyp->hint = GNUNET_strdup (error_msg_hint);
+ }
+ qs = TEH_plugin->update_kyc_requirement_by_row (
+ TEH_plugin->cls,
+ kyp->legitimization_uuid,
+ kyp->required,
+ &kyp->h_payto,
+ provider_user_id,
+ provider_legitimization_id,
+ GNUNET_TIME_UNIT_ZERO_ABS);
+ 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),
+ qs,
+ __FILE__,
+ __LINE__);
+ GNUNET_assert (kyp->suspended);
+ kyp->suspended = false;
+ GNUNET_CONTAINER_DLL_remove (kyp_head,
+ kyp_tail,
+ kyp);
+ MHD_resume_connection (kyp->connection);
+ TALER_MHD_daemon_trigger ();
+}
+
+
+/**
* Function implementing database transaction to check wallet's KYC status.
* Runs the transaction logic; IF it returns a non-error code, the transaction
* logic MUST NOT queue a MHD response. IF it returns an hard error, the
@@ -168,21 +278,82 @@ kyc_check (void *cls,
{
struct KycPoller *kyp = cls;
enum GNUNET_DB_QueryStatus qs;
-
- qs = TEH_plugin->select_kyc_status (TEH_plugin->cls,
- &kyp->h_payto,
- &kyp->kyc);
+ 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;
+
+ 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);
+ 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);
+ return qs;
+ }
if (qs < 0)
{
- if (GNUNET_DB_STATUS_SOFT_ERROR == qs)
- return qs;
- GNUNET_break (0);
+ 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);
+ GNUNET_break_op (0);
*mhd_ret = TALER_MHD_reply_with_error (connection,
- MHD_HTTP_INTERNAL_SERVER_ERROR,
- TALER_EC_GENERIC_DB_FETCH_FAILED,
- "inselect_wallet_status");
+ MHD_HTTP_FORBIDDEN,
+ TALER_EC_EXCHANGE_KYC_CHECK_AUTHORIZATION_FAILED,
+ "h_payto");
+ return GNUNET_DB_STATUS_HARD_ERROR;
+ }
+ kyp->found = true;
+ if (GNUNET_TIME_absolute_is_future (expiration))
+ {
+ /* kyc not required, we are done */
return qs;
}
+
+ ret = TALER_KYCLOGIC_kyc_get_logic (kyp->required,
+ &kyp->ih_logic,
+ &pd);
+ if (GNUNET_OK != ret)
+ {
+ GNUNET_log (GNUNET_ERROR_TYPE_INFO,
+ "KYC logic for `%s' not configured but used in database!\n",
+ kyp->required);
+ *mhd_ret = TALER_MHD_reply_with_error (connection,
+ MHD_HTTP_INTERNAL_SERVER_ERROR,
+ TALER_EC_EXCHANGE_KYC_GENERIC_LOGIC_GONE,
+ kyp->required);
+ return GNUNET_DB_STATUS_HARD_ERROR;
+ }
+ if (kyp->ih_done)
+ return qs;
+ GNUNET_log (GNUNET_ERROR_TYPE_INFO,
+ "Initiating KYC check with logic %s\n",
+ kyp->required);
+ kyp->ih = kyp->ih_logic->initiate (kyp->ih_logic->cls,
+ pd,
+ &h_payto,
+ kyp->legitimization_uuid,
+ &initiate_cb,
+ kyp);
+ GNUNET_break (NULL != kyp->ih);
return qs;
}
@@ -230,7 +401,7 @@ db_event_cb (void *cls,
MHD_RESULT
TEH_handler_kyc_check (
struct TEH_RequestContext *rc,
- const char *const args[])
+ const char *const args[2])
{
struct KycPoller *kyp = rc->rh_ctx;
MHD_RESULT res;
@@ -245,24 +416,37 @@ TEH_handler_kyc_check (
rc->rh_cleaner = &kyp_cleanup;
{
- // FIXME: now 'legitimization_uuid'!
- unsigned long long payment_target_uuid;
+ unsigned long long legitimization_uuid;
char dummy;
if (1 !=
sscanf (args[0],
"%llu%c",
- &payment_target_uuid,
+ &legitimization_uuid,
&dummy))
{
GNUNET_break_op (0);
return TALER_MHD_reply_with_error (rc->connection,
MHD_HTTP_BAD_REQUEST,
TALER_EC_GENERIC_PARAMETER_MALFORMED,
- "payment_target_uuid");
+ "legitimization_uuid");
}
- kyp->auth_payment_target_uuid = (uint64_t) payment_target_uuid;
+ kyp->legitimization_uuid = (uint64_t) legitimization_uuid;
+ }
+
+ if (GNUNET_OK !=
+ GNUNET_STRINGS_string_to_data (args[1],
+ strlen (args[1]),
+ &kyp->h_payto,
+ sizeof (kyp->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");
}
+
{
const char *ts;
@@ -291,41 +475,8 @@ TEH_handler_kyc_check (
tms));
}
}
-
- // FIXME: replace with args[1]!
- kyp->hps = MHD_lookup_connection_value (rc->connection,
- MHD_GET_ARGUMENT_KIND,
- "h_payto");
- if (NULL == kyp->hps)
- {
- GNUNET_break_op (0);
- return TALER_MHD_reply_with_error (rc->connection,
- MHD_HTTP_BAD_REQUEST,
- TALER_EC_GENERIC_PARAMETER_MISSING,
- "h_payto");
- }
- if (GNUNET_OK !=
- GNUNET_STRINGS_string_to_data (kyp->hps,
- strlen (kyp->hps),
- &kyp->h_payto,
- sizeof (kyp->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");
- }
}
- if (TEH_KYC_NONE == TEH_kyc_config.mode)
- return TALER_MHD_reply_static (
- rc->connection,
- MHD_HTTP_NO_CONTENT,
- NULL,
- NULL,
- 0);
-
if ( (NULL == kyp->eh) &&
GNUNET_TIME_absolute_is_future (kyp->timeout) )
{
@@ -353,27 +504,48 @@ TEH_handler_kyc_check (
&kyc_check,
kyp);
if (GNUNET_SYSERR == ret)
+ {
+ GNUNET_log (GNUNET_ERROR_TYPE_INFO,
+ "Transaction failed.\n");
return res;
+ }
- if (kyp->auth_payment_target_uuid !=
- kyp->kyc.payment_target_uuid)
+ if ( (NULL == kyp->ih) &&
+ (! kyp->found) )
{
- GNUNET_log (GNUNET_ERROR_TYPE_WARNING,
- "Account %llu provided, but payto %s is for %llu\n",
- (unsigned long long) kyp->auth_payment_target_uuid,
- kyp->hps,
- (unsigned long long) kyp->kyc.payment_target_uuid);
- GNUNET_break_op (0);
- return TALER_MHD_reply_with_error (rc->connection,
- MHD_HTTP_FORBIDDEN,
- TALER_EC_EXCHANGE_KYC_CHECK_AUTHORIZATION_FAILED,
- "h_payto");
+ /* KYC not required */
+ GNUNET_log (GNUNET_ERROR_TYPE_INFO,
+ "KYC not required %llu\n",
+ (unsigned long long) kyp->legitimization_uuid);
+ return TALER_MHD_reply_static (
+ rc->connection,
+ MHD_HTTP_NO_CONTENT,
+ NULL,
+ NULL,
+ 0);
+ }
+
+ if (NULL != kyp->ih)
+ {
+ GNUNET_log (GNUNET_ERROR_TYPE_INFO,
+ "Suspending HTTP request on KYC logic...\n");
+ kyp->suspended = true;
+ GNUNET_CONTAINER_DLL_insert (kyp_head,
+ kyp_tail,
+ kyp);
+ MHD_suspend_connection (kyp->connection);
+ return MHD_YES;
}
/* long polling? */
- if ( (! kyp->kyc.ok) &&
+ if ( (NULL != kyp->required) &&
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 (
+ kyp->timeout),
+ true));
GNUNET_assert (NULL != kyp->eh);
kyp->suspended = true;
GNUNET_CONTAINER_DLL_insert (kyp_head,
@@ -383,37 +555,24 @@ TEH_handler_kyc_check (
return MHD_YES;
}
- /* KYC failed? */
- if (! kyp->kyc.ok)
+ /* KYC plugin generated reply? */
+ if (NULL != kyp->kyc_url)
{
- char *url;
- char *redirect_uri;
- char *redirect_uri_encoded;
-
- GNUNET_assert (TEH_KYC_OAUTH2 == TEH_kyc_config.mode);
- GNUNET_asprintf (&redirect_uri,
- "%s/kyc-proof/%s",
- TEH_base_url,
- kyp->hps);
- redirect_uri_encoded = TALER_urlencode (redirect_uri);
- GNUNET_free (redirect_uri);
- GNUNET_asprintf (&url,
- "%s?client_id=%s&redirect_uri=%s",
- TEH_kyc_config.details.oauth2.login_url,
- TEH_kyc_config.details.oauth2.client_id,
- redirect_uri_encoded);
- GNUNET_free (redirect_uri_encoded);
-
- res = TALER_MHD_REPLY_JSON_PACK (
+ return TALER_MHD_REPLY_JSON_PACK (
rc->connection,
MHD_HTTP_ACCEPTED,
GNUNET_JSON_pack_string ("kyc_url",
- url));
- GNUNET_free (url);
- return res;
+ kyp->kyc_url));
+ }
+
+ if (TALER_EC_NONE != kyp->ec)
+ {
+ return TALER_MHD_reply_with_ec (rc->connection,
+ kyp->ec,
+ kyp->hint);
}
- /* KYC succeeded! */
+ /* KYC must have succeeded! */
{
struct TALER_ExchangePublicKeyP pub;
struct TALER_ExchangeSignatureP sig;