Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
Expand Up @@ -41,6 +41,7 @@
import jakarta.ws.rs.core.Response;
import jakarta.ws.rs.core.UriInfo;

import org.keycloak.authorization.fgap.AdminPermissionsSchema;
import org.keycloak.common.Profile;
import org.keycloak.common.util.Encode;
import org.keycloak.events.admin.OperationType;
Expand All @@ -52,6 +53,7 @@
import org.keycloak.models.RealmModel;
import org.keycloak.models.RoleContainerModel;
import org.keycloak.models.RoleModel;
import org.keycloak.models.UserModel;
import org.keycloak.models.utils.ModelToRepresentation;
import org.keycloak.representations.idm.GroupRepresentation;
import org.keycloak.representations.idm.ManagementPermissionReference;
Expand All @@ -62,6 +64,7 @@
import org.keycloak.services.resources.admin.fgap.AdminPermissionEvaluator;
import org.keycloak.services.resources.admin.fgap.AdminPermissionManagement;
import org.keycloak.services.resources.admin.fgap.AdminPermissions;
import org.keycloak.services.resources.admin.fgap.UserPermissionEvaluator;
import org.keycloak.utils.ProfileHelper;

import org.eclipse.microprofile.openapi.annotations.Operation;
Expand Down Expand Up @@ -597,9 +600,19 @@ public Stream<UserRepresentation> getUsersInRole(final @Parameter(description =
}

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);
Comment on lines +607 to +608
}
Comment on lines 602 to +609

return members.map(user -> {
UserRepresentation userRep = ModelToRepresentation.toRepresentation(session, user, briefRep);

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

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?

userRep.setAccess(usersEvaluator.getAccessForListing(user));
return userRep;
});
}

/**
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -568,6 +568,49 @@ public void testRoleMemberFilteringByViewPermission() {
assertThat(roleMembers, hasItems(allowedUsers.toArray(new String[0])));
}

@Test
public void testRealmRoleMemberFilteringByViewPermission() {
RoleRepresentation role = new RoleRepresentation();
role.setName("test_realm_role");
realm.admin().roles().create(role);
role = realm.admin().roles().get(role.getName()).toRepresentation();
realm.cleanup().add(r -> r.roles().deleteRole("test_realm_role"));

for (String username : List.of("user_x", "user_y", "user_z")) {
String userId = ApiUtil.getCreatedId(realm.admin().users().create(UserBuilder.create()
.username(username)
.password("password")
.firstName("user")
.lastName(username)
.email(username + "@test")
.build()));
realm.admin().users().get(userId).roles().realmLevel().add(List.of(role));
realm.cleanup().add(r -> r.users().delete(userId).close());
}

UserPolicyRepresentation policy = createUserPolicy(realm, adminPermissionsClient, "Myadmin user policy",
realm.admin().users().search("myadmin").get(0).getId());
Set<String> allowedUsers = Set.of("user_x", "user_y");
createPermission(adminPermissionsClient, allowedUsers, AdminPermissionsSchema.USERS.getType(),
Set.of(AdminPermissionsSchema.VIEW), policy);

String realmMgmtClientId = realm.admin().clients()
.findByClientId(Constants.REALM_MANAGEMENT_CLIENT_ID).get(0).getId();
RoleRepresentation viewRealmRole = realm.admin().clients().get(realmMgmtClientId)
.roles().get(AdminRoles.VIEW_REALM).toRepresentation();
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)));
Comment on lines +601 to +604

List<String> roleMembers = realmAdminClient.realm(realm.getName())
.roles().get(role.getName()).getUserMembers().stream()
.map(UserRepresentation::getUsername).toList();

assertThat(roleMembers, hasSize(allowedUsers.size()));
assertThat(roleMembers, hasItems(allowedUsers.toArray(new String[0])));
}

@Test
public void testViewGroupMembersPolicyUsingAggregatedPolicy() {
List<UserRepresentation> search = realmAdminClient.realm(realm.getName()).users().search(null, 0, 10);
Expand Down
Loading