commit 053ab084a6ba5598326aa214d8692b54092416d4
parent 44d052a35e0964eee72a79b53b37131dc4f8c6d5
Author: Martin Schanzenbach <schanzen@gnunet.org>
Date: Mon, 7 Jul 2025 16:10:10 +0200
refactor authorization logic
Diffstat:
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)))