merchant

Merchant backend to process payments, run by merchants
Log | Files | Refs | Submodules | README | LICENSE

commit 053ab084a6ba5598326aa214d8692b54092416d4
parent 44d052a35e0964eee72a79b53b37131dc4f8c6d5
Author: Martin Schanzenbach <schanzen@gnunet.org>
Date:   Mon,  7 Jul 2025 16:10:10 +0200

refactor authorization logic

Diffstat:
Msrc/backend/taler-merchant-httpd.c | 208++++++++++++++++++++++++++++++++++++++++++++++---------------------------------
1 file changed, 121 insertions(+), 87 deletions(-)

diff --git a/src/backend/taler-merchant-httpd.c b/src/backend/taler-merchant-httpd.c @@ -1031,6 +1031,116 @@ full_url_track_callback (void *cls, /** + * Function used to process Basic authorization header value. + * Sets correct scope in the auth_scope parameter of the + * #TMH_HandlerContext. + * + * @param hc the handler context + * @param authn_s the value of the authorization header + */ +static void +process_basic_auth (struct TMH_HandlerContext *hc, const char*authn_s) +{ + /* Handle token endpoint slightly differently: Only allow + * instance password (Basic auth) to retrieve access token. + * We need to handle authorization with Basic auth here first + * The only time we need to handle authentication like this is + * for the token endpoint! + */ + if ( (0 != strncmp (hc->rh->url_prefix, + "/token", + strlen ("/token"))) || + (0 != strncmp (MHD_HTTP_METHOD_POST, + hc->rh->method, + strlen (MHD_HTTP_METHOD_POST))) || + (NULL == hc->instance)) + { + GNUNET_log (GNUNET_ERROR_TYPE_WARNING, + "Called endpoint `%s' with Basic authentication. Rejecting...\n", + hc->rh->url_prefix); + hc->auth_scope = TMH_AS_NONE; + return; + } + if (GNUNET_OK == + check_auth_instance (authn_s, + hc->instance)) + hc->auth_scope = TMH_AS_ALL; + else + hc->auth_scope = TMH_AS_NONE; +} + + +/** + * Function used to process Bearer authorization header value. + * Sets correct scope in the auth_scope parameter of the + * #TMH_HandlerContext.. + * + * @param hc the handler context + * @param authn_s the value of the authorization header + * @return TALER_EC_NONE on success. + */ +static enum TALER_ErrorCode +process_bearer_auth (struct TMH_HandlerContext *hc, const char*authn_s) +{ + if (NULL == hc->instance) + { + hc->auth_scope = TMH_AS_NONE; + return TALER_EC_NONE; + } + if (GNUNET_is_zero (&hc->instance->auth.auth_hash)) + { + /* hash zero means no authentication for instance */ + hc->auth_scope = TMH_AS_ALL; + return TALER_EC_NONE; + } + { + enum TALER_ErrorCode ec; + + ec = TMH_check_token (authn_s, + hc->instance->settings.id, + &hc->auth_scope); + if (TALER_EC_NONE != ec) + { + char *dec; + size_t dec_len; + const char *token; + + /* NOTE: Deprecated, remove sometime after v1.1 */ + if (0 != strncasecmp (authn_s, + RFC_8959_PREFIX, + strlen (RFC_8959_PREFIX))) + { + GNUNET_break_op (0); + hc->auth_scope = TMH_AS_NONE; + return ec; + } + GNUNET_log (GNUNET_ERROR_TYPE_WARNING, + "Trying deprecated secret-token:password API authN\n"); + token = authn_s + strlen (RFC_8959_PREFIX); + dec_len = GNUNET_STRINGS_urldecode (token, + strlen (token), + &dec); + if ( (0 == dec_len) || + (GNUNET_OK != + TMH_check_auth (dec, + &hc->instance->auth.auth_salt, + &hc->instance->auth.auth_hash)) ) + { + GNUNET_log (GNUNET_ERROR_TYPE_WARNING, + "Login failed\n"); + hc->auth_scope = TMH_AS_NONE; + GNUNET_free (dec); + return TALER_EC_NONE; + } + hc->auth_scope = TMH_AS_ALL; + GNUNET_free (dec); + } + } + return TALER_EC_NONE; +} + + +/** * A client has requested the given url using the given method * (#MHD_HTTP_METHOD_GET, #MHD_HTTP_METHOD_PUT, * #MHD_HTTP_METHOD_DELETE, #MHD_HTTP_METHOD_POST, etc). The callback @@ -2219,100 +2329,24 @@ url_handler (void *cls, } else if (is_basic_auth) { - /* Handle token endpoint slightly differently: Only allow - * instance password (Basic auth) to retrieve access token. - * We need to handle authorization with Basic auth here first - * The only time we need to handle authentication like this is - * for the token endpoint! - */ - if ( (0 != strncmp (hc->rh->url_prefix, - "/token", - strlen ("/token"))) || - (0 != strncmp (MHD_HTTP_METHOD_POST, - hc->rh->method, - strlen (MHD_HTTP_METHOD_POST))) || - (NULL == hc->instance)) - { - // FIXME this should never happen, but according to the comment below, - // We must not error out here for some reason that has to do with - // disabled authZ behind reverse proxy...? - hc->auth_scope = TMH_AS_NONE; - } - else - { - if (GNUNET_OK == - check_auth_instance (auth, - hc->instance)) - hc->auth_scope = TMH_AS_ALL; - else - hc->auth_scope = TMH_AS_NONE; - } + process_basic_auth (hc, auth); } else /* Check bearer token */ { - if (NULL != hc->instance) + enum TALER_ErrorCode ec; + ec = process_bearer_auth (hc, auth); + if (TALER_EC_NONE != ec) { - if (GNUNET_is_zero (&hc->instance->auth.auth_hash)) - { - /* hash zero means no authentication for instance */ - hc->auth_scope = TMH_AS_ALL; - } - else - { - enum TALER_ErrorCode ec; - - ec = TMH_check_token (auth, - hc->instance->settings.id, - &hc->auth_scope); - if (TALER_EC_NONE != ec) - { - char *dec; - size_t dec_len; - const char *token; - - /* NOTE: Deprecated, remove sometime after v1.1 */ - if (0 != strncasecmp (auth, - RFC_8959_PREFIX, - strlen (RFC_8959_PREFIX))) - { - GNUNET_break_op (0); - return TALER_MHD_reply_with_ec (connection, - ec, - NULL); - } - GNUNET_log (GNUNET_ERROR_TYPE_WARNING, - "Trying deprecated secret-token:password API authN\n"); - token = auth + strlen (RFC_8959_PREFIX); - dec_len = GNUNET_STRINGS_urldecode (token, - strlen (token), - &dec); - if ( (0 == dec_len) || - (GNUNET_OK != - TMH_check_auth (dec, - &hc->instance->auth.auth_salt, - &hc->instance->auth.auth_hash)) ) - { - GNUNET_log (GNUNET_ERROR_TYPE_WARNING, - "Login failed\n"); - hc->auth_scope = TMH_AS_NONE; - } - else - { - hc->auth_scope = TMH_AS_ALL; - } - GNUNET_free (dec); - } - } - } - else - { - hc->auth_scope = TMH_AS_NONE; + return TALER_MHD_reply_with_ec (connection, + ec, + NULL); } } /* We grant access if: - - scope is 'all' - - rh has an explicit non-NONE scope that matches - - scope is 'read only' and we have a GET request */ + - Endpoint does not require permissions + - Authorization scope of bearer token contains permissions + required by endpoint. + */ if ( (NULL != hc->rh->permission) && (! permission_in_scope (hc->rh->permission, hc->auth_scope)))