[KEYCLOAK-14282] Create additional filtering for GET /users endpoint for enabled/disabled users

This commit is contained in:
Plamen Kostov 2020-05-22 20:49:46 +03:00 committed by Pedro Igor
parent 6abae8bccc
commit 914b226d11
6 changed files with 74 additions and 2 deletions

View file

@ -50,6 +50,7 @@ public interface UsersResource {
@QueryParam("email") String email, @QueryParam("email") String email,
@QueryParam("first") Integer firstResult, @QueryParam("first") Integer firstResult,
@QueryParam("max") Integer maxResults, @QueryParam("max") Integer maxResults,
@QueryParam("enabled") Boolean enabled,
@QueryParam("briefRepresentation") Boolean briefRepresentation); @QueryParam("briefRepresentation") Boolean briefRepresentation);
@GET @GET

View file

@ -870,10 +870,13 @@ public class JpaUserProvider implements UserProvider, UserCredentialStore {
case LAST_NAME: case LAST_NAME:
case EMAIL: case EMAIL:
if (Boolean.valueOf(attributes.getOrDefault(UserModel.EXACT, Boolean.FALSE.toString()))) { if (Boolean.valueOf(attributes.getOrDefault(UserModel.EXACT, Boolean.FALSE.toString()))) {
predicates.add(builder.equal(builder.lower(root.get(key)), value.toLowerCase())); predicates.add(builder.equal(builder.lower(root.get(key)), value.toLowerCase()));
} else { } else {
predicates.add(builder.like(builder.lower(root.get(key)), "%" + value.toLowerCase() + "%")); predicates.add(builder.like(builder.lower(root.get(key)), "%" + value.toLowerCase() + "%"));
} }
break;
case UserModel.ENABLED:
predicates.add(builder.equal(builder.lower(root.get(key)), Boolean.parseBoolean(value.toLowerCase())));
} }
} }

View file

