From c291748f43bf3e0f079c714d86b4c50abc10ade5 Mon Sep 17 00:00:00 2001 From: Bill Burke Date: Thu, 18 May 2017 16:48:04 -0400 Subject: [PATCH] KEYCLOAK-4929 --- .../client/ClientPolicyProviderFactory.java | 8 +- .../provider/js/JSPolicyProviderFactory.java | 4 +- .../ResourcePolicyProviderFactory.java | 3 +- .../role/RolePolicyProviderFactory.java | 10 +- .../time/TimePolicyProviderFactory.java | 3 +- .../user/UserPolicyProviderFactory.java | 7 +- .../drools/DroolsPolicyProviderFactory.java | 16 +- ...ltInfinispanConnectionProviderFactory.java | 21 +- .../InfinispanConnectionProvider.java | 2 + .../infinispan/AbstractCachedStore.java | 73 -- .../infinispan/CachedPolicyStore.java | 500 ------------- .../infinispan/CachedResourceServerStore.java | 172 ----- .../infinispan/CachedResourceStore.java | 321 --------- .../infinispan/CachedScopeStore.java | 231 ------ .../InfinispanStoreFactoryProvider.java | 166 ----- .../InfinispanStoreProviderFactory.java | 89 --- .../infinispan/StoreFactoryCacheManager.java | 72 -- .../AuthorizationInvalidationEvent.java | 44 -- .../models/cache/infinispan/CacheManager.java | 2 +- ...ispanCacheStoreFactoryProviderFactory.java | 102 +++ .../authorization/PolicyAdapter.java | 280 ++++++++ .../authorization/ResourceAdapter.java | 184 +++++ .../authorization/ResourceServerAdapter.java | 127 ++++ .../authorization/ScopeAdapter.java | 127 ++++ .../StoreFactoryCacheManager.java | 93 +++ .../StoreFactoryCacheSession.java | 667 ++++++++++++++++++ .../authorization}/entities/CachedPolicy.java | 122 +--- .../entities/CachedResource.java | 61 +- .../entities/CachedResourceServer.java | 32 +- .../authorization}/entities/CachedScope.java | 49 +- .../entities/InResourceServer.java | 25 + .../entities/PolicyListQuery.java | 36 + .../entities/ResourceListQuery.java | 36 + .../entities/ResourceServerListQuery.java | 29 + .../entities/ScopeListQuery.java | 36 + .../AuthorizationCacheInvalidationEvent.java} | 23 +- .../events/PolicyRemovedEvent.java | 56 ++ .../events/PolicyUpdatedEvent.java | 56 ++ .../events/ResourceRemovedEvent.java | 56 ++ .../events/ResourceServerRemovedEvent.java | 54 ++ .../events/ResourceServerUpdatedEvent.java | 54 ++ .../events/ResourceUpdatedEvent.java | 56 ++ .../events/ScopeRemovedEvent.java | 56 ++ .../events/ScopeUpdatedEvent.java | 56 ++ .../stream/InResourceServerPredicate.java | 35 + ...e.authorization.CachedStoreProviderFactory | 2 +- .../jpa/entities/PolicyEntity.java | 98 +-- .../jpa/entities/ResourceEntity.java | 75 +- .../jpa/entities/ResourceServerEntity.java | 30 +- .../jpa/entities/ScopeEntity.java | 31 +- .../store/JPAAuthorizationStoreFactory.java | 5 +- .../jpa/store/JPAPolicyStore.java | 112 ++- .../jpa/store/JPAResourceServerStore.java | 52 +- .../jpa/store/JPAResourceStore.java | 114 +-- .../jpa/store/JPAScopeStore.java | 52 +- .../jpa/store/JPAStoreFactory.java | 11 +- .../jpa/store/PolicyAdapter.java | 235 ++++++ .../jpa/store/ResourceAdapter.java | 166 +++++ .../jpa/store/ResourceServerAdapter.java | 106 +++ .../authorization/jpa/store/ScopeAdapter.java | 103 +++ .../authorization/AuthorizationProvider.java | 32 +- .../authorization/model/CachedModel.java | 45 ++ .../keycloak/authorization/model/Policy.java | 14 +- .../authorization/model/Resource.java | 4 +- .../store/AuthorizationStoreFactory.java | 2 + .../authorization/store/ResourceStore.java | 2 +- .../ClientApplicationSynchronizer.java | 6 +- .../migration/migrators/MigrateTo2_1_0.java | 2 +- .../CachedStoreProviderFactory.java | 3 + .../models/utils/RepresentationToModel.java | 6 +- .../DefaultAuthorizationProviderFactory.java | 6 +- .../AuthorizationTokenService.java | 4 + .../entitlement/EntitlementService.java | 5 + .../authorization/util/Permissions.java | 8 +- .../authz/ConflictingScopePermissionTest.java | 2 +- .../keycloak-infinispan.xml | 2 +- 76 files changed, 3340 insertions(+), 2247 deletions(-) delete mode 100644 model/infinispan/src/main/java/org/keycloak/models/authorization/infinispan/AbstractCachedStore.java delete mode 100644 model/infinispan/src/main/java/org/keycloak/models/authorization/infinispan/CachedPolicyStore.java delete mode 100644 model/infinispan/src/main/java/org/keycloak/models/authorization/infinispan/CachedResourceServerStore.java delete mode 100644 model/infinispan/src/main/java/org/keycloak/models/authorization/infinispan/CachedResourceStore.java delete mode 100644 model/infinispan/src/main/java/org/keycloak/models/authorization/infinispan/CachedScopeStore.java delete mode 100644 model/infinispan/src/main/java/org/keycloak/models/authorization/infinispan/InfinispanStoreFactoryProvider.java delete mode 100644 model/infinispan/src/main/java/org/keycloak/models/authorization/infinispan/InfinispanStoreProviderFactory.java delete mode 100644 model/infinispan/src/main/java/org/keycloak/models/authorization/infinispan/StoreFactoryCacheManager.java delete mode 100644 model/infinispan/src/main/java/org/keycloak/models/authorization/infinispan/events/AuthorizationInvalidationEvent.java create mode 100755 model/infinispan/src/main/java/org/keycloak/models/cache/infinispan/authorization/InfinispanCacheStoreFactoryProviderFactory.java create mode 100644 model/infinispan/src/main/java/org/keycloak/models/cache/infinispan/authorization/PolicyAdapter.java create mode 100644 model/infinispan/src/main/java/org/keycloak/models/cache/infinispan/authorization/ResourceAdapter.java create mode 100644 model/infinispan/src/main/java/org/keycloak/models/cache/infinispan/authorization/ResourceServerAdapter.java create mode 100644 model/infinispan/src/main/java/org/keycloak/models/cache/infinispan/authorization/ScopeAdapter.java create mode 100644 model/infinispan/src/main/java/org/keycloak/models/cache/infinispan/authorization/StoreFactoryCacheManager.java create mode 100644 model/infinispan/src/main/java/org/keycloak/models/cache/infinispan/authorization/StoreFactoryCacheSession.java rename model/infinispan/src/main/java/org/keycloak/models/{authorization/infinispan => cache/infinispan/authorization}/entities/CachedPolicy.java (53%) rename model/infinispan/src/main/java/org/keycloak/models/{authorization/infinispan => cache/infinispan/authorization}/entities/CachedResource.java (61%) rename model/infinispan/src/main/java/org/keycloak/models/{authorization/infinispan => cache/infinispan/authorization}/entities/CachedResourceServer.java (64%) rename model/infinispan/src/main/java/org/keycloak/models/{authorization/infinispan => cache/infinispan/authorization}/entities/CachedScope.java (56%) create mode 100644 model/infinispan/src/main/java/org/keycloak/models/cache/infinispan/authorization/entities/InResourceServer.java create mode 100755 model/infinispan/src/main/java/org/keycloak/models/cache/infinispan/authorization/entities/PolicyListQuery.java create mode 100755 model/infinispan/src/main/java/org/keycloak/models/cache/infinispan/authorization/entities/ResourceListQuery.java create mode 100755 model/infinispan/src/main/java/org/keycloak/models/cache/infinispan/authorization/entities/ResourceServerListQuery.java create mode 100755 model/infinispan/src/main/java/org/keycloak/models/cache/infinispan/authorization/entities/ScopeListQuery.java rename model/infinispan/src/main/java/org/keycloak/models/{authorization/infinispan/events/ResourceServerRemovedEvent.java => cache/infinispan/authorization/events/AuthorizationCacheInvalidationEvent.java} (58%) create mode 100644 model/infinispan/src/main/java/org/keycloak/models/cache/infinispan/authorization/events/PolicyRemovedEvent.java create mode 100644 model/infinispan/src/main/java/org/keycloak/models/cache/infinispan/authorization/events/PolicyUpdatedEvent.java create mode 100644 model/infinispan/src/main/java/org/keycloak/models/cache/infinispan/authorization/events/ResourceRemovedEvent.java create mode 100644 model/infinispan/src/main/java/org/keycloak/models/cache/infinispan/authorization/events/ResourceServerRemovedEvent.java create mode 100644 model/infinispan/src/main/java/org/keycloak/models/cache/infinispan/authorization/events/ResourceServerUpdatedEvent.java create mode 100644 model/infinispan/src/main/java/org/keycloak/models/cache/infinispan/authorization/events/ResourceUpdatedEvent.java create mode 100644 model/infinispan/src/main/java/org/keycloak/models/cache/infinispan/authorization/events/ScopeRemovedEvent.java create mode 100644 model/infinispan/src/main/java/org/keycloak/models/cache/infinispan/authorization/events/ScopeUpdatedEvent.java create mode 100755 model/infinispan/src/main/java/org/keycloak/models/cache/infinispan/authorization/stream/InResourceServerPredicate.java create mode 100644 model/jpa/src/main/java/org/keycloak/authorization/jpa/store/PolicyAdapter.java create mode 100644 model/jpa/src/main/java/org/keycloak/authorization/jpa/store/ResourceAdapter.java create mode 100644 model/jpa/src/main/java/org/keycloak/authorization/jpa/store/ResourceServerAdapter.java create mode 100644 model/jpa/src/main/java/org/keycloak/authorization/jpa/store/ScopeAdapter.java create mode 100644 server-spi-private/src/main/java/org/keycloak/authorization/model/CachedModel.java diff --git a/authz/policy/common/src/main/java/org/keycloak/authorization/policy/provider/client/ClientPolicyProviderFactory.java b/authz/policy/common/src/main/java/org/keycloak/authorization/policy/provider/client/ClientPolicyProviderFactory.java index d061357218..49a54ebe28 100644 --- a/authz/policy/common/src/main/java/org/keycloak/authorization/policy/provider/client/ClientPolicyProviderFactory.java +++ b/authz/policy/common/src/main/java/org/keycloak/authorization/policy/provider/client/ClientPolicyProviderFactory.java @@ -107,7 +107,7 @@ public class ClientPolicyProviderFactory implements PolicyProviderFactory config = policy.getConfig(); - - config.put("clients", JsonSerialization.writeValueAsString(updatedClients)); - - policy.setConfig(config); + policy.putConfig("clients", JsonSerialization.writeValueAsString(updatedClients)); } catch (IOException cause) { throw new RuntimeException("Failed to serialize clients", cause); } diff --git a/authz/policy/common/src/main/java/org/keycloak/authorization/policy/provider/js/JSPolicyProviderFactory.java b/authz/policy/common/src/main/java/org/keycloak/authorization/policy/provider/js/JSPolicyProviderFactory.java index 1a1ed34ecc..3e68d7f603 100644 --- a/authz/policy/common/src/main/java/org/keycloak/authorization/policy/provider/js/JSPolicyProviderFactory.java +++ b/authz/policy/common/src/main/java/org/keycloak/authorization/policy/provider/js/JSPolicyProviderFactory.java @@ -70,9 +70,7 @@ public class JSPolicyProviderFactory implements PolicyProviderFactory config = policy.getConfig(); - config.put("code", code); - policy.setConfig(config); + policy.putConfig("code", code); } @Override diff --git a/authz/policy/common/src/main/java/org/keycloak/authorization/policy/provider/resource/ResourcePolicyProviderFactory.java b/authz/policy/common/src/main/java/org/keycloak/authorization/policy/provider/resource/ResourcePolicyProviderFactory.java index 6ea7230f58..1de28f53e9 100644 --- a/authz/policy/common/src/main/java/org/keycloak/authorization/policy/provider/resource/ResourcePolicyProviderFactory.java +++ b/authz/policy/common/src/main/java/org/keycloak/authorization/policy/provider/resource/ResourcePolicyProviderFactory.java @@ -1,5 +1,6 @@ package org.keycloak.authorization.policy.provider.resource; +import java.util.HashMap; import java.util.Map; import org.keycloak.Config; @@ -64,7 +65,7 @@ public class ResourcePolicyProviderFactory implements PolicyProviderFactory config = policy.getConfig(); + Map config = new HashMap(policy.getConfig()); config.compute("defaultResourceType", (key, value) -> { String resourceType = resourcePermission.getResourceType(); diff --git a/authz/policy/common/src/main/java/org/keycloak/authorization/policy/provider/role/RolePolicyProviderFactory.java b/authz/policy/common/src/main/java/org/keycloak/authorization/policy/provider/role/RolePolicyProviderFactory.java index 03ea156798..64bcf4956e 100644 --- a/authz/policy/common/src/main/java/org/keycloak/authorization/policy/provider/role/RolePolicyProviderFactory.java +++ b/authz/policy/common/src/main/java/org/keycloak/authorization/policy/provider/role/RolePolicyProviderFactory.java @@ -163,11 +163,7 @@ public class RolePolicyProviderFactory implements PolicyProviderFactory config = policy.getConfig(); - - config.put("roles", JsonSerialization.writeValueAsString(updatedRoles)); - - policy.setConfig(config); + policy.putConfig("roles", JsonSerialization.writeValueAsString(updatedRoles)); } catch (IOException cause) { throw new RuntimeException("Failed to serialize roles", cause); } @@ -224,9 +220,7 @@ public class RolePolicyProviderFactory implements PolicyProviderFactory config = policy.getConfig(); - config.put("roles", JsonSerialization.writeValueAsString(roles)); - policy.setConfig(config); + policy.putConfig("roles", JsonSerialization.writeValueAsString(roles)); } } catch (IOException e) { throw new RuntimeException("Error while synchronizing roles with policy [" + policy.getName() + "].", e); diff --git a/authz/policy/common/src/main/java/org/keycloak/authorization/policy/provider/time/TimePolicyProviderFactory.java b/authz/policy/common/src/main/java/org/keycloak/authorization/policy/provider/time/TimePolicyProviderFactory.java index a3958b9202..fc69f3ba70 100644 --- a/authz/policy/common/src/main/java/org/keycloak/authorization/policy/provider/time/TimePolicyProviderFactory.java +++ b/authz/policy/common/src/main/java/org/keycloak/authorization/policy/provider/time/TimePolicyProviderFactory.java @@ -1,6 +1,7 @@ package org.keycloak.authorization.policy.provider.time; import java.text.SimpleDateFormat; +import java.util.HashMap; import java.util.Map; import org.keycloak.Config; @@ -118,7 +119,7 @@ public class TimePolicyProviderFactory implements PolicyProviderFactory config = policy.getConfig(); + Map config = new HashMap(policy.getConfig()); config.compute("nbf", (s, s2) -> nbf != null ? nbf : null); config.compute("noa", (s, s2) -> noa != null ? noa : null); diff --git a/authz/policy/common/src/main/java/org/keycloak/authorization/policy/provider/user/UserPolicyProviderFactory.java b/authz/policy/common/src/main/java/org/keycloak/authorization/policy/provider/user/UserPolicyProviderFactory.java index e4bbaf9900..a21bf74bf0 100644 --- a/authz/policy/common/src/main/java/org/keycloak/authorization/policy/provider/user/UserPolicyProviderFactory.java +++ b/authz/policy/common/src/main/java/org/keycloak/authorization/policy/provider/user/UserPolicyProviderFactory.java @@ -138,11 +138,8 @@ public class UserPolicyProviderFactory implements PolicyProviderFactory config = policy.getConfig(); - config.put("users", JsonSerialization.writeValueAsString(updatedUsers)); - - policy.setConfig(config); + policy.putConfig("users", JsonSerialization.writeValueAsString(updatedUsers)); } catch (IOException cause) { throw new RuntimeException("Failed to serialize users", cause); } @@ -181,7 +178,7 @@ public class UserPolicyProviderFactory implements PolicyProviderFactory config = policy.getConfig(); - config.put("mavenArtifactGroupId", representation.getArtifactGroupId()); - config.put("mavenArtifactId", representation.getArtifactId()); - config.put("mavenArtifactVersion", representation.getArtifactVersion()); - config.put("scannerPeriod", representation.getScannerPeriod()); - config.put("scannerPeriodUnit", representation.getScannerPeriodUnit()); - config.put("sessionName", representation.getSessionName()); - config.put("moduleName", representation.getModuleName()); + policy.putConfig("mavenArtifactGroupId", representation.getArtifactGroupId()); + policy.putConfig("mavenArtifactId", representation.getArtifactId()); + policy.putConfig("mavenArtifactVersion", representation.getArtifactVersion()); + policy.putConfig("scannerPeriod", representation.getScannerPeriod()); + policy.putConfig("scannerPeriodUnit", representation.getScannerPeriodUnit()); + policy.putConfig("sessionName", representation.getSessionName()); + policy.putConfig("moduleName", representation.getModuleName()); - policy.setConfig(config); } void update(Policy policy) { diff --git a/model/infinispan/src/main/java/org/keycloak/connections/infinispan/DefaultInfinispanConnectionProviderFactory.java b/model/infinispan/src/main/java/org/keycloak/connections/infinispan/DefaultInfinispanConnectionProviderFactory.java index 916db65cc3..5309fc9e70 100755 --- a/model/infinispan/src/main/java/org/keycloak/connections/infinispan/DefaultInfinispanConnectionProviderFactory.java +++ b/model/infinispan/src/main/java/org/keycloak/connections/infinispan/DefaultInfinispanConnectionProviderFactory.java @@ -118,9 +118,18 @@ public class DefaultInfinispanConnectionProviderFactory implements InfinispanCon cacheManager.defineConfiguration(InfinispanConnectionProvider.USER_REVISIONS_CACHE_NAME, getRevisionCacheConfig(userRevisionsMaxEntries)); cacheManager.getCache(InfinispanConnectionProvider.USER_REVISIONS_CACHE_NAME, true); - cacheManager.getCache(InfinispanConnectionProvider.AUTHORIZATION_CACHE_NAME, true); cacheManager.getCache(InfinispanConnectionProvider.KEYS_CACHE_NAME, true); + long authzRevisionsMaxEntries = cacheManager.getCache(InfinispanConnectionProvider.AUTHORIZATION_CACHE_NAME).getCacheConfiguration().eviction().maxEntries(); + authzRevisionsMaxEntries = authzRevisionsMaxEntries > 0 + ? 2 * authzRevisionsMaxEntries + : InfinispanConnectionProvider.AUTHORIZATION_REVISIONS_CACHE_DEFAULT_MAX; + + cacheManager.defineConfiguration(InfinispanConnectionProvider.AUTHORIZATION_REVISIONS_CACHE_NAME, getRevisionCacheConfig(authzRevisionsMaxEntries)); + cacheManager.getCache(InfinispanConnectionProvider.AUTHORIZATION_REVISIONS_CACHE_NAME, true); + + + logger.debugv("Using container managed Infinispan cache container, lookup={1}", cacheContainerLookup); } catch (Exception e) { throw new RuntimeException("Failed to retrieve cache container", e); @@ -151,6 +160,7 @@ public class DefaultInfinispanConnectionProviderFactory implements InfinispanCon Configuration modelCacheConfiguration = modelCacheConfigBuilder.build(); cacheManager.defineConfiguration(InfinispanConnectionProvider.REALM_CACHE_NAME, modelCacheConfiguration); + cacheManager.defineConfiguration(InfinispanConnectionProvider.AUTHORIZATION_CACHE_NAME, modelCacheConfiguration); cacheManager.defineConfiguration(InfinispanConnectionProvider.USER_CACHE_NAME, modelCacheConfiguration); ConfigurationBuilder sessionConfigBuilder = new ConfigurationBuilder(); @@ -180,7 +190,6 @@ public class DefaultInfinispanConnectionProviderFactory implements InfinispanCon cacheManager.defineConfiguration(InfinispanConnectionProvider.SESSION_CACHE_NAME, sessionCacheConfiguration); cacheManager.defineConfiguration(InfinispanConnectionProvider.OFFLINE_SESSION_CACHE_NAME, sessionCacheConfiguration); cacheManager.defineConfiguration(InfinispanConnectionProvider.LOGIN_FAILURE_CACHE_NAME, sessionCacheConfiguration); - cacheManager.defineConfiguration(InfinispanConnectionProvider.AUTHORIZATION_CACHE_NAME, sessionCacheConfiguration); ConfigurationBuilder replicationConfigBuilder = new ConfigurationBuilder(); if (clustered) { @@ -219,6 +228,14 @@ public class DefaultInfinispanConnectionProviderFactory implements InfinispanCon cacheManager.defineConfiguration(InfinispanConnectionProvider.KEYS_CACHE_NAME, getKeysCacheConfig()); cacheManager.getCache(InfinispanConnectionProvider.KEYS_CACHE_NAME, true); + + long authzRevisionsMaxEntries = cacheManager.getCache(InfinispanConnectionProvider.AUTHORIZATION_CACHE_NAME).getCacheConfiguration().eviction().maxEntries(); + authzRevisionsMaxEntries = authzRevisionsMaxEntries > 0 + ? 2 * authzRevisionsMaxEntries + : InfinispanConnectionProvider.AUTHORIZATION_REVISIONS_CACHE_DEFAULT_MAX; + + cacheManager.defineConfiguration(InfinispanConnectionProvider.AUTHORIZATION_REVISIONS_CACHE_NAME, getRevisionCacheConfig(authzRevisionsMaxEntries)); + cacheManager.getCache(InfinispanConnectionProvider.AUTHORIZATION_REVISIONS_CACHE_NAME, true); } private Configuration getRevisionCacheConfig(long maxEntries) { diff --git a/model/infinispan/src/main/java/org/keycloak/connections/infinispan/InfinispanConnectionProvider.java b/model/infinispan/src/main/java/org/keycloak/connections/infinispan/InfinispanConnectionProvider.java index 7c255fd0b8..bd57793dfb 100755 --- a/model/infinispan/src/main/java/org/keycloak/connections/infinispan/InfinispanConnectionProvider.java +++ b/model/infinispan/src/main/java/org/keycloak/connections/infinispan/InfinispanConnectionProvider.java @@ -38,6 +38,8 @@ public interface InfinispanConnectionProvider extends Provider { String LOGIN_FAILURE_CACHE_NAME = "loginFailures"; String WORK_CACHE_NAME = "work"; String AUTHORIZATION_CACHE_NAME = "authorization"; + String AUTHORIZATION_REVISIONS_CACHE_NAME = "authorizationRevisions"; + int AUTHORIZATION_REVISIONS_CACHE_DEFAULT_MAX = 20000; String KEYS_CACHE_NAME = "keys"; int KEYS_CACHE_DEFAULT_MAX = 1000; diff --git a/model/infinispan/src/main/java/org/keycloak/models/authorization/infinispan/AbstractCachedStore.java b/model/infinispan/src/main/java/org/keycloak/models/authorization/infinispan/AbstractCachedStore.java deleted file mode 100644 index 90fe8b636c..0000000000 --- a/model/infinispan/src/main/java/org/keycloak/models/authorization/infinispan/AbstractCachedStore.java +++ /dev/null @@ -1,73 +0,0 @@ -/* - * 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 Pedro Igor - */ -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 putCacheEntry(String resourceServerId, String cacheKeyForPolicy, E cachedPolicy) { - cacheStoreFactory.putCacheEntry(resourceServerId, cacheKeyForPolicy, Arrays.asList(cachedPolicy)); - return cachedPolicy; - } - - protected List 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; - } -} diff --git a/model/infinispan/src/main/java/org/keycloak/models/authorization/infinispan/CachedPolicyStore.java b/model/infinispan/src/main/java/org/keycloak/models/authorization/infinispan/CachedPolicyStore.java deleted file mode 100644 index 73284d705c..0000000000 --- a/model/infinispan/src/main/java/org/keycloak/models/authorization/infinispan/CachedPolicyStore.java +++ /dev/null @@ -1,500 +0,0 @@ -/* - * JBoss, Home of Professional Open Source. - * Copyright 2016 Red Hat, Inc., and individual 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.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.Function; -import java.util.function.Supplier; -import java.util.stream.Collectors; - -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.models.authorization.infinispan.entities.CachedPolicy; -import org.keycloak.representations.idm.authorization.AbstractPolicyRepresentation; -import org.keycloak.representations.idm.authorization.DecisionStrategy; -import org.keycloak.representations.idm.authorization.Logic; - -/** - * @author Pedro Igor - */ -public class CachedPolicyStore extends AbstractCachedStore implements PolicyStore { - - private static final String POLICY_CACHE_PREFIX = "pc-"; - - private PolicyStore delegate; - - public CachedPolicyStore(InfinispanStoreFactoryProvider cacheStoreFactory, StoreFactory storeFactory) { - super(cacheStoreFactory, storeFactory); - this.delegate = storeFactory.getPolicyStore(); - } - - @Override - public Policy create(AbstractPolicyRepresentation representation, ResourceServer resourceServer) { - Policy policy = getDelegate().create(representation, getStoreFactory().getResourceServerStore().findById(resourceServer.getId())); - String id = policy.getId(); - - addInvalidation(getCacheKeyForPolicy(policy.getId())); - addInvalidation(getCacheKeyForPolicyName(policy.getName())); - addInvalidation(getCacheKeyForPolicyType(policy.getType())); - - configureTransaction(resourceServer, id); - - return createAdapter(new CachedPolicy(policy)); - } - - @Override - public void delete(String id) { - Policy policy = getDelegate().findById(id, null); - if (policy == null) { - return; - } - - addInvalidation(getCacheKeyForPolicy(policy.getId())); - addInvalidation(getCacheKeyForPolicyName(policy.getName())); - addInvalidation(getCacheKeyForPolicyType(policy.getType())); - - getDelegate().delete(id); - configureTransaction(policy.getResourceServer(), policy.getId()); - } - - @Override - public Policy findById(String id, String resourceServerId) { - if (resourceServerId == null) { - return getDelegate().findById(id, null); - } - - if (isInvalid(getCacheKeyForPolicy(id))) { - return getDelegate().findById(id, resourceServerId); - } - - String cacheKeyForPolicy = getCacheKeyForPolicy(id); - List cached = resolveCacheEntry(resourceServerId, cacheKeyForPolicy); - - if (cached == null) { - Policy policy = getDelegate().findById(id, resourceServerId); - - if (policy != null) { - return createAdapter(putCacheEntry(resourceServerId, cacheKeyForPolicy, new CachedPolicy(policy))); - } - - return null; - } - - return createAdapter(CachedPolicy.class.cast(cached.get(0))); - } - - @Override - public Policy findByName(String name, String 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 - public List findByResourceServer(String resourceServerId) { - return getDelegate().findByResourceServer(resourceServerId); - } - - @Override - public List findByResourceServer(Map attributes, String resourceServerId, int firstResult, int maxResult) { - return getDelegate().findByResourceServer(attributes, resourceServerId, firstResult, maxResult); - } - - @Override - public List findByResource(String resourceId, String resourceServerId) { - String cacheKey = getCacheKeyForResource(resourceId); - - if (isInvalid(cacheKey)) { - return getDelegate().findByResource(resourceId, resourceServerId); - } - - return cacheResult(resourceServerId, cacheKey, () -> getDelegate().findByResource(resourceId, resourceServerId)); - } - - @Override - public List findByResourceType(String resourceType, String resourceServerId) { - String cacheKey = getCacheKeyForResourceType(resourceType); - - if (isInvalid(cacheKey)) { - return getDelegate().findByResourceType(resourceType, resourceServerId); - } - - return cacheResult(resourceServerId, cacheKey, () -> getDelegate().findByResourceType(resourceType, resourceServerId)); - } - - @Override - public List findByScopeIds(List scopeIds, String resourceServerId) { - List policies = new ArrayList<>(); - - for (String scopeId : scopeIds) { - 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; - } - - @Override - public List findByType(String type, String resourceServerId) { - String cacheKey = getCacheKeyForPolicyType(type); - - if (isInvalid(cacheKey)) { - return getDelegate().findByType(type, resourceServerId); - } - - return cacheResult(resourceServerId, cacheKey, () -> getDelegate().findByType(type, resourceServerId)); - } - - @Override - public List findDependentPolicies(String id, String resourceServerId) { - return getDelegate().findDependentPolicies(id, resourceServerId); - } - - private String getCacheKeyForPolicy(String id) { - return new StringBuilder().append(POLICY_CACHE_PREFIX).append("id-").append(id).toString(); - } - - private String getCacheKeyForPolicyType(String type) { - return new StringBuilder().append(POLICY_CACHE_PREFIX).append("findByType-").append(type).toString(); - } - - private String getCacheKeyForPolicyName(String name) { - return new StringBuilder().append(POLICY_CACHE_PREFIX).append("findByName-").append(name).toString(); - } - - private String getCacheKeyForResourceType(String resourceType) { - return new StringBuilder().append(POLICY_CACHE_PREFIX).append("findByResourceType-").append(resourceType).toString(); - } - - private String getCacheForScope(String scopeId) { - return new StringBuilder().append(POLICY_CACHE_PREFIX).append("findByScopeIds-").append(scopeId).toString(); - } - - private String getCacheKeyForResource(String resourceId) { - return new StringBuilder().append(POLICY_CACHE_PREFIX).append("findByResource-").append(resourceId).toString(); - } - - private Policy createAdapter(CachedPolicy cached) { - return new Policy() { - - private Set scopes; - private Set resources; - private Set associatedPolicies; - private Policy updated; - - @Override - public String getId() { - return cached.getId(); - } - - @Override - public String getType() { - return cached.getType(); - } - - @Override - public DecisionStrategy getDecisionStrategy() { - return cached.getDecisionStrategy(); - } - - @Override - public void setDecisionStrategy(DecisionStrategy decisionStrategy) { - getDelegateForUpdate().setDecisionStrategy(decisionStrategy); - cached.setDecisionStrategy(decisionStrategy); - } - - @Override - public Logic getLogic() { - return cached.getLogic(); - } - - @Override - public void setLogic(Logic logic) { - getDelegateForUpdate().setLogic(logic); - cached.setLogic(logic); - } - - @Override - public Map getConfig() { - return new HashMap<>(cached.getConfig()); - } - - @Override - public void setConfig(Map 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); - } - - @Override - public String getName() { - return cached.getName(); - } - - @Override - public void setName(String name) { - addInvalidation(getCacheKeyForPolicyName(name)); - addInvalidation(getCacheKeyForPolicyName(cached.getName())); - getDelegateForUpdate().setName(name); - cached.setName(name); - } - - @Override - public String getDescription() { - return cached.getDescription(); - } - - @Override - public void setDescription(String description) { - getDelegateForUpdate().setDescription(description); - cached.setDescription(description); - } - - @Override - public ResourceServer getResourceServer() { - return getCachedStoreFactory().getResourceServerStore().findById(cached.getResourceServerId()); - } - - @Override - public void addScope(Scope scope) { - 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) { - Scope model = getStoreFactory().getScopeStore().findById(scope.getId(), cached.getResourceServerId()); - addInvalidation(getCacheForScope(scope.getId())); - getDelegateForUpdate().removeScope(model); - cached.removeScope(scope); - scopes.remove(scope); - } - - @Override - public void addAssociatedPolicy(Policy associatedPolicy) { - getDelegateForUpdate().addAssociatedPolicy(getStoreFactory().getPolicyStore().findById(associatedPolicy.getId(), cached.getResourceServerId())); - cached.addAssociatedPolicy(associatedPolicy); - } - - @Override - public void removeAssociatedPolicy(Policy associatedPolicy) { - getDelegateForUpdate().removeAssociatedPolicy(getStoreFactory().getPolicyStore().findById(associatedPolicy.getId(), cached.getResourceServerId())); - cached.removeAssociatedPolicy(associatedPolicy); - associatedPolicies.remove(associatedPolicy); - } - - @Override - public void addResource(Resource resource) { - 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) { - 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 getAssociatedPolicies() { - if (associatedPolicies == null || updated != null) { - associatedPolicies = new HashSet<>(); - - for (String id : cached.getAssociatedPoliciesIds()) { - Policy policy = findById(id, cached.getResourceServerId()); - - if (policy != null) { - associatedPolicies.add(policy); - } - } - } - - return associatedPolicies; - } - - @Override - public Set getResources() { - if (resources == null || updated != null) { - resources = new HashSet<>(); - - for (String id : cached.getResourcesIds()) { - Resource resource = getCachedStoreFactory().getResourceStore().findById(id, cached.getResourceServerId()); - - if (resource != null) { - resources.add(resource); - } - } - } - - return resources; - } - - @Override - public Set getScopes() { - if (scopes == null || updated != null) { - scopes = new HashSet<>(); - - for (String id : cached.getScopesIds()) { - Scope scope = getCachedStoreFactory().getScopeStore().findById(id, cached.getResourceServerId()); - - if (scope != null) { - scopes.add(scope); - } - } - } - - return scopes; - } - - @Override - public boolean equals(Object o) { - if (o == this) return true; - - if (getId() == null) return false; - - if (!Policy.class.isInstance(o)) return false; - - Policy that = (Policy) o; - - if (!getId().equals(that.getId())) return false; - - return true; - - } - - @Override - public int hashCode() { - return getId()!=null ? getId().hashCode() : super.hashCode(); - } - - private Policy getDelegateForUpdate() { - if (this.updated == null) { - this.updated = getDelegate().findById(getId(), cached.getResourceServerId()); - if (this.updated == null) throw new IllegalStateException("Not found in database"); - addInvalidation(getCacheKeyForPolicy(updated.getId())); - configureTransaction(updated.getResourceServer(), updated.getId()); - } - - return this.updated; - } - }; - } - - private List cacheResult(String resourceServerId, String key, Supplier> provider) { - List cached = getCachedStoreFactory().computeIfCachedEntryAbsent(resourceServerId, key, (Function>) o -> { - List 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 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 + "]"); - } - } -} \ No newline at end of file diff --git a/model/infinispan/src/main/java/org/keycloak/models/authorization/infinispan/CachedResourceServerStore.java b/model/infinispan/src/main/java/org/keycloak/models/authorization/infinispan/CachedResourceServerStore.java deleted file mode 100644 index 6322843fb5..0000000000 --- a/model/infinispan/src/main/java/org/keycloak/models/authorization/infinispan/CachedResourceServerStore.java +++ /dev/null @@ -1,172 +0,0 @@ -/* - * JBoss, Home of Professional Open Source. - * Copyright 2016 Red Hat, Inc., and individual 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.List; - -import org.keycloak.authorization.model.ResourceServer; -import org.keycloak.authorization.store.ResourceServerStore; -import org.keycloak.authorization.store.StoreFactory; -import org.keycloak.models.authorization.infinispan.entities.CachedResourceServer; -import org.keycloak.representations.idm.authorization.PolicyEnforcementMode; - -/** - * @author Pedro Igor - */ -public class CachedResourceServerStore extends AbstractCachedStore implements ResourceServerStore { - - private static final String RS_PREFIX = "rs-"; - - private final ResourceServerStore 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); - - getTransaction().whenCommit(() -> getCachedStoreFactory().removeEntries(resourceServer)); - getTransaction().whenRollback(() -> removeCachedEntry(resourceServer.getId(), getCacheKeyForResourceServer(resourceServer.getId()))); - - return createAdapter(new CachedResourceServer(resourceServer)); - } - - @Override - public void delete(String id) { - ResourceServer resourceServer = getDelegate().findById(id); - - if (resourceServer != null) { - getDelegate().delete(id); - getTransaction().whenCommit(() -> getCachedStoreFactory().removeEntries(resourceServer)); - } - } - - @Override - public ResourceServer findById(String id) { - String cacheKey = getCacheKeyForResourceServer(id); - - if (isInvalid(cacheKey)) { - return getDelegate().findById(id); - } - - List cached = resolveCacheEntry(id, cacheKey); - - if (cached == null) { - ResourceServer resourceServer = getDelegate().findById(id); - - if (resourceServer != null) { - return createAdapter(putCacheEntry(id, cacheKey, new CachedResourceServer(resourceServer))); - } - - return null; - } - - return createAdapter(CachedResourceServer.class.cast(cached.get(0))); - } - - @Override - public ResourceServer findByClient(String id) { - String cacheKey = getCacheKeyForResourceServerClientId(id); - - if (isInvalid(cacheKey)) { - return getDelegate().findByClient(id); - } - - List cached = resolveCacheEntry(id, cacheKey); - - if (cached == null) { - ResourceServer resourceServer = getDelegate().findByClient(id); - - if (resourceServer != null) { - return findById(putCacheEntry(id, cacheKey, resourceServer.getId())); - } - - return null; - } - - return findById(cached.get(0).toString()); - } - - private String getCacheKeyForResourceServer(String id) { - return new StringBuilder(RS_PREFIX).append("id-").append(id).toString(); - } - - private String getCacheKeyForResourceServerClientId(String id) { - return new StringBuilder(RS_PREFIX).append("findByClientId-").append(id).toString(); - } - - private ResourceServerStore getDelegate() { - return this.delegate; - } - - private ResourceServer createAdapter(ResourceServer cached) { - return new ResourceServer() { - - private ResourceServer updated; - - @Override - public String getId() { - return cached.getId(); - } - - @Override - public String getClientId() { - return cached.getClientId(); - } - - @Override - public boolean isAllowRemoteResourceManagement() { - return cached.isAllowRemoteResourceManagement(); - } - - @Override - public void setAllowRemoteResourceManagement(boolean allowRemoteResourceManagement) { - getDelegateForUpdate().setAllowRemoteResourceManagement(allowRemoteResourceManagement); - cached.setAllowRemoteResourceManagement(allowRemoteResourceManagement); - } - - @Override - public PolicyEnforcementMode getPolicyEnforcementMode() { - return cached.getPolicyEnforcementMode(); - } - - @Override - public void setPolicyEnforcementMode(PolicyEnforcementMode enforcementMode) { - getDelegateForUpdate().setPolicyEnforcementMode(enforcementMode); - cached.setPolicyEnforcementMode(enforcementMode); - } - - private ResourceServer getDelegateForUpdate() { - if (this.updated == null) { - this.updated = getDelegate().findById(getId()); - if (this.updated == null) throw new IllegalStateException("Not found in database"); - addInvalidation(getCacheKeyForResourceServer(updated.getId())); - getTransaction().whenCommit(() -> { - invalidate(updated.getId()); - }); - } - - return this.updated; - } - }; - } -} diff --git a/model/infinispan/src/main/java/org/keycloak/models/authorization/infinispan/CachedResourceStore.java b/model/infinispan/src/main/java/org/keycloak/models/authorization/infinispan/CachedResourceStore.java deleted file mode 100644 index bc892d0e51..0000000000 --- a/model/infinispan/src/main/java/org/keycloak/models/authorization/infinispan/CachedResourceStore.java +++ /dev/null @@ -1,321 +0,0 @@ -/* - * JBoss, Home of Professional Open Source. - * Copyright 2016 Red Hat, Inc., and individual 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.ArrayList; -import java.util.Arrays; -import java.util.Collections; -import java.util.List; -import java.util.Map; -import java.util.Set; -import java.util.function.Function; -import java.util.function.Supplier; -import java.util.stream.Collectors; - -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.models.authorization.infinispan.entities.CachedResource; - -/** - * @author Pedro Igor - */ -public class CachedResourceStore extends AbstractCachedStore implements ResourceStore { - - private static final String RESOURCE_CACHE_PREFIX = "rs-"; - - private ResourceStore 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, getStoreFactory().getResourceServerStore().findById(resourceServer.getId()), owner); - - addInvalidation(getCacheKeyForResource(resource.getId())); - addInvalidation(getCacheKeyForResourceName(resource.getName())); - addInvalidation(getCacheKeyForOwner(owner)); - - getCachedStoreFactory().getPolicyStore().addInvalidations(resource); - - getTransaction().whenRollback(() -> removeCachedEntry(resourceServer.getId(), getCacheKeyForResource(resource.getId()))); - getTransaction().whenCommit(() -> invalidate(resourceServer.getId())); - - return createAdapter(new CachedResource(resource)); - } - - @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); - - getTransaction().whenCommit(() -> { - invalidate(resourceServer.getId()); - }); - } - - @Override - public Resource findById(String id, String resourceServerId) { - String cacheKeyForResource = getCacheKeyForResource(id); - - if (isInvalid(cacheKeyForResource)) { - return getDelegate().findById(id, resourceServerId); - } - - List cached = resolveCacheEntry(resourceServerId, cacheKeyForResource); - - if (cached == null) { - Resource resource = getDelegate().findById(id, resourceServerId); - - if (resource != null) { - return createAdapter(putCacheEntry(resourceServerId, cacheKeyForResource, new CachedResource(resource))); - } - - return null; - } - - return createAdapter(CachedResource.class.cast(cached.get(0))); - } - - @Override - public List findByOwner(String ownerId, String resourceServerId) { - String cacheKey = getCacheKeyForOwner(ownerId); - - if (isInvalid(cacheKey)) { - return getDelegate().findByOwner(ownerId, resourceServerId); - } - - return cacheResult(resourceServerId, cacheKey, () -> getDelegate().findByOwner(ownerId, resourceServerId)); - } - - @Override - public List findByUri(String uri, String resourceServerId) { - String cacheKey = getCacheKeyForUri(uri); - - if (isInvalid(cacheKey)) { - return getDelegate().findByUri(uri, resourceServerId); - } - - return cacheResult(resourceServerId, cacheKey, () -> getDelegate().findByUri(uri, resourceServerId)); - } - - @Override - public List findByResourceServer(String resourceServerId) { - return getDelegate().findByResourceServer(resourceServerId); - } - - @Override - public List findByResourceServer(Map attributes, String resourceServerId, int firstResult, int maxResult) { - return getDelegate().findByResourceServer(attributes, resourceServerId, firstResult, maxResult); - } - - @Override - public List findByScope(List id, String resourceServerId) { - return getDelegate().findByScope(id, resourceServerId); - } - - @Override - public Resource findByName(String name, String resourceServerId) { - String cacheKey = getCacheKeyForResourceName(name); - - if (isInvalid(cacheKey)) { - return getDelegate().findByName(name, resourceServerId); - } - - 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 - public List findByType(String type, String resourceServerId) { - return getDelegate().findByType(type, resourceServerId); - } - - private String getCacheKeyForResource(String id) { - return new StringBuilder(RESOURCE_CACHE_PREFIX).append("id-").append(id).toString(); - } - - 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() { - return this.delegate; - } - - private List cacheResult(String resourceServerId, String key, Supplier> provider) { - List cached = getCachedStoreFactory().computeIfCachedEntryAbsent(resourceServerId, key, (Function>) o -> { - List 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) { - return new Resource() { - - private List scopes; - private Resource updated; - - @Override - public String getId() { - return cached.getId(); - } - - @Override - public String getName() { - return cached.getName(); - } - - @Override - public void setName(String name) { - addInvalidation(getCacheKeyForResourceName(name)); - addInvalidation(getCacheKeyForResourceName(cached.getName())); - getDelegateForUpdate().setName(name); - cached.setName(name); - } - - @Override - public String getUri() { - return cached.getUri(); - } - - @Override - public void setUri(String uri) { - addInvalidation(getCacheKeyForUri(uri)); - addInvalidation(getCacheKeyForUri(cached.getUri())); - getDelegateForUpdate().setUri(uri); - cached.setUri(uri); - } - - @Override - public String getType() { - return cached.getType(); - } - - @Override - public void setType(String type) { - getCachedStoreFactory().getPolicyStore().addInvalidations(cached); - getDelegateForUpdate().setType(type); - cached.setType(type); - } - - @Override - public List getScopes() { - if (scopes == null) { - scopes = new ArrayList<>(); - - for (String id : cached.getScopesIds()) { - Scope scope = getCachedStoreFactory().getScopeStore().findById(id, cached.getResourceServerId()); - - if (scope != null) { - scopes.add(scope); - } - } - } - - return scopes; - } - - @Override - public String getIconUri() { - return cached.getIconUri(); - } - - @Override - public void setIconUri(String iconUri) { - getDelegateForUpdate().setIconUri(iconUri); - cached.setIconUri(iconUri); - } - - @Override - public ResourceServer getResourceServer() { - return getCachedStoreFactory().getResourceServerStore().findById(cached.getResourceServerId()); - } - - @Override - public String getOwner() { - return cached.getOwner(); - } - - @Override - public void updateScopes(Set scopes) { - getDelegateForUpdate().updateScopes(scopes.stream().map(scope -> getStoreFactory().getScopeStore().findById(scope.getId(), cached.getResourceServerId())).collect(Collectors.toSet())); - cached.updateScopes(scopes); - } - - private Resource getDelegateForUpdate() { - if (this.updated == null) { - String resourceServerId = cached.getResourceServerId(); - this.updated = getDelegate().findById(getId(), resourceServerId); - if (this.updated == null) throw new IllegalStateException("Not found in database"); - addInvalidation(getCacheKeyForResource(updated.getId())); - getCachedStoreFactory().getPolicyStore().addInvalidations(updated); - getTransaction().whenCommit(() -> invalidate(resourceServerId)); - getTransaction().whenRollback(() -> removeCachedEntry(resourceServerId, getCacheKeyForResource(cached.getId()))); - } - - return this.updated; - } - }; - } -} diff --git a/model/infinispan/src/main/java/org/keycloak/models/authorization/infinispan/CachedScopeStore.java b/model/infinispan/src/main/java/org/keycloak/models/authorization/infinispan/CachedScopeStore.java deleted file mode 100644 index 741f5f716c..0000000000 --- a/model/infinispan/src/main/java/org/keycloak/models/authorization/infinispan/CachedScopeStore.java +++ /dev/null @@ -1,231 +0,0 @@ -/* - * JBoss, Home of Professional Open Source. - * Copyright 2016 Red Hat, Inc., and individual 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.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.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.models.authorization.infinispan.entities.CachedScope; - -/** - * @author Pedro Igor - */ -public class CachedScopeStore extends AbstractCachedStore implements ScopeStore { - - private static final String SCOPE_CACHE_PREFIX = "scp-"; - - private final ScopeStore 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())); - - 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)); - } - - @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); - - getTransaction().whenCommit(() -> invalidate(resourceServer.getId())); - } - - @Override - public Scope findById(String id, String resourceServerId) { - String cacheKey = getCacheKeyForScope(id); - - if (isInvalid(cacheKey)) { - return getDelegate().findById(id, resourceServerId); - } - - List cached = resolveCacheEntry(resourceServerId, cacheKey); - - if (cached == null) { - Scope scope = getDelegate().findById(id, resourceServerId); - - if (scope != null) { - return createAdapter(putCacheEntry(resourceServerId, cacheKey, new CachedScope(scope))); - } - - return null; - } - - return createAdapter(CachedScope.class.cast(cached.get(0))); - } - - @Override - public Scope findByName(String name, String resourceServerId) { - String cacheKey = getCacheKeyForScopeName(name); - - if (isInvalid(cacheKey)) { - return getDelegate().findByName(name, resourceServerId); - } - - 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 - public List findByResourceServer(String id) { - return getDelegate().findByResourceServer(id); - } - - @Override - public List findByResourceServer(Map attributes, String resourceServerId, int firstResult, int maxResult) { - return getDelegate().findByResourceServer(attributes, resourceServerId, firstResult, maxResult); - } - - private String getCacheKeyForScope(String id) { - return new StringBuilder(SCOPE_CACHE_PREFIX).append("id-").append(id).toString(); - } - - private String getCacheKeyForScopeName(String name) { - return new StringBuilder(SCOPE_CACHE_PREFIX).append("findByName-").append(name).toString(); - } - - private ScopeStore getDelegate() { - return this.delegate; - } - - private List cacheResult(String resourceServerId, String key, Supplier> provider) { - List cached = getCachedStoreFactory().computeIfCachedEntryAbsent(resourceServerId, key, (Function>) o -> { - List 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) { - return new Scope() { - - private Scope updated; - - @Override - public String getId() { - return cached.getId(); - } - - @Override - public String getName() { - return cached.getName(); - } - - @Override - public void setName(String name) { - addInvalidation(getCacheKeyForScopeName(name)); - addInvalidation(getCacheKeyForScopeName(cached.getName())); - getDelegateForUpdate().setName(name); - cached.setName(name); - } - - @Override - public String getIconUri() { - return cached.getIconUri(); - } - - @Override - public void setIconUri(String iconUri) { - getDelegateForUpdate().setIconUri(iconUri); - cached.setIconUri(iconUri); - } - - @Override - public ResourceServer getResourceServer() { - return getCachedStoreFactory().getResourceServerStore().findById(cached.getResourceServerId()); - } - - private Scope getDelegateForUpdate() { - if (this.updated == null) { - this.updated = getDelegate().findById(getId(), cached.getResourceServerId()); - if (this.updated == null) throw new IllegalStateException("Not found in database"); - addInvalidation(getCacheKeyForScope(updated.getId())); - getCachedStoreFactory().getPolicyStore().addInvalidations(updated); - getTransaction().whenCommit(() -> invalidate(cached.getResourceServerId())); - getTransaction().whenRollback(() -> removeCachedEntry(cached.getResourceServerId(), getCacheKeyForScope(cached.getId()))); - } - - return this.updated; - } - - @Override - public boolean equals(Object o) { - if (this == o) return true; - if (o == null || !Scope.class.isInstance(o)) return false; - Scope that = (Scope) o; - return Objects.equals(getId(), that.getId()); - } - - @Override - public int hashCode() { - return Objects.hash(getId()); - } - }; - } -} diff --git a/model/infinispan/src/main/java/org/keycloak/models/authorization/infinispan/InfinispanStoreFactoryProvider.java b/model/infinispan/src/main/java/org/keycloak/models/authorization/infinispan/InfinispanStoreFactoryProvider.java deleted file mode 100644 index 692173c4cf..0000000000 --- a/model/infinispan/src/main/java/org/keycloak/models/authorization/infinispan/InfinispanStoreFactoryProvider.java +++ /dev/null @@ -1,166 +0,0 @@ -/* - * JBoss, Home of Professional Open Source. - * Copyright 2016 Red Hat, Inc., and individual 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.ArrayList; -import java.util.HashSet; -import java.util.List; -import java.util.Set; -import java.util.function.Function; - -import org.keycloak.authorization.model.ResourceServer; -import org.keycloak.authorization.store.ResourceServerStore; -import org.keycloak.authorization.store.ResourceStore; -import org.keycloak.authorization.store.ScopeStore; -import org.keycloak.authorization.store.StoreFactory; -import org.keycloak.models.KeycloakSession; -import org.keycloak.models.KeycloakTransaction; -import org.keycloak.models.cache.authorization.CachedStoreFactoryProvider; - -/** - * @author Pedro Igor - */ -public class InfinispanStoreFactoryProvider implements CachedStoreFactoryProvider { - - private final CacheTransaction transaction; - 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 invalidations = new HashSet<>(); - - 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(this, delegate); - resourceServerStore = new CachedResourceServerStore(this, delegate); - scopeStore = new CachedScopeStore(this, delegate); - policyStore = new CachedPolicyStore(this, delegate); - } - - @Override - public ResourceStore getResourceStore() { - return resourceStore; - } - - @Override - public ResourceServerStore getResourceServerStore() { - return resourceServerStore; - } - - @Override - public ScopeStore getScopeStore() { - return scopeStore; - } - - @Override - public CachedPolicyStore getPolicyStore() { - return policyStore; - } - - @Override - public void close() { - - } - - 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 resolveCachedEntry(String resourceServerId, String cacheKeyForPolicy) { - return cacheManager.resolveResourceServerCache(resourceServerId).get(cacheKeyForPolicy); - } - - void putCacheEntry(String resourceServerId, String key, List entry) { - cacheManager.resolveResourceServerCache(resourceServerId).put(key, entry); - } - - List computeIfCachedEntryAbsent(String resourceServerId, String key, Function> 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 completeTasks = new ArrayList<>(); - private List rollbackTasks = new ArrayList<>(); - - @Override - public void begin() { - - } - - @Override - public void commit() { - this.completeTasks.forEach(task -> task.run()); - } - - @Override - public void rollback() { - this.rollbackTasks.forEach(task -> task.run()); - } - - @Override - public void setRollbackOnly() { - - } - - @Override - public boolean getRollbackOnly() { - return false; - } - - @Override - public boolean isActive() { - return false; - } - - protected void whenCommit(Runnable task) { - this.completeTasks.add(task); - } - - protected void whenRollback(Runnable task) { - this.rollbackTasks.add(task); - } - } -} diff --git a/model/infinispan/src/main/java/org/keycloak/models/authorization/infinispan/InfinispanStoreProviderFactory.java b/model/infinispan/src/main/java/org/keycloak/models/authorization/infinispan/InfinispanStoreProviderFactory.java deleted file mode 100644 index 672b565584..0000000000 --- a/model/infinispan/src/main/java/org/keycloak/models/authorization/infinispan/InfinispanStoreProviderFactory.java +++ /dev/null @@ -1,89 +0,0 @@ -/* - * JBoss, Home of Professional Open Source. - * Copyright 2016 Red Hat, Inc., and individual 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.List; -import java.util.Map; - -import org.infinispan.Cache; -import org.keycloak.Config; -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; - -/** - * @author Pedro Igor - */ -public class InfinispanStoreProviderFactory implements CachedStoreProviderFactory, EnvironmentDependentProviderFactory { - - private StoreFactoryCacheManager cacheManager; - - @Override - public CachedStoreFactoryProvider create(KeycloakSession session) { - return new InfinispanStoreFactoryProvider(session, cacheManager); - } - - @Override - public void init(Config.Scope config) { - - } - - @Override - public void postInit(KeycloakSessionFactory factory) { - KeycloakSession session = factory.create(); - - try { - InfinispanConnectionProvider provider = session.getProvider(InfinispanConnectionProvider.class); - Cache>> 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 - public void close() { - - } - - @Override - public String getId() { - return "infinispan-authz-store-factory"; - } - - @Override - public boolean isSupported() { - return true; - } -} diff --git a/model/infinispan/src/main/java/org/keycloak/models/authorization/infinispan/StoreFactoryCacheManager.java b/model/infinispan/src/main/java/org/keycloak/models/authorization/infinispan/StoreFactoryCacheManager.java deleted file mode 100644 index 4fd5f6f750..0000000000 --- a/model/infinispan/src/main/java/org/keycloak/models/authorization/infinispan/StoreFactoryCacheManager.java +++ /dev/null @@ -1,72 +0,0 @@ -/* - * 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 Pedro Igor - */ -public class StoreFactoryCacheManager { - - private static final String AUTHORIZATION_UPDATE_TASK_KEY = "authorization-update"; - - private final Cache>> cache; - - StoreFactoryCacheManager(Cache>> 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> resolveResourceServerCache = resolveResourceServerCache(event.getId()); - - for (String key : event.getInvalidations()) { - resolveResourceServerCache.remove(key); - } - } - } - - public void invalidate(KeycloakSession session, String resourceServerId, Set invalidations) { - getClusterProvider(session).notify(AUTHORIZATION_UPDATE_TASK_KEY, new AuthorizationInvalidationEvent(resourceServerId, invalidations), false); - } - - public Map> 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); - } -} diff --git a/model/infinispan/src/main/java/org/keycloak/models/authorization/infinispan/events/AuthorizationInvalidationEvent.java b/model/infinispan/src/main/java/org/keycloak/models/authorization/infinispan/events/AuthorizationInvalidationEvent.java deleted file mode 100644 index 1d449235e3..0000000000 --- a/model/infinispan/src/main/java/org/keycloak/models/authorization/infinispan/events/AuthorizationInvalidationEvent.java +++ /dev/null @@ -1,44 +0,0 @@ -/* - * 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 Pedro Igor - */ -public class AuthorizationInvalidationEvent extends InvalidationEvent { - - private final String resourceServerId; - private Set invalidations; - - public AuthorizationInvalidationEvent(String resourceServerId, Set invalidations) { - this.resourceServerId = resourceServerId; - this.invalidations = invalidations; - } - - public Set getInvalidations() { - return invalidations; - } - - @Override - public String getId() { - return resourceServerId; - } -} diff --git a/model/infinispan/src/main/java/org/keycloak/models/cache/infinispan/CacheManager.java b/model/infinispan/src/main/java/org/keycloak/models/cache/infinispan/CacheManager.java index c254ea7164..c9832ff558 100755 --- a/model/infinispan/src/main/java/org/keycloak/models/cache/infinispan/CacheManager.java +++ b/model/infinispan/src/main/java/org/keycloak/models/cache/infinispan/CacheManager.java @@ -215,7 +215,7 @@ public abstract class CacheManager { } - protected void invalidationEventReceived(InvalidationEvent event) { + public void invalidationEventReceived(InvalidationEvent event) { Set invalidations = new HashSet<>(); addInvalidationsFromEvent(event, invalidations); diff --git a/model/infinispan/src/main/java/org/keycloak/models/cache/infinispan/authorization/InfinispanCacheStoreFactoryProviderFactory.java b/model/infinispan/src/main/java/org/keycloak/models/cache/infinispan/authorization/InfinispanCacheStoreFactoryProviderFactory.java new file mode 100755 index 0000000000..c5dc7fc23a --- /dev/null +++ b/model/infinispan/src/main/java/org/keycloak/models/cache/infinispan/authorization/InfinispanCacheStoreFactoryProviderFactory.java @@ -0,0 +1,102 @@ +/* + * 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.cache.infinispan.authorization; + +import org.infinispan.Cache; +import org.jboss.logging.Logger; +import org.keycloak.Config; +import org.keycloak.authorization.AuthorizationProvider; +import org.keycloak.cluster.ClusterEvent; +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.cache.CacheRealmProvider; +import org.keycloak.models.cache.CacheRealmProviderFactory; +import org.keycloak.models.cache.authorization.CachedStoreFactoryProvider; +import org.keycloak.models.cache.authorization.CachedStoreProviderFactory; +import org.keycloak.models.cache.infinispan.RealmCacheManager; +import org.keycloak.models.cache.infinispan.RealmCacheSession; +import org.keycloak.models.cache.infinispan.entities.Revisioned; +import org.keycloak.models.cache.infinispan.events.InvalidationEvent; + +/** + * @author Bill Burke + * @author Stian Thorgersen + */ +public class InfinispanCacheStoreFactoryProviderFactory implements CachedStoreProviderFactory { + + private static final Logger log = Logger.getLogger(InfinispanCacheStoreFactoryProviderFactory.class); + public static final String AUTHORIZATION_CLEAR_CACHE_EVENTS = "AUTHORIZATION_CLEAR_CACHE_EVENTS"; + + protected volatile StoreFactoryCacheManager storeCache; + + @Override + public CachedStoreFactoryProvider create(KeycloakSession session) { + lazyInit(session); + return new StoreFactoryCacheSession(storeCache, session); + } + + private void lazyInit(KeycloakSession session) { + if (storeCache == null) { + synchronized (this) { + if (storeCache == null) { + Cache cache = session.getProvider(InfinispanConnectionProvider.class).getCache(InfinispanConnectionProvider.AUTHORIZATION_CACHE_NAME); + Cache revisions = session.getProvider(InfinispanConnectionProvider.class).getCache(InfinispanConnectionProvider.AUTHORIZATION_REVISIONS_CACHE_NAME); + storeCache = new StoreFactoryCacheManager(cache, revisions); + + ClusterProvider cluster = session.getProvider(ClusterProvider.class); + cluster.registerListener(ClusterProvider.ALL, (ClusterEvent event) -> { + + if (event instanceof InvalidationEvent) { + InvalidationEvent invalidationEvent = (InvalidationEvent) event; + storeCache.invalidationEventReceived(invalidationEvent); + } + }); + + cluster.registerListener(AUTHORIZATION_CLEAR_CACHE_EVENTS, (ClusterEvent event) -> { + + storeCache.clear(); + + }); + + log.debug("Registered cluster listeners"); + } + } + } + } + + @Override + public void init(Config.Scope config) { + } + + @Override + public void postInit(KeycloakSessionFactory factory) { + + } + + @Override + public void close() { + } + + @Override + public String getId() { + return "default"; + } + +} 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 new file mode 100644 index 0000000000..a8cd37994c --- /dev/null +++ b/model/infinispan/src/main/java/org/keycloak/models/cache/infinispan/authorization/PolicyAdapter.java @@ -0,0 +1,280 @@ +/* + * 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.cache.infinispan.authorization; + +import org.keycloak.authorization.model.CachedModel; +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.models.cache.infinispan.authorization.entities.CachedPolicy; +import org.keycloak.representations.idm.authorization.DecisionStrategy; +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; + +/** + * @author Bill Burke + * @version $Revision: 1 $ + */ +public class PolicyAdapter implements Policy, CachedModel { + protected CachedPolicy cached; + protected StoreFactoryCacheSession cacheSession; + protected Policy updated; + + public PolicyAdapter(CachedPolicy cached, StoreFactoryCacheSession cacheSession) { + this.cached = cached; + this.cacheSession = cacheSession; + } + + @Override + public Policy getDelegateForUpdate() { + if (updated == null) { + cacheSession.registerPolicyInvalidation(cached.getId(), cached.getName(), cached.getResourceServerId()); + updated = cacheSession.getPolicyStoreDelegate().findById(cached.getId(), cached.getResourceServerId()); + if (updated == null) throw new IllegalStateException("Not found in database"); + } + return updated; + } + + protected boolean invalidated; + + protected void invalidateFlag() { + invalidated = true; + + } + + @Override + public void invalidate() { + invalidated = true; + getDelegateForUpdate(); + } + + @Override + public long getCacheTimestamp() { + return cached.getCacheTimestamp(); + } + + protected boolean isUpdated() { + if (updated != null) return true; + if (!invalidated) return false; + updated = cacheSession.getPolicyStoreDelegate().findById(cached.getId(), cached.getResourceServerId()); + if (updated == null) throw new IllegalStateException("Not found in database"); + return true; + } + + + @Override + public String getId() { + if (isUpdated()) return updated.getId(); + return cached.getId(); + } + + @Override + public String getName() { + if (isUpdated()) return updated.getName(); + return cached.getName(); + } + + @Override + public void setName(String name) { + getDelegateForUpdate(); + updated.setName(name); + + } + + @Override + public ResourceServer getResourceServer() { + return cacheSession.getResourceServerStore().findById(cached.getResourceServerId()); + } + + @Override + public String getType() { + if (isUpdated()) return updated.getType(); + return cached.getType(); + } + + @Override + public DecisionStrategy getDecisionStrategy() { + if (isUpdated()) return updated.getDecisionStrategy(); + return cached.getDecisionStrategy(); + } + + @Override + public void setDecisionStrategy(DecisionStrategy decisionStrategy) { + getDelegateForUpdate(); + updated.setDecisionStrategy(decisionStrategy); + + } + + @Override + public Logic getLogic() { + if (isUpdated()) return updated.getLogic(); + return cached.getLogic(); + } + + @Override + public void setLogic(Logic logic) { + getDelegateForUpdate(); + updated.setLogic(logic); + + } + + @Override + public Map getConfig() { + if (isUpdated()) return updated.getConfig(); + return cached.getConfig(); + } + + @Override + public void setConfig(Map config) { + getDelegateForUpdate(); + updated.setConfig(config); + + } + + @Override + public void removeConfig(String name) { + getDelegateForUpdate(); + updated.removeConfig(name); + + } + + @Override + public void putConfig(String name, String value) { + getDelegateForUpdate(); + updated.putConfig(name, value); + } + + @Override + public String getDescription() { + if (isUpdated()) return updated.getDescription(); + return cached.getDescription(); + } + + @Override + public void setDescription(String description) { + getDelegateForUpdate(); + updated.setDescription(description); + } + + protected Set associatedPolicies; + + @Override + public Set getAssociatedPolicies() { + if (isUpdated()) return updated.getAssociatedPolicies(); + if (associatedPolicies != null) return associatedPolicies; + associatedPolicies = new HashSet<>(); + for (String scopeId : cached.getAssociatedPoliciesIds()) { + associatedPolicies.add(cacheSession.getPolicyStore().findById(scopeId, cached.getResourceServerId())); + } + associatedPolicies = Collections.unmodifiableSet(associatedPolicies); + return associatedPolicies; + } + + protected Set resources; + @Override + public Set getResources() { + if (isUpdated()) return updated.getResources(); + if (resources != null) return resources; + resources = new HashSet<>(); + for (String resourceId : cached.getResourcesIds()) { + resources.add(cacheSession.getResourceStore().findById(resourceId, cached.getResourceServerId())); + } + resources = Collections.unmodifiableSet(resources); + return resources; + } + + @Override + public void addScope(Scope scope) { + getDelegateForUpdate(); + updated.addScope(scope); + + } + + @Override + public void removeScope(Scope scope) { + getDelegateForUpdate(); + updated.removeScope(scope); + + } + + @Override + public void addAssociatedPolicy(Policy associatedPolicy) { + getDelegateForUpdate(); + updated.addAssociatedPolicy(associatedPolicy); + + } + + @Override + public void removeAssociatedPolicy(Policy associatedPolicy) { + getDelegateForUpdate(); + updated.removeAssociatedPolicy(associatedPolicy); + + } + + @Override + public void addResource(Resource resource) { + getDelegateForUpdate(); + updated.addResource(resource); + + } + + @Override + public void removeResource(Resource resource) { + getDelegateForUpdate(); + updated.removeResource(resource); + + } + + protected Set scopes; + + @Override + public Set getScopes() { + if (isUpdated()) return updated.getScopes(); + if (scopes != null) return scopes; + scopes = new HashSet<>(); + for (String scopeId : cached.getScopesIds()) { + scopes.add(cacheSession.getScopeStore().findById(scopeId, cached.getResourceServerId())); + } + scopes = Collections.unmodifiableSet(scopes); + return scopes; + } + + @Override + public boolean equals(Object o) { + if (this == o) return true; + if (o == null || !(o instanceof Policy)) return false; + + Policy that = (Policy) o; + return that.getId().equals(getId()); + } + + @Override + public int hashCode() { + return getId().hashCode(); + } + + + + +} 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 new file mode 100644 index 0000000000..d44cc7c58d --- /dev/null +++ b/model/infinispan/src/main/java/org/keycloak/models/cache/infinispan/authorization/ResourceAdapter.java @@ -0,0 +1,184 @@ +/* + * 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.cache.infinispan.authorization; + +import org.keycloak.authorization.model.CachedModel; +import org.keycloak.authorization.model.Resource; +import org.keycloak.authorization.model.ResourceServer; +import org.keycloak.authorization.model.Scope; +import org.keycloak.models.cache.infinispan.authorization.entities.CachedResource; + +import java.util.Collections; +import java.util.LinkedList; +import java.util.List; +import java.util.Set; + +/** + * @author Bill Burke + * @version $Revision: 1 $ + */ +public class ResourceAdapter implements Resource, CachedModel { + protected CachedResource cached; + protected StoreFactoryCacheSession cacheSession; + protected Resource updated; + + public ResourceAdapter(CachedResource cached, StoreFactoryCacheSession cacheSession) { + this.cached = cached; + this.cacheSession = cacheSession; + } + + @Override + public Resource getDelegateForUpdate() { + if (updated == null) { + cacheSession.registerResourceInvalidation(cached.getId(), cached.getName(), cached.getResourceServerId()); + updated = cacheSession.getResourceStoreDelegate().findById(cached.getId(), cached.getResourceServerId()); + if (updated == null) throw new IllegalStateException("Not found in database"); + } + return updated; + } + + protected boolean invalidated; + + protected void invalidateFlag() { + invalidated = true; + + } + + @Override + public void invalidate() { + invalidated = true; + getDelegateForUpdate(); + } + + @Override + public long getCacheTimestamp() { + return cached.getCacheTimestamp(); + } + + protected boolean isUpdated() { + if (updated != null) return true; + if (!invalidated) return false; + updated = cacheSession.getResourceStoreDelegate().findById(cached.getId(), cached.getResourceServerId()); + if (updated == null) throw new IllegalStateException("Not found in database"); + return true; + } + + + @Override + public String getId() { + if (isUpdated()) return updated.getId(); + return cached.getId(); + } + + @Override + public String getName() { + if (isUpdated()) return updated.getName(); + return cached.getName(); + } + + @Override + public void setName(String name) { + getDelegateForUpdate(); + updated.setName(name); + + } + + @Override + public String getIconUri() { + if (isUpdated()) return updated.getIconUri(); + return cached.getIconUri(); + } + + @Override + public void setIconUri(String iconUri) { + getDelegateForUpdate(); + updated.setIconUri(iconUri); + + } + + @Override + public ResourceServer getResourceServer() { + return cacheSession.getResourceServerStore().findById(cached.getResourceServerId()); + } + + @Override + public String getUri() { + if (isUpdated()) return updated.getUri(); + return cached.getUri(); + } + + @Override + public void setUri(String uri) { + getDelegateForUpdate(); + updated.setUri(uri); + + } + + @Override + public String getType() { + if (isUpdated()) return updated.getType(); + return cached.getType(); + } + + @Override + public void setType(String type) { + getDelegateForUpdate(); + updated.setType(type); + + } + + protected List scopes; + + @Override + public List getScopes() { + if (isUpdated()) return updated.getScopes(); + if (scopes != null) return scopes; + scopes = new LinkedList<>(); + for (String scopeId : cached.getScopesIds()) { + scopes.add(cacheSession.getScopeStore().findById(scopeId, cached.getResourceServerId())); + } + scopes = Collections.unmodifiableList(scopes); + return scopes; + } + + @Override + public String getOwner() { + if (isUpdated()) return updated.getOwner(); + return cached.getOwner(); + } + + @Override + public void updateScopes(Set scopes) { + getDelegateForUpdate(); + updated.updateScopes(scopes); + } + + @Override + public boolean equals(Object o) { + if (this == o) return true; + if (o == null || !(o instanceof Resource)) return false; + + Resource that = (Resource) o; + return that.getId().equals(getId()); + } + + @Override + public int hashCode() { + return getId().hashCode(); + } + +} diff --git a/model/infinispan/src/main/java/org/keycloak/models/cache/infinispan/authorization/ResourceServerAdapter.java b/model/infinispan/src/main/java/org/keycloak/models/cache/infinispan/authorization/ResourceServerAdapter.java new file mode 100644 index 0000000000..bb3ec6cbc0 --- /dev/null +++ b/model/infinispan/src/main/java/org/keycloak/models/cache/infinispan/authorization/ResourceServerAdapter.java @@ -0,0 +1,127 @@ +/* + * 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.cache.infinispan.authorization; + +import org.keycloak.authorization.model.CachedModel; +import org.keycloak.authorization.model.ResourceServer; +import org.keycloak.models.cache.infinispan.authorization.entities.CachedResourceServer; +import org.keycloak.representations.idm.authorization.PolicyEnforcementMode; + +/** + * @author Bill Burke + * @version $Revision: 1 $ + */ +public class ResourceServerAdapter implements ResourceServer, CachedModel { + protected CachedResourceServer cached; + protected StoreFactoryCacheSession cacheSession; + protected ResourceServer updated; + + public ResourceServerAdapter(CachedResourceServer cached, StoreFactoryCacheSession cacheSession) { + this.cached = cached; + this.cacheSession = cacheSession; + } + + @Override + public ResourceServer getDelegateForUpdate() { + if (updated == null) { + cacheSession.registerResourceServerInvalidation(cached.getId(), cached.getClientId()); + updated = cacheSession.getResourceServerStoreDelegate().findById(cached.getId()); + if (updated == null) throw new IllegalStateException("Not found in database"); + } + return updated; + } + + protected boolean invalidated; + + protected void invalidateFlag() { + invalidated = true; + + } + + @Override + public void invalidate() { + invalidated = true; + getDelegateForUpdate(); + } + + @Override + public long getCacheTimestamp() { + return cached.getCacheTimestamp(); + } + + protected boolean isUpdated() { + if (updated != null) return true; + if (!invalidated) return false; + updated = cacheSession.getResourceServerStoreDelegate().findById(cached.getId()); + if (updated == null) throw new IllegalStateException("Not found in database"); + return true; + } + + + @Override + public String getId() { + if (isUpdated()) return updated.getId(); + return cached.getId(); + } + + @Override + public String getClientId() { + if (isUpdated()) return updated.getClientId(); + return cached.getClientId(); + } + + @Override + public boolean isAllowRemoteResourceManagement() { + if (isUpdated()) return updated.isAllowRemoteResourceManagement(); + return cached.isAllowRemoteResourceManagement(); + } + + @Override + public void setAllowRemoteResourceManagement(boolean allowRemoteResourceManagement) { + getDelegateForUpdate(); + updated.setAllowRemoteResourceManagement(allowRemoteResourceManagement); + } + + @Override + public PolicyEnforcementMode getPolicyEnforcementMode() { + if (isUpdated()) return updated.getPolicyEnforcementMode(); + return cached.getPolicyEnforcementMode(); + } + + @Override + public void setPolicyEnforcementMode(PolicyEnforcementMode enforcementMode) { + getDelegateForUpdate(); + updated.setPolicyEnforcementMode(enforcementMode); + + } + + @Override + public boolean equals(Object o) { + if (this == o) return true; + if (o == null || !(o instanceof ResourceServer)) return false; + + ResourceServer that = (ResourceServer) o; + return that.getId().equals(getId()); + } + + @Override + public int hashCode() { + return getId().hashCode(); + } + + +} diff --git a/model/infinispan/src/main/java/org/keycloak/models/cache/infinispan/authorization/ScopeAdapter.java b/model/infinispan/src/main/java/org/keycloak/models/cache/infinispan/authorization/ScopeAdapter.java new file mode 100644 index 0000000000..d90b27a19f --- /dev/null +++ b/model/infinispan/src/main/java/org/keycloak/models/cache/infinispan/authorization/ScopeAdapter.java @@ -0,0 +1,127 @@ +/* + * 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.cache.infinispan.authorization; + +import org.keycloak.authorization.model.CachedModel; +import org.keycloak.authorization.model.ResourceServer; +import org.keycloak.authorization.model.Scope; +import org.keycloak.models.cache.infinispan.authorization.entities.CachedScope; + +/** + * @author Bill Burke + * @version $Revision: 1 $ + */ +public class ScopeAdapter implements Scope, CachedModel { + protected CachedScope cached; + protected StoreFactoryCacheSession cacheSession; + protected Scope updated; + + public ScopeAdapter(CachedScope cached, StoreFactoryCacheSession cacheSession) { + this.cached = cached; + this.cacheSession = cacheSession; + } + + @Override + public Scope getDelegateForUpdate() { + if (updated == null) { + cacheSession.registerScopeInvalidation(cached.getId(), cached.getName(), cached.getResourceServerId()); + updated = cacheSession.getScopeStoreDelegate().findById(cached.getId(), cached.getResourceServerId()); + if (updated == null) throw new IllegalStateException("Not found in database"); + } + return updated; + } + + protected boolean invalidated; + + protected void invalidateFlag() { + invalidated = true; + + } + + @Override + public void invalidate() { + invalidated = true; + getDelegateForUpdate(); + } + + @Override + public long getCacheTimestamp() { + return cached.getCacheTimestamp(); + } + + protected boolean isUpdated() { + if (updated != null) return true; + if (!invalidated) return false; + updated = cacheSession.getScopeStoreDelegate().findById(cached.getId(), cached.getResourceServerId()); + if (updated == null) throw new IllegalStateException("Not found in database"); + return true; + } + + + @Override + public String getId() { + if (isUpdated()) return updated.getId(); + return cached.getId(); + } + + @Override + public String getName() { + if (isUpdated()) return updated.getName(); + return cached.getName(); + } + + @Override + public void setName(String name) { + getDelegateForUpdate(); + updated.setName(name); + + } + + @Override + public String getIconUri() { + if (isUpdated()) return updated.getIconUri(); + return cached.getIconUri(); + } + + @Override + public void setIconUri(String iconUri) { + getDelegateForUpdate(); + updated.setIconUri(iconUri); + + } + + @Override + public ResourceServer getResourceServer() { + return cacheSession.getResourceServerStore().findById(cached.getResourceServerId()); + } + + @Override + public boolean equals(Object o) { + if (this == o) return true; + if (o == null || !(o instanceof Scope)) return false; + + Scope that = (Scope) o; + return that.getId().equals(getId()); + } + + @Override + public int hashCode() { + return getId().hashCode(); + } + + +} 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 new file mode 100644 index 0000000000..2c86648737 --- /dev/null +++ b/model/infinispan/src/main/java/org/keycloak/models/cache/infinispan/authorization/StoreFactoryCacheManager.java @@ -0,0 +1,93 @@ +/* + * 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.cache.infinispan.authorization; + +import org.infinispan.Cache; +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.InResourceServerPredicate; +import org.keycloak.models.cache.infinispan.entities.Revisioned; +import org.keycloak.models.cache.infinispan.events.InvalidationEvent; + +import java.util.Set; + +/** + * @author Bill Burke + * @version $Revision: 1 $ + */ +public class StoreFactoryCacheManager extends CacheManager { + private static final Logger logger = Logger.getLogger(RealmCacheManager.class); + + public StoreFactoryCacheManager(Cache cache, Cache revisions) { + super(cache, revisions); + } + @Override + protected Logger getLogger() { + return logger; + } + + @Override + protected void addInvalidationsFromEvent(InvalidationEvent event, Set invalidations) { + if (event instanceof AuthorizationCacheInvalidationEvent) { + invalidations.add(event.getId()); + + ((AuthorizationCacheInvalidationEvent) event).addInvalidations(this, invalidations); + } + } + + public void resourceServerUpdated(String id, String clientId, Set invalidations) { + invalidations.add(id); + invalidations.add(StoreFactoryCacheSession.getResourceServerByClientCacheKey(clientId)); + } + + public void resourceServerRemoval(String id, String name, Set invalidations) { + resourceServerUpdated(id, name, invalidations); + + addInvalidations(InResourceServerPredicate.create().resourceServer(id), invalidations); + } + + public void scopeUpdated(String id, String name, String serverId, Set invalidations) { + invalidations.add(id); + invalidations.add(StoreFactoryCacheSession.getScopeByNameCacheKey(name, serverId)); + } + + public void scopeRemoval(String id, String name, String serverId, Set invalidations) { + scopeUpdated(id, name, serverId, invalidations); + } + + public void resourceUpdated(String id, String name, String serverId, Set invalidations) { + invalidations.add(id); + invalidations.add(StoreFactoryCacheSession.getResourceByNameCacheKey(name, serverId)); + } + + public void resourceRemoval(String id, String name, String serverId, Set invalidations) { + resourceUpdated(id, name, serverId, invalidations); + } + + public void policyUpdated(String id, String name, String serverId, Set invalidations) { + invalidations.add(id); + invalidations.add(StoreFactoryCacheSession.getPolicyByNameCacheKey(name, serverId)); + } + + public void policyRemoval(String id, String name, String serverId, Set invalidations) { + policyUpdated(id, name, 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 new file mode 100644 index 0000000000..87f311b707 --- /dev/null +++ b/model/infinispan/src/main/java/org/keycloak/models/cache/infinispan/authorization/StoreFactoryCacheSession.java @@ -0,0 +1,667 @@ +/* + * 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.cache.infinispan.authorization; + +import org.jboss.logging.Logger; +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.ResourceServerStore; +import org.keycloak.authorization.store.ResourceStore; +import org.keycloak.authorization.store.ScopeStore; +import org.keycloak.authorization.store.StoreFactory; +import org.keycloak.models.KeycloakSession; +import org.keycloak.models.KeycloakTransaction; +import org.keycloak.models.cache.authorization.CachedStoreFactoryProvider; +import org.keycloak.models.cache.infinispan.authorization.entities.CachedPolicy; +import org.keycloak.models.cache.infinispan.authorization.entities.CachedResource; +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.ResourceListQuery; +import org.keycloak.models.cache.infinispan.authorization.entities.ResourceServerListQuery; +import org.keycloak.models.cache.infinispan.authorization.entities.ScopeListQuery; +import org.keycloak.models.cache.infinispan.authorization.events.PolicyRemovedEvent; +import org.keycloak.models.cache.infinispan.authorization.events.PolicyUpdatedEvent; +import org.keycloak.models.cache.infinispan.authorization.events.ResourceRemovedEvent; +import org.keycloak.models.cache.infinispan.authorization.events.ResourceServerRemovedEvent; +import org.keycloak.models.cache.infinispan.authorization.events.ResourceServerUpdatedEvent; +import org.keycloak.models.cache.infinispan.authorization.events.ResourceUpdatedEvent; +import org.keycloak.models.cache.infinispan.authorization.events.ScopeRemovedEvent; +import org.keycloak.models.cache.infinispan.authorization.events.ScopeUpdatedEvent; +import org.keycloak.models.cache.infinispan.events.InvalidationEvent; +import org.keycloak.representations.idm.authorization.AbstractPolicyRepresentation; + +import java.util.HashMap; +import java.util.HashSet; +import java.util.List; +import java.util.Map; +import java.util.Set; + +/** + * @author Bill Burke + * @version $Revision: 1 $ + */ +public class StoreFactoryCacheSession implements CachedStoreFactoryProvider { + protected static final Logger logger = Logger.getLogger(StoreFactoryCacheSession.class); + + protected StoreFactoryCacheManager cache; + protected boolean transactionActive; + protected boolean setRollbackOnly; + + protected Map managedResourceServers = new HashMap<>(); + protected Map managedScopes = new HashMap<>(); + protected Map managedResources = new HashMap<>(); + protected Map managedPolicies = new HashMap<>(); + protected Set invalidations = new HashSet<>(); + protected Set invalidationEvents = new HashSet<>(); // Events to be sent across cluster + + protected boolean clearAll; + protected final long startupRevision; + protected StoreFactory delegate; + protected KeycloakSession session; + protected ResourceServerCache resourceServerCache; + protected ScopeCache scopeCache; + protected ResourceCache resourceCache; + protected PolicyCache policyCache; + + public StoreFactoryCacheSession(StoreFactoryCacheManager cache, KeycloakSession session) { + this.cache = cache; + this.startupRevision = cache.getCurrentCounter(); + this.session = session; + this.resourceServerCache = new ResourceServerCache(); + this.scopeCache = new ScopeCache(); + this.resourceCache = new ResourceCache(); + this.policyCache = new PolicyCache(); + session.getTransactionManager().enlistPrepare(getPrepareTransaction()); + session.getTransactionManager().enlistAfterCompletion(getAfterTransaction()); + } + + @Override + public ResourceServerStore getResourceServerStore() { + return resourceServerCache; + } + + @Override + public ScopeStore getScopeStore() { + return scopeCache; + } + + @Override + public ResourceStore getResourceStore() { + return resourceCache; + } + + @Override + public PolicyStore getPolicyStore() { + return policyCache; + } + + public void close() { + } + + private KeycloakTransaction getPrepareTransaction() { + return new KeycloakTransaction() { + @Override + public void begin() { + transactionActive = true; + } + + @Override + public void commit() { + } + + @Override + public void rollback() { + setRollbackOnly = true; + transactionActive = false; + } + + @Override + public void setRollbackOnly() { + setRollbackOnly = true; + } + + @Override + public boolean getRollbackOnly() { + return setRollbackOnly; + } + + @Override + public boolean isActive() { + return transactionActive; + } + }; + } + + private KeycloakTransaction getAfterTransaction() { + return new KeycloakTransaction() { + @Override + public void begin() { + transactionActive = true; + } + + @Override + public void commit() { + try { + if (getDelegate() == null) return; + if (clearAll) { + cache.clear(); + } + runInvalidations(); + transactionActive = false; + } finally { + cache.endRevisionBatch(); + } + } + + @Override + public void rollback() { + try { + setRollbackOnly = true; + runInvalidations(); + transactionActive = false; + } finally { + cache.endRevisionBatch(); + } + } + + @Override + public void setRollbackOnly() { + setRollbackOnly = true; + } + + @Override + public boolean getRollbackOnly() { + return setRollbackOnly; + } + + @Override + public boolean isActive() { + return transactionActive; + } + }; + } + + protected void runInvalidations() { + for (String id : invalidations) { + cache.invalidateObject(id); + } + + cache.sendInvalidationEvents(session, invalidationEvents); + } + + + + public long getStartupRevision() { + return startupRevision; + } + + public boolean isInvalid(String id) { + return invalidations.contains(id); + } + + public void registerResourceServerInvalidation(String id, String clientId) { + cache.resourceServerUpdated(id, clientId, invalidations); + ResourceServerAdapter adapter = managedResourceServers.get(id); + if (adapter != null) adapter.invalidateFlag(); + + invalidationEvents.add(ResourceServerUpdatedEvent.create(id, clientId)); + } + + public void registerScopeInvalidation(String id, String name, String serverId) { + cache.scopeUpdated(id, name, serverId, invalidations); + ScopeAdapter adapter = managedScopes.get(id); + if (adapter != null) adapter.invalidateFlag(); + + invalidationEvents.add(ScopeUpdatedEvent.create(id, name, serverId)); + } + + public void registerResourceInvalidation(String id, String name, String serverId) { + cache.resourceUpdated(id, name, serverId, invalidations); + ResourceAdapter adapter = managedResources.get(id); + if (adapter != null) adapter.invalidateFlag(); + + invalidationEvents.add(ResourceUpdatedEvent.create(id, name, serverId)); + } + + public void registerPolicyInvalidation(String id, String name, String serverId) { + cache.policyUpdated(id, name, serverId, invalidations); + PolicyAdapter adapter = managedPolicies.get(id); + if (adapter != null) adapter.invalidateFlag(); + + invalidationEvents.add(PolicyUpdatedEvent.create(id, name, serverId)); + } + + public ResourceServerStore getResourceServerStoreDelegate() { + return getDelegate().getResourceServerStore(); + } + + public ScopeStore getScopeStoreDelegate() { + return getDelegate().getScopeStore(); + } + + public ResourceStore getResourceStoreDelegate() { + return getDelegate().getResourceStore(); + } + + public PolicyStore getPolicyStoreDelegate() { + return getDelegate().getPolicyStore(); + } + + public static String getResourceServerByClientCacheKey(String clientId) { + return "resource.server.client.id." + clientId; + } + + public static String getScopeByNameCacheKey(String name, String serverId) { + return "scope.name." + name + "." + serverId; + } + + public static String getResourceByNameCacheKey(String name, String serverId) { + return "resource.name." + name + "." + serverId; + } + + public static String getPolicyByNameCacheKey(String name, String serverId) { + return "policy.name." + name + "." + serverId; + } + + public StoreFactory getDelegate() { + if (delegate != null) return delegate; + delegate = session.getProvider(StoreFactory.class); + return delegate; + } + + + protected class ResourceServerCache implements ResourceServerStore { + @Override + public ResourceServer create(String clientId) { + ResourceServer server = getResourceServerStoreDelegate().create(clientId); + registerResourceServerInvalidation(server.getId(), server.getClientId()); + return server; + } + + @Override + public void delete(String id) { + if (id == null) return; + ResourceServer server = findById(id); + if (server == null) return; + + cache.invalidateObject(id); + invalidationEvents.add(ResourceServerRemovedEvent.create(id, server.getClientId())); + cache.resourceServerRemoval(id, server.getClientId(), invalidations); + getResourceServerStoreDelegate().delete(id); + + } + + @Override + public ResourceServer findById(String id) { + if (id == null) return null; + CachedResourceServer cached = cache.get(id, CachedResourceServer.class); + if (cached != null) { + logger.tracev("by id cache hit: {0}", cached.getId()); + } + boolean wasCached = false; + if (cached == null) { + Long loaded = cache.getCurrentRevision(id); + ResourceServer model = getResourceServerStoreDelegate().findById(id); + if (model == null) return null; + if (invalidations.contains(id)) return model; + cached = new CachedResourceServer(loaded, model); + cache.addRevisioned(cached, startupRevision); + wasCached =true; + } else if (invalidations.contains(id)) { + return getResourceServerStoreDelegate().findById(id); + } else if (managedResourceServers.containsKey(id)) { + return managedResourceServers.get(id); + } + ResourceServerAdapter adapter = new ResourceServerAdapter(cached, StoreFactoryCacheSession.this); + managedResourceServers.put(id, adapter); + return adapter; + } + + + @Override + public ResourceServer findByClient(String clientId) { + String cacheKey = getResourceServerByClientCacheKey(clientId); + ResourceServerListQuery query = cache.get(cacheKey, ResourceServerListQuery.class); + if (query != null) { + logger.tracev("ResourceServer by clientId cache hit: {0}", clientId); + } + if (query == null) { + Long loaded = cache.getCurrentRevision(cacheKey); + ResourceServer model = getResourceServerStoreDelegate().findByClient(clientId); + if (model == null) return null; + if (invalidations.contains(model.getId())) return model; + query = new ResourceServerListQuery(loaded, cacheKey, model.getId()); + cache.addRevisioned(query, startupRevision); + return model; + } else if (invalidations.contains(cacheKey)) { + return getResourceServerStoreDelegate().findByClient(clientId); + } else { + String serverId = query.getResourceServers().iterator().next(); + if (invalidations.contains(serverId)) { + return getResourceServerStoreDelegate().findByClient(clientId); + } + return findById(serverId); + } + } + } + + protected class ScopeCache implements ScopeStore { + @Override + public Scope create(String name, ResourceServer resourceServer) { + Scope scope = getScopeStoreDelegate().create(name, resourceServer); + registerScopeInvalidation(scope.getId(), scope.getName(), resourceServer.getId()); + return scope; + } + + @Override + public void delete(String id) { + if (id == null) return; + Scope scope = findById(id, null); + if (scope == null) return; + + cache.invalidateObject(id); + invalidationEvents.add(ScopeRemovedEvent.create(id, scope.getName(), scope.getResourceServer().getId())); + cache.scopeRemoval(id, scope.getName(), scope.getResourceServer().getId(), invalidations); + getScopeStoreDelegate().delete(id); + } + + @Override + public Scope findById(String id, String resourceServerId) { + if (id == null) return null; + CachedScope cached = cache.get(id, CachedScope.class); + if (cached != null) { + logger.tracev("by id cache hit: {0}", cached.getId()); + } + boolean wasCached = false; + if (cached == null) { + Long loaded = cache.getCurrentRevision(id); + Scope model = getScopeStoreDelegate().findById(id, resourceServerId); + if (model == null) return null; + if (invalidations.contains(id)) return model; + cached = new CachedScope(loaded, model); + cache.addRevisioned(cached, startupRevision); + wasCached =true; + } else if (invalidations.contains(id)) { + return getScopeStoreDelegate().findById(id, resourceServerId); + } else if (managedScopes.containsKey(id)) { + return managedScopes.get(id); + } + ScopeAdapter adapter = new ScopeAdapter(cached, StoreFactoryCacheSession.this); + managedScopes.put(id, adapter); + return adapter; + } + + @Override + public Scope findByName(String name, String resourceServerId) { + if (name == null) return null; + String cacheKey = getScopeByNameCacheKey(name, resourceServerId); + ScopeListQuery query = cache.get(cacheKey, ScopeListQuery.class); + if (query != null) { + logger.tracev("scope by name cache hit: {0}", name); + } + if (query == null) { + Long loaded = cache.getCurrentRevision(cacheKey); + Scope model = getScopeStoreDelegate().findByName(name, resourceServerId); + if (model == null) return null; + if (invalidations.contains(model.getId())) return model; + query = new ScopeListQuery(loaded, cacheKey, model.getId(), resourceServerId); + cache.addRevisioned(query, startupRevision); + return model; + } else if (invalidations.contains(cacheKey)) { + return getScopeStoreDelegate().findByName(name, resourceServerId); + } else { + String id = query.getScopes().iterator().next(); + if (invalidations.contains(id)) { + return getScopeStoreDelegate().findByName(name, resourceServerId); + } + return findById(id, query.getResourceServerId()); + } + } + + @Override + public List findByResourceServer(String id) { + return getScopeStoreDelegate().findByResourceServer(id); + } + + @Override + public List findByResourceServer(Map attributes, String resourceServerId, int firstResult, int maxResult) { + return getScopeStoreDelegate().findByResourceServer(attributes, resourceServerId, firstResult, maxResult); + } + } + + protected class ResourceCache implements ResourceStore { + @Override + public Resource create(String name, ResourceServer resourceServer, String owner) { + Resource resource = getResourceStoreDelegate().create(name, resourceServer, owner); + registerResourceInvalidation(resource.getId(), resource.getName(), resourceServer.getId()); + return resource; + } + + @Override + public void delete(String id) { + if (id == null) return; + Resource resource = findById(id, null); + if (resource == null) return; + + cache.invalidateObject(id); + invalidationEvents.add(ResourceRemovedEvent.create(id, resource.getName(), resource.getResourceServer().getId())); + cache.resourceRemoval(id, resource.getName(), resource.getResourceServer().getId(), invalidations); + getResourceStoreDelegate().delete(id); + + } + + @Override + public Resource findById(String id, String resourceServerId) { + if (id == null) return null; + CachedResource cached = cache.get(id, CachedResource.class); + if (cached != null) { + logger.tracev("by id cache hit: {0}", cached.getId()); + } + boolean wasCached = false; + if (cached == null) { + Long loaded = cache.getCurrentRevision(id); + Resource model = getResourceStoreDelegate().findById(id, resourceServerId); + if (model == null) return null; + if (invalidations.contains(id)) return model; + cached = new CachedResource(loaded, model); + cache.addRevisioned(cached, startupRevision); + wasCached =true; + } else if (invalidations.contains(id)) { + return getResourceStoreDelegate().findById(id, resourceServerId); + } else if (managedResources.containsKey(id)) { + return managedResources.get(id); + } + ResourceAdapter adapter = new ResourceAdapter(cached, StoreFactoryCacheSession.this); + managedResources.put(id, adapter); + return adapter; + } + + @Override + 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); + } + return findById(id, query.getResourceServerId()); + } + } + + @Override + public List findByOwner(String ownerId, String resourceServerId) { + return getResourceStoreDelegate().findByOwner(ownerId, resourceServerId); + } + + @Override + public List findByUri(String uri, String resourceServerId) { + return getResourceStoreDelegate().findByUri(uri, resourceServerId); + } + + @Override + public List findByResourceServer(String resourceServerId) { + return getResourceStoreDelegate().findByResourceServer(resourceServerId); + } + + @Override + public List findByResourceServer(Map attributes, String resourceServerId, int firstResult, int maxResult) { + return getResourceStoreDelegate().findByResourceServer(attributes, resourceServerId, firstResult, maxResult); + } + + @Override + public List findByScope(List ids, String resourceServerId) { + return getResourceStoreDelegate().findByScope(ids, resourceServerId); + } + + @Override + public List findByType(String type, String resourceServerId) { + return getResourceStoreDelegate().findByType(type, resourceServerId); + } + } + + 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; + } + + @Override + public void delete(String id) { + if (id == null) return; + Policy policy = findById(id, null); + 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); + getPolicyStoreDelegate().delete(id); + + } + + @Override + public Policy findById(String id, String resourceServerId) { + if (id == null) return null; + + CachedPolicy cached = cache.get(id, CachedPolicy.class); + if (cached != null) { + logger.tracev("by id cache hit: {0}", cached.getId()); + } + boolean wasCached = false; + if (cached == null) { + Long loaded = cache.getCurrentRevision(id); + Policy model = getPolicyStoreDelegate().findById(id, resourceServerId); + if (model == null) return null; + if (invalidations.contains(id)) return model; + cached = new CachedPolicy(loaded, model); + cache.addRevisioned(cached, startupRevision); + wasCached =true; + } else if (invalidations.contains(id)) { + return getPolicyStoreDelegate().findById(id, resourceServerId); + } else if (managedPolicies.containsKey(id)) { + return managedPolicies.get(id); + } + PolicyAdapter adapter = new PolicyAdapter(cached, StoreFactoryCacheSession.this); + managedPolicies.put(id, adapter); + return adapter; + } + + @Override + 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); + } + return findById(id, query.getResourceServerId()); + } + } + + @Override + public List findByResourceServer(String resourceServerId) { + return getPolicyStoreDelegate().findByResourceServer(resourceServerId); + } + + @Override + public List findByResourceServer(Map attributes, String resourceServerId, int firstResult, int maxResult) { + return getPolicyStoreDelegate().findByResourceServer(attributes, resourceServerId, firstResult, maxResult); + } + + @Override + public List findByResource(String resourceId, String resourceServerId) { + return getPolicyStoreDelegate().findByResource(resourceId, resourceServerId); + } + + @Override + public List findByResourceType(String resourceType, String resourceServerId) { + return getPolicyStoreDelegate().findByResourceType(resourceType, resourceServerId); + } + + @Override + public List findByScopeIds(List scopeIds, String resourceServerId) { + return getPolicyStoreDelegate().findByScopeIds(scopeIds, resourceServerId); + } + + @Override + public List findByType(String type, String resourceServerId) { + return getPolicyStoreDelegate().findByType(type, resourceServerId); + } + + @Override + public List findDependentPolicies(String id, String resourceServerId) { + return getPolicyStoreDelegate().findDependentPolicies(id, resourceServerId); + } + } + + +} diff --git a/model/infinispan/src/main/java/org/keycloak/models/authorization/infinispan/entities/CachedPolicy.java b/model/infinispan/src/main/java/org/keycloak/models/cache/infinispan/authorization/entities/CachedPolicy.java similarity index 53% rename from model/infinispan/src/main/java/org/keycloak/models/authorization/infinispan/entities/CachedPolicy.java rename to model/infinispan/src/main/java/org/keycloak/models/cache/infinispan/authorization/entities/CachedPolicy.java index c7bef79092..0ea771b172 100644 --- a/model/infinispan/src/main/java/org/keycloak/models/authorization/infinispan/entities/CachedPolicy.java +++ b/model/infinispan/src/main/java/org/keycloak/models/cache/infinispan/authorization/entities/CachedPolicy.java @@ -16,12 +16,13 @@ * limitations under the License. */ -package org.keycloak.models.authorization.infinispan.entities; +package org.keycloak.models.cache.infinispan.authorization.entities; 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.models.cache.infinispan.entities.AbstractRevisioned; import org.keycloak.representations.idm.authorization.DecisionStrategy; import org.keycloak.representations.idm.authorization.Logic; @@ -34,11 +35,8 @@ import java.util.stream.Collectors; /** * @author Pedro Igor */ -public class CachedPolicy implements Policy, Serializable { +public class CachedPolicy extends AbstractRevisioned implements InResourceServer { - private static final long serialVersionUID = -144247681046298128L; - - private String id; private String type; private DecisionStrategy decisionStrategy; private Logic logic; @@ -50,8 +48,8 @@ public class CachedPolicy implements Policy, Serializable { private Set resourcesIds; private Set scopesIds; - public CachedPolicy(Policy policy) { - this.id = policy.getId(); + public CachedPolicy(Long revision, Policy policy) { + super(revision, policy.getId()); this.type = policy.getType(); this.decisionStrategy = policy.getDecisionStrategy(); this.logic = policy.getLogic(); @@ -64,120 +62,30 @@ public class CachedPolicy implements Policy, Serializable { this.scopesIds = policy.getScopes().stream().map(Scope::getId).collect(Collectors.toSet()); } - public CachedPolicy(String id) { - this.id = id; - } - - @Override - public String getId() { - return this.id; - } - - @Override public String getType() { return this.type; } - @Override public DecisionStrategy getDecisionStrategy() { return this.decisionStrategy; } - @Override - public void setDecisionStrategy(DecisionStrategy decisionStrategy) { - this.decisionStrategy = decisionStrategy; - } - - @Override public Logic getLogic() { return this.logic; } - @Override - public void setLogic(Logic logic) { - this.logic = logic; - } - - @Override public Map getConfig() { return this.config; } - @Override - public void setConfig(Map config) { - this.config = config; - } - - @Override public String getName() { return this.name; } - @Override - public void setName(String name) { - this.name = name; - } - - @Override public String getDescription() { return this.description; } - @Override - public void setDescription(String description) { - this.description = description; - } - - @Override - public ResourceServer getResourceServer() { - throw new RuntimeException("Not implemented"); - } - - @Override - public void addScope(Scope scope) { - this.scopesIds.add(scope.getId()); - } - - @Override - public void removeScope(Scope scope) { - this.scopesIds.remove(scope.getId()); - } - - @Override - public void addAssociatedPolicy(Policy associatedPolicy) { - this.associatedPoliciesIds.add(associatedPolicy.getId()); - } - - @Override - public void removeAssociatedPolicy(Policy associatedPolicy) { - this.associatedPoliciesIds.remove(associatedPolicy.getId()); - } - - @Override - public void addResource(Resource resource) { - this.resourcesIds.add(resource.getId()); - } - - @Override - public void removeResource(Resource resource) { - this.resourcesIds.remove(resource.getId()); - } - - @Override - public Set getAssociatedPolicies() { - throw new RuntimeException("Not implemented"); - } - - @Override - public Set getResources() { - throw new RuntimeException("Not implemented"); - } - - @Override - public Set getScopes() { - throw new RuntimeException("Not implemented"); - } - public Set getAssociatedPoliciesIds() { return this.associatedPoliciesIds; } @@ -194,24 +102,4 @@ public class CachedPolicy implements Policy, Serializable { return this.resourceServerId; } - @Override - public boolean equals(Object o) { - if (o == this) return true; - - if (this.id == null) return false; - - if (o == null || getClass() != o.getClass()) return false; - - Policy that = (Policy) o; - - if (!getId().equals(that.getId())) return false; - - return true; - - } - - @Override - public int hashCode() { - return id!=null ? id.hashCode() : super.hashCode(); - } } diff --git a/model/infinispan/src/main/java/org/keycloak/models/authorization/infinispan/entities/CachedResource.java b/model/infinispan/src/main/java/org/keycloak/models/cache/infinispan/authorization/entities/CachedResource.java similarity index 61% rename from model/infinispan/src/main/java/org/keycloak/models/authorization/infinispan/entities/CachedResource.java rename to model/infinispan/src/main/java/org/keycloak/models/cache/infinispan/authorization/entities/CachedResource.java index ee1212f703..584d311507 100644 --- a/model/infinispan/src/main/java/org/keycloak/models/authorization/infinispan/entities/CachedResource.java +++ b/model/infinispan/src/main/java/org/keycloak/models/cache/infinispan/authorization/entities/CachedResource.java @@ -16,11 +16,12 @@ * limitations under the License. */ -package org.keycloak.models.authorization.infinispan.entities; +package org.keycloak.models.cache.infinispan.authorization.entities; import org.keycloak.authorization.model.Resource; import org.keycloak.authorization.model.ResourceServer; import org.keycloak.authorization.model.Scope; +import org.keycloak.models.cache.infinispan.entities.AbstractRevisioned; import java.io.Serializable; import java.util.List; @@ -30,11 +31,8 @@ import java.util.stream.Collectors; /** * @author Pedro Igor */ -public class CachedResource implements Resource, Serializable { +public class CachedResource extends AbstractRevisioned implements InResourceServer { - private static final long serialVersionUID = -6886179034626995165L; - - private final String id; private String resourceServerId; private String iconUri; private String owner; @@ -43,8 +41,8 @@ public class CachedResource implements Resource, Serializable { private String uri; private Set scopesIds; - public CachedResource(Resource resource) { - this.id = resource.getId(); + public CachedResource(Long revision, Resource resource) { + super(revision, resource.getId()); this.name = resource.getName(); this.uri = resource.getUri(); this.type = resource.getType(); @@ -54,76 +52,27 @@ public class CachedResource implements Resource, Serializable { this.scopesIds = resource.getScopes().stream().map(Scope::getId).collect(Collectors.toSet()); } - public CachedResource(String id) { - this.id = id; - } - @Override - public String getId() { - return this.id; - } - - @Override public String getName() { return this.name; } - @Override - public void setName(String name) { - this.name = name; - } - - @Override public String getUri() { return this.uri; } - @Override - public void setUri(String uri) { - this.uri = uri; - } - - @Override public String getType() { return this.type; } - @Override - public void setType(String type) { - this.type = type; - } - - @Override - public List getScopes() { - throw new RuntimeException("Not implemented"); - } - - @Override public String getIconUri() { return this.iconUri; } - @Override - public void setIconUri(String iconUri) { - this.iconUri = iconUri; - } - - @Override - public ResourceServer getResourceServer() { - throw new RuntimeException("Not implemented"); - } - - @Override public String getOwner() { return this.owner; } - @Override - public void updateScopes(Set scopes) { - this.scopesIds.clear(); - this.scopesIds.addAll(scopes.stream().map(Scope::getId).collect(Collectors.toSet())); - } - public String getResourceServerId() { return this.resourceServerId; } diff --git a/model/infinispan/src/main/java/org/keycloak/models/authorization/infinispan/entities/CachedResourceServer.java b/model/infinispan/src/main/java/org/keycloak/models/cache/infinispan/authorization/entities/CachedResourceServer.java similarity index 64% rename from model/infinispan/src/main/java/org/keycloak/models/authorization/infinispan/entities/CachedResourceServer.java rename to model/infinispan/src/main/java/org/keycloak/models/cache/infinispan/authorization/entities/CachedResourceServer.java index 7dab0b20eb..7dfb5fbf86 100644 --- a/model/infinispan/src/main/java/org/keycloak/models/authorization/infinispan/entities/CachedResourceServer.java +++ b/model/infinispan/src/main/java/org/keycloak/models/cache/infinispan/authorization/entities/CachedResourceServer.java @@ -16,9 +16,10 @@ * limitations under the License. */ -package org.keycloak.models.authorization.infinispan.entities; +package org.keycloak.models.cache.infinispan.authorization.entities; import org.keycloak.authorization.model.ResourceServer; +import org.keycloak.models.cache.infinispan.entities.AbstractRevisioned; import org.keycloak.representations.idm.authorization.PolicyEnforcementMode; import java.io.Serializable; @@ -26,53 +27,30 @@ import java.io.Serializable; /** * @author Pedro Igor */ -public class CachedResourceServer implements ResourceServer, Serializable { +public class CachedResourceServer extends AbstractRevisioned { - private static final long serialVersionUID = 5054253390723121289L; - - private final String id; private String clientId; private boolean allowRemoteResourceManagement; private PolicyEnforcementMode policyEnforcementMode; - public CachedResourceServer(ResourceServer resourceServer) { - this.id = resourceServer.getId(); + public CachedResourceServer(Long revision, ResourceServer resourceServer) { + super(revision, resourceServer.getId()); this.clientId = resourceServer.getClientId(); this.allowRemoteResourceManagement = resourceServer.isAllowRemoteResourceManagement(); this.policyEnforcementMode = resourceServer.getPolicyEnforcementMode(); } - public CachedResourceServer(String id) { - this.id = id; - } - @Override - public String getId() { - return this.id; - } - - @Override public String getClientId() { return this.clientId; } - @Override public boolean isAllowRemoteResourceManagement() { return this.allowRemoteResourceManagement; } - @Override - public void setAllowRemoteResourceManagement(boolean allowRemoteResourceManagement) { - this.allowRemoteResourceManagement = allowRemoteResourceManagement; - } - - @Override public PolicyEnforcementMode getPolicyEnforcementMode() { return this.policyEnforcementMode; } - @Override - public void setPolicyEnforcementMode(PolicyEnforcementMode enforcementMode) { - this.policyEnforcementMode = enforcementMode; - } } diff --git a/model/infinispan/src/main/java/org/keycloak/models/authorization/infinispan/entities/CachedScope.java b/model/infinispan/src/main/java/org/keycloak/models/cache/infinispan/authorization/entities/CachedScope.java similarity index 56% rename from model/infinispan/src/main/java/org/keycloak/models/authorization/infinispan/entities/CachedScope.java rename to model/infinispan/src/main/java/org/keycloak/models/cache/infinispan/authorization/entities/CachedScope.java index 3e931d8e60..0519805c6a 100644 --- a/model/infinispan/src/main/java/org/keycloak/models/authorization/infinispan/entities/CachedScope.java +++ b/model/infinispan/src/main/java/org/keycloak/models/cache/infinispan/authorization/entities/CachedScope.java @@ -16,10 +16,11 @@ * limitations under the License. */ -package org.keycloak.models.authorization.infinispan.entities; +package org.keycloak.models.cache.infinispan.authorization.entities; import org.keycloak.authorization.model.ResourceServer; import org.keycloak.authorization.model.Scope; +import org.keycloak.models.cache.infinispan.entities.AbstractRevisioned; import java.io.Serializable; import java.util.Objects; @@ -27,70 +28,30 @@ import java.util.Objects; /** * @author Pedro Igor */ -public class CachedScope implements Scope, Serializable { +public class CachedScope extends AbstractRevisioned implements InResourceServer { - private static final long serialVersionUID = -3919706923417065454L; - - private final String id; private String resourceServerId; private String name; private String iconUri; - public CachedScope(Scope scope) { - this.id = scope.getId(); + public CachedScope(Long revision, Scope scope) { + super(revision, scope.getId()); this.name = scope.getName(); this.iconUri = scope.getIconUri(); this.resourceServerId = scope.getResourceServer().getId(); } - public CachedScope(String id) { - this.id = id; - } - - @Override - public String getId() { - return this.id; - } - - @Override public String getName() { return this.name; } - @Override - public void setName(String name) { - this.name = name; - } - - @Override public String getIconUri() { return this.iconUri; } @Override - public void setIconUri(String iconUri) { - this.iconUri = iconUri; - } - - @Override - public ResourceServer getResourceServer() { - throw new RuntimeException("Not implemented"); - } - public String getResourceServerId() { return this.resourceServerId; } - @Override - public boolean equals(Object o) { - if (this == o) return true; - if (o == null || !Scope.class.isInstance(o)) return false; - Scope that = (Scope) o; - return Objects.equals(id, that.getId()); - } - - @Override - public int hashCode() { - return Objects.hash(id); - } } diff --git a/model/infinispan/src/main/java/org/keycloak/models/cache/infinispan/authorization/entities/InResourceServer.java b/model/infinispan/src/main/java/org/keycloak/models/cache/infinispan/authorization/entities/InResourceServer.java new file mode 100644 index 0000000000..807588e44f --- /dev/null +++ b/model/infinispan/src/main/java/org/keycloak/models/cache/infinispan/authorization/entities/InResourceServer.java @@ -0,0 +1,25 @@ +/* + * 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.cache.infinispan.authorization.entities; + +/** + * @author Bill Burke + * @version $Revision: 1 $ + */ +public interface InResourceServer { + String getResourceServerId(); +} 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 new file mode 100755 index 0000000000..1cbf044d01 --- /dev/null +++ b/model/infinispan/src/main/java/org/keycloak/models/cache/infinispan/authorization/entities/PolicyListQuery.java @@ -0,0 +1,36 @@ +package org.keycloak.models.cache.infinispan.authorization.entities; + +import org.keycloak.models.cache.infinispan.entities.AbstractRevisioned; + +import java.util.HashSet; +import java.util.Set; + +/** + * @author Bill Burke + * @version $Revision: 1 $ + */ +public class PolicyListQuery extends AbstractRevisioned implements InResourceServer { + private final Set policies; + private final String serverId; + + public PolicyListQuery(Long revision, String id, String policyId, String serverId) { + super(revision, id); + this.serverId = serverId; + policies = new HashSet<>(); + policies.add(policyId); + } + public PolicyListQuery(Long revision, String id, Set policies, String serverId) { + super(revision, id); + this.serverId = serverId; + this.policies = policies; + } + + @Override + public String getResourceServerId() { + return serverId; + } + + public Set getPolicies() { + return policies; + } +} \ 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 new file mode 100755 index 0000000000..d322b62c02 --- /dev/null +++ b/model/infinispan/src/main/java/org/keycloak/models/cache/infinispan/authorization/entities/ResourceListQuery.java @@ -0,0 +1,36 @@ +package org.keycloak.models.cache.infinispan.authorization.entities; + +import org.keycloak.models.cache.infinispan.entities.AbstractRevisioned; + +import java.util.HashSet; +import java.util.Set; + +/** + * @author Bill Burke + * @version $Revision: 1 $ + */ +public class ResourceListQuery extends AbstractRevisioned implements InResourceServer { + private final Set resources; + private final String serverId; + + public ResourceListQuery(Long revision, String id, String resourceId, String serverId) { + super(revision, id); + this.serverId = serverId; + resources = new HashSet<>(); + resources.add(resourceId); + } + public ResourceListQuery(Long revision, String id, Set resources, String serverId) { + super(revision, id); + this.serverId = serverId; + this.resources = resources; + } + + @Override + public String getResourceServerId() { + return serverId; + } + + public Set getResources() { + return resources; + } +} \ No newline at end of file diff --git a/model/infinispan/src/main/java/org/keycloak/models/cache/infinispan/authorization/entities/ResourceServerListQuery.java b/model/infinispan/src/main/java/org/keycloak/models/cache/infinispan/authorization/entities/ResourceServerListQuery.java new file mode 100755 index 0000000000..7cfc122864 --- /dev/null +++ b/model/infinispan/src/main/java/org/keycloak/models/cache/infinispan/authorization/entities/ResourceServerListQuery.java @@ -0,0 +1,29 @@ +package org.keycloak.models.cache.infinispan.authorization.entities; + +import org.keycloak.models.cache.infinispan.entities.AbstractRevisioned; +import org.keycloak.models.cache.infinispan.entities.RealmQuery; + +import java.util.HashSet; +import java.util.Set; + +/** + * @author Bill Burke + * @version $Revision: 1 $ + */ +public class ResourceServerListQuery extends AbstractRevisioned { + private final Set servers; + + public ResourceServerListQuery(Long revision, String id, String serverId) { + super(revision, id); + servers = new HashSet<>(); + servers.add(serverId); + } + public ResourceServerListQuery(Long revision, String id, Set servers) { + super(revision, id); + this.servers = servers; + } + + public Set getResourceServers() { + return servers; + } +} \ No newline at end of file diff --git a/model/infinispan/src/main/java/org/keycloak/models/cache/infinispan/authorization/entities/ScopeListQuery.java b/model/infinispan/src/main/java/org/keycloak/models/cache/infinispan/authorization/entities/ScopeListQuery.java new file mode 100755 index 0000000000..2579e61b78 --- /dev/null +++ b/model/infinispan/src/main/java/org/keycloak/models/cache/infinispan/authorization/entities/ScopeListQuery.java @@ -0,0 +1,36 @@ +package org.keycloak.models.cache.infinispan.authorization.entities; + +import org.keycloak.models.cache.infinispan.entities.AbstractRevisioned; + +import java.util.HashSet; +import java.util.Set; + +/** + * @author Bill Burke + * @version $Revision: 1 $ + */ +public class ScopeListQuery extends AbstractRevisioned implements InResourceServer { + private final Set scopes; + private final String serverId; + + public ScopeListQuery(Long revision, String id, String scopeId, String serverId) { + super(revision, id); + this.serverId = serverId; + scopes = new HashSet<>(); + scopes.add(scopeId); + } + public ScopeListQuery(Long revision, String id, Set scopes, String serverId) { + super(revision, id); + this.serverId = serverId; + this.scopes = scopes; + } + + @Override + public String getResourceServerId() { + return serverId; + } + + public Set getScopes() { + return scopes; + } +} \ No newline at end of file diff --git a/model/infinispan/src/main/java/org/keycloak/models/authorization/infinispan/events/ResourceServerRemovedEvent.java b/model/infinispan/src/main/java/org/keycloak/models/cache/infinispan/authorization/events/AuthorizationCacheInvalidationEvent.java similarity index 58% rename from model/infinispan/src/main/java/org/keycloak/models/authorization/infinispan/events/ResourceServerRemovedEvent.java rename to model/infinispan/src/main/java/org/keycloak/models/cache/infinispan/authorization/events/AuthorizationCacheInvalidationEvent.java index 9fde6d9bc4..3f4add18f8 100644 --- a/model/infinispan/src/main/java/org/keycloak/models/authorization/infinispan/events/ResourceServerRemovedEvent.java +++ b/model/infinispan/src/main/java/org/keycloak/models/cache/infinispan/authorization/events/AuthorizationCacheInvalidationEvent.java @@ -14,23 +14,16 @@ * See the License for the specific language governing permissions and * limitations under the License. */ -package org.keycloak.models.authorization.infinispan.events; +package org.keycloak.models.cache.infinispan.authorization.events; -import java.util.Collections; +import org.keycloak.models.cache.infinispan.authorization.StoreFactoryCacheManager; + +import java.util.Set; /** - * @author Pedro Igor + * @author Bill Burke + * @version $Revision: 1 $ */ -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; - } +public interface AuthorizationCacheInvalidationEvent { + void addInvalidations(StoreFactoryCacheManager realmCache, Set invalidations); } 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 new file mode 100644 index 0000000000..3a721baa89 --- /dev/null +++ b/model/infinispan/src/main/java/org/keycloak/models/cache/infinispan/authorization/events/PolicyRemovedEvent.java @@ -0,0 +1,56 @@ +/* + * 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.cache.infinispan.authorization.events; + +import org.keycloak.models.cache.infinispan.authorization.StoreFactoryCacheManager; +import org.keycloak.models.cache.infinispan.events.InvalidationEvent; + +import java.util.Set; + +/** + * @author Marek Posolda + */ +public class PolicyRemovedEvent extends InvalidationEvent implements AuthorizationCacheInvalidationEvent { + + private String id; + private String name; + private String serverId; + + public static PolicyRemovedEvent create(String id, String name, String serverId) { + PolicyRemovedEvent event = new PolicyRemovedEvent(); + event.id = id; + event.name = name; + event.serverId = serverId; + return event; + } + + @Override + public String getId() { + return id; + } + + @Override + public String toString() { + return String.format("PolicyRemovedEvent [ id=%s, name=%s]", id, name); + } + + @Override + public void addInvalidations(StoreFactoryCacheManager cache, Set invalidations) { + cache.policyRemoval(id, name, 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 new file mode 100644 index 0000000000..1e1d9925bf --- /dev/null +++ b/model/infinispan/src/main/java/org/keycloak/models/cache/infinispan/authorization/events/PolicyUpdatedEvent.java @@ -0,0 +1,56 @@ +/* + * 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.cache.infinispan.authorization.events; + +import org.keycloak.models.cache.infinispan.authorization.StoreFactoryCacheManager; +import org.keycloak.models.cache.infinispan.events.InvalidationEvent; + +import java.util.Set; + +/** + * @author Marek Posolda + */ +public class PolicyUpdatedEvent extends InvalidationEvent implements AuthorizationCacheInvalidationEvent { + + private String id; + private String name; + private String serverId; + + public static PolicyUpdatedEvent create(String id, String name, String serverId) { + PolicyUpdatedEvent event = new PolicyUpdatedEvent(); + event.id = id; + event.name = name; + event.serverId = serverId; + return event; + } + + @Override + public String getId() { + return id; + } + + @Override + public String toString() { + return String.format("PolicyUpdatedEvent [ id=%s, name=%s ]", id, name); + } + + @Override + public void addInvalidations(StoreFactoryCacheManager cache, Set invalidations) { + cache.policyUpdated(id, name, serverId, invalidations); + } +} diff --git a/model/infinispan/src/main/java/org/keycloak/models/cache/infinispan/authorization/events/ResourceRemovedEvent.java b/model/infinispan/src/main/java/org/keycloak/models/cache/infinispan/authorization/events/ResourceRemovedEvent.java new file mode 100644 index 0000000000..4e1a31fbd0 --- /dev/null +++ b/model/infinispan/src/main/java/org/keycloak/models/cache/infinispan/authorization/events/ResourceRemovedEvent.java @@ -0,0 +1,56 @@ +/* + * 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.cache.infinispan.authorization.events; + +import org.keycloak.models.cache.infinispan.authorization.StoreFactoryCacheManager; +import org.keycloak.models.cache.infinispan.events.InvalidationEvent; + +import java.util.Set; + +/** + * @author Marek Posolda + */ +public class ResourceRemovedEvent extends InvalidationEvent implements AuthorizationCacheInvalidationEvent { + + private String id; + private String name; + private String serverId; + + public static ResourceRemovedEvent create(String id, String name, String serverId) { + ResourceRemovedEvent event = new ResourceRemovedEvent(); + event.id = id; + event.name = name; + event.serverId = serverId; + return event; + } + + @Override + public String getId() { + return id; + } + + @Override + public String toString() { + return String.format("ResourceRemovedEvent [ id=%s, name=%s ]", id, name); + } + + @Override + public void addInvalidations(StoreFactoryCacheManager cache, Set invalidations) { + cache.resourceRemoval(id, name, serverId, invalidations); + } +} diff --git a/model/infinispan/src/main/java/org/keycloak/models/cache/infinispan/authorization/events/ResourceServerRemovedEvent.java b/model/infinispan/src/main/java/org/keycloak/models/cache/infinispan/authorization/events/ResourceServerRemovedEvent.java new file mode 100644 index 0000000000..fbe5a7aa48 --- /dev/null +++ b/model/infinispan/src/main/java/org/keycloak/models/cache/infinispan/authorization/events/ResourceServerRemovedEvent.java @@ -0,0 +1,54 @@ +/* + * 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.cache.infinispan.authorization.events; + +import org.keycloak.models.cache.infinispan.authorization.StoreFactoryCacheManager; +import org.keycloak.models.cache.infinispan.events.InvalidationEvent; + +import java.util.Set; + +/** + * @author Marek Posolda + */ +public class ResourceServerRemovedEvent extends InvalidationEvent implements AuthorizationCacheInvalidationEvent { + + private String id; + private String clientId; + + public static ResourceServerRemovedEvent create(String id, String clientId) { + ResourceServerRemovedEvent event = new ResourceServerRemovedEvent(); + event.id = id; + event.clientId = clientId; + return event; + } + + @Override + public String getId() { + return id; + } + + @Override + public String toString() { + return String.format("ResourceServerRemovedEvent [ id=%s, clientId=%s ]", id, clientId); + } + + @Override + public void addInvalidations(StoreFactoryCacheManager cache, Set invalidations) { + cache.resourceServerRemoval(id, clientId, invalidations); + } +} diff --git a/model/infinispan/src/main/java/org/keycloak/models/cache/infinispan/authorization/events/ResourceServerUpdatedEvent.java b/model/infinispan/src/main/java/org/keycloak/models/cache/infinispan/authorization/events/ResourceServerUpdatedEvent.java new file mode 100644 index 0000000000..2034c9b4d6 --- /dev/null +++ b/model/infinispan/src/main/java/org/keycloak/models/cache/infinispan/authorization/events/ResourceServerUpdatedEvent.java @@ -0,0 +1,54 @@ +/* + * 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.cache.infinispan.authorization.events; + +import org.keycloak.models.cache.infinispan.authorization.StoreFactoryCacheManager; +import org.keycloak.models.cache.infinispan.events.InvalidationEvent; + +import java.util.Set; + +/** + * @author Marek Posolda + */ +public class ResourceServerUpdatedEvent extends InvalidationEvent implements AuthorizationCacheInvalidationEvent { + + private String id; + private String clientId; + + public static ResourceServerUpdatedEvent create(String id, String clientId) { + ResourceServerUpdatedEvent event = new ResourceServerUpdatedEvent(); + event.id = id; + event.clientId = clientId; + return event; + } + + @Override + public String getId() { + return id; + } + + @Override + public String toString() { + return String.format("ResourceServerRemovedEvent [ id=%s, clientId=%s ]", id, clientId); + } + + @Override + public void addInvalidations(StoreFactoryCacheManager cache, Set invalidations) { + cache.resourceServerUpdated(id, clientId, invalidations); + } +} diff --git a/model/infinispan/src/main/java/org/keycloak/models/cache/infinispan/authorization/events/ResourceUpdatedEvent.java b/model/infinispan/src/main/java/org/keycloak/models/cache/infinispan/authorization/events/ResourceUpdatedEvent.java new file mode 100644 index 0000000000..113d5e0a53 --- /dev/null +++ b/model/infinispan/src/main/java/org/keycloak/models/cache/infinispan/authorization/events/ResourceUpdatedEvent.java @@ -0,0 +1,56 @@ +/* + * 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.cache.infinispan.authorization.events; + +import org.keycloak.models.cache.infinispan.authorization.StoreFactoryCacheManager; +import org.keycloak.models.cache.infinispan.events.InvalidationEvent; + +import java.util.Set; + +/** + * @author Marek Posolda + */ +public class ResourceUpdatedEvent extends InvalidationEvent implements AuthorizationCacheInvalidationEvent { + + private String id; + private String name; + private String serverId; + + public static ResourceUpdatedEvent create(String id, String name, String serverId) { + ResourceUpdatedEvent event = new ResourceUpdatedEvent(); + event.id = id; + event.name = name; + event.serverId = serverId; + return event; + } + + @Override + public String getId() { + return id; + } + + @Override + public String toString() { + return String.format("ResourceUpdatedEvent [ id=%s, name=%s ]", id, name); + } + + @Override + public void addInvalidations(StoreFactoryCacheManager cache, Set invalidations) { + cache.resourceUpdated(id, name, serverId, invalidations); + } +} diff --git a/model/infinispan/src/main/java/org/keycloak/models/cache/infinispan/authorization/events/ScopeRemovedEvent.java b/model/infinispan/src/main/java/org/keycloak/models/cache/infinispan/authorization/events/ScopeRemovedEvent.java new file mode 100644 index 0000000000..eb2747c7a4 --- /dev/null +++ b/model/infinispan/src/main/java/org/keycloak/models/cache/infinispan/authorization/events/ScopeRemovedEvent.java @@ -0,0 +1,56 @@ +/* + * 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.cache.infinispan.authorization.events; + +import org.keycloak.models.cache.infinispan.authorization.StoreFactoryCacheManager; +import org.keycloak.models.cache.infinispan.events.InvalidationEvent; + +import java.util.Set; + +/** + * @author Marek Posolda + */ +public class ScopeRemovedEvent extends InvalidationEvent implements AuthorizationCacheInvalidationEvent { + + private String id; + private String name; + private String serverId; + + public static ScopeRemovedEvent create(String id, String name, String serverId) { + ScopeRemovedEvent event = new ScopeRemovedEvent(); + event.id = id; + event.name = name; + event.serverId = serverId; + return event; + } + + @Override + public String getId() { + return id; + } + + @Override + public String toString() { + return String.format("ScopeRemovedEvent [ id=%s, name=%s]", id, name); + } + + @Override + public void addInvalidations(StoreFactoryCacheManager cache, Set invalidations) { + cache.scopeRemoval(id, name, serverId, invalidations); + } +} diff --git a/model/infinispan/src/main/java/org/keycloak/models/cache/infinispan/authorization/events/ScopeUpdatedEvent.java b/model/infinispan/src/main/java/org/keycloak/models/cache/infinispan/authorization/events/ScopeUpdatedEvent.java new file mode 100644 index 0000000000..9fbbd443bb --- /dev/null +++ b/model/infinispan/src/main/java/org/keycloak/models/cache/infinispan/authorization/events/ScopeUpdatedEvent.java @@ -0,0 +1,56 @@ +/* + * 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.cache.infinispan.authorization.events; + +import org.keycloak.models.cache.infinispan.authorization.StoreFactoryCacheManager; +import org.keycloak.models.cache.infinispan.events.InvalidationEvent; + +import java.util.Set; + +/** + * @author Marek Posolda + */ +public class ScopeUpdatedEvent extends InvalidationEvent implements AuthorizationCacheInvalidationEvent { + + private String id; + private String name; + private String serverId; + + public static ScopeUpdatedEvent create(String id, String name, String serverId) { + ScopeUpdatedEvent event = new ScopeUpdatedEvent(); + event.id = id; + event.name = name; + event.serverId = serverId; + return event; + } + + @Override + public String getId() { + return id; + } + + @Override + public String toString() { + return String.format("ScopeUpdatedEvent [ id=%s, name=%s ]", id, name); + } + + @Override + public void addInvalidations(StoreFactoryCacheManager cache, Set invalidations) { + cache.scopeUpdated(id, name, serverId, invalidations); + } +} diff --git a/model/infinispan/src/main/java/org/keycloak/models/cache/infinispan/authorization/stream/InResourceServerPredicate.java b/model/infinispan/src/main/java/org/keycloak/models/cache/infinispan/authorization/stream/InResourceServerPredicate.java new file mode 100755 index 0000000000..46782b85f6 --- /dev/null +++ b/model/infinispan/src/main/java/org/keycloak/models/cache/infinispan/authorization/stream/InResourceServerPredicate.java @@ -0,0 +1,35 @@ +package org.keycloak.models.cache.infinispan.authorization.stream; + +import org.keycloak.models.cache.infinispan.authorization.entities.InResourceServer; +import org.keycloak.models.cache.infinispan.entities.InRealm; +import org.keycloak.models.cache.infinispan.entities.Revisioned; + +import java.io.Serializable; +import java.util.Map; +import java.util.function.Predicate; + +/** + * @author Bill Burke + * @version $Revision: 1 $ + */ +public class InResourceServerPredicate implements Predicate>, Serializable { + private String serverId; + + public static InResourceServerPredicate create() { + return new InResourceServerPredicate(); + } + + public InResourceServerPredicate resourceServer(String id) { + serverId = id; + return this; + } + + @Override + public boolean test(Map.Entry entry) { + Object value = entry.getValue(); + if (value == null) return false; + if (!(value instanceof InResourceServer)) return false; + + return serverId.equals(((InResourceServer)value).getResourceServerId()); + } +} diff --git a/model/infinispan/src/main/resources/META-INF/services/org.keycloak.models.cache.authorization.CachedStoreProviderFactory b/model/infinispan/src/main/resources/META-INF/services/org.keycloak.models.cache.authorization.CachedStoreProviderFactory index 0a46bb3ad2..d9bed49c9b 100644 --- a/model/infinispan/src/main/resources/META-INF/services/org.keycloak.models.cache.authorization.CachedStoreProviderFactory +++ b/model/infinispan/src/main/resources/META-INF/services/org.keycloak.models.cache.authorization.CachedStoreProviderFactory @@ -16,4 +16,4 @@ # limitations under the License. # -org.keycloak.models.authorization.infinispan.InfinispanStoreProviderFactory \ No newline at end of file +org.keycloak.models.cache.infinispan.authorization.InfinispanCacheStoreFactoryProviderFactory \ No newline at end of file diff --git a/model/jpa/src/main/java/org/keycloak/authorization/jpa/entities/PolicyEntity.java b/model/jpa/src/main/java/org/keycloak/authorization/jpa/entities/PolicyEntity.java index 0305ae50d1..dc11724e6c 100644 --- a/model/jpa/src/main/java/org/keycloak/authorization/jpa/entities/PolicyEntity.java +++ b/model/jpa/src/main/java/org/keycloak/authorization/jpa/entities/PolicyEntity.java @@ -35,6 +35,8 @@ import javax.persistence.JoinColumn; import javax.persistence.JoinTable; import javax.persistence.ManyToOne; import javax.persistence.MapKeyColumn; +import javax.persistence.NamedQueries; +import javax.persistence.NamedQuery; import javax.persistence.OneToMany; import javax.persistence.Table; import javax.persistence.UniqueConstraint; @@ -52,11 +54,25 @@ import org.keycloak.representations.idm.authorization.Logic; @Table(name = "RESOURCE_SERVER_POLICY", uniqueConstraints = { @UniqueConstraint(columnNames = {"NAME", "RESOURCE_SERVER_ID"}) }) -public class PolicyEntity implements Policy { +@NamedQueries( + { + @NamedQuery(name="findPolicyIdByServerId", query="select p.id from PolicyEntity p where p.resourceServer.id = :serverId "), + @NamedQuery(name="findPolicyIdByName", query="select p.id from PolicyEntity p where p.resourceServer.id = :serverId and p.name = :name"), + @NamedQuery(name="findPolicyIdByResource", query="select p.id from PolicyEntity p inner join p.resources r where p.resourceServer.id = :serverId and (r.resourceServer.id = :serverId and r.id = :resourceId)"), + @NamedQuery(name="findPolicyIdByScope", query="select pe.id from PolicyEntity pe where pe.resourceServer.id = :serverId and pe.id IN (select p.id from ScopeEntity s inner join s.policies p where s.resourceServer.id = :serverId and (p.resourceServer.id = :serverId and p.type = 'scope' and s.id in (:scopeIds)))"), + @NamedQuery(name="findPolicyIdByType", query="select p.id from PolicyEntity p where p.resourceServer.id = :serverId and p.type = :type"), + @NamedQuery(name="findPolicyIdByResourceType", query="select p.id from PolicyEntity p inner join p.config c where p.resourceServer.id = :serverId and KEY(c) = 'defaultResourceType' and c like :type"), + @NamedQuery(name="findPolicyIdByDependentPolices", query="select p.id from PolicyEntity p inner join p.associatedPolicies ap where p.resourceServer.id = :serverId and (ap.resourceServer.id = :serverId and ap.id = :policyId)"), + @NamedQuery(name="deletePolicyByResourceServer", query="delete from PolicyEntity p where p.resourceServer.id = :serverId") + } +) + +public class PolicyEntity { @Id - @Column(name="ID", length = 36) - @Access(AccessType.PROPERTY) // we do this because relationships often fetch id, but not entity. This avoids an extra SQL + @Column(name = "ID", length = 36) + @Access(AccessType.PROPERTY) + // we do this because relationships often fetch id, but not entity. This avoids an extra SQL private String id; @Column(name = "NAME") @@ -75,9 +91,9 @@ public class PolicyEntity implements Policy { private Logic logic = Logic.POSITIVE; @ElementCollection(fetch = FetchType.LAZY) - @MapKeyColumn(name="NAME") - @Column(name="VALUE", columnDefinition = "TEXT") - @CollectionTable(name="POLICY_CONFIG", joinColumns={ @JoinColumn(name="POLICY_ID") }) + @MapKeyColumn(name = "NAME") + @Column(name = "VALUE", columnDefinition = "TEXT") + @CollectionTable(name = "POLICY_CONFIG", joinColumns = {@JoinColumn(name = "POLICY_ID")}) private Map config = new HashMap(); @ManyToOne(optional = false, fetch = FetchType.LAZY) @@ -96,7 +112,6 @@ public class PolicyEntity implements Policy { @JoinTable(name = "SCOPE_POLICY", joinColumns = @JoinColumn(name = "POLICY_ID"), inverseJoinColumns = @JoinColumn(name = "SCOPE_ID")) private Set scopes = new HashSet<>(); - @Override public String getId() { return this.id; } @@ -105,7 +120,6 @@ public class PolicyEntity implements Policy { this.id = id; } - @Override public String getType() { return this.type; } @@ -114,57 +128,46 @@ public class PolicyEntity implements Policy { this.type = type; } - @Override public DecisionStrategy getDecisionStrategy() { return this.decisionStrategy; } - @Override public void setDecisionStrategy(DecisionStrategy decisionStrategy) { this.decisionStrategy = decisionStrategy; } - @Override public Logic getLogic() { return this.logic; } - @Override public void setLogic(Logic logic) { this.logic = logic; } - @Override public Map getConfig() { return this.config; } - @Override public void setConfig(Map config) { this.config = config; } - @Override public String getName() { return this.name; } - @Override public void setName(String name) { this.name = name; } - @Override public String getDescription() { return this.description; } - @Override public void setDescription(String description) { this.description = description; } - @Override public ResourceServerEntity getResourceServer() { return this.resourceServer; } @@ -173,16 +176,6 @@ public class PolicyEntity implements Policy { this.resourceServer = resourceServer; } - @Override - public

Set

getAssociatedPolicies() { - return (Set

) this.associatedPolicies; - } - - public void setAssociatedPolicies(Set associatedPolicies) { - this.associatedPolicies = associatedPolicies; - } - - @Override public Set getResources() { return this.resources; } @@ -191,7 +184,6 @@ public class PolicyEntity implements Policy { this.resources = resources; } - @Override public Set getScopes() { return this.scopes; } @@ -200,54 +192,26 @@ public class PolicyEntity implements Policy { this.scopes = scopes; } - @Override - public void addScope(Scope scope) { - getScopes().add((ScopeEntity) scope); + public Set getAssociatedPolicies() { + return associatedPolicies; } - @Override - public void removeScope(Scope scope) { - getScopes().remove(scope); - } - - @Override - public void addAssociatedPolicy(Policy associatedPolicy) { - getAssociatedPolicies().add(associatedPolicy); - } - - @Override - public void removeAssociatedPolicy(Policy associatedPolicy) { - getAssociatedPolicies().remove(associatedPolicy); - } - - @Override - public void addResource(Resource resource) { - getResources().add((ResourceEntity) resource); - } - - @Override - public void removeResource(Resource resource) { - getResources().remove(resource); + public void setAssociatedPolicies(Set associatedPolicies) { + this.associatedPolicies = associatedPolicies; } @Override public boolean equals(Object o) { - if (o == this) return true; + if (this == o) return true; + if (o == null || getClass() != o.getClass()) return false; - if (this.id == null) return false; - - if (!Policy.class.isInstance(o)) return false; - - Policy that = (Policy) o; - - if (!getId().equals(that.getId())) return false; - - return true; + PolicyEntity that = (PolicyEntity) o; + return getId().equals(that.getId()); } @Override public int hashCode() { - return id!=null ? id.hashCode() : super.hashCode(); + return getId().hashCode(); } } diff --git a/model/jpa/src/main/java/org/keycloak/authorization/jpa/entities/ResourceEntity.java b/model/jpa/src/main/java/org/keycloak/authorization/jpa/entities/ResourceEntity.java index 29b5740d0c..a71745725f 100644 --- a/model/jpa/src/main/java/org/keycloak/authorization/jpa/entities/ResourceEntity.java +++ b/model/jpa/src/main/java/org/keycloak/authorization/jpa/entities/ResourceEntity.java @@ -31,10 +31,13 @@ import javax.persistence.JoinColumn; import javax.persistence.JoinTable; import javax.persistence.ManyToMany; import javax.persistence.ManyToOne; +import javax.persistence.NamedQueries; +import javax.persistence.NamedQuery; import javax.persistence.Table; import javax.persistence.UniqueConstraint; import java.util.ArrayList; import java.util.HashSet; +import java.util.LinkedList; import java.util.List; import java.util.Set; @@ -45,7 +48,18 @@ import java.util.Set; @Table(name = "RESOURCE_SERVER_RESOURCE", uniqueConstraints = { @UniqueConstraint(columnNames = {"NAME", "RESOURCE_SERVER_ID", "OWNER"}) }) -public class ResourceEntity implements Resource { +@NamedQueries( + { + @NamedQuery(name="findResourceIdByOwner", query="select r.id from ResourceEntity r where r.resourceServer.id = :serverId and r.owner = :owner"), + @NamedQuery(name="findResourceIdByUri", query="select r.id from ResourceEntity r where r.resourceServer.id = :serverId and r.uri = :uri"), + @NamedQuery(name="findResourceIdByName", query="select r.id from ResourceEntity r where r.resourceServer.id = :serverId and r.name = :name"), + @NamedQuery(name="findResourceIdByType", query="select r.id from ResourceEntity r where r.resourceServer.id = :serverId and r.type = :type"), + @NamedQuery(name="findResourceIdByServerId", query="select r.id from ResourceEntity r where r.resourceServer.id = :serverId "), + @NamedQuery(name="findResourceIdByScope", query="select r.id from ResourceEntity r inner join r.scopes s where r.resourceServer.id = :serverId and (s.resourceServer.id = :serverId and s.id in (:scopeIds))"), + @NamedQuery(name="deleteResourceByResourceServer", query="delete from ResourceEntity r where r.resourceServer.id = :serverId") + } +) +public class ResourceEntity { @Id @Column(name="ID", length = 36) @@ -73,13 +87,12 @@ public class ResourceEntity implements Resource { @ManyToMany(fetch = FetchType.LAZY, cascade = {}) @JoinTable(name = "RESOURCE_SCOPE", joinColumns = @JoinColumn(name = "RESOURCE_ID"), inverseJoinColumns = @JoinColumn(name = "SCOPE_ID")) - private List scopes = new ArrayList<>(); + private List scopes = new LinkedList<>(); @ManyToMany(fetch = FetchType.LAZY, cascade = {}) @JoinTable(name = "RESOURCE_POLICY", joinColumns = @JoinColumn(name = "RESOURCE_ID"), inverseJoinColumns = @JoinColumn(name = "POLICY_ID")) - private List policies = new ArrayList<>(); + private List policies = new LinkedList<>(); - @Override public String getId() { return id; } @@ -88,52 +101,42 @@ public class ResourceEntity implements Resource { this.id = id; } - @Override public String getName() { return name; } - @Override public void setName(String name) { this.name = name; } - @Override public String getUri() { return uri; } - @Override public void setUri(String uri) { this.uri = uri; } - @Override public String getType() { return type; } - @Override public void setType(String type) { this.type = type; } - @Override public List getScopes() { return this.scopes; } - @Override public String getIconUri() { return iconUri; } - @Override public void setIconUri(String iconUri) { this.iconUri = iconUri; } - @Override public ResourceServerEntity getResourceServer() { return resourceServer; } @@ -154,37 +157,23 @@ public class ResourceEntity implements Resource { return this.policies; } - public void updateScopes(Set toUpdate) { - for (Scope scope : toUpdate) { - boolean hasScope = false; - - for (Scope existingScope : this.scopes) { - if (existingScope.equals(scope)) { - hasScope = true; - } - } - - if (!hasScope) { - this.scopes.add((ScopeEntity) scope); - } - } - - for (Scope scopeModel : new HashSet(this.scopes)) { - boolean hasScope = false; - - for (Scope scope : toUpdate) { - if (scopeModel.equals(scope)) { - hasScope = true; - } - } - - if (!hasScope) { - this.scopes.remove(scopeModel); - } - } - } public void setPolicies(List policies) { this.policies = policies; } + + @Override + public boolean equals(Object o) { + if (this == o) return true; + if (o == null || getClass() != o.getClass()) return false; + + ResourceEntity that = (ResourceEntity) o; + + return getId().equals(that.getId()); + } + + @Override + public int hashCode() { + return getId().hashCode(); + } } diff --git a/model/jpa/src/main/java/org/keycloak/authorization/jpa/entities/ResourceServerEntity.java b/model/jpa/src/main/java/org/keycloak/authorization/jpa/entities/ResourceServerEntity.java index a0be18ae02..9ee63b679d 100644 --- a/model/jpa/src/main/java/org/keycloak/authorization/jpa/entities/ResourceServerEntity.java +++ b/model/jpa/src/main/java/org/keycloak/authorization/jpa/entities/ResourceServerEntity.java @@ -26,6 +26,8 @@ import javax.persistence.AccessType; import javax.persistence.Column; import javax.persistence.Entity; import javax.persistence.Id; +import javax.persistence.NamedQueries; +import javax.persistence.NamedQuery; import javax.persistence.OneToMany; import javax.persistence.Table; import javax.persistence.UniqueConstraint; @@ -36,7 +38,12 @@ import java.util.List; */ @Entity @Table(name = "RESOURCE_SERVER", uniqueConstraints = {@UniqueConstraint(columnNames = "CLIENT_ID")}) -public class ResourceServerEntity implements ResourceServer { +@NamedQueries( + { + @NamedQuery(name="findResourceServerIdByClient", query="select r.id from ResourceServerEntity r where r.clientId = :clientId"), + } +) +public class ResourceServerEntity { @Id @Column(name="ID", length = 36) @@ -58,7 +65,6 @@ public class ResourceServerEntity implements ResourceServer { @OneToMany (mappedBy = "resourceServer") private List scopes; - @Override public String getId() { return this.id; } @@ -67,7 +73,6 @@ public class ResourceServerEntity implements ResourceServer { this.id = id; } - @Override public String getClientId() { return this.clientId; } @@ -76,22 +81,18 @@ public class ResourceServerEntity implements ResourceServer { this.clientId = clientId; } - @Override public boolean isAllowRemoteResourceManagement() { return this.allowRemoteResourceManagement; } - @Override public void setAllowRemoteResourceManagement(boolean allowRemoteResourceManagement) { this.allowRemoteResourceManagement = allowRemoteResourceManagement; } - @Override public PolicyEnforcementMode getPolicyEnforcementMode() { return this.policyEnforcementMode; } - @Override public void setPolicyEnforcementMode(PolicyEnforcementMode policyEnforcementMode) { this.policyEnforcementMode = policyEnforcementMode; } @@ -111,4 +112,19 @@ public class ResourceServerEntity implements ResourceServer { public void setScopes(final List scopes) { this.scopes = scopes; } + + @Override + public boolean equals(Object o) { + if (this == o) return true; + if (o == null || getClass() != o.getClass()) return false; + + ResourceServerEntity that = (ResourceServerEntity) o; + + return getId().equals(that.getId()); + } + + @Override + public int hashCode() { + return getId().hashCode(); + } } diff --git a/model/jpa/src/main/java/org/keycloak/authorization/jpa/entities/ScopeEntity.java b/model/jpa/src/main/java/org/keycloak/authorization/jpa/entities/ScopeEntity.java index 523f38a95c..c1619f9d8b 100644 --- a/model/jpa/src/main/java/org/keycloak/authorization/jpa/entities/ScopeEntity.java +++ b/model/jpa/src/main/java/org/keycloak/authorization/jpa/entities/ScopeEntity.java @@ -31,6 +31,8 @@ import javax.persistence.JoinColumn; import javax.persistence.JoinTable; import javax.persistence.ManyToMany; import javax.persistence.ManyToOne; +import javax.persistence.NamedQueries; +import javax.persistence.NamedQuery; import javax.persistence.Table; import javax.persistence.UniqueConstraint; import java.util.ArrayList; @@ -44,7 +46,14 @@ import java.util.Objects; @Table(name = "RESOURCE_SERVER_SCOPE", uniqueConstraints = { @UniqueConstraint(columnNames = {"NAME", "RESOURCE_SERVER_ID"}) }) -public class ScopeEntity implements Scope { +@NamedQueries( + { + @NamedQuery(name="findScopeIdByName", query="select s.id from ScopeEntity s where s.resourceServer.id = :serverId and s.name = :name"), + @NamedQuery(name="findScopeIdByResourceServer", query="select s.id from ScopeEntity s where s.resourceServer.id = :serverId"), + @NamedQuery(name="deleteScopeByResourceServer", query="delete from ScopeEntity s where s.resourceServer.id = :serverId") + } +) +public class ScopeEntity { @Id @Column(name="ID", length = 36) @@ -65,7 +74,6 @@ public class ScopeEntity implements Scope { @JoinTable(name = "SCOPE_POLICY", joinColumns = @JoinColumn(name = "SCOPE_ID"), inverseJoinColumns = @JoinColumn(name = "POLICY_ID")) private List policies = new ArrayList<>(); - @Override public String getId() { return id; } @@ -74,33 +82,28 @@ public class ScopeEntity implements Scope { this.id = id; } - @Override public String getName() { return name; } - @Override public void setName(String name) { this.name = name; } - @Override public String getIconUri() { return iconUri; } - @Override public void setIconUri(String iconUri) { this.iconUri = iconUri; } - @Override public ResourceServerEntity getResourceServer() { return resourceServer; } - public List getPolicies() { - return this.policies; + public List getPolicies() { + return policies; } public void setPolicies(List policies) { @@ -114,13 +117,15 @@ public class ScopeEntity implements Scope { @Override public boolean equals(Object o) { if (this == o) return true; - if (o == null || !Scope.class.isInstance(o)) return false; - Scope that = (Scope) o; - return Objects.equals(id, that.getId()); + if (o == null || getClass() != o.getClass()) return false; + + ScopeEntity that = (ScopeEntity) o; + + return getId().equals(that.getId()); } @Override public int hashCode() { - return Objects.hash(id); + return getId().hashCode(); } } diff --git a/model/jpa/src/main/java/org/keycloak/authorization/jpa/store/JPAAuthorizationStoreFactory.java b/model/jpa/src/main/java/org/keycloak/authorization/jpa/store/JPAAuthorizationStoreFactory.java index 87508071cc..9f17606a71 100644 --- a/model/jpa/src/main/java/org/keycloak/authorization/jpa/store/JPAAuthorizationStoreFactory.java +++ b/model/jpa/src/main/java/org/keycloak/authorization/jpa/store/JPAAuthorizationStoreFactory.java @@ -32,8 +32,9 @@ import org.keycloak.models.KeycloakSession; */ public class JPAAuthorizationStoreFactory implements AuthorizationStoreFactory { @Override - public StoreFactory create(KeycloakSession session) { - return new JPAStoreFactory(getEntityManager(session)); + public StoreFactory create(KeycloakSession session) { + AuthorizationProvider provider = session.getProvider(AuthorizationProvider.class); + return new JPAStoreFactory(getEntityManager(session), provider); } @Override diff --git a/model/jpa/src/main/java/org/keycloak/authorization/jpa/store/JPAPolicyStore.java b/model/jpa/src/main/java/org/keycloak/authorization/jpa/store/JPAPolicyStore.java index c6671de0e5..eb350be7ae 100644 --- a/model/jpa/src/main/java/org/keycloak/authorization/jpa/store/JPAPolicyStore.java +++ b/model/jpa/src/main/java/org/keycloak/authorization/jpa/store/JPAPolicyStore.java @@ -19,23 +19,29 @@ package org.keycloak.authorization.jpa.store; import java.util.ArrayList; import java.util.Collections; +import java.util.LinkedList; import java.util.List; import java.util.Map; import javax.persistence.EntityManager; import javax.persistence.NoResultException; import javax.persistence.Query; +import javax.persistence.TypedQuery; import javax.persistence.criteria.CriteriaBuilder; import javax.persistence.criteria.CriteriaQuery; import javax.persistence.criteria.Predicate; import javax.persistence.criteria.Root; +import org.keycloak.authorization.AuthorizationProvider; import org.keycloak.authorization.jpa.entities.PolicyEntity; import org.keycloak.authorization.jpa.entities.ResourceServerEntity; import org.keycloak.authorization.model.Policy; +import org.keycloak.authorization.model.Resource; import org.keycloak.authorization.model.ResourceServer; import org.keycloak.authorization.store.PolicyStore; +import org.keycloak.authorization.store.StoreFactory; import org.keycloak.models.utils.KeycloakModelUtils; +import org.keycloak.models.utils.RepresentationToModel; import org.keycloak.representations.idm.authorization.AbstractPolicyRepresentation; /** @@ -44,9 +50,10 @@ import org.keycloak.representations.idm.authorization.AbstractPolicyRepresentati public class JPAPolicyStore implements PolicyStore { private final EntityManager entityManager; - - public JPAPolicyStore(EntityManager entityManager) { + private final AuthorizationProvider provider; + public JPAPolicyStore(EntityManager entityManager, AuthorizationProvider provider) { this.entityManager = entityManager; + this.provider = provider; } @Override @@ -56,17 +63,17 @@ public class JPAPolicyStore implements PolicyStore { entity.setId(KeycloakModelUtils.generateId()); entity.setType(representation.getType()); entity.setName(representation.getName()); - entity.setResourceServer((ResourceServerEntity) resourceServer); + entity.setResourceServer(ResourceServerAdapter.toEntity(entityManager, resourceServer)); this.entityManager.persist(entity); this.entityManager.flush(); - return entity; + Policy model = new PolicyAdapter(entity, entityManager, provider.getStoreFactory()); + return model; } @Override public void delete(String id) { - Policy policy = entityManager.find(PolicyEntity.class, id); - + PolicyEntity policy = entityManager.find(PolicyEntity.class, id); if (policy != null) { this.entityManager.remove(policy); } @@ -79,39 +86,38 @@ public class JPAPolicyStore implements PolicyStore { return null; } - if (resourceServerId == null) { - return entityManager.find(PolicyEntity.class, id); - } + PolicyEntity entity = entityManager.find(PolicyEntity.class, id); + if (entity == null) return null; - Query query = entityManager.createQuery("from PolicyEntity where resourceServer.id = :serverId and id = :id"); - - query.setParameter("serverId", resourceServerId); - query.setParameter("id", id); - - return entityManager.find(PolicyEntity.class, id); + return new PolicyAdapter(entity, entityManager, provider.getStoreFactory()); } @Override public Policy findByName(String name, String resourceServerId) { + TypedQuery query = entityManager.createNamedQuery("findPolicyIdByName", String.class); + + query.setParameter("serverId", resourceServerId); + query.setParameter("name", name); try { - Query query = entityManager.createQuery("from PolicyEntity where name = :name and resourceServer.id = :serverId"); - - query.setParameter("name", name); - query.setParameter("serverId", resourceServerId); - - return (Policy) query.getSingleResult(); - } catch (NoResultException nre) { + String id = query.getSingleResult(); + return provider.getStoreFactory().getPolicyStore().findById(id, resourceServerId); + } catch (NoResultException ex) { return null; } } @Override public List findByResourceServer(final String resourceServerId) { - Query query = entityManager.createQuery("from PolicyEntity where resourceServer.id = :serverId"); + TypedQuery query = entityManager.createNamedQuery("findPolicyIdByServerId", String.class); query.setParameter("serverId", resourceServerId); - return query.getResultList(); + List result = query.getResultList(); + List list = new LinkedList<>(); + for (String id : result) { + list.add(provider.getStoreFactory().getPolicyStore().findById(id, resourceServerId)); + } + return list; } @Override @@ -120,6 +126,7 @@ public class JPAPolicyStore implements PolicyStore { CriteriaQuery querybuilder = builder.createQuery(PolicyEntity.class); Root root = querybuilder.from(PolicyEntity.class); List predicates = new ArrayList(); + querybuilder.select(root.get("id")); predicates.add(builder.equal(root.get("resourceServer").get("id"), resourceServerId)); @@ -148,27 +155,42 @@ public class JPAPolicyStore implements PolicyStore { query.setMaxResults(maxResult); } - return query.getResultList(); + List result = query.getResultList(); + List list = new LinkedList<>(); + for (String id : result) { + list.add(provider.getStoreFactory().getPolicyStore().findById(id, resourceServerId)); + } + return list; } @Override public List findByResource(final String resourceId, String resourceServerId) { - Query query = entityManager.createQuery("select p from PolicyEntity p inner join p.resources r where p.resourceServer.id = :serverId and (r.resourceServer.id = :serverId and r.id = :resourceId)"); + TypedQuery query = entityManager.createNamedQuery("findPolicyIdByResource", String.class); query.setParameter("resourceId", resourceId); query.setParameter("serverId", resourceServerId); - return query.getResultList(); + List result = query.getResultList(); + List list = new LinkedList<>(); + for (String id : result) { + list.add(provider.getStoreFactory().getPolicyStore().findById(id, resourceServerId)); + } + return list; } @Override public List findByResourceType(final String resourceType, String resourceServerId) { - Query query = entityManager.createQuery("select p from PolicyEntity p inner join p.config c where p.resourceServer.id = :serverId and KEY(c) = 'defaultResourceType' and c like :type"); + TypedQuery query = entityManager.createNamedQuery("findPolicyIdByResourceType", String.class); - query.setParameter("serverId", resourceServerId); query.setParameter("type", resourceType); + query.setParameter("serverId", resourceServerId); - return query.getResultList(); + List result = query.getResultList(); + List list = new LinkedList<>(); + for (String id : result) { + list.add(provider.getStoreFactory().getPolicyStore().findById(id, resourceServerId)); + } + return list; } @Override @@ -178,31 +200,47 @@ public class JPAPolicyStore implements PolicyStore { } // Use separate subquery to handle DB2 and MSSSQL - Query query = entityManager.createQuery("select pe from PolicyEntity pe where pe.resourceServer.id = :serverId and pe.id IN (select p.id from ScopeEntity s inner join s.policies p where s.resourceServer.id = :serverId and (p.resourceServer.id = :serverId and p.type = 'scope' and s.id in (:scopeIds)))"); + TypedQuery query = entityManager.createNamedQuery("findPolicyIdByScope", String.class); - query.setParameter("serverId", resourceServerId); query.setParameter("scopeIds", scopeIds); + query.setParameter("serverId", resourceServerId); - return query.getResultList(); + List result = query.getResultList(); + List list = new LinkedList<>(); + for (String id : result) { + list.add(provider.getStoreFactory().getPolicyStore().findById(id, resourceServerId)); + } + return list; } @Override public List findByType(String type, String resourceServerId) { - Query query = entityManager.createQuery("select p from PolicyEntity p where p.resourceServer.id = :serverId and p.type = :type"); + TypedQuery query = entityManager.createNamedQuery("findPolicyIdByType", String.class); query.setParameter("serverId", resourceServerId); query.setParameter("type", type); - return query.getResultList(); + List result = query.getResultList(); + List list = new LinkedList<>(); + for (String id : result) { + list.add(provider.getStoreFactory().getPolicyStore().findById(id, resourceServerId)); + } + return list; } @Override public List findDependentPolicies(String policyId, String resourceServerId) { - Query query = entityManager.createQuery("select p from PolicyEntity p inner join p.associatedPolicies ap where p.resourceServer.id = :serverId and (ap.resourceServer.id = :serverId and ap.id = :policyId)"); + + TypedQuery query = entityManager.createNamedQuery("findPolicyIdByDependentPolices", String.class); query.setParameter("serverId", resourceServerId); query.setParameter("policyId", policyId); - return query.getResultList(); + List result = query.getResultList(); + List list = new LinkedList<>(); + for (String id : result) { + list.add(provider.getStoreFactory().getPolicyStore().findById(id, resourceServerId)); + } + return list; } } diff --git a/model/jpa/src/main/java/org/keycloak/authorization/jpa/store/JPAResourceServerStore.java b/model/jpa/src/main/java/org/keycloak/authorization/jpa/store/JPAResourceServerStore.java index 51d0369245..20404e5a5a 100644 --- a/model/jpa/src/main/java/org/keycloak/authorization/jpa/store/JPAResourceServerStore.java +++ b/model/jpa/src/main/java/org/keycloak/authorization/jpa/store/JPAResourceServerStore.java @@ -17,13 +17,19 @@ */ package org.keycloak.authorization.jpa.store; +import org.keycloak.authorization.AuthorizationProvider; +import org.keycloak.authorization.jpa.entities.PolicyEntity; import org.keycloak.authorization.jpa.entities.ResourceServerEntity; +import org.keycloak.authorization.model.Policy; import org.keycloak.authorization.model.ResourceServer; import org.keycloak.authorization.store.ResourceServerStore; import org.keycloak.models.utils.KeycloakModelUtils; import javax.persistence.EntityManager; +import javax.persistence.NoResultException; import javax.persistence.Query; +import javax.persistence.TypedQuery; +import java.util.LinkedList; import java.util.List; /** @@ -32,9 +38,11 @@ import java.util.List; public class JPAResourceServerStore implements ResourceServerStore { private final EntityManager entityManager; + private final AuthorizationProvider provider; - public JPAResourceServerStore(EntityManager entityManager) { + public JPAResourceServerStore(EntityManager entityManager, AuthorizationProvider provider) { this.entityManager = entityManager; + this.provider = provider; } @Override @@ -46,30 +54,54 @@ public class JPAResourceServerStore implements ResourceServerStore { this.entityManager.persist(entity); - return entity; + return new ResourceServerAdapter(entity, entityManager, provider.getStoreFactory()); } @Override public void delete(String id) { - this.entityManager.remove(findById(id)); + ResourceServerEntity entity = entityManager.find(ResourceServerEntity.class, id); + if (entity == null) return; + //This didn't work, had to loop through and remove each policy individually + //entityManager.createNamedQuery("deletePolicyByResourceServer") + // .setParameter("serverId", id).executeUpdate(); + + TypedQuery query = entityManager.createNamedQuery("findPolicyIdByServerId", String.class); + query.setParameter("serverId", id); + List result = query.getResultList(); + List list = new LinkedList<>(); + for (String policyId : result) { + entityManager.remove(entityManager.getReference(PolicyEntity.class, policyId)); + } + + entityManager.flush(); + entityManager.createNamedQuery("deleteResourceByResourceServer") + .setParameter("serverId", id).executeUpdate(); + entityManager.flush(); + entityManager.createNamedQuery("deleteScopeByResourceServer") + .setParameter("serverId", id).executeUpdate(); + entityManager.flush(); + + this.entityManager.remove(entity); + entityManager.flush(); } @Override public ResourceServer findById(String id) { - return entityManager.find(ResourceServerEntity.class, id); + ResourceServerEntity entity = entityManager.find(ResourceServerEntity.class, id); + if (entity == null) return null; + return new ResourceServerAdapter(entity, entityManager, provider.getStoreFactory()); } @Override public ResourceServer findByClient(final String clientId) { - Query query = entityManager.createQuery("from ResourceServerEntity where clientId = :clientId"); + TypedQuery query = entityManager.createNamedQuery("findResourceServerIdByClient", String.class); query.setParameter("clientId", clientId); - List result = query.getResultList(); - - if (result.isEmpty()) { + try { + String id = query.getSingleResult(); + return provider.getStoreFactory().getResourceServerStore().findById(id); + } catch (NoResultException ex) { return null; } - - return (ResourceServer) result.get(0); } } diff --git a/model/jpa/src/main/java/org/keycloak/authorization/jpa/store/JPAResourceStore.java b/model/jpa/src/main/java/org/keycloak/authorization/jpa/store/JPAResourceStore.java index 0b04388e55..8a647d8a85 100644 --- a/model/jpa/src/main/java/org/keycloak/authorization/jpa/store/JPAResourceStore.java +++ b/model/jpa/src/main/java/org/keycloak/authorization/jpa/store/JPAResourceStore.java @@ -17,6 +17,7 @@ */ package org.keycloak.authorization.jpa.store; +import org.keycloak.authorization.AuthorizationProvider; import org.keycloak.authorization.jpa.entities.ResourceEntity; import org.keycloak.authorization.jpa.entities.ResourceServerEntity; import org.keycloak.authorization.model.Resource; @@ -25,13 +26,16 @@ import org.keycloak.authorization.store.ResourceStore; import org.keycloak.models.utils.KeycloakModelUtils; import javax.persistence.EntityManager; +import javax.persistence.NoResultException; import javax.persistence.Query; +import javax.persistence.TypedQuery; import javax.persistence.criteria.CriteriaBuilder; import javax.persistence.criteria.CriteriaQuery; import javax.persistence.criteria.Predicate; import javax.persistence.criteria.Root; import java.util.ArrayList; import java.util.Arrays; +import java.util.LinkedList; import java.util.List; import java.util.Map; @@ -41,38 +45,34 @@ import java.util.Map; public class JPAResourceStore implements ResourceStore { private final EntityManager entityManager; + private final AuthorizationProvider provider; - public JPAResourceStore(EntityManager entityManager) { + public JPAResourceStore(EntityManager entityManager, AuthorizationProvider provider) { this.entityManager = entityManager; + this.provider = provider; } @Override public Resource create(String name, ResourceServer resourceServer, String owner) { - if (!(resourceServer instanceof ResourceServerEntity)) { - throw new RuntimeException("Unexpected type [" + resourceServer.getClass() + "]."); - } - ResourceEntity entity = new ResourceEntity(); entity.setId(KeycloakModelUtils.generateId()); entity.setName(name); - entity.setResourceServer((ResourceServerEntity) resourceServer); + entity.setResourceServer(ResourceServerAdapter.toEntity(entityManager, resourceServer)); entity.setOwner(owner); this.entityManager.persist(entity); - return entity; + return new ResourceAdapter(entity, entityManager, provider.getStoreFactory()); } @Override public void delete(String id) { - Resource resource = entityManager.find(ResourceEntity.class, id); + ResourceEntity resource = entityManager.find(ResourceEntity.class, id); + if (resource == null) return; resource.getScopes().clear(); - - if (resource != null) { - this.entityManager.remove(resource); - } + this.entityManager.remove(resource); } @Override @@ -81,52 +81,61 @@ public class JPAResourceStore implements ResourceStore { return null; } - if (resourceServerId == null) { - return entityManager.find(ResourceEntity.class, id); - } - - Query query = entityManager.createQuery("from ResourceEntity where resourceServer.id = :serverId and id = :id"); - - query.setParameter("serverId", resourceServerId); - query.setParameter("id", id); - - return entityManager.find(ResourceEntity.class, id); + ResourceEntity entity = entityManager.find(ResourceEntity.class, id); + if (entity == null) return null; + return new ResourceAdapter(entity, entityManager, provider.getStoreFactory()); } @Override public List findByOwner(String ownerId, String resourceServerId) { - Query query = entityManager.createQuery("from ResourceEntity where resourceServer.id = :serverId and owner = :ownerId"); + TypedQuery query = entityManager.createNamedQuery("findResourceIdByOwner", String.class); - query.setParameter("ownerId", ownerId); + query.setParameter("owner", ownerId); query.setParameter("serverId", resourceServerId); - return query.getResultList(); + List result = query.getResultList(); + List list = new LinkedList<>(); + for (String id : result) { + list.add(provider.getStoreFactory().getResourceStore().findById(id, resourceServerId)); + } + return list; } @Override public List findByUri(String uri, String resourceServerId) { - Query query = entityManager.createQuery("from ResourceEntity where resourceServer.id = :serverId and uri = :uri"); + TypedQuery query = entityManager.createNamedQuery("findResourceIdByUri", String.class); query.setParameter("uri", uri); query.setParameter("serverId", resourceServerId); - return query.getResultList(); + List result = query.getResultList(); + List list = new LinkedList<>(); + for (String id : result) { + list.add(provider.getStoreFactory().getResourceStore().findById(id, resourceServerId)); + } + return list; } @Override - public List findByResourceServer(String resourceServerId) { - Query query = entityManager.createQuery("from ResourceEntity where resourceServer.id = :serverId"); + public List findByResourceServer(String resourceServerId) { + TypedQuery query = entityManager.createNamedQuery("findResourceIdByServerId", String.class); query.setParameter("serverId", resourceServerId); - return query.getResultList(); + List result = query.getResultList(); + List list = new LinkedList<>(); + for (String id : result) { + list.add(provider.getStoreFactory().getResourceStore().findById(id, resourceServerId)); + } + return list; } @Override - public List findByResourceServer(Map attributes, String resourceServerId, int firstResult, int maxResult) { + public List findByResourceServer(Map attributes, String resourceServerId, int firstResult, int maxResult) { CriteriaBuilder builder = entityManager.getCriteriaBuilder(); CriteriaQuery querybuilder = builder.createQuery(ResourceEntity.class); Root root = querybuilder.from(ResourceEntity.class); + querybuilder.select(root.get("id")); List predicates = new ArrayList(); predicates.add(builder.equal(root.get("resourceServer").get("id"), resourceServerId)); @@ -152,42 +161,55 @@ public class JPAResourceStore implements ResourceStore { query.setMaxResults(maxResult); } - return query.getResultList(); + List result = query.getResultList(); + List list = new LinkedList<>(); + for (String id : result) { + list.add(provider.getStoreFactory().getResourceStore().findById(id, resourceServerId)); + } + return list; } @Override - public List findByScope(List id, String resourceServerId) { - Query query = entityManager.createQuery("select r from ResourceEntity r inner join r.scopes s where r.resourceServer.id = :serverId and (s.resourceServer.id = :serverId and s.id in (:scopeIds))"); + public List findByScope(List scopes, String resourceServerId) { + TypedQuery query = entityManager.createNamedQuery("findResourceIdByScope", String.class); - query.setParameter("scopeIds", id); + query.setParameter("scopeIds", scopes); query.setParameter("serverId", resourceServerId); - return query.getResultList(); + List result = query.getResultList(); + List list = new LinkedList<>(); + for (String id : result) { + list.add(provider.getStoreFactory().getResourceStore().findById(id, resourceServerId)); + } + return list; } @Override public Resource findByName(String name, String resourceServerId) { - Query query = entityManager.createQuery("from ResourceEntity where resourceServer.id = :serverId and name = :name"); + TypedQuery query = entityManager.createNamedQuery("findResourceIdByName", String.class); query.setParameter("serverId", resourceServerId); query.setParameter("name", name); - - List result = query.getResultList(); - - if (!result.isEmpty()) { - return result.get(0); + try { + String id = query.getSingleResult(); + return provider.getStoreFactory().getResourceStore().findById(id, resourceServerId); + } catch (NoResultException ex) { + return null; } - - return null; } @Override public List findByType(String type, String resourceServerId) { - Query query = entityManager.createQuery("from ResourceEntity r where r.resourceServer.id = :serverId and type = :type"); + TypedQuery query = entityManager.createNamedQuery("findResourceIdByType", String.class); query.setParameter("type", type); query.setParameter("serverId", resourceServerId); - return query.getResultList(); + List result = query.getResultList(); + List list = new LinkedList<>(); + for (String id : result) { + list.add(provider.getStoreFactory().getResourceStore().findById(id, resourceServerId)); + } + return list; } } diff --git a/model/jpa/src/main/java/org/keycloak/authorization/jpa/store/JPAScopeStore.java b/model/jpa/src/main/java/org/keycloak/authorization/jpa/store/JPAScopeStore.java index 031eb4a087..f8a9350442 100644 --- a/model/jpa/src/main/java/org/keycloak/authorization/jpa/store/JPAScopeStore.java +++ b/model/jpa/src/main/java/org/keycloak/authorization/jpa/store/JPAScopeStore.java @@ -18,17 +18,20 @@ package org.keycloak.authorization.jpa.store; import java.util.ArrayList; +import java.util.LinkedList; import java.util.List; import java.util.Map; import javax.persistence.EntityManager; import javax.persistence.NoResultException; import javax.persistence.Query; +import javax.persistence.TypedQuery; import javax.persistence.criteria.CriteriaBuilder; import javax.persistence.criteria.CriteriaQuery; import javax.persistence.criteria.Predicate; import javax.persistence.criteria.Root; +import org.keycloak.authorization.AuthorizationProvider; import org.keycloak.authorization.jpa.entities.ResourceServerEntity; import org.keycloak.authorization.jpa.entities.ScopeEntity; import org.keycloak.authorization.model.ResourceServer; @@ -42,9 +45,11 @@ import org.keycloak.models.utils.KeycloakModelUtils; public class JPAScopeStore implements ScopeStore { private final EntityManager entityManager; + private final AuthorizationProvider provider; - public JPAScopeStore(EntityManager entityManager) { + public JPAScopeStore(EntityManager entityManager, AuthorizationProvider provider) { this.entityManager = entityManager; + this.provider = provider; } @Override @@ -53,16 +58,16 @@ public class JPAScopeStore implements ScopeStore { entity.setId(KeycloakModelUtils.generateId()); entity.setName(name); - entity.setResourceServer((ResourceServerEntity) resourceServer); + entity.setResourceServer(ResourceServerAdapter.toEntity(entityManager, resourceServer)); this.entityManager.persist(entity); - return entity; + return new ScopeAdapter(entity, entityManager, provider.getStoreFactory()); } @Override public void delete(String id) { - Scope scope = entityManager.find(ScopeEntity.class, id); + ScopeEntity scope = entityManager.find(ScopeEntity.class, id); if (scope != null) { this.entityManager.remove(scope); @@ -75,28 +80,21 @@ public class JPAScopeStore implements ScopeStore { return null; } - if (resourceServerId == null) { - return entityManager.find(ScopeEntity.class, id); - } - - Query query = entityManager.createQuery("from ScopeEntity where resourceServer.id = :serverId and id = :id"); - - query.setParameter("serverId", resourceServerId); - query.setParameter("id", id); - - return entityManager.find(ScopeEntity.class, id); + ScopeEntity entity = entityManager.find(ScopeEntity.class, id); + if (entity == null) return null; + return new ScopeAdapter(entity, entityManager, provider.getStoreFactory()); } @Override public Scope findByName(String name, String resourceServerId) { try { - Query query = entityManager.createQuery("select s from ScopeEntity s inner join s.resourceServer rs where rs.id = :resourceServerId and name = :name"); + TypedQuery query = entityManager.createNamedQuery("findScopeIdByName", String.class); - query.setParameter("resourceServerId", resourceServerId); + query.setParameter("serverId", resourceServerId); query.setParameter("name", name); - - return (Scope) query.getSingleResult(); + String id = query.getSingleResult(); + return provider.getStoreFactory().getScopeStore().findById(id, resourceServerId); } catch (NoResultException nre) { return null; } @@ -104,11 +102,16 @@ public class JPAScopeStore implements ScopeStore { @Override public List findByResourceServer(final String serverId) { - Query query = entityManager.createQuery("from ScopeEntity where resourceServer.id = :serverId"); + TypedQuery query = entityManager.createNamedQuery("findScopeIdByResourceServer", String.class); query.setParameter("serverId", serverId); - return query.getResultList(); + List result = query.getResultList(); + List list = new LinkedList<>(); + for (String id : result) { + list.add(provider.getStoreFactory().getScopeStore().findById(id, serverId)); + } + return list; } @Override @@ -116,6 +119,7 @@ public class JPAScopeStore implements ScopeStore { CriteriaBuilder builder = entityManager.getCriteriaBuilder(); CriteriaQuery querybuilder = builder.createQuery(ScopeEntity.class); Root root = querybuilder.from(ScopeEntity.class); + querybuilder.select(root.get("id")); List predicates = new ArrayList(); predicates.add(builder.equal(root.get("resourceServer").get("id"), resourceServerId)); @@ -139,6 +143,12 @@ public class JPAScopeStore implements ScopeStore { query.setMaxResults(maxResult); } - return query.getResultList(); + List result = query.getResultList(); + List list = new LinkedList<>(); + for (Object id : result) { + list.add(provider.getStoreFactory().getScopeStore().findById((String)id, resourceServerId)); + } + return list; + } } diff --git a/model/jpa/src/main/java/org/keycloak/authorization/jpa/store/JPAStoreFactory.java b/model/jpa/src/main/java/org/keycloak/authorization/jpa/store/JPAStoreFactory.java index e45d343af2..855f66a53a 100644 --- a/model/jpa/src/main/java/org/keycloak/authorization/jpa/store/JPAStoreFactory.java +++ b/model/jpa/src/main/java/org/keycloak/authorization/jpa/store/JPAStoreFactory.java @@ -20,6 +20,7 @@ package org.keycloak.authorization.jpa.store; import javax.persistence.EntityManager; +import org.keycloak.authorization.AuthorizationProvider; import org.keycloak.authorization.store.PolicyStore; import org.keycloak.authorization.store.ResourceServerStore; import org.keycloak.authorization.store.ResourceStore; @@ -36,11 +37,11 @@ public class JPAStoreFactory implements StoreFactory { private final ResourceStore resourceStore; private final ScopeStore scopeStore; - public JPAStoreFactory(EntityManager entityManager) { - policyStore = new JPAPolicyStore(entityManager); - resourceServerStore = new JPAResourceServerStore(entityManager); - resourceStore = new JPAResourceStore(entityManager); - scopeStore = new JPAScopeStore(entityManager); + public JPAStoreFactory(EntityManager entityManager, AuthorizationProvider provider) { + policyStore = new JPAPolicyStore(entityManager, provider); + resourceServerStore = new JPAResourceServerStore(entityManager, provider); + resourceStore = new JPAResourceStore(entityManager, provider); + scopeStore = new JPAScopeStore(entityManager, provider); } @Override diff --git a/model/jpa/src/main/java/org/keycloak/authorization/jpa/store/PolicyAdapter.java b/model/jpa/src/main/java/org/keycloak/authorization/jpa/store/PolicyAdapter.java new file mode 100644 index 0000000000..6fc2d1e85e --- /dev/null +++ b/model/jpa/src/main/java/org/keycloak/authorization/jpa/store/PolicyAdapter.java @@ -0,0 +1,235 @@ +/* + * 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.authorization.jpa.store; + +import org.keycloak.authorization.AuthorizationProvider; +import org.keycloak.authorization.jpa.entities.PolicyEntity; +import org.keycloak.authorization.jpa.entities.ResourceEntity; +import org.keycloak.authorization.jpa.entities.ScopeEntity; +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.StoreFactory; +import org.keycloak.models.jpa.JpaModel; +import org.keycloak.representations.idm.authorization.DecisionStrategy; +import org.keycloak.representations.idm.authorization.Logic; + +import javax.persistence.EntityManager; +import java.util.Collections; +import java.util.HashMap; +import java.util.HashSet; +import java.util.Map; +import java.util.Set; + +/** + * @author Bill Burke + * @version $Revision: 1 $ + */ +public class PolicyAdapter implements Policy, JpaModel { + private PolicyEntity entity; + private EntityManager em; + private StoreFactory storeFactory; + + public PolicyAdapter(PolicyEntity entity, EntityManager em, StoreFactory storeFactory) { + this.entity = entity; + this.em = em; + this.storeFactory = storeFactory; + } + + @Override + public PolicyEntity getEntity() { + return entity; + } + + @Override + public String getId() { + return entity.getId(); + } + + @Override + public String getType() { + return entity.getType(); + } + + @Override + public DecisionStrategy getDecisionStrategy() { + return entity.getDecisionStrategy(); + } + + @Override + public void setDecisionStrategy(DecisionStrategy decisionStrategy) { + entity.setDecisionStrategy(decisionStrategy); + + } + + @Override + public Logic getLogic() { + return entity.getLogic(); + } + + @Override + public void setLogic(Logic logic) { + entity.setLogic(logic); + } + + @Override + public Map getConfig() { + Map result = new HashMap(); + if (entity.getConfig() != null) result.putAll(entity.getConfig()); + return Collections.unmodifiableMap(result); + } + + @Override + public void setConfig(Map config) { + if (entity.getConfig() == null) { + entity.setConfig(new HashMap<>()); + } else { + entity.getConfig().clear(); + } + entity.getConfig().putAll(config); + } + + @Override + public void removeConfig(String name) { + if (entity.getConfig() == null) { + return; + } + entity.getConfig().remove(name); + } + + @Override + public void putConfig(String name, String value) { + if (entity.getConfig() == null) { + entity.setConfig(new HashMap<>()); + } + entity.getConfig().put(name, value); + + } + + @Override + public String getName() { + return entity.getName(); + } + + @Override + public void setName(String name) { + entity.setName(name); + + } + + @Override + public String getDescription() { + return entity.getDescription(); + } + + @Override + public void setDescription(String description) { + entity.setDescription(description); + + } + + @Override + public ResourceServer getResourceServer() { + return storeFactory.getResourceServerStore().findById(entity.getResourceServer().getId()); + } + + @Override + public Set getAssociatedPolicies() { + Set result = new HashSet<>(); + for (PolicyEntity policy : entity.getAssociatedPolicies()) { + Policy p = storeFactory.getPolicyStore().findById(policy.getId(), entity.getResourceServer().getId()); + result.add(p); + } + return Collections.unmodifiableSet(result); + } + + @Override + public Set getResources() { + Set set = new HashSet<>(); + for (ResourceEntity res : entity.getResources()) { + set.add(storeFactory.getResourceStore().findById(res.getId(), entity.getResourceServer().getId())); + } + return Collections.unmodifiableSet(set); + } + + @Override + public Set getScopes() { + Set set = new HashSet<>(); + for (ScopeEntity res : entity.getScopes()) { + set.add(storeFactory.getScopeStore().findById(res.getId(), entity.getResourceServer().getId())); + } + return Collections.unmodifiableSet(set); + } + + @Override + public void addScope(Scope scope) { + entity.getScopes().add(ScopeAdapter.toEntity(em, scope)); + } + + @Override + public void removeScope(Scope scope) { + entity.getScopes().remove(ScopeAdapter.toEntity(em, scope)); + + } + + @Override + public void addAssociatedPolicy(Policy associatedPolicy) { + entity.getAssociatedPolicies().add(toEntity(em, associatedPolicy)); + } + + @Override + public void removeAssociatedPolicy(Policy associatedPolicy) { + entity.getAssociatedPolicies().remove(toEntity(em, associatedPolicy)); + + } + + @Override + public void addResource(Resource resource) { + entity.getResources().add(ResourceAdapter.toEntity(em, resource)); + } + + @Override + public void removeResource(Resource resource) { + entity.getResources().remove(ResourceAdapter.toEntity(em, resource)); + } + + @Override + public boolean equals(Object o) { + if (this == o) return true; + if (o == null || !(o instanceof Policy)) return false; + + Policy that = (Policy) o; + return that.getId().equals(getId()); + } + + @Override + public int hashCode() { + return getId().hashCode(); + } + + public static PolicyEntity toEntity(EntityManager em, Policy policy) { + if (policy instanceof PolicyAdapter) { + return ((PolicyAdapter)policy).getEntity(); + } else { + return em.getReference(PolicyEntity.class, policy.getId()); + } + } + + + +} diff --git a/model/jpa/src/main/java/org/keycloak/authorization/jpa/store/ResourceAdapter.java b/model/jpa/src/main/java/org/keycloak/authorization/jpa/store/ResourceAdapter.java new file mode 100644 index 0000000000..5c55114901 --- /dev/null +++ b/model/jpa/src/main/java/org/keycloak/authorization/jpa/store/ResourceAdapter.java @@ -0,0 +1,166 @@ +/* + * 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.authorization.jpa.store; + +import org.keycloak.authorization.AuthorizationProvider; +import org.keycloak.authorization.jpa.entities.ResourceEntity; +import org.keycloak.authorization.jpa.entities.ScopeEntity; +import org.keycloak.authorization.model.Resource; +import org.keycloak.authorization.model.ResourceServer; +import org.keycloak.authorization.model.Scope; +import org.keycloak.authorization.store.StoreFactory; +import org.keycloak.models.jpa.JpaModel; + +import javax.persistence.EntityManager; +import java.util.Collections; +import java.util.HashSet; +import java.util.Iterator; +import java.util.LinkedList; +import java.util.List; +import java.util.Set; + +/** + * @author Bill Burke + * @version $Revision: 1 $ + */ +public class ResourceAdapter implements Resource, JpaModel { + private ResourceEntity entity; + private EntityManager em; + private StoreFactory storeFactory; + + public ResourceAdapter(ResourceEntity entity, EntityManager em, StoreFactory storeFactory) { + this.entity = entity; + this.em = em; + this.storeFactory = storeFactory; + } + + @Override + public ResourceEntity getEntity() { + return entity; + } + + @Override + public String getId() { + return entity.getId(); + } + + @Override + public String getName() { + return entity.getName(); + } + + @Override + public void setName(String name) { + entity.setName(name); + + } + + @Override + public String getUri() { + return entity.getUri(); + } + + @Override + public void setUri(String uri) { + entity.setUri(uri); + + } + + @Override + public String getType() { + return entity.getType(); + } + + @Override + public void setType(String type) { + entity.setType(type); + + } + + @Override + public List getScopes() { + List scopes = new LinkedList<>(); + for (ScopeEntity scope : entity.getScopes()) { + scopes.add(storeFactory.getScopeStore().findById(scope.getId(), entity.getResourceServer().getId())); + } + + return Collections.unmodifiableList(scopes); + } + + @Override + public String getIconUri() { + return entity.getIconUri(); + } + + @Override + public void setIconUri(String iconUri) { + entity.setIconUri(iconUri); + + } + + @Override + public ResourceServer getResourceServer() { + return storeFactory.getResourceServerStore().findById(entity.getResourceServer().getId()); + } + + @Override + public String getOwner() { + return entity.getOwner(); + } + + @Override + public void updateScopes(Set toUpdate) { + Set ids = new HashSet<>(); + for (Scope scope : toUpdate) { + ids.add(scope.getId()); + } + Iterator it = entity.getScopes().iterator(); + while (it.hasNext()) { + ScopeEntity next = it.next(); + if (!ids.contains(next.getId())) it.remove(); + else ids.remove(next.getId()); + } + for (String addId : ids) { + entity.getScopes().add(em.getReference(ScopeEntity.class, addId)); + } + } + + + public static ResourceEntity toEntity(EntityManager em, Resource resource) { + if (resource instanceof ResourceAdapter) { + return ((ResourceAdapter)resource).getEntity(); + } else { + return em.getReference(ResourceEntity.class, resource.getId()); + } + } + + @Override + public boolean equals(Object o) { + if (this == o) return true; + if (o == null || !(o instanceof Resource)) return false; + + Resource that = (Resource) o; + return that.getId().equals(getId()); + } + + @Override + public int hashCode() { + return getId().hashCode(); + } + + +} diff --git a/model/jpa/src/main/java/org/keycloak/authorization/jpa/store/ResourceServerAdapter.java b/model/jpa/src/main/java/org/keycloak/authorization/jpa/store/ResourceServerAdapter.java new file mode 100644 index 0000000000..56d585650e --- /dev/null +++ b/model/jpa/src/main/java/org/keycloak/authorization/jpa/store/ResourceServerAdapter.java @@ -0,0 +1,106 @@ +/* + * 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.authorization.jpa.store; + +import org.keycloak.authorization.AuthorizationProvider; +import org.keycloak.authorization.jpa.entities.ResourceEntity; +import org.keycloak.authorization.jpa.entities.ResourceServerEntity; +import org.keycloak.authorization.model.Policy; +import org.keycloak.authorization.model.Resource; +import org.keycloak.authorization.model.ResourceServer; +import org.keycloak.authorization.store.StoreFactory; +import org.keycloak.models.jpa.JpaModel; +import org.keycloak.representations.idm.authorization.PolicyEnforcementMode; + +import javax.persistence.EntityManager; + +/** + * @author Bill Burke + * @version $Revision: 1 $ + */ +public class ResourceServerAdapter implements ResourceServer, JpaModel { + private ResourceServerEntity entity; + private EntityManager em; + private StoreFactory storeFactory; + + public ResourceServerAdapter(ResourceServerEntity entity, EntityManager em, StoreFactory storeFactory) { + this.entity = entity; + this.em = em; + this.storeFactory = storeFactory; + } + + @Override + public ResourceServerEntity getEntity() { + return entity; + } + + @Override + public String getId() { + return entity.getId(); + } + + @Override + public String getClientId() { + return entity.getClientId(); + } + + @Override + public boolean isAllowRemoteResourceManagement() { + return entity.isAllowRemoteResourceManagement(); + } + + @Override + public void setAllowRemoteResourceManagement(boolean allowRemoteResourceManagement) { + entity.setAllowRemoteResourceManagement(allowRemoteResourceManagement); + + } + + @Override + public PolicyEnforcementMode getPolicyEnforcementMode() { + return entity.getPolicyEnforcementMode(); + } + + @Override + public void setPolicyEnforcementMode(PolicyEnforcementMode enforcementMode) { + entity.setPolicyEnforcementMode(enforcementMode); + + } + + @Override + public boolean equals(Object o) { + if (this == o) return true; + if (o == null || !(o instanceof ResourceServer)) return false; + + ResourceServer that = (ResourceServer) o; + return that.getId().equals(getId()); + } + + @Override + public int hashCode() { + return getId().hashCode(); + } + + public static ResourceServerEntity toEntity(EntityManager em, ResourceServer resource) { + if (resource instanceof ResourceAdapter) { + return ((ResourceServerAdapter)resource).getEntity(); + } else { + return em.getReference(ResourceServerEntity.class, resource.getId()); + } + } + + +} diff --git a/model/jpa/src/main/java/org/keycloak/authorization/jpa/store/ScopeAdapter.java b/model/jpa/src/main/java/org/keycloak/authorization/jpa/store/ScopeAdapter.java new file mode 100644 index 0000000000..6b59dc8895 --- /dev/null +++ b/model/jpa/src/main/java/org/keycloak/authorization/jpa/store/ScopeAdapter.java @@ -0,0 +1,103 @@ +/* + * 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.authorization.jpa.store; + +import org.keycloak.authorization.AuthorizationProvider; +import org.keycloak.authorization.jpa.entities.ScopeEntity; +import org.keycloak.authorization.model.ResourceServer; +import org.keycloak.authorization.model.Scope; +import org.keycloak.authorization.store.StoreFactory; +import org.keycloak.models.KeycloakSession; +import org.keycloak.models.jpa.JpaModel; + +import javax.persistence.EntityManager; + +/** + * @author Bill Burke + * @version $Revision: 1 $ + */ +public class ScopeAdapter implements Scope, JpaModel { + private ScopeEntity entity; + private EntityManager em; + private StoreFactory storeFactory; + + public ScopeAdapter(ScopeEntity entity, EntityManager em, StoreFactory storeFactory) { + this.entity = entity; + this.em = em; + this.storeFactory = storeFactory; + } + + @Override + public ScopeEntity getEntity() { + return entity; + } + + @Override + public String getId() { + return entity.getId(); + } + + @Override + public String getName() { + return entity.getName(); + } + + @Override + public void setName(String name) { + entity.setName(name); + + } + + @Override + public String getIconUri() { + return entity.getIconUri(); + } + + @Override + public void setIconUri(String iconUri) { + entity.setIconUri(iconUri); + + } + + @Override + public ResourceServer getResourceServer() { + return storeFactory.getResourceServerStore().findById(entity.getResourceServer().getId()); + } + + public static ScopeEntity toEntity(EntityManager em, Scope scope) { + if (scope instanceof ScopeAdapter) { + return ((ScopeAdapter)scope).getEntity(); + } else { + return em.getReference(ScopeEntity.class, scope.getId()); + } + } + + @Override + public boolean equals(Object o) { + if (this == o) return true; + if (o == null || !(o instanceof Scope)) return false; + + Scope that = (Scope) o; + return that.getId().equals(getId()); + } + + @Override + public int hashCode() { + return getId().hashCode(); + } + +} diff --git a/server-spi-private/src/main/java/org/keycloak/authorization/AuthorizationProvider.java b/server-spi-private/src/main/java/org/keycloak/authorization/AuthorizationProvider.java index c5b75d38db..8a55ff14c3 100644 --- a/server-spi-private/src/main/java/org/keycloak/authorization/AuthorizationProvider.java +++ b/server-spi-private/src/main/java/org/keycloak/authorization/AuthorizationProvider.java @@ -28,6 +28,7 @@ import org.keycloak.authorization.permission.evaluator.Evaluators; import org.keycloak.authorization.policy.evaluation.DefaultPolicyEvaluator; import org.keycloak.authorization.policy.provider.PolicyProvider; import org.keycloak.authorization.policy.provider.PolicyProviderFactory; +import org.keycloak.authorization.store.AuthorizationStoreFactory; import org.keycloak.authorization.store.PolicyStore; import org.keycloak.authorization.store.ResourceServerStore; import org.keycloak.authorization.store.ResourceStore; @@ -35,13 +36,15 @@ import org.keycloak.authorization.store.ScopeStore; import org.keycloak.authorization.store.StoreFactory; import org.keycloak.models.KeycloakSession; import org.keycloak.models.RealmModel; +import org.keycloak.models.cache.authorization.CachedStoreFactoryProvider; +import org.keycloak.models.cache.authorization.CachedStoreProviderFactory; import org.keycloak.models.utils.RepresentationToModel; import org.keycloak.provider.Provider; import org.keycloak.representations.idm.authorization.AbstractPolicyRepresentation; /** *

The main contract here is the creation of {@link org.keycloak.authorization.permission.evaluator.PermissionEvaluator} instances. Usually - * an application has a single {@link AuthorizationProvider} instance and threads servicing client requests obtain {@link org.keycloak.authorization.core.permission.evaluator.PermissionEvaluator} + * an application has a single {@link AuthorizationProvider} instance and threads servicing client requests obtain {@link org.keycloak.authorization.permission.evaluator.PermissionEvaluator} * from the {@link #evaluators()} method. * *

The internal state of a {@link AuthorizationProvider} is immutable. This internal state includes all of the metadata @@ -69,14 +72,14 @@ public final class AuthorizationProvider implements Provider { private final DefaultPolicyEvaluator policyEvaluator; private StoreFactory storeFactory; + private StoreFactory storeFactoryDelegate; private final Map policyProviderFactories; private final KeycloakSession keycloakSession; private final RealmModel realm; - public AuthorizationProvider(KeycloakSession session, RealmModel realm, StoreFactory storeFactory, Map policyProviderFactories) { + public AuthorizationProvider(KeycloakSession session, RealmModel realm, Map policyProviderFactories) { this.keycloakSession = session; this.realm = realm; - this.storeFactory = storeFactory; this.policyProviderFactories = policyProviderFactories; this.policyEvaluator = new DefaultPolicyEvaluator(this); } @@ -92,15 +95,32 @@ public final class AuthorizationProvider implements Provider { } /** + * Cache sits in front of this + * * Returns a {@link StoreFactory}. * * @return the {@link StoreFactory} */ public StoreFactory getStoreFactory() { - return createStoreFactory(); + if (storeFactory != null) return storeFactory; + storeFactory = keycloakSession.getProvider(CachedStoreFactoryProvider.class); + if (storeFactory == null) storeFactory = getLocalStoreFactory(); + storeFactory = createStoreFactory(storeFactory); + return storeFactory; } - private StoreFactory createStoreFactory() { + /** + * No cache sits in front of this + * + * @return + */ + public StoreFactory getLocalStoreFactory() { + if (storeFactoryDelegate != null) return storeFactoryDelegate; + storeFactoryDelegate = keycloakSession.getProvider(StoreFactory.class); + return storeFactoryDelegate; + } + + private StoreFactory createStoreFactory(StoreFactory storeFactory) { return new StoreFactory() { @Override public ResourceStore getResourceStore() { @@ -222,7 +242,7 @@ public final class AuthorizationProvider implements Provider { * Returns a {@link PolicyProviderFactory} given a type. * * @param type the type of the policy provider - * @param the expected type of the provider + * @param

the expected type of the provider * @return a {@link PolicyProvider} with the given type */ public

P getProvider(String type) { diff --git a/server-spi-private/src/main/java/org/keycloak/authorization/model/CachedModel.java b/server-spi-private/src/main/java/org/keycloak/authorization/model/CachedModel.java new file mode 100644 index 0000000000..d908a84bc0 --- /dev/null +++ b/server-spi-private/src/main/java/org/keycloak/authorization/model/CachedModel.java @@ -0,0 +1,45 @@ +/* + * 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.authorization.model; + +/** + * Cached authorization model classes will implement this interface. + * + * @author Bill Burke + * @version $Revision: 1 $ + */ +public interface CachedModel { + /** + * Invalidates the cache for this model and returns a delegate that represents the actual data provider + * + * @return + */ + Model getDelegateForUpdate(); + + /** + * Invalidate the cache for this model + * + */ + void invalidate(); + + /** + * When was the model was loaded from database. + * + * @return + */ + long getCacheTimestamp(); +} diff --git a/server-spi-private/src/main/java/org/keycloak/authorization/model/Policy.java b/server-spi-private/src/main/java/org/keycloak/authorization/model/Policy.java index 03596d948c..f46f61ad29 100644 --- a/server-spi-private/src/main/java/org/keycloak/authorization/model/Policy.java +++ b/server-spi-private/src/main/java/org/keycloak/authorization/model/Policy.java @@ -76,10 +76,11 @@ public interface Policy { /** * Returns a {@link Map} holding string-based key/value pairs representing any additional configuration for this policy. * - * @return a map with any additional configuration defined for this policy. + * @return a unmodifiable map with any additional configuration defined for this policy. */ Map getConfig(); + /** * Sets a {@link Map} with string-based key/value pairs representing any additional configuration for this policy. * @@ -87,6 +88,9 @@ public interface Policy { */ void setConfig(Map config); + void removeConfig(String name); + void putConfig(String name, String value); + /** * Returns the name of this policy. * @@ -120,7 +124,7 @@ public interface Policy { * * @return a resource server */ - R getResourceServer(); + ResourceServer getResourceServer(); /** * Returns the {@link Policy} instances associated with this policy and used to evaluate authorization decisions when @@ -128,21 +132,21 @@ public interface Policy { * * @return the associated policies or an empty set if no policy is associated with this policy */ -

Set

getAssociatedPolicies(); + Set getAssociatedPolicies(); /** * Returns the {@link Resource} instances where this policy applies. * * @return a set with all resource instances where this policy applies. Or an empty set if there is no resource associated with this policy */ - Set getResources(); + Set getResources(); /** * Returns the {@link Scope} instances where this policy applies. * * @return a set with all scope instances where this policy applies. Or an empty set if there is no scope associated with this policy */ - Set getScopes(); + Set getScopes(); void addScope(Scope scope); diff --git a/server-spi-private/src/main/java/org/keycloak/authorization/model/Resource.java b/server-spi-private/src/main/java/org/keycloak/authorization/model/Resource.java index 2bf2c6fa3f..4c2521ccd7 100644 --- a/server-spi-private/src/main/java/org/keycloak/authorization/model/Resource.java +++ b/server-spi-private/src/main/java/org/keycloak/authorization/model/Resource.java @@ -82,7 +82,7 @@ public interface Resource { * * @return a list with all scopes associated with this resource */ - List getScopes(); + List getScopes(); /** * Returns an icon {@link java.net.URI} for this resource. @@ -103,7 +103,7 @@ public interface Resource { * * @return the resource server associated with this resource */ - R getResourceServer(); + ResourceServer getResourceServer(); /** * Returns the resource's owner, which is usually an identifier that uniquely identifies the resource's owner. diff --git a/server-spi-private/src/main/java/org/keycloak/authorization/store/AuthorizationStoreFactory.java b/server-spi-private/src/main/java/org/keycloak/authorization/store/AuthorizationStoreFactory.java index 76a3fab2fe..1c9c0df82c 100644 --- a/server-spi-private/src/main/java/org/keycloak/authorization/store/AuthorizationStoreFactory.java +++ b/server-spi-private/src/main/java/org/keycloak/authorization/store/AuthorizationStoreFactory.java @@ -21,10 +21,12 @@ package org.keycloak.authorization.store; import java.util.HashMap; import java.util.Map; +import org.keycloak.authorization.AuthorizationProvider; import org.keycloak.authorization.store.syncronization.ClientApplicationSynchronizer; import org.keycloak.authorization.store.syncronization.RealmSynchronizer; import org.keycloak.authorization.store.syncronization.Synchronizer; import org.keycloak.authorization.store.syncronization.UserSynchronizer; +import org.keycloak.models.KeycloakSession; import org.keycloak.models.KeycloakSessionFactory; import org.keycloak.models.RealmModel.ClientRemovedEvent; import org.keycloak.models.RealmModel.RealmRemovedEvent; diff --git a/server-spi-private/src/main/java/org/keycloak/authorization/store/ResourceStore.java b/server-spi-private/src/main/java/org/keycloak/authorization/store/ResourceStore.java index b55ec746c8..9a2ac51609 100644 --- a/server-spi-private/src/main/java/org/keycloak/authorization/store/ResourceStore.java +++ b/server-spi-private/src/main/java/org/keycloak/authorization/store/ResourceStore.java @@ -66,7 +66,7 @@ public interface ResourceStore { /** * Finds all {@link Resource} instances with the given uri. * - * @param ownerId the identifier of the owner + * @param uri the identifier of the uri * @return a list with all resource instances owned by the given owner */ List findByUri(String uri, String resourceServerId); diff --git a/server-spi-private/src/main/java/org/keycloak/authorization/store/syncronization/ClientApplicationSynchronizer.java b/server-spi-private/src/main/java/org/keycloak/authorization/store/syncronization/ClientApplicationSynchronizer.java index 686eeef4b0..aeb039dc4f 100644 --- a/server-spi-private/src/main/java/org/keycloak/authorization/store/syncronization/ClientApplicationSynchronizer.java +++ b/server-spi-private/src/main/java/org/keycloak/authorization/store/syncronization/ClientApplicationSynchronizer.java @@ -41,9 +41,9 @@ public class ClientApplicationSynchronizer implements Synchronizer storeFactory.getResourceStore().delete(resource.getId())); - storeFactory.getScopeStore().findByResourceServer(id).forEach(scope -> storeFactory.getScopeStore().delete(scope.getId())); - storeFactory.getPolicyStore().findByResourceServer(id).forEach(scope -> storeFactory.getPolicyStore().delete(scope.getId())); + //storeFactory.getResourceStore().findByResourceServer(id).forEach(resource -> storeFactory.getResourceStore().delete(resource.getId())); + //storeFactory.getScopeStore().findByResourceServer(id).forEach(scope -> storeFactory.getScopeStore().delete(scope.getId())); + //storeFactory.getPolicyStore().findByResourceServer(id).forEach(scope -> storeFactory.getPolicyStore().delete(scope.getId())); storeFactory.getResourceServerStore().delete(id); } } diff --git a/server-spi-private/src/main/java/org/keycloak/migration/migrators/MigrateTo2_1_0.java b/server-spi-private/src/main/java/org/keycloak/migration/migrators/MigrateTo2_1_0.java index f8844f15bb..d1e0ca2b35 100644 --- a/server-spi-private/src/main/java/org/keycloak/migration/migrators/MigrateTo2_1_0.java +++ b/server-spi-private/src/main/java/org/keycloak/migration/migrators/MigrateTo2_1_0.java @@ -71,7 +71,7 @@ public class MigrateTo2_1_0 implements Migration { if (resourceServer != null) { policyStore.findByType("role", resourceServer.getId()).forEach(policy -> { - Map config = policy.getConfig(); + Map config = new HashMap(policy.getConfig()); String roles = config.get("roles"); List roleConfig; diff --git a/server-spi-private/src/main/java/org/keycloak/models/cache/authorization/CachedStoreProviderFactory.java b/server-spi-private/src/main/java/org/keycloak/models/cache/authorization/CachedStoreProviderFactory.java index b8563cb74c..d466f7ae90 100644 --- a/server-spi-private/src/main/java/org/keycloak/models/cache/authorization/CachedStoreProviderFactory.java +++ b/server-spi-private/src/main/java/org/keycloak/models/cache/authorization/CachedStoreProviderFactory.java @@ -18,6 +18,9 @@ package org.keycloak.models.cache.authorization; +import org.keycloak.authorization.AuthorizationProvider; +import org.keycloak.authorization.store.StoreFactory; +import org.keycloak.models.KeycloakSession; import org.keycloak.provider.ProviderFactory; /** diff --git a/server-spi-private/src/main/java/org/keycloak/models/utils/RepresentationToModel.java b/server-spi-private/src/main/java/org/keycloak/models/utils/RepresentationToModel.java index b427477846..b857bad8b3 100755 --- a/server-spi-private/src/main/java/org/keycloak/models/utils/RepresentationToModel.java +++ b/server-spi-private/src/main/java/org/keycloak/models/utils/RepresentationToModel.java @@ -2098,7 +2098,7 @@ public class RepresentationToModel { } } - policy.getConfig().remove("scopes"); + policy.removeConfig("scopes"); } private static void updateAssociatedPolicies(Set policyIds, Policy policy, StoreFactory storeFactory) { @@ -2151,7 +2151,7 @@ public class RepresentationToModel { } } - policy.getConfig().remove("applyPolicies"); + policy.removeConfig("applyPolicies"); } private static void updateResources(Set resourceIds, Policy policy, StoreFactory storeFactory) { @@ -2197,7 +2197,7 @@ public class RepresentationToModel { } } - policy.getConfig().remove("resources"); + policy.removeConfig("resources"); } public static Resource toModel(ResourceRepresentation resource, ResourceServer resourceServer, AuthorizationProvider authorization) { diff --git a/services/src/main/java/org/keycloak/authorization/DefaultAuthorizationProviderFactory.java b/services/src/main/java/org/keycloak/authorization/DefaultAuthorizationProviderFactory.java index cc06284c6c..d9d7b2d02c 100644 --- a/services/src/main/java/org/keycloak/authorization/DefaultAuthorizationProviderFactory.java +++ b/services/src/main/java/org/keycloak/authorization/DefaultAuthorizationProviderFactory.java @@ -65,11 +65,7 @@ public class DefaultAuthorizationProviderFactory implements AuthorizationProvide @Override public AuthorizationProvider create(KeycloakSession session, RealmModel realm) { - StoreFactory storeFactory = session.getProvider(CachedStoreFactoryProvider.class); - if (storeFactory == null) { - storeFactory = session.getProvider(StoreFactory.class); - } - return new AuthorizationProvider(session, realm, storeFactory, policyProviderFactories); + return new AuthorizationProvider(session, realm, policyProviderFactories); } private Map configurePolicyProviderFactories(KeycloakSessionFactory keycloakSessionFactory) { diff --git a/services/src/main/java/org/keycloak/authorization/authorization/AuthorizationTokenService.java b/services/src/main/java/org/keycloak/authorization/authorization/AuthorizationTokenService.java index fb28054740..3a9337e730 100644 --- a/services/src/main/java/org/keycloak/authorization/authorization/AuthorizationTokenService.java +++ b/services/src/main/java/org/keycloak/authorization/authorization/AuthorizationTokenService.java @@ -16,6 +16,7 @@ */ package org.keycloak.authorization.authorization; +import org.jboss.logging.Logger; import org.jboss.resteasy.spi.HttpRequest; import org.keycloak.OAuth2Constants; import org.keycloak.OAuthErrorException; @@ -46,6 +47,7 @@ import org.keycloak.representations.idm.authorization.Permission; import org.keycloak.representations.idm.authorization.ScopeRepresentation; import org.keycloak.services.ErrorResponseException; import org.keycloak.services.resources.Cors; +import org.keycloak.services.resources.RealmsResource; import javax.ws.rs.Consumes; import javax.ws.rs.OPTIONS; @@ -72,6 +74,7 @@ import java.util.stream.Stream; * @author Pedro Igor */ public class AuthorizationTokenService { + protected static final Logger logger = Logger.getLogger(AuthorizationTokenService.class); private final AuthorizationProvider authorization; @@ -131,6 +134,7 @@ public class AuthorizationTokenService { @Override public void onError(Throwable cause) { + logger.error("failed authorize", cause); asyncResponse.resume(cause); } }); diff --git a/services/src/main/java/org/keycloak/authorization/entitlement/EntitlementService.java b/services/src/main/java/org/keycloak/authorization/entitlement/EntitlementService.java index 326deb6453..463ff0bf19 100644 --- a/services/src/main/java/org/keycloak/authorization/entitlement/EntitlementService.java +++ b/services/src/main/java/org/keycloak/authorization/entitlement/EntitlementService.java @@ -41,10 +41,12 @@ import javax.ws.rs.core.Context; import javax.ws.rs.core.Response; import javax.ws.rs.core.Response.Status; +import org.jboss.logging.Logger; import org.jboss.resteasy.spi.HttpRequest; import org.keycloak.OAuth2Constants; import org.keycloak.OAuthErrorException; import org.keycloak.authorization.AuthorizationProvider; +import org.keycloak.authorization.authorization.AuthorizationTokenService; import org.keycloak.authorization.common.KeycloakEvaluationContext; import org.keycloak.authorization.common.KeycloakIdentity; import org.keycloak.authorization.entitlement.representation.EntitlementRequest; @@ -79,6 +81,7 @@ import org.keycloak.services.resources.Cors; */ public class EntitlementService { + protected static final Logger logger = Logger.getLogger(EntitlementService.class); private final AuthorizationProvider authorization; @Context @@ -122,6 +125,7 @@ public class EntitlementService { @Override public void onError(Throwable cause) { + logger.error("failed", cause); asyncResponse.resume(cause); } @@ -175,6 +179,7 @@ public class EntitlementService { authorization.evaluators().from(createPermissions(entitlementRequest, resourceServer, authorization), new KeycloakEvaluationContext(this.authorization.getKeycloakSession())).evaluate(new DecisionResultCollector() { @Override public void onError(Throwable cause) { + logger.error("failed", cause); asyncResponse.resume(cause); } diff --git a/services/src/main/java/org/keycloak/authorization/util/Permissions.java b/services/src/main/java/org/keycloak/authorization/util/Permissions.java index 2e2c4a8b6a..a805fbc7c9 100644 --- a/services/src/main/java/org/keycloak/authorization/util/Permissions.java +++ b/services/src/main/java/org/keycloak/authorization/util/Permissions.java @@ -21,6 +21,7 @@ package org.keycloak.authorization.util; import java.util.ArrayList; import java.util.HashMap; import java.util.HashSet; +import java.util.LinkedList; import java.util.List; import java.util.Map; import java.util.Set; @@ -61,8 +62,8 @@ public final class Permissions { StoreFactory storeFactory = authorization.getStoreFactory(); ResourceStore resourceStore = storeFactory.getResourceStore(); - resourceStore.findByOwner(resourceServer.getClientId(), resourceServer.getId()).stream().forEach(resource -> permissions.addAll(createResourcePermissionsWithScopes(resource, resource.getScopes(), authorization))); - resourceStore.findByOwner(identity.getId(), resourceServer.getId()).stream().forEach(resource -> permissions.addAll(createResourcePermissionsWithScopes(resource, resource.getScopes(), authorization))); + resourceStore.findByOwner(resourceServer.getClientId(), resourceServer.getId()).stream().forEach(resource -> permissions.addAll(createResourcePermissionsWithScopes(resource, new LinkedList(resource.getScopes()), authorization))); + resourceStore.findByOwner(identity.getId(), resourceServer.getId()).stream().forEach(resource -> permissions.addAll(createResourcePermissionsWithScopes(resource, new LinkedList(resource.getScopes()), authorization))); return permissions; } @@ -74,7 +75,7 @@ public final class Permissions { List scopes; if (requestedScopes.isEmpty()) { - scopes = resource.getScopes(); + scopes = new LinkedList<>(resource.getScopes()); // check if there is a typed resource whose scopes are inherited by the resource being requested. In this case, we assume that parent resource // is owned by the resource server itself if (type != null && !resource.getOwner().equals(resourceServer.getClientId())) { @@ -102,7 +103,6 @@ public final class Permissions { return byName; }).collect(Collectors.toList()); } - permissions.add(new ResourcePermission(resource, scopes, resource.getResourceServer())); return permissions; diff --git a/testsuite/integration-arquillian/tests/base/src/test/java/org/keycloak/testsuite/authz/ConflictingScopePermissionTest.java b/testsuite/integration-arquillian/tests/base/src/test/java/org/keycloak/testsuite/authz/ConflictingScopePermissionTest.java index 6db4891238..4d5897cf56 100644 --- a/testsuite/integration-arquillian/tests/base/src/test/java/org/keycloak/testsuite/authz/ConflictingScopePermissionTest.java +++ b/testsuite/integration-arquillian/tests/base/src/test/java/org/keycloak/testsuite/authz/ConflictingScopePermissionTest.java @@ -214,7 +214,7 @@ public class ConflictingScopePermissionTest extends AbstractKeycloakTest { } representation.addScope(scopes.toArray(new String[scopes.size()])); - representation.addPolicy(scopes.toArray(new String[policies.size()])); + representation.addPolicy(policies.toArray(new String[policies.size()])); authorization.permissions().scope().create(representation); } diff --git a/wildfly/server-subsystem/src/main/resources/subsystem-templates/keycloak-infinispan.xml b/wildfly/server-subsystem/src/main/resources/subsystem-templates/keycloak-infinispan.xml index 205c1f51af..fb832fa23a 100755 --- a/wildfly/server-subsystem/src/main/resources/subsystem-templates/keycloak-infinispan.xml +++ b/wildfly/server-subsystem/src/main/resources/subsystem-templates/keycloak-infinispan.xml @@ -36,7 +36,7 @@ - +