Skip to content

Add Client resource type and scopes to authorization schema#6

Open
akshayutture-augment wants to merge 1 commit into
feature-clients-authz-baselinefrom
feature-clients-authz-implementation
Open

Add Client resource type and scopes to authorization schema#6
akshayutture-augment wants to merge 1 commit into
feature-clients-authz-baselinefrom
feature-clients-authz-implementation

Conversation

@akshayutture-augment

@akshayutture-augment akshayutture-augment commented Nov 14, 2025

Copy link
Copy Markdown

Test 5

Summary by CodeRabbit

Release Notes

  • New Features
    • Enhanced fine-grained authorization for client management with support for granular permission controls, including configure, manage, view, and role mapping capabilities
    • Introduced improved permission evaluation system for client-scoped access control and permission checking

✏️ Tip: You can customize this high-level summary in your review settings.


Replicated from ai-code-review-evaluation/keycloak-coderabbit#5

…valuation implementation for ClientsPermissionsV2

Closes #35564

Signed-off-by: Martin Kanis <mkanis@redhat.com>
@akshayutture-augment

Copy link
Copy Markdown
Author

@coderabbitai review

@akshayutture-augment

Copy link
Copy Markdown
Author

@coderabbitai review

@coderabbitai

coderabbitai Bot commented Nov 22, 2025

Copy link
Copy Markdown
✅ Actions performed

Review triggered.

Note: CodeRabbit is an incremental review system and does not re-review already reviewed commits. This command is applicable only when automatic reviews are paused.

@coderabbitai

coderabbitai Bot commented Nov 22, 2025

Copy link
Copy Markdown

Walkthrough

This pull request introduces fine-grained authorization support for Keycloak admin clients by adding a new CLIENTS resource type with associated scopes and a V2 permission evaluation implementation, alongside event listener feature-flag guarding and comprehensive test coverage.

Changes

Cohort / File(s) Summary
Authorization Schema
server-spi-private/src/main/java/org/keycloak/authorization/AdminPermissionsSchema.java
Added CLIENTS resource type with scopes (CONFIGURE, MANAGE, VIEW, MAP_ROLES, MAP_ROLES_CLIENT_SCOPE, MAP_ROLES_COMPOSITE); new resolveClient method to resolve clients by ID or clientId; updated constructor to register both USERS and CLIENTS resource types.
Event Listener Guards
services/src/main/java/org/keycloak/services/resources/admin/permissions/AdminPermissions.java
Wrapped RoleRemovedEvent, ClientRemovedEvent, and GroupRemovedEvent handling within Admin Fine-Grained Authz feature flag conditional; event listener now guarded with realm resolution logic.
Client Permission API
services/src/main/java/org/keycloak/services/resources/admin/permissions/ClientPermissionEvaluator.java
Expanded interface with 26+ new methods for capability checks: canManage/canView/canList/canConfigure variants, role-mapping checks (canMapRoles, canMapCompositeRoles, canMapClientScopeRoles), and aggregation methods (getAccess, getClientsWithPermission).
V2 Permission Implementation
services/src/main/java/org/keycloak/services/resources/admin/permissions/ClientPermissionsV2.java
New class implementing V2 permission evaluation for clients; provides scope-based checks via hasPermission helpers, delegation to MgmtPermissionsV2, and per-client evaluation context; UnsupportedOperationException for legacy methods.
V2 Permissions Manager
services/src/main/java/org/keycloak/services/resources/admin/permissions/MgmtPermissionsV2.java
Added clientPermissions field and clients() method for lazy initialization of ClientPermissionsV2 instance.
Test Infrastructure
tests/base/src/test/java/org/keycloak/tests/admin/authz/fgap/AbstractPermissionTest.java
Refactored helper methods to static variants parameterized by ManagedClient; added policy creation utilities (createUserPolicy, createClientPolicy, createAllPermission) for flexible permission composition.
Client Permission Tests
tests/base/src/test/java/org/keycloak/tests/admin/authz/fgap/PermissionClientTest.java
New comprehensive test suite with 5 test cases validating MANAGE, CONFIGURE, VIEW, and MAP_ROLES scopes across single/all client operations; verifies permission grants, forbidden responses, and scope-specific access control.
Integration Tests
tests/base/src/test/java/org/keycloak/tests/admin/authz/fgap/PermissionRESTTest.java, UserResourceTypeEvaluationTest.java, UserResourceTypePermissionTest.java
Updated to pass client context to createPermission and getScopePermissionsResource calls; refactored to use new static helper methods with ManagedClient and realm parameters.

