diff --git a/distribution/demo-dist/src/main/xslt/standalone.xsl b/distribution/demo-dist/src/main/xslt/standalone.xsl
index d78ff753e7..5ea7e93ce0 100755
--- a/distribution/demo-dist/src/main/xslt/standalone.xsl
+++ b/distribution/demo-dist/src/main/xslt/standalone.xsl
@@ -92,7 +92,9 @@
-
+
+
+
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 a8cd37994c..970d1b9e34 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
@@ -27,8 +27,6 @@ import org.keycloak.representations.idm.authorization.Logic;
import java.util.Collections;
import java.util.HashSet;
-import java.util.LinkedList;
-import java.util.List;
import java.util.Map;
import java.util.Set;
@@ -49,7 +47,7 @@ public class PolicyAdapter implements Policy, CachedModel {
@Override
public Policy getDelegateForUpdate() {
if (updated == null) {
- cacheSession.registerPolicyInvalidation(cached.getId(), cached.getName(), cached.getResourceServerId());
+ cacheSession.registerPolicyInvalidation(cached.getId(), cached.getName(), cached.getResourcesIds(), cached.getResourceServerId());
updated = cacheSession.getPolicyStoreDelegate().findById(cached.getId(), cached.getResourceServerId());
if (updated == null) throw new IllegalStateException("Not found in database");
}
@@ -98,6 +96,7 @@ public class PolicyAdapter implements Policy, CachedModel {
@Override
public void setName(String name) {
getDelegateForUpdate();
+ cacheSession.registerPolicyInvalidation(cached.getId(), name, cached.getResourcesIds(), cached.getResourceServerId());
updated.setName(name);
}
@@ -208,7 +207,6 @@ public class PolicyAdapter implements Policy, CachedModel {
public void addScope(Scope scope) {
getDelegateForUpdate();
updated.addScope(scope);
-
}
@Override
@@ -235,6 +233,9 @@ public class PolicyAdapter implements Policy, CachedModel {
@Override
public void addResource(Resource resource) {
getDelegateForUpdate();
+ HashSet resources = new HashSet<>();
+ resources.add(resource.getId());
+ cacheSession.registerPolicyInvalidation(cached.getId(), cached.getName(), resources, cached.getResourceServerId());
updated.addResource(resource);
}
@@ -242,6 +243,9 @@ public class PolicyAdapter implements Policy, CachedModel {
@Override
public void removeResource(Resource resource) {
getDelegateForUpdate();
+ HashSet resources = new HashSet<>();
+ resources.add(resource.getId());
+ cacheSession.registerPolicyInvalidation(cached.getId(), cached.getName(), resources, cached.getResourceServerId());
updated.removeResource(resource);
}
diff --git a/model/infinispan/src/main/java/org/keycloak/models/cache/infinispan/authorization/ResourceAdapter.java b/model/infinispan/src/main/java/org/keycloak/models/cache/infinispan/authorization/ResourceAdapter.java
index 2706681884..dc721b7112 100644
--- a/model/infinispan/src/main/java/org/keycloak/models/cache/infinispan/authorization/ResourceAdapter.java
+++ b/model/infinispan/src/main/java/org/keycloak/models/cache/infinispan/authorization/ResourceAdapter.java
@@ -95,6 +95,7 @@ public class ResourceAdapter implements Resource, CachedModel {
@Override
public void setName(String name) {
getDelegateForUpdate();
+ cacheSession.registerResourceInvalidation(cached.getId(), name, cached.getType(), cached.getUri(), cached.getScopesIds(), cached.getResourceServerId());
updated.setName(name);
}
@@ -126,8 +127,8 @@ public class ResourceAdapter implements Resource, CachedModel {
@Override
public void setUri(String uri) {
getDelegateForUpdate();
+ cacheSession.registerResourceInvalidation(cached.getId(), cached.getName(), cached.getType(), uri, cached.getScopesIds(), cached.getResourceServerId());
updated.setUri(uri);
-
}
@Override
@@ -139,6 +140,7 @@ public class ResourceAdapter implements Resource, CachedModel {
@Override
public void setType(String type) {
getDelegateForUpdate();
+ cacheSession.registerResourceInvalidation(cached.getId(), cached.getName(), type, cached.getUri(), cached.getScopesIds(), cached.getResourceServerId());
updated.setType(type);
}
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 c64db03ee9..794358926f 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
@@ -21,6 +21,7 @@ import org.jboss.logging.Logger;
import org.keycloak.models.cache.infinispan.CacheManager;
import org.keycloak.models.cache.infinispan.RealmCacheManager;
import org.keycloak.models.cache.infinispan.authorization.events.AuthorizationCacheInvalidationEvent;
+import org.keycloak.models.cache.infinispan.authorization.stream.InResourcePredicate;
import org.keycloak.models.cache.infinispan.authorization.stream.InResourceServerPredicate;
import org.keycloak.models.cache.infinispan.authorization.stream.InScopePredicate;
import org.keycloak.models.cache.infinispan.entities.Revisioned;
@@ -80,6 +81,7 @@ public class StoreFactoryCacheManager extends CacheManager {
if (type != null) {
invalidations.add(StoreFactoryCacheSession.getResourceByTypeCacheKey(type, serverId));
+ addInvalidations(InResourcePredicate.create().resource(type), invalidations);
}
if (uri != null) {
@@ -89,6 +91,7 @@ public class StoreFactoryCacheManager extends CacheManager {
if (scopes != null) {
for (String scope : scopes) {
invalidations.add(StoreFactoryCacheSession.getResourceByScopeCacheKey(scope, serverId));
+ addInvalidations(InScopePredicate.create().scope(scope), invalidations);
}
}
}
@@ -96,15 +99,22 @@ public class StoreFactoryCacheManager extends CacheManager {
public void resourceRemoval(String id, String name, String type, String uri, String owner, Set scopes, String serverId, Set invalidations) {
resourceUpdated(id, name, type, uri, scopes, serverId, invalidations);
invalidations.add(StoreFactoryCacheSession.getResourceByOwnerCacheKey(owner, serverId));
+ addInvalidations(InResourcePredicate.create().resource(id), invalidations);
}
- public void policyUpdated(String id, String name, String serverId, Set invalidations) {
+ public void policyUpdated(String id, String name, Set resources, String serverId, Set invalidations) {
invalidations.add(id);
invalidations.add(StoreFactoryCacheSession.getPolicyByNameCacheKey(name, serverId));
+
+ if (resources != null) {
+ for (String resource : resources) {
+ invalidations.add(StoreFactoryCacheSession.getPolicyByResource(resource, serverId));
+ }
+ }
}
- public void policyRemoval(String id, String name, String serverId, Set invalidations) {
- policyUpdated(id, name, serverId, invalidations);
+ public void policyRemoval(String id, String name, Set resources, String serverId, Set invalidations) {
+ policyUpdated(id, name, resources, serverId, invalidations);
}
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 db39562cc8..3e4c2052b2 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
@@ -16,6 +16,18 @@
*/
package org.keycloak.models.cache.infinispan.authorization;
+import java.util.ArrayList;
+import java.util.Arrays;
+import java.util.Collections;
+import java.util.HashMap;
+import java.util.HashSet;
+import java.util.List;
+import java.util.Map;
+import java.util.Set;
+import java.util.function.BiFunction;
+import java.util.function.Supplier;
+import java.util.stream.Collectors;
+
import org.jboss.logging.Logger;
import org.keycloak.authorization.model.Policy;
import org.keycloak.authorization.model.Resource;
@@ -34,7 +46,11 @@ import org.keycloak.models.cache.infinispan.authorization.entities.CachedResourc
import org.keycloak.models.cache.infinispan.authorization.entities.CachedResourceServer;
import org.keycloak.models.cache.infinispan.authorization.entities.CachedScope;
import org.keycloak.models.cache.infinispan.authorization.entities.PolicyListQuery;
+import org.keycloak.models.cache.infinispan.authorization.entities.PolicyQuery;
+import org.keycloak.models.cache.infinispan.authorization.entities.PolicyResourceListQuery;
+import org.keycloak.models.cache.infinispan.authorization.entities.PolicyScopeListQuery;
import org.keycloak.models.cache.infinispan.authorization.entities.ResourceListQuery;
+import org.keycloak.models.cache.infinispan.authorization.entities.ResourceQuery;
import org.keycloak.models.cache.infinispan.authorization.entities.ResourceScopeListQuery;
import org.keycloak.models.cache.infinispan.authorization.entities.ResourceServerListQuery;
import org.keycloak.models.cache.infinispan.authorization.entities.ScopeListQuery;
@@ -49,17 +65,6 @@ import org.keycloak.models.cache.infinispan.authorization.events.ScopeUpdatedEve
import org.keycloak.models.cache.infinispan.events.InvalidationEvent;
import org.keycloak.representations.idm.authorization.AbstractPolicyRepresentation;
-import java.util.ArrayList;
-import java.util.Arrays;
-import java.util.HashMap;
-import java.util.HashSet;
-import java.util.Iterator;
-import java.util.List;
-import java.util.Map;
-import java.util.Set;
-import java.util.function.Function;
-import java.util.stream.Collectors;
-
/**
* @author Bill Burke
* @version $Revision: 1 $
@@ -247,12 +252,12 @@ public class StoreFactoryCacheSession implements CachedStoreFactoryProvider {
invalidationEvents.add(ResourceUpdatedEvent.create(id, name, type, uri, scopes, serverId));
}
- public void registerPolicyInvalidation(String id, String name, String serverId) {
- cache.policyUpdated(id, name, serverId, invalidations);
+ public void registerPolicyInvalidation(String id, String name, Set resources, String serverId) {
+ cache.policyUpdated(id, name, resources, serverId, invalidations);
PolicyAdapter adapter = managedPolicies.get(id);
if (adapter != null) adapter.invalidateFlag();
- invalidationEvents.add(PolicyUpdatedEvent.create(id, name, serverId));
+ invalidationEvents.add(PolicyUpdatedEvent.create(id, name, resources, serverId));
}
public ResourceServerStore getResourceServerStoreDelegate() {
@@ -303,6 +308,18 @@ public class StoreFactoryCacheSession implements CachedStoreFactoryProvider {
return "policy.name." + name + "." + serverId;
}
+ public static String getPolicyByResource(String resourceId, String serverId) {
+ return "policy.resource." + resourceId + "." + serverId;
+ }
+
+ public static String getPolicyByResourceType(String type, String serverId) {
+ return "policy.resource.type." + type + "." + serverId;
+ }
+
+ public static String getPolicyByScope(String scope, String serverId) {
+ return "policy.scope." + scope + "." + serverId;
+ }
+
public StoreFactory getDelegate() {
if (delegate != null) return delegate;
delegate = session.getProvider(StoreFactory.class);
@@ -520,73 +537,37 @@ public class StoreFactoryCacheSession implements CachedStoreFactoryProvider {
public Resource findByName(String name, String resourceServerId) {
if (name == null) return null;
String cacheKey = getResourceByNameCacheKey(name, resourceServerId);
- ResourceListQuery query = cache.get(cacheKey, ResourceListQuery.class);
- if (query != null) {
- logger.tracev("resource by name cache hit: {0}", name);
- }
- if (query == null) {
- Long loaded = cache.getCurrentRevision(cacheKey);
- Resource model = getResourceStoreDelegate().findByName(name, resourceServerId);
- if (model == null) return null;
- if (invalidations.contains(model.getId())) return model;
- query = new ResourceListQuery(loaded, cacheKey, model.getId(), resourceServerId);
- cache.addRevisioned(query, startupRevision);
- return model;
- } else if (invalidations.contains(cacheKey)) {
- return getResourceStoreDelegate().findByName(name, resourceServerId);
- } else {
- String id = query.getResources().iterator().next();
- if (invalidations.contains(id)) {
- return getResourceStoreDelegate().findByName(name, resourceServerId);
+ List result = cacheQuery(cacheKey, ResourceListQuery.class, () -> {
+ Resource resource = getResourceStoreDelegate().findByName(name, resourceServerId);
+
+ if (resource == null) {
+ return Collections.emptyList();
}
- return findById(id, query.getResourceServerId());
+
+ return Arrays.asList(resource);
+ },
+ (revision, resources) -> new ResourceListQuery(revision, cacheKey, resources.stream().map(resource -> resource.getId()).collect(Collectors.toSet()), resourceServerId), resourceServerId);
+
+ if (result.isEmpty()) {
+ return null;
}
+
+ return result.get(0);
}
@Override
public List findByOwner(String ownerId, String resourceServerId) {
- if (ownerId == null) return null;
String cacheKey = getResourceByOwnerCacheKey(ownerId, resourceServerId);
- ResourceListQuery query = cache.get(cacheKey, ResourceListQuery.class);
- if (query != null) {
- logger.tracev("resource by owner cache hit: {0}", ownerId);
- }
- if (query == null) {
- Long loaded = cache.getCurrentRevision(cacheKey);
- List model = getResourceStoreDelegate().findByOwner(ownerId, resourceServerId);
- if (model == null) return null;
- if (invalidations.contains(cacheKey)) return model;
- query = new ResourceListQuery(loaded, cacheKey, model.stream().map(resource -> resource.getId()).collect(Collectors.toSet()), resourceServerId);
- cache.addRevisioned(query, startupRevision);
- return model;
- } else if (invalidations.contains(cacheKey)) {
- return getResourceStoreDelegate().findByOwner(ownerId, resourceServerId);
- } else {
- return query.getResources().stream().map(resourceId -> findById(resourceId, resourceServerId)).collect(Collectors.toList());
- }
+ return cacheQuery(cacheKey, ResourceListQuery.class, () -> getResourceStoreDelegate().findByOwner(ownerId, resourceServerId),
+ (revision, resources) -> new ResourceListQuery(revision, cacheKey, resources.stream().map(resource -> resource.getId()).collect(Collectors.toSet()), resourceServerId), resourceServerId);
}
@Override
public List findByUri(String uri, String resourceServerId) {
if (uri == null) return null;
String cacheKey = getResourceByUriCacheKey(uri, resourceServerId);
- ResourceListQuery query = cache.get(cacheKey, ResourceListQuery.class);
- if (query != null) {
- logger.tracev("resource by uri cache hit: {0}", uri);
- }
- if (query == null) {
- Long loaded = cache.getCurrentRevision(cacheKey);
- List model = getResourceStoreDelegate().findByUri(uri, resourceServerId);
- if (model == null) return null;
- if (invalidations.contains(cacheKey)) return model;
- query = new ResourceListQuery(loaded, cacheKey, model.stream().map(resource -> resource.getId()).collect(Collectors.toSet()), resourceServerId);
- cache.addRevisioned(query, startupRevision);
- return model;
- } else if (invalidations.contains(cacheKey)) {
- return getResourceStoreDelegate().findByUri(uri, resourceServerId);
- } else {
- return query.getResources().stream().map(resourceId -> findById(resourceId, resourceServerId)).collect(Collectors.toList());
- }
+ return cacheQuery(cacheKey, ResourceListQuery.class, () -> getResourceStoreDelegate().findByUri(uri, resourceServerId),
+ (revision, resources) -> new ResourceListQuery(revision, cacheKey, resources.stream().map(resource -> resource.getId()).collect(Collectors.toSet()), resourceServerId), resourceServerId);
}
@Override
@@ -603,30 +584,10 @@ public class StoreFactoryCacheSession implements CachedStoreFactoryProvider {
public List findByScope(List ids, String resourceServerId) {
if (ids == null) return null;
List result = new ArrayList<>();
- Iterator iterator = ids.iterator();
- while (iterator.hasNext()) {
- String id = iterator.next();
+ for (String id : ids) {
String cacheKey = getResourceByScopeCacheKey(id, resourceServerId);
- ResourceScopeListQuery query = cache.get(cacheKey, ResourceScopeListQuery.class);
- if (query != null) {
- logger.tracev("resource by scope cache hit: {0}", id);
- if (invalidations.contains(cacheKey)) {
- result.addAll(getResourceStoreDelegate().findByScope(ids, resourceServerId));
- } else {
- result.addAll(query.getResources().stream().map(resourceId -> findById(resourceId, resourceServerId)).collect(Collectors.toList()));
- }
- } else if (invalidations.contains(id)) {
- result.addAll(getResourceStoreDelegate().findByScope(ids, resourceServerId));
- } else {
- Long loaded = cache.getCurrentRevision(cacheKey);
- List model = getResourceStoreDelegate().findByScope(Arrays.asList(id), resourceServerId);
- if (model == null) return null;
- if (invalidations.contains(cacheKey)) return model;
- query = new ResourceScopeListQuery(loaded, cacheKey, id, model.stream().map(resource -> resource.getId()).collect(Collectors.toSet()), resourceServerId);
- cache.addRevisioned(query, startupRevision);
- result.addAll(query.getResources().stream().map(resourceId -> findById(resourceId, resourceServerId)).collect(Collectors.toList()));
- }
+ result.addAll(cacheQuery(cacheKey, ResourceScopeListQuery.class, () -> getResourceStoreDelegate().findByScope(Arrays.asList(id), resourceServerId), (revision, resources) -> new ResourceScopeListQuery(revision, cacheKey, id, resources.stream().map(resource -> resource.getId()).collect(Collectors.toSet()), resourceServerId), resourceServerId));
}
return result;
@@ -636,32 +597,37 @@ public class StoreFactoryCacheSession implements CachedStoreFactoryProvider {
public List findByType(String type, String resourceServerId) {
if (type == null) return null;
String cacheKey = getResourceByTypeCacheKey(type, resourceServerId);
- ResourceListQuery query = cache.get(cacheKey, ResourceListQuery.class);
- if (query != null) {
- logger.tracev("resource by type cache hit: {0}", type);
- }
- if (query == null) {
- Long loaded = cache.getCurrentRevision(cacheKey);
- List model = getResourceStoreDelegate().findByType(type, resourceServerId);
- if (model == null) return null;
- if (invalidations.contains(cacheKey)) return model;
- query = new ResourceListQuery(loaded, cacheKey, model.stream().map(resource -> resource.getId()).collect(Collectors.toSet()), resourceServerId);
- cache.addRevisioned(query, startupRevision);
- return model;
- } else if (invalidations.contains(cacheKey)) {
- return getResourceStoreDelegate().findByType(type, resourceServerId);
- } else {
- return query.getResources().stream().map(resourceId -> findById(resourceId, resourceServerId)).collect(Collectors.toList());
- }
+ return cacheQuery(cacheKey, ResourceListQuery.class, () -> getResourceStoreDelegate().findByType(type, resourceServerId),
+ (revision, resources) -> new ResourceListQuery(revision, cacheKey, resources.stream().map(resource -> resource.getId()).collect(Collectors.toSet()), resourceServerId), resourceServerId);
+ }
+
+ private List cacheQuery(String cacheKey, Class queryType, Supplier> resultSupplier, BiFunction, Q> querySupplier, String resourceServerId) {
+ Q query = cache.get(cacheKey, queryType);
+ if (query != null) {
+ logger.tracev("cache hit for key: {0}", cacheKey);
+ }
+ if (query == null) {
+ Long loaded = cache.getCurrentRevision(cacheKey);
+ List model = resultSupplier.get();
+ if (model == null) return null;
+ if (invalidations.contains(cacheKey)) return model;
+ query = querySupplier.apply(loaded, model);
+ cache.addRevisioned(query, startupRevision);
+ return model;
+ } else if (query.isInvalid(invalidations)) {
+ return resultSupplier.get();
+ } else {
+ return query.getResources().stream().map(resourceId -> (R) findById(resourceId, resourceServerId)).collect(Collectors.toList());
+ }
}
}
protected class PolicyCache implements PolicyStore {
@Override
public Policy create(AbstractPolicyRepresentation representation, ResourceServer resourceServer) {
- Policy resource = getPolicyStoreDelegate().create(representation, resourceServer);
- registerPolicyInvalidation(resource.getId(), resource.getName(), resourceServer.getId());
- return resource;
+ Policy policy = getPolicyStoreDelegate().create(representation, resourceServer);
+ registerPolicyInvalidation(policy.getId(), policy.getName(), policy.getResources().stream().map(resource1 -> resource1.getId()).collect(Collectors.toSet()), resourceServer.getId());
+ return policy;
}
@Override
@@ -671,8 +637,8 @@ public class StoreFactoryCacheSession implements CachedStoreFactoryProvider {
if (policy == null) return;
cache.invalidateObject(id);
- invalidationEvents.add(PolicyRemovedEvent.create(id, policy.getName(), policy.getResourceServer().getId()));
- cache.policyRemoval(id, policy.getName(), policy.getResourceServer().getId(), invalidations);
+ invalidationEvents.add(PolicyRemovedEvent.create(id, policy.getName(), policy.getResources().stream().map(resource1 -> resource1.getId()).collect(Collectors.toSet()), policy.getResourceServer().getId()));
+ cache.policyRemoval(id, policy.getName(), policy.getResources().stream().map(resource1 -> resource1.getId()).collect(Collectors.toSet()), policy.getResourceServer().getId(), invalidations);
getPolicyStoreDelegate().delete(id);
}
@@ -708,27 +674,22 @@ public class StoreFactoryCacheSession implements CachedStoreFactoryProvider {
public Policy findByName(String name, String resourceServerId) {
if (name == null) return null;
String cacheKey = getPolicyByNameCacheKey(name, resourceServerId);
- PolicyListQuery query = cache.get(cacheKey, PolicyListQuery.class);
- if (query != null) {
- logger.tracev("policy by name cache hit: {0}", name);
- }
- if (query == null) {
- Long loaded = cache.getCurrentRevision(cacheKey);
- Policy model = getPolicyStoreDelegate().findByName(name, resourceServerId);
- if (model == null) return null;
- if (invalidations.contains(model.getId())) return model;
- query = new PolicyListQuery(loaded, cacheKey, model.getId(), resourceServerId);
- cache.addRevisioned(query, startupRevision);
- return model;
- } else if (invalidations.contains(cacheKey)) {
- return getPolicyStoreDelegate().findByName(name, resourceServerId);
- } else {
- String id = query.getPolicies().iterator().next();
- if (invalidations.contains(id)) {
- return getPolicyStoreDelegate().findByName(name, resourceServerId);
+ List result = cacheQuery(cacheKey, PolicyListQuery.class, () -> {
+ Policy policy = getPolicyStoreDelegate().findByName(name, resourceServerId);
+
+ if (policy == null) {
+ return Collections.emptyList();
}
- return findById(id, query.getResourceServerId());
+
+ return Arrays.asList(policy);
+ },
+ (revision, policies) -> new PolicyListQuery(revision, cacheKey, policies.stream().map(policy -> policy.getId()).collect(Collectors.toSet()), resourceServerId), resourceServerId);
+
+ if (result.isEmpty()) {
+ return null;
}
+
+ return result.get(0);
}
@Override
@@ -743,17 +704,29 @@ public class StoreFactoryCacheSession implements CachedStoreFactoryProvider {
@Override
public List findByResource(String resourceId, String resourceServerId) {
- return getPolicyStoreDelegate().findByResource(resourceId, resourceServerId);
+ String cacheKey = getPolicyByResource(resourceId, resourceServerId);
+ return cacheQuery(cacheKey, PolicyResourceListQuery.class, () -> getPolicyStoreDelegate().findByResource(resourceId, resourceServerId),
+ (revision, policies) -> new PolicyResourceListQuery(revision, cacheKey, resourceId, policies.stream().map(policy -> policy.getId()).collect(Collectors.toSet()), resourceServerId), resourceServerId);
}
@Override
public List findByResourceType(String resourceType, String resourceServerId) {
- return getPolicyStoreDelegate().findByResourceType(resourceType, resourceServerId);
+ String cacheKey = getPolicyByResourceType(resourceType, resourceServerId);
+ return cacheQuery(cacheKey, PolicyResourceListQuery.class, () -> getPolicyStoreDelegate().findByResourceType(resourceType, resourceServerId),
+ (revision, policies) -> new PolicyResourceListQuery(revision, cacheKey, resourceType, policies.stream().map(policy -> policy.getId()).collect(Collectors.toSet()), resourceServerId), resourceServerId);
}
@Override
public List findByScopeIds(List scopeIds, String resourceServerId) {
- return getPolicyStoreDelegate().findByScopeIds(scopeIds, resourceServerId);
+ if (scopeIds == null) return null;
+ List result = new ArrayList<>();
+
+ for (String id : scopeIds) {
+ String cacheKey = getPolicyByScope(id, resourceServerId);
+ result.addAll(cacheQuery(cacheKey, PolicyScopeListQuery.class, () -> getPolicyStoreDelegate().findByScopeIds(Arrays.asList(id), resourceServerId), (revision, resources) -> new PolicyScopeListQuery(revision, cacheKey, id, resources.stream().map(resource -> resource.getId()).collect(Collectors.toSet()), resourceServerId), resourceServerId));
+ }
+
+ return result;
}
@Override
@@ -765,6 +738,26 @@ public class StoreFactoryCacheSession implements CachedStoreFactoryProvider {
public List findDependentPolicies(String id, String resourceServerId) {
return getPolicyStoreDelegate().findDependentPolicies(id, resourceServerId);
}
+
+ private List cacheQuery(String cacheKey, Class queryType, Supplier> resultSupplier, BiFunction, Q> querySupplier, String resourceServerId) {
+ Q query = cache.get(cacheKey, queryType);
+ if (query != null) {
+ logger.tracev("cache hit for key: {0}", cacheKey);
+ }
+ if (query == null) {
+ Long loaded = cache.getCurrentRevision(cacheKey);
+ List model = resultSupplier.get();
+ if (model == null) return null;
+ if (invalidations.contains(cacheKey)) return model;
+ query = querySupplier.apply(loaded, model);
+ cache.addRevisioned(query, startupRevision);
+ return model;
+ } else if (query.isInvalid(invalidations)) {
+ return resultSupplier.get();
+ } else {
+ return query.getPolicies().stream().map(resourceId -> (R) findById(resourceId, resourceServerId)).collect(Collectors.toList());
+ }
+ }
}
diff --git a/model/infinispan/src/main/java/org/keycloak/models/cache/infinispan/authorization/entities/InResource.java b/model/infinispan/src/main/java/org/keycloak/models/cache/infinispan/authorization/entities/InResource.java
new file mode 100644
index 0000000000..e316b1c78a
--- /dev/null
+++ b/model/infinispan/src/main/java/org/keycloak/models/cache/infinispan/authorization/entities/InResource.java
@@ -0,0 +1,24 @@
+/*
+ * Copyright 2017 Red Hat, Inc. and/or its affiliates
+ * and other contributors as indicated by the @author tags.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.keycloak.models.cache.infinispan.authorization.entities;
+
+/**
+ * @author Pedro Igor
+ */
+public interface InResource {
+ String getResourceId();
+}
diff --git a/model/infinispan/src/main/java/org/keycloak/models/cache/infinispan/authorization/entities/InScope.java b/model/infinispan/src/main/java/org/keycloak/models/cache/infinispan/authorization/entities/InScope.java
index aab2643413..c345677c50 100644
--- a/model/infinispan/src/main/java/org/keycloak/models/cache/infinispan/authorization/entities/InScope.java
+++ b/model/infinispan/src/main/java/org/keycloak/models/cache/infinispan/authorization/entities/InScope.java
@@ -16,11 +16,8 @@
*/
package org.keycloak.models.cache.infinispan.authorization.entities;
-import java.util.Set;
-
/**
- * @author Bill Burke
- * @version $Revision: 1 $
+ * @author Pedro Igor
*/
public interface InScope {
String getScopeId();
diff --git a/model/infinispan/src/main/java/org/keycloak/models/cache/infinispan/authorization/entities/PolicyListQuery.java b/model/infinispan/src/main/java/org/keycloak/models/cache/infinispan/authorization/entities/PolicyListQuery.java
index 1cbf044d01..0b2227ab92 100755
--- a/model/infinispan/src/main/java/org/keycloak/models/cache/infinispan/authorization/entities/PolicyListQuery.java
+++ b/model/infinispan/src/main/java/org/keycloak/models/cache/infinispan/authorization/entities/PolicyListQuery.java
@@ -9,7 +9,7 @@ import java.util.Set;
* @author Bill Burke
* @version $Revision: 1 $
*/
-public class PolicyListQuery extends AbstractRevisioned implements InResourceServer {
+public class PolicyListQuery extends AbstractRevisioned implements PolicyQuery {
private final Set policies;
private final String serverId;
@@ -33,4 +33,9 @@ public class PolicyListQuery extends AbstractRevisioned implements InResourceSer
public Set getPolicies() {
return policies;
}
+
+ @Override
+ public boolean isInvalid(Set invalidations) {
+ return invalidations.contains(getId()) || invalidations.contains(getResourceServerId());
+ }
}
\ No newline at end of file
diff --git a/model/infinispan/src/main/java/org/keycloak/models/cache/infinispan/authorization/entities/PolicyQuery.java b/model/infinispan/src/main/java/org/keycloak/models/cache/infinispan/authorization/entities/PolicyQuery.java
new file mode 100644
index 0000000000..a844b25213
--- /dev/null
+++ b/model/infinispan/src/main/java/org/keycloak/models/cache/infinispan/authorization/entities/PolicyQuery.java
@@ -0,0 +1,30 @@
+/*
+ * Copyright 2017 Red Hat, Inc. and/or its affiliates
+ * and other contributors as indicated by the @author tags.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.keycloak.models.cache.infinispan.authorization.entities;
+
+import java.util.Set;
+
+import org.keycloak.models.cache.infinispan.entities.Revisioned;
+
+/**
+ * @author Pedro Igor
+ */
+public interface PolicyQuery extends InResourceServer, Revisioned {
+
+ Set getPolicies();
+ boolean isInvalid(Set invalidations);
+}
diff --git a/model/infinispan/src/main/java/org/keycloak/models/cache/infinispan/authorization/entities/PolicyResourceListQuery.java b/model/infinispan/src/main/java/org/keycloak/models/cache/infinispan/authorization/entities/PolicyResourceListQuery.java
new file mode 100755
index 0000000000..d0af77b8f1
--- /dev/null
+++ b/model/infinispan/src/main/java/org/keycloak/models/cache/infinispan/authorization/entities/PolicyResourceListQuery.java
@@ -0,0 +1,42 @@
+/*
+ * Copyright 2017 Red Hat, Inc. and/or its affiliates
+ * and other contributors as indicated by the @author tags.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.keycloak.models.cache.infinispan.authorization.entities;
+
+import java.util.Set;
+
+/**
+ * @author Pedro Igor
+ */
+public class PolicyResourceListQuery extends PolicyListQuery implements InResource {
+
+ private final String resourceId;
+
+ public PolicyResourceListQuery(Long revision, String id, String resourceId, Set policies, String serverId) {
+ super(revision, id, policies, serverId);
+ this.resourceId = resourceId;
+ }
+
+ @Override
+ public boolean isInvalid(Set invalidations) {
+ return super.isInvalid(invalidations) || invalidations.contains(getResourceId());
+ }
+
+ @Override
+ public String getResourceId() {
+ return resourceId;
+ }
+}
\ No newline at end of file
diff --git a/model/infinispan/src/main/java/org/keycloak/models/cache/infinispan/authorization/entities/PolicyScopeListQuery.java b/model/infinispan/src/main/java/org/keycloak/models/cache/infinispan/authorization/entities/PolicyScopeListQuery.java
new file mode 100755
index 0000000000..7db8a0198a
--- /dev/null
+++ b/model/infinispan/src/main/java/org/keycloak/models/cache/infinispan/authorization/entities/PolicyScopeListQuery.java
@@ -0,0 +1,42 @@
+/*
+ * Copyright 2017 Red Hat, Inc. and/or its affiliates
+ * and other contributors as indicated by the @author tags.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.keycloak.models.cache.infinispan.authorization.entities;
+
+import java.util.Set;
+
+/**
+ * @author Pedro Igor
+ */
+public class PolicyScopeListQuery extends PolicyListQuery implements InScope {
+
+ private final String scopeId;
+
+ public PolicyScopeListQuery(Long revision, String id, String scopeId, Set resources, String serverId) {
+ super(revision, id, resources, serverId);
+ this.scopeId = scopeId;
+ }
+
+ @Override
+ public String getScopeId() {
+ return scopeId;
+ }
+
+ @Override
+ public boolean isInvalid(Set invalidations) {
+ return super.isInvalid(invalidations) || invalidations.contains(getScopeId());
+ }
+}
\ No newline at end of file
diff --git a/model/infinispan/src/main/java/org/keycloak/models/cache/infinispan/authorization/entities/ResourceListQuery.java b/model/infinispan/src/main/java/org/keycloak/models/cache/infinispan/authorization/entities/ResourceListQuery.java
index d322b62c02..d8db81bd5c 100755
--- a/model/infinispan/src/main/java/org/keycloak/models/cache/infinispan/authorization/entities/ResourceListQuery.java
+++ b/model/infinispan/src/main/java/org/keycloak/models/cache/infinispan/authorization/entities/ResourceListQuery.java
@@ -9,7 +9,7 @@ import java.util.Set;
* @author Bill Burke
* @version $Revision: 1 $
*/
-public class ResourceListQuery extends AbstractRevisioned implements InResourceServer {
+public class ResourceListQuery extends AbstractRevisioned implements ResourceQuery, InResourceServer {
private final Set resources;
private final String serverId;
@@ -33,4 +33,9 @@ public class ResourceListQuery extends AbstractRevisioned implements InResourceS
public Set getResources() {
return resources;
}
+
+ @Override
+ public boolean isInvalid(Set invalidations) {
+ return invalidations.contains(getId()) || invalidations.contains(getResourceServerId());
+ }
}
\ No newline at end of file
diff --git a/model/infinispan/src/main/java/org/keycloak/models/cache/infinispan/authorization/entities/ResourceQuery.java b/model/infinispan/src/main/java/org/keycloak/models/cache/infinispan/authorization/entities/ResourceQuery.java
new file mode 100644
index 0000000000..db07bc8ce5
--- /dev/null
+++ b/model/infinispan/src/main/java/org/keycloak/models/cache/infinispan/authorization/entities/ResourceQuery.java
@@ -0,0 +1,30 @@
+/*
+ * Copyright 2017 Red Hat, Inc. and/or its affiliates
+ * and other contributors as indicated by the @author tags.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.keycloak.models.cache.infinispan.authorization.entities;
+
+import java.util.Set;
+
+import org.keycloak.models.cache.infinispan.entities.Revisioned;
+
+/**
+ * @author Pedro Igor
+ */
+public interface ResourceQuery extends Revisioned {
+
+ Set getResources();
+ boolean isInvalid(Set invalidations);
+}
diff --git a/model/infinispan/src/main/java/org/keycloak/models/cache/infinispan/authorization/entities/ResourceScopeListQuery.java b/model/infinispan/src/main/java/org/keycloak/models/cache/infinispan/authorization/entities/ResourceScopeListQuery.java
index 116e1aaa3b..ee733446f5 100755
--- a/model/infinispan/src/main/java/org/keycloak/models/cache/infinispan/authorization/entities/ResourceScopeListQuery.java
+++ b/model/infinispan/src/main/java/org/keycloak/models/cache/infinispan/authorization/entities/ResourceScopeListQuery.java
@@ -1,13 +1,9 @@
package org.keycloak.models.cache.infinispan.authorization.entities;
-import java.util.HashSet;
import java.util.Set;
-import org.keycloak.models.cache.infinispan.entities.AbstractRevisioned;
-
/**
- * @author Bill Burke
- * @version $Revision: 1 $
+ * @author Pedro Igor
*/
public class ResourceScopeListQuery extends ResourceListQuery implements InScope {
@@ -22,4 +18,9 @@ public class ResourceScopeListQuery extends ResourceListQuery implements InScope
public String getScopeId() {
return scopeId;
}
+
+ @Override
+ public boolean isInvalid(Set invalidations) {
+ return super.isInvalid(invalidations) || invalidations.contains(getScopeId());
+ }
}
\ No newline at end of file
diff --git a/model/infinispan/src/main/java/org/keycloak/models/cache/infinispan/authorization/events/PolicyRemovedEvent.java b/model/infinispan/src/main/java/org/keycloak/models/cache/infinispan/authorization/events/PolicyRemovedEvent.java
index 3a721baa89..759c284b93 100644
--- a/model/infinispan/src/main/java/org/keycloak/models/cache/infinispan/authorization/events/PolicyRemovedEvent.java
+++ b/model/infinispan/src/main/java/org/keycloak/models/cache/infinispan/authorization/events/PolicyRemovedEvent.java
@@ -17,11 +17,11 @@
package org.keycloak.models.cache.infinispan.authorization.events;
+import java.util.Set;
+
import org.keycloak.models.cache.infinispan.authorization.StoreFactoryCacheManager;
import org.keycloak.models.cache.infinispan.events.InvalidationEvent;
-import java.util.Set;
-
/**
* @author Marek Posolda
*/
@@ -29,12 +29,14 @@ public class PolicyRemovedEvent extends InvalidationEvent implements Authorizati
private String id;
private String name;
+ private Set resources;
private String serverId;
- public static PolicyRemovedEvent create(String id, String name, String serverId) {
+ public static PolicyRemovedEvent create(String id, String name, Set resources, String serverId) {
PolicyRemovedEvent event = new PolicyRemovedEvent();
event.id = id;
event.name = name;
+ event.resources = resources;
event.serverId = serverId;
return event;
}
@@ -51,6 +53,6 @@ public class PolicyRemovedEvent extends InvalidationEvent implements Authorizati
@Override
public void addInvalidations(StoreFactoryCacheManager cache, Set invalidations) {
- cache.policyRemoval(id, name, serverId, invalidations);
+ cache.policyRemoval(id, name, resources, serverId, invalidations);
}
}
diff --git a/model/infinispan/src/main/java/org/keycloak/models/cache/infinispan/authorization/events/PolicyUpdatedEvent.java b/model/infinispan/src/main/java/org/keycloak/models/cache/infinispan/authorization/events/PolicyUpdatedEvent.java
index 1e1d9925bf..b576bda165 100644
--- a/model/infinispan/src/main/java/org/keycloak/models/cache/infinispan/authorization/events/PolicyUpdatedEvent.java
+++ b/model/infinispan/src/main/java/org/keycloak/models/cache/infinispan/authorization/events/PolicyUpdatedEvent.java
@@ -29,12 +29,14 @@ public class PolicyUpdatedEvent extends InvalidationEvent implements Authorizati
private String id;
private String name;
+ private static Set resources;
private String serverId;
- public static PolicyUpdatedEvent create(String id, String name, String serverId) {
+ public static PolicyUpdatedEvent create(String id, String name, Set resources, String serverId) {
PolicyUpdatedEvent event = new PolicyUpdatedEvent();
event.id = id;
event.name = name;
+ event.resources = resources;
event.serverId = serverId;
return event;
}
@@ -51,6 +53,6 @@ public class PolicyUpdatedEvent extends InvalidationEvent implements Authorizati
@Override
public void addInvalidations(StoreFactoryCacheManager cache, Set invalidations) {
- cache.policyUpdated(id, name, serverId, invalidations);
+ cache.policyUpdated(id, name, resources, serverId, invalidations);
}
}
diff --git a/model/infinispan/src/main/java/org/keycloak/models/cache/infinispan/authorization/stream/InResourcePredicate.java b/model/infinispan/src/main/java/org/keycloak/models/cache/infinispan/authorization/stream/InResourcePredicate.java
new file mode 100755
index 0000000000..f72de49c85
--- /dev/null
+++ b/model/infinispan/src/main/java/org/keycloak/models/cache/infinispan/authorization/stream/InResourcePredicate.java
@@ -0,0 +1,50 @@
+/*
+ * Copyright 2017 Red Hat, Inc. and/or its affiliates
+ * and other contributors as indicated by the @author tags.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.keycloak.models.cache.infinispan.authorization.stream;
+
+import java.io.Serializable;
+import java.util.Map;
+import java.util.function.Predicate;
+
+import org.keycloak.models.cache.infinispan.authorization.entities.InResource;
+import org.keycloak.models.cache.infinispan.entities.Revisioned;
+
+/**
+ * @author Pedro Igor
+ */
+public class InResourcePredicate implements Predicate>, Serializable {
+
+ private String resourceId;
+
+ public static InResourcePredicate create() {
+ return new InResourcePredicate();
+ }
+
+ public InResourcePredicate resource(String id) {
+ resourceId = id;
+ return this;
+ }
+
+ @Override
+ public boolean test(Map.Entry entry) {
+ Object value = entry.getValue();
+ if (value == null) return false;
+ if (!(value instanceof InResource)) return false;
+
+ return resourceId.equals(((InResource)value).getResourceId());
+ }
+}
diff --git a/wildfly/server-subsystem/src/main/resources/subsystem-templates/keycloak-infinispan2.xml b/wildfly/server-subsystem/src/main/resources/subsystem-templates/keycloak-infinispan2.xml
index a76162b83a..839ecdf2df 100755
--- a/wildfly/server-subsystem/src/main/resources/subsystem-templates/keycloak-infinispan2.xml
+++ b/wildfly/server-subsystem/src/main/resources/subsystem-templates/keycloak-infinispan2.xml
@@ -37,7 +37,7 @@
-
+