exchange

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

commit 1d17a50c4d5858e737f9f156db664cc0b5772836
parent d34edb354a0dca17069a29ed61561d1765f60fc9
Author: Christian Grothoff <christian@grothoff.org>
Date:   Sun, 14 Dec 2025 15:08:59 +0100

enforce FORM_ID is present in all attributes (#10757)

Diffstat:
Msrc/exchange/taler-exchange-httpd_aml-decision.c | 12++++++++++++
Msrc/exchange/taler-exchange-httpd_common_kyc.c | 1+
Msrc/exchange/taler-exchange-httpd_config.h | 2+-
Msrc/exchange/taler-exchange-httpd_kyc-upload.c | 11+++++++++++
Msrc/exchange/taler-exchange-httpd_reserves_attest.c | 3++-
Msrc/include/taler/taler_kyclogic_plugin.h | 4++--
Msrc/kyclogic/plugin_kyclogic_kycaid.c | 30++++++++++++++++++++++++++++++
Msrc/kyclogic/plugin_kyclogic_oauth2.c | 33+++++++++++++++++++++++++++++++++
Msrc/kyclogic/plugin_kyclogic_persona.c | 38++++++++++++++++++++++++++++++++++++++
Msrc/kyclogic/taler-exchange-kyc-kycaid-converter.sh | 4++--
Msrc/kyclogic/taler-exchange-kyc-oauth2-challenger.sh | 2+-
Msrc/kyclogic/taler-exchange-kyc-oauth2-nda.sh | 2+-
Msrc/kyclogic/taler-exchange-kyc-oauth2-test-converter.sh | 2+-
Msrc/kyclogic/taler-exchange-kyc-persona-converter.sh | 2+-
14 files changed, 136 insertions(+), 10 deletions(-)

diff --git a/src/exchange/taler-exchange-httpd_aml-decision.c b/src/exchange/taler-exchange-httpd_aml-decision.c @@ -273,6 +273,18 @@ TEH_handler_post_aml_decision ( ret = MHD_YES /* failure */; goto done; } + if ( (NULL != attributes) && + (! json_is_string (json_object_get (attributes, + "FORM_ID"))) ) + { + GNUNET_break_op (0); + ret = TALER_MHD_reply_with_error ( + connection, + MHD_HTTP_BAD_REQUEST, + TALER_EC_GENERIC_PARAMETER_MALFORMED, + "attributes['FORM_ID']"); + goto done; + } } if (NULL != payto_uri.full_payto) { diff --git a/src/exchange/taler-exchange-httpd_common_kyc.c b/src/exchange/taler-exchange-httpd_common_kyc.c @@ -337,6 +337,7 @@ TEH_kyc_store_attributes ( { form_id = json_string_value (json_object_get (new_attributes, "FORM_ID")); + GNUNET_assert (NULL != form_id); TALER_CRYPTO_kyc_attributes_encrypt (&TEH_attribute_key, new_attributes, &ea, diff --git a/src/exchange/taler-exchange-httpd_config.h b/src/exchange/taler-exchange-httpd_config.h @@ -41,7 +41,7 @@ * * Returned via both /config and /keys endpoints. */ -#define EXCHANGE_PROTOCOL_VERSION "31:0:9" +#define EXCHANGE_PROTOCOL_VERSION "31:1:9" /** diff --git a/src/exchange/taler-exchange-httpd_kyc-upload.c b/src/exchange/taler-exchange-httpd_kyc-upload.c @@ -496,6 +496,17 @@ TEH_handler_kyc_upload ( } GNUNET_log (GNUNET_ERROR_TYPE_INFO, "/kyc-upload received form submission\n"); + if ( (NULL != root) && + (! json_is_string (json_object_get (root, + "FORM_ID"))) ) + { + GNUNET_break_op (0); + return TALER_MHD_reply_with_error ( + rc->connection, + MHD_HTTP_BAD_REQUEST, + TALER_EC_GENERIC_PARAMETER_MALFORMED, + "FORM_ID"); + } json_dumpf (root, stderr, JSON_INDENT (2)); diff --git a/src/exchange/taler-exchange-httpd_reserves_attest.c b/src/exchange/taler-exchange-httpd_reserves_attest.c @@ -190,7 +190,8 @@ kyc_process_cb (void *cls, } json_object_foreach (attrs, name, val) { - bool requested = false; + bool requested = strcmp (name, + "FORM_ID"); /* we always return the FORM_ID */ size_t idx; json_t *str; diff --git a/src/include/taler/taler_kyclogic_plugin.h b/src/include/taler/taler_kyclogic_plugin.h @@ -164,7 +164,7 @@ typedef void * @param provider_name name of the provider with the attributes * @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 attributes user attributes returned by the provider + * @param attributes user attributes returned by the provider, MUST include "FORM_ID" of type String, or NULL on error * @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 @@ -197,7 +197,7 @@ typedef void * @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 attributes user attributes returned by the provider, MUST include "FORM_ID" of type String (or NULL on error) * @param http_status HTTP status code of @a response * @param[in] response to return to the HTTP client */ diff --git a/src/kyclogic/plugin_kyclogic_kycaid.c b/src/kyclogic/plugin_kyclogic_kycaid.c @@ -903,6 +903,36 @@ webhook_conversion_cb (void *cls, kycaid_webhook_cancel (wh); return; } + if (! json_is_string (json_object_get (result, + "FORM_ID"))) + { + /* Failure in our helper */ + GNUNET_log (GNUNET_ERROR_TYPE_WARNING, + "Mandatory FORM_ID not set in result\n"); + json_dumpf (result, + stderr, + JSON_INDENT (2)); + resp = TALER_MHD_MAKE_JSON_PACK ( + GNUNET_JSON_pack_uint64 ("kycaid_http_status", + wh->kycaid_response_code), + GNUNET_JSON_pack_object_incref ("kycaid_body", + (json_t *) wh->json_response)); + wh->cb (wh->cb_cls, + wh->process_row, + &wh->h_payto, + wh->is_wallet, + wh->pd->section, + wh->applicant_id, + wh->verification_id, + TALER_KYCLOGIC_STATUS_PROVIDER_FAILED, + GNUNET_TIME_UNIT_ZERO_ABS, /* expiration */ + NULL, + MHD_HTTP_BAD_GATEWAY, + resp); + kycaid_webhook_cancel (wh); + return; + } + expiration = GNUNET_TIME_relative_to_absolute (wh->pd->validity); resp = MHD_create_response_from_buffer_static (0, ""); diff --git a/src/kyclogic/plugin_kyclogic_oauth2.c b/src/kyclogic/plugin_kyclogic_oauth2.c @@ -1181,6 +1181,39 @@ converted_proof_cb (void *cls, } ph->provider_user_id = GNUNET_strdup (id); } + if (! json_is_string (json_object_get (attr, + "FORM_ID"))) + { + json_t *body; + + GNUNET_break_op (0); + ph->status = TALER_KYCLOGIC_STATUS_PROVIDER_FAILED; + ph->http_status = MHD_HTTP_BAD_GATEWAY; + body = GNUNET_JSON_PACK ( + GNUNET_JSON_pack_string ("converter", + pd->conversion_binary), + GNUNET_JSON_pack_string ("message", + "Missing 'FORM_ID' field in attributes"), + GNUNET_JSON_pack_bool ("debug", + ph->pd->debug_mode), + GNUNET_JSON_pack_object_incref ("attributes", + (json_t *) attr), + TALER_JSON_pack_ec ( + TALER_EC_EXCHANGE_KYC_PROOF_BACKEND_INVALID_RESPONSE)); + GNUNET_break ( + GNUNET_SYSERR != + TALER_TEMPLATING_build (ph->connection, + &ph->http_status, + "oauth2-conversion-failure", + NULL, + NULL, + body, + &ph->response)); + json_decref (body); + ph->task = GNUNET_SCHEDULER_add_now (&return_proof_response, + ph); + return; + } ph->status = TALER_KYCLOGIC_STATUS_SUCCESS; ph->response = MHD_create_response_from_buffer_static (0, ""); diff --git a/src/kyclogic/plugin_kyclogic_persona.c b/src/kyclogic/plugin_kyclogic_persona.c @@ -354,6 +354,11 @@ struct TALER_KYCLOGIC_WebhookHandle uint64_t process_row; /** + * HTTP status returned by Persona to us. + */ + unsigned int persona_http_status; + + /** * HTTP response code to return asynchronously. */ unsigned int response_code; @@ -1635,6 +1640,38 @@ webhook_post_conversion_cb (void *cls, struct TALER_KYCLOGIC_WebhookHandle *wh = cls; wh->ec = NULL; + if (! json_is_string (json_object_get (attr, + "FORM_ID"))) + { + struct MHD_Response *resp; + + /* Failure in our helper */ + GNUNET_log (GNUNET_ERROR_TYPE_WARNING, + "Mandatory FORM_ID not set in result\n"); + json_dumpf (attr, + stderr, + JSON_INDENT (2)); + resp = TALER_MHD_MAKE_JSON_PACK ( + GNUNET_JSON_pack_uint64 ("persona_http_status", + wh->persona_http_status), + GNUNET_JSON_pack_object_incref ("persona_body", + (json_t *) attr)); + wh->cb (wh->cb_cls, + wh->process_row, + &wh->h_payto, + wh->is_wallet, + wh->pd->section, + NULL, + wh->inquiry_id, + TALER_KYCLOGIC_STATUS_PROVIDER_FAILED, + GNUNET_TIME_UNIT_ZERO_ABS, /* expiration */ + NULL, + MHD_HTTP_BAD_GATEWAY, + resp); + persona_webhook_cancel (wh); + return; + } + webhook_generic_reply (wh, TALER_KYCLOGIC_STATUS_SUCCESS, wh->account_id, @@ -1663,6 +1700,7 @@ handle_webhook_finished (void *cls, "data"); wh->job = NULL; + wh->persona_http_status = response_code; switch (response_code) { case MHD_HTTP_OK: diff --git a/src/kyclogic/taler-exchange-kyc-kycaid-converter.sh b/src/kyclogic/taler-exchange-kyc-kycaid-converter.sh @@ -73,7 +73,7 @@ then echo "$J" \ | jq \ --arg full_name "${FULLNAME}" \ - '{$full_name,"birthdate":.dob,"pep":.pep,"phone":.phone,"email":.email,"residences":.residence_country}' \ + '{"FORM_ID":"kycaid-individual",$full_name,"birthdate":.dob,"pep":.pep,"phone":.phone,"email":.email,"residences":.residence_country}' \ | jq \ 'del(..|select(.==null))' @@ -82,7 +82,7 @@ else echo "$J" \ | jq \ "$DOCS_RAW" \ - "{\"company_name\":.company_name,\"phone\":.phone,\"email\":.email,\"registration_country\":.registration_country,\"documents\":[${DOCS_JSON}]}" \ + '{"FORM_ID":"kycaid-business","company_name":.company_name,"phone":.phone,"email":.email,"registration_country":.registration_country,"documents":['${DOCS_JSON}']}' \ | jq \ 'del(..|select(.==null))' fi diff --git a/src/kyclogic/taler-exchange-kyc-oauth2-challenger.sh b/src/kyclogic/taler-exchange-kyc-oauth2-challenger.sh @@ -22,6 +22,6 @@ fi echo "$J" \ | jq \ --arg id "${ROWID}" \ - '{$id,"email":.email,"expires":.expires}' + '{"FORM_ID":"challenger-email",$id,"email":.email,"expires":.expires}' exit 0 diff --git a/src/kyclogic/taler-exchange-kyc-oauth2-nda.sh b/src/kyclogic/taler-exchange-kyc-oauth2-nda.sh @@ -23,7 +23,7 @@ FULLNAME=$(echo "$J" | jq -r '[.first_name,.last_name]|join(" ")') echo "$J" \ | jq \ --arg full_name "${FULLNAME}" \ - '{FULL_NAME: $full_name, "phone": .phone, "id": .id}' \ + '{"FORM_ID": "oauth2-nda-form","FULL_NAME": $full_name, "phone": .phone, "id": .id}' \ | jq \ 'del(..|select(.==null))' diff --git a/src/kyclogic/taler-exchange-kyc-oauth2-test-converter.sh b/src/kyclogic/taler-exchange-kyc-oauth2-test-converter.sh @@ -26,7 +26,7 @@ FULLNAME=$(echo "$J" | jq -r '[.first,.last]|join(" ")') echo "$J" \ | jq \ --arg full_name "${FULLNAME}" \ - '{FORM_ID:"test",FULL_NAME: $full_name,"DATE_OF_BIRTH":.birthdate,"id":.id,}' \ + '{"FORM_ID":"test","FULL_NAME": $full_name,"DATE_OF_BIRTH":.birthdate,"id":.id,}' \ | jq \ 'del(..|select(.==null))' exit 0 diff --git a/src/kyclogic/taler-exchange-kyc-persona-converter.sh b/src/kyclogic/taler-exchange-kyc-persona-converter.sh @@ -50,7 +50,7 @@ echo "$J" \ --arg street "${STREET}" \ --arg city "${CITY}" \ --rawfile photo "${PHOTO_FILE}" \ - '{FULL_NAME: $full_name, $street, $city, DATE_OF_BIRTH: .birthdate, "residences":.cc, "identification_number": ."identification-number", $photo}' \ + '{"FORM_ID":"persona-individual",FULL_NAME: $full_name, $street, $city, DATE_OF_BIRTH: .birthdate, "residences":.cc, "identification_number": ."identification-number", $photo}' \ | jq \ 'del(..|select(.==null))'