donau

Donation authority for GNU Taler (experimental)
Log | Files | Refs | Submodules | README | LICENSE

commit b0902b5efd07d766767f7645a37ad11b93a810db
parent 756c2ba769ab71933348876e03d34375a02c1644
Author: Christian Grothoff <grothoff@gnunet.org>
Date:   Sun,  3 Aug 2025 01:39:40 +0200

add bearer token logic

Diffstat:
Msrc/donau/donau-httpd.c | 53+++++++++++++++++++++++++++++++++++++++++++++--------
Msrc/donau/donau-httpd.h | 5+++++
Msrc/donau/donau.conf | 3+++
Msrc/donaudb/test_donaudb.c | 8++++----
Msrc/lib/donau_api_charities_get.c | 22++++++++++++++++++++++
Msrc/lib/donau_api_charity_delete.c | 17+++++++++++++++++
Msrc/lib/donau_api_charity_get.c | 22++++++++++++++++++++++
Msrc/lib/donau_api_charity_post.c | 17+++++++++++++++++
Msrc/testing/test_donau_api.c | 97+++++++++++++++++++++++++++++++++++++++----------------------------------------
Msrc/testing/test_donau_api.conf | 5+++++
10 files changed, 188 insertions(+), 61 deletions(-)

diff --git a/src/donau/donau-httpd.c b/src/donau/donau-httpd.c @@ -126,6 +126,11 @@ char *DH_domain; char *DH_base_url; /** + * Our administrative bearer token. + */ +static char *admin_bearer; + +/** * Default timeout in seconds for HTTP requests. */ static unsigned int connection_timeout = 30; @@ -279,6 +284,28 @@ proceed_with_handler (struct DH_RequestContext *rc, json_t *root = NULL; MHD_RESULT ret; + if ( (rh->needs_authorization) && + (NULL != admin_bearer) ) + { + const char *ah; + + ah = MHD_lookup_connection_value (rc->connection, + MHD_HEADER_KIND, + MHD_HTTP_HEADER_AUTHORIZATION); + if ( (NULL == ah) || + (0 != strncasecmp (ah, + "Bearer ", + strlen ("Bearer "))) || + (0 != strcmp (ah + strlen ("Bearer "), + admin_bearer)) ) + { + GNUNET_break_op (0); + return TALER_MHD_reply_with_error (rc->connection, + MHD_HTTP_FORBIDDEN, + TALER_EC_GENERIC_TOKEN_PERMISSION_INSUFFICIENT, + "authorization required"); + } + } /* We do check for "ulen" here, because we'll later stack-allocate a buffer of that size and don't want to enable malicious clients to cause us huge stack allocations. */ @@ -395,11 +422,8 @@ handle_get_charities (struct DH_RequestContext *rc, { return DH_handler_charities_get (rc); } - else - { - return DH_handler_charity_get (rc, - &args[0]); - } + return DH_handler_charity_get (rc, + &args[0]); } @@ -457,20 +481,23 @@ handle_mhd_request (void *cls, .method = MHD_HTTP_METHOD_GET, .handler.get = &handle_get_charities, .nargs = 1, - .nargs_is_upper_bound = true + .nargs_is_upper_bound = true, + .needs_authorization = true }, /* POST charities */ { .url = "charities", .method = MHD_HTTP_METHOD_POST, - .handler.post = &DH_handler_charity_post + .handler.post = &DH_handler_charity_post, + .needs_authorization = true }, /* DELETE charities */ { .url = "charities", .method = MHD_HTTP_METHOD_DELETE, .handler.delete = &DH_handler_charity_delete, - .nargs = 1 + .nargs = 1, + .needs_authorization = true }, /* POST get csr values*/ { @@ -779,6 +806,16 @@ donau_serve_process_config (void) if (GNUNET_OK != GNUNET_CONFIGURATION_get_value_string (DH_cfg, "donau", + "ADMIN_BEARER_TOKEN", + &admin_bearer)) + { + GNUNET_log_config_missing (GNUNET_ERROR_TYPE_WARNING, + "donau", + "ADMIN_BEARER_TOKEN"); + } + if (GNUNET_OK != + GNUNET_CONFIGURATION_get_value_string (DH_cfg, + "donau", "BASE_URL", &DH_base_url)) { diff --git a/src/donau/donau-httpd.h b/src/donau/donau-httpd.h @@ -174,6 +174,11 @@ struct DH_RequestHandler const char *method; /** + * True if HTTP authorization via Bearer token is required. + */ + bool needs_authorization; + + /** * Callbacks for handling of the request. Which one is used * depends on @e method. */ diff --git a/src/donau/donau.conf b/src/donau/donau.conf @@ -2,6 +2,9 @@ # [donau] +# Bearer access token required to manage charities. +#ADMIN_BEARER_TOKEN = "secret-token:password" + # How many digits does the currency use by default on displays. # Hint provided to wallets. Should be 2 for EUR/USD/CHF, # and 0 for JPY. Default is 2 as that is most common. diff --git a/src/donaudb/test_donaudb.c b/src/donaudb/test_donaudb.c @@ -176,12 +176,12 @@ run (void *cls) struct DONAU_DonationReceiptHashP h_receipt; struct TALER_Amount amount_receipts; bool smaller_than_max_per_year; - struct DONAUDB_IssuedReceiptsMetaData ir_meta; + // struct DONAUDB_IssuedReceiptsMetaData ir_meta; // Submitted receipts information - struct DONAU_HashDonorTaxId h_donor_tax_id; - size_t num_dr = 1; - struct DONAU_DonationReceipt donation_receipts[num_dr]; + // struct DONAU_HashDonorTaxId h_donor_tax_id; + // size_t num_dr = 1; + // struct DONAU_DonationReceipt donation_receipts[num_dr]; if (NULL == (plugin = DONAUDB_plugin_load (cfg))) diff --git a/src/lib/donau_api_charities_get.c b/src/lib/donau_api_charities_get.c @@ -169,6 +169,11 @@ handle_charities_get_finished (void *cls, gcresp.hr.ec = TALER_JSON_get_error_code (j); gcresp.hr.hint = TALER_JSON_get_error_hint (j); break; + case MHD_HTTP_FORBIDDEN: + /* Nothing really to verify */ + gcresp.hr.ec = TALER_JSON_get_error_code (j); + gcresp.hr.hint = TALER_JSON_get_error_hint (j); + break; case MHD_HTTP_NOT_FOUND: /* Nothing really to verify, this should never happen, we should pass the JSON reply to the application */ @@ -245,6 +250,23 @@ DONAU_charities_get ( eh, &handle_charities_get_finished, cgh); + GNUNET_assert (NULL != cgh->job); + if (NULL != bearer) + { + struct curl_slist *auth; + char *hdr; + + GNUNET_asprintf (&hdr, + "%s: Bearer %s", + MHD_HTTP_HEADER_AUTHORIZATION, + bearer->token); + auth = curl_slist_append (NULL, + hdr); + GNUNET_free (hdr); + GNUNET_CURL_extend_headers (cgh->job, + auth); + curl_slist_free_all (auth); + } return cgh; } diff --git a/src/lib/donau_api_charity_delete.c b/src/lib/donau_api_charity_delete.c @@ -193,6 +193,23 @@ DONAU_charity_delete ( eh, &handle_charity_delete_finished, cdh); + GNUNET_assert (NULL != cdh->job); + if (NULL != bearer) + { + struct curl_slist *auth; + char *hdr; + + GNUNET_asprintf (&hdr, + "%s: Bearer %s", + MHD_HTTP_HEADER_AUTHORIZATION, + bearer->token); + auth = curl_slist_append (NULL, + hdr); + GNUNET_free (hdr); + GNUNET_CURL_extend_headers (cdh->job, + auth); + curl_slist_free_all (auth); + } return cdh; } diff --git a/src/lib/donau_api_charity_get.c b/src/lib/donau_api_charity_get.c @@ -161,6 +161,11 @@ handle_charity_get_finished (void *cls, gcresp.hr.ec = TALER_JSON_get_error_code (j); gcresp.hr.hint = TALER_JSON_get_error_hint (j); break; + case MHD_HTTP_FORBIDDEN: + /* Nothing really to verify */ + gcresp.hr.ec = TALER_JSON_get_error_code (j); + gcresp.hr.hint = TALER_JSON_get_error_hint (j); + break; case MHD_HTTP_NOT_FOUND: /* Nothing really to verify, this should never happen, we should pass the JSON reply to the application */ @@ -243,6 +248,23 @@ DONAU_charity_get ( eh, &handle_charity_get_finished, cgh); + GNUNET_assert (NULL != cgh->job); + if (NULL != bearer) + { + struct curl_slist *auth; + char *hdr; + + GNUNET_asprintf (&hdr, + "%s: Bearer %s", + MHD_HTTP_HEADER_AUTHORIZATION, + bearer->token); + auth = curl_slist_append (NULL, + hdr); + GNUNET_free (hdr); + GNUNET_CURL_extend_headers (cgh->job, + auth); + curl_slist_free_all (auth); + } return cgh; } diff --git a/src/lib/donau_api_charity_post.c b/src/lib/donau_api_charity_post.c @@ -215,6 +215,23 @@ DONAU_charity_post ( cph->post_ctx.headers, &handle_charity_post_finished, cph); + GNUNET_assert (NULL != cph->job); + if (NULL != bearer) + { + struct curl_slist *auth; + char *hdr; + + GNUNET_asprintf (&hdr, + "%s: Bearer %s", + MHD_HTTP_HEADER_AUTHORIZATION, + bearer->token); + auth = curl_slist_append (NULL, + hdr); + GNUNET_free (hdr); + GNUNET_CURL_extend_headers (cph->job, + auth); + curl_slist_free_all (auth); + } return cph; } diff --git a/src/testing/test_donau_api.c b/src/testing/test_donau_api.c @@ -60,59 +60,58 @@ static void run (void *cls, struct TALER_TESTING_Interpreter *is) { - const struct DONAU_BearerToken bearer = { - .token = NULL + const static struct DONAU_BearerToken bearer = { + .token = "secret-token:secret" }; - { - struct TALER_TESTING_Command commands[] = { - TALER_TESTING_cmd_system_start ("start-donau", - config_file, - "-D", - "NULL"), - TALER_TESTING_cmd_get_donau ("get-donau", - cred.cfg, - true), - TALER_TESTING_cmd_charity_post ("post-charity", - "example", - "example.com", - "EUR:50", // max_per_year - "EUR:0", // receipts_to_date - 2025, // current year - &bearer, - MHD_HTTP_CREATED), - TALER_TESTING_cmd_charity_get ("get-charity-by-id", - "post-charity", // cmd trait reference + struct TALER_TESTING_Command commands[] = { + TALER_TESTING_cmd_system_start ("start-donau", + config_file, + "-D", + "NULL"), + TALER_TESTING_cmd_get_donau ("get-donau", + cred.cfg, + true), + TALER_TESTING_cmd_charity_post ("post-charity", + "example", + "example.com", + "EUR:50", // max_per_year + "EUR:0", // receipts_to_date + 2025, // current year + &bearer, + MHD_HTTP_CREATED), + TALER_TESTING_cmd_charity_get ("get-charity-by-id", + "post-charity", // cmd trait reference + &bearer, + MHD_HTTP_OK), + TALER_TESTING_cmd_charities_get ("get-charities", &bearer, MHD_HTTP_OK), - TALER_TESTING_cmd_charities_get ("get-charities", - &bearer, - MHD_HTTP_OK), - // FIXME: CSR signatures - TALER_TESTING_cmd_issue_receipts ("issue-receipts", - "post-charity", - uses_cs, - 2025, - "7560001010000", // tax id - "1234", // salt for tax id hash - MHD_HTTP_CREATED), - TALER_TESTING_cmd_submit_receipts ("submit-receipts", - "issue-receipts", // cmd trait reference - 2025, - MHD_HTTP_CREATED), - TALER_TESTING_cmd_donation_statement_get ("donation-statement", - 2025, - MHD_HTTP_OK), - TALER_TESTING_cmd_charity_delete ("delete-charity", - "post-charity", // cmd trait reference - &bearer, - MHD_HTTP_NO_CONTENT), - /* End the suite. */ - TALER_TESTING_cmd_end () - }; + // FIXME: CSR signatures + TALER_TESTING_cmd_issue_receipts ("issue-receipts", + "post-charity", + uses_cs, + 2025, + "7560001010000", // tax id + "1234", // salt for tax id hash + MHD_HTTP_CREATED), + TALER_TESTING_cmd_submit_receipts ("submit-receipts", + "issue-receipts", // cmd trait reference + 2025, + MHD_HTTP_CREATED), + TALER_TESTING_cmd_donation_statement_get ("donation-statement", + 2025, + MHD_HTTP_OK), + TALER_TESTING_cmd_charity_delete ("delete-charity", + "post-charity", // cmd trait reference + &bearer, + MHD_HTTP_NO_CONTENT), + /* End the suite. */ + TALER_TESTING_cmd_end () + }; - TALER_TESTING_run (is, - commands); - } + (void) cls; + TALER_TESTING_run (is, + commands); } diff --git a/src/testing/test_donau_api.conf b/src/testing/test_donau_api.conf @@ -6,6 +6,11 @@ TALER_TEST_HOME = test_donau_api_home/ DONAU_RUNTIME_DIR = ${TMPDIR:-${TMP:-/tmp}}/donau-system-runtime/ [donau] + +# Bearer access token required to manage charities. +ADMIN_BEARER_TOKEN = "secret-token:secret" + + CURRENCY = EUR CURRENCY_ROUND_UNIT = EUR:0.01 TERMS_ETAG = tos