keycloak-uncached

Merge pull request #3152 from pedroigor/KEYCLOAK-3377 [KEYCLOAK-3377]

8/18/2016 7:43:34 PM

Changes

Details

diff --git a/model/infinispan/src/main/java/org/keycloak/models/authorization/infinispan/CachedPolicyStore.java b/model/infinispan/src/main/java/org/keycloak/models/authorization/infinispan/CachedPolicyStore.java
index 4657633..b503bce 100644
--- a/model/infinispan/src/main/java/org/keycloak/models/authorization/infinispan/CachedPolicyStore.java
+++ b/model/infinispan/src/main/java/org/keycloak/models/authorization/infinispan/CachedPolicyStore.java
@@ -104,6 +104,11 @@ public class CachedPolicyStore implements PolicyStore {
     }
 
     @Override
+    public List<Policy> findByResourceServer(Map<String, String[]> attributes, String resourceServerId, int firstResult, int maxResult) {
+        return getDelegate().findByResourceServer(attributes, resourceServerId, firstResult, maxResult);
+    }
+
+    @Override
     public List<Policy> findByResource(String resourceId) {
         List<Policy> cache = new ArrayList<>();
 
diff --git a/model/infinispan/src/main/java/org/keycloak/models/authorization/infinispan/CachedResourceStore.java b/model/infinispan/src/main/java/org/keycloak/models/authorization/infinispan/CachedResourceStore.java
index 4d9a946..f86de2f 100644
--- a/model/infinispan/src/main/java/org/keycloak/models/authorization/infinispan/CachedResourceStore.java
+++ b/model/infinispan/src/main/java/org/keycloak/models/authorization/infinispan/CachedResourceStore.java
@@ -32,6 +32,7 @@ import org.keycloak.models.authorization.infinispan.entities.CachedResource;
 import java.util.ArrayList;
 import java.util.Collections;
 import java.util.List;
+import java.util.Map;
 import java.util.Map.Entry;
 import java.util.Set;
 import java.util.stream.Collectors;
@@ -127,6 +128,11 @@ public class CachedResourceStore implements ResourceStore {
     }
 
     @Override
+    public List<Resource> findByResourceServer(Map<String, String[]> attributes, String resourceServerId, int firstResult, int maxResult) {
+        return getDelegate().findByResourceServer(attributes, resourceServerId, firstResult, maxResult);
+    }
+
+    @Override
     public List<Resource> findByScope(String... id) {
         return getDelegate().findByScope(id).stream().map(resource -> findById(resource.getId())).collect(Collectors.toList());
     }
diff --git a/model/infinispan/src/main/java/org/keycloak/models/authorization/infinispan/CachedScopeStore.java b/model/infinispan/src/main/java/org/keycloak/models/authorization/infinispan/CachedScopeStore.java
index 72f3f25..f86a7d1 100644
--- a/model/infinispan/src/main/java/org/keycloak/models/authorization/infinispan/CachedScopeStore.java
+++ b/model/infinispan/src/main/java/org/keycloak/models/authorization/infinispan/CachedScopeStore.java
@@ -30,6 +30,7 @@ import org.keycloak.models.authorization.infinispan.entities.CachedScope;
 
 import java.util.ArrayList;
 import java.util.List;
+import java.util.Map;
 import java.util.Map.Entry;
 
 /**
@@ -114,6 +115,11 @@ public class CachedScopeStore implements ScopeStore {
         return getDelegate().findByResourceServer(id);
     }
 
+    @Override
+    public List<Scope> findByResourceServer(Map<String, String[]> attributes, String resourceServerId, int firstResult, int maxResult) {
+        return getDelegate().findByResourceServer(attributes, resourceServerId, firstResult, maxResult);
+    }
+
     private String getCacheKeyForScope(String id) {
         return SCOPE_ID_CACHE_PREFIX + id;
     }
diff --git a/model/jpa/src/main/java/org/keycloak/authorization/jpa/store/JPAPolicyStore.java b/model/jpa/src/main/java/org/keycloak/authorization/jpa/store/JPAPolicyStore.java
index 8b88ad1..b57cd1e 100644
--- a/model/jpa/src/main/java/org/keycloak/authorization/jpa/store/JPAPolicyStore.java
+++ b/model/jpa/src/main/java/org/keycloak/authorization/jpa/store/JPAPolicyStore.java
@@ -27,11 +27,15 @@ import org.keycloak.models.utils.KeycloakModelUtils;
 import javax.persistence.EntityManager;
 import javax.persistence.NoResultException;
 import javax.persistence.Query;
+import javax.persistence.criteria.CriteriaBuilder;
+import javax.persistence.criteria.CriteriaQuery;
+import javax.persistence.criteria.Predicate;
+import javax.persistence.criteria.Root;
 import java.util.ArrayList;
 import java.util.Arrays;
 import java.util.Collections;
-import java.util.LinkedList;
 import java.util.List;
+import java.util.Map;
 
 /**
  * @author <a href="mailto:psilva@redhat.com">Pedro Igor</a>
@@ -101,6 +105,43 @@ public class JPAPolicyStore implements PolicyStore {
     }
 
     @Override
+    public List<Policy> findByResourceServer(Map<String, String[]> attributes, String resourceServerId, int firstResult, int maxResult) {
+        CriteriaBuilder builder = entityManager.getCriteriaBuilder();
+        CriteriaQuery<PolicyEntity> querybuilder = builder.createQuery(PolicyEntity.class);
+        Root<PolicyEntity> root = querybuilder.from(PolicyEntity.class);
+        List<Predicate> predicates = new ArrayList();
+
+        predicates.add(builder.equal(root.get("resourceServer").get("id"), resourceServerId));
+
+        attributes.forEach((name, value) -> {
+            if ("permission".equals(name)) {
+                if (Boolean.valueOf(value[0])) {
+                    predicates.add(root.get("type").in("resource", "scope"));
+                } else {
+                    predicates.add(builder.not(root.get("type").in("resource", "scope")));
+                }
+            } else if ("id".equals(name)) {
+                predicates.add(root.get(name).in(value));
+            } else {
+                predicates.add(builder.like(builder.lower(root.get(name)), "%" + value[0].toLowerCase() + "%"));
+            }
+        });
+
+        querybuilder.where(predicates.toArray(new Predicate[predicates.size()])).orderBy(builder.asc(root.get("name")));
+
+        Query query = entityManager.createQuery(querybuilder);
+
+        if (firstResult != -1) {
+            query.setFirstResult(firstResult);
+        }
+        if (maxResult != -1) {
+            query.setMaxResults(maxResult);
+        }
+
+        return query.getResultList();
+    }
+
+    @Override
     public List<Policy> findByResource(final String resourceId) {
         Query query = getEntityManager().createQuery("select p from PolicyEntity p inner join p.resources r where r.id = :resourceId");
 
diff --git a/model/jpa/src/main/java/org/keycloak/authorization/jpa/store/JPAResourceStore.java b/model/jpa/src/main/java/org/keycloak/authorization/jpa/store/JPAResourceStore.java
index 986d007..6d00bb6 100644
--- a/model/jpa/src/main/java/org/keycloak/authorization/jpa/store/JPAResourceStore.java
+++ b/model/jpa/src/main/java/org/keycloak/authorization/jpa/store/JPAResourceStore.java
@@ -26,8 +26,14 @@ import org.keycloak.models.utils.KeycloakModelUtils;
 
 import javax.persistence.EntityManager;
 import javax.persistence.Query;
+import javax.persistence.criteria.CriteriaBuilder;
+import javax.persistence.criteria.CriteriaQuery;
+import javax.persistence.criteria.Predicate;
+import javax.persistence.criteria.Root;
+import java.util.ArrayList;
 import java.util.Arrays;
 import java.util.List;
+import java.util.Map;
 
 /**
  * @author <a href="mailto:psilva@redhat.com">Pedro Igor</a>
@@ -97,6 +103,37 @@ public class JPAResourceStore implements ResourceStore {
     }
 
     @Override
+    public List findByResourceServer(Map<String, String[]> attributes, String resourceServerId, int firstResult, int maxResult) {
+        CriteriaBuilder builder = entityManager.getCriteriaBuilder();
+        CriteriaQuery<ResourceEntity> querybuilder = builder.createQuery(ResourceEntity.class);
+        Root<ResourceEntity> root = querybuilder.from(ResourceEntity.class);
+        List<Predicate> predicates = new ArrayList();
+
+        predicates.add(builder.equal(root.get("resourceServer").get("id"), resourceServerId));
+
+        attributes.forEach((name, value) -> {
+            if ("scope".equals(name)) {
+                predicates.add(root.join("scopes").get("id").in(value));
+            } else {
+                predicates.add(builder.like(builder.lower(root.get(name)), "%" + value[0].toLowerCase() + "%"));
+            }
+        });
+
+        querybuilder.where(predicates.toArray(new Predicate[predicates.size()])).orderBy(builder.asc(root.get("name")));
+
+        Query query = entityManager.createQuery(querybuilder);
+
+        if (firstResult != -1) {
+            query.setFirstResult(firstResult);
+        }
+        if (maxResult != -1) {
+            query.setMaxResults(maxResult);
+        }
+
+        return query.getResultList();
+    }
+
+    @Override
     public List<Resource> findByScope(String... id) {
         Query query = entityManager.createQuery("select r from ResourceEntity r inner join r.scopes s where s.id in (:scopeIds)");
 
diff --git a/model/jpa/src/main/java/org/keycloak/authorization/jpa/store/JPAScopeStore.java b/model/jpa/src/main/java/org/keycloak/authorization/jpa/store/JPAScopeStore.java
index cc9a956..d468314 100644
--- a/model/jpa/src/main/java/org/keycloak/authorization/jpa/store/JPAScopeStore.java
+++ b/model/jpa/src/main/java/org/keycloak/authorization/jpa/store/JPAScopeStore.java
@@ -27,7 +27,13 @@ import org.keycloak.models.utils.KeycloakModelUtils;
 import javax.persistence.EntityManager;
 import javax.persistence.NoResultException;
 import javax.persistence.Query;
+import javax.persistence.criteria.CriteriaBuilder;
+import javax.persistence.criteria.CriteriaQuery;
+import javax.persistence.criteria.Predicate;
+import javax.persistence.criteria.Root;
+import java.util.ArrayList;
 import java.util.List;
+import java.util.Map;
 
 /**
  * @author <a href="mailto:psilva@redhat.com">Pedro Igor</a>
@@ -85,4 +91,31 @@ public class JPAScopeStore implements ScopeStore {
 
         return query.getResultList();
     }
+
+    @Override
+    public List<Scope> findByResourceServer(Map<String, String[]> attributes, String resourceServerId, int firstResult, int maxResult) {
+        CriteriaBuilder builder = entityManager.getCriteriaBuilder();
+        CriteriaQuery<ScopeEntity> querybuilder = builder.createQuery(ScopeEntity.class);
+        Root<ScopeEntity> root = querybuilder.from(ScopeEntity.class);
+        List<Predicate> predicates = new ArrayList();
+
+        predicates.add(builder.equal(root.get("resourceServer").get("id"), resourceServerId));
+
+        attributes.forEach((name, value) -> {
+            predicates.add(builder.like(builder.lower(root.get(name)), "%" + value[0].toLowerCase() + "%"));
+        });
+
+        querybuilder.where(predicates.toArray(new Predicate[predicates.size()])).orderBy(builder.asc(root.get("name")));
+
+        Query query = entityManager.createQuery(querybuilder);
+
+        if (firstResult != -1) {
+            query.setFirstResult(firstResult);
+        }
+        if (maxResult != -1) {
+            query.setMaxResults(maxResult);
+        }
+
+        return query.getResultList();
+    }
 }
diff --git a/model/mongo/src/main/java/org/keycloak/authorization/mongo/store/MongoPolicyStore.java b/model/mongo/src/main/java/org/keycloak/authorization/mongo/store/MongoPolicyStore.java
index 6f0ba5d..04a3d9a 100644
--- a/model/mongo/src/main/java/org/keycloak/authorization/mongo/store/MongoPolicyStore.java
+++ b/model/mongo/src/main/java/org/keycloak/authorization/mongo/store/MongoPolicyStore.java
@@ -32,6 +32,8 @@ import org.keycloak.connections.mongo.api.context.MongoStoreInvocationContext;
 import org.keycloak.models.utils.KeycloakModelUtils;
 
 import java.util.List;
+import java.util.Map;
+import java.util.regex.Pattern;
 
 import static java.util.stream.Collectors.toList;
 
@@ -102,6 +104,31 @@ public class MongoPolicyStore implements PolicyStore {
     }
 
     @Override
+    public List<Policy> findByResourceServer(Map<String, String[]> attributes, String resourceServerId, int firstResult, int maxResult) {
+        QueryBuilder queryBuilder = new QueryBuilder()
+                .and("resourceServerId").is(resourceServerId);
+
+        attributes.forEach((name, value) -> {
+            if ("permission".equals(name)) {
+                if (Boolean.valueOf(value[0])) {
+                    queryBuilder.and("type").in(new String[] {"resource", "scope"});
+                } else {
+                    queryBuilder.and("type").notIn(new String[] {"resource", "scope"});
+                }
+            } else if ("id".equals(name)) {
+                queryBuilder.and("_id").in(value);
+            } else {
+                queryBuilder.and(name).regex(Pattern.compile(".*" + value[0] + ".*", Pattern.CASE_INSENSITIVE));
+            }
+        });
+
+        DBObject sort = new BasicDBObject("name", 1);
+
+        return getMongoStore().loadEntities(PolicyEntity.class, queryBuilder.get(), sort, firstResult, maxResult, invocationContext).stream()
+                .map(policy -> findById(policy.getId())).collect(toList());
+    }
+
+    @Override
     public List<Policy> findByResource(String resourceId) {
         DBObject query = new QueryBuilder()
                 .and("resources").is(resourceId)
diff --git a/model/mongo/src/main/java/org/keycloak/authorization/mongo/store/MongoResourceStore.java b/model/mongo/src/main/java/org/keycloak/authorization/mongo/store/MongoResourceStore.java
index 11b735b..a85de72 100644
--- a/model/mongo/src/main/java/org/keycloak/authorization/mongo/store/MongoResourceStore.java
+++ b/model/mongo/src/main/java/org/keycloak/authorization/mongo/store/MongoResourceStore.java
@@ -18,6 +18,7 @@
 
 package org.keycloak.authorization.mongo.store;
 
+import com.mongodb.BasicDBObject;
 import com.mongodb.DBObject;
 import com.mongodb.QueryBuilder;
 import org.keycloak.authorization.AuthorizationProvider;
@@ -26,12 +27,13 @@ import org.keycloak.authorization.model.ResourceServer;
 import org.keycloak.authorization.mongo.adapter.ResourceAdapter;
 import org.keycloak.authorization.mongo.entities.ResourceEntity;
 import org.keycloak.authorization.store.ResourceStore;
-import org.keycloak.connections.mongo.api.MongoIdentifiableEntity;
 import org.keycloak.connections.mongo.api.MongoStore;
 import org.keycloak.connections.mongo.api.context.MongoStoreInvocationContext;
 import org.keycloak.models.utils.KeycloakModelUtils;
 
 import java.util.List;
+import java.util.Map;
+import java.util.regex.Pattern;
 
 import static java.util.stream.Collectors.toList;
 
@@ -99,9 +101,28 @@ public class MongoResourceStore implements ResourceStore {
     }
 
     @Override
+    public List<Resource> findByResourceServer(Map<String, String[]> attributes, String resourceServerId, int firstResult, int maxResult) {
+        QueryBuilder queryBuilder = new QueryBuilder()
+                .and("resourceServerId").is(resourceServerId);
+
+        attributes.forEach((name, value) -> {
+            if ("scope".equals(name)) {
+                queryBuilder.and("scopes").in(value);
+            } else {
+                queryBuilder.and(name).regex(Pattern.compile(".*" + value[0] + ".*", Pattern.CASE_INSENSITIVE));
+            }
+        });
+
+        DBObject sort = new BasicDBObject("name", 1);
+
+        return getMongoStore().loadEntities(ResourceEntity.class, queryBuilder.get(), sort, firstResult, maxResult, invocationContext).stream()
+                .map(scope -> findById(scope.getId())).collect(toList());
+    }
+
+    @Override
     public List<Resource> findByScope(String... id) {
         DBObject query = new QueryBuilder()
-                .and("scopes.id").in(id)
+                .and("scopes").in(id)
                 .get();
 
         return getMongoStore().loadEntities(ResourceEntity.class, query, getInvocationContext()).stream()
diff --git a/model/mongo/src/main/java/org/keycloak/authorization/mongo/store/MongoScopeStore.java b/model/mongo/src/main/java/org/keycloak/authorization/mongo/store/MongoScopeStore.java
index e57b69b..4b7edd6 100644
--- a/model/mongo/src/main/java/org/keycloak/authorization/mongo/store/MongoScopeStore.java
+++ b/model/mongo/src/main/java/org/keycloak/authorization/mongo/store/MongoScopeStore.java
@@ -18,6 +18,7 @@
 
 package org.keycloak.authorization.mongo.store;
 
+import com.mongodb.BasicDBObject;
 import com.mongodb.DBObject;
 import com.mongodb.QueryBuilder;
 import org.keycloak.authorization.AuthorizationProvider;
@@ -31,6 +32,8 @@ import org.keycloak.connections.mongo.api.context.MongoStoreInvocationContext;
 import org.keycloak.models.utils.KeycloakModelUtils;
 
 import java.util.List;
+import java.util.Map;
+import java.util.regex.Pattern;
 
 import static java.util.stream.Collectors.toList;
 
@@ -98,6 +101,21 @@ public class MongoScopeStore implements ScopeStore {
                 .collect(toList());
     }
 
+    @Override
+    public List<Scope> findByResourceServer(Map<String, String[]> attributes, String resourceServerId, int firstResult, int maxResult) {
+        QueryBuilder queryBuilder = new QueryBuilder()
+                .and("resourceServerId").is(resourceServerId);
+
+        attributes.forEach((name, value) -> {
+            queryBuilder.and(name).regex(Pattern.compile(".*" + value[0] + ".*", Pattern.CASE_INSENSITIVE));
+        });
+
+        DBObject sort = new BasicDBObject("name", 1);
+
+        return getMongoStore().loadEntities(ScopeEntity.class, queryBuilder.get(), sort, firstResult, maxResult, invocationContext).stream()
+                .map(scope -> findById(scope.getId())).collect(toList());
+    }
+
     private MongoStoreInvocationContext getInvocationContext() {
         return this.invocationContext;
     }
diff --git a/server-spi/src/main/java/org/keycloak/authorization/store/PolicyStore.java b/server-spi/src/main/java/org/keycloak/authorization/store/PolicyStore.java
index f55db99..51c7dd7 100644
--- a/server-spi/src/main/java/org/keycloak/authorization/store/PolicyStore.java
+++ b/server-spi/src/main/java/org/keycloak/authorization/store/PolicyStore.java
@@ -19,11 +19,10 @@ package org.keycloak.authorization.store;
 
 
 import org.keycloak.authorization.model.Policy;
-import org.keycloak.authorization.model.Resource;
 import org.keycloak.authorization.model.ResourceServer;
-import org.keycloak.authorization.model.Scope;
 
 import java.util.List;
+import java.util.Map;
 
 /**
  * A {@link PolicyStore} is responsible to manage the persistence of {@link Policy} instances.
@@ -76,6 +75,15 @@ public interface PolicyStore {
     List<Policy> findByResourceServer(String resourceServerId);
 
     /**
+     * Returns a list of {@link Policy} associated with a {@link ResourceServer} with the given <code>resourceServerId</code>.
+     *
+     * @param attributes a map holding the attributes that will be used as a filter
+     * @param resourceServerId the identifier of a resource server
+     * @return a list of policies that belong to the given resource server
+     */
+    List<Policy> findByResourceServer(Map<String, String[]> attributes, String resourceServerId, int firstResult, int maxResult);
+
+    /**
      * Returns a list of {@link Policy} associated with a {@link org.keycloak.authorization.core.model.Resource} with the given <code>resourceId</code>.
      *
      * @param resourceId the identifier of a resource
diff --git a/server-spi/src/main/java/org/keycloak/authorization/store/ResourceStore.java b/server-spi/src/main/java/org/keycloak/authorization/store/ResourceStore.java
index 5b92808..f06be78 100644
--- a/server-spi/src/main/java/org/keycloak/authorization/store/ResourceStore.java
+++ b/server-spi/src/main/java/org/keycloak/authorization/store/ResourceStore.java
@@ -19,10 +19,9 @@ package org.keycloak.authorization.store;
 
 import org.keycloak.authorization.model.Resource;
 import org.keycloak.authorization.model.ResourceServer;
-import org.keycloak.authorization.model.Scope;
 
 import java.util.List;
-import java.util.Set;
+import java.util.Map;
 
 /**
  * A {@link ResourceStore} is responsible to manage the persistence of {@link Resource} instances.
@@ -73,6 +72,15 @@ public interface ResourceStore {
     List<Resource> findByResourceServer(String resourceServerId);
 
     /**
+     * Finds all {@link Resource} instances associated with a given resource server.
+     *
+     * @param attributes a map holding the attributes that will be used as a filter
+     * @param resourceServerId the identifier of the resource server
+     * @return a list with all resources associated with the given resource server
+     */
+    List<Resource> findByResourceServer(Map<String, String[]> attributes, String resourceServerId, int firstResult, int maxResult);
+
+    /**
      * Finds all {@link Resource} associated with a given scope.
      *
      * @param id one or more scope identifiers
diff --git a/server-spi/src/main/java/org/keycloak/authorization/store/ScopeStore.java b/server-spi/src/main/java/org/keycloak/authorization/store/ScopeStore.java
index 501217f..81a7064 100644
--- a/server-spi/src/main/java/org/keycloak/authorization/store/ScopeStore.java
+++ b/server-spi/src/main/java/org/keycloak/authorization/store/ScopeStore.java
@@ -22,6 +22,7 @@ import org.keycloak.authorization.model.ResourceServer;
 import org.keycloak.authorization.model.Scope;
 
 import java.util.List;
+import java.util.Map;
 
 /**
  * A {@link ScopeStore} is responsible to manage the persistence of {@link Scope} instances.
@@ -75,4 +76,14 @@ public interface ScopeStore {
      * @return a list of scopes that belong to the given resource server
      */
     List<Scope> findByResourceServer(String id);
-}
+
+    /**
+     * Returns a list of {@link Scope} associated with a {@link ResourceServer} with the given <code>resourceServerId</code>.
+     *
+     * @param attributes a map holding the attributes that will be used as a filter
+     * @param resourceServerId the identifier of a resource server
+     *
+     * @return a list of scopes that belong to the given resource server
+     */
+    List<Scope> findByResourceServer(Map<String, String[]> attributes, String resourceServerId, int firstResult, int maxResult);
+}
\ No newline at end of file
diff --git a/services/src/main/java/org/keycloak/authorization/admin/PolicyService.java b/services/src/main/java/org/keycloak/authorization/admin/PolicyService.java
index d7e6dee..3b9c194 100644
--- a/services/src/main/java/org/keycloak/authorization/admin/PolicyService.java
+++ b/services/src/main/java/org/keycloak/authorization/admin/PolicyService.java
@@ -26,8 +26,10 @@ import org.keycloak.authorization.policy.provider.PolicyProviderAdminService;
 import org.keycloak.authorization.policy.provider.PolicyProviderFactory;
 import org.keycloak.authorization.store.PolicyStore;
 import org.keycloak.authorization.store.StoreFactory;
