apache/druid · error · ForbiddenException

<authResult.getErrorMessage()>

Error message

<authResult.getErrorMessage()>

What it means

AuthorizationUtils.verifyUnrestrictedAccessToDatasource throws ForbiddenException when the authenticated user is authorized to access a datasource only with restrictions (policies/row filters), not with unrestricted access. Druid throws it because some HTTP resources require full, unfiltered access to a datasource before serving data. The message carries the authorizer-supplied error text explaining why access was restricted or denied.

Solutions

  1. Grant the user's role unrestricted READ (and required action) permissions on the datasource in the authorizer configuration.
  2. Remove or relax row-level policies that restrict the datasource for this user.
  3. Verify the correct authenticator/authorizer chain is configured so the user maps to the intended roles.
  4. If the operation legitimately should be restricted, use an API that supports filtered access instead.

Example fix

// before: user has row-filtered READ only -> 403 ForbiddenException
// after: in authorizer config, grant full datasource permission
//   authorizer:
//     roles:
//       datasourceReader:
//         permissions:
//           - resource: {name: "wikipedia", type: "DATASOURCE"}
//             action: READ
//           - resource: {name: ".*", type: "DATASOURCE"}
//             action: READ
//         users: ["alice"]
Defensive patterns

Strategy: try-catch

Validate before calling

final Access access = AuthorizationUtils.authorizeResourceAction(req, resourceAction, authorizerMapper);
if (!access.isAllow()) { /* skip call or request higher privilege */ }

Type guard

if (authorizerMapper == null || authorizerMapper.getAuthorizer(authResult.getAuthorizerName()) == null) { /* misconfigured authorizer; fix config before calling */ }

Try / catch

try { AuthorizationUtils.verifyUnrestrictedAccessToDatasource(req, datasource, authorizerMapper); }
catch (ForbiddenException e) { log.warn("Unrestricted access denied for datasource %s: %s", datasource, e.getMessage()); }

Prevention

When it happens

Trigger: Calling an endpoint that routes through verifyUnrestrictedAccessToDatasource (e.g. datasource metadata/lookup HTTP resources) while the user's authorizer grants basic access but applies a policy, or denies access entirely.

Common situations: Users granted READ with row-level filters attempting to use endpoints that require full table access; overly restrictive role definitions in the authorizer config; newly created users lacking an authorization role.

Understand the failure class

Background: Permission denied / not authorized / 403 Forbidden: access-control rejections when the caller lacks the required role, grant, or ownership — this error's family across 18 libraries.

Related errors


AI-assisted analysis of apache/druid@9b90983fd2 (2026-09-07). Data as JSON: /api/errors/44ab48da67d86c94. Report an issue: GitHub.

Appendix: source

Thrown at server/src/main/java/org/apache/druid/server/security/AuthorizationUtils.java:132

      AuthorizerMapper authorizerMapper
  )
  {
    ResourceAction resourceAction = createDatasourceResourceAction(datasource, req);
    AuthorizationResult authResult = authorizeResourceAction(req, resourceAction, authorizerMapper);
    if (!authResult.allowAccessWithNoRestriction()) {
      if (authResult.allowBasicAccess()) {
        // Basic access was granted, but access was restricted by a policy.
        // This is checked to avoid double emitting the forbidden metric if the basic access was denied,
        // since the authorizeResourceAction method already emits the metric in that case.
        emitAuthMetric(
            authorizerMapper.getServiceEmitter(),
            authenticationResultFromRequest(req),
            resourceAction,
            METRIC_FORBIDDEN,
            authResult.getErrorMessage()
        );
      }
      throw new ForbiddenException(authResult.getErrorMessage());
    }
  }

  /**
   * Returns the authentication information for a request.
   *
   * @param request http request
   * @return authentication result
   * @throws IllegalStateException if the request was not authenticated
   */
  public static AuthenticationResult authenticationResultFromRequest(final HttpServletRequest request)
  {
    final AuthenticationResult authenticationResult = (AuthenticationResult) request.getAttribute(
        AuthConfig.DRUID_AUTHENTICATION_RESULT
    );

    if (authenticationResult == null) {
      throw new ISE("Null authentication result");

View on GitHub (pinned to 9b90983fd2)