KEYCLOAK-8613 Group Membership Pagination
This commit is contained in:
1 parent
39bf08e1b9
commit
85f11873c3
19 files changed
+922
-180
No files matched your search
+3
-2
@@ -31,6 +31,7 @@ import org.keycloak.models.utils.RoleUtils;
|
||||
|
||||
import java.util.Collections;
|
||||
import java.util.HashSet;
|
||||
import java.util.LinkedHashSet;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
import java.util.Set;
|
||||
@@ -256,7 +257,7 @@ public class UserAdapter implements CachedUserModel {
|
||||
public void setFederationLink(String link) {
|
||||
getDelegateForUpdate();
|
||||
updated.setFederationLink(link);
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public String getServiceAccountClientLink() {
|
||||
@@ -346,7 +347,7 @@ public class UserAdapter implements CachedUserModel {
|
||||
@Override
|
||||
public Set<GroupModel> getGroups() {
|
||||
if (updated != null) return updated.getGroups();
|
||||
Set<GroupModel> groups = new HashSet<GroupModel>();
|
||||
Set<GroupModel> groups = new LinkedHashSet<>();
|
||||
for (String id : cached.getGroups(modelSupplier)) {
|
||||
GroupModel groupModel = keycloakSession.realms().getGroupById(id, realm);
|
||||
if (groupModel == null) {
|
||||
|
||||
Vendored
+2
-1
@@ -26,6 +26,7 @@ import org.keycloak.models.cache.infinispan.DefaultLazyLoader;
|
||||
import org.keycloak.models.cache.infinispan.LazyLoader;
|
||||
|
||||
import java.util.Collections;
|
||||
import java.util.LinkedHashSet;
|
||||
import java.util.Set;
|
||||
import java.util.function.Supplier;
|
||||
import java.util.stream.Collectors;
|
||||
@@ -68,7 +69,7 @@ public class CachedUser extends AbstractExtendableRevisioned implements InRealm
|
||||
this.requiredActions = new DefaultLazyLoader<>(UserModel::getRequiredActions, Collections::emptySet);
|
||||
this.attributes = new DefaultLazyLoader<>(userModel -> new MultivaluedHashMap<>(userModel.getAttributes()), MultivaluedHashMap::new);
|
||||
this.roleMappings = new DefaultLazyLoader<>(userModel -> userModel.getRoleMappings().stream().map(RoleModel::getId).collect(Collectors.toSet()), Collections::emptySet);
|
||||
this.groups = new DefaultLazyLoader<>(userModel -> userModel.getGroups().stream().map(GroupModel::getId).collect(Collectors.toSet()), Collections::emptySet);
|
||||
this.groups = new DefaultLazyLoader<>(userModel -> userModel.getGroups().stream().map(GroupModel::getId).collect(Collectors.toCollection(LinkedHashSet::new)), LinkedHashSet::new);
|
||||
}
|
||||
|
||||
public String getRealm() {
|
||||
|
||||
@@ -25,6 +25,7 @@ import org.keycloak.models.RealmModel;
|
||||
import org.keycloak.models.RoleContainerModel;
|
||||
import org.keycloak.models.RoleModel;
|
||||
import org.keycloak.models.UserModel;
|
||||
import org.keycloak.models.jpa.entities.GroupEntity;
|
||||
import org.keycloak.models.jpa.entities.UserAttributeEntity;
|
||||
import org.keycloak.models.jpa.entities.UserEntity;
|
||||
import org.keycloak.models.jpa.entities.UserGroupMembershipEntity;
|
||||
@@ -36,12 +37,20 @@ import org.keycloak.models.utils.RoleUtils;
|
||||
import javax.persistence.EntityManager;
|
||||
import javax.persistence.Query;
|
||||
import javax.persistence.TypedQuery;
|
||||
import javax.persistence.criteria.CriteriaBuilder;
|
||||
import javax.persistence.criteria.CriteriaQuery;
|
||||
import javax.persistence.criteria.Join;
|
||||
import javax.persistence.criteria.Predicate;
|
||||
import javax.persistence.criteria.Root;
|
||||
import java.util.ArrayList;
|
||||
import java.util.Collection;
|
||||
import java.util.HashSet;
|
||||
import java.util.Iterator;
|
||||
import java.util.LinkedHashSet;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
import java.util.Set;
|
||||
import java.util.Objects;
|
||||
|
||||
/**
|
||||
* @author <a href="mailto:bill@burkecentral.com">Bill Burke</a>
|
||||
@@ -289,22 +298,78 @@ public class UserAdapter implements UserModel, JpaModel<UserEntity> {
|
||||
user.setEmailVerified(verified);
|
||||
}
|
||||
|
||||
@Override
|
||||
public Set<GroupModel> getGroups() {
|
||||
private TypedQuery<String> createGetGroupsQuery(String search, Integer first, Integer max) {
|
||||
// we query ids only as the group might be cached and following the @ManyToOne will result in a load
|
||||
// even if we're getting just the id.
|
||||
TypedQuery<String> query = em.createNamedQuery("userGroupIds", String.class);
|
||||
query.setParameter("user", getEntity());
|
||||
List<String> ids = query.getResultList();
|
||||
Set<GroupModel> groups = new HashSet<>();
|
||||
for (String groupId : ids) {
|
||||
GroupModel group = realm.getGroupById(groupId);
|
||||
if (group == null) continue;
|
||||
groups.add(group);
|
||||
CriteriaBuilder builder = em.getCriteriaBuilder();
|
||||
CriteriaQuery<String> queryBuilder = builder.createQuery(String.class);
|
||||
Root<UserGroupMembershipEntity> root = queryBuilder.from(UserGroupMembershipEntity.class);
|
||||
|
||||
List<Predicate> predicates = new ArrayList<>();
|
||||
predicates.add(builder.equal(root.get("user"), getEntity()));
|
||||
Join<UserGroupMembershipEntity, GroupEntity> join = root.join("group");
|
||||
if (Objects.nonNull(search) && !search.isEmpty()) {
|
||||
predicates.add(builder.like(builder.lower(join.get("name")), builder.lower(builder.literal("%" + search + "%"))));
|
||||
}
|
||||
|
||||
queryBuilder.select(root.get("groupId"));
|
||||
queryBuilder.where(predicates.toArray(new Predicate[0]));
|
||||
queryBuilder.orderBy(builder.asc(join.get("name")));
|
||||
|
||||
TypedQuery<String> query = em.createQuery(queryBuilder);
|
||||
if (Objects.nonNull(first) && Objects.nonNull(max)) {
|
||||
query.setFirstResult(first).setMaxResults(max);
|
||||
}
|
||||
return query;
|
||||
}
|
||||
|
||||
private TypedQuery<Long> createCountGroupsQuery(String search) {
|
||||
// we query ids only as the group might be cached and following the @ManyToOne will result in a load
|
||||
// even if we're getting just the id.
|
||||
CriteriaBuilder builder = em.getCriteriaBuilder();
|
||||
CriteriaQuery<Long> queryBuilder = builder.createQuery(Long.class);
|
||||
Root<UserGroupMembershipEntity> root = queryBuilder.from(UserGroupMembershipEntity.class);
|
||||
|
||||
List<Predicate> predicates = new ArrayList<>();
|
||||
predicates.add(builder.equal(root.get("user"), getEntity()));
|
||||
if (Objects.nonNull(search) && !search.isEmpty()) {
|
||||
Join<UserGroupMembershipEntity, GroupEntity> join = root.join("group");
|
||||
predicates.add(builder.like(join.get("name"), builder.literal("%" + search + "%")));
|
||||
}
|
||||
|
||||
queryBuilder.select(builder.count(root));
|
||||
queryBuilder.where(predicates.toArray(new Predicate[0]));
|
||||
return em.createQuery(queryBuilder);
|
||||
}
|
||||
|
||||
private Set<GroupModel> getGroupModels(Collection<String> groupIds) {
|
||||
Set<GroupModel> groups = new LinkedHashSet<>();
|
||||
for (String id : groupIds) {
|
||||
groups.add(realm.getGroupById(id));
|
||||
}
|
||||
return groups;
|
||||
}
|
||||
|
||||
@Override
|
||||
public Set<GroupModel> getGroups() {
|
||||
return getGroupModels(createGetGroupsQuery(null, null, null).getResultList());
|
||||
}
|
||||
|
||||
@Override
|
||||
public Set<GroupModel> getGroups(String search, int first, int max) {
|
||||
return getGroupModels(createGetGroupsQuery(search, first, max).getResultList());
|
||||
}
|
||||
|
||||
@Override
|
||||
public long getGroupsCount() {
|
||||
return createCountGroupsQuery(null).getSingleResult();
|
||||
}
|
||||
|
||||
@Override
|
||||
public long getGroupsCountByNameContaining(String search) {
|
||||
return createCountGroupsQuery(search).getSingleResult();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void joinGroup(GroupModel group) {
|
||||
if (isMemberOf(group)) return;
|
||||
@@ -354,7 +419,7 @@ public class UserAdapter implements UserModel, JpaModel<UserEntity> {
|
||||
public boolean hasRole(RoleModel role) {
|
||||
Set<RoleModel> roles = getRoleMappings();
|
||||
return RoleUtils.hasRole(roles, role)
|
||||
|| RoleUtils.hasRoleFromGroup(getGroups(), role, true);
|
||||
|| RoleUtils.hasRoleFromGroup(getGroups(), role, true);
|
||||
}
|
||||
|
||||
protected TypedQuery<UserRoleMappingEntity> getUserRoleMappingEntityTypedQuery(RoleModel role) {
|
||||
@@ -431,9 +496,9 @@ public class UserAdapter implements UserModel, JpaModel<UserEntity> {
|
||||
for (RoleModel role : roleMappings) {
|
||||
RoleContainerModel container = role.getContainer();
|
||||
if (container instanceof ClientModel) {
|
||||
ClientModel appModel = (ClientModel)container;
|
||||
ClientModel appModel = (ClientModel) container;
|
||||
if (appModel.getId().equals(app.getId())) {
|
||||
roles.add(role);
|
||||
roles.add(role);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -476,5 +541,4 @@ public class UserAdapter implements UserModel, JpaModel<UserEntity> {
|
||||
}
|
||||
|
||||
|
||||
|
||||
}
|
||||
+5
-2
@@ -37,12 +37,10 @@ import java.io.Serializable;
|
||||
@NamedQuery(name="userMemberOf", query="select m from UserGroupMembershipEntity m where m.user = :user and m.groupId = :groupId"),
|
||||
@NamedQuery(name="userGroupMembership", query="select m from UserGroupMembershipEntity m where m.user = :user"),
|
||||
@NamedQuery(name="groupMembership", query="select g.user from UserGroupMembershipEntity g where g.groupId = :groupId order by g.user.username"),
|
||||
@NamedQuery(name="userGroupIds", query="select m.groupId from UserGroupMembershipEntity m where m.user = :user"),
|
||||
@NamedQuery(name="deleteUserGroupMembershipByRealm", query="delete from UserGroupMembershipEntity mapping where mapping.user IN (select u from UserEntity u where u.realmId=:realmId)"),
|
||||
@NamedQuery(name="deleteUserGroupMembershipsByRealmAndLink", query="delete from UserGroupMembershipEntity mapping where mapping.user IN (select u from UserEntity u where u.realmId=:realmId and u.federationLink=:link)"),
|
||||
@NamedQuery(name="deleteUserGroupMembershipsByGroup", query="delete from UserGroupMembershipEntity m where m.groupId = :groupId"),
|
||||
@NamedQuery(name="deleteUserGroupMembershipsByUser", query="delete from UserGroupMembershipEntity m where m.user = :user")
|
||||
|
||||
})
|
||||
@Table(name="USER_GROUP_MEMBERSHIP")
|
||||
@Entity
|
||||
@@ -54,6 +52,11 @@ public class UserGroupMembershipEntity {
|
||||
@JoinColumn(name="USER_ID")
|
||||
protected UserEntity user;
|
||||
|
||||
@Id
|
||||
@ManyToOne(fetch= FetchType.LAZY)
|
||||
@JoinColumn(name="GROUP_ID", insertable=false, updatable=false)
|
||||
protected GroupEntity group;
|
||||
|
||||
@Id
|
||||
@Column(name = "GROUP_ID")
|
||||
protected String groupId;
|
||||
|
||||
Reference in new issue
Block a user