Sequence Diagram(s)

sequenceDiagram
    participant Admin as Admin Client
    participant Schema as AdminPermissionsSchema
    participant Evaluator as ClientPermissionEvaluator
    participant V2 as ClientPermissionsV2
    participant AuthzProvider as AuthorizationProvider

    Admin->>Evaluator: canManage(ClientModel)
    Evaluator->>V2: Delegate permission check
    V2->>V2: hasPermission(client, "manage")
    V2->>AuthzProvider: Evaluate scope "manage"
    AuthzProvider-->>V2: Permission granted?
    V2-->>Evaluator: boolean result
    Evaluator-->>Admin: boolean result
Loading
sequenceDiagram
    participant Event as Keycloak Event
    participant Listener as AdminPermissions Listener
    participant FeatureFlag as FGAP Feature Flag
    participant Handler as Permission Handler

    Event->>Listener: RoleRemovedEvent/ClientRemovedEvent
    Listener->>FeatureFlag: Is Admin Fine-Grained Authz enabled?
    alt Feature enabled
        FeatureFlag-->>Listener: true
        Listener->>Handler: Process event (resolve realm/update permissions)
        Handler-->>Listener: Complete
    else Feature disabled
        FeatureFlag-->>Listener: false
        Listener-->>Event: Skip processing
    end
Loading

Estimated code review effort

🎯 4 (Complex) | ⏱️ ~45 minutes

  • AdminPermissionsSchema.java: Logic changes for resource resolution and new scopes require validation of resolver implementation and integration points.
  • ClientPermissionsV2.java: New class with 18+ overridden methods; careful review needed for consistency across permission delegation patterns, evaluation context setup, and UnsupportedOperationException rationale.
  • ClientPermissionEvaluator.java: 26+ interface methods added; verify naming conventions, documentation accuracy, and method overload consistency.
  • PermissionClientTest.java: 5 new test cases; validate test coverage completeness and assertions for each permission scope (MANAGE, CONFIGURE, VIEW, MAP_ROLES).
  • Test refactoring across 4 files: Verify all call sites correctly updated to pass client context and that behavioral intent is preserved.

Poem

🐰 A CLIENTS resource blooms, five scopes in sight,
With CONFIGURE, MANAGE, VIEW burning bright,
V2 permission checks dance through the code,
Evaluators guide us down the admin road,
Fine-grained access, feature-flagged just right!

Pre-merge checks and finishing touches

❌ Failed checks (2 warnings)
Check name Status Explanation Resolution
Description check ⚠️ Warning The description is largely incomplete and only contains placeholder text ('Test 5') without substantive implementation details, requirements, or context. Provide a detailed description following the template, explaining the motivation, implementation approach, and any related changes or considerations.
Docstring Coverage ⚠️ Warning Docstring coverage is 22.94% which is insufficient. The required threshold is 80.00%. You can run @coderabbitai generate docstrings to improve docstring coverage.
✅ Passed checks (1 passed)
Check name Status Explanation
Title check ✅ Passed The title 'Add Client resource type and scopes to authorization schema' accurately and concisely describes the primary change in the changeset.
✨ Finishing touches
  • 📝 Generate docstrings
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Post copyable unit tests in a comment
  • Commit unit tests in branch feature-clients-authz-implementation

