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

View file

@ -284,9 +284,8 @@ public class LDAPStorageProvider implements UserStorageProvider,
UserModel proxy = proxy(realm, user, ldapUser, true); UserModel proxy = proxy(realm, user, ldapUser, true);
DefaultRoles.addDefaultRoles(realm, proxy); DefaultRoles.addDefaultRoles(realm, proxy);
for (GroupModel g : realm.getDefaultGroups()) { realm.getDefaultGroupsStream().forEach(proxy::joinGroup);
proxy.joinGroup(g);
}
for (RequiredActionProviderModel r : realm.getRequiredActionProviders()) { for (RequiredActionProviderModel r : realm.getRequiredActionProviders()) {
if (r.isEnabled() && r.isDefaultAction()) { if (r.isEnabled() && r.isDefaultAction()) {
proxy.addRequiredAction(r.getAlias()); 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.model.LDAPObject;
import org.keycloak.storage.ldap.idm.query.internal.LDAPQuery; import org.keycloak.storage.ldap.idm.query.internal.LDAPQuery;
import java.util.HashSet; import java.util.stream.Stream;
import java.util.Set;
/** /**
* @author <a href="mailto:jean-loup.maillet@yesitis.fr">Jean-Loup Maillet</a> * @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) { return new UserModelDelegate(delegate) {
@Override @Override
public Set<GroupModel> getGroups() { public Stream<GroupModel> getGroupsStream() {
Set<GroupModel> groups = new HashSet<GroupModel>(super.getGroups()); Stream<GroupModel> groups = super.getGroupsStream();
GroupModel group = getGroup(realm); GroupModel group = getGroup(realm);
if (group != null) { if (group != null) {
groups.add(group); return Stream.concat(groups, Stream.of(group));
} }
return groups; return groups;

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

@ -49,7 +49,7 @@ public class CachedGroup extends AbstractRevisioned implements InRealm {
this.parentId = group.getParentId(); this.parentId = group.getParentId();
this.attributes = new DefaultLazyLoader<>(source -> new MultivaluedHashMap<>(source.getAttributes()), MultivaluedHashMap::new); 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.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() { public String getRealm() {

View file

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

View file

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

View file

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

View file

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

View file

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

View file

@ -43,11 +43,9 @@ import javax.persistence.criteria.Join;
import javax.persistence.criteria.Predicate; import javax.persistence.criteria.Predicate;
import javax.persistence.criteria.Root; import javax.persistence.criteria.Root;
import java.util.ArrayList; import java.util.ArrayList;
import java.util.Collection;
import java.util.Collections; import java.util.Collections;
import java.util.HashSet; import java.util.HashSet;
import java.util.Iterator; import java.util.Iterator;
import java.util.LinkedHashSet;
import java.util.List; import java.util.List;
import java.util.Map; import java.util.Map;
import java.util.Set; import java.util.Set;
@ -55,6 +53,8 @@ import java.util.Objects;
import java.util.stream.Stream; import java.util.stream.Stream;
import javax.persistence.LockModeType; import javax.persistence.LockModeType;
import static org.keycloak.utils.StreamsUtil.closing;
/** /**
* @author <a href="mailto:bill@burkecentral.com">Bill Burke</a> * @author <a href="mailto:bill@burkecentral.com">Bill Burke</a>
* @version $Revision: 1 $ * @version $Revision: 1 $
@ -401,22 +401,18 @@ public class UserAdapter implements UserModel, JpaModel<UserEntity> {
return em.createQuery(queryBuilder); return em.createQuery(queryBuilder);
} }
private Set<GroupModel> getGroupModels(Collection<String> groupIds) { private Stream<GroupModel> getGroupModels(Stream<String> groupIds) {
Set<GroupModel> groups = new LinkedHashSet<>(); return groupIds.map(realm::getGroupById);
for (String id : groupIds) {
groups.add(realm.getGroupById(id));
}
return groups;
} }
@Override @Override
public Set<GroupModel> getGroups() { public Stream<GroupModel> getGroupsStream() {
return getGroupModels(createGetGroupsQuery(null, null, null).getResultList()); return closing(getGroupModels(createGetGroupsQuery(null, null, null).getResultStream()));
} }
@Override @Override
public Set<GroupModel> getGroups(String search, int first, int max) { public Stream<GroupModel> getGroupsStream(String search, int first, int max) {
return getGroupModels(createGetGroupsQuery(search, first, max).getResultList()); return closing(getGroupModels(createGetGroupsQuery(search, first, max).getResultStream()));
} }
@Override @Override
@ -463,8 +459,7 @@ public class UserAdapter implements UserModel, JpaModel<UserEntity> {
@Override @Override
public boolean isMemberOf(GroupModel group) { public boolean isMemberOf(GroupModel group) {
Set<GroupModel> roles = getGroups(); return RoleUtils.isMember(getGroupsStream(), group);
return RoleUtils.isMember(roles, group);
} }
protected TypedQuery<UserGroupMembershipEntity> getUserGroupMappingQuery(GroupModel group) { protected TypedQuery<UserGroupMembershipEntity> getUserGroupMappingQuery(GroupModel group) {
@ -479,7 +474,7 @@ public class UserAdapter implements UserModel, JpaModel<UserEntity> {
public boolean hasRole(RoleModel role) { public boolean hasRole(RoleModel role) {
Set<RoleModel> roles = getRoleMappings(); Set<RoleModel> roles = getRoleMappings();
return RoleUtils.hasRole(roles, role) return RoleUtils.hasRole(roles, role)
|| RoleUtils.hasRoleFromGroup(getGroups(), role, true); || RoleUtils.hasRoleFromGroup(getGroupsStream(), role, true);
} }
protected TypedQuery<UserRoleMappingEntity> getUserRoleMappingEntityTypedQuery(RoleModel role) { 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.UserConsentModel;
import org.keycloak.models.UserModel; import org.keycloak.models.UserModel;
import org.keycloak.models.jpa.JpaUserCredentialStore; import org.keycloak.models.jpa.JpaUserCredentialStore;
import org.keycloak.models.jpa.entities.CredentialEntity;
import org.keycloak.models.utils.KeycloakModelUtils; import org.keycloak.models.utils.KeycloakModelUtils;
import org.keycloak.storage.StorageId; import org.keycloak.storage.StorageId;
import org.keycloak.storage.UserStorageProvider; import org.keycloak.storage.UserStorageProvider;
@ -61,10 +60,12 @@ import java.util.Collection;
import java.util.HashSet; import java.util.HashSet;
import java.util.LinkedList; import java.util.LinkedList;
import java.util.List; import java.util.List;
import java.util.ListIterator;
import java.util.Set; import java.util.Set;
import java.util.stream.Stream;
import javax.persistence.LockModeType; import javax.persistence.LockModeType;
import static org.keycloak.utils.StreamsUtil.closing;
/** /**
* @author <a href="mailto:bill@burkecentral.com">Bill Burke</a> * @author <a href="mailto:bill@burkecentral.com">Bill Burke</a>
* @version $Revision: 1 $ * @version $Revision: 1 $
@ -439,17 +440,10 @@ public class JpaUserFederatedStorageProvider implements
} }
@Override @Override
public Set<GroupModel> getGroups(RealmModel realm, String userId) { public Stream<GroupModel> getGroupsStream(RealmModel realm, String userId) {
Set<GroupModel> set = new HashSet<>();
TypedQuery<FederatedUserGroupMembershipEntity> query = em.createNamedQuery("feduserGroupMembership", FederatedUserGroupMembershipEntity.class); TypedQuery<FederatedUserGroupMembershipEntity> query = em.createNamedQuery("feduserGroupMembership", FederatedUserGroupMembershipEntity.class);
query.setParameter("userId", userId); query.setParameter("userId", userId);
List<FederatedUserGroupMembershipEntity> results = query.getResultList(); return closing(query.getResultStream().map(FederatedUserGroupMembershipEntity::getGroupId).map(realm::getGroupById));
if (results.size() == 0) return set;
for (FederatedUserGroupMembershipEntity entity : results) {
GroupModel group = realm.getGroupById(entity.getGroupId());
set.add(group);
}
return set;
} }
@Override @Override

View file

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

View file

@ -57,8 +57,12 @@ import java.security.cert.X509Certificate;
import java.util.Collection; import java.util.Collection;
import java.util.HashSet; import java.util.HashSet;
import java.util.List; import java.util.List;
import java.util.Objects;
import java.util.Optional;
import java.util.Set; import java.util.Set;
import java.util.UUID; 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. * 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) { public static List<String> resolveAttribute(GroupModel group, String name) {
List<String> values = group.getAttribute(name); List<String> values = group.getAttributeStream(name).collect(Collectors.toList());
if (values != null && !values.isEmpty()) return values; if (!values.isEmpty()) return values;
if (group.getParentId() == null) return null; if (group.getParentId() == null) return null;
return resolveAttribute(group.getParent(), name); return resolveAttribute(group.getParent(), name);
} }
@ -418,21 +403,24 @@ public final class KeycloakModelUtils {
} }
aggrValues.addAll(values); aggrValues.addAll(values);
} }
for (GroupModel group : user.getGroups()) { Stream<List<String>> attributes = user.getGroupsStream()
values = resolveAttribute(group, name); .map(group -> resolveAttribute(group, name))
if (values != null && !values.isEmpty()) { .filter(Objects::nonNull)
.filter(attr -> !attr.isEmpty());
if (!aggregateAttrs) { if (!aggregateAttrs) {
return values; Optional<List<String>> first = attributes.findFirst();
} if (first.isPresent()) return first.get();
aggrValues.addAll(values); } else {
} aggrValues.addAll(attributes.flatMap(Collection::stream).collect(Collectors.toSet()));
} }
return aggrValues; return aggrValues;
} }
private static GroupModel findSubGroup(String[] segments, int index, GroupModel parent) { 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 groupName = group.getName();
String[] pathSegments = formatPathSegments(segments, index, groupName); String[] pathSegments = formatPathSegments(segments, index, groupName);
@ -444,14 +432,11 @@ public final class KeycloakModelUtils {
if (index + 1 < pathSegments.length) { if (index + 1 < pathSegments.length) {
GroupModel found = findSubGroup(pathSegments, index + 1, group); GroupModel found = findSubGroup(pathSegments, index + 1, group);
if (found != null) return found; 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("/"); String[] split = path.split("/");
if (split.length == 0) return null; if (split.length == 0) return null;
GroupModel found = null;
for (GroupModel group : realm.getTopLevelGroups()) { return realm.getTopLevelGroupsStream().map(group -> {
String groupName = group.getName(); String groupName = group.getName();
String[] pathSegments = formatPathSegments(split, 0, groupName); String[] pathSegments = formatPathSegments(split, 0, groupName);
if (groupName.equals(pathSegments[0])) { if (groupName.equals(pathSegments[0])) {
if (pathSegments.length == 1) { if (pathSegments.length == 1) {
found = group; return group;
break;
} }
else { else {
if (pathSegments.length > 1) { if (pathSegments.length > 1) {
found = findSubGroup(pathSegments, 1, group); GroupModel subGroup = findSubGroup(pathSegments, 1, group);
if (found != null) break; if (subGroup != null) return subGroup;
} }
} }
} }
} return null;
return found; }).filter(Objects::nonNull).findFirst().orElse(null);
} }
public static Set<RoleModel> getClientScopeMappings(ClientModel client, ScopeContainerModel container) { 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.LinkedList;
import java.util.List; import java.util.List;
import java.util.Map; import java.util.Map;
import java.util.Objects;
import java.util.Set; import java.util.Set;
import java.util.stream.Collectors; import java.util.stream.Collectors;
import java.util.stream.Stream;
/** /**
* @author <a href="mailto:bill@burkecentral.com">Bill Burke</a> * @author <a href="mailto:bill@burkecentral.com">Bill Burke</a>
@ -139,63 +139,40 @@ public class ModelToRepresentation {
return rep; return rep;
} }
public static List<GroupRepresentation> searchForGroupByName(RealmModel realm, boolean full, String search, Integer first, Integer max) { public static Stream<GroupRepresentation> searchForGroupByName(RealmModel realm, boolean full, String search, Integer first, Integer max) {
List<GroupRepresentation> result = new LinkedList<>(); return realm.searchForGroupByNameStream(search, first, max)
List<GroupModel> groups = realm.searchForGroupByName(search, first, max); .map(g -> toGroupHierarchy(g, full));
if (Objects.isNull(groups)) return result;
for (GroupModel group : groups) {
GroupRepresentation rep = toGroupHierarchy(group, full);
result.add(rep);
}
return result;
} }
public static List<GroupRepresentation> searchForGroupByName(UserModel user, boolean full, String search, Integer first, Integer max) { public static Stream<GroupRepresentation> searchForGroupByName(UserModel user, boolean full, String search, Integer first, Integer max) {
return user.getGroups(search, first, max).stream() return user.getGroupsStream(search, first, max)
.map(group -> toRepresentation(group, full)) .map(group -> toRepresentation(group, full));
.collect(Collectors.toList());
} }
public static List<GroupRepresentation> toGroupHierarchy(RealmModel realm, boolean full, Integer first, Integer max) { public static Stream<GroupRepresentation> toGroupHierarchy(RealmModel realm, boolean full, Integer first, Integer max) {
List<GroupRepresentation> hierarchy = new LinkedList<>(); return realm.getTopLevelGroupsStream(first, max)
List<GroupModel> groups = realm.getTopLevelGroups(first, max); .map(g -> toGroupHierarchy(g, full));
if (Objects.isNull(groups)) return hierarchy;
for (GroupModel group : groups) {
GroupRepresentation rep = toGroupHierarchy(group, full);
hierarchy.add(rep);
}
return hierarchy;
} }
public static List<GroupRepresentation> toGroupHierarchy(UserModel user, boolean full, Integer first, Integer max) { public static Stream<GroupRepresentation> toGroupHierarchy(UserModel user, boolean full, Integer first, Integer max) {
return user.getGroups(first, max).stream() return user.getGroupsStream(null, first, max)
.map(group -> toRepresentation(group, full)) .map(group -> toRepresentation(group, full));
.collect(Collectors.toList());
} }
public static List<GroupRepresentation> toGroupHierarchy(RealmModel realm, boolean full) { public static Stream<GroupRepresentation> toGroupHierarchy(RealmModel realm, boolean full) {
List<GroupRepresentation> hierarchy = new LinkedList<>(); return realm.getTopLevelGroupsStream()
List<GroupModel> groups = realm.getTopLevelGroups(); .map(g -> toGroupHierarchy(g, full));
if (Objects.isNull(groups)) return hierarchy;
for (GroupModel group : groups) {
GroupRepresentation rep = toGroupHierarchy(group, full);
hierarchy.add(rep);
}
return hierarchy;
} }
public static List<GroupRepresentation> toGroupHierarchy(UserModel user, boolean full) { public static Stream<GroupRepresentation> toGroupHierarchy(UserModel user, boolean full) {
return user.getGroups().stream() return user.getGroupsStream()
.map(group -> toRepresentation(group, full)) .map(group -> toRepresentation(group, full));
.collect(Collectors.toList());
} }
public static GroupRepresentation toGroupHierarchy(GroupModel group, boolean full) { public static GroupRepresentation toGroupHierarchy(GroupModel group, boolean full) {
GroupRepresentation rep = toRepresentation(group, full); GroupRepresentation rep = toRepresentation(group, full);
List<GroupRepresentation> subGroups = new LinkedList<>(); List<GroupRepresentation> subGroups = group.getSubGroupsStream()
for (GroupModel subGroup : group.getSubGroups()) { .map(subGroup -> toGroupHierarchy(subGroup, full)).collect(Collectors.toList());
subGroups.add(toGroupHierarchy(subGroup, full));
}
rep.setSubGroups(subGroups); rep.setSubGroups(subGroups);
return rep; return rep;
} }
@ -437,13 +414,10 @@ public class ModelToRepresentation {
List<String> roleStrings = new ArrayList<>(defaultRoles); List<String> roleStrings = new ArrayList<>(defaultRoles);
rep.setDefaultRoles(roleStrings); rep.setDefaultRoles(roleStrings);
} }
List<GroupModel> defaultGroups = realm.getDefaultGroups(); List<String> defaultGroups = realm.getDefaultGroupsStream()
.map(ModelToRepresentation::buildGroupPath).collect(Collectors.toList());
if (!defaultGroups.isEmpty()) { if (!defaultGroups.isEmpty()) {
List<String> groupPaths = new LinkedList<>(); rep.setDefaultGroups(defaultGroups);
for (GroupModel group : defaultGroups) {
groupPaths.add(ModelToRepresentation.buildGroupPath(group));
}
rep.setDefaultGroups(groupPaths);
} }
List<RequiredCredentialModel> requiredCredentialModels = realm.getRequiredCredentials(); List<RequiredCredentialModel> requiredCredentialModels = realm.getRequiredCredentials();
@ -502,8 +476,7 @@ public class ModelToRepresentation {
} }
public static void exportGroups(RealmModel realm, RealmRepresentation rep) { public static void exportGroups(RealmModel realm, RealmRepresentation rep) {
List<GroupRepresentation> groups = toGroupHierarchy(realm, true); rep.setGroups(toGroupHierarchy(realm, true).collect(Collectors.toList()));
rep.setGroups(groups);
} }
public static void exportAuthenticationFlows(RealmModel realm, RealmRepresentation rep) { 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.models.utils.RoleUtils;
import org.keycloak.storage.ReadOnlyException; import org.keycloak.storage.ReadOnlyException;
import java.util.Collections;
import java.util.HashSet; import java.util.HashSet;
import java.util.LinkedList; import java.util.LinkedList;
import java.util.List; import java.util.List;
import java.util.Map; import java.util.Map;
import java.util.Set; import java.util.Set;
import java.util.stream.Stream;
/** /**
* @author <a href="mailto:bill@burkecentral.com">Bill Burke</a> * @author <a href="mailto:bill@burkecentral.com">Bill Burke</a>
@ -79,10 +79,7 @@ public class InMemoryUserAdapter extends UserModelDefaultMethods {
public void addDefaults() { public void addDefaults() {
DefaultRoles.addDefaultRoles(realm, this); DefaultRoles.addDefaultRoles(realm, this);
for (GroupModel g : realm.getDefaultGroups()) { realm.getDefaultGroupsStream().forEach(this::joinGroup);
joinGroup(g);
}
} }
public void setReadonly(boolean flag) { public void setReadonly(boolean flag) {
@ -213,13 +210,8 @@ public class InMemoryUserAdapter extends UserModelDefaultMethods {
} }
@Override @Override
public Set<GroupModel> getGroups() { public Stream<GroupModel> getGroupsStream() {
if (groupIds.isEmpty()) return new HashSet<>(); return groupIds.stream().map(realm::getGroupById);
Set<GroupModel> groups = new HashSet<>();
for (String id : groupIds) {
groups.add(realm.getGroupById(id));
}
return groups;
} }
@Override @Override
@ -240,8 +232,7 @@ public class InMemoryUserAdapter extends UserModelDefaultMethods {
public boolean isMemberOf(GroupModel group) { public boolean isMemberOf(GroupModel group) {
if (groupIds == null) return false; if (groupIds == null) return false;
if (groupIds.contains(group.getId())) return true; if (groupIds.contains(group.getId())) return true;
Set<GroupModel> groups = getGroups(); return RoleUtils.isMember(getGroupsStream(), group);
return RoleUtils.isMember(groups, group);
} }
@Override @Override
@ -299,7 +290,7 @@ public class InMemoryUserAdapter extends UserModelDefaultMethods {
public boolean hasRole(RoleModel role) { public boolean hasRole(RoleModel role) {
Set<RoleModel> roles = getRoleMappings(); Set<RoleModel> roles = getRoleMappings();
return RoleUtils.hasRole(roles, role) return RoleUtils.hasRole(roles, role)
|| RoleUtils.hasRoleFromGroup(getGroups(), role, true); || RoleUtils.hasRoleFromGroup(getGroupsStream(), role, true);
} }
@Override @Override

View file

@ -22,6 +22,8 @@ import org.keycloak.provider.ProviderEvent;
import java.util.List; import java.util.List;
import java.util.Map; import java.util.Map;
import java.util.Set; import java.util.Set;
import java.util.stream.Collectors;
import java.util.stream.Stream;
/** /**
* @author <a href="mailto:bill@burkecentral.com">Bill Burke</a> * @author <a href="mailto:bill@burkecentral.com">Bill Burke</a>
@ -61,13 +63,24 @@ public interface GroupModel extends RoleMapperModel {
* @param name * @param name
* @return list of all attribute values or empty list if there are not any values. Never return null * @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(); Map<String, List<String>> getAttributes();
GroupModel getParent(); GroupModel getParent();
String getParentId(); 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 * 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 org.keycloak.provider.Provider;
import java.util.List; 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. * @param realm Realm.
* @return List of groups in the 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. * 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 firstResult First result to return. Ignored if negative.
* @param maxResults Maximum number of results to return. Ignored if negative. * @param maxResults Maximum number of results to return. Ignored if negative.
* @return List of groups with the given role. * @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. * Returns all top level groups (i.e. groups without parent group) for the given realm.
* *
* @param realm Realm. * @param realm Realm.
* @return List of all top level groups in the 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. * 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 firstResult First result to return.
* @param maxResults Maximum number of results to return. * @param maxResults Maximum number of results to return.
* @return List of top level groups in the realm. * @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. * 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 firstResult First result to return. Ignored if {@code null}.
* @param maxResults Maximum number of results 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. * @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. * 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); RoleModel getRoleById(String id);
List<GroupModel> getDefaultGroups(); @Deprecated
default List<GroupModel> getDefaultGroups() {
return getDefaultGroupsStream().collect(Collectors.toList());
}
Stream<GroupModel> getDefaultGroupsStream();
void addDefaultGroup(GroupModel group); void addDefaultGroup(GroupModel group);
@ -535,12 +540,38 @@ public interface RealmModel extends RoleContainerModel {
GroupModel createGroup(String id, String name, GroupModel toParent); GroupModel createGroup(String id, String name, GroupModel toParent);
GroupModel getGroupById(String id); 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 getGroupsCount(Boolean onlyTopGroups);
Long getGroupsCountByNameContaining(String search); Long getGroupsCountByNameContaining(String search);
List<GroupModel> getTopLevelGroups();
List<GroupModel> getTopLevelGroups(Integer first, Integer max); @Deprecated
List<GroupModel> searchForGroupByName(String search, Integer first, Integer max); 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); boolean removeGroup(GroupModel group);
void moveGroup(GroupModel group, GroupModel toParent); 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}. */ * @deprecated Use the corresponding method from {@link GroupProvider}. */
@Override @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}. */ * @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}. */ * @deprecated Use the corresponding method from {@link GroupProvider}. */
@Override @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}. */ * @deprecated Use the corresponding method from {@link GroupProvider}. */
@Override @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}. */ * @deprecated Use the corresponding method from {@link GroupProvider}. */
@Override @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}. */ * @deprecated Use the corresponding method from {@link GroupProvider}. */
@Override @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}. */ * @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.Map;
import java.util.Set; import java.util.Set;
import java.util.stream.Collectors; import java.util.stream.Collectors;
import java.util.stream.Stream;
/** /**
* @author <a href="mailto:bill@burkecentral.com">Bill Burke</a> * @author <a href="mailto:bill@burkecentral.com">Bill Burke</a>
@ -118,18 +119,29 @@ public interface UserModel extends RoleMapperModel {
void setEmailVerified(boolean verified); void setEmailVerified(boolean verified);
Set<GroupModel> getGroups(); @Deprecated
default Set<GroupModel> getGroups() {
default Set<GroupModel> getGroups(int first, int max) { return getGroupsStream().collect(Collectors.toSet());
return getGroups(null, first, max);
} }
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) { 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())) .filter(group -> search == null || group.getName().toLowerCase().contains(search.toLowerCase()))
.skip(first) .skip(first)
.limit(max) .limit(max);
.collect(Collectors.toCollection(LinkedHashSet::new));
} }
default long getGroupsCount() { default long getGroupsCount() {
@ -138,11 +150,11 @@ public interface UserModel extends RoleMapperModel {
default long getGroupsCountByNameContaining(String search) { default long getGroupsCountByNameContaining(String search) {
if (search == null) { if (search == null) {
return getGroups().size(); return getGroupsStream().count();
} }
String s = search.toLowerCase(); 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); void joinGroup(GroupModel group);

View file

@ -39,13 +39,15 @@ public class RoleUtils {
* @param groups * @param groups
* @param targetGroup * @param targetGroup
* @return true if targetGroup is in groups (directly or indirectly via parent child relationship) * @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) { 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; if (groups.contains(targetGroup)) return true;
for (GroupModel mapping : groups) { for (GroupModel mapping : groups) {
GroupModel child = mapping; GroupModel child = mapping;
while(child.getParent() != null) { while (child.getParent() != null) {
if (child.getParent().equals(targetGroup)) return true; if (child.getParent().equals(targetGroup)) return true;
child = child.getParent(); child = child.getParent();
} }
@ -53,6 +55,27 @@ public class RoleUtils {
return false; 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 roles
* @param targetRole * @param targetRole
@ -94,6 +117,7 @@ public class RoleUtils {
* @param targetRole * @param targetRole
* @param checkParentGroup When {@code true}, also parent group is recursively checked for role * @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) * @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) { public static boolean hasRoleFromGroup(Iterable<GroupModel> groups, RoleModel targetRole, boolean checkParentGroup) {
if (groups == null) { if (groups == null) {
@ -104,6 +128,22 @@ public class RoleUtils {
.anyMatch(group -> hasRoleFromGroup(group, targetRole, checkParentGroup)); .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. * Recursively expands composite roles into their composite.
* @param role * @param role
@ -156,10 +196,7 @@ public class RoleUtils {
*/ */
public static Set<RoleModel> getDeepUserRoleMappings(UserModel user) { public static Set<RoleModel> getDeepUserRoleMappings(UserModel user) {
Set<RoleModel> roleMappings = new HashSet<>(user.getRoleMappings()); Set<RoleModel> roleMappings = new HashSet<>(user.getRoleMappings());
for (GroupModel group : user.getGroups()) { user.getGroupsStream().forEach(group -> addGroupRoles(group, roleMappings));
addGroupRoles(group, roleMappings);
}
return expandCompositeRoles(roleMappings); return expandCompositeRoles(roleMappings);
} }

