summaryrefslogtreecommitdiff
path: root/src/kyclogic/taler-exchange-kyc-tester.c
diff options
context:
space:
mode:
Diffstat (limited to 'src/kyclogic/taler-exchange-kyc-tester.c')
-rw-r--r--src/kyclogic/taler-exchange-kyc-tester.c1081
1 files changed, 963 insertions, 118 deletions
diff --git a/src/kyclogic/taler-exchange-kyc-tester.c b/src/kyclogic/taler-exchange-kyc-tester.c
index 24809a82d..c2efafd72 100644
--- a/src/kyclogic/taler-exchange-kyc-tester.c
+++ b/src/kyclogic/taler-exchange-kyc-tester.c
@@ -27,7 +27,9 @@
#include <limits.h>
#include "taler_mhd_lib.h"
#include "taler_json_lib.h"
-#include "taler_crypto_lib.h"
+#include "taler_templating_lib.h"
+#include "taler_util.h"
+#include "taler_kyclogic_lib.h"
#include "taler_kyclogic_plugin.h"
#include <gnunet/gnunet_mhd_compat.h>
@@ -60,6 +62,11 @@ struct TEKT_RequestContext
struct MHD_Connection *connection;
/**
+ * HTTP response to return (or NULL).
+ */
+ struct MHD_Response *response;
+
+ /**
* @e rh-specific cleanup routine. Function called
* upon completion of the request that should
* clean up @a rh_ctx. Can be NULL.
@@ -73,6 +80,18 @@ struct TEKT_RequestContext
* Can be NULL.
*/
void *rh_ctx;
+
+ /**
+ * Uploaded JSON body, if any.
+ */
+ json_t *root;
+
+ /**
+ * HTTP status to return upon resume if @e response
+ * is non-NULL.
+ */
+ unsigned int http_status;
+
};
@@ -161,6 +180,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)
*/
static const struct GNUNET_CONFIGURATION_Handle *TEKT_cfg;
@@ -171,14 +232,45 @@ static const struct GNUNET_CONFIGURATION_Handle *TEKT_cfg;
static struct MHD_Daemon *mhd;
/**
- * Our currency.
+ * Our base URL.
*/
-static char *TEKT_currency;
+static char *TEKT_base_url;
/**
- * Our base URL.
+ * Payto set via command-line (or otherwise random).
*/
-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;
+
+/**
+ * Name of the configuration section with the
+ * configuration data of the selected provider.
+ */
+static const char *provider_section_name;
+
+/**
+ * Row ID to use, override with '-r'
+ */
+static unsigned int kyc_row_id = 42;
+
+/**
+ * -P command-line option.
+ */
+static int print_h_payto;
+
+/**
+ * -w command-line option.
+ */
+static int run_webservice;
/**
* Value to return from main()
@@ -186,6 +278,26 @@ static char *TEKT_base_url;
static int global_ret;
/**
+ * -r command-line flag.
+ */
+static char *requirements;
+
+/**
+ * -i command-line flag.
+ */
+static char *ut_s = "individual";
+
+/**
+ * Handle for ongoing initiation operation.
+ */
+static struct TALER_KYCLOGIC_InitiateHandle *ih;
+
+/**
+ * KYC logic running for @e ih.
+ */
+static struct TALER_KYCLOGIC_Plugin *ih_logic;
+
+/**
* Port to run the daemon on.
*/
static uint16_t serve_port;
@@ -203,20 +315,547 @@ static struct GNUNET_CURL_RescheduleContext *exchange_curl_rc;
/**
- * Generate a 404 "not found" reply on @a connection with
- * the hint @a details.
+ * 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;
+
+ /**
+ * Name of the configuration
+ * section defining the KYC logic.
+ */
+ const char *section_name;
+
+ /**
+ * 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);
+}
+
+
+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 process_row legitimization process request the webhook was about
+ * @param account_id account the webhook was about
+ * @param provider_section configuration section of the logic
+ * @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 attributes user attributes returned by the provider
+ * @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 process_row,
+ const struct TALER_PaytoHashP *account_id,
+ const char *provider_section,
+ const char *provider_user_id,
+ const char *provider_legitimization_id,
+ enum TALER_KYCLOGIC_KycStatus status,
+ struct GNUNET_TIME_Absolute expiration,
+ const json_t *attributes,
+ unsigned int http_status,
+ struct MHD_Response *response)
+{
+ struct KycWebhookContext *kwh = cls;
+
+ (void) expiration;
+ (void) provider_section;
+ kwh->wh = NULL;
+ if ( (NULL != account_id) &&
+ (0 != GNUNET_memcmp (account_id,
+ &cmd_line_h_payto)) )
+ {
+ GNUNET_log (GNUNET_ERROR_TYPE_WARNING,
+ "Received webhook for unexpected account\n");
+ }
+ if ( (NULL != provider_user_id) &&
+ (NULL != cmd_provider_user_id) &&
+ (0 != strcmp (provider_user_id,
+ cmd_provider_user_id)) )
+ {
+ GNUNET_log (GNUNET_ERROR_TYPE_WARNING,
+ "Received webhook for unexpected provider user ID (%s)\n",
+ provider_user_id);
+ }
+ if ( (NULL != provider_legitimization_id) &&
+ (NULL != cmd_provider_legitimization_id) &&
+ (0 != strcmp (provider_legitimization_id,
+ cmd_provider_legitimization_id)) )
+ {
+ GNUNET_log (GNUNET_ERROR_TYPE_WARNING,
+ "Received webhook for unexpected provider legitimization ID (%s)\n",
+ provider_legitimization_id);
+ }
+ 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);
+ GNUNET_break (NULL != attributes);
+ fprintf (stderr,
+ "Extracted attributes:\n");
+ json_dumpf (attributes,
+ stderr,
+ JSON_INDENT (2));
+ break;
+ default:
+ GNUNET_log (GNUNET_ERROR_TYPE_INFO,
+ "KYC status of %s/%s (process #%llu) is %d\n",
+ provider_user_id,
+ provider_legitimization_id,
+ (unsigned long long) process_row,
+ status);
+ break;
+ }
+ kwh->response = response;
+ kwh->response_code = http_status;
+ kwh_resume (kwh);
+ TALER_MHD_daemon_trigger ();
+}
+
+
+/**
+ * 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);
+}
+
+
+/**
+ * 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
+ * @param[out] legi_row where to write the row ID for the legitimization ID
+ * @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,
+ uint64_t *legi_row)
+{
+ (void) cls;
+ GNUNET_log (GNUNET_ERROR_TYPE_INFO,
+ "Simulated account lookup using `%s/%s'\n",
+ provider_section,
+ provider_legitimization_id);
+ *h_payto = cmd_line_h_payto;
+ *legi_row = kyc_row_id;
+ return GNUNET_DB_STATUS_SUCCESS_ONE_RESULT;
+}
+
+
+/**
+ * Handle a (GET or POST) "/kyc-webhook" request.
*
- * @param connection where to send the reply on
- * @param details details for the error message, can be NULL
+ * @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 legitimization_uuid
+ * @return MHD result code
*/
static MHD_RESULT
-r404 (struct MHD_Connection *connection,
- const char *details)
+handler_kyc_webhook_generic (
+ struct TEKT_RequestContext *rc,
+ const char *method,
+ const json_t *root,
+ const char *const args[])
{
- return TALER_MHD_reply_with_error (connection,
- MHD_HTTP_NOT_FOUND,
- TALER_EC_EXCHANGE_GENERIC_OPERATION_UNKNOWN,
- details);
+ struct KycWebhookContext *kwh = rc->rh_ctx;
+
+ if (NULL == kwh)
+ { /* first time */
+ kwh = GNUNET_new (struct KycWebhookContext);
+ kwh->rc = rc;
+ rc->rh_ctx = kwh;
+ rc->rh_cleaner = &clean_kwh;
+
+ if ( (NULL == args[0]) ||
+ (GNUNET_OK !=
+ TALER_KYCLOGIC_lookup_logic (args[0],
+ &kwh->plugin,
+ &kwh->pd,
+ &kwh->section_name)) )
+ {
+ GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
+ "KYC logic `%s' unknown (check KYC provider configuration)\n",
+ args[0]);
+ return TALER_MHD_reply_with_error (rc->connection,
+ MHD_HTTP_NOT_FOUND,
+ TALER_EC_EXCHANGE_KYC_GENERIC_LOGIC_UNKNOWN,
+ args[0]);
+ }
+ GNUNET_log (GNUNET_ERROR_TYPE_INFO,
+ "Calling KYC provider specific webhook\n");
+ 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)
+ {
+ GNUNET_log (GNUNET_ERROR_TYPE_INFO,
+ "Returning queued reply for KWH\n");
+ /* 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_assert (0);
+ return TALER_MHD_reply_with_error (rc->connection,
+ MHD_HTTP_INTERNAL_SERVER_ERROR,
+ TALER_EC_GENERIC_INTERNAL_INVARIANT_FAILURE,
+ "resumed without response");
+}
+
+
+/**
+ * Handle a GET "/kyc-webhook" request.
+ *
+ * @param rc request to handle
+ * @param args one argument with the legitimization_uuid
+ * @return MHD result code
+ */
+static MHD_RESULT
+handler_kyc_webhook_get (
+ struct TEKT_RequestContext *rc,
+ const char *const args[])
+{
+ GNUNET_log (GNUNET_ERROR_TYPE_INFO,
+ "Webhook GET triggered\n");
+ return handler_kyc_webhook_generic (rc,
+ MHD_HTTP_METHOD_GET,
+ NULL,
+ args);
+}
+
+
+/**
+ * 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 legitimization_uuid
+ * @return MHD result code
+ */
+static MHD_RESULT
+handler_kyc_webhook_post (
+ struct TEKT_RequestContext *rc,
+ const json_t *root,
+ const char *const args[])
+{
+ GNUNET_log (GNUNET_ERROR_TYPE_INFO,
+ "Webhook POST triggered\n");
+ return handler_kyc_webhook_generic (rc,
+ MHD_HTTP_METHOD_POST,
+ root,
+ args);
+}
+
+
+/**
+ * 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 attributes attributes about the user
+ * @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,
+ const json_t *attributes,
+ unsigned int http_status,
+ struct MHD_Response *response)
+{
+ struct ProofRequestState *rs = cls;
+
+ (void) expiration;
+ GNUNET_log (GNUNET_ERROR_TYPE_MESSAGE,
+ "KYC legitimization %s completed with status %d (%u) for %s\n",
+ provider_legitimization_id,
+ status,
+ http_status,
+ provider_user_id);
+ if (TALER_KYCLOGIC_STATUS_SUCCESS == status)
+ {
+ GNUNET_break (NULL != attributes);
+ fprintf (stderr,
+ "Extracted attributes:\n");
+ json_dumpf (attributes,
+ stderr,
+ JSON_INDENT (2));
+ }
+ GNUNET_log (GNUNET_ERROR_TYPE_INFO,
+ "Returning response %p with status %u\n",
+ response,
+ http_status);
+ rs->rc->response = response;
+ rs->rc->http_status = http_status;
+ GNUNET_CONTAINER_DLL_remove (rs_head,
+ rs_tail,
+ rs);
+ MHD_resume_connection (rs->rc->connection);
+ TALER_MHD_daemon_trigger ();
+ 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] should be the logic plugin name
+ */
+static MHD_RESULT
+handler_kyc_proof_get (
+ struct TEKT_RequestContext *rc,
+ const char *const args[1])
+{
+ struct TALER_PaytoHashP h_payto;
+ struct TALER_KYCLOGIC_ProviderDetails *pd;
+ struct TALER_KYCLOGIC_Plugin *logic;
+ struct ProofRequestState *rs;
+ const char *section_name;
+ const char *h_paytos;
+
+ GNUNET_log (GNUNET_ERROR_TYPE_INFO,
+ "GET /kyc-proof triggered\n");
+ if (NULL == args[0])
+ {
+ GNUNET_break_op (0);
+ return TALER_MHD_reply_with_error (rc->connection,
+ MHD_HTTP_NOT_FOUND,
+ TALER_EC_GENERIC_ENDPOINT_UNKNOWN,
+ "'/kyc-proof/$PROVIDER_SECTION?state=$H_PAYTO' required");
+ }
+ h_paytos = MHD_lookup_connection_value (rc->connection,
+ MHD_GET_ARGUMENT_KIND,
+ "state");
+ if (NULL == h_paytos)
+ {
+ GNUNET_break_op (0);
+ return TALER_MHD_reply_with_error (rc->connection,
+ MHD_HTTP_BAD_REQUEST,
+ TALER_EC_GENERIC_PARAMETER_MISSING,
+ "h_payto");
+ }
+ if (GNUNET_OK !=
+ GNUNET_STRINGS_string_to_data (h_paytos,
+ strlen (h_paytos),
+ &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_lookup_logic (args[0],
+ &logic,
+ &pd,
+ &section_name))
+ {
+ GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
+ "Could not initiate KYC with provider `%s' (configuration error?)\n",
+ args[0]);
+ return TALER_MHD_reply_with_error (rc->connection,
+ MHD_HTTP_NOT_FOUND,
+ TALER_EC_EXCHANGE_KYC_GENERIC_LOGIC_UNKNOWN,
+ args[0]);
+ }
+ 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,
+ rc->connection,
+ &h_payto,
+ kyc_row_id,
+ cmd_provider_user_id,
+ cmd_provider_legitimization_id,
+ &proof_cb,
+ rs);
+ GNUNET_assert (NULL != rs->ph);
+ return MHD_YES;
}
@@ -272,6 +911,8 @@ handle_mhd_completion_callback (void *cls,
TALER_MHD_parse_post_cleanup_callback (rc->opaque_post_parsing_context);
/* Sanity-check that we didn't leave any transactions hanging */
+ if (NULL != rc->root)
+ json_decref (rc->root);
GNUNET_free (rc);
*con_cls = NULL;
}
@@ -295,10 +936,8 @@ 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;
MHD_RESULT ret;
/* We do check for "ulen" here, because we'll later stack-allocate a buffer
@@ -319,8 +958,9 @@ proceed_with_handler (struct TEKT_RequestContext *rc,
/* All POST endpoints come with a body in JSON format. So we parse
the JSON here. */
- if (0 == strcasecmp (rh->method,
- MHD_HTTP_METHOD_POST))
+ if ( (NULL == rc->root) &&
+ (0 == strcasecmp (rh->method,
+ MHD_HTTP_METHOD_POST)) )
{
enum GNUNET_GenericReturnValue res;
@@ -328,16 +968,17 @@ proceed_with_handler (struct TEKT_RequestContext *rc,
&rc->opaque_post_parsing_context,
upload_data,
upload_data_size,
- &root);
+ &rc->root);
if (GNUNET_SYSERR == res)
{
- GNUNET_assert (NULL == root);
+ GNUNET_assert (NULL == rc->root);
+ GNUNET_break (0);
return MHD_NO; /* bad upload, could not even generate error */
}
if ( (GNUNET_NO == res) ||
- (NULL == root) )
+ (NULL == rc->root) )
{
- GNUNET_assert (NULL == root);
+ GNUNET_assert (NULL == rc->root);
return MHD_YES; /* so far incomplete upload or parser error */
}
}
@@ -349,9 +990,9 @@ proceed_with_handler (struct TEKT_RequestContext *rc,
/* Parse command-line arguments */
/* make a copy of 'url' because 'strtok_r()' will modify */
- memcpy (d,
- url,
- ulen);
+ GNUNET_memcpy (d,
+ url,
+ ulen);
i = 0;
args[i++] = strtok_r (d, "/", &sp);
while ( (NULL != args[i - 1]) &&
@@ -374,7 +1015,6 @@ proceed_with_handler (struct TEKT_RequestContext *rc,
rh->url,
url);
GNUNET_break_op (0);
- json_decref (root);
return TALER_MHD_reply_with_error (rc->connection,
MHD_HTTP_NOT_FOUND,
TALER_EC_EXCHANGE_GENERIC_WRONG_NUMBER_OF_SEGMENTS,
@@ -384,20 +1024,35 @@ proceed_with_handler (struct TEKT_RequestContext *rc,
/* Above logic ensures that 'root' is exactly non-NULL for POST operations,
so we test for 'root' to decide which handler to invoke. */
- if (NULL != root)
+ if (NULL != rc->root)
ret = rh->handler.post (rc,
- root,
+ rc->root,
args);
else /* We also only have "POST" or "GET" in the API for at this point
(OPTIONS/HEAD are taken care of earlier) */
ret = rh->handler.get (rc,
args);
}
- json_decref (root);
return ret;
}
+static void
+rh_cleaner_cb (struct TEKT_RequestContext *rc)
+{
+ if (NULL != rc->response)
+ {
+ MHD_destroy_response (rc->response);
+ rc->response = NULL;
+ }
+ if (NULL != rc->root)
+ {
+ json_decref (rc->root);
+ rc->root = NULL;
+ }
+}
+
+
/**
* Handle incoming HTTP request.
*
@@ -422,39 +1077,27 @@ handle_mhd_request (void *cls,
void **con_cls)
{
static struct TEKT_RequestHandler handlers[] = {
-#if FIXME
- /* KYC endpoints */
- {
- .url = "kyc-check",
- .method = MHD_HTTP_METHOD_GET,
- .handler.get = &TEKT_handler_kyc_check,
- .nargs = 1
- },
+ /* simulated KYC endpoints */
{
.url = "kyc-proof",
.method = MHD_HTTP_METHOD_GET,
- .handler.get = &TEKT_handler_kyc_proof,
+ .handler.get = &handler_kyc_proof_get,
.nargs = 1
},
{
- .url = "kyc-wallet",
- .method = MHD_HTTP_METHOD_POST,
- .handler.post = &TEKT_handler_kyc_wallet,
- .nargs = 0
- },
- {
.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
@@ -472,6 +1115,13 @@ handle_mhd_request (void *cls,
rc = *con_cls = GNUNET_new (struct TEKT_RequestContext);
rc->url = url;
rc->connection = connection;
+ rc->rh_cleaner = &rh_cleaner_cb;
+ }
+ if (NULL != rc->response)
+ {
+ return MHD_queue_response (rc->connection,
+ rc->http_status,
+ rc->response);
}
GNUNET_log (GNUNET_ERROR_TYPE_INFO,
@@ -481,7 +1131,6 @@ handle_mhd_request (void *cls,
/* on repeated requests, check our cache first */
if (NULL != rc->rh)
{
- MHD_RESULT ret;
const char *start;
if ('\0' == url[0])
@@ -490,11 +1139,10 @@ handle_mhd_request (void *cls,
start = strchr (url + 1, '/');
if (NULL == start)
start = "";
- ret = proceed_with_handler (rc,
- start,
- upload_data,
- upload_data_size);
- return ret;
+ return proceed_with_handler (rc,
+ start,
+ upload_data,
+ upload_data_size);
}
if (0 == strcasecmp (method,
MHD_HTTP_METHOD_HEAD))
@@ -532,25 +1180,25 @@ handle_mhd_request (void *cls,
continue;
found = true;
/* The URL is a match! What we now do depends on the method. */
- if (0 == strcasecmp (method, MHD_HTTP_METHOD_OPTIONS))
+ if (0 == strcasecmp (method,
+ MHD_HTTP_METHOD_OPTIONS))
{
return TALER_MHD_reply_cors_preflight (connection);
}
GNUNET_assert (NULL != rh->method);
- if (0 == strcasecmp (method,
+ if (0 != strcasecmp (method,
rh->method))
{
- MHD_RESULT ret;
-
- /* cache to avoid the loop next time */
- rc->rh = rh;
- /* run handler */
- ret = proceed_with_handler (rc,
- url + tok_size + 1,
- upload_data,
- upload_data_size);
- return ret;
+ found = true;
+ continue;
}
+ /* cache to avoid the loop next time */
+ rc->rh = rh;
+ GNUNET_log (GNUNET_ERROR_TYPE_INFO,
+ "Handler found for %s '%s'\n",
+ method,
+ url);
+ return MHD_YES;
}
if (found)
@@ -614,15 +1262,10 @@ handle_mhd_request (void *cls,
}
/* No handler matches, generate not found */
- {
- MHD_RESULT ret;
-
- ret = TALER_MHD_reply_with_error (connection,
- MHD_HTTP_NOT_FOUND,
- TALER_EC_GENERIC_ENDPOINT_UNKNOWN,
- url);
- return ret;
- }
+ return TALER_MHD_reply_with_error (connection,
+ MHD_HTTP_NOT_FOUND,
+ TALER_EC_GENERIC_ENDPOINT_UNKNOWN,
+ url);
}
@@ -668,8 +1311,25 @@ 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)
+ {
+ ih_logic->initiate_cancel (ih);
+ ih = NULL;
+ }
+ kyc_webhook_cleanup ();
+ TALER_KYCLOGIC_kyc_done ();
mhd = TALER_MHD_daemon_stop ();
if (NULL != mhd)
MHD_stop_daemon (mhd);
@@ -683,6 +1343,74 @@ do_shutdown (void *cls)
GNUNET_CURL_gnunet_rc_destroy (exchange_curl_rc);
exchange_curl_rc = NULL;
}
+ TALER_TEMPLATING_done ();
+}
+
+
+/**
+ * Function called with the result of a KYC initiation
+ * operation.
+ *
+ * @param cls closure
+ * @param ec #TALER_EC_NONE on success
+ * @param redirect_url set to where to redirect the user on success, NULL on failure
+ * @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 error_msg_hint set to additional details to return to user, NULL on success
+ */
+static void
+initiate_cb (
+ void *cls,
+ enum TALER_ErrorCode ec,
+ const char *redirect_url,
+ const char *provider_user_id,
+ const char *provider_legitimization_id,
+ const char *error_msg_hint)
+{
+ (void) cls;
+ ih = NULL;
+ if (TALER_EC_NONE != ec)
+ {
+ fprintf (stderr,
+ "Failed to start KYC process: %s (#%d)\n",
+ error_msg_hint,
+ ec);
+ global_ret = EXIT_FAILURE;
+ GNUNET_SCHEDULER_shutdown ();
+ return;
+ }
+ {
+ char *s;
+
+ s = GNUNET_STRINGS_data_to_string_alloc (&cmd_line_h_payto,
+ sizeof (cmd_line_h_payto));
+ if (NULL != provider_user_id)
+ {
+ fprintf (stdout,
+ "Visit `%s' to begin KYC process.\nAlso use: taler-exchange-kyc-tester -w -u '%s' -U '%s' -p %s\n",
+ redirect_url,
+ provider_user_id,
+ provider_legitimization_id,
+ s);
+ }
+ else
+ {
+ fprintf (stdout,
+ "Visit `%s' to begin KYC process.\nAlso use: taler-exchange-kyc-tester -w -U '%s' -p %s\n",
+ redirect_url,
+ provider_legitimization_id,
+ s);
+ }
+ GNUNET_free (s);
+ }
+ 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);
+ if (! run_webservice)
+ GNUNET_SCHEDULER_shutdown ();
}
@@ -706,63 +1434,130 @@ run (void *cls,
(void) cls;
(void) args;
(void ) cfgfile;
- TALER_MHD_setup (TALER_MHD_GO_NONE);
- TEKT_cfg = config;
-
if (GNUNET_OK !=
- exchange_serve_process_config ())
+ TALER_TEMPLATING_init ("exchange"))
{
- global_ret = EXIT_NOTCONFIGURED;
- GNUNET_SCHEDULER_shutdown ();
+ GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
+ "Could not load templates. Installation broken.\n");
return;
}
- TEKT_curl_ctx
- = GNUNET_CURL_init (&GNUNET_CURL_gnunet_scheduler_reschedule,
- &exchange_curl_rc);
- if (NULL == TEKT_curl_ctx)
+ if (print_h_payto)
{
- GNUNET_break (0);
- global_ret = EXIT_FAILURE;
- GNUNET_SCHEDULER_shutdown ();
- return;
+ char *s;
+
+ s = GNUNET_STRINGS_data_to_string_alloc (&cmd_line_h_payto,
+ sizeof (cmd_line_h_payto));
+ fprintf (stdout,
+ "%s\n",
+ s);
+ GNUNET_free (s);
}
- exchange_curl_rc = GNUNET_CURL_gnunet_rc_create (TEKT_curl_ctx);
+ TALER_MHD_setup (TALER_MHD_GO_NONE);
+ TEKT_cfg = config;
GNUNET_SCHEDULER_add_shutdown (&do_shutdown,
NULL);
- fh = TALER_MHD_bind (TEKT_cfg,
- "exchange",
- &serve_port);
- if ( (0 == serve_port) &&
- (-1 == fh) )
+ if (GNUNET_OK !=
+ TALER_KYCLOGIC_kyc_init (config))
{
+ global_ret = EXIT_NOTCONFIGURED;
GNUNET_SCHEDULER_shutdown ();
return;
}
- mhd = MHD_start_daemon (MHD_USE_SUSPEND_RESUME
- | MHD_USE_PIPE_FOR_SHUTDOWN
- | MHD_USE_DEBUG | MHD_USE_DUAL_STACK
- | MHD_USE_TCP_FASTOPEN,
- (-1 == fh) ? serve_port : 0,
- NULL, NULL,
- &handle_mhd_request, NULL,
- MHD_OPTION_LISTEN_SOCKET,
- fh,
- MHD_OPTION_EXTERNAL_LOGGER,
- &TALER_MHD_handle_logs,
- NULL,
- MHD_OPTION_NOTIFY_COMPLETED,
- &handle_mhd_completion_callback,
- NULL,
- MHD_OPTION_END);
- if (NULL == mhd)
+ if (GNUNET_OK !=
+ exchange_serve_process_config ())
{
- GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
- "Failed to launch HTTP service. Is the port in use?\n");
+ global_ret = EXIT_NOTCONFIGURED;
GNUNET_SCHEDULER_shutdown ();
return;
}
global_ret = EXIT_SUCCESS;
- TALER_MHD_daemon_start (mhd);
+ if (NULL != requirements)
+ {
+ struct TALER_KYCLOGIC_ProviderDetails *pd;
+ enum TALER_KYCLOGIC_KycUserType ut;
+
+ if (GNUNET_OK !=
+ TALER_KYCLOGIC_kyc_user_type_from_string (ut_s,
+ &ut))
+ {
+ GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
+ "Invalid user type specified ('-i')\n");
+ global_ret = EXIT_INVALIDARGUMENT;
+ GNUNET_SCHEDULER_shutdown ();
+ return;
+ }
+ if (GNUNET_OK !=
+ TALER_KYCLOGIC_requirements_to_logic (requirements,
+ ut,
+ &ih_logic,
+ &pd,
+ &provider_section_name))
+ {
+ GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
+ "Could not initiate KYC for requirements `%s' (configuration error?)\n",
+ requirements);
+ global_ret = EXIT_NOTCONFIGURED;
+ GNUNET_SCHEDULER_shutdown ();
+ return;
+ }
+ 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)
+ {
+ TEKT_curl_ctx
+ = GNUNET_CURL_init (&GNUNET_CURL_gnunet_scheduler_reschedule,
+ &exchange_curl_rc);
+ if (NULL == TEKT_curl_ctx)
+ {
+ GNUNET_break (0);
+ global_ret = EXIT_FAILURE;
+ GNUNET_SCHEDULER_shutdown ();
+ return;
+ }
+ exchange_curl_rc = GNUNET_CURL_gnunet_rc_create (TEKT_curl_ctx);
+ fh = TALER_MHD_bind (TEKT_cfg,
+ "exchange",
+ &serve_port);
+ if ( (0 == serve_port) &&
+ (-1 == fh) )
+ {
+ GNUNET_SCHEDULER_shutdown ();
+ return;
+ }
+ GNUNET_log (GNUNET_ERROR_TYPE_INFO,
+ "Starting daemon on port %u\n",
+ (unsigned int) serve_port);
+ mhd = MHD_start_daemon (MHD_USE_SUSPEND_RESUME
+ | MHD_USE_PIPE_FOR_SHUTDOWN
+ | MHD_USE_DEBUG | MHD_USE_DUAL_STACK
+ | MHD_USE_TCP_FASTOPEN,
+ (-1 == fh) ? serve_port : 0,
+ NULL, NULL,
+ &handle_mhd_request, NULL,
+ MHD_OPTION_LISTEN_SOCKET,
+ fh,
+ MHD_OPTION_EXTERNAL_LOGGER,
+ &TALER_MHD_handle_logs,
+ NULL,
+ MHD_OPTION_NOTIFY_COMPLETED,
+ &handle_mhd_completion_callback,
+ NULL,
+ MHD_OPTION_END);
+ if (NULL == mhd)
+ {
+ GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
+ "Failed to launch HTTP service. Is the port in use?\n");
+ GNUNET_SCHEDULER_shutdown ();
+ return;
+ }
+ TALER_MHD_daemon_start (mhd);
+ }
}
@@ -780,11 +1575,61 @@ main (int argc,
const struct GNUNET_GETOPT_CommandLineOption options[] = {
GNUNET_GETOPT_option_help (
"tool to test KYC provider integrations"),
+ GNUNET_GETOPT_option_flag (
+ 'P',
+ "print-payto-hash",
+ "output the hash of the payto://-URI",
+ &print_h_payto),
+ GNUNET_GETOPT_option_uint (
+ 'r',
+ "rowid",
+ "NUMBER",
+ "override row ID to use in simulation (default: 42)",
+ &kyc_row_id),
+ GNUNET_GETOPT_option_flag (
+ 'w',
+ "run-webservice",
+ "run the integrated HTTP service",
+ &run_webservice),
+ GNUNET_GETOPT_option_string (
+ 'R',
+ "requirements",
+ "CHECKS",
+ "initiate KYC check for the given list of (space-separated) checks",
+ &requirements),
+ GNUNET_GETOPT_option_string (
+ 'i',
+ "identify",
+ "USERTYPE",
+ "self-identify as USERTYPE 'business' or 'individual' (defaults to 'individual')",
+ &requirements),
+ 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 (
+ 'U',
+ "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",
+ "HASH",
+ "base32 encoding of the hash of a payto://-URI to use for the account (otherwise a random value will be used)",
+ &cmd_line_h_payto,
+ sizeof (cmd_line_h_payto)),
GNUNET_GETOPT_OPTION_END
};
enum GNUNET_GenericReturnValue ret;
TALER_OS_init ();
+ GNUNET_CRYPTO_random_block (GNUNET_CRYPTO_QUALITY_NONCE,
+ &cmd_line_h_payto,
+ sizeof (cmd_line_h_payto));
ret = GNUNET_PROGRAM_run (argc, argv,
"taler-exchange-kyc-tester",
"tool to test KYC provider integrations",