Update UserQueryProvider methods

Closes #20438
This commit is contained in:
vramik 2023-05-26 17:43:53 +02:00 committed by Hynek Mlnařík
parent ae5a47d548
commit 535bba5792
18 changed files with 82 additions and 163 deletions

View file

@ -349,3 +349,4 @@ their corresponding replacements.
* `Streams` interfaces in federated storage provider classes were deprecated. * `Streams` interfaces in federated storage provider classes were deprecated.
* `KeycloakModelUtils#getClientScopeMappings` was removed. * `KeycloakModelUtils#getClientScopeMappings` was removed.
* Deprecated methods from `KeycloakSession` were removed. * Deprecated methods from `KeycloakSession` were removed.
* `UserQueryProvider#getUsersStream` methods were removed.

View file

@ -343,11 +343,6 @@ public class LDAPStorageProvider implements UserStorageProvider,
return getUserByUsername(realm, storageId.getExternalId()); return getUserByUsername(realm, storageId.getExternalId());
} }
@Override
public Stream<UserModel> searchForUserStream(RealmModel realm, String search, Integer firstResult, Integer maxResults) {
return searchForUserStream(realm, Map.of(UserModel.SEARCH, search), firstResult, maxResults);
}
/** /**
* It supports * It supports
* <ul> * <ul>

View file

@ -454,42 +454,18 @@ public class UserStorageManager extends AbstractStorageManager<UserStorageProvid
return localStorage().getUsersCount(realm, params, groupIds); return localStorage().getUsersCount(realm, params, groupIds);
} }
@Override
public Stream<UserModel> searchForUserStream(RealmModel realm, String search, Integer firstResult, Integer maxResults) {
Stream<UserModel> results = query((provider, firstResultInQuery, maxResultsInQuery) -> {
if (provider instanceof UserQueryMethodsProvider) {
return ((UserQueryMethodsProvider)provider).searchForUserStream(realm, search, firstResultInQuery, maxResultsInQuery);
}
return Stream.empty();
}, (provider, firstResultInQuery, maxResultsInQuery) -> {
if (provider instanceof UserCountMethodsProvider) {
return ((UserCountMethodsProvider)provider).getUsersCount(realm, search);
}
return 0;
}, realm, firstResult, maxResults);
return importValidation(realm, results);
}
@Override @Override
public Stream<UserModel> searchForUserStream(RealmModel realm, Map<String, String> attributes, Integer firstResult, Integer maxResults) { public Stream<UserModel> searchForUserStream(RealmModel realm, Map<String, String> attributes, Integer firstResult, Integer maxResults) {
Stream<UserModel> results = query((provider, firstResultInQuery, maxResultsInQuery) -> { Stream<UserModel> results = query((provider, firstResultInQuery, maxResultsInQuery) -> {
if (provider instanceof UserQueryMethodsProvider) { if (provider instanceof UserQueryMethodsProvider) {
if (attributes.containsKey(UserModel.SEARCH)) {
return ((UserQueryMethodsProvider)provider).searchForUserStream(realm, attributes.get(UserModel.SEARCH), firstResultInQuery, maxResultsInQuery);
} else {
return ((UserQueryMethodsProvider)provider).searchForUserStream(realm, attributes, firstResultInQuery, maxResultsInQuery); return ((UserQueryMethodsProvider)provider).searchForUserStream(realm, attributes, firstResultInQuery, maxResultsInQuery);
} }
}
return Stream.empty(); return Stream.empty();
}, },
(provider, firstResultInQuery, maxResultsInQuery) -> { (provider, firstResultInQuery, maxResultsInQuery) -> {
if (provider instanceof UserCountMethodsProvider) { if (provider instanceof UserCountMethodsProvider) {
if (attributes.containsKey(UserModel.SEARCH)) {
return ((UserCountMethodsProvider)provider).getUsersCount(realm, attributes.get(UserModel.SEARCH));
} else {
return ((UserCountMethodsProvider)provider).getUsersCount(realm, attributes); return ((UserCountMethodsProvider)provider).getUsersCount(realm, attributes);
} }
}
return 0; return 0;
} }
, realm, firstResult, maxResults); , realm, firstResult, maxResults);

View file

@ -30,6 +30,12 @@
<name>Keycloak Server SPI</name> <name>Keycloak Server SPI</name>
<description/> <description/>
<properties>
<maven.compiler.release>11</maven.compiler.release>
<maven.compiler.source>11</maven.compiler.source>
<maven.compiler.target>11</maven.compiler.target>
</properties>
<dependencies> <dependencies>
<dependency> <dependency>
<groupId>jakarta.transaction</groupId> <groupId>jakarta.transaction</groupId>

View file

@ -68,7 +68,9 @@ public interface UserCountMethodsProvider {
* @param realm the realm * @param realm the realm
* @param search case insensitive list of strings separated by whitespaces. * @param search case insensitive list of strings separated by whitespaces.
* @return number of users that match the search * @return number of users that match the search
* @deprecated Use {@link #getUsersCount(RealmModel, Map)} with an {@code params} map containing {@link UserModel#SEARCH} instead.
*/ */
@Deprecated
default int getUsersCount(RealmModel realm, String search) { default int getUsersCount(RealmModel realm, String search) {
if (!(this instanceof UserQueryMethodsProvider)) { if (!(this instanceof UserQueryMethodsProvider)) {
return 0; return 0;
@ -85,7 +87,9 @@ public interface UserCountMethodsProvider {
* @param search case insensitive list of strings separated by whitespaces. * @param search case insensitive list of strings separated by whitespaces.
* @param groupIds set of groups IDs, the returned user needs to belong to at least one of them * @param groupIds set of groups IDs, the returned user needs to belong to at least one of them
* @return number of users that match the search and given groups * @return number of users that match the search and given groups
* @deprecated Use {@link #getUsersCount(RealmModel, Map, Set)} with an {@code params} map containing {@link UserModel#SEARCH} instead.
*/ */
@Deprecated
default int getUsersCount(RealmModel realm, String search, Set<String> groupIds) { default int getUsersCount(RealmModel realm, String search, Set<String> groupIds) {
if (groupIds == null || groupIds.isEmpty() || !(this instanceof UserQueryMethodsProvider)) { if (groupIds == null || groupIds.isEmpty() || !(this instanceof UserQueryMethodsProvider)) {
return 0; return 0;

View file

@ -22,7 +22,6 @@ import org.keycloak.models.RealmModel;
import org.keycloak.models.RoleModel; import org.keycloak.models.RoleModel;
import org.keycloak.models.UserModel; import org.keycloak.models.UserModel;
import java.util.Collections;
import java.util.Map; import java.util.Map;
import java.util.stream.Stream; import java.util.stream.Stream;
@ -38,31 +37,6 @@ import java.util.stream.Stream;
* @version $Revision: 1 $ * @version $Revision: 1 $
*/ */
public interface UserQueryMethodsProvider { public interface UserQueryMethodsProvider {
/**
* Searches all users in the realm.
*
* @param realm a reference to the realm.
* @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) {
return searchForUserStream(realm, Collections.emptyMap());
}
/**
* 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 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.
* @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) {
return searchForUserStream(realm, Collections.emptyMap(), firstResult, maxResults);
}
/** /**
* Searches for users whose username, email, first name or last name contain any of the strings in {@code search} separated by whitespace. * Searches for users whose username, email, first name or last name contain any of the strings in {@code search} separated by whitespace.
@ -74,9 +48,11 @@ public interface UserQueryMethodsProvider {
* @param realm a reference to the realm. * @param realm a reference to the realm.
* @param search case insensitive list of string separated by whitespaces. * @param search case insensitive list of string separated by whitespaces.
* @return a non-null {@link Stream} of users that match the search string. * @return a non-null {@link Stream} of users that match the search string.
* @deprecated Use {@link #searchForUserStream(RealmModel, Map)} with an {@code params} map containing {@link UserModel#SEARCH} instead.
*/ */
@Deprecated
default Stream<UserModel> searchForUserStream(RealmModel realm, String search) { default Stream<UserModel> searchForUserStream(RealmModel realm, String search) {
return searchForUserStream(realm, search, null, null); return searchForUserStream(realm, Map.of(UserModel.SEARCH, search), null, null);
} }
/** /**
@ -91,8 +67,12 @@ public interface UserQueryMethodsProvider {
* @param firstResult first result to return. Ignored if negative, zero, or {@code null}. * @param firstResult first result to return. Ignored if negative, zero, 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 that match the search criteria. * @return a non-null {@link Stream} of users that match the search criteria.
* @deprecated Use {@link #searchForUserStream(RealmModel, Map, Integer, Integer)} with an {@code params} map containing {@link UserModel#SEARCH} instead.
*/ */
Stream<UserModel> searchForUserStream(RealmModel realm, String search, Integer firstResult, Integer maxResults); @Deprecated
default Stream<UserModel> searchForUserStream(RealmModel realm, String search, Integer firstResult, Integer maxResults) {
return searchForUserStream(realm, Map.of(UserModel.SEARCH, search), firstResult, maxResults);
}
/** /**
* Searches for user by parameter. * Searches for user by parameter.
@ -100,10 +80,12 @@ public interface UserQueryMethodsProvider {
* <p/> * <p/>
* Valid parameters are: * Valid parameters are:
* <ul> * <ul>
* <li>{@link UserModel#SEARCH} - search for users whose username, email, first name or last name contain any of the strings in {@code search} separated by whitespace, when {@code SEARCH} is set all other params are ignored</li>
* <li>{@link UserModel#FIRST_NAME} - first name (case insensitive string)</li> * <li>{@link UserModel#FIRST_NAME} - first name (case insensitive string)</li>
* <li>{@link UserModel#LAST_NAME} - last name (case insensitive string)</li> * <li>{@link UserModel#LAST_NAME} - last name (case insensitive string)</li>
* <li>{@link UserModel#EMAIL} - email (case insensitive string)</li> * <li>{@link UserModel#EMAIL} - email (case insensitive string)</li>
* <li>{@link UserModel#USERNAME} - username (case insensitive string)</li> * <li>{@link UserModel#USERNAME} - username (case insensitive string)</li>
* <li>{@link UserModel#EXACT} - whether search with FIRST_NAME, LAST_NAME, USERNAME or EMAIL should be exact match</li>
* <li>{@link UserModel#EMAIL_VERIFIED} - search only for users with verified/non-verified email (true/false)</li> * <li>{@link UserModel#EMAIL_VERIFIED} - search only for users with verified/non-verified email (true/false)</li>
* <li>{@link UserModel#ENABLED} - search only for enabled/disabled users (true/false)</li> * <li>{@link UserModel#ENABLED} - search only for enabled/disabled users (true/false)</li>
* <li>{@link UserModel#IDP_ALIAS} - search only for users that have a federated identity * <li>{@link UserModel#IDP_ALIAS} - search only for users that have a federated identity
@ -128,10 +110,12 @@ public interface UserQueryMethodsProvider {
* <p/> * <p/>
* Valid parameters are: * Valid parameters are:
* <ul> * <ul>
* <li>{@link UserModel#SEARCH} - search for users whose username, email, first name or last name contain any of the strings in {@code search} separated by whitespace, when {@code SEARCH} is set all other params are ignored</li>
* <li>{@link UserModel#FIRST_NAME} - first name (case insensitive string)</li> * <li>{@link UserModel#FIRST_NAME} - first name (case insensitive string)</li>
* <li>{@link UserModel#LAST_NAME} - last name (case insensitive string)</li> * <li>{@link UserModel#LAST_NAME} - last name (case insensitive string)</li>
* <li>{@link UserModel#EMAIL} - email (case insensitive string)</li> * <li>{@link UserModel#EMAIL} - email (case insensitive string)</li>
* <li>{@link UserModel#USERNAME} - username (case insensitive string)</li> * <li>{@link UserModel#USERNAME} - username (case insensitive string)</li>
* <li>{@link UserModel#EXACT} - whether search with FIRST_NAME, LAST_NAME, USERNAME or EMAIL should be exact match</li>
* <li>{@link UserModel#EMAIL_VERIFIED} - search only for users with verified/non-verified email (true/false)</li> * <li>{@link UserModel#EMAIL_VERIFIED} - search only for users with verified/non-verified email (true/false)</li>
* <li>{@link UserModel#ENABLED} - search only for enabled/disabled users (true/false)</li> * <li>{@link UserModel#ENABLED} - search only for enabled/disabled users (true/false)</li>
* <li>{@link UserModel#IDP_ALIAS} - search only for users that have a federated identity * <li>{@link UserModel#IDP_ALIAS} - search only for users that have a federated identity

View file

@ -320,19 +320,6 @@ public class BackwardsCompatibilityUserStorage implements UserLookupProvider, Us
return users.size(); return users.size();
} }
@Override
public Stream<UserModel> getUsersStream(RealmModel realm) {
return getUsers(realm, -1, -1).stream();
}
private List<UserModel> getUsers(RealmModel realm, int firstResult, int maxResults) {
return users.values()
.stream()
.skip(firstResult).limit(maxResults)
.map(myUser -> createUser(realm, myUser.username))
.collect(Collectors.toList());
}
@Override @Override
public Stream<UserModel> searchForUserStream(RealmModel realm, String search) { public Stream<UserModel> searchForUserStream(RealmModel realm, String search) {
return searchForUserStream(realm, search, -1, -1); return searchForUserStream(realm, search, -1, -1);
@ -346,14 +333,12 @@ public class BackwardsCompatibilityUserStorage implements UserLookupProvider, Us
@Override @Override
public Stream<UserModel> searchForUserStream(RealmModel realm, Map<String, String> params) { public Stream<UserModel> searchForUserStream(RealmModel realm, Map<String, String> params) {
// Assume that this is not supported return searchForUserStream(realm, params, null, null);
return Stream.empty();
} }
@Override @Override
public Stream<UserModel> searchForUserStream(RealmModel realm, Map<String, String> params, Integer firstResult, Integer maxResults) { public Stream<UserModel> searchForUserStream(RealmModel realm, Map<String, String> params, Integer firstResult, Integer maxResults) {
// Assume that this is not supported return searchForUserStream(realm, params.get(UserModel.SEARCH), firstResult, maxResults);
return Stream.empty();
} }
@Override @Override

View file

@ -221,20 +221,6 @@ public class FailableHardcodedStorageProvider implements UserStorageProvider, Us
return 1; return 1;
} }
@Override
public Stream<UserModel> getUsersStream(RealmModel realm) {
checkForceFail();
UserModel model = getUserByUsername(realm, username);
return model != null ? Stream.of(model) : Stream.empty();
}
@Override
public Stream<UserModel> getUsersStream(RealmModel realm, Integer firstResult, Integer maxResults) {
checkForceFail();
UserModel model = getUserByUsername(realm, username);
return model != null ? Stream.of(model) : Stream.empty();
}
@Override @Override
public Stream<UserModel> searchForUserStream(RealmModel realm, String search) { public Stream<UserModel> searchForUserStream(RealmModel realm, String search) {
checkForceFail(); checkForceFail();

View file

@ -295,19 +295,6 @@ public class UserMapStorage implements UserLookupProvider, UserStorageProvider,
return userPasswords.size(); return userPasswords.size();
} }
@Override
public Stream<UserModel> getUsersStream(RealmModel realm) {
return userPasswords.keySet().stream()
.map(userName -> createUser(realm, userName));
}
@Override
public Stream<UserModel> getUsersStream(RealmModel realm, Integer firstResult, Integer maxResults) {
Stream<String> userStream = userPasswords.keySet().stream().sorted();
return paginatedStream(userStream, firstResult, maxResults).map(userName -> createUser(realm, userName));
}
@Override @Override
public Stream<UserModel> searchForUserStream(RealmModel realm, String search) { public Stream<UserModel> searchForUserStream(RealmModel realm, String search) {
String tSearch = translateUserName(search); String tSearch = translateUserName(search);

View file

@ -54,8 +54,8 @@ import static org.keycloak.utils.StreamsUtil.paginatedStream;
*/ */
public class UserPropertyFileStorage implements UserLookupProvider, UserStorageProvider, UserQueryProvider, CredentialInputValidator { public class UserPropertyFileStorage implements UserLookupProvider, UserStorageProvider, UserQueryProvider, CredentialInputValidator {
public static final String SEARCH_METHOD = "searchForUserStream(RealmMode, String, Integer, Integer)"; public static final String SEARCH_METHOD = "searchForUserStream(RealmMode, Map, Integer, Integer)";
public static final String COUNT_SEARCH_METHOD = "getUsersCount(RealmModel, String)"; public static final String COUNT_SEARCH_METHOD = "getUsersCount(RealmModel, Map)";
protected Properties userPasswords; protected Properties userPasswords;
protected ComponentModel model; protected ComponentModel model;
@ -107,10 +107,10 @@ public class UserPropertyFileStorage implements UserLookupProvider, UserStorageP
} }
@Override @Override
public int getUsersCount(RealmModel realm, String search) { public int getUsersCount(RealmModel realm, Map<String, String> params) {
addCall(COUNT_SEARCH_METHOD); addCall(COUNT_SEARCH_METHOD);
return (int) searchForUser(realm, search, null, null, username -> username.contains(search)).count(); return (int) searchForUser(realm, params.get(UserModel.SEARCH), null, null, username -> username.contains(params.get(UserModel.SEARCH))).count();
} }
@Override @Override
@ -150,6 +150,7 @@ public class UserPropertyFileStorage implements UserLookupProvider, UserStorageP
} }
} }
@Override
public UserModel getUserByUsername(RealmModel realm, String username) { public UserModel getUserByUsername(RealmModel realm, String username) {
if (!userPasswords.containsKey(username)) return null; if (!userPasswords.containsKey(username)) return null;
@ -202,27 +203,15 @@ public class UserPropertyFileStorage implements UserLookupProvider, UserStorageP
return userPasswords.size(); return userPasswords.size();
} }
@Override // @Override
public Stream<UserModel> getUsersStream(RealmModel realm) { // public Stream<UserModel> searchForUserStream(RealmModel realm, String search, Integer firstResult, Integer maxResults) {
return userPasswords.keySet().stream() // addCall(SEARCH_METHOD, firstResult, maxResults);
.map(username -> createUser(realm, (String) username)); // return searchForUser(realm, search, firstResult, maxResults, username -> username.contains(search));
} // }
@Override
public Stream<UserModel> getUsersStream(RealmModel realm, Integer firstResult, Integer maxResults) {
if (maxResults != null && maxResults == 0) return Stream.empty();
return paginatedStream(userPasswords.keySet().stream(), firstResult, maxResults)
.map(username -> createUser(realm, (String) username));
}
@Override
public Stream<UserModel> searchForUserStream(RealmModel realm, String search, Integer firstResult, Integer maxResults) {
addCall(SEARCH_METHOD, firstResult, maxResults);
return searchForUser(realm, search, firstResult, maxResults, username -> username.contains(search));
}
@Override @Override
public Stream<UserModel> searchForUserStream(RealmModel realm, Map<String, String> attributes, Integer firstResult, Integer maxResults) { public Stream<UserModel> searchForUserStream(RealmModel realm, Map<String, String> attributes, Integer firstResult, Integer maxResults) {
addCall(SEARCH_METHOD, firstResult, maxResults);
String search = Optional.ofNullable(attributes.get(UserModel.USERNAME)) String search = Optional.ofNullable(attributes.get(UserModel.USERNAME))
.orElseGet(()-> attributes.get(UserModel.SEARCH)); .orElseGet(()-> attributes.get(UserModel.SEARCH));
Predicate<String> p; Predicate<String> p;

View file

@ -652,7 +652,7 @@ public class LDAPProvidersIntegrationTest extends AbstractLDAPTest {
LDAPTestUtils.removeLDAPUserByUsername(ctx.getLdapProvider(), ctx.getRealm(), config, "maryjane"); LDAPTestUtils.removeLDAPUserByUsername(ctx.getLdapProvider(), ctx.getRealm(), config, "maryjane");
// Make sure the deletion took place. // Make sure the deletion took place.
Assert.assertEquals(0, session.users().searchForUserStream(ctx.getRealm(), "mary yram").count()); Assert.assertEquals(0, session.users().searchForUserStream(ctx.getRealm(), Map.of(UserModel.SEARCH, "mary yram")).count());
}); });
} }
@ -1003,23 +1003,23 @@ public class LDAPProvidersIntegrationTest extends AbstractLDAPTest {
Assert.assertNull(UserStoragePrivateUtil.userLocalStorage(session).getUserByUsername(appRealm, "username4")); Assert.assertNull(UserStoragePrivateUtil.userLocalStorage(session).getUserByUsername(appRealm, "username4"));
// search by username (we use a terminal operation on the stream to ensure it is consumed) // search by username (we use a terminal operation on the stream to ensure it is consumed)
Assert.assertEquals(1, session.users().searchForUserStream(appRealm, "\"username1\"").count()); Assert.assertEquals(1, session.users().searchForUserStream(appRealm, Map.of(UserModel.SEARCH, "\"username1\"")).count());
LDAPTestAsserts.assertUserImported(UserStoragePrivateUtil.userLocalStorage(session), appRealm, "username1", "John1", "Doel1", "user1@email.org", "121"); LDAPTestAsserts.assertUserImported(UserStoragePrivateUtil.userLocalStorage(session), appRealm, "username1", "John1", "Doel1", "user1@email.org", "121");
// search by email (we use a terminal operation on the stream to ensure it is consumed) // search by email (we use a terminal operation on the stream to ensure it is consumed)
Assert.assertEquals(1, session.users().searchForUserStream(appRealm, "user2@email.org").count()); Assert.assertEquals(1, session.users().searchForUserStream(appRealm, Map.of(UserModel.SEARCH, "user2@email.org")).count());
LDAPTestAsserts.assertUserImported(UserStoragePrivateUtil.userLocalStorage(session), appRealm, "username2", "John2", "Doel2", "user2@email.org", "122"); LDAPTestAsserts.assertUserImported(UserStoragePrivateUtil.userLocalStorage(session), appRealm, "username2", "John2", "Doel2", "user2@email.org", "122");
// search by lastName (we use a terminal operation on the stream to ensure it is consumed) // search by lastName (we use a terminal operation on the stream to ensure it is consumed)
Assert.assertEquals(1, session.users().searchForUserStream(appRealm, "Doel3").count()); Assert.assertEquals(1, session.users().searchForUserStream(appRealm, Map.of(UserModel.SEARCH, "Doel3")).count());
LDAPTestAsserts.assertUserImported(UserStoragePrivateUtil.userLocalStorage(session), appRealm, "username3", "John3", "Doel3", "user3@email.org", "123"); LDAPTestAsserts.assertUserImported(UserStoragePrivateUtil.userLocalStorage(session), appRealm, "username3", "John3", "Doel3", "user3@email.org", "123");
// search by firstName + lastName (we use a terminal operation on the stream to ensure it is consumed) // search by firstName + lastName (we use a terminal operation on the stream to ensure it is consumed)
Assert.assertEquals(1, session.users().searchForUserStream(appRealm, "John4 Doel4").count()); Assert.assertEquals(1, session.users().searchForUserStream(appRealm, Map.of(UserModel.SEARCH, "John4 Doel4")).count());
LDAPTestAsserts.assertUserImported(UserStoragePrivateUtil.userLocalStorage(session), appRealm, "username4", "John4", "Doel4", "user4@email.org", "124"); LDAPTestAsserts.assertUserImported(UserStoragePrivateUtil.userLocalStorage(session), appRealm, "username4", "John4", "Doel4", "user4@email.org", "124");
// search by a string that matches multiple fields. Should still return the one entity it matches. // search by a string that matches multiple fields. Should still return the one entity it matches.
Assert.assertEquals(1, session.users().searchForUserStream(appRealm, "*11*").count()); Assert.assertEquals(1, session.users().searchForUserStream(appRealm, Map.of(UserModel.SEARCH, "*11*")).count());
LDAPTestAsserts.assertUserImported(UserStoragePrivateUtil.userLocalStorage(session), appRealm, "username11", "John11", "Doel11", "user11@email.org", "124"); LDAPTestAsserts.assertUserImported(UserStoragePrivateUtil.userLocalStorage(session), appRealm, "username11", "John11", "Doel11", "user11@email.org", "124");
// search by a string that has special characters. Should succeed with an empty set, but no exceptions. // search by a string that has special characters. Should succeed with an empty set, but no exceptions.
@ -1048,14 +1048,14 @@ public class LDAPProvidersIntegrationTest extends AbstractLDAPTest {
LDAPTestUtils.addLDAPUser(ctx.getLdapProvider(), appRealm, "username7", "John7", "Doel7", "user7@email.org", null, "127"); LDAPTestUtils.addLDAPUser(ctx.getLdapProvider(), appRealm, "username7", "John7", "Doel7", "user7@email.org", null, "127");
// search by email (we use a terminal operation on the stream to ensure it is consumed) // search by email (we use a terminal operation on the stream to ensure it is consumed)
session.users().searchForUserStream(appRealm, "user5@email.org").count(); session.users().searchForUserStream(appRealm, Map.of(UserModel.SEARCH, "user5@email.org")).count();
LDAPTestAsserts.assertUserImported(UserStoragePrivateUtil.userLocalStorage(session), appRealm, "username5", "John5", "Doel5", "user5@email.org", "125"); LDAPTestAsserts.assertUserImported(UserStoragePrivateUtil.userLocalStorage(session), appRealm, "username5", "John5", "Doel5", "user5@email.org", "125");
session.users().searchForUserStream(appRealm, "John6 Doel6").count(); session.users().searchForUserStream(appRealm, Map.of(UserModel.SEARCH, "John6 Doel6")).count();
LDAPTestAsserts.assertUserImported(UserStoragePrivateUtil.userLocalStorage(session), appRealm, "username6", "John6", "Doel6", "user6@email.org", "126"); LDAPTestAsserts.assertUserImported(UserStoragePrivateUtil.userLocalStorage(session), appRealm, "username6", "John6", "Doel6", "user6@email.org", "126");
session.users().searchForUserStream(appRealm, "user7@email.org").count(); session.users().searchForUserStream(appRealm, Map.of(UserModel.SEARCH, "user7@email.org")).count();
session.users().searchForUserStream(appRealm, "John7 Doel7").count(); session.users().searchForUserStream(appRealm, Map.of(UserModel.SEARCH, "John7 Doel7")).count();
Assert.assertNull(UserStoragePrivateUtil.userLocalStorage(session).getUserByUsername(appRealm, "username7")); Assert.assertNull(UserStoragePrivateUtil.userLocalStorage(session).getUserByUsername(appRealm, "username7"));
// Remove custom filter // Remove custom filter
@ -1372,7 +1372,7 @@ public class LDAPProvidersIntegrationTest extends AbstractLDAPTest {
testingClient.server().run(session -> { testingClient.server().run(session -> {
RealmModel appRealm = session.realms().getRealmByName(TEST_REALM_NAME); RealmModel appRealm = session.realms().getRealmByName(TEST_REALM_NAME);
Optional<UserModel> userVerified = session.users().searchForUserStream(appRealm, "john@test.com").findFirst(); Optional<UserModel> userVerified = session.users().searchForUserStream(appRealm, Map.of(UserModel.SEARCH, "john@test.com")).findFirst();
Assert.assertTrue(userVerified.get().isEmailVerified()); Assert.assertTrue(userVerified.get().isEmailVerified());
}); });
@ -1389,7 +1389,7 @@ public class LDAPProvidersIntegrationTest extends AbstractLDAPTest {
testingClient.server().run(session -> { testingClient.server().run(session -> {
RealmModel appRealm = session.realms().getRealmByName(TEST_REALM_NAME); RealmModel appRealm = session.realms().getRealmByName(TEST_REALM_NAME);
Optional<UserModel> userNotVerified = session.users().searchForUserStream(appRealm, "john2@test.com").findFirst(); Optional<UserModel> userNotVerified = session.users().searchForUserStream(appRealm, Map.of(UserModel.SEARCH, "john2@test.com")).findFirst();
Assert.assertFalse(userNotVerified.get().isEmailVerified()); Assert.assertFalse(userNotVerified.get().isEmailVerified());
}); });
} }

View file

@ -23,6 +23,7 @@ import java.util.stream.Collectors;
import jakarta.ws.rs.BadRequestException; import jakarta.ws.rs.BadRequestException;
import jakarta.ws.rs.core.Response; import jakarta.ws.rs.core.Response;
import java.util.Map;
import org.junit.Assert; import org.junit.Assert;
import org.junit.Assume; import org.junit.Assume;
@ -101,31 +102,31 @@ public class LDAPProvidersIntegrationNoImportTest extends LDAPProvidersIntegrati
LDAPTestUtils.addLDAPUser(ctx.getLdapProvider(), appRealm, "username8", "John8", "Doel8", "user8@email.org", null, "124"); LDAPTestUtils.addLDAPUser(ctx.getLdapProvider(), appRealm, "username8", "John8", "Doel8", "user8@email.org", null, "124");
// search by username // search by username
List<UserModel> users = session.users().searchForUserStream(appRealm, "username1").collect(Collectors.toList()); List<UserModel> users = session.users().searchForUserStream(appRealm, Map.of(UserModel.SEARCH, "username1")).collect(Collectors.toList());
Assert.assertEquals(1, users.size()); Assert.assertEquals(1, users.size());
UserModel user = users.get(0); UserModel user = users.get(0);
LDAPTestAsserts.assertLoaded(user, "username1", "John1", "Doel1", "user1@email.org", "121"); LDAPTestAsserts.assertLoaded(user, "username1", "John1", "Doel1", "user1@email.org", "121");
// search by email // search by email
users = session.users().searchForUserStream(appRealm, "user2@email.org").collect(Collectors.toList()); users = session.users().searchForUserStream(appRealm, Map.of(UserModel.SEARCH, "user2@email.org")).collect(Collectors.toList());
Assert.assertEquals(1, users.size()); Assert.assertEquals(1, users.size());
user = users.get(0); user = users.get(0);
LDAPTestAsserts.assertLoaded(user, "username2", "John2", "Doel2", "user2@email.org", "122"); LDAPTestAsserts.assertLoaded(user, "username2", "John2", "Doel2", "user2@email.org", "122");
// search by lastName // search by lastName
users = session.users().searchForUserStream(appRealm, "Doel3").collect(Collectors.toList()); users = session.users().searchForUserStream(appRealm, Map.of(UserModel.SEARCH, "Doel3")).collect(Collectors.toList());
Assert.assertEquals(1, users.size()); Assert.assertEquals(1, users.size());
user = users.get(0); user = users.get(0);
LDAPTestAsserts.assertLoaded(user, "username3", "John3", "Doel3", "user3@email.org", "123"); LDAPTestAsserts.assertLoaded(user, "username3", "John3", "Doel3", "user3@email.org", "123");
// search by firstName + lastName // search by firstName + lastName
users = session.users().searchForUserStream(appRealm, "John4 Doel4").collect(Collectors.toList()); users = session.users().searchForUserStream(appRealm, Map.of(UserModel.SEARCH, "John4 Doel4")).collect(Collectors.toList());
Assert.assertEquals(1, users.size()); Assert.assertEquals(1, users.size());
user = users.get(0); user = users.get(0);
LDAPTestAsserts.assertLoaded(user, "username4", "John4", "Doel4", "user4@email.org", "124"); LDAPTestAsserts.assertLoaded(user, "username4", "John4", "Doel4", "user4@email.org", "124");
// search by a string that matches multiple fields. Should still return the one entity it matches // search by a string that matches multiple fields. Should still return the one entity it matches
users = session.users().searchForUserStream(appRealm, "*8*").collect(Collectors.toList()); users = session.users().searchForUserStream(appRealm, Map.of(UserModel.SEARCH, "*8*")).collect(Collectors.toList());
Assert.assertEquals(1, users.size()); Assert.assertEquals(1, users.size());
user = users.get(0); user = users.get(0);
LDAPTestAsserts.assertLoaded(user, "username8", "John8", "Doel8", "user8@email.org", "124"); LDAPTestAsserts.assertLoaded(user, "username8", "John8", "Doel8", "user8@email.org", "124");
@ -162,14 +163,14 @@ public class LDAPProvidersIntegrationNoImportTest extends LDAPProvidersIntegrati
LDAPTestUtils.addLDAPUser(ctx.getLdapProvider(), appRealm, "username7", "John7", "Doel7", "user7@email.org", null, "127"); LDAPTestUtils.addLDAPUser(ctx.getLdapProvider(), appRealm, "username7", "John7", "Doel7", "user7@email.org", null, "127");
// search by email // search by email
UserModel user = session.users().searchForUserStream(appRealm, "user5@email.org").findFirst().get(); UserModel user = session.users().searchForUserStream(appRealm, Map.of(UserModel.SEARCH, "user5@email.org")).findFirst().get();
LDAPTestAsserts.assertLoaded(user, "username5", "John5", "Doel5", "user5@email.org", "125"); LDAPTestAsserts.assertLoaded(user, "username5", "John5", "Doel5", "user5@email.org", "125");
user = session.users().searchForUserStream(appRealm, "John6 Doel6").findFirst().get(); user = session.users().searchForUserStream(appRealm, Map.of(UserModel.SEARCH, "John6 Doel6")).findFirst().get();
LDAPTestAsserts.assertLoaded(user, "username6", "John6", "Doel6", "user6@email.org", "126"); LDAPTestAsserts.assertLoaded(user, "username6", "John6", "Doel6", "user6@email.org", "126");
Assert.assertEquals(0, session.users().searchForUserStream(appRealm, "user7@email.org").count()); Assert.assertEquals(0, session.users().searchForUserStream(appRealm, Map.of(UserModel.SEARCH, "user7@email.org")).count());
Assert.assertEquals(0, session.users().searchForUserStream(appRealm, "John7 Doel7").count()); Assert.assertEquals(0, session.users().searchForUserStream(appRealm, Map.of(UserModel.SEARCH, "John7 Doel7")).count());
// Remove custom filter // Remove custom filter
ctx.getLdapModel().getConfig().remove(LDAPConstants.CUSTOM_USER_SEARCH_FILTER); ctx.getLdapModel().getConfig().remove(LDAPConstants.CUSTOM_USER_SEARCH_FILTER);

View file

@ -311,13 +311,13 @@ public class UserStorageFailureTest extends AbstractTestRealmKeycloakTest {
UserModel local = session.users().getUserByUsername(realm, LOCAL_USER); UserModel local = session.users().getUserByUsername(realm, LOCAL_USER);
Assert.assertNotNull(local); Assert.assertNotNull(local);
Stream<UserModel> result; Stream<UserModel> result;
result = session.users().searchForUserStream(realm, LOCAL_USER); result = session.users().searchForUserStream(realm, Map.of(UserModel.SEARCH, LOCAL_USER));
Assert.assertEquals(1, result.count()); Assert.assertEquals(1, result.count());
result = session.users().searchForUserStream(realm, FailableHardcodedStorageProvider.username); result = session.users().searchForUserStream(realm, Map.of(UserModel.SEARCH, FailableHardcodedStorageProvider.username));
Assert.assertEquals(1, result.count()); Assert.assertEquals(1, result.count());
result = session.users().searchForUserStream(realm, LOCAL_USER, 0, 2); result = session.users().searchForUserStream(realm, Map.of(UserModel.SEARCH, LOCAL_USER), 0, 2);
Assert.assertEquals(1, result.count()); Assert.assertEquals(1, result.count());
result = session.users().searchForUserStream(realm, FailableHardcodedStorageProvider.username, 0, 2); result = session.users().searchForUserStream(realm, Map.of(UserModel.SEARCH, FailableHardcodedStorageProvider.username), 0, 2);
Assert.assertEquals(1, result.count()); Assert.assertEquals(1, result.count());
Map<String, String> localParam = new HashMap<>(); Map<String, String> localParam = new HashMap<>();
localParam.put("username", LOCAL_USER); localParam.put("username", LOCAL_USER);

View file

@ -35,6 +35,7 @@ import java.util.concurrent.atomic.AtomicReference;
import org.keycloak.models.Constants; import org.keycloak.models.Constants;
import java.util.Collections; import java.util.Collections;
import java.util.List; import java.util.List;
import java.util.Map;
import java.util.stream.Collectors; import java.util.stream.Collectors;
import java.util.stream.Stream; import java.util.stream.Stream;
import static org.hamcrest.MatcherAssert.assertThat; import static org.hamcrest.MatcherAssert.assertThat;
@ -105,7 +106,7 @@ public class MultipleRealmsTest extends AbstractTestRealmKeycloakTest {
Assert.assertTrue(r2user1.credentialManager().isValid(UserCredentialModel.password("pass2"))); Assert.assertTrue(r2user1.credentialManager().isValid(UserCredentialModel.password("pass2")));
// Test searching // Test searching
Assert.assertEquals(2, currentSession.users().searchForUserStream(realm1, "user").count()); Assert.assertEquals(2, currentSession.users().searchForUserStream(realm1, Map.of(UserModel.SEARCH, "user")).count());
return new String[] { id1, id2 }; return new String[] { id1, id2 };
}); });
@ -124,8 +125,8 @@ public class MultipleRealmsTest extends AbstractTestRealmKeycloakTest {
currentSession.users().removeUser(realm1, r1user1); currentSession.users().removeUser(realm1, r1user1);
UserModel user2 = currentSession.users().getUserByUsername(realm1, "user2"); UserModel user2 = currentSession.users().getUserByUsername(realm1, "user2");
currentSession.users().removeUser(realm1, user2); currentSession.users().removeUser(realm1, user2);
Assert.assertEquals(0, currentSession.users().searchForUserStream(realm1, "user").count()); Assert.assertEquals(0, currentSession.users().searchForUserStream(realm1, Map.of(UserModel.SEARCH, "user")).count());
Assert.assertEquals(2, currentSession.users().searchForUserStream(realm2, "user").count()); Assert.assertEquals(2, currentSession.users().searchForUserStream(realm2, Map.of(UserModel.SEARCH, "user")).count());
UserModel user1 = currentSession.users().getUserByUsername(realm1, "user1"); UserModel user1 = currentSession.users().getUserByUsername(realm1, "user1");

View file

@ -370,7 +370,7 @@ public class UserModelTest extends AbstractTestRealmKeycloakTest {
UserModel user1 = currentSession.users().getUserByUsername(realm, "user1"); UserModel user1 = currentSession.users().getUserByUsername(realm, "user1");
List<UserModel> users = currentSession.users().searchForUserStream(realm, "user", 0, 7) List<UserModel> users = currentSession.users().searchForUserStream(realm, Map.of(UserModel.SEARCH, "user"), 0, 7)
.collect(Collectors.toList()); .collect(Collectors.toList());
Assert.assertThat(users, hasSize(1)); Assert.assertThat(users, hasSize(1));
Assert.assertThat(users, contains(user1)); Assert.assertThat(users, contains(user1));
@ -451,7 +451,7 @@ public class UserModelTest extends AbstractTestRealmKeycloakTest {
// Search // Search
Assert.assertThat(currentSession.users().getServiceAccount(client), nullValue()); Assert.assertThat(currentSession.users().getServiceAccount(client), nullValue());
List<UserModel> users = currentSession.users().searchForUserStream(realm, "John Doe") List<UserModel> users = currentSession.users().searchForUserStream(realm, Map.of(UserModel.SEARCH, "John Doe", UserModel.INCLUDE_SERVICE_ACCOUNT, "true"))
.collect(Collectors.toList()); .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));
@ -471,7 +471,7 @@ public class UserModelTest extends AbstractTestRealmKeycloakTest {
ClientModel client = realm.getClientByClientId("foo"); ClientModel client = realm.getClientByClientId("foo");
UserModel searched = currentSession.users().getServiceAccount(client); UserModel searched = currentSession.users().getServiceAccount(client);
Assert.assertThat(searched, equalTo(user1)); Assert.assertThat(searched, equalTo(user1));
List<UserModel> users = currentSession.users().searchForUserStream(realm, "John Doe") List<UserModel> users = currentSession.users().searchForUserStream(realm, Map.of(UserModel.SEARCH, "John Doe", UserModel.INCLUDE_SERVICE_ACCOUNT, "false"))
.collect(Collectors.toList()); .collect(Collectors.toList());
Assert.assertThat(users, hasSize(1)); Assert.assertThat(users, hasSize(1));
Assert.assertThat(users, contains(user2)); Assert.assertThat(users, contains(user2));

View file

@ -15,6 +15,10 @@
<packaging>jar</packaging> <packaging>jar</packaging>
<properties> <properties>
<maven.compiler.release>11</maven.compiler.release>
<maven.compiler.source>11</maven.compiler.source>
<maven.compiler.target>11</maven.compiler.target>
<keycloak.connectionsJpa.driver>org.h2.Driver</keycloak.connectionsJpa.driver> <keycloak.connectionsJpa.driver>org.h2.Driver</keycloak.connectionsJpa.driver>
<keycloak.connectionsJpa.database>keycloak</keycloak.connectionsJpa.database> <keycloak.connectionsJpa.database>keycloak</keycloak.connectionsJpa.database>
<keycloak.connectionsJpa.user>sa</keycloak.connectionsJpa.user> <keycloak.connectionsJpa.user>sa</keycloak.connectionsJpa.user>

View file

@ -59,7 +59,6 @@ import org.hamcrest.Matchers;
import org.junit.Test; import org.junit.Test;
import static org.hamcrest.MatcherAssert.assertThat; import static org.hamcrest.MatcherAssert.assertThat;
import static org.hamcrest.Matchers.containsInAnyOrder;
import static org.hamcrest.Matchers.equalTo; import static org.hamcrest.Matchers.equalTo;
/** /**
@ -357,7 +356,7 @@ public class OfflineSessionPersistenceTest extends KeycloakModelTest {
reinitializeKeycloakSessionFactory(); reinitializeKeycloakSessionFactory();
Map<String, Set<String>> actualOfflineSessionIds = withRealm(realmId, (session, realm) -> session.users() Map<String, Set<String>> actualOfflineSessionIds = withRealm(realmId, (session, realm) -> session.users()
.getUsersStream(realm) .searchForUserStream(realm, Collections.emptyMap())
.collect(Collectors.toMap( .collect(Collectors.toMap(
UserModel::getId, UserModel::getId,
user -> session.sessions().getOfflineUserSessionsStream(realm, user).map(UserSessionModel::getId).collect(Collectors.toCollection(TreeSet::new)) user -> session.sessions().getOfflineUserSessionsStream(realm, user).map(UserSessionModel::getId).collect(Collectors.toCollection(TreeSet::new))

View file

@ -20,6 +20,7 @@ import org.keycloak.testsuite.model.RequireProvider;
import java.util.Arrays; import java.util.Arrays;
import java.util.Collections; import java.util.Collections;
import java.util.List; import java.util.List;
import java.util.Map;
import java.util.stream.Collectors; import java.util.stream.Collectors;
import static org.hamcrest.MatcherAssert.assertThat; import static org.hamcrest.MatcherAssert.assertThat;
@ -69,7 +70,7 @@ public class UserPaginationTest extends KeycloakModelTest {
@Test @Test
public void testNoPaginationCalls() { public void testNoPaginationCalls() {
List<UserModel> list = withRealm(realmId, (session, realm) -> List<UserModel> list = withRealm(realmId, (session, realm) ->
session.users().searchForUserStream(realm,"", 0, Constants.DEFAULT_MAX_RESULTS) // Default values used in UsersResource session.users().searchForUserStream(realm, Map.of(UserModel.SEARCH, ""), 0, Constants.DEFAULT_MAX_RESULTS) // Default values used in UsersResource
.collect(Collectors.toList())); .collect(Collectors.toList()));
assertThat(list, hasSize(8)); assertThat(list, hasSize(8));
@ -83,7 +84,7 @@ public class UserPaginationTest extends KeycloakModelTest {
@Test @Test
public void testPaginationStarting0() { public void testPaginationStarting0() {
List<UserModel> list = withRealm(realmId, (session, realm) -> List<UserModel> list = withRealm(realmId, (session, realm) ->
session.users().searchForUserStream(realm,"", 0, 6) session.users().searchForUserStream(realm, Map.of(UserModel.SEARCH, ""), 0, 6)
.collect(Collectors.toList())); .collect(Collectors.toList()));
assertThat(list, hasSize(6)); assertThat(list, hasSize(6));
@ -98,7 +99,7 @@ public class UserPaginationTest extends KeycloakModelTest {
@Test @Test
public void testPaginationFirstResultInFirstProvider() { public void testPaginationFirstResultInFirstProvider() {
List<UserModel> list = withRealm(realmId, (session, realm) -> List<UserModel> list = withRealm(realmId, (session, realm) ->
session.users().searchForUserStream(realm,"", 1, 6) session.users().searchForUserStream(realm, Map.of(UserModel.SEARCH, ""), 1, 6)
.collect(Collectors.toList())); .collect(Collectors.toList()));
assertThat(list, hasSize(6)); assertThat(list, hasSize(6));
@ -111,7 +112,7 @@ public class UserPaginationTest extends KeycloakModelTest {
@Test @Test
public void testPaginationFirstResultIsExactlyTheAmountOfUsersInTheFirstProvider() { public void testPaginationFirstResultIsExactlyTheAmountOfUsersInTheFirstProvider() {
List<UserModel> list = withRealm(realmId, (session, realm) -> List<UserModel> list = withRealm(realmId, (session, realm) ->
session.users().searchForUserStream(realm,"", 4, 6) session.users().searchForUserStream(realm, Map.of(UserModel.SEARCH, ""), 4, 6)
.collect(Collectors.toList())); .collect(Collectors.toList()));
assertThat(list, hasSize(4)); assertThat(list, hasSize(4));
@ -124,7 +125,7 @@ public class UserPaginationTest extends KeycloakModelTest {
@Test @Test
public void testPaginationFirstResultIsInSecondProvider() { public void testPaginationFirstResultIsInSecondProvider() {
List<UserModel> list = withRealm(realmId, (session, realm) -> List<UserModel> list = withRealm(realmId, (session, realm) ->
session.users().searchForUserStream(realm,"", 5, 6) session.users().searchForUserStream(realm, Map.of(UserModel.SEARCH, ""), 5, 6)
.collect(Collectors.toList())); .collect(Collectors.toList()));
assertThat(list, hasSize(3)); assertThat(list, hasSize(3));