From d58334cf89f2c43dcd20e4a8deb01f45c852f7df Mon Sep 17 00:00:00 2001 From: Christian Grothoff Date: Tue, 9 Aug 2022 13:00:58 +0200 Subject: implement kyc-proof hook in kyc-tester --- contrib/gana | 2 +- src/exchange/taler-exchange-httpd_kyc-webhook.c | 2 +- src/include/taler_kyclogic_plugin.h | 4 +- src/kyclogic/plugin_kyclogic_oauth2.c | 2 +- src/kyclogic/plugin_kyclogic_template.c | 2 +- src/kyclogic/taler-exchange-kyc-tester.c | 253 ++++++++++++++++++++++-- 6 files changed, 243 insertions(+), 22 deletions(-) diff --git a/contrib/gana b/contrib/gana index 02da8656d..2075d4271 160000 --- a/contrib/gana +++ b/contrib/gana @@ -1 +1 @@ -Subproject commit 02da8656d6d915df023de0b90d18ade9e80603fa +Subproject commit 2075d42719b00f2763fe71d327ef4b9fd23be476 diff --git a/src/exchange/taler-exchange-httpd_kyc-webhook.c b/src/exchange/taler-exchange-httpd_kyc-webhook.c index bba8e1a51..6faa2556d 100644 --- a/src/exchange/taler-exchange-httpd_kyc-webhook.c +++ b/src/exchange/taler-exchange-httpd_kyc-webhook.c @@ -272,7 +272,7 @@ handler_kyc_webhook_generic ( kwh->logic); return TALER_MHD_reply_with_error (rc->connection, MHD_HTTP_NOT_FOUND, - TALER_EC_EXCHANGE_KYC_WEBHOOK_LOGIC_UNKNOWN, + TALER_EC_EXCHANGE_KYC_GENERIC_LOGIC_UNKNOWN, "$LOGIC"); } kwh->wh = kwh->plugin->webhook (kwh->plugin->cls, diff --git a/src/include/taler_kyclogic_plugin.h b/src/include/taler_kyclogic_plugin.h index 8e52e0510..a4bddde61 100644 --- a/src/include/taler_kyclogic_plugin.h +++ b/src/include/taler_kyclogic_plugin.h @@ -292,7 +292,7 @@ struct TALER_KYCLOGIC_Plugin * * @param cls the @e cls of this struct with the plugin-specific state * @param pd provider configuration details - * @param url_path rest of the URL after `/kyc-webhook/` + * @param url_path rest of the URL after `/kyc-webhook/$H_PAYTO/$LOGIC` * @param connection MHD connection object (for HTTP headers) * @param account_id which account to trigger process for * @param provider_user_id user ID (or NULL) the proof is for @@ -304,7 +304,7 @@ struct TALER_KYCLOGIC_Plugin struct TALER_KYCLOGIC_ProofHandle * (*proof)(void *cls, const struct TALER_KYCLOGIC_ProviderDetails *pd, - const char *url_path, + const char *const url_path[], struct MHD_Connection *connection, const struct TALER_PaytoHashP *account_id, const char *provider_user_id, diff --git a/src/kyclogic/plugin_kyclogic_oauth2.c b/src/kyclogic/plugin_kyclogic_oauth2.c index 0a19a8497..cbf5ea3ae 100644 --- a/src/kyclogic/plugin_kyclogic_oauth2.c +++ b/src/kyclogic/plugin_kyclogic_oauth2.c @@ -767,7 +767,7 @@ handle_curl_proof_finished (void *cls, static struct TALER_KYCLOGIC_ProofHandle * oauth2_proof (void *cls, const struct TALER_KYCLOGIC_ProviderDetails *pd, - const char *url_path, + const char *const url_path[], struct MHD_Connection *connection, const struct TALER_PaytoHashP *account_id, const char *provider_user_id, diff --git a/src/kyclogic/plugin_kyclogic_template.c b/src/kyclogic/plugin_kyclogic_template.c index e59c15da1..f5e583db4 100644 --- a/src/kyclogic/plugin_kyclogic_template.c +++ b/src/kyclogic/plugin_kyclogic_template.c @@ -262,7 +262,7 @@ template_initiate_cancel (struct TALER_KYCLOGIC_InitiateHandle *ih) static struct TALER_KYCLOGIC_ProofHandle * template_proof (void *cls, const struct TALER_KYCLOGIC_ProviderDetails *pd, - const char *url_path, + const char *const url_path[], struct MHD_Connection *connection, const struct TALER_PaytoHashP *account_id, const char *provider_user_id, diff --git a/src/kyclogic/taler-exchange-kyc-tester.c b/src/kyclogic/taler-exchange-kyc-tester.c index 753c65176..a389b0812 100644 --- a/src/kyclogic/taler-exchange-kyc-tester.c +++ b/src/kyclogic/taler-exchange-kyc-tester.c @@ -161,6 +161,48 @@ struct TEKT_RequestHandler }; +/** + * Information we track per ongoing kyc-proof request. + */ +struct ProofRequestState +{ + /** + * Kept in a DLL. + */ + struct ProofRequestState *next; + + /** + * Kept in a DLL. + */ + struct ProofRequestState *prev; + + /** + * Handle for operation with the plugin. + */ + struct TALER_KYCLOGIC_ProofHandle *ph; + + /** + * Logic plugin we are using. + */ + struct TALER_KYCLOGIC_Plugin *logic; + + /** + * HTTP request details. + */ + struct TEKT_RequestContext *rc; + +}; + +/** + * Head of DLL. + */ +static struct ProofRequestState *rs_head; + +/** + * Tail of DLL. + */ +static struct ProofRequestState *rs_tail; + /** * The exchange's configuration (global) */ @@ -181,6 +223,16 @@ static char *TEKT_base_url; */ static struct TALER_PaytoHashP cmd_line_h_payto; +/** + * Provider user ID to use. + */ +static char *cmd_provider_user_id; + +/** + * Provider legitimization ID to use. + */ +static char *cmd_provider_legitimization_id; + /** * Row ID to use, override with '-r' */ @@ -214,7 +266,7 @@ static struct TALER_KYCLOGIC_InitiateHandle *ih; /** * KYC logic running for @e ih. */ -static struct TALER_KYCLOGIC_Plugin *logic; +static struct TALER_KYCLOGIC_Plugin *ih_logic; /** * Port to run the daemon on. @@ -484,7 +536,7 @@ handler_kyc_webhook_generic ( kwh->logic); return TALER_MHD_reply_with_error (rc->connection, MHD_HTTP_NOT_FOUND, - TALER_EC_EXCHANGE_KYC_WEBHOOK_LOGIC_UNKNOWN, + TALER_EC_EXCHANGE_KYC_GENERIC_LOGIC_UNKNOWN, "$LOGIC"); } kwh->wh = kwh->plugin->webhook (kwh->plugin->cls, @@ -531,6 +583,13 @@ handler_kyc_webhook_generic ( } +/** + * Handle a GET "/kyc-webhook" request. + * + * @param rc request to handle + * @param args one argument with the payment_target_uuid + * @return MHD result code + */ static MHD_RESULT handler_kyc_webhook_get ( struct TEKT_RequestContext *rc, @@ -543,6 +602,14 @@ handler_kyc_webhook_get ( } +/** + * Handle a POST "/kyc-webhook" request. + * + * @param rc request to handle + * @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_post ( struct TEKT_RequestContext *rc, @@ -556,6 +623,127 @@ handler_kyc_webhook_post ( } +/** + * Function called with the result of a proof check operation. + * + * 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 with the `struct ProofRequestState` + * @param status KYC status + * @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 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 +proof_cb ( + void *cls, + enum TALER_KYCLOGIC_KycStatus status, + const char *provider_user_id, + const char *provider_legitimization_id, + struct GNUNET_TIME_Absolute expiration, + unsigned int http_status, + struct MHD_Response *response) +{ + struct ProofRequestState *rs = cls; + + MHD_resume_connection (rs->rc->connection); + // FIXME: kick MHD event loop! + // FIXME: actually queue response... + GNUNET_CONTAINER_DLL_remove (rs_head, + rs_tail, + rs); + GNUNET_free (rs); +} + + +/** + * Function called when we receive a 'GET' to the + * '/kyc-proof' endpoint. + * + * @param rc request context + * @param args remaining URL arguments; + * args[0] is the 'h_payto', + * args[1] should be the logic plugin name + */ +static MHD_RESULT +handler_kyc_proof_get ( + struct TEKT_RequestContext *rc, + const char *const args[]) +{ + struct TALER_PaytoHashP h_payto; + struct TALER_KYCLOGIC_ProviderDetails *pd; + struct TALER_KYCLOGIC_Plugin *logic; + struct ProofRequestState *rs; + + if ( (NULL == args[0]) || + (NULL == args[1]) ) + { + GNUNET_break_op (0); + return TALER_MHD_reply_with_error (rc->connection, + MHD_HTTP_NOT_FOUND, + TALER_EC_GENERIC_ENDPOINT_UNKNOWN, + "'/$H_PAYTO/$LOGIC' required after '/kyc-proof'"); + } + if (GNUNET_OK != + GNUNET_STRINGS_string_to_data (args[0], + strlen (args[0]), + &h_payto, + sizeof (h_payto))) + { + GNUNET_break_op (0); + return TALER_MHD_reply_with_error (rc->connection, + MHD_HTTP_BAD_REQUEST, + TALER_EC_GENERIC_PARAMETER_MALFORMED, + "h_payto"); + } + if (0 != + GNUNET_memcmp (&h_payto, + &cmd_line_h_payto)) + { + GNUNET_break_op (0); + return TALER_MHD_reply_with_error (rc->connection, + MHD_HTTP_NOT_FOUND, + TALER_EC_EXCHANGE_KYC_PROOF_REQUEST_UNKNOWN, + "h_payto"); + } + + if (GNUNET_OK != + TALER_KYCLOGIC_kyc_get_logic (args[1], + &logic, + &pd)) + { + GNUNET_log (GNUNET_ERROR_TYPE_ERROR, + "Could not initiate KYC with provider `%s' (configuration error?)\n", + initiate_section); + return TALER_MHD_reply_with_error (rc->connection, + MHD_HTTP_NOT_FOUND, + TALER_EC_EXCHANGE_KYC_GENERIC_LOGIC_UNKNOWN, + args[1]); + } + rs = GNUNET_new (struct ProofRequestState); + rs->rc = rc; + rs->logic = logic; + MHD_suspend_connection (rc->connection); + GNUNET_CONTAINER_DLL_insert (rs_head, + rs_tail, + rs); + rs->ph = logic->proof (logic->cls, + pd, + &args[2], + rc->connection, + &h_payto, + cmd_provider_user_id, + cmd_provider_legitimization_id, + &proof_cb, + rs); + GNUNET_assert (NULL != rs->ph); + return MHD_YES; +} + + /** * Function called whenever MHD is done with a request. If the * request was a POST, we may have stored a `struct Buffer *` in the @@ -757,15 +945,14 @@ handle_mhd_request (void *cls, void **con_cls) { static struct TEKT_RequestHandler handlers[] = { -#if FIXME /* simulated KYC endpoints */ { .url = "kyc-proof", .method = MHD_HTTP_METHOD_GET, - .handler.get = &TEKT_handler_kyc_proof, - .nargs = 1 + .handler.get = &handler_kyc_proof_get, + .nargs = 128, + .nargs_is_upper_bound = true }, -#endif { .url = "kyc-webhook", .method = MHD_HTTP_METHOD_POST, @@ -993,11 +1180,21 @@ static void do_shutdown (void *cls) { struct MHD_Daemon *mhd; - (void) cls; + struct ProofRequestState *rs; + (void) cls; + while (NULL != (rs = rs_head)) + { + GNUNET_CONTAINER_DLL_remove (rs_head, + rs_tail, + rs); + rs->logic->proof_cancel (rs->ph); + MHD_resume_connection (rs->rc->connection); + GNUNET_free (rs); + } if (NULL != ih) { - logic->initiate_cancel (ih); + ih_logic->initiate_cancel (ih); ih = NULL; } kyc_webhook_cleanup (); @@ -1050,10 +1247,16 @@ initiate_cb ( return; } fprintf (stdout, - "Visit `%s' to begin KYC process (%s/%s)\n", + "Visit `%s' to begin KYC process (-u: '%s', -l: '%s')\n", redirect_url, provider_user_id, provider_legitimization_id); + GNUNET_free (cmd_provider_user_id); + GNUNET_free (cmd_provider_legitimization_id); + if (NULL != provider_user_id) + cmd_provider_user_id = GNUNET_strdup (provider_user_id); + if (NULL != provider_legitimization_id) + cmd_provider_legitimization_id = GNUNET_strdup (provider_legitimization_id); } @@ -1113,7 +1316,7 @@ run (void *cls, if (GNUNET_OK != TALER_KYCLOGIC_kyc_get_logic (initiate_section, - &logic, + &ih_logic, &pd)) { GNUNET_log (GNUNET_ERROR_TYPE_ERROR, @@ -1123,12 +1326,12 @@ run (void *cls, GNUNET_SCHEDULER_shutdown (); return; } - ih = logic->initiate (logic->cls, - pd, - &cmd_line_h_payto, - kyc_row_id, - &initiate_cb, - NULL); + ih = ih_logic->initiate (ih_logic->cls, + pd, + &cmd_line_h_payto, + kyc_row_id, + &initiate_cb, + NULL); GNUNET_break (NULL != ih); } if (run_webservice) @@ -1217,6 +1420,24 @@ main (int argc, "SECTION_NAME", "initiate KYC check using provider configured in SECTION_NAME of the configuration", &initiate_section), + GNUNET_GETOPT_option_string ( + 'i', + "initiate", + "SECTION_NAME", + "initiate KYC check using provider configured in SECTION_NAME of the configuration", + &initiate_section), + GNUNET_GETOPT_option_string ( + 'u', + "user", + "ID", + "use the given provider user ID (overridden if -i is also used)", + &cmd_provider_user_id), + GNUNET_GETOPT_option_string ( + 'l', + "legitimization", + "ID", + "use the given provider legitimization ID (overridden if -i is also used)", + &cmd_provider_legitimization_id), GNUNET_GETOPT_option_base32_fixed_size ( 'p', "payto-hash", -- cgit v1.2.3