Comment @coderabbitai help to get the list of available commands and usage tips.

@coderabbitai coderabbitai Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 2

Caution

Some comments are outside the diff and can’t be posted inline due to platform limitations.

⚠️ Outside diff range comments (1)
services/src/main/java/org/keycloak/services/resources/admin/permissions/AdminPermissions.java (1)

45-50: V2 evaluator overload likely ignoring target realm

In evaluator(KeycloakSession session, RealmModel realm, RealmModel adminsRealm, UserModel admin), the V2 branch currently calls:

return new MgmtPermissionsV2(session, adminsRealm, admin);

whereas the non-V2 branch calls:

return new MgmtPermissions(session, realm, adminsRealm, admin);

Given MgmtPermissionsV2 defines a matching 4‑arg constructor (session, realm, adminsRealm, admin), this V2 path appears to drop the realm context and treat adminsRealm as the managed realm, which is likely incorrect when admin and managed realms differ.

Consider switching to the 4‑arg constructor to maintain parity with the v1 behavior:

return new MgmtPermissionsV2(session, realm, adminsRealm, admin);
🧹 Nitpick comments (8)
server-spi-private/src/main/java/org/keycloak/authorization/AdminPermissionsSchema.java (1)

91-95: Client resolution logic mirrors user resolution; consider future-friendly naming

resolveClient correctly tries ID first and then clientId, and getOrCreateResource now handles CLIENTS symmetrically with USERS, which fits the FGAP model.

If you later need human-friendly labels for client resources (similar to "All users" in getResourceName), you might add a CLIENTS branch there as well; not required for functionality now but could improve diagnostics/UI messages.

Also applies to: 175-184

tests/base/src/test/java/org/keycloak/tests/admin/authz/fgap/PermissionClientTest.java (4)

126-189: testManageOnlyOneClient exercises per-client MANAGE semantics; minor test nits

The test correctly verifies:

  • All relevant client operations fail with ForbiddenException before granting MANAGE.
  • After granting MANAGE for myclient only, view/list/update/default-scope operations are allowed for that client.
  • Cross-client update (realmClient) remains forbidden.

Two minor test-quality suggestions:

  • Using JUnit 5 assertThrows(ForbiddenException.class, ...) would simplify the try/catch + fail() pattern.
  • Accessing getDefaultClientScopes().get(1) assumes at least two default scopes; consider asserting the size first to avoid brittle failures if realm config changes.

191-247: testConfigureOnlyOneClient cleanly separates CONFIGURE vs MANAGE responsibilities

This test nicely captures that CONFIGURE on a single client enables secrets and read-only scope operations, while scope membership changes still require MANAGE. Same small nits as above:

  • Prefer assertThrows over manual try/catch where practical.
  • Guard defaultClientScopes.get(0) with a size assertion to avoid hard index assumptions.

283-323: testViewAllClients covers VIEW-all-clients vs write operations; watch the create(null) call

The test clearly distinguishes:

  • No listing/mapper access before VIEW permission.
  • After granting VIEW-all-clients, listing and reading mappers are allowed, but creating mappers and clients remains forbidden.

One potential fragility: clients().create(null) relies on the admin client tolerating a null representation. If the client implementation changes to validate input earlier, this might start returning a different status or error. Passing a minimal ClientRepresentation instead would be more robust.


325-365: testMapRolesAndCompositesOnlyOneClient correctly combines user+client MAP_ scopes*

The test sensibly:

  • Pre-creates client roles with an unrestricted admin.
  • Asserts both client-level role assignment and composite addition are forbidden for myadmin initially.
  • Grants MAP_ROLES on the user plus MAP_ROLES, MAP_ROLES_COMPOSITE, and CONFIGURE on the specific client, then verifies both operations succeed.

Optional: if the test framework ever reuses the same realm instance, registering cleanup for the created client roles (similar to what you do for policies) would keep state tidy.

