Merge pull request #4213 from pedroigor/cache-fixes

Missing invalidation for some queries cache
This commit is contained in:
Pedro Igor 2017-06-09 09:13:13 -03:00 committed by GitHub
commit 2b55cd7902
7 changed files with 123 additions and 23 deletions

View file

@ -47,7 +47,7 @@ public class PolicyAdapter implements Policy, CachedModel<Policy> {
@Override @Override
public Policy getDelegateForUpdate() { public Policy getDelegateForUpdate() {
if (updated == null) { if (updated == null) {
cacheSession.registerPolicyInvalidation(cached.getId(), cached.getName(), cached.getResourcesIds(), cached.getResourceServerId()); cacheSession.registerPolicyInvalidation(cached.getId(), cached.getName(), cached.getResourcesIds(), cached.getScopesIds(), cached.getResourceServerId());
updated = cacheSession.getPolicyStoreDelegate().findById(cached.getId(), cached.getResourceServerId()); updated = cacheSession.getPolicyStoreDelegate().findById(cached.getId(), cached.getResourceServerId());
if (updated == null) throw new IllegalStateException("Not found in database"); if (updated == null) throw new IllegalStateException("Not found in database");
} }
@ -96,7 +96,7 @@ public class PolicyAdapter implements Policy, CachedModel<Policy> {
@Override @Override
public void setName(String name) { public void setName(String name) {
getDelegateForUpdate(); getDelegateForUpdate();
cacheSession.registerPolicyInvalidation(cached.getId(), name, cached.getResourcesIds(), cached.getResourceServerId()); cacheSession.registerPolicyInvalidation(cached.getId(), name, cached.getResourcesIds(), cached.getScopesIds(), cached.getResourceServerId());
updated.setName(name); updated.setName(name);
} }
@ -235,7 +235,7 @@ public class PolicyAdapter implements Policy, CachedModel<Policy> {
getDelegateForUpdate(); getDelegateForUpdate();
HashSet<String> resources = new HashSet<>(); HashSet<String> resources = new HashSet<>();
resources.add(resource.getId()); resources.add(resource.getId());
cacheSession.registerPolicyInvalidation(cached.getId(), cached.getName(), resources, cached.getResourceServerId()); cacheSession.registerPolicyInvalidation(cached.getId(), cached.getName(), resources, cached.getScopesIds(), cached.getResourceServerId());
updated.addResource(resource); updated.addResource(resource);
} }
@ -245,7 +245,7 @@ public class PolicyAdapter implements Policy, CachedModel<Policy> {
getDelegateForUpdate(); getDelegateForUpdate();
HashSet<String> resources = new HashSet<>(); HashSet<String> resources = new HashSet<>();
resources.add(resource.getId()); resources.add(resource.getId());
cacheSession.registerPolicyInvalidation(cached.getId(), cached.getName(), resources, cached.getResourceServerId()); cacheSession.registerPolicyInvalidation(cached.getId(), cached.getName(), resources, cached.getScopesIds(), cached.getResourceServerId());
updated.removeResource(resource); updated.removeResource(resource);
} }

View file

@ -102,7 +102,7 @@ public class StoreFactoryCacheManager extends CacheManager {
addInvalidations(InResourcePredicate.create().resource(id), invalidations); addInvalidations(InResourcePredicate.create().resource(id), invalidations);
} }
public void policyUpdated(String id, String name, Set<String> resources, String serverId, Set<String> invalidations) { public void policyUpdated(String id, String name, Set<String> resources, Set<String> resourceTypes, Set<String> scopes, String serverId, Set<String> invalidations) {
invalidations.add(id); invalidations.add(id);
invalidations.add(StoreFactoryCacheSession.getPolicyByNameCacheKey(name, serverId)); invalidations.add(StoreFactoryCacheSession.getPolicyByNameCacheKey(name, serverId));
@ -111,10 +111,22 @@ public class StoreFactoryCacheManager extends CacheManager {
invalidations.add(StoreFactoryCacheSession.getPolicyByResource(resource, serverId)); invalidations.add(StoreFactoryCacheSession.getPolicyByResource(resource, serverId));
} }
} }
if (resourceTypes != null) {
for (String type : resourceTypes) {
invalidations.add(StoreFactoryCacheSession.getPolicyByResourceType(type, serverId));
}
} }
public void policyRemoval(String id, String name, Set<String> resources, String serverId, Set<String> invalidations) { if (scopes != null) {
policyUpdated(id, name, resources, serverId, invalidations); for (String scope : scopes) {
invalidations.add(StoreFactoryCacheSession.getPolicyByScope(scope, serverId));
}
}
}
public void policyRemoval(String id, String name, Set<String> resources, Set<String> resourceTypes, Set<String> scopes, String serverId, Set<String> invalidations) {
policyUpdated(id, name, resources, resourceTypes, scopes, serverId, invalidations);
} }

