KEYCLOAK-4978 Add endpoint to get groups by role
This commit is contained in:
parent
a3c175a21e
commit
e18fb56389
7 changed files with 133 additions and 2 deletions
|
@ -17,6 +17,7 @@
|
|||
|
||||
package org.keycloak.admin.client.resource;
|
||||
|
||||
import org.keycloak.representations.idm.GroupRepresentation;
|
||||
import org.keycloak.representations.idm.ManagementPermissionReference;
|
||||
import org.keycloak.representations.idm.ManagementPermissionRepresentation;
|
||||
import org.keycloak.representations.idm.RoleRepresentation;
|
||||
|
@ -127,4 +128,30 @@ public interface RoleResource {
|
|||
Set<UserRepresentation> getRoleUserMembers(@QueryParam("first") Integer firstResult,
|
||||
@QueryParam("max") Integer maxResults);
|
||||
|
||||
/**
|
||||
* Get role groups
|
||||
* <p/>
|
||||
* Returns groups that have the given role
|
||||
*
|
||||
* @return a list of groups with the given role
|
||||
*/
|
||||
@GET
|
||||
@Path("groups")
|
||||
@Produces(MediaType.APPLICATION_JSON)
|
||||
Set<GroupRepresentation> getRoleGroupMembers();
|
||||
|
||||
/**
|
||||
* Get role groups
|
||||
* <p/>
|
||||
* Returns groups that have the given role, paginated according to the query parameters
|
||||
*
|
||||
* @param firstResult Pagination offset
|
||||
* @param maxResults Pagination size
|
||||
* @return a list of groups with the given role
|
||||
*/
|
||||
@GET
|
||||
@Path("groups")
|
||||
@Produces(MediaType.APPLICATION_JSON)
|
||||
Set<GroupRepresentation> getRoleGroupMembers(@QueryParam("first") Integer firstResult,
|
||||
@QueryParam("max") Integer maxResults);
|
||||
}
|
||||
|
|
|
@ -879,6 +879,11 @@ public class RealmCacheSession implements CacheRealmProvider {
|
|||
return getRealmDelegate().getGroupsCountByNameContaining(realm, search);
|
||||
}
|
||||
|
||||
@Override
|
||||
public List<GroupModel> getGroupsByRole(RealmModel realm, RoleModel role, int firstResult, int maxResults) {
|
||||
return getRealmDelegate().getGroupsByRole(realm, role, firstResult, maxResults);
|
||||
}
|
||||
|
||||
@Override
|
||||
public List<GroupModel> getTopLevelGroups(RealmModel realm) {
|
||||
String cacheKey = getTopGroupsQueryCacheKey(realm.getId());
|
||||
|
|
|
@ -386,6 +386,25 @@ public class JpaRealmProvider implements RealmProvider {
|
|||
return (long) searchForGroupByName(realm, search, null, null).size();
|
||||
}
|
||||
|
||||
@Override
|
||||
public List<GroupModel> getGroupsByRole(RealmModel realm, RoleModel role, int firstResult, int maxResults) {
|
||||
TypedQuery<GroupEntity> query = em.createNamedQuery("groupsInRole", GroupEntity.class);
|
||||
query.setParameter("roleId", role.getId());
|
||||
if (firstResult != -1) {
|
||||
query.setFirstResult(firstResult);
|
||||
}
|
||||
if (maxResults != -1) {
|
||||
query.setMaxResults(maxResults);
|
||||
}
|
||||
List<GroupEntity> results = query.getResultList();
|
||||
|
||||
return results.stream()
|
||||
.map(g -> new GroupAdapter(realm, em, g))
|
||||
.sorted(Comparator.comparing(GroupModel::getName))
|
||||
.collect(Collectors.collectingAndThen(
|
||||
Collectors.toList(), Collections::unmodifiableList));
|
||||
}
|
||||
|
||||
@Override
|
||||
public List<GroupModel> getTopLevelGroups(RealmModel realm) {
|
||||
RealmEntity ref = em.getReference(RealmEntity.class, realm.getId());
|
||||
|
|
|
@ -34,6 +34,7 @@ import java.io.Serializable;
|
|||
* @version $Revision: 1 $
|
||||
*/
|
||||
@NamedQueries({
|
||||
@NamedQuery(name="groupsInRole", query="select g from GroupRoleMappingEntity m, GroupEntity g where m.roleId=:roleId and g.id=m.group"),
|
||||
@NamedQuery(name="groupHasRole", query="select m from GroupRoleMappingEntity m where m.group = :group and m.roleId = :roleId"),
|
||||
@NamedQuery(name="groupRoleMappings", query="select m from GroupRoleMappingEntity m where m.group = :group"),
|
||||
@NamedQuery(name="groupRoleMappingIds", query="select m.roleId from GroupRoleMappingEntity m where m.group = :group"),
|
||||
|
|
|
@ -44,6 +44,8 @@ public interface RealmProvider extends Provider, ClientProvider {
|
|||
|
||||
Long getGroupsCountByNameContaining(RealmModel realm, String search);
|
||||
|
||||
List<GroupModel> getGroupsByRole(RealmModel realm, RoleModel role, int firstResult, int maxResults);
|
||||
|
||||
List<GroupModel> getTopLevelGroups(RealmModel realm);
|
||||
|
||||
List<GroupModel> getTopLevelGroups(RealmModel realm, Integer first, Integer max);
|
||||
|
|
|
@ -23,6 +23,7 @@ import org.keycloak.events.admin.OperationType;
|
|||
import org.keycloak.events.admin.ResourceType;
|
||||
import org.keycloak.models.ClientModel;
|
||||
import org.keycloak.models.Constants;
|
||||
import org.keycloak.models.GroupModel;
|
||||
import org.keycloak.models.KeycloakSession;
|
||||
import org.keycloak.models.ModelDuplicateException;
|
||||
import org.keycloak.models.RealmModel;
|
||||
|
@ -30,6 +31,7 @@ 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;
|
||||
import org.keycloak.representations.idm.RoleRepresentation;
|
||||
import org.keycloak.representations.idm.UserRepresentation;
|
||||
|
@ -41,6 +43,7 @@ import org.keycloak.services.resources.admin.permissions.AdminPermissions;
|
|||
import javax.ws.rs.BadRequestException;
|
||||
import javax.ws.rs.Consumes;
|
||||
import javax.ws.rs.DELETE;
|
||||
import javax.ws.rs.DefaultValue;
|
||||
import javax.ws.rs.GET;
|
||||
import javax.ws.rs.POST;
|
||||
import javax.ws.rs.PUT;
|
||||
|
@ -54,6 +57,7 @@ import javax.ws.rs.core.UriInfo;
|
|||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
import java.util.Set;
|
||||
import java.util.stream.Collectors;
|
||||
|
||||
/**
|
||||
* @resource Roles
|
||||
|
@ -402,4 +406,35 @@ public class RoleContainerResource extends RoleResource {
|
|||
return results;
|
||||
|
||||
}
|
||||
|
||||
/**
|
||||
* Return List of Groups that have the specified role name
|
||||
*
|
||||
*
|
||||
* @param roleName
|
||||
* @param firstResult
|
||||
* @param maxResults
|
||||
* @param fullRepresentation if true, return a full representation of the GroupRepresentation objects
|
||||
* @return
|
||||
*/
|
||||
@Path("{role-name}/groups")
|
||||
@GET
|
||||
@Produces(MediaType.APPLICATION_JSON)
|
||||
@NoCache
|
||||
public List<GroupRepresentation> getGroupsInRole(final @PathParam("role-name") String roleName,
|
||||
@QueryParam("first") Integer firstResult,
|
||||
@QueryParam("max") Integer maxResults,
|
||||
@QueryParam("full") @DefaultValue("false") boolean fullRepresentation) {
|
||||
|
||||
auth.roles().requireView(roleContainer);
|
||||
firstResult = firstResult != null ? firstResult : 0;
|
||||
maxResults = maxResults != null ? maxResults : Constants.DEFAULT_MAX_RESULTS;
|
||||
|
||||
RoleModel role = roleContainer.getRole(roleName);
|
||||
List<GroupModel> groupsModel = session.realms().getGroupsByRole(realm, role, firstResult, maxResults);
|
||||
|
||||
return groupsModel.stream()
|
||||
.map(g -> ModelToRepresentation.toRepresentation(g, fullRepresentation))
|
||||
.collect(Collectors.toList());
|
||||
}
|
||||
}
|
||||
|
|
|
@ -25,6 +25,7 @@ import org.keycloak.admin.client.resource.UserResource;
|
|||
import org.keycloak.events.admin.OperationType;
|
||||
import org.keycloak.events.admin.ResourceType;
|
||||
import org.keycloak.representations.idm.ClientRepresentation;
|
||||
import org.keycloak.representations.idm.GroupRepresentation;
|
||||
import org.keycloak.representations.idm.RoleRepresentation;
|
||||
import org.keycloak.representations.idm.UserRepresentation;
|
||||
import org.keycloak.testsuite.Assert;
|
||||
|
@ -110,6 +111,11 @@ public class RealmRolesTest extends AbstractAdminTest {
|
|||
getCleanup().addRoleId(ids.get("role-without-users"));
|
||||
getCleanup().addUserId(adminClient.realm(REALM_NAME).users().search(userRep.getUsername()).get(0).getId());
|
||||
|
||||
GroupRepresentation groupRep = new GroupRepresentation();
|
||||
groupRep.setName("test-role-group");
|
||||
groupRep.setPath("/test-role-group");
|
||||
adminClient.realm(REALM_NAME).groups().add(groupRep);
|
||||
getCleanup().addGroupId(adminClient.realm(REALM_NAME).groups().groups().get(0).getId());
|
||||
|
||||
resource = adminClient.realm(REALM_NAME).roles();
|
||||
|
||||
|
@ -122,7 +128,7 @@ public class RealmRolesTest extends AbstractAdminTest {
|
|||
assertAdminEvents.assertEvent(realmId, OperationType.CREATE, AdminEventPaths.clientRoleResourcePath(clientUuid, "role-c"), roleC, ResourceType.CLIENT_ROLE);
|
||||
|
||||
assertAdminEvents.assertEvent(realmId, OperationType.CREATE, AdminEventPaths.userResourcePath(adminClient.realm(REALM_NAME).users().search(userRep.getUsername()).get(0).getId()), userRep, ResourceType.USER);
|
||||
|
||||
assertAdminEvents.assertEvent(realmId, OperationType.CREATE, AdminEventPaths.groupPath(adminClient.realm(REALM_NAME).groups().groups().get(0).getId()), groupRep, ResourceType.GROUP);
|
||||
|
||||
}
|
||||
|
||||
|
@ -221,6 +227,7 @@ public class RealmRolesTest extends AbstractAdminTest {
|
|||
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* KEYCLOAK-2035 Verifies that Role with no users assigned is being properly retrieved without members in API endpoint for role membership
|
||||
*/
|
||||
|
@ -234,6 +241,41 @@ public class RealmRolesTest extends AbstractAdminTest {
|
|||
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* KEYCLOAK-4978 Verifies that Groups assigned to Role are being properly retrieved as members in API endpoint for role membership
|
||||
*/
|
||||
@Test
|
||||
public void testGroupsInRole() {
|
||||
RoleResource role = resource.get("role-with-users");
|
||||
|
||||
List<GroupRepresentation> groups = adminClient.realm(REALM_NAME).groups().groups();
|
||||
GroupRepresentation groupRep = groups.stream().filter(g -> g.getPath().equals("/test-role-group")).findFirst().get();
|
||||
|
||||
RoleResource roleResource = adminClient.realm(REALM_NAME).roles().get(role.toRepresentation().getName());
|
||||
List<RoleRepresentation> rolesToAdd = new LinkedList<>();
|
||||
rolesToAdd.add(roleResource.toRepresentation());
|
||||
adminClient.realm(REALM_NAME).groups().group(groupRep.getId()).roles().realmLevel().add(rolesToAdd);
|
||||
|
||||
roleResource = adminClient.realm(REALM_NAME).roles().get(role.toRepresentation().getName());
|
||||
|
||||
Set<GroupRepresentation> groupsInRole = roleResource.getRoleGroupMembers();
|
||||
assertTrue(groupsInRole.stream().filter(g -> g.getPath().equals("/test-role-group")).findFirst().isPresent());
|
||||
}
|
||||
|
||||
/**
|
||||
* KEYCLOAK-4978 Verifies that Role with no users assigned is being properly retrieved without groups in API endpoint for role membership
|
||||
*/
|
||||
@Test
|
||||
public void testGroupsNotInRole() {
|
||||
RoleResource role = resource.get("role-without-users");
|
||||
|
||||
role = adminClient.realm(REALM_NAME).roles().get(role.toRepresentation().getName());
|
||||
|
||||
Set<GroupRepresentation> groupsInRole = role.getRoleGroupMembers();
|
||||
assertTrue(groupsInRole.isEmpty());
|
||||
}
|
||||
|
||||
/**
|
||||
* KEYCLOAK-2035 Verifies that Role Membership is ok after user removal
|
||||
*/
|
||||
|
|
Loading…
Reference in a new issue