Repository navigation
Allow extending fine-grained admin permissions for groups to their children - #55019
baptistegh wants to merge 5 commits into
Conversation
…ildren Adds an opt-in "Extend to children" option (extendChildren in the REST API) to admin permissions for specific groups. When enabled, the permission also applies to all the subgroups of the selected groups, including subgroups created later, so that the administration of a group hierarchy can be delegated. A subgroup's own permission for a scope takes precedence over inherited ones. For group scopes, the closest ancestor with an extended permission decides. For member scopes, a deny from any extended ancestor wins, consistently with the existing member scope cascading. Inherited permissions are also taken into account when searching groups and users. Closes keycloak#43865 Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> Signed-off-by: Baptiste Gouhoury <gouhoury.baptiste@gmail.com>
When evaluating users, the member scopes of any ancestor permission, extended or not, already protect the members of a group. Group checks and searches only considered extended permissions, so a deny without extend children on an intermediate group was ignored for the members of its subgroups in group member checks and user searches, while users were denied when accessed directly. Once a member scope is inherited from an extended ancestor, the permissions of the other ancestors for that scope are now also evaluated, and an inherited grant no longer reaches the descendants of a group whose own permission denies. Also adds tests for the composition with user evaluation and documents the inheritance through groups with a permission that is not extended and the behavior of permissions without policies. Closes keycloak#43865 Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> Signed-off-by: Baptiste Gouhoury <gouhoury.baptiste@gmail.com>
There was a problem hiding this comment.
🟡 Changes recommended
An unresolved authorization flaw exposes descendant members in search/count despite ancestor denies, and resource UUIDs bypass validation of invalid extendChildren settings.
2 open issues
What changed in this PR
Adds opt-in inheritance for fine-grained group admin permissions, allowing delegated administrators to manage existing and newly created subgroups without separate grants.
Changes:
- Adds API support, persistence, and validation for
extendChildren. - Implements inherited permission evaluation and expands group and user searches.
- Adds an Admin Console switch, integration and UI tests, and documentation.
| File | Description |
|---|---|
| tests/base/src/test/java/org/keycloak/tests/admin/authz/fgap/GroupResourceTypeExtendChildrenTest.java | Tests inheritance, precedence, validation, and search. |
| server-spi-private/src/main/java/org/keycloak/authorization/fgap/evaluation/partial/ScopeRestrictedPolicy.java | Restricts inherited permissions to applicable scopes. |
| server-spi-private/src/main/java/org/keycloak/authorization/fgap/evaluation/partial/PartialEvaluator.java | Integrates inheritance into search filtering and separates cache contexts. |
| server-spi-private/src/main/java/org/keycloak/authorization/fgap/evaluation/partial/GroupResourceTypePolicyEvaluator.java | Evaluates ancestor permissions for group access. |
| server-spi-private/src/main/java/org/keycloak/authorization/fgap/evaluation/partial/ExtendChildrenPartialEvaluator.java | Resolves inherited search allowances and denials. |
| server-spi-private/src/main/java/org/keycloak/authorization/fgap/evaluation/FGAPPolicyEvaluator.java | Registers group-specific evaluation. |
| server-spi-private/src/main/java/org/keycloak/authorization/fgap/evaluation/FGAPEvaluation.java | Gives inherited grants precedence over all-groups permissions. |
| server-spi-private/src/main/java/org/keycloak/authorization/fgap/AdminPermissionsSchema.java | Validates and detects extended permissions. |
| js/libs/keycloak-admin-client/src/defs/policyRepresentation.ts | Exposes the flag in the TypeScript client. |
| js/apps/admin-ui/test/permissions/main.spec.ts | Tests switch visibility and persistence. |
| js/apps/admin-ui/src/permissions-configuration/resource-types/ResourceType.tsx | Adds the group permission switch. |
| js/apps/admin-ui/maven-resources/theme/keycloak.v2/admin/messages/messages_en.properties | Adds explanatory help text. |
| docs/documentation/server_admin/topics/admin-console-permissions/fine-grain-v2.adoc | Documents inheritance rules and limitations. |
| docs/documentation/release_notes/topics/27_0_0.adoc | Announces group hierarchy delegation. |
| core/src/main/java/org/keycloak/representations/idm/authorization/ScopePermissionRepresentation.java | Adds the nullable API property. |
| authz/policy/common/src/main/java/org/keycloak/authorization/policy/provider/permission/ScopePolicyProviderFactory.java | Persists and serializes the flag. |
Review effort: Balanced
A member scope grant extended from a group was propagated to its descendants in group and user searches even when the group itself or one of its ancestors had a permission denying that scope, while direct checks denied the members. The grant is no longer propagated in that case. Also rejects extending permissions to children when the resource for all groups is referenced by the id of its authorization resource, which was accepted and then silently treated as not extended. Closes keycloak#43865 Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> Signed-off-by: Baptiste Gouhoury <gouhoury.baptiste@gmail.com>
There was a problem hiding this comment.
🟡 Changes recommended
The evaluator treats missing permissions as denials even though permissions are filtered by the current admin, incorrectly hiding descendants from authorized group search results.
1 open issue
2 resolved since last review
Review effort: Balanced
The permissions returned by the policy providers during partial evaluation only include those whose policies reference the current admin. Extended permissions missing from them were considered as denying access, although a permission whose policy denies another admin grants access during direct evaluation, so their descendants were wrongly hidden from searches. These permissions are now evaluated with the same logic as the others. When none of their policies can be partially evaluated, they neither grant nor deny access to the descendants, as for permissions that are not extended, instead of hiding them. Also adds tests for other policy types, several extended permissions on the same group, moving groups, user counts, the remaining member scopes, disabling or removing an extended permission, permissions for several groups, and deeper hierarchies. Closes keycloak#43865 Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> Signed-off-by: Baptiste Gouhoury <gouhoury.baptiste@gmail.com>
|
While adding more tests I hit two gaps in search. They aren't caused by this PR, they also happen without extend children:
In the extended case, if search can't decide a permission, it now just falls back to the other permissions instead of hiding the subgroups. I didn't try to fix the two gaps themselves since they're outside the scope of this PR, but I can open a separate issue if that helps. |
There was a problem hiding this comment.
🟡 Changes recommended
Critical flaws in scope filtering, deny propagation, and persisted inheritance flags cause inconsistent access decisions and can silently re-enable subtree permissions.
3 open issues
1 resolved since last review
Review effort: Balanced
| return; | ||
| } | ||
|
|
||
| policyConsumer.accept(unclaimed.size() == policy.getScopes().size() ? policy : new ScopeRestrictedPolicy(policy, unclaimed)); |
There was a problem hiding this comment.
This comes from UserResourceTypePolicyEvaluator, which this PR doesn't change.
…pecific groups The flag was kept when a client unaware of it updated a permission without sending it. After changing an extended permission to all groups, the flag was hidden but still stored, so changing it back to specific groups without sending the flag silently extended it to their subgroups again. The flag is now removed once the permission no longer targets specific groups. Closes keycloak#43865 Assisted-By: Claude Opus 5.5 <noreply@anthropic.com> Signed-off-by: Baptiste Gouhoury <gouhoury 10000 .baptiste@gmail.com>
There was a problem hiding this comment.
🔵 Needs a closer look
Human assessment is needed because the help text conflates group- and member-scope precedence, potentially misleading administrators about ancestor denies overriding closer member-scope grants.
2 resolved since last review
Review effort: Balanced
|
Hi @vramik, I answered the last Copilot thread. This PR is ready for review on my end. |
|
Thanks for the update and for marking it ready, @baptistegh. I've started going through the changes — the evolution across the 5 commits looks solid so far. I need a bit more time to finish the full review. I should get back to you by the end of next week. |


