From 3f99e4f3f8a89dfafcf37fcbf9046134adca436a Mon Sep 17 00:00:00 2001 From: Christian Grothoff Date: Sun, 7 Aug 2022 15:35:06 +0200 Subject: -implement new kyc-webhook endpoint --- src/exchange/Makefile.am | 1 + src/exchange/taler-exchange-httpd_kyc-webhook.c | 347 ++++++++++++++++++++++++ src/exchange/taler-exchange-httpd_kyc-webhook.h | 64 +++++ 3 files changed, 412 insertions(+) create mode 100644 src/exchange/taler-exchange-httpd_kyc-webhook.c create mode 100644 src/exchange/taler-exchange-httpd_kyc-webhook.h (limited to 'src/exchange') 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 +*/ +/** + * @file taler-exchange-httpd_kyc-webhook.c + * @brief Handle notification of KYC completion via webhook. + * @author Christian Grothoff + */ +#include "platform.h" +#include +#include +#include +#include +#include +#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 +*/ +/** + * @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 +#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 -- cgit v1.2.3