diff options
-rw-r--r-- | contrib/Makefile.am.in | 12 | ||||
-rw-r--r-- | contrib/kyc-proof-already-done.en.must | 9 | ||||
-rw-r--r-- | contrib/kyc-proof-bad-request.en.must | 15 | ||||
-rw-r--r-- | contrib/kyc-proof-endpoint-unknown.en.must | 15 | ||||
-rw-r--r-- | contrib/kyc-proof-internal-error.en.must | 16 | ||||
-rw-r--r-- | contrib/kyc-proof-logic-failure.en.must | 15 | ||||
-rw-r--r-- | contrib/kyc-proof-target-unknown.en.must | 15 | ||||
-rw-r--r-- | contrib/kycaid-invalid-request.en.must | 12 | ||||
-rw-r--r-- | contrib/oauth2-authorization-failure-malformed.en.must | 13 | ||||
-rw-r--r-- | contrib/oauth2-authorization-failure.en.must | 12 | ||||
-rw-r--r-- | contrib/oauth2-bad-request.en.must | 15 | ||||
-rw-r--r-- | contrib/oauth2-conversion-failure.en.must | 28 | ||||
-rw-r--r-- | contrib/oauth2-provider-failure.en.must | 22 | ||||
-rw-r--r-- | src/exchange/taler-exchange-httpd_kyc-proof.c | 119 | ||||
-rw-r--r-- | src/kyclogic/Makefile.am | 3 | ||||
-rw-r--r-- | src/kyclogic/plugin_kyclogic_kycaid.c | 23 | ||||
-rw-r--r-- | src/kyclogic/plugin_kyclogic_oauth2.c | 382 |
17 files changed, 568 insertions, 158 deletions
diff --git a/contrib/Makefile.am.in b/contrib/Makefile.am.in index 5f9c93d5e..e31f4274f 100644 --- a/contrib/Makefile.am.in +++ b/contrib/Makefile.am.in @@ -4,6 +4,18 @@ SUBDIRS = . tmplpkgdatadir = $(datadir)/taler/exchange/templates/ dist_tmplpkgdata_DATA = \ + kycaid-invalid-request.en.must \ + kyc-proof-already-done.en.must \ + kyc-proof-bad-request.en.must \ + kyc-proof-endpoint-unknown.en.must \ + kyc-proof-internal-error.en.must \ + kyc-proof-logic-failure.en.must \ + kyc-proof-target-unknown.en.must \ + oauth-authorization-failure.en.must \ + oauth-authorization-failure-malformed.en.must \ + oauth-bad-request.en.must \ + oauth-conversion-failure.en.must \ + oauth-provider-failure.en.must \ persona-exchange-unauthorized.en.must \ persona-load-failure.en.must \ persona-exchange-unpaid.en.must \ diff --git a/contrib/kyc-proof-already-done.en.must b/contrib/kyc-proof-already-done.en.must new file mode 100644 index 000000000..c676aa931 --- /dev/null +++ b/contrib/kyc-proof-already-done.en.must @@ -0,0 +1,9 @@ +<html> +<head> +<title>200: KYC already completed</title> +</head> +<body> + The KYC process is already completed. + There is nothing left to do here. +</body> +</html> diff --git a/contrib/kyc-proof-bad-request.en.must b/contrib/kyc-proof-bad-request.en.must new file mode 100644 index 000000000..f55516873 --- /dev/null +++ b/contrib/kyc-proof-bad-request.en.must @@ -0,0 +1,15 @@ +<html> +<head> +<title>400: Malformed client request</title> +</head> +<body> +The client's request was malformed. +<pre> +<!-- Taler error code --> {{ code }}: +<!-- GANA EC hint --> {{ hint }} +</pre> +<p> +<!-- optional human-readable message --> {{ message }} +</p> +</body> +</html> diff --git a/contrib/kyc-proof-endpoint-unknown.en.must b/contrib/kyc-proof-endpoint-unknown.en.must new file mode 100644 index 000000000..275f42380 --- /dev/null +++ b/contrib/kyc-proof-endpoint-unknown.en.must @@ -0,0 +1,15 @@ +<html> +<head> +<title>404: Endpoint unknown</title> +</head> +<body> +The given URL does not match any supported endpoint. +<pre> +<!-- Taler error code --> {{ code }}: +<!-- GANA EC hint --> {{ hint }} +</pre> +<p> +<!-- optional human-readable message --> {{ message }} +</p> +</body> +</html> diff --git a/contrib/kyc-proof-internal-error.en.must b/contrib/kyc-proof-internal-error.en.must new file mode 100644 index 000000000..ab7845e0d --- /dev/null +++ b/contrib/kyc-proof-internal-error.en.must @@ -0,0 +1,16 @@ +<html> +<head> +<title>500: Internal server error</title> +</head> +<body> + The server experienced an internal error handling + the request. +<pre> +<!-- Taler error code --> {{ code }}: +<!-- GANA EC hint --> {{ hint }} +</pre> +<p> +<!-- optional human-readable message --> {{ message }} +</p> +</body> +</html> diff --git a/contrib/kyc-proof-logic-failure.en.must b/contrib/kyc-proof-logic-failure.en.must new file mode 100644 index 000000000..f7f058500 --- /dev/null +++ b/contrib/kyc-proof-logic-failure.en.must @@ -0,0 +1,15 @@ +<html> +<head> +<title>500: Internal logic failure</title> +</head> +<body> + We had an internal error processing the KYC request. +<pre> +<!-- Taler Error code --> {{ code }}: +<!-- Taler EC hint --> {{ hint }} +</pre> +<p> + <!-- human-readable message --> {{ message}} +</p> +</body> +</html> diff --git a/contrib/kyc-proof-target-unknown.en.must b/contrib/kyc-proof-target-unknown.en.must new file mode 100644 index 000000000..1a698506f --- /dev/null +++ b/contrib/kyc-proof-target-unknown.en.must @@ -0,0 +1,15 @@ +<html> +<head> +<title>404: KYC request target unknown</title> +</head> +<body> +We could not identify the KYC operation requested. +<pre> +<!-- Taler error code --> {{ code }}: +<!-- GANA EC hint --> {{ hint }} +</pre> +<p> +<!-- optional human-readable message --> {{ message }} +</p> +</body> +</html> diff --git a/contrib/kycaid-invalid-request.en.must b/contrib/kycaid-invalid-request.en.must new file mode 100644 index 000000000..c33e3883f --- /dev/null +++ b/contrib/kycaid-invalid-request.en.must @@ -0,0 +1,12 @@ +<html> +<head> +<title>400: Invalid endpoint for KYCaid</title> +</head> +<body> +This endpoint is not defined for the KYCaid plugin. +<pre> +<!-- Taler error code --> {{ code }}: +<!-- GANA string for EC --> {{ hint }} +</pre> +</body> +</html> diff --git a/contrib/oauth2-authorization-failure-malformed.en.must b/contrib/oauth2-authorization-failure-malformed.en.must new file mode 100644 index 000000000..6f7552eae --- /dev/null +++ b/contrib/oauth2-authorization-failure-malformed.en.must @@ -0,0 +1,13 @@ +<html> +<head> +<title>502: OAuth 2.0 service returned malformed response</title> +</head> +<body> + The KYC backend returned an authorization failure, + but additionally provided a malformed error response. + <pre> +<!-- Should we output potentially sensitive data? --> {{ debug }} +<!-- Optional: server response --> {{ server_response }} +</pre> +</body> +</html> diff --git a/contrib/oauth2-authorization-failure.en.must b/contrib/oauth2-authorization-failure.en.must new file mode 100644 index 000000000..c184d10d8 --- /dev/null +++ b/contrib/oauth2-authorization-failure.en.must @@ -0,0 +1,12 @@ +<html> +<head> +<title>403: KYC server refused access</title> +</head> +<body> +The KYC backend refused the authorization code used by the exchange operator. Please inform the exchange operator about this failure. +<pre> +<!-- as provided by OAuth2.0 server --> {{ error }}: +<!-- as provided by OAuth2.0 server --> {{ error_description }} +</pre> +</body> +</html> diff --git a/contrib/oauth2-bad-request.en.must b/contrib/oauth2-bad-request.en.must new file mode 100644 index 000000000..f55516873 --- /dev/null +++ b/contrib/oauth2-bad-request.en.must @@ -0,0 +1,15 @@ +<html> +<head> +<title>400: Malformed client request</title> +</head> +<body> +The client's request was malformed. +<pre> +<!-- Taler error code --> {{ code }}: +<!-- GANA EC hint --> {{ hint }} +</pre> +<p> +<!-- optional human-readable message --> {{ message }} +</p> +</body> +</html> diff --git a/contrib/oauth2-conversion-failure.en.must b/contrib/oauth2-conversion-failure.en.must new file mode 100644 index 000000000..e117f5de6 --- /dev/null +++ b/contrib/oauth2-conversion-failure.en.must @@ -0,0 +1,28 @@ +<html> +<head> +<title>502: Failed to convert KYC data into internal format</title> +</head> +<body> + The KYC backend returned a response which we then failed + to convert into our internal format. +<pre> +<!-- Taler Error code --> {{ code }}: +<!-- Taler EC hint --> {{ hint }} +</pre> +<p> + <!-- human-readable message --> {{ message}} +</p> +<p> + Converter command used was: "{{ converter }}" +</p> +<p> + Converter JSON output: +<pre> + <!-- true if we are in debug mode --> {{ debug }} + <!-- optional: attributes returned by script + (note: may contain user's private information) --> {{ attributes }} +</pre> +</p> +</pre> +</body> +</html> diff --git a/contrib/oauth2-provider-failure.en.must b/contrib/oauth2-provider-failure.en.must new file mode 100644 index 000000000..d4c3585bf --- /dev/null +++ b/contrib/oauth2-provider-failure.en.must @@ -0,0 +1,22 @@ +<html> +<head> +<title>502: KYC provider had an internal error</title> +</head> +<body> +The KYC OAuth2 backend had an internal error. +<pre> +<!-- taler error code --> {{ code }}: +<!-- GANA hint for EC --> {{ hint }} +</pre> +<p> + <!-- human readable message with more specifics --> + {{ message }} +</p> +<pre> + <!-- true if we are in debug mode --> {{ debug }} + <!-- optional: full response from OAuth provider; + may contain private information! --> + {{ server_response }} +</p> +</body> +</html> diff --git a/src/exchange/taler-exchange-httpd_kyc-proof.c b/src/exchange/taler-exchange-httpd_kyc-proof.c index a3eb5cb55..ef3583d02 100644 --- a/src/exchange/taler-exchange-httpd_kyc-proof.c +++ b/src/exchange/taler-exchange-httpd_kyc-proof.c @@ -27,6 +27,7 @@ #include "taler_json_lib.h" #include "taler_kyclogic_lib.h" #include "taler_mhd_lib.h" +#include "taler_templating_lib.h" #include "taler-exchange-httpd_common_kyc.h" #include "taler-exchange-httpd_kyc-proof.h" #include "taler-exchange-httpd_responses.h" @@ -291,6 +292,51 @@ clean_kpc (struct TEH_RequestContext *rc) } +/** + * Respond with an HTML message on the given @a rc. + * + * @param[in,out] rc request to respond to + * @param http_status HTTP status code to use + * @param template template to fill in + * @param ec error code to use for the template + * @param message additional message to return + * @return MHD result code + */ +static MHD_RESULT +respond_html_ec (struct TEH_RequestContext *rc, + unsigned int http_status, + const char *template, + enum TALER_ErrorCode ec, + const char *message) +{ + json_t *body; + struct MHD_Response *response; + MHD_RESULT res; + + body = GNUNET_JSON_PACK ( + GNUNET_JSON_pack_allow_null ( + GNUNET_JSON_pack_string ("message", + message)), + TALER_JSON_pack_ec ( + ec)); + GNUNET_break ( + GNUNET_SYSERR != + TALER_TEMPLATING_build (rc->connection, + &http_status, + template, + NULL, + NULL, + body, + &response)); + json_decref (body); + res = MHD_queue_response (rc->connection, + http_status, + response); + MHD_destroy_response (response); + return res; +} + + MHD_RESULT TEH_handler_kyc_proof ( struct TEH_RequestContext *rc, @@ -305,10 +351,11 @@ TEH_handler_kyc_proof ( if (NULL == provider_section_or_logic) { 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"); + return respond_html_ec (rc, + MHD_HTTP_NOT_FOUND, + "kyc-proof-endpoint-unknown", + TALER_EC_GENERIC_ENDPOINT_UNKNOWN, + "'/kyc-proof/$PROVIDER_SECTION?state=$H_PAYTO' required"); } kpc = GNUNET_new (struct KycProofContext); kpc->rc = rc; @@ -324,10 +371,11 @@ TEH_handler_kyc_proof ( &kpc->provider_section)) { GNUNET_break_op (0); - return TALER_MHD_reply_with_error (rc->connection, - MHD_HTTP_NOT_FOUND, - TALER_EC_EXCHANGE_KYC_GENERIC_LOGIC_UNKNOWN, - provider_section_or_logic); + return respond_html_ec (rc, + MHD_HTTP_NOT_FOUND, + "kyc-proof-target-unknown", + TALER_EC_EXCHANGE_KYC_GENERIC_LOGIC_UNKNOWN, + provider_section_or_logic); } if (NULL != kpc->provider_section) { @@ -338,10 +386,11 @@ TEH_handler_kyc_proof ( kpc->provider_section)) { GNUNET_break_op (0); - return TALER_MHD_reply_with_error (rc->connection, - MHD_HTTP_BAD_REQUEST, - TALER_EC_GENERIC_PARAMETER_MALFORMED, - "PROVIDER_SECTION"); + return respond_html_ec (rc, + MHD_HTTP_BAD_REQUEST, + "kyc-proof-bad-request", + TALER_EC_GENERIC_PARAMETER_MALFORMED, + "PROVIDER_SECTION"); } qs = TEH_plugin->lookup_kyc_process_by_account ( @@ -356,26 +405,28 @@ TEH_handler_kyc_proof ( { case GNUNET_DB_STATUS_HARD_ERROR: case GNUNET_DB_STATUS_SOFT_ERROR: - return TALER_MHD_reply_with_ec (rc->connection, - TALER_EC_GENERIC_DB_STORE_FAILED, - "lookup_kyc_requirement_by_account"); + return respond_html_ec (rc, + MHD_HTTP_INTERNAL_SERVER_ERROR, + "kyc-proof-internal-error", + TALER_EC_GENERIC_DB_STORE_FAILED, + "lookup_kyc_requirement_by_account"); case GNUNET_DB_STATUS_SUCCESS_NO_RESULTS: - return TALER_MHD_reply_with_error (rc->connection, - MHD_HTTP_NOT_FOUND, - TALER_EC_EXCHANGE_KYC_PROOF_REQUEST_UNKNOWN, - kpc->provider_section); + return respond_html_ec (rc, + MHD_HTTP_NOT_FOUND, + "kyc-proof-target-unknown", + TALER_EC_EXCHANGE_KYC_PROOF_REQUEST_UNKNOWN, + kpc->provider_section); case GNUNET_DB_STATUS_SUCCESS_ONE_RESULT: break; } if (GNUNET_TIME_absolute_is_future (expiration)) { /* KYC not required */ - return TALER_MHD_reply_static ( - rc->connection, - MHD_HTTP_NO_CONTENT, - NULL, - NULL, - 0); + return respond_html_ec (rc, + MHD_HTTP_OK, + "kyc-proof-already-done", + TALER_EC_NONE, + NULL); } } kpc->ph = kpc->logic->proof (kpc->logic->cls, @@ -390,10 +441,11 @@ TEH_handler_kyc_proof ( if (NULL == kpc->ph) { GNUNET_break (0); - return TALER_MHD_reply_with_error (rc->connection, - MHD_HTTP_INTERNAL_SERVER_ERROR, - TALER_EC_GENERIC_INTERNAL_INVARIANT_FAILURE, - "could not start proof with KYC logic"); + return respond_html_ec (rc, + MHD_HTTP_INTERNAL_SERVER_ERROR, + "kyc-proof-internal-error", + TALER_EC_GENERIC_INTERNAL_INVARIANT_FAILURE, + "could not start proof with KYC logic"); } @@ -408,10 +460,11 @@ TEH_handler_kyc_proof ( if (NULL == kpc->response) { GNUNET_break (0); - return TALER_MHD_reply_with_error (rc->connection, - MHD_HTTP_INTERNAL_SERVER_ERROR, - TALER_EC_GENERIC_INTERNAL_INVARIANT_FAILURE, - "handler resumed without response"); + return respond_html_ec (rc, + MHD_HTTP_INTERNAL_SERVER_ERROR, + "kyc-proof-internal-error", + TALER_EC_GENERIC_INTERNAL_INVARIANT_FAILURE, + "handler resumed without response"); } /* return response from KYC logic */ diff --git a/src/kyclogic/Makefile.am b/src/kyclogic/Makefile.am index fae865bad..0281553fc 100644 --- a/src/kyclogic/Makefile.am +++ b/src/kyclogic/Makefile.am @@ -19,7 +19,7 @@ bin_SCRIPTS = \ taler-exchange-kyc-persona-converter.sh \ taler-exchange-kyc-oauth2-test-converter.sh \ taler-exchange-kyc-oauth2-challenger.sh \ - taler-exchange-kyc-oauth2-nda.sh + taler-exchange-kyc-oauth2-nda.sh EXTRA_DIST = \ $(pkgcfg_DATA) \ @@ -106,6 +106,7 @@ libtaler_plugin_kyclogic_kycaid_la_LIBADD = \ $(LTLIBINTL) libtaler_plugin_kyclogic_kycaid_la_LDFLAGS = \ $(TALER_PLUGIN_LDFLAGS) \ + $(top_builddir)/src/templating/libtalertemplating.la \ $(top_builddir)/src/mhd/libtalermhd.la \ $(top_builddir)/src/json/libtalerjson.la \ $(top_builddir)/src/curl/libtalercurl.la \ diff --git a/src/kyclogic/plugin_kyclogic_kycaid.c b/src/kyclogic/plugin_kyclogic_kycaid.c index 6c8ced55d..243ff7c34 100644 --- a/src/kyclogic/plugin_kyclogic_kycaid.c +++ b/src/kyclogic/plugin_kyclogic_kycaid.c @@ -1,6 +1,6 @@ /* This file is part of GNU Taler - Copyright (C) 2022, 2023 Taler Systems SA + Copyright (C) 2022--2024 Taler Systems SA Taler is free software; you can redistribute it and/or modify it under the terms of the GNU Affero General Public License as published by the Free Software @@ -25,6 +25,7 @@ #include "taler_mhd_lib.h" #include "taler_curl_lib.h" #include "taler_json_lib.h" +#include "taler_templating_lib.h" #include <regex.h> #include "taler_util.h" @@ -669,16 +670,30 @@ proof_reply (void *cls) { struct TALER_KYCLOGIC_ProofHandle *ph = cls; struct MHD_Response *resp; + enum GNUNET_GenericReturnValue ret; + json_t *body; + unsigned int http_status; - resp = TALER_MHD_make_error (TALER_EC_GENERIC_ENDPOINT_UNKNOWN, - "there is no '/kyc-proof' for kycaid"); + http_status = MHD_HTTP_BAD_REQUEST; + body = GNUNET_JSON_PACK ( + TALER_JSON_pack_ec (TALER_EC_GENERIC_ENDPOINT_UNKNOWN)); + GNUNET_assert (NULL != body); + ret = TALER_TEMPLATING_build (ph->connection, + &http_status, + "kycaid-invalid-request", + NULL, + NULL, + body, + &resp); + json_decref (body); + GNUNET_break (GNUNET_SYSERR != ret); ph->cb (ph->cb_cls, TALER_KYCLOGIC_STATUS_PROVIDER_FAILED, NULL, /* user id */ NULL, /* provider legi ID */ GNUNET_TIME_UNIT_ZERO_ABS, /* expiration */ NULL, /* attributes */ - MHD_HTTP_BAD_REQUEST, + http_status, resp); } diff --git a/src/kyclogic/plugin_kyclogic_oauth2.c b/src/kyclogic/plugin_kyclogic_oauth2.c index 53b03e1eb..250875cd2 100644 --- a/src/kyclogic/plugin_kyclogic_oauth2.c +++ b/src/kyclogic/plugin_kyclogic_oauth2.c @@ -1,6 +1,6 @@ /* This file is part of GNU Taler - Copyright (C) 2022-2023 Taler Systems SA + Copyright (C) 2022-2024 Taler Systems SA Taler is free software; you can redistribute it and/or modify it under the terms of the GNU Affero General Public License as published by the Free Software @@ -123,6 +123,12 @@ struct TALER_KYCLOGIC_ProviderDetails */ struct GNUNET_TIME_Relative validity; + /** + * Set to true if we are operating in DEBUG + * mode and may return private details in HTML + * responses to make diagnostics easier. + */ + bool debug_mode; }; @@ -516,6 +522,11 @@ oauth2_load_configuration (void *cls, oauth2_unload_configuration (pd); return NULL; } + if (GNUNET_OK == + GNUNET_CONFIGURATION_get_value_yesno (ps->cfg, + provider_section_name, + "KYC_OAUTH2_DEBUG_MODE")) + pd->debug_mode = true; return pd; } @@ -859,18 +870,18 @@ static void handle_proof_error (struct TALER_KYCLOGIC_ProofHandle *ph, const json_t *j) { - const char *msg; - const char *desc; - struct GNUNET_JSON_Specification spec[] = { - GNUNET_JSON_spec_string ("error", - &msg), - GNUNET_JSON_spec_string ("error_description", - &desc), - GNUNET_JSON_spec_end () - }; + enum GNUNET_GenericReturnValue res; { - enum GNUNET_GenericReturnValue res; + const char *msg; + const char *desc; + struct GNUNET_JSON_Specification spec[] = { + GNUNET_JSON_spec_string ("error", + &msg), + GNUNET_JSON_spec_string ("error_description", + &desc), + GNUNET_JSON_spec_end () + }; const char *emsg; unsigned int line; @@ -878,40 +889,48 @@ handle_proof_error (struct TALER_KYCLOGIC_ProofHandle *ph, spec, &emsg, &line); - if (GNUNET_OK != res) - { - GNUNET_break_op (0); - ph->status = TALER_KYCLOGIC_STATUS_PROVIDER_FAILED; - ph->response - = TALER_MHD_make_error ( - TALER_EC_EXCHANGE_KYC_PROOF_BACKEND_INVALID_RESPONSE, - "Unexpected response from KYC gateway: proof error"); - ph->http_status - = MHD_HTTP_BAD_GATEWAY; - return; - } } - /* case TALER_EC_EXCHANGE_KYC_PROOF_BACKEND_AUTHORZATION_FAILED, - we MAY want to in the future look at the requested content type - and possibly respond in JSON if indicated. */ + + if (GNUNET_OK != res) { - char *reply; - - GNUNET_asprintf (&reply, - "<html><head><title>%s</title></head><body><h1>%s</h1><p>%s</p></body></html>", - msg, - msg, - desc); - ph->status = TALER_KYCLOGIC_STATUS_USER_ABORTED; - ph->response - = MHD_create_response_from_buffer (strlen (reply), - reply, - MHD_RESPMEM_MUST_COPY); - GNUNET_assert (NULL != ph->response); - GNUNET_free (reply); + 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_allow_null ( + GNUNET_JSON_pack_object_incref ("server_response", + (json_t *) j)), + GNUNET_JSON_pack_bool ("debug", + ph->pd->debug_mode), + TALER_JSON_pack_ec ( + TALER_EC_EXCHANGE_KYC_PROOF_BACKEND_INVALID_RESPONSE)); + GNUNET_assert (NULL != body); + GNUNET_break ( + GNUNET_SYSERR != + TALER_TEMPLATING_build (ph->connection, + &ph->http_status, + "oauth2-authorization-failure-malformed", + NULL, + NULL, + body, + &ph->response)); + json_decref (body); + return; } ph->status = TALER_KYCLOGIC_STATUS_USER_ABORTED; ph->http_status = MHD_HTTP_FORBIDDEN; + GNUNET_break ( + GNUNET_SYSERR != + TALER_TEMPLATING_build (ph->connection, + &ph->http_status, + "oauth2-authorization-failure", + NULL, + NULL, + j, + &ph->response)); } @@ -930,19 +949,48 @@ converted_proof_cb (void *cls, const json_t *attr) { struct TALER_KYCLOGIC_ProofHandle *ph = cls; + const struct TALER_KYCLOGIC_ProviderDetails *pd = ph->pd; ph->ec = NULL; if ( (NULL == attr) || (0 != code) ) { + json_t *body; + char *msg; + GNUNET_break_op (0); ph->status = TALER_KYCLOGIC_STATUS_PROVIDER_FAILED; - ph->response - = TALER_MHD_make_error ( - TALER_EC_EXCHANGE_KYC_PROOF_BACKEND_INVALID_RESPONSE, - "Unexpected response from KYC gateway: proof success must contain data and status"); - ph->http_status - = MHD_HTTP_BAD_GATEWAY; + ph->http_status = MHD_HTTP_BAD_GATEWAY; + if (0 != code) + GNUNET_asprintf (&msg, + "Attribute converter exited with status %ld", + code); + else + msg = GNUNET_strdup ( + "Attribute converter response was not in JSON format"); + body = GNUNET_JSON_PACK ( + GNUNET_JSON_pack_string ("converter", + pd->conversion_binary), + GNUNET_JSON_pack_allow_null ( + GNUNET_JSON_pack_object_incref ("attributes", + (json_t *) attr)), + GNUNET_JSON_pack_bool ("debug", + ph->pd->debug_mode), + GNUNET_JSON_pack_string ("message", + msg), + TALER_JSON_pack_ec ( + TALER_EC_EXCHANGE_KYC_PROOF_BACKEND_INVALID_RESPONSE)); + GNUNET_free (msg); + 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; @@ -965,17 +1013,34 @@ converted_proof_cb (void *cls, &line); if (GNUNET_OK != res) { + json_t *body; + GNUNET_break_op (0); - json_dumpf (attr, - stderr, - JSON_INDENT (2)); ph->status = TALER_KYCLOGIC_STATUS_PROVIDER_FAILED; - ph->response - = TALER_MHD_make_error ( - TALER_EC_EXCHANGE_KYC_PROOF_BACKEND_INVALID_RESPONSE, - "Unexpected response from KYC gateway: data must contain id"); - ph->http_status - = MHD_HTTP_BAD_GATEWAY; + ph->http_status = MHD_HTTP_BAD_GATEWAY; + body = GNUNET_JSON_PACK ( + GNUNET_JSON_pack_string ("converter", + pd->conversion_binary), + GNUNET_JSON_pack_string ("message", + "Unexpected response from KYC attribute converter: returned JSON data must contain 'id' field"), + 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->provider_user_id = GNUNET_strdup (id); @@ -1023,20 +1088,38 @@ parse_proof_success_reply (struct TALER_KYCLOGIC_ProofHandle *ph, pd->conversion_binary, pd->conversion_binary, NULL); - if (NULL == ph->ec) - { - GNUNET_log (GNUNET_ERROR_TYPE_ERROR, - "Failed to start KYCAID conversion helper `%s'\n", - pd->conversion_binary); - ph->status = TALER_KYCLOGIC_STATUS_INTERNAL_ERROR; - ph->response - = TALER_MHD_make_error ( - TALER_EC_EXCHANGE_GENERIC_KYC_CONVERTER_FAILED, - "Failed to launch KYC conversion helper"); - ph->http_status - = MHD_HTTP_INTERNAL_SERVER_ERROR; + if (NULL != ph->ec) return; + GNUNET_log (GNUNET_ERROR_TYPE_ERROR, + "Failed to start KYCAID conversion helper `%s'\n", + pd->conversion_binary); + ph->status = TALER_KYCLOGIC_STATUS_INTERNAL_ERROR; + ph->http_status = MHD_HTTP_INTERNAL_SERVER_ERROR; + { + json_t *body; + + body = GNUNET_JSON_PACK ( + GNUNET_JSON_pack_string ("converter", + pd->conversion_binary), + GNUNET_JSON_pack_bool ("debug", + ph->pd->debug_mode), + GNUNET_JSON_pack_string ("message", + "Failed to launch KYC conversion helper process."), + TALER_JSON_pack_ec ( + TALER_EC_EXCHANGE_GENERIC_KYC_CONVERTER_FAILED)); + 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); } @@ -1061,13 +1144,30 @@ handle_curl_proof_finished (void *cls, switch (response_code) { case 0: - ph->status = TALER_KYCLOGIC_STATUS_PROVIDER_FAILED; - ph->response - = TALER_MHD_make_error ( - TALER_EC_EXCHANGE_KYC_PROOF_BACKEND_INVALID_RESPONSE, - "No response from KYC gateway"); - ph->http_status - = MHD_HTTP_BAD_GATEWAY; + { + json_t *body; + + ph->status = TALER_KYCLOGIC_STATUS_PROVIDER_FAILED; + ph->http_status = MHD_HTTP_BAD_GATEWAY; + + body = GNUNET_JSON_PACK ( + GNUNET_JSON_pack_string ("message", + "No response from KYC gateway"), + GNUNET_JSON_pack_bool ("debug", + ph->pd->debug_mode), + 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-provider-failure", + NULL, + NULL, + body, + &ph->response)); + json_decref (body); + } break; case MHD_HTTP_OK: parse_proof_success_reply (ph, @@ -1121,13 +1221,11 @@ handle_curl_login_finished (void *cls, GNUNET_JSON_spec_mark_optional ( GNUNET_JSON_spec_uint64 ("expires_in", &expires_in_s), - &no_expires - ), + &no_expires), GNUNET_JSON_spec_mark_optional ( GNUNET_JSON_spec_string ("refresh_token", &refresh_token), - &no_refresh - ), + &no_refresh), GNUNET_JSON_spec_end () }; CURL *eh; @@ -1143,26 +1241,59 @@ handle_curl_login_finished (void *cls, &line); if (GNUNET_OK != res) { + json_t *body; + GNUNET_break_op (0); - ph->response - = TALER_MHD_make_error ( - TALER_EC_EXCHANGE_KYC_PROOF_BACKEND_INVALID_RESPONSE, - "Unexpected response from KYC gateway: login finished"); ph->http_status = MHD_HTTP_BAD_GATEWAY; + body = GNUNET_JSON_PACK ( + GNUNET_JSON_pack_object_incref ("server_response", + (json_t *) j), + GNUNET_JSON_pack_bool ("debug", + ph->pd->debug_mode), + GNUNET_JSON_pack_string ("message", + "Unexpected response from KYC gateway: required fields missing or malformed"), + 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-provider-failure", + NULL, + NULL, + body, + &ph->response)); + json_decref (body); break; } } if (0 != strcasecmp (token_type, "bearer")) { + json_t *body; + GNUNET_break_op (0); - ph->response - = TALER_MHD_make_error ( - TALER_EC_EXCHANGE_KYC_PROOF_BACKEND_INVALID_RESPONSE, - "Unexpected token type in response from KYC gateway"); - ph->http_status - = MHD_HTTP_BAD_GATEWAY; + ph->http_status = MHD_HTTP_BAD_GATEWAY; + body = GNUNET_JSON_PACK ( + GNUNET_JSON_pack_object_incref ("server_response", + (json_t *) j), + GNUNET_JSON_pack_bool ("debug", + ph->pd->debug_mode), + GNUNET_JSON_pack_string ("message", + "Unexpected 'token_type' in response from KYC gateway: 'bearer' token required"), + 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-provider-failure", + NULL, + NULL, + body, + &ph->response)); + json_decref (body); break; } @@ -1177,28 +1308,34 @@ handle_curl_login_finished (void *cls, (NULL != strchr (access_token, ';')) ) { + json_t *body; + GNUNET_break_op (0); - ph->response - = TALER_MHD_make_error ( - TALER_EC_EXCHANGE_KYC_PROOF_BACKEND_INVALID_RESPONSE, - "Illegal character in access token"); - ph->http_status - = MHD_HTTP_BAD_GATEWAY; + ph->http_status = MHD_HTTP_BAD_GATEWAY; + body = GNUNET_JSON_PACK ( + GNUNET_JSON_pack_object_incref ("server_response", + (json_t *) j), + GNUNET_JSON_pack_bool ("debug", + ph->pd->debug_mode), + GNUNET_JSON_pack_string ("message", + "Illegal character in access token"), + 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-provider-failure", + NULL, + NULL, + body, + &ph->response)); + json_decref (body); break; } eh = curl_easy_init (); - if (NULL == eh) - { - GNUNET_break (0); - ph->response - = TALER_MHD_make_error ( - TALER_EC_GENERIC_ALLOCATION_FAILURE, - "curl_easy_init"); - ph->http_status - = MHD_HTTP_INTERNAL_SERVER_ERROR; - break; - } + GNUNET_assert (NULL != eh); GNUNET_assert (CURLE_OK == curl_easy_setopt (eh, CURLOPT_URL, @@ -1289,30 +1426,35 @@ oauth2_proof (void *cls, "code"); if (NULL == code) { + json_t *body; + GNUNET_break_op (0); ph->status = TALER_KYCLOGIC_STATUS_USER_PENDING; ph->http_status = MHD_HTTP_BAD_REQUEST; - ph->response = TALER_MHD_make_error ( - TALER_EC_GENERIC_PARAMETER_MALFORMED, - "code"); + body = GNUNET_JSON_PACK ( + GNUNET_JSON_pack_bool ("debug", + ph->pd->debug_mode), + GNUNET_JSON_pack_string ("message", + "'code' parameter malformed"), + TALER_JSON_pack_ec ( + TALER_EC_GENERIC_PARAMETER_MALFORMED)); + GNUNET_break ( + GNUNET_SYSERR != + TALER_TEMPLATING_build (ph->connection, + &ph->http_status, + "oauth2-bad-request", + NULL, + NULL, + body, + &ph->response)); + json_decref (body); ph->task = GNUNET_SCHEDULER_add_now (&return_proof_response, ph); return ph; } ph->eh = curl_easy_init (); - if (NULL == ph->eh) - { - GNUNET_break (0); - ph->status = TALER_KYCLOGIC_STATUS_USER_PENDING; - ph->http_status = MHD_HTTP_INTERNAL_SERVER_ERROR; - ph->response = TALER_MHD_make_error ( - TALER_EC_GENERIC_ALLOCATION_FAILURE, - "curl_easy_init"); - ph->task = GNUNET_SCHEDULER_add_now (&return_proof_response, - ph); - return ph; - } + GNUNET_assert (NULL != ph->eh); GNUNET_log (GNUNET_ERROR_TYPE_INFO, "Requesting OAuth 2.0 data via HTTP POST `%s'\n", pd->token_url); |