keycloak-uncached

Merge pull request #5021 from pedroigor/KEYCLOAK-6621 [KEYCLOAK-6621]

3/1/2018 9:40:02 AM

Changes

Details

diff --git a/core/src/main/java/org/keycloak/representations/idm/authorization/ResourceRepresentation.java b/core/src/main/java/org/keycloak/representations/idm/authorization/ResourceRepresentation.java
index ae876f0..ce823c9 100644
--- a/core/src/main/java/org/keycloak/representations/idm/authorization/ResourceRepresentation.java
+++ b/core/src/main/java/org/keycloak/representations/idm/authorization/ResourceRepresentation.java
@@ -51,9 +51,8 @@ public class ResourceRepresentation {
 
     @JsonInclude(JsonInclude.Include.NON_EMPTY)
     private List<PolicyRepresentation> policies;
-    private List<ScopeRepresentation> typedScopes;
-    private String displayName;
 
+    private String displayName;
     /**
      * Creates a new instance.
      *
@@ -187,14 +186,6 @@ public class ResourceRepresentation {
         this.ownerManagedAccess = ownerManagedAccess;
     }
 
-    public void setTypedScopes(List<ScopeRepresentation> typedScopes) {
-        this.typedScopes = typedScopes;
-    }
-
-    public List<ScopeRepresentation> getTypedScopes() {
-        return typedScopes;
-    }
-
     public void addScope(String... scopeNames) {
         if (scopes == null) {
             scopes = new HashSet<>();
diff --git a/examples/authz/servlet-authz/servlet-authz-app-config.json b/examples/authz/servlet-authz/servlet-authz-app-config.json
index 43ebde4..5b64811 100644
--- a/examples/authz/servlet-authz/servlet-authz-app-config.json
+++ b/examples/authz/servlet-authz/servlet-authz-app-config.json
@@ -25,7 +25,6 @@
     {
       "name": "Premium Resource",
       "uri": "/protected/premium/*",
-      "type": "urn:servlet-authz:protected:resource",
       "scopes": [
         {
           "name": "urn:servlet-authz:protected:premium:access"
@@ -34,7 +33,6 @@
     },
     {
       "name": "Main Page",
-      "type": "urn:servlet-authz:protected:resource",
       "scopes": [
         {
           "name": "urn:servlet-authz:page:main:actionForAdmin"
diff --git a/model/infinispan/src/main/java/org/keycloak/models/cache/infinispan/authorization/PolicyAdapter.java b/model/infinispan/src/main/java/org/keycloak/models/cache/infinispan/authorization/PolicyAdapter.java
index ae96113..c75ffa4 100644
--- a/model/infinispan/src/main/java/org/keycloak/models/cache/infinispan/authorization/PolicyAdapter.java
+++ b/model/infinispan/src/main/java/org/keycloak/models/cache/infinispan/authorization/PolicyAdapter.java
@@ -48,8 +48,9 @@ public class PolicyAdapter implements Policy, CachedModel<Policy> {
     @Override
     public Policy getDelegateForUpdate() {
         if (updated == null) {
-            cacheSession.registerPolicyInvalidation(cached.getId(), cached.getName(), cached.getResourcesIds(), cached.getScopesIds(), cached.getResourceServerId());
             updated = cacheSession.getPolicyStoreDelegate().findById(cached.getId(), cached.getResourceServerId());
+            String defaultResourceType = updated.getConfig().get("defaultResourceType");
+            cacheSession.registerPolicyInvalidation(cached.getId(), cached.getName(), cached.getResourcesIds(), cached.getScopesIds(), defaultResourceType, cached.getResourceServerId());
             if (updated == null) throw new IllegalStateException("Not found in database");
         }
         return updated;
@@ -97,7 +98,7 @@ public class PolicyAdapter implements Policy, CachedModel<Policy> {
     @Override
     public void setName(String name) {
         getDelegateForUpdate();
-        cacheSession.registerPolicyInvalidation(cached.getId(), name, cached.getResourcesIds(), cached.getScopesIds(), cached.getResourceServerId());
+        cacheSession.registerPolicyInvalidation(cached.getId(), name, cached.getResourcesIds(), cached.getScopesIds(), cached.getConfig().get("defaultResourceType"), cached.getResourceServerId());
         updated.setName(name);
 
     }
@@ -148,6 +149,10 @@ public class PolicyAdapter implements Policy, CachedModel<Policy> {
     @Override
     public void setConfig(Map<String, String> config) {
         getDelegateForUpdate();
+        if (config.containsKey("defaultResourceType") || cached.getConfig().containsKey("defaultResourceType")) {
+            cacheSession.registerPolicyInvalidation(cached.getId(), cached.getName(), cached.getResourcesIds(), cached.getScopesIds(), cached.getConfig().get("defaultResourceType"), cached.getResourceServerId());
+            cacheSession.registerPolicyInvalidation(cached.getId(), cached.getName(), cached.getResourcesIds(), cached.getScopesIds(), config.get("defaultResourceType"), cached.getResourceServerId());
+        }
         updated.setConfig(config);
 
     }
@@ -155,6 +160,9 @@ public class PolicyAdapter implements Policy, CachedModel<Policy> {
     @Override
     public void removeConfig(String name) {
         getDelegateForUpdate();
+        if (name.equals("defaultResourceType")) {
+            cacheSession.registerPolicyInvalidation(cached.getId(), cached.getName(), cached.getResourcesIds(), cached.getScopesIds(), cached.getConfig().get("defaultResourceType"), cached.getResourceServerId());
+        }
         updated.removeConfig(name);
 
     }
@@ -162,6 +170,10 @@ public class PolicyAdapter implements Policy, CachedModel<Policy> {
     @Override
     public void putConfig(String name, String value) {
         getDelegateForUpdate();
+        if (name.equals("defaultResourceType")) {
+            cacheSession.registerPolicyInvalidation(cached.getId(), cached.getName(), cached.getResourcesIds(), cached.getScopesIds(), cached.getConfig().get("defaultResourceType"), cached.getResourceServerId());
+            cacheSession.registerPolicyInvalidation(cached.getId(), cached.getName(), cached.getResourcesIds(), cached.getScopesIds(), value, cached.getResourceServerId());
+        }
         updated.putConfig(name, value);
     }
 
@@ -207,14 +219,14 @@ public class PolicyAdapter implements Policy, CachedModel<Policy> {
     @Override
     public void addScope(Scope scope) {
         getDelegateForUpdate();
-        cacheSession.registerPolicyInvalidation(cached.getId(), cached.getName(), cached.getResourcesIds(), new HashSet<>(Arrays.asList(scope.getId())), cached.getResourceServerId());
+        cacheSession.registerPolicyInvalidation(cached.getId(), cached.getName(), cached.getResourcesIds(), new HashSet<>(Arrays.asList(scope.getId())), cached.getConfig().get("defaultResourceType"), cached.getResourceServerId());
         updated.addScope(scope);
     }
 
     @Override
     public void removeScope(Scope scope) {
         getDelegateForUpdate();
-        cacheSession.registerPolicyInvalidation(cached.getId(), cached.getName(), cached.getResourcesIds(), new HashSet<>(Arrays.asList(scope.getId())), cached.getResourceServerId());
+        cacheSession.registerPolicyInvalidation(cached.getId(), cached.getName(), cached.getResourcesIds(), new HashSet<>(Arrays.asList(scope.getId())), cached.getConfig().get("defaultResourceType"), cached.getResourceServerId());
         updated.removeScope(scope);
     }
 
@@ -237,7 +249,7 @@ public class PolicyAdapter implements Policy, CachedModel<Policy> {
         getDelegateForUpdate();
         HashSet<String> resources = new HashSet<>();
         resources.add(resource.getId());
-        cacheSession.registerPolicyInvalidation(cached.getId(), cached.getName(), resources, cached.getScopesIds(), cached.getResourceServerId());
+        cacheSession.registerPolicyInvalidation(cached.getId(), cached.getName(), resources, cached.getScopesIds(), cached.getConfig().get("defaultResourceType"), cached.getResourceServerId());
         updated.addResource(resource);
 
     }
@@ -247,7 +259,7 @@ public class PolicyAdapter implements Policy, CachedModel<Policy> {
         getDelegateForUpdate();
         HashSet<String> resources = new HashSet<>();
         resources.add(resource.getId());
-        cacheSession.registerPolicyInvalidation(cached.getId(), cached.getName(), resources, cached.getScopesIds(), cached.getResourceServerId());
+        cacheSession.registerPolicyInvalidation(cached.getId(), cached.getName(), resources, cached.getScopesIds(), cached.getConfig().get("defaultResourceType"), cached.getResourceServerId());
         updated.removeResource(resource);
 
     }
diff --git a/model/infinispan/src/main/java/org/keycloak/models/cache/infinispan/authorization/StoreFactoryCacheManager.java b/model/infinispan/src/main/java/org/keycloak/models/cache/infinispan/authorization/StoreFactoryCacheManager.java
index 7b463be..0f245ee 100644
--- a/model/infinispan/src/main/java/org/keycloak/models/cache/infinispan/authorization/StoreFactoryCacheManager.java
+++ b/model/infinispan/src/main/java/org/keycloak/models/cache/infinispan/authorization/StoreFactoryCacheManager.java
@@ -27,6 +27,7 @@ import org.keycloak.models.cache.infinispan.authorization.stream.InScopePredicat
 import org.keycloak.models.cache.infinispan.entities.Revisioned;
 import org.keycloak.models.cache.infinispan.events.InvalidationEvent;
 
+import java.util.Objects;
 import java.util.Set;
 
 /**
@@ -112,6 +113,11 @@ public class StoreFactoryCacheManager extends CacheManager {
         if (resources != null) {
             for (String resource : resources) {
                 invalidations.add(StoreFactoryCacheSession.getPolicyByResource(resource, serverId));
+                if (Objects.nonNull(scopes)) {
+                    for (String scope : scopes) {
+                        invalidations.add(StoreFactoryCacheSession.getPolicyByResourceScope(scope, resource, serverId));
+                    }
+                }
             }
         }
 
@@ -124,6 +130,7 @@ public class StoreFactoryCacheManager extends CacheManager {
         if (scopes != null) {
             for (String scope : scopes) {
                 invalidations.add(StoreFactoryCacheSession.getPolicyByScope(scope, serverId));
+                invalidations.add(StoreFactoryCacheSession.getPolicyByResourceScope(scope, null, serverId));
             }
         }
     }
diff --git a/model/infinispan/src/main/java/org/keycloak/models/cache/infinispan/authorization/StoreFactoryCacheSession.java b/model/infinispan/src/main/java/org/keycloak/models/cache/infinispan/authorization/StoreFactoryCacheSession.java
index 33c1ddc..c50cec1 100644
--- a/model/infinispan/src/main/java/org/keycloak/models/cache/infinispan/authorization/StoreFactoryCacheSession.java
+++ b/model/infinispan/src/main/java/org/keycloak/models/cache/infinispan/authorization/StoreFactoryCacheSession.java
@@ -271,8 +271,11 @@ public class StoreFactoryCacheSession implements CachedStoreFactoryProvider {
         invalidationEvents.add(ResourceUpdatedEvent.create(id, name, type, uri, scopes, serverId, owner));
     }
 
-    public void registerPolicyInvalidation(String id, String name, Set<String> resources, Set<String> scopes, String serverId) {
+    public void registerPolicyInvalidation(String id, String name, Set<String> resources, Set<String> scopes, String defaultResourceType, String serverId) {
         Set<String> resourceTypes = getResourceTypes(resources, serverId);
+        if (Objects.nonNull(defaultResourceType)) {
+            resourceTypes.add(defaultResourceType);
+        }
         cache.policyUpdated(id, name, resources, resourceTypes, scopes, serverId, invalidations);
         PolicyAdapter adapter = managedPolicies.get(id);
         if (adapter != null) adapter.invalidateFlag();
@@ -369,6 +372,10 @@ public class StoreFactoryCacheSession implements CachedStoreFactoryProvider {
         return "policy.scope." + scope + "." + serverId;
     }
 
+    public static String getPolicyByResourceScope(String scope, String resourceId, String serverId) {
+        return "policy.resource. " + resourceId + ".scope." + scope + "." + serverId;
+    }
+
     public static String getPermissionTicketByResource(String resourceId, String serverId) {
         return "permission.ticket.resource." + resourceId + "." + serverId;
     }
@@ -664,8 +671,9 @@ public class StoreFactoryCacheSession implements CachedStoreFactoryProvider {
         @Override
         public Policy create(AbstractPolicyRepresentation representation, ResourceServer resourceServer) {
             Policy policy = getPolicyStoreDelegate().create(representation, resourceServer);
-            registerPolicyInvalidation(policy.getId(), representation.getName(), representation.getResources(), representation.getScopes(), resourceServer.getId());
-            return policy;
+            Policy cached = findById(policy.getId(), resourceServer.getId());
+            registerPolicyInvalidation(policy.getId(), representation.getName(), representation.getResources(), representation.getScopes(), null, resourceServer.getId());
+            return cached;
         }
 
         @Override
@@ -678,6 +686,10 @@ public class StoreFactoryCacheSession implements CachedStoreFactoryProvider {
             Set<String> resources = policy.getResources().stream().map(resource -> resource.getId()).collect(Collectors.toSet());
             ResourceServer resourceServer = policy.getResourceServer();
             Set<String> resourceTypes = getResourceTypes(resources, resourceServer.getId());
+            String defaultResourceType = policy.getConfig().get("defaultResourceType");
+            if (Objects.nonNull(defaultResourceType)) {
+                resourceTypes.add(defaultResourceType);
+            }
             Set<String> scopes = policy.getScopes().stream().map(scope -> scope.getId()).collect(Collectors.toSet());
             invalidationEvents.add(PolicyRemovedEvent.create(id, policy.getName(), resources, resourceTypes, scopes, resourceServer.getId()));
             cache.policyRemoval(id, policy.getName(), resources, resourceTypes, scopes, resourceServer.getId(), invalidations);
@@ -772,6 +784,19 @@ public class StoreFactoryCacheSession implements CachedStoreFactoryProvider {
         }
 
         @Override
+        public List<Policy> findByScopeIds(List<String> scopeIds, String resourceId, String resourceServerId) {
+            if (scopeIds == null) return null;
+            List<Policy> result = new ArrayList<>();
+
+            for (String id : scopeIds) {
+                String cacheKey = getPolicyByResourceScope(id, resourceId, resourceServerId);
+                result.addAll(cacheQuery(cacheKey, PolicyScopeListQuery.class, () -> getPolicyStoreDelegate().findByScopeIds(Arrays.asList(id), resourceId, resourceServerId), (revision, resources) -> new PolicyScopeListQuery(revision, cacheKey, id, resources.stream().map(resource -> resource.getId()).collect(Collectors.toSet()), resourceServerId), resourceServerId));
+            }
+
+            return result;
+        }
+
+        @Override
         public List<Policy> findByType(String type, String resourceServerId) {
             return getPolicyStoreDelegate().findByType(type, resourceServerId);
         }
diff --git a/model/jpa/src/main/java/org/keycloak/authorization/jpa/entities/PolicyEntity.java b/model/jpa/src/main/java/org/keycloak/authorization/jpa/entities/PolicyEntity.java
index 7601d84..9b7e3b1 100644
--- a/model/jpa/src/main/java/org/keycloak/authorization/jpa/entities/PolicyEntity.java
+++ b/model/jpa/src/main/java/org/keycloak/authorization/jpa/entities/PolicyEntity.java
@@ -57,6 +57,8 @@ import org.keycloak.representations.idm.authorization.Logic;
                 @NamedQuery(name="findPolicyIdByName", query="select p.id from PolicyEntity p where  p.resourceServer.id = :serverId  and p.name = :name"),
                 @NamedQuery(name="findPolicyIdByResource", query="select p.id from PolicyEntity p inner join p.resources r where p.resourceServer.id = :serverId and (r.resourceServer.id = :serverId and r.id = :resourceId)"),
                 @NamedQuery(name="findPolicyIdByScope", query="select pe.id from PolicyEntity pe where pe.resourceServer.id = :serverId and pe.id IN (select p.id from ScopeEntity s inner join s.policies p where s.resourceServer.id = :serverId and (p.resourceServer.id = :serverId and p.type = 'scope' and s.id in (:scopeIds)))"),
+                @NamedQuery(name="findPolicyIdByResourceScope", query="select pe.id from PolicyEntity pe where pe.resourceServer.id = :serverId and pe.id IN (select p.id from ScopeEntity s inner join s.policies p where s.resourceServer.id = :serverId and (p.resourceServer.id = :serverId and p.type = 'scope' and s.id in (:scopeIds))) and pe.id IN (select p.id from ResourceEntity r inner join r.policies p where r.resourceServer.id = :serverId and (p.resourceServer.id = :serverId and p.type = 'scope' and r.id in (:resourceId))))"),
+                @NamedQuery(name="findPolicyIdByNullResourceScope", query="select pe.id from PolicyEntity pe where pe.resourceServer.id = :serverId and pe.id IN (select p.id from ScopeEntity s inner join s.policies p where s.resourceServer.id = :serverId and (p.resourceServer.id = :serverId and p.type = 'scope' and s.id in (:scopeIds))) and pe.resources is empty"),
                 @NamedQuery(name="findPolicyIdByType", query="select p.id from PolicyEntity p where p.resourceServer.id = :serverId and p.type = :type"),
                 @NamedQuery(name="findPolicyIdByResourceType", query="select p.id from PolicyEntity p inner join p.config c where p.resourceServer.id = :serverId and KEY(c) = 'defaultResourceType' and c like :type"),
                 @NamedQuery(name="findPolicyIdByDependentPolices", query="select p.id from PolicyEntity p inner join p.associatedPolicies ap where p.resourceServer.id = :serverId and (ap.resourceServer.id = :serverId and ap.id = :policyId)"),
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 1bd41e2..139973c 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
@@ -22,6 +22,7 @@ import java.util.Collections;
 import java.util.LinkedList;
 import java.util.List;
 import java.util.Map;
+import java.util.Objects;
 
 import javax.persistence.EntityManager;
 import javax.persistence.FlushModeType;
@@ -114,7 +115,10 @@ public class JPAPolicyStore implements PolicyStore {
         List<String> result = query.getResultList();
         List<Policy> list = new LinkedList<>();
         for (String id : result) {
-            list.add(provider.getStoreFactory().getPolicyStore().findById(id, resourceServerId));
+            Policy policy = provider.getStoreFactory().getPolicyStore().findById(id, resourceServerId);
+            if (Objects.nonNull(policy)) {
+                list.add(policy);
+            }
         }
         return list;
     }
@@ -157,7 +161,10 @@ public class JPAPolicyStore implements PolicyStore {
         List<String> result = query.getResultList();
         List<Policy> list = new LinkedList<>();
         for (String id : result) {
-            list.add(provider.getStoreFactory().getPolicyStore().findById(id, resourceServerId));
+            Policy policy = provider.getStoreFactory().getPolicyStore().findById(id, resourceServerId);
+            if (Objects.nonNull(policy)) {
+                list.add(policy);
+            }
         }
         return list;
     }
@@ -173,7 +180,10 @@ public class JPAPolicyStore implements PolicyStore {
         List<String> result = query.getResultList();
         List<Policy> list = new LinkedList<>();
         for (String id : result) {
-            list.add(provider.getStoreFactory().getPolicyStore().findById(id, resourceServerId));
+            Policy policy = provider.getStoreFactory().getPolicyStore().findById(id, resourceServerId);
+            if (Objects.nonNull(policy)) {
+                list.add(policy);
+            }
         }
         return list;
     }
@@ -189,7 +199,10 @@ public class JPAPolicyStore implements PolicyStore {
         List<String> result = query.getResultList();
         List<Policy> list = new LinkedList<>();
         for (String id : result) {
-            list.add(provider.getStoreFactory().getPolicyStore().findById(id, resourceServerId));
+            Policy policy = provider.getStoreFactory().getPolicyStore().findById(id, resourceServerId);
+            if (Objects.nonNull(policy)) {
+                list.add(policy);
+            }
         }
         return list;
     }
@@ -210,7 +223,41 @@ public class JPAPolicyStore implements PolicyStore {
         List<String> result = query.getResultList();
         List<Policy> list = new LinkedList<>();
         for (String id : result) {
-            list.add(provider.getStoreFactory().getPolicyStore().findById(id, resourceServerId));
+            Policy policy = provider.getStoreFactory().getPolicyStore().findById(id, resourceServerId);
+            if (Objects.nonNull(policy)) {
+                list.add(policy);
+            }
+        }
+        return list;
+    }
+
+    @Override
+    public List<Policy> findByScopeIds(List<String> scopeIds, String resourceId, String resourceServerId) {
+        if (scopeIds==null || scopeIds.isEmpty()) {
+            return Collections.emptyList();
+        }
+
+        // Use separate subquery to handle DB2 and MSSSQL
+        TypedQuery<String> query;
+
+        if (resourceId == null) {
+            query = entityManager.createNamedQuery("findPolicyIdByNullResourceScope", String.class);
+        } else {
+            query = entityManager.createNamedQuery("findPolicyIdByResourceScope", String.class);
+            query.setParameter("resourceId", resourceId);
+        }
+
+        query.setFlushMode(FlushModeType.COMMIT);
+        query.setParameter("scopeIds", scopeIds);
+        query.setParameter("serverId", resourceServerId);
+
+        List<String> result = query.getResultList();
+        List<Policy> list = new LinkedList<>();
+        for (String id : result) {
+            Policy policy = provider.getStoreFactory().getPolicyStore().findById(id, resourceServerId);
+            if (Objects.nonNull(policy)) {
+                list.add(policy);
+            }
         }
         return list;
     }
@@ -226,7 +273,10 @@ public class JPAPolicyStore implements PolicyStore {
         List<String> result = query.getResultList();
         List<Policy> list = new LinkedList<>();
         for (String id : result) {
-            list.add(provider.getStoreFactory().getPolicyStore().findById(id, resourceServerId));
+            Policy policy = provider.getStoreFactory().getPolicyStore().findById(id, resourceServerId);
+            if (Objects.nonNull(policy)) {
+                list.add(policy);
+            }
         }
         return list;
     }
@@ -243,7 +293,10 @@ public class JPAPolicyStore implements PolicyStore {
         List<String> result = query.getResultList();
         List<Policy> list = new LinkedList<>();
         for (String id : result) {
-            list.add(provider.getStoreFactory().getPolicyStore().findById(id, resourceServerId));
+            Policy policy = provider.getStoreFactory().getPolicyStore().findById(id, resourceServerId);
+            if (Objects.nonNull(policy)) {
+                list.add(policy);
+            }
         }
         return list;
     }
diff --git a/server-spi-private/src/main/java/org/keycloak/authorization/AuthorizationProvider.java b/server-spi-private/src/main/java/org/keycloak/authorization/AuthorizationProvider.java
index 81da078..0f58741 100644
--- a/server-spi-private/src/main/java/org/keycloak/authorization/AuthorizationProvider.java
+++ b/server-spi-private/src/main/java/org/keycloak/authorization/AuthorizationProvider.java
@@ -386,6 +386,11 @@ public final class AuthorizationProvider implements Provider {
             }
 
             @Override
+            public List<Policy> findByScopeIds(List<String> scopeIds, String resourceId, String resourceServerId) {
+                return policyStore.findByScopeIds(scopeIds, resourceId, resourceServerId);
+            }
+
+            @Override
             public List<Policy> findByType(String type, String resourceServerId) {
                 return policyStore.findByType(type, resourceServerId);
             }
diff --git a/server-spi-private/src/main/java/org/keycloak/authorization/policy/evaluation/DefaultPolicyEvaluator.java b/server-spi-private/src/main/java/org/keycloak/authorization/policy/evaluation/DefaultPolicyEvaluator.java
index 9b708c5..0cc0622 100644
--- a/server-spi-private/src/main/java/org/keycloak/authorization/policy/evaluation/DefaultPolicyEvaluator.java
+++ b/server-spi-private/src/main/java/org/keycloak/authorization/policy/evaluation/DefaultPolicyEvaluator.java
@@ -79,22 +79,19 @@ public class DefaultPolicyEvaluator implements PolicyEvaluator {
                 evaluatePolicies(() -> {
                     List<Policy> policies = policyStore.findByResourceType(resource.getType(), resourceServer.getId());
 
-                    for (Resource typedResource : resourceStore.findByType(resource.getType(), resourceServer.getId())) {
-                        policies.addAll(policyStore.findByResource(typedResource.getId(), resourceServer.getId()));
+                    if (!resource.getOwner().equals(resourceServer.getId())) {
+                        for (Resource typedResource : resourceStore.findByType(resource.getType(), resourceServer.getId())) {
+                            policies.addAll(policyStore.findByResource(typedResource.getId(), resourceServer.getId()));
+                        }
                     }
 
                     return policies;
                 }, consumer);
             }
-
-            if (scopes.isEmpty() && !resource.getScopes().isEmpty()) {
-                scopes.removeAll(resource.getScopes());
-                evaluatePolicies(() -> policyStore.findByScopeIds(resource.getScopes().stream().map(Scope::getId).collect(Collectors.toList()), resourceServer.getId()), consumer);
-            }
         }
 
         if (!scopes.isEmpty()) {
-            evaluatePolicies(() -> policyStore.findByScopeIds(scopes.stream().map(Scope::getId).collect(Collectors.toList()), resourceServer.getId()), consumer);
+            evaluatePolicies(() -> policyStore.findByScopeIds(scopes.stream().map(Scope::getId).collect(Collectors.toList()), null, resourceServer.getId()), consumer);
         }
 
         if (PolicyEnforcementMode.PERMISSIVE.equals(enforcementMode) && !verified.get()) {
diff --git a/server-spi-private/src/main/java/org/keycloak/authorization/store/PolicyStore.java b/server-spi-private/src/main/java/org/keycloak/authorization/store/PolicyStore.java
index dbf64a4..29ff235 100644
--- a/server-spi-private/src/main/java/org/keycloak/authorization/store/PolicyStore.java
+++ b/server-spi-private/src/main/java/org/keycloak/authorization/store/PolicyStore.java
@@ -112,6 +112,16 @@ public interface PolicyStore {
     List<Policy> findByScopeIds(List<String> scopeIds, String resourceServerId);
 
     /**
+     * Returns a list of {@link Policy} associated with a {@link org.keycloak.authorization.core.model.Scope} with the given <code>resourceId</code> and <code>scopeIds</code>.
+     *
+     * @param scopeIds the id of the scopes
+     * @param resourceId the id of the resource
+     * @param resourceServerId the resource server id
+     * @return a list of policies associated with the given scopes
+     */
+    List<Policy> findByScopeIds(List<String> scopeIds, String resourceId, String resourceServerId);
+
+    /**
      * Returns a list of {@link Policy} with the given <code>type</code>.
      *
      * @param type the type of the policy
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 56c95e5..22b4a3e 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
@@ -841,24 +841,6 @@ public class ModelToRepresentation {
                 }
                 return scope;
             }).collect(Collectors.toSet()));
-
-            if (resource.getType() != null) {
-                ResourceStore resourceStore = authorization.getStoreFactory().getResourceStore();
-                for (Resource typed : resourceStore.findByType(resource.getType(), resourceServer.getId())) {
-                    if (typed.getOwner().equals(resourceServer.getId()) && !typed.getId().equals(resource.getId())) {
-                        resource.setTypedScopes(typed.getScopes().stream().map(model1 -> {
-                            ScopeRepresentation scope = new ScopeRepresentation();
-                            scope.setId(model1.getId());
-                            scope.setName(model1.getName());
-                            String iconUri = model1.getIconUri();
-                            if (iconUri != null) {
-                                scope.setIconUri(iconUri);
-                            }
-                            return scope;
-                        }).filter(scopeRepresentation -> !resource.getScopes().contains(scopeRepresentation)).collect(Collectors.toList()));
-                    }
-                }
-            }
         }
 
         return resource;
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 ff294c9..0e4f911 100644
--- a/services/src/main/java/org/keycloak/authorization/admin/ResourceSetService.java
+++ b/services/src/main/java/org/keycloak/authorization/admin/ResourceSetService.java
@@ -234,7 +234,7 @@ public class ResourceSetService {
             return representation;
         }).collect(Collectors.toList());
 
-        if (model.getType() != null) {
+        if (model.getType() != null && !model.getOwner().equals(resourceServer.getId())) {
             ResourceStore resourceStore = authorization.getStoreFactory().getResourceStore();
             for (Resource typed : resourceStore.findByType(model.getType(), resourceServer.getId())) {
                 if (typed.getOwner().equals(resourceServer.getId()) && !typed.getId().equals(model.getId())) {
@@ -273,7 +273,8 @@ public class ResourceSetService {
 
         policies.addAll(policyStore.findByResource(model.getId(), resourceServer.getId()));
         policies.addAll(policyStore.findByResourceType(model.getType(), resourceServer.getId()));
-        policies.addAll(policyStore.findByScopeIds(model.getScopes().stream().map(scope -> scope.getId()).collect(Collectors.toList()), resourceServer.getId()));
+        policies.addAll(policyStore.findByScopeIds(model.getScopes().stream().map(scope -> scope.getId()).collect(Collectors.toList()), id, resourceServer.getId()));
+        policies.addAll(policyStore.findByScopeIds(model.getScopes().stream().map(scope -> scope.getId()).collect(Collectors.toList()), null, resourceServer.getId()));
 
         List<PolicyRepresentation> representation = new ArrayList<>();
 
diff --git a/testsuite/integration-arquillian/test-apps/servlet-authz/servlet-authz-app-authz-service.json b/testsuite/integration-arquillian/test-apps/servlet-authz/servlet-authz-app-authz-service.json
index 0a31003..b074ebc 100644
--- a/testsuite/integration-arquillian/test-apps/servlet-authz/servlet-authz-app-authz-service.json
+++ b/testsuite/integration-arquillian/test-apps/servlet-authz/servlet-authz-app-authz-service.json
@@ -25,7 +25,6 @@
     {
       "name": "Premium Resource",
       "uri": "/protected/premium/*",
-      "type": "urn:servlet-authz:protected:resource",
       "scopes": [
         {
           "name": "urn:servlet-authz:protected:premium:access"
@@ -34,7 +33,6 @@
     },
     {
       "name": "Main Page",
-      "type": "urn:servlet-authz:protected:resource",
       "scopes": [
         {
           "name": "urn:servlet-authz:page:main:actionForAdmin"
diff --git a/testsuite/integration-arquillian/test-apps/servlet-policy-enforcer/servlet-policy-enforcer-authz-realm.json b/testsuite/integration-arquillian/test-apps/servlet-policy-enforcer/servlet-policy-enforcer-authz-realm.json
index a5299bf..073dd80 100644
--- a/testsuite/integration-arquillian/test-apps/servlet-policy-enforcer/servlet-policy-enforcer-authz-realm.json
+++ b/testsuite/integration-arquillian/test-apps/servlet-policy-enforcer/servlet-policy-enforcer-authz-realm.json
@@ -62,64 +62,51 @@
                 "resources": [
                     {
                         "name": "Welcome Resource",
-                        "uri": "",
-                        "typedScopes": []
+                        "uri": ""
                     },
                     {
                         "name": "Pattern 1",
-                        "uri": "",
-                        "typedScopes": []
+                        "uri": ""
                     },
                     {
                         "name": "Pattern 2",
-                        "uri": "/resource/resource-a",
-                        "typedScopes": []
+                        "uri": "/resource/resource-a"
                     },
                     {
                         "name": "Pattern 3",
-                        "uri": "/resource/resource-b/test",
-                        "typedScopes": []
+                        "uri": "/resource/resource-b/test"
                     },
                     {
                         "name": "Pattern 4",
-                        "uri": "/resource-c",
-                        "typedScopes": []
+                        "uri": "/resource-c"
                     },
                     {
                         "name": "Pattern 5",
-                        "uri": "/resource/d/resource-d",
-                        "typedScopes": []
+                        "uri": "/resource/d/resource-d"
                     },
                     {
                         "name": "Pattern 6",
-                        "uri": "",
-                        "typedScopes": []
+                        "uri": ""
                     },
                     {
                         "name": "Pattern 7",
-                        "uri": "",
-                        "typedScopes": []
+                        "uri": ""
                     },
                     {
-                        "name": "Pattern 8",
-                        "typedScopes": []
+                        "name": "Pattern 8"
                     },
                     {
-                        "name": "Pattern 9",
-                        "typedScopes": []
+                        "name": "Pattern 9"
                     },
                     {
-                        "name": "Pattern 10",
-                        "typedScopes": []
+                        "name": "Pattern 10"
                     },
                     {
-                        "name": "Pattern 11",
-                        "typedScopes": []
+                        "name": "Pattern 11"
                     },
                     {
                         "name": "Pattern 12",
-                        "uri": "/realm_uri",
-                        "typedScopes": []
+                        "uri": "/realm_uri"
                     }
                 ],
                 "policies": [
diff --git a/testsuite/integration-arquillian/tests/base/src/test/resources/authorization-test/acme-resource-server-cleanup-test.json b/testsuite/integration-arquillian/tests/base/src/test/resources/authorization-test/acme-resource-server-cleanup-test.json
index 3c1f55f..902e861 100644
--- a/testsuite/integration-arquillian/tests/base/src/test/resources/authorization-test/acme-resource-server-cleanup-test.json
+++ b/testsuite/integration-arquillian/tests/base/src/test/resources/authorization-test/acme-resource-server-cleanup-test.json
@@ -13,8 +13,7 @@
         {
           "name": "urn:acme.com:scopes:admin:view"
         }
-      ],
-      "typedScopes": []
+      ]
     },
     {
       "name": "Role resource",
@@ -24,8 +23,7 @@
         {
           "name": "urn:acme.com:scopes:role:view"
         }
-      ],
-      "typedScopes": []
+      ]
     },
     {
       "name": "User profile resource",
@@ -38,8 +36,7 @@
         {
           "name": "urn:acme.com:scopes:userprofile:view"
         }
-      ],
-      "typedScopes": []
+      ]
     },
     {
       "name": "Account resource",
@@ -49,8 +46,7 @@
         {
           "name": "urn:acme.com:scopes:account:manage"
         }
-      ],
-      "typedScopes": []
+      ]
     }
   ],
   "policies": [
diff --git a/testsuite/integration-arquillian/tests/base/src/test/resources/migration-test/migration-realm-2.2.1.Final.json b/testsuite/integration-arquillian/tests/base/src/test/resources/migration-test/migration-realm-2.2.1.Final.json
index 910905f..eddd6b9 100644
--- a/testsuite/integration-arquillian/tests/base/src/test/resources/migration-test/migration-realm-2.2.1.Final.json
+++ b/testsuite/integration-arquillian/tests/base/src/test/resources/migration-test/migration-realm-2.2.1.Final.json
@@ -134,8 +134,7 @@
                 {
                   "name": "urn:photoz.com:scopes:profile:view"
                 }
-              ],
-              "typedScopes": []
+              ]
             },
             {
               "name": "Album Resource",
@@ -151,8 +150,7 @@
                 {
                   "name": "urn:photoz.com:scopes:album:delete"
                 }
-              ],
-              "typedScopes": []
+              ]
             },
             {
               "name": "Admin Resources",
@@ -162,8 +160,7 @@
                 {
                   "name": "urn:photoz.com:scopes:album:admin:manage"
                 }
-              ],
-              "typedScopes": []
+              ]
             }
           ],
           "policies": [
diff --git a/testsuite/integration-arquillian/tests/other/console/src/main/java/org/keycloak/testsuite/console/page/clients/authorization/permission/ScopePermissionForm.java b/testsuite/integration-arquillian/tests/other/console/src/main/java/org/keycloak/testsuite/console/page/clients/authorization/permission/ScopePermissionForm.java
index 4172843..0f59f7e 100644
--- a/testsuite/integration-arquillian/tests/other/console/src/main/java/org/keycloak/testsuite/console/page/clients/authorization/permission/ScopePermissionForm.java
+++ b/testsuite/integration-arquillian/tests/other/console/src/main/java/org/keycloak/testsuite/console/page/clients/authorization/permission/ScopePermissionForm.java
@@ -44,6 +44,7 @@ import org.keycloak.testsuite.console.page.fragment.ModalDialog;
 import org.keycloak.testsuite.console.page.fragment.MultipleStringSelect2;
 import org.keycloak.testsuite.console.page.fragment.SingleStringSelect2;
 import org.keycloak.testsuite.page.Form;
+import org.openqa.selenium.By;
 import org.openqa.selenium.WebElement;
 import org.openqa.selenium.support.FindBy;
 import org.openqa.selenium.support.ui.Select;
@@ -115,6 +116,7 @@ public class ScopePermissionForm extends Form {
             resourceSelect.update(resources);
             resourceScopeSelect.update(expected.getScopes());
         } else {
+            driver.findElement(By.className("select2-search-choice-close")).click();
             scopeSelect.update(expected.getScopes());
         }
 
diff --git a/testsuite/integration-arquillian/tests/other/console/src/test/java/org/keycloak/testsuite/console/authorization/ScopePermissionManagementTest.java b/testsuite/integration-arquillian/tests/other/console/src/test/java/org/keycloak/testsuite/console/authorization/ScopePermissionManagementTest.java
index 2e2804f..1a296ad 100644
--- a/testsuite/integration-arquillian/tests/other/console/src/test/java/org/keycloak/testsuite/console/authorization/ScopePermissionManagementTest.java
+++ b/testsuite/integration-arquillian/tests/other/console/src/test/java/org/keycloak/testsuite/console/authorization/ScopePermissionManagementTest.java
@@ -88,7 +88,7 @@ public class ScopePermissionManagementTest extends AbstractAuthorizationSettings
     }
 
     @Test
-    public void testCreateWithoutPolicies() throws InterruptedException {
+    public void testCreateWithoutPolicies() {
         authorizationPage.navigateTo();
         ScopePermissionRepresentation expected = new ScopePermissionRepresentation();
 
@@ -105,7 +105,7 @@ public class ScopePermissionManagementTest extends AbstractAuthorizationSettings
     }
 
     @Test
-    public void testUpdateResourceScope() throws InterruptedException {
+    public void testUpdateResourceScope() {
         authorizationPage.navigateTo();
         ScopePermissionRepresentation expected = new ScopePermissionRepresentation();
 
@@ -149,7 +149,32 @@ public class ScopePermissionManagementTest extends AbstractAuthorizationSettings
     }
 
     @Test
-    public void testUpdateScopeOnly() throws InterruptedException {
+    public void testUpdateWithoutResource() {
+        authorizationPage.navigateTo();
+        ScopePermissionRepresentation expected = new ScopePermissionRepresentation();
+
+        expected.setName("testUpdateWithoutResource Permission");
+        expected.setDescription("description");
+        expected.addResource("Resource A");
+        expected.addScope("Scope A");
+        expected.addPolicy("Policy C");
+
+        expected = createPermission(expected);
+
+        expected.getResources().clear();
+        expected.addScope("Scope B");
+
+        authorizationPage.navigateTo();
+        authorizationPage.authorizationTabs().permissions().update(expected.getName(), expected);
+        assertAlertSuccess();
+
+        authorizationPage.navigateTo();
+        ScopePermission actual = authorizationPage.authorizationTabs().permissions().name(expected.getName());
+        assertPolicy(expected, actual);
+    }
+
+    @Test
+    public void testUpdateScopeOnly() {
         authorizationPage.navigateTo();
         ScopePermissionRepresentation expected = new ScopePermissionRepresentation();
 
@@ -180,7 +205,7 @@ public class ScopePermissionManagementTest extends AbstractAuthorizationSettings
     }
 
     @Test
-    public void testDelete() throws InterruptedException {
+    public void testDelete() {
         authorizationPage.navigateTo();
         ScopePermissionRepresentation expected = new ScopePermissionRepresentation();
 
@@ -198,7 +223,7 @@ public class ScopePermissionManagementTest extends AbstractAuthorizationSettings
     }
 
     @Test
-    public void testDeleteFromList() throws InterruptedException {
+    public void testDeleteFromList() {
         authorizationPage.navigateTo();
         ScopePermissionRepresentation expected = new ScopePermissionRepresentation();
 
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 430b2d2..612fa97 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
@@ -1292,7 +1292,7 @@ module.controller('ResourceServerPolicyScopeDetailCtrl', function($scope, $route
             if ($scope.selectedResource != null) {
                 $scope.policy.resources = [$scope.selectedResource._id];
             } else {
-                delete $scope.policy.resources;
+                $scope.policy.resources = [];
             }
 
             var scopes = [];