aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorChristian Grothoff <christian@grothoff.org>2024-06-04 14:24:18 +0200
committerChristian Grothoff <christian@grothoff.org>2024-07-29 12:18:42 +0200
commit8d1e83097d360916c07552b7765339727760e2a8 (patch)
treeb3bd2c50b2246400adf4d36a8b4339d88ba6e1b7
parent9218606c55d80fcdd673fbacdb5b73a32793b6b3 (diff)
work on kyc-info endpoint
-rw-r--r--src/exchange/Makefile.am1
-rw-r--r--src/exchange/taler-exchange-httpd_kyc-info.c623
-rw-r--r--src/exchange/taler-exchange-httpd_kyc-start.c641
-rw-r--r--src/exchange/taler-exchange-httpd_reserves_open.c2
-rw-r--r--src/exchangedb/Makefile.am1
-rw-r--r--src/exchangedb/pg_insert_aml_decision.c10
-rw-r--r--src/exchangedb/pg_insert_kyc_attributes.c7
-rw-r--r--src/exchangedb/pg_insert_kyc_failure.c4
-rw-r--r--src/exchangedb/pg_lookup_kyc_status_by_token.c65
-rw-r--r--src/exchangedb/pg_lookup_kyc_status_by_token.h46
-rw-r--r--src/exchangedb/pg_lookup_wire_fee_by_time.c2
-rw-r--r--src/exchangedb/pg_update_kyc_process_by_row.c6
-rw-r--r--src/exchangedb/plugin_exchangedb_postgres.c4
-rw-r--r--src/include/taler_exchangedb_plugin.h21
-rw-r--r--src/include/taler_kyclogic_lib.h36
-rw-r--r--src/kyclogic/kyclogic_api.c138
-rw-r--r--src/kyclogic/taler-exchange-kyc-tester.c6
17 files changed, 1174 insertions, 439 deletions
diff --git a/src/exchange/Makefile.am b/src/exchange/Makefile.am
index 2f5d088d8..eebe97d2f 100644
--- a/src/exchange/Makefile.am
+++ b/src/exchange/Makefile.am
@@ -142,6 +142,7 @@ taler_exchange_httpd_SOURCES = \
taler-exchange-httpd_extensions.c taler-exchange-httpd_extensions.h \
taler-exchange-httpd_keys.c taler-exchange-httpd_keys.h \
taler-exchange-httpd_kyc-check.c taler-exchange-httpd_kyc-check.h \
+ taler-exchange-httpd_kyc-info.c taler-exchange-httpd_kyc-info.h \
taler-exchange-httpd_kyc-proof.c taler-exchange-httpd_kyc-proof.h \
taler-exchange-httpd_kyc-wallet.c taler-exchange-httpd_kyc-wallet.h \
taler-exchange-httpd_kyc-webhook.c taler-exchange-httpd_kyc-webhook.h \
diff --git a/src/exchange/taler-exchange-httpd_kyc-info.c b/src/exchange/taler-exchange-httpd_kyc-info.c
index 974d3b84f..5f5b95da8 100644
--- a/src/exchange/taler-exchange-httpd_kyc-info.c
+++ b/src/exchange/taler-exchange-httpd_kyc-info.c
@@ -1,6 +1,6 @@
/*
This file is part of TALER
- Copyright (C) 2021-2023 Taler Systems SA
+ Copyright (C) 2021-2024 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
@@ -55,37 +55,16 @@ 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.
*/
struct GNUNET_DB_EventHandler *eh;
/**
- * Row of the requirement being infoed.
- */
- uint64_t requirement_row;
-
- /**
- * Row of KYC process being initiated.
- */
- uint64_t process_row;
-
- /**
- * Hash of the payto:// URI we are confirming to
- * have finished the KYC for.
+ * #MHD_HTTP_HEADER_IF_NONE_MATCH Etag value sent by the client. 0 for none
+ * (or malformed).
*/
- struct TALER_PaytoHashP h_payto;
+ uint64_t etag_in;
/**
* When will this request time out?
@@ -93,60 +72,16 @@ struct KycPoller
struct GNUNET_TIME_Absolute timeout;
/**
- * Public limits that apply to the account,
- * if @e have_token is true.
- * NULL if no limits are public.
- */
- json_t *limits;
-
- /**
* Set to access token for a KYC process by the account,
* if @e have_token is true.
*/
struct TALER_AccountAccessTokenP access_token;
/**
- * Signature by the account owner authorizing this
- * operation.
- */
- union TALER_AccountSignatureP account_sig;
-
- /**
- * 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 account is under AML review and this is
- * exposed.
- */
- bool aml_review;
-
- /**
- * True if @e access_token was initialized.
- */
- bool have_token;
-
- /**
* True if we are still suspended.
*/
bool suspended;
- /**
- * False if KYC is not required.
- */
- bool kyc_required;
-
- /**
- * True if we once tried the KYC initiation.
- */
- bool ih_done;
-
};
@@ -171,11 +106,6 @@ TEH_kyc_info_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;
@@ -205,261 +135,10 @@ 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;
- }
- json_decref (kyp->kyc_details);
- GNUNET_free (kyp->kyc_url);
- GNUNET_free (kyp->hint);
GNUNET_free (kyp);
}
-#if FIXME
-/**
- * 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 `%s' completed with ec=%d (%s)\n",
- provider_legitimization_id,
- 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_process_by_row (
- TEH_plugin->cls,
- 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)
- 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 ();
-}
-
-
-#endif
-
-
-/**
- * Function implementing database transaction to info 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
- * transaction logic MUST queue a MHD response and set @a mhd_ret. IF it
- * returns the soft error code, the function MAY be called again to retry and
- * MUST not queue a MHD response.
- *
- * @param cls closure with a `struct KycPoller *`
- * @param connection MHD request which triggered the transaction
- * @param[out] mhd_ret set to MHD response status for @a connection,
- * if transaction failed (!)
- * @return transaction status
- */
-static enum GNUNET_DB_QueryStatus
-kyc_info (void *cls,
- struct MHD_Connection *connection,
- MHD_RESULT *mhd_ret)
-{
-#if FIXME
- struct KycPoller *kyp = cls;
- enum GNUNET_DB_QueryStatus qs;
- struct TALER_KYCLOGIC_ProviderDetails *pd;
- enum GNUNET_GenericReturnValue ret;
- struct TALER_PaytoHashP h_payto;
- char *requirements;
- char *redirect_url;
- bool satisfied;
-
- qs = TEH_plugin->lookup_kyc_requirement_by_row (
- TEH_plugin->cls,
- kyp->requirement_row,
- &requirements,
- &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->requirement_row);
- return qs;
- }
- if (qs < 0)
- {
- GNUNET_break (GNUNET_DB_STATUS_HARD_ERROR != qs);
- return qs;
- }
- if (0 !=
- GNUNET_memcmp (&kyp->h_payto,
- &h_payto))
- {
- GNUNET_log (GNUNET_ERROR_TYPE_WARNING,
- "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_INFO_AUTHORIZATION_FAILED,
- "h_payto");
- GNUNET_free (requirements);
- return GNUNET_DB_STATUS_HARD_ERROR;
- }
- qs = TALER_KYCLOGIC_info_satisfied (
- &requirements,
- &h_payto,
- &kyp->kyc_details,
- TEH_plugin->select_satisfied_kyc_processes,
- TEH_plugin->cls,
- &satisfied);
- 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_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;
- }
-
- kyp->kyc_required = true;
- ret = TALER_KYCLOGIC_requirements_to_logic (requirements,
- &kyp->ih_logic,
- &pd,
- &kyp->section_name);
- if (GNUNET_OK != ret)
- {
- GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
- "KYC requirements `%s' cannot be infoed, 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,
- 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 info with logic %s\n",
- kyp->ih_logic->name);
- kyp->ih = kyp->ih_logic->initiate (kyp->ih_logic->cls,
- pd,
- &h_payto,
- kyp->process_row,
- &initiate_cb,
- kyp);
- GNUNET_break (NULL != kyp->ih);
- return qs;
-#else
- GNUNET_break (0);
- return GNUNET_DB_STATUS_HARD_ERROR;
-#endif
-}
-
-
/**
* Function called on events received from Postgres.
* Wakes up long pollers.
@@ -482,24 +161,147 @@ db_event_cb (void *cls,
if (! kyp->suspended)
return; /* event triggered while main transaction
was still running, or got multiple wake-up events */
- GNUNET_log (GNUNET_ERROR_TYPE_INFO,
- "Received KYC update event\n");
- kyp->suspended = false;
GNUNET_async_scope_enter (&rc->async_scope_id,
&old_scope);
- TEH_info_invariants ();
GNUNET_log (GNUNET_ERROR_TYPE_INFO,
"Resuming from long-polling on KYC status\n");
GNUNET_CONTAINER_DLL_remove (kyp_head,
kyp_tail,
kyp);
+ kyp->suspended = false;
MHD_resume_connection (kyp->connection);
TALER_MHD_daemon_trigger ();
- TEH_info_invariants ();
GNUNET_async_scope_restore (&old_scope);
}
+/**
+ * Generate a reply with the KycProcessClientInformation from
+ * the LegitimizationMeasures.
+ *
+ * @param[in,out] kyp request to reply on
+ * @param row_id etag to set for the response
+ * @param jmeasures measures to encode
+ * @return MHD status code
+ */
+static MHD_RESULT
+generate_reply (struct KycPoller *kyp,
+ uint64_t row_id,
+ const json_t *jmeasures)
+{
+ const json_t *mis;
+ bool is_and_combinator = false;
+ bool verboten;
+ struct GNUNET_JSON_Specification spec[] = {
+ GNUNET_JSON_spec_mark_optional (
+ GNUNET_JSON_spec_bool ("is_and_combinator",
+ &is_and_combinator),
+ NULL),
+ GNUNET_JSON_spec_bool ("verboten",
+ &verboten),
+ GNUNET_JSON_spec_array_const ("measures",
+ &mis),
+ GNUNET_JSON_spec_end ()
+ };
+ enum GNUNET_GenericReturnValue res;
+ const char *ename;
+ unsigned int eline;
+ json_t *kris;
+ size_t i;
+ json_t *mi;
+
+ res = GNUNET_JSON_parse (jmeasures,
+ spec,
+ &ename,
+ &eline);
+ if (GNUNET_OK != res)
+ {
+ GNUNET_break (0);
+ return TALER_MHD_reply_with_ec (
+ kyp->connection,
+ TALER_EC_GENERIC_DB_INVARIANT_FAILURE,
+ ename);
+ }
+ kris = json_array ();
+ GNUNET_assert (NULL != kris);
+ json_array_foreach ((json_t *) mis, i, mi)
+ {
+ const char *check_name;
+ const char *prog_name;
+ struct GNUNET_JSON_Specification ispec[] = {
+ GNUNET_JSON_spec_string ("check_name",
+ &check_name),
+ GNUNET_JSON_spec_string ("prog_name",
+ &prog_name),
+ GNUNET_JSON_spec_end ()
+ };
+ json_t *kri;
+
+ res = GNUNET_JSON_parse (mi,
+ ispec,
+ &ename,
+ &eline);
+ if (GNUNET_OK != res)
+ {
+ GNUNET_break (0);
+ json_decref (kris);
+ return TALER_MHD_reply_with_ec (
+ kyp->connection,
+ TALER_EC_GENERIC_DB_INVARIANT_FAILURE,
+ ename);
+ }
+ kri = TALER_KYCLOGIC_measure_to_requirement (
+ check_name,
+ prog_name,
+ &kyp->access_token,
+ i,
+ row_id);
+ if (NULL == kri)
+ {
+ GNUNET_break (0);
+ json_decref (kris);
+ return TALER_MHD_reply_with_ec (
+ kyp->connection,
+ TALER_EC_GENERIC_DB_INVARIANT_FAILURE,
+ "could not convert measure to requirement");
+ }
+ GNUNET_assert (0 ==
+ json_array_append_new (kris,
+ kri));
+ }
+
+ {
+ char etags[64];
+ struct MHD_Response *resp;
+ MHD_RESULT res;
+
+ GNUNET_snprintf (etags,
+ sizeof (etags),
+ "%llu",
+ (unsigned long long) row_id);
+ resp = TALER_MHD_MAKE_JSON_PACK (
+ GNUNET_JSON_pack_array_steal ("requirements",
+ kris),
+ GNUNET_JSON_pack_bool ("is_and_combinator",
+ is_and_combinator),
+ GNUNET_JSON_pack_allow_null (
+ /* TODO: support vATTEST */
+ GNUNET_JSON_pack_object_steal ("voluntary_checks",
+ NULL)));
+ GNUNET_break (MHD_YES ==
+ MHD_add_response_header (resp,
+ MHD_HTTP_HEADER_ETAG,
+ etags));
+ res = MHD_queue_response (kyp->connection,
+ MHD_HTTP_OK,
+ resp);
+ GNUNET_break (MHD_YES == res);
+ MHD_destroy_response (resp);
+ return res;
+ }
+}
+
+
MHD_RESULT
TEH_handler_kyc_info (
struct TEH_RequestContext *rc,
@@ -507,7 +309,9 @@ TEH_handler_kyc_info (
{
struct KycPoller *kyp = rc->rh_ctx;
MHD_RESULT res;
- enum GNUNET_GenericReturnValue ret;
+ enum GNUNET_DB_QueryStatus qs;
+ uint64_t last_row;
+ json_t *jmeasures;
if (NULL == kyp)
{
@@ -516,27 +320,52 @@ TEH_handler_kyc_info (
rc->rh_ctx = kyp;
rc->rh_cleaner = &kyp_cleanup;
+ if (GNUNET_OK !=
+ GNUNET_STRINGS_string_to_data (
+ args[0],
+ strlen (args[0]),
+ &kyp->access_token,
+ sizeof (kyp->access_token)))
{
- unsigned long long requirement_row;
- char dummy;
-
- if (1 !=
- sscanf (args[0],
- "%llu%c",
- &requirement_row,
- &dummy))
- {
- GNUNET_break_op (0);
- return TALER_MHD_reply_with_error (rc->connection,
- MHD_HTTP_BAD_REQUEST,
- TALER_EC_GENERIC_PARAMETER_MALFORMED,
- "requirement_row");
- }
- kyp->requirement_row = (uint64_t) requirement_row;
+ GNUNET_break_op (0);
+ return TALER_MHD_reply_with_error (
+ rc->connection,
+ MHD_HTTP_BAD_REQUEST,
+ TALER_EC_GENERIC_PARAMETER_MALFORMED,
+ "access token");
}
TALER_MHD_parse_request_timeout (rc->connection,
&kyp->timeout);
- }
+
+ /* Get etag */
+ {
+ const char *etags;
+
+ etags = MHD_lookup_connection_value (
+ rc->connection,
+ MHD_HEADER_KIND,
+ MHD_HTTP_HEADER_IF_NONE_MATCH);
+ if (NULL != etags)
+ {
+ char dummy;
+ unsigned long long ev;
+
+ if (1 != sscanf (etags,
+ "\"%llu\"%c",
+ &ev,
+ &dummy))
+ {
+ GNUNET_log (GNUNET_ERROR_TYPE_WARNING,
+ "Client send malformed `If-None-Match' header `%s'\n",
+ etags);
+ }
+ else
+ {
+ kyp->etag_in = (uint64_t) ev;
+ }
+ }
+ } /* etag */
+ } /* one-time initialization */
if ( (NULL == kyp->eh) &&
GNUNET_TIME_absolute_is_future (kyp->timeout) )
@@ -544,7 +373,7 @@ TEH_handler_kyc_info (
struct TALER_KycCompletedEventP rep = {
.header.size = htons (sizeof (rep)),
.header.type = htons (TALER_DBEVENT_EXCHANGE_KYC_COMPLETED),
- .h_payto = kyp->h_payto
+ .access_token = kyp->access_token
};
GNUNET_log (GNUNET_ERROR_TYPE_INFO,
@@ -557,84 +386,54 @@ TEH_handler_kyc_info (
rc);
}
- ret = TEH_DB_run_transaction (rc->connection,
- "kyc info",
- TEH_MT_REQUEST_OTHER,
- &res,
- &kyc_info,
- kyp);
- if (GNUNET_SYSERR == ret)
- {
- GNUNET_log (GNUNET_ERROR_TYPE_INFO,
- "Transaction failed.\n");
- return res;
- }
-
- if (TALER_EC_NONE != kyp->ec)
+ qs = TEH_plugin->lookup_kyc_status_by_token (
+ TEH_plugin->cls,
+ &kyp->access_token,
+ &last_row,
+ &jmeasures);
+ if (qs < 0)
{
- return TALER_MHD_reply_with_ec (rc->connection,
- kyp->ec,
- kyp->hint);
+ GNUNET_break (0);
+ return TALER_MHD_reply_with_ec (
+ rc->connection,
+ TALER_EC_GENERIC_DB_FETCH_FAILED,
+ "lookup_kyc_status_by_token");
}
-
- if (NULL != kyp->ih)
+ if (GNUNET_DB_STATUS_SUCCESS_NO_RESULTS == qs)
{
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;
+ "No KYC requirement open\n");
+ return TALER_MHD_REPLY_JSON_PACK (
+ rc->connection,
+ MHD_HTTP_OK,
+ GNUNET_JSON_pack_allow_null (
+ /* TODO: support vATTEST */
+ GNUNET_JSON_pack_object_steal ("voluntary_checks",
+ NULL)));
}
-
- /* long polling for positive result? */
- if ( (kyp->kyc_required) &&
- GNUNET_TIME_absolute_is_future (kyp->timeout))
+ if ( (last_row == kyp->etag_in) &&
+ 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_remaining (
- kyp->timeout),
- true));
+ "Suspending HTTP request on timeout (%s)\n",
+ 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);
- MHD_suspend_connection (kyp->connection);
+ MHD_suspend_connection (rc->connection);
return MHD_YES;
}
- /* KYC plugin generated reply? */
- if (kyp->have_token)
- {
- return TALER_MHD_REPLY_JSON_PACK (
- rc->connection,
- kyp->kyc_required
- ? MHD_HTTP_ACCEPTED
- : MHD_HTTP_OK,
- GNUNET_JSON_pack_bool ("aml_review",
- kyp->aml_review),
- GNUNET_JSON_pack_data_auto ("access_token",
- &kyp->access_token),
- GNUNET_JSON_pack_allow_null (
- GNUNET_JSON_pack_array_steal ("limits",
- kyp->limits)));
- }
-
- /* KYC not required */
- GNUNET_log (GNUNET_ERROR_TYPE_INFO,
- "KYC not required %llu\n",
- (unsigned long long) kyp->requirement_row);
- return TALER_MHD_reply_static (
- rc->connection,
- MHD_HTTP_NO_CONTENT,
- NULL,
- NULL,
- 0);
+ res = generate_reply (kyp,
+ last_row,
+ jmeasures);
+ json_decref (jmeasures);
+ return res;
}
diff --git a/src/exchange/taler-exchange-httpd_kyc-start.c b/src/exchange/taler-exchange-httpd_kyc-start.c
new file mode 100644
index 000000000..54870b52c
--- /dev/null
+++ b/src/exchange/taler-exchange-httpd_kyc-start.c
@@ -0,0 +1,641 @@
+/*
+ This file is part of TALER
+ 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
+ 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_kyc-start.c
+ * @brief Handle request for starting a KYC process with an external provider.
+ * @author Christian Grothoff
+ */
+#include "platform.h"
+#include <gnunet/gnunet_util_lib.h>
+#include <gnunet/gnunet_json_lib.h>
+#include <jansson.h>
+#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"
+#include "taler-exchange-httpd_keys.h"
+#include "taler-exchange-httpd_kyc-wallet.h"
+#include "taler-exchange-httpd_responses.h"
+
+
+/**
+ * Reserve GET request that is long-polling.
+ */
+struct KycPoller
+{
+ /**
+ * Kept in a DLL.
+ */
+ struct KycPoller *next;
+
+ /**
+ * Kept in a DLL.
+ */
+ struct KycPoller *prev;
+
+ /**
+ * Connection we are handling.
+ */
+ 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.
+ */
+ struct GNUNET_DB_EventHandler *eh;
+
+ /**
+ * Row of the requirement being started.
+ */
+ uint64_t requirement_row;
+
+ /**
+ * Row of KYC process being initiated.
+ */
+ uint64_t process_row;
+
+ /**
+ * Hash of the payto:// URI we are confirming to
+ * have finished the KYC for.
+ */
+ struct TALER_PaytoHashP h_payto;
+
+ /**
+ * When will this request time out?
+ */
+ struct GNUNET_TIME_Absolute timeout;
+
+ /**
+ * Public limits that apply to the account,
+ * if @e have_token is true.
+ * NULL if no limits are public.
+ */
+ json_t *limits;
+
+ /**
+ * Set to access token for a KYC process by the account,
+ * if @e have_token is true.
+ */
+ struct TALER_AccountAccessTokenP access_token;
+
+ /**
+ * Signature by the account owner authorizing this
+ * operation.
+ */
+ union TALER_AccountSignatureP account_sig;
+
+ /**
+ * 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 account is under AML review and this is
+ * exposed.
+ */
+ bool aml_review;
+
+ /**
+ * True if @e access_token was initialized.
+ */
+ bool have_token;
+
+ /**
+ * True if we are still suspended.
+ */
+ bool suspended;
+
+ /**
+ * False if KYC is not required.
+ */
+ bool kyc_required;
+
+ /**
+ * True if we once tried the KYC initiation.
+ */
+ bool ih_done;
+
+};
+
+
+/**
+ * Head of list of requests in long polling.
+ */
+static struct KycPoller *kyp_head;
+
+/**
+ * Tail of list of requests in long polling.
+ */
+static struct KycPoller *kyp_tail;
+
+
+void
+TEH_kyc_start_cleanup ()
+{
+ struct KycPoller *kyp;
+
+ while (NULL != (kyp = kyp_head))
+ {
+ 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;
+ MHD_resume_connection (kyp->connection);
+ }
+ }
+}
+
+
+/**
+ * Function called once a connection is done to
+ * clean up the `struct ReservePoller` state.
+ *
+ * @param rc context to clean up for
+ */
+static void
+kyp_cleanup (struct TEH_RequestContext *rc)
+{
+ struct KycPoller *kyp = rc->rh_ctx;
+
+ GNUNET_assert (! kyp->suspended);
+ if (NULL != kyp->eh)
+ {
+ GNUNET_log (GNUNET_ERROR_TYPE_START,
+ "Cancelling DB event listening\n");
+ TEH_plugin->event_listen_cancel (TEH_plugin->cls,
+ kyp->eh);
+ kyp->eh = NULL;
+ }
+ if (NULL != kyp->ih)
+ {
+ 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);
+}
+
+
+#if FIXME
+/**
+ * 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_START,
+ "KYC initiation `%s' completed with ec=%d (%s)\n",
+ provider_legitimization_id,
+ 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_process_by_row (
+ TEH_plugin->cls,
+ 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)
+ 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 ();
+}
+
+
+#endif
+
+
+/**
+ * Function implementing database transaction to start 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
+ * transaction logic MUST queue a MHD response and set @a mhd_ret. IF it
+ * returns the soft error code, the function MAY be called again to retry and
+ * MUST not queue a MHD response.
+ *
+ * @param cls closure with a `struct KycPoller *`
+ * @param connection MHD request which triggered the transaction
+ * @param[out] mhd_ret set to MHD response status for @a connection,
+ * if transaction failed (!)
+ * @return transaction status
+ */
+static enum GNUNET_DB_QueryStatus
+kyc_start (void *cls,
+ struct MHD_Connection *connection,
+ MHD_RESULT *mhd_ret)
+{
+#if FIXME
+ struct KycPoller *kyp = cls;
+ enum GNUNET_DB_QueryStatus qs;
+ struct TALER_KYCLOGIC_ProviderDetails *pd;
+ enum GNUNET_GenericReturnValue ret;
+ struct TALER_PaytoHashP h_payto;
+ char *requirements;
+ char *redirect_url;
+ bool satisfied;
+
+ qs = TEH_plugin->lookup_kyc_requirement_by_row (
+ TEH_plugin->cls,
+ kyp->requirement_row,
+ &requirements,
+ &h_payto);
+ if (GNUNET_DB_STATUS_SUCCESS_NO_RESULTS == qs)
+ {
+ GNUNET_log (GNUNET_ERROR_TYPE_START,
+ "No KYC requirements open for %llu\n",
+ (unsigned long long) kyp->requirement_row);
+ return qs;
+ }
+ if (qs < 0)
+ {
+ GNUNET_break (GNUNET_DB_STATUS_HARD_ERROR != qs);
+ return qs;
+ }
+ if (0 !=
+ GNUNET_memcmp (&kyp->h_payto,
+ &h_payto))
+ {
+ GNUNET_log (GNUNET_ERROR_TYPE_WARNING,
+ "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_START_AUTHORIZATION_FAILED,
+ "h_payto");
+ GNUNET_free (requirements);
+ return GNUNET_DB_STATUS_HARD_ERROR;
+ }
+ qs = TALER_KYCLOGIC_start_satisfied (
+ &requirements,
+ &h_payto,
+ &kyp->kyc_details,
+ TEH_plugin->select_satisfied_kyc_processes,
+ TEH_plugin->cls,
+ &satisfied);
+ 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_FETCH_FAILED,
+ "kyc_test_required");
+ GNUNET_free (requirements);
+ return GNUNET_DB_STATUS_HARD_ERROR;
+ }
+ if (satisfied)
+ {
+ GNUNET_log (GNUNET_ERROR_TYPE_START,
+ "KYC requirements `%s' already satisfied\n",
+ requirements);
+ GNUNET_free (requirements);
+ return GNUNET_DB_STATUS_SUCCESS_NO_RESULTS;
+ }
+
+ kyp->kyc_required = true;
+ ret = TALER_KYCLOGIC_requirements_to_logic (requirements,
+ &kyp->ih_logic,
+ &pd,
+ &kyp->section_name);
+ if (GNUNET_OK != ret)
+ {
+ GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
+ "KYC requirements `%s' cannot be started, 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,
+ 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_START,
+ "Initiating KYC start with logic %s\n",
+ kyp->ih_logic->name);
+ kyp->ih = kyp->ih_logic->initiate (kyp->ih_logic->cls,
+ pd,
+ &h_payto,
+ kyp->process_row,
+ &initiate_cb,
+ kyp);
+ GNUNET_break (NULL != kyp->ih);
+ return qs;
+#else
+ GNUNET_break (0);
+ return GNUNET_DB_STATUS_HARD_ERROR;
+#endif
+}
+
+
+/**
+ * Function called on events received from Postgres.
+ * Wakes up long pollers.
+ *
+ * @param cls the `struct TEH_RequestContext *`
+ * @param extra additional event data provided
+ * @param extra_size number of bytes in @a extra
+ */
+static void
+db_event_cb (void *cls,
+ const void *extra,
+ size_t extra_size)
+{
+ struct TEH_RequestContext *rc = cls;
+ struct KycPoller *kyp = rc->rh_ctx;
+ struct GNUNET_AsyncScopeSave old_scope;
+
+ (void) extra;
+ (void) extra_size;
+ if (! kyp->suspended)
+ return; /* event triggered while main transaction
+ was still running, or got multiple wake-up events */
+ GNUNET_log (GNUNET_ERROR_TYPE_START,
+ "Received KYC update event\n");
+ kyp->suspended = false;
+ GNUNET_async_scope_enter (&rc->async_scope_id,
+ &old_scope);
+ TEH_start_invariants ();
+ GNUNET_log (GNUNET_ERROR_TYPE_START,
+ "Resuming from long-polling on KYC status\n");
+ GNUNET_CONTAINER_DLL_remove (kyp_head,
+ kyp_tail,
+ kyp);
+ MHD_resume_connection (kyp->connection);
+ TALER_MHD_daemon_trigger ();
+ TEH_start_invariants ();
+ GNUNET_async_scope_restore (&old_scope);
+}
+
+
+MHD_RESULT
+TEH_handler_kyc_start (
+ struct TEH_RequestContext *rc,
+ const char *const args[1])
+{
+ struct KycPoller *kyp = rc->rh_ctx;
+ MHD_RESULT res;
+ enum GNUNET_GenericReturnValue ret;
+
+ if (NULL == kyp)
+ {
+ kyp = GNUNET_new (struct KycPoller);
+ kyp->connection = rc->connection;
+ rc->rh_ctx = kyp;
+ rc->rh_cleaner = &kyp_cleanup;
+
+ {
+ unsigned long long requirement_row;
+ char dummy;
+
+ if (1 !=
+ sscanf (args[0],
+ "%llu%c",
+ &requirement_row,
+ &dummy))
+ {
+ GNUNET_break_op (0);
+ return TALER_MHD_reply_with_error (rc->connection,
+ MHD_HTTP_BAD_REQUEST,
+ TALER_EC_GENERIC_PARAMETER_MALFORMED,
+ "requirement_row");
+ }
+ kyp->requirement_row = (uint64_t) requirement_row;
+ }
+ TALER_MHD_parse_request_timeout (rc->connection,
+ &kyp->timeout);
+ }
+
+ if ( (NULL == kyp->eh) &&
+ GNUNET_TIME_absolute_is_future (kyp->timeout) )
+ {
+ struct TALER_KycCompletedEventP rep = {
+ .header.size = htons (sizeof (rep)),
+ .header.type = htons (TALER_DBEVENT_EXCHANGE_KYC_COMPLETED),
+ .h_payto = kyp->h_payto
+ };
+
+ GNUNET_log (GNUNET_ERROR_TYPE_START,
+ "Starting DB event listening\n");
+ kyp->eh = TEH_plugin->event_listen (
+ TEH_plugin->cls,
+ GNUNET_TIME_absolute_get_remaining (kyp->timeout),
+ &rep.header,
+ &db_event_cb,
+ rc);
+ }
+
+ ret = TEH_DB_run_transaction (rc->connection,
+ "kyc start",
+ TEH_MT_REQUEST_OTHER,
+ &res,
+ &kyc_start,
+ kyp);
+ if (GNUNET_SYSERR == ret)
+ {
+ GNUNET_log (GNUNET_ERROR_TYPE_START,
+ "Transaction failed.\n");
+ return res;
+ }
+
+ if (TALER_EC_NONE != kyp->ec)
+ {
+ return TALER_MHD_reply_with_ec (rc->connection,
+ kyp->ec,
+ kyp->hint);
+ }
+
+ if (NULL != kyp->ih)
+ {
+ GNUNET_log (GNUNET_ERROR_TYPE_START,
+ "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 for positive result? */
+ if ( (kyp->kyc_required) &&
+ GNUNET_TIME_absolute_is_future (kyp->timeout))
+ {
+ GNUNET_log (GNUNET_ERROR_TYPE_START,
+ "Suspending HTTP request on timeout (%s) now...\n",
+ 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);
+ MHD_suspend_connection (kyp->connection);
+ return MHD_YES;
+ }
+
+ /* KYC plugin generated reply? */
+ if (kyp->have_token)
+ {
+ return TALER_MHD_REPLY_JSON_PACK (
+ rc->connection,
+ kyp->kyc_required
+ ? MHD_HTTP_ACCEPTED
+ : MHD_HTTP_OK,
+ GNUNET_JSON_pack_bool ("aml_review",
+ kyp->aml_review),
+ GNUNET_JSON_pack_data_auto ("access_token",
+ &kyp->access_token),
+ GNUNET_JSON_pack_allow_null (
+ GNUNET_JSON_pack_array_steal ("limits",
+ kyp->limits)));
+ }
+
+ /* KYC not required */
+ GNUNET_log (GNUNET_ERROR_TYPE_START,
+ "KYC not required %llu\n",
+ (unsigned long long) kyp->requirement_row);
+ return TALER_MHD_reply_static (
+ rc->connection,
+ MHD_HTTP_NO_CONTENT,
+ NULL,
+ NULL,
+ 0);
+}
+
+
+/* end of taler-exchange-httpd_kyc-start.c */
diff --git a/src/exchange/taler-exchange-httpd_reserves_open.c b/src/exchange/taler-exchange-httpd_reserves_open.c
index 5aadc9e40..abc26a7e1 100644
--- a/src/exchange/taler-exchange-httpd_reserves_open.c
+++ b/src/exchange/taler-exchange-httpd_reserves_open.c
@@ -35,7 +35,7 @@
* checking the request timestamp?
*/
#define TIMESTAMP_TOLERANCE \
- GNUNET_TIME_relative_multiply (GNUNET_TIME_UNIT_MINUTES, 15)
+ GNUNET_TIME_relative_multiply (GNUNET_TIME_UNIT_MINUTES, 15)
/**
diff --git a/src/exchangedb/Makefile.am b/src/exchangedb/Makefile.am
index f846370cd..270171456 100644
--- a/src/exchangedb/Makefile.am
+++ b/src/exchangedb/Makefile.am
@@ -193,6 +193,7 @@ libtaler_plugin_exchangedb_postgres_la_SOURCES = \
pg_start_deferred_wire_out.h pg_start_deferred_wire_out.c \
pg_store_wire_transfer_out.h pg_store_wire_transfer_out.c \
pg_gc.h pg_gc.c \
+ pg_lookup_kyc_status_by_token.h pg_lookup_kyc_status_by_token.c \
pg_select_coin_deposits_above_serial_id.h pg_select_coin_deposits_above_serial_id.c \
pg_select_purse_decisions_above_serial_id.h pg_select_purse_decisions_above_serial_id.c \
pg_select_purse_deposits_by_purse.h pg_select_purse_deposits_by_purse.c \
diff --git a/src/exchangedb/pg_insert_aml_decision.c b/src/exchangedb/pg_insert_aml_decision.c
index b0edceefa..d12f34af4 100644
--- a/src/exchangedb/pg_insert_aml_decision.c
+++ b/src/exchangedb/pg_insert_aml_decision.c
@@ -43,12 +43,20 @@ TEH_PG_insert_aml_decision (
struct GNUNET_TIME_Timestamp *last_date)
{
struct PostgresClosure *pg = cls;
+#if FIXME
+ /* We used to do h_payto, now we need the
+ account access token! */
struct TALER_KycCompletedEventP rep = {
.header.size = htons (sizeof (rep)),
.header.type = htons (TALER_DBEVENT_EXCHANGE_KYC_COMPLETED),
.h_payto = *h_payto
};
- char *notify_s = GNUNET_PQ_get_event_notify_channel (&rep.header);
+ char *notify_s
+ = GNUNET_PQ_get_event_notify_channel (&rep.header);
+#else
+ char *notify_s
+ = GNUNET_strdup ("FIXME");
+#endif
struct GNUNET_PQ_QueryParam params[] = {
GNUNET_PQ_query_param_auto_from_type (h_payto),
GNUNET_PQ_query_param_timestamp (&decision_time),
diff --git a/src/exchangedb/pg_insert_kyc_attributes.c b/src/exchangedb/pg_insert_kyc_attributes.c
index 45d53347a..a72e4e67b 100644
--- a/src/exchangedb/pg_insert_kyc_attributes.c
+++ b/src/exchangedb/pg_insert_kyc_attributes.c
@@ -44,6 +44,9 @@ TEH_PG_insert_kyc_attributes (
struct PostgresClosure *pg = cls;
struct GNUNET_TIME_Timestamp expiration
= GNUNET_TIME_absolute_to_timestamp (expiration_time);
+#if FIXME
+ /* We used to do h_payto, now we need the
+ account access token! */
struct TALER_KycCompletedEventP rep = {
.header.size = htons (sizeof (rep)),
.header.type = htons (TALER_DBEVENT_EXCHANGE_KYC_COMPLETED),
@@ -51,6 +54,10 @@ TEH_PG_insert_kyc_attributes (
};
char *kyc_completed_notify_s
= GNUNET_PQ_get_event_notify_channel (&rep.header);
+#else
+ char *kyc_completed_notify_s
+ = GNUNET_strdup ("FIXME");
+#endif
struct GNUNET_PQ_QueryParam params[] = {
GNUNET_PQ_query_param_uint64 (&process_row),
GNUNET_PQ_query_param_auto_from_type (h_payto),
diff --git a/src/exchangedb/pg_insert_kyc_failure.c b/src/exchangedb/pg_insert_kyc_failure.c
index c5d1a7085..903c717ad 100644
--- a/src/exchangedb/pg_insert_kyc_failure.c
+++ b/src/exchangedb/pg_insert_kyc_failure.c
@@ -67,6 +67,9 @@ TEH_PG_insert_kyc_failure (
if (qs > 0)
{
/* FIXME: might want to do this eventually in the same transaction... */
+#if FIXME
+ /* We used to do h_payto, now we need the
+ account access token! */
struct TALER_KycCompletedEventP rep = {
.header.size = htons (sizeof (rep)),
.header.type = htons (TALER_DBEVENT_EXCHANGE_KYC_COMPLETED),
@@ -77,6 +80,7 @@ TEH_PG_insert_kyc_failure (
&rep.header,
NULL,
0);
+#endif
}
return qs;
}
diff --git a/src/exchangedb/pg_lookup_kyc_status_by_token.c b/src/exchangedb/pg_lookup_kyc_status_by_token.c
new file mode 100644
index 000000000..6d3cc4bec
--- /dev/null
+++ b/src/exchangedb/pg_lookup_kyc_status_by_token.c
@@ -0,0 +1,65 @@
+/*
+ This file is part of TALER
+ Copyright (C) 2024 Taler Systems SA
+
+ TALER is free software; you can redistribute it and/or modify it under the
+ terms of the GNU 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 General Public License for more details.
+
+ You should have received a copy of the GNU General Public License along with
+ TALER; see the file COPYING. If not, see <http://www.gnu.org/licenses/>
+ */
+/**
+ * @file exchangedb/pg_lookup_kyc_status_by_token.c
+ * @brief Implementation of the lookup_kyc_status_by_token function for Postgres
+ * @author Christian Grothoff
+ */
+#include "platform.h"
+#include "taler_error_codes.h"
+#include "taler_dbevents.h"
+#include "taler_pq_lib.h"
+#include "pg_lookup_kyc_status_by_token.h"
+#include "pg_helper.h"
+
+
+enum GNUNET_DB_QueryStatus
+TEH_PG_lookup_kyc_status_by_token (
+ void *cls,
+ const struct TALER_AccountAccessTokenP *access_token,
+ uint64_t *row,
+ json_t **jmeasures)
+{
+ struct PostgresClosure *pg = cls;
+ struct GNUNET_PQ_QueryParam params[] = {
+ GNUNET_PQ_query_param_auto_from_type (access_token),
+ GNUNET_PQ_query_param_end
+ };
+ struct GNUNET_PQ_ResultSpec rs[] = {
+ GNUNET_PQ_result_spec_uint64 (
+ "legitimization_measure_serial_id",
+ row),
+ TALER_PQ_result_spec_json (
+ "jmeasures",
+ jmeasures),
+ GNUNET_PQ_result_spec_end
+ };
+
+ PREPARE (pg,
+ "lookup_kyc_status_by_token",
+ "SELECT"
+ " legitimization_measure_serial_id"
+ ",jmeasures"
+ " FROM legitimization_measures"
+ " WHERE access_token=$1"
+ " AND NOT is_finished"
+ " ORDER BY display_priority DESC"
+ " LIMIT 1;");
+ return GNUNET_PQ_eval_prepared_singleton_select (pg->conn,
+ "lookup_kyc_status_by_token",
+ params,
+ rs);
+}
diff --git a/src/exchangedb/pg_lookup_kyc_status_by_token.h b/src/exchangedb/pg_lookup_kyc_status_by_token.h
new file mode 100644
index 000000000..77c845228
--- /dev/null
+++ b/src/exchangedb/pg_lookup_kyc_status_by_token.h
@@ -0,0 +1,46 @@
+/*
+ This file is part of TALER
+ Copyright (C) 2024 Taler Systems SA
+
+ TALER is free software; you can redistribute it and/or modify it under the
+ terms of the GNU 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 General Public License for more details.
+
+ You should have received a copy of the GNU General Public License along with
+ TALER; see the file COPYING. If not, see <http://www.gnu.org/licenses/>
+ */
+/**
+ * @file exchangedb/pg_lookup_kyc_status_by_token.h
+ * @brief implementation of the lookup_kyc_status_by_token function for Postgres
+ * @author Christian Grothoff
+ */
+#ifndef PG_LOOKUP_KYC_STATUS_BY_TOKEN_H
+#define PG_LOOKUP_KYC_STATUS_BY_TOKEN_H
+
+#include "taler_util.h"
+#include "taler_json_lib.h"
+#include "taler_exchangedb_plugin.h"
+
+
+/**
+ * Lookup KYC status by account access token.
+ *
+ * @param cls closure
+ * @param access_token key to look under
+ * @param[out] row set to requirement row that matches
+ * @param[out] jmeasures set to the LegitimizationMeasures for the @a access_token; must be freed by caller!
+ * @return database transaction status
+ */
+enum GNUNET_DB_QueryStatus
+TEH_PG_lookup_kyc_status_by_token (
+ void *cls,
+ const struct TALER_AccountAccessTokenP *access_token,
+ uint64_t *row,
+ json_t **jmeasures);
+
+
+#endif
diff --git a/src/exchangedb/pg_lookup_wire_fee_by_time.c b/src/exchangedb/pg_lookup_wire_fee_by_time.c
index 775232a48..1f136b135 100644
--- a/src/exchangedb/pg_lookup_wire_fee_by_time.c
+++ b/src/exchangedb/pg_lookup_wire_fee_by_time.c
@@ -138,7 +138,7 @@ TEH_PG_lookup_wire_fee_by_time (
.fees = fees,
.pg = pg
};
- /* used in #postgres_lookup_wire_fee_by_time() */
+
PREPARE (pg,
"lookup_wire_fee_by_time",
"SELECT"
diff --git a/src/exchangedb/pg_update_kyc_process_by_row.c b/src/exchangedb/pg_update_kyc_process_by_row.c
index 448ac2154..2f6724810 100644
--- a/src/exchangedb/pg_update_kyc_process_by_row.c
+++ b/src/exchangedb/pg_update_kyc_process_by_row.c
@@ -86,11 +86,13 @@ TEH_PG_update_kyc_process_by_row (
if (GNUNET_TIME_absolute_is_future (expiration))
{
enum GNUNET_DB_QueryStatus qs2;
+#if FIXME
struct TALER_KycCompletedEventP rep = {
.header.size = htons (sizeof (rep)),
.header.type = htons (TALER_DBEVENT_EXCHANGE_KYC_COMPLETED),
.h_payto = *h_payto
};
+#endif
uint32_t trigger_type = 1;
struct GNUNET_PQ_QueryParam params2[] = {
GNUNET_PQ_query_param_auto_from_type (h_payto),
@@ -98,10 +100,14 @@ TEH_PG_update_kyc_process_by_row (
GNUNET_PQ_query_param_end
};
+#if FIXME
+ /* We used to do h_payto, now we need the
+ account access token! */
GNUNET_PQ_event_notify (pg->conn,
&rep.header,
NULL,
0);
+#endif
PREPARE (pg,
"alert_kyc_status_change",
"INSERT INTO kyc_alerts"
diff --git a/src/exchangedb/plugin_exchangedb_postgres.c b/src/exchangedb/plugin_exchangedb_postgres.c
index d26db2029..897d09bef 100644
--- a/src/exchangedb/plugin_exchangedb_postgres.c
+++ b/src/exchangedb/plugin_exchangedb_postgres.c
@@ -50,6 +50,7 @@
#include "pg_iterate_kyc_reference.h"
#include "pg_iterate_reserve_close_info.h"
#include "pg_lookup_records_by_table.h"
+#include "pg_lookup_kyc_status_by_token.h"
#include "pg_lookup_serial_by_table.h"
#include "pg_select_account_merges_above_serial_id.h"
#include "pg_select_all_purse_decisions_above_serial_id.h"
@@ -782,7 +783,8 @@ libtaler_plugin_exchangedb_postgres_init (void *cls)
= &TEH_PG_lookup_kyc_requirement_by_row;
plugin->trigger_kyc_rule_for_account
= &TEH_PG_trigger_kyc_rule_for_account;
-
+ plugin->lookup_kyc_status_by_token
+ = &TEH_PG_lookup_kyc_status_by_token;
plugin->batch_ensure_coin_known
= &TEH_PG_batch_ensure_coin_known;
plugin->inject_auditor_triggers
diff --git a/src/include/taler_exchangedb_plugin.h b/src/include/taler_exchangedb_plugin.h
index 067f925b1..541dc23ed 100644
--- a/src/include/taler_exchangedb_plugin.h
+++ b/src/include/taler_exchangedb_plugin.h
@@ -238,9 +238,9 @@ struct TALER_KycCompletedEventP
struct GNUNET_DB_EventHeaderP header;
/**
- * Public key of the reserve the event is about.
+ * Access token the KYC was completed for.
*/
- struct TALER_PaytoHashP h_payto;
+ struct TALER_AccountAccessTokenP access_token;
};
@@ -6828,6 +6828,23 @@ struct TALER_EXCHANGEDB_Plugin
/**
+ * Lookup KYC status by account access token.
+ *
+ * @param cls closure
+ * @param access_token key to look under
+ * @param[out] row set to requirement row that matches
+ * @param[out] jmeasures set to the LegitimizationMeasures for the @a access_token; must be freed by caller!
+ * @return database transaction status
+ */
+ enum GNUNET_DB_QueryStatus
+ (*lookup_kyc_status_by_token)(
+ void *cls,
+ const struct TALER_AccountAccessTokenP *access_token,
+ uint64_t *row,
+ json_t **jmeasures);
+
+
+ /**
* Lookup KYC process meta data.
*
* @param cls closure
diff --git a/src/include/taler_kyclogic_lib.h b/src/include/taler_kyclogic_lib.h
index 86812f001..b9c677235 100644
--- a/src/include/taler_kyclogic_lib.h
+++ b/src/include/taler_kyclogic_lib.h
@@ -73,22 +73,22 @@ enum TALER_KYCLOGIC_KycTriggerEvent
/**
* Types of KYC checks.
*/
-enum CheckType
+enum TALER_KYCLOGIC_CheckType
{
/**
* Wait for staff or contact staff out-of-band.
*/
- CT_INFO,
+ TALER_KYCLOGIC_CT_INFO,
/**
* SPA should show an inline form.
*/
- CT_FORM,
+ TALER_KYCLOGIC_CT_FORM,
/**
* SPA may start external KYC process.
*/
- CT_LINK
+ TALER_KYCLOGIC_CT_LINK
};
@@ -156,7 +156,7 @@ struct TALER_KYCLOGIC_KycCheck
/**
* Type of the KYC check.
*/
- enum CheckType type;
+ enum TALER_KYCLOGIC_CheckType type;
/**
* Details depending on @e type.
@@ -165,7 +165,7 @@ struct TALER_KYCLOGIC_KycCheck
{
/**
- * Fields present only if @e type is #CT_FORM.
+ * Fields present only if @e type is #TALER_KYCLOGIC_CT_FORM.
*/
struct
{
@@ -178,7 +178,7 @@ struct TALER_KYCLOGIC_KycCheck
} form;
/**
- * Fields present only if @e type is CT_LINK.
+ * Fields present only if @e type is TALER_KYCLOGIC_CT_LINK.
*/
struct
{
@@ -411,14 +411,32 @@ TALER_KYCLOGIC_rule_to_measures (const struct TALER_KYCLOGIC_KycRule *r);
* NULL to use default rules
* @return set to JSON array with public limits
* of type ``AccountLimit``
- *
- * FIXME: not implemented!
*/
json_t *
TALER_KYCLOGIC_rules_to_limits (const json_t *jrules);
/**
+ * Convert MeasureInformation into the
+ * KycRequirementInformation used by the client.
+ *
+ * @param check_name the prescribed check
+ * @param prog_name the program to run
+ * @param access_token access token for the measure
+ * @param offset offset of the measure
+ * @param row_id row in the legitimization_measures table
+ * @return JSON object with matching KycRequirementInformation
+ */
+json_t *
+TALER_KYCLOGIC_measure_to_requirement (
+ const char *check_name,
+ const char *prog_name,
+ const struct TALER_AccountAccessTokenP *access_token,
+ size_t offset,
+ uint64_t row_id);
+
+
+/**
* Extract logic data from a KYC @a provider.
*
* @param provider provider to get logic data from
diff --git a/src/kyclogic/kyclogic_api.c b/src/kyclogic/kyclogic_api.c
index 96d3afad5..c4faead6c 100644
--- a/src/kyclogic/kyclogic_api.c
+++ b/src/kyclogic/kyclogic_api.c
@@ -301,6 +301,31 @@ static struct TALER_KYCLOGIC_AmlProgram **aml_programs;
static unsigned int num_aml_programs;
+/**
+ * Lookup a KYC check by @a check_name
+ *
+ * @param check_name name to search for
+ * @return NULL if not found
+ */
+static struct TALER_KYCLOGIC_KycCheck *
+find_check (const char *check_name)
+{
+ for (unsigned int i = 0; i<num_kyc_checks; i++)
+ {
+ struct TALER_KYCLOGIC_KycCheck *kyc_check
+ = kyc_checks[i];
+
+ if (0 == strcmp (check_name,
+ kyc_check->check_name))
+ return kyc_check;
+ }
+ GNUNET_log (GNUNET_ERROR_TYPE_WARNING,
+ "Check `%s' unknown\n",
+ check_name);
+ return NULL;
+}
+
+
struct TALER_KYCLOGIC_LegitimizationRuleSet *
TALER_KYCLOGIC_rules_parse (const json_t *jlrs)
{
@@ -823,16 +848,16 @@ command_output (const char *command,
static enum GNUNET_GenericReturnValue
check_type_from_string (
const char *ctype_s,
- enum CheckType *ctype)
+ enum TALER_KYCLOGIC_CheckType *ctype)
{
struct
{
const char *in;
- enum CheckType out;
+ enum TALER_KYCLOGIC_CheckType out;
} map [] = {
- { "INFO", CT_INFO },
- { "LINK", CT_LINK },
- { "FORM", CT_FORM },
+ { "INFO", TALER_KYCLOGIC_CT_INFO },
+ { "LINK", TALER_KYCLOGIC_CT_LINK },
+ { "FORM", TALER_KYCLOGIC_CT_FORM },
{ NULL, 0 }
};
@@ -1160,7 +1185,7 @@ add_check (const struct GNUNET_CONFIGURATION_Handle *cfg,
const char *section)
{
bool voluntary;
- enum CheckType ct;
+ enum TALER_KYCLOGIC_CheckType ct;
char *form_name = NULL;
char *description = NULL;
json_t *description_i18n = NULL;
@@ -1781,12 +1806,12 @@ TALER_KYCLOGIC_kyc_done (void)
0);
switch (kc->type)
{
- case CT_INFO:
+ case TALER_KYCLOGIC_CT_INFO:
break;
- case CT_FORM:
+ case TALER_KYCLOGIC_CT_FORM:
GNUNET_free (kc->details.form.name);
break;
- case CT_LINK:
+ case TALER_KYCLOGIC_CT_LINK:
break;
}
GNUNET_free (kc);
@@ -2082,4 +2107,99 @@ TALER_KYCLOGIC_kyc_test_required (
}
+json_t *
+TALER_KYCLOGIC_measure_to_requirement (
+ const char *check_name,
+ const char *prog_name,
+ const struct TALER_AccountAccessTokenP *access_token,
+ size_t offset,
+ uint64_t row_id)
+{
+ struct TALER_KYCLOGIC_KycCheck *kc;
+ json_t *kri;
+ struct GNUNET_ShortHashCode shv;
+ uint64_t be = GNUNET_htonll (row_id);
+ uint32_t be32 = htonl ((uint32_t) offset);
+ char *ids;
+ char *xids;
+
+ GNUNET_assert (offset <= UINT_MAX);
+ GNUNET_assert (offset <= UINT32_MAX);
+ kc = find_check (check_name);
+ if (NULL == kc)
+ {
+ GNUNET_break (0);
+ return NULL;
+ }
+ /* FIXME: should be moved to someplace
+ in util/crypto as the $ID-handlers
+ need exactly the same computation! */
+ GNUNET_assert (
+ GNUNET_YES ==
+ GNUNET_CRYPTO_kdf (&shv,
+ sizeof (shv),
+ &be,
+ sizeof (be),
+ access_token,
+ sizeof (*access_token),
+ &be32,
+ sizeof (be32),
+ NULL,
+ 0));
+ switch (kc->type)
+ {
+ case TALER_KYCLOGIC_CT_INFO:
+ return GNUNET_JSON_PACK (
+ GNUNET_JSON_pack_string ("form",
+ "INFO"),
+ GNUNET_JSON_pack_string ("description",
+ kc->description),
+ GNUNET_JSON_pack_object_incref ("description_i18n",
+ (json_t *) kc->description_i18n));
+ case TALER_KYCLOGIC_CT_FORM:
+ ids = GNUNET_STRINGS_data_to_string_alloc (&shv,
+ sizeof (shv));
+ GNUNET_asprintf (&xids,
+ "%llu/%u/%s",
+ (unsigned long long) row_id,
+ (unsigned int) offset,
+ ids);
+ GNUNET_free (ids);
+ kri = GNUNET_JSON_PACK (
+ GNUNET_JSON_pack_string ("form",
+ kc->details.form.name),
+ GNUNET_JSON_pack_string ("id",
+ xids),
+ GNUNET_JSON_pack_string ("description",
+ kc->description),
+ GNUNET_JSON_pack_object_steal ("description_i18n",
+ (json_t *) kc->description_i18n));
+ GNUNET_free (xids);
+ return kri;
+ case TALER_KYCLOGIC_CT_LINK:
+ ids = GNUNET_STRINGS_data_to_string_alloc (&shv,
+ sizeof (shv));
+ GNUNET_asprintf (&xids,
+ "%llu/%u/%s",
+ (unsigned long long) row_id,
+ (unsigned int) offset,
+ ids);
+ GNUNET_free (ids);
+ kri = GNUNET_JSON_PACK (
+ GNUNET_JSON_pack_string ("form",
+ "LINK"),
+ GNUNET_JSON_pack_string ("id",
+ xids),
+ GNUNET_JSON_pack_string ("description",
+ kc->description),
+ GNUNET_JSON_pack_object_steal ("description_i18n",
+ (json_t *) kc->description_i18n));
+ GNUNET_free (xids);
+ return kri;
+ }
+ GNUNET_break (0); /* invalid type */
+ return NULL;
+}
+
+
/* end of kyclogic_api.c */
diff --git a/src/kyclogic/taler-exchange-kyc-tester.c b/src/kyclogic/taler-exchange-kyc-tester.c
index ae357c953..51c19efb1 100644
--- a/src/kyclogic/taler-exchange-kyc-tester.c
+++ b/src/kyclogic/taler-exchange-kyc-tester.c
@@ -1638,18 +1638,18 @@ run (void *cls,
}
switch (kcc.check->type)
{
- case CT_INFO:
+ case TALER_KYCLOGIC_CT_INFO:
GNUNET_log (GNUNET_ERROR_TYPE_MESSAGE,
"KYC information is `%s'\n",
kcc.check->description);
break;
- case CT_FORM:
+ case TALER_KYCLOGIC_CT_FORM:
GNUNET_log (GNUNET_ERROR_TYPE_MESSAGE,
"Would initiate KYC check `%s' with form `%s'\n",
kcc.check->check_name,
kcc.check->details.form.name);
break;
- case CT_LINK:
+ case TALER_KYCLOGIC_CT_LINK:
{
struct TALER_KYCLOGIC_ProviderDetails *pd;
const char *provider_name;