KEYCLOAK-12988 Deprecate getUsers* methods in favor of searchUsers* variants

Closes #14018
This commit is contained in:
Michal Hajas 2022-08-26 13:09:41 +02:00 committed by Hynek Mlnařík
parent f789b7997e
commit f69497eb28
17 changed files with 98 additions and 160 deletions

View file

@ -544,15 +544,11 @@ public class UserCacheSession implements UserCache.Streams, OnCreateComponent, O
} }
} }
@Override
public Stream<UserModel> getUsersStream(RealmModel realm, boolean includeServiceAccounts) {
return getDelegate().getUsersStream(realm, includeServiceAccounts);
}
@Override @Override
public CredentialValidationOutput getUserByCredential(RealmModel realm, CredentialInput input) { public CredentialValidationOutput getUserByCredential(RealmModel realm, CredentialInput input) {
return getDelegate().getUserByCredential(realm, input); return getDelegate().getUserByCredential(realm, input);
} }
@Override @Override
public int getUsersCount(RealmModel realm, boolean includeServiceAccount) { public int getUsersCount(RealmModel realm, boolean includeServiceAccount) {
return getDelegate().getUsersCount(realm, includeServiceAccount); return getDelegate().getUsersCount(realm, includeServiceAccount);
@ -583,21 +579,6 @@ public class UserCacheSession implements UserCache.Streams, OnCreateComponent, O
return getDelegate().getUsersCount(realm, params, groupIds); return getDelegate().getUsersCount(realm, params, groupIds);
} }
@Override
public Stream<UserModel> getUsersStream(RealmModel realm, Integer firstResult, Integer maxResults, boolean includeServiceAccounts) {
return getDelegate().getUsersStream(realm, firstResult, maxResults, includeServiceAccounts);
}
@Override
public Stream<UserModel> getUsersStream(RealmModel realm) {
return getUsersStream(realm, false);
}
@Override
public Stream<UserModel> getUsersStream(RealmModel realm, Integer firstResult, Integer maxResults) {
return getUsersStream(realm, firstResult, maxResults, false);
}
@Override @Override
public Stream<UserModel> searchForUserStream(RealmModel realm, String search) { public Stream<UserModel> searchForUserStream(RealmModel realm, String search) {
return getDelegate().searchForUserStream(realm, search); return getDelegate().searchForUserStream(realm, search);

View file

@ -68,6 +68,7 @@ import java.util.HashMap;
import java.util.HashSet; import java.util.HashSet;
import java.util.List; import java.util.List;
import java.util.Map; import java.util.Map;
import java.util.Objects;
import java.util.Set; import java.util.Set;
import java.util.stream.Stream; import java.util.stream.Stream;
@ -748,21 +749,6 @@ public class JpaUserProvider implements UserProvider.Streams, UserCredentialStor
return result.intValue(); return result.intValue();
} }
@Override
public Stream<UserModel> getUsersStream(RealmModel realm, Integer firstResult, Integer maxResults) {
return getUsersStream(realm, firstResult, maxResults, false);
}
@Override
public Stream<UserModel> getUsersStream(RealmModel realm, Integer firstResult, Integer maxResults, boolean includeServiceAccounts) {
String queryName = includeServiceAccounts ? "getAllUsersByRealm" : "getAllUsersByRealmExcludeServiceAccount" ;
TypedQuery<UserEntity> query = em.createNamedQuery(queryName, UserEntity.class);
query.setParameter("realmId", realm.getId());
return closing(paginateQuery(query, firstResult, maxResults).getResultStream().map(entity -> new UserAdapter(session, realm, em, entity)));
}
@Override @Override
public Stream<UserModel> getGroupMembersStream(RealmModel realm, GroupModel group, Integer firstResult, Integer maxResults) { public Stream<UserModel> getGroupMembersStream(RealmModel realm, GroupModel group, Integer firstResult, Integer maxResults) {
TypedQuery<UserEntity> query = em.createNamedQuery("groupMembership", UserEntity.class); TypedQuery<UserEntity> query = em.createNamedQuery("groupMembership", UserEntity.class);
@ -781,9 +767,10 @@ public class JpaUserProvider implements UserProvider.Streams, UserCredentialStor
@Override @Override
public Stream<UserModel> searchForUserStream(RealmModel realm, String search, Integer firstResult, Integer maxResults) { public Stream<UserModel> searchForUserStream(RealmModel realm, String search, Integer firstResult, Integer maxResults) {
Map<String, String> attributes = new HashMap<>(); Map<String, String> attributes = new HashMap<>(2);
attributes.put(UserModel.SEARCH, search); attributes.put(UserModel.SEARCH, search);
session.setAttribute(UserModel.INCLUDE_SERVICE_ACCOUNT, false); attributes.put(UserModel.INCLUDE_SERVICE_ACCOUNT, Boolean.FALSE.toString());
return searchForUserStream(realm, attributes, firstResult, maxResults); return searchForUserStream(realm, attributes, firstResult, maxResults);
} }
@ -798,10 +785,6 @@ public class JpaUserProvider implements UserProvider.Streams, UserCredentialStor
predicates.add(builder.equal(root.get("realmId"), realm.getId())); predicates.add(builder.equal(root.get("realmId"), realm.getId()));
if (!session.getAttributeOrDefault(UserModel.INCLUDE_SERVICE_ACCOUNT, true)) {
predicates.add(root.get("serviceAccountClientLink").isNull());
}
Join<Object, Object> federatedIdentitiesJoin = null; Join<Object, Object> federatedIdentitiesJoin = null;
for (Map.Entry<String, String> entry : attributes.entrySet()) { for (Map.Entry<String, String> entry : attributes.entrySet()) {
@ -863,6 +846,13 @@ public class JpaUserProvider implements UserProvider.Streams, UserCredentialStor
builder.equal(builder.lower(attributesJoin.get("value")), value.toLowerCase()))); builder.equal(builder.lower(attributesJoin.get("value")), value.toLowerCase())));
break; break;
case UserModel.INCLUDE_SERVICE_ACCOUNT: {
if (!attributes.containsKey(UserModel.INCLUDE_SERVICE_ACCOUNT)
|| !Boolean.parseBoolean(attributes.get(UserModel.INCLUDE_SERVICE_ACCOUNT))) {
predicates.add(root.get("serviceAccountClientLink").isNull());
}
break;
}
} }
} }
@ -908,7 +898,8 @@ public class JpaUserProvider implements UserProvider.Streams, UserCredentialStor
UserProvider users = session.users(); UserProvider users = session.users();
return closing(paginateQuery(query, firstResult, maxResults).getResultStream()) return closing(paginateQuery(query, firstResult, maxResults).getResultStream())
.map(userEntity -> users.getUserById(realm, userEntity.getId())); .map(userEntity -> users.getUserById(realm, userEntity.getId()))
.filter(Objects::nonNull);
} }
@Override @Override

