From ff48ada7d5ba026a39c0e8ddefd54b80b813425b Mon Sep 17 00:00:00 2001 From: Christian Grothoff Date: Mon, 8 Aug 2022 15:22:45 +0200 Subject: move kyclogic into libtalerkyclogic --- src/kyclogic/taler-exchange-kyc-tester.c | 347 ++++++++++++++++++++++++++++++- 1 file changed, 340 insertions(+), 7 deletions(-) (limited to 'src/kyclogic/taler-exchange-kyc-tester.c') diff --git a/src/kyclogic/taler-exchange-kyc-tester.c b/src/kyclogic/taler-exchange-kyc-tester.c index 24809a82d..1fb0afa47 100644 --- a/src/kyclogic/taler-exchange-kyc-tester.c +++ b/src/kyclogic/taler-exchange-kyc-tester.c @@ -28,6 +28,7 @@ #include "taler_mhd_lib.h" #include "taler_json_lib.h" #include "taler_crypto_lib.h" +#include "taler_kyclogic_lib.h" #include "taler_kyclogic_plugin.h" #include @@ -220,6 +221,328 @@ r404 (struct MHD_Connection *connection, } +/** + * 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 TEKT_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 (); +} + + +static void +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 */ + GNUNET_log (GNUNET_ERROR_TYPE_MESSAGE, + "KYC successful for user `%s' (legi: %s)\n", + provider_user_id, + provider_legitimization_id); + 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 TEKT_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); +} + + +/** + * Function the plugin can use to lookup an + * @a h_payto by @a provider_legitimization_id. + * + * @param cls closure, NULL + * @param provider_section + * @param provider_legitimization_id legi to look up + * @param[out] h_payto where to write the result + * @return database transaction status + */ +static enum GNUNET_DB_QueryStatus +kyc_provider_account_lookup ( + void *cls, + const char *provider_section, + const char *provider_legitimization_id, + struct TALER_PaytoHashP *h_payto) +{ + // FIXME: pass value to use for h_payto via command-line? + memset (h_payto, + 42, + sizeof (*h_payto)); + return 1; // FIXME... +} + + +/** + * 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 TEKT_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 != + TALER_KYCLOGIC_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, + &kyc_provider_account_lookup, + NULL, + 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"); +} + + +static MHD_RESULT +handler_kyc_webhook_get ( + struct TEKT_RequestContext *rc, + const char *const args[]) +{ + return handler_kyc_webhook_generic (rc, + MHD_HTTP_METHOD_GET, + NULL, + args); +} + + +static MHD_RESULT +handler_kyc_webhook_post ( + struct TEKT_RequestContext *rc, + const json_t *root, + const char *const args[]) +{ + return handler_kyc_webhook_generic (rc, + MHD_HTTP_METHOD_POST, + root, + args); +} + + /** * Function called whenever MHD is done with a request. If the * request was a POST, we may have stored a `struct Buffer *` in the @@ -295,7 +618,6 @@ proceed_with_handler (struct TEKT_RequestContext *rc, size_t *upload_data_size) { const struct TEKT_RequestHandler *rh = rc->rh; - // FIXME: handle '-1 == rh->nargs'!!! const char *args[rh->nargs + 2]; size_t ulen = strlen (url) + 1; json_t *root = NULL; @@ -442,19 +764,21 @@ handle_mhd_request (void *cls, .handler.post = &TEKT_handler_kyc_wallet, .nargs = 0 }, +#endif { .url = "kyc-webhook", .method = MHD_HTTP_METHOD_POST, - .handler.post = &TEKT_handler_kyc_webhook_post, - .nargs = -1 + .handler.post = &handler_kyc_webhook_post, + .nargs = 128, + .nargs_is_upper_bound = true }, { .url = "kyc-webhook", .method = MHD_HTTP_METHOD_GET, - .handler.post = &TEKT_handler_kyc_webhook_get, - .nargs = -1 + .handler.get = &handler_kyc_webhook_get, + .nargs = 128, + .nargs_is_upper_bound = true }, -#endif /* mark end of list */ { .url = NULL @@ -670,6 +994,8 @@ do_shutdown (void *cls) struct MHD_Daemon *mhd; (void) cls; + kyc_webhook_cleanup (); + TALER_KYCLOGIC_kyc_done (); mhd = TALER_MHD_daemon_stop (); if (NULL != mhd) MHD_stop_daemon (mhd); @@ -708,11 +1034,18 @@ run (void *cls, (void ) cfgfile; TALER_MHD_setup (TALER_MHD_GO_NONE); TEKT_cfg = config; - + if (GNUNET_OK != + TALER_KYCLOGIC_kyc_init (config)) + { + global_ret = EXIT_NOTCONFIGURED; + GNUNET_SCHEDULER_shutdown (); + return; + } if (GNUNET_OK != exchange_serve_process_config ()) { global_ret = EXIT_NOTCONFIGURED; + TALER_KYCLOGIC_kyc_done (); GNUNET_SCHEDULER_shutdown (); return; } -- cgit v1.2.3