View file

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

View file

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

View file

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

View file

@ -21,13 +21,21 @@ import org.keycloak.models.RealmModel;
import java.util.List; import java.util.List;
import java.util.Set; import java.util.Set;
import java.util.stream.Collectors;
import java.util.stream.Stream;
/** /**
* @author <a href="mailto:bill@burkecentral.com">Bill Burke</a> * @author <a href="mailto:bill@burkecentral.com">Bill Burke</a>
* @version $Revision: 1 $ * @version $Revision: 1 $
*/ */
public interface UserGroupMembershipFederatedStorage { 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 joinGroup(RealmModel realm, String userId, GroupModel group);
void leaveGroup(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); 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.List;
import java.util.Map; import java.util.Map;
import java.util.Set; import java.util.Set;
import java.util.stream.Collectors;
/** /**
* Optional capability interface implemented by UserStorageProviders. * 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 * @return number of users that are in at least one of the groups
*/ */
static int countUsersInGroups(List<UserModel> users, Set<String> groupIds) { static int countUsersInGroups(List<UserModel> users, Set<String> groupIds) {
return (int) users.stream().filter(u -> { return (int) users.stream()
for (GroupModel group : u.getGroups()) { .filter(u -> u.getGroupsStream().anyMatch(group -> groupIds.contains(group.getId())))
if (groupIds.contains(group.getId())) { .count();
return true;
}
}
return false;
}).count();
} }
/** /**

View file

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

View file

@ -27,9 +27,10 @@ import org.keycloak.representations.IDToken;
import java.util.ArrayList; import java.util.ArrayList;
import java.util.HashMap; import java.util.HashMap;
import java.util.LinkedList;
import java.util.List; import java.util.List;
import java.util.Map; import java.util.Map;
import java.util.function.Function;
import java.util.stream.Collectors;
/** /**
* Maps user group membership * Maps user group membership
@ -93,16 +94,10 @@ public class GroupMembershipMapper extends AbstractOIDCProtocolMapper implements
* @param userSession * @param userSession
*/ */
protected void setClaim(IDToken token, ProtocolMapperModel mappingModel, UserSessionModel 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); String protocolClaim = mappingModel.getConfig().get(OIDCAttributeMapperHelper.TOKEN_CLAIM_NAME);
token.getOtherClaims().put(protocolClaim, membership); 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.AttributeStatementType;
import org.keycloak.dom.saml.v2.assertion.AttributeType; import org.keycloak.dom.saml.v2.assertion.AttributeType;
import org.keycloak.models.AuthenticatedClientSessionModel; import org.keycloak.models.AuthenticatedClientSessionModel;
import org.keycloak.models.GroupModel;
import org.keycloak.models.KeycloakSession; import org.keycloak.models.KeycloakSession;
import org.keycloak.models.ProtocolMapperModel; import org.keycloak.models.ProtocolMapperModel;
import org.keycloak.models.UserSessionModel; import org.keycloak.models.UserSessionModel;
@ -32,6 +31,7 @@ import java.util.ArrayList;
import java.util.HashMap; import java.util.HashMap;
import java.util.List; import java.util.List;
import java.util.Map; import java.util.Map;
import java.util.concurrent.atomic.AtomicReference;
/** /**
* @author <a href="mailto:bill@burkecentral.com">Bill Burke</a> * @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 singleAttribute = Boolean.parseBoolean(single);
boolean fullPath = useFullPath(mappingModel); boolean fullPath = useFullPath(mappingModel);
AttributeType singleAttributeType = null; final AtomicReference<AttributeType> singleAttributeType = new AtomicReference<>(null);
for (GroupModel group : userSession.getUser().getGroups()) { userSession.getUser().getGroupsStream().forEach(group -> {
String groupName; String groupName;
if (fullPath) { if (fullPath) {
groupName = ModelToRepresentation.buildGroupPath(group); groupName = ModelToRepresentation.buildGroupPath(group);
} else { } else {
groupName = group.getName(); groupName = group.getName();
} }
AttributeType attributeType = null; AttributeType attributeType;
if (singleAttribute) { if (singleAttribute) {
if (singleAttributeType == null) { if (singleAttributeType.get() == null) {
singleAttributeType = AttributeStatementHelper.createAttributeType(mappingModel); singleAttributeType.set(AttributeStatementHelper.createAttributeType(mappingModel));
attributeStatement.addAttribute(new AttributeStatementType.ASTChoiceType(singleAttributeType)); attributeStatement.addAttribute(new AttributeStatementType.ASTChoiceType(singleAttributeType.get()));
} }
attributeType = singleAttributeType; attributeType = singleAttributeType.get();
} else { } else {
attributeType = AttributeStatementHelper.createAttributeType(mappingModel); attributeType = AttributeStatementHelper.createAttributeType(mappingModel);
attributeStatement.addAttribute(new AttributeStatementType.ASTChoiceType(attributeType)); attributeStatement.addAttribute(new AttributeStatementType.ASTChoiceType(attributeType));
} }
attributeType.addAttributeValue(groupName); attributeType.addAttributeValue(groupName);
} });
} }
public static ProtocolMapperModel create(String name, String samlAttributeName, String nameFormat, String friendlyName, boolean singleAttribute) { 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.Map;
import java.util.Objects; import java.util.Objects;
import java.util.Set; import java.util.Set;
import java.util.function.Predicate;
import java.util.stream.Stream;
/** /**
* @resource Groups * @resource Groups
@ -101,12 +103,11 @@ public class GroupResource {
public Response updateGroup(GroupRepresentation rep) { public Response updateGroup(GroupRepresentation rep) {
this.auth.groups().requireManage(group); this.auth.groups().requireManage(group);
for (GroupModel sibling: siblings()) { boolean exists = siblings().filter(s -> !Objects.equals(s.getId(), group.getId()))
if (Objects.equals(sibling.getId(), group.getId())) continue; .anyMatch(s -> Objects.equals(s.getName(), rep.getName()));
if (sibling.getName().equals(rep.getName())) { if (exists) {
return ErrorResponse.exists("Sibling group named '" + rep.getName() + "' already exists."); return ErrorResponse.exists("Sibling group named '" + rep.getName() + "' already exists.");
} }
}
updateGroup(rep, group); updateGroup(rep, group);
adminEvent.operation(OperationType.UPDATE).resourcePath(session.getContext().getUri()).representation(rep).success(); adminEvent.operation(OperationType.UPDATE).resourcePath(session.getContext().getUri()).representation(rep).success();
@ -114,11 +115,11 @@ public class GroupResource {
return Response.noContent().build(); return Response.noContent().build();
} }
private List<GroupModel> siblings() { private Stream<GroupModel> siblings() {
if (group.getParentId() == null) { if (group.getParentId() == null) {
return realm.getTopLevelGroups(); return realm.getTopLevelGroupsStream();
} else { } else {
return new ArrayList(group.getParent().getSubGroups()); return group.getParent().getSubGroupsStream();
} }
} }
@ -145,11 +146,9 @@ public class GroupResource {
public Response addChild(GroupRepresentation rep) { public Response addChild(GroupRepresentation rep) {
this.auth.groups().requireManage(group); this.auth.groups().requireManage(group);
for (GroupModel group : group.getSubGroups()) { if (group.getSubGroupsStream().map(GroupModel::getName).anyMatch(Predicate.isEqual(rep.getName()))) {
if (group.getName().equals(rep.getName())) {
return ErrorResponse.exists("Parent already contains subgroup named '" + rep.getName() + "'"); return ErrorResponse.exists("Parent already contains subgroup named '" + rep.getName() + "'");
} }
}
Response.ResponseBuilder builder = Response.status(204); Response.ResponseBuilder builder = Response.status(204);
GroupModel child = null; GroupModel child = null;

View file

@ -42,9 +42,9 @@ import javax.ws.rs.core.MediaType;
import javax.ws.rs.core.Response; import javax.ws.rs.core.Response;
import java.net.URI; import java.net.URI;
import java.util.HashMap; import java.util.HashMap;
import java.util.List;
import java.util.Map; import java.util.Map;
import java.util.Objects; import java.util.Objects;
import java.util.stream.Stream;
/** /**
* @resource Groups * @resource Groups
@ -73,23 +73,19 @@ public class GroupsResource {
@GET @GET
@NoCache @NoCache
@Produces(MediaType.APPLICATION_JSON) @Produces(MediaType.APPLICATION_JSON)
public List<GroupRepresentation> getGroups(@QueryParam("search") String search, public Stream<GroupRepresentation> getGroups(@QueryParam("search") String search,
@QueryParam("first") Integer firstResult, @QueryParam("first") Integer firstResult,
@QueryParam("max") Integer maxResults, @QueryParam("max") Integer maxResults,
@QueryParam("briefRepresentation") @DefaultValue("true") boolean briefRepresentation) { @QueryParam("briefRepresentation") @DefaultValue("true") boolean briefRepresentation) {
auth.groups().requireList(); auth.groups().requireList();
List<GroupRepresentation> results;
if (Objects.nonNull(search)) { 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)) { } else if(Objects.nonNull(firstResult) && Objects.nonNull(maxResults)) {
results = ModelToRepresentation.toGroupHierarchy(realm, !briefRepresentation, firstResult, maxResults); return ModelToRepresentation.toGroupHierarchy(realm, !briefRepresentation, firstResult, maxResults);
} else { } 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.Map;
import java.util.Set; import java.util.Set;
import java.util.stream.Collectors; import java.util.stream.Collectors;
import java.util.stream.Stream;
import javax.ws.rs.BadRequestException; import javax.ws.rs.BadRequestException;
import javax.ws.rs.Consumes; import javax.ws.rs.Consumes;
@ -120,6 +121,7 @@ import org.keycloak.representations.idm.LDAPCapabilityRepresentation;
import org.keycloak.utils.ReservedCharValidator; import org.keycloak.utils.ReservedCharValidator;
import com.fasterxml.jackson.core.type.TypeReference; import com.fasterxml.jackson.core.type.TypeReference;
import org.keycloak.utils.ServicesUtils;
/** /**
* Base resource class for the admin REST api of one realm * Base resource class for the admin REST api of one realm
@ -1045,14 +1047,10 @@ public class RealmAdminResource {
@NoCache @NoCache
@Produces(MediaType.APPLICATION_JSON) @Produces(MediaType.APPLICATION_JSON)
@Path("default-groups") @Path("default-groups")
public List<GroupRepresentation> getDefaultGroups() { public Stream<GroupRepresentation> getDefaultGroups() {
auth.realm().requireViewRealm(); auth.realm().requireViewRealm();
List<GroupRepresentation> defaults = new LinkedList<>(); return realm.getDefaultGroupsStream().map(ServicesUtils::groupToBriefRepresentation);
for (GroupModel group : realm.getDefaultGroups()) {
defaults.add(ModelToRepresentation.toRepresentation(group, false));
}
return defaults;
} }
@PUT @PUT
@NoCache @NoCache

View file

@ -59,7 +59,6 @@ import java.util.List;
import java.util.Objects; import java.util.Objects;
import java.util.Set; import java.util.Set;
import java.util.function.Function; import java.util.function.Function;
import java.util.stream.Collectors;
import java.util.stream.Stream; import java.util.stream.Stream;
/** /**
@ -440,7 +439,7 @@ public class RoleContainerResource extends RoleResource {
@GET @GET
@Produces(MediaType.APPLICATION_JSON) @Produces(MediaType.APPLICATION_JSON)
@NoCache @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("first") Integer firstResult,
@QueryParam("max") Integer maxResults, @QueryParam("max") Integer maxResults,
@QueryParam("briefRepresentation") @DefaultValue("true") boolean briefRepresentation) { @QueryParam("briefRepresentation") @DefaultValue("true") boolean briefRepresentation) {
@ -455,10 +454,8 @@ public class RoleContainerResource extends RoleResource {
throw new NotFoundException("Could not find role"); 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() return groupsModel.map(g -> ModelToRepresentation.toRepresentation(g, !briefRepresentation));
.map(g -> ModelToRepresentation.toRepresentation(g, !briefRepresentation))
.collect(Collectors.toList());
} }
} }

View file

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

View file

@ -546,10 +546,7 @@ class UserPermissions implements UserPermissionEvaluator, UserPermissionManageme
private boolean evaluateHierarchy(UserModel user, Predicate<GroupModel> eval) { private boolean evaluateHierarchy(UserModel user, Predicate<GroupModel> eval) {
Set<GroupModel> visited = new HashSet<>(); Set<GroupModel> visited = new HashSet<>();
for (GroupModel group : user.getGroups()) { return user.getGroupsStream().anyMatch(group -> evaluateHierarchy(eval, group, visited));
if (evaluateHierarchy(eval, group, visited)) return true;
}
return false;
} }
private boolean evaluateHierarchy(Predicate<GroupModel> eval, GroupModel group, Set<GroupModel> 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.jboss.logging.Logger;
import org.keycloak.executors.ExecutorsProvider; import org.keycloak.executors.ExecutorsProvider;
import org.keycloak.models.GroupModel;
import org.keycloak.models.KeycloakSession; 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.Callable;
import java.util.concurrent.ExecutionException; 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 @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)); Set<String> set = userGroups.get(getUserIdInMap(realm, userId));
if (set == null) { if (set == null) {
return Collections.EMPTY_SET; return Stream.empty();
} }
return set.stream() return set.stream().map(realm::getGroupById);
.map(realm::getGroupById)
.collect(Collectors.toSet());
} }
@Override @Override

View file

@ -150,7 +150,7 @@ public class LDAPGroupMapper2WaySyncTest extends AbstractLDAPTest {
GroupModel kcGroup12 = KeycloakModelUtils.findGroupByPath(realm, "/group12"); GroupModel kcGroup12 = KeycloakModelUtils.findGroupByPath(realm, "/group12");
GroupModel kcGroup2 = KeycloakModelUtils.findGroupByPath(realm, "/group2"); 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.assertEquals("group1 - description1", kcGroup1.getFirstAttribute(descriptionAttrName));
Assert.assertNull(kcGroup11.getFirstAttribute(descriptionAttrName)); Assert.assertNull(kcGroup11.getFirstAttribute(descriptionAttrName));
@ -212,7 +212,7 @@ public class LDAPGroupMapper2WaySyncTest extends AbstractLDAPTest {
GroupModel kcGroup12 = KeycloakModelUtils.findGroupByPath(realm, "/group1/group12"); GroupModel kcGroup12 = KeycloakModelUtils.findGroupByPath(realm, "/group1/group12");
GroupModel kcGroup2 = KeycloakModelUtils.findGroupByPath(realm, "/group2"); 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.assertEquals("group1 - description1", kcGroup1.getFirstAttribute(descriptionAttrName));
Assert.assertNull(kcGroup11.getFirstAttribute(descriptionAttrName)); Assert.assertNull(kcGroup11.getFirstAttribute(descriptionAttrName));
@ -226,9 +226,7 @@ public class LDAPGroupMapper2WaySyncTest extends AbstractLDAPTest {
private static void removeAllModelGroups(RealmModel appRealm) { private static void removeAllModelGroups(RealmModel appRealm) {
for (GroupModel group : appRealm.getTopLevelGroups()) { appRealm.getTopLevelGroupsStream().forEach(appRealm::removeGroup);
appRealm.removeGroup(group);
}
} }
private static void testDropNonExisting(KeycloakSession session, LDAPTestContext ctx, ComponentModel mapperModel) { 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.Date;
import java.util.List; import java.util.List;
import java.util.Set; import java.util.Set;
import java.util.stream.Collectors;
import static org.keycloak.testsuite.util.LDAPTestUtils.getGroupDescriptionLDAPAttrName; import static org.keycloak.testsuite.util.LDAPTestUtils.getGroupDescriptionLDAPAttrName;
@ -105,10 +106,7 @@ public class LDAPGroupMapperSyncTest extends AbstractLDAPTest {
LDAPTestContext ctx = LDAPTestContext.init(session); LDAPTestContext ctx = LDAPTestContext.init(session);
RealmModel realm = ctx.getRealm(); RealmModel realm = ctx.getRealm();
List<GroupModel> kcGroups = realm.getTopLevelGroups(); realm.getTopLevelGroupsStream().forEach(realm::removeGroup);
for (GroupModel kcGroup : kcGroups) {
realm.removeGroup(kcGroup);
}
}); });
} }
@ -169,7 +167,7 @@ public class LDAPGroupMapperSyncTest extends AbstractLDAPTest {
GroupModel kcGroup11 = KeycloakModelUtils.findGroupByPath(realm, "/group11"); GroupModel kcGroup11 = KeycloakModelUtils.findGroupByPath(realm, "/group11");
GroupModel kcGroup12 = KeycloakModelUtils.findGroupByPath(realm, "/group12"); 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.assertEquals("group1 - description", kcGroup1.getFirstAttribute(descriptionAttrName));
Assert.assertNull(kcGroup11.getFirstAttribute(descriptionAttrName)); Assert.assertNull(kcGroup11.getFirstAttribute(descriptionAttrName));
@ -221,7 +219,7 @@ public class LDAPGroupMapperSyncTest extends AbstractLDAPTest {
GroupModel kcGroup11 = KeycloakModelUtils.findGroupByPath(realm, "/group1/group11"); GroupModel kcGroup11 = KeycloakModelUtils.findGroupByPath(realm, "/group1/group11");
GroupModel kcGroup12 = KeycloakModelUtils.findGroupByPath(realm, "/group1/group12"); 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.assertEquals("group1 - description", kcGroup1.getFirstAttribute(descriptionAttrName));
Assert.assertNull(kcGroup11.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/group11"));
Assert.assertNotNull(KeycloakModelUtils.findGroupByPath(realm, "/group1/group12")); 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 // Create some new groups in keycloak
GroupModel model1 = realm.createGroup("model1"); GroupModel model1 = realm.createGroup("model1");
@ -341,7 +339,7 @@ public class LDAPGroupMapperSyncTest extends AbstractLDAPTest {
// Load user from LDAP to Keycloak DB // Load user from LDAP to Keycloak DB
UserModel john = session.users().getUserByUsername("johnkeycloak", realm); 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 // Assert just those groups, which john was memberOf exists because they were lazily created
GroupModel group1 = KeycloakModelUtils.findGroupByPath(realm, "/group1"); GroupModel group1 = KeycloakModelUtils.findGroupByPath(realm, "/group1");

View file

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

View file

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

View file

@ -42,6 +42,7 @@ import javax.ws.rs.NotFoundException;
import java.io.File; import java.io.File;
import java.util.LinkedList; import java.util.LinkedList;
import java.util.List; import java.util.List;
import java.util.stream.Collectors;
import org.keycloak.testsuite.arquillian.annotation.AuthServerContainerExclude.AuthServer; 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(attributes.getList("list1").contains("2"));
Assert.assertTrue(session.userFederatedStorage().getRequiredActions(realm, userId).contains("UPDATE_PASSWORD")); Assert.assertTrue(session.userFederatedStorage().getRequiredActions(realm, userId).contains("UPDATE_PASSWORD"));
Assert.assertTrue(session.userFederatedStorage().getRoleMappings(realm, userId).contains(role)); 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); List<CredentialModel> creds = session.userFederatedStorage().getStoredCredentials(realm, userId);
Assert.assertEquals(1, creds.size()); Assert.assertEquals(1, creds.size());
Assert.assertTrue(FederatedStorageExportImportTest.getHashProvider(session, realm.getPasswordPolicy()) 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(attributes.getList("list1").contains("2"));
Assert.assertTrue(session.userFederatedStorage().getRequiredActions(realm, userId).contains("UPDATE_PASSWORD")); Assert.assertTrue(session.userFederatedStorage().getRequiredActions(realm, userId).contains("UPDATE_PASSWORD"));
Assert.assertTrue(session.userFederatedStorage().getRoleMappings(realm, userId).contains(role)); 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)); Assert.assertEquals(50, session.userFederatedStorage().getNotBeforeOfUser(realm, userId));
List<CredentialModel> creds = session.userFederatedStorage().getStoredCredentials(realm, userId); List<CredentialModel> creds = session.userFederatedStorage().getStoredCredentials(realm, userId);
Assert.assertEquals(1, creds.size()); Assert.assertEquals(1, creds.size());

View file

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