Skip to content

FGAP V2: isInternal denylist missing admin-permissions client allows delegated admin to update it #51630

Description

@abstractj

Description

ClientPermissions.isInternal denylists only the realm-management client (and, in the master realm, client IDs ending in the -realm suffix). The admin-permissions client — the resource server holding the realm's entire FGAP V2 model — is not in that list.

As a result, a delegated administrator holding a FGAP Clients:manage permission (and no realm-management roles) can update and disable the admin-permissions client through the Admin API. The API's own access report (getAccess) correctly returns manage: false for this client, but the enforcement path in canManage does not agree — it falls through to the FGAP permission check and allows the write.

This is a gap in the isInternal denylist introduced in 26.7.0. No privilege escalation results from this — FGAP enforcement resolves the client by internal ID, not by clientId, and disabling the client does not relax the delegated admin's own restrictions.

Version affected

26.7.0 and main. The isInternal guard was introduced in 26.7.0; 26.6.x has a different code path and is not affected.

Expected behavior

The admin-permissions client should be treated as internal, consistent with realm-management. A delegated admin with Clients:manage should not be able to update or disable it. The enforcement path and the access report should agree.

Actual behavior

  • isInternal returns false for the admin-permissions client
  • canManage falls through to the FGAP permission check, which grants access via Clients:manage
  • getAccess returns manage: false (it special-cases this client), contradicting enforcement
  • The delegated admin can set serviceAccountsEnabled, add redirect URIs, enable directAccessGrantsEnabled, and rename clientId
  • Deleting the client is correctly refused (HTTP 400)

Steps to reproduce

  1. Start Keycloak 26.7.0 with default features (FGAP V2 enabled by default).
  2. In a realm, enable adminPermissionsEnabled (FGAP V2 configuration).
  3. Create a delegated admin user with a FGAP Clients:manage permission but no realm-management roles.
  4. As the delegated admin, GET /admin/realms/{realm}/clients/{admin-permissions-client-id} — note access.manage = false.
  5. As the delegated admin, PUT /admin/realms/{realm}/clients/{admin-permissions-client-id} with a modified body (e.g., add a redirect URI) — observe HTTP 204 success.
  6. As the master admin, verify the change persisted.

Root cause

ClientPermissions.isInternal (line 719) checks only for realm-management and -realm suffix clients. Constants.ADMIN_PERMISSIONS_CLIENT_ID is missing from the predicate. The getAccess method at line 676 correctly special-cases this client, but canManage at line 63 does not mirror the same check.

Suggested fix

Add Constants.ADMIN_PERMISSIONS_CLIENT_ID to ClientPermissions.isInternal. This is the smallest change and matches how realm-management is already handled.

Acknowledgement

Mahdi Alhakim (GitHub: mahdi-al-hakim)


This issue was originally tracked in the private repository. Migrated by @abstractj.

Metadata

Metadata

Assignees

No one assigned

    Type

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions