provectus/kafka-ui · error · AccessDeniedException

Access denied

Error message

Access denied

What it means

AccessControlService.validateAccess() checks RBAC permission for the target resource. When the request carries application-config actions, it resolves the current authenticated user and asks isApplicationConfigAccessible(); if no RBAC role grants the user that access it throws Spring's AccessDeniedException with message 'Access denied', which surfaces as HTTP 403.

Solutions

  1. Add or fix an RBAC role granting the user's role/subject the `applicationconfig` resource with the needed action (view/edit)
  2. Check roles.yml: resource name must be `applicationconfig`, actions lowercase, subject must match the authenticated user's principal/group
  3. Verify the user actually authenticates with the identity (group/username) the role targets

Example fix

// roles.yml before
roles:
  - name: viewer
    resources: [topic]
    actions: [read]
// after
roles:
  - name: viewer
    resources: [topic, applicationconfig]
    actions: [read]
Defensive patterns

Strategy: try-catch

Validate before calling

// Check before calling: does any role grant applicationconfig access to this user?
boolean canEditAppConfig = roles.stream().anyMatch(r ->
    r.getResources().contains("applicationconfig") && r.getActions().contains("edit"));

Try / catch

webClient.get()
  .uri("/api/config")
  .retrieve()
  .onStatus(HttpStatusCode::is4xxClientForbidden,
             r -> Mono.error(new IllegalStateException("User lacks applicationconfig RBAC grant")))
  .bodyToMono(String.class);

Prevention

When it happens

Trigger: Calling an endpoint that touches application config (e.g. dynamic config GET/PUT) while logged in as a user whose RBAC roles do not include the `applicationconfig` resource with the required action (view/edit).

Common situations: Deploying RBAC roles for admins only, then a regular user opens the dynamic-config screen; role name or subject typo in roles.yml so the grant never matches; JWT/OAuth user whose groups differ from role subjects.

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.


AI-assisted analysis of provectus/kafka-ui@83b5a60cc0 (2026-09-08). Data as JSON: /api/errors/f7faf3aa9588a44d. Report an issue: GitHub.

Appendix: source

Thrown at kafka-ui-api/src/main/java/com/provectus/kafka/ui/service/rbac/AccessControlService.java:109

    if (!properties.getRoles().isEmpty()
        && "oauth2".equalsIgnoreCase(environment.getProperty("auth.type"))
        && (clientRegistrationRepository == null || !clientRegistrationRepository.iterator().hasNext())) {
      log.error("Roles are configured but no authentication methods are present. Authentication might fail.");
    }
  }

  public Mono<Void> validateAccess(AccessContext context) {
    if (!rbacEnabled) {
      return Mono.empty();
    }

    if (CollectionUtils.isNotEmpty(context.getApplicationConfigActions())) {
      return getUser()
          .doOnNext(user -> {
            boolean accessGranted = isApplicationConfigAccessible(context, user);

            if (!accessGranted) {
              throw new AccessDeniedException(ACCESS_DENIED);
            }
          }).then();
    }

    return getUser()
        .doOnNext(user -> {
          boolean accessGranted =
              isApplicationConfigAccessible(context, user)
                  && isClusterAccessible(context, user)
                  && isClusterConfigAccessible(context, user)
                  && isTopicAccessible(context, user)
                  && isConsumerGroupAccessible(context, user)
                  && isConnectAccessible(context, user)
                  && isConnectorAccessible(context, user) // TODO connector selectors
                  && isSchemaAccessible(context, user)
                  && isKsqlAccessible(context, user)
                  && isAclAccessible(context, user)
                  && isAuditAccessible(context, user);

View on GitHub (pinned to 83b5a60cc0)