View file

@ -23,6 +23,7 @@ import java.util.HashMap;
import java.util.HashSet; import java.util.HashSet;
import java.util.List; import java.util.List;
import java.util.Map; import java.util.Map;
import java.util.Objects;
import java.util.Set; import java.util.Set;
import java.util.function.BiFunction; import java.util.function.BiFunction;
import java.util.function.Supplier; import java.util.function.Supplier;
@ -252,12 +253,30 @@ public class StoreFactoryCacheSession implements CachedStoreFactoryProvider {
invalidationEvents.add(ResourceUpdatedEvent.create(id, name, type, uri, scopes, serverId)); invalidationEvents.add(ResourceUpdatedEvent.create(id, name, type, uri, scopes, serverId));
} }
public void registerPolicyInvalidation(String id, String name, Set<String> resources, String serverId) { public void registerPolicyInvalidation(String id, String name, Set<String> resources, Set<String> scopes, String serverId) {
cache.policyUpdated(id, name, resources, serverId, invalidations); Set<String> resourceTypes = getResourceTypes(resources, serverId);
cache.policyUpdated(id, name, resources, resourceTypes, scopes, serverId, invalidations);
PolicyAdapter adapter = managedPolicies.get(id); PolicyAdapter adapter = managedPolicies.get(id);
if (adapter != null) adapter.invalidateFlag(); if (adapter != null) adapter.invalidateFlag();
invalidationEvents.add(PolicyUpdatedEvent.create(id, name, resources, serverId)); invalidationEvents.add(PolicyUpdatedEvent.create(id, name, resources, resourceTypes, scopes, serverId));
}
private Set<String> getResourceTypes(Set<String> resources, String serverId) {
if (resources == null) {
return Collections.emptySet();
}
return resources.stream().map(resourceId -> {
Resource resource = getResourceStore().findById(resourceId, serverId);
String type = resource.getType();
if (type != null) {
return type;
}
return null;
}).filter(Objects::nonNull).collect(Collectors.toSet());
} }
public ResourceServerStore getResourceServerStoreDelegate() { public ResourceServerStore getResourceServerStoreDelegate() {
@ -626,7 +645,7 @@ public class StoreFactoryCacheSession implements CachedStoreFactoryProvider {
@Override @Override
public Policy create(AbstractPolicyRepresentation representation, ResourceServer resourceServer) { public Policy create(AbstractPolicyRepresentation representation, ResourceServer resourceServer) {
Policy policy = getPolicyStoreDelegate().create(representation, resourceServer); Policy policy = getPolicyStoreDelegate().create(representation, resourceServer);
registerPolicyInvalidation(policy.getId(), policy.getName(), policy.getResources().stream().map(resource1 -> resource1.getId()).collect(Collectors.toSet()), resourceServer.getId()); registerPolicyInvalidation(policy.getId(), representation.getName(), representation.getResources(), representation.getScopes(), resourceServer.getId());
return policy; return policy;
} }
@ -637,8 +656,12 @@ public class StoreFactoryCacheSession implements CachedStoreFactoryProvider {
if (policy == null) return; if (policy == null) return;
cache.invalidateObject(id); cache.invalidateObject(id);
invalidationEvents.add(PolicyRemovedEvent.create(id, policy.getName(), policy.getResources().stream().map(resource1 -> resource1.getId()).collect(Collectors.toSet()), policy.getResourceServer().getId())); Set<String> resources = policy.getResources().stream().map(resource -> resource.getId()).collect(Collectors.toSet());
cache.policyRemoval(id, policy.getName(), policy.getResources().stream().map(resource1 -> resource1.getId()).collect(Collectors.toSet()), policy.getResourceServer().getId(), invalidations); ResourceServer resourceServer = policy.getResourceServer();
Set<String> resourceTypes = getResourceTypes(resources, resourceServer.getId());
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);
getPolicyStoreDelegate().delete(id); getPolicyStoreDelegate().delete(id);
} }

View file

@ -30,13 +30,17 @@ public class PolicyRemovedEvent extends InvalidationEvent implements Authorizati
private String id; private String id;
private String name; private String name;
private Set<String> resources; private Set<String> resources;
private Set<String> resourceTypes;
private Set<String> scopes;
private String serverId; private String serverId;
public static PolicyRemovedEvent create(String id, String name, Set<String> resources, String serverId) { public static PolicyRemovedEvent create(String id, String name, Set<String> resources, Set<String> resourceTypes, Set<String> scopes, String serverId) {
PolicyRemovedEvent event = new PolicyRemovedEvent(); PolicyRemovedEvent event = new PolicyRemovedEvent();
event.id = id; event.id = id;
event.name = name; event.name = name;
event.resources = resources; event.resources = resources;
event.resourceTypes = resourceTypes;
event.scopes = scopes;
event.serverId = serverId; event.serverId = serverId;
return event; return event;
} }
@ -53,6 +57,6 @@ public class PolicyRemovedEvent extends InvalidationEvent implements Authorizati
@Override @Override
public void addInvalidations(StoreFactoryCacheManager cache, Set<String> invalidations) { public void addInvalidations(StoreFactoryCacheManager cache, Set<String> invalidations) {
cache.policyRemoval(id, name, resources, serverId, invalidations); cache.policyRemoval(id, name, resources, resourceTypes, scopes, serverId, invalidations);
} }
} }