tests/base/src/test/java/org/keycloak/tests/admin/authz/fgap/AbstractPermissionTest.java (1)

117-161: User/client policy helpers with cleanup are useful; consider making cleanup more robust

The new createUserPolicy, createClientPolicy, and createAllPermission helpers:

  • Centralize policy construction.
  • Assert on 201 Created.
  • Register realm cleanup to remove the created policy.

Two optional robustness tweaks:

  • Capture the created policy ID from the Location header or initial create response (if available) and store it, instead of re-resolving via findByName(name) at cleanup time.
  • If names are ever reused, this avoids ambiguity around which policy instance gets removed.
services/src/main/java/org/keycloak/services/resources/admin/permissions/ClientPermissionsV2.java (2)

124-145: getClientsWithPermission/hasGrantedPermission behavior is coherent; ensure callers expect client IDs

getClientsWithPermission walks all resources of type CLIENTS_RESOURCE_TYPE and uses hasGrantedPermission to filter those having the given scope, returning resource.getName() for each.

Given how resources are created (per-client resources named by client.getId() and an “all-clients” resource named "Clients"), this effectively returns:

  • Specific client IDs with the requested permission.
  • Potentially the "Clients" resource name if a global permission is defined.

That’s reasonable, but downstream code must treat these as resource names/client IDs, not display names. If any consumer expects clientId (the human-readable identifier), you’ll need a translation step.

Also applies to: 274-286


262-272: getEvaluationContext is currently unused; consider removing or documenting intended usage

getEvaluationContext builds a client-based EvaluationContext with a kc.client.id attribute but is not referenced anywhere in this class. If there’s no planned use, you can drop it to avoid dead code; otherwise a brief comment about the intended consumer would help future readers.

📜 Review details

Configuration used: CodeRabbit UI

Review profile: CHILL

Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between baa6ebb and 7e55408.

📒 Files selected for processing (10)
  • server-spi-private/src/main/java/org/keycloak/authorization/AdminPermissionsSchema.java (3 hunks)
  • services/src/main/java/org/keycloak/services/resources/admin/permissions/AdminPermissions.java (1 hunks)
  • services/src/main/java/org/keycloak/services/resources/admin/permissions/ClientPermissionEvaluator.java (2 hunks)
  • services/src/main/java/org/keycloak/services/resources/admin/permissions/ClientPermissionsV2.java (1 hunks)
  • services/src/main/java/org/keycloak/services/resources/admin/permissions/MgmtPermissionsV2.java (2 hunks)
  • tests/base/src/test/java/org/keycloak/tests/admin/authz/fgap/AbstractPermissionTest.java (3 hunks)
  • tests/base/src/test/java/org/keycloak/tests/admin/authz/fgap/PermissionClientTest.java (3 hunks)
  • tests/base/src/test/java/org/keycloak/tests/admin/authz/fgap/PermissionRESTTest.java (2 hunks)
  • tests/base/src/test/java/org/keycloak/tests/admin/authz/fgap/UserResourceTypeEvaluationTest.java (16 hunks)
  • tests/base/src/test/java/org/keycloak/tests/admin/authz/fgap/UserResourceTypePermissionTest.java (9 hunks)
🧰 Additional context used
🪛 Gitleaks (8.29.0)
services/src/main/java/org/keycloak/services/resources/admin/permissions/ClientPermissionsV2.java

[high] 53-53: Detected a Generic API Key, potentially exposing access to various services and sensitive operations.

(generic-api-key)

🔇 Additional comments (14)
services/src/main/java/org/keycloak/services/resources/admin/permissions/MgmtPermissionsV2.java (1)

29-68: Client permissions wiring matches existing pattern

The new clientPermissions field and clients() lazy initialization mirror the existing users() implementation and correctly construct ClientPermissionsV2(session, realm, authz, this). Looks consistent and safe.

