exchange

Base system with REST service to issue digital coins, run by the payment service provider
Log | Files | Refs | Submodules | README | LICENSE

commit 7467b99c3c29ab607a5b476a3c93821d8d3a26d4
parent de59cf30a257dd7f7ca68306741eb6dea6046722
Author: Christian Grothoff <christian@grothoff.org>
Date:   Wed,  9 Apr 2025 13:18:51 +0200

implement JSON support for kyc-upload (fixes #9715)

Diffstat:
Msrc/exchange/taler-exchange-httpd.c | 57+++++++--------------------------------------------------
Msrc/exchange/taler-exchange-httpd_kyc-upload.c | 235++-----------------------------------------------------------------------------
Msrc/exchange/taler-exchange-httpd_kyc-upload.h | 11++++-------
Msrc/testing/test_kyc_api.c | 4++--
4 files changed, 18 insertions(+), 289 deletions(-)

diff --git a/src/exchange/taler-exchange-httpd.c b/src/exchange/taler-exchange-httpd.c @@ -1837,6 +1837,13 @@ handle_mhd_request (void *cls, .nargs = 1 }, { + .url = "kyc-upload", + .method = MHD_HTTP_METHOD_POST, + .handler.post = &TEH_handler_kyc_upload, + .nargs = 1, + .nargs_is_upper_bound = true + }, + { .url = "kyc-webhook", .method = MHD_HTTP_METHOD_POST, .handler.post = &TEH_handler_kyc_webhook_post, @@ -2118,56 +2125,6 @@ handle_mhd_request (void *cls, return ret; } } - /* FIXME: Why is this handled separately? Fix or justify! */ - if (0 == strncmp (url, - "/kyc-upload/", - strlen ("/kyc-upload/"))) - { - if (0 == strcasecmp (method, - MHD_HTTP_METHOD_OPTIONS)) - { - GNUNET_async_scope_restore (&old_scope); - return TALER_MHD_reply_cors_preflight (connection); - } - if (0 != strcasecmp (method, - MHD_HTTP_METHOD_POST)) - { - MHD_RESULT ret; - struct MHD_Response *reply; - - reply = TALER_MHD_make_error (TALER_EC_GENERIC_METHOD_INVALID, - method); - GNUNET_break (MHD_YES == - MHD_add_response_header (reply, - MHD_HTTP_HEADER_ALLOW, - MHD_HTTP_METHOD_POST)); - ret = MHD_queue_response (connection, - MHD_HTTP_METHOD_NOT_ALLOWED, - reply); - MHD_destroy_response (reply); - GNUNET_async_scope_restore (&old_scope); - return ret; - } - { - MHD_RESULT ret; - - ret = TEH_handler_kyc_upload (rc, - &url[strlen ("/kyc-upload/")], - upload_data_size, - upload_data); - if (GNUNET_OK != - TEH_plugin->preflight (TEH_plugin->cls)) - { - GNUNET_log (GNUNET_ERROR_TYPE_ERROR, - "Handler %s left open database transaction behind!\n", - url); - GNUNET_assert (0); - } - GNUNET_async_scope_restore (&old_scope); - return ret; - } - } - /* No handler matches, generate not found */ { diff --git a/src/exchange/taler-exchange-httpd_kyc-upload.c b/src/exchange/taler-exchange-httpd_kyc-upload.c @@ -22,14 +22,6 @@ #include "taler-exchange-httpd_common_kyc.h" #include "taler-exchange-httpd_kyc-upload.h" -/** - * Size of the MHD post processor upload buffer. Rather large, as we expect - * large documents. Note that we *additionally* do stream processing, so the - * actual uploads can be larger and are bounded (globally) by - * #TALER_MHD_REQUEST_BUFFER_MAX. - */ -#define UPLOAD_BUFFER_SIZE (1024 * 1024) - /** * Context used for processing the KYC upload req @@ -74,11 +66,6 @@ struct UploadContext struct MHD_Response *response; /** - * Our post processor. - */ - struct MHD_PostProcessor *pp; - - /** * Request we are processing. */ struct TEH_RequestContext *rc; @@ -91,37 +78,7 @@ struct UploadContext /** * Uploaded data, in JSON. */ - json_t *result; - - /** - * Last key in upload. - */ - char *last_key; - - /** - * Name of the file, possibly NULL. - */ - char *filename; - - /** - * Content type, possibly NULL. - */ - char *content_type; - - /** - * Current upload data. - */ - char *curr_buf; - - /** - * Size of @e curr_buf allocation. - */ - size_t buf_size; - - /** - * Number of bytes of actual data in @a curr_buf. - */ - size_t buf_pos; + const json_t *result; }; @@ -153,81 +110,6 @@ TEH_kyc_upload_cleanup () /** - * Check if the upload data is in binary and thus - * must be base32-encoded. - * - * @param uc upload context with data to eval - */ -static bool -is_binary (const struct UploadContext *uc) -{ - if (NULL != memchr (uc->curr_buf, - '\0', - uc->buf_pos)) - return true; - if (NULL != uc->filename) - return true; /* we always encode all files */ - if (NULL == uc->content_type) - return false; /* fingers crossed */ - if (0 == strncmp (uc->content_type, - "text/", - strlen ("text/"))) - return false; /* good */ - return true; -} - - -/** - * Finish processing the data in @a uc under the current - * key. - * - * @param[in,out] uc upload context with key to process - */ -static void -finish_key (struct UploadContext *uc) -{ - json_t *val; - - if (NULL == uc->last_key) - return; /* nothing to do */ - if (is_binary (uc)) - { - val = GNUNET_JSON_PACK ( - GNUNET_JSON_pack_allow_null ( - GNUNET_JSON_pack_string ("filename", - uc->filename)), - GNUNET_JSON_pack_allow_null ( - GNUNET_JSON_pack_string ("content_type", - uc->content_type)), - GNUNET_JSON_pack_data_varsize ("data", - uc->curr_buf, - uc->buf_pos) - ); - } - else - { - val = GNUNET_JSON_PACK ( - GNUNET_JSON_pack_allow_null ( - GNUNET_JSON_pack_string ("content_type", - uc->content_type)), - GNUNET_JSON_pack_string ("text", - uc->curr_buf)); - } - GNUNET_assert (0 == - json_object_set_new (uc->result, - uc->last_key, - val)); - GNUNET_free (uc->last_key); - GNUNET_free (uc->filename); - GNUNET_free (uc->content_type); - memset (uc->curr_buf, - 0, - uc->buf_pos); - uc->buf_pos = 0; -} - - -/** * Function called to clean up upload context. * * @param[in,out] rc context to clean up @@ -247,89 +129,11 @@ upload_cleaner (struct TEH_RequestContext *rc) MHD_destroy_response (uc->response); uc->response = NULL; } - MHD_destroy_post_processor (uc->pp); - GNUNET_free (uc->filename); - GNUNET_free (uc->content_type); - GNUNET_free (uc->last_key); - GNUNET_free (uc->curr_buf); - json_decref (uc->result); GNUNET_free (uc); } /** - * Iterator over key-value pairs where the value may be made available in - * increments and/or may not be zero-terminated. Used for processing POST - * data. - * - * @param cls user-specified closure - * @param kind type of the value, always #MHD_POSTDATA_KIND when called from MHD - * @param key 0-terminated key for the value, NULL if not known. This value - * is never NULL for url-encoded POST data. - * @param filename name of the uploaded file, NULL if not known - * @param content_type mime-type of the data, NULL if not known - * @param transfer_encoding encoding of the data, NULL if not known - * @param data pointer to @a size bytes of data at the - * specified offset - * @param off offset of data in the overall value - * @param size number of bytes in @a data available - * @return #MHD_YES to continue iterating, - * #MHD_NO to abort the iteration - */ -static enum MHD_Result -post_helper (void *cls, - enum MHD_ValueKind kind, - const char *key, - const char *filename, - const char *content_type, - const char *transfer_encoding, - const char *data, - uint64_t off, - size_t size) -{ - struct UploadContext *uc = cls; - - if ( (NULL != uc->last_key) && - (0 != strcmp (key, - uc->last_key)) ) - finish_key (uc); - if (NULL == uc->last_key) - { - uc->last_key = GNUNET_strdup (key); - if (NULL != filename) - uc->filename = GNUNET_strdup (filename); - if (NULL != content_type) - uc->content_type = GNUNET_strdup (content_type); - } - if (size > uc->buf_size - uc->buf_pos) - { - char *tmp; - size_t tgt; - - tgt = uc->buf_size * 2; - if (tgt >= GNUNET_MAX_MALLOC_CHECKED - 1) - tgt = GNUNET_MAX_MALLOC_CHECKED - 1; - if (tgt < size + uc->buf_pos) - tgt = size + uc->buf_pos; - if (tgt >= GNUNET_MAX_MALLOC_CHECKED - 1) - return MHD_NO; - tmp = GNUNET_malloc (tgt + 1); /* for 0-termination */ - memcpy (tmp, - uc->curr_buf, - uc->buf_pos); - GNUNET_free (uc->curr_buf); - uc->buf_size = tgt; - uc->curr_buf = tmp; - } - memcpy (uc->curr_buf + uc->buf_pos, - data, - size); - uc->buf_pos += size; - return MHD_YES; -} - - -/** * Function called after the KYC-AML trigger is done. * * @param cls closure @@ -380,11 +184,11 @@ aml_trigger_callback ( MHD_RESULT TEH_handler_kyc_upload ( struct TEH_RequestContext *rc, - const char *id, - size_t *upload_data_size, - const char *upload_data) + const json_t *root, + const char *const args[1]) { struct UploadContext *uc = rc->rh_ctx; + const char *id = args[0]; if (NULL == uc) { @@ -393,26 +197,9 @@ TEH_handler_kyc_upload ( uc = GNUNET_new (struct UploadContext); uc->rc = rc; - uc->pp = MHD_create_post_processor ( - rc->connection, - UPLOAD_BUFFER_SIZE, - &post_helper, - uc); - if (NULL == uc->pp) - { - GNUNET_break (0); - GNUNET_free (uc); - return TALER_MHD_reply_with_error ( - rc->connection, - MHD_HTTP_INTERNAL_SERVER_ERROR, - TALER_EC_GENERIC_ALLOCATION_FAILURE, - "MHD_create_post_processor"); - } - uc->result = json_object (); - GNUNET_assert (NULL != uc->result); + uc->result = root; rc->rh_ctx = uc; rc->rh_cleaner = &upload_cleaner; - slash = strchr (id, '-'); if (NULL == slash) { @@ -459,18 +246,6 @@ TEH_handler_kyc_upload ( uc->response); } - if (0 != *upload_data_size) - { - MHD_RESULT mres; - - mres = MHD_post_process (uc->pp, - upload_data, - *upload_data_size); - *upload_data_size = 0; - return mres; - } - finish_key (uc); - GNUNET_assert (NULL == uc->kat); { uint64_t legi_process_row; diff --git a/src/exchange/taler-exchange-httpd_kyc-upload.h b/src/exchange/taler-exchange-httpd_kyc-upload.h @@ -40,17 +40,14 @@ TEH_kyc_upload_cleanup (void); * Handle a "/kyc-upload/$ID" request. * * @param rc request context - * @param id the ID from the URL (without "/") - * @param[in,out] upload_data_size length of @a upload_data, - * to be update to reflect number of bytes remaining - * @param upload_data upload data of the POST, if any + * @param root json body being uploaded + * @param args includes the ID from the URL (without "/") * @return MHD result code */ MHD_RESULT TEH_handler_kyc_upload (struct TEH_RequestContext *rc, - const char *id, - size_t *upload_data_size, - const char *upload_data); + const json_t *root, + const char *const args[1]); #endif diff --git a/src/testing/test_kyc_api.c b/src/testing/test_kyc_api.c @@ -770,8 +770,8 @@ run (void *cls, "wallet-post-kyc-form", "get-kyc-info-form", 0, /* requirement index */ - "application/x-www-form-urlencoded", - "full_name=Bob&birthdate=1990-00-00", + "application/json", + "{\"full_name\":\"Bob\",\"birthdate\":\"1990-00-00\"}", MHD_HTTP_NO_CONTENT), /* now this should be allowed */ TALER_TESTING_cmd_wallet_kyc_get (