KEYCLOAK-8150 Improve loading user list
This commit is contained in:
parent
df76afb513
commit
bee3894cdf
6 changed files with 90 additions and 10 deletions
|
@ -42,6 +42,16 @@ public interface UsersResource {
|
||||||
@QueryParam("first") Integer firstResult,
|
@QueryParam("first") Integer firstResult,
|
||||||
@QueryParam("max") Integer maxResults);
|
@QueryParam("max") Integer maxResults);
|
||||||
|
|
||||||
|
@GET
|
||||||
|
@Produces(MediaType.APPLICATION_JSON)
|
||||||
|
List<UserRepresentation> search(@QueryParam("username") String username,
|
||||||
|
@QueryParam("firstName") String firstName,
|
||||||
|
@QueryParam("lastName") String lastName,
|
||||||
|
@QueryParam("email") String email,
|
||||||
|
@QueryParam("first") Integer firstResult,
|
||||||
|
@QueryParam("max") Integer maxResults,
|
||||||
|
@QueryParam("briefRepresentation") Boolean briefRepresentation);
|
||||||
|
|
||||||
@GET
|
@GET
|
||||||
@Produces(MediaType.APPLICATION_JSON)
|
@Produces(MediaType.APPLICATION_JSON)
|
||||||
List<UserRepresentation> search(@QueryParam("username") String username);
|
List<UserRepresentation> search(@QueryParam("username") String username);
|
||||||
|
@ -65,6 +75,29 @@ public interface UsersResource {
|
||||||
@QueryParam("first") Integer firstResult,
|
@QueryParam("first") Integer firstResult,
|
||||||
@QueryParam("max") Integer maxResults);
|
@QueryParam("max") Integer maxResults);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Search for users whose username or email matches the value provided by {@code search}. The {@code search}
|
||||||
|
* argument also allows finding users by specific attributes as follows:
|
||||||
|
*
|
||||||
|
* <ul>
|
||||||
|
* <li><i>id:</i> - Find users by identifier. For instance, <i>id:aa497859-bbf5-44ac-bf1a-74dbffcaf197</i></li>
|
||||||
|
* </ul>
|
||||||
|
*
|
||||||
|
* @param search the value to search. It can be the username, email or any of the supported options to query based on user attributes
|
||||||
|
* @param firstResult the position of the first result to retrieve
|
||||||
|
* @param maxResults the maximum number of results to retreive
|
||||||
|
* @param briefRepresentation Only return basic information (only guaranteed to return id, username, created, first and last name,
|
||||||
|
* email, enabled state, email verification state, federation link, and access.
|
||||||
|
* Note that it means that namely user attributes, required actions, and not before are not returned.)
|
||||||
|
* @return a list of {@link UserRepresentation}
|
||||||
|
*/
|
||||||
|
@GET
|
||||||
|
@Produces(MediaType.APPLICATION_JSON)
|
||||||
|
List<UserRepresentation> search(@QueryParam("search") String search,
|
||||||
|
@QueryParam("first") Integer firstResult,
|
||||||
|
@QueryParam("max") Integer maxResults,
|
||||||
|
@QueryParam("briefRepresentation") Boolean briefRepresentation);
|
||||||
|
|
||||||
@GET
|
@GET
|
||||||
@Produces(MediaType.APPLICATION_JSON)
|
@Produces(MediaType.APPLICATION_JSON)
|
||||||
List<UserRepresentation> list(@QueryParam("first") Integer firstResult,
|
List<UserRepresentation> list(@QueryParam("first") Integer firstResult,
|
||||||
|
|
|
@ -166,6 +166,21 @@ public class ModelToRepresentation {
|
||||||
return rep;
|
return rep;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public static UserRepresentation toBriefRepresentation(UserModel user) {
|
||||||
|
UserRepresentation rep = new UserRepresentation();
|
||||||
|
rep.setId(user.getId());
|
||||||
|
rep.setUsername(user.getUsername());
|
||||||
|
rep.setCreatedTimestamp(user.getCreatedTimestamp());
|
||||||
|
rep.setLastName(user.getLastName());
|
||||||
|
rep.setFirstName(user.getFirstName());
|
||||||
|
rep.setEmail(user.getEmail());
|
||||||
|
rep.setEnabled(user.isEnabled());
|
||||||
|
rep.setEmailVerified(user.isEmailVerified());
|
||||||
|
rep.setFederationLink(user.getFederationLink());
|
||||||
|
|
||||||
|
return rep;
|
||||||
|
}
|
||||||
|
|
||||||
public static EventRepresentation toRepresentation(Event event) {
|
public static EventRepresentation toRepresentation(Event event) {
|
||||||
EventRepresentation rep = new EventRepresentation();
|
EventRepresentation rep = new EventRepresentation();
|
||||||
rep.setTime(event.getTime());
|
rep.setTime(event.getTime());
|
||||||
|
|
|
@ -179,7 +179,8 @@ public class UsersResource {
|
||||||
@QueryParam("email") String email,
|
@QueryParam("email") String email,
|
||||||
@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("briefRepresentation") Boolean briefRepresentation) {
|
||||||
auth.users().requireQuery();
|
auth.users().requireQuery();
|
||||||
|
|
||||||
firstResult = firstResult != null ? firstResult : -1;
|
firstResult = firstResult != null ? firstResult : -1;
|
||||||
|
@ -216,9 +217,12 @@ public class UsersResource {
|
||||||
}
|
}
|
||||||
|
|
||||||
boolean canViewGlobal = auth.users().canView();
|
boolean canViewGlobal = auth.users().canView();
|
||||||
|
boolean briefRepresentationB = briefRepresentation != null && briefRepresentation;
|
||||||
for (UserModel user : userModels) {
|
for (UserModel user : userModels) {
|
||||||
if (!canViewGlobal && !auth.users().canView(user)) continue;
|
if (!canViewGlobal && !auth.users().canView(user)) continue;
|
||||||
UserRepresentation userRep = ModelToRepresentation.toRepresentation(session, realm, user);
|
UserRepresentation userRep = briefRepresentationB
|
||||||
|
? ModelToRepresentation.toBriefRepresentation(user)
|
||||||
|
: ModelToRepresentation.toRepresentation(session, realm, user);
|
||||||
userRep.setAccess(auth.users().getAccess(user));
|
userRep.setAccess(auth.users().getAccess(user));
|
||||||
results.add(userRep);
|
results.add(userRep);
|
||||||
}
|
}
|
||||||
|
|
|
@ -86,12 +86,8 @@ import java.util.LinkedList;
|
||||||
import java.util.List;
|
import java.util.List;
|
||||||
import java.util.concurrent.atomic.AtomicInteger;
|
import java.util.concurrent.atomic.AtomicInteger;
|
||||||
|
|
||||||
import static org.junit.Assert.assertEquals;
|
import static org.hamcrest.Matchers.is;
|
||||||
import static org.junit.Assert.assertNotEquals;
|
import static org.junit.Assert.*;
|
||||||
import static org.junit.Assert.assertNotNull;
|
|
||||||
import static org.junit.Assert.assertNull;
|
|
||||||
import static org.junit.Assert.assertTrue;
|
|
||||||
import static org.junit.Assert.fail;
|
|
||||||
import static org.keycloak.testsuite.Assert.assertNames;
|
import static org.keycloak.testsuite.Assert.assertNames;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
@ -1407,14 +1403,36 @@ public class UserTest extends AbstractAdminTest {
|
||||||
UsersResource users = adminClient.realms().realm("test").users();
|
UsersResource users = adminClient.realms().realm("test").users();
|
||||||
|
|
||||||
for (int i = 0; i < 110; i++) {
|
for (int i = 0; i < 110; i++) {
|
||||||
users.create(UserBuilder.create().username("test-" + i).build()).close();
|
users.create(UserBuilder.create().username("test-" + i).addAttribute("aName", "aValue").build()).close();
|
||||||
|
}
|
||||||
|
|
||||||
|
List<UserRepresentation> result = users.search("test", null, null);
|
||||||
|
assertEquals(100, result.size());
|
||||||
|
for (UserRepresentation user : result) {
|
||||||
|
assertThat(user.getAttributes(), Matchers.notNullValue());
|
||||||
|
assertThat(user.getAttributes().keySet(), Matchers.hasSize(1));
|
||||||
|
assertThat(user.getAttributes(), Matchers.hasEntry(is("aName"), Matchers.contains("aValue")));
|
||||||
}
|
}
|
||||||
|
|
||||||
assertEquals(100, users.search("test", null, null).size());
|
|
||||||
assertEquals(105, users.search("test", 0, 105).size());
|
assertEquals(105, users.search("test", 0, 105).size());
|
||||||
assertEquals(111, users.search("test", 0, 1000).size());
|
assertEquals(111, users.search("test", 0, 1000).size());
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void defaultMaxResultsBrief() {
|
||||||
|
UsersResource users = adminClient.realms().realm("test").users();
|
||||||
|
|
||||||
|
for (int i = 0; i < 110; i++) {
|
||||||
|
users.create(UserBuilder.create().username("test-" + i).addAttribute("aName", "aValue").build()).close();
|
||||||
|
}
|
||||||
|
|
||||||
|
List<UserRepresentation> result = users.search("test", null, null, true);
|
||||||
|
assertEquals(100, result.size());
|
||||||
|
for (UserRepresentation user : result) {
|
||||||
|
assertThat(user.getAttributes(), Matchers.nullValue());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
private void switchEditUsernameAllowedOn(boolean enable) {
|
private void switchEditUsernameAllowedOn(boolean enable) {
|
||||||
RealmRepresentation rep = realm.toRepresentation();
|
RealmRepresentation rep = realm.toRepresentation();
|
||||||
rep.setEditUsernameAllowed(enable);
|
rep.setEditUsernameAllowed(enable);
|
||||||
|
|
|
@ -82,6 +82,15 @@ public class UserBuilder {
|
||||||
return this;
|
return this;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public UserBuilder addAttribute(String name, String... values) {
|
||||||
|
if (rep.getAttributes() == null) {
|
||||||
|
rep.setAttributes(new HashMap<>());
|
||||||
|
}
|
||||||
|
|
||||||
|
rep.getAttributes().put(name, Arrays.asList(values));
|
||||||
|
return this;
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* This method makes sure that there is one single password for the user.
|
* This method makes sure that there is one single password for the user.
|
||||||
*/
|
*/
|
||||||
|
|
|
@ -235,6 +235,7 @@ module.controller('UserListCtrl', function($scope, realm, User, UserSearchState,
|
||||||
|
|
||||||
UserSearchState.query.realm = realm.realm;
|
UserSearchState.query.realm = realm.realm;
|
||||||
$scope.query = UserSearchState.query;
|
$scope.query = UserSearchState.query;
|
||||||
|
$scope.query.briefRepresentation = 'true';
|
||||||
|
|
||||||
if (!UserSearchState.isFirstSearch) $scope.searchQuery();
|
if (!UserSearchState.isFirstSearch) $scope.searchQuery();
|
||||||
};
|
};
|
||||||
|
|
Loading…
Reference in a new issue