[KEYCLOAK-4867] - Cluster events and invalidations
This commit is contained in:
parent
f4f9a98e70
commit
e14be4460b
14 changed files with 662 additions and 340 deletions
|
@ -0,0 +1,73 @@
|
|||
/*
|
||||
* Copyright 2016 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.authorization.infinispan;
|
||||
|
||||
import java.util.Arrays;
|
||||
import java.util.List;
|
||||
|
||||
import org.keycloak.authorization.store.StoreFactory;
|
||||
|
||||
/**
|
||||
* @author <a href="mailto:psilva@redhat.com">Pedro Igor</a>
|
||||
*/
|
||||
public abstract class AbstractCachedStore {
|
||||
|
||||
private final InfinispanStoreFactoryProvider cacheStoreFactory;
|
||||
private final StoreFactory storeFactory;
|
||||
|
||||
AbstractCachedStore(InfinispanStoreFactoryProvider cacheStoreFactory, StoreFactory storeFactory) {
|
||||
this.cacheStoreFactory = cacheStoreFactory;
|
||||
this.storeFactory = storeFactory;
|
||||
}
|
||||
|
||||
protected void addInvalidation(String cacheKeyForPolicy) {
|
||||
getCachedStoreFactory().addInvalidation(cacheKeyForPolicy);
|
||||
}
|
||||
|
||||
protected <E> E putCacheEntry(String resourceServerId, String cacheKeyForPolicy, E cachedPolicy) {
|
||||
cacheStoreFactory.putCacheEntry(resourceServerId, cacheKeyForPolicy, Arrays.asList(cachedPolicy));
|
||||
return cachedPolicy;
|
||||
}
|
||||
|
||||
protected List<Object> resolveCacheEntry(String resourceServerId, String cacheKeyForPolicy) {
|
||||
return cacheStoreFactory.resolveCachedEntry(resourceServerId, cacheKeyForPolicy);
|
||||
}
|
||||
|
||||
protected void removeCachedEntry(String resourceServerId, String key) {
|
||||
getCachedStoreFactory().removeCachedEntry(resourceServerId, key);
|
||||
}
|
||||
|
||||
protected void invalidate(String resourceServerId) {
|
||||
cacheStoreFactory.invalidate(resourceServerId);
|
||||
}
|
||||
|
||||
protected StoreFactory getStoreFactory() {
|
||||
return this.storeFactory;
|
||||
}
|
||||
|
||||
protected boolean isInvalid(String cacheKey) {
|
||||
return cacheStoreFactory.isInvalid(cacheKey);
|
||||
}
|
||||
|
||||
protected InfinispanStoreFactoryProvider.CacheTransaction getTransaction() {
|
||||
return cacheStoreFactory.getTransaction();
|
||||
}
|
||||
|
||||
protected InfinispanStoreFactoryProvider getCachedStoreFactory() {
|
||||
return cacheStoreFactory;
|
||||
}
|
||||
}
|
|
@ -30,18 +30,13 @@ import java.util.function.Function;
|
|||
import java.util.function.Supplier;
|
||||
import java.util.stream.Collectors;
|
||||
|
||||
import org.infinispan.Cache;
|
||||
import org.keycloak.authorization.model.Policy;
|
||||
import org.keycloak.authorization.model.Resource;
|
||||
import org.keycloak.authorization.model.ResourceServer;
|
||||
import org.keycloak.authorization.model.Scope;
|
||||
import org.keycloak.authorization.store.PolicyStore;
|
||||
import org.keycloak.authorization.store.StoreFactory;
|
||||
import org.keycloak.connections.infinispan.InfinispanConnectionProvider;
|
||||
import org.keycloak.models.KeycloakSession;
|
||||
import org.keycloak.models.authorization.infinispan.InfinispanStoreFactoryProvider.CacheTransaction;
|
||||
import org.keycloak.models.authorization.infinispan.entities.CachedPolicy;
|
||||
import org.keycloak.models.cache.authorization.CachedStoreFactoryProvider;
|
||||
import org.keycloak.representations.idm.authorization.AbstractPolicyRepresentation;
|
||||
import org.keycloak.representations.idm.authorization.DecisionStrategy;
|
||||
import org.keycloak.representations.idm.authorization.Logic;
|
||||
|
@ -49,28 +44,15 @@ import org.keycloak.representations.idm.authorization.Logic;
|
|||
/**
|
||||
* @author <a href="mailto:psilva@redhat.com">Pedro Igor</a>
|
||||
*/
|
||||
public class CachedPolicyStore implements PolicyStore {
|
||||
public class CachedPolicyStore extends AbstractCachedStore implements PolicyStore {
|
||||
|
||||
private static final String POLICY_ID_CACHE_PREFIX = "policy-id-";
|
||||
private static final String POLICY_CACHE_PREFIX = "pc-";
|
||||
|
||||
private final Cache<String, Map<String, List<CachedPolicy>>> cache;
|
||||
private final CachedStoreFactoryProvider cacheStoreFactory;
|
||||
private final CacheTransaction transaction;
|
||||
private final List<String> cacheKeys;
|
||||
private final StoreFactory storeFactory;
|
||||
private PolicyStore delegate;
|
||||
|
||||
public CachedPolicyStore(KeycloakSession session, CachedStoreFactoryProvider cacheStoreFactory, CacheTransaction transaction, StoreFactory delegate) {
|
||||
this.cacheStoreFactory = cacheStoreFactory;
|
||||
this.transaction = transaction;
|
||||
InfinispanConnectionProvider provider = session.getProvider(InfinispanConnectionProvider.class);
|
||||
this.cache = provider.getCache(InfinispanConnectionProvider.AUTHORIZATION_CACHE_NAME);
|
||||
cacheKeys = new ArrayList<>();
|
||||
cacheKeys.add("findByResource");
|
||||
cacheKeys.add("findByResourceType");
|
||||
cacheKeys.add("findByScopeIds");
|
||||
cacheKeys.add("findByType");
|
||||
this.storeFactory = delegate;
|
||||
public CachedPolicyStore(InfinispanStoreFactoryProvider cacheStoreFactory, StoreFactory storeFactory) {
|
||||
super(cacheStoreFactory, storeFactory);
|
||||
this.delegate = storeFactory.getPolicyStore();
|
||||
}
|
||||
|
||||
@Override
|
||||
|
@ -78,13 +60,11 @@ public class CachedPolicyStore implements PolicyStore {
|
|||
Policy policy = getDelegate().create(representation, getStoreFactory().getResourceServerStore().findById(resourceServer.getId()));
|
||||
String id = policy.getId();
|
||||
|
||||
this.transaction.whenRollback(() -> {
|
||||
resolveResourceServerCache(resourceServer.getId()).remove(getCacheKeyForPolicy(id));
|
||||
});
|
||||
addInvalidation(getCacheKeyForPolicy(policy.getId()));
|
||||
addInvalidation(getCacheKeyForPolicyName(policy.getName()));
|
||||
addInvalidation(getCacheKeyForPolicyType(policy.getType()));
|
||||
|
||||
this.transaction.whenCommit(() -> {
|
||||
invalidateCache(resourceServer.getId());
|
||||
});
|
||||
configureTransaction(resourceServer, id);
|
||||
|
||||
return createAdapter(new CachedPolicy(policy));
|
||||
}
|
||||
|
@ -95,9 +75,13 @@ public class CachedPolicyStore implements PolicyStore {
|
|||
if (policy == null) {
|
||||
return;
|
||||
}
|
||||
ResourceServer resourceServer = policy.getResourceServer();
|
||||
|
||||
addInvalidation(getCacheKeyForPolicy(policy.getId()));
|
||||
addInvalidation(getCacheKeyForPolicyName(policy.getName()));
|
||||
addInvalidation(getCacheKeyForPolicyType(policy.getType()));
|
||||
|
||||
getDelegate().delete(id);
|
||||
invalidateCache(resourceServer.getId());
|
||||
configureTransaction(policy.getResourceServer(), policy.getId());
|
||||
}
|
||||
|
||||
@Override
|
||||
|
@ -106,27 +90,43 @@ public class CachedPolicyStore implements PolicyStore {
|
|||
return getDelegate().findById(id, null);
|
||||
}
|
||||
|
||||
if (isInvalid(getCacheKeyForPolicy(id))) {
|
||||
return getDelegate().findById(id, resourceServerId);
|
||||
}
|
||||
|
||||
String cacheKeyForPolicy = getCacheKeyForPolicy(id);
|
||||
List<CachedPolicy> cached = resolveResourceServerCache(resourceServerId).get(cacheKeyForPolicy);
|
||||
List<Object> cached = resolveCacheEntry(resourceServerId, cacheKeyForPolicy);
|
||||
|
||||
if (cached == null) {
|
||||
Policy policy = getDelegate().findById(id, resourceServerId);
|
||||
|
||||
if (policy != null) {
|
||||
CachedPolicy cachedPolicy = new CachedPolicy(policy);
|
||||
resolveResourceServerCache(resourceServerId).put(cacheKeyForPolicy, Arrays.asList(cachedPolicy));
|
||||
return createAdapter(cachedPolicy);
|
||||
return createAdapter(putCacheEntry(resourceServerId, cacheKeyForPolicy, new CachedPolicy(policy)));
|
||||
}
|
||||
|
||||
return null;
|
||||
}
|
||||
|
||||
return createAdapter(cached.get(0));
|
||||
return createAdapter(CachedPolicy.class.cast(cached.get(0)));
|
||||
}
|
||||
|
||||
@Override
|
||||
public Policy findByName(String name, String resourceServerId) {
|
||||
return getDelegate().findByName(name, resourceServerId);
|
||||
String cacheKey = getCacheKeyForPolicyName(name);
|
||||
|
||||
if (isInvalid(cacheKey)) {
|
||||
return getDelegate().findByName(name, resourceServerId);
|
||||
}
|
||||
|
||||
return cacheResult(resourceServerId, cacheKey, () -> {
|
||||
Policy policy = getDelegate().findByName(name, resourceServerId);
|
||||
|
||||
if (policy == null) {
|
||||
return Collections.emptyList();
|
||||
}
|
||||
|
||||
return Arrays.asList(policy);
|
||||
}).stream().findFirst().orElse(null);
|
||||
}
|
||||
|
||||
@Override
|
||||
|
@ -141,12 +141,24 @@ public class CachedPolicyStore implements PolicyStore {
|
|||
|
||||
@Override
|
||||
public List<Policy> findByResource(String resourceId, String resourceServerId) {
|
||||
return cacheResult(resourceServerId, new StringBuilder("findByResource").append(resourceId).toString(), () -> getDelegate().findByResource(resourceId, resourceServerId));
|
||||
String cacheKey = getCacheKeyForResource(resourceId);
|
||||
|
||||
if (isInvalid(cacheKey)) {
|
||||
return getDelegate().findByResource(resourceId, resourceServerId);
|
||||
}
|
||||
|
||||
return cacheResult(resourceServerId, cacheKey, () -> getDelegate().findByResource(resourceId, resourceServerId));
|
||||
}
|
||||
|
||||
@Override
|
||||
public List<Policy> findByResourceType(String resourceType, String resourceServerId) {
|
||||
return cacheResult(resourceServerId, new StringBuilder("findByResourceType").append(resourceType).toString(), () -> getDelegate().findByResourceType(resourceType, resourceServerId));
|
||||
String cacheKey = getCacheKeyForResourceType(resourceType);
|
||||
|
||||
if (isInvalid(cacheKey)) {
|
||||
return getDelegate().findByResourceType(resourceType, resourceServerId);
|
||||
}
|
||||
|
||||
return cacheResult(resourceServerId, cacheKey, () -> getDelegate().findByResourceType(resourceType, resourceServerId));
|
||||
}
|
||||
|
||||
@Override
|
||||
|
@ -154,7 +166,13 @@ public class CachedPolicyStore implements PolicyStore {
|
|||
List<Policy> policies = new ArrayList<>();
|
||||
|
||||
for (String scopeId : scopeIds) {
|
||||
policies.addAll(cacheResult(resourceServerId, new StringBuilder("findByScopeIds").append(scopeId).toString(), () -> getDelegate().findByScopeIds(Arrays.asList(scopeId), resourceServerId)));
|
||||
String cacheKey = getCacheForScope(scopeId);
|
||||
|
||||
if (isInvalid(cacheKey)) {
|
||||
policies.addAll(getDelegate().findByScopeIds(Arrays.asList(scopeId), resourceServerId));
|
||||
} else {
|
||||
policies.addAll(cacheResult(resourceServerId, cacheKey, () -> getDelegate().findByScopeIds(Arrays.asList(scopeId), resourceServerId)));
|
||||
}
|
||||
}
|
||||
|
||||
return policies;
|
||||
|
@ -162,7 +180,13 @@ public class CachedPolicyStore implements PolicyStore {
|
|||
|
||||
@Override
|
||||
public List<Policy> findByType(String type, String resourceServerId) {
|
||||
return cacheResult(resourceServerId, new StringBuilder("findByType").append(type).toString(), () -> getDelegate().findByType(type, resourceServerId));
|
||||
String cacheKey = getCacheKeyForPolicyType(type);
|
||||
|
||||
if (isInvalid(cacheKey)) {
|
||||
return getDelegate().findByType(type, resourceServerId);
|
||||
}
|
||||
|
||||
return cacheResult(resourceServerId, cacheKey, () -> getDelegate().findByType(type, resourceServerId));
|
||||
}
|
||||
|
||||
@Override
|
||||
|
@ -170,35 +194,24 @@ public class CachedPolicyStore implements PolicyStore {
|
|||
return getDelegate().findDependentPolicies(id, resourceServerId);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void notifyChange(Object cached) {
|
||||
String resourceServerId;
|
||||
|
||||
if (Resource.class.isInstance(cached)) {
|
||||
resourceServerId = ((Resource) cached).getResourceServer().getId();
|
||||
} else if (Scope.class.isInstance(cached)){
|
||||
resourceServerId = ((Scope) cached).getResourceServer().getId();
|
||||
} else {
|
||||
throw new RuntimeException("Unexpected notification [" + cached + "]");
|
||||
}
|
||||
|
||||
invalidateCache(resourceServerId);
|
||||
private String getCacheKeyForPolicy(String id) {
|
||||
return new StringBuilder().append(POLICY_CACHE_PREFIX).append("id-").append(id).toString();
|
||||
}
|
||||
|
||||
private String getCacheKeyForPolicy(String policyId) {
|
||||
return POLICY_ID_CACHE_PREFIX + policyId;
|
||||
private String getCacheKeyForPolicyType(String type) {
|
||||
return new StringBuilder().append(POLICY_CACHE_PREFIX).append("findByType-").append(type).toString();
|
||||
}
|
||||
|
||||
private StoreFactory getStoreFactory() {
|
||||
return this.storeFactory;
|
||||
private String getCacheKeyForPolicyName(String name) {
|
||||
return new StringBuilder().append(POLICY_CACHE_PREFIX).append("findByName-").append(name).toString();
|
||||
}
|
||||
|
||||
private PolicyStore getDelegate() {
|
||||
if (this.delegate == null) {
|
||||
this.delegate = getStoreFactory().getPolicyStore();
|
||||
}
|
||||
private String getCacheKeyForResourceType(String resourceType) {
|
||||
return new StringBuilder().append(POLICY_CACHE_PREFIX).append("findByResourceType-").append(resourceType).toString();
|
||||
}
|
||||
|
||||
return this.delegate;
|
||||
private String getCacheForScope(String scopeId) {
|
||||
return new StringBuilder().append(POLICY_CACHE_PREFIX).append("findByScopeIds-").append(scopeId).toString();
|
||||
}
|
||||
|
||||
private Policy createAdapter(CachedPolicy cached) {
|
||||
|
@ -243,11 +256,21 @@ public class CachedPolicyStore implements PolicyStore {
|
|||
|
||||
@Override
|
||||
public Map<String, String> getConfig() {
|
||||
return cached.getConfig();
|
||||
return new HashMap<>(cached.getConfig());
|
||||
}
|
||||
|
||||
@Override
|
||||
public void setConfig(Map<String, String> config) {
|
||||
String resourceType = config.get("defaultResourceType");
|
||||
|
||||
if (resourceType != null) {
|
||||
addInvalidation(getCacheKeyForResourceType(resourceType));
|
||||
String cachedResourceType = cached.getConfig().get("defaultResourceType");
|
||||
if (cachedResourceType != null && !resourceType.equals(cachedResourceType)) {
|
||||
addInvalidation(getCacheKeyForResourceType(cachedResourceType));
|
||||
}
|
||||
}
|
||||
|
||||
getDelegateForUpdate().setConfig(config);
|
||||
cached.setConfig(config);
|
||||
}
|
||||
|
@ -259,6 +282,8 @@ public class CachedPolicyStore implements PolicyStore {
|
|||
|
||||
@Override
|
||||
public void setName(String name) {
|
||||
addInvalidation(getCacheKeyForPolicyName(name));
|
||||
addInvalidation(getCacheKeyForPolicyName(cached.getName()));
|
||||
getDelegateForUpdate().setName(name);
|
||||
cached.setName(name);
|
||||
}
|
||||
|
@ -281,13 +306,18 @@ public class CachedPolicyStore implements PolicyStore {
|
|||
|
||||
@Override
|
||||
public void addScope(Scope scope) {
|
||||
getDelegateForUpdate().addScope(getStoreFactory().getScopeStore().findById(scope.getId(), cached.getResourceServerId()));
|
||||
Scope model = getStoreFactory().getScopeStore().findById(scope.getId(), cached.getResourceServerId());
|
||||
addInvalidation(getCacheForScope(model.getId()));
|
||||
getDelegateForUpdate().addScope(model);
|
||||
cached.addScope(scope);
|
||||
scopes.add(scope);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void removeScope(Scope scope) {
|
||||
getDelegateForUpdate().removeScope(getStoreFactory().getScopeStore().findById(scope.getId(), cached.getResourceServerId()));
|
||||
Scope model = getStoreFactory().getScopeStore().findById(scope.getId(), cached.getResourceServerId());
|
||||
addInvalidation(getCacheForScope(scope.getId()));
|
||||
getDelegateForUpdate().removeScope(model);
|
||||
cached.removeScope(scope);
|
||||
scopes.remove(scope);
|
||||
}
|
||||
|
@ -307,20 +337,37 @@ public class CachedPolicyStore implements PolicyStore {
|
|||
|
||||
@Override
|
||||
public void addResource(Resource resource) {
|
||||
getDelegateForUpdate().addResource(getStoreFactory().getResourceStore().findById(resource.getId(), cached.getResourceServerId()));
|
||||
Resource model = getStoreFactory().getResourceStore().findById(resource.getId(), cached.getResourceServerId());
|
||||
|
||||
addInvalidation(getCacheKeyForResource(model.getId()));
|
||||
|
||||
if (model.getType() != null) {
|
||||
addInvalidation(getCacheKeyForResourceType(model.getType()));
|
||||
}
|
||||
|
||||
getDelegateForUpdate().addResource(model);
|
||||
cached.addResource(resource);
|
||||
resources.add(resource);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void removeResource(Resource resource) {
|
||||
getDelegateForUpdate().removeResource(getStoreFactory().getResourceStore().findById(resource.getId(), cached.getResourceServerId()));
|
||||
Resource model = getStoreFactory().getResourceStore().findById(resource.getId(), cached.getResourceServerId());
|
||||
|
||||
addInvalidation(getCacheKeyForResource(model.getId()));
|
||||
|
||||
if (model.getType() != null) {
|
||||
addInvalidation(getCacheKeyForResourceType(model.getType()));
|
||||
}
|
||||
|
||||
getDelegateForUpdate().removeResource(model);
|
||||
cached.removeResource(resource);
|
||||
resources.remove(resource);
|
||||
}
|
||||
|
||||
@Override
|
||||
public Set<Policy> getAssociatedPolicies() {
|
||||
if (associatedPolicies == null) {
|
||||
if (associatedPolicies == null || updated != null) {
|
||||
associatedPolicies = new HashSet<>();
|
||||
|
||||
for (String id : cached.getAssociatedPoliciesIds()) {
|
||||
|
@ -337,7 +384,7 @@ public class CachedPolicyStore implements PolicyStore {
|
|||
|
||||
@Override
|
||||
public Set<Resource> getResources() {
|
||||
if (resources == null) {
|
||||
if (resources == null || updated != null) {
|
||||
resources = new HashSet<>();
|
||||
|
||||
for (String id : cached.getResourcesIds()) {
|
||||
|
@ -354,7 +401,7 @@ public class CachedPolicyStore implements PolicyStore {
|
|||
|
||||
@Override
|
||||
public Set<Scope> getScopes() {
|
||||
if (scopes == null) {
|
||||
if (scopes == null || updated != null) {
|
||||
scopes = new HashSet<>();
|
||||
|
||||
for (String id : cached.getScopesIds()) {
|
||||
|
@ -394,12 +441,8 @@ public class CachedPolicyStore implements PolicyStore {
|
|||
if (this.updated == null) {
|
||||
this.updated = getDelegate().findById(getId(), cached.getResourceServerId());
|
||||
if (this.updated == null) throw new IllegalStateException("Not found in database");
|
||||
transaction.whenCommit(() -> {
|
||||
invalidateCache(cached.getResourceServerId());
|
||||
});
|
||||
transaction.whenRollback(() -> {
|
||||
resolveResourceServerCache(cached.getResourceServerId()).remove(getCacheKeyForPolicy(getId()));
|
||||
});
|
||||
addInvalidation(getCacheKeyForPolicy(updated.getId()));
|
||||
configureTransaction(updated.getResourceServer(), updated.getId());
|
||||
}
|
||||
|
||||
return this.updated;
|
||||
|
@ -407,33 +450,51 @@ public class CachedPolicyStore implements PolicyStore {
|
|||
};
|
||||
}
|
||||
|
||||
private CachedStoreFactoryProvider getCachedStoreFactory() {
|
||||
return cacheStoreFactory;
|
||||
}
|
||||
|
||||
private void invalidateCache(String resourceServerId) {
|
||||
cache.remove(resourceServerId);
|
||||
private String getCacheKeyForResource(String resourceId) {
|
||||
return new StringBuilder("findByResource").append(resourceId).toString();
|
||||
}
|
||||
|
||||
private List<Policy> cacheResult(String resourceServerId, String key, Supplier<List<Policy>> provider) {
|
||||
List<CachedPolicy> cached = resolveResourceServerCache(resourceServerId).computeIfAbsent(key, (Function<String, List<CachedPolicy>>) o -> {
|
||||
List<Object> cached = getCachedStoreFactory().computeIfCachedEntryAbsent(resourceServerId, key, (Function<String, List<Object>>) o -> {
|
||||
List<Policy> result = provider.get();
|
||||
|
||||
if (result.isEmpty()) {
|
||||
return Collections.emptyList();
|
||||
}
|
||||
|
||||
return result.stream().map(policy -> new CachedPolicy(policy)).collect(Collectors.toList());
|
||||
return result.stream().map(policy -> policy.getId()).collect(Collectors.toList());
|
||||
});
|
||||
|
||||
if (cached == null) {
|
||||
return Collections.emptyList();
|
||||
}
|
||||
|
||||
return cached.stream().map(cachedPolicy -> createAdapter(cachedPolicy)).collect(Collectors.toList());
|
||||
return cached.stream().map(id -> findById(id.toString(), resourceServerId)).collect(Collectors.toList());
|
||||
}
|
||||
|
||||
private Map<String, List<CachedPolicy>> resolveResourceServerCache(String id) {
|
||||
return cache.computeIfAbsent(id, key -> new HashMap<>());
|
||||
private void configureTransaction(ResourceServer resourceServer, String id) {
|
||||
getTransaction().whenRollback(() -> removeCachedEntry(resourceServer.getId(), getCacheKeyForPolicy(id)));
|
||||
getTransaction().whenCommit(() -> invalidate(resourceServer.getId()));
|
||||
}
|
||||
|
||||
private PolicyStore getDelegate() {
|
||||
return delegate;
|
||||
}
|
||||
|
||||
void addInvalidations(Object object) {
|
||||
if (Resource.class.isInstance(object)) {
|
||||
Resource resource = (Resource) object;
|
||||
addInvalidation(getCacheKeyForResource(resource.getId()));
|
||||
String type = resource.getType();
|
||||
|
||||
if (type != null) {
|
||||
addInvalidation(getCacheKeyForResourceType(type));
|
||||
}
|
||||
} else if (Scope.class.isInstance(object)) {
|
||||
Scope scope = (Scope) object;
|
||||
addInvalidation(getCacheForScope(scope.getId()));
|
||||
} else {
|
||||
throw new RuntimeException("Unexpected notification [" + object + "]");
|
||||
}
|
||||
}
|
||||
}
|
|
@ -18,46 +18,34 @@
|
|||
|
||||
package org.keycloak.models.authorization.infinispan;
|
||||
|
||||
import java.util.Arrays;
|
||||
import java.util.HashMap;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
|
||||
import org.infinispan.Cache;
|
||||
import org.keycloak.authorization.model.ResourceServer;
|
||||
import org.keycloak.authorization.store.ResourceServerStore;
|
||||
import org.keycloak.authorization.store.StoreFactory;
|
||||
import org.keycloak.connections.infinispan.InfinispanConnectionProvider;
|
||||
import org.keycloak.models.KeycloakSession;
|
||||
import org.keycloak.models.authorization.infinispan.InfinispanStoreFactoryProvider.CacheTransaction;
|
||||
import org.keycloak.models.authorization.infinispan.entities.CachedResourceServer;
|
||||
import org.keycloak.representations.idm.authorization.PolicyEnforcementMode;
|
||||
|
||||
/**
|
||||
* @author <a href="mailto:psilva@redhat.com">Pedro Igor</a>
|
||||
*/
|
||||
public class CachedResourceServerStore implements ResourceServerStore {
|
||||
public class CachedResourceServerStore extends AbstractCachedStore implements ResourceServerStore {
|
||||
|
||||
private static final String RS_ID_CACHE_PREFIX = "rs-id-";
|
||||
private static final String RS_CLIENT_ID_CACHE_PREFIX = "rs-client-id-";
|
||||
private static final String RS_PREFIX = "rs-";
|
||||
|
||||
private final CacheTransaction transaction;
|
||||
private StoreFactory storeFactory;
|
||||
private ResourceServerStore delegate;
|
||||
private final Cache<String, Map<String, List<CachedResourceServer>>> cache;
|
||||
private final ResourceServerStore delegate;
|
||||
|
||||
public CachedResourceServerStore(KeycloakSession session, CacheTransaction transaction, StoreFactory delegate) {
|
||||
this.transaction = transaction;
|
||||
InfinispanConnectionProvider provider = session.getProvider(InfinispanConnectionProvider.class);
|
||||
this.cache = provider.getCache(InfinispanConnectionProvider.AUTHORIZATION_CACHE_NAME);
|
||||
this.storeFactory = delegate;
|
||||
public CachedResourceServerStore(InfinispanStoreFactoryProvider cachedStoreFactory, StoreFactory storeFactory) {
|
||||
super(cachedStoreFactory, storeFactory);
|
||||
this.delegate = storeFactory.getResourceServerStore();
|
||||
}
|
||||
|
||||
@Override
|
||||
public ResourceServer create(String clientId) {
|
||||
ResourceServer resourceServer = getDelegate().create(clientId);
|
||||
|
||||
this.transaction.whenRollback(() -> resolveResourceServerCache(resourceServer.getId()).remove(getCacheKeyForResourceServer(resourceServer.getId())));
|
||||
getTransaction().whenCommit(() -> getCachedStoreFactory().removeEntries(resourceServer));
|
||||
getTransaction().whenRollback(() -> removeCachedEntry(resourceServer.getId(), getCacheKeyForResourceServer(resourceServer.getId())));
|
||||
|
||||
return createAdapter(new CachedResourceServer(resourceServer));
|
||||
}
|
||||
|
@ -65,71 +53,71 @@ public class CachedResourceServerStore implements ResourceServerStore {
|
|||
@Override
|
||||
public void delete(String id) {
|
||||
ResourceServer resourceServer = getDelegate().findById(id);
|
||||
getDelegate().delete(id);
|
||||
this.transaction.whenCommit(() -> {
|
||||
cache.remove(id);
|
||||
cache.remove(resourceServer.getClientId());
|
||||
});
|
||||
|
||||
if (resourceServer != null) {
|
||||
getDelegate().delete(id);
|
||||
getTransaction().whenCommit(() -> getCachedStoreFactory().removeEntries(resourceServer));
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public ResourceServer findById(String id) {
|
||||
String cacheKeyForResourceServer = getCacheKeyForResourceServer(id);
|
||||
List<CachedResourceServer> cached = resolveResourceServerCache(id).get(cacheKeyForResourceServer);
|
||||
String cacheKey = getCacheKeyForResourceServer(id);
|
||||
|
||||
if (isInvalid(cacheKey)) {
|
||||
return getDelegate().findById(id);
|
||||
}
|
||||
|
||||
List<Object> cached = resolveCacheEntry(id, cacheKey);
|
||||
|
||||
if (cached == null) {
|
||||
ResourceServer resourceServer = getDelegate().findById(id);
|
||||
|
||||
if (resourceServer != null) {
|
||||
CachedResourceServer cachedResourceServer = new CachedResourceServer(resourceServer);
|
||||
resolveResourceServerCache(id).put(cacheKeyForResourceServer, Arrays.asList(cachedResourceServer));
|
||||
return createAdapter(cachedResourceServer);
|
||||
return createAdapter(putCacheEntry(id, cacheKey, new CachedResourceServer(resourceServer)));
|
||||
}
|
||||
|
||||
return null;
|
||||
}
|
||||
|
||||
return createAdapter(cached.get(0));
|
||||
return createAdapter(CachedResourceServer.class.cast(cached.get(0)));
|
||||
}
|
||||
|
||||
@Override
|
||||
public ResourceServer findByClient(String id) {
|
||||
String cacheKeyForResourceServer = getCacheKeyForResourceServerClientId(id);
|
||||
List<CachedResourceServer> cached = resolveResourceServerCache(id).get(cacheKeyForResourceServer);
|
||||
String cacheKey = getCacheKeyForResourceServerClientId(id);
|
||||
|
||||
if (isInvalid(cacheKey)) {
|
||||
return getDelegate().findByClient(id);
|
||||
}
|
||||
|
||||
List<Object> cached = resolveCacheEntry(id, cacheKey);
|
||||
|
||||
if (cached == null) {
|
||||
ResourceServer resourceServer = getDelegate().findByClient(id);
|
||||
|
||||
if (resourceServer != null) {
|
||||
resolveResourceServerCache(cacheKeyForResourceServer).put(cacheKeyForResourceServer, Arrays.asList(new CachedResourceServer(resourceServer)));
|
||||
return findById(resourceServer.getId());
|
||||
return findById(putCacheEntry(id, cacheKey, resourceServer.getId()));
|
||||
}
|
||||
|
||||
return null;
|
||||
}
|
||||
|
||||
return createAdapter(cached.get(0));
|
||||
return findById(cached.get(0).toString());
|
||||
}
|
||||
|
||||
private String getCacheKeyForResourceServer(String id) {
|
||||
return RS_ID_CACHE_PREFIX + id;
|
||||
return new StringBuilder(RS_PREFIX).append("id-").append(id).toString();
|
||||
}
|
||||
|
||||
private String getCacheKeyForResourceServerClientId(String id) {
|
||||
return RS_CLIENT_ID_CACHE_PREFIX + id;
|
||||
return new StringBuilder(RS_PREFIX).append("findByClientId-").append(id).toString();
|
||||
}
|
||||
|
||||
private ResourceServerStore getDelegate() {
|
||||
if (this.delegate == null) {
|
||||
this.delegate = getStoreFactory().getResourceServerStore();
|
||||
}
|
||||
|
||||
return this.delegate;
|
||||
}
|
||||
|
||||
private StoreFactory getStoreFactory() {
|
||||
return this.storeFactory;
|
||||
}
|
||||
private ResourceServer createAdapter(ResourceServer cached) {
|
||||
return new ResourceServer() {
|
||||
|
||||
|
@ -171,9 +159,9 @@ public class CachedResourceServerStore implements ResourceServerStore {
|
|||
if (this.updated == null) {
|
||||
this.updated = getDelegate().findById(getId());
|
||||
if (this.updated == null) throw new IllegalStateException("Not found in database");
|
||||
transaction.whenCommit(() -> {
|
||||
cache.remove(getId());
|
||||
cache.remove(getClientId());
|
||||
addInvalidation(getCacheKeyForResourceServer(updated.getId()));
|
||||
getTransaction().whenCommit(() -> {
|
||||
invalidate(updated.getId());
|
||||
});
|
||||
}
|
||||
|
||||
|
@ -181,8 +169,4 @@ public class CachedResourceServerStore implements ResourceServerStore {
|
|||
}
|
||||
};
|
||||
}
|
||||
|
||||
private Map<String, List<CachedResourceServer>> resolveResourceServerCache(String id) {
|
||||
return cache.computeIfAbsent(id, key -> new HashMap<>());
|
||||
}
|
||||
}
|
||||
|
|
|
@ -21,7 +21,6 @@ package org.keycloak.models.authorization.infinispan;
|
|||
import java.util.ArrayList;
|
||||
import java.util.Arrays;
|
||||
import java.util.Collections;
|
||||
import java.util.HashMap;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
import java.util.Set;
|
||||
|
@ -29,56 +28,37 @@ import java.util.function.Function;
|
|||
import java.util.function.Supplier;
|
||||
import java.util.stream.Collectors;
|
||||
|
||||
import org.infinispan.Cache;
|
||||
import org.keycloak.authorization.model.Resource;
|
||||
import org.keycloak.authorization.model.ResourceServer;
|
||||
import org.keycloak.authorization.model.Scope;
|
||||
import org.keycloak.authorization.store.ResourceStore;
|
||||
import org.keycloak.authorization.store.StoreFactory;
|
||||
import org.keycloak.connections.infinispan.InfinispanConnectionProvider;
|
||||
import org.keycloak.models.KeycloakSession;
|
||||
import org.keycloak.models.authorization.infinispan.InfinispanStoreFactoryProvider.CacheTransaction;
|
||||
import org.keycloak.models.authorization.infinispan.entities.CachedResource;
|
||||
import org.keycloak.models.cache.authorization.CachedStoreFactoryProvider;
|
||||
|
||||
/**
|
||||
* @author <a href="mailto:psilva@redhat.com">Pedro Igor</a>
|
||||
*/
|
||||
public class CachedResourceStore implements ResourceStore {
|
||||
public class CachedResourceStore extends AbstractCachedStore implements ResourceStore {
|
||||
|
||||
private static final String RESOURCE_ID_CACHE_PREFIX = "rsc-id-";
|
||||
private static final String RESOURCE_NAME_CACHE_PREFIX = "rsc-name-";
|
||||
private static final String RESOURCE_CACHE_PREFIX = "rs-";
|
||||
|
||||
private final CachedStoreFactoryProvider cacheStoreFactory;
|
||||
private final CacheTransaction transaction;
|
||||
private final List<String> cacheKeys;
|
||||
private StoreFactory delegateStoreFactory;
|
||||
private ResourceStore delegate;
|
||||
private final Cache<String, Map<String, List<CachedResource>>> cache;
|
||||
|
||||
public CachedResourceStore(KeycloakSession session, CachedStoreFactoryProvider cacheStoreFactory, CacheTransaction transaction, StoreFactory delegate) {
|
||||
this.cacheStoreFactory = cacheStoreFactory;
|
||||
InfinispanConnectionProvider provider = session.getProvider(InfinispanConnectionProvider.class);
|
||||
this.cache = provider.getCache(InfinispanConnectionProvider.AUTHORIZATION_CACHE_NAME);
|
||||
this.transaction = transaction;
|
||||
cacheKeys = new ArrayList<>();
|
||||
cacheKeys.add("findByOwner");
|
||||
cacheKeys.add("findByUri");
|
||||
cacheKeys.add("findByName");
|
||||
this.delegateStoreFactory = delegate;
|
||||
public CachedResourceStore(InfinispanStoreFactoryProvider cacheStoreFactory, StoreFactory storeFactory) {
|
||||
super(cacheStoreFactory, storeFactory);
|
||||
delegate = storeFactory.getResourceStore();
|
||||
}
|
||||
|
||||
@Override
|
||||
public Resource create(String name, ResourceServer resourceServer, String owner) {
|
||||
Resource resource = getDelegate().create(name, getDelegateStoreFactory().getResourceServerStore().findById(resourceServer.getId()), owner);
|
||||
Resource resource = getDelegate().create(name, getStoreFactory().getResourceServerStore().findById(resourceServer.getId()), owner);
|
||||
|
||||
this.transaction.whenRollback(() -> {
|
||||
resolveResourceServerCache(resourceServer.getId()).remove(getCacheKeyForResource(resource.getId()));
|
||||
});
|
||||
addInvalidation(getCacheKeyForResource(resource.getId()));
|
||||
addInvalidation(getCacheKeyForResourceName(resource.getName()));
|
||||
getCachedStoreFactory().getPolicyStore().addInvalidations(resource);
|
||||
|
||||
this.transaction.whenCommit(() -> {
|
||||
invalidateCache(resourceServer.getId());
|
||||
});
|
||||
getTransaction().whenRollback(() -> removeCachedEntry(resourceServer.getId(), getCacheKeyForResource(resource.getId())));
|
||||
getTransaction().whenCommit(() -> invalidate(resourceServer.getId()));
|
||||
|
||||
return createAdapter(new CachedResource(resource));
|
||||
}
|
||||
|
@ -86,44 +66,69 @@ public class CachedResourceStore implements ResourceStore {
|
|||
@Override
|
||||
public void delete(String id) {
|
||||
Resource resource = getDelegate().findById(id, null);
|
||||
|
||||
if (resource == null) {
|
||||
return;
|
||||
}
|
||||
|
||||
ResourceServer resourceServer = resource.getResourceServer();
|
||||
|
||||
addInvalidation(getCacheKeyForResource(resource.getId()));
|
||||
addInvalidation(getCacheKeyForResourceName(resource.getName()));
|
||||
addInvalidation(getCacheKeyForOwner(resource.getOwner()));
|
||||
addInvalidation(getCacheKeyForUri(resource.getUri()));
|
||||
getCachedStoreFactory().getPolicyStore().addInvalidations(resource);
|
||||
|
||||
getDelegate().delete(id);
|
||||
this.transaction.whenCommit(() -> {
|
||||
invalidateCache(resourceServer.getId());
|
||||
|
||||
getTransaction().whenCommit(() -> {
|
||||
invalidate(resourceServer.getId());
|
||||
});
|
||||
}
|
||||
|
||||
@Override
|
||||
public Resource findById(String id, String resourceServerId) {
|
||||
String cacheKeyForResource = getCacheKeyForResource(id);
|
||||
List<CachedResource> cached = resolveResourceServerCache(resourceServerId).get(cacheKeyForResource);
|
||||
|
||||
if (isInvalid(cacheKeyForResource)) {
|
||||
return getDelegate().findById(id, resourceServerId);
|
||||
}
|
||||
|
||||
List<Object> cached = resolveCacheEntry(resourceServerId, cacheKeyForResource);
|
||||
|
||||
if (cached == null) {
|
||||
Resource resource = getDelegate().findById(id, resourceServerId);
|
||||
|
||||
if (resource != null) {
|
||||
CachedResource cachedResource = new CachedResource(resource);
|
||||
resolveResourceServerCache(resourceServerId).put(cacheKeyForResource, Arrays.asList(cachedResource));
|
||||
return createAdapter(cachedResource);
|
||||
return createAdapter(putCacheEntry(resourceServerId, cacheKeyForResource, new CachedResource(resource)));
|
||||
}
|
||||
|
||||
return null;
|
||||
}
|
||||
|
||||
return createAdapter(cached.get(0));
|
||||
return createAdapter(CachedResource.class.cast(cached.get(0)));
|
||||
}
|
||||
|
||||
@Override
|
||||
public List<Resource> findByOwner(String ownerId, String resourceServerId) {
|
||||
return cacheResult(resourceServerId, new StringBuilder("findByOwner").append(ownerId).toString(), () -> getDelegate().findByOwner(ownerId, resourceServerId));
|
||||
String cacheKey = getCacheKeyForOwner(ownerId);
|
||||
|
||||
if (isInvalid(cacheKey)) {
|
||||
return getDelegate().findByOwner(ownerId, resourceServerId);
|
||||
}
|
||||
|
||||
return cacheResult(resourceServerId, cacheKey, () -> getDelegate().findByOwner(ownerId, resourceServerId));
|
||||
}
|
||||
|
||||
@Override
|
||||
public List<Resource> findByUri(String uri, String resourceServerId) {
|
||||
return cacheResult(resourceServerId, new StringBuilder("findByUri").append(uri).toString(), () -> getDelegate().findByUri(uri, resourceServerId));
|
||||
String cacheKey = getCacheKeyForUri(uri);
|
||||
|
||||
if (isInvalid(cacheKey)) {
|
||||
return getDelegate().findByUri(uri, resourceServerId);
|
||||
}
|
||||
|
||||
return cacheResult(resourceServerId, cacheKey, () -> getDelegate().findByUri(uri, resourceServerId));
|
||||
}
|
||||
|
||||
@Override
|
||||
|
@ -143,22 +148,21 @@ public class CachedResourceStore implements ResourceStore {
|
|||
|
||||
@Override
|
||||
public Resource findByName(String name, String resourceServerId) {
|
||||
String cacheKeyForResource = getCacheKeyForResourceName(name, resourceServerId);
|
||||
List<CachedResource> cached = resolveResourceServerCache(resourceServerId).get(cacheKeyForResource);
|
||||
String cacheKey = getCacheKeyForResourceName(name);
|
||||
|
||||
if (cached == null) {
|
||||
Resource resource = getDelegate().findByName(name, resourceServerId);
|
||||
|
||||
if (resource != null) {
|
||||
invalidateCache(resourceServerId);
|
||||
resolveResourceServerCache(resourceServerId).put(cacheKeyForResource, Arrays.asList(new CachedResource(resource)));
|
||||
return findById(resource.getId(), resourceServerId);
|
||||
}
|
||||
|
||||
return null;
|
||||
if (isInvalid(cacheKey)) {
|
||||
return getDelegate().findByName(name, resourceServerId);
|
||||
}
|
||||
|
||||
return createAdapter(cached.get(0));
|
||||
return cacheResult(resourceServerId, cacheKey, () -> {
|
||||
Resource resource = getDelegate().findByName(name, resourceServerId);
|
||||
|
||||
if (resource == null) {
|
||||
return Collections.emptyList();
|
||||
}
|
||||
|
||||
return Arrays.asList(resource);
|
||||
}).stream().findFirst().orElse(null);
|
||||
}
|
||||
|
||||
@Override
|
||||
|
@ -167,23 +171,41 @@ public class CachedResourceStore implements ResourceStore {
|
|||
}
|
||||
|
||||
private String getCacheKeyForResource(String id) {
|
||||
return RESOURCE_ID_CACHE_PREFIX + id;
|
||||
return new StringBuilder(RESOURCE_CACHE_PREFIX).append("id-").append(id).toString();
|
||||
}
|
||||
|
||||
private String getCacheKeyForResourceName(String name, String resourceServerId) {
|
||||
return RESOURCE_NAME_CACHE_PREFIX + name + "-" + resourceServerId;
|
||||
private String getCacheKeyForResourceName(String name) {
|
||||
return new StringBuilder(RESOURCE_CACHE_PREFIX).append("findByName-").append(name).toString();
|
||||
}
|
||||
|
||||
private String getCacheKeyForOwner(String name) {
|
||||
return new StringBuilder(RESOURCE_CACHE_PREFIX).append("findByOwner-").append(name).toString();
|
||||
}
|
||||
|
||||
private String getCacheKeyForUri(String uri) {
|
||||
return new StringBuilder(RESOURCE_CACHE_PREFIX).append("findByUri-").append(uri).toString();
|
||||
}
|
||||
|
||||
private ResourceStore getDelegate() {
|
||||
if (this.delegate == null) {
|
||||
this.delegate = getDelegateStoreFactory().getResourceStore();
|
||||
}
|
||||
|
||||
return this.delegate;
|
||||
}
|
||||
|
||||
private StoreFactory getDelegateStoreFactory() {
|
||||
return this.delegateStoreFactory;
|
||||
private List<Resource> cacheResult(String resourceServerId, String key, Supplier<List<Resource>> provider) {
|
||||
List<Object> cached = getCachedStoreFactory().computeIfCachedEntryAbsent(resourceServerId, key, (Function<String, List<Object>>) o -> {
|
||||
List<Resource> result = provider.get();
|
||||
|
||||
if (result.isEmpty()) {
|
||||
return Collections.emptyList();
|
||||
}
|
||||
|
||||
return result.stream().map(policy -> policy.getId()).collect(Collectors.toList());
|
||||
});
|
||||
|
||||
if (cached == null) {
|
||||
return Collections.emptyList();
|
||||
}
|
||||
|
||||
return cached.stream().map(id -> findById(id.toString(), resourceServerId)).collect(Collectors.toList());
|
||||
}
|
||||
|
||||
private Resource createAdapter(CachedResource cached) {
|
||||
|
@ -204,6 +226,8 @@ public class CachedResourceStore implements ResourceStore {
|
|||
|
||||
@Override
|
||||
public void setName(String name) {
|
||||
addInvalidation(getCacheKeyForResourceName(name));
|
||||
addInvalidation(getCacheKeyForResourceName(cached.getName()));
|
||||
getDelegateForUpdate().setName(name);
|
||||
cached.setName(name);
|
||||
}
|
||||
|
@ -215,6 +239,8 @@ public class CachedResourceStore implements ResourceStore {
|
|||
|
||||
@Override
|
||||
public void setUri(String uri) {
|
||||
addInvalidation(getCacheKeyForUri(uri));
|
||||
addInvalidation(getCacheKeyForUri(cached.getUri()));
|
||||
getDelegateForUpdate().setUri(uri);
|
||||
cached.setUri(uri);
|
||||
}
|
||||
|
@ -226,6 +252,7 @@ public class CachedResourceStore implements ResourceStore {
|
|||
|
||||
@Override
|
||||
public void setType(String type) {
|
||||
getCachedStoreFactory().getPolicyStore().addInvalidations(cached);
|
||||
getDelegateForUpdate().setType(type);
|
||||
cached.setType(type);
|
||||
}
|
||||
|
@ -270,7 +297,7 @@ public class CachedResourceStore implements ResourceStore {
|
|||
|
||||
@Override
|
||||
public void updateScopes(Set<Scope> scopes) {
|
||||
getDelegateForUpdate().updateScopes(scopes.stream().map(scope -> getDelegateStoreFactory().getScopeStore().findById(scope.getId(), cached.getResourceServerId())).collect(Collectors.toSet()));
|
||||
getDelegateForUpdate().updateScopes(scopes.stream().map(scope -> getStoreFactory().getScopeStore().findById(scope.getId(), cached.getResourceServerId())).collect(Collectors.toSet()));
|
||||
cached.updateScopes(scopes);
|
||||
}
|
||||
|
||||
|
@ -279,52 +306,14 @@ public class CachedResourceStore implements ResourceStore {
|
|||
String resourceServerId = cached.getResourceServerId();
|
||||
this.updated = getDelegate().findById(getId(), resourceServerId);
|
||||
if (this.updated == null) throw new IllegalStateException("Not found in database");
|
||||
transaction.whenCommit(() -> {
|
||||
invalidateCache(resourceServerId);
|
||||
});
|
||||
transaction.whenRollback(() -> {
|
||||
resolveResourceServerCache(resourceServerId).remove(getCacheKeyForResource(cached.getId()));
|
||||
});
|
||||
addInvalidation(getCacheKeyForResource(updated.getId()));
|
||||
getCachedStoreFactory().getPolicyStore().addInvalidations(updated);
|
||||
getTransaction().whenCommit(() -> invalidate(resourceServerId));
|
||||
getTransaction().whenRollback(() -> removeCachedEntry(resourceServerId, getCacheKeyForResource(cached.getId())));
|
||||
}
|
||||
|
||||
return this.updated;
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
private CachedStoreFactoryProvider getCachedStoreFactory() {
|
||||
return cacheStoreFactory;
|
||||
}
|
||||
|
||||
private List<Resource> cacheResult(String resourceServerId, String key, Supplier<List<Resource>> provider) {
|
||||
List<CachedResource> cached = resolveResourceServerCache(resourceServerId).computeIfAbsent(key, (Function<String, List<CachedResource>>) o -> {
|
||||
List<Resource> result = provider.get();
|
||||
|
||||
if (result.isEmpty()) {
|
||||
return null;
|
||||
}
|
||||
|
||||
return result.stream().map(resource -> new CachedResource(resource)).collect(Collectors.toList());
|
||||
});
|
||||
|
||||
if (cached == null) {
|
||||
return Collections.emptyList();
|
||||
}
|
||||
|
||||
List<Resource> adapters = new ArrayList<>();
|
||||
|
||||
for (CachedResource resource : cached) {
|
||||
adapters.add(createAdapter(resource));
|
||||
}
|
||||
|
||||
return adapters;
|
||||
}
|
||||
|
||||
private void invalidateCache(String resourceServerId) {
|
||||
cache.remove(resourceServerId);
|
||||
}
|
||||
|
||||
private Map<String, List<CachedResource>> resolveResourceServerCache(String id) {
|
||||
return cache.computeIfAbsent(id, key -> new HashMap<>());
|
||||
}
|
||||
}
|
||||
|
|
|
@ -19,52 +19,44 @@
|
|||
package org.keycloak.models.authorization.infinispan;
|
||||
|
||||
import java.util.Arrays;
|
||||
import java.util.HashMap;
|
||||
import java.util.Collections;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
import java.util.Objects;
|
||||
import java.util.function.Function;
|
||||
import java.util.function.Supplier;
|
||||
import java.util.stream.Collectors;
|
||||
|
||||
import org.infinispan.Cache;
|
||||
import org.keycloak.authorization.model.ResourceServer;
|
||||
import org.keycloak.authorization.model.Scope;
|
||||
import org.keycloak.authorization.store.ScopeStore;
|
||||
import org.keycloak.authorization.store.StoreFactory;
|
||||
import org.keycloak.connections.infinispan.InfinispanConnectionProvider;
|
||||
import org.keycloak.models.KeycloakSession;
|
||||
import org.keycloak.models.authorization.infinispan.InfinispanStoreFactoryProvider.CacheTransaction;
|
||||
import org.keycloak.models.authorization.infinispan.entities.CachedScope;
|
||||
import org.keycloak.models.cache.authorization.CachedStoreFactoryProvider;
|
||||
|
||||
/**
|
||||
* @author <a href="mailto:psilva@redhat.com">Pedro Igor</a>
|
||||
*/
|
||||
public class CachedScopeStore implements ScopeStore {
|
||||
public class CachedScopeStore extends AbstractCachedStore implements ScopeStore {
|
||||
|
||||
private static final String SCOPE_ID_CACHE_PREFIX = "scp-id-";
|
||||
private static final String SCOPE_NAME_CACHE_PREFIX = "scp-name-";
|
||||
private static final String SCOPE_CACHE_PREFIX = "scp-";
|
||||
|
||||
private final Cache<String, Map<String, List<CachedScope>>> cache;
|
||||
private final CachedStoreFactoryProvider cacheStoreFactory;
|
||||
private final CacheTransaction transaction;
|
||||
private ScopeStore delegate;
|
||||
private StoreFactory storeFactory;
|
||||
private final ScopeStore delegate;
|
||||
|
||||
public CachedScopeStore(KeycloakSession session, CachedStoreFactoryProvider cacheStoreFactory, CacheTransaction transaction, StoreFactory delegate) {
|
||||
this.cacheStoreFactory = cacheStoreFactory;
|
||||
this.transaction = transaction;
|
||||
InfinispanConnectionProvider provider = session.getProvider(InfinispanConnectionProvider.class);
|
||||
this.cache = provider.getCache(InfinispanConnectionProvider.AUTHORIZATION_CACHE_NAME);
|
||||
this.storeFactory = delegate;
|
||||
public CachedScopeStore(InfinispanStoreFactoryProvider cacheStoreFactory, StoreFactory storeFactory) {
|
||||
super(cacheStoreFactory, storeFactory);
|
||||
this.delegate = storeFactory.getScopeStore();
|
||||
}
|
||||
|
||||
@Override
|
||||
public Scope create(String name, ResourceServer resourceServer) {
|
||||
Scope scope = getDelegate().create(name, getStoreFactory().getResourceServerStore().findById(resourceServer.getId()));
|
||||
|
||||
this.transaction.whenRollback(() -> resolveResourceServerCache(resourceServer.getId()).remove(getCacheKeyForScope(scope.getId())));
|
||||
this.transaction.whenCommit(() -> {
|
||||
invalidateCache(resourceServer.getId());
|
||||
});
|
||||
addInvalidation(getCacheKeyForScope(scope.getId()));
|
||||
addInvalidation(getCacheKeyForScopeName(scope.getName()));
|
||||
getCachedStoreFactory().getPolicyStore().addInvalidations(scope);
|
||||
|
||||
getTransaction().whenRollback(() -> removeCachedEntry(resourceServer.getId(), getCacheKeyForScope(scope.getId())));
|
||||
getTransaction().whenCommit(() -> invalidate(resourceServer.getId()));
|
||||
|
||||
return createAdapter(new CachedScope(scope));
|
||||
}
|
||||
|
@ -72,53 +64,62 @@ public class CachedScopeStore implements ScopeStore {
|
|||
@Override
|
||||
public void delete(String id) {
|
||||
Scope scope = getDelegate().findById(id, null);
|
||||
|
||||
if (scope == null) {
|
||||
return;
|
||||
}
|
||||
|
||||
ResourceServer resourceServer = scope.getResourceServer();
|
||||
|
||||
addInvalidation(getCacheKeyForScope(scope.getId()));
|
||||
addInvalidation(getCacheKeyForScopeName(scope.getName()));
|
||||
getCachedStoreFactory().getPolicyStore().addInvalidations(scope);
|
||||
|
||||
getDelegate().delete(id);
|
||||
this.transaction.whenCommit(() -> {
|
||||
invalidateCache(resourceServer.getId());
|
||||
});
|
||||
|
||||
getTransaction().whenCommit(() -> invalidate(resourceServer.getId()));
|
||||
}
|
||||
|
||||
@Override
|
||||
public Scope findById(String id, String resourceServerId) {
|
||||
String cacheKeyForScope = getCacheKeyForScope(id);
|
||||
List<CachedScope> cached = resolveResourceServerCache(resourceServerId).get(cacheKeyForScope);
|
||||
String cacheKey = getCacheKeyForScope(id);
|
||||
|
||||
if (isInvalid(cacheKey)) {
|
||||
return getDelegate().findById(id, resourceServerId);
|
||||
}
|
||||
|
||||
List<Object> cached = resolveCacheEntry(resourceServerId, cacheKey);
|
||||
|
||||
if (cached == null) {
|
||||
Scope scope = getDelegate().findById(id, resourceServerId);
|
||||
|
||||
if (scope != null) {
|
||||
CachedScope cachedScope = new CachedScope(scope);
|
||||
resolveResourceServerCache(resourceServerId).put(cacheKeyForScope, Arrays.asList(cachedScope));
|
||||
return createAdapter(cachedScope);
|
||||
return createAdapter(putCacheEntry(resourceServerId, cacheKey, new CachedScope(scope)));
|
||||
}
|
||||
|
||||
return null;
|
||||
}
|
||||
|
||||
return createAdapter(cached.get(0));
|
||||
return createAdapter(CachedScope.class.cast(cached.get(0)));
|
||||
}
|
||||
|
||||
@Override
|
||||
public Scope findByName(String name, String resourceServerId) {
|
||||
String cacheKeyForScope = getCacheKeyForScopeName(name);
|
||||
List<CachedScope> cached = resolveResourceServerCache(resourceServerId).get(cacheKeyForScope);
|
||||
String cacheKey = getCacheKeyForScopeName(name);
|
||||
|
||||
if (cached == null) {
|
||||
Scope scope = getDelegate().findByName(name, resourceServerId);
|
||||
|
||||
if (scope != null) {
|
||||
resolveResourceServerCache(resourceServerId).put(cacheKeyForScope, Arrays.asList(new CachedScope(scope)));
|
||||
return findById(scope.getId(), resourceServerId);
|
||||
}
|
||||
|
||||
return null;
|
||||
if (isInvalid(cacheKey)) {
|
||||
return getDelegate().findByName(name, resourceServerId);
|
||||
}
|
||||
|
||||
return createAdapter(cached.get(0));
|
||||
return cacheResult(resourceServerId, cacheKey, () -> {
|
||||
Scope scope = getDelegate().findByName(name, resourceServerId);
|
||||
|
||||
if (scope == null) {
|
||||
return Collections.emptyList();
|
||||
}
|
||||
|
||||
return Arrays.asList(scope);
|
||||
}).stream().findFirst().orElse(null);
|
||||
}
|
||||
|
||||
@Override
|
||||
|
@ -132,23 +133,33 @@ public class CachedScopeStore implements ScopeStore {
|
|||
}
|
||||
|
||||
private String getCacheKeyForScope(String id) {
|
||||
return SCOPE_ID_CACHE_PREFIX + id;
|
||||
return new StringBuilder(SCOPE_CACHE_PREFIX).append("id-").append(id).toString();
|
||||
}
|
||||
|
||||
private String getCacheKeyForScopeName(String name) {
|
||||
return SCOPE_NAME_CACHE_PREFIX + name;
|
||||
return new StringBuilder(SCOPE_CACHE_PREFIX).append("findByName-").append(name).toString();
|
||||
}
|
||||
|
||||
private ScopeStore getDelegate() {
|
||||
if (this.delegate == null) {
|
||||
this.delegate = getStoreFactory().getScopeStore();
|
||||
}
|
||||
|
||||
return this.delegate;
|
||||
}
|
||||
|
||||
private StoreFactory getStoreFactory() {
|
||||
return this.storeFactory;
|
||||
private List<Scope> cacheResult(String resourceServerId, String key, Supplier<List<Scope>> provider) {
|
||||
List<Object> cached = getCachedStoreFactory().computeIfCachedEntryAbsent(resourceServerId, key, (Function<String, List<Object>>) o -> {
|
||||
List<Scope> result = provider.get();
|
||||
|
||||
if (result.isEmpty()) {
|
||||
return Collections.emptyList();
|
||||
}
|
||||
|
||||
return result.stream().map(policy -> policy.getId()).collect(Collectors.toList());
|
||||
});
|
||||
|
||||
if (cached == null) {
|
||||
return Collections.emptyList();
|
||||
}
|
||||
|
||||
return cached.stream().map(id -> findById(id.toString(), resourceServerId)).collect(Collectors.toList());
|
||||
}
|
||||
|
||||
private Scope createAdapter(CachedScope cached) {
|
||||
|
@ -168,6 +179,8 @@ public class CachedScopeStore implements ScopeStore {
|
|||
|
||||
@Override
|
||||
public void setName(String name) {
|
||||
addInvalidation(getCacheKeyForScopeName(name));
|
||||
addInvalidation(getCacheKeyForScopeName(cached.getName()));
|
||||
getDelegateForUpdate().setName(name);
|
||||
cached.setName(name);
|
||||
}
|
||||
|
@ -192,13 +205,10 @@ public class CachedScopeStore implements ScopeStore {
|
|||
if (this.updated == null) {
|
||||
this.updated = getDelegate().findById(getId(), cached.getResourceServerId());
|
||||
if (this.updated == null) throw new IllegalStateException("Not found in database");
|
||||
transaction.whenCommit(() -> {
|
||||
invalidateCache(cached.getResourceServerId());
|
||||
});
|
||||
transaction.whenRollback(() -> {
|
||||
resolveResourceServerCache(cached.getResourceServerId()).remove(getCacheKeyForScope(cached.getId()));
|
||||
resolveResourceServerCache(cached.getResourceServerId()).remove(getCacheKeyForScopeName(cached.getName()));
|
||||
});
|
||||
addInvalidation(getCacheKeyForScope(updated.getId()));
|
||||
getCachedStoreFactory().getPolicyStore().addInvalidations(updated);
|
||||
getTransaction().whenCommit(() -> invalidate(cached.getResourceServerId()));
|
||||
getTransaction().whenRollback(() -> removeCachedEntry(cached.getResourceServerId(), getCacheKeyForScope(cached.getId())));
|
||||
}
|
||||
|
||||
return this.updated;
|
||||
|
@ -218,16 +228,4 @@ public class CachedScopeStore implements ScopeStore {
|
|||
}
|
||||
};
|
||||
}
|
||||
|
||||
private CachedStoreFactoryProvider getCachedStoreFactory() {
|
||||
return cacheStoreFactory;
|
||||
}
|
||||
|
||||
private void invalidateCache(String resourceServerId) {
|
||||
cache.remove(resourceServerId);
|
||||
}
|
||||
|
||||
private Map<String, List<CachedScope>> resolveResourceServerCache(String id) {
|
||||
return cache.computeIfAbsent(id, key -> new HashMap<>());
|
||||
}
|
||||
}
|
||||
|
|
|
@ -19,9 +19,12 @@
|
|||
package org.keycloak.models.authorization.infinispan;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.HashSet;
|
||||
import java.util.List;
|
||||
import java.util.Set;
|
||||
import java.util.function.Function;
|
||||
|
||||
import org.keycloak.authorization.store.PolicyStore;
|
||||
import org.keycloak.authorization.model.ResourceServer;
|
||||
import org.keycloak.authorization.store.ResourceServerStore;
|
||||
import org.keycloak.authorization.store.ResourceStore;
|
||||
import org.keycloak.authorization.store.ScopeStore;
|
||||
|
@ -39,16 +42,21 @@ public class InfinispanStoreFactoryProvider implements CachedStoreFactoryProvide
|
|||
private final CachedResourceStore resourceStore;
|
||||
private final CachedScopeStore scopeStore;
|
||||
private final CachedPolicyStore policyStore;
|
||||
private final KeycloakSession session;
|
||||
private final StoreFactoryCacheManager cacheManager;
|
||||
private ResourceServerStore resourceServerStore;
|
||||
private Set<String> invalidations = new HashSet<>();
|
||||
|
||||
public InfinispanStoreFactoryProvider(KeycloakSession session) {
|
||||
public InfinispanStoreFactoryProvider(KeycloakSession session, StoreFactoryCacheManager cacheManager) {
|
||||
this.session = session;
|
||||
this.cacheManager = cacheManager;
|
||||
this.transaction = new CacheTransaction();
|
||||
session.getTransactionManager().enlistAfterCompletion(transaction);
|
||||
StoreFactory delegate = session.getProvider(StoreFactory.class);
|
||||
resourceStore = new CachedResourceStore(session, this, this.transaction, delegate);
|
||||
resourceServerStore = new CachedResourceServerStore(session, this.transaction, delegate);
|
||||
scopeStore = new CachedScopeStore(session, this, this.transaction, delegate);
|
||||
policyStore = new CachedPolicyStore(session, this, this.transaction, delegate);
|
||||
resourceStore = new CachedResourceStore(this, delegate);
|
||||
resourceServerStore = new CachedResourceServerStore(this, delegate);
|
||||
scopeStore = new CachedScopeStore(this, delegate);
|
||||
policyStore = new CachedPolicyStore(this, delegate);
|
||||
}
|
||||
|
||||
@Override
|
||||
|
@ -67,7 +75,7 @@ public class InfinispanStoreFactoryProvider implements CachedStoreFactoryProvide
|
|||
}
|
||||
|
||||
@Override
|
||||
public PolicyStore getPolicyStore() {
|
||||
public CachedPolicyStore getPolicyStore() {
|
||||
return policyStore;
|
||||
}
|
||||
|
||||
|
@ -76,6 +84,42 @@ public class InfinispanStoreFactoryProvider implements CachedStoreFactoryProvide
|
|||
|
||||
}
|
||||
|
||||
void addInvalidation(String cacheKey) {
|
||||
invalidations.add(cacheKey);
|
||||
}
|
||||
|
||||
boolean isInvalid(String cacheKeyForPolicy) {
|
||||
return invalidations.contains(cacheKeyForPolicy);
|
||||
}
|
||||
|
||||
void invalidate(String resourceServerId) {
|
||||
cacheManager.invalidate(session, resourceServerId, invalidations);
|
||||
}
|
||||
|
||||
List<Object> resolveCachedEntry(String resourceServerId, String cacheKeyForPolicy) {
|
||||
return cacheManager.resolveResourceServerCache(resourceServerId).get(cacheKeyForPolicy);
|
||||
}
|
||||
|
||||
void putCacheEntry(String resourceServerId, String key, List<Object> entry) {
|
||||
cacheManager.resolveResourceServerCache(resourceServerId).put(key, entry);
|
||||
}
|
||||
|
||||
List<Object> computeIfCachedEntryAbsent(String resourceServerId, String key, Function<String, List<Object>> function) {
|
||||
return cacheManager.resolveResourceServerCache(resourceServerId).computeIfAbsent(key, function);
|
||||
}
|
||||
|
||||
CacheTransaction getTransaction() {
|
||||
return transaction;
|
||||
}
|
||||
|
||||
void removeCachedEntry(String id, String key) {
|
||||
cacheManager.resolveResourceServerCache(id).remove(key);
|
||||
}
|
||||
|
||||
void removeEntries(ResourceServer resourceServer) {
|
||||
cacheManager.removeAll(session, resourceServer);
|
||||
}
|
||||
|
||||
static class CacheTransaction implements KeycloakTransaction {
|
||||
|
||||
private List<Runnable> completeTasks = new ArrayList<>();
|
||||
|
|
|
@ -18,11 +18,16 @@
|
|||
|
||||
package org.keycloak.models.authorization.infinispan;
|
||||
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
|
||||
import org.infinispan.Cache;
|
||||
import org.keycloak.Config;
|
||||
import org.keycloak.authorization.AuthorizationProvider;
|
||||
import org.keycloak.authorization.store.StoreFactory;
|
||||
import org.keycloak.cluster.ClusterProvider;
|
||||
import org.keycloak.connections.infinispan.InfinispanConnectionProvider;
|
||||
import org.keycloak.models.KeycloakSession;
|
||||
import org.keycloak.models.KeycloakSessionFactory;
|
||||
import org.keycloak.models.authorization.infinispan.events.AuthorizationInvalidationEvent;
|
||||
import org.keycloak.models.cache.authorization.CachedStoreFactoryProvider;
|
||||
import org.keycloak.models.cache.authorization.CachedStoreProviderFactory;
|
||||
import org.keycloak.provider.EnvironmentDependentProviderFactory;
|
||||
|
@ -31,9 +36,12 @@ import org.keycloak.provider.EnvironmentDependentProviderFactory;
|
|||
* @author <a href="mailto:psilva@redhat.com">Pedro Igor</a>
|
||||
*/
|
||||
public class InfinispanStoreProviderFactory implements CachedStoreProviderFactory, EnvironmentDependentProviderFactory {
|
||||
|
||||
private StoreFactoryCacheManager cacheManager;
|
||||
|
||||
@Override
|
||||
public CachedStoreFactoryProvider create(KeycloakSession session) {
|
||||
return new InfinispanStoreFactoryProvider(session);
|
||||
return new InfinispanStoreFactoryProvider(session, cacheManager);
|
||||
}
|
||||
|
||||
@Override
|
||||
|
@ -43,7 +51,25 @@ public class InfinispanStoreProviderFactory implements CachedStoreProviderFactor
|
|||
|
||||
@Override
|
||||
public void postInit(KeycloakSessionFactory factory) {
|
||||
KeycloakSession session = factory.create();
|
||||
|
||||
try {
|
||||
InfinispanConnectionProvider provider = session.getProvider(InfinispanConnectionProvider.class);
|
||||
Cache<String, Map<String, List<Object>>> cache = provider.getCache(InfinispanConnectionProvider.AUTHORIZATION_CACHE_NAME);
|
||||
ClusterProvider clusterProvider = session.getProvider(ClusterProvider.class);
|
||||
|
||||
cacheManager = new StoreFactoryCacheManager(cache);
|
||||
|
||||
clusterProvider.registerListener(ClusterProvider.ALL, event -> {
|
||||
if (event instanceof AuthorizationInvalidationEvent) {
|
||||
cacheManager.invalidate(AuthorizationInvalidationEvent.class.cast(event));
|
||||
}
|
||||
});
|
||||
} finally {
|
||||
if (session != null) {
|
||||
session.close();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
|
|
|
@ -0,0 +1,72 @@
|
|||
/*
|
||||
* Copyright 2016 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.authorization.infinispan;
|
||||
|
||||
import java.util.HashMap;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
import java.util.Set;
|
||||
|
||||
import org.infinispan.Cache;
|
||||
import org.keycloak.authorization.model.ResourceServer;
|
||||
import org.keycloak.cluster.ClusterProvider;
|
||||
import org.keycloak.models.KeycloakSession;
|
||||
import org.keycloak.models.authorization.infinispan.events.AuthorizationInvalidationEvent;
|
||||
import org.keycloak.models.authorization.infinispan.events.ResourceServerRemovedEvent;
|
||||
|
||||
/**
|
||||
* @author <a href="mailto:psilva@redhat.com">Pedro Igor</a>
|
||||
*/
|
||||
public class StoreFactoryCacheManager {
|
||||
|
||||
private static final String AUTHORIZATION_UPDATE_TASK_KEY = "authorization-update";
|
||||
|
||||
private final Cache<String, Map<String, List<Object>>> cache;
|
||||
|
||||
StoreFactoryCacheManager(Cache<String, Map<String, List<Object>>> cache) {
|
||||
this.cache = cache;
|
||||
}
|
||||
|
||||
void invalidate(AuthorizationInvalidationEvent event) {
|
||||
if (event instanceof ResourceServerRemovedEvent) {
|
||||
cache.remove(event.getId());
|
||||
cache.remove(ResourceServerRemovedEvent.class.cast(event).getClientId());
|
||||
} else {
|
||||
Map<String, List<Object>> resolveResourceServerCache = resolveResourceServerCache(event.getId());
|
||||
|
||||
for (String key : event.getInvalidations()) {
|
||||
resolveResourceServerCache.remove(key);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public void invalidate(KeycloakSession session, String resourceServerId, Set<String> invalidations) {
|
||||
getClusterProvider(session).notify(AUTHORIZATION_UPDATE_TASK_KEY, new AuthorizationInvalidationEvent(resourceServerId, invalidations), false);
|
||||
}
|
||||
|
||||
public Map<String, List<Object>> resolveResourceServerCache(String id) {
|
||||
return cache.computeIfAbsent(id, key -> new HashMap<>());
|
||||
}
|
||||
|
||||
void removeAll(KeycloakSession session, ResourceServer id) {
|
||||
getClusterProvider(session).notify(AUTHORIZATION_UPDATE_TASK_KEY, new ResourceServerRemovedEvent(id.getId(), id.getClientId()), false);
|
||||
}
|
||||
|
||||
private ClusterProvider getClusterProvider(KeycloakSession session) {
|
||||
return session.getProvider(ClusterProvider.class);
|
||||
}
|
||||
}
|
|
@ -0,0 +1,44 @@
|
|||
/*
|
||||
* Copyright 2016 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.authorization.infinispan.events;
|
||||
|
||||
import java.util.Set;
|
||||
|
||||
import org.keycloak.models.cache.infinispan.events.InvalidationEvent;
|
||||
|
||||
/**
|
||||
* @author <a href="mailto:psilva@redhat.com">Pedro Igor</a>
|
||||
*/
|
||||
public class AuthorizationInvalidationEvent extends InvalidationEvent {
|
||||
|
||||
private final String resourceServerId;
|
||||
private Set<String> invalidations;
|
||||
|
||||
public AuthorizationInvalidationEvent(String resourceServerId, Set<String> invalidations) {
|
||||
this.resourceServerId = resourceServerId;
|
||||
this.invalidations = invalidations;
|
||||
}
|
||||
|
||||
public Set<String> getInvalidations() {
|
||||
return invalidations;
|
||||
}
|
||||
|
||||
@Override
|
||||
public String getId() {
|
||||
return resourceServerId;
|
||||
}
|
||||
}
|
|
@ -0,0 +1,36 @@
|
|||
/*
|
||||
* Copyright 2016 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.authorization.infinispan.events;
|
||||
|
||||
import java.util.Collections;
|
||||
|
||||
/**
|
||||
* @author <a href="mailto:psilva@redhat.com">Pedro Igor</a>
|
||||
*/
|
||||
public class ResourceServerRemovedEvent extends AuthorizationInvalidationEvent {
|
||||
|
||||
private final String clientId;
|
||||
|
||||
public ResourceServerRemovedEvent(String id, String clientId) {
|
||||
super(id, Collections.emptySet());
|
||||
this.clientId = clientId;
|
||||
}
|
||||
|
||||
public String getClientId() {
|
||||
return clientId;
|
||||
}
|
||||
}
|
|
@ -74,7 +74,7 @@ public class PolicyEntity implements Policy {
|
|||
@Column(name = "LOGIC")
|
||||
private Logic logic = Logic.POSITIVE;
|
||||
|
||||
@ElementCollection
|
||||
@ElementCollection(fetch = FetchType.LAZY)
|
||||
@MapKeyColumn(name="NAME")
|
||||
@Column(name="VALUE", columnDefinition = "TEXT")
|
||||
@CollectionTable(name="POLICY_CONFIG", joinColumns={ @JoinColumn(name="POLICY_ID") })
|
||||
|
@ -92,7 +92,7 @@ public class PolicyEntity implements Policy {
|
|||
@JoinTable(name = "RESOURCE_POLICY", joinColumns = @JoinColumn(name = "POLICY_ID"), inverseJoinColumns = @JoinColumn(name = "RESOURCE_ID"))
|
||||
private Set<ResourceEntity> resources = new HashSet<>();
|
||||
|
||||
@OneToMany(fetch = FetchType.EAGER, cascade = {})
|
||||
@OneToMany(fetch = FetchType.LAZY, cascade = {})
|
||||
@JoinTable(name = "SCOPE_POLICY", joinColumns = @JoinColumn(name = "POLICY_ID"), inverseJoinColumns = @JoinColumn(name = "SCOPE_ID"))
|
||||
private Set<ScopeEntity> scopes = new HashSet<>();
|
||||
|
||||
|
|
|
@ -128,13 +128,4 @@ public interface PolicyStore {
|
|||
* @return a list of policies that depends on the a policy with the given identifier
|
||||
*/
|
||||
List<Policy> findDependentPolicies(String id, String resourceServerId);
|
||||
|
||||
/**
|
||||
* Notify this store about changes to data associated with policies. E.g.: resources and scopes..
|
||||
*
|
||||
* TODO: need a better strategy to handle cross-references between stores, specially in cases where the store is caching data. Use some event-based solution here.
|
||||
*
|
||||
* @param cached
|
||||
*/
|
||||
default void notifyChange(Object cached) {}
|
||||
}
|
||||
|
|
|
@ -99,7 +99,9 @@
|
|||
<distributed-cache name="sessions" mode="SYNC" owners="1"/>
|
||||
<distributed-cache name="offlineSessions" mode="SYNC" owners="1"/>
|
||||
<distributed-cache name="loginFailures" mode="SYNC" owners="1"/>
|
||||
<distributed-cache name="authorization" mode="SYNC" owners="1"/>
|
||||
<local-cache name="authorization">
|
||||
<eviction max-entries="10000" strategy="LRU"/>
|
||||
</local-cache>
|
||||
<replicated-cache name="work" mode="SYNC" />
|
||||
<local-cache name="keys">
|
||||
<eviction max-entries="1000" strategy="LRU"/>
|
||||
|
|
|
@ -102,7 +102,9 @@
|
|||
<distributed-cache name="sessions" mode="SYNC" owners="1"/>
|
||||
<distributed-cache name="offlineSessions" mode="SYNC" owners="1"/>
|
||||
<distributed-cache name="loginFailures" mode="SYNC" owners="1"/>
|
||||
<distributed-cache name="authorization" mode="SYNC" owners="1"/>
|
||||
<local-cache name="authorization">
|
||||
<eviction max-entries="10000" strategy="LRU"/>
|
||||
</local-cache>
|
||||
<replicated-cache name="work" mode="SYNC" />
|
||||
<local-cache name="keys">
|
||||
<eviction max-entries="1000" strategy="LRU"/>
|
||||
|
|
Loading…
Reference in a new issue