KEYCLOAK-15102 Complement methods for accessing groups with Stream variants

This commit is contained in:
Martin Kanis 2020-08-11 12:13:21 +02:00 committed by Hynek Mlnařík
parent 6d45d715d3
commit d59a74c364
51 changed files with 606 additions and 678 deletions

View file

@ -24,6 +24,7 @@ import java.util.HashSet;
import java.util.List;
import java.util.Map;
import java.util.Set;
import java.util.stream.Collectors;
import org.keycloak.Config;
import org.keycloak.authorization.AuthorizationProvider;
@ -162,7 +163,7 @@ public class GroupPolicyProviderFactory implements PolicyProviderFactory<GroupPo
config.put("groupsClaim", groupsClaim);
}
List<GroupModel> topLevelGroups = authorization.getRealm().getTopLevelGroups();
List<GroupModel> topLevelGroups = authorization.getRealm().getTopLevelGroupsStream().collect(Collectors.toList());
for (GroupPolicyRepresentation.GroupDefinition definition : groups) {
GroupModel group = null;
@ -184,7 +185,7 @@ public class GroupPolicyProviderFactory implements PolicyProviderFactory<GroupPo
if (parent == null) {
parent = topLevelGroups.stream().filter(groupModel -> groupModel.getName().equals(part)).findFirst().orElseThrow(() -> new RuntimeException("Top level group with name [" + part + "] not found"));
} else {
group = parent.getSubGroups().stream().filter(groupModel -> groupModel.getName().equals(part)).findFirst().orElseThrow(() -> new RuntimeException("Group with name [" + part + "] not found"));
group = parent.getSubGroupsStream().filter(groupModel -> groupModel.getName().equals(part)).findFirst().orElseThrow(() -> new RuntimeException("Group with name [" + part + "] not found"));
parent = group;
}
}

View file

