diff --git a/distribution/galleon-feature-packs/server-galleon-pack/src/main/resources/modules/system/layers/keycloak/org/keycloak/keycloak-model-infinispan/main/module.xml b/distribution/galleon-feature-packs/server-galleon-pack/src/main/resources/modules/system/layers/keycloak/org/keycloak/keycloak-model-infinispan/main/module.xml index c89a501b7f..8b9c164347 100755 --- a/distribution/galleon-feature-packs/server-galleon-pack/src/main/resources/modules/system/layers/keycloak/org/keycloak/keycloak-model-infinispan/main/module.xml +++ b/distribution/galleon-feature-packs/server-galleon-pack/src/main/resources/modules/system/layers/keycloak/org/keycloak/keycloak-model-infinispan/main/module.xml @@ -31,6 +31,7 @@ + diff --git a/model/infinispan/pom.xml b/model/infinispan/pom.xml index f58499c349..ada22daefd 100755 --- a/model/infinispan/pom.xml +++ b/model/infinispan/pom.xml @@ -42,6 +42,10 @@ org.keycloak keycloak-model-legacy + + org.keycloak + keycloak-model-legacy-private + org.keycloak keycloak-server-spi-private diff --git a/model/infinispan/src/main/java/org/keycloak/models/cache/infinispan/InfinispanUserCacheProviderFactory.java b/model/infinispan/src/main/java/org/keycloak/models/cache/infinispan/InfinispanUserCacheProviderFactory.java index 4d0f445dea..1677c537dd 100755 --- a/model/infinispan/src/main/java/org/keycloak/models/cache/infinispan/InfinispanUserCacheProviderFactory.java +++ b/model/infinispan/src/main/java/org/keycloak/models/cache/infinispan/InfinispanUserCacheProviderFactory.java @@ -29,11 +29,14 @@ import org.keycloak.models.cache.UserCache; import org.keycloak.models.cache.UserCacheProviderFactory; import org.keycloak.models.cache.infinispan.entities.Revisioned; import org.keycloak.models.cache.infinispan.events.InvalidationEvent; +import org.keycloak.provider.InvalidationHandler; +import org.keycloak.provider.InvalidationHandler.InvalidableObjectType;; +import org.keycloak.provider.InvalidationHandler.ObjectType;; /** * @author Stian Thorgersen */ -public class InfinispanUserCacheProviderFactory implements UserCacheProviderFactory { +public class InfinispanUserCacheProviderFactory implements UserCacheProviderFactory, InvalidationHandler { private static final Logger log = Logger.getLogger(InfinispanUserCacheProviderFactory.class); public static final String USER_CLEAR_CACHE_EVENTS = "USER_CLEAR_CACHE_EVENTS"; @@ -78,6 +81,15 @@ public class InfinispanUserCacheProviderFactory implements UserCacheProviderFact } } + @Override + public void invalidate(KeycloakSession session, InvalidableObjectType type, Object... params) { + if (type == ObjectType.REALM || type == ObjectType.USER) { + if (this.userCache != null) { + this.userCache.clear(); + } + } + } + @Override public void init(Config.Scope config) { } @@ -95,6 +107,4 @@ public class InfinispanUserCacheProviderFactory implements UserCacheProviderFact public String getId() { return "default"; } - - } diff --git a/server-spi-private/src/main/java/org/keycloak/models/cache/CacheUserProviderSpi.java b/model/legacy-private/src/main/java/org/keycloak/models/cache/CacheUserProviderSpi.java similarity index 100% rename from server-spi-private/src/main/java/org/keycloak/models/cache/CacheUserProviderSpi.java rename to model/legacy-private/src/main/java/org/keycloak/models/cache/CacheUserProviderSpi.java diff --git a/server-spi-private/src/main/java/org/keycloak/models/cache/UserCacheProviderFactory.java b/model/legacy-private/src/main/java/org/keycloak/models/cache/UserCacheProviderFactory.java similarity index 100% rename from server-spi-private/src/main/java/org/keycloak/models/cache/UserCacheProviderFactory.java rename to model/legacy-private/src/main/java/org/keycloak/models/cache/UserCacheProviderFactory.java diff --git a/model/legacy-private/src/main/resources/META-INF/services/org.keycloak.provider.Spi b/model/legacy-private/src/main/resources/META-INF/services/org.keycloak.provider.Spi new file mode 100644 index 0000000000..a59266b1d8 --- /dev/null +++ b/model/legacy-private/src/main/resources/META-INF/services/org.keycloak.provider.Spi @@ -0,0 +1,18 @@ +# +# Copyright 2022 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. +# + +org.keycloak.models.cache.CacheUserProviderSpi diff --git a/model/legacy-services/src/main/java/org/keycloak/services/legacysessionsupport/LegacySessionSupportProviderImpl.java b/model/legacy-services/src/main/java/org/keycloak/services/legacysessionsupport/LegacySessionSupportProviderImpl.java index de1927af4e..c3a1c431aa 100644 --- a/model/legacy-services/src/main/java/org/keycloak/services/legacysessionsupport/LegacySessionSupportProviderImpl.java +++ b/model/legacy-services/src/main/java/org/keycloak/services/legacysessionsupport/LegacySessionSupportProviderImpl.java @@ -21,6 +21,7 @@ import org.keycloak.credential.UserCredentialStoreManager; import org.keycloak.models.KeycloakSession; import org.keycloak.models.UserCredentialManager; import org.keycloak.models.LegacySessionSupportProvider; +import org.keycloak.models.cache.UserCache; /** * @author Alexander Schwartz @@ -45,4 +46,9 @@ public class LegacySessionSupportProviderImpl implements LegacySessionSupportPro return new UserCredentialStoreManager(session); } + @Override + public UserCache userCache() { + return session.getProvider(UserCache.class); + } + } diff --git a/model/legacy-services/src/main/java/org/keycloak/services/resources/admin/ClearUserCacheRealmAdminProvider.java b/model/legacy-services/src/main/java/org/keycloak/services/resources/admin/ClearUserCacheRealmAdminProvider.java new file mode 100644 index 0000000000..298ceaee5e --- /dev/null +++ b/model/legacy-services/src/main/java/org/keycloak/services/resources/admin/ClearUserCacheRealmAdminProvider.java @@ -0,0 +1,57 @@ +/* + * Copyright 2022 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.services.resources.admin; + +import org.keycloak.Config.Scope; +import org.keycloak.models.KeycloakSession; +import org.keycloak.models.KeycloakSessionFactory; +import org.keycloak.models.RealmModel; +import org.keycloak.services.resources.admin.ext.AdminRealmResourceProvider; +import org.keycloak.services.resources.admin.ext.AdminRealmResourceProviderFactory; +import org.keycloak.services.resources.admin.permissions.AdminPermissionEvaluator; + +public class ClearUserCacheRealmAdminProvider implements AdminRealmResourceProviderFactory, AdminRealmResourceProvider { + + @Override + public AdminRealmResourceProvider create(KeycloakSession session) { + return this; + } + + @Override + public void init(Scope config) { + } + + @Override + public void postInit(KeycloakSessionFactory factory) { + } + + @Override + public void close() { + } + + @Override + public String getId() { + return "clear-user-cache"; + } + + @Override + public Object getResource(KeycloakSession session, RealmModel realm, AdminPermissionEvaluator auth, AdminEventBuilder adminEvent) { + return new ClearUserCacheResource(realm, auth, adminEvent); + } + +} diff --git a/model/legacy-services/src/main/java/org/keycloak/services/resources/admin/ClearUserCacheResource.java b/model/legacy-services/src/main/java/org/keycloak/services/resources/admin/ClearUserCacheResource.java new file mode 100644 index 0000000000..8e73da06e8 --- /dev/null +++ b/model/legacy-services/src/main/java/org/keycloak/services/resources/admin/ClearUserCacheResource.java @@ -0,0 +1,62 @@ +/* + * Copyright 2022 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.services.resources.admin; + +import org.keycloak.events.admin.OperationType; +import org.keycloak.models.KeycloakSession; +import org.keycloak.models.RealmModel; +import org.keycloak.models.cache.UserCache; +import org.keycloak.services.resources.admin.permissions.AdminPermissionEvaluator; + +import javax.ws.rs.POST; +import javax.ws.rs.core.Context; + +/** + * Clear user cache. + */ +public class ClearUserCacheResource { + protected RealmModel realm; + + protected AdminPermissionEvaluator auth; + + protected AdminEventBuilder adminEvent; + + @Context + protected KeycloakSession session; + + public ClearUserCacheResource(RealmModel realm, AdminPermissionEvaluator auth, AdminEventBuilder adminEvent) { + this.auth = auth; + this.realm = realm; + this.adminEvent = adminEvent; + } + + /** + * Clear user cache + */ + @POST + public void clearUserCache() { + auth.realm().requireManageRealm(); + + UserCache cache = session.getProvider(UserCache.class); + if (cache != null) { + cache.clear(); + } + + adminEvent.operation(OperationType.ACTION).resourcePath(session.getContext().getUri()).success(); + } + +} diff --git a/model/legacy-services/src/main/resources/META-INF/services/org.keycloak.services.resources.admin.ext.AdminRealmResourceProviderFactory b/model/legacy-services/src/main/resources/META-INF/services/org.keycloak.services.resources.admin.ext.AdminRealmResourceProviderFactory index ac600edf4c..b6ec273fac 100644 --- a/model/legacy-services/src/main/resources/META-INF/services/org.keycloak.services.resources.admin.ext.AdminRealmResourceProviderFactory +++ b/model/legacy-services/src/main/resources/META-INF/services/org.keycloak.services.resources.admin.ext.AdminRealmResourceProviderFactory @@ -16,3 +16,4 @@ # org.keycloak.services.resources.admin.UserStorageProviderRealmAdminProvider +org.keycloak.services.resources.admin.ClearUserCacheRealmAdminProvider diff --git a/server-spi/src/main/java/org/keycloak/models/cache/UserCache.java b/model/legacy/src/main/java/org/keycloak/models/cache/UserCache.java similarity index 100% rename from server-spi/src/main/java/org/keycloak/models/cache/UserCache.java rename to model/legacy/src/main/java/org/keycloak/models/cache/UserCache.java diff --git a/server-spi-private/src/main/java/org/keycloak/models/LegacySessionSupportProvider.java b/server-spi-private/src/main/java/org/keycloak/models/LegacySessionSupportProvider.java index 1123234a8a..0e39999d18 100644 --- a/server-spi-private/src/main/java/org/keycloak/models/LegacySessionSupportProvider.java +++ b/server-spi-private/src/main/java/org/keycloak/models/LegacySessionSupportProvider.java @@ -29,4 +29,7 @@ public interface LegacySessionSupportProvider extends Provider { @Deprecated UserCredentialManager userCredentialManager(); + + @Deprecated + UserProvider userCache(); } diff --git a/server-spi-private/src/main/resources/META-INF/services/org.keycloak.provider.Spi b/server-spi-private/src/main/resources/META-INF/services/org.keycloak.provider.Spi index e3badf2987..62dd831eaf 100755 --- a/server-spi-private/src/main/resources/META-INF/services/org.keycloak.provider.Spi +++ b/server-spi-private/src/main/resources/META-INF/services/org.keycloak.provider.Spi @@ -58,7 +58,6 @@ org.keycloak.theme.ThemeSpi org.keycloak.truststore.TruststoreSpi org.keycloak.connections.httpclient.HttpClientSpi org.keycloak.models.cache.CacheRealmProviderSpi -org.keycloak.models.cache.CacheUserProviderSpi org.keycloak.authentication.AuthenticatorSpi org.keycloak.authentication.ClientAuthenticatorSpi org.keycloak.authentication.RequiredActionSpi diff --git a/server-spi/src/main/java/org/keycloak/models/KeycloakSession.java b/server-spi/src/main/java/org/keycloak/models/KeycloakSession.java index 1ab564612f..5bda6b6f90 100755 --- a/server-spi/src/main/java/org/keycloak/models/KeycloakSession.java +++ b/server-spi/src/main/java/org/keycloak/models/KeycloakSession.java @@ -18,7 +18,6 @@ package org.keycloak.models; import org.keycloak.component.ComponentModel; -import org.keycloak.models.cache.UserCache; import org.keycloak.provider.InvalidationHandler.InvalidableObjectType; import org.keycloak.provider.Provider; import org.keycloak.services.clientpolicy.ClientPolicyManager; @@ -210,7 +209,7 @@ public interface KeycloakSession { * @return may be null if cache is disabled */ @Deprecated - UserCache userCache(); + UserProvider userCache(); /** * A cached view of all users in system including users loaded by UserStorageProviders diff --git a/services/src/main/java/org/keycloak/services/DefaultKeycloakSession.java b/services/src/main/java/org/keycloak/services/DefaultKeycloakSession.java index 1127a9f799..a6493503b0 100644 --- a/services/src/main/java/org/keycloak/services/DefaultKeycloakSession.java +++ b/services/src/main/java/org/keycloak/services/DefaultKeycloakSession.java @@ -39,7 +39,6 @@ import org.keycloak.models.UserCredentialManager; import org.keycloak.models.UserLoginFailureProvider; import org.keycloak.models.UserProvider; import org.keycloak.models.UserSessionProvider; -import org.keycloak.models.cache.UserCache; import org.keycloak.models.utils.KeycloakModelUtils; import org.keycloak.provider.Provider; import org.keycloak.provider.ProviderFactory; @@ -111,9 +110,13 @@ public class DefaultKeycloakSession implements KeycloakSession { } @Override - public UserCache userCache() { - return getProvider(UserCache.class); - + @Deprecated + public UserProvider userCache() { + LegacySessionSupportProvider provider = this.getProvider(LegacySessionSupportProvider.class); + if (provider == null) { + throw new IllegalStateException("legacy support for userCache is not enabled"); + } + return provider.userCache(); } @Override diff --git a/services/src/main/java/org/keycloak/services/resources/admin/RealmAdminResource.java b/services/src/main/java/org/keycloak/services/resources/admin/RealmAdminResource.java index 70c259ab8d..4771d323a4 100644 --- a/services/src/main/java/org/keycloak/services/resources/admin/RealmAdminResource.java +++ b/services/src/main/java/org/keycloak/services/resources/admin/RealmAdminResource.java @@ -78,6 +78,7 @@ import org.keycloak.models.ClientScopeModel; import org.keycloak.models.Constants; import org.keycloak.models.GroupModel; import org.keycloak.models.KeycloakSession; +import org.keycloak.models.LegacySessionSupportProvider; import org.keycloak.models.ModelDuplicateException; import org.keycloak.models.ModelException; import org.keycloak.models.RealmModel; @@ -85,13 +86,13 @@ import org.keycloak.models.RequiredActionProviderModel; import org.keycloak.models.UserModel; import org.keycloak.models.UserSessionModel; import org.keycloak.models.cache.CacheRealmProvider; -import org.keycloak.models.cache.UserCache; import org.keycloak.models.utils.KeycloakModelUtils; import org.keycloak.models.utils.ModelToRepresentation; import org.keycloak.models.utils.RepresentationToModel; import org.keycloak.models.utils.StripSecretsUtils; import org.keycloak.partialimport.PartialImportManager; import org.keycloak.protocol.oidc.TokenManager; +import org.keycloak.provider.InvalidationHandler; import org.keycloak.representations.adapters.action.GlobalRequestResult; import org.keycloak.representations.idm.AdminEventRepresentation; import org.keycloak.representations.idm.ClientRepresentation; @@ -440,8 +441,7 @@ public class RealmAdminResource { adminEvent.operation(OperationType.UPDATE).representation(StripSecretsUtils.strip(rep)).success(); if (rep.isDuplicateEmailsAllowed() != null && rep.isDuplicateEmailsAllowed() != wasDuplicateEmailsAllowed) { - UserCache cache = session.getProvider(UserCache.class); - if (cache != null) cache.clear(); + session.invalidate(InvalidationHandler.ObjectType.REALM, realm.getId()); } return Response.noContent().build(); @@ -1097,23 +1097,6 @@ public class RealmAdminResource { adminEvent.operation(OperationType.ACTION).resourcePath(session.getContext().getUri()).success(); } - /** - * Clear user cache - * - */ - @Path("clear-user-cache") - @POST - public void clearUserCache() { - auth.realm().requireManageRealm(); - - UserCache cache = session.getProvider(UserCache.class); - if (cache != null) { - cache.clear(); - } - - adminEvent.operation(OperationType.ACTION).resourcePath(session.getContext().getUri()).success(); - } - /** * Clear cache of external public keys (Public keys of clients or Identity providers) *