keycloak-aplcache
Changes
model/infinispan/src/main/java/org/keycloak/models/cache/infinispan/InfinispanCacheRealmProviderFactory.java 12(+12 -0)
model/infinispan/src/main/java/org/keycloak/models/cache/infinispan/InfinispanUserCacheProviderFactory.java 11(+11 -0)
Details
diff --git a/model/infinispan/src/main/java/org/keycloak/models/cache/infinispan/ClearCacheEvent.java b/model/infinispan/src/main/java/org/keycloak/models/cache/infinispan/ClearCacheEvent.java
new file mode 100644
index 0000000..ccfeef4
--- /dev/null
+++ b/model/infinispan/src/main/java/org/keycloak/models/cache/infinispan/ClearCacheEvent.java
@@ -0,0 +1,26 @@
+/*
+ * 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;
+
+import org.keycloak.cluster.ClusterEvent;
+
+/**
+ * @author <a href="mailto:bill@burkecentral.com">Bill Burke</a>
+ * @version $Revision: 1 $
+ */
+public class ClearCacheEvent implements ClusterEvent {
+}
diff --git a/model/infinispan/src/main/java/org/keycloak/models/cache/infinispan/InfinispanCacheRealmProviderFactory.java b/model/infinispan/src/main/java/org/keycloak/models/cache/infinispan/InfinispanCacheRealmProviderFactory.java
index 40c4e09..4bbe4c7 100755
--- a/model/infinispan/src/main/java/org/keycloak/models/cache/infinispan/InfinispanCacheRealmProviderFactory.java
+++ b/model/infinispan/src/main/java/org/keycloak/models/cache/infinispan/InfinispanCacheRealmProviderFactory.java
@@ -20,6 +20,9 @@ package org.keycloak.models.cache.infinispan;
import org.infinispan.Cache;
import org.jboss.logging.Logger;
import org.keycloak.Config;
+import org.keycloak.cluster.ClusterEvent;
+import org.keycloak.cluster.ClusterListener;
+import org.keycloak.cluster.ClusterProvider;
import org.keycloak.connections.infinispan.InfinispanConnectionProvider;
import org.keycloak.models.KeycloakSession;
import org.keycloak.models.KeycloakSessionFactory;
@@ -34,6 +37,7 @@ import org.keycloak.models.cache.infinispan.entities.Revisioned;
public class InfinispanCacheRealmProviderFactory implements CacheRealmProviderFactory {
private static final Logger log = Logger.getLogger(InfinispanCacheRealmProviderFactory.class);
+ public static final String REALM_CLEAR_CACHE_EVENTS = "REALM_CLEAR_CACHE_EVENTS";
protected volatile RealmCacheManager realmCache;
@@ -50,6 +54,14 @@ public class InfinispanCacheRealmProviderFactory implements CacheRealmProviderFa
Cache<String, Revisioned> cache = session.getProvider(InfinispanConnectionProvider.class).getCache(InfinispanConnectionProvider.REALM_CACHE_NAME);
Cache<String, Long> revisions = session.getProvider(InfinispanConnectionProvider.class).getCache(InfinispanConnectionProvider.REALM_REVISIONS_CACHE_NAME);
realmCache = new RealmCacheManager(cache, revisions);
+ ClusterProvider cluster = session.getProvider(ClusterProvider.class);
+ cluster.registerListener(REALM_CLEAR_CACHE_EVENTS, new ClusterListener() {
+ @Override
+ public void run(ClusterEvent event) {
+ realmCache.clear();
+ }
+ });
+
}
}
}
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 c520538..14d420b 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
@@ -20,6 +20,9 @@ package org.keycloak.models.cache.infinispan;
import org.infinispan.Cache;
import org.jboss.logging.Logger;
import org.keycloak.Config;
+import org.keycloak.cluster.ClusterEvent;
+import org.keycloak.cluster.ClusterListener;
+import org.keycloak.cluster.ClusterProvider;
import org.keycloak.connections.infinispan.InfinispanConnectionProvider;
import org.keycloak.models.KeycloakSession;
import org.keycloak.models.KeycloakSessionFactory;
@@ -33,6 +36,7 @@ import org.keycloak.models.cache.infinispan.entities.Revisioned;
public class InfinispanUserCacheProviderFactory implements UserCacheProviderFactory {
private static final Logger log = Logger.getLogger(InfinispanUserCacheProviderFactory.class);
+ public static final String USER_CLEAR_CACHE_EVENTS = "USER_CLEAR_CACHE_EVENTS";
protected volatile UserCacheManager userCache;
@@ -51,6 +55,13 @@ public class InfinispanUserCacheProviderFactory implements UserCacheProviderFact
Cache<String, Revisioned> cache = session.getProvider(InfinispanConnectionProvider.class).getCache(InfinispanConnectionProvider.USER_CACHE_NAME);
Cache<String, Long> revisions = session.getProvider(InfinispanConnectionProvider.class).getCache(InfinispanConnectionProvider.USER_REVISIONS_CACHE_NAME);
userCache = new UserCacheManager(cache, revisions);
+ ClusterProvider cluster = session.getProvider(ClusterProvider.class);
+ cluster.registerListener(USER_CLEAR_CACHE_EVENTS, new ClusterListener() {
+ @Override
+ public void run(ClusterEvent event) {
+ userCache.clear();
+ }
+ });
}
}
}
diff --git a/model/infinispan/src/main/java/org/keycloak/models/cache/infinispan/RealmCacheSession.java b/model/infinispan/src/main/java/org/keycloak/models/cache/infinispan/RealmCacheSession.java
index 7a72964..6cd91a5 100755
--- a/model/infinispan/src/main/java/org/keycloak/models/cache/infinispan/RealmCacheSession.java
+++ b/model/infinispan/src/main/java/org/keycloak/models/cache/infinispan/RealmCacheSession.java
@@ -18,6 +18,7 @@
package org.keycloak.models.cache.infinispan;
import org.jboss.logging.Logger;
+import org.keycloak.cluster.ClusterProvider;
import org.keycloak.migration.MigrationModel;
import org.keycloak.models.ClientModel;
import org.keycloak.models.ClientTemplateModel;
@@ -147,6 +148,8 @@ public class RealmCacheSession implements CacheRealmProvider {
@Override
public void clear() {
cache.clear();
+ ClusterProvider cluster = session.getProvider(ClusterProvider.class);
+ cluster.notify(InfinispanCacheRealmProviderFactory.REALM_CLEAR_CACHE_EVENTS, new ClearCacheEvent());
}
@Override
diff --git a/model/infinispan/src/main/java/org/keycloak/models/cache/infinispan/UserCacheSession.java b/model/infinispan/src/main/java/org/keycloak/models/cache/infinispan/UserCacheSession.java
index 7c090a6..c36f002 100755
--- a/model/infinispan/src/main/java/org/keycloak/models/cache/infinispan/UserCacheSession.java
+++ b/model/infinispan/src/main/java/org/keycloak/models/cache/infinispan/UserCacheSession.java
@@ -18,6 +18,7 @@
package org.keycloak.models.cache.infinispan;
import org.jboss.logging.Logger;
+import org.keycloak.cluster.ClusterProvider;
import org.keycloak.common.constants.ServiceAccountConstants;
import org.keycloak.component.ComponentModel;
import org.keycloak.models.ClientModel;
@@ -42,6 +43,7 @@ import org.keycloak.models.cache.infinispan.entities.CachedUser;
import org.keycloak.models.cache.infinispan.entities.CachedUserConsent;
import org.keycloak.models.cache.infinispan.entities.CachedUserConsents;
import org.keycloak.models.cache.infinispan.entities.UserListQuery;
+import org.keycloak.storage.UserStorageProvider;
import java.util.HashMap;
import java.util.HashSet;
@@ -78,6 +80,8 @@ public class UserCacheSession implements UserCache {
@Override
public void clear() {
cache.clear();
+ ClusterProvider cluster = session.getProvider(ClusterProvider.class);
+ cluster.notify(InfinispanUserCacheProviderFactory.USER_CLEAR_CACHE_EVENTS, new ClearCacheEvent());
}
public UserProvider getDelegate() {
@@ -652,10 +656,12 @@ public class UserCacheSession implements UserCache {
@Override
public void preRemove(RealmModel realm, RoleModel role) {
+ realmInvalidations.add(realm.getId()); // easier to just invalidate whole realm
getDelegate().preRemove(realm, role);
}
@Override
public void preRemove(RealmModel realm, GroupModel group) {
+ realmInvalidations.add(realm.getId()); // easier to just invalidate whole realm
getDelegate().preRemove(realm, group);
}
@@ -679,6 +685,8 @@ public class UserCacheSession implements UserCache {
@Override
public void preRemove(RealmModel realm, ComponentModel component) {
+ if (!component.getProviderType().equals(UserStorageProvider.class.getName())) return;
+ realmInvalidations.add(realm.getId()); // easier to just invalidate whole realm
getDelegate().preRemove(realm, component);
}
diff --git a/model/mongo/src/main/java/org/keycloak/storage/mongo/MongoUserFederatedStorageProvider.java b/model/mongo/src/main/java/org/keycloak/storage/mongo/MongoUserFederatedStorageProvider.java
index f232450..ec970bf 100644
--- a/model/mongo/src/main/java/org/keycloak/storage/mongo/MongoUserFederatedStorageProvider.java
+++ b/model/mongo/src/main/java/org/keycloak/storage/mongo/MongoUserFederatedStorageProvider.java
@@ -37,8 +37,10 @@ import org.keycloak.models.UserFederationProviderModel;
import org.keycloak.models.UserModel;
import org.keycloak.models.mongo.keycloak.entities.CredentialEntity;
import org.keycloak.models.mongo.keycloak.entities.FederatedIdentityEntity;
+import org.keycloak.models.mongo.keycloak.entities.MongoUserEntity;
import org.keycloak.models.utils.KeycloakModelUtils;
import org.keycloak.storage.StorageId;
+import org.keycloak.storage.UserStorageProvider;
import org.keycloak.storage.federated.UserAttributeFederatedStorage;
import org.keycloak.storage.federated.UserBrokerLinkFederatedStorage;
import org.keycloak.storage.federated.UserConsentFederatedStorage;
@@ -218,7 +220,10 @@ public class MongoUserFederatedStorageProvider implements
@Override
public void preRemove(RealmModel realm) {
-
+ DBObject query = new QueryBuilder()
+ .and("realmId").is(realm.getId())
+ .get();
+ getMongoStore().removeEntities(FederatedUser.class, query, true, invocationContext);
}
@Override
@@ -228,11 +233,23 @@ public class MongoUserFederatedStorageProvider implements
@Override
public void preRemove(RealmModel realm, GroupModel group) {
+ DBObject query = new QueryBuilder()
+ .and("groupIds").is(group.getId())
+ .get();
+
+ DBObject pull = new BasicDBObject("$pull", query);
+ getMongoStore().updateEntities(FederatedUser.class, query, pull, invocationContext);
}
@Override
public void preRemove(RealmModel realm, RoleModel role) {
+ DBObject query = new QueryBuilder()
+ .and("roleIds").is(role.getId())
+ .get();
+
+ DBObject pull = new BasicDBObject("$pull", query);
+ getMongoStore().updateEntities(FederatedUser.class, query, pull, invocationContext);
}
@@ -248,11 +265,17 @@ public class MongoUserFederatedStorageProvider implements
@Override
public void preRemove(RealmModel realm, UserModel user) {
+ getMongoStore().removeEntity(FederatedUser.class, user.getId(), invocationContext);
}
@Override
public void preRemove(RealmModel realm, ComponentModel model) {
+ if (!model.getProviderType().equals(UserStorageProvider.class.getName())) return;
+ DBObject query = new QueryBuilder()
+ .and("storageId").is(model.getId())
+ .get();
+ getMongoStore().removeEntities(FederatedUser.class, query, true, invocationContext);
}
diff --git a/server-spi/src/main/java/org/keycloak/models/UserFederationManager.java b/server-spi/src/main/java/org/keycloak/models/UserFederationManager.java
index 205c9c0..771a03a 100755
--- a/server-spi/src/main/java/org/keycloak/models/UserFederationManager.java
+++ b/server-spi/src/main/java/org/keycloak/models/UserFederationManager.java
@@ -520,6 +520,7 @@ public class UserFederationManager implements UserProvider {
@Override
public void preRemove(RealmModel realm, ComponentModel component) {
+ session.userStorage().preRemove(realm, component);
}
diff --git a/services/src/main/java/org/keycloak/storage/UserStorageManager.java b/services/src/main/java/org/keycloak/storage/UserStorageManager.java
index a03682c..90c8f72 100755
--- a/services/src/main/java/org/keycloak/storage/UserStorageManager.java
+++ b/services/src/main/java/org/keycloak/storage/UserStorageManager.java
@@ -561,6 +561,9 @@ public class UserStorageManager implements UserProvider, OnUserCache {
@Override
public void preRemove(RealmModel realm, ComponentModel component) {
+ if (!component.getProviderType().equals(UserStorageProvider.class.getName())) return;
+ localStorage().preRemove(realm, component);
+ if (getFederatedStorage() != null) getFederatedStorage().preRemove(realm, component);
}
diff --git a/testsuite/integration/src/test/java/org/keycloak/testsuite/federation/storage/UserStorageTest.java b/testsuite/integration/src/test/java/org/keycloak/testsuite/federation/storage/UserStorageTest.java
index 2b2d962..1f6dd10 100644
--- a/testsuite/integration/src/test/java/org/keycloak/testsuite/federation/storage/UserStorageTest.java
+++ b/testsuite/integration/src/test/java/org/keycloak/testsuite/federation/storage/UserStorageTest.java
@@ -50,6 +50,7 @@ import java.util.Set;
*/
public class UserStorageTest {
public static ComponentModel memoryProvider = null;
+ public static ComponentModel writableProvider = null;
@ClassRule
public static KeycloakRule keycloakRule = new KeycloakRule(new KeycloakRule.KeycloakSetup() {
@@ -69,16 +70,22 @@ public class UserStorageTest {
model.setParentId(appRealm.getId());
model.getConfig().putSingle("propertyFile", "/storage-test/read-only-user-password.properties");
appRealm.addComponentModel(model);
- model = new UserStorageProviderModel();
- model.setName("user-props");
- model.setPriority(2);
- model.setParentId(appRealm.getId());
- model.setProviderId(UserPropertyFileStorageFactory.PROVIDER_ID);
- model.getConfig().putSingle("propertyFile", "/storage-test/user-password.properties");
- model.getConfig().putSingle("federatedStorage", "true");
- appRealm.addComponentModel(model);
+ createUserPropModel(appRealm);
}
});
+
+ private static void createUserPropModel(RealmModel appRealm) {
+ UserStorageProviderModel model;
+ model = new UserStorageProviderModel();
+ model.setName("user-props");
+ model.setPriority(2);
+ model.setParentId(appRealm.getId());
+ model.setProviderId(UserPropertyFileStorageFactory.PROVIDER_ID);
+ model.getConfig().putSingle("propertyFile", "/storage-test/user-password.properties");
+ model.getConfig().putSingle("federatedStorage", "true");
+ writableProvider = appRealm.addComponentModel(model);
+ }
+
@Rule
public WebRule webRule = new WebRule(this);
@@ -162,6 +169,40 @@ public class UserStorageTest {
session.userCredentialManager().updateCredential(realm, thor, UserCredentialModel.password("lightning"));
keycloakRule.stopSession(session, true);
loginSuccessAndLogout("thor", "lightning");
+
+ // test removal of provider
+ session = keycloakRule.startSession();
+ realm = session.realms().getRealmByName("test");
+ realm.removeComponent(writableProvider);
+ keycloakRule.stopSession(session, true);
+ session = keycloakRule.startSession();
+ realm = session.realms().getRealmByName("test");
+ createUserPropModel(realm);
+ keycloakRule.stopSession(session, true);
+
+ loginSuccessAndLogout("thor", "hammer");
+
+ session = keycloakRule.startSession();
+ realm = session.realms().getRealmByName("test");
+
+ thor = session.users().getUserByUsername("thor", realm);
+ Assert.assertNull(thor.getFirstName());
+ Assert.assertNull(thor.getLastName());
+ Assert.assertNull(thor.getEmail());
+ Assert.assertNull(thor.getFirstAttribute("test-attribute"));
+ Assert.assertFalse(thor.isEmailVerified());
+ role = realm.getRole("foo-role");
+ Assert.assertFalse(thor.hasRole(role));
+
+ groups = thor.getGroups();
+ foundGroup = false;
+ for (GroupModel g : groups) {
+ if (g.getName().equals("my-group")) foundGroup = true;
+
+ }
+ Assert.assertFalse(foundGroup);
+
+
}
@Test