[KEYCLOAK-2538] - groups pagination and group search
This commit is contained in:
parent
9be9e30ad6
commit
c4da7637d6
11 changed files with 206 additions and 231 deletions
|
@ -20,12 +20,7 @@ package org.keycloak.admin.client.resource;
|
|||
import org.jboss.resteasy.annotations.cache.NoCache;
|
||||
import org.keycloak.representations.idm.GroupRepresentation;
|
||||
|
||||
import javax.ws.rs.Consumes;
|
||||
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.*;
|
||||
import javax.ws.rs.core.MediaType;
|
||||
import javax.ws.rs.core.Response;
|
||||
import java.util.List;
|
||||
|
@ -35,11 +30,43 @@ import java.util.List;
|
|||
* @version $Revision: 1 $
|
||||
*/
|
||||
public interface GroupsResource {
|
||||
|
||||
/**
|
||||
* Get all groups.
|
||||
* @return A list containing all groups.
|
||||
*/
|
||||
@GET
|
||||
@NoCache
|
||||
@Produces(MediaType.APPLICATION_JSON)
|
||||
List<GroupRepresentation> groups();
|
||||
|
||||
/**
|
||||
* Get groups by pagination params.
|
||||
* @param first index of the first element
|
||||
* @param max max number of occurrences
|
||||
* @return A list containing the slice of all groups.
|
||||
*/
|
||||
@GET
|
||||
@NoCache
|
||||
@Produces(MediaType.APPLICATION_JSON)
|
||||
@Consumes(MediaType.APPLICATION_JSON)
|
||||
List<GroupRepresentation> groups(@QueryParam("first") Integer first, @QueryParam("max") Integer max);
|
||||
|
||||
/**
|
||||
* Get groups 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 groups.
|
||||
*/
|
||||
@GET
|
||||
@NoCache
|
||||
@Produces(MediaType.APPLICATION_JSON)
|
||||
@Consumes(MediaType.APPLICATION_JSON)
|
||||
List<GroupRepresentation> groups(@QueryParam("search") String search,
|
||||
@QueryParam("first") Integer first,
|
||||
@QueryParam("max") Integer max);
|
||||
|
||||
/**
|
||||
* create or add a top level realm groupSet or create child. This will update the group and set the parent if it exists. Create it and set the parent
|
||||
* if the group doesn't exist.
|
||||
|
|
|
@ -20,32 +20,12 @@ package org.keycloak.models.cache.infinispan;
|
|||
import org.keycloak.Config;
|
||||
import org.keycloak.common.enums.SslRequired;
|
||||
import org.keycloak.component.ComponentModel;
|
||||
import org.keycloak.models.AuthenticationExecutionModel;
|
||||
import org.keycloak.models.AuthenticationFlowModel;
|
||||
import org.keycloak.models.AuthenticatorConfigModel;
|
||||
import org.keycloak.models.ClientModel;
|
||||
import org.keycloak.models.ClientTemplateModel;
|
||||
import org.keycloak.models.GroupModel;
|
||||
import org.keycloak.models.IdentityProviderMapperModel;
|
||||
import org.keycloak.models.IdentityProviderModel;
|
||||
import org.keycloak.models.KeycloakSession;
|
||||
import org.keycloak.models.OTPPolicy;
|
||||
import org.keycloak.models.PasswordPolicy;
|
||||
import org.keycloak.models.RealmModel;
|
||||
import org.keycloak.models.RequiredActionProviderModel;
|
||||
import org.keycloak.models.RequiredCredentialModel;
|
||||
import org.keycloak.models.RoleModel;
|
||||
import org.keycloak.models.*;
|
||||
import org.keycloak.models.cache.CachedRealmModel;
|
||||
import org.keycloak.models.cache.infinispan.entities.CachedRealm;
|
||||
import org.keycloak.storage.UserStorageProvider;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.Collections;
|
||||
import java.util.HashSet;
|
||||
import java.util.LinkedList;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
import java.util.Set;
|
||||
import java.util.*;
|
||||
import java.util.concurrent.ConcurrentHashMap;
|
||||
|
||||
/**
|
||||
|
@ -328,7 +308,7 @@ public class RealmAdapter implements CachedRealmModel {
|
|||
getDelegateForUpdate();
|
||||
updated.setLoginWithEmailAllowed(loginWithEmailAllowed);
|
||||
}
|
||||
|
||||
|
||||
@Override
|
||||
public boolean isDuplicateEmailsAllowed() {
|
||||
if (isUpdated()) return updated.isDuplicateEmailsAllowed();
|
||||
|
@ -797,9 +777,9 @@ public class RealmAdapter implements CachedRealmModel {
|
|||
@Override
|
||||
public void setEnabledEventTypes(Set<String> enabledEventTypes) {
|
||||
getDelegateForUpdate();
|
||||
updated.setEnabledEventTypes(enabledEventTypes);
|
||||
updated.setEnabledEventTypes(enabledEventTypes);
|
||||
}
|
||||
|
||||
|
||||
@Override
|
||||
public boolean isAdminEventsEnabled() {
|
||||
if (isUpdated()) return updated.isAdminEventsEnabled();
|
||||
|
@ -823,7 +803,7 @@ public class RealmAdapter implements CachedRealmModel {
|
|||
getDelegateForUpdate();
|
||||
updated.setAdminEventsDetailsEnabled(enabled);
|
||||
}
|
||||
|
||||
|
||||
@Override
|
||||
public ClientModel getMasterAdminClient() {
|
||||
return cached.getMasterAdminClient()==null ? null : cacheSession.getRealm(Config.getAdminRealm()).getClientById(cached.getMasterAdminClient());
|
||||
|
@ -1225,6 +1205,16 @@ public class RealmAdapter implements CachedRealmModel {
|
|||
return cacheSession.getTopLevelGroups(this);
|
||||
}
|
||||
|
||||
@Override
|
||||
public List<GroupModel> getTopLevelGroups(Integer first, Integer max) {
|
||||
return cacheSession.getTopLevelGroups(this, first, max);
|
||||
}
|
||||
|
||||
@Override
|
||||
public List<GroupModel> searchForGroupByName(String search, Integer first, Integer max) {
|
||||
return cacheSession.searchForGroupByName(this, search, first, max);
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean removeGroup(GroupModel group) {
|
||||
return cacheSession.removeGroup(this, group);
|
||||
|
|
|
@ -19,50 +19,15 @@ package org.keycloak.models.cache.infinispan;
|
|||
|
||||
import org.jboss.logging.Logger;
|
||||
import org.keycloak.cluster.ClusterProvider;
|
||||
import org.keycloak.models.cache.infinispan.events.InvalidationEvent;
|
||||
import org.keycloak.migration.MigrationModel;
|
||||
import org.keycloak.models.ClientModel;
|
||||
import org.keycloak.models.ClientTemplateModel;
|
||||
import org.keycloak.models.GroupModel;
|
||||
import org.keycloak.models.KeycloakSession;
|
||||
import org.keycloak.models.KeycloakTransaction;
|
||||
import org.keycloak.models.RealmModel;
|
||||
import org.keycloak.models.RealmProvider;
|
||||
import org.keycloak.models.RoleModel;
|
||||
import org.keycloak.models.*;
|
||||
import org.keycloak.models.cache.CacheRealmProvider;
|
||||
import org.keycloak.models.cache.CachedRealmModel;
|
||||
import org.keycloak.models.cache.infinispan.entities.CachedClient;
|
||||
import org.keycloak.models.cache.infinispan.entities.CachedClientRole;
|
||||
import org.keycloak.models.cache.infinispan.entities.CachedClientTemplate;
|
||||
import org.keycloak.models.cache.infinispan.entities.CachedGroup;
|
||||
import org.keycloak.models.cache.infinispan.entities.CachedRealm;
|
||||
import org.keycloak.models.cache.infinispan.entities.CachedRealmRole;
|
||||
import org.keycloak.models.cache.infinispan.entities.CachedRole;
|
||||
import org.keycloak.models.cache.infinispan.entities.ClientListQuery;
|
||||
import org.keycloak.models.cache.infinispan.entities.GroupListQuery;
|
||||
import org.keycloak.models.cache.infinispan.entities.RealmListQuery;
|
||||
import org.keycloak.models.cache.infinispan.entities.RoleListQuery;
|
||||
import org.keycloak.models.cache.infinispan.events.ClientAddedEvent;
|
||||
import org.keycloak.models.cache.infinispan.events.ClientRemovedEvent;
|
||||
import org.keycloak.models.cache.infinispan.events.ClientTemplateEvent;
|
||||
import org.keycloak.models.cache.infinispan.events.ClientUpdatedEvent;
|
||||
import org.keycloak.models.cache.infinispan.events.GroupAddedEvent;
|
||||
import org.keycloak.models.cache.infinispan.events.GroupMovedEvent;
|
||||
import org.keycloak.models.cache.infinispan.events.GroupRemovedEvent;
|
||||
import org.keycloak.models.cache.infinispan.events.GroupUpdatedEvent;
|
||||
import org.keycloak.models.cache.infinispan.events.RealmRemovedEvent;
|
||||
import org.keycloak.models.cache.infinispan.events.RealmUpdatedEvent;
|
||||
import org.keycloak.models.cache.infinispan.events.RoleAddedEvent;
|
||||
import org.keycloak.models.cache.infinispan.events.RoleRemovedEvent;
|
||||
import org.keycloak.models.cache.infinispan.events.RoleUpdatedEvent;
|
||||
import org.keycloak.models.cache.infinispan.entities.*;
|
||||
import org.keycloak.models.cache.infinispan.events.*;
|
||||
import org.keycloak.models.utils.KeycloakModelUtils;
|
||||
|
||||
import java.util.HashMap;
|
||||
import java.util.HashSet;
|
||||
import java.util.LinkedList;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
import java.util.Set;
|
||||
import java.util.*;
|
||||
|
||||
|
||||
/**
|
||||
|
@ -912,6 +877,47 @@ public class RealmCacheSession implements CacheRealmProvider {
|
|||
return list;
|
||||
}
|
||||
|
||||
@Override
|
||||
public List<GroupModel> getTopLevelGroups(RealmModel realm, Integer first, Integer max) {
|
||||
String cacheKey = getTopGroupsQueryCacheKey(realm.getId() + first + max);
|
||||
boolean queryDB = invalidations.contains(cacheKey) || listInvalidations.contains(realm.getId() + first + max);
|
||||
if (queryDB) {
|
||||
return getDelegate().getTopLevelGroups(realm, first, max);
|
||||
}
|
||||
|
||||
GroupListQuery query = cache.get(cacheKey, GroupListQuery.class);
|
||||
if (Objects.nonNull(query)) {
|
||||
logger.tracev("getTopLevelGroups cache hit: {0}", realm.getName());
|
||||
}
|
||||
|
||||
if (Objects.isNull(query)) {
|
||||
Long loaded = cache.getCurrentRevision(cacheKey);
|
||||
List<GroupModel> model = getDelegate().getTopLevelGroups(realm, first, max);
|
||||
if (model == null) return null;
|
||||
Set<String> ids = new HashSet<>();
|
||||
for (GroupModel client : model) ids.add(client.getId());
|
||||
query = new GroupListQuery(loaded, cacheKey, realm, ids);
|
||||
logger.tracev("adding realm getTopLevelGroups cache miss: realm {0} key {1}", realm.getName(), cacheKey);
|
||||
cache.addRevisioned(query, startupRevision);
|
||||
return model;
|
||||
}
|
||||
List<GroupModel> list = new LinkedList<>();
|
||||
for (String id : query.getGroups()) {
|
||||
GroupModel group = session.realms().getGroupById(id, realm);
|
||||
if (Objects.isNull(group)) {
|
||||
invalidations.add(cacheKey);
|
||||
return getDelegate().getTopLevelGroups(realm);
|
||||
}
|
||||
list.add(group);
|
||||
}
|
||||
return list;
|
||||
}
|
||||
|
||||
@Override
|
||||
public List<GroupModel> searchForGroupByName(RealmModel realm, String search, Integer first, Integer max) {
|
||||
return getDelegate().searchForGroupByName(realm, search, first, max);
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean removeGroup(RealmModel realm, GroupModel group) {
|
||||
invalidateGroup(group.getId(), realm.getId(), true);
|
||||
|
|
|
@ -20,29 +20,13 @@ package org.keycloak.models.jpa;
|
|||
import org.jboss.logging.Logger;
|
||||
import org.keycloak.connections.jpa.util.JpaUtils;
|
||||
import org.keycloak.migration.MigrationModel;
|
||||
import org.keycloak.models.ClientModel;
|
||||
import org.keycloak.models.ClientTemplateModel;
|
||||
import org.keycloak.models.GroupModel;
|
||||
import org.keycloak.models.KeycloakSession;
|
||||
import org.keycloak.models.RealmModel;
|
||||
import org.keycloak.models.RealmProvider;
|
||||
import org.keycloak.models.RoleContainerModel;
|
||||
import org.keycloak.models.RoleModel;
|
||||
import org.keycloak.models.jpa.entities.ClientEntity;
|
||||
import org.keycloak.models.jpa.entities.ClientTemplateEntity;
|
||||
import org.keycloak.models.jpa.entities.GroupEntity;
|
||||
import org.keycloak.models.jpa.entities.RealmEntity;
|
||||
import org.keycloak.models.jpa.entities.RoleEntity;
|
||||
import org.keycloak.models.*;
|
||||
import org.keycloak.models.jpa.entities.*;
|
||||
import org.keycloak.models.utils.KeycloakModelUtils;
|
||||
|
||||
import javax.persistence.EntityManager;
|
||||
import javax.persistence.TypedQuery;
|
||||
import java.util.ArrayList;
|
||||
import java.util.Collections;
|
||||
import java.util.HashSet;
|
||||
import java.util.LinkedList;
|
||||
import java.util.List;
|
||||
import java.util.Set;
|
||||
import java.util.*;
|
||||
import java.util.stream.Collectors;
|
||||
|
||||
/**
|
||||
|
@ -349,6 +333,24 @@ public class JpaRealmProvider implements RealmProvider {
|
|||
Collectors.toList(), Collections::unmodifiableList));
|
||||
}
|
||||
|
||||
@Override
|
||||
public List<GroupModel> getTopLevelGroups(RealmModel realm, Integer first, Integer max) {
|
||||
List<String> groupIds = em.createNamedQuery("getTopLevelGroupIds", String.class)
|
||||
.setParameter("realm", realm.getId())
|
||||
.setFirstResult(first)
|
||||
.setMaxResults(max)
|
||||
.getResultList();
|
||||
List<GroupModel> list = new ArrayList<>();
|
||||
if(Objects.nonNull(groupIds) && !groupIds.isEmpty()) {
|
||||
for (String id : groupIds) {
|
||||
GroupModel group = getGroupById(id, realm);
|
||||
list.add(group);
|
||||
}
|
||||
}
|
||||
|
||||
return Collections.unmodifiableList(list);
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean removeGroup(RealmModel realm, GroupModel group) {
|
||||
if (group == null) {
|
||||
|
@ -519,4 +521,21 @@ public class JpaRealmProvider implements RealmProvider {
|
|||
ClientTemplateAdapter adapter = new ClientTemplateAdapter(realm, em, session, app);
|
||||
return adapter;
|
||||
}
|
||||
|
||||
@Override
|
||||
public List<GroupModel> searchForGroupByName(RealmModel realm, String search, Integer first, Integer max) {
|
||||
TypedQuery<String> query = em.createNamedQuery("getGroupIdsByNameContaining", String.class)
|
||||
.setParameter("realm", realm.getId())
|
||||
.setParameter("search", search);
|
||||
if(Objects.nonNull(first) && Objects.nonNull(max)) {
|
||||
query= query.setFirstResult(first).setMaxResults(max);
|
||||
}
|
||||
List<String> groups = query.getResultList();
|
||||
if (Objects.isNull(groups)) return Collections.EMPTY_LIST;
|
||||
List<GroupModel> list = new LinkedList<>();
|
||||
for (String id : groups) {
|
||||
list.add(session.realms().getGroupById(id, realm));
|
||||
}
|
||||
return Collections.unmodifiableList(list);
|
||||
}
|
||||
}
|
||||
|
|
|
@ -22,53 +22,13 @@ import org.keycloak.common.enums.SslRequired;
|
|||
import org.keycloak.common.util.MultivaluedHashMap;
|
||||
import org.keycloak.component.ComponentFactory;
|
||||
import org.keycloak.component.ComponentModel;
|
||||
import org.keycloak.models.AuthenticationExecutionModel;
|
||||
import org.keycloak.models.AuthenticationFlowModel;
|
||||
import org.keycloak.models.AuthenticatorConfigModel;
|
||||
import org.keycloak.models.ClientModel;
|
||||
import org.keycloak.models.ClientTemplateModel;
|
||||
import org.keycloak.models.GroupModel;
|
||||
import org.keycloak.models.IdentityProviderMapperModel;
|
||||
import org.keycloak.models.IdentityProviderModel;
|
||||
import org.keycloak.models.KeycloakSession;
|
||||
import org.keycloak.models.ModelException;
|
||||
import org.keycloak.models.OTPPolicy;
|
||||
import org.keycloak.models.PasswordPolicy;
|
||||
import org.keycloak.models.RealmModel;
|
||||
import org.keycloak.models.RequiredActionProviderModel;
|
||||
import org.keycloak.models.RequiredCredentialModel;
|
||||
import org.keycloak.models.RoleModel;
|
||||
import org.keycloak.models.jpa.entities.AuthenticationExecutionEntity;
|
||||
import org.keycloak.models.jpa.entities.AuthenticationFlowEntity;
|
||||
import org.keycloak.models.jpa.entities.AuthenticatorConfigEntity;
|
||||
import org.keycloak.models.jpa.entities.ClientEntity;
|
||||
import org.keycloak.models.jpa.entities.ClientTemplateEntity;
|
||||
import org.keycloak.models.jpa.entities.ComponentConfigEntity;
|
||||
import org.keycloak.models.jpa.entities.ComponentEntity;
|
||||
import org.keycloak.models.jpa.entities.GroupEntity;
|
||||
import org.keycloak.models.jpa.entities.IdentityProviderEntity;
|
||||
import org.keycloak.models.jpa.entities.IdentityProviderMapperEntity;
|
||||
import org.keycloak.models.jpa.entities.RealmAttributeEntity;
|
||||
import org.keycloak.models.jpa.entities.RealmAttributes;
|
||||
import org.keycloak.models.jpa.entities.RealmEntity;
|
||||
import org.keycloak.models.jpa.entities.RequiredActionProviderEntity;
|
||||
import org.keycloak.models.jpa.entities.RequiredCredentialEntity;
|
||||
import org.keycloak.models.jpa.entities.RoleEntity;
|
||||
import org.keycloak.models.*;
|
||||
import org.keycloak.models.jpa.entities.*;
|
||||
import org.keycloak.models.utils.ComponentUtil;
|
||||
import org.keycloak.models.utils.KeycloakModelUtils;
|
||||
|
||||
import javax.persistence.EntityManager;
|
||||
import java.util.ArrayList;
|
||||
import java.util.Collection;
|
||||
import java.util.Collections;
|
||||
import java.util.HashMap;
|
||||
import java.util.HashSet;
|
||||
import java.util.Iterator;
|
||||
import java.util.LinkedList;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
import java.util.Objects;
|
||||
import java.util.Set;
|
||||
import java.util.*;
|
||||
import java.util.function.Predicate;
|
||||
import java.util.stream.Collectors;
|
||||
|
||||
|
@ -360,7 +320,7 @@ public class RealmAdapter implements RealmModel, JpaModel<RealmEntity> {
|
|||
realm.setVerifyEmail(verifyEmail);
|
||||
em.flush();
|
||||
}
|
||||
|
||||
|
||||
@Override
|
||||
public boolean isLoginWithEmailAllowed() {
|
||||
return realm.isLoginWithEmailAllowed();
|
||||
|
@ -372,7 +332,7 @@ public class RealmAdapter implements RealmModel, JpaModel<RealmEntity> {
|
|||
if (loginWithEmailAllowed) realm.setDuplicateEmailsAllowed(false);
|
||||
em.flush();
|
||||
}
|
||||
|
||||
|
||||
@Override
|
||||
public boolean isDuplicateEmailsAllowed() {
|
||||
return realm.isDuplicateEmailsAllowed();
|
||||
|
@ -1719,6 +1679,16 @@ public class RealmAdapter implements RealmModel, JpaModel<RealmEntity> {
|
|||
return session.realms().getTopLevelGroups(this);
|
||||
}
|
||||
|
||||
@Override
|
||||
public List<GroupModel> getTopLevelGroups(Integer first, Integer max) {
|
||||
return session.realms().getTopLevelGroups(this, first, max);
|
||||
}
|
||||
|
||||
@Override
|
||||
public List<GroupModel> searchForGroupByName(String search, Integer first, Integer max) {
|
||||
return session.realms().searchForGroupByName(this, search, first, max);
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean removeGroup(GroupModel group) {
|
||||
return session.realms().removeGroup(this, group);
|
||||
|
|
|
@ -17,19 +17,7 @@
|
|||
|
||||
package org.keycloak.models.jpa.entities;
|
||||
|
||||
import javax.persistence.Access;
|
||||
import javax.persistence.AccessType;
|
||||
import javax.persistence.CascadeType;
|
||||
import javax.persistence.Column;
|
||||
import javax.persistence.Entity;
|
||||
import javax.persistence.FetchType;
|
||||
import javax.persistence.Id;
|
||||
import javax.persistence.JoinColumn;
|
||||
import javax.persistence.ManyToOne;
|
||||
import javax.persistence.NamedQueries;
|
||||
import javax.persistence.NamedQuery;
|
||||
import javax.persistence.OneToMany;
|
||||
import javax.persistence.Table;
|
||||
import javax.persistence.*;
|
||||
import java.util.ArrayList;
|
||||
import java.util.Collection;
|
||||
|
||||
|
@ -39,6 +27,8 @@ import java.util.Collection;
|
|||
*/
|
||||
@NamedQueries({
|
||||
@NamedQuery(name="getGroupIdsByParent", query="select u.id from GroupEntity u where u.parent = :parent"),
|
||||
@NamedQuery(name="getGroupIdsByNameContaining", query="select u.id from GroupEntity u where u.realm.id = :realm and u.name like concat('%',:search,'%') order by u.name ASC"),
|
||||
@NamedQuery(name="getTopLevelGroupIds", query="select u.id from GroupEntity u where u.parent is null and u.realm.id = :realm")
|
||||
})
|
||||
@Entity
|
||||
@Table(name="KEYCLOAK_GROUP")
|
||||
|
|
|
@ -18,7 +18,6 @@ package org.keycloak.models.cache;
|
|||
|
||||
import org.keycloak.models.KeycloakSession;
|
||||
import org.keycloak.models.RealmModel;
|
||||
import org.keycloak.models.UserModel;
|
||||
import org.keycloak.provider.ProviderEvent;
|
||||
|
||||
import java.util.concurrent.ConcurrentHashMap;
|
||||
|
|
|
@ -17,17 +17,6 @@
|
|||
|
||||
package org.keycloak.models.utils;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.Collections;
|
||||
import java.util.Comparator;
|
||||
import java.util.HashMap;
|
||||
import java.util.HashSet;
|
||||
import java.util.LinkedList;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
import java.util.Set;
|
||||
import java.util.stream.Collectors;
|
||||
|
||||
import org.keycloak.authorization.AuthorizationProvider;
|
||||
import org.keycloak.authorization.model.Policy;
|
||||
import org.keycloak.authorization.model.Resource;
|
||||
|
@ -42,60 +31,15 @@ import org.keycloak.credential.CredentialModel;
|
|||
import org.keycloak.events.Event;
|
||||
import org.keycloak.events.admin.AdminEvent;
|
||||
import org.keycloak.events.admin.AuthDetails;
|
||||
import org.keycloak.models.AuthenticationExecutionModel;
|
||||
import org.keycloak.models.AuthenticationFlowModel;
|
||||
import org.keycloak.models.AuthenticatorConfigModel;
|
||||
import org.keycloak.models.AuthenticatedClientSessionModel;
|
||||
import org.keycloak.models.ClientModel;
|
||||
import org.keycloak.models.ClientTemplateModel;
|
||||
import org.keycloak.models.FederatedIdentityModel;
|
||||
import org.keycloak.models.GroupModel;
|
||||
import org.keycloak.models.IdentityProviderMapperModel;
|
||||
import org.keycloak.models.IdentityProviderModel;
|
||||
import org.keycloak.models.KeycloakSession;
|
||||
import org.keycloak.models.ModelException;
|
||||
import org.keycloak.models.OTPPolicy;
|
||||
import org.keycloak.models.ProtocolMapperModel;
|
||||
import org.keycloak.models.RealmModel;
|
||||
import org.keycloak.models.RequiredActionProviderModel;
|
||||
import org.keycloak.models.RequiredCredentialModel;
|
||||
import org.keycloak.models.RoleModel;
|
||||
import org.keycloak.models.UserConsentModel;
|
||||
import org.keycloak.models.UserCredentialModel;
|
||||
import org.keycloak.models.UserModel;
|
||||
import org.keycloak.models.UserSessionModel;
|
||||
import org.keycloak.models.*;
|
||||
import org.keycloak.provider.ProviderConfigProperty;
|
||||
import org.keycloak.representations.idm.AdminEventRepresentation;
|
||||
import org.keycloak.representations.idm.AuthDetailsRepresentation;
|
||||
import org.keycloak.representations.idm.AuthenticationExecutionExportRepresentation;
|
||||
import org.keycloak.representations.idm.AuthenticationFlowRepresentation;
|
||||
import org.keycloak.representations.idm.AuthenticatorConfigRepresentation;
|
||||
import org.keycloak.representations.idm.ClientRepresentation;
|
||||
import org.keycloak.representations.idm.ClientTemplateRepresentation;
|
||||
import org.keycloak.representations.idm.ComponentRepresentation;
|
||||
import org.keycloak.representations.idm.ConfigPropertyRepresentation;
|
||||
import org.keycloak.representations.idm.CredentialRepresentation;
|
||||
import org.keycloak.representations.idm.EventRepresentation;
|
||||
import org.keycloak.representations.idm.FederatedIdentityRepresentation;
|
||||
import org.keycloak.representations.idm.GroupRepresentation;
|
||||
import org.keycloak.representations.idm.IdentityProviderMapperRepresentation;
|
||||
import org.keycloak.representations.idm.IdentityProviderRepresentation;
|
||||
import org.keycloak.representations.idm.ProtocolMapperRepresentation;
|
||||
import org.keycloak.representations.idm.RealmEventsConfigRepresentation;
|
||||
import org.keycloak.representations.idm.RealmRepresentation;
|
||||
import org.keycloak.representations.idm.RequiredActionProviderRepresentation;
|
||||
import org.keycloak.representations.idm.RoleRepresentation;
|
||||
import org.keycloak.representations.idm.UserConsentRepresentation;
|
||||
import org.keycloak.representations.idm.UserRepresentation;
|
||||
import org.keycloak.representations.idm.UserSessionRepresentation;
|
||||
import org.keycloak.representations.idm.authorization.AbstractPolicyRepresentation;
|
||||
import org.keycloak.representations.idm.authorization.PolicyRepresentation;
|
||||
import org.keycloak.representations.idm.authorization.ResourceOwnerRepresentation;
|
||||
import org.keycloak.representations.idm.authorization.ResourceRepresentation;
|
||||
import org.keycloak.representations.idm.authorization.ResourceServerRepresentation;
|
||||
import org.keycloak.representations.idm.authorization.ScopeRepresentation;
|
||||
import org.keycloak.representations.idm.*;
|
||||
import org.keycloak.representations.idm.authorization.*;
|
||||
import org.keycloak.storage.StorageId;
|
||||
|
||||
import java.util.*;
|
||||
import java.util.stream.Collectors;
|
||||
|
||||
/**
|
||||
* @author <a href="mailto:bill@burkecentral.com">Bill Burke</a>
|
||||
* @version $Revision: 1 $
|
||||
|
@ -147,10 +91,32 @@ public class ModelToRepresentation {
|
|||
return rep;
|
||||
}
|
||||
|
||||
public static List<GroupRepresentation> searchForGroupByName(RealmModel realm, String search, Integer first, Integer max) {
|
||||
List<GroupRepresentation> result = new LinkedList<>();
|
||||
List<GroupModel> groups = realm.searchForGroupByName(search, first, max);
|
||||
if (Objects.isNull(groups)) return result;
|
||||
for (GroupModel group : groups) {
|
||||
GroupRepresentation rep = toGroupHierarchy(group, false);
|
||||
result.add(rep);
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
public static List<GroupRepresentation> toGroupHierarchy(RealmModel realm, boolean full, Integer first, Integer max) {
|
||||
List<GroupRepresentation> hierarchy = new LinkedList<>();
|
||||
List<GroupModel> groups = realm.getTopLevelGroups(first, max);
|
||||
if (Objects.isNull(groups)) return hierarchy;
|
||||
for (GroupModel group : groups) {
|
||||
GroupRepresentation rep = toGroupHierarchy(group, full);
|
||||
hierarchy.add(rep);
|
||||
}
|
||||
return hierarchy;
|
||||
}
|
||||
|
||||
public static List<GroupRepresentation> toGroupHierarchy(RealmModel realm, boolean full) {
|
||||
List<GroupRepresentation> hierarchy = new LinkedList<>();
|
||||
List<GroupModel> groups = realm.getTopLevelGroups();
|
||||
if (groups == null) return hierarchy;
|
||||
if (Objects.isNull(groups)) return hierarchy;
|
||||
for (GroupModel group : groups) {
|
||||
GroupRepresentation rep = toGroupHierarchy(group, full);
|
||||
hierarchy.add(rep);
|
||||
|
|
|
@ -23,11 +23,7 @@ import org.keycloak.provider.ProviderEvent;
|
|||
import org.keycloak.storage.UserStorageProvider;
|
||||
import org.keycloak.storage.UserStorageProviderModel;
|
||||
|
||||
import java.util.Collections;
|
||||
import java.util.LinkedList;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
import java.util.Set;
|
||||
import java.util.*;
|
||||
|
||||
/**
|
||||
* @author <a href="mailto:bill@burkecentral.com">Bill Burke</a>
|
||||
|
@ -147,11 +143,11 @@ public interface RealmModel extends RoleContainerModel {
|
|||
boolean isVerifyEmail();
|
||||
|
||||
void setVerifyEmail(boolean verifyEmail);
|
||||
|
||||
|
||||
boolean isLoginWithEmailAllowed();
|
||||
|
||||
void setLoginWithEmailAllowed(boolean loginWithEmailAllowed);
|
||||
|
||||
|
||||
boolean isDuplicateEmailsAllowed();
|
||||
|
||||
void setDuplicateEmailsAllowed(boolean duplicateEmailsAllowed);
|
||||
|
@ -402,6 +398,8 @@ public interface RealmModel extends RoleContainerModel {
|
|||
GroupModel getGroupById(String id);
|
||||
List<GroupModel> getGroups();
|
||||
List<GroupModel> getTopLevelGroups();
|
||||
List<GroupModel> getTopLevelGroups(Integer first, Integer max);
|
||||
List<GroupModel> searchForGroupByName(String search, Integer first, Integer max);
|
||||
boolean removeGroup(GroupModel group);
|
||||
void moveGroup(GroupModel group, GroupModel toParent);
|
||||
|
||||
|
|
|
@ -42,6 +42,10 @@ public interface RealmProvider extends Provider {
|
|||
|
||||
List<GroupModel> getTopLevelGroups(RealmModel realm);
|
||||
|
||||
List<GroupModel> getTopLevelGroups(RealmModel realm, Integer first, Integer max);
|
||||
|
||||
List<GroupModel> searchForGroupByName(RealmModel realm, String search, Integer first, Integer max);
|
||||
|
||||
boolean removeGroup(RealmModel realm, GroupModel group);
|
||||
|
||||
GroupModel createGroup(RealmModel realm, String name);
|
||||
|
@ -85,8 +89,6 @@ public interface RealmProvider extends Provider {
|
|||
ClientTemplateModel getClientTemplateById(String id, RealmModel realm);
|
||||
GroupModel getGroupById(String id, RealmModel realm);
|
||||
|
||||
|
||||
|
||||
List<RealmModel> getRealms();
|
||||
boolean removeRealm(String id);
|
||||
void close();
|
||||
|
|
|
@ -26,20 +26,16 @@ import org.keycloak.models.KeycloakSession;
|
|||
import org.keycloak.models.RealmModel;
|
||||
import org.keycloak.models.utils.ModelToRepresentation;
|
||||
import org.keycloak.representations.idm.GroupRepresentation;
|
||||
import org.keycloak.services.ErrorResponse;
|
||||
|
||||
import javax.ws.rs.Consumes;
|
||||
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.*;
|
||||
import javax.ws.rs.core.Context;
|
||||
import javax.ws.rs.core.MediaType;
|
||||
import javax.ws.rs.core.Response;
|
||||
import javax.ws.rs.core.UriInfo;
|
||||
import java.net.URI;
|
||||
import java.util.List;
|
||||
import org.keycloak.services.ErrorResponse;
|
||||
import java.util.Objects;
|
||||
|
||||
/**
|
||||
* @resource Groups
|
||||
|
@ -71,10 +67,22 @@ public class GroupsResource {
|
|||
@GET
|
||||
@NoCache
|
||||
@Produces(MediaType.APPLICATION_JSON)
|
||||
public List<GroupRepresentation> getGroups() {
|
||||
public List<GroupRepresentation> getGroupsByName(@QueryParam("search") String search,
|
||||
@QueryParam("first") Integer firstResult,
|
||||
@QueryParam("max") Integer maxResults) {
|
||||
auth.requireView();
|
||||
|
||||
return ModelToRepresentation.toGroupHierarchy(realm, false);
|
||||
List<GroupRepresentation> results;
|
||||
|
||||
if (Objects.nonNull(search)) {
|
||||
results = ModelToRepresentation.searchForGroupByName(realm, search.trim(), firstResult, maxResults);
|
||||
} else if(Objects.nonNull(firstResult) && Objects.nonNull(maxResults)) {
|
||||
results = ModelToRepresentation.toGroupHierarchy(realm, false, firstResult, maxResults);
|
||||
} else {
|
||||
results = ModelToRepresentation.toGroupHierarchy(realm, false);
|
||||
}
|
||||
|
||||
return results;
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -109,7 +117,7 @@ public class GroupsResource {
|
|||
return ErrorResponse.exists("Top level group named '" + rep.getName() + "' already exists.");
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
GroupModel child = null;
|
||||
Response.ResponseBuilder builder = Response.status(204);
|
||||
if (rep.getId() != null) {
|
||||
|
|
Loading…
Reference in a new issue