+import org.keycloak.models.utils.ModelToRepresentation;
 import org.keycloak.representations.idm.authorization.PolicyProviderRepresentation;
 import org.keycloak.representations.idm.authorization.PolicyRepresentation;
+import org.keycloak.representations.idm.authorization.ResourceRepresentation;
 import org.keycloak.services.resources.admin.RealmAuth;
 
 import javax.ws.rs.Consumes;
@@ -41,6 +43,12 @@ import javax.ws.rs.Produces;
 import javax.ws.rs.QueryParam;
 import javax.ws.rs.core.Response;
 import javax.ws.rs.core.Response.Status;
+import java.util.ArrayList;
+import java.util.Collections;
+import java.util.HashMap;
+import java.util.List;
+import java.util.Map;
+import java.util.function.Consumer;
 import java.util.stream.Collectors;
 
 import static org.keycloak.models.utils.ModelToRepresentation.toRepresentation;
@@ -188,11 +196,54 @@ public class PolicyService {
     @GET
     @Produces("application/json")
     @NoCache
-    public Response findAll() {
+    public Response findAll(@QueryParam("name") String name,
+                            @QueryParam("type") String type,
+                            @QueryParam("resource") String resource,
+                            @QueryParam("permission") Boolean permission,
+                            @QueryParam("first") Integer firstResult,
+                            @QueryParam("max") Integer maxResult) {
         this.auth.requireView();
+
+        Map<String, String[]> search = new HashMap<>();
+
+        if (name != null && !"".equals(name.trim())) {
+            search.put("name", new String[] {name});
+        }
+
+        if (type != null && !"".equals(type.trim())) {
+            search.put("type", new String[] {type});
+        }
+
         StoreFactory storeFactory = authorization.getStoreFactory();
+
+        if (resource != null && !"".equals(resource.trim())) {
+            List<Policy> policies = new ArrayList<>();
+            HashMap<String, String[]> resourceSearch = new HashMap<>();
+
+            resourceSearch.put("name", new String[] {resource});
+
+            storeFactory.getResourceStore().findByResourceServer(resourceSearch, resourceServer.getId(), -1, -1).forEach(resource1 -> {
+                ResourceRepresentation resourceRepresentation = ModelToRepresentation.toRepresentation(resource1, resourceServer, authorization);
+                resourceRepresentation.getPolicies().forEach(policyRepresentation -> {
+                    Policy associated = storeFactory.getPolicyStore().findById(policyRepresentation.getId());
+                    policies.add(associated);
+                    findAssociatedPolicies(associated, policies);
+                });
+            });
+
+            if (policies.isEmpty()) {
+                return Response.ok(Collections.emptyList()).build();
+            }
+
+            search.put("id", policies.stream().map(Policy::getId).toArray(String[]::new));
+        }
+
+        if (permission != null) {
+            search.put("permission", new String[] {permission.toString()});
+        }
+
         return Response.ok(
-                storeFactory.getPolicyStore().findByResourceServer(resourceServer.getId()).stream()
+                storeFactory.getPolicyStore().findByResourceServer(search, resourceServer.getId(), firstResult != null ? firstResult : -1, maxResult != null ? maxResult : -1).stream()
                         .map(policy -> toRepresentation(policy, authorization))
                         .collect(Collectors.toList()))
                 .build();
@@ -244,4 +295,11 @@ public class PolicyService {
 
         return null;
     }
+
+    private void findAssociatedPolicies(Policy policy, List<Policy> policies) {
+        policy.getAssociatedPolicies().forEach(associated -> {
+            policies.add(associated);
+            findAssociatedPolicies(associated, policies);
+        });
+    }
 }
diff --git a/services/src/main/java/org/keycloak/authorization/admin/ResourceSetService.java b/services/src/main/java/org/keycloak/authorization/admin/ResourceSetService.java
index c628a82..d31146f 100644
--- a/services/src/main/java/org/keycloak/authorization/admin/ResourceSetService.java
+++ b/services/src/main/java/org/keycloak/authorization/admin/ResourceSetService.java
@@ -22,9 +22,13 @@ import org.keycloak.authorization.AuthorizationProvider;
 import org.keycloak.authorization.model.Policy;
 import org.keycloak.authorization.model.Resource;
 import org.keycloak.authorization.model.ResourceServer;
+import org.keycloak.authorization.model.Scope;
 import org.keycloak.authorization.store.PolicyStore;
 import org.keycloak.authorization.store.ResourceStore;
 import org.keycloak.authorization.store.StoreFactory;
+import org.keycloak.models.ClientModel;
+import org.keycloak.models.RealmModel;
+import org.keycloak.models.UserModel;
 import org.keycloak.representations.idm.authorization.ResourceRepresentation;
 import org.keycloak.services.ErrorResponse;
 import org.keycloak.services.resources.admin.RealmAuth;
@@ -40,7 +44,10 @@ import javax.ws.rs.Produces;
 import javax.ws.rs.QueryParam;
 import javax.ws.rs.core.Response;
 import javax.ws.rs.core.Response.Status;
+import java.util.Collections;
+import java.util.HashMap;
 import java.util.List;
+import java.util.Map;
 import java.util.stream.Collectors;
 
 import static org.keycloak.models.utils.ModelToRepresentation.toRepresentation;
@@ -170,12 +177,63 @@ public class ResourceSetService {
     @GET
     @NoCache
     @Produces("application/json")
-    public Response findAll() {
+    public Response findAll(@QueryParam("name") String name,
+                            @QueryParam("uri") String uri,
+                            @QueryParam("owner") String owner,
+                            @QueryParam("type") String type,
+                            @QueryParam("scope") String scope,
+                            @QueryParam("first") Integer firstResult,
+                            @QueryParam("max") Integer maxResult) {
         requireView();
         StoreFactory storeFactory = authorization.getStoreFactory();
 
+        Map<String, String[]> search = new HashMap<>();
+
+        if (name != null && !"".equals(name.trim())) {
+            search.put("name", new String[] {name});
+        }
+
+        if (uri != null && !"".equals(uri.trim())) {
+            search.put("uri", new String[] {uri});
+        }
+
+        if (owner != null && !"".equals(owner.trim())) {
+            RealmModel realm = authorization.getKeycloakSession().getContext().getRealm();
+            ClientModel clientModel = realm.getClientByClientId(owner);
+
+            if (clientModel != null) {
+                owner = clientModel.getId();
+            } else {
+                UserModel user = authorization.getKeycloakSession().users().getUserByUsername(owner, realm);
+
+                if (user != null) {
+                    owner = user.getId();
+                }
+            }
+
+            search.put("owner", new String[] {owner});
+        }
+
+        if (type != null && !"".equals(type.trim())) {
+            search.put("type", new String[] {type});
+        }
+
+        if (scope != null && !"".equals(scope.trim())) {
+            HashMap<String, String[]> scopeFilter = new HashMap<>();
+
+            scopeFilter.put("name", new String[] {scope});
+
+            List<Scope> scopes = authorization.getStoreFactory().getScopeStore().findByResourceServer(scopeFilter, resourceServer.getId(), -1, -1);
+
+            if (scopes.isEmpty()) {
+                return Response.ok(Collections.emptyList()).build();
+            }
+
+            search.put("scope", scopes.stream().map(Scope::getId).toArray(String[]::new));
+        }
+
         return Response.ok(
-                storeFactory.getResourceStore().findByResourceServer(this.resourceServer.getId()).stream()
+                storeFactory.getResourceStore().findByResourceServer(search, this.resourceServer.getId(), firstResult != null ? firstResult : -1, maxResult != null ? maxResult : -1).stream()
                         .map(resource -> toRepresentation(resource, this.resourceServer, authorization))
                         .collect(Collectors.toList()))
                 .build();
diff --git a/services/src/main/java/org/keycloak/authorization/admin/ScopeService.java b/services/src/main/java/org/keycloak/authorization/admin/ScopeService.java
index df0e6da..44464b4 100644
--- a/services/src/main/java/org/keycloak/authorization/admin/ScopeService.java
+++ b/services/src/main/java/org/keycloak/authorization/admin/ScopeService.java
@@ -41,7 +41,9 @@ import javax.ws.rs.QueryParam;
 import javax.ws.rs.core.Response;
 import javax.ws.rs.core.Response.Status;
 import java.util.Arrays;
+import java.util.HashMap;
 import java.util.List;
+import java.util.Map;
 import java.util.stream.Collectors;
 
 import static org.keycloak.models.utils.ModelToRepresentation.toRepresentation;
@@ -158,10 +160,19 @@ public class ScopeService {
 
     @GET
     @Produces("application/json")
-    public Response findAll() {
+    public Response findAll(@QueryParam("name") String name,
+                            @QueryParam("first") Integer firstResult,
+                            @QueryParam("max") Integer maxResult) {
         this.auth.requireView();
+
+        Map<String, String[]> search = new HashMap<>();
+
+        if (name != null && !"".equals(name.trim())) {
+            search.put("name", new String[] {name});
+        }
+
         return Response.ok(
-                this.authorization.getStoreFactory().getScopeStore().findByResourceServer(this.resourceServer.getId()).stream()
+                this.authorization.getStoreFactory().getScopeStore().findByResourceServer(search, this.resourceServer.getId(), firstResult != null ? firstResult : -1, maxResult != null ? maxResult : -1).stream()
                         .map(scope -> toRepresentation(scope, this.authorization))
                         .collect(Collectors.toList()))
                 .build();
diff --git a/services/src/main/java/org/keycloak/authorization/protection/resource/ResourceService.java b/services/src/main/java/org/keycloak/authorization/protection/resource/ResourceService.java
index 168d62b..b02a935 100644
--- a/services/src/main/java/org/keycloak/authorization/protection/resource/ResourceService.java
+++ b/services/src/main/java/org/keycloak/authorization/protection/resource/ResourceService.java
@@ -109,7 +109,7 @@ public class ResourceService {
     }
 
     private Set<String> findAll() {
-        Response response = this.resourceManager.findAll();
+        Response response = this.resourceManager.findAll(null, null, null, null, null, -1, -1);
         List<ResourceRepresentation> resources = (List<ResourceRepresentation>) response.getEntity();
         return resources.stream().map(ResourceRepresentation::getId).collect(Collectors.toSet());
     }
diff --git a/themes/src/main/resources/theme/base/admin/resources/js/authz/authz-controller.js b/themes/src/main/resources/theme/base/admin/resources/js/authz/authz-controller.js
index ec37854..9c67740 100644
--- a/themes/src/main/resources/theme/base/admin/resources/js/authz/authz-controller.js
+++ b/themes/src/main/resources/theme/base/admin/resources/js/authz/authz-controller.js
@@ -83,6 +83,13 @@ module.controller('ResourceServerResourceCtrl', function($scope, $http, $route, 
     $scope.realm = realm;
     $scope.client = client;
 
+    $scope.query = {
+        realm: realm.realm,
+        client : client.id,
+        max : 20,
+        first : 0
+    };
+
     ResourceServer.get({
         realm : $route.current.params.realm,
         client : client.id
@@ -93,10 +100,35 @@ module.controller('ResourceServerResourceCtrl', function($scope, $http, $route, 
             $location.path('/realms/' + $route.current.params.realm + '/clients/' + client.id + '/authz/resource-server/permission/resource/create').search({rsrid: resource._id});
         }
 
-        ResourceServerResource.query({realm : realm.realm, client : client.id}, function (data) {
-            $scope.resources = data;
-        });
+        $scope.searchQuery();
     });
+
+    $scope.firstPage = function() {
+        $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.resources = ResourceServerResource.query($scope.query, function() {
+            $scope.searchLoaded = true;
+            $scope.lastSearch = $scope.query.search;
+        });
+    };
 });
 
 module.controller('ResourceServerResourceDetailCtrl', function($scope, $http, $route, $location, realm, ResourceServer, client, ResourceServerResource, ResourceServerScope, AuthzDialog, Notifications) {
@@ -234,20 +266,52 @@ module.controller('ResourceServerScopeCtrl', function($scope, $http, $route, $lo
     $scope.realm = realm;
     $scope.client = client;
 
+    $scope.query = {
+        realm: realm.realm,
+        client : client.id,
+        max : 20,
+        first : 0
+    };
+
     ResourceServer.get({
         realm : $route.current.params.realm,
         client : client.id
     }, function(data) {
         $scope.server = data;
 
-        ResourceServerScope.query({realm : realm.realm, client : client.id}, function (data) {
-            $scope.scopes = data;
-        });
-
         $scope.createPolicy = function(scope) {
             $location.path('/realms/' + $route.current.params.realm + '/clients/' + client.id + '/authz/resource-server/permission/scope/create').search({scpid: scope.id});
         }
+
+        $scope.searchQuery();
     });
+
+    $scope.firstPage = function() {
+        $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.scopes = ResourceServerScope.query($scope.query, function() {
+            $scope.searchLoaded = true;
+            $scope.lastSearch = $scope.query.search;
+        });
+    };
 });
 
 module.controller('ResourceServerScopeDetailCtrl', function($scope, $http, $route, $location, realm, ResourceServer, client, ResourceServerScope, AuthzDialog, Notifications) {
@@ -364,6 +428,14 @@ module.controller('ResourceServerPolicyCtrl', function($scope, $http, $route, $l
     $scope.client = client;
     $scope.policyProviders = [];
 
+    $scope.query = {
+        realm: realm.realm,
+        client : client.id,
+        permission: false,
+        max : 20,
+        first : 0
+    };
+
     PolicyProvider.query({
         realm : $route.current.params.realm,
         client : client.id
@@ -380,8 +452,35 @@ module.controller('ResourceServerPolicyCtrl', function($scope, $http, $route, $l
         client : client.id
     }, function(data) {
         $scope.server = data;
+        $scope.searchQuery();
+    });
+
+    $scope.addPolicy = function(policyType) {
+        $location.url("/realms/" + realm.realm + "/clients/" + client.id + "/authz/resource-server/policy/" + policyType.type + "/create");
+    }
 
-        ResourceServerPolicy.query({realm : realm.realm, client : client.id}, function (data) {
+    $scope.firstPage = function() {
+        $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;
+
+        ResourceServerPolicy.query($scope.query, function(data) {
             $scope.policies = [];
 
             for (i = 0; i < data.length; i++) {
@@ -389,12 +488,11 @@ module.controller('ResourceServerPolicyCtrl', function($scope, $http, $route, $l
                     $scope.policies.push(data[i]);
                 }
             }
-        });
-    });
 
-    $scope.addPolicy = function(policyType) {
-        $location.url("/realms/" + realm.realm + "/clients/" + client.id + "/authz/resource-server/policy/" + policyType.type + "/create");
-    }
+            $scope.searchLoaded = true;
+            $scope.lastSearch = $scope.query.search;
+        });
+    };
 });
 
 module.controller('ResourceServerPermissionCtrl', function($scope, $http, $route, $location, realm, ResourceServer, ResourceServerPolicy, PolicyProvider, client) {
@@ -402,6 +500,14 @@ module.controller('ResourceServerPermissionCtrl', function($scope, $http, $route
     $scope.client = client;
     $scope.policyProviders = [];
 
+    $scope.query = {
+        realm: realm.realm,
+        client : client.id,
+        permission: true,
+        max : 20,
+        first : 0
+    };
+
     PolicyProvider.query({
         realm : $route.current.params.realm,
         client : client.id
@@ -418,8 +524,35 @@ module.controller('ResourceServerPermissionCtrl', function($scope, $http, $route
         client : client.id
     }, function(data) {
         $scope.server = data;
+        $scope.searchQuery();
+    });
 
-        ResourceServerPolicy.query({realm : realm.realm, client : client.id}, function (data) {
+    $scope.addPolicy = function(policyType) {
+        $location.url("/realms/" + realm.realm + "/clients/" + client.id + "/authz/resource-server/permission/" + policyType.type + "/create");
+    }
+
+    $scope.firstPage = function() {
+        $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;
+
+        ResourceServerPolicy.query($scope.query, function(data) {
             $scope.policies = [];
 
             for (i = 0; i < data.length; i++) {
@@ -427,12 +560,11 @@ module.controller('ResourceServerPermissionCtrl', function($scope, $http, $route
                     $scope.policies.push(data[i]);
                 }
             }
-        });
-    });
 
-    $scope.addPolicy = function(policyType) {
-        $location.url("/realms/" + realm.realm + "/clients/" + client.id + "/authz/resource-server/permission/" + policyType.type + "/create");
-    }
+            $scope.searchLoaded = true;
+            $scope.lastSearch = $scope.query.search;
+        });
+    };
 });
 
 module.controller('ResourceServerPolicyDroolsDetailCtrl', function($scope, $http, $route, realm, client, PolicyController) {
diff --git a/themes/src/main/resources/theme/base/admin/resources/partials/authz/permission/resource-server-permission-list.html b/themes/src/main/resources/theme/base/admin/resources/partials/authz/permission/resource-server-permission-list.html
index a41fe18..bfd1b22 100644
--- a/themes/src/main/resources/theme/base/admin/resources/partials/authz/permission/resource-server-permission-list.html
+++ b/themes/src/main/resources/theme/base/admin/resources/partials/authz/permission/resource-server-permission-list.html
@@ -10,12 +10,21 @@
                         <div class="form-group">
                             {{:: 'filter' | translate}}:&nbsp;&nbsp;
                             <div class="input-group">
-                                <input type="text" placeholder="{{:: 'name' | translate}}" data-ng-model="search.name" class="form-control search" onkeyup="if(event.keyCode == 13){$(this).next('I').click();}">
+                                <input type="text" placeholder="{{:: 'name' | translate}}" data-ng-model="query.name" class="form-control search" onkeydown="if (event.keyCode == 13) document.getElementById('policySearch').click()">
+                                <div class="input-group-addon">
+                                    <i class="fa fa-search" id="policySearch" type="submit" data-ng-click="firstPage()"></i>
+                                </div>
                             </div>
                             <div class="input-group">
-                                <select class="form-control search" data-ng-model="search.type"
-                                        ng-options="p.type as p.name group by p.group for p in policyProviders track by p.type">
-                                    <option value="" selected ng-click="search.type = ''">{{:: 'authz-all-types' | translate}}</option>
+                                <input type="text" placeholder="{{:: 'authz-resource' | translate}}" data-ng-model="query.resource" class="form-control search" onkeydown="if (event.keyCode == 13) document.getElementById('policySearch').click()">
+                                <div class="input-group-addon">
+                                    <i class="fa fa-search" type="submit" data-ng-click="firstPage()"></i>
+                                </div>
+                            </div>
+                            <div class="input-group">
+                                <select class="form-control search" data-ng-model="query.type"
+                                        ng-options="p.type as p.name group by p.group for p in policyProviders track by p.type" data-ng-change="firstPage()">
+                                    <option value="" selected ng-click="query.type = ''">{{:: 'authz-all-types' | translate}}</option>
                                 </select>
                             </div>
                         </div>
@@ -36,6 +45,17 @@
                 <th>{{:: 'authz-associated-policies' | translate}}</th>
             </tr>
         </thead>
+        <tfoot data-ng-show="policies && (policies.length >= query.max || query.first > 0)">
+        <tr>
+            <td colspan="7">
+                <div class="table-nav">
+                    <button data-ng-click="firstPage()" class="first" ng-disabled="query.first == 0">{{:: 'first-page' | translate}}</button>
+                    <button data-ng-click="previousPage()" class="prev" ng-disabled="query.first == 0">{{:: 'previous-page' | translate}}</button>
+                    <button data-ng-click="nextPage()" class="next" ng-disabled="policies.length < query.max">{{:: 'next-page' | translate}}</button>
+                </div>
+            </td>
+        </tr>
+        </tfoot>
         <tbody>
             <tr ng-repeat="policy in policies | filter: {name: search.name, type: search.type} | orderBy:'name'">
                 <td><a href="#/realms/{{realm.realm}}/clients/{{client.id}}/authz/resource-server/permission/{{policy.type}}/{{policy.id}}">{{policy.name}}</a></td>
diff --git a/themes/src/main/resources/theme/base/admin/resources/partials/authz/policy/resource-server-policy-list.html b/themes/src/main/resources/theme/base/admin/resources/partials/authz/policy/resource-server-policy-list.html
index 34240b6..84600c0 100644
--- a/themes/src/main/resources/theme/base/admin/resources/partials/authz/policy/resource-server-policy-list.html
+++ b/themes/src/main/resources/theme/base/admin/resources/partials/authz/policy/resource-server-policy-list.html
@@ -10,12 +10,21 @@
                         <div class="form-group">
                             {{:: 'filter' | translate}}:&nbsp;&nbsp;
                             <div class="input-group">
-                                <input type="text" placeholder="{{:: 'name' | translate}}" data-ng-model="search.name" class="form-control search" onkeyup="if(event.keyCode == 13){$(this).next('I').click();}">
+                                <input type="text" placeholder="{{:: 'name' | translate}}" data-ng-model="query.name" class="form-control search" onkeydown="if (event.keyCode == 13) document.getElementById('policySearch').click()">
+                                <div class="input-group-addon">
+                                    <i class="fa fa-search" id="policySearch" type="submit" data-ng-click="firstPage()"></i>
+                                </div>
                             </div>
                             <div class="input-group">
-                                <select class="form-control search" data-ng-model="search.type"
-                                        ng-options="p.type as p.name group by p.group for p in policyProviders track by p.type">
-                                    <option value="" selected ng-click="search.type = ''">{{:: 'authz-all-types' | translate}}</option>
+                                <input type="text" placeholder="{{:: 'authz-resource' | translate}}" data-ng-model="query.resource" class="form-control search" onkeydown="if (event.keyCode == 13) document.getElementById('policySearch').click()">
+                                <div class="input-group-addon">
+                                    <i class="fa fa-search" type="submit" data-ng-click="firstPage()"></i>
+                                </div>
+                            </div>
+                            <div class="input-group">
+                                <select class="form-control search" data-ng-model="query.type"
+                                        ng-options="p.type as p.name group by p.group for p in policyProviders track by p.type" data-ng-change="firstPage()">
+                                    <option value="" selected ng-click="query.type = ''">{{:: 'authz-all-types' | translate}}</option>
                                 </select>
                             </div>
                         </div>
@@ -35,6 +44,17 @@
                 <th>{{:: 'type' | translate}}</th>
             </tr>
         </thead>
+        <tfoot data-ng-show="policies && (policies.length >= query.max || query.first > 0)">
+        <tr>
+            <td colspan="7">
+                <div class="table-nav">
+                    <button data-ng-click="firstPage()" class="first" ng-disabled="query.first == 0">{{:: 'first-page' | translate}}</button>
+                    <button data-ng-click="previousPage()" class="prev" ng-disabled="query.first == 0">{{:: 'previous-page' | translate}}</button>
+                    <button data-ng-click="nextPage()" class="next" ng-disabled="policies.length < query.max">{{:: 'next-page' | translate}}</button>
+                </div>
+            </td>
+        </tr>
+        </tfoot>
         <tbody>
             <tr ng-repeat="policy in policies | filter: {name: search.name, type: search.type} | orderBy:'name'">
                 <td><a href="#/realms/{{realm.realm}}/clients/{{client.id}}/authz/resource-server/policy/{{policy.type}}/{{policy.id}}">{{policy.name}}</a></td>
diff --git a/themes/src/main/resources/theme/base/admin/resources/partials/authz/resource-server-resource-list.html b/themes/src/main/resources/theme/base/admin/resources/partials/authz/resource-server-resource-list.html
index 40b2cf9..b1c3978 100644
--- a/themes/src/main/resources/theme/base/admin/resources/partials/authz/resource-server-resource-list.html
+++ b/themes/src/main/resources/theme/base/admin/resources/partials/authz/resource-server-resource-list.html
@@ -10,22 +10,34 @@
                         {{:: 'filter' | translate}}:&nbsp;&nbsp;
                         <div class="form-group">
                             <div class="input-group">
-                                <input type="text" placeholder="{{:: 'name' | translate}}" data-ng-model="search.name" class="form-control search" onkeyup="if(event.keyCode == 13){$(this).next('I').click();}">
+                                <input type="text" placeholder="{{:: 'name' | translate}}" data-ng-model="query.name" class="form-control search" onkeydown="if (event.keyCode == 13) document.getElementById('resourceSearch').click()">
                                 <div class="input-group-addon">
-                                    <i class="fa fa-search" type="submit"></i>
+                                    <i class="fa fa-search" id="resourceSearch" type="submit" data-ng-click="firstPage()"></i>
                                 </div>
                             </div>
                             <div class="input-group">
-                                <input type="text" placeholder="{{:: 'authz-owner' | translate}}" data-ng-model="search.owner.name" class="form-control search" onkeyup="if(event.keyCode == 13){$(this).next('I').click();}">
+                                <input type="text" placeholder="{{:: 'type' | translate}}" data-ng-model="query.type" class="form-control search" onkeydown="if (event.keyCode == 13) document.getElementById('resourceSearch').click()">
                                 <div class="input-group-addon">
-                                    <i class="fa fa-search" type="submit"></i>
+                                    <i class="fa fa-search" type="submit" data-ng-click="firstPage()"></i>
                                 </div>
                             </div>
                             <div class="input-group">
-                                <select class="form-control search" data-ng-model="search.type"
-                                        ng-options="r.type as r.type for r in resources | unique : 'type'">
-                                    <option value="" selected ng-click="search.type = ''">{{:: 'type' | translate}}</option>
-                                </select>
+                                <input type="text" placeholder="{{:: 'authz-uri' | translate}}" data-ng-model="query.uri" class="form-control search" onkeydown="if (event.keyCode == 13) document.getElementById('resourceSearch').click()">
+                                <div class="input-group-addon">
+                                    <i class="fa fa-search" type="submit" data-ng-click="firstPage()"></i>
+                                </div>
+                            </div>
+                            <div class="input-group">
+                                <input type="text" placeholder="{{:: 'authz-owner' | translate}}" data-ng-model="query.owner" class="form-control search" onkeydown="if (event.keyCode == 13) document.getElementById('resourceSearch').click()">
+                                <div class="input-group-addon">
+                                    <i class="fa fa-search" type="submit" data-ng-click="firstPage()"></i>
+                                </div>
+                            </div>
+                            <div class="input-group">
+                                <input type="text" placeholder="{{:: 'authz-scope' | translate}}" data-ng-model="query.scope" class="form-control search" onkeydown="if (event.keyCode == 13) document.getElementById('resourceSearch').click()">
+                                <div class="input-group-addon">
+                                    <i class="fa fa-search" type="submit" data-ng-click="firstPage()"></i>
+                                </div>
                             </div>
                         </div>
 
@@ -45,6 +57,17 @@
                 <th>{{:: 'actions' | translate}}</th>
             </tr>
         </thead>
+        <tfoot data-ng-show="resources && (resources.length >= query.max || query.first > 0)">
+        <tr>
+            <td colspan="7">
+                <div class="table-nav">
+                    <button data-ng-click="firstPage()" class="first" ng-disabled="query.first == 0">{{:: 'first-page' | translate}}</button>
+                    <button data-ng-click="previousPage()" class="prev" ng-disabled="query.first == 0">{{:: 'previous-page' | translate}}</button>
+                    <button data-ng-click="nextPage()" class="next" ng-disabled="resources.length < query.max">{{:: 'next-page' | translate}}</button>
+                </div>
+            </td>
+        </tr>
+        </tfoot>
         <tbody>
             <tr ng-repeat="resource in resources | filter:search | orderBy:'name'">
                 <td><a href="#/realms/{{realm.realm}}/clients/{{client.id}}/authz/resource-server/resource/{{resource._id}}">{{resource.name}}</a></td>
diff --git a/themes/src/main/resources/theme/base/admin/resources/partials/authz/resource-server-scope-list.html b/themes/src/main/resources/theme/base/admin/resources/partials/authz/resource-server-scope-list.html
index 39cfc77..7886129 100644
--- a/themes/src/main/resources/theme/base/admin/resources/partials/authz/resource-server-scope-list.html
+++ b/themes/src/main/resources/theme/base/admin/resources/partials/authz/resource-server-scope-list.html
@@ -9,13 +9,12 @@
                     <div class="form-inline">
                         <div class="form-group">
                             <div class="input-group">
-                                <input type="text" placeholder="{{:: 'search.placeholder' | translate}}" data-ng-model="search.name" class="form-control search" onkeyup="if(event.keyCode == 13){$(this).next('I').click();}">
+                                <input type="text" placeholder="{{:: 'name' | translate}}" data-ng-model="query.name" class="form-control search" onkeydown="if (event.keyCode == 13) document.getElementById('scopeSearch').click()">
                                 <div class="input-group-addon">
-                                    <i class="fa fa-search" type="submit"></i>
+                                    <i class="fa fa-search" id="scopeSearch" type="submit" data-ng-click="firstPage()"></i>
                                 </div>
                             </div>
                         </div>
-
                         <div class="pull-right">
                             <a id="createScope" class="btn btn-default" href="#/realms/{{realm.realm}}/clients/{{client.id}}/authz/resource-server/scope/create">{{:: 'create' | translate}}</a>
                         </div>
@@ -29,6 +28,17 @@
                 <th>{{:: 'actions' | translate}}</th>
             </tr>
         </thead>
+        <tfoot data-ng-show="scopes && (scopes.length >= query.max || query.first > 0)">
+        <tr>
+            <td colspan="7">
+                <div class="table-nav">
+                    <button data-ng-click="firstPage()" class="first" ng-disabled="query.first == 0">{{:: 'first-page' | translate}}</button>
+                    <button data-ng-click="previousPage()" class="prev" ng-disabled="query.first == 0">{{:: 'previous-page' | translate}}</button>
+                    <button data-ng-click="nextPage()" class="next" ng-disabled="scopes.length < query.max">{{:: 'next-page' | translate}}</button>
+                </div>
+            </td>
+        </tr>
+        </tfoot>
         <tbody>
             <tr ng-repeat="scope in scopes | filter:search | orderBy:'name'">
                 <td><a href="#/realms/{{realm.realm}}/clients/{{client.id}}/authz/resource-server/scope/{{scope.id}}">{{scope.name}}</a></td>