Skip to content

Commit

Permalink
KEYCLOAK-13442 Backwards compatibility in users searching. searchForU…
Browse files Browse the repository at this point in the history
…ser(String, RealmModel, int, int) is no longer called when searching users from the admin console

(cherry picked from commit 6f62c0e)
  • Loading branch information
mposolda authored and hmlnarik committed Mar 27, 2020
1 parent 32bdd7c commit 7e1d4c8
Show file tree
Hide file tree
Showing 5 changed files with 91 additions and 17 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -59,6 +59,7 @@
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;
Expand Down Expand Up @@ -806,19 +807,10 @@ public List<UserModel> searchForUser(String search, RealmModel realm) {

@Override
public List<UserModel> searchForUser(String search, RealmModel realm, int firstResult, int maxResults) {
TypedQuery<UserEntity> query = em.createNamedQuery("searchForUser", UserEntity.class);
query.setParameter("realmId", realm.getId());
query.setParameter("search", "%" + search.toLowerCase() + "%");
if (firstResult != -1) {
query.setFirstResult(firstResult);
}
if (maxResults != -1) {
query.setMaxResults(maxResults);
}
List<UserEntity> results = query.getResultList();
List<UserModel> users = new LinkedList<>();
for (UserEntity entity : results) users.add(new UserAdapter(session, realm, em, entity));
return users;
Map<String, String> attributes = new HashMap<>();
attributes.put(UserModel.SEARCH, search);
session.setAttribute(UserModel.INCLUDE_SERVICE_ACCOUNT, false);
return searchForUser(attributes, realm, firstResult, maxResults);
}

@Override
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -44,8 +44,6 @@
@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="searchForUser", query="select u from UserEntity u where u.realmId = :realmId and (u.serviceAccountClientLink is null) and " +
"( lower(u.username) like :search or lower(concat(coalesce(u.firstName, ''), ' ', coalesce(u.lastName, ''))) like :search or u.email like :search ) order by u.username"),
@NamedQuery(name="searchForUserCount", query="select count(u) from UserEntity u where u.realmId = :realmId and (u.serviceAccountClientLink is null) and " +
"( lower(u.username) like :search or lower(concat(coalesce(u.firstName, ''), ' ', coalesce(u.lastName, ''))) like :search or u.email like :search )"),
@NamedQuery(name="getRealmUserByUsername", query="select u from UserEntity u where u.username = :username and u.realmId = :realmId"),
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -580,7 +580,11 @@ public List<UserModel> searchForUser(Map<String, String> attributes, RealmModel
public List<UserModel> searchForUser(Map<String, String> attributes, RealmModel realm, int firstResult, int maxResults) {
List<UserModel> results = query((provider, first, max) -> {
if (provider instanceof UserQueryProvider) {
return ((UserQueryProvider)provider).searchForUser(attributes, realm, first, max);
if (attributes.containsKey(UserModel.SEARCH)) {
return ((UserQueryProvider)provider).searchForUser(attributes.get(UserModel.SEARCH), realm, first, max);
} else {
return ((UserQueryProvider)provider).searchForUser(attributes, realm, first, max);
}

}
return Collections.EMPTY_LIST;
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -18,10 +18,13 @@

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 org.jboss.logging.Logger;
import org.keycloak.common.util.Time;
Expand All @@ -31,6 +34,7 @@
import org.keycloak.credential.CredentialInputValidator;
import org.keycloak.credential.CredentialModel;
import org.keycloak.credential.hash.PasswordHashProvider;
import org.keycloak.models.GroupModel;
import org.keycloak.models.KeycloakSession;
import org.keycloak.models.OTPPolicy;
import org.keycloak.models.PasswordPolicy;
Expand All @@ -44,6 +48,7 @@
import org.keycloak.storage.UserStorageProvider;
import org.keycloak.storage.adapter.AbstractUserAdapterFederatedStorage;
import org.keycloak.storage.user.UserLookupProvider;
import org.keycloak.storage.user.UserQueryProvider;
import org.keycloak.storage.user.UserRegistrationProvider;

/**
Expand All @@ -54,7 +59,8 @@
*
* @author <a href="mailto:mposolda@redhat.com">Marek Posolda</a>
*/
public class BackwardsCompatibilityUserStorage implements UserLookupProvider, UserStorageProvider, UserRegistrationProvider, CredentialInputUpdater, CredentialInputValidator {
public class BackwardsCompatibilityUserStorage implements UserLookupProvider, UserStorageProvider, UserRegistrationProvider,
CredentialInputUpdater, CredentialInputValidator, UserQueryProvider {

private static final Logger log = Logger.getLogger(BackwardsCompatibilityUserStorage.class);

Expand Down Expand Up @@ -304,6 +310,69 @@ public boolean removeUser(RealmModel realm, UserModel user) {
return users.remove(user.getUsername()) != null;
}


// UserQueryProvider methods

@Override
public int getUsersCount(RealmModel realm) {
return users.size();
}

@Override
public List<UserModel> getUsers(RealmModel realm) {
return getUsers(realm, -1, -1);
}

@Override
public 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
public List<UserModel> searchForUser(String search, RealmModel realm) {
return searchForUser(search, realm, -1, -1);
}

@Override
public List<UserModel> searchForUser(String search, RealmModel realm, int firstResult, int maxResults) {
UserModel user = getUserByUsername(search, realm);
return user == null ? Collections.emptyList() : Arrays.asList(user);
}

@Override
public List<UserModel> searchForUser(Map<String, String> params, RealmModel realm) {
// Assume that this is not supported
return Collections.emptyList();
}

@Override
public List<UserModel> searchForUser(Map<String, String> params, RealmModel realm, int firstResult, int maxResults) {
// Assume that this is not supported
return Collections.emptyList();
}

@Override
public List<UserModel> getGroupMembers(RealmModel realm, GroupModel group, int firstResult, int maxResults) {
// Assume that this is not supported
return Collections.emptyList();
}

@Override
public List<UserModel> getGroupMembers(RealmModel realm, GroupModel group) {
// Assume that this is not supported
return Collections.emptyList();
}

@Override
public List<UserModel> searchForUserByUserAttribute(String attrName, String attrValue, RealmModel realm) {
// Assume that this is not supported
return Collections.emptyList();
}

@Override
public void close() {
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -250,6 +250,17 @@ public void testDisableCredentialsInUserStorage() {
loginSuccessAndLogout("otp1", "pass");
}


@Test
public void testSearchUserStorage() {
String userId = addUserAndResetPassword("searching", "pass");
getCleanup().addUserId(userId);

// Uses same parameters as admin console when searching users
List<UserRepresentation> users = testRealmResource().users().search("searching", 0, 20, true);
Assert.assertNames(users, "searching");
}

// return created totpSecret
private String setupOTPForUserWithRequiredAction(String userId) {
// Add required action to the user to reset OTP
Expand Down

0 comments on commit 7e1d4c8

Please sign in to comment.