tests/base/src/test/java/org/keycloak/tests/admin/authz/fgap/PermissionRESTTest.java (1)

128-174: Tests correctly migrated to client-aware createPermission helper

All updated usages of createPermission consistently pass client and preserve the original permission shapes and expected statuses. No behavioral regressions apparent.

services/src/main/java/org/keycloak/services/resources/admin/permissions/AdminPermissions.java (1)

77-95: Confirm feature flag used to guard admin-permission cleanup listener

The listener is now fully guarded by Profile.Feature.ADMIN_FINE_GRAINED_AUTHZ, and the new RoleRemovedEvent handling correctly derives the realm from either a ClientModel container or a RealmModel container before disabling permissions on the removed role.

Given other entry points in this class switch to V2 based on ADMIN_FINE_GRAINED_AUTHZ_V2, please confirm that gating this listener solely on ADMIN_FINE_GRAINED_AUTHZ (and not also on the V2 flag) matches the intended feature-flag semantics and deployment scenarios.

tests/base/src/test/java/org/keycloak/tests/admin/authz/fgap/UserResourceTypePermissionTest.java (1)

67-201: User permission tests correctly switched to client-scoped helpers

All uses of getScopePermissionsResource(...) and createPermission(...) are consistently updated to pass client, and the permission definitions and assertions remain unchanged. The cleanup in @AfterEach also correctly uses the client-scoped resource. Change set looks sound.

tests/base/src/test/java/org/keycloak/tests/admin/authz/fgap/UserResourceTypeEvaluationTest.java (1)

66-367: Evaluation tests cleanly migrated to client-scoped helpers with centralized usersType

Introducing usersType = AdminPermissionsSchema.USERS.getType() removes repetition, and all permission/policy helper calls are consistently updated to pass client and, where needed, usersType. The assertions and overall scenarios remain intact, so behavior should be preserved while aligning with the new client-scoped API surface.

server-spi-private/src/main/java/org/keycloak/authorization/AdminPermissionsSchema.java (1)

54-69: CLIENTS resource type and scopes are wired consistently

The new CLIENTS_RESOURCE_TYPE, associated scopes, and CLIENTS ResourceType registration in the constructor look coherent and match the usages in V2 permissions and tests. No issues spotted with the schema wiring.

Also applies to: 73-73

tests/base/src/test/java/org/keycloak/tests/admin/authz/fgap/PermissionClientTest.java (3)

70-83: Realm admin client injection and per-test permission cleanup look sound

The @InjectAdminClient/@InjectClient fixtures plus onAfter() cleanup against getScopePermissionsResource(client) provide a clear separation between the realm admin under test and the FGAP admin-permissions client. This should keep scope permissions isolated across tests.


85-105: Policy-type tests correctly exercise FGAP-aware permissions endpoint

Routing testUnsupportedPolicyTypes/testSupportedPolicyTypes through getPermissionsResource(client) ensures these assertions are made against the admin-permissions client’s authorization server, which is what the schema is defining. The scope policy test using USERS resource type/scopes also matches the schema.


249-281: testManageAllClients validates global MANAGE-all-clients permission

The flow (create denied before permission, empty list, granting createAllPermission(... MANAGE), then successful create/delete/list) aligns well with the intended “all-clients” MANAGE semantics and the new CLIENTS resource type.

tests/base/src/test/java/org/keycloak/tests/admin/authz/fgap/AbstractPermissionTest.java (2)

163-179: Overloaded createPermission helpers build correct scope permissions

The overloaded createPermission methods correctly:

  • Set logic, resourceType, scopes, and resources with the given resourceId.
  • Attach all provided policies by name.
  • Delegate persistence to the static createPermission helper.

The construction and wiring are consistent with how tests use them for both per-object and all-clients permissions.


54-72: All call sites correctly use new static helper signatures; verification passed

