Add briefRepresentation query parameter to getUsersInRole endpoint

Closes #29480

Signed-off-by: rmartinc <rmartinc@redhat.com>
This commit is contained in:
rmartinc 2024-06-20 12:26:30 +02:00 committed by Marek Posolda
parent 6b135ff6e7
commit 592c2250fc
8 changed files with 60 additions and 22 deletions

View file

@ -120,9 +120,8 @@ public interface RoleResource {
/**
* Get role members.
* <p/>
* Returns users that have the given role, sorted by username ascending, paginated according to the query
* parameters.
* <p>Returns users that have the given role, sorted by username ascending, paginated according to the query
* parameters.</p>
*
* @param firstResult Pagination offset
* @param maxResults Pagination size
@ -135,9 +134,25 @@ public interface RoleResource {
@QueryParam("max") Integer maxResults);
/**
* Get role groups
* <p/>
* Returns groups that have the given role
* Get role members.
* <p>Returns users that have the given role, sorted by username ascending, paginated according to the query
* parameters.</p>
*
* @param briefRepresentation If the user should be returned in brief or full representation
* @param firstResult Pagination offset
* @param maxResults Pagination size
* @return a list of users with the given role
*/
@GET
@Path("users")
@Produces(MediaType.APPLICATION_JSON)
List<UserRepresentation> getUserMembers(@QueryParam("briefRepresentation") Boolean briefRepresentation,
@QueryParam("first") Integer firstResult,
@QueryParam("max") Integer maxResults);
/**
* Get role groups.
* <p>Returns groups that have the given role.</p>
*
* @return a list of groups with the given role
*/
@ -147,9 +162,8 @@ public interface RoleResource {
Set<GroupRepresentation> getRoleGroupMembers();
/**
* Get role groups
* <p/>
* Returns groups that have the given role, paginated according to the query parameters
* Get role groups.
* <p>Returns groups that have the given role, paginated according to the query parameters.</p>
*
* @param firstResult Pagination offset
* @param maxResults Pagination size
@ -162,9 +176,8 @@ public interface RoleResource {
@QueryParam("max") Integer maxResults);
/**
* Get role members
* <p/>
* Returns users that have the given role
* Get role members.
* <p>Returns users that have the given role.</p>
*
* @return a set of users with the given role
*
@ -177,9 +190,8 @@ public interface RoleResource {
Set<UserRepresentation> getRoleUserMembers();
/**
* Get role members
* <p/>
* Returns users that have the given role, paginated according to the query parameters
* Get role members.
* <p>Returns users that have the given role, paginated according to the query parameters.</p>
*
* @param firstResult Pagination offset
* @param maxResults Pagination size

View file

@ -30,6 +30,7 @@ export const UsersInRoleTab = () => {
return adminClient.clients.findUsersWithRole({
roleName: role.name!,
id: clientId,
briefRepresentation: true,
first,
max,
});
@ -37,6 +38,7 @@ export const UsersInRoleTab = () => {
return adminClient.roles.findUsersWithRole({
name: role.name!,
briefRepresentation: true,
first,
max,
});

View file

@ -139,7 +139,13 @@ export class Clients extends Resource<{ realm?: string }> {
});
public findUsersWithRole = this.makeRequest<
{ id: string; roleName: string; first?: number; max?: number },
{
id: string;
roleName: string;
briefRepresentation?: boolean;
first?: number;
max?: number;
},
UserRepresentation[]
>({
method: "GET",

View file

@ -58,7 +58,12 @@ export class Roles extends Resource<{ realm?: string }> {
});
public findUsersWithRole = this.makeRequest<
{ name: string; first?: number; max?: number },
{
name: string;
briefRepresentation?: boolean;
first?: number;
max?: number;
},
UserRepresentation[]
>({
method: "GET",

View file

@ -733,7 +733,10 @@ public class JpaUserProvider implements UserProvider, UserCredentialStore {
TypedQuery<UserEntity> query = em.createNamedQuery("usersInRole", UserEntity.class);
query.setParameter("roleId", role.getId());
return closing(paginateQuery(query, firstResult, maxResults).getResultStream().map(user -> new UserAdapter(session, realm, em, user)));
final UserProvider users = session.users();
return closing(paginateQuery(query, firstResult, maxResults).getResultStream())
.map(userEntity -> users.getUserById(realm, userEntity.getId()))
.filter(Objects::nonNull);
}
@Override

View file

@ -34,6 +34,7 @@ import org.keycloak.models.ModelDuplicateException;
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;
@ -477,6 +478,7 @@ public class RoleContainerResource extends RoleResource {
* @param roleName the role name.
* @param firstResult first result to return. Ignored if negative or {@code null}.
* @param maxResults maximum number of results to return. Ignored if negative or {@code null}.
* @param briefRepresentation Boolean which defines whether brief representations are returned (default: false)
* @return a non-empty {@code Stream} of users.
*/
@Path("{role-name}/users")
@ -486,6 +488,7 @@ public class RoleContainerResource extends RoleResource {
@Tag(name = KeycloakOpenAPI.Admin.Tags.ROLES)
@Operation( summary = "Returns a stream of users that have the specified role name.")
public Stream<UserRepresentation> getUsersInRole(final @Parameter(description = "the role name.") @PathParam("role-name") String roleName,
@Parameter(description = "Boolean which defines whether brief representations are returned (default: false)") @QueryParam("briefRepresentation") Boolean briefRepresentation,
@Parameter(description = "first result to return. Ignored if negative or {@code null}.") @QueryParam("first") Integer firstResult,
@Parameter(description = "maximum number of results to return. Ignored if negative or {@code null}.") @QueryParam("max") Integer maxResults) {
@ -498,8 +501,11 @@ public class RoleContainerResource extends RoleResource {
throw new NotFoundException("Could not find role");
}
final Function<UserModel, UserRepresentation> toRepresentation = briefRepresentation != null && briefRepresentation
? ModelToRepresentation::toBriefRepresentation
: user -> ModelToRepresentation.toRepresentation(session, realm, user);
return session.users().getRoleMembersStream(realm, role, firstResult, maxResults)
.map(user -> ModelToRepresentation.toRepresentation(session, realm, user));
.map(toRepresentation);
}
/**

View file

@ -239,8 +239,10 @@ public class ClientRolesTest extends AbstractClientTest {
// pagination
List<UserRepresentation> usersInRole1 = roleResource.getUserMembers(0, 5);
assertEquals(createUsernames(0, 5), extractUsernames(usersInRole1));
List<UserRepresentation> usersInRole2 = roleResource.getUserMembers(5, 10);
Assert.assertNotNull("Not in full representation", usersInRole1.get(0).getNotBefore());
List<UserRepresentation> usersInRole2 = roleResource.getUserMembers(true, 5, 10);
assertEquals(createUsernames(5, 10), extractUsernames(usersInRole2));
Assert.assertNull("Not in brief representation", usersInRole2.get(0).getNotBefore());
}
private static List<String> createUsernames(int startIndex, int endIndex) {

View file

@ -352,12 +352,14 @@ public class RealmRolesTest extends AbstractAdminTest {
List<UserRepresentation> roleUserMembers = roleResource.getUserMembers(0, 1);
assertEquals(Collections.singletonList("test-role-member"), extractUsernames(roleUserMembers));
Assert.assertNotNull("Not in full representation", roleUserMembers.get(0).getNotBefore());
roleUserMembers = roleResource.getUserMembers(1, 1);
roleUserMembers = roleResource.getUserMembers(true, 1, 1);
assertThat(roleUserMembers, hasSize(1));
assertEquals(Collections.singletonList("test-role-member2"), extractUsernames(roleUserMembers));
Assert.assertNull("Not in brief representation", roleUserMembers.get(0).getNotBefore());
roleUserMembers = roleResource.getUserMembers(2, 1);
roleUserMembers = roleResource.getUserMembers(true, 2, 1);
assertThat(roleUserMembers, is(empty()));
}