KEYCLOAK-13163 Fixed searching for user with fine-grained permissions

This commit is contained in:
Sebastian Schuster 2020-03-05 14:50:19 +01:00 committed by Pedro Igor
parent 8cfd4d60e6
commit 99aba33980
7 changed files with 48 additions and 18 deletions

View file

@ -336,19 +336,7 @@ public class LDAPStorageProvider implements UserStorageProvider,
@Override @Override
public List<UserModel> searchForUser(String search, RealmModel realm, int firstResult, int maxResults) { public List<UserModel> searchForUser(String search, RealmModel realm, int firstResult, int maxResults) {
Map<String, String> attributes = new HashMap<String, String>(); Map<String, String> attributes = new HashMap<String, String>();
int spaceIndex = search.lastIndexOf(' '); attributes.put(UserModel.SEARCH,search);
if (spaceIndex > -1) {
String firstName = search.substring(0, spaceIndex).trim();
String lastName = search.substring(spaceIndex).trim();
attributes.put(UserModel.FIRST_NAME, firstName);
attributes.put(UserModel.LAST_NAME, lastName);
} else if (search.indexOf('@') > -1) {
attributes.put(UserModel.USERNAME, search.trim().toLowerCase());
attributes.put(UserModel.EMAIL, search.trim().toLowerCase());
} else {
attributes.put(UserModel.LAST_NAME, search.trim());
attributes.put(UserModel.USERNAME, search.trim().toLowerCase());
}
return searchForUser(attributes, realm, firstResult, maxResults); return searchForUser(attributes, realm, firstResult, maxResults);
} }
@ -359,6 +347,23 @@ public class LDAPStorageProvider implements UserStorageProvider,
@Override @Override
public List<UserModel> searchForUser(Map<String, String> params, RealmModel realm, int firstResult, int maxResults) { public List<UserModel> searchForUser(Map<String, String> params, RealmModel realm, int firstResult, int maxResults) {
String search = params.get(UserModel.SEARCH);
if(search!=null) {
int spaceIndex = search.lastIndexOf(' ');
if (spaceIndex > -1) {
String firstName = search.substring(0, spaceIndex).trim();
String lastName = search.substring(spaceIndex).trim();
params.put(UserModel.FIRST_NAME, firstName);
params.put(UserModel.LAST_NAME, lastName);
} else if (search.indexOf('@') > -1) {
params.put(UserModel.USERNAME, search.trim().toLowerCase());
params.put(UserModel.EMAIL, search.trim().toLowerCase());
} else {
params.put(UserModel.LAST_NAME, search.trim());
params.put(UserModel.USERNAME, search.trim().toLowerCase());
}
}
List<UserModel> searchResults =new LinkedList<UserModel>(); List<UserModel> searchResults =new LinkedList<UserModel>();
List<LDAPObject> ldapUsers = searchLDAP(realm, params, maxResults + firstResult); List<LDAPObject> ldapUsers = searchLDAP(realm, params, maxResults + firstResult);

View file

@ -849,6 +849,21 @@ public class JpaUserProvider implements UserProvider, UserCredentialStore {
} }
switch (key) { switch (key) {
case UserModel.SEARCH:
List<Predicate> orPredicates = new ArrayList();
orPredicates.add(builder.like(builder.lower(root.get(UserModel.USERNAME)), "%" + value.toLowerCase() + "%"));
orPredicates.add(builder.like(builder.lower(root.get(UserModel.EMAIL)), "%" + value.toLowerCase() + "%"));
orPredicates.add(builder.like(
builder.lower(builder.concat(builder.concat(
builder.coalesce(root.get(UserModel.FIRST_NAME), builder.literal("")), " "),
builder.coalesce(root.get(UserModel.LAST_NAME), builder.literal("")))),
"%" + value.toLowerCase() + "%"));
predicates.add(builder.or(orPredicates.toArray(new Predicate[orPredicates.size()])));
break;
case UserModel.USERNAME: case UserModel.USERNAME:
case UserModel.FIRST_NAME: case UserModel.FIRST_NAME:
case UserModel.LAST_NAME: case UserModel.LAST_NAME:

View file

@ -37,6 +37,7 @@ public interface UserModel extends RoleMapperModel {
String LOCALE = "locale"; String LOCALE = "locale";
String INCLUDE_SERVICE_ACCOUNT = "keycloak.session.realm.users.query.include_service_account"; String INCLUDE_SERVICE_ACCOUNT = "keycloak.session.realm.users.query.include_service_account";
String GROUPS = "keycloak.session.realm.users.query.groups"; String GROUPS = "keycloak.session.realm.users.query.groups";
String SEARCH = "keycloak.session.realm.users.query.search";
interface UserRemovedEvent extends ProviderEvent { interface UserRemovedEvent extends ProviderEvent {
RealmModel getRealm(); RealmModel getRealm();

View file

@ -214,7 +214,9 @@ public class UsersResource {
userModels = Arrays.asList(userModel); userModels = Arrays.asList(userModel);
} }
} else { } else {
userModels = session.users().searchForUser(search.trim(), realm, firstResult, maxResults); Map<String, String> attributes = new HashMap<>();
attributes.put(UserModel.SEARCH, search.trim());
return searchForUser(attributes, realm, userPermissionEvaluator, briefRepresentation, firstResult, maxResults, false);
} }
} else if (last != null || first != null || email != null || username != null) { } else if (last != null || first != null || email != null || username != null) {
Map<String, String> attributes = new HashMap<>(); Map<String, String> attributes = new HashMap<>();

View file

@ -345,6 +345,7 @@ public class UserMapStorage implements UserLookupProvider, UserStorageProvider,
switch (key) { switch (key) {
case UserModel.USERNAME: case UserModel.USERNAME:
case UserModel.SEARCH:
userStream = userStream.filter(s -> s.toLowerCase().contains(value.toLowerCase())); userStream = userStream.filter(s -> s.toLowerCase().contains(value.toLowerCase()));
} }
} }

View file

@ -37,6 +37,7 @@ import java.util.Collections;
import java.util.LinkedList; import java.util.LinkedList;
import java.util.List; import java.util.List;
import java.util.Map; import java.util.Map;
import java.util.Optional;
import java.util.Properties; import java.util.Properties;
/** /**
@ -191,9 +192,10 @@ public class UserPropertyFileStorage implements UserLookupProvider, UserStorageP
@Override @Override
public List<UserModel> searchForUser(Map<String, String> attributes, RealmModel realm, int firstResult, int maxResults) { public List<UserModel> searchForUser(Map<String, String> attributes, RealmModel realm, int firstResult, int maxResults) {
String username = attributes.get(UserModel.USERNAME); String search = Optional.ofNullable(attributes.get(UserModel.USERNAME))
if (username == null) return Collections.EMPTY_LIST; .orElseGet(()-> attributes.get(UserModel.SEARCH));
return searchForUser(username, realm, firstResult, maxResults); if (search == null) return Collections.EMPTY_LIST;
return searchForUser(search, realm, firstResult, maxResults);
} }
@Override @Override

View file

@ -894,7 +894,6 @@ public class FineGrainAdminUnitTest extends AbstractKeycloakTest {
GroupModel customerAGroup = session.realms().createGroup(realm, "Customer A"); GroupModel customerAGroup = session.realms().createGroup(realm, "Customer A");
UserModel customerAManager = session.users().addUser(realm, "customer-a-manager"); UserModel customerAManager = session.users().addUser(realm, "customer-a-manager");
session.userCredentialManager().updateCredential(realm, customerAManager, UserCredentialModel.password("password")); session.userCredentialManager().updateCredential(realm, customerAManager, UserCredentialModel.password("password"));
customerAManager.joinGroup(customerAGroup);
ClientModel realmAdminClient = realm.getClientByClientId(Constants.REALM_MANAGEMENT_CLIENT_ID); ClientModel realmAdminClient = realm.getClientByClientId(Constants.REALM_MANAGEMENT_CLIENT_ID);
customerAManager.grantRole(realmAdminClient.getRole(AdminRoles.QUERY_USERS)); customerAManager.grantRole(realmAdminClient.getRole(AdminRoles.QUERY_USERS));
customerAManager.setEnabled(true); customerAManager.setEnabled(true);
@ -969,6 +968,11 @@ public class FineGrainAdminUnitTest extends AbstractKeycloakTest {
Assert.assertEquals(20, result.size()); Assert.assertEquals(20, result.size());
Assert.assertThat(result, Matchers.everyItem(Matchers.hasProperty("username", Matchers.startsWith("b")))); Assert.assertThat(result, Matchers.everyItem(Matchers.hasProperty("username", Matchers.startsWith("b"))));
result = client.realm("test").users().search("test", -1, 20, false);
Assert.assertEquals(20, result.size());
Assert.assertThat(result, Matchers.everyItem(Matchers.hasProperty("username", Matchers.startsWith("b"))));
result = client.realm("test").users().search("a", -1, 20, false); result = client.realm("test").users().search("a", -1, 20, false);
Assert.assertEquals(0, result.size()); Assert.assertEquals(0, result.size());