The shell script confirms that all usages of the static permission helpers have been properly migrated:

  • getPermissionsResource(client) and getScopePermissionsResource(client) are consistently called with the new static signatures across all test classes
  • No legacy no-arg calls to getPermissionsResource() or getScopePermissionsResource() remain
  • All static createPermission calls pass ManagedClient client as the required first parameter
  • The overloaded createPermission methods with resourceId and other parameters are appropriately defined as protected instance methods in the base test class and called correctly from subclasses
services/src/main/java/org/keycloak/services/resources/admin/permissions/ClientPermissionsV2.java (3)

57-123: V2 client and client-scope permission checks align with schema and evaluator docs

The implementations of:

  • canConfigure(ClientModel), canManage(ClientModel), canManage()
  • canView(ClientModel), canView()
  • canMapRoles, canMapCompositeRoles, canMapClientScopeRoles
  • canManageClientScopes, canManage(ClientScopeModel), canView(ClientScopeModel)

are consistent with:

  • Role-based shortcuts (AdminRoles.MANAGE_CLIENTS/VIEW_CLIENTS).
  • FGAP V2 scopes from AdminPermissionsSchema (MANAGE/VIEW/CONFIGURE/MAP_*).
  • The documented behavior in ClientPermissionEvaluator.

The fallbacks to global “all-clients” permissions via hasPermission(String scope) complement the per-client checks well.


237-260: Global hasPermission(String scope) implementation is straightforward and matches schema

The global-scope checker correctly:

  • Ensures same-realm admin and non-null ResourceServer.
  • Resolves the "Clients" resource by name/owner.
  • Evaluates permissions and checks for the requested scope.

The early resource == null guard already protects against missing type-resources here, so no changes needed.


147-205: V2 code paths are properly segregated via feature flag; confirm at runtime that unsupported methods are not invoked

The factory pattern correctly routes to V2 implementations when ADMIN_FINE_GRAINED_AUTHZ_V2 is enabled (AdminPermissions.java lines 40–70). ClientPermissionsV2 extends ClientPermissions and overrides all legacy methods to throw UnsupportedOperationException.

The concern is valid: verify that when V2 is active, code paths don't accidentally trigger these exceptions. Static analysis shows proper segregation via feature flags and exception guards, but runtime execution flow (particularly around parent class initialization and inherited method chains) should be manually verified to confirm V2 flows never call exchangeToPermission(), mapRolesPermission(), getPermissions(), etc.

Comment on lines +95 to 103
/**
* Returns {@code true} if {@link #canView()} returns {@code true}.
*/
void requireView();

/**
* Returns {@code true} if {@link #canViewClientScopes()} returns {@code true}.
*/
void requireViewClientScopes();

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue | 🟡 Minor

Fix misleading Javadoc on requireView/requireViewClientScopes

Both methods are void “require” guards but their Javadoc says “Returns true if …”, which is inconsistent with the rest of the API (and with how other require* methods are documented).

Suggested wording:

-    /**
-     * Returns {@code true} if {@link #canView()} returns {@code true}.
-     */
+    /**
+     * Throws ForbiddenException if {@link #canView()} returns {@code false}.
+     */
     void requireView();
@@
-    /**
-     * Returns {@code true} if {@link #canViewClientScopes()} returns {@code true}.
-     */
+    /**
+     * Throws ForbiddenException if {@link #canViewClientScopes()} returns {@code false}.
+     */
     void requireViewClientScopes();

This matches the intent and the pattern used by the other require* methods.

📝 Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