View file

@ -30,13 +30,17 @@ public class PolicyUpdatedEvent extends InvalidationEvent implements Authorizati
private String id; private String id;
private String name; private String name;
private static Set<String> resources; private static Set<String> resources;
private Set<String> resourceTypes;
private Set<String> scopes;
private String serverId; private String serverId;
public static PolicyUpdatedEvent create(String id, String name, Set<String> resources, String serverId) { public static PolicyUpdatedEvent create(String id, String name, Set<String> resources, Set<String> resourceTypes, Set<String> scopes, String serverId) {
PolicyUpdatedEvent event = new PolicyUpdatedEvent(); PolicyUpdatedEvent event = new PolicyUpdatedEvent();
event.id = id; event.id = id;
event.name = name; event.name = name;
event.resources = resources; event.resources = resources;
event.resourceTypes = resourceTypes;
event.scopes = scopes;
event.serverId = serverId; event.serverId = serverId;
return event; return event;
} }
@ -53,6 +57,6 @@ public class PolicyUpdatedEvent extends InvalidationEvent implements Authorizati
@Override @Override
public void addInvalidations(StoreFactoryCacheManager cache, Set<String> invalidations) { public void addInvalidations(StoreFactoryCacheManager cache, Set<String> invalidations) {
cache.policyUpdated(id, name, resources, serverId, invalidations); cache.policyUpdated(id, name, resources, resourceTypes, scopes, serverId, invalidations);
} }
} }

View file

