keycloak-aplcache

final mongo fixes

9/30/2016 8:08:34 PM

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