@ -284,9 +284,8 @@ public class LDAPStorageProvider implements UserStorageProvider,
UserModel proxy = proxy(realm, user, ldapUser, true);
DefaultRoles.addDefaultRoles(realm, proxy);
for (GroupModel g : realm.getDefaultGroups()) {
proxy.joinGroup(g);
}
realm.getDefaultGroupsStream().forEach(proxy::joinGroup);
for (RequiredActionProviderModel r : realm.getRequiredActionProviders()) {
if (r.isEnabled() && r.isDefaultAction()) {
proxy.addRequiredAction(r.getAlias());

View file

@ -26,8 +26,7 @@ import org.keycloak.storage.ldap.LDAPStorageProvider;
import org.keycloak.storage.ldap.idm.model.LDAPObject;
import org.keycloak.storage.ldap.idm.query.internal.LDAPQuery;
import java.util.HashSet;
import java.util.Set;
import java.util.stream.Stream;
/**
* @author <a href="mailto:jean-loup.maillet@yesitis.fr">Jean-Loup Maillet</a>
@ -51,12 +50,12 @@ public class HardcodedLDAPGroupStorageMapper extends AbstractLDAPStorageMapper {
return new UserModelDelegate(delegate) {
@Override
public Set<GroupModel> getGroups() {
Set<GroupModel> groups = new HashSet<GroupModel>(super.getGroups());
public Stream<GroupModel> getGroupsStream() {
Stream<GroupModel> groups = super.getGroupsStream();
GroupModel group = getGroup(realm);
if (group != null) {
groups.add(group);
return Stream.concat(groups, Stream.of(group));
}
return groups;

View file

@ -18,7 +18,6 @@
package org.keycloak.storage.ldap.mappers.membership.group;
import org.jboss.logging.Logger;
import org.keycloak.common.util.Time;
import org.keycloak.component.ComponentModel;
import org.keycloak.models.GroupModel;
import org.keycloak.models.ModelException;
@ -44,7 +43,6 @@ import org.keycloak.storage.ldap.mappers.membership.MembershipType;
import org.keycloak.storage.ldap.mappers.membership.UserRolesRetrieveStrategy;
import org.keycloak.storage.user.SynchronizationResult;
import java.util.ArrayList;
import java.util.Collection;
import java.util.Collections;
import java.util.HashMap;
@ -53,9 +51,12 @@ 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.function.Function;
import java.util.function.Predicate;
import java.util.stream.Collectors;
import java.util.stream.Stream;
/**
* @author <a href="mailto:mposolda@redhat.com">Marek Posolda</a>
@ -269,7 +270,6 @@ public class GroupLDAPStorageMapper extends AbstractLDAPStorageMapper implements
// List of group path groups known to the whole transaction
Map<String, GroupModel> transactionGroupPathGroups = getKcSubGroups(currentRealm, null)
.stream()
.collect(Collectors.toMap(GroupModel::getName, Function.identity()));
for (int i = 0; i < groupsPerTransaction && it.hasNext(); i++) {
@ -310,14 +310,8 @@ public class GroupLDAPStorageMapper extends AbstractLDAPStorageMapper implements
String groupName = groupTreeEntry.getGroupName();
// Check if group already exists
GroupModel kcGroup = null;
Collection<GroupModel> subgroups = getKcSubGroups(realm, kcParent);
for (GroupModel group : subgroups) {
if (group.getName().equals(groupName)) {
kcGroup = group;
break;
}
}
GroupModel kcGroup = getKcSubGroups(realm, kcParent)
.filter(g -> Objects.equals(g.getName(), groupName)).findFirst().orElse(null);
if (kcGroup != null) {
logger.debugf("Updated Keycloak group '%s' from LDAP", kcGroup.getName());
@ -344,14 +338,13 @@ public class GroupLDAPStorageMapper extends AbstractLDAPStorageMapper implements
private void dropNonExistingKcGroups(RealmModel realm, SynchronizationResult syncResult, Set<String> visitedGroupIds) {
// Remove keycloak groups, which doesn't exists in LDAP
List<GroupModel> allGroups = getAllKcGroups(realm);
for (GroupModel kcGroup : allGroups) {
if (!visitedGroupIds.contains(kcGroup.getId())) {
logger.debugf("Removing Keycloak group '%s', which doesn't exist in LDAP", kcGroup.getName());
realm.removeGroup(kcGroup);
syncResult.increaseRemoved();
}
}
getAllKcGroups(realm)
.filter(kcGroup -> !visitedGroupIds.contains(kcGroup.getId()))
.forEach(kcGroup -> {
logger.debugf("Removing Keycloak group '%s', which doesn't exist in LDAP", kcGroup.getName());
realm.removeGroup(kcGroup);
syncResult.increaseRemoved();
});
}
private void updateAttributesOfKCGroup(GroupModel kcGroup, LDAPObject ldapGroup) {
@ -374,14 +367,8 @@ public class GroupLDAPStorageMapper extends AbstractLDAPStorageMapper implements
if (config.isPreserveGroupsInheritance()) {
// Override if better effectivity or different algorithm is needed
List<GroupModel> groups = getAllKcGroups(realm);
for (GroupModel group : groups) {
if (group.getName().equals(groupName)) {
return group;
}
}
return null;
return getAllKcGroups(realm)
.filter(group -> Objects.equals(group.getName(), groupName)).findFirst().orElse(null);
} else {
// Without preserved inheritance, it's always at groups path
return KeycloakModelUtils.findGroupByPath(realm, getKcGroupPathFromLDAPGroupName(groupName));
@ -462,9 +449,8 @@ public class GroupLDAPStorageMapper extends AbstractLDAPStorageMapper implements
Set<String> ldapGroupNames = new HashSet<>();
// Create or update KC groups to LDAP including their attributes
for (GroupModel kcGroup : getKcSubGroups(realm, null)) {
processKeycloakGroupSyncToLDAP(kcGroup, ldapGroupsMap, ldapGroupNames, syncResult);
}
getKcSubGroups(realm, null)
.forEach(kcGroup -> processKeycloakGroupSyncToLDAP(kcGroup, ldapGroupsMap, ldapGroupNames, syncResult));
// If dropNonExisting, then drop all groups, which doesn't exist in KC from LDAP as well
if (config.isDropNonExistingGroupsDuringSync()) {
@ -480,9 +466,8 @@ public class GroupLDAPStorageMapper extends AbstractLDAPStorageMapper implements
// Finally process memberships,
if (config.isPreserveGroupsInheritance()) {
for (GroupModel kcGroup : getKcSubGroups(realm, null)) {
processKeycloakGroupMembershipsSyncToLDAP(kcGroup, ldapGroupsMap);
}
getKcSubGroups(realm, null)
.forEach(kcGroup -> processKeycloakGroupMembershipsSyncToLDAP(kcGroup, ldapGroupsMap));
}
return syncResult;
@ -497,9 +482,8 @@ public class GroupLDAPStorageMapper extends AbstractLDAPStorageMapper implements
// extract group attributes to be updated to LDAP
Map<String, Set<String>> supportedLdapAttributes = new HashMap<>();
for (String attrName : config.getGroupAttributes()) {
List<String> kcAttrValues = kcGroup.getAttribute(attrName);
Set<String> attrValues2 = (kcAttrValues == null || kcAttrValues.isEmpty()) ? null : new HashSet<>(kcAttrValues);
supportedLdapAttributes.put(attrName, attrValues2);
Set<String> valueSet = kcGroup.getAttributeStream(attrName).collect(Collectors.toSet());
supportedLdapAttributes.put(attrName, valueSet.isEmpty() ? null : valueSet);
}
LDAPObject ldapGroup = ldapGroupsMap.get(groupName);
@ -520,9 +504,8 @@ public class GroupLDAPStorageMapper extends AbstractLDAPStorageMapper implements
ldapGroupNames.add(groupName);
// process KC subgroups
for (GroupModel kcSubgroup : kcGroup.getSubGroups()) {
processKeycloakGroupSyncToLDAP(kcSubgroup, ldapGroupsMap, ldapGroupNames, syncResult);
}
kcGroup.getSubGroupsStream()
.forEach(kcSubgroup -> processKeycloakGroupSyncToLDAP(kcSubgroup, ldapGroupsMap, ldapGroupNames, syncResult));
}
// Update memberships of group in LDAP based on subgroups from KC. Do it recursively
@ -533,7 +516,7 @@ public class GroupLDAPStorageMapper extends AbstractLDAPStorageMapper implements
String membershipUserLdapAttrName = getMembershipUserLdapAttribute(); // Not applicable for groups, but needs to be here
// Add LDAP subgroups, which are KC subgroups
Set<GroupModel> kcSubgroups = kcGroup.getSubGroups();
Set<GroupModel> kcSubgroups = kcGroup.getSubGroupsStream().collect(Collectors.toSet());
for (GroupModel kcSubgroup : kcSubgroups) {
LDAPObject ldapSubgroup = ldapGroupsMap.get(kcSubgroup.getName());
if (!toRemoveSubgroupsDNs.remove(ldapSubgroup.getDn())) {
@ -549,7 +532,7 @@ public class GroupLDAPStorageMapper extends AbstractLDAPStorageMapper implements
LDAPUtils.deleteMember(ldapProvider, MembershipType.DN, config.getMembershipLdapAttribute(), membershipUserLdapAttrName, ldapGroup, fakeGroup);
}
for (GroupModel kcSubgroup : kcGroup.getSubGroups()) {
for (GroupModel kcSubgroup : kcSubgroups) {
processKeycloakGroupMembershipsSyncToLDAP(kcSubgroup, ldapGroupsMap);
}
}
@ -709,20 +692,18 @@ public class GroupLDAPStorageMapper extends AbstractLDAPStorageMapper implements
@Override
public boolean hasRole(RoleModel role) {
return super.hasRole(role) || RoleUtils.hasRoleFromGroup(getGroups(), role, true);
return super.hasRole(role) || RoleUtils.hasRoleFromGroup(getGroupsStream(), role, true);
}
@Override
public Set<GroupModel> getGroups() {
Set<GroupModel> ldapGroupMappings = getLDAPGroupMappingsConverted();
public Stream<GroupModel> getGroupsStream() {
Stream<GroupModel> ldapGroupMappings = getLDAPGroupMappingsConverted();
if (config.getMode() == LDAPGroupMapperMode.LDAP_ONLY) {
// Use just group mappings from LDAP
return ldapGroupMappings;
} else {
// Merge mappings from both DB and LDAP
Set<GroupModel> modelGroupMappings = super.getGroups();
ldapGroupMappings.addAll(modelGroupMappings);
return ldapGroupMappings;
return Stream.concat(ldapGroupMappings, super.getGroupsStream());
}
}
@ -770,28 +751,22 @@ public class GroupLDAPStorageMapper extends AbstractLDAPStorageMapper implements
@Override
public boolean isMemberOf(GroupModel group) {
Set<GroupModel> ldapGroupMappings = getGroups();
return ldapGroupMappings.contains(group);
return getGroupsStream().anyMatch(Predicate.isEqual(group));
}
protected Set<GroupModel> getLDAPGroupMappingsConverted() {
protected Stream<GroupModel> getLDAPGroupMappingsConverted() {
if (cachedLDAPGroupMappings != null) {
return new HashSet<>(cachedLDAPGroupMappings);
return cachedLDAPGroupMappings.stream();
}
List<LDAPObject> ldapGroups = getLDAPGroupMappings(ldapUser);
Set<GroupModel> result = new HashSet<>();
for (LDAPObject ldapGroup : ldapGroups) {
GroupModel kcGroup = findKcGroupOrSyncFromLDAP(realm, ldapGroup, this);
if (kcGroup != null) {
result.add(kcGroup);
}
}
cachedLDAPGroupMappings = ldapGroups.stream()
.map(ldapGroup -> findKcGroupOrSyncFromLDAP(realm, ldapGroup, this))
.filter(Objects::nonNull)
.collect(Collectors.toSet());
cachedLDAPGroupMappings = new HashSet<>(result);
return result;
return cachedLDAPGroupMappings.stream();
}
}
@ -826,56 +801,34 @@ public class GroupLDAPStorageMapper extends AbstractLDAPStorageMapper implements
/**
* Provides a list of all KC sub groups from given parent group or from groups path.
*/
protected Collection<GroupModel> getKcSubGroups(RealmModel realm, GroupModel parentGroup) {
protected Stream<GroupModel> getKcSubGroups(RealmModel realm, GroupModel parentGroup) {
// If no parent group given then use groups path
if (parentGroup == null) {
parentGroup = getKcGroupsPathGroup(realm);
}
return parentGroup == null ? realm.getTopLevelGroups() : parentGroup.getSubGroups();
return parentGroup == null ? realm.getTopLevelGroupsStream() : parentGroup.getSubGroupsStream();
}
/**
* Provides a list of all KC groups (with their sub groups) from groups path configured by the "Groups Path" configuration property.
* Provides a stream of all KC groups (with their sub groups) from groups path configured by the "Groups Path" configuration property.
*/
protected List<GroupModel> getAllKcGroups(RealmModel realm) {
Long start = null;
if (logger.isTraceEnabled()) {
logger.trace("Calling getAllKcGroups started");
start = Time.currentTimeMillis();
}
protected Stream<GroupModel> getAllKcGroups(RealmModel realm) {
GroupModel topParentGroup = getKcGroupsPathGroup(realm);
List<GroupModel> allGroups = realm.getGroups();
List<GroupModel> filtered = new ArrayList<>();
for (GroupModel group : allGroups) {
if (topParentGroup == null) {
filtered.add(group);
} else {
// Check if group is descendant of the topParentGroup (which is group configured by "Groups Path")
boolean finished = false;
GroupModel parent = group.getParent();
while (!finished) {
if (parent == null) {
finished = true;
} else {
if (parent.getId().equals(topParentGroup.getId())) {
filtered.add(group);
finished = true;
} else {
parent = parent.getParent();
}
}
Stream<GroupModel> allGroups = realm.getGroupsStream();
if (topParentGroup == null) return allGroups;
return allGroups.filter(group -> {
// Check if group is descendant of the topParentGroup (which is group configured by "Groups Path")
GroupModel parent = group.getParent();
while (parent != null) {
if (parent.getId().equals(topParentGroup.getId())) {
return true;
}
parent = parent.getParent();
}
}
if (logger.isTraceEnabled()) {
long took = Time.currentTimeMillis() - start;
logger.tracef("Calling getAllKcGroups took %d ms. Count of groups %d", took, filtered.size());
}
return filtered;
return false;
});
}
}

View file

@ -369,7 +369,7 @@ public class RoleLDAPStorageMapper extends AbstractLDAPStorageMapper implements
public boolean hasRole(RoleModel role) {
Set<RoleModel> roles = getRoleMappings();
return RoleUtils.hasRole(roles, role)
|| RoleUtils.hasRoleFromGroup(getGroups(), role, true);
|| RoleUtils.hasRoleFromGroup(getGroupsStream(), role, true);
}
@Override

View file

@ -30,6 +30,7 @@ import java.util.List;
import java.util.Map;
import java.util.Set;
import java.util.function.Supplier;
import java.util.stream.Stream;
/**
* @author <a href="mailto:bill@burkecentral.com">Bill Burke</a>
@ -136,10 +137,10 @@ public class GroupAdapter implements GroupModel {
}
@Override
public List<String> getAttribute(String name) {
public Stream<String> getAttributeStream(String name) {
List<String> values = cached.getAttributes(modelSupplier).get(name);
if (values == null) return null;
return values;
if (values == null) return Stream.empty();
return values.stream();
}
@Override
@ -234,20 +235,20 @@ public class GroupAdapter implements GroupModel {
}
@Override
public Set<GroupModel> getSubGroups() {
if (isUpdated()) return updated.getSubGroups();
public Stream<GroupModel> getSubGroupsStream() {
if (isUpdated()) return updated.getSubGroupsStream();
Set<GroupModel> subGroups = new HashSet<>();
for (String id : cached.getSubGroups(modelSupplier)) {
GroupModel subGroup = keycloakSession.groups().getGroupById(realm, id);
if (subGroup == null) {
// chance that role was removed, so just delegate to persistence and get user invalidated
getDelegateForUpdate();
return updated.getSubGroups();
return updated.getSubGroupsStream();
}
subGroups.add(subGroup);
}
return subGroups;
return subGroups.stream();
}

View file

@ -705,15 +705,9 @@ public class RealmAdapter implements CachedRealmModel {
}
@Override
public List<GroupModel> getDefaultGroups() {
if (isUpdated()) return updated.getDefaultGroups();
List<GroupModel> defaultGroups = new LinkedList<>();
for (String id : cached.getDefaultGroups()) {
defaultGroups.add(cacheSession.getGroupById(this, id));
}
return Collections.unmodifiableList(defaultGroups);
public Stream<GroupModel> getDefaultGroupsStream() {
if (isUpdated()) return updated.getDefaultGroupsStream();
return cached.getDefaultGroups().stream().map(this::getGroupById);
}
@Override
@ -1420,8 +1414,8 @@ public class RealmAdapter implements CachedRealmModel {
}
@Override
public List<GroupModel> getGroups() {
return cacheSession.getGroups(this);
public Stream<GroupModel> getGroupsStream() {
return cacheSession.getGroupsStream(this);
}
@Override
@ -1435,18 +1429,18 @@ public class RealmAdapter implements CachedRealmModel {
}
@Override
public List<GroupModel> getTopLevelGroups() {
return cacheSession.getTopLevelGroups(this);
public Stream<GroupModel> getTopLevelGroupsStream() {
return cacheSession.getTopLevelGroupsStream(this);
}
@Override
public List<GroupModel> getTopLevelGroups(Integer first, Integer max) {
return cacheSession.getTopLevelGroups(this, first, max);
public Stream<GroupModel> getTopLevelGroupsStream(Integer first, Integer max) {
return cacheSession.getTopLevelGroupsStream(this, first, max);
}
@Override
public List<GroupModel> searchForGroupByName(String search, Integer first, Integer max) {
return cacheSession.searchForGroupByName(this, search, first, max);
public Stream<GroupModel> searchForGroupByNameStream(String search, Integer first, Integer max) {
return cacheSession.searchForGroupByNameStream(this, search, first, max);
}
@Override

View file

@ -866,11 +866,11 @@ public class RealmCacheSession implements CacheRealmProvider {
}
@Override
public List<GroupModel> getGroups(RealmModel realm) {
public Stream<GroupModel> getGroupsStream(RealmModel realm) {
String cacheKey = getGroupsQueryCacheKey(realm.getId());
boolean queryDB = invalidations.contains(cacheKey) || listInvalidations.contains(realm.getId());
if (queryDB) {
return getGroupDelegate().getGroups(realm);
return getGroupDelegate().getGroupsStream(realm);
}
GroupListQuery query = cache.get(cacheKey, GroupListQuery.class);
@ -880,28 +880,26 @@ public class RealmCacheSession implements CacheRealmProvider {
if (query == null) {
Long loaded = cache.getCurrentRevision(cacheKey);
List<GroupModel> model = getGroupDelegate().getGroups(realm);
if (model == null) return null;
List<GroupModel> model = getGroupDelegate().getGroupsStream(realm).collect(Collectors.toList());
if (model.isEmpty()) return Stream.empty();
Set<String> ids = new HashSet<>();
for (GroupModel client : model) ids.add(client.getId());
query = new GroupListQuery(loaded, cacheKey, realm, ids);
logger.tracev("adding realm getGroups cache miss: realm {0} key {1}", realm.getName(), cacheKey);
cache.addRevisioned(query, startupRevision);
return model;
return model.stream();
}
List<GroupModel> list = new LinkedList<>();
for (String id : query.getGroups()) {
GroupModel group = session.groups().getGroupById(realm, id);
if (group == null) {
invalidations.add(cacheKey);
return getGroupDelegate().getGroups(realm);
return getGroupDelegate().getGroupsStream(realm);
}
list.add(group);
}
list.sort(Comparator.comparing(GroupModel::getName));
return list;
return list.stream().sorted(Comparator.comparing(GroupModel::getName));
}
@Override
@ -920,16 +918,16 @@ public class RealmCacheSession implements CacheRealmProvider {
}
@Override
public List<GroupModel> getGroupsByRole(RealmModel realm, RoleModel role, int firstResult, int maxResults) {
return getGroupDelegate().getGroupsByRole(realm, role, firstResult, maxResults);
public Stream<GroupModel> getGroupsByRoleStream(RealmModel realm, RoleModel role, int firstResult, int maxResults) {
return getGroupDelegate().getGroupsByRoleStream(realm, role, firstResult, maxResults);
}
@Override
public List<GroupModel> getTopLevelGroups(RealmModel realm) {
public Stream<GroupModel> getTopLevelGroupsStream(RealmModel realm) {
String cacheKey = getTopGroupsQueryCacheKey(realm.getId());
boolean queryDB = invalidations.contains(cacheKey) || listInvalidations.contains(realm.getId());
if (queryDB) {
return getGroupDelegate().getTopLevelGroups(realm);
return getGroupDelegate().getTopLevelGroupsStream(realm);
}
GroupListQuery query = cache.get(cacheKey, GroupListQuery.class);
@ -939,36 +937,34 @@ public class RealmCacheSession implements CacheRealmProvider {
if (query == null) {
Long loaded = cache.getCurrentRevision(cacheKey);
List<GroupModel> model = getGroupDelegate().getTopLevelGroups(realm);
if (model == null) return null;
List<GroupModel> model = getGroupDelegate().getTopLevelGroupsStream(realm).collect(Collectors.toList());
if (model.isEmpty()) return Stream.empty();
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;
return model.stream();
}
List<GroupModel> list = new LinkedList<>();
for (String id : query.getGroups()) {
GroupModel group = session.groups().getGroupById(realm, id);
if (group == null) {
invalidations.add(cacheKey);
return getGroupDelegate().getTopLevelGroups(realm);
return getGroupDelegate().getTopLevelGroupsStream(realm);
}
list.add(group);
}
list.sort(Comparator.comparing(GroupModel::getName));
return list;
return list.stream().sorted(Comparator.comparing(GroupModel::getName));
}
@Override
public List<GroupModel> getTopLevelGroups(RealmModel realm, Integer first, Integer max) {
public Stream<GroupModel> getTopLevelGroupsStream(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 getGroupDelegate().getTopLevelGroups(realm, first, max);
return getGroupDelegate().getTopLevelGroupsStream(realm, first, max);
}
GroupListQuery query = cache.get(cacheKey, GroupListQuery.class);
@ -978,33 +974,31 @@ public class RealmCacheSession implements CacheRealmProvider {
if (Objects.isNull(query)) {
Long loaded = cache.getCurrentRevision(cacheKey);
List<GroupModel> model = getGroupDelegate().getTopLevelGroups(realm, first, max);
if (model == null) return null;
List<GroupModel> model = getGroupDelegate().getTopLevelGroupsStream(realm, first, max).collect(Collectors.toList());
if (model.isEmpty()) return Stream.empty();
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;
return model.stream();
}
List<GroupModel> list = new LinkedList<>();
for (String id : query.getGroups()) {
GroupModel group = session.groups().getGroupById(realm, id);
if (Objects.isNull(group)) {
invalidations.add(cacheKey);
return getGroupDelegate().getTopLevelGroups(realm);
return getGroupDelegate().getTopLevelGroupsStream(realm);
}
list.add(group);
}
list.sort(Comparator.comparing(GroupModel::getName));
return list;
return list.stream().sorted(Comparator.comparing(GroupModel::getName));
}
@Override
public List<GroupModel> searchForGroupByName(RealmModel realm, String search, Integer first, Integer max) {
return getGroupDelegate().searchForGroupByName(realm, search, first, max);
public Stream<GroupModel> searchForGroupByNameStream(RealmModel realm, String search, Integer first, Integer max) {
return getGroupDelegate().searchForGroupByNameStream(realm, search, first, max);
}
@Override

View file

@ -37,6 +37,7 @@ import java.util.Map;
import java.util.Set;
import java.util.concurrent.ConcurrentHashMap;
import java.util.function.Supplier;
import java.util.stream.Stream;
/**
* @author <a href="mailto:bill@burkecentral.com">Bill Burke</a>
@ -315,7 +316,7 @@ public class UserAdapter implements CachedUserModel {
for (RoleModel mapping: mappings) {
if (mapping.hasRole(role)) return true;
}
return RoleUtils.hasRoleFromGroup(getGroups(), role, true);
return RoleUtils.hasRoleFromGroup(getGroupsStream(), role, true);
}
@Override
@ -348,20 +349,20 @@ public class UserAdapter implements CachedUserModel {
}
@Override
public Set<GroupModel> getGroups() {
if (updated != null) return updated.getGroups();
public Stream<GroupModel> getGroupsStream() {
if (updated != null) return updated.getGroupsStream();
Set<GroupModel> groups = new LinkedHashSet<>();
for (String id : cached.getGroups(modelSupplier)) {
GroupModel groupModel = keycloakSession.groups().getGroupById(realm, id);
if (groupModel == null) {
// chance that role was removed, so just delete to persistence and get user invalidated
getDelegateForUpdate();
return updated.getGroups();
return updated.getGroupsStream();
}
groups.add(groupModel);
}
return groups;
return groups.stream();
}
@Override
@ -381,8 +382,7 @@ public class UserAdapter implements CachedUserModel {
public boolean isMemberOf(GroupModel group) {
if (updated != null) return updated.isMemberOf(group);
if (cached.getGroups(modelSupplier).contains(group.getId())) return true;
Set<GroupModel> roles = getGroups();
return RoleUtils.isMember(roles, group);
return RoleUtils.isMember(getGroupsStream(), group);
}
@Override

View file

@ -49,7 +49,7 @@ public class CachedGroup extends AbstractRevisioned implements InRealm {
this.parentId = group.getParentId();
this.attributes = new DefaultLazyLoader<>(source -> new MultivaluedHashMap<>(source.getAttributes()), MultivaluedHashMap::new);
this.roleMappings = new DefaultLazyLoader<>(source -> source.getRoleMappings().stream().map(RoleModel::getId).collect(Collectors.toSet()), Collections::emptySet);
this.subGroups = new DefaultLazyLoader<>(source -> source.getSubGroups().stream().map(GroupModel::getId).collect(Collectors.toSet()), Collections::emptySet);
this.subGroups = new DefaultLazyLoader<>(source -> source.getSubGroupsStream().map(GroupModel::getId).collect(Collectors.toSet()), Collections::emptySet);
}
public String getRealm() {

View file

@ -43,6 +43,7 @@ import java.util.LinkedList;
import java.util.List;
import java.util.Map;
import java.util.Set;
import java.util.stream.Collectors;
/**
* @author <a href="mailto:bill@burkecentral.com">Bill Burke</a>
@ -148,7 +149,7 @@ public class CachedRealm extends AbstractExtendableRevisioned {
return identityProviderMapperSet;
}
protected List<String> defaultGroups = new LinkedList<>();
protected List<String> defaultGroups;
protected List<String> clientScopes = new LinkedList<>();
protected List<String> defaultDefaultClientScopes = new LinkedList<>();
protected List<String> optionalDefaultClientScopes = new LinkedList<>();
@ -282,9 +283,7 @@ public class CachedRealm extends AbstractExtendableRevisioned {
requiredActionProvidersByAlias.put(action.getAlias(), action);
}
for (GroupModel group : model.getDefaultGroups()) {
defaultGroups.add(group.getId());
}
defaultGroups = model.getDefaultGroupsStream().map(GroupModel::getId).collect(Collectors.toList());
browserFlow = model.getBrowserFlow();
registrationFlow = model.getRegistrationFlow();

View file

@ -65,7 +65,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.toCollection(LinkedHashSet::new)), LinkedHashSet::new);
this.groups = new DefaultLazyLoader<>(userModel -> userModel.getGroupsStream().map(GroupModel::getId).collect(Collectors.toCollection(LinkedHashSet::new)), LinkedHashSet::new);
}
public String getRealm() {

View file

@ -36,9 +36,13 @@ import java.util.HashSet;
import java.util.Iterator;
import java.util.List;
import java.util.Map;
import java.util.Objects;
import java.util.Set;
import java.util.stream.Stream;
import javax.persistence.LockModeType;
import static org.keycloak.utils.StreamsUtil.closing;
/**
* @author <a href="mailto:bill@burkecentral.com">Bill Burke</a>
* @version $Revision: 1 $
@ -119,17 +123,10 @@ public class GroupAdapter implements GroupModel , JpaModel<GroupEntity> {
}
@Override
public Set<GroupModel> getSubGroups() {
public Stream<GroupModel> getSubGroupsStream() {
TypedQuery<String> query = em.createNamedQuery("getGroupIdsByParent", String.class);
query.setParameter("parent", group.getId());
List<String> ids = query.getResultList();
Set<GroupModel> set = new HashSet<>();
for (String id : ids) {
GroupModel subGroup = realm.getGroupById(id);
if (subGroup == null) continue;
set.add(subGroup);
}
return set;
return closing(query.getResultStream().map(realm::getGroupById).filter(Objects::nonNull));
}
@Override
@ -203,14 +200,10 @@ public class GroupAdapter implements GroupModel , JpaModel<GroupEntity> {
}
@Override
public List<String> getAttribute(String name) {
List<String> result = new ArrayList<>();
for (GroupAttributeEntity attr : group.getAttributes()) {
if (attr.getName().equals(name)) {
result.add(attr.getValue());
}
}
return result;
public Stream<String> getAttributeStream(String name) {
return group.getAttributes().stream()
.filter(attr -> Objects.equals(attr.getName(), name))
.map(GroupAttributeEntity::getValue);
}
@Override

View file

@ -171,9 +171,7 @@ public class JpaRealmProvider implements RealmProvider, ClientProvider, GroupPro
removeRoles(adapter);
for (GroupModel group : adapter.getGroups()) {
session.groups().removeGroup(adapter, group);
}
adapter.getGroupsStream().forEach(adapter::removeGroup);
num = em.createNamedQuery("removeClientInitialAccessByRealm")
.setParameter("realm", realm).executeUpdate();
@ -420,14 +418,12 @@ public class JpaRealmProvider implements RealmProvider, ClientProvider, GroupPro
}
@Override
public List<GroupModel> getGroups(RealmModel realm) {
public Stream<GroupModel> getGroupsStream(RealmModel realm) {
RealmEntity ref = em.getReference(RealmEntity.class, realm.getId());
return ref.getGroups().stream()
.map(g -> session.groups().getGroupById(realm, g.getId()))
.sorted(Comparator.comparing(GroupModel::getName))
.collect(Collectors.collectingAndThen(
Collectors.toList(), Collections::unmodifiableList));
.sorted(Comparator.comparing(GroupModel::getName));
}
@Override
@ -454,11 +450,11 @@ public class JpaRealmProvider implements RealmProvider, ClientProvider, GroupPro
@Override
public Long getGroupsCountByNameContaining(RealmModel realm, String search) {
return (long) searchForGroupByName(realm, search, null, null).size();
return searchForGroupByNameStream(realm, search, null, null).count();
}
@Override
public List<GroupModel> getGroupsByRole(RealmModel realm, RoleModel role, int firstResult, int maxResults) {
public Stream<GroupModel> getGroupsByRoleStream(RealmModel realm, RoleModel role, int firstResult, int maxResults) {
TypedQuery<GroupEntity> query = em.createNamedQuery("groupsInRole", GroupEntity.class);
query.setParameter("roleId", role.getId());
if (firstResult != -1) {
@ -467,44 +463,33 @@ public class JpaRealmProvider implements RealmProvider, ClientProvider, GroupPro
if (maxResults != -1) {
query.setMaxResults(maxResults);
}
List<GroupEntity> results = query.getResultList();
Stream<GroupEntity> results = query.getResultStream();
return results.stream()
.map(g -> new GroupAdapter(realm, em, g))
.sorted(Comparator.comparing(GroupModel::getName))
.collect(Collectors.collectingAndThen(
Collectors.toList(), Collections::unmodifiableList));
return closing(results
.map(g -> (GroupModel) new GroupAdapter(realm, em, g))
.sorted(Comparator.comparing(GroupModel::getName)));
}
@Override
public List<GroupModel> getTopLevelGroups(RealmModel realm) {
public Stream<GroupModel> getTopLevelGroupsStream(RealmModel realm) {
RealmEntity ref = em.getReference(RealmEntity.class, realm.getId());
return ref.getGroups().stream()
.filter(g -> GroupEntity.TOP_PARENT_ID.equals(g.getParentId()))
.map(g -> session.groups().getGroupById(realm, g.getId()))
.sorted(Comparator.comparing(GroupModel::getName))
.collect(Collectors.collectingAndThen(
Collectors.toList(), Collections::unmodifiableList));
.sorted(Comparator.comparing(GroupModel::getName));
}
@Override
public List<GroupModel> getTopLevelGroups(RealmModel realm, Integer first, Integer max) {
List<String> groupIds = em.createNamedQuery("getTopLevelGroupIds", String.class)
public Stream<GroupModel> getTopLevelGroupsStream(RealmModel realm, Integer first, Integer max) {
Stream<String> groupIds = em.createNamedQuery("getTopLevelGroupIds", String.class)
.setParameter("realm", realm.getId())
.setParameter("parent", GroupEntity.TOP_PARENT_ID)
.setFirstResult(first)
.setMaxResults(max)
.getResultList();
List<GroupModel> list = new ArrayList<>();
if(Objects.nonNull(groupIds) && !groupIds.isEmpty()) {
for (String id : groupIds) {
GroupModel group = getGroupById(realm, id);
list.add(group);
}
}
// no need to sort, it's sorted at database level
return Collections.unmodifiableList(list);
.setMaxResults(max)
.getResultStream();
return closing(groupIds.map(realm::getGroupById));
}
@Override
@ -534,9 +519,8 @@ public class JpaRealmProvider implements RealmProvider, ClientProvider, GroupPro
session.users().preRemove(realm, group);
realm.removeDefaultGroup(group);
for (GroupModel subGroup : group.getSubGroups()) {
session.groups().removeGroup(realm, subGroup);
}
group.getSubGroupsStream().forEach(realm::removeGroup);
GroupEntity groupEntity = em.find(GroupEntity.class, group.getId(), LockModeType.PESSIMISTIC_WRITE);
if ((groupEntity == null) || (!groupEntity.getRealm().equals(realm.getId()))) {
return false;
@ -745,28 +729,22 @@ public class JpaRealmProvider implements RealmProvider, ClientProvider, GroupPro
}
@Override
public List<GroupModel> searchForGroupByName(RealmModel realm, String search, Integer first, Integer max) {
public Stream<GroupModel> searchForGroupByNameStream(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 ArrayList<>();
for (String id : groups) {
Stream<String> groups = query.getResultStream();
return closing(groups.map(id -> {
GroupModel groupById = session.groups().getGroupById(realm, id);
while(Objects.nonNull(groupById.getParentId())) {
while (Objects.nonNull(groupById.getParentId())) {
groupById = session.groups().getGroupById(realm, groupById.getParentId());
}
if(!list.contains(groupById)) {
list.add(groupById);
}
}
list.sort(Comparator.comparing(GroupModel::getName));
return Collections.unmodifiableList(list);
return groupById;
}).sorted(Comparator.comparing(GroupModel::getName)).distinct());
}
@Override

View file

@ -50,7 +50,6 @@ import org.keycloak.storage.UserStorageProvider;
import org.keycloak.storage.client.ClientStorageProvider;
import javax.persistence.EntityManager;
import javax.persistence.Query;
import javax.persistence.TypedQuery;
import javax.persistence.criteria.CriteriaBuilder;
import javax.persistence.criteria.CriteriaQuery;
@ -110,9 +109,8 @@ public class JpaUserProvider implements UserProvider, UserCredentialStore {
if (addDefaultRoles) {
DefaultRoles.addDefaultRoles(realm, userModel);
for (GroupModel g : realm.getDefaultGroups()) {
userModel.joinGroupImpl(g); // No need to check if user has group as it's new user
}
// No need to check if user has group as it's new user
realm.getDefaultGroupsStream().forEach(userModel::joinGroupImpl);
}
if (addDefaultRequiredActions){

View file

@ -792,14 +792,10 @@ public class RealmAdapter implements RealmModel, JpaModel<RealmEntity> {
}
@Override
public List<GroupModel> getDefaultGroups() {
public Stream<GroupModel> getDefaultGroupsStream() {
Collection<GroupEntity> entities = realm.getDefaultGroups();
if (entities == null || entities.isEmpty()) return Collections.EMPTY_LIST;
List<GroupModel> defaultGroups = new LinkedList<>();
for (GroupEntity entity : entities) {
defaultGroups.add(session.groups().getGroupById(this, entity.getId()));
}
return Collections.unmodifiableList(defaultGroups);
if (entities == null || entities.isEmpty()) return Stream.empty();
return entities.stream().map(GroupEntity::getId).map(this::getGroupById);
}
@Override
@ -2036,8 +2032,8 @@ public class RealmAdapter implements RealmModel, JpaModel<RealmEntity> {
}
@Override
public List<GroupModel> getGroups() {
return session.groups().getGroups(this);
public Stream<GroupModel> getGroupsStream() {
return session.groups().getGroupsStream(this);
}
@Override
@ -2051,18 +2047,18 @@ public class RealmAdapter implements RealmModel, JpaModel<RealmEntity> {
}
@Override
public List<GroupModel> getTopLevelGroups() {
return session.groups().getTopLevelGroups(this);
public Stream<GroupModel> getTopLevelGroupsStream() {
return session.groups().getTopLevelGroupsStream(this);
}
@Override
public List<GroupModel> getTopLevelGroups(Integer first, Integer max) {
return session.groups().getTopLevelGroups(this, first, max);
public Stream<GroupModel> getTopLevelGroupsStream(Integer first, Integer max) {
return session.groups().getTopLevelGroupsStream(this, first, max);
}
@Override
public List<GroupModel> searchForGroupByName(String search, Integer first, Integer max) {
return session.groups().searchForGroupByName(this, search, first, max);
public Stream<GroupModel> searchForGroupByNameStream(String search, Integer first, Integer max) {
return session.groups().searchForGroupByNameStream(this, search, first, max);
}
@Override

View file

@ -43,11 +43,9 @@ 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.Collections;
import java.util.HashSet;
import java.util.Iterator;
import java.util.LinkedHashSet;
import java.util.List;
import java.util.Map;
import java.util.Set;
@ -55,6 +53,8 @@ import java.util.Objects;
import java.util.stream.Stream;
import javax.persistence.LockModeType;
import static org.keycloak.utils.StreamsUtil.closing;
/**
* @author <a href="mailto:bill@burkecentral.com">Bill Burke</a>
* @version $Revision: 1 $
@ -401,22 +401,18 @@ public class UserAdapter implements UserModel, JpaModel<UserEntity> {
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;
private Stream<GroupModel> getGroupModels(Stream<String> groupIds) {
return groupIds.map(realm::getGroupById);
}
@Override
public Set<GroupModel> getGroups() {
return getGroupModels(createGetGroupsQuery(null, null, null).getResultList());
public Stream<GroupModel> getGroupsStream() {
return closing(getGroupModels(createGetGroupsQuery(null, null, null).getResultStream()));
}
@Override
public Set<GroupModel> getGroups(String search, int first, int max) {
return getGroupModels(createGetGroupsQuery(search, first, max).getResultList());
public Stream<GroupModel> getGroupsStream(String search, int first, int max) {
return closing(getGroupModels(createGetGroupsQuery(search, first, max).getResultStream()));
}
@Override
@ -463,8 +459,7 @@ public class UserAdapter implements UserModel, JpaModel<UserEntity> {
@Override
public boolean isMemberOf(GroupModel group) {
Set<GroupModel> roles = getGroups();
return RoleUtils.isMember(roles, group);
return RoleUtils.isMember(getGroupsStream(), group);
}
protected TypedQuery<UserGroupMembershipEntity> getUserGroupMappingQuery(GroupModel group) {
@ -479,7 +474,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(getGroupsStream(), role, true);
}
protected TypedQuery<UserRoleMappingEntity> getUserRoleMappingEntityTypedQuery(RoleModel role) {

View file

@ -37,7 +37,6 @@ import org.keycloak.models.RoleModel;
import org.keycloak.models.UserConsentModel;
import org.keycloak.models.UserModel;
import org.keycloak.models.jpa.JpaUserCredentialStore;
import org.keycloak.models.jpa.entities.CredentialEntity;
import org.keycloak.models.utils.KeycloakModelUtils;
import org.keycloak.storage.StorageId;
import org.keycloak.storage.UserStorageProvider;
@ -61,10 +60,12 @@ import java.util.Collection;
import java.util.HashSet;
import java.util.LinkedList;
import java.util.List;
import java.util.ListIterator;
import java.util.Set;
import java.util.stream.Stream;
import javax.persistence.LockModeType;
import static org.keycloak.utils.StreamsUtil.closing;
/**
* @author <a href="mailto:bill@burkecentral.com">Bill Burke</a>
* @version $Revision: 1 $
@ -439,17 +440,10 @@ public class JpaUserFederatedStorageProvider implements
}
@Override
public Set<GroupModel> getGroups(RealmModel realm, String userId) {
Set<GroupModel> set = new HashSet<>();
public Stream<GroupModel> getGroupsStream(RealmModel realm, String userId) {
TypedQuery<FederatedUserGroupMembershipEntity> query = em.createNamedQuery("feduserGroupMembership", FederatedUserGroupMembershipEntity.class);
query.setParameter("userId", userId);
List<FederatedUserGroupMembershipEntity> results = query.getResultList();
if (results.size() == 0) return set;
for (FederatedUserGroupMembershipEntity entity : results) {
GroupModel group = realm.getGroupById(entity.getGroupId());
set.add(group);
}
return set;
return closing(query.getResultStream().map(FederatedUserGroupMembershipEntity::getGroupId).map(realm::getGroupById));
}
@Override

View file

@ -160,7 +160,7 @@ public class DefaultEvaluation implements Evaluation {
}
if (checkParent) {
return RoleUtils.isMember(user.getGroups(), group);
return RoleUtils.isMember(user.getGroupsStream(), group);
}
return user.isMemberOf(group);
@ -253,7 +253,7 @@ public class DefaultEvaluation implements Evaluation {
@Override
public List<String> getUserGroups(String id) {
return getUser(id, authorizationProvider.getKeycloakSession()).getGroups().stream()
return getUser(id, authorizationProvider.getKeycloakSession()).getGroupsStream()
.map(ModelToRepresentation::buildGroupPath)
.collect(Collectors.toList());
}

View file

@ -57,8 +57,12 @@ import java.security.cert.X509Certificate;
import java.util.Collection;
import java.util.HashSet;
import java.util.List;
import java.util.Objects;
import java.util.Optional;
import java.util.Set;
import java.util.UUID;
import java.util.stream.Collectors;
import java.util.stream.Stream;
/**
* Set of helper methods, which are useful in various model implementations.
@ -382,30 +386,11 @@ public final class KeycloakModelUtils {
}
/**
*
*
* @param user
* @param name
* @return
*/
public static String resolveFirstAttribute(UserModel user, String name) {
String value = user.getFirstAttribute(name);
if (value != null) return value;
for (GroupModel group : user.getGroups()) {
value = resolveFirstAttribute(group, name);
if (value != null) return value;
}
return null;
}
public static List<String> resolveAttribute(GroupModel group, String name) {
List<String> values = group.getAttribute(name);
if (values != null && !values.isEmpty()) return values;
List<String> values = group.getAttributeStream(name).collect(Collectors.toList());
if (!values.isEmpty()) return values;
if (group.getParentId() == null) return null;
return resolveAttribute(group.getParent(), name);
}
@ -418,21 +403,24 @@ public final class KeycloakModelUtils {
}
aggrValues.addAll(values);
}
for (GroupModel group : user.getGroups()) {
values = resolveAttribute(group, name);
if (values != null && !values.isEmpty()) {
if (!aggregateAttrs) {
return values;
}
aggrValues.addAll(values);
}
Stream<List<String>> attributes = user.getGroupsStream()
.map(group -> resolveAttribute(group, name))
.filter(Objects::nonNull)
.filter(attr -> !attr.isEmpty());
if (!aggregateAttrs) {
Optional<List<String>> first = attributes.findFirst();
if (first.isPresent()) return first.get();
} else {
aggrValues.addAll(attributes.flatMap(Collection::stream).collect(Collectors.toSet()));
}
return aggrValues;
}
private static GroupModel findSubGroup(String[] segments, int index, GroupModel parent) {
for (GroupModel group : parent.getSubGroups()) {
return parent.getSubGroupsStream().map(group -> {
String groupName = group.getName();
String[] pathSegments = formatPathSegments(segments, index, groupName);
@ -444,14 +432,11 @@ public final class KeycloakModelUtils {
if (index + 1 < pathSegments.length) {
GroupModel found = findSubGroup(pathSegments, index + 1, group);
if (found != null) return found;
} else {
return null;
}
}
}
}
return null;
return null;
}).filter(Objects::nonNull).findFirst().orElse(null);
}
/**
@ -504,26 +489,25 @@ public final class KeycloakModelUtils {
}
String[] split = path.split("/");
if (split.length == 0) return null;
GroupModel found = null;
for (GroupModel group : realm.getTopLevelGroups()) {
return realm.getTopLevelGroupsStream().map(group -> {
String groupName = group.getName();
String[] pathSegments = formatPathSegments(split, 0, groupName);
if (groupName.equals(pathSegments[0])) {
if (pathSegments.length == 1) {
found = group;
break;
return group;
}
else {
if (pathSegments.length > 1) {
found = findSubGroup(pathSegments, 1, group);
if (found != null) break;
GroupModel subGroup = findSubGroup(pathSegments, 1, group);
if (subGroup != null) return subGroup;
}
}
}
}
return found;
return null;
}).filter(Objects::nonNull).findFirst().orElse(null);
}
public static Set<RoleModel> getClientScopeMappings(ClientModel client, ScopeContainerModel container) {

View file

@ -46,9 +46,9 @@ import java.util.HashSet;
import java.util.LinkedList;
import java.util.List;
import java.util.Map;
import java.util.Objects;
import java.util.Set;
import java.util.stream.Collectors;
import java.util.stream.Stream;
/**
* @author <a href="mailto:bill@burkecentral.com">Bill Burke</a>
@ -139,63 +139,40 @@ public class ModelToRepresentation {
return rep;
}
public static List<GroupRepresentation> searchForGroupByName(RealmModel realm, boolean full, 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, full);
result.add(rep);
}
return result;
public static Stream<GroupRepresentation> searchForGroupByName(RealmModel realm, boolean full, String search, Integer first, Integer max) {
return realm.searchForGroupByNameStream(search, first, max)
.map(g -> toGroupHierarchy(g, full));
}
public static List<GroupRepresentation> searchForGroupByName(UserModel user, boolean full, String search, Integer first, Integer max) {
return user.getGroups(search, first, max).stream()
.map(group -> toRepresentation(group, full))
.collect(Collectors.toList());
public static Stream<GroupRepresentation> searchForGroupByName(UserModel user, boolean full, String search, Integer first, Integer max) {
return user.getGroupsStream(search, first, max)
.map(group -> toRepresentation(group, full));
}
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 Stream<GroupRepresentation> toGroupHierarchy(RealmModel realm, boolean full, Integer first, Integer max) {
return realm.getTopLevelGroupsStream(first, max)
.map(g -> toGroupHierarchy(g, full));
}
public static List<GroupRepresentation> toGroupHierarchy(UserModel user, boolean full, Integer first, Integer max) {
return user.getGroups(first, max).stream()
.map(group -> toRepresentation(group, full))
.collect(Collectors.toList());
public static Stream<GroupRepresentation> toGroupHierarchy(UserModel user, boolean full, Integer first, Integer max) {
return user.getGroupsStream(null, first, max)
.map(group -> toRepresentation(group, full));
}
public static List<GroupRepresentation> toGroupHierarchy(RealmModel realm, boolean full) {
List<GroupRepresentation> hierarchy = new LinkedList<>();
List<GroupModel> groups = realm.getTopLevelGroups();
if (Objects.isNull(groups)) return hierarchy;
for (GroupModel group : groups) {
GroupRepresentation rep = toGroupHierarchy(group, full);
hierarchy.add(rep);
}
return hierarchy;
public static Stream<GroupRepresentation> toGroupHierarchy(RealmModel realm, boolean full) {
return realm.getTopLevelGroupsStream()
.map(g -> toGroupHierarchy(g, full));
}
public static List<GroupRepresentation> toGroupHierarchy(UserModel user, boolean full) {
return user.getGroups().stream()
.map(group -> toRepresentation(group, full))
.collect(Collectors.toList());
public static Stream<GroupRepresentation> toGroupHierarchy(UserModel user, boolean full) {
return user.getGroupsStream()
.map(group -> toRepresentation(group, full));
}
public static GroupRepresentation toGroupHierarchy(GroupModel group, boolean full) {
GroupRepresentation rep = toRepresentation(group, full);
List<GroupRepresentation> subGroups = new LinkedList<>();
for (GroupModel subGroup : group.getSubGroups()) {
subGroups.add(toGroupHierarchy(subGroup, full));
}
List<GroupRepresentation> subGroups = group.getSubGroupsStream()
.map(subGroup -> toGroupHierarchy(subGroup, full)).collect(Collectors.toList());
rep.setSubGroups(subGroups);
return rep;
}
@ -437,13 +414,10 @@ public class ModelToRepresentation {
List<String> roleStrings = new ArrayList<>(defaultRoles);
rep.setDefaultRoles(roleStrings);
}
List<GroupModel> defaultGroups = realm.getDefaultGroups();
List<String> defaultGroups = realm.getDefaultGroupsStream()
.map(ModelToRepresentation::buildGroupPath).collect(Collectors.toList());
if (!defaultGroups.isEmpty()) {
List<String> groupPaths = new LinkedList<>();
for (GroupModel group : defaultGroups) {
groupPaths.add(ModelToRepresentation.buildGroupPath(group));
}
rep.setDefaultGroups(groupPaths);
rep.setDefaultGroups(defaultGroups);
}
List<RequiredCredentialModel> requiredCredentialModels = realm.getRequiredCredentials();
@ -502,8 +476,7 @@ public class ModelToRepresentation {
}
public static void exportGroups(RealmModel realm, RealmRepresentation rep) {
List<GroupRepresentation> groups = toGroupHierarchy(realm, true);
rep.setGroups(groups);
rep.setGroups(toGroupHierarchy(realm, true).collect(Collectors.toList()));
}
public static void exportAuthenticationFlows(RealmModel realm, RealmRepresentation rep) {

View file

@ -30,12 +30,12 @@ import org.keycloak.models.utils.KeycloakModelUtils;
import org.keycloak.models.utils.RoleUtils;
import org.keycloak.storage.ReadOnlyException;
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.stream.Stream;
/**
* @author <a href="mailto:bill@burkecentral.com">Bill Burke</a>
@ -79,10 +79,7 @@ public class InMemoryUserAdapter extends UserModelDefaultMethods {
public void addDefaults() {
DefaultRoles.addDefaultRoles(realm, this);
for (GroupModel g : realm.getDefaultGroups()) {
joinGroup(g);
}
realm.getDefaultGroupsStream().forEach(this::joinGroup);
}
public void setReadonly(boolean flag) {
@ -213,13 +210,8 @@ public class InMemoryUserAdapter extends UserModelDefaultMethods {
}
@Override
public Set<GroupModel> getGroups() {
if (groupIds.isEmpty()) return new HashSet<>();
Set<GroupModel> groups = new HashSet<>();
for (String id : groupIds) {
groups.add(realm.getGroupById(id));
}
return groups;
public Stream<GroupModel> getGroupsStream() {
return groupIds.stream().map(realm::getGroupById);
}
@Override
@ -240,8 +232,7 @@ public class InMemoryUserAdapter extends UserModelDefaultMethods {
public boolean isMemberOf(GroupModel group) {
if (groupIds == null) return false;
if (groupIds.contains(group.getId())) return true;
Set<GroupModel> groups = getGroups();
return RoleUtils.isMember(groups, group);
return RoleUtils.isMember(getGroupsStream(), group);
}
@Override
@ -299,7 +290,7 @@ public class InMemoryUserAdapter extends UserModelDefaultMethods {
public boolean hasRole(RoleModel role) {
Set<RoleModel> roles = getRoleMappings();
return RoleUtils.hasRole(roles, role)
|| RoleUtils.hasRoleFromGroup(getGroups(), role, true);
|| RoleUtils.hasRoleFromGroup(getGroupsStream(), role, true);
}
@Override

View file

@ -22,6 +22,8 @@ import org.keycloak.provider.ProviderEvent;
import java.util.List;
import java.util.Map;
import java.util.Set;
import java.util.stream.Collectors;
import java.util.stream.Stream;
/**
* @author <a href="mailto:bill@burkecentral.com">Bill Burke</a>
@ -61,13 +63,24 @@ public interface GroupModel extends RoleMapperModel {
* @param name
* @return list of all attribute values or empty list if there are not any values. Never return null
*/
List<String> getAttribute(String name);
@Deprecated
default List<String> getAttribute(String name) {
return getAttributeStream(name).collect(Collectors.toList());
}
Stream<String> getAttributeStream(String name);
Map<String, List<String>> getAttributes();
GroupModel getParent();
String getParentId();
Set<GroupModel> getSubGroups();
@Deprecated
default Set<GroupModel> getSubGroups() {
return getSubGroupsStream().collect(Collectors.toSet());
}
Stream<GroupModel> getSubGroupsStream();
/**
* You must also call addChild on the parent group, addChild on RealmModel if there is no parent group

View file

@ -20,6 +20,8 @@ package org.keycloak.models;
import org.keycloak.provider.Provider;
import java.util.List;
import java.util.stream.Collectors;
import java.util.stream.Stream;
/**
*
@ -55,8 +57,20 @@ public interface GroupProvider extends Provider {
*
* @param realm Realm.
* @return List of groups in the Realm.
* @deprecated Use {@link #getGroupsStream(RealmModel)} getGroupsStream} instead.
*/
List<GroupModel> getGroups(RealmModel realm);
@Deprecated
default List<GroupModel> getGroups(RealmModel realm) {
return getGroupsStream(realm).collect(Collectors.toList());
}
/**
* Returns groups for the given realm.
*
* @param realm Realm.
* @return Stream of groups in the Realm.
*/
Stream<GroupModel> getGroupsStream(RealmModel realm);
/**
* Returns a number of groups/top level groups (i.e. groups without parent group) for the given realm.
@ -84,16 +98,43 @@ public interface GroupProvider extends Provider {
* @param firstResult First result to return. Ignored if negative.
* @param maxResults Maximum number of results to return. Ignored if negative.
* @return List of groups with the given role.
* @deprecated Use {@link #getGroupsByRoleStream(RealmModel, RoleModel, int, int)} getGroupsByRoleStream} instead.
*/
List<GroupModel> getGroupsByRole(RealmModel realm, RoleModel role, int firstResult, int maxResults);
@Deprecated
default List<GroupModel> getGroupsByRole(RealmModel realm, RoleModel role, int firstResult, int maxResults) {
return getGroupsByRoleStream(realm, role, firstResult, maxResults).collect(Collectors.toList());
}
/**
* Returns groups with the given role in the given realm.
*
* @param realm Realm.
* @param role Role.
* @param firstResult First result to return. Ignored if negative.
* @param maxResults Maximum number of results to return. Ignored if negative.
* @return Stream of groups with the given role.
*/
Stream<GroupModel> getGroupsByRoleStream(RealmModel realm, RoleModel role, int firstResult, int maxResults);
/**
* Returns all top level groups (i.e. groups without parent group) for the given realm.
*
* @param realm Realm.
* @return List of all top level groups in the realm.
* @deprecated Use {@link #getTopLevelGroupsStream(RealmModel)} getTopLevelGroupsStream} instead.
*/
List<GroupModel> getTopLevelGroups(RealmModel realm);
@Deprecated
default List<GroupModel> getTopLevelGroups(RealmModel realm) {
return getTopLevelGroupsStream(realm).collect(Collectors.toList());
}
/**
* Returns all top level groups (i.e. groups without parent group) for the given realm.
*
* @param realm Realm.
* @return Stream of all top level groups in the realm.
*/
Stream<GroupModel> getTopLevelGroupsStream(RealmModel realm);
/**
* Returns top level groups (i.e. groups without parent group) for the given realm.
@ -102,8 +143,22 @@ public interface GroupProvider extends Provider {
* @param firstResult First result to return.
* @param maxResults Maximum number of results to return.
* @return List of top level groups in the realm.
* @deprecated Use {@link #getTopLevelGroupsStream(RealmModel, Integer, Integer)} getTopLevelGroupsStream} instead.
*/
List<GroupModel> getTopLevelGroups(RealmModel realm, Integer firstResult, Integer maxResults);
@Deprecated
default List<GroupModel> getTopLevelGroups(RealmModel realm, Integer firstResult, Integer maxResults) {
return getTopLevelGroupsStream(realm, firstResult, maxResults).collect(Collectors.toList());
}
/**
* Returns top level groups (i.e. groups without parent group) for the given realm.
*
* @param realm Realm.
* @param firstResult First result to return.
* @param maxResults Maximum number of results to return.
* @return Stream of top level groups in the realm.
*/
Stream<GroupModel> getTopLevelGroupsStream(RealmModel realm, Integer firstResult, Integer maxResults);
/**
* Returns groups with the given string in name for the given realm.
@ -113,8 +168,23 @@ public interface GroupProvider extends Provider {
* @param firstResult First result to return. Ignored if {@code null}.
* @param maxResults Maximum number of results to return. Ignored if {@code null}.
* @return List of groups with the given string in name.
* @deprecated Use {@link #searchForGroupByNameStream(RealmModel, String, Integer, Integer)} searchForGroupByNameStream} instead.
*/
List<GroupModel> searchForGroupByName(RealmModel realm, String search, Integer firstResult, Integer maxResults);
@Deprecated
default List<GroupModel> searchForGroupByName(RealmModel realm, String search, Integer firstResult, Integer maxResults) {
return searchForGroupByNameStream(realm, search, firstResult, maxResults).collect(Collectors.toList());
}
/**
* Returns groups with the given string in name for the given realm.
*
* @param realm Realm.
* @param search Searched string.
* @param firstResult First result to return. Ignored if {@code null}.
* @param maxResults Maximum number of results to return. Ignored if {@code null}.
* @return Stream of groups with the given string in name.
*/
Stream<GroupModel> searchForGroupByNameStream(RealmModel realm, String search, Integer firstResult, Integer maxResults);
/**
* Creates a new group with the given name in the given realm.

View file

@ -282,7 +282,12 @@ public interface RealmModel extends RoleContainerModel {
RoleModel getRoleById(String id);
List<GroupModel> getDefaultGroups();
@Deprecated
default List<GroupModel> getDefaultGroups() {
return getDefaultGroupsStream().collect(Collectors.toList());
}
Stream<GroupModel> getDefaultGroupsStream();
void addDefaultGroup(GroupModel group);
@ -535,12 +540,38 @@ public interface RealmModel extends RoleContainerModel {
GroupModel createGroup(String id, String name, GroupModel toParent);
GroupModel getGroupById(String id);
List<GroupModel> getGroups();
@Deprecated
default List<GroupModel> getGroups() {
return getGroupsStream().collect(Collectors.toList());
}
Stream<GroupModel> getGroupsStream();
Long getGroupsCount(Boolean onlyTopGroups);
Long getGroupsCountByNameContaining(String search);
List<GroupModel> getTopLevelGroups();
List<GroupModel> getTopLevelGroups(Integer first, Integer max);
List<GroupModel> searchForGroupByName(String search, Integer first, Integer max);
@Deprecated
default List<GroupModel> getTopLevelGroups() {
return getTopLevelGroupsStream().collect(Collectors.toList());
}
Stream<GroupModel> getTopLevelGroupsStream();
@Deprecated
default List<GroupModel> getTopLevelGroups(Integer first, Integer max) {
return getTopLevelGroupsStream(first, max).collect(Collectors.toList());
}
Stream<GroupModel> getTopLevelGroupsStream(Integer first, Integer max);
@Deprecated
default List<GroupModel> searchForGroupByName(String search, Integer first, Integer max) {
return searchForGroupByNameStream(search, first, max).collect(Collectors.toList());
}
Stream<GroupModel> searchForGroupByNameStream(String search, Integer first, Integer max);
boolean removeGroup(GroupModel group);
void moveGroup(GroupModel group, GroupModel toParent);

View file

@ -219,7 +219,9 @@ public interface RealmProvider extends Provider /* TODO: Remove in future versio
/**
* @deprecated Use the corresponding method from {@link GroupProvider}. */
@Override
List<GroupModel> getGroups(RealmModel realm);
default List<GroupModel> getGroups(RealmModel realm) {
return getGroupsStream(realm).collect(Collectors.toList());
}
/**
* @deprecated Use the corresponding method from {@link GroupProvider}. */
@ -234,22 +236,30 @@ public interface RealmProvider extends Provider /* TODO: Remove in future versio
/**
* @deprecated Use the corresponding method from {@link GroupProvider}. */
@Override
List<GroupModel> getGroupsByRole(RealmModel realm, RoleModel role, int firstResult, int maxResults);
default List<GroupModel> getGroupsByRole(RealmModel realm, RoleModel role, int firstResult, int maxResults) {
return getGroupsByRoleStream(realm, role, firstResult, maxResults).collect(Collectors.toList());
}
/**
* @deprecated Use the corresponding method from {@link GroupProvider}. */
@Override
List<GroupModel> getTopLevelGroups(RealmModel realm);
default List<GroupModel> getTopLevelGroups(RealmModel realm) {
return getTopLevelGroupsStream(realm).collect(Collectors.toList());
}
/**
* @deprecated Use the corresponding method from {@link GroupProvider}. */
@Override
List<GroupModel> getTopLevelGroups(RealmModel realm, Integer first, Integer max);
default List<GroupModel> getTopLevelGroups(RealmModel realm, Integer first, Integer max) {
return getTopLevelGroupsStream(realm, first, max).collect(Collectors.toList());
}
/**
* @deprecated Use the corresponding method from {@link GroupProvider}. */
@Override
List searchForGroupByName(RealmModel realm, String search, Integer first, Integer max);
default List<GroupModel> searchForGroupByName(RealmModel realm, String search, Integer first, Integer max) {
return searchForGroupByNameStream(realm, search, first, max).collect(Collectors.toList());
}
/**
* @deprecated Use the corresponding method from {@link GroupProvider}. */

View file

@ -24,6 +24,7 @@ import java.util.List;
import java.util.Map;
import java.util.Set;
import java.util.stream.Collectors;
import java.util.stream.Stream;
/**
* @author <a href="mailto:bill@burkecentral.com">Bill Burke</a>
@ -118,18 +119,29 @@ public interface UserModel extends RoleMapperModel {
void setEmailVerified(boolean verified);
Set<GroupModel> getGroups();
default Set<GroupModel> getGroups(int first, int max) {
return getGroups(null, first, max);
@Deprecated
default Set<GroupModel> getGroups() {
return getGroupsStream().collect(Collectors.toSet());
}
Stream<GroupModel> getGroupsStream();
@Deprecated
default Set<GroupModel> getGroups(int first, int max) {
return getGroupsStream(null, first, max).collect(Collectors.toSet());
}
@Deprecated
default Set<GroupModel> getGroups(String search, int first, int max) {
return getGroups().stream()
return getGroupsStream(search, first, max)
.collect(Collectors.toCollection(LinkedHashSet::new));
}
default Stream<GroupModel> getGroupsStream(String search, int first, int max) {
return getGroupsStream()
.filter(group -> search == null || group.getName().toLowerCase().contains(search.toLowerCase()))
.skip(first)
.limit(max)
.collect(Collectors.toCollection(LinkedHashSet::new));
.limit(max);
}
default long getGroupsCount() {
@ -138,11 +150,11 @@ public interface UserModel extends RoleMapperModel {
default long getGroupsCountByNameContaining(String search) {
if (search == null) {
return getGroups().size();
return getGroupsStream().count();
}
String s = search.toLowerCase();
return getGroups().stream().filter(group -> group.getName().toLowerCase().contains(s)).count();
return getGroupsStream().filter(group -> group.getName().toLowerCase().contains(s)).count();
}
void joinGroup(GroupModel group);

View file

@ -39,13 +39,15 @@ public class RoleUtils {
* @param groups
* @param targetGroup
* @return true if targetGroup is in groups (directly or indirectly via parent child relationship)
* @deprecated Use {@link #isMember(Stream, GroupModel)} isMember(Stream, GroupModel)} instead.
*/
public static boolean isMember(Set<GroupModel> groups, GroupModel targetGroup) {
// collecting to set to keep "Breadth First Search" like functionality
if (groups.contains(targetGroup)) return true;
for (GroupModel mapping : groups) {
GroupModel child = mapping;
while(child.getParent() != null) {
while (child.getParent() != null) {
if (child.getParent().equals(targetGroup)) return true;
child = child.getParent();
}
@ -53,6 +55,27 @@ public class RoleUtils {
return false;
}
/**
*
* @param groups
* @param targetGroup
* @return true if targetGroup is in groups (directly or indirectly via parent child relationship)
*/
public static boolean isMember(Stream<GroupModel> groups, GroupModel targetGroup) {
// collecting to set to keep "Breadth First Search" like functionality
Set<GroupModel> groupsSet = groups.collect(Collectors.toSet());
if (groupsSet.contains(targetGroup)) return true;
return groupsSet.stream().anyMatch(mapping -> {
GroupModel child = mapping;
while (child.getParent() != null) {
if (child.getParent().equals(targetGroup)) return true;
child = child.getParent();
}
return false;
});
}
/**
* @param roles
* @param targetRole
@ -94,6 +117,7 @@ public class RoleUtils {
* @param targetRole
* @param checkParentGroup When {@code true}, also parent group is recursively checked for role
* @return true if targetRole is in roles (directly or indirectly via composite role)
* @deprecated Use {@link #hasRoleFromGroup(Stream, RoleModel, boolean)} hasRoleFromGroup(Stream, RoleModel, boolean)} instead.
*/
public static boolean hasRoleFromGroup(Iterable<GroupModel> groups, RoleModel targetRole, boolean checkParentGroup) {
if (groups == null) {
@ -104,6 +128,22 @@ public class RoleUtils {
.anyMatch(group -> hasRoleFromGroup(group, targetRole, checkParentGroup));
}
/**
* Checks whether the {@code targetRole} is contained in any of the {@code groups} or their parents
* (if requested)
* @param groups
* @param targetRole
* @param checkParentGroup When {@code true}, also parent group is recursively checked for role
* @return true if targetRole is in roles (directly or indirectly via composite role)
*/
public static boolean hasRoleFromGroup(Stream<GroupModel> groups, RoleModel targetRole, boolean checkParentGroup) {
if (groups == null) {
return false;
}
return groups.anyMatch(group -> hasRoleFromGroup(group, targetRole, checkParentGroup));
}
/**
* Recursively expands composite roles into their composite.
* @param role
@ -156,10 +196,7 @@ public class RoleUtils {
*/
public static Set<RoleModel> getDeepUserRoleMappings(UserModel user) {
Set<RoleModel> roleMappings = new HashSet<>(user.getRoleMappings());
for (GroupModel group : user.getGroups()) {
addGroupRoles(group, roleMappings);
}
user.getGroupsStream().forEach(group -> addGroupRoles(group, roleMappings));
return expandCompositeRoles(roleMappings);
}

View file

@ -25,6 +25,7 @@ import org.keycloak.models.UserModel;
import java.util.List;
import java.util.Map;
import java.util.Set;
import java.util.stream.Stream;
/**
* Delegation pattern. Used to proxy UserModel implementations.
@ -224,8 +225,8 @@ public class UserModelDelegate implements UserModel {
}
@Override
public Set<GroupModel> getGroups() {
return delegate.getGroups();
public Stream<GroupModel> getGroupsStream() {
return delegate.getGroupsStream();
}
@Override

View file

@ -36,6 +36,7 @@ import java.util.HashSet;
import java.util.List;
import java.util.Map;
import java.util.Set;
import java.util.stream.Stream;
/**
* This abstract class provides implementations for everything but getUsername(). getId() returns a default value
@ -94,8 +95,8 @@ public abstract class AbstractUserAdapter extends UserModelDefaultMethods {
*
* @return
*/
protected Set<GroupModel> getGroupsInternal() {
return Collections.emptySet();
protected Stream<GroupModel> getGroupsInternal() {
return Stream.empty();
}
/**
@ -110,11 +111,10 @@ public abstract class AbstractUserAdapter extends UserModelDefaultMethods {
}
@Override
public Set<GroupModel> getGroups() {
Set<GroupModel> set = new HashSet<>();
if (appendDefaultGroups()) set.addAll(realm.getDefaultGroups());
set.addAll(getGroupsInternal());
return set;
public Stream<GroupModel> getGroupsStream() {
Stream<GroupModel> groups = getGroupsInternal();
if (appendDefaultGroups()) groups = Stream.concat(groups, realm.getDefaultGroupsStream());
return groups;
}
@Override
@ -131,8 +131,7 @@ public abstract class AbstractUserAdapter extends UserModelDefaultMethods {
@Override
public boolean isMemberOf(GroupModel group) {
Set<GroupModel> roles = getGroups();
return RoleUtils.isMember(roles, group);
return RoleUtils.isMember(getGroupsStream(), group);
}
@Override
@ -170,7 +169,7 @@ public abstract class AbstractUserAdapter extends UserModelDefaultMethods {
public boolean hasRole(RoleModel role) {
Set<RoleModel> roles = getRoleMappings();
return RoleUtils.hasRole(roles, role)
|| RoleUtils.hasRoleFromGroup(getGroups(), role, true);
|| RoleUtils.hasRoleFromGroup(getGroupsStream(), role, true);
}
@Override

View file

@ -36,6 +36,7 @@ import java.util.HashSet;
import java.util.List;
import java.util.Map;
import java.util.Set;
import java.util.stream.Stream;
/**
* Assumes everything is managed by federated storage except for username. getId() returns a default value
@ -103,8 +104,8 @@ public abstract class AbstractUserAdapterFederatedStorage extends UserModelDefau
*
* @return
*/
protected Set<GroupModel> getGroupsInternal() {
return Collections.emptySet();
protected Stream<GroupModel> getGroupsInternal() {
return Stream.empty();
}
/**
@ -127,11 +128,10 @@ public abstract class AbstractUserAdapterFederatedStorage extends UserModelDefau
* @return
*/
@Override
public Set<GroupModel> getGroups() {
Set<GroupModel> set = new HashSet<>(getFederatedStorage().getGroups(realm, this.getId()));
if (appendDefaultGroups()) set.addAll(realm.getDefaultGroups());
set.addAll(getGroupsInternal());
return set;
public Stream<GroupModel> getGroupsStream() {
Stream<GroupModel> groups = getFederatedStorage().getGroupsStream(realm, this.getId());
if (appendDefaultGroups()) groups = Stream.concat(groups, realm.getDefaultGroupsStream());
return Stream.concat(groups, getGroupsInternal());
}
@Override
@ -148,8 +148,7 @@ public abstract class AbstractUserAdapterFederatedStorage extends UserModelDefau
@Override
public boolean isMemberOf(GroupModel group) {
Set<GroupModel> roles = getGroups();
return RoleUtils.isMember(roles, group);
return RoleUtils.isMember(getGroupsStream(), group);
}
/**
@ -203,7 +202,7 @@ public abstract class AbstractUserAdapterFederatedStorage extends UserModelDefau
public boolean hasRole(RoleModel role) {
Set<RoleModel> roles = getRoleMappings();
return RoleUtils.hasRole(roles, role)
|| RoleUtils.hasRoleFromGroup(getGroups(), role, true);
|| RoleUtils.hasRoleFromGroup(getGroupsStream(), role, true);
}
@Override

View file

@ -21,13 +21,21 @@ import org.keycloak.models.RealmModel;
import java.util.List;
import java.util.Set;
import java.util.stream.Collectors;
import java.util.stream.Stream;
/**
* @author <a href="mailto:bill@burkecentral.com">Bill Burke</a>
* @version $Revision: 1 $
*/
public interface UserGroupMembershipFederatedStorage {
Set<GroupModel> getGroups(RealmModel realm, String userId);
@Deprecated
default Set<GroupModel> getGroups(RealmModel realm, String userId) {
return getGroupsStream(realm, userId).collect(Collectors.toSet());
}
Stream<GroupModel> getGroupsStream(RealmModel realm, String userId);
void joinGroup(RealmModel realm, String userId, GroupModel group);
void leaveGroup(RealmModel realm, String userId, GroupModel group);
List<String> getMembership(RealmModel realm, GroupModel group, int firstResult, int max);

View file

@ -25,7 +25,6 @@ import java.util.Collections;
import java.util.List;
import java.util.Map;
import java.util.Set;
import java.util.stream.Collectors;
/**
* Optional capability interface implemented by UserStorageProviders.
@ -128,14 +127,9 @@ public interface UserQueryProvider {
* @return number of users that are in at least one of the groups
*/
static int countUsersInGroups(List<UserModel> users, Set<String> groupIds) {
return (int) users.stream().filter(u -> {
for (GroupModel group : u.getGroups()) {
if (groupIds.contains(group.getId())) {
return true;
}
}
return false;
}).count();
return (int) users.stream()
.filter(u -> u.getGroupsStream().anyMatch(group -> groupIds.contains(group.getId())))
.count();
}
/**

View file

@ -567,10 +567,7 @@ public class ExportUtils {
}
if (options.isGroupsAndRolesIncluded()) {
List<String> groups = new LinkedList<>();
for (GroupModel group : user.getGroups()) {
groups.add(ModelToRepresentation.buildGroupPath(group));
}
List<String> groups = user.getGroupsStream().map(ModelToRepresentation::buildGroupPath).collect(Collectors.toList());
userRep.setGroups(groups);
}
return userRep;
@ -737,10 +734,8 @@ public class ExportUtils {
userRep.setNotBefore(notBefore);
if (options.isGroupsAndRolesIncluded()) {
List<String> groups = new LinkedList<>();
for (GroupModel group : session.userFederatedStorage().getGroups(realm, id)) {
groups.add(ModelToRepresentation.buildGroupPath(group));
}
List<String> groups = session.userFederatedStorage().getGroupsStream(realm, id)
.map(ModelToRepresentation::buildGroupPath).collect(Collectors.toList());
userRep.setGroups(groups);
}
return userRep;

View file

@ -27,9 +27,10 @@ import org.keycloak.representations.IDToken;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.LinkedList;
import java.util.List;
import java.util.Map;
import java.util.function.Function;
import java.util.stream.Collectors;
/**
* Maps user group membership
@ -93,16 +94,10 @@ public class GroupMembershipMapper extends AbstractOIDCProtocolMapper implements
* @param userSession
*/
protected void setClaim(IDToken token, ProtocolMapperModel mappingModel, UserSessionModel userSession) {
Function<GroupModel, String> toGroupRepresentation = useFullPath(mappingModel) ?
ModelToRepresentation::buildGroupPath : GroupModel::getName;
List<String> membership = userSession.getUser().getGroupsStream().map(toGroupRepresentation).collect(Collectors.toList());
List<String> membership = new LinkedList<>();
boolean fullPath = useFullPath(mappingModel);
for (GroupModel group : userSession.getUser().getGroups()) {
if (fullPath) {
membership.add(ModelToRepresentation.buildGroupPath(group));
} else {
membership.add(group.getName());
}
}
String protocolClaim = mappingModel.getConfig().get(OIDCAttributeMapperHelper.TOKEN_CLAIM_NAME);
token.getOtherClaims().put(protocolClaim, membership);

View file

@ -20,7 +20,6 @@ package org.keycloak.protocol.saml.mappers;
import org.keycloak.dom.saml.v2.assertion.AttributeStatementType;
import org.keycloak.dom.saml.v2.assertion.AttributeType;
import org.keycloak.models.AuthenticatedClientSessionModel;
import org.keycloak.models.GroupModel;
import org.keycloak.models.KeycloakSession;
import org.keycloak.models.ProtocolMapperModel;
import org.keycloak.models.UserSessionModel;
@ -32,6 +31,7 @@ import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.concurrent.atomic.AtomicReference;
/**
* @author <a href="mailto:bill@burkecentral.com">Bill Burke</a>
@ -122,27 +122,27 @@ public class GroupMembershipMapper extends AbstractSAMLProtocolMapper implements
boolean singleAttribute = Boolean.parseBoolean(single);
boolean fullPath = useFullPath(mappingModel);
AttributeType singleAttributeType = null;
for (GroupModel group : userSession.getUser().getGroups()) {
final AtomicReference<AttributeType> singleAttributeType = new AtomicReference<>(null);
userSession.getUser().getGroupsStream().forEach(group -> {
String groupName;
if (fullPath) {
groupName = ModelToRepresentation.buildGroupPath(group);
} else {
groupName = group.getName();
}
AttributeType attributeType = null;
AttributeType attributeType;
if (singleAttribute) {
if (singleAttributeType == null) {
singleAttributeType = AttributeStatementHelper.createAttributeType(mappingModel);
attributeStatement.addAttribute(new AttributeStatementType.ASTChoiceType(singleAttributeType));
if (singleAttributeType.get() == null) {
singleAttributeType.set(AttributeStatementHelper.createAttributeType(mappingModel));
attributeStatement.addAttribute(new AttributeStatementType.ASTChoiceType(singleAttributeType.get()));
}
attributeType = singleAttributeType;
attributeType = singleAttributeType.get();
} else {
attributeType = AttributeStatementHelper.createAttributeType(mappingModel);
attributeStatement.addAttribute(new AttributeStatementType.ASTChoiceType(attributeType));
}
attributeType.addAttributeValue(groupName);
}
});
}
public static ProtocolMapperModel create(String name, String samlAttributeName, String nameFormat, String friendlyName, boolean singleAttribute) {

View file

@ -52,6 +52,8 @@ import java.util.List;
import java.util.Map;
import java.util.Objects;
import java.util.Set;
import java.util.function.Predicate;
import java.util.stream.Stream;
/**
* @resource Groups
@ -101,11 +103,10 @@ public class GroupResource {
public Response updateGroup(GroupRepresentation rep) {
this.auth.groups().requireManage(group);
for (GroupModel sibling: siblings()) {
if (Objects.equals(sibling.getId(), group.getId())) continue;
if (sibling.getName().equals(rep.getName())) {
return ErrorResponse.exists("Sibling group named '" + rep.getName() + "' already exists.");
}
boolean exists = siblings().filter(s -> !Objects.equals(s.getId(), group.getId()))
.anyMatch(s -> Objects.equals(s.getName(), rep.getName()));
if (exists) {
return ErrorResponse.exists("Sibling group named '" + rep.getName() + "' already exists.");
}
updateGroup(rep, group);
@ -114,11 +115,11 @@ public class GroupResource {
return Response.noContent().build();
}
private List<GroupModel> siblings() {
private Stream<GroupModel> siblings() {
if (group.getParentId() == null) {
return realm.getTopLevelGroups();
return realm.getTopLevelGroupsStream();
} else {
return new ArrayList(group.getParent().getSubGroups());
return group.getParent().getSubGroupsStream();
}
}
@ -145,10 +146,8 @@ public class GroupResource {
public Response addChild(GroupRepresentation rep) {
this.auth.groups().requireManage(group);
for (GroupModel group : group.getSubGroups()) {
if (group.getName().equals(rep.getName())) {
return ErrorResponse.exists("Parent already contains subgroup named '" + rep.getName() + "'");
}
if (group.getSubGroupsStream().map(GroupModel::getName).anyMatch(Predicate.isEqual(rep.getName()))) {
return ErrorResponse.exists("Parent already contains subgroup named '" + rep.getName() + "'");
}
Response.ResponseBuilder builder = Response.status(204);

View file

@ -42,9 +42,9 @@ import javax.ws.rs.core.MediaType;
import javax.ws.rs.core.Response;
import java.net.URI;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.Objects;
import java.util.stream.Stream;
/**
* @resource Groups
@ -73,23 +73,19 @@ public class GroupsResource {
@GET
@NoCache
@Produces(MediaType.APPLICATION_JSON)
public List<GroupRepresentation> getGroups(@QueryParam("search") String search,
@QueryParam("first") Integer firstResult,
@QueryParam("max") Integer maxResults,
@QueryParam("briefRepresentation") @DefaultValue("true") boolean briefRepresentation) {
public Stream<GroupRepresentation> getGroups(@QueryParam("search") String search,
@QueryParam("first") Integer firstResult,
@QueryParam("max") Integer maxResults,
@QueryParam("briefRepresentation") @DefaultValue("true") boolean briefRepresentation) {
auth.groups().requireList();
List<GroupRepresentation> results;
if (Objects.nonNull(search)) {
results = ModelToRepresentation.searchForGroupByName(realm, !briefRepresentation, search.trim(), firstResult, maxResults);
return ModelToRepresentation.searchForGroupByName(realm, !briefRepresentation, search.trim(), firstResult, maxResults);
} else if(Objects.nonNull(firstResult) && Objects.nonNull(maxResults)) {
results = ModelToRepresentation.toGroupHierarchy(realm, !briefRepresentation, firstResult, maxResults);
return ModelToRepresentation.toGroupHierarchy(realm, !briefRepresentation, firstResult, maxResults);
} else {
results = ModelToRepresentation.toGroupHierarchy(realm, !briefRepresentation);
return ModelToRepresentation.toGroupHierarchy(realm, !briefRepresentation);
}
return results;
}
/**

View file

@ -30,6 +30,7 @@ import java.util.List;
import java.util.Map;
import java.util.Set;
import java.util.stream.Collectors;
import java.util.stream.Stream;
import javax.ws.rs.BadRequestException;
import javax.ws.rs.Consumes;
@ -120,6 +121,7 @@ import org.keycloak.representations.idm.LDAPCapabilityRepresentation;
import org.keycloak.utils.ReservedCharValidator;
import com.fasterxml.jackson.core.type.TypeReference;
import org.keycloak.utils.ServicesUtils;
/**
* Base resource class for the admin REST api of one realm
@ -1045,14 +1047,10 @@ public class RealmAdminResource {
@NoCache
@Produces(MediaType.APPLICATION_JSON)
@Path("default-groups")
public List<GroupRepresentation> getDefaultGroups() {
public Stream<GroupRepresentation> getDefaultGroups() {
auth.realm().requireViewRealm();
List<GroupRepresentation> defaults = new LinkedList<>();
for (GroupModel group : realm.getDefaultGroups()) {
defaults.add(ModelToRepresentation.toRepresentation(group, false));
}
return defaults;
return realm.getDefaultGroupsStream().map(ServicesUtils::groupToBriefRepresentation);
}
@PUT
@NoCache

View file

@ -59,7 +59,6 @@ import java.util.List;
import java.util.Objects;
import java.util.Set;
import java.util.function.Function;
import java.util.stream.Collectors;
import java.util.stream.Stream;
/**
@ -440,7 +439,7 @@ public class RoleContainerResource extends RoleResource {
@GET
@Produces(MediaType.APPLICATION_JSON)
@NoCache
public List<GroupRepresentation> getGroupsInRole(final @PathParam("role-name") String roleName,
public Stream<GroupRepresentation> getGroupsInRole(final @PathParam("role-name") String roleName,
@QueryParam("first") Integer firstResult,
@QueryParam("max") Integer maxResults,
@QueryParam("briefRepresentation") @DefaultValue("true") boolean briefRepresentation) {
@ -455,10 +454,8 @@ public class RoleContainerResource extends RoleResource {
throw new NotFoundException("Could not find role");
}
List<GroupModel> groupsModel = session.groups().getGroupsByRole(realm, role, firstResult, maxResults);
Stream<GroupModel> groupsModel = session.groups().getGroupsByRoleStream(realm, role, firstResult, maxResults);
return groupsModel.stream()
.map(g -> ModelToRepresentation.toRepresentation(g, !briefRepresentation))
.collect(Collectors.toList());
return groupsModel.map(g -> ModelToRepresentation.toRepresentation(g, !briefRepresentation));
}
}

View file

@ -106,6 +106,7 @@ import java.util.Properties;
import java.util.Set;
import java.util.concurrent.TimeUnit;
import java.util.stream.Collectors;
import java.util.stream.Stream;
import static org.keycloak.models.ImpersonationSessionNote.IMPERSONATOR_ID;
import static org.keycloak.models.ImpersonationSessionNote.IMPERSONATOR_USERNAME;
@ -868,22 +869,19 @@ public class UserResource {
@Path("groups")
@NoCache
@Produces(MediaType.APPLICATION_JSON)
public List<GroupRepresentation> groupMembership(@QueryParam("search") String search,
@QueryParam("first") Integer firstResult,
@QueryParam("max") Integer maxResults,
@QueryParam("briefRepresentation") @DefaultValue("true") boolean briefRepresentation) {
public Stream<GroupRepresentation> groupMembership(@QueryParam("search") String search,
@QueryParam("first") Integer firstResult,
@QueryParam("max") Integer maxResults,
@QueryParam("briefRepresentation") @DefaultValue("true") boolean briefRepresentation) {
auth.users().requireView(user);
List<GroupRepresentation> results;
if (Objects.nonNull(search) && Objects.nonNull(firstResult) && Objects.nonNull(maxResults)) {
results = ModelToRepresentation.searchForGroupByName(user, !briefRepresentation, search.trim(), firstResult, maxResults);
return ModelToRepresentation.searchForGroupByName(user, !briefRepresentation, search.trim(), firstResult, maxResults);
} else if(Objects.nonNull(firstResult) && Objects.nonNull(maxResults)) {
results = ModelToRepresentation.toGroupHierarchy(user, !briefRepresentation, firstResult, maxResults);
return ModelToRepresentation.toGroupHierarchy(user, !briefRepresentation, firstResult, maxResults);
} else {
results = ModelToRepresentation.toGroupHierarchy(user, !briefRepresentation);
return ModelToRepresentation.toGroupHierarchy(user, !briefRepresentation);
}
return results;
}
@GET

View file

@ -546,10 +546,7 @@ class UserPermissions implements UserPermissionEvaluator, UserPermissionManageme
private boolean evaluateHierarchy(UserModel user, Predicate<GroupModel> eval) {
Set<GroupModel> visited = new HashSet<>();
for (GroupModel group : user.getGroups()) {
if (evaluateHierarchy(eval, group, visited)) return true;
}
return false;
return user.getGroupsStream().anyMatch(group -> evaluateHierarchy(eval, group, visited));
}
private boolean evaluateHierarchy(Predicate<GroupModel> eval, GroupModel group, Set<GroupModel> visited) {

View file

@ -19,7 +19,10 @@ package org.keycloak.utils;
import org.jboss.logging.Logger;
import org.keycloak.executors.ExecutorsProvider;
import org.keycloak.models.GroupModel;
import org.keycloak.models.KeycloakSession;
import org.keycloak.models.utils.ModelToRepresentation;
import org.keycloak.representations.idm.GroupRepresentation;
import java.util.concurrent.Callable;
import java.util.concurrent.ExecutionException;
@ -53,4 +56,8 @@ public class ServicesUtils {
}
};
}
public static GroupRepresentation groupToBriefRepresentation(GroupModel g) {
return ModelToRepresentation.toRepresentation(g, false);
}
}

View file

@ -389,14 +389,12 @@ public class UserMapStorage implements UserLookupProvider, UserStorageProvider,
}
@Override
public Set<GroupModel> getGroups(RealmModel realm, String userId) {
public Stream<GroupModel> getGroupsStream(RealmModel realm, String userId) {
Set<String> set = userGroups.get(getUserIdInMap(realm, userId));
if (set == null) {
return Collections.EMPTY_SET;
return Stream.empty();
}
return set.stream()
.map(realm::getGroupById)
.collect(Collectors.toSet());
return set.stream().map(realm::getGroupById);
}
@Override

View file

@ -150,7 +150,7 @@ public class LDAPGroupMapper2WaySyncTest extends AbstractLDAPTest {
GroupModel kcGroup12 = KeycloakModelUtils.findGroupByPath(realm, "/group12");
GroupModel kcGroup2 = KeycloakModelUtils.findGroupByPath(realm, "/group2");
Assert.assertEquals(0, kcGroup1.getSubGroups().size());
Assert.assertEquals(0, kcGroup1.getSubGroupsStream().count());
Assert.assertEquals("group1 - description1", kcGroup1.getFirstAttribute(descriptionAttrName));
Assert.assertNull(kcGroup11.getFirstAttribute(descriptionAttrName));
@ -212,7 +212,7 @@ public class LDAPGroupMapper2WaySyncTest extends AbstractLDAPTest {
GroupModel kcGroup12 = KeycloakModelUtils.findGroupByPath(realm, "/group1/group12");
GroupModel kcGroup2 = KeycloakModelUtils.findGroupByPath(realm, "/group2");
Assert.assertEquals(2, kcGroup1.getSubGroups().size());
Assert.assertEquals(2, kcGroup1.getSubGroupsStream().count());
Assert.assertEquals("group1 - description1", kcGroup1.getFirstAttribute(descriptionAttrName));
Assert.assertNull(kcGroup11.getFirstAttribute(descriptionAttrName));
@ -226,9 +226,7 @@ public class LDAPGroupMapper2WaySyncTest extends AbstractLDAPTest {
private static void removeAllModelGroups(RealmModel appRealm) {
for (GroupModel group : appRealm.getTopLevelGroups()) {
appRealm.removeGroup(group);
}
appRealm.getTopLevelGroupsStream().forEach(appRealm::removeGroup);
}
private static void testDropNonExisting(KeycloakSession session, LDAPTestContext ctx, ComponentModel mapperModel) {

View file

@ -50,6 +50,7 @@ import javax.ws.rs.BadRequestException;
import java.util.Date;
import java.util.List;
import java.util.Set;
import java.util.stream.Collectors;
import static org.keycloak.testsuite.util.LDAPTestUtils.getGroupDescriptionLDAPAttrName;
@ -105,10 +106,7 @@ public class LDAPGroupMapperSyncTest extends AbstractLDAPTest {
LDAPTestContext ctx = LDAPTestContext.init(session);
RealmModel realm = ctx.getRealm();
List<GroupModel> kcGroups = realm.getTopLevelGroups();
for (GroupModel kcGroup : kcGroups) {
realm.removeGroup(kcGroup);
}
realm.getTopLevelGroupsStream().forEach(realm::removeGroup);
});
}
@ -169,7 +167,7 @@ public class LDAPGroupMapperSyncTest extends AbstractLDAPTest {
GroupModel kcGroup11 = KeycloakModelUtils.findGroupByPath(realm, "/group11");
GroupModel kcGroup12 = KeycloakModelUtils.findGroupByPath(realm, "/group12");
Assert.assertEquals(0, kcGroup1.getSubGroups().size());
Assert.assertEquals(0, kcGroup1.getSubGroupsStream().count());
Assert.assertEquals("group1 - description", kcGroup1.getFirstAttribute(descriptionAttrName));
Assert.assertNull(kcGroup11.getFirstAttribute(descriptionAttrName));
@ -221,7 +219,7 @@ public class LDAPGroupMapperSyncTest extends AbstractLDAPTest {
GroupModel kcGroup11 = KeycloakModelUtils.findGroupByPath(realm, "/group1/group11");
GroupModel kcGroup12 = KeycloakModelUtils.findGroupByPath(realm, "/group1/group12");
Assert.assertEquals(2, kcGroup1.getSubGroups().size());
Assert.assertEquals(2, kcGroup1.getSubGroupsStream().count());
Assert.assertEquals("group1 - description", kcGroup1.getFirstAttribute(descriptionAttrName));
Assert.assertNull(kcGroup11.getFirstAttribute(descriptionAttrName));
@ -272,7 +270,7 @@ public class LDAPGroupMapperSyncTest extends AbstractLDAPTest {
Assert.assertNotNull(KeycloakModelUtils.findGroupByPath(realm, "/group1/group11"));
Assert.assertNotNull(KeycloakModelUtils.findGroupByPath(realm, "/group1/group12"));
Assert.assertEquals(2, kcGroup1.getSubGroups().size());
Assert.assertEquals(2, kcGroup1.getSubGroupsStream().count());
// Create some new groups in keycloak
GroupModel model1 = realm.createGroup("model1");
@ -341,7 +339,7 @@ public class LDAPGroupMapperSyncTest extends AbstractLDAPTest {
// Load user from LDAP to Keycloak DB
UserModel john = session.users().getUserByUsername("johnkeycloak", realm);
Set<GroupModel> johnGroups = john.getGroups();
Set<GroupModel> johnGroups = john.getGroupsStream().collect(Collectors.toSet());
// Assert just those groups, which john was memberOf exists because they were lazily created
GroupModel group1 = KeycloakModelUtils.findGroupByPath(realm, "/group1");

View file

@ -92,9 +92,7 @@ public class LDAPGroupMapperSyncWithGroupsPathTest extends AbstractLDAPTest {
RealmModel realm = ctx.getRealm();
GroupModel groupsPathGroup = KeycloakModelUtils.findGroupByPath(realm, LDAP_GROUPS_PATH);
for (GroupModel kcGroup : groupsPathGroup.getSubGroups()) {
realm.removeGroup(kcGroup);
}
groupsPathGroup.getSubGroupsStream().forEach(realm::removeGroup);
});
}
@ -126,7 +124,7 @@ public class LDAPGroupMapperSyncWithGroupsPathTest extends AbstractLDAPTest {
GroupModel kcGroup11 = KeycloakModelUtils.findGroupByPath(realm, LDAP_GROUPS_PATH + "/group1/group11");
GroupModel kcGroup12 = KeycloakModelUtils.findGroupByPath(realm, LDAP_GROUPS_PATH + "/group1/group12");
Assert.assertEquals(2, kcGroup1.getSubGroups().size());
Assert.assertEquals(2, kcGroup1.getSubGroupsStream().count());
Assert.assertEquals("group1 - description", kcGroup1.getFirstAttribute(descriptionAttrName));
Assert.assertNull(kcGroup11.getFirstAttribute(descriptionAttrName));
@ -175,7 +173,7 @@ public class LDAPGroupMapperSyncWithGroupsPathTest extends AbstractLDAPTest {
Assert.assertNotNull(KeycloakModelUtils.findGroupByPath(realm, LDAP_GROUPS_PATH + "/group1/group11"));
Assert.assertNotNull(KeycloakModelUtils.findGroupByPath(realm, LDAP_GROUPS_PATH + "/group1/group12"));
Assert.assertEquals(2, kcGroup1.getSubGroups().size());
Assert.assertEquals(2, kcGroup1.getSubGroupsStream().count());
// Create some new groups in keycloak
GroupModel groupsPathGroup = KeycloakModelUtils.findGroupByPath(realm, LDAP_GROUPS_PATH);

View file

@ -127,20 +127,11 @@ public class LDAPGroupMapperTest extends AbstractLDAPTest {
RealmModel appRealm = ctx.getRealm();
UserModel johnDb = session.userLocalStorage().getUserByUsername("johnkeycloak", appRealm);
Set<GroupModel> johnDbGroups = johnDb.getGroups();
Assert.assertEquals(2, johnDbGroups.size());
Set<GroupModel> johnDbGroupsWithGr = johnDb.getGroups("Gr", 0, 10);
Assert.assertEquals(2, johnDbGroupsWithGr.size());
Set<GroupModel> johnDbGroupsWithGr2 = johnDb.getGroups("Gr", 1, 10);
Assert.assertEquals(1, johnDbGroupsWithGr2.size());
Set<GroupModel> johnDbGroupsWithGr3 = johnDb.getGroups("Gr", 0, 1);
Assert.assertEquals(1, johnDbGroupsWithGr3.size());
Set<GroupModel> johnDbGroupsWith12 = johnDb.getGroups("12", 0, 10);
Assert.assertEquals(1, johnDbGroupsWith12.size());
Assert.assertEquals(2, johnDb.getGroupsStream().count());
Assert.assertEquals(2, johnDb.getGroupsStream("Gr", 0, 10).count());
Assert.assertEquals(1, johnDb.getGroupsStream("Gr", 1, 10).count());
Assert.assertEquals(1, johnDb.getGroupsStream("Gr", 0, 1).count());
Assert.assertEquals(1, johnDb.getGroupsStream("12", 0, 10).count());
long dbGroupCount = johnDb.getGroupsCount();
Assert.assertEquals(2, dbGroupCount);
@ -161,7 +152,7 @@ public class LDAPGroupMapperTest extends AbstractLDAPTest {
UserModel john = session.users().getUserByUsername("johnkeycloak", appRealm);
UserModel mary = session.users().getUserByUsername("marykeycloak", appRealm);
Set<GroupModel> johnGroups = john.getGroups();
Set<GroupModel> johnGroups = john.getGroupsStream().collect(Collectors.toSet());
Assert.assertEquals(4, johnGroups.size());
long groupCount = john.getGroupsCount();
Assert.assertEquals(4, groupCount);
@ -171,23 +162,12 @@ public class LDAPGroupMapperTest extends AbstractLDAPTest {
Assert.assertTrue(johnGroups.contains(groupTeam20162017));
Assert.assertTrue(johnGroups.contains(groupTeamChild20182019));
Set<GroupModel> johnGroupsWithGr = john.getGroups("gr", 0, 10);
Assert.assertEquals(2, johnGroupsWithGr.size());
Set<GroupModel> johnGroupsWithGr2 = john.getGroups("gr", 1, 10);
Assert.assertEquals(1, johnGroupsWithGr2.size());
Set<GroupModel> johnGroupsWithGr3 = john.getGroups("gr", 0, 1);
Assert.assertEquals(1, johnGroupsWithGr3.size());
Set<GroupModel> johnGroupsWith12 = john.getGroups("12", 0, 10);
Assert.assertEquals(1, johnGroupsWith12.size());
Set<GroupModel> johnGroupsWith2017 = john.getGroups("2017", 0, 10);
Assert.assertEquals(1, johnGroupsWith2017.size());
Set<GroupModel> johnGroupsWith2018 = john.getGroups("2018", 0, 10);
Assert.assertEquals(1, johnGroupsWith2017.size());
Assert.assertEquals(2, john.getGroupsStream("gr", 0, 10).count());
Assert.assertEquals(1, john.getGroupsStream("gr", 1, 10).count());
Assert.assertEquals(1, john.getGroupsStream("gr", 0, 1).count());
Assert.assertEquals(1, john.getGroupsStream("12", 0, 10).count());
Assert.assertEquals(1, john.getGroupsStream("2017", 0, 10).count());
Assert.assertEquals(1, john.getGroupsStream("2018", 0, 10).count());
// 4 - Check through userProvider
List<UserModel> group1Members = session.users().getGroupMembers(appRealm, group1, 0, 10);
@ -217,8 +197,7 @@ public class LDAPGroupMapperTest extends AbstractLDAPTest {
mary.leaveGroup(groupTeam20162017);
mary.leaveGroup(groupTeamChild20182019);
johnGroups = john.getGroups();
Assert.assertEquals(0, johnGroups.size());
Assert.assertEquals(0, john.getGroupsStream().count());
groupCount = john.getGroupsCount();
Assert.assertEquals(0, groupCount);
@ -266,7 +245,7 @@ public class LDAPGroupMapperTest extends AbstractLDAPTest {
mary.joinGroup(group12);
// Assert that mary has both LDAP and DB mapped groups
Set<GroupModel> maryGroups = mary.getGroups();
Set<GroupModel> maryGroups = mary.getGroupsStream().collect(Collectors.toSet());
Assert.assertEquals(5, maryGroups.size());
Assert.assertTrue(maryGroups.contains(group1));
Assert.assertTrue(maryGroups.contains(group11));
@ -275,17 +254,10 @@ public class LDAPGroupMapperTest extends AbstractLDAPTest {
long groupCount = mary.getGroupsCount();
Assert.assertEquals(5, groupCount);
Set<GroupModel> maryGroupsWithGr = mary.getGroups("gr", 0, 10);
Assert.assertEquals(5, maryGroupsWithGr.size());
Set<GroupModel> maryGroupsWithGr2 = mary.getGroups("gr", 1, 10);
Assert.assertEquals(4, maryGroupsWithGr2.size());
Set<GroupModel> maryGroupsWithGr3 = mary.getGroups("gr", 0, 1);
Assert.assertEquals(1, maryGroupsWithGr3.size());
Set<GroupModel> maryGroupsWith12 = mary.getGroups("12", 0, 10);
Assert.assertEquals(2, maryGroupsWith12.size());
Assert.assertEquals(5, mary.getGroupsStream("gr", 0, 10).count());
Assert.assertEquals(4, mary.getGroupsStream("gr", 1, 10).count());
Assert.assertEquals(1, mary.getGroupsStream("gr", 0, 1).count());
Assert.assertEquals(2, mary.getGroupsStream("12", 0, 10).count());
});
} else {
testingClient.server().run(session -> {
@ -315,7 +287,7 @@ public class LDAPGroupMapperTest extends AbstractLDAPTest {
GroupModel group12 = KeycloakModelUtils.findGroupByPath(appRealm, "/group1/group12");
// Assert that mary has both LDAP and DB mapped groups
Set<GroupModel> maryGroups = mary.getGroups();
Set<GroupModel> maryGroups = mary.getGroupsStream().collect(Collectors.toSet());
Assert.assertEquals(4, maryGroups.size());
Assert.assertTrue(maryGroups.contains(group1));
Assert.assertTrue(maryGroups.contains(group11));
@ -324,17 +296,10 @@ public class LDAPGroupMapperTest extends AbstractLDAPTest {
long groupCount = mary.getGroupsCount();
Assert.assertEquals(4, groupCount);
Set<GroupModel> maryGroupsWithGr = mary.getGroups("gr", 0, 10);
Assert.assertEquals(4, maryGroupsWithGr.size());
Set<GroupModel> maryGroupsWithGr2 = mary.getGroups("gr", 1, 10);
Assert.assertEquals(3, maryGroupsWithGr2.size());
Set<GroupModel> maryGroupsWithGr3 = mary.getGroups("gr", 0, 1);
Assert.assertEquals(1, maryGroupsWithGr3.size());
Set<GroupModel> maryGroupsWith12 = mary.getGroups("12", 0, 10);
Assert.assertEquals(1, maryGroupsWith12.size());
Assert.assertEquals(4, mary.getGroupsStream("gr", 0, 10).count());
Assert.assertEquals(3, mary.getGroupsStream("gr", 1, 10).count());
Assert.assertEquals(1, mary.getGroupsStream("gr", 0, 1).count());
Assert.assertEquals(1, mary.getGroupsStream("12", 0, 10).count());
});
}
@ -350,22 +315,15 @@ public class LDAPGroupMapperTest extends AbstractLDAPTest {
UserModel maryDB = session.userLocalStorage().getUserByUsername("marykeycloak", appRealm);
Set<GroupModel> maryDBGroups = maryDB.getGroups();
Set<GroupModel> maryDBGroups = maryDB.getGroupsStream().collect(Collectors.toSet());
Assert.assertFalse(maryDBGroups.contains(group1));
Assert.assertFalse(maryDBGroups.contains(group11));
Assert.assertTrue(maryDBGroups.contains(group12));
Set<GroupModel> maryDBGroupsWithGr = maryDB.getGroups("Gr", 0, 10);
Assert.assertEquals(3, maryDBGroupsWithGr.size());
Set<GroupModel> maryDBGroupsWithGr2 = maryDB.getGroups("Gr", 1, 10);
Assert.assertEquals(2, maryDBGroupsWithGr2.size());
Set<GroupModel> maryDBGroupsWithGr3 = maryDB.getGroups("Gr", 0, 1);
Assert.assertEquals(1, maryDBGroupsWithGr3.size());
Set<GroupModel> maryDBGroupsWith12 = maryDB.getGroups("12", 0, 10);
Assert.assertEquals(2, maryDBGroupsWith12.size());
Assert.assertEquals(3, maryDB.getGroupsStream("Gr", 0, 10).count());
Assert.assertEquals(2, maryDB.getGroupsStream("Gr", 1, 10).count());
Assert.assertEquals(1, maryDB.getGroupsStream("Gr", 0, 1).count());
Assert.assertEquals(2, maryDB.getGroupsStream("12", 0, 10).count());
long dbGroupCount = maryDB.getGroupsCount();
Assert.assertEquals(3, dbGroupCount);
@ -455,23 +413,16 @@ public class LDAPGroupMapperTest extends AbstractLDAPTest {
// Get user and check that he has requested groups from LDAP
UserModel rob = session.users().getUserByUsername("robkeycloak", appRealm);
Set<GroupModel> robGroups = rob.getGroups();
Set<GroupModel> robGroups = rob.getGroupsStream().collect(Collectors.toSet());
Assert.assertFalse(robGroups.contains(group1));
Assert.assertTrue(robGroups.contains(group11));
Assert.assertTrue(robGroups.contains(group12));
Set<GroupModel> robGroupsWithGr = rob.getGroups("Gr", 0, 10);
Assert.assertEquals(4, robGroupsWithGr.size());
Set<GroupModel> robGroupsWithGr2 = rob.getGroups("Gr", 1, 10);
Assert.assertEquals(3, robGroupsWithGr2.size());
Set<GroupModel> robGroupsWithGr3 = rob.getGroups("Gr", 0, 1);
Assert.assertEquals(1, robGroupsWithGr3.size());
Set<GroupModel> robGroupsWith12 = rob.getGroups("12", 0, 10);
Assert.assertEquals(2, robGroupsWith12.size());
Assert.assertEquals(4, rob.getGroupsStream("Gr", 0, 10).count());
Assert.assertEquals(3, rob.getGroupsStream("Gr", 1, 10).count());
Assert.assertEquals(1, rob.getGroupsStream("Gr", 0, 1).count());
Assert.assertEquals(2, rob.getGroupsStream("12", 0, 10).count());
long dbGroupCount = rob.getGroupsCount();
Assert.assertEquals(4, dbGroupCount);
@ -494,7 +445,7 @@ public class LDAPGroupMapperTest extends AbstractLDAPTest {
ldapGroup = groupMapper.loadLDAPGroupByName("group12");
groupMapper.deleteGroupMappingInLDAP(robLdap, ldapGroup);
robGroups = rob.getGroups();
robGroups = rob.getGroupsStream().collect(Collectors.toSet());
Assert.assertTrue(robGroups.contains(group11));
Assert.assertTrue(robGroups.contains(group12));
@ -512,8 +463,7 @@ public class LDAPGroupMapperTest extends AbstractLDAPTest {
// Delete group mappings through model and verifies that user doesn't have them anymore
rob.leaveGroup(group11);
rob.leaveGroup(group12);
robGroups = rob.getGroups();
Assert.assertEquals(2, robGroups.size());
Assert.assertEquals(2, rob.getGroupsStream().count());
});
}
@ -604,7 +554,7 @@ public class LDAPGroupMapperTest extends AbstractLDAPTest {
// Get user in Keycloak. Ensure that he is member of requested group
UserModel carlos = session.users().getUserByUsername("carloskeycloak", appRealm);
Set<GroupModel> carlosGroups = carlos.getGroups();
Set<GroupModel> carlosGroups = carlos.getGroupsStream().collect(Collectors.toSet());
GroupModel group1 = KeycloakModelUtils.findGroupByPath(appRealm, "/group1");
GroupModel group11 = KeycloakModelUtils.findGroupByPath(appRealm, "/group1/group11");
@ -676,7 +626,7 @@ public class LDAPGroupMapperTest extends AbstractLDAPTest {
GroupModel group32 = KeycloakModelUtils.findGroupByPath(appRealm, "/group3/group32");
GroupModel group4 = KeycloakModelUtils.findGroupByPath(appRealm, "/group4");
Set<GroupModel> groups = john.getGroups();
Set<GroupModel> groups = john.getGroupsStream().collect(Collectors.toSet());
Assert.assertTrue(groups.contains(group14));
Assert.assertFalse(groups.contains(group3));
Assert.assertTrue(groups.contains(group31));
@ -686,20 +636,11 @@ public class LDAPGroupMapperTest extends AbstractLDAPTest {
long groupsCount = john.getGroupsCount();
Assert.assertEquals(4, groupsCount);
Set<GroupModel> groupsWith3v1 = john.getGroups("3", 0, 10);
Assert.assertEquals(2, groupsWith3v1.size());
Set<GroupModel> groupsWith3v2 = john.getGroups("3", 1, 10);
Assert.assertEquals(1, groupsWith3v2.size());
Set<GroupModel> groupsWith3v3 = john.getGroups("3", 1, 1);
Assert.assertEquals(1, groupsWith3v3.size());
Set<GroupModel> groupsWith3v4 = john.getGroups("3", 1, 0);
Assert.assertEquals(0, groupsWith3v4.size());
Set<GroupModel> groupsWithKeycloak = john.getGroups("Keycloak", 0, 10);
Assert.assertEquals(0, groupsWithKeycloak.size());
Assert.assertEquals(2, john.getGroupsStream("3", 0, 10).count());
Assert.assertEquals(1, john.getGroupsStream("3", 1, 10).count());
Assert.assertEquals(1, john.getGroupsStream("3", 1, 1).count());
Assert.assertEquals(0, john.getGroupsStream("3", 1, 0).count());
Assert.assertEquals(0, john.getGroupsStream("Keycloak", 0, 10).count());
});
}
@ -731,7 +672,7 @@ public class LDAPGroupMapperTest extends AbstractLDAPTest {
GroupModel group4 = KeycloakModelUtils.findGroupByPath(appRealm, "/group4");
Assert.assertNotNull(group4);
Set<GroupModel> groups = david.getGroups();
Set<GroupModel> groups = david.getGroupsStream().collect(Collectors.toSet());
Assert.assertTrue(groups.contains(defaultGroup11));
Assert.assertTrue(groups.contains(defaultGroup12));
Assert.assertFalse(groups.contains(group31));
@ -797,7 +738,7 @@ public class LDAPGroupMapperTest extends AbstractLDAPTest {
// check all the users have the group assigned
for (int i = 0; i < membersToTest; i++) {
UserModel kcUser = session.users().getUserByUsername(String.format("user%02d", i), appRealm);
Assert.assertTrue("User contains biggroup " + i, kcUser.getGroups().contains(kcBigGroup));
Assert.assertTrue("User contains biggroup " + i, kcUser.getGroupsStream().collect(Collectors.toSet()).contains(kcBigGroup));
}
// check the group contains all the users as member
List<UserModel> groupMembers = session.users().getGroupMembers(appRealm, kcBigGroup, 0, membersToTest);
@ -846,7 +787,7 @@ public class LDAPGroupMapperTest extends AbstractLDAPTest {
List<UserModel> groupMembers = session.users().getGroupMembers(appRealm, kcDeleteGroup, 0, 5);
Assert.assertEquals(1, groupMembers.size());
Assert.assertEquals("marykeycloak", groupMembers.iterator().next().getUsername());
Set<GroupModel> maryGroups = mary.getGroups();
Set<GroupModel> maryGroups = mary.getGroupsStream().collect(Collectors.toSet());
Assert.assertEquals(1, maryGroups.size());
Assert.assertEquals("deletegroup", maryGroups.iterator().next().getName());

View file

@ -41,6 +41,7 @@ import org.keycloak.testsuite.util.LDAPTestUtils;
import java.util.List;
import java.util.Set;
import java.util.stream.Collectors;
import static org.keycloak.testsuite.util.LDAPTestUtils.getGroupDescriptionLDAPAttrName;
@ -162,7 +163,7 @@ public class LDAPSpecialCharsTest extends AbstractLDAPTest {
// 2 - Check that group mappings are in LDAP and hence available through federation
Set<GroupModel> userGroups = specialUser.getGroups();
Set<GroupModel> userGroups = specialUser.getGroupsStream().collect(Collectors.toSet());
Assert.assertEquals(2, userGroups.size());
Assert.assertTrue(userGroups.contains(specialGroup));
@ -182,8 +183,7 @@ public class LDAPSpecialCharsTest extends AbstractLDAPTest {
specialUser.leaveGroup(specialGroup);
specialUser.leaveGroup(groupWithSlashes);
userGroups = specialUser.getGroups();
Assert.assertEquals(0, userGroups.size());
Assert.assertEquals(0, specialUser.getGroupsStream().count());
});
}

View file

@ -42,6 +42,7 @@ import javax.ws.rs.NotFoundException;
import java.io.File;
import java.util.LinkedList;
import java.util.List;
import java.util.stream.Collectors;
import org.keycloak.testsuite.arquillian.annotation.AuthServerContainerExclude.AuthServer;
@ -149,7 +150,7 @@ public class FederatedStorageExportImportTest extends AbstractAuthTest {
Assert.assertTrue(attributes.getList("list1").contains("2"));
Assert.assertTrue(session.userFederatedStorage().getRequiredActions(realm, userId).contains("UPDATE_PASSWORD"));
Assert.assertTrue(session.userFederatedStorage().getRoleMappings(realm, userId).contains(role));
Assert.assertTrue(session.userFederatedStorage().getGroups(realm, userId).contains(group));
Assert.assertTrue(session.userFederatedStorage().getGroupsStream(realm, userId).collect(Collectors.toSet()).contains(group));
List<CredentialModel> creds = session.userFederatedStorage().getStoredCredentials(realm, userId);
Assert.assertEquals(1, creds.size());
Assert.assertTrue(FederatedStorageExportImportTest.getHashProvider(session, realm.getPasswordPolicy())
@ -216,7 +217,7 @@ public class FederatedStorageExportImportTest extends AbstractAuthTest {
Assert.assertTrue(attributes.getList("list1").contains("2"));
Assert.assertTrue(session.userFederatedStorage().getRequiredActions(realm, userId).contains("UPDATE_PASSWORD"));
Assert.assertTrue(session.userFederatedStorage().getRoleMappings(realm, userId).contains(role));
Assert.assertTrue(session.userFederatedStorage().getGroups(realm, userId).contains(group));
Assert.assertTrue(session.userFederatedStorage().getGroupsStream(realm, userId).collect(Collectors.toSet()).contains(group));
Assert.assertEquals(50, session.userFederatedStorage().getNotBeforeOfUser(realm, userId));
List<CredentialModel> creds = session.userFederatedStorage().getStoredCredentials(realm, userId);
Assert.assertEquals(1, creds.size());

View file

@ -44,9 +44,7 @@ public class TestCacheUtils {
cacheRoles(session, realm, realm);
for (GroupModel group : realm.getTopLevelGroups()) {
cacheGroupRecursive(realm, group);
}
realm.getTopLevelGroupsStream().forEach(group -> cacheGroupRecursive(realm, group));
for (ClientScopeModel clientScope : realm.getClientScopes()) {
realm.getClientScopeById(clientScope.getId());
@ -81,8 +79,6 @@ public class TestCacheUtils {
private static void cacheGroupRecursive(RealmModel realm, GroupModel group) {
realm.getGroupById(group.getId());
for (GroupModel sub : group.getSubGroups()) {
cacheGroupRecursive(realm, sub);
}
group.getSubGroupsStream().forEach(sub -> cacheGroupRecursive(realm, sub));
}
}