GET /roles/{role}/users returns user PII without the per-user view fi… - #51192
Conversation
There was a problem hiding this comment.
Pull request overview
Fixes CVE-2026-17059 by filtering realm-role members according to FGAP V1 user-view permissions.
Changes:
- Applies per-user authorization filtering to role-member results.
- Adds access metadata and a realm-role regression test.
Reviewed changes
Copilot reviewed 2 out of 2 changed files in this pull request and generated 1 comment.
| File | Description |
|---|---|
RoleContainerResource.java |
Filters role members by user-view permission. |
UserResourceTypeFilteringTest.java |
Adds realm-role member filtering coverage. |
| if (!AdminPermissionsSchema.SCHEMA.isAdminPermissionsEnabled(realm)) { | ||
| members = members.filter(usersEvaluator::canView); |
| } | ||
|
|
||
| return members.map(user -> { | ||
| UserRepresentation userRep = ModelToRepresentation.toRepresentation(session, user, briefRep); |
There was a problem hiding this comment.
One might also consider PII disclosure if the returned attributes are not based on the user profile settings. We had similar issues in the past where, in addition to the view check, we had to build the representation via the user profile provider.
Perhaps we should just return (of course, in addition to the view check) the id and username when resolving users associated with a role? And whenever returning users associated with some other realm resource?
pedroigor
left a comment
There was a problem hiding this comment.
It LGTM if we are only fixing for now the missing view check.
I'm OK to have a follow-up on filtering user attributes based on the user profile settings.
…lter with FGAP V1 Closes keycloak#51142 Signed-off-by: vramik <vramik@redhat.com>
d8f7813 to
4518e4d
Compare
| boolean briefRep = Boolean.TRUE.equals(briefRepresentation); | ||
| UserPermissionEvaluator usersEvaluator = auth.users(); | ||
|
|
||
| return session.users().getRoleMembersStream(realm, role, firstResult, maxResults) | ||
| .map((u) -> ModelToRepresentation.toRepresentation(session, u, briefRep)); | ||
| Stream<UserModel> members = session.users().getRoleMembersStream(realm, role, firstResult, maxResults); | ||
|
|
||
| if (!AdminPermissionsSchema.SCHEMA.isAdminPermissionsEnabled(realm)) { | ||
| members = members.filter(usersEvaluator::canView); | ||
| } |
| String myadminId = realm.admin().users().search("myadmin").get(0).getId(); | ||
| realm.admin().users().get(myadminId).roles().clientLevel(realmMgmtClientId).add(List.of(viewRealmRole)); | ||
| realm.cleanup().add(r -> r.users().get(r.users().search("myadmin").get(0).getId()) | ||
| .roles().clientLevel(realmMgmtClientId).remove(List.of(viewRealmRole))); |
|
Created #51233. |
…lter with FGAP V1
Closes #51142