apolloconfig/apollo · error · AccessDeniedException

Access is denied

Error message

Access is denied

What it means

Thrown by ItemController.requireConfigReadForUserToken when a USER_TOKEN identity attempts to read items/config for a namespace that is hidden from the current user. The shouldHideConfigToCurrentUser check evaluates the token holder's role against namespace visibility rules. Maps to HTTP 403 AccessDeniedException.

Source

Thrown at apollo-portal/src/main/java/com/ctrip/framework/apollo/openapi/v1/controller/ItemController.java:312

      return operator;
    }

    throw new BadRequestException("Unsupported auth type: %s", authType);
  }

  private boolean shouldHideConfigToPortalUser(String appId, String env, String clusterName,
      String namespaceName) {
    return UserIdentityConstants.USER.equals(UserIdentityContextHolder.getAuthType())
        && unifiedPermissionValidator.shouldHideConfigToCurrentUser(appId, env, clusterName,
            namespaceName);
  }

  private void requireConfigReadForUserToken(String appId, String env, String clusterName,
      String namespaceName) {
    if (UserIdentityConstants.USER_TOKEN.equals(UserIdentityContextHolder.getAuthType())
        && unifiedPermissionValidator.shouldHideConfigToCurrentUser(appId, env, clusterName,
            namespaceName)) {
      throw new AccessDeniedException("Access is denied");
    }
  }

  private void requireSyncNamespacesReadableForUserToken(OpenNamespaceSyncDTO model) {
    if (!UserIdentityConstants.USER_TOKEN.equals(UserIdentityContextHolder.getAuthType())) {
      return;
    }
    for (OpenNamespaceIdentifier namespaceIdentifier : model.getSyncToNamespaces()) {
      requireConfigReadForUserToken(namespaceIdentifier.getAppId(), namespaceIdentifier.getEnv(),
          namespaceIdentifier.getClusterName(), namespaceIdentifier.getNamespaceName());
    }
  }

  private OpenItemPageDTO emptyPage(Integer page, Integer size) {
    OpenItemPageDTO result = new OpenItemPageDTO();
    result.setPage(page);
    result.setSize(size);
    result.setTotal(0L);

View on GitHub (pinned to d95fc18d11)

Solutions

  1. Grant the token holder's account edit or release permission on the target namespace through the Portal.
  2. Ensure the USER_TOKEN has config:read scope and is authorized for the target appId.
  3. Switch to a CONSUMER token with appropriate namespace permissions for the target app.
Defensive patterns

Strategy: validation

Validate before calling

// Before item read with USER_TOKEN, verify the namespace is not hidden
if (authType.equals("USER_TOKEN") && isNamespaceHidden(appId, env, clusterName, namespaceName)) {
    throw new SecurityException("Namespace is hidden from the current user. Grant edit/release permission.");
}

Try / catch

try {
    return client.get("/openapi/v1/envs/" + env + "/apps/" + appId + "/clusters/" + clusterName
        + "/namespaces/" + namespaceName + "/items");
} catch (AccessDeniedException e) {
    logger.warn("USER_TOKEN denied item read on namespace. Check namespace visibility and token scope.");
    throw e;
}

Prevention

When it happens

Trigger: GET item-by-namespace or item-diff endpoints with a USER_TOKEN where the namespace is restricted and the token holder lacks edit/release permission. The method only throws for USER_TOKEN; interactive USER identity gets silent empty results via shouldHideConfigToPortalUser instead.

Common situations: A personal access token scoped to one app is used to read items from a namespace in another app that is hidden. A namespace is marked as restricted after the token was issued, so previously-working reads now fail with 403.

Related errors


AI-assisted analysis of apolloconfig/apollo@d95fc18d11 (2026-08-14). Data as JSON: /api/errors/31518dea8cf2c578. Report an issue: GitHub.