aboutsummaryrefslogtreecommitdiff
path: root/src/backend
diff options
context:
space:
mode:
authorChristian Grothoff <christian@grothoff.org>2024-09-08 23:39:50 +0200
committerChristian Grothoff <christian@grothoff.org>2024-09-08 23:39:50 +0200
commitfd6e459cc7fcad3703c993e812f25a09e1ca9d36 (patch)
tree821fefcf4b7cff609c4f4df2ab33b9acd9c396e7 /src/backend
parentaa29da994abd4b4dcb0648e67484589db7351ae9 (diff)
towards new /kyc API: test_kyc_api still fails, but getting close
Diffstat (limited to 'src/backend')
-rw-r--r--src/backend/taler-merchant-httpd_private-get-instances-ID-kyc.c982
-rw-r--r--src/backend/taler-merchant-kyccheck.c32
2 files changed, 404 insertions, 610 deletions
diff --git a/src/backend/taler-merchant-httpd_private-get-instances-ID-kyc.c b/src/backend/taler-merchant-httpd_private-get-instances-ID-kyc.c
index 69e962d0..7fb229b2 100644
--- a/src/backend/taler-merchant-httpd_private-get-instances-ID-kyc.c
+++ b/src/backend/taler-merchant-httpd_private-get-instances-ID-kyc.c
@@ -27,6 +27,7 @@
#include "taler-merchant-httpd_helper.h"
#include "taler-merchant-httpd_exchanges.h"
#include <taler/taler_json_lib.h>
+#include <taler/taler_dbevents.h>
#include <regex.h>
/**
@@ -77,7 +78,8 @@ struct ExchangeKycRequest
/**
* JSON array of payto-URIs with KYC auth wire transfer
- * instructions.
+ * instructions. Provided if @e auth_ok is false and
+ * @e kyc_auth_conflict is false.
*/
json_t *pkaa;
@@ -92,14 +94,10 @@ struct ExchangeKycRequest
struct KycContext *kc;
/**
- * Hash of the wire account (with salt) we are checking.
- */
- struct TALER_MerchantWireHashP h_wire;
-
- /**
- * Handle for the actual HTTP request to the exchange.
+ * JSON array of AccountLimits that apply, NULL if
+ * unknown (and likely defaults apply).
*/
- struct TALER_EXCHANGE_KycCheckHandle *kyc;
+ json_t *jlimits;
/**
* Our account's payto URI.
@@ -112,10 +110,62 @@ struct ExchangeKycRequest
char *exchange_url;
/**
+ * Hash of the wire account (with salt) we are checking.
+ */
+ struct TALER_MerchantWireHashP h_wire;
+
+ /**
+ * Current access token for the KYC SPA. Only set
+ * if @e auth_ok is true.
+ */
+ struct TALER_AccountAccessTokenP access_token;
+
+ /**
* Timestamp when we last got a reply from the exchange.
*/
struct GNUNET_TIME_Timestamp last_check;
+ /**
+ * Last HTTP status code obtained via /kyc-check from
+ * the exchange.
+ */
+ unsigned int last_http_status;
+
+ /**
+ * Last Taler error code returned from /kyc-check.
+ */
+ enum TALER_ErrorCode last_ec;
+
+ /**
+ * True if this account
+ * cannot work at this exchange because KYC auth is
+ * impossible.
+ */
+ bool kyc_auth_conflict;
+
+ /**
+ * We could not get /keys from the exchange.
+ */
+ bool no_keys;
+
+ /**
+ * True if @e access_token is available.
+ */
+ bool auth_ok;
+
+ /**
+ * True if we believe no KYC is currently required
+ * for this account at this exchange.
+ */
+ bool kyc_ok;
+
+ /**
+ * True if the exchange exposed to us that the account
+ * is currently under AML review.
+ */
+ bool in_aml_review;
+
+
};
@@ -150,11 +200,6 @@ struct KycContext
struct TMH_HandlerContext *hc;
/**
- * Task to trigger on request timeout, or NULL.
- */
- struct GNUNET_SCHEDULER_Task *timeout_task;
-
- /**
* Response to return, NULL if we don't have one yet.
*/
struct MHD_Response *response;
@@ -163,19 +208,7 @@ struct KycContext
* JSON array where we are building up the array with
* pending KYC operations.
*/
- json_t *pending_kycs;
-
- /**
- * JSON array where we are building up the array with
- * pending KYC operations.
- */
- json_t *voluntary_kycs;
-
- /**
- * JSON array where we are building up the array with
- * troubled KYC operations.
- */
- json_t *timeout_kycs;
+ json_t *kycs_data;
/**
* Head of DLL of requests we are making to an
@@ -196,6 +229,12 @@ struct KycContext
const char *exchange_url;
/**
+ * Notification handler from database on changes
+ * to the KYC status.
+ */
+ struct GNUNET_DB_EventHandler *eh;
+
+ /**
* Set to the h_wire of the merchant account if
* @a have_h_wire is true, used to filter by account.
*/
@@ -222,6 +261,11 @@ struct KycContext
enum GNUNET_GenericReturnValue suspended;
/**
+ * What state are we long-polling for?
+ */
+ enum TALER_EXCHANGE_KycLongPollTarget lpt;
+
+ /**
* True if @e h_wire was given.
*/
bool have_h_wire;
@@ -230,7 +274,7 @@ struct KycContext
* We're still waiting on the exchange to determine
* the KYC status of our deposit(s).
*/
- bool kyc_serial_pending;
+ bool return_immediately;
};
@@ -253,11 +297,6 @@ TMH_force_kyc_resume ()
NULL != kc;
kc = kc->next)
{
- if (NULL != kc->timeout_task)
- {
- GNUNET_SCHEDULER_cancel (kc->timeout_task);
- kc->timeout_task = NULL;
- }
if (GNUNET_YES == kc->suspended)
{
kc->suspended = GNUNET_SYSERR;
@@ -283,27 +322,23 @@ kyc_context_cleanup (void *cls)
GNUNET_CONTAINER_DLL_remove (kc->exchange_pending_head,
kc->exchange_pending_tail,
ekr);
- if (NULL != ekr->kyc)
- {
- TALER_EXCHANGE_kyc_check_cancel (ekr->kyc);
- ekr->kyc = NULL;
- }
if (NULL != ekr->fo)
{
TMH_EXCHANGES_keys4exchange_cancel (ekr->fo);
ekr->fo = NULL;
}
json_decref (ekr->pkaa);
+ json_decref (ekr->jlimits);
if (NULL != ekr->keys)
TALER_EXCHANGE_keys_decref (ekr->keys);
GNUNET_free (ekr->exchange_url);
GNUNET_free (ekr->payto_uri);
GNUNET_free (ekr);
}
- if (NULL != kc->timeout_task)
+ if (NULL != kc->eh)
{
- GNUNET_SCHEDULER_cancel (kc->timeout_task);
- kc->timeout_task = NULL;
+ TMH_db->event_listen_cancel (kc->eh);
+ kc->eh = NULL;
}
if (NULL != kc->response)
{
@@ -313,73 +348,42 @@ kyc_context_cleanup (void *cls)
GNUNET_CONTAINER_DLL_remove (kc_head,
kc_tail,
kc);
- json_decref (kc->voluntary_kycs);
- json_decref (kc->pending_kycs);
- json_decref (kc->timeout_kycs);
+ json_decref (kc->kycs_data);
GNUNET_free (kc);
}
/**
- * Resume the given KYC context and send the given response. Stores the
+ * Resume the given KYC context and send the final response. Stores the
* response in the @a kc and signals MHD to resume the connection. Also
* ensures MHD runs immediately.
*
* @param kc KYC context
- * @param response_code response code to use
- * @param response response data to send back
*/
static void
-resume_kyc_with_response (struct KycContext *kc,
- unsigned int response_code,
- struct MHD_Response *response)
+resume_kyc_with_response (struct KycContext *kc)
{
char dat[128];
- kc->response_code = response_code;
- kc->response = response;
- switch (response_code)
- {
- case MHD_HTTP_OK:
- /* KYC failed, cache briefly */
- TALER_MHD_get_date_string (GNUNET_TIME_relative_to_absolute (
- EXPIRATION_KYC_FAILURE),
- dat);
- GNUNET_break (MHD_YES ==
- MHD_add_response_header (response,
- MHD_HTTP_HEADER_EXPIRES,
- dat));
- GNUNET_break (MHD_YES ==
- MHD_add_response_header (response,
- MHD_HTTP_HEADER_CACHE_CONTROL,
- "max-age=300"));
- break;
- case MHD_HTTP_NO_CONTENT:
- /* KYC passed, cache for a long time! */
- TALER_MHD_get_date_string (GNUNET_TIME_relative_to_absolute (
- EXPIRATION_KYC_SUCCESS),
- dat);
- GNUNET_break (MHD_YES ==
- MHD_add_response_header (response,
- MHD_HTTP_HEADER_EXPIRES,
- dat));
- GNUNET_break (MHD_YES ==
- MHD_add_response_header (response,
- MHD_HTTP_HEADER_CACHE_CONTROL,
- "max-age=3600"));
- break;
- case MHD_HTTP_BAD_GATEWAY:
- case MHD_HTTP_GATEWAY_TIMEOUT:
- break; /* no caching */
- }
- GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
+ kc->response_code = MHD_HTTP_OK;
+ kc->response = TALER_MHD_MAKE_JSON_PACK (
+ GNUNET_JSON_pack_array_incref ("kyc_data",
+ kc->kycs_data));
+ /* KYC failed, cache briefly */
+ TALER_MHD_get_date_string (GNUNET_TIME_relative_to_absolute (
+ EXPIRATION_KYC_FAILURE),
+ dat);
+ GNUNET_break (MHD_YES ==
+ MHD_add_response_header (kc->response,
+ MHD_HTTP_HEADER_EXPIRES,
+ dat));
+ GNUNET_break (MHD_YES ==
+ MHD_add_response_header (kc->response,
+ MHD_HTTP_HEADER_CACHE_CONTROL,
+ "max-age=300"));
+ GNUNET_log (GNUNET_ERROR_TYPE_INFO,
"Resuming /kyc handling as exchange interaction is done (%u)\n",
- response_code);
- if (NULL != kc->timeout_task)
- {
- GNUNET_SCHEDULER_cancel (kc->timeout_task);
- kc->timeout_task = NULL;
- }
+ MHD_HTTP_OK);
GNUNET_assert (GNUNET_YES == kc->suspended);
kc->suspended = GNUNET_NO;
MHD_resume_connection (kc->connection);
@@ -388,58 +392,26 @@ resume_kyc_with_response (struct KycContext *kc,
/**
- * Handle a timeout for the processing of the kyc request.
+ * Handle a DB event about an update relevant
+ * for the processing of the kyc request.
*
* @param cls our `struct KycContext`
+ * @param extra additional event data provided
+ * @param extra_size number of bytes in @a extra
*/
static void
-handle_kyc_timeout (void *cls)
+kyc_change_cb (void *cls,
+ const void *extra,
+ size_t extra_size)
{
struct KycContext *kc = cls;
- struct ExchangeKycRequest *ekr;
- kc->timeout_task = NULL;
- while (NULL != (ekr = kc->exchange_pending_head))
- {
- GNUNET_CONTAINER_DLL_remove (kc->exchange_pending_head,
- kc->exchange_pending_tail,
- ekr);
- if (NULL != ekr->kyc)
- {
- TALER_EXCHANGE_kyc_check_cancel (ekr->kyc);
- ekr->kyc = NULL;
- }
- if (NULL != ekr->fo)
- {
- TMH_EXCHANGES_keys4exchange_cancel (ekr->fo);
- ekr->fo = NULL;
- }
- GNUNET_assert (
- 0 ==
- json_array_append_new (
- kc->timeout_kycs,
- GNUNET_JSON_PACK (
- GNUNET_JSON_pack_string ("exchange_url",
- ekr->exchange_url),
- GNUNET_JSON_pack_uint64 ("exchange_code",
- TALER_EC_MERCHANT_GENERIC_EXCHANGE_TIMEOUT),
- GNUNET_JSON_pack_uint64 ("exchange_http_status",
- 0))));
- GNUNET_free (ekr->exchange_url);
- GNUNET_free (ekr->payto_uri);
- GNUNET_free (ekr);
- }
GNUNET_assert (GNUNET_YES == kc->suspended);
GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
"Resuming KYC with gateway timeout\n");
- resume_kyc_with_response (
- kc,
- MHD_HTTP_GATEWAY_TIMEOUT,
- TALER_MHD_MAKE_JSON_PACK (
- GNUNET_JSON_pack_array_incref ("pending_kycs",
- kc->pending_kycs),
- GNUNET_JSON_pack_array_incref ("timeout_kycs",
- kc->timeout_kycs)));
+ kc->suspended = GNUNET_NO;
+ MHD_resume_connection (kc->connection);
+ TALER_MHD_daemon_trigger (); /* we resumed, kick MHD */
}
@@ -472,107 +444,6 @@ pack_limit (const struct TALER_EXCHANGE_AccountLimit *limit,
/**
- * We are done with the KYC request @a ekr. Remove it from the work list and
- * check if we are done overall.
- *
- * @param[in] ekr key request that is done (and will be freed)
- */
-static void
-ekr_finished (struct ExchangeKycRequest *ekr)
-{
- struct KycContext *kc = ekr->kc;
-
- GNUNET_CONTAINER_DLL_remove (kc->exchange_pending_head,
- kc->exchange_pending_tail,
- ekr);
- json_decref (ekr->pkaa);
- if (NULL != ekr->keys)
- TALER_EXCHANGE_keys_decref (ekr->keys);
- GNUNET_free (ekr->exchange_url);
- GNUNET_free (ekr->payto_uri);
- GNUNET_free (ekr);
- if (NULL != kc->exchange_pending_head)
- return; /* wait for more */
- /* All exchange requests done, create final
- big response from cumulated replies */
- if ( (0 == json_array_size (kc->pending_kycs)) &&
- (0 == json_array_size (kc->timeout_kycs)) )
- {
- /* special case: all KYC operations did succeed
- after we asked at the exchanges => 204 */
- struct MHD_Response *response;
-
- response = MHD_create_response_from_buffer_static (0,
- "");
- resume_kyc_with_response (kc,
- MHD_HTTP_NO_CONTENT,
- response);
- return;
- }
- resume_kyc_with_response (
- kc,
- kc->response_code, /* MHD_HTTP_OK or MHD_HTTP_ACCEPTED */
- TALER_MHD_MAKE_JSON_PACK (
- GNUNET_JSON_pack_array_incref ("pending_kycs",
- kc->pending_kycs),
- GNUNET_JSON_pack_array_incref ("timeout_kycs",
- kc->timeout_kycs)));
-}
-
-
-/**
- * Store KYC response from the exchange in the
- * local database.
- *
- * @param ekr request context
- * @param ks HTTP response details
- * @param account_kyc_status account KYC status details
- * @return true if the operation was successful
- */
-static bool
-store_kyc_status (
- const struct ExchangeKycRequest *ekr,
- const struct TALER_EXCHANGE_KycStatus *ks,
- const struct TALER_EXCHANGE_AccountKycStatus *account_kyc_status)
-{
- json_t *jlimits;
- enum GNUNET_DB_QueryStatus qs;
-
- jlimits = json_array ();
- GNUNET_assert (NULL != jlimits);
- for (unsigned int i = 0; i<account_kyc_status->limits_length; i++)
- {
- const struct TALER_EXCHANGE_AccountLimit *limit
- = &account_kyc_status->limits[i];
-
- pack_limit (limit,
- jlimits);
- }
-
- qs = TMH_db->account_kyc_set_status (
- TMH_db->cls,
- ekr->kc->mi->settings.id,
- &ekr->h_wire,
- ekr->exchange_url,
- GNUNET_TIME_timestamp_get (),
- ks->hr.http_status,
- ks->hr.ec,
- &account_kyc_status->access_token,
- jlimits,
- account_kyc_status->aml_review,
- false);
- json_decref (jlimits);
- if (qs < 0)
- {
- GNUNET_log (GNUNET_ERROR_TYPE_WARNING,
- "Failed to store KYC status in database!\n");
- return false;
- }
- return true;
-}
-
-
-/**
* Return JSON array with AccountLimit objects giving
* the current limits for this exchange.
*
@@ -581,297 +452,141 @@ store_kyc_status (
*/
static json_t *
get_exchange_limits (
- struct ExchangeKycRequest *ekr,
- const struct TALER_EXCHANGE_AccountKycStatus *account_kyc_status)
+ struct ExchangeKycRequest *ekr)
{
+ const struct TALER_EXCHANGE_Keys *keys = ekr->keys;
json_t *limits;
+ if (NULL != ekr->jlimits)
+ return json_incref (ekr->jlimits);
+ if (NULL == keys)
+ return NULL;
limits = json_array ();
GNUNET_assert (NULL != limits);
- if (NULL == account_kyc_status)
+ for (unsigned int i = 0; i<keys->hard_limits_length; i++)
{
- const struct TALER_EXCHANGE_Keys *keys = ekr->keys;
-
- GNUNET_assert (NULL != keys);
- for (unsigned int i = 0; i<keys->hard_limits_length; i++)
- {
- const struct TALER_EXCHANGE_AccountLimit *limit
- = &keys->hard_limits[i];
-
- pack_limit (limit,
- limits);
- }
- for (unsigned int i = 0; i<keys->zero_limits_length; i++)
- {
- const struct TALER_EXCHANGE_ZeroLimitedOperation *zlimit
- = &keys->zero_limits[i];
- json_t *jl;
- struct TALER_Amount zero;
+ const struct TALER_EXCHANGE_AccountLimit *limit
+ = &keys->hard_limits[i];
- GNUNET_assert (GNUNET_OK ==
- TALER_amount_set_zero (keys->currency,
- &zero));
- jl = GNUNET_JSON_PACK (
- TALER_JSON_pack_kycte ("operation_type",
- zlimit->operation_type),
- GNUNET_JSON_pack_time_rel ("timeframe",
- GNUNET_TIME_UNIT_ZERO),
- TALER_JSON_pack_amount ("threshold",
- &zero),
- GNUNET_JSON_pack_bool ("soft_limit",
- true)
- );
- GNUNET_assert (0 ==
- json_array_append_new (limits,
- jl));
- }
+ pack_limit (limit,
+ limits);
}
- else
+ for (unsigned int i = 0; i<keys->zero_limits_length; i++)
{
- for (unsigned int i = 0; i<account_kyc_status->limits_length; i++)
- {
- const struct TALER_EXCHANGE_AccountLimit *limit
- = &account_kyc_status->limits[i];
-
- pack_limit (limit,
- limits);
- }
+ const struct TALER_EXCHANGE_ZeroLimitedOperation *zlimit
+ = &keys->zero_limits[i];
+ json_t *jl;
+ struct TALER_Amount zero;
+
+ GNUNET_assert (GNUNET_OK ==
+ TALER_amount_set_zero (keys->currency,
+ &zero));
+ jl = GNUNET_JSON_PACK (
+ TALER_JSON_pack_kycte ("operation_type",
+ zlimit->operation_type),
+ GNUNET_JSON_pack_time_rel ("timeframe",
+ GNUNET_TIME_UNIT_ZERO),
+ TALER_JSON_pack_amount ("threshold",
+ &zero),
+ GNUNET_JSON_pack_bool ("soft_limit",
+ true)
+ );
+ GNUNET_assert (0 ==
+ json_array_append_new (limits,
+ jl));
}
return limits;
}
/**
- * Update exchange KYC account status, storing it
- * in the database and returning it in the response.
+ * Take data from @a ekr to expand our response.
*
- * @param[in,out] ekr our request context
- * @param ks overall HTTP response
- * @param account_kyc_status specific KYC status
+ * @param ekr exchange we are done inspecting
*/
static void
-update_account_status (
- struct ExchangeKycRequest *ekr,
- const struct TALER_EXCHANGE_KycStatus *ks,
- const struct TALER_EXCHANGE_AccountKycStatus *account_kyc_status)
+ekr_expand_response (struct ExchangeKycRequest *ekr)
{
- char *kyc_url;
-
- GNUNET_break (store_kyc_status (ekr,
- ks,
- account_kyc_status));
- {
- char *ats;
-
- ats = GNUNET_STRINGS_data_to_string_alloc (
- &account_kyc_status->access_token,
- sizeof (account_kyc_status->access_token));
- GNUNET_asprintf (&kyc_url,
- "%s/kyc-spa/%s",
- ekr->exchange_url,
- ats);
- GNUNET_free (ats);
- }
-
GNUNET_assert (
0 ==
json_array_append_new (
- ekr->kc->pending_kycs,
+ ekr->kc->kycs_data,
GNUNET_JSON_PACK (
- GNUNET_JSON_pack_allow_null (
- GNUNET_JSON_pack_array_steal (
- "limits",
- get_exchange_limits (ekr,
- account_kyc_status))),
- (TALER_EC_NONE == ks->hr.ec)
- ? GNUNET_JSON_pack_allow_null (
- GNUNET_JSON_pack_string (
- "dummy",
- NULL))
- : GNUNET_JSON_pack_uint64 ("exchange_code",
- ks->hr.ec),
- GNUNET_JSON_pack_uint64 ("exchange_http_status",
- ks->hr.http_status),
- GNUNET_JSON_pack_data_auto (
- "access_token",
- &account_kyc_status->access_token),
GNUNET_JSON_pack_string (
- "kyc_url",
- kyc_url),
+ "payto_uri",
+ ekr->payto_uri),
GNUNET_JSON_pack_string (
"exchange_url",
ekr->exchange_url),
- GNUNET_JSON_pack_string (
- "payto_uri",
- ekr->payto_uri))));
- GNUNET_free (kyc_url);
-}
-
-
-/**
- * Return exchange KYC account status when KYC auth
- * is required for authorization to the KYC state.
- *
- * @param[in,out] ekr our request context
- * @param ks overall HTTP response
- */
-static void
-return_auth_required (
- struct ExchangeKycRequest *ekr,
- const struct TALER_EXCHANGE_KycStatus *ks)
-{
- struct KycContext *kc = ekr->kc;
- enum GNUNET_DB_QueryStatus qs;
-
- qs = TMH_db->account_kyc_set_status (
- TMH_db->cls,
- kc->mi->settings.id,
- &ekr->h_wire,
- ekr->exchange_url,
- GNUNET_TIME_timestamp_get (),
- ks->hr.http_status,
- ks->hr.ec,
- NULL,
- NULL,
- false,
- true);
- if (qs < 0)
- {
- GNUNET_log (GNUNET_ERROR_TYPE_WARNING,
- "Failed to store KYC status in database!\n");
- }
-
- GNUNET_assert (
- 0 ==
- json_array_append_new (
- kc->pending_kycs,
- GNUNET_JSON_PACK (
- GNUNET_JSON_pack_allow_null (
- GNUNET_JSON_pack_array_steal (
- "limits",
- get_exchange_limits (ekr,
- NULL))),
- (TALER_EC_NONE == ks->hr.ec)
+ GNUNET_JSON_pack_bool ("no_keys",
+ ekr->no_keys),
+ GNUNET_JSON_pack_bool ("auth_conflict",
+ ekr->kyc_auth_conflict),
+ GNUNET_JSON_pack_uint64 ("exchange_http_status",
+ ekr->last_http_status),
+ (TALER_EC_NONE == ekr->last_ec)
? GNUNET_JSON_pack_allow_null (
GNUNET_JSON_pack_string (
"dummy",
NULL))
: GNUNET_JSON_pack_uint64 ("exchange_code",
- ks->hr.ec),
- GNUNET_JSON_pack_uint64 ("exchange_http_status",
- ks->hr.http_status),
- GNUNET_JSON_pack_array_incref ("payto_kycauths",
- ekr->pkaa),
- GNUNET_JSON_pack_string ("exchange_url",
- ekr->exchange_url),
- GNUNET_JSON_pack_string ("payto_uri",
- ekr->payto_uri))));
+ ekr->last_ec),
+ ekr->auth_ok
+ ? GNUNET_JSON_pack_data_auto (
+ "access_token",
+ &ekr->access_token)
+ : GNUNET_JSON_pack_allow_null (
+ GNUNET_JSON_pack_string (
+ "dummy",
+ NULL)),
+ GNUNET_JSON_pack_allow_null (
+ GNUNET_JSON_pack_array_steal (
+ "limits",
+ get_exchange_limits (ekr))),
+ GNUNET_JSON_pack_allow_null (
+ GNUNET_JSON_pack_array_incref ("payto_kycauths",
+ ekr->pkaa))
+ )));
}
/**
- * Function called with the result of a KYC check.
+ * We are done with the KYC request @a ekr. Remove it from the work list and
+ * check if we are done overall.
*
- * @param cls a `struct ExchangeKycRequest *`
- * @param ks the account's KYC status details
+ * @param[in] ekr key request that is done (and will be freed)
*/
static void
-exchange_check_cb (
- void *cls,
- const struct TALER_EXCHANGE_KycStatus *ks)
+ekr_finished (struct ExchangeKycRequest *ekr)
{
- struct ExchangeKycRequest *ekr = cls;
struct KycContext *kc = ekr->kc;
- ekr->kyc = NULL;
- GNUNET_log (GNUNET_ERROR_TYPE_INFO,
- "Checking KYC status of `%s' at `%s' is %u\n",
- ekr->payto_uri,
- ekr->exchange_url,
- ks->hr.http_status);
- switch (ks->hr.http_status)
+ ekr_expand_response (ekr);
+ GNUNET_CONTAINER_DLL_remove (kc->exchange_pending_head,
+ kc->exchange_pending_tail,
+ ekr);
+ json_decref (ekr->jlimits);
+ json_decref (ekr->pkaa);
+ if (NULL != ekr->keys)
+ TALER_EXCHANGE_keys_decref (ekr->keys);
+ GNUNET_free (ekr->exchange_url);
+ GNUNET_free (ekr->payto_uri);
+ GNUNET_free (ekr);
+
+ if (NULL != kc->exchange_pending_head)
+ return; /* wait for more */
+
+ if ( (! kc->return_immediately) &&
+ (! GNUNET_TIME_absolute_is_past (kc->timeout)) )
{
- case MHD_HTTP_OK:
- update_account_status (ekr,
- ks,
- &ks->details.ok);
- break;
- case MHD_HTTP_ACCEPTED:
- kc->response_code = MHD_HTTP_ACCEPTED;
- update_account_status (ekr,
- ks,
- &ks->details.accepted);
- break;
- case MHD_HTTP_NO_CONTENT:
- {
- enum GNUNET_DB_QueryStatus qs;
-
- qs = TMH_db->account_kyc_set_status (
- TMH_db->cls,
- kc->mi->settings.id,
- &ekr->h_wire,
- ekr->exchange_url,
- GNUNET_TIME_timestamp_get (),
- MHD_HTTP_NO_CONTENT,
- TALER_EC_NONE,
- NULL,
- NULL,
- false,
- true);
- if (qs < 0)
- {
- GNUNET_log (GNUNET_ERROR_TYPE_WARNING,
- "Failed to store KYC status in database!\n");
- }
- }
- break;
- case MHD_HTTP_FORBIDDEN: /* bad signature */
- case MHD_HTTP_NOT_FOUND: /* account unknown */
- case MHD_HTTP_CONFLICT: /* no account_pub known */
- return_auth_required (ekr,
- ks);
- break;
- default:
- {
- enum GNUNET_DB_QueryStatus qs;
-
- GNUNET_log (GNUNET_ERROR_TYPE_WARNING,
- "Exchange responded with HTTP status %u (%d) to /kyc-check request!\n",
- ks->hr.http_status,
- ks->hr.ec);
- kc->response_code = MHD_HTTP_BAD_GATEWAY;
- GNUNET_assert (
- 0 ==
- json_array_append_new (
- kc->timeout_kycs,
- GNUNET_JSON_PACK (
- GNUNET_JSON_pack_string ("exchange_url",
- ekr->exchange_url),
- GNUNET_JSON_pack_uint64 ("exchange_code",
- ks->hr.ec),
- GNUNET_JSON_pack_uint64 ("exchange_http_status",
- ks->hr.http_status))));
- qs = TMH_db->account_kyc_set_status (
- TMH_db->cls,
- kc->mi->settings.id,
- &ekr->h_wire,
- ekr->exchange_url,
- GNUNET_TIME_timestamp_get (),
- ks->hr.http_status,
- ks->hr.ec,
- NULL,
- NULL,
- false,
- true);
- if (qs < 0)
- {
- GNUNET_log (GNUNET_ERROR_TYPE_WARNING,
- "Failed to store KYC status in database!\n");
- }
- break;
- }
+ GNUNET_log (GNUNET_ERROR_TYPE_INFO,
+ "Remaining suspended: long poll target %d not reached\n",
+ kc->lpt);
+ return;
}
- ekr_finished (ekr);
+ /* All exchange requests done, create final
+ big response from cumulated replies */
+ resume_kyc_with_response (kc);
}
@@ -882,14 +597,13 @@ exchange_check_cb (
* in @a ekr.
*
* @param[in,out] request we are processing
- * @param keys exchange keys
*/
static void
determine_eligible_accounts (
- struct ExchangeKycRequest *ekr,
- const struct TALER_EXCHANGE_Keys *keys)
+ struct ExchangeKycRequest *ekr)
{
struct KycContext *kc = ekr->kc;
+ const struct TALER_EXCHANGE_Keys *keys = ekr->keys;
struct TALER_Amount kyc_amount;
char *merchant_pub_str;
@@ -1022,58 +736,35 @@ kyc_with_exchange (void *cls,
struct TMH_Exchange *exchange)
{
struct ExchangeKycRequest *ekr = cls;
- struct KycContext *kc = ekr->kc;
- struct TALER_PaytoHashP h_payto;
- union TALER_AccountPrivateKeyP ap;
(void) exchange;
ekr->fo = NULL;
if (NULL == keys)
{
GNUNET_log (GNUNET_ERROR_TYPE_WARNING,
- "Failed to download `%s/keys`\n",
+ "Failed to download `%skeys`\n",
ekr->exchange_url);
- kc->response_code = MHD_HTTP_BAD_GATEWAY;
- GNUNET_assert (
- 0 ==
- json_array_append_new (
- kc->timeout_kycs,
- GNUNET_JSON_PACK (
- TALER_JSON_pack_ec (
- TALER_EC_MERCHANT_GENERIC_EXCHANGE_KEYS_FAILURE))));
+ ekr->no_keys = true;
ekr_finished (ekr);
return;
}
- determine_eligible_accounts (ekr,
- keys);
- if (0 == json_array_size (ekr->pkaa))
+ ekr->keys = TALER_EXCHANGE_keys_incref (keys);
+ if (! ekr->auth_ok)
{
- /* No KYC auth wire transfers are possible to this exchange from
- our merchant bank account, so we cannot use this account with
- this exchange if it has any KYC requirements! */
- GNUNET_log (GNUNET_ERROR_TYPE_INFO,
- "KYC auth to `%s' impossible for merchant account `%s'\n",
- ekr->exchange_url,
- ekr->payto_uri);
- ekr_finished (ekr);
- return;
+ determine_eligible_accounts (ekr);
+ if (0 == json_array_size (ekr->pkaa))
+ {
+ /* No KYC auth wire transfers are possible to this exchange from
+ our merchant bank account, so we cannot use this account with
+ this exchange if it has any KYC requirements! */
+ GNUNET_log (GNUNET_ERROR_TYPE_INFO,
+ "KYC auth to `%s' impossible for merchant account `%s'\n",
+ ekr->exchange_url,
+ ekr->payto_uri);
+ ekr->kyc_auth_conflict = true;
+ }
}
- ekr->keys = TALER_EXCHANGE_keys_incref (keys);
- TALER_payto_hash (ekr->payto_uri,
- &h_payto);
- ap.merchant_priv = kc->mi->merchant_priv;
- GNUNET_log (GNUNET_ERROR_TYPE_INFO,
- "Checking KYC status of `%s' at `%s'\n",
- ekr->payto_uri,
- ekr->exchange_url);
- ekr->kyc = TALER_EXCHANGE_kyc_check (
- TMH_curl_ctx,
- ekr->exchange_url,
- &h_payto,
- &ap,
- GNUNET_TIME_absolute_get_remaining (kc->timeout),
- &exchange_check_cb,
- ekr);
+ ekr_finished (ekr);
}
@@ -1083,46 +774,84 @@ kyc_with_exchange (void *cls,
*
* @param cls our `struct KycContext *`
* @param h_wire hash of the wire account
- * @param exchange_kyc_serial serial number for the KYC process at the exchange, 0 if unknown
* @param payto_uri payto:// URI of the merchant's bank account
* @param exchange_url base URL of the exchange for which this is a status
* @param last_check when did we last get an update on our KYC status from the exchange
* @param kyc_ok true if we satisfied the KYC requirements
+ * @param access_token access token for the KYC SPA, NULL if we cannot access it yet (need KYC auth wire transfer)
+ * @param last_http_status last HTTP status from /kyc-check
+ * @param last_ec last Taler error code from /kyc-check
+ * @param in_aml_review true if the account is pending review
+ * @param jlimits JSON array of applicable AccountLimits, or NULL if unknown (like defaults apply)
*/
static void
kyc_status_cb (
void *cls,
const struct TALER_MerchantWireHashP *h_wire,
- uint64_t exchange_kyc_serial,
const char *payto_uri,
const char *exchange_url,
struct GNUNET_TIME_Timestamp last_check,
- bool kyc_ok)
+ bool kyc_ok,
+ const struct TALER_AccountAccessTokenP *access_token,
+ unsigned int last_http_status,
+ enum TALER_ErrorCode last_ec,
+ bool in_aml_review,
+ const json_t *jlimits)
{
struct KycContext *kc = cls;
struct ExchangeKycRequest *ekr;
- if (kyc_ok &&
- (GNUNET_TIME_relative_cmp (
- GNUNET_TIME_absolute_get_duration (last_check.abs_time),
- <,
- STALE_KYC_TIMEOUT)) )
- return; /* KYC ok, ignore! */
- kc->response_code = MHD_HTTP_OK;
+ switch (kc->lpt)
+ {
+ case TALER_EXCHANGE_KLPT_NONE:
+ break;
+ case TALER_EXCHANGE_KLPT_KYC_AUTH_TRANSFER:
+ if (NULL != access_token)
+ kc->return_immediately = true;
+ break;
+ case TALER_EXCHANGE_KLPT_INVESTIGATION_DONE:
+ if (! in_aml_review)
+ kc->return_immediately = true;
+ break;
+ case TALER_EXCHANGE_KLPT_KYC_OK:
+ if (kyc_ok)
+ kc->return_immediately = true;
+ break;
+ }
ekr = GNUNET_new (struct ExchangeKycRequest);
GNUNET_CONTAINER_DLL_insert (kc->exchange_pending_head,
kc->exchange_pending_tail,
ekr);
+ ekr->last_http_status = last_http_status;
+ ekr->last_ec = last_ec;
+ if (NULL != jlimits)
+ ekr->jlimits = json_incref ((json_t *) jlimits);
ekr->h_wire = *h_wire;
ekr->exchange_url = GNUNET_strdup (exchange_url);
ekr->payto_uri = GNUNET_strdup (payto_uri);
ekr->last_check = last_check;
+ ekr->kyc_ok = kyc_ok;
ekr->kc = kc;
- ekr->fo = TMH_EXCHANGES_keys4exchange (
- exchange_url,
- false,
- &kyc_with_exchange,
- ekr);
+ ekr->in_aml_review = in_aml_review;
+ ekr->auth_ok = (NULL != access_token);
+ if ( (! ekr->auth_ok) ||
+ (NULL == ekr->jlimits) )
+ {
+ /* Figure out wire transfer instructions */
+ if (GNUNET_NO == kc->suspended)
+ {
+ MHD_suspend_connection (kc->connection);
+ kc->suspended = GNUNET_YES;
+ }
+ ekr->fo = TMH_EXCHANGES_keys4exchange (
+ exchange_url,
+ false,
+ &kyc_with_exchange,
+ ekr);
+ return;
+ }
+ ekr->access_token = *access_token;
+ ekr_finished (ekr);
}
@@ -1153,32 +882,38 @@ get_instances_ID_kyc (
kc);
kc->connection = connection;
kc->hc = hc;
- kc->pending_kycs = json_array ();
- GNUNET_assert (NULL != kc->pending_kycs);
- kc->timeout_kycs = json_array ();
- GNUNET_assert (NULL != kc->timeout_kycs);
-
+ kc->kycs_data = json_array ();
+ GNUNET_assert (NULL != kc->kycs_data);
TALER_MHD_parse_request_timeout (connection,
&kc->timeout);
- if (! GNUNET_TIME_absolute_is_past (kc->timeout))
- kc->timeout_task
- = GNUNET_SCHEDULER_add_at (kc->timeout,
- &handle_kyc_timeout,
- kc);
-
+ {
+ uint64_t num = 0;
+ int val;
+
+ TALER_MHD_parse_request_number (connection,
+ "lpt",
+ &num);
+ val = (int) num;
+ if ( (val < 0) ||
+ (val > TALER_EXCHANGE_KLPT_MAX) )
+ {
+ /* Protocol violation, but we can be graceful and
+ just ignore the long polling! */
+ GNUNET_break_op (0);
+ val = TALER_EXCHANGE_KLPT_NONE;
+ }
+ kc->lpt = (enum TALER_EXCHANGE_KycLongPollTarget) val;
+ }
+ kc->return_immediately
+ = (TALER_EXCHANGE_KLPT_NONE == kc->lpt);
/* process 'exchange_url' argument */
kc->exchange_url = MHD_lookup_connection_value (
connection,
MHD_GET_ARGUMENT_KIND,
"exchange_url");
if ( (NULL != kc->exchange_url) &&
- (! TALER_url_valid_charset (kc->exchange_url) ||
- ( (0 != strncasecmp (kc->exchange_url,
- "http://",
- strlen ("http://"))) &&
- (0 != strncasecmp (kc->exchange_url,
- "https://",
- strlen ("https://"))) ) ) )
+ ( (! TALER_url_valid_charset (kc->exchange_url)) ||
+ (! TALER_is_web_url (kc->exchange_url)) ) )
{
GNUNET_break_op (0);
return TALER_MHD_reply_with_error (
@@ -1192,68 +927,101 @@ get_instances_ID_kyc (
"h_wire",
&kc->h_wire,
kc->have_h_wire);
- /* Check our database */
+
+ if ( (TALER_EXCHANGE_KLPT_NONE != kc->lpt) &&
+ (! GNUNET_TIME_absolute_is_past (kc->timeout)) )
{
- enum GNUNET_DB_QueryStatus qs;
-
- qs = TMH_db->account_kyc_get_status (
- TMH_db->cls,
- mi->settings.id,
- kc->have_h_wire
- ? &kc->h_wire
- : NULL,
- kc->exchange_url,
- &kyc_status_cb,
- kc);
- if (qs < 0)
+ if (kc->have_h_wire)
{
- GNUNET_break (0);
- return TALER_MHD_reply_with_ec (
- connection,
- TALER_EC_GENERIC_DB_FETCH_FAILED,
- "account_kyc_get_status");
+ struct TALER_MERCHANTDB_MerchantKycStatusChangeEventP ev = {
+ .header.size = htons (sizeof (ev)),
+ .header.type = htons (
+ TALER_DBEVENT_MERCHANT_EXCHANGE_KYC_STATUS_CHANGED
+ ),
+ .h_wire = kc->h_wire
+ };
+
+ kc->eh = TMH_db->event_listen (
+ TMH_db->cls,
+ &ev.header,
+ GNUNET_TIME_absolute_get_remaining (kc->timeout),
+ &kyc_change_cb,
+ kc);
}
- }
- if (kc->kyc_serial_pending)
+ else
+ {
+ struct GNUNET_DB_EventHeaderP hdr = {
+ .size = htons (sizeof (hdr)),
+ .type = htons (TALER_DBEVENT_MERCHANT_KYC_STATUS_CHANGED)
+ };
+
+ kc->eh = TMH_db->event_listen (
+ TMH_db->cls,
+ &hdr,
+ GNUNET_TIME_absolute_get_remaining (kc->timeout),
+ &kyc_change_cb,
+ kc);
+ }
+ } /* end register LISTEN hooks */
+ } /* end 1st time initialization */
+
+ if (GNUNET_SYSERR == kc->suspended)
+ return MHD_NO; /* during shutdown, we don't generate any more replies */
+ GNUNET_assert (GNUNET_NO == kc->suspended);
+
+ if (NULL != kc->response)
+ return MHD_queue_response (connection,
+ kc->response_code,
+ kc->response);
+
+ /* Check our database */
+ {
+ enum GNUNET_DB_QueryStatus qs;
+
+ GNUNET_break (0 ==
+ json_array_clear (kc->kycs_data));
+ qs = TMH_db->account_kyc_get_status (
+ TMH_db->cls,
+ mi->settings.id,
+ kc->have_h_wire
+ ? &kc->h_wire
+ : NULL,
+ kc->exchange_url,
+ &kyc_status_cb,
+ kc);
+ if (qs < 0)
{
- GNUNET_log (GNUNET_ERROR_TYPE_INFO,
- "Exchange legitimization UUID unknown, assuming KYC pending\n");
- return TALER_MHD_REPLY_JSON_PACK (
+ /* Database error */
+ GNUNET_break (0);
+ if (GNUNET_YES == kc->suspended)
+ {
+ /* must have suspended before DB error, resume! */
+ MHD_resume_connection (connection);
+ kc->suspended = GNUNET_NO;
+ }
+ return TALER_MHD_reply_with_ec (
connection,
- MHD_HTTP_SERVICE_UNAVAILABLE,
- GNUNET_JSON_pack_string ("hint",
- "awaiting legitimization UUID"));
+ TALER_EC_GENERIC_DB_FETCH_FAILED,
+ "account_kyc_get_status");
}
- if (NULL == kc->exchange_pending_head)
+ if (GNUNET_DB_STATUS_SUCCESS_NO_RESULTS == qs)
+ {
+ /* no matching accounts, could not have suspended */
+ GNUNET_assert (GNUNET_NO == kc->suspended);
return TALER_MHD_reply_static (connection,
MHD_HTTP_NO_CONTENT,
NULL,
NULL,
0);
- MHD_suspend_connection (connection);
- kc->suspended = GNUNET_YES;
- GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
- "Suspending KYC request handling while checking with the exchange(s)\n");
- return MHD_YES;
- }
- if (GNUNET_SYSERR == kc->suspended)
- return MHD_NO; /* during shutdown, we don't generate any more replies */
- GNUNET_assert (GNUNET_NO == kc->suspended);
- if (0 != kc->response_code)
- {
- /* We are *done* processing the request, just queue the response (!) */
- if (UINT_MAX == kc->response_code)
- {
- GNUNET_break (0);
- return MHD_NO; /* hard error */
}
- return MHD_queue_response (connection,
- kc->response_code,
- kc->response);
}
- /* we should never get here */
- GNUNET_break (0);
- return MHD_NO;
+ if (GNUNET_YES == kc->suspended)
+ return MHD_YES;
+ /* Should have generated a response */
+ GNUNET_break (NULL != kc->response);
+ return MHD_queue_response (connection,
+ kc->response_code,
+ kc->response);
}
diff --git a/src/backend/taler-merchant-kyccheck.c b/src/backend/taler-merchant-kyccheck.c
index 279be891..f001285c 100644
--- a/src/backend/taler-merchant-kyccheck.c
+++ b/src/backend/taler-merchant-kyccheck.c
@@ -39,6 +39,14 @@
30)
/**
+ * How long do we wait between requests if all we wait
+ * for is a change in the AML investigation status?
+ */
+#define AML_FREQ GNUNET_TIME_relative_multiply ( \
+ GNUNET_TIME_UNIT_HOURS, \
+ 6)
+
+/**
* How many inquiries do we process concurrently at most.
*/
#define OPEN_INQUIRY_LIMIT 1024
@@ -467,8 +475,15 @@ exchange_check_cb (
store_kyc_status (i,
&ks->details.ok);
i->backoff = GNUNET_TIME_UNIT_ZERO;
- /* KYC is OK, only check again if triggered */
- i->due = GNUNET_TIME_UNIT_FOREVER_ABS;
+ if (i->aml_review)
+ {
+ i->due = GNUNET_TIME_relative_to_absolute (AML_FREQ);
+ }
+ else
+ {
+ /* KYC is OK, only check again if triggered */
+ i->due = GNUNET_TIME_UNIT_FOREVER_ABS;
+ }
break;
case MHD_HTTP_ACCEPTED:
i->last_kyc_check = GNUNET_TIME_timestamp_get ();
@@ -573,6 +588,7 @@ static void
inquiry_work (void *cls)
{
struct Inquiry *i = cls;
+ enum TALER_EXCHANGE_KycLongPollTarget lpt;
i->task = NULL;
if (! GNUNET_TIME_absolute_is_past (i->due))
@@ -600,11 +616,19 @@ inquiry_work (void *cls)
i->e->keys->exchange_url);
i->timeout
= GNUNET_TIME_relative_to_absolute (EXCHANGE_TIMEOUT);
+ lpt = TALER_EXCHANGE_KLPT_NONE;
+ if (! i->auth_ok)
+ lpt = TALER_EXCHANGE_KLPT_NONE;
+ else if (! i->kyc_ok)
+ lpt = TALER_EXCHANGE_KLPT_KYC_OK;
+ else if (i->aml_review)
+ lpt = TALER_EXCHANGE_KLPT_INVESTIGATION_DONE;
i->kyc = TALER_EXCHANGE_kyc_check (
ctx,
i->e->keys->exchange_url,
&i->a->h_payto,
&i->a->ap,
+ lpt,
EXCHANGE_TIMEOUT,
&exchange_check_cb,
i);
@@ -997,7 +1021,9 @@ find_keys (const char *exchange_url)
}
if (GNUNET_DB_STATUS_SUCCESS_NO_RESULTS == qs)
{
- GNUNET_break (0);
+ GNUNET_log (GNUNET_ERROR_TYPE_INFO,
+ "No %s/keys yet!\n",
+ exchange_url);
return;
}
for (e = e_head; NULL != e; e = e->next)