@ -35,6 +35,7 @@ public interface UserModel extends RoleMapperModel {
String LAST_NAME = "lastName"; String LAST_NAME = "lastName";
String EMAIL = "email"; String EMAIL = "email";
String LOCALE = "locale"; String LOCALE = "locale";
String ENABLED = "enabled";
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"; String SEARCH = "keycloak.session.realm.users.query.search";

View file

@ -204,6 +204,7 @@ public interface UserQueryProvider {
* "last" - last name * "last" - last name
* "email" - email * "email" - email
* "username" - username * "username" - username
* "enabled" - is user enabled (true/false)
* *
* If possible, implementations should treat the parameter values as patterns i.e. in RDMBS terms use LIKE. * If possible, implementations should treat the parameter values as patterns i.e. in RDMBS terms use LIKE.
* This method is used by the REST API when querying users. * This method is used by the REST API when querying users.

View file

@ -189,6 +189,7 @@ public class UsersResource {
* @param first * @param first
* @param email * @param email
* @param username * @param username
* @param enabled Boolean representing if user is enabled or not
* @param first Pagination offset * @param first Pagination offset
* @param maxResults Maximum results size (defaults to 100) * @param maxResults Maximum results size (defaults to 100)
* @return * @return
@ -203,6 +204,7 @@ public class UsersResource {
@QueryParam("username") String username, @QueryParam("username") String username,
@QueryParam("first") Integer firstResult, @QueryParam("first") Integer firstResult,
@QueryParam("max") Integer maxResults, @QueryParam("max") Integer maxResults,
@QueryParam("enabled") Boolean enabled,
@QueryParam("briefRepresentation") Boolean briefRepresentation, @QueryParam("briefRepresentation") Boolean briefRepresentation,
@QueryParam("exact") Boolean exact) { @QueryParam("exact") Boolean exact) {
UserPermissionEvaluator userPermissionEvaluator = auth.users(); UserPermissionEvaluator userPermissionEvaluator = auth.users();
@ -222,6 +224,9 @@ public class UsersResource {
} else { } else {
Map<String, String> attributes = new HashMap<>(); Map<String, String> attributes = new HashMap<>();
attributes.put(UserModel.SEARCH, search.trim()); attributes.put(UserModel.SEARCH, search.trim());
if (enabled != null) {
attributes.put(UserModel.ENABLED, enabled.toString());
}
return searchForUser(attributes, realm, userPermissionEvaluator, briefRepresentation, firstResult, maxResults, false); 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) {
@ -238,6 +243,9 @@ public class UsersResource {
if (username != null) { if (username != null) {
attributes.put(UserModel.USERNAME, username); attributes.put(UserModel.USERNAME, username);
} }
if (enabled != null) {
attributes.put(UserModel.ENABLED, enabled.toString());
}
if (exact != null) { if (exact != null) {
attributes.put(UserModel.EXACT, exact.toString()); attributes.put(UserModel.EXACT, exact.toString());
} }

View file

@ -32,7 +32,6 @@ import org.keycloak.admin.client.resource.RealmResource;
import org.keycloak.admin.client.resource.RoleMappingResource; import org.keycloak.admin.client.resource.RoleMappingResource;
import org.keycloak.admin.client.resource.UserResource; import org.keycloak.admin.client.resource.UserResource;
import org.keycloak.admin.client.resource.UsersResource; import org.keycloak.admin.client.resource.UsersResource;
import org.keycloak.broker.provider.util.SimpleHttp;
import org.keycloak.common.VerificationException; import org.keycloak.common.VerificationException;
import org.keycloak.common.util.Base64; import org.keycloak.common.util.Base64;
import org.keycloak.common.util.ObjectUtil; import org.keycloak.common.util.ObjectUtil;
@ -626,6 +625,65 @@ public class UserTest extends AbstractAdminTest {
assertEquals(1, users.size()); assertEquals(1, users.size());
} }
@Test
public void searchByEnabled() {
String userCommonName = "enabled-disabled-user";
UserRepresentation user1 = new UserRepresentation();
user1.setUsername(userCommonName + "1");
user1.setRequiredActions(Collections.emptyList());
user1.setEnabled(true);
createUser(user1);
UserRepresentation user2 = new UserRepresentation();
user2.setUsername(userCommonName + "2");
user2.setRequiredActions(Collections.emptyList());
user2.setEnabled(false);
createUser(user2);
List<UserRepresentation> enabledUsers = realm.users().search(null, null, null, null, null, null, true, false);
assertEquals(1, enabledUsers.size());
List<UserRepresentation> enabledUsersWithFilter = realm.users().search(userCommonName, null, null, null, null, null, true, true);
assertEquals(1, enabledUsersWithFilter.size());
assertEquals(user1.getUsername(), enabledUsersWithFilter.get(0).getUsername());
List<UserRepresentation> disabledUsers = realm.users().search(userCommonName, null, null, null, null, null, false, false);
assertEquals(1, disabledUsers.size());
assertEquals(user2.getUsername(), disabledUsers.get(0).getUsername());
List<UserRepresentation> allUsers = realm.users().search(userCommonName, null, null, null, 0, 100, null, true);
assertEquals(2, allUsers.size());
}
@Test
public void searchWithFilters() {
createUser();
UserRepresentation user = new UserRepresentation();
user.setUsername("user2");
user.setFirstName("First");
user.setLastName("Last");
user.setEmail("user2@localhost");
user.setRequiredActions(Collections.emptyList());
user.setEnabled(false);
createUser(user);
List<UserRepresentation> searchFirstNameAndDisabled = realm.users().search(null, "First", null, null, null, null, false, true);
assertEquals(1, searchFirstNameAndDisabled.size());
assertEquals(user.getUsername(), searchFirstNameAndDisabled.get(0).getUsername());
List<UserRepresentation> searchLastNameAndEnabled = realm.users().search(null, null, "Last", null, null, null, true, false);
assertEquals(0, searchLastNameAndEnabled.size());
List<UserRepresentation> searchEmailAndDisabled = realm.users().search(null, null, null, "user2@localhost", 0, 50, false, true);
assertEquals(1, searchEmailAndDisabled.size());
assertEquals(user.getUsername(), searchEmailAndDisabled.get(0).getUsername());
List<UserRepresentation> searchInvalidSizeAndDisabled = realm.users().search(null, null, null, null, 10, 20, null, false);
assertEquals(0, searchInvalidSizeAndDisabled.size());
}
@Test @Test
public void searchById() { public void searchById() {
String expectedUserId = createUsers().get(0); String expectedUserId = createUsers().get(0);