keycloak-memoizeit

Merge pull request #529 from stianst/master KEYCLOAK-435

7/16/2014 2:04:36 PM

Details

diff --git a/connections/mongo/src/main/java/org/keycloak/connections/mongo/api/MongoStore.java b/connections/mongo/src/main/java/org/keycloak/connections/mongo/api/MongoStore.java
index 87d3669..36f448c 100755
--- a/connections/mongo/src/main/java/org/keycloak/connections/mongo/api/MongoStore.java
+++ b/connections/mongo/src/main/java/org/keycloak/connections/mongo/api/MongoStore.java
@@ -31,7 +31,7 @@ public interface MongoStore {
 
     <T extends MongoIdentifiableEntity> List<T> loadEntities(Class<T> type, DBObject query, MongoStoreInvocationContext context);
 
-    <T extends MongoIdentifiableEntity> List<T> loadEntities(Class<T> type, DBObject query, DBObject sort, MongoStoreInvocationContext context, int firstResult, int maxResults);
+    <T extends MongoIdentifiableEntity> List<T> loadEntities(Class<T> type, DBObject query, DBObject sort, int firstResult, int maxResults, MongoStoreInvocationContext context);
 
     <T extends MongoIdentifiableEntity> int countEntities(Class<T> type, DBObject query, MongoStoreInvocationContext context);
 
diff --git a/connections/mongo/src/main/java/org/keycloak/connections/mongo/impl/MongoStoreImpl.java b/connections/mongo/src/main/java/org/keycloak/connections/mongo/impl/MongoStoreImpl.java
index 889194d..af4212b 100755
--- a/connections/mongo/src/main/java/org/keycloak/connections/mongo/impl/MongoStoreImpl.java
+++ b/connections/mongo/src/main/java/org/keycloak/connections/mongo/impl/MongoStoreImpl.java
@@ -281,14 +281,18 @@ public class MongoStoreImpl implements MongoStore {
     }
 
     @Override
-    public <T extends MongoIdentifiableEntity> List<T> loadEntities(Class<T> type, DBObject query, DBObject sort, MongoStoreInvocationContext context, int firstResult, int maxResults) {
+    public <T extends MongoIdentifiableEntity> List<T> loadEntities(Class<T> type, DBObject query, DBObject sort, int firstResult, int maxResults, MongoStoreInvocationContext context) {
         // First we should execute all pending tasks before searching DB
         context.beforeDBSearch(type);
 
         DBCollection dbCollection = getDBCollectionForType(type);
         DBCursor cursor = dbCollection.find(query);
-        cursor.skip(firstResult);
-        cursor.limit(maxResults);
+        if (firstResult != -1) {
+            cursor.skip(firstResult);
+        }
+        if (maxResults != -1) {
+            cursor.limit(maxResults);
+        }
         if (sort != null) {
             cursor.sort(sort);
         }
diff --git a/forms/common-themes/src/main/resources/theme/admin/base/resources/js/controllers/users.js b/forms/common-themes/src/main/resources/theme/admin/base/resources/js/controllers/users.js
index e38b4e1..571bbfc 100755
--- a/forms/common-themes/src/main/resources/theme/admin/base/resources/js/controllers/users.js
+++ b/forms/common-themes/src/main/resources/theme/admin/base/resources/js/controllers/users.js
@@ -134,18 +134,42 @@ module.controller('UserSocialCtrl', function($scope, realm, user, socialLinks) {
 
 module.controller('UserListCtrl', function($scope, realm, User) {
     $scope.realm = realm;
+    $scope.page = 0;
+
+    $scope.query = {
+        realm: realm.realm,
+        max : 5,
+        first : 0
+    }
+
+    $scope.firstPage = function() {
+        $scope.query.first = 0;
+        if ($scope.query.first < 0) {
+            $scope.query.first = 0;
+        }
+        $scope.searchQuery();
+    }
+
+    $scope.previousPage = function() {
+        $scope.query.first -= parseInt($scope.query.max);
+        if ($scope.query.first < 0) {
+            $scope.query.first = 0;
+        }
+        $scope.searchQuery();
+    }
+
+    $scope.nextPage = function() {
+        $scope.query.first += parseInt($scope.query.max);
+        $scope.searchQuery();
+    }
+
     $scope.searchQuery = function() {
         $scope.searchLoaded = false;
         $scope.currentSearch = $scope.search;
 
-        var params = { realm: realm.realm };
-        if ($scope.search) {
-            params.search = $scope.search;
-        }
-
-        $scope.users = User.query(params, function() {
+        $scope.users = User.query($scope.query, function() {
             $scope.searchLoaded = true;
-            $scope.lastSearch = params.search;
+            $scope.lastSearch = $scope.query.search;
         });
     };
 });
diff --git a/forms/common-themes/src/main/resources/theme/admin/base/resources/partials/user-list.html b/forms/common-themes/src/main/resources/theme/admin/base/resources/partials/user-list.html
index dc54a55..d6dcd49 100755
--- a/forms/common-themes/src/main/resources/theme/admin/base/resources/partials/user-list.html
+++ b/forms/common-themes/src/main/resources/theme/admin/base/resources/partials/user-list.html
@@ -15,7 +15,7 @@
             <tr>
                 <th class="kc-table-actions" colspan="4">
                     <div class="search-comp clearfix">
-                        <input type="text" placeholder="Search..." data-ng-model="search" class="form-control search"
+                        <input type="text" placeholder="Search..." data-ng-model="query.search" class="form-control search"
                                onkeyup="if(event.keyCode == 13){$(this).next('button').click();}">
                         <button data-ng-click="searchQuery()" type="submit"
                                 class="kc-icon-search" tooltip-placement="right"
@@ -38,19 +38,17 @@
             </tr>
             </tr>
             </thead>
-            <!-- todo -->
-            <!--<tfoot data-ng-show="users && users.length > 10">
+            <tfoot data-ng-hide="!users || users.length == 0">
             <tr>
-                <td colspan="4">
+                <td colspan="7">
                     <div class="table-nav">
-                        <a href="#" class="first disabled">First page</a><a href="#" class="prev disabled">Previous
-                        page</a><span><strong>1-8</strong> of <strong>10</strong></span><a href="#"
-                                                                                           class="next">Next
-                        page</a><a href="#" class="last">Last page</a>
+                        <button data-ng-click="firstPage()" class="first" ng-disabled="query.first == 0">First page</button>
+                        <button data-ng-click="previousPage()" class="prev" ng-disabled="query.first == 0">Previous page</button>
+                        <button data-ng-click="nextPage()" class="next" ng-disabled="users.length < query.max">Next page</button>
                     </div>
                 </td>
             </tr>
-            </tfoot>-->
+            </tfoot>
             <tbody>
             <tr ng-repeat="user in users">
                 <td><a href="#/realms/{{realm.realm}}/users/{{user.username}}">{{user.username}}</a></td>
diff --git a/model/api/src/main/java/org/keycloak/models/UserProvider.java b/model/api/src/main/java/org/keycloak/models/UserProvider.java
index 4898642..ffb0842 100755
--- a/model/api/src/main/java/org/keycloak/models/UserProvider.java
+++ b/model/api/src/main/java/org/keycloak/models/UserProvider.java
@@ -25,8 +25,11 @@ public interface UserProvider extends Provider {
     UserModel getUserByEmail(String email, RealmModel realm);
     UserModel getUserBySocialLink(SocialLinkModel socialLink, RealmModel realm);
     List<UserModel> getUsers(RealmModel realm);
+    List<UserModel> getUsers(RealmModel realm, int firstResult, int maxResults);
     List<UserModel> searchForUser(String search, RealmModel realm);
+    List<UserModel> searchForUser(String search, RealmModel realm, int firstResult, int maxResults);
     List<UserModel> searchForUserByAttributes(Map<String, String> attributes, RealmModel realm);
+    List<UserModel> searchForUserByAttributes(Map<String, String> attributes, RealmModel realm, int firstResult, int maxResults);
     Set<SocialLinkModel> getSocialLinks(UserModel user, RealmModel realm);
     SocialLinkModel getSocialLink(UserModel user, String socialProvider, RealmModel realm);
 
diff --git a/model/invalidation-cache/model-adapters/src/main/java/org/keycloak/models/cache/DefaultCacheUserProvider.java b/model/invalidation-cache/model-adapters/src/main/java/org/keycloak/models/cache/DefaultCacheUserProvider.java
index dbb465c..35521b4 100755
--- a/model/invalidation-cache/model-adapters/src/main/java/org/keycloak/models/cache/DefaultCacheUserProvider.java
+++ b/model/invalidation-cache/model-adapters/src/main/java/org/keycloak/models/cache/DefaultCacheUserProvider.java
@@ -185,16 +185,31 @@ public class DefaultCacheUserProvider implements CacheUserProvider {
     }
 
     @Override
+    public List<UserModel> getUsers(RealmModel realm, int firstResult, int maxResults) {
+        return getDelegate().getUsers(realm, firstResult, maxResults);
+    }
+
+    @Override
     public List<UserModel> searchForUser(String search, RealmModel realm) {
         return getDelegate().searchForUser(search, realm);
     }
 
     @Override
+    public List<UserModel> searchForUser(String search, RealmModel realm, int firstResult, int maxResults) {
+        return getDelegate().searchForUser(search, realm, firstResult, maxResults);
+    }
+
+    @Override
     public List<UserModel> searchForUserByAttributes(Map<String, String> attributes, RealmModel realm) {
         return getDelegate().searchForUserByAttributes(attributes, realm);
     }
 
     @Override
+    public List<UserModel> searchForUserByAttributes(Map<String, String> attributes, RealmModel realm, int firstResult, int maxResults) {
+        return getDelegate().searchForUserByAttributes(attributes, realm, firstResult, maxResults);
+    }
+
+    @Override
     public Set<SocialLinkModel> getSocialLinks(UserModel user, RealmModel realm) {
         return getDelegate().getSocialLinks(user, realm);
     }
diff --git a/model/invalidation-cache/model-adapters/src/main/java/org/keycloak/models/cache/NoCacheUserProvider.java b/model/invalidation-cache/model-adapters/src/main/java/org/keycloak/models/cache/NoCacheUserProvider.java
index ce2858d..687db36 100755
--- a/model/invalidation-cache/model-adapters/src/main/java/org/keycloak/models/cache/NoCacheUserProvider.java
+++ b/model/invalidation-cache/model-adapters/src/main/java/org/keycloak/models/cache/NoCacheUserProvider.java
@@ -67,16 +67,31 @@ public class NoCacheUserProvider implements CacheUserProvider {
     }
 
     @Override
+    public List<UserModel> getUsers(RealmModel realm, int firstResult, int maxResults) {
+        return getDelegate().getUsers(realm, firstResult, maxResults);
+    }
+
+    @Override
     public List<UserModel> searchForUser(String search, RealmModel realm) {
         return getDelegate().searchForUser(search, realm);
     }
 
     @Override
+    public List<UserModel> searchForUser(String search, RealmModel realm, int firstResult, int maxResults) {
+        return getDelegate().searchForUser(search, realm, firstResult, maxResults);
+    }
+
+    @Override
     public List<UserModel> searchForUserByAttributes(Map<String, String> attributes, RealmModel realm) {
         return getDelegate().searchForUserByAttributes(attributes, realm);
     }
 
     @Override
+    public List<UserModel> searchForUserByAttributes(Map<String, String> attributes, RealmModel realm, int firstResult, int maxResults) {
+        return getDelegate().searchForUserByAttributes(attributes, realm, firstResult, maxResults);
+    }
+
+    @Override
     public Set<SocialLinkModel> getSocialLinks(UserModel user, RealmModel realm) {
         return getDelegate().getSocialLinks(user, realm);
     }
diff --git a/model/jpa/src/main/java/org/keycloak/models/jpa/JpaUserProvider.java b/model/jpa/src/main/java/org/keycloak/models/jpa/JpaUserProvider.java
index ccdec22..db947e2 100755
--- a/model/jpa/src/main/java/org/keycloak/models/jpa/JpaUserProvider.java
+++ b/model/jpa/src/main/java/org/keycloak/models/jpa/JpaUserProvider.java
@@ -192,9 +192,20 @@ public class JpaUserProvider implements UserProvider {
 
     @Override
     public List<UserModel> getUsers(RealmModel realm) {
-        TypedQuery<UserEntity> query = em.createQuery("select u from UserEntity u where u.realm = :realm", UserEntity.class);
+        return getUsers(realm, -1, -1);
+    }
+
+    @Override
+    public List<UserModel> getUsers(RealmModel realm, int firstResult, int maxResults) {
+        TypedQuery<UserEntity> query = em.createQuery("select u from UserEntity u where u.realm = :realm order by u.username", UserEntity.class);
         RealmEntity realmEntity = em.getReference(RealmEntity.class, realm.getId());
         query.setParameter("realm", realmEntity);
+        if (firstResult != -1) {
+            query.setFirstResult(firstResult);
+        }
+        if (maxResults != -1) {
+            query.setMaxResults(maxResults);
+        }
         List<UserEntity> results = query.getResultList();
         List<UserModel> users = new ArrayList<UserModel>();
         for (UserEntity entity : results) users.add(new UserAdapter(realm, em, entity));
@@ -203,10 +214,21 @@ public class JpaUserProvider implements UserProvider {
 
     @Override
     public List<UserModel> searchForUser(String search, RealmModel realm) {
-        TypedQuery<UserEntity> query = em.createQuery("select u from UserEntity u where u.realm = :realm and ( lower(u.username) like :search or lower(concat(u.firstName, ' ', u.lastName)) like :search or u.email like :search )", UserEntity.class);
+        return searchForUser(search, realm, -1, -1);
+    }
+
+    @Override
+    public List<UserModel> searchForUser(String search, RealmModel realm, int firstResult, int maxResults) {
+        TypedQuery<UserEntity> query = em.createQuery("select u from UserEntity u where u.realm = :realm and ( lower(u.username) like :search or lower(concat(u.firstName, ' ', u.lastName)) like :search or u.email like :search ) order by u.username", UserEntity.class);
         RealmEntity realmEntity = em.getReference(RealmEntity.class, realm.getId());
         query.setParameter("realm", realmEntity);
         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 ArrayList<UserModel>();
         for (UserEntity entity : results) users.add(new UserAdapter(realm, em, entity));
@@ -215,6 +237,11 @@ public class JpaUserProvider implements UserProvider {
 
     @Override
     public List<UserModel> searchForUserByAttributes(Map<String, String> attributes, RealmModel realm) {
+        return searchForUserByAttributes(attributes, realm, -1, -1);
+    }
+
+    @Override
+    public List<UserModel> searchForUserByAttributes(Map<String, String> attributes, RealmModel realm, int firstResult, int maxResults) {
         StringBuilder builder = new StringBuilder("select u from UserEntity u");
         boolean first = true;
         for (Map.Entry<String, String> entry : attributes.entrySet()) {
@@ -237,10 +264,17 @@ public class JpaUserProvider implements UserProvider {
             }
             builder.append(attribute).append(" like '%").append(entry.getValue().toLowerCase()).append("%'");
         }
+        builder.append(" order by u.username");
         String q = builder.toString();
         TypedQuery<UserEntity> query = em.createQuery(q, UserEntity.class);
         RealmEntity realmEntity = em.getReference(RealmEntity.class, realm.getId());
         query.setParameter("realm", realmEntity);
+        if (firstResult != -1) {
+            query.setFirstResult(firstResult);
+        }
+        if (maxResults != -1) {
+            query.setMaxResults(maxResults);
+        }
         List<UserEntity> results = query.getResultList();
         List<UserModel> users = new ArrayList<UserModel>();
         for (UserEntity entity : results) users.add(new UserAdapter(realm, em, entity));
diff --git a/model/mongo/src/main/java/org/keycloak/models/mongo/keycloak/adapters/MongoUserProvider.java b/model/mongo/src/main/java/org/keycloak/models/mongo/keycloak/adapters/MongoUserProvider.java
index f9411e1..de37f54 100755
--- a/model/mongo/src/main/java/org/keycloak/models/mongo/keycloak/adapters/MongoUserProvider.java
+++ b/model/mongo/src/main/java/org/keycloak/models/mongo/keycloak/adapters/MongoUserProvider.java
@@ -1,5 +1,6 @@
 package org.keycloak.models.mongo.keycloak.adapters;
 
+import com.mongodb.BasicDBObject;
 import com.mongodb.DBObject;
 import com.mongodb.QueryBuilder;
 import org.keycloak.connections.mongo.api.MongoStore;
@@ -112,15 +113,26 @@ public class MongoUserProvider implements UserProvider {
 
     @Override
     public List<UserModel> getUsers(RealmModel realm) {
+        return getUsers(realm, -1, -1);
+    }
+
+    @Override
+    public List<UserModel> getUsers(RealmModel realm, int firstResult, int maxResults) {
         DBObject query = new QueryBuilder()
                 .and("realmId").is(realm.getId())
                 .get();
-        List<MongoUserEntity> users = getMongoStore().loadEntities(MongoUserEntity.class, query, invocationContext);
+        DBObject sort = new BasicDBObject("username", 1);
+        List<MongoUserEntity> users = getMongoStore().loadEntities(MongoUserEntity.class, query, sort, firstResult, maxResults, invocationContext);
         return convertUserEntities(realm, users);
     }
 
     @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) {
         search = search.trim();
         Pattern caseInsensitivePattern = Pattern.compile("(?i:" + search + ")");
 
@@ -155,11 +167,19 @@ public class MongoUserProvider implements UserProvider {
                 ).get()
         );
 
-        List<MongoUserEntity> users = getMongoStore().loadEntities(MongoUserEntity.class, builder.get(), invocationContext);
-        return convertUserEntities(realm, users);    }
+        DBObject sort = new BasicDBObject("username", 1);
+
+        List<MongoUserEntity> users = getMongoStore().loadEntities(MongoUserEntity.class, builder.get(), sort, firstResult, maxResults, invocationContext);
+        return convertUserEntities(realm, users);
+    }
 
     @Override
     public List<UserModel> searchForUserByAttributes(Map<String, String> attributes, RealmModel realm) {
+        return searchForUserByAttributes(attributes, realm, -1, -1);
+    }
+
+    @Override
+    public List<UserModel> searchForUserByAttributes(Map<String, String> attributes, RealmModel realm, int firstResult, int maxResults) {
         QueryBuilder queryBuilder = new QueryBuilder()
                 .and("realmId").is(realm.getId());
 
@@ -176,7 +196,10 @@ public class MongoUserProvider implements UserProvider {
                 queryBuilder.and(UserModel.EMAIL).regex(Pattern.compile("(?i:" + entry.getValue() + "$)"));
             }
         }
-        List<MongoUserEntity> users = getMongoStore().loadEntities(MongoUserEntity.class, queryBuilder.get(), invocationContext);
+
+        DBObject sort = new BasicDBObject("username", 1);
+
+        List<MongoUserEntity> users = getMongoStore().loadEntities(MongoUserEntity.class, queryBuilder.get(), sort, firstResult, maxResults, invocationContext);
         return convertUserEntities(realm, users);
     }
 
diff --git a/model/sessions-jpa/src/main/java/org/keycloak/models/sessions/jpa/JpaUserSessionProvider.java b/model/sessions-jpa/src/main/java/org/keycloak/models/sessions/jpa/JpaUserSessionProvider.java
index 182019e..2965c9b 100644
--- a/model/sessions-jpa/src/main/java/org/keycloak/models/sessions/jpa/JpaUserSessionProvider.java
+++ b/model/sessions-jpa/src/main/java/org/keycloak/models/sessions/jpa/JpaUserSessionProvider.java
@@ -97,16 +97,20 @@ public class JpaUserSessionProvider implements UserSessionProvider {
 
     @Override
     public List<UserSessionModel> getUserSessions(RealmModel realm, ClientModel client) {
-        return getUserSessions(realm, client, 0, Integer.MAX_VALUE);
+        return getUserSessions(realm, client, -1, -1);
     }
 
     public List<UserSessionModel> getUserSessions(RealmModel realm, ClientModel client, int firstResult, int maxResults) {
         List<UserSessionModel> list = new LinkedList<UserSessionModel>();
         TypedQuery<UserSessionEntity> query = em.createNamedQuery("getUserSessionByClient", UserSessionEntity.class)
                 .setParameter("realmId", realm.getId())
-                .setParameter("clientId", client.getClientId())
-                .setFirstResult(firstResult)
-                .setMaxResults(maxResults);
+                .setParameter("clientId", client.getClientId());
+        if (firstResult != -1) {
+            query.setFirstResult(firstResult);
+        }
+        if (maxResults != -1) {
+            query.setMaxResults(maxResults);
+        }
         for (UserSessionEntity entity : query.getResultList()) {
             list.add(new UserSessionAdapter(session, em, realm, entity));
         }
diff --git a/model/sessions-mongo/src/main/java/org/keycloak/models/sessions/mongo/MongoUserSessionProvider.java b/model/sessions-mongo/src/main/java/org/keycloak/models/sessions/mongo/MongoUserSessionProvider.java
index cbce2bd..b49036a 100755
--- a/model/sessions-mongo/src/main/java/org/keycloak/models/sessions/mongo/MongoUserSessionProvider.java
+++ b/model/sessions-mongo/src/main/java/org/keycloak/models/sessions/mongo/MongoUserSessionProvider.java
@@ -72,16 +72,7 @@ public class MongoUserSessionProvider implements UserSessionProvider {
 
     @Override
     public List<UserSessionModel> getUserSessions(RealmModel realm, ClientModel client) {
-        DBObject query = new QueryBuilder()
-                .and("associatedClientIds").is(client.getId())
-                .get();
-        List<MongoUserSessionEntity> sessions = mongoStore.loadEntities(MongoUserSessionEntity.class, query, invocationContext);
-
-        List<UserSessionModel> result = new LinkedList<UserSessionModel>();
-        for (MongoUserSessionEntity session : sessions) {
-            result.add(new UserSessionAdapter(this.session, session, realm, invocationContext));
-        }
-        return result;
+        return getUserSessions(realm, client, -1, -1);
     }
 
     public List<UserSessionModel> getUserSessions(RealmModel realm, ClientModel client, int firstResult, int maxResults) {
@@ -90,7 +81,7 @@ public class MongoUserSessionProvider implements UserSessionProvider {
                 .get();
         DBObject sort = new BasicDBObject("started", 1).append("id", 1);
 
-        List<MongoUserSessionEntity> sessions = mongoStore.loadEntities(MongoUserSessionEntity.class, query, sort, invocationContext, firstResult, maxResults);
+        List<MongoUserSessionEntity> sessions = mongoStore.loadEntities(MongoUserSessionEntity.class, query, sort, firstResult, maxResults, invocationContext);
         List<UserSessionModel> result = new LinkedList<UserSessionModel>();
         for (MongoUserSessionEntity session : sessions) {
             result.add(new UserSessionAdapter(this.session, session, realm, invocationContext));
diff --git a/services/src/main/java/org/keycloak/services/resources/admin/ApplicationResource.java b/services/src/main/java/org/keycloak/services/resources/admin/ApplicationResource.java
index e438faa..4b2b39c 100755
--- a/services/src/main/java/org/keycloak/services/resources/admin/ApplicationResource.java
+++ b/services/src/main/java/org/keycloak/services/resources/admin/ApplicationResource.java
@@ -343,8 +343,8 @@ public class ApplicationResource {
     @Produces(MediaType.APPLICATION_JSON)
     public List<UserSessionRepresentation> getUserSessions(@QueryParam("first") Integer firstResult, @QueryParam("max") Integer maxResults) {
         auth.requireView();
-        firstResult = firstResult != null ? firstResult : 0;
-        maxResults = maxResults != null ? maxResults : Integer.MAX_VALUE;
+        firstResult = firstResult != null ? firstResult : -1;
+        maxResults = maxResults != null ? maxResults : -1;
         List<UserSessionRepresentation> sessions = new ArrayList<UserSessionRepresentation>();
         for (UserSessionModel userSession : session.sessions().getUserSessions(application.getRealm(), application, firstResult, maxResults)) {
             UserSessionRepresentation rep = ModelToRepresentation.toRepresentation(userSession);
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 ac35189..a3fe4a2 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
@@ -317,14 +317,18 @@ public class UsersResource {
                                              @QueryParam("lastName") String last,
                                              @QueryParam("firstName") String first,
                                              @QueryParam("email") String email,
-                                             @QueryParam("username") String username) {
+                                             @QueryParam("username") String username,
+                                             @QueryParam("first") Integer firstResult,
+                                             @QueryParam("max") Integer maxResults) {
         auth.requireView();
 
-        RealmManager manager = new RealmManager(session);
+        firstResult = firstResult != null ? firstResult : -1;
+        maxResults = maxResults != null ? maxResults : -1;
+
         List<UserRepresentation> results = new ArrayList<UserRepresentation>();
         List<UserModel> userModels;
         if (search != null) {
-            userModels = manager.searchUsers(search, realm);
+            userModels = session.users().searchForUser(search.trim(), realm, firstResult, maxResults);
         } else if (last != null || first != null || email != null || username != null) {
             Map<String, String> attributes = new HashMap<String, String>();
             if (last != null) {
@@ -339,12 +343,12 @@ public class UsersResource {
             if (username != null) {
                 attributes.put(UserModel.LOGIN_NAME, username);
             }
-            userModels = session.users().searchForUserByAttributes(attributes, realm);
+            userModels = session.users().searchForUserByAttributes(attributes, realm, firstResult, maxResults);
             for (UserModel user : userModels) {
                 results.add(ModelToRepresentation.toRepresentation(user));
             }
         } else {
-            userModels = session.users().getUsers(realm);
+            userModels = session.users().getUsers(realm, firstResult, maxResults);
         }
 
         for (UserModel user : userModels) {