Suggested change
/**
* Returns {@code true} if {@link #canView()} returns {@code true}.
*/
void requireView();
/**
* Returns {@code true} if {@link #canViewClientScopes()} returns {@code true}.
*/
void requireViewClientScopes();
/**
* Throws ForbiddenException if {@link #canView()} returns {@code false}.
*/
void requireView();
/**
* Throws ForbiddenException if {@link #canViewClientScopes()} returns {@code false}.
*/
void requireViewClientScopes();
🤖 Prompt for AI Agents
In
services/src/main/java/org/keycloak/services/resources/admin/permissions/ClientPermissionEvaluator.java
around lines 95 to 103, the Javadoc for requireView and requireViewClientScopes
incorrectly says “Returns true if …”; update the comments to reflect that these
are void guard methods that enforce permission and throw an exception when the
permission is missing (e.g. "Requires view permission for the client; throws
ForbiddenException if not allowed.") and similarly for client scopes ("Requires
view permission for client scopes; throws ForbiddenException if not allowed."),
matching the pattern used by the other require* methods.

Comment on lines +207 to +235
private boolean hasPermission(ClientModel client, String scope) {
if (!root.isAdminSameRealm()) {
return false;
}
ResourceServer server = root.realmResourceServer();
if (server == null) return false;

Resource resource = resourceStore.findByName(server, client.getId(), server.getId());
if (resource == null) {
// check if there is permission for "all-clients". If so, load its resource and proceed with evaluation
resource = AdminPermissionsSchema.SCHEMA.getResourceTypeResource(session, server, AdminPermissionsSchema.CLIENTS_RESOURCE_TYPE);

if (authz.getStoreFactory().getPolicyStore().findByResource(server, resource).isEmpty()) {
return false;
}
}

Collection<Permission> permissions = root.evaluatePermission(new ResourcePermission(resource, resource.getScopes(), server), server);
List<String> expectedScopes = Arrays.asList(scope);
for (Permission permission : permissions) {
for (String s : permission.getScopes()) {
if (expectedScopes.contains(s)) {
return true;
}
}
}

return false;
}

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue | 🟠 Major

Guard against missing type-resource to avoid potential NPE in hasPermission(ClientModel, ...)

If resourceStore.findByName(server, client.getId(), server.getId()) returns null, you fall back to:

resource = AdminPermissionsSchema.SCHEMA.getResourceTypeResource(session, server, AdminPermissionsSchema.CLIENTS_RESOURCE_TYPE);

if (authz.getStoreFactory().getPolicyStore().findByResource(server, resource).isEmpty()) {
    return false;
}

However, getResourceTypeResource can legitimately return null (e.g., FGAP V2 disabled or the “Clients” resource missing), which would lead to a NullPointerException inside findByResource. Safer to bail out when the type-resource is absent.

Suggested fix:

-        Resource resource =  resourceStore.findByName(server, client.getId(), server.getId());
+        Resource resource =  resourceStore.findByName(server, client.getId(), server.getId());
         if (resource == null) {
             // check if there is permission for "all-clients". If so, load its resource and proceed with evaluation
-            resource = AdminPermissionsSchema.SCHEMA.getResourceTypeResource(session, server, AdminPermissionsSchema.CLIENTS_RESOURCE_TYPE);
-
-            if (authz.getStoreFactory().getPolicyStore().findByResource(server, resource).isEmpty()) {
+            resource = AdminPermissionsSchema.SCHEMA.getResourceTypeResource(session, server, AdminPermissionsSchema.CLIENTS_RESOURCE_TYPE);
+            if (resource == null) {
+                return false;
+            }
+            if (authz.getStoreFactory().getPolicyStore().findByResource(server, resource).isEmpty()) {
                 return false;
             }
         }

This keeps the semantics while avoiding a hard failure in misconfigured realms.

🤖 Prompt for AI Agents
In
services/src/main/java/org/keycloak/services/resources/admin/permissions/ClientPermissionsV2.java
around lines 207 to 235, the fallback call to
AdminPermissionsSchema.SCHEMA.getResourceTypeResource(...) can return null which
leads to a possible NullPointerException when passing that null into
authz.getStoreFactory().getPolicyStore().findByResource(...); update the method
to check whether the returned type-resource is null and immediately return false
(or otherwise bail out) before calling findByResource, so findByResource is
never invoked with a null resource.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants