Keycloak-11526 search and pagination for roles
This commit is contained in:
parent
73eaa38357
commit
b73553e305
22 changed files with 676 additions and 70 deletions
|
@ -21,11 +21,13 @@ import org.keycloak.representations.idm.RoleRepresentation;
|
|||
|
||||
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.Path;
|
||||
import javax.ws.rs.PathParam;
|
||||
import javax.ws.rs.Produces;
|
||||
import javax.ws.rs.QueryParam;
|
||||
import javax.ws.rs.core.MediaType;
|
||||
import java.util.List;
|
||||
|
||||
|
@ -38,6 +40,78 @@ public interface RolesResource {
|
|||
@Produces(MediaType.APPLICATION_JSON)
|
||||
List<RoleRepresentation> list();
|
||||
|
||||
/**
|
||||
* @param briefRepresentation if false, return roles with their attributes
|
||||
* @return A list containing all roles.
|
||||
*/
|
||||
@GET
|
||||
@Produces(MediaType.APPLICATION_JSON)
|
||||
List<RoleRepresentation> list(@QueryParam("briefRepresentation") @DefaultValue("true") boolean briefRepresentation);
|
||||
|
||||
/**
|
||||
* Get roles by pagination params.
|
||||
* @param search max number of occurrences
|
||||
* @param first index of the first element
|
||||
* @param max max number of occurrences
|
||||
* @return A list containing the slice of all roles.
|
||||
*/
|
||||
@GET
|
||||
@Produces(MediaType.APPLICATION_JSON)
|
||||
List<RoleRepresentation> list(@QueryParam("first") Integer firstResult,
|
||||
@QueryParam("max") Integer maxResults);
|
||||
|
||||
/**
|
||||
* Get roles by pagination params.
|
||||
* @param first index of the first element
|
||||
* @param max max number of occurrences
|
||||
* @param briefRepresentation if false, return roles with their attributes
|
||||
* @return A list containing the slice of all roles.
|
||||
*/
|
||||
@GET
|
||||
@Produces(MediaType.APPLICATION_JSON)
|
||||
List<RoleRepresentation> list(@QueryParam("first") Integer firstResult,
|
||||
@QueryParam("max") Integer maxResults,
|
||||
@QueryParam("briefRepresentation") @DefaultValue("true") boolean briefRepresentation);
|
||||
|
||||
/**
|
||||
* Get roles by pagination params.
|
||||
* @param search max number of occurrences
|
||||
* @param briefRepresentation if false, return roles with their attributes
|
||||
* @return A list containing the slice of all roles.
|
||||
*/
|
||||
@GET
|
||||
@Produces(MediaType.APPLICATION_JSON)
|
||||
List<RoleRepresentation> list(@QueryParam("search") @DefaultValue("") String search,
|
||||
@QueryParam("briefRepresentation") @DefaultValue("true") boolean briefRepresentation);
|
||||
|
||||
/**
|
||||
* Get roles by pagination params.
|
||||
* @param search max number of occurrences
|
||||
* @param first index of the first element
|
||||
* @param max max number of occurrences
|
||||
* @return A list containing the slice of all roles.
|
||||
*/
|
||||
@GET
|
||||
@Produces(MediaType.APPLICATION_JSON)
|
||||
List<RoleRepresentation> list(@QueryParam("search") @DefaultValue("") String search,
|
||||
@QueryParam("first") Integer firstResult,
|
||||
@QueryParam("max") Integer maxResults);
|
||||
|
||||
/**
|
||||
* Get roles by pagination params.
|
||||
* @param search max number of occurrences
|
||||
* @param first index of the first element
|
||||
* @param max max number of occurrences
|
||||
* @param briefRepresentation if false, return roles with their attributes
|
||||
* @return A list containing the slice of all roles.
|
||||
*/
|
||||
@GET
|
||||
@Produces(MediaType.APPLICATION_JSON)
|
||||
List<RoleRepresentation> list(@QueryParam("search") @DefaultValue("") String search,
|
||||
@QueryParam("first") Integer firstResult,
|
||||
@QueryParam("max") Integer maxResults,
|
||||
@QueryParam("briefRepresentation") @DefaultValue("true") boolean briefRepresentation);
|
||||
|
||||
@POST
|
||||
@Consumes(MediaType.APPLICATION_JSON)
|
||||
void create(RoleRepresentation roleRepresentation);
|
||||
|
|
|
@ -612,6 +612,16 @@ public class ClientAdapter implements ClientModel, CachedObject {
|
|||
return cacheSession.getClientRoles(cachedRealm, this);
|
||||
}
|
||||
|
||||
@Override
|
||||
public Set<RoleModel> getRoles(Integer first, Integer max) {
|
||||
return cacheSession.getClientRoles(cachedRealm, this, first, max);
|
||||
}
|
||||
|
||||
@Override
|
||||
public Set<RoleModel> searchForRoles(String search, Integer first, Integer max) {
|
||||
return cacheSession.searchForClientRoles(cachedRealm, this, search, first, max);
|
||||
}
|
||||
|
||||
@Override
|
||||
public int getNodeReRegistrationTimeout() {
|
||||
if (isUpdated()) return updated.getNodeReRegistrationTimeout();
|
||||
|
@ -675,5 +685,4 @@ public class ClientAdapter implements ClientModel, CachedObject {
|
|||
public int hashCode() {
|
||||
return getId().hashCode();
|
||||
}
|
||||
|
||||
}
|
||||
|
|
|
@ -974,6 +974,15 @@ public class RealmAdapter implements CachedRealmModel {
|
|||
return cacheSession.getRealmRoles(this);
|
||||
}
|
||||
|
||||
@Override
|
||||
public Set<RoleModel> getRoles(Integer first, Integer max) {
|
||||
return cacheSession.getRealmRoles(this, first, max);
|
||||
}
|
||||
|
||||
@Override
|
||||
public Set<RoleModel> searchForRoles(String search, Integer first, Integer max) {
|
||||
return cacheSession.searchForRoles(this, search, first, max);
|
||||
}
|
||||
|
||||
@Override
|
||||
public RoleModel addRole(String name) {
|
||||
|
|
|
@ -669,6 +669,27 @@ public class RealmCacheSession implements CacheRealmProvider {
|
|||
return list;
|
||||
}
|
||||
|
||||
@Override
|
||||
public Set<RoleModel> getRealmRoles(RealmModel realm, Integer first, Integer max) {
|
||||
return getRealmDelegate().getRealmRoles(realm, first, max);
|
||||
}
|
||||
|
||||
@Override
|
||||
public Set<RoleModel> getClientRoles(RealmModel realm, ClientModel client, Integer first, Integer max) {
|
||||
return getRealmDelegate().getClientRoles(realm, client, first, max);
|
||||
}
|
||||
|
||||
@Override
|
||||
public Set<RoleModel> searchForClientRoles(RealmModel realm, ClientModel client, String search, Integer first,
|
||||
Integer max) {
|
||||
return getRealmDelegate().searchForClientRoles(realm, client, search, first, max);
|
||||
}
|
||||
|
||||
@Override
|
||||
public Set<RoleModel> searchForRoles(RealmModel realm, String search, Integer first, Integer max) {
|
||||
return getRealmDelegate().searchForRoles(realm, search, first, max);
|
||||
}
|
||||
|
||||
@Override
|
||||
public RoleModel addClientRole(RealmModel realm, ClientModel client, String name) {
|
||||
return addClientRole(realm, client, KeycloakModelUtils.generateId(), name);
|
||||
|
@ -1196,4 +1217,5 @@ public class RealmCacheSession implements CacheRealmProvider {
|
|||
public void decreaseRemainingCount(RealmModel realm, ClientInitialAccessModel clientInitialAccess) {
|
||||
getRealmDelegate().decreaseRemainingCount(realm, clientInitialAccess);
|
||||
}
|
||||
|
||||
}
|
||||
|
|
|
@ -664,6 +664,16 @@ public class ClientAdapter implements ClientModel, JpaModel<ClientEntity> {
|
|||
return session.realms().getClientRoles(realm, this);
|
||||
}
|
||||
|
||||
@Override
|
||||
public Set<RoleModel> getRoles(Integer first, Integer max) {
|
||||
return session.realms().getClientRoles(realm, this, first, max);
|
||||
}
|
||||
|
||||
@Override
|
||||
public Set<RoleModel> searchForRoles(String search, Integer first, Integer max) {
|
||||
return session.realms().searchForClientRoles(realm, this, search, first, max);
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean hasScope(RoleModel role) {
|
||||
if (isFullScopeAllowed()) return true;
|
||||
|
|
|
@ -46,6 +46,7 @@ import javax.persistence.TypedQuery;
|
|||
import java.util.*;
|
||||
import java.util.stream.Collectors;
|
||||
|
||||
|
||||
/**
|
||||
* @author <a href="mailto:bill@burkecentral.com">Bill Burke</a>
|
||||
* @version $Revision: 1 $
|
||||
|
@ -294,6 +295,67 @@ public class JpaRealmProvider implements RealmProvider {
|
|||
return list;
|
||||
}
|
||||
|
||||
@Override
|
||||
public Set<RoleModel> getRealmRoles(RealmModel realm, Integer first, Integer max) {
|
||||
TypedQuery<RoleEntity> query = em.createNamedQuery("getRealmRoles", RoleEntity.class);
|
||||
query.setParameter("realm", realm.getId());
|
||||
|
||||
return getRoles(query, realm, first, max);
|
||||
}
|
||||
|
||||
@Override
|
||||
public Set<RoleModel> getClientRoles(RealmModel realm, ClientModel client, Integer first, Integer max) {
|
||||
TypedQuery<RoleEntity> query = em.createNamedQuery("getClientRoles", RoleEntity.class);
|
||||
query.setParameter("client", client.getId());
|
||||
|
||||
return getRoles(query, realm, first, max);
|
||||
}
|
||||
|
||||
protected Set<RoleModel> getRoles(TypedQuery<RoleEntity> query, RealmModel realm, Integer first, Integer max) {
|
||||
if(Objects.nonNull(first) && Objects.nonNull(max)
|
||||
&& first >= 0 && max >= 0) {
|
||||
query= query.setFirstResult(first).setMaxResults(max);
|
||||
}
|
||||
|
||||
List<RoleEntity> results = query.getResultList();
|
||||
|
||||
return results.stream()
|
||||
.map(role -> new RoleAdapter(session, realm, em, role))
|
||||
.collect(Collectors.collectingAndThen(
|
||||
Collectors.toCollection(LinkedHashSet::new), Collections::unmodifiableSet));
|
||||
}
|
||||
|
||||
@Override
|
||||
public Set<RoleModel> searchForClientRoles(RealmModel realm, ClientModel client, String search, Integer first, Integer max) {
|
||||
TypedQuery<RoleEntity> query = em.createNamedQuery("searchForClientRoles", RoleEntity.class);
|
||||
query.setParameter("client", client.getId());
|
||||
return searchForRoles(query, realm, search, first, max);
|
||||
}
|
||||
|
||||
@Override
|
||||
public Set<RoleModel> searchForRoles(RealmModel realm, String search, Integer first, Integer max) {
|
||||
TypedQuery<RoleEntity> query = em.createNamedQuery("searchForRealmRoles", RoleEntity.class);
|
||||
query.setParameter("realm", realm.getId());
|
||||
|
||||
return searchForRoles(query, realm, search, first, max);
|
||||
}
|
||||
|
||||
protected Set<RoleModel> searchForRoles(TypedQuery<RoleEntity> query, RealmModel realm, String search, Integer first, Integer max) {
|
||||
|
||||
query.setParameter("search", "%" + search.trim().toLowerCase() + "%");
|
||||
if(Objects.nonNull(first) && Objects.nonNull(max)
|
||||
&& first >= 0 && max >= 0) {
|
||||
query= query.setFirstResult(first).setMaxResults(max);
|
||||
}
|
||||
|
||||
List<RoleEntity> results = query.getResultList();
|
||||
|
||||
return results.stream()
|
||||
.map(role -> new RoleAdapter(session, realm, em, role))
|
||||
.collect(Collectors.collectingAndThen(
|
||||
Collectors.toSet(), Collections::unmodifiableSet));
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean removeRole(RealmModel realm, RoleModel role) {
|
||||
session.users().preRemove(realm, role);
|
||||
|
@ -778,4 +840,5 @@ public class JpaRealmProvider implements RealmProvider {
|
|||
model.setTimestamp(entity.getTimestamp());
|
||||
return model;
|
||||
}
|
||||
|
||||
}
|
|
@ -896,6 +896,16 @@ public class RealmAdapter implements RealmModel, JpaModel<RealmEntity> {
|
|||
return session.realms().getRealmRoles(this);
|
||||
}
|
||||
|
||||
@Override
|
||||
public Set<RoleModel> getRoles(Integer first, Integer max) {
|
||||
return session.realms().getRealmRoles(this, first, max);
|
||||
}
|
||||
|
||||
@Override
|
||||
public Set<RoleModel> searchForRoles(String search, Integer first, Integer max) {
|
||||
return session.realms().searchForRoles(this, search, first, max);
|
||||
}
|
||||
|
||||
@Override
|
||||
public RoleModel getRoleById(String id) {
|
||||
return session.realms().getRoleById(id, this);
|
||||
|
@ -2259,5 +2269,4 @@ public class RealmAdapter implements RealmModel, JpaModel<RealmEntity> {
|
|||
if (c == null) return null;
|
||||
return entityToModel(c);
|
||||
}
|
||||
|
||||
}
|
|
@ -55,14 +55,16 @@ import java.util.Set;
|
|||
@UniqueConstraint(columnNames = { "NAME", "CLIENT_REALM_CONSTRAINT" })
|
||||
})
|
||||
@NamedQueries({
|
||||
@NamedQuery(name="getClientRoles", query="select role from RoleEntity role where role.client = :client"),
|
||||
@NamedQuery(name="getClientRoles", query="select role from RoleEntity role where role.client.id = :client order by role.name"),
|
||||
@NamedQuery(name="getClientRoleIds", query="select role.id from RoleEntity role where role.client.id = :client"),
|
||||
@NamedQuery(name="getClientRoleByName", query="select role from RoleEntity role where role.name = :name and role.client = :client"),
|
||||
@NamedQuery(name="getClientRoleIdByName", query="select role.id from RoleEntity role where role.name = :name and role.client.id = :client"),
|
||||
@NamedQuery(name="getRealmRoles", query="select role from RoleEntity role where role.clientRole = false and role.realm = :realm"),
|
||||
@NamedQuery(name="searchForClientRoles", query="select role from RoleEntity role where role.client.id = :client and ( lower(role.name) like :search or lower(role.description) like :search ) order by role.name"),
|
||||
@NamedQuery(name="getRealmRoles", query="select role from RoleEntity role where role.clientRole = false and role.realm.id = :realm order by role.name"),
|
||||
@NamedQuery(name="getRealmRoleIds", query="select role.id from RoleEntity role where role.clientRole = false and role.realm.id = :realm"),
|
||||
@NamedQuery(name="getRealmRoleByName", query="select role from RoleEntity role where role.clientRole = false and role.name = :name and role.realm = :realm"),
|
||||
@NamedQuery(name="getRealmRoleIdByName", query="select role.id from RoleEntity role where role.clientRole = false and role.name = :name and role.realm.id = :realm")
|
||||
@NamedQuery(name="getRealmRoleIdByName", query="select role.id from RoleEntity role where role.clientRole = false and role.name = :name and role.realm.id = :realm"),
|
||||
@NamedQuery(name="searchForRealmRoles", query="select role from RoleEntity role where role.clientRole = false and role.realm.id = :realm and ( lower(role.name) like :search or lower(role.description) like :search ) order by role.name"),
|
||||
})
|
||||
|
||||
public class RoleEntity {
|
||||
|
|
|
@ -56,6 +56,16 @@ public abstract class UnsupportedOperationsClientStorageAdapter implements Clien
|
|||
return Collections.EMPTY_SET;
|
||||
}
|
||||
|
||||
@Override
|
||||
public final Set<RoleModel> getRoles(Integer first, Integer max) {
|
||||
return Collections.EMPTY_SET;
|
||||
}
|
||||
|
||||
@Override
|
||||
public final Set<RoleModel> searchForRoles(String search, Integer first, Integer max) {
|
||||
return Collections.EMPTY_SET;
|
||||
}
|
||||
|
||||
@Override
|
||||
public final List<String> getDefaultRoles() {
|
||||
return Collections.EMPTY_LIST;
|
||||
|
|
|
@ -62,7 +62,6 @@ public interface RealmProvider extends Provider, ClientProvider {
|
|||
|
||||
void addTopLevelGroup(RealmModel realm, GroupModel subGroup);
|
||||
|
||||
|
||||
RoleModel addRealmRole(RealmModel realm, String name);
|
||||
|
||||
RoleModel addRealmRole(RealmModel realm, String id, String name);
|
||||
|
@ -71,6 +70,15 @@ public interface RealmProvider extends Provider, ClientProvider {
|
|||
|
||||
Set<RoleModel> getRealmRoles(RealmModel realm);
|
||||
|
||||
Set<RoleModel> getRealmRoles(RealmModel realm, Integer first, Integer max);
|
||||
|
||||
Set<RoleModel> getClientRoles(RealmModel realm, ClientModel client, Integer first, Integer max);
|
||||
|
||||
Set<RoleModel> searchForClientRoles(RealmModel realm, ClientModel client, String search, Integer first,
|
||||
Integer max);
|
||||
|
||||
Set<RoleModel> searchForRoles(RealmModel realm, String search, Integer first, Integer max);
|
||||
|
||||
boolean removeRole(RealmModel realm, RoleModel role);
|
||||
|
||||
RoleModel getRoleById(String id, RealmModel realm);
|
||||
|
@ -91,4 +99,5 @@ public interface RealmProvider extends Provider, ClientProvider {
|
|||
List<ClientInitialAccessModel> listClientInitialAccess(RealmModel realm);
|
||||
void removeExpiredClientInitialAccess();
|
||||
void decreaseRemainingCount(RealmModel realm, ClientInitialAccessModel clientInitialAccess); // Separate provider method to ensure we decrease remainingCount atomically instead of doing classic update
|
||||
|
||||
}
|
||||
|
|
|
@ -45,6 +45,10 @@ public interface RoleContainerModel {
|
|||
|
||||
Set<RoleModel> getRoles();
|
||||
|
||||
Set<RoleModel> getRoles(Integer firstResult, Integer maxResults);
|
||||
|
||||
Set<RoleModel> searchForRoles(String search, Integer first, Integer max);
|
||||
|
||||
List<String> getDefaultRoles();
|
||||
|
||||
void addDefaultRole(String name);
|
||||
|
@ -52,4 +56,5 @@ public interface RoleContainerModel {
|
|||
void updateDefaultRoles(String... defaultRoles);
|
||||
|
||||
void removeDefaultRoles(String... defaultRoles);
|
||||
|
||||
}
|
||||
|
|
|
@ -17,6 +17,7 @@
|
|||
|
||||
package org.keycloak.services.resources.admin;
|
||||
|
||||
import org.apache.commons.lang.StringUtils;
|
||||
import org.jboss.resteasy.annotations.cache.NoCache;
|
||||
import javax.ws.rs.NotFoundException;
|
||||
import org.keycloak.events.admin.OperationType;
|
||||
|
@ -55,7 +56,9 @@ import javax.ws.rs.core.MediaType;
|
|||
import javax.ws.rs.core.Response;
|
||||
import javax.ws.rs.core.UriInfo;
|
||||
import java.util.ArrayList;
|
||||
import java.util.HashSet;
|
||||
import java.util.List;
|
||||
import java.util.Objects;
|
||||
import java.util.Set;
|
||||
import java.util.stream.Collectors;
|
||||
|
||||
|
@ -92,13 +95,29 @@ public class RoleContainerResource extends RoleResource {
|
|||
@GET
|
||||
@NoCache
|
||||
@Produces(MediaType.APPLICATION_JSON)
|
||||
public List<RoleRepresentation> getRoles() {
|
||||
public List<RoleRepresentation> getRoles(@QueryParam("search") @DefaultValue("") String search,
|
||||
@QueryParam("first") Integer firstResult,
|
||||
@QueryParam("max") Integer maxResults,
|
||||
@QueryParam("briefRepresentation") @DefaultValue("true") boolean briefRepresentation) {
|
||||
auth.roles().requireList(roleContainer);
|
||||
|
||||
Set<RoleModel> roleModels = roleContainer.getRoles();
|
||||
Set<RoleModel> roleModels = new HashSet<RoleModel>();
|
||||
|
||||
if(search != null && search.trim().length() > 0) {
|
||||
roleModels = roleContainer.searchForRoles(search, firstResult, maxResults);
|
||||
} else if (!Objects.isNull(firstResult) && !Objects.isNull(maxResults)) {
|
||||
roleModels = roleContainer.getRoles(firstResult, maxResults);
|
||||
} else {
|
||||
roleModels = roleContainer.getRoles();
|
||||
}
|
||||
|
||||
List<RoleRepresentation> roles = new ArrayList<RoleRepresentation>();
|
||||
for (RoleModel roleModel : roleModels) {
|
||||
roles.add(ModelToRepresentation.toBriefRepresentation(roleModel));
|
||||
if(briefRepresentation) {
|
||||
roles.add(ModelToRepresentation.toBriefRepresentation(roleModel));
|
||||
} else {
|
||||
roles.add(ModelToRepresentation.toRepresentation(roleModel));
|
||||
}
|
||||
}
|
||||
return roles;
|
||||
}
|
||||
|
|
|
@ -30,15 +30,19 @@ import org.keycloak.representations.idm.UserRepresentation;
|
|||
import org.keycloak.testsuite.Assert;
|
||||
import org.keycloak.testsuite.util.AdminEventPaths;
|
||||
|
||||
import java.util.Arrays;
|
||||
import java.util.Collections;
|
||||
import java.util.HashMap;
|
||||
import java.util.HashSet;
|
||||
import java.util.LinkedList;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
import java.util.Optional;
|
||||
import java.util.Set;
|
||||
|
||||
import static org.junit.Assert.assertEquals;
|
||||
import static org.junit.Assert.assertFalse;
|
||||
import static org.junit.Assert.assertNull;
|
||||
import static org.junit.Assert.assertTrue;
|
||||
|
||||
/**
|
||||
|
@ -180,4 +184,129 @@ public class ClientRolesTest extends AbstractClientTest {
|
|||
assertTrue((result1.isPresent() || result2.isPresent()) && !(result1.isPresent() && result2.isPresent()));
|
||||
}
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testSearchForRoles() {
|
||||
|
||||
for(int i = 0; i<15; i++) {
|
||||
String roleName = "role"+i;
|
||||
RoleRepresentation role = makeRole(roleName);
|
||||
rolesRsc.create(role);
|
||||
assertAdminEvents.assertEvent(getRealmId(), OperationType.CREATE, AdminEventPaths.clientRoleResourcePath(clientDbId,roleName), role, ResourceType.CLIENT_ROLE);
|
||||
}
|
||||
|
||||
String roleNameA = "abcdef";
|
||||
RoleRepresentation roleA = makeRole(roleNameA);
|
||||
rolesRsc.create(roleA);
|
||||
assertAdminEvents.assertEvent(getRealmId(), OperationType.CREATE, AdminEventPaths.clientRoleResourcePath(clientDbId,roleNameA), roleA, ResourceType.CLIENT_ROLE);
|
||||
|
||||
String roleNameB = "defghi";
|
||||
RoleRepresentation roleB = makeRole(roleNameB);
|
||||
rolesRsc.create(roleB);
|
||||
assertAdminEvents.assertEvent(getRealmId(), OperationType.CREATE, AdminEventPaths.clientRoleResourcePath(clientDbId,roleNameB), roleB, ResourceType.CLIENT_ROLE);
|
||||
|
||||
List<RoleRepresentation> resultSearch = rolesRsc.list("def", -1, -1);
|
||||
assertEquals(2,resultSearch.size());
|
||||
|
||||
List<RoleRepresentation> resultSearch2 = rolesRsc.list("role", -1, -1);
|
||||
assertEquals(15,resultSearch2.size());
|
||||
|
||||
List<RoleRepresentation> resultSearchPagination = rolesRsc.list("role", 1, 5);
|
||||
assertEquals(5,resultSearchPagination.size());
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testPaginationRoles() {
|
||||
|
||||
for(int i = 0; i<15; i++) {
|
||||
String roleName = "role"+i;
|
||||
RoleRepresentation role = makeRole(roleName);
|
||||
rolesRsc.create(role);
|
||||
assertAdminEvents.assertEvent(getRealmId(), OperationType.CREATE, AdminEventPaths.clientRoleResourcePath(clientDbId,roleName), role, ResourceType.CLIENT_ROLE);
|
||||
}
|
||||
|
||||
List<RoleRepresentation> resultSearchWithoutPagination = rolesRsc.list();
|
||||
assertEquals(15,resultSearchWithoutPagination.size());
|
||||
|
||||
List<RoleRepresentation> resultSearchPagination = rolesRsc.list(1, 5);
|
||||
assertEquals(5,resultSearchPagination.size());
|
||||
|
||||
List<RoleRepresentation> resultSearchPaginationIncoherentParams = rolesRsc.list(1, null);
|
||||
assertTrue(resultSearchPaginationIncoherentParams.size() >= 15);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testPaginationRolesCache() {
|
||||
|
||||
for(int i = 0; i<5; i++) {
|
||||
String roleName = "paginaterole"+i;
|
||||
RoleRepresentation role = makeRole(roleName);
|
||||
rolesRsc.create(role);
|
||||
assertAdminEvents.assertEvent(getRealmId(), OperationType.CREATE, AdminEventPaths.clientRoleResourcePath(clientDbId,roleName), role, ResourceType.CLIENT_ROLE);
|
||||
}
|
||||
|
||||
List<RoleRepresentation> resultBeforeAddingRoleToTestCache = rolesRsc.list(1, 1000);
|
||||
|
||||
// after a first call which init the cache, we add a new role to see if the result change
|
||||
|
||||
RoleRepresentation role = makeRole("anewrole");
|
||||
rolesRsc.create(role);
|
||||
assertAdminEvents.assertEvent(getRealmId(), OperationType.CREATE, AdminEventPaths.clientRoleResourcePath(clientDbId,"anewrole"), role, ResourceType.CLIENT_ROLE);
|
||||
|
||||
List<RoleRepresentation> resultafterAddingRoleToTestCache = rolesRsc.list(1, 1000);
|
||||
|
||||
assertEquals(resultBeforeAddingRoleToTestCache.size()+1, resultafterAddingRoleToTestCache.size());
|
||||
}
|
||||
|
||||
@Test
|
||||
public void getRolesWithFullRepresentation() {
|
||||
for(int i = 0; i<5; i++) {
|
||||
String roleName = "attributesrole"+i;
|
||||
RoleRepresentation role = makeRole(roleName);
|
||||
|
||||
Map<String, List<String>> attributes = new HashMap<String, List<String>>();
|
||||
attributes.put("attribute1", Arrays.asList("value1","value2"));
|
||||
role.setAttributes(attributes);
|
||||
|
||||
rolesRsc.create(role);
|
||||
assertAdminEvents.assertEvent(getRealmId(), OperationType.CREATE, AdminEventPaths.clientRoleResourcePath(clientDbId,roleName), role, ResourceType.CLIENT_ROLE);
|
||||
|
||||
// we have to update the role to set the attributes because
|
||||
// the add role endpoint only care about name and description
|
||||
RoleResource roleToUpdate = rolesRsc.get(roleName);
|
||||
role.setId(roleToUpdate.toRepresentation().getId());
|
||||
|
||||
roleToUpdate.update(role);
|
||||
assertAdminEvents.assertEvent(getRealmId(), OperationType.UPDATE, AdminEventPaths.clientRoleResourcePath(clientDbId,roleName), role, ResourceType.CLIENT_ROLE);
|
||||
}
|
||||
|
||||
List<RoleRepresentation> roles = rolesRsc.list(false);
|
||||
assertTrue(roles.get(0).getAttributes().containsKey("attribute1"));
|
||||
}
|
||||
|
||||
@Test
|
||||
public void getRolesWithBriefRepresentation() {
|
||||
for(int i = 0; i<5; i++) {
|
||||
String roleName = "attributesrole"+i;
|
||||
RoleRepresentation role = makeRole(roleName);
|
||||
|
||||
Map<String, List<String>> attributes = new HashMap<String, List<String>>();
|
||||
attributes.put("attribute1", Arrays.asList("value1","value2"));
|
||||
role.setAttributes(attributes);
|
||||
|
||||
rolesRsc.create(role);
|
||||
assertAdminEvents.assertEvent(getRealmId(), OperationType.CREATE, AdminEventPaths.clientRoleResourcePath(clientDbId,roleName), role, ResourceType.CLIENT_ROLE);
|
||||
|
||||
// we have to update the role to set the attributes because
|
||||
// the add role endpoint only care about name and description
|
||||
RoleResource roleToUpdate = rolesRsc.get(roleName);
|
||||
role.setId(roleToUpdate.toRepresentation().getId());
|
||||
|
||||
roleToUpdate.update(role);
|
||||
assertAdminEvents.assertEvent(getRealmId(), OperationType.UPDATE, AdminEventPaths.clientRoleResourcePath(clientDbId,roleName), role, ResourceType.CLIENT_ROLE);
|
||||
}
|
||||
|
||||
List<RoleRepresentation> roles = rolesRsc.list();
|
||||
assertNull(roles.get(0).getAttributes());
|
||||
}
|
||||
}
|
||||
|
|
|
@ -19,6 +19,7 @@ package org.keycloak.testsuite.admin.realm;
|
|||
|
||||
import org.junit.Before;
|
||||
import org.junit.Test;
|
||||
import org.keycloak.admin.client.resource.RealmResource;
|
||||
import org.keycloak.admin.client.resource.RoleResource;
|
||||
import org.keycloak.admin.client.resource.RolesResource;
|
||||
import org.keycloak.admin.client.resource.UserResource;
|
||||
|
@ -38,6 +39,7 @@ import org.keycloak.testsuite.util.RoleBuilder;
|
|||
import javax.ws.rs.NotFoundException;
|
||||
import javax.ws.rs.core.Response;
|
||||
|
||||
import java.util.Arrays;
|
||||
import java.util.Collections;
|
||||
import java.util.HashMap;
|
||||
import java.util.HashSet;
|
||||
|
@ -53,7 +55,11 @@ import static org.hamcrest.Matchers.is;
|
|||
import static org.hamcrest.collection.IsCollectionWithSize.hasSize;
|
||||
import static org.junit.Assert.assertEquals;
|
||||
import static org.junit.Assert.assertFalse;
|
||||
import static org.junit.Assert.assertNotEquals;
|
||||
import static org.junit.Assert.assertNotNull;
|
||||
import static org.junit.Assert.assertNotSame;
|
||||
import static org.junit.Assert.assertNull;
|
||||
import static org.junit.Assert.assertSame;
|
||||
import static org.junit.Assert.assertTrue;
|
||||
import static org.junit.Assert.fail;
|
||||
|
||||
|
@ -132,6 +138,12 @@ public class RealmRolesTest extends AbstractAdminTest {
|
|||
|
||||
}
|
||||
|
||||
private RoleRepresentation makeRole(String name) {
|
||||
RoleRepresentation role = new RoleRepresentation();
|
||||
role.setName(name);
|
||||
return role;
|
||||
}
|
||||
|
||||
@Test
|
||||
public void getRole() {
|
||||
RoleRepresentation role = resource.get("role-a").toRepresentation();
|
||||
|
@ -344,4 +356,131 @@ public class RealmRolesTest extends AbstractAdminTest {
|
|||
assertThat(expectedMembers, containsInAnyOrder("test-role-member", "test-role-member2"));
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testSearchForRoles() {
|
||||
|
||||
for(int i = 0; i<15; i++) {
|
||||
String roleName = "testrole"+i;
|
||||
RoleRepresentation role = makeRole(roleName);
|
||||
resource.create(role);
|
||||
assertAdminEvents.assertEvent(realmId, OperationType.CREATE, AdminEventPaths.roleResourcePath(roleName), role, ResourceType.REALM_ROLE);
|
||||
}
|
||||
|
||||
String roleNameA = "abcdef";
|
||||
RoleRepresentation roleA = makeRole(roleNameA);
|
||||
resource.create(roleA);
|
||||
assertAdminEvents.assertEvent(realmId, OperationType.CREATE, AdminEventPaths.roleResourcePath(roleNameA), roleA, ResourceType.REALM_ROLE);
|
||||
|
||||
String roleNameB = "defghi";
|
||||
RoleRepresentation roleB = makeRole(roleNameB);
|
||||
resource.create(roleB);
|
||||
assertAdminEvents.assertEvent(realmId, OperationType.CREATE, AdminEventPaths.roleResourcePath(roleNameB), roleB, ResourceType.REALM_ROLE);
|
||||
|
||||
List<RoleRepresentation> resultSearch = resource.list("def", -1, -1);
|
||||
assertEquals(2,resultSearch.size());
|
||||
|
||||
List<RoleRepresentation> resultSearch2 = resource.list("testrole", -1, -1);
|
||||
assertEquals(15,resultSearch2.size());
|
||||
|
||||
List<RoleRepresentation> resultSearchPagination = resource.list("testrole", 1, 5);
|
||||
assertEquals(5,resultSearchPagination.size());
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testPaginationRoles() {
|
||||
|
||||
for(int i = 0; i<15; i++) {
|
||||
String roleName = "role"+i;
|
||||
RoleRepresentation role = makeRole(roleName);
|
||||
resource.create(role);
|
||||
assertAdminEvents.assertEvent(realmId, OperationType.CREATE, AdminEventPaths.roleResourcePath(roleName), role, ResourceType.REALM_ROLE);
|
||||
}
|
||||
|
||||
List<RoleRepresentation> resultSearchPagination = resource.list(1, 5);
|
||||
assertEquals(5,resultSearchPagination.size());
|
||||
|
||||
List<RoleRepresentation> resultSearchPagination2 = resource.list(5, 5);
|
||||
assertEquals(5,resultSearchPagination2.size());
|
||||
|
||||
List<RoleRepresentation> resultSearchPagination3 = resource.list(1, 5);
|
||||
assertEquals(5,resultSearchPagination3.size());
|
||||
|
||||
List<RoleRepresentation> resultSearchPaginationIncoherentParams = resource.list(1, null);
|
||||
assertTrue(resultSearchPaginationIncoherentParams.size() > 15);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testPaginationRolesCache() {
|
||||
|
||||
for(int i = 0; i<5; i++) {
|
||||
String roleName = "paginaterole"+i;
|
||||
RoleRepresentation role = makeRole(roleName);
|
||||
resource.create(role);
|
||||
assertAdminEvents.assertEvent(realmId, OperationType.CREATE, AdminEventPaths.roleResourcePath(roleName), role, ResourceType.REALM_ROLE);
|
||||
}
|
||||
|
||||
List<RoleRepresentation> resultBeforeAddingRoleToTestCache = resource.list(1, 1000);
|
||||
|
||||
// after a first call which init the cache, we add a new role to see if the result change
|
||||
|
||||
RoleRepresentation role = makeRole("anewrole");
|
||||
resource.create(role);
|
||||
assertAdminEvents.assertEvent(realmId, OperationType.CREATE, AdminEventPaths.roleResourcePath("anewrole"), role, ResourceType.REALM_ROLE);
|
||||
|
||||
List<RoleRepresentation> resultafterAddingRoleToTestCache = resource.list(1, 1000);
|
||||
|
||||
assertEquals(resultBeforeAddingRoleToTestCache.size()+1, resultafterAddingRoleToTestCache.size());
|
||||
}
|
||||
|
||||
@Test
|
||||
public void getRolesWithFullRepresentation() {
|
||||
for(int i = 0; i<5; i++) {
|
||||
String roleName = "attributesrole"+i;
|
||||
RoleRepresentation role = makeRole(roleName);
|
||||
|
||||
Map<String, List<String>> attributes = new HashMap<String, List<String>>();
|
||||
attributes.put("attribute1", Arrays.asList("value1","value2"));
|
||||
role.setAttributes(attributes);
|
||||
|
||||
resource.create(role);
|
||||
assertAdminEvents.assertEvent(realmId, OperationType.CREATE, AdminEventPaths.roleResourcePath(roleName), role, ResourceType.REALM_ROLE);
|
||||
|
||||
// we have to update the role to set the attributes because
|
||||
// the add role endpoint only care about name and description
|
||||
RoleResource roleToUpdate = resource.get(roleName);
|
||||
role.setId(roleToUpdate.toRepresentation().getId());
|
||||
|
||||
roleToUpdate.update(role);
|
||||
assertAdminEvents.assertEvent(realmId, OperationType.UPDATE, AdminEventPaths.roleResourcePath(roleName), role, ResourceType.REALM_ROLE);
|
||||
}
|
||||
|
||||
List<RoleRepresentation> roles = resource.list("attributesrole", false);
|
||||
assertTrue(roles.get(0).getAttributes().containsKey("attribute1"));
|
||||
}
|
||||
|
||||
@Test
|
||||
public void getRolesWithBriefRepresentation() {
|
||||
for(int i = 0; i<5; i++) {
|
||||
String roleName = "attributesrolebrief"+i;
|
||||
RoleRepresentation role = makeRole(roleName);
|
||||
|
||||
Map<String, List<String>> attributes = new HashMap<String, List<String>>();
|
||||
attributes.put("attribute1", Arrays.asList("value1","value2"));
|
||||
role.setAttributes(attributes);
|
||||
|
||||
resource.create(role);
|
||||
assertAdminEvents.assertEvent(realmId, OperationType.CREATE, AdminEventPaths.roleResourcePath(roleName), role, ResourceType.REALM_ROLE);
|
||||
|
||||
// we have to update the role to set the attributes because
|
||||
// the add role endpoint only care about name and description
|
||||
RoleResource roleToUpdate = resource.get(roleName);
|
||||
role.setId(roleToUpdate.toRepresentation().getId());
|
||||
|
||||
roleToUpdate.update(role);
|
||||
assertAdminEvents.assertEvent(realmId, OperationType.UPDATE, AdminEventPaths.roleResourcePath(roleName), role, ResourceType.REALM_ROLE);
|
||||
}
|
||||
|
||||
List<RoleRepresentation> roles = resource.list("attributesrolebrief", true);
|
||||
assertNull(roles.get(0).getAttributes());
|
||||
}
|
||||
}
|
||||
|
|
|
@ -1255,6 +1255,7 @@ membership.available-groups.tooltip=Groups a user can join. Select a group and c
|
|||
table-of-realm-users=Table of Realm Users
|
||||
view-all-users=View all users
|
||||
view-all-groups=View all groups
|
||||
view-all-roles=View all roles
|
||||
unlock-users=Unlock users
|
||||
no-users-available=No users available
|
||||
users.instruction=Please enter a search, or click on view all users
|
||||
|
|
|
@ -817,9 +817,6 @@ module.config([ '$routeProvider', function($routeProvider) {
|
|||
resolve : {
|
||||
realm : function(RealmLoader) {
|
||||
return RealmLoader();
|
||||
},
|
||||
roles : function(RoleListLoader) {
|
||||
return RoleListLoader();
|
||||
}
|
||||
},
|
||||
controller : 'RoleListCtrl'
|
||||
|
@ -1355,9 +1352,6 @@ module.config([ '$routeProvider', function($routeProvider) {
|
|||
},
|
||||
client : function(ClientLoader) {
|
||||
return ClientLoader();
|
||||
},
|
||||
roles : function(ClientRoleListLoader) {
|
||||
return ClientRoleListLoader();
|
||||
}
|
||||
},
|
||||
controller : 'ClientRoleListCtrl'
|
||||
|
|
|
@ -18,11 +18,54 @@ module.controller('ClientTabCtrl', function(Dialog, $scope, Current, Notificatio
|
|||
};
|
||||
});
|
||||
|
||||
module.controller('ClientRoleListCtrl', function($scope, $location, realm, client, roles, $route, RoleById, Notifications, Dialog) {
|
||||
module.controller('ClientRoleListCtrl', function($scope, $route, realm, client, ClientRoleList, RoleById, Notifications, Dialog) {
|
||||
$scope.realm = realm;
|
||||
$scope.roles = roles;
|
||||
$scope.roles = [];
|
||||
$scope.client = client;
|
||||
|
||||
$scope.query = {
|
||||
realm: realm.realm,
|
||||
client: $scope.client.id,
|
||||
search : null,
|
||||
max : 20,
|
||||
first : 0
|
||||
}
|
||||
|
||||
$scope.$watch('query.search', function (newVal, oldVal) {
|
||||
if($scope.query.search && $scope.query.search.length >= 3) {
|
||||
$scope.firstPage();
|
||||
}
|
||||
}, true);
|
||||
|
||||
$scope.firstPage = function() {
|
||||
$scope.query.first = 0;
|
||||
$scope.searchQuery();
|
||||
}
|
||||
|
||||
$scope.previousPage = function() {
|
||||
$scope.query.first -= parseInt($scope.query.max);
|
||||
if ($scope.query.first < 0) {
|
||||
$scope.query.first = 0;
|
||||
}
|
||||
$scope.searchQuery();
|
||||
}
|
||||
|
||||
$scope.nextPage = function() {
|
||||
$scope.query.first += parseInt($scope.query.max);
|
||||
$scope.searchQuery();
|
||||
}
|
||||
|
||||
$scope.searchQuery = function() {
|
||||
$scope.searchLoaded = false;
|
||||
|
||||
$scope.roles = ClientRoleList.query($scope.query, function() {
|
||||
$scope.searchLoaded = true;
|
||||
$scope.lastSearch = $scope.query.search;
|
||||
});
|
||||
};
|
||||
|
||||
$scope.searchQuery();
|
||||
|
||||
$scope.removeRole = function(role) {
|
||||
Dialog.confirmDelete(role.name, 'role', function() {
|
||||
RoleById.remove({
|
||||
|
@ -34,12 +77,6 @@ module.controller('ClientRoleListCtrl', function($scope, $location, realm, clien
|
|||
});
|
||||
});
|
||||
};
|
||||
|
||||
$scope.$watch(function() {
|
||||
return $location.path();
|
||||
}, function() {
|
||||
$scope.path = $location.path().substring(1).split("/");
|
||||
});
|
||||
});
|
||||
|
||||
module.controller('ClientCredentialsCtrl', function($scope, $location, realm, client, clientAuthenticatorProviders, clientConfigProperties, Client, ClientRegistrationAccessToken, Notifications) {
|
||||
|
|
|
@ -1464,22 +1464,52 @@ module.controller('RoleTabCtrl', function(Dialog, $scope, Current, Notifications
|
|||
});
|
||||
|
||||
|
||||
module.controller('RoleListCtrl', function($scope, $route, Dialog, Notifications, realm, roles, RoleById, filterFilter) {
|
||||
module.controller('RoleListCtrl', function($scope, $route, Dialog, Notifications, realm, RoleList, RoleById, filterFilter) {
|
||||
$scope.realm = realm;
|
||||
$scope.roles = roles;
|
||||
$scope.currentPage = 1;
|
||||
$scope.currentPageInput = 1;
|
||||
$scope.pageSize = 20;
|
||||
$scope.numberOfPages = Math.ceil($scope.roles.length/$scope.pageSize);
|
||||
$scope.roles = [];
|
||||
|
||||
$scope.$watch('searchQuery', function (newVal, oldVal) {
|
||||
$scope.filtered = filterFilter($scope.roles, {name: newVal});
|
||||
$scope.totalItems = $scope.filtered.length;
|
||||
$scope.numberOfPages = Math.ceil($scope.totalItems/$scope.pageSize);
|
||||
$scope.currentPage = 1;
|
||||
$scope.currentPageInput = 1;
|
||||
$scope.query = {
|
||||
realm: realm.realm,
|
||||
search : null,
|
||||
max : 20,
|
||||
first : 0
|
||||
}
|
||||
|
||||
$scope.$watch('query.search', function (newVal, oldVal) {
|
||||
if($scope.query.search && $scope.query.search.length >= 3) {
|
||||
$scope.firstPage();
|
||||
}
|
||||
}, true);
|
||||
|
||||
$scope.firstPage = function() {
|
||||
$scope.query.first = 0;
|
||||
$scope.searchQuery();
|
||||
}
|
||||
|
||||
$scope.previousPage = function() {
|
||||
$scope.query.first -= parseInt($scope.query.max);
|
||||
if ($scope.query.first < 0) {
|
||||
$scope.query.first = 0;
|
||||
}
|
||||
$scope.searchQuery();
|
||||
}
|
||||
|
||||
$scope.nextPage = function() {
|
||||
$scope.query.first += parseInt($scope.query.max);
|
||||
$scope.searchQuery();
|
||||
}
|
||||
|
||||
$scope.searchQuery = function() {
|
||||
$scope.searchLoaded = false;
|
||||
|
||||
$scope.roles = RoleList.query($scope.query, function() {
|
||||
$scope.searchLoaded = true;
|
||||
$scope.lastSearch = $scope.query.search;
|
||||
});
|
||||
};
|
||||
|
||||
$scope.searchQuery();
|
||||
|
||||
$scope.removeRole = function (role) {
|
||||
Dialog.confirmDelete(role.name, 'role', function () {
|
||||
RoleById.remove({
|
||||
|
|
|
@ -304,17 +304,6 @@ module.factory('ClientOptionalClientScopesLoader', function(Loader, ClientOption
|
|||
});
|
||||
});
|
||||
|
||||
module.factory('ClientRoleListLoader', function(Loader, ClientRole, $route, $q) {
|
||||
return Loader.query(ClientRole, function() {
|
||||
return {
|
||||
realm : $route.current.params.realm,
|
||||
client : $route.current.params.client
|
||||
}
|
||||
});
|
||||
});
|
||||
|
||||
|
||||
|
||||
module.factory('ClientLoader', function(Loader, Client, $route, $q) {
|
||||
return Loader.get(Client, function() {
|
||||
return {
|
||||
|
|
|
@ -2002,6 +2002,12 @@ module.factory('GroupMembership', function($resource) {
|
|||
});
|
||||
});
|
||||
|
||||
module.factory('RoleList', function($resource) {
|
||||
return $resource(authUrl + '/admin/realms/:realm/roles', {
|
||||
realm : '@realm'
|
||||
});
|
||||
});
|
||||
|
||||
module.factory('RoleMembership', function($resource) {
|
||||
return $resource(authUrl + '/admin/realms/:realm/roles/:role/users', {
|
||||
realm : '@realm',
|
||||
|
@ -2009,6 +2015,13 @@ module.factory('RoleMembership', function($resource) {
|
|||
});
|
||||
});
|
||||
|
||||
module.factory('ClientRoleList', function($resource) {
|
||||
return $resource(authUrl + '/admin/realms/:realm/clients/:client/roles', {
|
||||
realm : '@realm',
|
||||
client : '@client'
|
||||
});
|
||||
});
|
||||
|
||||
module.factory('ClientRoleMembership', function($resource) {
|
||||
return $resource(authUrl + '/admin/realms/:realm/clients/:client/roles/:role/users', {
|
||||
realm : '@realm',
|
||||
|
|
|
@ -10,9 +10,20 @@
|
|||
<table class="table table-striped table-bordered">
|
||||
<thead>
|
||||
<tr>
|
||||
<th class="kc-table-actions" colspan="5" data-ng-show="client.access.configure">
|
||||
<div class="pull-right">
|
||||
<a class="btn btn-default" href="#/create/role/{{realm.realm}}/clients/{{client.id}}">{{:: 'add-role' | translate}}</a>
|
||||
<th class="kc-table-actions" colspan="5">
|
||||
<div class="form-inline">
|
||||
<div class="form-group">
|
||||
<div class="input-group">
|
||||
<input type="text" placeholder="{{:: 'search.placeholder' | translate}}" data-ng-model="query.search" ng-model-options="{debounce: 500}" class="form-control search">
|
||||
<div class="input-group-addon">
|
||||
<i class="fa fa-search" type="submit" data-ng-click="firstPage()"></i>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<button id="viewAllRoles" class="btn btn-default" ng-click="query.search = null; firstPage()">{{:: 'view-all-roles' | translate}}</button>
|
||||
<div class="pull-right" data-ng-show="access.manageRealm">
|
||||
<a class="btn btn-default" href="#/create/role/{{realm.realm}}/clients/{{client.id}}">{{:: 'add-role' | translate}}</a>
|
||||
</div>
|
||||
</div>
|
||||
</th>
|
||||
</tr>
|
||||
|
@ -24,17 +35,29 @@
|
|||
</tr>
|
||||
</thead>
|
||||
<tbody>
|
||||
<tr ng-repeat="role in roles">
|
||||
<td><a href="#/realms/{{realm.realm}}/clients/{{client.id}}/roles/{{role.id}}">{{role.name}}</a></td>
|
||||
<td translate="{{role.composite}}"></td>
|
||||
<td>{{role.description}}</td>
|
||||
<td class="kc-action-cell" kc-open="/realms/{{realm.realm}}/clients/{{client.id}}/roles/{{role.id}}">{{:: 'edit' | translate}}</td>
|
||||
<td class="kc-action-cell" data-ng-show="client.access.configure" data-ng-click="removeRole(role)">{{:: 'delete' | translate}}</td>
|
||||
</tr>
|
||||
<tr data-ng-show="!roles || roles.length == 0">
|
||||
<td>{{:: 'no-client-roles-available' | translate}}</td>
|
||||
</tr>
|
||||
<tr ng-repeat="role in roles">
|
||||
<td><a href="#/realms/{{realm.realm}}/clients/{{client.id}}/roles/{{role.id}}">{{role.name}}</a></td>
|
||||
<td translate="{{role.composite}}"></td>
|
||||
<td>{{role.description}}</td>
|
||||
<td class="kc-action-cell" kc-open="/realms/{{realm.realm}}/clients/{{client.id}}/roles/{{role.id}}">{{:: 'edit' | translate}}</td>
|
||||
<td class="kc-action-cell" data-ng-show="client.access.configure" data-ng-click="removeRole(role)">{{:: 'delete' | translate}}</td>
|
||||
</tr>
|
||||
<tr data-ng-show="(roles | filter:{name: query.search}).length == 0">
|
||||
<td class="text-muted" colspan="4" data-ng-show="searchLoaded && roles.length == 0 && lastSearch != null">{{:: 'no-results' | translate}}</td>
|
||||
<td class="text-muted" colspan="4" data-ng-show="searchLoaded && roles.length == 0 && lastSearch == null">{{:: 'no-client-roles-available' | translate}}</td>
|
||||
</tr>
|
||||
</tbody>
|
||||
<tfoot data-ng-show="roles && (roles.length >= query.max || query.first > 0)">
|
||||
<tr>
|
||||
<td colspan="5">
|
||||
<div class="table-nav">
|
||||
<button data-ng-click="firstPage()" class="first" ng-disabled="query.first == 0">{{:: 'first-page' | translate}}</button>
|
||||
<button data-ng-click="previousPage()" class="prev" ng-disabled="query.first == 0">{{:: 'previous-page' | translate}}</button>
|
||||
<button data-ng-click="nextPage()" class="next" ng-disabled="roles.length < query.max">{{:: 'next-page' | translate}}</button>
|
||||
</div>
|
||||
</td>
|
||||
</tr>
|
||||
</tfoot>
|
||||
</table>
|
||||
</div>
|
||||
|
||||
|
|
|
@ -6,20 +6,20 @@
|
|||
<li><a href="#/realms/{{realm.realm}}/default-roles">{{:: 'default-roles' | translate}}</a></li>
|
||||
</ul>
|
||||
|
||||
<table class="datatable table table-striped table-bordered dataTable no-footer">
|
||||
<table class="table table-striped table-bordered">
|
||||
<thead>
|
||||
<tr>
|
||||
<th class="kc-table-actions" colspan="5">
|
||||
<div class="form-inline">
|
||||
<div class="form-group">
|
||||
<div class="input-group">
|
||||
<input type="text" placeholder="{{:: 'search.placeholder' | translate}}" data-ng-model="searchQuery" class="form-control search" onkeyup="if (event.keyCode === 13){$(this).next('I').click(); }">
|
||||
<input type="text" placeholder="{{:: 'search.placeholder' | translate}}" data-ng-model="query.search" ng-model-options="{debounce: 500}" class="form-control search">
|
||||
<div class="input-group-addon">
|
||||
<i class="fa fa-search" type="submit"></i>
|
||||
<i class="fa fa-search" type="submit" data-ng-click="firstPage()"></i>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<button id="viewAllRoles" class="btn btn-default" ng-click="query.search = null; firstPage()">{{:: 'view-all-roles' | translate}}</button>
|
||||
<div class="pull-right" data-ng-show="access.manageRealm">
|
||||
<a id="createRole" class="btn btn-default" href="#/create/role/{{realm.realm}}">{{:: 'add-role' | translate}}</a>
|
||||
</div>
|
||||
|
@ -34,20 +34,30 @@
|
|||
</tr>
|
||||
</thead>
|
||||
<tbody>
|
||||
<tr ng-repeat="role in roles| filter:{name: searchQuery} | orderBy:'name'| startFrom:(currentPage - 1) * pageSize | limitTo:pageSize">
|
||||
<tr ng-repeat="role in roles">
|
||||
<td><a href="#/realms/{{realm.realm}}/roles/{{role.id}}">{{role.name}}</a></td>
|
||||
<td translate="{{role.composite}}"></td>
|
||||
<td>{{role.description}}</td>
|
||||
<td class="kc-action-cell" kc-open="/realms/{{realm.realm}}/roles/{{role.id}}">{{:: 'edit' | translate}}</td>
|
||||
<td class="kc-action-cell" data-ng-click="removeRole(role)">{{:: 'delete' | translate}}</td>
|
||||
</tr>
|
||||
<tr data-ng-show="(roles | filter:{name: searchQuery}).length == 0">
|
||||
<td class="text-muted" colspan="4" data-ng-show="searchQuery">{{:: 'no-results' | translate}}</td>
|
||||
<td class="text-muted" colspan="4" data-ng-hide="searchQuery">{{:: 'no-realm-roles-available' | translate}}</td>
|
||||
<tr data-ng-show="(roles | filter:{name: query.search}).length == 0">
|
||||
<td class="text-muted" colspan="4" data-ng-show="searchLoaded && roles.length == 0 && lastSearch != null">{{:: 'no-results' | translate}}</td>
|
||||
<td class="text-muted" colspan="4" data-ng-show="searchLoaded && roles.length == 0 && lastSearch == null">{{:: 'no-realm-roles-available' | translate}}</td>
|
||||
</tr>
|
||||
</tbody>
|
||||
<tfoot data-ng-show="roles && (roles.length >= query.max || query.first > 0)">
|
||||
<tr>
|
||||
<td colspan="5">
|
||||
<div class="table-nav">
|
||||
<button data-ng-click="firstPage()" class="first" ng-disabled="query.first == 0">{{:: 'first-page' | translate}}</button>
|
||||
<button data-ng-click="previousPage()" class="prev" ng-disabled="query.first == 0">{{:: 'previous-page' | translate}}</button>
|
||||
<button data-ng-click="nextPage()" class="next" ng-disabled="roles.length < query.max">{{:: 'next-page' | translate}}</button>
|
||||
</div>
|
||||
</td>
|
||||
</tr>
|
||||
</tfoot>
|
||||
</table>
|
||||
<kc-paging current-page='currentPage' number-of-pages='numberOfPages' current-page-input='currentPageInput'></kc-paging>
|
||||
</div>
|
||||
|
||||
<kc-menu></kc-menu>
|
Loading…
Reference in a new issue