Add Client resource type and scopes to authorization schema
keycloak/keycloak#36880
Golden recall: 33.3%
Extra findings: 2
Golden Comments (1/3 found)
| # | Comment | Severity | Status | FriendlyReviewer Detail |
|---|---|---|---|---|
| #1 | Inconsistent feature flag bug causing orphaned permissions — event listener cleanup is gated by ADMIN_FINE_GRAINED_AUTHZ (V1) instead of V2. If V2 is enabled but V1 is not, cleanup never runs |
HIGH | ✓ Found | FR: "Event listener cleanup is gated only for V1; V2 lacks lifecycle management for removed clients/roles/groups." Exactly identified. |
| #2 | hasPermission(ClientModel client, String scope) uses findByName(server, client.getId(), server.getId()) but getOrCreateResource creates resources with resourceServer.getClientId() — lookup never finds per-client resources |
HIGH | ✗ Missed | FR did not identify that findByName uses the wrong parameter compared to resource creation. |
| #3 | getClientsWithPermission(String scope) iterates resourceStore.findByType(server, CLIENTS_RESOURCE_TYPE) and returns resource.getName() — but per-client resources have no type, so only the type-level "Clients" resource is found |
HIGH | ✗ Missed | FR did not detect that resourceStore.findByType misses per-client resources (no type), making the method inoperative. |
Supplementary Findings (8 findings)
| Finding | File | Severity | Legitimate? |
|---|---|---|---|
Potential NullPointerException when getResourceTypeResource returns null — findByResource crashes | ClientPermissionsV2.java:203 | HIGH | ✓ Real bug |
Assertion assertThat(existing, nullValue()) expects null but findAll() returns a List (empty) — will fail at runtime | UserResourceTypePermissionTest.java:132 | MEDIUM | ✓ Test bug |
Unused static import TOKEN_EXCHANGE | ClientPermissionsV2.java:1 | LOW | ✓ Dead code |
| Unused logger | ClientPermissionsV2.java:34 | LOW | ✓ Cleanup |
Dead method getEvaluationContext not used | ClientPermissionsV2.java:249 | LOW | ✓ Dead code |
Fragile test: indexed access getDefaultClientScopes().get(1) assumes at least 2 scopes | PermissionClientTest.java:159 | LOW | ✓ Fragile |
| Test verifies success by absence of exception — missing explicit assertions | PermissionClientTest.java:347 | LOW | ✓ Incomplete test |
getPolicies() is non-static while other methods are static | AbstractPermissionTest.java:73 | LOW | ✓ Inconsistency |
Honest Analysis
FriendlyReviewer's full review
As posted on the PR — the complete output from FriendlyReviewer.
Hi there 👋
| 🌥️ Tech |
|---|
| 2 high, 1 medium, 6 low |
The MR introduces the Client resource type to the FGAP V2 authorization schema, including new ClientPermissionsV2 implementation, scopes, and integration tests. The overall design is sound, but several issues require attention: a potential NullPointerException in permission checks, missing cleanup event handling for V2 resources, a test assertion bug that expects null instead of an empty list, a fragile test relying on index-based access, and minor dead code/unused imports. Addressing these will improve correctness, reliability, and maintainability.
These might need a close look
- 🔴 services/src/main/java/org/keycloak/services/resources/admin/permissions/ClientPermissionsV2.java (L203)
Potential NullPointerException whengetResourceTypeResourcereturns null, leading to NPE infindByResource. - 🔴 services/src/main/java/org/keycloak/services/resources/admin/permissions/AdminPermissions.java (L74)
Event listener cleanup is gated only for V1; V2 lacks lifecycle management for removed clients/roles/groups.
Worth checking
- 🟡 testsuite/integration-arquillian/tests/base/src/test/java/org/keycloak/testsuite/admin/authz/fgap/UserResourceTypePermissionTest.java (L132)
Assertion bug:assertThat(existing, nullValue())expects null, butfindAll()returns an empty List, not null. This assertion will fail at runtime. Change toassertThat(existing, empty()).
Small things (take or leave)
- 🔵 services/src/main/java/org/keycloak/services/resources/admin/permissions/ClientPermissionsV2.java (L1)
Unused static import:TOKEN_EXCHANGE. Remove it. - 🔵 services/src/main/java/org/keycloak/services/resources/admin/permissions/ClientPermissionsV2.java (L34)
Unused logger. Remove it. - 🔵 services/src/main/java/org/keycloak/services/resources/admin/permissions/ClientPermissionsV2.java (L249)
Dead code:getEvaluationContextmethod is defined but never called anywhere in the codebase. Remove it. - 🔵 testsuite/integration-arquillian/tests/base/src/test/java/org/keycloak/testsuite/admin/authz/fgap/PermissionClientTest.java (L159)
Fragile test:getDefaultClientScopes().get(1)assumes at least 2 default client scopes exist. Use a named/descriptive check instead. - 🔵 testsuite/integration-arquillian/tests/base/src/test/java/org/keycloak/testsuite/admin/authz/fgap/PermissionClientTest.java (L347)
Test only checks that no exception is thrown, without asserting the expected outcome. Add explicit assertions on the response. - 🔵 testsuite/integration-arquillian/tests/base/src/test/java/org/keycloak/testsuite/admin/authz/fgap/AbstractPermissionTest.java (L73)
Inconsistency:getPolicies()is non-static while all other utility methods in this class are static. Make it static for consistency.
FriendlyReviewer found 1 of 3 golden bugs (the V1 vs V2 feature flag issue, #1) but missed the two resource lookup bugs (#2 and #3) which are subtler problems requiring understanding of how the authorization store creates versus looks up per-client resources.
The 8 supplementary findings include a HIGH-severity NPE when
getResourceTypeResourcereturns null, and a test bug whereassertThat(existing, nullValue())will fail at runtime becausefindAll()returns a List (empty), not null.The remaining 6 findings are mostly LOW-severity: dead code, unused imports, fragile tests. This is a noticeable drop in finding quality compared to other PRs — more noise and less deep analysis.