keycloak-memoizeit

Details

diff --git a/integration/admin-client/src/main/java/org/keycloak/admin/client/resource/UsersResource.java b/integration/admin-client/src/main/java/org/keycloak/admin/client/resource/UsersResource.java
index ca80245..dfac03f 100755
--- a/integration/admin-client/src/main/java/org/keycloak/admin/client/resource/UsersResource.java
+++ b/integration/admin-client/src/main/java/org/keycloak/admin/client/resource/UsersResource.java
@@ -44,6 +44,16 @@ public interface UsersResource {
 
     @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
+    @Produces(MediaType.APPLICATION_JSON)
     List<UserRepresentation> search(@QueryParam("username") String username);
 
     /**
@@ -65,6 +75,29 @@ public interface UsersResource {
                                     @QueryParam("first") Integer firstResult,
                                     @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
     @Produces(MediaType.APPLICATION_JSON)
     List<UserRepresentation> list(@QueryParam("first") Integer firstResult,
diff --git a/server-spi-private/src/main/java/org/keycloak/models/utils/ModelToRepresentation.java b/server-spi-private/src/main/java/org/keycloak/models/utils/ModelToRepresentation.java
index 17e8c1e..48bd73d 100755
--- a/server-spi-private/src/main/java/org/keycloak/models/utils/ModelToRepresentation.java
+++ b/server-spi-private/src/main/java/org/keycloak/models/utils/ModelToRepresentation.java
@@ -166,6 +166,21 @@ public class ModelToRepresentation {
         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) {
         EventRepresentation rep = new EventRepresentation();
         rep.setTime(event.getTime());
diff --git a/services/src/main/java/org/keycloak/services/resources/admin/UsersResource.java b/services/src/main/java/org/keycloak/services/resources/admin/UsersResource.java
index 6c15f68..e8a57c7 100755
--- a/services/src/main/java/org/keycloak/services/resources/admin/UsersResource.java
+++ b/services/src/main/java/org/keycloak/services/resources/admin/UsersResource.java
@@ -179,7 +179,8 @@ public class UsersResource {
                                              @QueryParam("email") String email,
                                              @QueryParam("username") String username,
                                              @QueryParam("first") Integer firstResult,
-                                             @QueryParam("max") Integer maxResults) {
+                                             @QueryParam("max") Integer maxResults,
+                                             @QueryParam("briefRepresentation") Boolean briefRepresentation) {
         auth.users().requireQuery();
 
         firstResult = firstResult != null ? firstResult : -1;
@@ -216,9 +217,12 @@ public class UsersResource {
         }
 
         boolean canViewGlobal = auth.users().canView();
+        boolean briefRepresentationB = briefRepresentation != null && briefRepresentation;
         for (UserModel user : userModels) {
             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));
             results.add(userRep);
         }
diff --git a/testsuite/integration-arquillian/tests/base/src/test/java/org/keycloak/testsuite/admin/UserTest.java b/testsuite/integration-arquillian/tests/base/src/test/java/org/keycloak/testsuite/admin/UserTest.java
index 2ea2122..266499d 100755
--- a/testsuite/integration-arquillian/tests/base/src/test/java/org/keycloak/testsuite/admin/UserTest.java
+++ b/testsuite/integration-arquillian/tests/base/src/test/java/org/keycloak/testsuite/admin/UserTest.java
@@ -86,12 +86,8 @@ import java.util.LinkedList;
 import java.util.List;
 import java.util.concurrent.atomic.AtomicInteger;
 
-import static org.junit.Assert.assertEquals;
-import static org.junit.Assert.assertNotEquals;
-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.hamcrest.Matchers.is;
+import static org.junit.Assert.*;
 import static org.keycloak.testsuite.Assert.assertNames;
 
 /**
@@ -1407,14 +1403,36 @@ public class UserTest extends AbstractAdminTest {
         UsersResource users = adminClient.realms().realm("test").users();
 
         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(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) {
         RealmRepresentation rep = realm.toRepresentation();
         rep.setEditUsernameAllowed(enable);
diff --git a/testsuite/integration-arquillian/tests/base/src/test/java/org/keycloak/testsuite/util/UserBuilder.java b/testsuite/integration-arquillian/tests/base/src/test/java/org/keycloak/testsuite/util/UserBuilder.java
index fd3ebbc..e27e97b 100644
--- a/testsuite/integration-arquillian/tests/base/src/test/java/org/keycloak/testsuite/util/UserBuilder.java
+++ b/testsuite/integration-arquillian/tests/base/src/test/java/org/keycloak/testsuite/util/UserBuilder.java
@@ -82,6 +82,15 @@ public class UserBuilder {
         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.
      */
diff --git a/themes/src/main/resources/theme/base/admin/resources/js/controllers/users.js b/themes/src/main/resources/theme/base/admin/resources/js/controllers/users.js
index 43d45b3..c51b9d5 100755
--- a/themes/src/main/resources/theme/base/admin/resources/js/controllers/users.js
+++ b/themes/src/main/resources/theme/base/admin/resources/js/controllers/users.js
@@ -235,6 +235,7 @@ module.controller('UserListCtrl', function($scope, realm, User, UserSearchState,
         
         UserSearchState.query.realm = realm.realm;
         $scope.query = UserSearchState.query;
+        $scope.query.briefRepresentation = 'true';
         
         if (!UserSearchState.isFirstSearch) $scope.searchQuery();
     };