From aa4673517308a2f6b8214b192eb2151554564718 Mon Sep 17 00:00:00 2001 From: Stefan Guilhen Date: Tue, 20 Oct 2020 14:55:47 -0300 Subject: [PATCH] [KEYCLOAK-15200] Complement methods for accessing users with Stream variants --- .../storage/ldap/LDAPStorageProvider.java | 133 ++++----- .../mappers/FullNameLDAPStorageMapper.java | 9 +- .../mappers/HardcodedAttributeMapper.java | 8 +- .../UserAttributeLDAPStorageMapper.java | 10 +- .../MSADUserAccountControlStorageMapper.java | 13 +- ...SADLDSUserAccountControlStorageMapper.java | 10 +- .../models/cache/infinispan/UserAdapter.java | 24 +- .../cache/infinispan/UserCacheSession.java | 89 +++--- .../cache/infinispan/entities/CachedUser.java | 2 +- .../keycloak/models/jpa/JpaUserProvider.java | 172 +++++------- .../org/keycloak/models/jpa/UserAdapter.java | 34 +-- .../jpa/JpaUserFederatedStorageProvider.java | 107 +++---- .../storage/adapter/InMemoryUserAdapter.java | 13 +- .../java/org/keycloak/models/UserModel.java | 45 ++- .../org/keycloak/models/UserProvider.java | 78 +++++- .../models/utils/UserModelDelegate.java | 8 +- .../storage/adapter/AbstractUserAdapter.java | 13 +- .../AbstractUserAdapterFederatedStorage.java | 13 +- .../UserAttributeFederatedStorage.java | 21 +- .../UserBrokerLinkFederatedStorage.java | 21 +- .../UserConsentFederatedStorage.java | 21 +- .../UserFederatedStorageProvider.java | 21 +- .../UserFederatedUserCredentialStore.java | 40 ++- .../UserGroupMembershipFederatedStorage.java | 20 +- .../UserRequiredActionsFederatedStorage.java | 20 +- .../UserRoleMappingsFederatedStorage.java | 10 + .../storage/user/UserQueryProvider.java | 265 +++++++++++++++--- .../broker/IdpReviewProfileAuthenticator.java | 5 +- .../keycloak/storage/UserStorageManager.java | 153 +++++----- .../BackwardsCompatibilityUserStorage.java | 42 ++- .../FailableHardcodedStorageProvider.java | 72 ++--- .../testsuite/federation/UserMapStorage.java | 93 +++--- .../federation/UserPropertyFileStorage.java | 78 ++---- .../testsuite/util/LDAPTestUtils.java | 24 +- .../ldap/LDAPBinaryAttributesTest.java | 15 +- .../LdapManyObjectsInitializerCommand.java | 31 +- 36 files changed, 998 insertions(+), 735 deletions(-) diff --git a/federation/ldap/src/main/java/org/keycloak/storage/ldap/LDAPStorageProvider.java b/federation/ldap/src/main/java/org/keycloak/storage/ldap/LDAPStorageProvider.java index 0224b0a841..45d34e572f 100755 --- a/federation/ldap/src/main/java/org/keycloak/storage/ldap/LDAPStorageProvider.java +++ b/federation/ldap/src/main/java/org/keycloak/storage/ldap/LDAPStorageProvider.java @@ -21,12 +21,12 @@ import java.util.ArrayList; import java.util.Collections; import java.util.HashMap; import java.util.HashSet; -import java.util.LinkedList; import java.util.List; import java.util.Map; import java.util.Set; import java.util.concurrent.atomic.AtomicReference; -import java.util.stream.Collectors; +import java.util.function.Predicate; +import java.util.stream.Stream; import javax.naming.AuthenticationException; @@ -247,7 +247,7 @@ public class LDAPStorageProvider implements UserStorageProvider, } @Override - public List searchForUserByUserAttribute(String attrName, String attrValue, RealmModel realm) { + public Stream searchForUserByUserAttributeStream(String attrName, String attrValue, RealmModel realm) { try (LDAPQuery ldapQuery = LDAPUtils.createQueryForUserSearch(this, realm)) { LDAPQueryConditionsBuilder conditionsBuilder = new LDAPQueryConditionsBuilder(); @@ -256,24 +256,15 @@ public class LDAPStorageProvider implements UserStorageProvider, List ldapObjects = ldapQuery.getResultList(); - if (ldapObjects == null || ldapObjects.isEmpty()) { - return Collections.emptyList(); - } - - List searchResults = new LinkedList<>(); - - for (LDAPObject ldapUser : ldapObjects) { + return ldapObjects.stream().map(ldapUser -> { String ldapUsername = LDAPUtils.getUsername(ldapUser, this.ldapIdentityStore.getConfig()); UserModel localUser = session.userLocalStorage().getUserByUsername(ldapUsername, realm); if (localUser == null) { - UserModel imported = importUserFromLDAP(session, realm, ldapUser); - searchResults.add(imported); + return importUserFromLDAP(session, realm, ldapUser); } else { - searchResults.add(proxy(realm, localUser, ldapUser, false)); + return proxy(realm, localUser, ldapUser, false); } - } - - return searchResults; + }); } } @@ -348,34 +339,34 @@ public class LDAPStorageProvider implements UserStorageProvider, } @Override - public List getUsers(RealmModel realm) { - return Collections.EMPTY_LIST; + public Stream getUsersStream(RealmModel realm) { + return Stream.empty(); } @Override - public List getUsers(RealmModel realm, int firstResult, int maxResults) { - return Collections.EMPTY_LIST; + public Stream getUsersStream(RealmModel realm, int firstResult, int maxResults) { + return Stream.empty(); } @Override - public List searchForUser(String search, RealmModel realm) { - return searchForUser(search, realm, 0, Integer.MAX_VALUE - 1); + public Stream searchForUserStream(String search, RealmModel realm) { + return searchForUserStream(search, realm, 0, Integer.MAX_VALUE - 1); } @Override - public List searchForUser(String search, RealmModel realm, int firstResult, int maxResults) { + public Stream searchForUserStream(String search, RealmModel realm, int firstResult, int maxResults) { Map attributes = new HashMap(); attributes.put(UserModel.SEARCH,search); - return searchForUser(attributes, realm, firstResult, maxResults); + return searchForUserStream(attributes, realm, firstResult, maxResults); } @Override - public List searchForUser(Map params, RealmModel realm) { - return searchForUser(params, realm, 0, Integer.MAX_VALUE - 1); + public Stream searchForUserStream(Map params, RealmModel realm) { + return searchForUserStream(params, realm, 0, Integer.MAX_VALUE - 1); } @Override - public List searchForUser(Map params, RealmModel realm, int firstResult, int maxResults) { + public Stream searchForUserStream(Map params, RealmModel realm, int firstResult, int maxResults) { String search = params.get(UserModel.SEARCH); if(search!=null) { int spaceIndex = search.lastIndexOf(' '); @@ -393,65 +384,47 @@ public class LDAPStorageProvider implements UserStorageProvider, } } - List searchResults =new LinkedList(); - - List ldapUsers = searchLDAP(realm, params, maxResults + firstResult); - int counter = 0; - for (LDAPObject ldapUser : ldapUsers) { - if (counter++ < firstResult) continue; - String ldapUsername = LDAPUtils.getUsername(ldapUser, this.ldapIdentityStore.getConfig()); - if (session.userLocalStorage().getUserByUsername(ldapUsername, realm) == null) { - UserModel imported = importUserFromLDAP(session, realm, ldapUser); - searchResults.add(imported); - } - } - - return searchResults; + Stream stream = searchLDAP(realm, params).stream() + .filter(ldapObject -> { + String ldapUsername = LDAPUtils.getUsername(ldapObject, this.ldapIdentityStore.getConfig()); + return (session.userLocalStorage().getUserByUsername(ldapUsername, realm) == null); + }); + if (firstResult > 0) + stream = stream.skip(firstResult); + if (maxResults >= 0) + stream = stream.limit(maxResults); + return stream.map(ldapObject -> importUserFromLDAP(session, realm, ldapObject)); } @Override - public List getGroupMembers(RealmModel realm, GroupModel group) { - return getGroupMembers(realm, group, 0, Integer.MAX_VALUE - 1); + public Stream getGroupMembersStream(RealmModel realm, GroupModel group) { + return getGroupMembersStream(realm, group, 0, Integer.MAX_VALUE - 1); } @Override - public List getGroupMembers(RealmModel realm, GroupModel group, int firstResult, int maxResults) { - List sortedMappers = realm.getComponentsStream(model.getId(), LDAPStorageMapper.class.getName()) + public Stream getGroupMembersStream(RealmModel realm, GroupModel group, int firstResult, int maxResults) { + return realm.getComponentsStream(model.getId(), LDAPStorageMapper.class.getName()) + .sorted(ldapMappersComparator.sortAsc()) + .map(mapperModel -> + mapperManager.getMapper(mapperModel).getGroupMembers(realm, group, firstResult, maxResults)) + .filter(((Predicate) List::isEmpty).negate()) + .map(List::stream) + .findFirst().orElse(Stream.empty()); + } + + @Override + public Stream getRoleMembersStream(RealmModel realm, RoleModel role) { + return getRoleMembersStream(realm, role, 0, Integer.MAX_VALUE - 1); + } + + @Override + public Stream getRoleMembersStream(RealmModel realm, RoleModel role, int firstResult, int maxResults) { + return realm.getComponentsStream(model.getId(), LDAPStorageMapper.class.getName()) .sorted(ldapMappersComparator.sortAsc()) - .collect(Collectors.toList()); - - for (ComponentModel mapperModel : sortedMappers) { - LDAPStorageMapper ldapMapper = mapperManager.getMapper(mapperModel); - List users = ldapMapper.getGroupMembers(realm, group, firstResult, maxResults); - - // Sufficient for now - if (users.size() > 0) { - return users; - } - } - return Collections.emptyList(); - } - - @Override - public List getRoleMembers(RealmModel realm, RoleModel role) { - return getRoleMembers(realm, role, 0, Integer.MAX_VALUE - 1); - } - - @Override - public List getRoleMembers(RealmModel realm, RoleModel role, int firstResult, int maxResults) { - List sortedMappers = realm.getComponentsStream(model.getId(), LDAPStorageMapper.class.getName()) - .sorted(ldapMappersComparator.sortAsc()) - .collect(Collectors.toList()); - for (ComponentModel mapperModel : sortedMappers) { - LDAPStorageMapper ldapMapper = mapperManager.getMapper(mapperModel); - List users = ldapMapper.getRoleMembers(realm, role, firstResult, maxResults); - - // Sufficient for now - if (users.size() > 0) { - return users; - } - } - return Collections.emptyList(); + .map(mapperModel -> mapperManager.getMapper(mapperModel).getRoleMembers(realm, role, firstResult, maxResults)) + .filter(((Predicate) List::isEmpty).negate()) + .map(List::stream) + .findFirst().orElse(Stream.empty()); } public List loadUsersByUsernames(List usernames, RealmModel realm) { @@ -469,7 +442,7 @@ public class LDAPStorageProvider implements UserStorageProvider, return result; } - protected List searchLDAP(RealmModel realm, Map attributes, int maxResults) { + protected List searchLDAP(RealmModel realm, Map attributes) { List results = new ArrayList(); if (attributes.containsKey(UserModel.USERNAME)) { diff --git a/federation/ldap/src/main/java/org/keycloak/storage/ldap/mappers/FullNameLDAPStorageMapper.java b/federation/ldap/src/main/java/org/keycloak/storage/ldap/mappers/FullNameLDAPStorageMapper.java index 4fa689503d..98bb9afe4a 100644 --- a/federation/ldap/src/main/java/org/keycloak/storage/ldap/mappers/FullNameLDAPStorageMapper.java +++ b/federation/ldap/src/main/java/org/keycloak/storage/ldap/mappers/FullNameLDAPStorageMapper.java @@ -34,6 +34,7 @@ import java.util.HashSet; import java.util.List; import java.util.Map; import java.util.Set; +import java.util.stream.Stream; /** * Mapper useful for the LDAP deployments when some attribute (usually CN) is mapped to full name of user @@ -108,13 +109,13 @@ public class FullNameLDAPStorageMapper extends AbstractLDAPStorageMapper { } @Override - public List getAttribute(String name) { + public Stream getAttributeStream(String name) { if (UserModel.FIRST_NAME.equals(name)) { - return firstName != null ? Collections.singletonList(firstName) : super.getAttribute(name); + return firstName != null ? Stream.of(firstName) : super.getAttributeStream(name); } else if (UserModel.LAST_NAME.equals(name)) { - return lastName != null ? Collections.singletonList(lastName) : super.getAttribute(name); + return lastName != null ? Stream.of(lastName) : super.getAttributeStream(name); } - return super.getAttribute(name); + return super.getAttributeStream(name); } @Override diff --git a/federation/ldap/src/main/java/org/keycloak/storage/ldap/mappers/HardcodedAttributeMapper.java b/federation/ldap/src/main/java/org/keycloak/storage/ldap/mappers/HardcodedAttributeMapper.java index 7f60c1f128..fa29b56b11 100644 --- a/federation/ldap/src/main/java/org/keycloak/storage/ldap/mappers/HardcodedAttributeMapper.java +++ b/federation/ldap/src/main/java/org/keycloak/storage/ldap/mappers/HardcodedAttributeMapper.java @@ -18,8 +18,8 @@ package org.keycloak.storage.ldap.mappers; import java.util.Arrays; -import java.util.List; import java.util.Map; +import java.util.stream.Stream; import org.jboss.logging.Logger; import org.keycloak.component.ComponentModel; @@ -73,11 +73,11 @@ public class HardcodedAttributeMapper extends AbstractLDAPStorageMapper { delegate = new UserModelDelegate(delegate) { @Override - public List getAttribute(String name) { + public Stream getAttributeStream(String name) { if(userModelAttrName.equals(name)){ - return Arrays.asList(attributeValue); + return Stream.of(attributeValue); } - return super.getAttribute(name); + return super.getAttributeStream(name); } @Override diff --git a/federation/ldap/src/main/java/org/keycloak/storage/ldap/mappers/UserAttributeLDAPStorageMapper.java b/federation/ldap/src/main/java/org/keycloak/storage/ldap/mappers/UserAttributeLDAPStorageMapper.java index 4754293891..17fe3e381f 100644 --- a/federation/ldap/src/main/java/org/keycloak/storage/ldap/mappers/UserAttributeLDAPStorageMapper.java +++ b/federation/ldap/src/main/java/org/keycloak/storage/ldap/mappers/UserAttributeLDAPStorageMapper.java @@ -37,12 +37,12 @@ import org.keycloak.storage.ldap.idm.query.internal.LDAPQuery; import java.util.ArrayList; import java.util.Collection; -import java.util.Collections; import java.util.HashMap; import java.util.LinkedHashSet; import java.util.List; import java.util.Map; import java.util.Set; +import java.util.stream.Stream; /** * @author Marek Posolda @@ -338,16 +338,16 @@ public class UserAttributeLDAPStorageMapper extends AbstractLDAPStorageMapper { } @Override - public List getAttribute(String name) { + public Stream getAttributeStream(String name) { if (name.equalsIgnoreCase(userModelAttrName)) { Collection ldapAttrValue = ldapUser.getAttributeAsSet(ldapAttrName); if (ldapAttrValue == null) { - return Collections.emptyList(); + return Stream.empty(); } else { - return new ArrayList<>(ldapAttrValue); + return ldapAttrValue.stream(); } } else { - return super.getAttribute(name); + return super.getAttributeStream(name); } } diff --git a/federation/ldap/src/main/java/org/keycloak/storage/ldap/mappers/msad/MSADUserAccountControlStorageMapper.java b/federation/ldap/src/main/java/org/keycloak/storage/ldap/mappers/msad/MSADUserAccountControlStorageMapper.java index a8e0476023..b76ecf641a 100644 --- a/federation/ldap/src/main/java/org/keycloak/storage/ldap/mappers/msad/MSADUserAccountControlStorageMapper.java +++ b/federation/ldap/src/main/java/org/keycloak/storage/ldap/mappers/msad/MSADUserAccountControlStorageMapper.java @@ -38,6 +38,7 @@ import java.util.HashSet; import java.util.Set; import java.util.regex.Matcher; import java.util.regex.Pattern; +import java.util.stream.Stream; /** * Mapper specific to MSAD. It's able to read the userAccountControl and pwdLastSet attributes and set actions in Keycloak based on that. @@ -299,18 +300,14 @@ public class MSADUserAccountControlStorageMapper extends AbstractLDAPStorageMapp } @Override - public Set getRequiredActions() { - Set requiredActions = super.getRequiredActions(); - + public Stream getRequiredActionsStream() { if (ldapProvider.getEditMode() == UserStorageProvider.EditMode.WRITABLE) { if (getPwdLastSet() == 0 || getUserAccountControl(ldapUser).has(UserAccountControl.PASSWORD_EXPIRED)) { - requiredActions = new HashSet<>(requiredActions); - requiredActions.add(RequiredAction.UPDATE_PASSWORD.toString()); - return requiredActions; + return Stream.concat(super.getRequiredActionsStream(), Stream.of(RequiredAction.UPDATE_PASSWORD.toString())) + .distinct(); } } - - return requiredActions; + return super.getRequiredActionsStream(); } protected long getPwdLastSet() { diff --git a/federation/ldap/src/main/java/org/keycloak/storage/ldap/mappers/msadlds/MSADLDSUserAccountControlStorageMapper.java b/federation/ldap/src/main/java/org/keycloak/storage/ldap/mappers/msadlds/MSADLDSUserAccountControlStorageMapper.java index efb1f45231..a02e853000 100644 --- a/federation/ldap/src/main/java/org/keycloak/storage/ldap/mappers/msadlds/MSADLDSUserAccountControlStorageMapper.java +++ b/federation/ldap/src/main/java/org/keycloak/storage/ldap/mappers/msadlds/MSADLDSUserAccountControlStorageMapper.java @@ -38,6 +38,7 @@ import java.util.HashSet; import java.util.Set; import java.util.regex.Matcher; import java.util.regex.Pattern; +import java.util.stream.Stream; /** * Mapper specific to MSAD LDS. It's able to read the msDS-UserAccountDisabled, msDS-UserPasswordExpired and pwdLastSet attributes and set actions in Keycloak based on that. @@ -257,17 +258,14 @@ public class MSADLDSUserAccountControlStorageMapper extends AbstractLDAPStorageM } @Override - public Set getRequiredActions() { - Set requiredActions = super.getRequiredActions(); + public Stream getRequiredActionsStream() { + Stream requiredActions = super.getRequiredActionsStream(); if (ldapProvider.getEditMode() == UserStorageProvider.EditMode.WRITABLE) { if (getPwdLastSet() == 0 || Boolean.parseBoolean(ldapUser.getAttributeAsString(LDAPConstants.MSDS_USER_PASSWORD_EXPIRED))) { - requiredActions = new HashSet<>(requiredActions); - requiredActions.add(RequiredAction.UPDATE_PASSWORD.toString()); - return requiredActions; + return Stream.concat(requiredActions, Stream.of(RequiredAction.UPDATE_PASSWORD.toString())).distinct(); } } - return requiredActions; } diff --git a/model/infinispan/src/main/java/org/keycloak/models/cache/infinispan/UserAdapter.java b/model/infinispan/src/main/java/org/keycloak/models/cache/infinispan/UserAdapter.java index aa2850afa4..157397c875 100755 --- a/model/infinispan/src/main/java/org/keycloak/models/cache/infinispan/UserAdapter.java +++ b/model/infinispan/src/main/java/org/keycloak/models/cache/infinispan/UserAdapter.java @@ -178,7 +178,7 @@ public class UserAdapter implements CachedUserModel { getDelegateForUpdate(); if (UserModel.USERNAME.equals(name) || UserModel.EMAIL.equals(name)) { String lowerCasedFirstValue = KeycloakModelUtils.toLowerCaseSafe((values != null && values.size() > 0) ? values.get(0) : null); - if (lowerCasedFirstValue != null) values=Collections.singletonList(lowerCasedFirstValue); + if (lowerCasedFirstValue != null) values = Collections.singletonList(lowerCasedFirstValue); } updated.setAttribute(name, values); } @@ -196,10 +196,10 @@ public class UserAdapter implements CachedUserModel { } @Override - public List getAttribute(String name) { - if (updated != null) return updated.getAttribute(name); + public Stream getAttributeStream(String name) { + if (updated != null) return updated.getAttributeStream(name); List result = cached.getAttributes(modelSupplier).get(name); - return (result == null) ? Collections.emptyList() : result; + return (result == null) ? Stream.empty() : result.stream(); } @Override @@ -209,9 +209,9 @@ public class UserAdapter implements CachedUserModel { } @Override - public Set getRequiredActions() { - if (updated != null) return updated.getRequiredActions(); - return cached.getRequiredActions(modelSupplier); + public Stream getRequiredActionsStream() { + if (updated != null) return updated.getRequiredActionsStream(); + return cached.getRequiredActions(modelSupplier).stream(); } @Override @@ -289,10 +289,9 @@ public class UserAdapter implements CachedUserModel { @Override public boolean hasRole(RoleModel role) { if (updated != null) return updated.hasRole(role); - if (cached.getRoleMappings(modelSupplier).contains(role.getId())) return true; - - return getRoleMappingsStream().anyMatch(r -> r.hasRole(role)) ? - true : RoleUtils.hasRoleFromGroup(getGroupsStream(), role, true); + return cached.getRoleMappings(modelSupplier).contains(role.getId()) || + getRoleMappingsStream().anyMatch(r -> r.hasRole(role)) || + RoleUtils.hasRoleFromGroup(getGroupsStream(), role, true); } @Override @@ -363,8 +362,7 @@ public class UserAdapter implements CachedUserModel { @Override public boolean isMemberOf(GroupModel group) { if (updated != null) return updated.isMemberOf(group); - if (cached.getGroups(modelSupplier).contains(group.getId())) return true; - return RoleUtils.isMember(getGroupsStream(), group); + return cached.getGroups(modelSupplier).contains(group.getId()) || RoleUtils.isMember(getGroupsStream(), group); } @Override diff --git a/model/infinispan/src/main/java/org/keycloak/models/cache/infinispan/UserCacheSession.java b/model/infinispan/src/main/java/org/keycloak/models/cache/infinispan/UserCacheSession.java index 087b7d45a3..6a0c37e105 100755 --- a/model/infinispan/src/main/java/org/keycloak/models/cache/infinispan/UserCacheSession.java +++ b/model/infinispan/src/main/java/org/keycloak/models/cache/infinispan/UserCacheSession.java @@ -60,10 +60,12 @@ import org.keycloak.storage.client.ClientStorageProvider; import java.util.HashMap; import java.util.HashSet; -import java.util.LinkedList; import java.util.List; import java.util.Map; +import java.util.Objects; import java.util.Set; +import java.util.stream.Collectors; +import java.util.stream.Stream; /** * @author Bill Burke @@ -452,26 +454,25 @@ public class UserCacheSession implements UserCache { } @Override - public List getGroupMembers(RealmModel realm, GroupModel group, int firstResult, int maxResults) { - return getDelegate().getGroupMembers(realm, group, firstResult, maxResults); + public Stream getGroupMembersStream(RealmModel realm, GroupModel group, int firstResult, int maxResults) { + return getDelegate().getGroupMembersStream(realm, group, firstResult, maxResults); } @Override - public List getGroupMembers(RealmModel realm, GroupModel group) { - return getDelegate().getGroupMembers(realm, group); + public Stream getGroupMembersStream(RealmModel realm, GroupModel group) { + return getDelegate().getGroupMembersStream(realm, group); } @Override - public List getRoleMembers(RealmModel realm, RoleModel role, int firstResult, int maxResults) { - return getDelegate().getRoleMembers(realm, role, firstResult, maxResults); + public Stream getRoleMembersStream(RealmModel realm, RoleModel role, int firstResult, int maxResults) { + return getDelegate().getRoleMembersStream(realm, role, firstResult, maxResults); } @Override - public List getRoleMembers(RealmModel realm, RoleModel role) { - return getDelegate().getRoleMembers(realm, role); + public Stream getRoleMembersStream(RealmModel realm, RoleModel role) { + return getDelegate().getRoleMembersStream(realm, role); } - @Override public UserModel getServiceAccount(ClientModel client) { // Just an attempt to find the user from cache by default serviceAccount username @@ -534,12 +535,9 @@ public class UserCacheSession implements UserCache { } } - - - @Override - public List getUsers(RealmModel realm, boolean includeServiceAccounts) { - return getDelegate().getUsers(realm, includeServiceAccounts); + public Stream getUsersStream(RealmModel realm, boolean includeServiceAccounts) { + return getDelegate().getUsersStream(realm, includeServiceAccounts); } @Override @@ -578,64 +576,65 @@ public class UserCacheSession implements UserCache { } @Override - public List getUsers(RealmModel realm, int firstResult, int maxResults, boolean includeServiceAccounts) { - return getDelegate().getUsers(realm, firstResult, maxResults, includeServiceAccounts); + public Stream getUsersStream(RealmModel realm, int firstResult, int maxResults, boolean includeServiceAccounts) { + return getDelegate().getUsersStream(realm, firstResult, maxResults, includeServiceAccounts); } @Override - public List getUsers(RealmModel realm) { - return getUsers(realm, false); + public Stream getUsersStream(RealmModel realm) { + return getUsersStream(realm, false); } @Override - public List getUsers(RealmModel realm, int firstResult, int maxResults) { - return getUsers(realm, firstResult, maxResults, false); + public Stream getUsersStream(RealmModel realm, int firstResult, int maxResults) { + return getUsersStream(realm, firstResult, maxResults, false); } @Override - public List searchForUser(String search, RealmModel realm) { - return getDelegate().searchForUser(search, realm); + public Stream searchForUserStream(String search, RealmModel realm) { + return getDelegate().searchForUserStream(search, realm); } @Override - public List searchForUser(String search, RealmModel realm, int firstResult, int maxResults) { - return getDelegate().searchForUser(search, realm, firstResult, maxResults); + public Stream searchForUserStream(String search, RealmModel realm, int firstResult, int maxResults) { + return getDelegate().searchForUserStream(search, realm, firstResult, maxResults); } @Override - public List searchForUser(Map attributes, RealmModel realm) { - return getDelegate().searchForUser(attributes, realm); + public Stream searchForUserStream(Map attributes, RealmModel realm) { + return getDelegate().searchForUserStream(attributes, realm); } @Override - public List searchForUser(Map attributes, RealmModel realm, int firstResult, int maxResults) { - return getDelegate().searchForUser(attributes, realm, firstResult, maxResults); + public Stream searchForUserStream(Map attributes, RealmModel realm, int firstResult, int maxResults) { + return getDelegate().searchForUserStream(attributes, realm, firstResult, maxResults); } @Override - public List searchForUserByUserAttribute(String attrName, String attrValue, RealmModel realm) { - return getDelegate().searchForUserByUserAttribute(attrName, attrValue, realm); + public Stream searchForUserByUserAttributeStream(String attrName, String attrValue, RealmModel realm) { + return getDelegate().searchForUserByUserAttributeStream(attrName, attrValue, realm); } @Override - public Set getFederatedIdentities(UserModel user, RealmModel realm) { + public Stream getFederatedIdentitiesStream(UserModel user, RealmModel realm) { logger.tracev("getFederatedIdentities: {0}", user.getUsername()); String cacheKey = getFederatedIdentityLinksCacheKey(user.getId()); if (realmInvalidations.contains(realm.getId()) || invalidations.contains(user.getId()) || invalidations.contains(cacheKey)) { - return getDelegate().getFederatedIdentities(user, realm); + return getDelegate().getFederatedIdentitiesStream(user, realm); } CachedFederatedIdentityLinks cachedLinks = cache.get(cacheKey, CachedFederatedIdentityLinks.class); if (cachedLinks == null) { Long loaded = cache.getCurrentRevision(cacheKey); - Set federatedIdentities = getDelegate().getFederatedIdentities(user, realm); + Set federatedIdentities = getDelegate().getFederatedIdentitiesStream(user, realm) + .collect(Collectors.toSet()); cachedLinks = new CachedFederatedIdentityLinks(loaded, cacheKey, realm, federatedIdentities); cache.addRevisioned(cachedLinks, startupRevision); - return federatedIdentities; + return federatedIdentities.stream(); } else { - return new HashSet<>(cachedLinks.getFederatedIdentities()); + return cachedLinks.getFederatedIdentities().stream(); } } @@ -708,31 +707,25 @@ public class UserCacheSession implements UserCache { } @Override - public List getConsents(RealmModel realm, String userId) { + public Stream getConsentsStream(RealmModel realm, String userId) { logger.tracev("getConsents: {0}", userId); String cacheKey = getConsentCacheKey(userId); if (realmInvalidations.contains(realm.getId()) || invalidations.contains(userId) || invalidations.contains(cacheKey)) { - return getDelegate().getConsents(realm, userId); + return getDelegate().getConsentsStream(realm, userId); } CachedUserConsents cached = cache.get(cacheKey, CachedUserConsents.class); if (cached == null) { Long loaded = cache.getCurrentRevision(cacheKey); - List consents = getDelegate().getConsents(realm, userId); + List consents = getDelegate().getConsentsStream(realm, userId).collect(Collectors.toList()); cached = new CachedUserConsents(loaded, cacheKey, realm, consents); cache.addRevisioned(cached, startupRevision); - return consents; + return consents.stream(); } else { - List result = new LinkedList<>(); - for (CachedUserConsent cachedConsent : cached.getConsents().values()) { - UserConsentModel consent = toConsentModel(realm, cachedConsent); - if (consent != null) { - result.add(consent); - } - } - return result; + return cached.getConsents().values().stream().map(cachedConsent -> toConsentModel(realm, cachedConsent)) + .filter(Objects::nonNull); } } diff --git a/model/infinispan/src/main/java/org/keycloak/models/cache/infinispan/entities/CachedUser.java b/model/infinispan/src/main/java/org/keycloak/models/cache/infinispan/entities/CachedUser.java index 68433cd5dc..206c7ef62f 100755 --- a/model/infinispan/src/main/java/org/keycloak/models/cache/infinispan/entities/CachedUser.java +++ b/model/infinispan/src/main/java/org/keycloak/models/cache/infinispan/entities/CachedUser.java @@ -62,7 +62,7 @@ public class CachedUser extends AbstractExtendableRevisioned implements InRealm this.federationLink = user.getFederationLink(); this.serviceAccountClientLink = user.getServiceAccountClientLink(); this.notBefore = notBefore; - this.requiredActions = new DefaultLazyLoader<>(UserModel::getRequiredActions, Collections::emptySet); + this.requiredActions = new DefaultLazyLoader<>(userModel -> userModel.getRequiredActionsStream().collect(Collectors.toSet()), Collections::emptySet); this.attributes = new DefaultLazyLoader<>(userModel -> new MultivaluedHashMap<>(userModel.getAttributes()), MultivaluedHashMap::new); this.roleMappings = new DefaultLazyLoader<>(userModel -> userModel.getRoleMappingsStream().map(RoleModel::getId).collect(Collectors.toSet()), Collections::emptySet); this.groups = new DefaultLazyLoader<>(userModel -> userModel.getGroupsStream().map(GroupModel::getId).collect(Collectors.toCollection(LinkedHashSet::new)), LinkedHashSet::new); diff --git a/model/jpa/src/main/java/org/keycloak/models/jpa/JpaUserProvider.java b/model/jpa/src/main/java/org/keycloak/models/jpa/JpaUserProvider.java index fa7b81661a..348009b9d9 100755 --- a/model/jpa/src/main/java/org/keycloak/models/jpa/JpaUserProvider.java +++ b/model/jpa/src/main/java/org/keycloak/models/jpa/JpaUserProvider.java @@ -58,10 +58,25 @@ import javax.persistence.criteria.Join; import javax.persistence.criteria.Predicate; import javax.persistence.criteria.Root; import javax.persistence.criteria.Subquery; -import java.util.*; + +import java.util.ArrayList; +import java.util.Collection; +import java.util.Comparator; +import java.util.HashMap; +import java.util.HashSet; +import java.util.LinkedList; +import java.util.List; +import java.util.Map; +import java.util.Optional; +import java.util.Set; import java.util.stream.Collectors; +import java.util.stream.Stream; + import javax.persistence.LockModeType; +import static org.keycloak.utils.StreamsUtil.closing; + + /** * @author Bill Burke * @version $Revision: 1 $ @@ -232,17 +247,10 @@ public class JpaUserProvider implements UserProvider, UserCredentialStore { } @Override - public List getConsents(RealmModel realm, String userId) { + public Stream getConsentsStream(RealmModel realm, String userId) { TypedQuery query = em.createNamedQuery("userConsentsByUser", UserConsentEntity.class); query.setParameter("userId", userId); - List results = query.getResultList(); - - List consents = new ArrayList(); - for (UserConsentEntity entity : results) { - UserConsentModel model = toConsentModel(realm, entity); - consents.add(model); - } - return consents; + return closing(query.getResultStream().map(entity -> toConsentModel(realm, entity))); } @Override @@ -369,7 +377,7 @@ public class JpaUserProvider implements UserProvider, UserCredentialStore { @Override public void grantToAllUsers(RealmModel realm, RoleModel role) { if (realm.equals(role.isClientRole() ? ((ClientModel)role.getContainer()).getRealm() : (RealmModel)role.getContainer())) { - int num = em.createNamedQuery("grantRoleToAllUsers") + em.createNamedQuery("grantRoleToAllUsers") .setParameter("realmId", realm.getId()) .setParameter("roleId", role.getId()) .executeUpdate(); @@ -378,61 +386,61 @@ public class JpaUserProvider implements UserProvider, UserCredentialStore { @Override public void preRemove(RealmModel realm) { - int num = em.createNamedQuery("deleteUserConsentClientScopesByRealm") + em.createNamedQuery("deleteUserConsentClientScopesByRealm") .setParameter("realmId", realm.getId()).executeUpdate(); - num = em.createNamedQuery("deleteUserConsentsByRealm") + em.createNamedQuery("deleteUserConsentsByRealm") .setParameter("realmId", realm.getId()).executeUpdate(); - num = em.createNamedQuery("deleteUserRoleMappingsByRealm") + em.createNamedQuery("deleteUserRoleMappingsByRealm") .setParameter("realmId", realm.getId()).executeUpdate(); - num = em.createNamedQuery("deleteUserRequiredActionsByRealm") + em.createNamedQuery("deleteUserRequiredActionsByRealm") .setParameter("realmId", realm.getId()).executeUpdate(); - num = em.createNamedQuery("deleteFederatedIdentityByRealm") + em.createNamedQuery("deleteFederatedIdentityByRealm") .setParameter("realmId", realm.getId()).executeUpdate(); - num = em.createNamedQuery("deleteCredentialsByRealm") + em.createNamedQuery("deleteCredentialsByRealm") .setParameter("realmId", realm.getId()).executeUpdate(); - num = em.createNamedQuery("deleteUserAttributesByRealm") + em.createNamedQuery("deleteUserAttributesByRealm") .setParameter("realmId", realm.getId()).executeUpdate(); - num = em.createNamedQuery("deleteUserGroupMembershipByRealm") + em.createNamedQuery("deleteUserGroupMembershipByRealm") .setParameter("realmId", realm.getId()).executeUpdate(); - num = em.createNamedQuery("deleteUsersByRealm") + em.createNamedQuery("deleteUsersByRealm") .setParameter("realmId", realm.getId()).executeUpdate(); } @Override public void removeImportedUsers(RealmModel realm, String storageProviderId) { - int num = em.createNamedQuery("deleteUserRoleMappingsByRealmAndLink") + em.createNamedQuery("deleteUserRoleMappingsByRealmAndLink") .setParameter("realmId", realm.getId()) .setParameter("link", storageProviderId) .executeUpdate(); - num = em.createNamedQuery("deleteUserRequiredActionsByRealmAndLink") + em.createNamedQuery("deleteUserRequiredActionsByRealmAndLink") .setParameter("realmId", realm.getId()) .setParameter("link", storageProviderId) .executeUpdate(); - num = em.createNamedQuery("deleteFederatedIdentityByRealmAndLink") + em.createNamedQuery("deleteFederatedIdentityByRealmAndLink") .setParameter("realmId", realm.getId()) .setParameter("link", storageProviderId) .executeUpdate(); - num = em.createNamedQuery("deleteCredentialsByRealmAndLink") + em.createNamedQuery("deleteCredentialsByRealmAndLink") .setParameter("realmId", realm.getId()) .setParameter("link", storageProviderId) .executeUpdate(); - num = em.createNamedQuery("deleteUserAttributesByRealmAndLink") + em.createNamedQuery("deleteUserAttributesByRealmAndLink") .setParameter("realmId", realm.getId()) .setParameter("link", storageProviderId) .executeUpdate(); - num = em.createNamedQuery("deleteUserGroupMembershipsByRealmAndLink") + em.createNamedQuery("deleteUserGroupMembershipsByRealmAndLink") .setParameter("realmId", realm.getId()) .setParameter("link", storageProviderId) .executeUpdate(); - num = em.createNamedQuery("deleteUserConsentClientScopesByRealmAndLink") + em.createNamedQuery("deleteUserConsentClientScopesByRealmAndLink") .setParameter("realmId", realm.getId()) .setParameter("link", storageProviderId) .executeUpdate(); - num = em.createNamedQuery("deleteUserConsentsByRealmAndLink") + em.createNamedQuery("deleteUserConsentsByRealmAndLink") .setParameter("realmId", realm.getId()) .setParameter("link", storageProviderId) .executeUpdate(); - num = em.createNamedQuery("deleteUsersByRealmAndLink") + em.createNamedQuery("deleteUsersByRealmAndLink") .setParameter("realmId", realm.getId()) .setParameter("link", storageProviderId) .executeUpdate(); @@ -455,10 +463,10 @@ public class JpaUserProvider implements UserProvider, UserCredentialStore { public void preRemove(RealmModel realm, ClientModel client) { StorageId clientStorageId = new StorageId(client.getId()); if (clientStorageId.isLocal()) { - int num = em.createNamedQuery("deleteUserConsentClientScopesByClient") + em.createNamedQuery("deleteUserConsentClientScopesByClient") .setParameter("clientId", client.getId()) .executeUpdate(); - num = em.createNamedQuery("deleteUserConsentsByClient") + em.createNamedQuery("deleteUserConsentsByClient") .setParameter("clientId", client.getId()) .executeUpdate(); } else { @@ -487,32 +495,19 @@ public class JpaUserProvider implements UserProvider, UserCredentialStore { } @Override - public List getGroupMembers(RealmModel realm, GroupModel group) { + public Stream getGroupMembersStream(RealmModel realm, GroupModel group) { TypedQuery query = em.createNamedQuery("groupMembership", UserEntity.class); query.setParameter("groupId", group.getId()); - List results = query.getResultList(); - - List users = new ArrayList(); - for (UserEntity user : results) { - users.add(new UserAdapter(session, realm, em, user)); - } - return users; + return closing(query.getResultStream().map(entity -> new UserAdapter(session, realm, em, entity))); } @Override - public List getRoleMembers(RealmModel realm, RoleModel role) { + public Stream getRoleMembersStream(RealmModel realm, RoleModel role) { TypedQuery query = em.createNamedQuery("usersInRole", UserEntity.class); query.setParameter("roleId", role.getId()); - List results = query.getResultList(); - - List users = new ArrayList(); - for (UserEntity user : results) { - users.add(new UserAdapter(session, realm, em, user)); - } - return users; + return closing(query.getResultStream().map(entity -> new UserAdapter(session, realm, em, entity))); } - @Override public void preRemove(RealmModel realm, GroupModel group) { em.createNamedQuery("deleteUserGroupMembershipsByGroup").setParameter("groupId", group.getId()).executeUpdate(); @@ -590,8 +585,8 @@ public class JpaUserProvider implements UserProvider, UserCredentialStore { } @Override - public List getUsers(RealmModel realm, boolean includeServiceAccounts) { - return getUsers(realm, -1, -1, includeServiceAccounts); + public Stream getUsersStream(RealmModel realm, boolean includeServiceAccounts) { + return getUsersStream(realm, -1, -1, includeServiceAccounts); } @Override @@ -746,17 +741,17 @@ public class JpaUserProvider implements UserProvider, UserCredentialStore { } @Override - public List getUsers(RealmModel realm) { - return getUsers(realm, false); + public Stream getUsersStream(RealmModel realm) { + return getUsersStream(realm, false); } @Override - public List getUsers(RealmModel realm, int firstResult, int maxResults) { - return getUsers(realm, firstResult, maxResults, false); + public Stream getUsersStream(RealmModel realm, int firstResult, int maxResults) { + return getUsersStream(realm, firstResult, maxResults, false); } @Override - public List getUsers(RealmModel realm, int firstResult, int maxResults, boolean includeServiceAccounts) { + public Stream getUsersStream(RealmModel realm, int firstResult, int maxResults, boolean includeServiceAccounts) { String queryName = includeServiceAccounts ? "getAllUsersByRealm" : "getAllUsersByRealmExcludeServiceAccount" ; TypedQuery query = em.createNamedQuery(queryName, UserEntity.class); @@ -767,14 +762,11 @@ public class JpaUserProvider implements UserProvider, UserCredentialStore { if (maxResults != -1) { query.setMaxResults(maxResults); } - List results = query.getResultList(); - List users = new LinkedList<>(); - for (UserEntity entity : results) users.add(new UserAdapter(session, realm, em, entity)); - return users; + return closing(query.getResultStream().map(entity -> new UserAdapter(session, realm, em, entity))); } @Override - public List getGroupMembers(RealmModel realm, GroupModel group, int firstResult, int maxResults) { + public Stream getGroupMembersStream(RealmModel realm, GroupModel group, int firstResult, int maxResults) { TypedQuery query = em.createNamedQuery("groupMembership", UserEntity.class); query.setParameter("groupId", group.getId()); if (firstResult != -1) { @@ -783,17 +775,11 @@ public class JpaUserProvider implements UserProvider, UserCredentialStore { if (maxResults != -1) { query.setMaxResults(maxResults); } - List results = query.getResultList(); - - List users = new LinkedList<>(); - for (UserEntity user : results) { - users.add(new UserAdapter(session, realm, em, user)); - } - return users; + return closing(query.getResultStream().map(user -> new UserAdapter(session, realm, em, user))); } @Override - public List getRoleMembers(RealmModel realm, RoleModel role, int firstResult, int maxResults) { + public Stream getRoleMembersStream(RealmModel realm, RoleModel role, int firstResult, int maxResults) { TypedQuery query = em.createNamedQuery("usersInRole", UserEntity.class); query.setParameter("roleId", role.getId()); if (firstResult != -1) { @@ -802,35 +788,29 @@ public class JpaUserProvider implements UserProvider, UserCredentialStore { if (maxResults != -1) { query.setMaxResults(maxResults); } - List results = query.getResultList(); - - List users = new LinkedList<>(); - for (UserEntity user : results) { - users.add(new UserAdapter(session, realm, em, user)); - } - return users; + return closing(query.getResultStream().map(user -> new UserAdapter(session, realm, em, user))); } @Override - public List searchForUser(String search, RealmModel realm) { - return searchForUser(search, realm, -1, -1); + public Stream searchForUserStream(String search, RealmModel realm) { + return searchForUserStream(search, realm, -1, -1); } @Override - public List searchForUser(String search, RealmModel realm, int firstResult, int maxResults) { + public Stream searchForUserStream(String search, RealmModel realm, int firstResult, int maxResults) { Map attributes = new HashMap<>(); attributes.put(UserModel.SEARCH, search); session.setAttribute(UserModel.INCLUDE_SERVICE_ACCOUNT, false); - return searchForUser(attributes, realm, firstResult, maxResults); + return searchForUserStream(attributes, realm, firstResult, maxResults); } @Override - public List searchForUser(Map attributes, RealmModel realm) { - return searchForUser(attributes, realm, -1, -1); + public Stream searchForUserStream(Map attributes, RealmModel realm) { + return searchForUserStream(attributes, realm, -1, -1); } @Override - public List searchForUser(Map attributes, RealmModel realm, int firstResult, int maxResults) { + public Stream searchForUserStream(Map attributes, RealmModel realm, int firstResult, int maxResults) { CriteriaBuilder builder = em.getCriteriaBuilder(); CriteriaQuery queryBuilder = builder.createQuery(UserEntity.class); Root root = queryBuilder.from(UserEntity.class); @@ -945,29 +925,18 @@ public class JpaUserProvider implements UserProvider, UserCredentialStore { query.setMaxResults(maxResults); } - List results = new ArrayList<>(); UserProvider users = session.users(); - - for (UserEntity entity : query.getResultList()) { - results.add(users.getUserById(entity.getId(), realm)); - } - - return results; + return closing(query.getResultStream().map(userEntity -> users.getUserById(userEntity.getId(), realm))); } @Override - public List searchForUserByUserAttribute(String attrName, String attrValue, RealmModel realm) { + public Stream searchForUserByUserAttributeStream(String attrName, String attrValue, RealmModel realm) { TypedQuery query = em.createNamedQuery("getRealmUsersByAttributeNameAndValue", UserEntity.class); query.setParameter("name", attrName); query.setParameter("value", attrValue); query.setParameter("realmId", realm.getId()); - List results = query.getResultList(); - List users = new ArrayList(); - for (UserEntity user : results) { - users.add(new UserAdapter(session, realm, em, user)); - } - return users; + return closing(query.getResultStream().map(userEntity -> new UserAdapter(session, realm, em, userEntity))); } private FederatedIdentityEntity findFederatedIdentity(UserModel user, String identityProvider, LockModeType lockMode) { @@ -982,16 +951,13 @@ public class JpaUserProvider implements UserProvider, UserCredentialStore { @Override - public Set getFederatedIdentities(UserModel user, RealmModel realm) { + public Stream getFederatedIdentitiesStream(UserModel user, RealmModel realm) { TypedQuery query = em.createNamedQuery("findFederatedIdentityByUser", FederatedIdentityEntity.class); UserEntity userEntity = em.getReference(UserEntity.class, user.getId()); query.setParameter("user", userEntity); - List results = query.getResultList(); - Set set = new HashSet(); - for (FederatedIdentityEntity entity : results) { - set.add(new FederatedIdentityModel(entity.getIdentityProvider(), entity.getUserId(), entity.getUserName(), entity.getToken())); - } - return set; + + return closing(query.getResultStream().map(entity -> new FederatedIdentityModel(entity.getIdentityProvider(), + entity.getUserId(), entity.getUserName(), entity.getToken())).distinct()); } @Override diff --git a/model/jpa/src/main/java/org/keycloak/models/jpa/UserAdapter.java b/model/jpa/src/main/java/org/keycloak/models/jpa/UserAdapter.java index c7d141d3f4..42d21d2d76 100755 --- a/model/jpa/src/main/java/org/keycloak/models/jpa/UserAdapter.java +++ b/model/jpa/src/main/java/org/keycloak/models/jpa/UserAdapter.java @@ -24,7 +24,6 @@ import org.keycloak.models.KeycloakSession; import org.keycloak.models.RealmModel; import org.keycloak.models.RoleModel; import org.keycloak.models.UserModel; -import org.keycloak.models.jpa.entities.GroupEntity; import org.keycloak.models.jpa.entities.UserAttributeEntity; import org.keycloak.models.jpa.entities.UserEntity; import org.keycloak.models.jpa.entities.UserGroupMembershipEntity; @@ -38,19 +37,13 @@ import javax.persistence.Query; import javax.persistence.TypedQuery; import javax.persistence.criteria.CriteriaBuilder; import javax.persistence.criteria.CriteriaQuery; -import javax.persistence.criteria.Join; import javax.persistence.criteria.Predicate; import javax.persistence.criteria.Root; import java.util.ArrayList; -import java.util.Collections; -import java.util.Comparator; -import java.util.HashSet; import java.util.Iterator; import java.util.List; import java.util.Map; -import java.util.Set; import java.util.Objects; -import java.util.stream.Collectors; import java.util.stream.Stream; import javax.persistence.LockModeType; @@ -238,23 +231,18 @@ public class UserAdapter implements UserModel, JpaModel { } @Override - public List getAttribute(String name) { + public Stream getAttributeStream(String name) { if (UserModel.FIRST_NAME.equals(name)) { - return Collections.singletonList(user.getFirstName()); + return Stream.of(user.getFirstName()); } else if (UserModel.LAST_NAME.equals(name)) { - return Collections.singletonList(user.getLastName()); + return Stream.of(user.getLastName()); } else if (UserModel.EMAIL.equals(name)) { - return Collections.singletonList(user.getEmail()); + return Stream.of(user.getEmail()); } else if (UserModel.USERNAME.equals(name)) { - return Collections.singletonList(user.getUsername()); + return Stream.of(user.getUsername()); } - List result = new ArrayList<>(); - for (UserAttributeEntity attr : user.getAttributes()) { - if (attr.getName().equals(name)) { - result.add(attr.getValue()); - } - } - return result; + return user.getAttributes().stream().filter(attribute -> Objects.equals(attribute.getName(), name)). + map(attribute -> attribute.getValue()); } @Override @@ -271,12 +259,8 @@ public class UserAdapter implements UserModel, JpaModel { } @Override - public Set getRequiredActions() { - Set result = new HashSet<>(); - for (UserRequiredActionEntity attr : user.getRequiredActions()) { - result.add(attr.getAction()); - } - return result; + public Stream getRequiredActionsStream() { + return user.getRequiredActions().stream().map(action -> action.getAction()).distinct(); } @Override diff --git a/model/jpa/src/main/java/org/keycloak/storage/jpa/JpaUserFederatedStorageProvider.java b/model/jpa/src/main/java/org/keycloak/storage/jpa/JpaUserFederatedStorageProvider.java index 7b3aab3d3b..ee4c4f5ca3 100644 --- a/model/jpa/src/main/java/org/keycloak/storage/jpa/JpaUserFederatedStorageProvider.java +++ b/model/jpa/src/main/java/org/keycloak/storage/jpa/JpaUserFederatedStorageProvider.java @@ -55,12 +55,11 @@ import org.keycloak.storage.jpa.entity.FederatedUserRoleMappingEntity; import javax.persistence.EntityManager; import javax.persistence.TypedQuery; -import java.util.ArrayList; import java.util.Collection; import java.util.HashSet; -import java.util.LinkedList; import java.util.List; -import java.util.Set; +import java.util.Objects; +import java.util.stream.Collectors; import java.util.stream.Stream; import javax.persistence.LockModeType; @@ -164,12 +163,12 @@ public class JpaUserFederatedStorageProvider implements } @Override - public List getUsersByUserAttribute(RealmModel realm, String name, String value) { + public Stream getUsersByUserAttributeStream(RealmModel realm, String name, String value) { TypedQuery query = em.createNamedQuery("getFederatedAttributesByNameAndValue", String.class) .setParameter("realmId", realm.getId()) .setParameter("name", name) .setParameter("value", value); - return query.getResultList(); + return closing(query.getResultStream()); } @Override @@ -242,16 +241,11 @@ public class JpaUserFederatedStorageProvider implements } @Override - public Set getFederatedIdentities(String userId, RealmModel realm) { + public Stream getFederatedIdentitiesStream(String userId, RealmModel realm) { TypedQuery query = em.createNamedQuery("findBrokerLinkByUser", BrokerLinkEntity.class) .setParameter("userId", userId); - List results = query.getResultList(); - Set set = new HashSet<>(); - for (BrokerLinkEntity entity : results) { - FederatedIdentityModel model = new FederatedIdentityModel(entity.getIdentityProvider(), entity.getBrokerUserId(), entity.getBrokerUserName(), entity.getToken()); - set.add(model); - } - return set; + return closing(query.getResultStream().map(entity -> new FederatedIdentityModel(entity.getIdentityProvider(), + entity.getBrokerUserId(), entity.getBrokerUserName(), entity.getToken())).distinct()); } @Override @@ -300,17 +294,10 @@ public class JpaUserFederatedStorageProvider implements } @Override - public List getConsents(RealmModel realm, String userId) { + public Stream getConsentsStream(RealmModel realm, String userId) { TypedQuery query = em.createNamedQuery("userFederatedConsentsByUser", FederatedUserConsentEntity.class); query.setParameter("userId", userId); - List results = query.getResultList(); - - List consents = new ArrayList(); - for (FederatedUserConsentEntity entity : results) { - UserConsentModel model = toConsentModel(realm, entity); - consents.add(model); - } - return consents; + return closing(query.getResultStream().map(entity -> toConsentModel(realm, entity))); } @Override @@ -478,7 +465,7 @@ public class JpaUserFederatedStorageProvider implements } @Override - public List getMembership(RealmModel realm, GroupModel group, int firstResult, int max) { + public Stream getMembershipStream(RealmModel realm, GroupModel group, int firstResult, int max) { TypedQuery query = em.createNamedQuery("fedgroupMembership", String.class) .setParameter("realmId", realm.getId()) .setParameter("groupId", group.getId()); @@ -489,28 +476,21 @@ public class JpaUserFederatedStorageProvider implements if (max != -1) { query.setMaxResults(max); } - - return query.getResultList(); + return closing(query.getResultStream()); } @Override - public Set getRequiredActions(RealmModel realm, String userId) { - Set set = new HashSet<>(); - List values = getRequiredActionEntities(realm, userId, LockModeType.NONE); - for (FederatedUserRequiredActionEntity entity : values) { - set.add(entity.getAction()); - } - - return set; - + public Stream getRequiredActionsStream(RealmModel realm, String userId) { + return this.getRequiredActionEntitiesStream(realm, userId, LockModeType.NONE). + map(FederatedUserRequiredActionEntity::getAction).distinct(); } - private List getRequiredActionEntities(RealmModel realm, String userId, LockModeType lockMode) { + private Stream getRequiredActionEntitiesStream(RealmModel realm, String userId, LockModeType lockMode) { TypedQuery query = em.createNamedQuery("getFederatedUserRequiredActionsByUser", FederatedUserRequiredActionEntity.class) .setParameter("userId", userId) .setParameter("realmId", realm.getId()); query.setLockMode(lockMode); - return query.getResultList(); + return closing(query.getResultStream()); } @Override @@ -529,12 +509,9 @@ public class JpaUserFederatedStorageProvider implements @Override public void removeRequiredAction(RealmModel realm, String userId, String action) { - List values = getRequiredActionEntities(realm, userId, LockModeType.PESSIMISTIC_WRITE); - for (FederatedUserRequiredActionEntity entity : values) { - if (action.equals(entity.getAction())) em.remove(entity); - } + this.getRequiredActionEntitiesStream(realm, userId, LockModeType.PESSIMISTIC_WRITE). + filter(entity -> Objects.equals(entity.getAction(), action)).collect(Collectors.toList()).forEach(em::remove); em.flush(); - } @Override @@ -598,7 +575,7 @@ public class JpaUserFederatedStorageProvider implements entity.setStorageProviderId(new StorageId(userId).getProviderId()); //add in linkedlist to last position - List credentials = getStoredCredentialEntities(userId); + List credentials = getStoredCredentialEntitiesStream(userId).collect(Collectors.toList()); int priority = credentials.isEmpty() ? JpaUserCredentialStore.PRIORITY_DIFFERENCE : credentials.get(credentials.size() - 1).getPriority() + JpaUserCredentialStore.PRIORITY_DIFFERENCE; entity.setPriority(priority); @@ -613,14 +590,8 @@ public class JpaUserFederatedStorageProvider implements int currentPriority = entity.getPriority(); - List credentials = getStoredCredentialEntities(userId); - - // Decrease priority of all credentials after our - for (FederatedUserCredentialEntity cred : credentials) { - if (cred.getPriority() > currentPriority) { - cred.setPriority(cred.getPriority() - JpaUserCredentialStore.PRIORITY_DIFFERENCE); - } - } + this.getStoredCredentialEntitiesStream(userId).filter(credentialEntity -> credentialEntity.getPriority() > currentPriority) + .forEach(credentialEntity -> credentialEntity.setPriority(credentialEntity.getPriority() - JpaUserCredentialStore.PRIORITY_DIFFERENCE)); em.remove(entity); return true; @@ -659,32 +630,22 @@ public class JpaUserFederatedStorageProvider implements } @Override - public List getStoredCredentials(RealmModel realm, String userId) { - List results = getStoredCredentialEntities(userId); - List rtn = new LinkedList<>(); - for (FederatedUserCredentialEntity entity : results) { - rtn.add(toModel(entity)); - } - return rtn; + public Stream getStoredCredentialsStream(RealmModel realm, String userId) { + return this.getStoredCredentialEntitiesStream(userId).map(this::toModel); } - private List getStoredCredentialEntities(String userId) { + private Stream getStoredCredentialEntitiesStream(String userId) { TypedQuery query = em.createNamedQuery("federatedUserCredentialByUser", FederatedUserCredentialEntity.class) .setParameter("userId", userId); - return query.getResultList(); + return closing(query.getResultStream()); } @Override - public List getStoredCredentialsByType(RealmModel realm, String userId, String type) { + public Stream getStoredCredentialsByTypeStream(RealmModel realm, String userId, String type) { TypedQuery query = em.createNamedQuery("federatedUserCredentialByUserAndType", FederatedUserCredentialEntity.class) .setParameter("type", type) .setParameter("userId", userId); - List results = query.getResultList(); - List rtn = new LinkedList<>(); - for (FederatedUserCredentialEntity entity : results) { - rtn.add(toModel(entity)); - } - return rtn; + return closing(query.getResultStream().map(this::toModel)); } @Override @@ -699,12 +660,13 @@ public class JpaUserFederatedStorageProvider implements } @Override - public List getStoredUsers(RealmModel realm, int first, int max) { + public Stream getStoredUsersStream(RealmModel realm, int first, int max) { TypedQuery query = em.createNamedQuery("getFederatedUserIds", String.class) - .setParameter("realmId", realm.getId()) - .setFirstResult(first); + .setParameter("realmId", realm.getId()); + if (first > 0) + query.setFirstResult(first); if (max > 0) query.setMaxResults(max); - return query.getResultList(); + return closing(query.getResultStream()); } @Override @@ -744,11 +706,8 @@ public class JpaUserFederatedStorageProvider implements @Override public boolean moveCredentialTo(RealmModel realm, UserModel user, String id, String newPreviousCredentialId) { - List sortedCreds = getStoredCredentialEntities(user.getId()); - // 1 - Create new list and move everything to it. - List newList = new ArrayList<>(); - newList.addAll(sortedCreds); + List newList = this.getStoredCredentialEntitiesStream(user.getId()).collect(Collectors.toList()); // 2 - Find indexes of our and newPrevious credential int ourCredentialIndex = -1; diff --git a/server-spi-private/src/main/java/org/keycloak/storage/adapter/InMemoryUserAdapter.java b/server-spi-private/src/main/java/org/keycloak/storage/adapter/InMemoryUserAdapter.java index 2f5fd09c8b..0d1471e5a2 100644 --- a/server-spi-private/src/main/java/org/keycloak/storage/adapter/InMemoryUserAdapter.java +++ b/server-spi-private/src/main/java/org/keycloak/storage/adapter/InMemoryUserAdapter.java @@ -152,12 +152,9 @@ public class InMemoryUserAdapter extends UserModelDefaultMethods { } @Override - public List getAttribute(String name) { - List value = attributes.get(name); - if (value == null) { - return new LinkedList<>(); - } - return value; + public Stream getAttributeStream(String name) { + List value = this.attributes.get(name); + return value != null ? value.stream() : Stream.empty(); } @Override @@ -166,8 +163,8 @@ public class InMemoryUserAdapter extends UserModelDefaultMethods { } @Override - public Set getRequiredActions() { - return requiredActions; + public Stream getRequiredActionsStream() { + return this.requiredActions.stream(); } @Override diff --git a/server-spi/src/main/java/org/keycloak/models/UserModel.java b/server-spi/src/main/java/org/keycloak/models/UserModel.java index f2e3281084..99738d036d 100755 --- a/server-spi/src/main/java/org/keycloak/models/UserModel.java +++ b/server-spi/src/main/java/org/keycloak/models/UserModel.java @@ -91,12 +91,37 @@ public interface UserModel extends RoleMapperModel { /** * @param name * @return list of all attribute values or empty list if there are not any values. Never return null + * @deprecated Use {@link #getAttributeStream(String) getAttributeStream} instead. */ - List getAttribute(String name); + @Deprecated + default List getAttribute(String name) { + return this.getAttributeStream(name).collect(Collectors.toList()); + } + + /** + * Obtains all values associated with the specified attribute name. + * + * @param name the name of the attribute. + * @return a non-null {@code Stream} of attribute values. + */ + Stream getAttributeStream(final String name); Map> getAttributes(); - Set getRequiredActions(); + /** + * @deprecated Use {@link #getRequiredActionsStream() getRequiredActionsStream} instead. + */ + @Deprecated + default Set getRequiredActions() { + return this.getRequiredActionsStream().collect(Collectors.toSet()); + } + + /** + * Obtains the names of required actions associated with the user. + * + * @return a non-null {@code Stream} of required action names. + */ + Stream getRequiredActionsStream(); void addRequiredAction(String action); @@ -122,18 +147,32 @@ public interface UserModel extends RoleMapperModel { void setEmailVerified(boolean verified); + /** + * @deprecated Use {@link #getGroupsStream() getGroupsStream} instead. + */ @Deprecated default Set getGroups() { return getGroupsStream().collect(Collectors.toSet()); } + /** + * Obtains the groups associated with the user. + * + * @return a non-null {@code Stream} of groups. + */ Stream getGroupsStream(); + /** + * @deprecated Use {@link #getGroupsStream(String, Integer, Integer) getGroupsStream} instead. + */ @Deprecated default Set getGroups(int first, int max) { return getGroupsStream(null, first, max).collect(Collectors.toSet()); } + /** + * @deprecated Use {@link #getGroupsStream(String, Integer, Integer) getGroupsStream} instead. + */ @Deprecated default Set getGroups(String search, int first, int max) { return getGroupsStream(search, first, max) @@ -141,7 +180,7 @@ public interface UserModel extends RoleMapperModel { } /** - * Returns a paginated stream of groups within this.realm with search in the name + * Returns a paginated stream of groups within this realm with search in the name * * @param search Case insensitive string which will be searched for. Ignored if null. * @param first Index of first group to return. Ignored if negative or {@code null}. diff --git a/server-spi/src/main/java/org/keycloak/models/UserProvider.java b/server-spi/src/main/java/org/keycloak/models/UserProvider.java index c3c5bb1939..a67fc4ac99 100755 --- a/server-spi/src/main/java/org/keycloak/models/UserProvider.java +++ b/server-spi/src/main/java/org/keycloak/models/UserProvider.java @@ -26,6 +26,8 @@ import org.keycloak.storage.user.UserRegistrationProvider; import java.util.List; import java.util.Set; +import java.util.stream.Collectors; +import java.util.stream.Stream; /** * @author Bill Burke @@ -42,13 +44,47 @@ public interface UserProvider extends Provider, boolean removeFederatedIdentity(RealmModel realm, UserModel user, String socialProvider); void preRemove(RealmModel realm, IdentityProviderModel provider); void updateFederatedIdentity(RealmModel realm, UserModel federatedUser, FederatedIdentityModel federatedIdentityModel); - Set getFederatedIdentities(UserModel user, RealmModel realm); + + /** + * @deprecated Use {@link #getFederatedIdentitiesStream(UserModel, RealmModel) getFederatedIdentitiesStream} instead. + */ + @Deprecated + default Set getFederatedIdentities(UserModel user, RealmModel realm) { + return this.getFederatedIdentitiesStream(user, realm).collect(Collectors.toSet()); + } + + /** + * Obtains the federated identities of the specified user. + * + * @param user a reference to the user. + * @param realm a reference to the realm. + * @return a non-null {@code Stream} of federated identities associated with the user. + */ + Stream getFederatedIdentitiesStream(UserModel user, RealmModel realm); + FederatedIdentityModel getFederatedIdentity(UserModel user, String socialProvider, RealmModel realm); UserModel getUserByFederatedIdentity(FederatedIdentityModel socialLink, RealmModel realm); void addConsent(RealmModel realm, String userId, UserConsentModel consent); UserConsentModel getConsentByClient(RealmModel realm, String userId, String clientInternalId); - List getConsents(RealmModel realm, String userId); + + /** + * @deprecated Use {@link #getConsentsStream(RealmModel, String) getConsentsStream} instead. + */ + @Deprecated + default List getConsents(RealmModel realm, String userId) { + return getConsentsStream(realm, userId).collect(Collectors.toList()); + } + + /** + * Obtains the consents associated with the user identified by the specified {@code userId}. + * + * @param realm a reference to the realm. + * @param userId the user identifier. + * @return a non-null {@code Stream} of consents associated with the user. + */ + Stream getConsentsStream(RealmModel realm, String userId); + void updateConsent(RealmModel realm, String userId, UserConsentModel consent); boolean revokeConsentForClient(RealmModel realm, String userId, String clientInternalId); @@ -56,8 +92,42 @@ public interface UserProvider extends Provider, int getNotBeforeOfUser(RealmModel realm, UserModel user); UserModel getServiceAccount(ClientModel client); - List getUsers(RealmModel realm, boolean includeServiceAccounts); - List getUsers(RealmModel realm, int firstResult, int maxResults, boolean includeServiceAccounts); + + /** + * @deprecated Use {@link #getUsersStream(RealmModel, boolean) getUsersStream} instead. + */ + @Deprecated + default List getUsers(RealmModel realm, boolean includeServiceAccounts) { + return this.getUsersStream(realm, includeServiceAccounts).collect(Collectors.toList()); + } + + /** + * Obtains the users associated with the specified realm. + * + * @param realm a reference to the realm being used for the search. + * @param includeServiceAccounts {@code true} if service accounts should be included in the result; {@code false} otherwise. + * @return a non-null {@code Stream} of users associated withe the realm. + */ + Stream getUsersStream(RealmModel realm, boolean includeServiceAccounts); + + /** + * @deprecated Use {@link #getUsersStream(RealmModel, int, int, boolean) getUsersStream} instead. + */ + @Deprecated + default List getUsers(RealmModel realm, int firstResult, int maxResults, boolean includeServiceAccounts) { + return this.getUsersStream(realm, firstResult, maxResults, includeServiceAccounts).collect(Collectors.toList()); + } + + /** + * Obtains the users associated with the specified realm. + * + * @param realm a reference to the realm being used for the search. + * @param firstResult first result to return. Ignored if negative. + * @param maxResults maximum number of results to return. Ignored if negative. + * @param includeServiceAccounts {@code true} if service accounts should be included in the result; {@code false} otherwise. + * @return a non-null {@code Stream} of users associated withe the realm. + */ + Stream getUsersStream(RealmModel realm, int firstResult, int maxResults, boolean includeServiceAccounts); /** * only used for local storage diff --git a/server-spi/src/main/java/org/keycloak/models/utils/UserModelDelegate.java b/server-spi/src/main/java/org/keycloak/models/utils/UserModelDelegate.java index ca1c9da21f..19fc110b5f 100755 --- a/server-spi/src/main/java/org/keycloak/models/utils/UserModelDelegate.java +++ b/server-spi/src/main/java/org/keycloak/models/utils/UserModelDelegate.java @@ -86,8 +86,8 @@ public class UserModelDelegate implements UserModel { } @Override - public List getAttribute(String name) { - return delegate.getAttribute(name); + public Stream getAttributeStream(String name) { + return delegate.getAttributeStream(name); } @Override @@ -96,8 +96,8 @@ public class UserModelDelegate implements UserModel { } @Override - public Set getRequiredActions() { - return delegate.getRequiredActions(); + public Stream getRequiredActionsStream() { + return delegate.getRequiredActionsStream(); } @Override diff --git a/server-spi/src/main/java/org/keycloak/storage/adapter/AbstractUserAdapter.java b/server-spi/src/main/java/org/keycloak/storage/adapter/AbstractUserAdapter.java index 315fe1efb7..511e99a4cc 100644 --- a/server-spi/src/main/java/org/keycloak/storage/adapter/AbstractUserAdapter.java +++ b/server-spi/src/main/java/org/keycloak/storage/adapter/AbstractUserAdapter.java @@ -30,11 +30,8 @@ import org.keycloak.models.utils.RoleUtils; import org.keycloak.storage.ReadOnlyException; import org.keycloak.storage.StorageId; -import java.util.Collections; -import java.util.HashSet; import java.util.List; import java.util.Map; -import java.util.Set; import java.util.stream.Stream; /** @@ -62,8 +59,8 @@ public abstract class AbstractUserAdapter extends UserModelDefaultMethods { } @Override - public Set getRequiredActions() { - return Collections.emptySet(); + public Stream getRequiredActionsStream() { + return Stream.empty(); } @Override @@ -303,11 +300,11 @@ public abstract class AbstractUserAdapter extends UserModelDefaultMethods { } @Override - public List getAttribute(String name) { + public Stream getAttributeStream(String name) { if (name.equals(UserModel.USERNAME)) { - return Collections.singletonList(getUsername()); + return Stream.of(getUsername()); } - return Collections.emptyList(); + return Stream.empty(); } @Override diff --git a/server-spi/src/main/java/org/keycloak/storage/adapter/AbstractUserAdapterFederatedStorage.java b/server-spi/src/main/java/org/keycloak/storage/adapter/AbstractUserAdapterFederatedStorage.java index fee1dafc5a..f48b94a9a7 100644 --- a/server-spi/src/main/java/org/keycloak/storage/adapter/AbstractUserAdapterFederatedStorage.java +++ b/server-spi/src/main/java/org/keycloak/storage/adapter/AbstractUserAdapterFederatedStorage.java @@ -30,11 +30,8 @@ import org.keycloak.models.utils.RoleUtils; import org.keycloak.storage.StorageId; import org.keycloak.storage.federated.UserFederatedStorageProvider; -import java.util.Collections; -import java.util.HashSet; import java.util.List; import java.util.Map; -import java.util.Set; import java.util.stream.Stream; /** @@ -71,8 +68,8 @@ public abstract class AbstractUserAdapterFederatedStorage extends UserModelDefau } @Override - public Set getRequiredActions() { - return getFederatedStorage().getRequiredActions(realm, this.getId()); + public Stream getRequiredActionsStream() { + return getFederatedStorage().getRequiredActionsStream(realm, this.getId()); } @Override @@ -360,12 +357,12 @@ public abstract class AbstractUserAdapterFederatedStorage extends UserModelDefau } @Override - public List getAttribute(String name) { + public Stream getAttributeStream(String name) { if (UserModel.USERNAME.equals(name)) { - return Collections.singletonList(getUsername()); + return Stream.of(getUsername()); } List result = getFederatedStorage().getAttributes(realm, this.getId()).get(mapAttribute(name)); - return (result == null) ? Collections.emptyList() : result; + return (result == null) ? Stream.empty() : result.stream(); } private String mapAttribute(String attributeName) { diff --git a/server-spi/src/main/java/org/keycloak/storage/federated/UserAttributeFederatedStorage.java b/server-spi/src/main/java/org/keycloak/storage/federated/UserAttributeFederatedStorage.java index 14d3bbfb68..b1919bc525 100644 --- a/server-spi/src/main/java/org/keycloak/storage/federated/UserAttributeFederatedStorage.java +++ b/server-spi/src/main/java/org/keycloak/storage/federated/UserAttributeFederatedStorage.java @@ -20,6 +20,8 @@ import org.keycloak.common.util.MultivaluedHashMap; import org.keycloak.models.RealmModel; import java.util.List; +import java.util.stream.Collectors; +import java.util.stream.Stream; /** * @author Bill Burke @@ -30,5 +32,22 @@ public interface UserAttributeFederatedStorage { void setAttribute(RealmModel realm, String userId, String name, List values); void removeAttribute(RealmModel realm, String userId, String name); MultivaluedHashMap getAttributes(RealmModel realm, String userId); - List getUsersByUserAttribute(RealmModel realm, String name, String value); + + /** + * @deprecated Use {@link #getUsersByUserAttributeStream(RealmModel, String, String) getUsersByUserAttributeStream} instead. + */ + @Deprecated + default List getUsersByUserAttribute(RealmModel realm, String name, String value) { + return this.getUsersByUserAttributeStream(realm, name, value).collect(Collectors.toList()); + } + + /** + * Searches for federated users that have an attribute with the specified {@code name} and {@code value}. + * + * @param realm a reference to the realm. + * @param name the attribute name. + * @param value the attribute value. + * @return a non-null {@code Stream} of users that match the search criteria. + */ + Stream getUsersByUserAttributeStream(RealmModel realm, String name, String value); } diff --git a/server-spi/src/main/java/org/keycloak/storage/federated/UserBrokerLinkFederatedStorage.java b/server-spi/src/main/java/org/keycloak/storage/federated/UserBrokerLinkFederatedStorage.java index ff5acc8737..68ae813e70 100644 --- a/server-spi/src/main/java/org/keycloak/storage/federated/UserBrokerLinkFederatedStorage.java +++ b/server-spi/src/main/java/org/keycloak/storage/federated/UserBrokerLinkFederatedStorage.java @@ -21,6 +21,8 @@ import org.keycloak.models.IdentityProviderModel; import org.keycloak.models.RealmModel; import java.util.Set; +import java.util.stream.Collectors; +import java.util.stream.Stream; /** * @author Bill Burke @@ -32,6 +34,23 @@ public interface UserBrokerLinkFederatedStorage { boolean removeFederatedIdentity(RealmModel realm, String userId, String socialProvider); void preRemove(RealmModel realm, IdentityProviderModel provider); void updateFederatedIdentity(RealmModel realm, String userId, FederatedIdentityModel federatedIdentityModel); - Set getFederatedIdentities(String userId, RealmModel realm); + + /** + * @deprecated Use {@link #getFederatedIdentitiesStream(String, RealmModel) getFederatedIdentitiesStream} instead. + */ + @Deprecated + default Set getFederatedIdentities(String userId, RealmModel realm) { + return this.getFederatedIdentitiesStream(userId, realm).collect(Collectors.toSet()); + } + + /** + * Obtains the identities of the federated user identified by {@code userId}. + * + * @param userId the user identifier. + * @param realm a reference to the realm. + * @return a non-null {@code Stream} of federated identities associated with the user. + */ + Stream getFederatedIdentitiesStream(String userId, RealmModel realm); + FederatedIdentityModel getFederatedIdentity(String userId, String socialProvider, RealmModel realm); } diff --git a/server-spi/src/main/java/org/keycloak/storage/federated/UserConsentFederatedStorage.java b/server-spi/src/main/java/org/keycloak/storage/federated/UserConsentFederatedStorage.java index 8e4bb8e4c6..86219a2a4a 100644 --- a/server-spi/src/main/java/org/keycloak/storage/federated/UserConsentFederatedStorage.java +++ b/server-spi/src/main/java/org/keycloak/storage/federated/UserConsentFederatedStorage.java @@ -20,6 +20,8 @@ import org.keycloak.models.RealmModel; import org.keycloak.models.UserConsentModel; import java.util.List; +import java.util.stream.Collectors; +import java.util.stream.Stream; /** * @author Bill Burke @@ -28,7 +30,24 @@ import java.util.List; public interface UserConsentFederatedStorage { void addConsent(RealmModel realm, String userId, UserConsentModel consent); UserConsentModel getConsentByClient(RealmModel realm, String userId, String clientInternalId); - List getConsents(RealmModel realm, String userId); + + /** + * @deprecated Use {@link #getConsentsStream(RealmModel, String) getConsentsStream} instead. + */ + @Deprecated + default List getConsents(RealmModel realm, String userId) { + return this.getConsentsStream(realm, userId).collect(Collectors.toList()); + } + + /** + * Obtains the consents associated with the federated user identified by {@code userId}. + * + * @param realm a reference to the realm. + * @param userId the user identifier. + * @return a non-null {@code Stream} of consents associated with the user. + */ + Stream getConsentsStream(RealmModel realm, String userId); + void updateConsent(RealmModel realm, String userId, UserConsentModel consent); boolean revokeConsentForClient(RealmModel realm, String userId, String clientInternalId); } diff --git a/server-spi/src/main/java/org/keycloak/storage/federated/UserFederatedStorageProvider.java b/server-spi/src/main/java/org/keycloak/storage/federated/UserFederatedStorageProvider.java index 82931c6ec9..0479631c9c 100755 --- a/server-spi/src/main/java/org/keycloak/storage/federated/UserFederatedStorageProvider.java +++ b/server-spi/src/main/java/org/keycloak/storage/federated/UserFederatedStorageProvider.java @@ -28,6 +28,8 @@ import org.keycloak.models.UserModel; import org.keycloak.provider.Provider; import java.util.List; +import java.util.stream.Collectors; +import java.util.stream.Stream; /** * @author Bill Burke @@ -43,7 +45,24 @@ public interface UserFederatedStorageProvider extends Provider, UserRoleMappingsFederatedStorage, UserFederatedUserCredentialStore { - List getStoredUsers(RealmModel realm, int first, int max); + /** + * @deprecated Use {@link #getStoredUsersStream(RealmModel, int, int) getStoredUsersStream} instead. + */ + @Deprecated + default List getStoredUsers(RealmModel realm, int first, int max) { + return getStoredUsersStream(realm, first, max).collect(Collectors.toList()); + } + + /** + * Obtains the ids of all federated users in the realm. + * + * @param realm a reference to the realm. + * @param first first result to return. Ignored if negative. + * @param max maximum number of results to return. Ignored if negative. + * @return a non-null {@code Stream} of federated user ids. + */ + Stream getStoredUsersStream(RealmModel realm, int first, int max); + int getStoredUsersCount(RealmModel realm); void preRemove(RealmModel realm); diff --git a/server-spi/src/main/java/org/keycloak/storage/federated/UserFederatedUserCredentialStore.java b/server-spi/src/main/java/org/keycloak/storage/federated/UserFederatedUserCredentialStore.java index 12f43e5f9f..676cce05d4 100644 --- a/server-spi/src/main/java/org/keycloak/storage/federated/UserFederatedUserCredentialStore.java +++ b/server-spi/src/main/java/org/keycloak/storage/federated/UserFederatedUserCredentialStore.java @@ -21,6 +21,8 @@ import org.keycloak.models.RealmModel; import org.keycloak.provider.Provider; import java.util.List; +import java.util.stream.Collectors; +import java.util.stream.Stream; /** * @author Bill Burke @@ -31,7 +33,41 @@ public interface UserFederatedUserCredentialStore extends Provider { CredentialModel createCredential(RealmModel realm, String userId, CredentialModel cred); boolean removeStoredCredential(RealmModel realm, String userId, String id); CredentialModel getStoredCredentialById(RealmModel realm, String userId, String id); - List getStoredCredentials(RealmModel realm, String userId); - List getStoredCredentialsByType(RealmModel realm, String userId, String type); + + /** + * @deprecated Use {@link #getStoredCredentialsStream(RealmModel, String) getStoredCredentialsStream} instead. + */ + @Deprecated + default List getStoredCredentials(RealmModel realm, String userId) { + return this.getStoredCredentialsStream(realm, userId).collect(Collectors.toList()); + } + + /** + * Obtains the credentials associated with the federated user identified by {@code userId}. + * + * @param realm a reference to the realm. + * @param userId the user identifier. + * @return a non-null {@code Stream} of credentials. + */ + Stream getStoredCredentialsStream(RealmModel realm, String userId); + + /** + * @deprecated Use {@link #getStoredCredentialsByTypeStream(RealmModel, String, String) getStoredCredentialsByTypeStream} instead. + */ + @Deprecated + default List getStoredCredentialsByType(RealmModel realm, String userId, String type) { + return this.getStoredCredentialsByTypeStream(realm, userId, type).collect(Collectors.toList()); + } + + /** + * Obtains the credentials of type {@code type} that are associated with the federated user identified by {@code userId}. + * + * @param realm a reference to the realm. + * @param userId the user identifier. + * @param type the credential type. + * @return a non-null {@code Stream} of credentials. + */ + Stream getStoredCredentialsByTypeStream(RealmModel realm, String userId, String type); + CredentialModel getStoredCredentialByNameAndType(RealmModel realm, String userId, String name, String type); } diff --git a/server-spi/src/main/java/org/keycloak/storage/federated/UserGroupMembershipFederatedStorage.java b/server-spi/src/main/java/org/keycloak/storage/federated/UserGroupMembershipFederatedStorage.java index eb116d2164..aad956d471 100644 --- a/server-spi/src/main/java/org/keycloak/storage/federated/UserGroupMembershipFederatedStorage.java +++ b/server-spi/src/main/java/org/keycloak/storage/federated/UserGroupMembershipFederatedStorage.java @@ -38,6 +38,24 @@ public interface UserGroupMembershipFederatedStorage { void joinGroup(RealmModel realm, String userId, GroupModel group); void leaveGroup(RealmModel realm, String userId, GroupModel group); - List getMembership(RealmModel realm, GroupModel group, int firstResult, int max); + + /** + * @deprecated Use {@link #getMembershipStream(RealmModel, GroupModel, int, int) getMembershipStream} instead. + */ + @Deprecated + default List getMembership(RealmModel realm, GroupModel group, int firstResult, int max) { + return this.getMembershipStream(realm, group, firstResult, max).collect(Collectors.toList()); + } + + /** + * Obtains the federated users that are members of the given {@code group} in the specified {@code realm}. + * + * @param realm a reference to the realm. + * @param group a reference to the group whose federated members are being searched. + * @param firstResult first result to return. Ignored if negative. + * @param max maximum number of results to return. Ignored if negative. + * @return a non-null {@code Stream} of federated user ids that are members of the group in the realm. + */ + Stream getMembershipStream(RealmModel realm, GroupModel group, int firstResult, int max); } diff --git a/server-spi/src/main/java/org/keycloak/storage/federated/UserRequiredActionsFederatedStorage.java b/server-spi/src/main/java/org/keycloak/storage/federated/UserRequiredActionsFederatedStorage.java index 05f5b2e23c..23dce8e90c 100644 --- a/server-spi/src/main/java/org/keycloak/storage/federated/UserRequiredActionsFederatedStorage.java +++ b/server-spi/src/main/java/org/keycloak/storage/federated/UserRequiredActionsFederatedStorage.java @@ -19,13 +19,31 @@ package org.keycloak.storage.federated; import org.keycloak.models.RealmModel; import java.util.Set; +import java.util.stream.Collectors; +import java.util.stream.Stream; /** * @author Bill Burke * @version $Revision: 1 $ */ public interface UserRequiredActionsFederatedStorage { - Set getRequiredActions(RealmModel realm, String userId); + + /** + * @deprecated Use {@link #getRequiredActionsStream(RealmModel, String) getRequiredActionsStream} instead. + */ + @Deprecated + default Set getRequiredActions(RealmModel realm, String userId) { + return this.getRequiredActionsStream(realm, userId).collect(Collectors.toSet()); + } + /** + * Obtains the names of required actions associated with the federated user identified by {@code userId}. + * + * @param realm a reference to the realm. + * @param userId the user identifier. + * @return a non-null {@code Stream} of required action names. + */ + Stream getRequiredActionsStream(RealmModel realm, String userId); + void addRequiredAction(RealmModel realm, String userId, String action); void removeRequiredAction(RealmModel realm, String userId, String action); } diff --git a/server-spi/src/main/java/org/keycloak/storage/federated/UserRoleMappingsFederatedStorage.java b/server-spi/src/main/java/org/keycloak/storage/federated/UserRoleMappingsFederatedStorage.java index 9b5a311bed..10ea27c3ec 100644 --- a/server-spi/src/main/java/org/keycloak/storage/federated/UserRoleMappingsFederatedStorage.java +++ b/server-spi/src/main/java/org/keycloak/storage/federated/UserRoleMappingsFederatedStorage.java @@ -31,11 +31,21 @@ public interface UserRoleMappingsFederatedStorage { void grantRole(RealmModel realm, String userId, RoleModel role); + /** + * @deprecated Use {@link #getRoleMappingsStream(RealmModel, String) getRoleMappingsStream} instead. + */ @Deprecated default Set getRoleMappings(RealmModel realm,String userId) { return getRoleMappingsStream(realm, userId).collect(Collectors.toSet()); } + /** + * Obtains the roles associated with the federated user identified by {@code userId}. + * + * @param realm a reference to the realm. + * @param userId the user identifier. + * @return a non-null {@code Stream} of roles. + */ Stream getRoleMappingsStream(RealmModel realm, String userId); void deleteRoleMapping(RealmModel realm, String userId, RoleModel role); diff --git a/server-spi/src/main/java/org/keycloak/storage/user/UserQueryProvider.java b/server-spi/src/main/java/org/keycloak/storage/user/UserQueryProvider.java index 91a66403b7..8e8de6ad5b 100644 --- a/server-spi/src/main/java/org/keycloak/storage/user/UserQueryProvider.java +++ b/server-spi/src/main/java/org/keycloak/storage/user/UserQueryProvider.java @@ -21,10 +21,11 @@ import org.keycloak.models.RealmModel; import org.keycloak.models.RoleModel; import org.keycloak.models.UserModel; -import java.util.Collections; import java.util.List; import java.util.Map; import java.util.Set; +import java.util.stream.Collectors; +import java.util.stream.Stream; /** * Optional capability interface implemented by UserStorageProviders. @@ -56,8 +57,7 @@ public interface UserQueryProvider { if (groupIds == null || groupIds.isEmpty()) { return 0; } - - return countUsersInGroups(getUsers(realm), groupIds); + return countUsersInGroups(getUsersStream(realm), groupIds); } /** @@ -68,7 +68,7 @@ public interface UserQueryProvider { * @return number of users that match the search */ default int getUsersCount(String search, RealmModel realm) { - return searchForUser(search, realm).size(); + return (int) searchForUserStream(search, realm).count(); } /** @@ -84,9 +84,7 @@ public interface UserQueryProvider { if (groupIds == null || groupIds.isEmpty()) { return 0; } - - List users = searchForUser(search, realm); - return countUsersInGroups(users, groupIds); + return countUsersInGroups(searchForUserStream(search, realm), groupIds); } /** @@ -97,7 +95,7 @@ public interface UserQueryProvider { * @return number of users that match the given filters */ default int getUsersCount(Map params, RealmModel realm) { - return searchForUser(params, realm).size(); + return (int) searchForUserStream(params, realm).count(); } /** @@ -113,9 +111,7 @@ public interface UserQueryProvider { if (groupIds == null || groupIds.isEmpty()) { return 0; } - - List users = searchForUser(params, realm); - return countUsersInGroups(users, groupIds); + return countUsersInGroups(searchForUserStream(params, realm), groupIds); } /** @@ -126,10 +122,8 @@ public interface UserQueryProvider { * @param groupIds id of groups that should be checked for * @return number of users that are in at least one of the groups */ - static int countUsersInGroups(List users, Set groupIds) { - return (int) users.stream() - .filter(u -> u.getGroupsStream().anyMatch(group -> groupIds.contains(group.getId()))) - .count(); + static int countUsersInGroups(Stream users, Set groupIds) { + return (int) users.filter(u -> u.getGroupsStream().map(GroupModel::getId).anyMatch(groupIds::contains)).count(); } /** @@ -143,8 +137,39 @@ public interface UserQueryProvider { throw new RuntimeException("Not implemented"); } - List getUsers(RealmModel realm); - List getUsers(RealmModel realm, int firstResult, int maxResults); + /** + * @deprecated Use {@link #getUsersStream(RealmModel) getUsersStream} instead. + */ + @Deprecated + default List getUsers(RealmModel realm) { + return this.getUsersStream(realm).collect(Collectors.toList()); + } + + /** + * Searches all users in the realm. + * + * @param realm a reference to the realm. + * @return a non-null {@code Stream} of users. + */ + Stream getUsersStream(RealmModel realm); + + /** + * @deprecated Use {@link #getUsersStream(RealmModel, int, int) getUsersStream} instead. + */ + @Deprecated + default List getUsers(RealmModel realm, int firstResult, int maxResults) { + return this.getUsersStream(realm, firstResult, maxResults).collect(Collectors.toList()); + } + + /** + * Searches all users in the realm, starting from the {@code firstResult} and containing at most {@code maxResults}. + * + * @param realm a reference to the realm. + * @param firstResult first result to return. Ignored if negative. + * @param maxResults maximum number of results to return. Ignored if negative. + * @return a non-null {@code Stream} of users. + */ + Stream getUsersStream(RealmModel realm, int firstResult, int maxResults); /** * Search for users with username, email or first + last name that is like search string. @@ -156,8 +181,24 @@ public interface UserQueryProvider { * @param search * @param realm * @return + * @deprecated Use {@link #searchForUserStream(String, RealmModel) searchForUserStream} instead. */ - List searchForUser(String search, RealmModel realm); + @Deprecated + default List searchForUser(String search, RealmModel realm) { + return this.searchForUserStream(search, realm).collect(Collectors.toList()); + } + + /** + * Searches for users with username, email or first + last name that is like search string. If possible, implementations + * should treat the parameter values as partial match patterns (i.e. in RDMBS terms use LIKE). + *

+ * This method is used by the admin console search box + * + * @param search case sensitive search string. + * @param realm a reference to the realm. + * @return a non-null {@code Stream} of users that match the search string. + */ + Stream searchForUserStream(String search, RealmModel realm); /** * Search for users with username, email or first + last name that is like search string. @@ -171,8 +212,26 @@ public interface UserQueryProvider { * @param firstResult * @param maxResults * @return + * @deprecated Use {@link #searchForUserStream(String, RealmModel, int, int) searchForUserStream} instead. */ - List searchForUser(String search, RealmModel realm, int firstResult, int maxResults); + @Deprecated + default List searchForUser(String search, RealmModel realm, int firstResult, int maxResults) { + return this.searchForUserStream(search, realm, firstResult, maxResults).collect(Collectors.toList()); + } + + /** + * Searches for users with username, email or first + last name that is like search string. If possible, implementations + * should treat the parameter values as partial match patterns (i.e. in RDMBS terms use LIKE). + *

+ * This method is used by the admin console search box + * + * @param search case sensitive search string. + * @param realm a reference to the realm. + * @param firstResult first result to return. Ignored if negative. + * @param maxResults maximum number of results to return. Ignored if negative. + * @return a non-null {@code Stream} of users that match the search criteria. + */ + Stream searchForUserStream(String search, RealmModel realm, int firstResult, int maxResults); /** * Search for user by parameter. Valid parameters are: @@ -189,8 +248,30 @@ public interface UserQueryProvider { * @param params * @param realm * @return + * @deprecated Use {@link #searchForUserStream(Map, RealmModel) searchForUserStream} instead. */ - List searchForUser(Map params, RealmModel realm); + @Deprecated + default List searchForUser(Map params, RealmModel realm) { + return this.searchForUserStream(params, realm).collect(Collectors.toList()); + } + + /** + * Searches for user by parameter. If possible, implementations should treat the parameter values as partial match patterns + * (i.e. in RDMBS terms use LIKE). Valid parameters are: + *

    + *
  • first - first name
  • + *
  • last - last name
  • + *
  • email - email
  • + *
  • username - username
  • + *
  • enabled - if user is enabled (true/false)
  • + *
+ * This method is used by the REST API when querying users. + * + * @param params a map containing the search parameters. + * @param realm a reference to the realm. + * @return a non-null {@code Stream} of users that match the search parameters. + */ + Stream searchForUserStream(Map params, RealmModel realm); /** * Search for user by parameter. Valid parameters are: @@ -209,8 +290,60 @@ public interface UserQueryProvider { * @param firstResult * @param maxResults * @return + * @deprecated Use {@link #searchForUserStream(Map, RealmModel, int, int) searchForUserStream} instead. */ - List searchForUser(Map params, RealmModel realm, int firstResult, int maxResults); + @Deprecated + default List searchForUser(Map params, RealmModel realm, int firstResult, int maxResults) { + return this.searchForUserStream(params, realm, firstResult, maxResults).collect(Collectors.toList()); + } + + /** + * Searches for user by parameter. If possible, implementations should treat the parameter values as partial match patterns + * (i.e. in RDMBS terms use LIKE). Valid parameters are: + *
    + *
  • first - first name
  • + *
  • last - last name
  • + *
  • email - email
  • + *
  • username - username
  • + *
  • enabled - if user is enabled (true/false)
  • + *
+ * This method is used by the REST API when querying users. + * + * @param params a map containing the search parameters. + * @param realm a reference to the realm. + * @param firstResult first result to return. Ignored if negative. + * @param maxResults maximum number of results to return. Ignored if negative. + * @return a non-null {@code Stream} of users that match the search criteria. + */ + Stream searchForUserStream(Map params, RealmModel realm, int firstResult, int maxResults); + + /** + * Get users that belong to a specific group. Implementations do not have to search in UserFederatedStorageProvider + * as this is done automatically. + * + * @see org.keycloak.storage.federated.UserFederatedStorageProvider + * + * @param realm + * @param group + * @return + * @deprecated Use {@link #getGroupMembersStream(RealmModel, GroupModel) getGroupMembersStream} instead. + */ + @Deprecated + default List getGroupMembers(RealmModel realm, GroupModel group) { + return this.getGroupMembersStream(realm, group).collect(Collectors.toList()); + } + + /** + * Obtains users that belong to a specific group. Implementations do not have to search in {@code UserFederatedStorageProvider} + * as this is done automatically. + * + * @see org.keycloak.storage.federated.UserFederatedStorageProvider + * + * @param realm a reference to the realm. + * @param group a reference to the group. + * @return a non-null {@code Stream} of users that belong to the group. + */ + Stream getGroupMembersStream(RealmModel realm, GroupModel group); /** * Get users that belong to a specific group. Implementations do not have to search in UserFederatedStorageProvider @@ -223,51 +356,77 @@ public interface UserQueryProvider { * @param firstResult * @param maxResults * @return + * @deprecated Use {@link #getGroupMembersStream(RealmModel, GroupModel, int, int) getGroupMembersStream} instead. */ - List getGroupMembers(RealmModel realm, GroupModel group, int firstResult, int maxResults); + @Deprecated + default List getGroupMembers(RealmModel realm, GroupModel group, int firstResult, int maxResults) { + return this.getGroupMembersStream(realm, group, firstResult, maxResults).collect(Collectors.toList()); + } + + /** + * Obtains users that belong to a specific group. Implementations do not have to search in {@code UserFederatedStorageProvider} + * as this is done automatically. + * + * @see org.keycloak.storage.federated.UserFederatedStorageProvider + * + * @param realm a reference to the realm. + * @param group a reference to the group. + * @param firstResult first result to return. Ignored if negative. + * @param maxResults maximum number of results to return. Ignored if negative. + * @return a non-null {@code Stream} of users that belong to the group. + */ + Stream getGroupMembersStream(RealmModel realm, GroupModel group, int firstResult, int maxResults); /** * Get users that belong to a specific role. * - * - * * @param realm * @param role * @return + * @deprecated Use {@link #getRoleMembersStream(RealmModel, RoleModel) getRoleMembersStream} instead. */ - default List getRoleMembers(RealmModel realm, RoleModel role) - { - return Collections.EMPTY_LIST; + @Deprecated + default List getRoleMembers(RealmModel realm, RoleModel role) { + return this.getRoleMembersStream(realm, role).collect(Collectors.toList()); + } + + /** + * Obtains users that have the specified role. + * + * @param realm a reference to the realm. + * @param role a reference to the role. + * @return a non-null {@code Stream} of users that have the specified role. + */ + default Stream getRoleMembersStream(RealmModel realm, RoleModel role) { + return Stream.empty(); } /** * Search for users that have a specific role with a specific roleId. * - * - * * @param firstResult * @param maxResults * @param role * @return + * @deprecated Use {@link #getRoleMembersStream(RealmModel, RoleModel, int, int) getRoleMembersStream} instead. */ - default List getRoleMembers(RealmModel realm, RoleModel role, int firstResult, int maxResults) - { - return Collections.EMPTY_LIST; + @Deprecated + default List getRoleMembers(RealmModel realm, RoleModel role, int firstResult, int maxResults) { + return this.getRoleMembersStream(realm, role, firstResult, maxResults).collect(Collectors.toList()); } /** - * Get users that belong to a specific group. Implementations do not have to search in UserFederatedStorageProvider - * as this is done automatically. + * Searches for users that have the specified role. * - * @see org.keycloak.storage.federated.UserFederatedStorageProvider - * - * - * - * @param realm - * @param group - * @return + * @param realm a reference to the realm. + * @param role a reference to the role. + * @param firstResult first result to return. Ignored if negative. + * @param maxResults maximum number of results to return. Ignored if negative. + * @return a non-null {@code Stream} of users that have the specified role. */ - List getGroupMembers(RealmModel realm, GroupModel group); + default Stream getRoleMembersStream(RealmModel realm, RoleModel role, int firstResult, int maxResults) { + return Stream.empty(); + } /** * Search for users that have a specific attribute with a specific value. @@ -275,13 +434,29 @@ public interface UserQueryProvider { * as this is done automatically. * * @see org.keycloak.storage.federated.UserFederatedStorageProvider - * - * * @param attrName * @param attrValue * @param realm * @return + * @deprecated Use {@link #searchForUserByUserAttributeStream(String, String, RealmModel) searchForUserByUserAttributeStream} + * instead. */ - List searchForUserByUserAttribute(String attrName, String attrValue, RealmModel realm); + @Deprecated + default List searchForUserByUserAttribute(String attrName, String attrValue, RealmModel realm) { + return this.searchForUserByUserAttributeStream(attrName, attrValue, realm).collect(Collectors.toList()); + } + + /** + * Searches for users that have a specific attribute with a specific value. Implementations do not have to search in + * {@code UserFederatedStorageProvider} as this is done automatically. + * + * @see org.keycloak.storage.federated.UserFederatedStorageProvider + * + * @param attrName the attribute name. + * @param attrValue the attribute value. + * @param realm a reference to the realm. + * @return a non-null {@code Stream} of users that match the search criteria. + */ + Stream searchForUserByUserAttributeStream(String attrName, String attrValue, RealmModel realm); } diff --git a/services/src/main/java/org/keycloak/authentication/authenticators/broker/IdpReviewProfileAuthenticator.java b/services/src/main/java/org/keycloak/authentication/authenticators/broker/IdpReviewProfileAuthenticator.java index 4a83f19f9f..571b6c7355 100755 --- a/services/src/main/java/org/keycloak/authentication/authenticators/broker/IdpReviewProfileAuthenticator.java +++ b/services/src/main/java/org/keycloak/authentication/authenticators/broker/IdpReviewProfileAuthenticator.java @@ -46,6 +46,7 @@ import javax.ws.rs.core.MultivaluedMap; import javax.ws.rs.core.Response; import java.util.List; import java.util.Map; +import java.util.stream.Stream; /** * @author Marek Posolda @@ -130,8 +131,8 @@ public class IdpReviewProfileAuthenticator extends AbstractIdpAuthenticator { } @Override - public List getAttribute(String name) { - return userCtx.getAttribute(name); + public Stream getAttributeStream(String name) { + return userCtx.getAttribute(name).stream(); } @Override diff --git a/services/src/main/java/org/keycloak/storage/UserStorageManager.java b/services/src/main/java/org/keycloak/storage/UserStorageManager.java index e501954719..02c4244464 100755 --- a/services/src/main/java/org/keycloak/storage/UserStorageManager.java +++ b/services/src/main/java/org/keycloak/storage/UserStorageManager.java @@ -17,6 +17,7 @@ package org.keycloak.storage; +import com.google.common.collect.Streams; import org.jboss.logging.Logger; import org.keycloak.component.ComponentFactory; import org.keycloak.component.ComponentModel; @@ -50,14 +51,12 @@ import org.keycloak.storage.user.UserQueryProvider; import org.keycloak.storage.user.UserRegistrationProvider; import java.util.HashSet; -import java.util.List; import java.util.Map; import java.util.Objects; import java.util.Optional; import java.util.Set; import java.util.function.Function; import java.util.function.Predicate; -import java.util.stream.Collectors; import java.util.stream.Stream; import static org.keycloak.models.utils.KeycloakModelUtils.runJobInTransaction; @@ -130,19 +129,14 @@ public class UserStorageManager extends AbstractStorageManager { + RealmModel realmModel = session.realms().getRealm(realm.getId()); + if (realmModel == null) return; + UserModel deletedUser = session.userLocalStorage().getUserById(userId, realmModel); + if (deletedUser != null) { + new UserManager(session).removeUser(realmModel, deletedUser, session.userLocalStorage()); + logger.debugf("Removed invalid user '%s'", userName); } - }); } @@ -270,70 +264,70 @@ public class UserStorageManager extends AbstractStorageManager getGroupMembers(RealmModel realm, GroupModel group) { - return getGroupMembers(realm, group, -1, -1); + public Stream getGroupMembersStream(RealmModel realm, GroupModel group) { + return getGroupMembersStream(realm, group, -1, -1); } @Override - public List getGroupMembers(final RealmModel realm, final GroupModel group, int firstResult, int maxResults) { + public Stream getGroupMembersStream(final RealmModel realm, final GroupModel group, int firstResult, int maxResults) { Stream results = query((provider) -> { if (provider instanceof UserQueryProvider) { - return ((UserQueryProvider)provider).getGroupMembers(realm, group).stream(); + return ((UserQueryProvider)provider).getGroupMembersStream(realm, group); } else if (provider instanceof UserFederatedStorageProvider) { - Stream ids = ((UserFederatedStorageProvider)provider).getMembership(realm, group, -1, -1).stream(); - return ids.map(id -> getUserById(id, realm)); + return ((UserFederatedStorageProvider)provider).getMembershipStream(realm, group, -1, -1). + map(id -> getUserById(id, realm)); } return Stream.empty(); }, realm, firstResult, maxResults); - return importValidation(realm, results).collect(Collectors.toList()); + return importValidation(realm, results); } @Override - public List getRoleMembers(RealmModel realm, RoleModel role) { - return getRoleMembers(realm, role, -1, -1); + public Stream getRoleMembersStream(RealmModel realm, RoleModel role) { + return getRoleMembersStream(realm, role, -1, -1); } @Override - public List getRoleMembers(final RealmModel realm, final RoleModel role, int firstResult, int maxResults) { + public Stream getRoleMembersStream(final RealmModel realm, final RoleModel role, int firstResult, int maxResults) { Stream results = query((provider) -> { if (provider instanceof UserQueryProvider) { - return ((UserQueryProvider)provider).getRoleMembers(realm, role).stream(); + return ((UserQueryProvider)provider).getRoleMembersStream(realm, role); } return Stream.empty(); }, realm, firstResult, maxResults); - return importValidation(realm, results).collect(Collectors.toList()); + return importValidation(realm, results); } @Override - public List getUsers(RealmModel realm) { - return getUsers(realm, false); + public Stream getUsersStream(RealmModel realm) { + return getUsersStream(realm, false); } @Override - public List getUsers(RealmModel realm, int firstResult, int maxResults) { - return getUsers(realm, firstResult, maxResults, false); + public Stream getUsersStream(RealmModel realm, int firstResult, int maxResults) { + return getUsersStream(realm, firstResult, maxResults, false); } @Override - public List getUsers(RealmModel realm, boolean includeServiceAccounts) { - return getUsers(realm, 0, Integer.MAX_VALUE - 1, includeServiceAccounts); + public Stream getUsersStream(RealmModel realm, boolean includeServiceAccounts) { + return getUsersStream(realm, 0, Integer.MAX_VALUE - 1, includeServiceAccounts); } @Override - public List getUsers(final RealmModel realm, int firstResult, int maxResults, final boolean includeServiceAccounts) { + public Stream getUsersStream(final RealmModel realm, int firstResult, int maxResults, final boolean includeServiceAccounts) { Stream results = query((provider) -> { - if (provider instanceof UserProvider) { // it is local storage - return ((UserProvider) provider).getUsers(realm, includeServiceAccounts).stream(); - } else if (provider instanceof UserQueryProvider) { - return ((UserQueryProvider)provider).getUsers(realm).stream(); - } - return Stream.empty(); - } - , realm, firstResult, maxResults); - return importValidation(realm, results).collect(Collectors.toList()); + if (provider instanceof UserProvider) { // it is local storage + return ((UserProvider) provider).getUsersStream(realm, includeServiceAccounts); + } else if (provider instanceof UserQueryProvider) { + return ((UserQueryProvider)provider).getUsersStream(realm); + } + return Stream.empty(); + } + , realm, firstResult, maxResults); + return importValidation(realm, results); } @Override @@ -377,51 +371,49 @@ public class UserStorageManager extends AbstractStorageManager searchForUser(String search, RealmModel realm) { - return searchForUser(search, realm, 0, Integer.MAX_VALUE - 1); + public Stream searchForUserStream(String search, RealmModel realm) { + return searchForUserStream(search, realm, 0, Integer.MAX_VALUE - 1); } @Override - public List searchForUser(String search, RealmModel realm, int firstResult, int maxResults) { + public Stream searchForUserStream(String search, RealmModel realm, int firstResult, int maxResults) { Stream results = query((provider) -> { if (provider instanceof UserQueryProvider) { - return ((UserQueryProvider)provider).searchForUser(search, realm).stream(); - + return ((UserQueryProvider)provider).searchForUserStream(search, realm); } return Stream.empty(); }, realm, firstResult, maxResults); - return importValidation(realm, results).collect(Collectors.toList()); + return importValidation(realm, results); } @Override - public List searchForUser(Map attributes, RealmModel realm) { - return searchForUser(attributes, realm, 0, Integer.MAX_VALUE - 1); + public Stream searchForUserStream(Map attributes, RealmModel realm) { + return searchForUserStream(attributes, realm, 0, Integer.MAX_VALUE - 1); } @Override - public List searchForUser(Map attributes, RealmModel realm, int firstResult, int maxResults) { - Stream results = query((provider) -> { - if (provider instanceof UserQueryProvider) { - if (attributes.containsKey(UserModel.SEARCH)) { - return ((UserQueryProvider)provider).searchForUser(attributes.get(UserModel.SEARCH), realm).stream(); - } else { - return ((UserQueryProvider)provider).searchForUser(attributes, realm).stream(); - } - } - return Stream.empty(); - } - , realm, firstResult, maxResults); - return importValidation(realm, results).collect(Collectors.toList()); - - } - - @Override - public List searchForUserByUserAttribute(String attrName, String attrValue, RealmModel realm) { + public Stream searchForUserStream(Map attributes, RealmModel realm, int firstResult, int maxResults) { Stream results = query((provider) -> { if (provider instanceof UserQueryProvider) { - return ((UserQueryProvider)provider).searchForUserByUserAttribute(attrName, attrValue, realm).stream(); + if (attributes.containsKey(UserModel.SEARCH)) { + return ((UserQueryProvider)provider).searchForUserStream(attributes.get(UserModel.SEARCH), realm); + } else { + return ((UserQueryProvider)provider).searchForUserStream(attributes, realm); + } + } + return Stream.empty(); + } + , realm, firstResult, maxResults); + return importValidation(realm, results); + } + + @Override + public Stream searchForUserByUserAttributeStream(String attrName, String attrValue, RealmModel realm) { + Stream results = query((provider) -> { + if (provider instanceof UserQueryProvider) { + return ((UserQueryProvider)provider).searchForUserByUserAttributeStream(attrName, attrValue, realm); } else if (provider instanceof UserFederatedStorageProvider) { - return ((UserFederatedStorageProvider)provider).getUsersByUserAttribute(realm, attrName, attrValue).stream() + return ((UserFederatedStorageProvider)provider).getUsersByUserAttributeStream(realm, attrName, attrValue) .map(id -> getUserById(id, realm)) .filter(Objects::nonNull); @@ -432,7 +424,7 @@ public class UserStorageManager extends AbstractStorageManager getConsents(RealmModel realm, String userId) { + public Stream getConsentsStream(RealmModel realm, String userId) { if (StorageId.isLocalStorage(userId)) { - return localStorage().getConsents(realm, userId); + return localStorage().getConsentsStream(realm, userId); } else { - return getFederatedStorage().getConsents(realm, userId); + return getFederatedStorage().getConsentsStream(realm, userId); } } @@ -608,14 +600,13 @@ public class UserStorageManager extends AbstractStorageManager getFederatedIdentities(UserModel user, RealmModel realm) { + public Stream getFederatedIdentitiesStream(UserModel user, RealmModel realm) { if (user == null) throw new IllegalStateException("Federated user no longer valid"); - Set set = new HashSet<>(); - if (StorageId.isLocalStorage(user)) { - set.addAll(localStorage().getFederatedIdentities(user, realm)); - } - if (getFederatedStorage() != null) set.addAll(getFederatedStorage().getFederatedIdentities(user.getId(), realm)); - return set; + Stream stream = StorageId.isLocalStorage(user) ? + localStorage().getFederatedIdentitiesStream(user, realm) : Stream.empty(); + if (getFederatedStorage() != null) + stream = Streams.concat(stream, getFederatedStorage().getFederatedIdentitiesStream(user.getId(), realm)); + return stream.distinct(); } @Override diff --git a/testsuite/integration-arquillian/servers/auth-server/services/testsuite-providers/src/main/java/org/keycloak/testsuite/federation/BackwardsCompatibilityUserStorage.java b/testsuite/integration-arquillian/servers/auth-server/services/testsuite-providers/src/main/java/org/keycloak/testsuite/federation/BackwardsCompatibilityUserStorage.java index 9f919228f0..ad8563e63a 100644 --- a/testsuite/integration-arquillian/servers/auth-server/services/testsuite-providers/src/main/java/org/keycloak/testsuite/federation/BackwardsCompatibilityUserStorage.java +++ b/testsuite/integration-arquillian/servers/auth-server/services/testsuite-providers/src/main/java/org/keycloak/testsuite/federation/BackwardsCompatibilityUserStorage.java @@ -18,13 +18,10 @@ package org.keycloak.testsuite.federation; -import java.util.Arrays; -import java.util.Collections; import java.util.HashSet; -import java.util.List; import java.util.Map; import java.util.Set; -import java.util.stream.Collectors; +import java.util.stream.Stream; import org.jboss.logging.Logger; import org.keycloak.common.util.Time; @@ -319,58 +316,57 @@ public class BackwardsCompatibilityUserStorage implements UserLookupProvider, Us } @Override - public List getUsers(RealmModel realm) { - return getUsers(realm, -1, -1); + public Stream getUsersStream(RealmModel realm) { + return getUsersStream(realm, -1, -1); } @Override - public List getUsers(RealmModel realm, int firstResult, int maxResults) { + public Stream getUsersStream(RealmModel realm, int firstResult, int maxResults) { return users.values() .stream() .skip(firstResult).limit(maxResults) - .map(myUser -> createUser(realm, myUser.username)) - .collect(Collectors.toList()); + .map(myUser -> createUser(realm, myUser.username)); } @Override - public List searchForUser(String search, RealmModel realm) { - return searchForUser(search, realm, -1, -1); + public Stream searchForUserStream(String search, RealmModel realm) { + return searchForUserStream(search, realm, -1, -1); } @Override - public List searchForUser(String search, RealmModel realm, int firstResult, int maxResults) { + public Stream searchForUserStream(String search, RealmModel realm, int firstResult, int maxResults) { UserModel user = getUserByUsername(search, realm); - return user == null ? Collections.emptyList() : Arrays.asList(user); + return user == null ? Stream.empty() : Stream.of(user); } @Override - public List searchForUser(Map params, RealmModel realm) { + public Stream searchForUserStream(Map params, RealmModel realm) { // Assume that this is not supported - return Collections.emptyList(); + return Stream.empty(); } @Override - public List searchForUser(Map params, RealmModel realm, int firstResult, int maxResults) { + public Stream searchForUserStream(Map params, RealmModel realm, int firstResult, int maxResults) { // Assume that this is not supported - return Collections.emptyList(); + return Stream.empty(); } @Override - public List getGroupMembers(RealmModel realm, GroupModel group, int firstResult, int maxResults) { + public Stream getGroupMembersStream(RealmModel realm, GroupModel group, int firstResult, int maxResults) { // Assume that this is not supported - return Collections.emptyList(); + return Stream.empty(); } @Override - public List getGroupMembers(RealmModel realm, GroupModel group) { + public Stream getGroupMembersStream(RealmModel realm, GroupModel group) { // Assume that this is not supported - return Collections.emptyList(); + return Stream.empty(); } @Override - public List searchForUserByUserAttribute(String attrName, String attrValue, RealmModel realm) { + public Stream searchForUserByUserAttributeStream(String attrName, String attrValue, RealmModel realm) { // Assume that this is not supported - return Collections.emptyList(); + return Stream.empty(); } @Override diff --git a/testsuite/integration-arquillian/servers/auth-server/services/testsuite-providers/src/main/java/org/keycloak/testsuite/federation/FailableHardcodedStorageProvider.java b/testsuite/integration-arquillian/servers/auth-server/services/testsuite-providers/src/main/java/org/keycloak/testsuite/federation/FailableHardcodedStorageProvider.java index da60dc9a59..23eb58283d 100644 --- a/testsuite/integration-arquillian/servers/auth-server/services/testsuite-providers/src/main/java/org/keycloak/testsuite/federation/FailableHardcodedStorageProvider.java +++ b/testsuite/integration-arquillian/servers/auth-server/services/testsuite-providers/src/main/java/org/keycloak/testsuite/federation/FailableHardcodedStorageProvider.java @@ -21,7 +21,6 @@ import org.keycloak.component.ComponentModel; import org.keycloak.credential.CredentialInput; import org.keycloak.credential.CredentialInputUpdater; import org.keycloak.credential.CredentialInputValidator; -import org.keycloak.credential.CredentialModel; import org.keycloak.models.GroupModel; import org.keycloak.models.KeycloakSession; import org.keycloak.models.RealmModel; @@ -35,11 +34,10 @@ import org.keycloak.storage.user.UserLookupProvider; import org.keycloak.storage.user.UserQueryProvider; import java.util.Collections; -import java.util.HashMap; -import java.util.LinkedList; import java.util.List; import java.util.Map; import java.util.Set; +import java.util.stream.Stream; /** * @author Bill Burke @@ -224,79 +222,67 @@ public class FailableHardcodedStorageProvider implements UserStorageProvider, Us } @Override - public List getUsers(RealmModel realm) { + public Stream getUsersStream(RealmModel realm) { checkForceFail(); - UserModel hardcoded = getUserByUsername(username, realm); - List list = new LinkedList<>(); - list.add(hardcoded); - return list; + UserModel model = getUserByUsername(username, realm); + return model != null ? Stream.of(model) : Stream.empty(); } @Override - public List getUsers(RealmModel realm, int firstResult, int maxResults) { + public Stream getUsersStream(RealmModel realm, int firstResult, int maxResults) { checkForceFail(); - UserModel hardcoded = getUserByUsername(username, realm); - List list = new LinkedList<>(); - list.add(hardcoded); - return list; + UserModel model = getUserByUsername(username, realm); + return model != null ? Stream.of(model) : Stream.empty(); } @Override - public List searchForUser(String search, RealmModel realm) { + public Stream searchForUserStream(String search, RealmModel realm) { checkForceFail(); - if (!search.equals(username)) return Collections.EMPTY_LIST; - UserModel hardcoded = getUserByUsername(username, realm); - List list = new LinkedList<>(); - list.add(hardcoded); - return list; + if (!search.equals(username)) return Stream.empty(); + UserModel model = getUserByUsername(username, realm); + return model != null ? Stream.of(model) : Stream.empty(); } @Override - public List searchForUser(String search, RealmModel realm, int firstResult, int maxResults) { + public Stream searchForUserStream(String search, RealmModel realm, int firstResult, int maxResults) { checkForceFail(); - if (!search.equals(username)) return Collections.EMPTY_LIST; - UserModel hardcoded = getUserByUsername(username, realm); - List list = new LinkedList<>(); - list.add(hardcoded); - return list; + if (!search.equals(username)) return Stream.empty(); + UserModel model = getUserByUsername(username, realm); + return model != null ? Stream.of(model) : Stream.empty(); } @Override - public List searchForUser(Map params, RealmModel realm) { + public Stream searchForUserStream(Map params, RealmModel realm) { checkForceFail(); - if (!username.equals(params.get("username")))return Collections.EMPTY_LIST; - UserModel hardcoded = getUserByUsername(username, realm); - List list = new LinkedList<>(); - list.add(hardcoded); - return list; + if (!username.equals(params.get("username")))return Stream.empty(); + UserModel model = getUserByUsername(username, realm); + return model != null ? Stream.of(model) : Stream.empty(); } @Override - public List searchForUser(Map params, RealmModel realm, int firstResult, int maxResults) { + public Stream searchForUserStream(Map params, RealmModel realm, int firstResult, int maxResults) { checkForceFail(); - if (!username.equals(params.get("username")))return Collections.EMPTY_LIST; - UserModel hardcoded = getUserByUsername(username, realm); - List list = new LinkedList<>(); - list.add(hardcoded); - return list; + if (!username.equals(params.get("username")))return Stream.empty(); + UserModel model = getUserByUsername(username, realm); + return model != null ? Stream.of(model) : Stream.empty(); } @Override - public List getGroupMembers(RealmModel realm, GroupModel group, int firstResult, int maxResults) { + public Stream getGroupMembersStream(RealmModel realm, GroupModel group, int firstResult, int maxResults) { checkForceFail(); - return Collections.EMPTY_LIST; + return Stream.empty(); } @Override - public List getGroupMembers(RealmModel realm, GroupModel group) { + public Stream getGroupMembersStream(RealmModel realm, GroupModel group) { checkForceFail(); - return Collections.EMPTY_LIST; + return Stream.empty(); } @Override - public List searchForUserByUserAttribute(String attrName, String attrValue, RealmModel realm) { + public Stream searchForUserByUserAttributeStream(String attrName, String attrValue, RealmModel realm) { checkForceFail(); - return Collections.EMPTY_LIST; + return Stream.empty(); } @Override diff --git a/testsuite/integration-arquillian/servers/auth-server/services/testsuite-providers/src/main/java/org/keycloak/testsuite/federation/UserMapStorage.java b/testsuite/integration-arquillian/servers/auth-server/services/testsuite-providers/src/main/java/org/keycloak/testsuite/federation/UserMapStorage.java index 2f2f16b31a..dc8da1c643 100644 --- a/testsuite/integration-arquillian/servers/auth-server/services/testsuite-providers/src/main/java/org/keycloak/testsuite/federation/UserMapStorage.java +++ b/testsuite/integration-arquillian/servers/auth-server/services/testsuite-providers/src/main/java/org/keycloak/testsuite/federation/UserMapStorage.java @@ -40,14 +40,12 @@ import org.keycloak.storage.user.UserQueryProvider; import org.keycloak.storage.user.UserRegistrationProvider; import java.util.Collections; -import java.util.List; import java.util.Map; import java.util.Objects; import java.util.Set; import java.util.concurrent.ConcurrentMap; import java.util.concurrent.ConcurrentSkipListSet; import java.util.concurrent.atomic.AtomicInteger; -import java.util.stream.Collectors; import java.util.stream.Stream; import org.jboss.logging.Logger; import static org.keycloak.storage.UserStorageProviderModel.IMPORT_ENABLED; @@ -293,49 +291,48 @@ public class UserMapStorage implements UserLookupProvider, UserStorageProvider, } @Override - public List getUsers(RealmModel realm) { + public Stream getUsersStream(RealmModel realm) { return userPasswords.keySet().stream() - .map(userName -> createUser(realm, userName)) - .collect(Collectors.toList()); + .map(userName -> createUser(realm, userName)); } @Override - public List getUsers(RealmModel realm, int firstResult, int maxResults) { - return userPasswords.keySet().stream() - .sorted() - .skip(firstResult) - .limit(maxResults) - .map(userName -> createUser(realm, userName)) - .collect(Collectors.toList()); + public Stream getUsersStream(RealmModel realm, int firstResult, int maxResults) { + Stream userStream = userPasswords.keySet().stream().sorted(); + if (firstResult > 0) + userStream = userStream.skip(firstResult); + if (maxResults >= 0) + userStream = userStream.limit(maxResults); + return userStream.map(userName -> createUser(realm, userName)); } @Override - public List searchForUser(String search, RealmModel realm) { + public Stream searchForUserStream(String search, RealmModel realm) { return userPasswords.keySet().stream() .sorted() .filter(userName -> userName.contains(search)) - .map(userName -> createUser(realm, userName)) - .collect(Collectors.toList()); + .map(userName -> createUser(realm, userName)); } @Override - public List searchForUser(String search, RealmModel realm, int firstResult, int maxResults) { - return userPasswords.keySet().stream() - .sorted() - .filter(userName -> userName.contains(search)) - .skip(firstResult) - .limit(maxResults) - .map(userName -> createUser(realm, userName)) - .collect(Collectors.toList()); + public Stream searchForUserStream(String search, RealmModel realm, int firstResult, int maxResults) { + Stream userStream = userPasswords.keySet().stream() + .sorted() + .filter(userName -> userName.contains(search)); + if (firstResult > 0) + userStream = userStream.skip(firstResult); + if (maxResults >= 0) + userStream = userStream.limit(maxResults); + return userStream.map(userName -> createUser(realm, userName)); } @Override - public List searchForUser(Map params, RealmModel realm) { - return searchForUser(params, realm, 0, Integer.MAX_VALUE - 1); + public Stream searchForUserStream(Map params, RealmModel realm) { + return searchForUserStream(params, realm, 0, Integer.MAX_VALUE - 1); } @Override - public List searchForUser(Map params, RealmModel realm, int firstResult, int maxResults) { + public Stream searchForUserStream(Map params, RealmModel realm, int firstResult, int maxResults) { Stream userStream = userPasswords.keySet().stream() .sorted(); @@ -358,33 +355,31 @@ public class UserMapStorage implements UserLookupProvider, UserStorageProvider, } } - return userStream - .skip(firstResult) - .limit(maxResults) - .map(userName -> createUser(realm, userName)) - .collect(Collectors.toList()); + if (firstResult > 0) + userStream = userStream.skip(firstResult); + if (maxResults >= 0) + userStream = userStream.limit(maxResults); + return userStream.map(userName -> createUser(realm, userName)); } @Override - public List getGroupMembers(RealmModel realm, GroupModel group, int firstResult, int maxResults) { - return getMembership(realm, group, firstResult, maxResults).stream() - .map(userName -> createUser(realm, userName)) - .collect(Collectors.toList()); + public Stream getGroupMembersStream(RealmModel realm, GroupModel group, int firstResult, int maxResults) { + return getMembershipStream(realm, group, firstResult, maxResults) + .map(userName -> createUser(realm, userName)); } @Override - public List getGroupMembers(RealmModel realm, GroupModel group) { - return getGroupMembers(realm, group, 0, Integer.MAX_VALUE - 1); + public Stream getGroupMembersStream(RealmModel realm, GroupModel group) { + return getGroupMembersStream(realm, group, 0, Integer.MAX_VALUE - 1); } @Override - public List searchForUserByUserAttribute(String attrName, String attrValue, RealmModel realm) { + public Stream searchForUserByUserAttributeStream(String attrName, String attrValue, RealmModel realm) { if (isImportEnabled()) { - return session.userLocalStorage().searchForUserByUserAttribute(attrName, attrValue, realm); + return session.userLocalStorage().searchForUserByUserAttributeStream(attrName, attrValue, realm); } else { - return session.userFederatedStorage().getUsersByUserAttribute(realm, attrName, attrValue).stream() - .map(userName -> createUser(realm, userName)) - .collect(Collectors.toList()); + return session.userFederatedStorage().getUsersByUserAttributeStream(realm, attrName, attrValue) + .map(userName -> createUser(realm, userName)); } } @@ -412,15 +407,17 @@ public class UserMapStorage implements UserLookupProvider, UserStorageProvider, } @Override - public List getMembership(RealmModel realm, GroupModel group, int firstResult, int max) { - return userGroups.entrySet().stream() + public Stream getMembershipStream(RealmModel realm, GroupModel group, int firstResult, int max) { + Stream userStream = userGroups.entrySet().stream() .filter(me -> me.getValue().contains(group.getId())) .map(Map.Entry::getKey) .filter(realmUser -> realmUser.startsWith(realm.getId())) - .map(realmUser -> realmUser.substring(realmUser.indexOf("/") + 1)) - .skip(firstResult) - .limit(max) - .collect(Collectors.toList()); + .map(realmUser -> realmUser.substring(realmUser.indexOf("/") + 1)); + if (firstResult > 0) + userStream = userStream.skip(firstResult); + if (max >= 0) + userStream = userStream.limit(max); + return userStream; } @Override diff --git a/testsuite/integration-arquillian/servers/auth-server/services/testsuite-providers/src/main/java/org/keycloak/testsuite/federation/UserPropertyFileStorage.java b/testsuite/integration-arquillian/servers/auth-server/services/testsuite-providers/src/main/java/org/keycloak/testsuite/federation/UserPropertyFileStorage.java index b9844bd19a..dd8ba1594e 100644 --- a/testsuite/integration-arquillian/servers/auth-server/services/testsuite-providers/src/main/java/org/keycloak/testsuite/federation/UserPropertyFileStorage.java +++ b/testsuite/integration-arquillian/servers/auth-server/services/testsuite-providers/src/main/java/org/keycloak/testsuite/federation/UserPropertyFileStorage.java @@ -33,13 +33,11 @@ import org.keycloak.storage.adapter.AbstractUserAdapterFederatedStorage; import org.keycloak.storage.user.UserLookupProvider; import org.keycloak.storage.user.UserQueryProvider; -import java.util.Collections; -import java.util.LinkedList; -import java.util.List; import java.util.Map; import java.util.Optional; import java.util.Properties; import java.util.function.Predicate; +import java.util.stream.Stream; /** * @author Bill Burke @@ -146,67 +144,59 @@ public class UserPropertyFileStorage implements UserLookupProvider, UserStorageP } @Override - public List getUsers(RealmModel realm) { - List users = new LinkedList<>(); - for (Object username : userPasswords.keySet()) { - users.add(createUser(realm, (String)username)); - } - return users; + public Stream getUsersStream(RealmModel realm) { + return userPasswords.keySet().stream().map(obj -> createUser(realm, (String) obj)); } @Override - public List searchForUser(Map attributes, RealmModel realm) { - return searchForUser(attributes, realm, 0, Integer.MAX_VALUE - 1); + public Stream searchForUserStream(Map attributes, RealmModel realm) { + return searchForUserStream(attributes, realm, 0, Integer.MAX_VALUE - 1); } @Override - public List getUsers(RealmModel realm, int firstResult, int maxResults) { - if (maxResults == 0) return Collections.EMPTY_LIST; - List users = new LinkedList<>(); - int count = 0; - for (Object un : userPasswords.keySet()) { - if (count++ < firstResult) continue; - String username = (String)un; - users.add(createUser(realm, username)); - if (users.size() + 1 > maxResults) break; - } - return users; + public Stream getUsersStream(RealmModel realm, int firstResult, int maxResults) { + Stream stream = userPasswords.keySet().stream(); + if (firstResult > 0) + stream = stream.skip(firstResult); + if (maxResults >= 0) + stream = stream.limit(maxResults); + return stream.map(obj -> createUser(realm, (String) obj)); } @Override - public List searchForUser(String search, RealmModel realm, int firstResult, int maxResults) { - return searchForUser(search, realm, firstResult, maxResults, username -> username.contains(search)); + public Stream searchForUserStream(String search, RealmModel realm, int firstResult, int maxResults) { + return searchForUserStream(search, realm, firstResult, maxResults, username -> username.contains(search)); } @Override - public List searchForUser(Map attributes, RealmModel realm, int firstResult, int maxResults) { + public Stream searchForUserStream(Map attributes, RealmModel realm, int firstResult, int maxResults) { String search = Optional.ofNullable(attributes.get(UserModel.USERNAME)) .orElseGet(()-> attributes.get(UserModel.SEARCH)); - if (search == null) return Collections.EMPTY_LIST; + if (search == null) return Stream.empty(); Predicate p = Boolean.valueOf(attributes.getOrDefault(UserModel.EXACT, Boolean.FALSE.toString())) ? username -> username.equals(search) : username -> username.contains(search); - return searchForUser(search, realm, firstResult, maxResults, p); + return searchForUserStream(search, realm, firstResult, maxResults, p); } @Override - public List getGroupMembers(RealmModel realm, GroupModel group, int firstResult, int maxResults) { - return Collections.EMPTY_LIST; + public Stream getGroupMembersStream(RealmModel realm, GroupModel group, int firstResult, int maxResults) { + return Stream.empty(); } @Override - public List getGroupMembers(RealmModel realm, GroupModel group) { - return Collections.EMPTY_LIST; + public Stream getGroupMembersStream(RealmModel realm, GroupModel group) { + return Stream.empty(); } @Override - public List searchForUser(String search, RealmModel realm) { - return searchForUser(search, realm, 0, Integer.MAX_VALUE - 1); + public Stream searchForUserStream(String search, RealmModel realm) { + return searchForUserStream(search, realm, 0, Integer.MAX_VALUE - 1); } @Override - public List searchForUserByUserAttribute(String attrName, String attrValue, RealmModel realm) { - return Collections.EMPTY_LIST; + public Stream searchForUserByUserAttributeStream(String attrName, String attrValue, RealmModel realm) { + return Stream.empty(); } @Override @@ -214,20 +204,8 @@ public class UserPropertyFileStorage implements UserLookupProvider, UserStorageP } - private List searchForUser(String search, RealmModel realm, int firstResult, int maxResults, Predicate matcher) { - if (maxResults == 0) return Collections.EMPTY_LIST; - List users = new LinkedList<>(); - int count = 0; - for (Object un : userPasswords.keySet()) { - String username = (String)un; - if (matcher.test(username)) { - if (count++ < firstResult) { - continue; - } - users.add(createUser(realm, username)); - if (users.size() + 1 > maxResults) break; - } - } - return users; + private Stream searchForUserStream(String search, RealmModel realm, int firstResult, int maxResults, Predicate matcher) { + return userPasswords.keySet().stream().filter(obj -> matcher.test((String) obj)).skip(firstResult < 0 ? 0 : firstResult) + .limit(maxResults < 0 ? 0 : maxResults).map(obj -> createUser(realm, (String) obj)); } } diff --git a/testsuite/integration-arquillian/servers/auth-server/services/testsuite-providers/src/main/java/org/keycloak/testsuite/util/LDAPTestUtils.java b/testsuite/integration-arquillian/servers/auth-server/services/testsuite-providers/src/main/java/org/keycloak/testsuite/util/LDAPTestUtils.java index 2aaf037505..9faf40eece 100644 --- a/testsuite/integration-arquillian/servers/auth-server/services/testsuite-providers/src/main/java/org/keycloak/testsuite/util/LDAPTestUtils.java +++ b/testsuite/integration-arquillian/servers/auth-server/services/testsuite-providers/src/main/java/org/keycloak/testsuite/util/LDAPTestUtils.java @@ -44,7 +44,13 @@ import org.keycloak.storage.ldap.mappers.membership.role.RoleLDAPStorageMapper; import org.keycloak.storage.ldap.mappers.membership.role.RoleLDAPStorageMapperFactory; import org.keycloak.storage.ldap.mappers.membership.role.RoleMapperConfig; -import java.util.*; +import java.util.Collections; +import java.util.HashMap; +import java.util.List; +import java.util.Map; +import java.util.Objects; +import java.util.Set; +import java.util.stream.Stream; /** * @author Marek Posolda @@ -101,21 +107,21 @@ public class LDAPTestUtils { } @Override - public List getAttribute(String name) { + public Stream getAttributeStream(String name) { if (UserModel.LAST_NAME.equals(name)) { - return Collections.singletonList(lastName); + return Stream.of(lastName); } else if (UserModel.FIRST_NAME.equals(name)) { - return Collections.singletonList(firstName); + return Stream.of(firstName); } else if (UserModel.EMAIL.equals(name)) { - return Collections.singletonList(email); + return Stream.of(email); } else if (UserModel.USERNAME.equals(name)) { - return Collections.singletonList(username); + return Stream.of(username); } else if ("postal_code".equals(name) && postalCode != null && postalCode.length > 0) { - return Arrays.asList(postalCode); + return Stream.of(postalCode); } else if ("street".equals(name) && street != null) { - return Collections.singletonList(street); + return Stream.of(street); } else { - return Collections.emptyList(); + return Stream.empty(); } } }; diff --git a/testsuite/integration-arquillian/tests/base/src/test/java/org/keycloak/testsuite/federation/ldap/LDAPBinaryAttributesTest.java b/testsuite/integration-arquillian/tests/base/src/test/java/org/keycloak/testsuite/federation/ldap/LDAPBinaryAttributesTest.java index 78e08042f7..7f1e5d8014 100644 --- a/testsuite/integration-arquillian/tests/base/src/test/java/org/keycloak/testsuite/federation/ldap/LDAPBinaryAttributesTest.java +++ b/testsuite/integration-arquillian/tests/base/src/test/java/org/keycloak/testsuite/federation/ldap/LDAPBinaryAttributesTest.java @@ -45,6 +45,7 @@ import javax.ws.rs.core.Response; import java.util.Arrays; import java.util.Collections; import java.util.List; +import java.util.stream.Stream; /** * @author Marek Posolda @@ -227,19 +228,19 @@ public class LDAPBinaryAttributesTest extends AbstractLDAPTest { } @Override - public List getAttribute(String name) { + public Stream getAttributeStream(String name) { if (UserModel.LAST_NAME.equals(name)) { - return Collections.singletonList(lastName); + return Stream.of(lastName); } else if (UserModel.FIRST_NAME.equals(name)) { - return Collections.singletonList(firstName); + return Stream.of(firstName); } else if (UserModel.EMAIL.equals(name)) { - return Collections.singletonList(email); + return Stream.of(email); } else if (UserModel.USERNAME.equals(name)) { - return Collections.singletonList(username); + return Stream.of(username); } else if (LDAPConstants.JPEG_PHOTO.equals(name)) { - return Arrays.asList(jpegPhoto); + return Stream.of(jpegPhoto); } else { - return Collections.emptyList(); + return Stream.empty(); } } }; diff --git a/testsuite/utils/src/main/java/org/keycloak/testsuite/util/cli/LdapManyObjectsInitializerCommand.java b/testsuite/utils/src/main/java/org/keycloak/testsuite/util/cli/LdapManyObjectsInitializerCommand.java index fdf246c015..322a8087ac 100644 --- a/testsuite/utils/src/main/java/org/keycloak/testsuite/util/cli/LdapManyObjectsInitializerCommand.java +++ b/testsuite/utils/src/main/java/org/keycloak/testsuite/util/cli/LdapManyObjectsInitializerCommand.java @@ -17,8 +17,15 @@ package org.keycloak.testsuite.util.cli; -import java.util.*; +import java.util.HashMap; +import java.util.HashSet; +import java.util.List; +import java.util.Map; +import java.util.Objects; +import java.util.Optional; +import java.util.Set; import java.util.stream.Collectors; +import java.util.stream.Stream; import org.keycloak.component.ComponentModel; import org.keycloak.models.KeycloakSession; @@ -166,28 +173,26 @@ public class LdapManyObjectsInitializerCommand extends AbstractCommand { } @Override - public List getAttribute(String name) { + public Stream getAttributeStream(String name) { if (UserModel.FIRST_NAME.equals(name)) { - return Collections.singletonList(firstName); + return Stream.of(firstName); } else if (UserModel.LAST_NAME.equals(name)) { - return Collections.singletonList(lastName); + return Stream.of(lastName); } else if (UserModel.EMAIL.equals(name)) { - return Collections.singletonList(email); + return Stream.of(email); } else if (UserModel.USERNAME.equals(name)) { - return Collections.singletonList(username); + return Stream.of(username); } else if ("street".equals(name)) { - - List groupNamesList = new ArrayList<>(); - for (int i=startOffsetGroups ; i builder = Stream.builder(); + for (int i = startOffsetGroups; i < startOffsetGroups + countGroups; i++) { String groupName = "group" + i; LDAPDn groupDn = LDAPDn.fromString(groupsDN); groupDn.addFirst("cn", groupName); - groupNamesList.add(groupDn.toString()); + builder.add(groupDn.toString()); } - return groupNamesList; - + return builder.build(); } else { - return Collections.emptyList(); + return Stream.empty(); } } };