Closes #43865
Decisions
view,manage,manage-membership), a permission without the option only applies to its own groups and does not stop the inheritance. An admin can therefore be granted a subgroup while being denied its parent. This is intentional, so that only extended permissions take part in inheritance, and it is documented with an example.view-membersand the other member scopes, a deny from any parent wins. User evaluation already applies the member scopes of every ancestor permission, extended or not, so once a member scope is inherited from an extended parent, the non-extended permissions of the other parents are evaluated too. This keeps group member checks, user checks and user search consistent, and the option stays opt-in: without an extended parent, group checks are unchanged.view-membersis extended (partially add 8000 resses Unable to view childgroup users with view-members permission on the parent group #16966).Known limitations
hasExtendChildrenPermissionsloads the group permissions of the realm to check for the option. A query filtering on the config key could replace it later if needed.findByResourceType, so search ignores it while direct checks treat it as the group's own permission. For member scopes, the members of that group can appear in user search while being denied when accessed directly. I did not change the shared named query for this edge case; it is documented with the recommendation to associate at least one policy with every permission.Testing
GroupResourceTypeExtendChildrenTest(53 tests) covers validation, precedence, member scopes and their composition with user evaluation, group, role and aggregated policies (including negative policies targeting others), several extended permissions on the same group, moving groups in and out of an extended hierarchy, user search and count, all member scopes (manage-membership,impersonate-members,delegate-members,manage-membership-of-members), disabling or removing an extended permission, permissions for several groups, and deeper hierarchies with siblings. Two Playwright tests cover the permission form. All 327 tests inorg.keycloak.tests.admin.authz.fgappass with the embedded server.🤖 Generated with Claude Code