@ -21,14 +21,17 @@ package org.keycloak.authorization;
import java.util.Collection; import java.util.Collection;
import java.util.List; import java.util.List;
import java.util.Map; import java.util.Map;
import java.util.Set;
import java.util.stream.Collectors;
import org.keycloak.authorization.model.Policy; import org.keycloak.authorization.model.Policy;
import org.keycloak.authorization.model.Resource;
import org.keycloak.authorization.model.ResourceServer; import org.keycloak.authorization.model.ResourceServer;
import org.keycloak.authorization.model.Scope;
import org.keycloak.authorization.permission.evaluator.Evaluators; import org.keycloak.authorization.permission.evaluator.Evaluators;
import org.keycloak.authorization.policy.evaluation.DefaultPolicyEvaluator; import org.keycloak.authorization.policy.evaluation.DefaultPolicyEvaluator;
import org.keycloak.authorization.policy.provider.PolicyProvider; import org.keycloak.authorization.policy.provider.PolicyProvider;
import org.keycloak.authorization.policy.provider.PolicyProviderFactory; import org.keycloak.authorization.policy.provider.PolicyProviderFactory;
import org.keycloak.authorization.store.AuthorizationStoreFactory;
import org.keycloak.authorization.store.PolicyStore; import org.keycloak.authorization.store.PolicyStore;
import org.keycloak.authorization.store.ResourceServerStore; import org.keycloak.authorization.store.ResourceServerStore;
import org.keycloak.authorization.store.ResourceStore; import org.keycloak.authorization.store.ResourceStore;
@ -37,7 +40,6 @@ import org.keycloak.authorization.store.StoreFactory;
import org.keycloak.models.KeycloakSession; import org.keycloak.models.KeycloakSession;
import org.keycloak.models.RealmModel; import org.keycloak.models.RealmModel;
import org.keycloak.models.cache.authorization.CachedStoreFactoryProvider; import org.keycloak.models.cache.authorization.CachedStoreFactoryProvider;
import org.keycloak.models.cache.authorization.CachedStoreProviderFactory;
import org.keycloak.models.utils.RepresentationToModel; import org.keycloak.models.utils.RepresentationToModel;
import org.keycloak.provider.Provider; import org.keycloak.provider.Provider;
import org.keycloak.representations.idm.authorization.AbstractPolicyRepresentation; import org.keycloak.representations.idm.authorization.AbstractPolicyRepresentation;
@ -143,6 +145,61 @@ public final class AuthorizationProvider implements Provider {
return new PolicyStore() { return new PolicyStore() {
@Override @Override
public Policy create(AbstractPolicyRepresentation representation, ResourceServer resourceServer) { public Policy create(AbstractPolicyRepresentation representation, ResourceServer resourceServer) {
Set<String> resources = representation.getResources();
if (resources != null) {
representation.setResources(resources.stream().map(id -> {
Resource resource = getResourceStore().findById(id, resourceServer.getId());
if (resource == null) {
resource = getResourceStore().findByName(id, resourceServer.getId());
}
if (resource == null) {
throw new RuntimeException("Resource [" + id + "] does not exist");
}
return resource.getId();
}).collect(Collectors.toSet()));
}
Set<String> scopes = representation.getScopes();
if (scopes != null) {
representation.setScopes(scopes.stream().map(id -> {
Scope scope = getScopeStore().findById(id, resourceServer.getId());
if (scope == null) {
scope = getScopeStore().findByName(id, resourceServer.getId());
}
if (scope == null) {
throw new RuntimeException("Scope [" + id + "] does not exist");
}
return scope.getId();
}).collect(Collectors.toSet()));
}
Set<String> policies = representation.getPolicies();
if (policies != null) {
representation.setPolicies(policies.stream().map(id -> {
Policy policy = getPolicyStore().findById(id, resourceServer.getId());
if (policy == null) {
policy = getPolicyStore().findByName(id, resourceServer.getId());
}
if (policy == null) {
throw new RuntimeException("Policy [" + id + "] does not exist");
}
return policy.getId();
}).collect(Collectors.toSet()));
}
return RepresentationToModel.toModel(representation, AuthorizationProvider.this, policyStore.create(representation, resourceServer)); return RepresentationToModel.toModel(representation, AuthorizationProvider.this, policyStore.create(representation, resourceServer));
} }

View file

@ -2171,13 +2171,13 @@ public class RepresentationToModel {
private static void updateResources(Set<String> resourceIds, Policy policy, StoreFactory storeFactory) { private static void updateResources(Set<String> resourceIds, Policy policy, StoreFactory storeFactory) {
if (resourceIds != null) { if (resourceIds != null) {
if (resourceIds.isEmpty()) { if (resourceIds.isEmpty()) {
for (Scope scope : new HashSet<Scope>(policy.getScopes())) { for (Resource resource : new HashSet<>(policy.getResources())) {
policy.removeScope(scope); policy.removeResource(resource);
} }
} }
for (String resourceId : resourceIds) { for (String resourceId : resourceIds) {
boolean hasResource = false; boolean hasResource = false;
for (Resource resourceModel : new HashSet<Resource>(policy.getResources())) { for (Resource resourceModel : new HashSet<>(policy.getResources())) {
if (resourceModel.getId().equals(resourceId) || resourceModel.getName().equals(resourceId)) { if (resourceModel.getId().equals(resourceId) || resourceModel.getName().equals(resourceId)) {
hasResource = true; hasResource = true;
} }
@ -2196,7 +2196,7 @@ public class RepresentationToModel {
} }
} }
for (Resource resourceModel : new HashSet<Resource>(policy.getResources())) { for (Resource resourceModel : new HashSet<>(policy.getResources())) {
boolean hasResource = false; boolean hasResource = false;
for (String resourceId : resourceIds) { for (String resourceId : resourceIds) {