-implement new kyc-webhook endpoint

This commit is contained in:
Christian Grothoff 2022-08-07 15:35:06 +02:00
parent 30b833232e
commit 3f99e4f3f8
No known key found for this signature in database
GPG Key ID: 939E6BE1E29FC3CC
8 changed files with 430 additions and 21 deletions

@ -1 +1 @@
Subproject commit 4752211918879f9ceec07bec5f64c8209d840b51
Subproject commit 02da8656d6d915df023de0b90d18ade9e80603fa

View File

@ -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 \

View File

@ -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 */

View File

@ -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

View File

@ -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)

View File

@ -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);

View File

@ -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);

View File

@ -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)
{