View file

@ -42,8 +42,6 @@ import java.util.LinkedList;
* @version $Revision: 1 $ * @version $Revision: 1 $
*/ */
@NamedQueries({ @NamedQueries({
@NamedQuery(name="getAllUsersByRealm", query="select u from UserEntity u where u.realmId = :realmId order by u.username"),
@NamedQuery(name="getAllUsersByRealmExcludeServiceAccount", query="select u from UserEntity u where u.realmId = :realmId and (u.serviceAccountClientLink is null) order by u.username"),
@NamedQuery(name="getRealmUserByUsername", query="select u from UserEntity u where u.username = :username and u.realmId = :realmId"), @NamedQuery(name="getRealmUserByUsername", query="select u from UserEntity u where u.username = :username and u.realmId = :realmId"),
@NamedQuery(name="getRealmUserByEmail", query="select u from UserEntity u where u.email = :email and u.realmId = :realmId"), @NamedQuery(name="getRealmUserByEmail", query="select u from UserEntity u where u.email = :email and u.realmId = :realmId"),
@NamedQuery(name="getRealmUserByLastName", query="select u from UserEntity u where u.lastName = :lastName and u.realmId = :realmId"), @NamedQuery(name="getRealmUserByLastName", query="select u from UserEntity u where u.lastName = :lastName and u.realmId = :realmId"),

View file

@ -24,6 +24,7 @@ import org.keycloak.models.KeycloakSession;
import org.keycloak.models.LDAPConstants; import org.keycloak.models.LDAPConstants;
import org.keycloak.models.LegacyRealmModel; import org.keycloak.models.LegacyRealmModel;
import org.keycloak.models.RealmModel; import org.keycloak.models.RealmModel;
import org.keycloak.models.UserModel;
import org.keycloak.models.cache.UserCache; import org.keycloak.models.cache.UserCache;
import org.keycloak.models.utils.DefaultAuthenticationFlows; import org.keycloak.models.utils.DefaultAuthenticationFlows;
import org.keycloak.models.utils.DefaultRequiredActions; import org.keycloak.models.utils.DefaultRequiredActions;
@ -33,7 +34,9 @@ import org.keycloak.storage.UserStoragePrivateUtil;
import org.keycloak.storage.UserStorageUtil; import org.keycloak.storage.UserStorageUtil;
import java.util.Arrays; import java.util.Arrays;
import java.util.HashMap;
import java.util.List; import java.util.List;
import java.util.Map;
import java.util.Objects; import java.util.Objects;
/** /**
@ -81,7 +84,10 @@ public class MigrateTo1_4_0 implements Migration {
} }
private void migrateUsers(KeycloakSession session, RealmModel realm) { private void migrateUsers(KeycloakSession session, RealmModel realm) {
UserStoragePrivateUtil.userLocalStorage(session).getUsersStream(realm, false) Map<String, String> searchAttributes = new HashMap<>(1);
searchAttributes.put(UserModel.INCLUDE_SERVICE_ACCOUNT, Boolean.FALSE.toString());
UserStoragePrivateUtil.userLocalStorage(session).searchForUserStream(realm, searchAttributes)
.forEach(user -> { .forEach(user -> {
String email = KeycloakModelUtils.toLowerCaseSafe(user.getEmail()); String email = KeycloakModelUtils.toLowerCaseSafe(user.getEmail());
if (email != null && !email.equals(user.getEmail())) { if (email != null && !email.equals(user.getEmail())) {

View file

@ -379,31 +379,6 @@ public class UserStorageManager extends AbstractStorageManager<UserStorageProvid
return importValidation(realm, results); return importValidation(realm, results);
} }
@Override
public Stream<UserModel> getUsersStream(RealmModel realm) {
return getUsersStream(realm, null, null, false);
}
@Override
public Stream<UserModel> getUsersStream(RealmModel realm, Integer firstResult, Integer maxResults) {
return getUsersStream(realm, firstResult, maxResults, false);
}
@Override
public Stream<UserModel> getUsersStream(final RealmModel realm, Integer firstResult, Integer maxResults, final boolean includeServiceAccounts) {
Stream<UserModel> results = query((provider, firstResultInQuery, maxResultsInQuery) -> {
if (provider instanceof UserProvider) { // it is local storage
return ((UserProvider) provider).getUsersStream(realm, firstResultInQuery, maxResultsInQuery, includeServiceAccounts);
} else if (provider instanceof UserQueryProvider) {
return ((UserQueryProvider)provider).getUsersStream(realm);
}
return Stream.empty();
}
, realm, firstResult, maxResults);
return importValidation(realm, results);
}
@Override @Override
public int getUsersCount(RealmModel realm, boolean includeServiceAccount) { public int getUsersCount(RealmModel realm, boolean includeServiceAccount) {
int localStorageUsersCount = localStorage().getUsersCount(realm, includeServiceAccount); int localStorageUsersCount = localStorage().getUsersCount(realm, includeServiceAccount);

View file

@ -31,6 +31,7 @@ import org.keycloak.representations.idm.RealmRepresentation;
import org.keycloak.storage.UserStorageUtil; import org.keycloak.storage.UserStorageUtil;
import java.io.IOException; import java.io.IOException;
import java.util.Collections;
import java.util.List; import java.util.List;
import java.util.stream.Collectors; import java.util.stream.Collectors;
@ -114,7 +115,7 @@ public abstract class MultipleStepsExportProvider implements ExportProvider {
protected void runExportImportTask(KeycloakSession session) throws IOException { protected void runExportImportTask(KeycloakSession session) throws IOException {
RealmModel realm = session.realms().getRealmByName(realmName); RealmModel realm = session.realms().getRealmByName(realmName);
usersHolder.users = session.users() usersHolder.users = session.users()
.getUsersStream(realm, usersHolder.currentPageStart, usersHolder.currentPageEnd - usersHolder.currentPageStart, true) .searchForUserStream(realm, Collections.emptyMap(), usersHolder.currentPageStart, usersHolder.currentPageEnd - usersHolder.currentPageStart)
.collect(Collectors.toList()); .collect(Collectors.toList());
writeUsers(realmName + "-users-" + (usersHolder.currentPageStart / countPerPage) + ".json", session, realm, usersHolder.users); writeUsers(realmName + "-users-" + (usersHolder.currentPageStart / countPerPage) + ".json", session, realm, usersHolder.users);

View file

@ -540,32 +540,12 @@ public class MapUserProvider implements UserProvider.Streams {
return (int) tx.getCount(withCriteria(mcb)); return (int) tx.getCount(withCriteria(mcb));
} }
@Override
public Stream<UserModel> getUsersStream(RealmModel realm, Integer firstResult, Integer maxResults, boolean includeServiceAccounts) {
LOG.tracef("getUsersStream(%s, %d, %d, %s)%s", realm, firstResult, maxResults, includeServiceAccounts, getShortStackTrace());
DefaultModelCriteria<UserModel> mcb = criteria();
mcb = mcb.compare(SearchableFields.REALM_ID, Operator.EQ, realm.getId());
if (! includeServiceAccounts) {
mcb = mcb.compare(SearchableFields.SERVICE_ACCOUNT_CLIENT, Operator.NOT_EXISTS);
}
return tx.read(withCriteria(mcb).pagination(firstResult, maxResults, SearchableFields.USERNAME))
.map(entityToAdapterFunc(realm));
}
@Override
public Stream<UserModel> getUsersStream(RealmModel realm, Integer firstResult, Integer maxResults) {
LOG.tracef("getUsersStream(%s, %d, %d)%s", realm, firstResult, maxResults, getShortStackTrace());
return getUsersStream(realm, firstResult, maxResults, false);
}
@Override @Override
public Stream<UserModel> searchForUserStream(RealmModel realm, String search, Integer firstResult, Integer maxResults) { public Stream<UserModel> searchForUserStream(RealmModel realm, String search, Integer firstResult, Integer maxResults) {
LOG.tracef("searchForUserStream(%s, %s, %d, %d)%s", realm, search, firstResult, maxResults, getShortStackTrace()); LOG.tracef("searchForUserStream(%s, %s, %d, %d)%s", realm, search, firstResult, maxResults, getShortStackTrace());
Map<String, String> attributes = new HashMap<>(); Map<String, String> attributes = new HashMap<>();
attributes.put(UserModel.SEARCH, search); attributes.put(UserModel.SEARCH, search);
session.setAttribute(UserModel.INCLUDE_SERVICE_ACCOUNT, false); attributes.put(UserModel.INCLUDE_SERVICE_ACCOUNT, Boolean.FALSE.toString());
return searchForUserStream(realm, attributes, firstResult, maxResults); return searchForUserStream(realm, attributes, firstResult, maxResults);
} }
@ -576,10 +556,6 @@ public class MapUserProvider implements UserProvider.Streams {
final DefaultModelCriteria<UserModel> mcb = criteria(); final DefaultModelCriteria<UserModel> mcb = criteria();
DefaultModelCriteria<UserModel> criteria = mcb.compare(SearchableFields.REALM_ID, Operator.EQ, realm.getId()); DefaultModelCriteria<UserModel> criteria = mcb.compare(SearchableFields.REALM_ID, Operator.EQ, realm.getId());
if (! session.getAttributeOrDefault(UserModel.INCLUDE_SERVICE_ACCOUNT, true)) {
criteria = criteria.compare(SearchableFields.SERVICE_ACCOUNT_CLIENT, Operator.NOT_EXISTS);
}
final boolean exactSearch = Boolean.parseBoolean(attributes.getOrDefault(UserModel.EXACT, Boolean.FALSE.toString())); final boolean exactSearch = Boolean.parseBoolean(attributes.getOrDefault(UserModel.EXACT, Boolean.FALSE.toString()));
for (Map.Entry<String, String> entry : attributes.entrySet()) { for (Map.Entry<String, String> entry : attributes.entrySet()) {
@ -638,6 +614,13 @@ public class MapUserProvider implements UserProvider.Streams {
value); value);
break; break;
} }
case UserModel.INCLUDE_SERVICE_ACCOUNT: {
if (!attributes.containsKey(UserModel.INCLUDE_SERVICE_ACCOUNT)
|| !Boolean.parseBoolean(attributes.get(UserModel.INCLUDE_SERVICE_ACCOUNT))) {
criteria = criteria.compare(SearchableFields.SERVICE_ACCOUNT_CLIENT, Operator.NOT_EXISTS);
}
break;
}
case UserModel.EXACT: case UserModel.EXACT:
break; break;
default: default:

View file

@ -24,7 +24,9 @@ import org.keycloak.storage.user.UserLookupProvider;
import org.keycloak.storage.user.UserQueryProvider; import org.keycloak.storage.user.UserQueryProvider;
import org.keycloak.storage.user.UserRegistrationProvider; import org.keycloak.storage.user.UserRegistrationProvider;
import java.util.HashMap;
import java.util.List; import java.util.List;
import java.util.Map;
import java.util.Set; import java.util.Set;
import java.util.stream.Collectors; import java.util.stream.Collectors;
import java.util.stream.Stream; import java.util.stream.Stream;
@ -71,10 +73,13 @@ public interface UserProvider extends Provider,
UserModel getServiceAccount(ClientModel client); UserModel getServiceAccount(ClientModel client);
/** /**
* @deprecated Use {@link #getUsersStream(RealmModel, boolean) getUsersStream} instead. * @deprecated Use {@link UserQueryProvider#searchForUserStream(RealmModel, Map)} with
* {@link UserModel#INCLUDE_SERVICE_ACCOUNT} within params instead.
*/ */
@Deprecated @Deprecated
List<UserModel> getUsers(RealmModel realm, boolean includeServiceAccounts); default List<UserModel> getUsers(RealmModel realm, boolean includeServiceAccounts) {
return this.getUsersStream(realm, includeServiceAccounts).collect(Collectors.toList());
}
/** /**
* Obtains the users associated with the specified realm. * Obtains the users associated with the specified realm.
@ -82,17 +87,25 @@ public interface UserProvider extends Provider,
* @param realm a reference to the realm being used for the search. * @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. * @param includeServiceAccounts {@code true} if service accounts should be included in the result; {@code false} otherwise.
* @return a non-null {@link Stream} of users associated withe the realm. * @return a non-null {@link Stream} of users associated withe the realm.
*
* @deprecated Use {@link UserQueryProvider#searchForUserStream(RealmModel, Map)} with
* {@link UserModel#INCLUDE_SERVICE_ACCOUNT} within params instead.
*/ */
@Deprecated
default Stream<UserModel> getUsersStream(RealmModel realm, boolean includeServiceAccounts) { default Stream<UserModel> getUsersStream(RealmModel realm, boolean includeServiceAccounts) {
List<UserModel> value = this.getUsers(realm, includeServiceAccounts); Map<String, String> searchAttributes = new HashMap<>(1);
return value != null ? value.stream() : Stream.empty(); searchAttributes.put(UserModel.INCLUDE_SERVICE_ACCOUNT, Boolean.toString(includeServiceAccounts));
return this.searchForUserStream(realm, searchAttributes);
} }
/** /**
* @deprecated Use {@link #getUsersStream(RealmModel, Integer, Integer, boolean) getUsersStream} instead. * @deprecated Use {@link UserQueryProvider#searchForUserStream(RealmModel, Map, Integer, Integer)} with
* {@link UserModel#INCLUDE_SERVICE_ACCOUNT} within params instead.
*/ */
@Deprecated @Deprecated
List<UserModel> getUsers(RealmModel realm, int firstResult, int maxResults, boolean includeServiceAccounts); default List<UserModel> 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. * Obtains the users associated with the specified realm.
@ -102,10 +115,15 @@ public interface UserProvider extends Provider,
* @param maxResults maximum number of results to return. Ignored if negative or {@code null}. * @param maxResults maximum number of results to return. Ignored if negative or {@code null}.
* @param includeServiceAccounts {@code true} if service accounts should be included in the result; {@code false} otherwise. * @param includeServiceAccounts {@code true} if service accounts should be included in the result; {@code false} otherwise.
* @return a non-null {@link Stream} of users associated withe the realm. * @return a non-null {@link Stream} of users associated withe the realm.
*
* @deprecated Use {@link UserQueryProvider#searchForUserStream(RealmModel, Map, Integer, Integer)}
* with {@link UserModel#INCLUDE_SERVICE_ACCOUNT} within params
*/ */
@Deprecated
default Stream<UserModel> getUsersStream(RealmModel realm, Integer firstResult, Integer maxResults, boolean includeServiceAccounts) { default Stream<UserModel> getUsersStream(RealmModel realm, Integer firstResult, Integer maxResults, boolean includeServiceAccounts) {
List<UserModel> value = this.getUsers(realm, firstResult == null ? -1 : firstResult, maxResults == null ? -1 : maxResults, includeServiceAccounts); Map<String, String> searchAttributes = new HashMap<>(1);
return value != null ? value.stream() : Stream.empty(); searchAttributes.put(UserModel.INCLUDE_SERVICE_ACCOUNT, Boolean.toString(includeServiceAccounts));
return this.searchForUserStream(realm, searchAttributes, firstResult, maxResults);
} }
/** /**
@ -412,23 +430,5 @@ public interface UserProvider extends Provider,
@Override @Override
Stream<UserConsentModel> getConsentsStream(RealmModel realm, String userId); Stream<UserConsentModel> getConsentsStream(RealmModel realm, String userId);
@Override
default List<UserModel> getUsers(RealmModel realm, boolean includeServiceAccounts) {
return this.getUsersStream(realm, includeServiceAccounts).collect(Collectors.toList());
}
@Override
default Stream<UserModel> getUsersStream(RealmModel realm, boolean includeServiceAccounts) {
return getUsersStream(realm, null, null, includeServiceAccounts);
}
@Override
default List<UserModel> getUsers(RealmModel realm, int firstResult, int maxResults, boolean includeServiceAccounts) {
return this.getUsersStream(realm, firstResult, maxResults, includeServiceAccounts).collect(Collectors.toList());
}
@Override
Stream<UserModel> getUsersStream(RealmModel realm, Integer firstResult, Integer maxResults, boolean includeServiceAccounts);
} }
} }

View file

@ -64,7 +64,7 @@ public interface UserQueryProvider {
if (groupIds == null || groupIds.isEmpty()) { if (groupIds == null || groupIds.isEmpty()) {
return 0; return 0;
} }
return countUsersInGroups(getUsersStream(realm), groupIds); return countUsersInGroups(searchForUserStream(realm, Collections.emptyMap()), groupIds);
} }
/** /**
@ -176,26 +176,31 @@ public interface UserQueryProvider {
} }
/** /**
* @deprecated Use {@link #getUsersStream(RealmModel) getUsersStream} instead. * @deprecated Use {@link #searchForUserStream(RealmModel, Map)} with an empty params map instead.
*/ */
@Deprecated @Deprecated
List<UserModel> getUsers(RealmModel realm); default List<UserModel> getUsers(RealmModel realm) {
return searchForUserStream(realm, Collections.emptyMap()).collect(Collectors.toList());
}
/** /**
* Searches all users in the realm. * Searches all users in the realm.
* *
* @param realm a reference to the realm. * @param realm a reference to the realm.
* @return a non-null {@link Stream} of users. * @return a non-null {@link Stream} of users.
* @deprecated Use {@link #searchForUserStream(RealmModel, Map)} with an empty params map instead.
*/ */
@Deprecated
default Stream<UserModel> getUsersStream(RealmModel realm) { default Stream<UserModel> getUsersStream(RealmModel realm) {
List<UserModel> value = this.getUsers(realm); return searchForUserStream(realm, Collections.emptyMap());
return value != null ? value.stream() : Stream.empty();
} }
/** /**
* @deprecated Use {@link #getUsersStream(RealmModel, Integer, Integer) getUsersStream} instead. * @deprecated Use {@link #searchForUserStream(RealmModel, Map, Integer, Integer)} with an empty params map instead.
*/ */
@Deprecated @Deprecated
List<UserModel> getUsers(RealmModel realm, int firstResult, int maxResults); default List<UserModel> getUsers(RealmModel realm, int firstResult, int maxResults) {
return searchForUserStream(realm, Collections.emptyMap(), firstResult, maxResults).collect(Collectors.toList());
}
/** /**
* Searches all users in the realm, starting from the {@code firstResult} and containing at most {@code maxResults}. * Searches all users in the realm, starting from the {@code firstResult} and containing at most {@code maxResults}.
@ -204,11 +209,11 @@ public interface UserQueryProvider {
* @param firstResult first result to return. Ignored if negative or {@code null}. * @param firstResult first result to return. Ignored if negative or {@code null}.
* @param maxResults maximum number of results to return. Ignored if negative or {@code null}. * @param maxResults maximum number of results to return. Ignored if negative or {@code null}.
* @return a non-null {@link Stream} of users. * @return a non-null {@link Stream} of users.
* @deprecated Use {@link #searchForUserStream(RealmModel, Map, Integer, Integer)} with an empty params map instead.
*/ */
@Deprecated
default Stream<UserModel> getUsersStream(RealmModel realm, Integer firstResult, Integer maxResults) { default Stream<UserModel> getUsersStream(RealmModel realm, Integer firstResult, Integer maxResults) {
List<UserModel> value = this.getUsers(realm, firstResult == null ? -1 : firstResult, return searchForUserStream(realm, Collections.emptyMap(), firstResult, maxResults);
maxResults == null ? -1 : maxResults);
return value != null ? value.stream() : Stream.empty();
} }
/** /**
@ -295,6 +300,14 @@ public interface UserQueryProvider {
* from idp with the given alias configured (case sensitive string)</li> * from idp with the given alias configured (case sensitive string)</li>
* <li>{@link UserModel#IDP_USER_ID} - search for users with federated identity with * <li>{@link UserModel#IDP_USER_ID} - search for users with federated identity with
* the given userId (case sensitive string)</li> * the given userId (case sensitive string)</li>
* <li>{@link UserModel#SEARCH} - Searches for users whose username, email, first name or last name contain any
* of the strings in {@code search} separated by whitespace. If possible, implementations should treat the
* parameter values as partial match patterns (i.e. in RDMBS terms use LIKE).</li>
* <li>{@link UserModel#EXACT} - Used alongside with {@link UserModel#FIRST_NAME}, {@link UserModel#LAST_NAME},
* {@link UserModel#EMAIL} and {@link UserModel#USERNAME} parameters; if true searching is done using equals
* otherwise partial match should be enough to fulfill these parameters</li>
* <li>{@link UserModel#INCLUDE_SERVICE_ACCOUNT} - When false, users that have a service account link are not
* included in result</li>
* </ul> * </ul>
* *
* If possible, implementations should treat the parameter values as partial match patterns i.e. in RDMBS terms use LIKE. * If possible, implementations should treat the parameter values as partial match patterns i.e. in RDMBS terms use LIKE.
@ -593,24 +606,6 @@ public interface UserQueryProvider {
return getUsersCount(realm, params, groupIds); return getUsersCount(realm, params, groupIds);
} }
@Override
default List<UserModel> getUsers(RealmModel realm) {
return this.getUsersStream(realm).collect(Collectors.toList());
}
@Override
default Stream<UserModel> getUsersStream(RealmModel realm) {
return getUsersStream(realm, null, null);
}
@Override
default List<UserModel> getUsers(RealmModel realm, int firstResult, int maxResults) {
return this.getUsersStream(realm, firstResult, maxResults).collect(Collectors.toList());
}
@Override
Stream<UserModel> getUsersStream(RealmModel realm, Integer firstResult, Integer maxResults);
@Override @Override
default List<UserModel> searchForUser(String search, RealmModel realm) { default List<UserModel> searchForUser(String search, RealmModel realm) {
return this.searchForUserStream(realm, search).collect(Collectors.toList()); return this.searchForUserStream(realm, search).collect(Collectors.toList());

View file

@ -65,6 +65,7 @@ import java.io.IOException;
import java.io.OutputStream; import java.io.OutputStream;
import java.util.ArrayList; import java.util.ArrayList;
import java.util.Collection; import java.util.Collection;
import java.util.Collections;
import java.util.HashMap; import java.util.HashMap;
import java.util.HashSet; import java.util.HashSet;
import java.util.LinkedList; import java.util.LinkedList;
@ -227,7 +228,7 @@ public class ExportUtils {
// Finally users if needed // Finally users if needed
if (options.isUsersIncluded()) { if (options.isUsersIncluded()) {
List<UserRepresentation> users = session.users().getUsersStream(realm, true) List<UserRepresentation> users = session.users().searchForUserStream(realm, Collections.emptyMap())
.map(user -> exportUser(session, realm, user, options, internal)) .map(user -> exportUser(session, realm, user, options, internal))
.collect(Collectors.toList()); .collect(Collectors.toList());

View file

@ -61,6 +61,8 @@ import javax.ws.rs.QueryParam;
import javax.ws.rs.core.MediaType; import javax.ws.rs.core.MediaType;
import javax.ws.rs.core.Response; import javax.ws.rs.core.Response;
import java.util.Arrays; import java.util.Arrays;
import java.util.Collections;
import java.util.HashMap;
import java.util.Map; import java.util.Map;
import java.util.Objects; import java.util.Objects;
import java.util.function.Function; import java.util.function.Function;
@ -191,7 +193,7 @@ public class IdentityProviderResource {
// Admin changed the ID (alias) of identity provider. We must update all clients and users // Admin changed the ID (alias) of identity provider. We must update all clients and users
logger.debug("Changing providerId in all clients and linked users. oldProviderId=" + oldProviderId + ", newProviderId=" + newProviderId); logger.debug("Changing providerId in all clients and linked users. oldProviderId=" + oldProviderId + ", newProviderId=" + newProviderId);
updateUsersAfterProviderAliasChange(session.users().getUsersStream(realm, false), updateUsersAfterProviderAliasChange(session.users().searchForUserStream(realm, Collections.singletonMap(UserModel.INCLUDE_SERVICE_ACCOUNT, Boolean.FALSE.toString())),
oldProviderId, newProviderId, realm, session); oldProviderId, newProviderId, realm, session);
} }
} }

View file

@ -433,7 +433,7 @@ public class UsersResource {
} }
private Stream<UserRepresentation> searchForUser(Map<String, String> attributes, RealmModel realm, UserPermissionEvaluator usersEvaluator, Boolean briefRepresentation, Integer firstResult, Integer maxResults, Boolean includeServiceAccounts) { private Stream<UserRepresentation> searchForUser(Map<String, String> attributes, RealmModel realm, UserPermissionEvaluator usersEvaluator, Boolean briefRepresentation, Integer firstResult, Integer maxResults, Boolean includeServiceAccounts) {
session.setAttribute(UserModel.INCLUDE_SERVICE_ACCOUNT, includeServiceAccounts); attributes.put(UserModel.INCLUDE_SERVICE_ACCOUNT, includeServiceAccounts.toString());
if (!auth.users().canView()) { if (!auth.users().canView()) {
Set<String> groupModels = auth.groups().getGroupsWithViewPermission(); Set<String> groupModels = auth.groups().getGroupsWithViewPermission();

View file

@ -58,7 +58,7 @@ public class ProfileAssume {
} }
public static void assumeFeatureDisabled(Profile.Feature feature) { public static void assumeFeatureDisabled(Profile.Feature feature) {
Assume.assumeTrue("Ignoring test as feature " + feature.name() + " is disabled", !isFeatureEnabled(feature)); Assume.assumeTrue("Ignoring test as feature " + feature.name() + " is enabled", !isFeatureEnabled(feature));
} }
public static void assumePreview() { public static void assumePreview() {

View file

@ -307,7 +307,8 @@ public class LDAPSyncTest extends AbstractLDAPTest {
LDAPTestContext ctx = LDAPTestContext.init(session); LDAPTestContext ctx = LDAPTestContext.init(session);
// Remove all users from model // Remove all users from model
UserStoragePrivateUtil.userLocalStorage(session).getUsersStream(ctx.getRealm(), true) UserStoragePrivateUtil.userLocalStorage(session)
.searchForUserStream(ctx.getRealm(), Collections.emptyMap())
.collect(Collectors.toList()) .collect(Collectors.toList())
.forEach(user -> UserStoragePrivateUtil.userLocalStorage(session).removeUser(ctx.getRealm(), user)); .forEach(user -> UserStoragePrivateUtil.userLocalStorage(session).removeUser(ctx.getRealm(), user));
@ -356,7 +357,8 @@ public class LDAPSyncTest extends AbstractLDAPTest {
LDAPTestContext ctx = LDAPTestContext.init(session); LDAPTestContext ctx = LDAPTestContext.init(session);
// Remove all users from model // Remove all users from model
UserStoragePrivateUtil.userLocalStorage(session).getUsersStream(ctx.getRealm(), true) UserStoragePrivateUtil.userLocalStorage(session)
.searchForUserStream(ctx.getRealm(), Collections.emptyMap())
.peek(user -> System.out.println("trying to delete user: " + user.getUsername())) .peek(user -> System.out.println("trying to delete user: " + user.getUsername()))
.collect(Collectors.toList()) .collect(Collectors.toList())
.forEach(user -> { .forEach(user -> {

View file

@ -56,6 +56,7 @@ import org.keycloak.testsuite.pages.AppPage;
import org.keycloak.testsuite.pages.LoginPage; import org.keycloak.testsuite.pages.LoginPage;
import org.keycloak.testsuite.util.OAuthClient; import org.keycloak.testsuite.util.OAuthClient;
import java.util.Collections;
import java.util.HashMap; import java.util.HashMap;
import java.util.HashSet; import java.util.HashSet;
import java.util.Map; import java.util.Map;
@ -345,7 +346,7 @@ public class UserStorageFailureTest extends AbstractTestRealmKeycloakTest {
Assert.assertEquals(1, result.count()); Assert.assertEquals(1, result.count());
// we run a terminal operation on the stream to make sure it is consumed. // we run a terminal operation on the stream to make sure it is consumed.
session.users().getUsersStream(realm).count(); session.users().searchForUserStream(realm, Collections.emptyMap()).count();
session.users().getUsersCount(realm); session.users().getUsersCount(realm);
UserModel user = session.users().getUserByUsername(realm, FailableHardcodedStorageProvider.username); UserModel user = session.users().getUserByUsername(realm, FailableHardcodedStorageProvider.username);

View file

@ -479,11 +479,11 @@ public class UserModelTest extends AbstractTestRealmKeycloakTest {
Assert.assertThat(users, hasSize(1)); Assert.assertThat(users, hasSize(1));
Assert.assertThat(users, contains(user2)); Assert.assertThat(users, contains(user2));
users = currentSession.users().getUsersStream(realm, false).collect(Collectors.toList()); users = currentSession.users().searchForUserStream(realm, Collections.singletonMap(UserModel.INCLUDE_SERVICE_ACCOUNT, Boolean.FALSE.toString())).collect(Collectors.toList());
Assert.assertThat(users, hasSize(1)); Assert.assertThat(users, hasSize(1));
Assert.assertThat(users, contains(user2)); Assert.assertThat(users, contains(user2));
users = currentSession.users().getUsersStream(realm, true).collect(Collectors.toList()); users = currentSession.users().searchForUserStream(realm, Collections.emptyMap()).collect(Collectors.toList());
Assert.assertThat(users, hasSize(2)); Assert.assertThat(users, hasSize(2));
Assert.assertThat(users, containsInAnyOrder(user1, user2)); Assert.assertThat(users, containsInAnyOrder(user1, user2));

View file

@ -24,6 +24,8 @@ import org.keycloak.models.KeycloakSession;
import org.keycloak.models.RealmModel; import org.keycloak.models.RealmModel;
import org.keycloak.models.RoleContainerModel; import org.keycloak.models.RoleContainerModel;
import java.util.Collections;
/** /**
* @author <a href="mailto:mposolda@redhat.com">Marek Posolda</a> * @author <a href="mailto:mposolda@redhat.com">Marek Posolda</a>
*/ */
@ -45,7 +47,7 @@ public class TestCacheUtils {
realm.getClientScopesStream().map(ClientScopeModel::getId).forEach(realm::getClientScopeById); realm.getClientScopesStream().map(ClientScopeModel::getId).forEach(realm::getClientScopeById);
session.users().getUsersStream(realm).forEach(user -> { session.users().searchForUserStream(realm, Collections.emptyMap()).forEach(user -> {
session.users().getUserById(realm, user.getId()); session.users().getUserById(realm, user.getId());
if (user.getEmail() != null) { if (user.getEmail() != null) {
session.users().getUserByEmail(realm, user.getEmail()); session.users().getUserByEmail(realm, user.getEmail());