summaryrefslogtreecommitdiff
path: root/src
diff options
context:
space:
mode:
authorChristian Grothoff <christian@grothoff.org>2022-08-07 15:35:06 +0200
committerChristian Grothoff <christian@grothoff.org>2022-08-07 15:35:13 +0200
commit3f99e4f3f8a89dfafcf37fcbf9046134adca436a (patch)
treea8535b839f66351884bb5b02e2ac3fc8b1a4d317 /src
parent30b833232ea1c4c0ca58635d046af2970b439a3e (diff)
downloadexchange-3f99e4f3f8a89dfafcf37fcbf9046134adca436a.tar.gz
exchange-3f99e4f3f8a89dfafcf37fcbf9046134adca436a.tar.bz2
exchange-3f99e4f3f8a89dfafcf37fcbf9046134adca436a.zip
-implement new kyc-webhook endpoint
Diffstat (limited to 'src')
-rw-r--r--src/exchange/Makefile.am1
-rw-r--r--src/exchange/taler-exchange-httpd_kyc-webhook.c347
-rw-r--r--src/exchange/taler-exchange-httpd_kyc-webhook.h64
-rw-r--r--src/exchangedb/plugin_exchangedb_postgres.c2
-rw-r--r--src/include/taler_exchangedb_plugin.h2
-rw-r--r--src/include/taler_kyclogic_plugin.h21
-rw-r--r--src/kyclogic/plugin_kyclogic_oauth2.c12
7 files changed, 429 insertions, 20 deletions
diff --git a/src/exchange/Makefile.am b/src/exchange/Makefile.am
index 23295c13d..7141758b1 100644
--- a/src/exchange/Makefile.am
+++ b/src/exchange/Makefile.am
@@ -136,6 +136,7 @@ taler_exchange_httpd_SOURCES = \
taler-exchange-httpd_kyc-check.c taler-exchange-httpd_kyc-check.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 \
taler-exchange-httpd_link.c taler-exchange-httpd_link.h \
taler-exchange-httpd_management.h \
taler-exchange-httpd_management_auditors.c \
diff --git a/src/exchange/taler-exchange-httpd_kyc-webhook.c b/src/exchange/taler-exchange-httpd_kyc-webhook.c
new file mode 100644
index 000000000..a5c11cec9
--- /dev/null
+++ b/src/exchange/taler-exchange-httpd_kyc-webhook.c
@@ -0,0 +1,347 @@
+/*
+ This file is part of TALER
+ Copyright (C) 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
+ 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-webhook.c
+ * @brief Handle notification of KYC completion via webhook.
+ * @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_mhd_lib.h"
+#include "taler-exchange-httpd_kyc.h"
+#include "taler-exchange-httpd_kyc-webhook.h"
+#include "taler-exchange-httpd_responses.h"
+
+
+/**
+ * Context for the webhook.
+ */
+struct KycWebhookContext
+{
+
+ /**
+ * Kept in a DLL while suspended.
+ */
+ struct KycWebhookContext *next;
+
+ /**
+ * Kept in a DLL while suspended.
+ */
+ struct KycWebhookContext *prev;
+
+ /**
+ * Details about the connection we are processing.
+ */
+ struct TEH_RequestContext *rc;
+
+ /**
+ * Plugin responsible for the webhook.
+ */
+ struct TALER_KYCLOGIC_Plugin *plugin;
+
+ /**
+ * Configuration for the specific action.
+ */
+ struct TALER_KYCLOGIC_ProviderDetails *pd;
+
+ /**
+ * Webhook activity.
+ */
+ struct TALER_KYCLOGIC_WebhookHandle *wh;
+
+ /**
+ * HTTP response to return.
+ */
+ struct MHD_Response *response;
+
+ /**
+ * Logic the request is for. Name of the configuration
+ * section defining the KYC logic.
+ */
+ char *logic;
+
+ /**
+ * HTTP response code to return.
+ */
+ unsigned int response_code;
+
+ /**
+ * #GNUNET_YES if we are suspended,
+ * #GNUNET_NO if not.
+ * #GNUNET_SYSERR if we had some error.
+ */
+ enum GNUNET_GenericReturnValue suspended;
+
+};
+
+
+/**
+ * Contexts are kept in a DLL while suspended.
+ */
+static struct KycWebhookContext *kwh_head;
+
+/**
+ * Contexts are kept in a DLL while suspended.
+ */
+static struct KycWebhookContext *kwh_tail;
+
+
+/**
+ * Resume processing the @a kwh request.
+ *
+ * @param kwh request to resume
+ */
+static void
+kwh_resume (struct KycWebhookContext *kwh)
+{
+ GNUNET_assert (GNUNET_YES == kwh->suspended);
+ kwh->suspended = GNUNET_NO;
+ GNUNET_CONTAINER_DLL_remove (kwh_head,
+ kwh_tail,
+ kwh);
+ MHD_resume_connection (kwh->rc->connection);
+ TALER_MHD_daemon_trigger ();
+}
+
+
+void
+TEH_kyc_webhook_cleanup (void)
+{
+ struct KycWebhookContext *kwh;
+
+ while (NULL != (kwh = kwh_head))
+ {
+ if (NULL != kwh->wh)
+ {
+ kwh->plugin->webhook_cancel (kwh->wh);
+ kwh->wh = NULL;
+ }
+ kwh_resume (kwh);
+ }
+}
+
+
+/**
+ * Function called with the result of a webhook
+ * operation.
+ *
+ * Note that the "decref" for the @a response
+ * will be done by the plugin.
+ *
+ * @param cls closure
+ * @param legi_row legitimization request the webhook was about
+ * @param account_id account the webhook was about
+ * @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 status KYC status
+ * @param expiration until when is the KYC check valid
+ * @param http_status HTTP status code of @a response
+ * @param[in] response to return to the HTTP client
+ */
+static void
+webhook_finished_cb (
+ void *cls,
+ uint64_t legi_row,
+ const struct TALER_PaytoHashP *account_id,
+ const char *provider_user_id,
+ const char *provider_legitimization_id,
+ enum TALER_KYCLOGIC_KycStatus status,
+ struct GNUNET_TIME_Absolute expiration,
+ unsigned int http_status,
+ struct MHD_Response *response)
+{
+ struct KycWebhookContext *kwh = cls;
+
+ kwh->wh = NULL;
+ switch (status)
+ {
+ case TALER_KYCLOGIC_STATUS_SUCCESS:
+ /* _successfully_ resumed case */
+ {
+ enum GNUNET_DB_QueryStatus qs;
+
+ qs = TEH_plugin->update_kyc_requirement_by_row (TEH_plugin->cls,
+ legi_row,
+ kwh->logic,
+ account_id,
+ provider_user_id,
+ provider_legitimization_id,
+ expiration);
+ if (qs < 0)
+ {
+ GNUNET_break (0);
+ kwh->response = TALER_MHD_make_error (TALER_EC_GENERIC_DB_STORE_FAILED,
+ "set_kyc_ok");
+ kwh->response_code = MHD_HTTP_INTERNAL_SERVER_ERROR;
+ kwh_resume (kwh);
+ return;
+ }
+ }
+ break;
+ default:
+ GNUNET_log (GNUNET_ERROR_TYPE_INFO,
+ "KYC status of %s/%s (Row #%llu) is %d\n",
+ provider_user_id,
+ provider_legitimization_id,
+ (unsigned long long) legi_row,
+ status);
+ break;
+ }
+ kwh->response = response;
+ kwh->response_code = http_status;
+ kwh_resume (kwh);
+}
+
+
+/**
+ * Function called to clean up a context.
+ *
+ * @param rc request context
+ */
+static void
+clean_kwh (struct TEH_RequestContext *rc)
+{
+ struct KycWebhookContext *kwh = rc->rh_ctx;
+
+ if (NULL != kwh->wh)
+ {
+ kwh->plugin->webhook_cancel (kwh->wh);
+ kwh->wh = NULL;
+ }
+ if (NULL != kwh->response)
+ {
+ MHD_destroy_response (kwh->response);
+ kwh->response = NULL;
+ }
+ GNUNET_free (kwh->logic);
+ GNUNET_free (kwh);
+}
+
+
+/**
+ * Handle a (GET or POST) "/kyc-webhook" request.
+ *
+ * @param rc request to handle
+ * @param method HTTP request method used by the client
+ * @param root uploaded JSON body (can be NULL)
+ * @param args one argument with the payment_target_uuid
+ * @return MHD result code
+ */
+static MHD_RESULT
+handler_kyc_webhook_generic (
+ struct TEH_RequestContext *rc,
+ const char *method,
+ const json_t *root,
+ const char *const args[])
+{
+ struct KycWebhookContext *kwh = rc->rh_ctx;
+
+ if (NULL == kwh)
+ { /* first time */
+ kwh = GNUNET_new (struct KycWebhookContext);
+ kwh->logic = GNUNET_strdup (args[0]);
+ kwh->rc = rc;
+ rc->rh_ctx = kwh;
+ rc->rh_cleaner = &clean_kwh;
+
+ if (GNUNET_OK !=
+ TEH_kyc_get_logic (kwh->logic,
+ &kwh->plugin,
+ &kwh->pd))
+ {
+ GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
+ "KYC logic `%s' unknown (check KYC provider configuration)\n",
+ kwh->logic);
+ return TALER_MHD_reply_with_error (rc->connection,
+ MHD_HTTP_NOT_FOUND,
+ TALER_EC_EXCHANGE_KYC_WEBHOOK_LOGIC_UNKNOWN,
+ "$LOGIC");
+ }
+ kwh->wh = kwh->plugin->webhook (kwh->plugin->cls,
+ kwh->pd,
+ TEH_plugin->kyc_provider_account_lookup,
+ TEH_plugin->cls,
+ method,
+ &args[1],
+ rc->connection,
+ root,
+ &webhook_finished_cb,
+ kwh);
+ if (NULL == kwh->wh)
+ {
+ GNUNET_break_op (0);
+ return TALER_MHD_reply_with_error (rc->connection,
+ MHD_HTTP_INTERNAL_SERVER_ERROR,
+ TALER_EC_GENERIC_INTERNAL_INVARIANT_FAILURE,
+ "failed to run webhook logic");
+ }
+ kwh->suspended = GNUNET_YES;
+ GNUNET_CONTAINER_DLL_insert (kwh_head,
+ kwh_tail,
+ kwh);
+ MHD_suspend_connection (rc->connection);
+ return MHD_YES;
+ }
+
+ if (NULL != kwh->response)
+ {
+ /* handle _failed_ resumed cases */
+ return MHD_queue_response (rc->connection,
+ kwh->response_code,
+ kwh->response);
+ }
+
+ /* We resumed, but got no response? This should
+ not happen. */
+ GNUNET_break (0);
+ return TALER_MHD_reply_with_error (rc->connection,
+ MHD_HTTP_INTERNAL_SERVER_ERROR,
+ TALER_EC_GENERIC_INTERNAL_INVARIANT_FAILURE,
+ "resumed without response");
+}
+
+
+MHD_RESULT
+TEH_handler_kyc_webhook_get (
+ struct TEH_RequestContext *rc,
+ const char *const args[])
+{
+ return handler_kyc_webhook_generic (rc,
+ MHD_HTTP_METHOD_GET,
+ NULL,
+ args);
+}
+
+
+MHD_RESULT
+TEH_handler_kyc_webhook_post (
+ struct TEH_RequestContext *rc,
+ const json_t *root,
+ const char *const args[])
+{
+ return handler_kyc_webhook_generic (rc,
+ MHD_HTTP_METHOD_POST,
+ root,
+ args);
+}
+
+
+/* end of taler-exchange-httpd_kyc-webhook.c */
diff --git a/src/exchange/taler-exchange-httpd_kyc-webhook.h b/src/exchange/taler-exchange-httpd_kyc-webhook.h
new file mode 100644
index 000000000..1f472a87e
--- /dev/null
+++ b/src/exchange/taler-exchange-httpd_kyc-webhook.h
@@ -0,0 +1,64 @@
+/*
+ This file is part of TALER
+ Copyright (C) 2021 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-webhook.h
+ * @brief Handle /kyc-webhook requests
+ * @author Christian Grothoff
+ */
+#ifndef TALER_EXCHANGE_HTTPD_KYC_WEBHOOK_H
+#define TALER_EXCHANGE_HTTPD_KYC_WEBHOOK_H
+
+#include <microhttpd.h>
+#include "taler-exchange-httpd.h"
+
+
+/**
+ * Shutdown kyc-webhook subsystem. Resumes all suspended long-polling clients
+ * and cleans up data structures.
+ */
+void
+TEH_kyc_webhook_cleanup (void);
+
+
+/**
+ * Handle a GET "/kyc-webhook" request.
+ *
+ * @param rc request to handle
+ * @param args one argument with the payment_target_uuid
+ * @return MHD result code
+ */
+MHD_RESULT
+TEH_handler_kyc_webhook_get (
+ struct TEH_RequestContext *rc,
+ const char *const args[]);
+
+
+/**
+ * Handle a POST "/kyc-webhook" request.
+ *
+ * @param rc request to handle
+ * @param root uploaded JSON body
+ * @param args one argument with the payment_target_uuid
+ * @return MHD result code
+ */
+MHD_RESULT
+TEH_handler_kyc_webhook_post (
+ struct TEH_RequestContext *rc,
+ const json_t *root,
+ const char *const args[]);
+
+
+#endif
diff --git a/src/exchangedb/plugin_exchangedb_postgres.c b/src/exchangedb/plugin_exchangedb_postgres.c
index 7c066784a..e501dc2d6 100644
--- a/src/exchangedb/plugin_exchangedb_postgres.c
+++ b/src/exchangedb/plugin_exchangedb_postgres.c
@@ -16602,7 +16602,7 @@ postgres_update_kyc_requirement_by_row (
void *cls,
uint64_t legi_row,
const char *provider_section,
- struct TALER_PaytoHashP *h_payto,
+ const struct TALER_PaytoHashP *h_payto,
const char *provider_account_id,
const char *provider_legitimization_id,
struct GNUNET_TIME_Absolute expiration)
diff --git a/src/include/taler_exchangedb_plugin.h b/src/include/taler_exchangedb_plugin.h
index 5411fbe17..42b8a7427 100644
--- a/src/include/taler_exchangedb_plugin.h
+++ b/src/include/taler_exchangedb_plugin.h
@@ -5677,7 +5677,7 @@ struct TALER_EXCHANGEDB_Plugin
void *cls,
uint64_t legi_row,
const char *provider_section,
- struct TALER_PaytoHashP *h_payto,
+ const struct TALER_PaytoHashP *h_payto,
const char *provider_account_id,
const char *provider_legitimization_id,
struct GNUNET_TIME_Absolute expiration);
diff --git a/src/include/taler_kyclogic_plugin.h b/src/include/taler_kyclogic_plugin.h
index f94849620..8e52e0510 100644
--- a/src/include/taler_kyclogic_plugin.h
+++ b/src/include/taler_kyclogic_plugin.h
@@ -147,11 +147,10 @@ typedef void
/**
- * Function called with the result of a proof check
- * operation.
+ * Function called with the result of a proof check operation.
*
* Note that the "decref" for the @a response
- * will be done by the plugin.
+ * will be done by the callee and MUST NOT be done by the plugin.
*
* @param cls closure
* @param status KYC status
@@ -173,13 +172,13 @@ typedef void
/**
- * Function called with the result of a webhook
- * operation.
+ * Function called with the result of a webhook operation.
*
- * Note that the "decref" for the @a response
- * will be done by the plugin.
+ * Note that the "decref" for the @a response will be done by the callee and
+ * MUST NOT be done by the plugin!
*
* @param cls closure
+ * @param legi_row legitimization request the webhook was about
* @param account_id account the webhook was about
* @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
@@ -191,6 +190,7 @@ typedef void
typedef void
(*TALER_KYCLOGIC_WebhookCallback)(
void *cls,
+ uint64_t legi_row,
const struct TALER_PaytoHashP *account_id,
const char *provider_user_id,
const char *provider_legitimization_id,
@@ -330,7 +330,7 @@ struct TALER_KYCLOGIC_Plugin
* @param plc callback to lookup accounts with
* @param plc_cls closure for @a plc
* @param http_method HTTP method used for the webhook
- * @param url_path rest of the URL after `/kyc-webhook/`
+ * @param url_path rest of the URL after `/kyc-webhook/$LOGIC/`
* @param connection MHD connection object (for HTTP headers)
* @param body_size number of bytes in @a body
* @param body HTTP request body
@@ -344,10 +344,9 @@ struct TALER_KYCLOGIC_Plugin
TALER_KYCLOGIC_ProviderLookupCallback plc,
void *plc_cls,
const char *http_method,
- const char *url_path,
+ const char *const url_path[],
struct MHD_Connection *connection,
- size_t body_size,
- const void *body,
+ const json_t *upload,
TALER_KYCLOGIC_WebhookCallback cb,
void *cb_cls);
diff --git a/src/kyclogic/plugin_kyclogic_oauth2.c b/src/kyclogic/plugin_kyclogic_oauth2.c
index 3bcd745c0..ac13355e4 100644
--- a/src/kyclogic/plugin_kyclogic_oauth2.c
+++ b/src/kyclogic/plugin_kyclogic_oauth2.c
@@ -556,7 +556,6 @@ return_proof_response (void *cls)
GNUNET_TIME_relative_to_absolute (ph->pd->expiration),
ph->http_status,
ph->response);
- MHD_destroy_response (ph->response);
GNUNET_free (ph->provider_user_id);
GNUNET_free (ph);
}
@@ -943,6 +942,7 @@ wh_return_not_found (void *cls)
"",
MHD_RESPMEM_PERSISTENT);
wh->cb (wh->cb_cls,
+ 0LLU,
NULL,
NULL,
NULL,
@@ -962,10 +962,9 @@ wh_return_not_found (void *cls)
* @param plc callback to lookup accounts with
* @param plc_cls closure for @a plc
* @param http_method HTTP method used for the webhook
- * @param url_path rest of the URL after `/kyc-webhook/`
+ * @param url_path rest of the URL after `/kyc-webhook/$LOGIC/`, as NULL-terminated array
* @param connection MHD connection object (for HTTP headers)
- * @param body_size number of bytes in @a body
- * @param body HTTP request body
+ * @param body HTTP request body, or NULL if not available
* @param cb function to call with the result
* @param cb_cls closure for @a cb
* @return handle to cancel operation early
@@ -976,10 +975,9 @@ oauth2_webhook (void *cls,
TALER_KYCLOGIC_ProviderLookupCallback plc,
void *plc_cls,
const char *http_method,
- const char *url_path,
+ const char *url_path[],
struct MHD_Connection *connection,
- size_t body_size,
- const void *body,
+ const json_t *body,
TALER_KYCLOGIC_WebhookCallback cb,
void *cb_cls)
{