diff --git a/model/infinispan/src/main/java/org/keycloak/models/cache/infinispan/ClientAdapter.java b/model/infinispan/src/main/java/org/keycloak/models/cache/infinispan/ClientAdapter.java index 3176506743..4c28ad69e5 100755 --- a/model/infinispan/src/main/java/org/keycloak/models/cache/infinispan/ClientAdapter.java +++ b/model/infinispan/src/main/java/org/keycloak/models/cache/infinispan/ClientAdapter.java @@ -23,6 +23,7 @@ import org.keycloak.models.ProtocolMapperModel; import org.keycloak.models.RealmModel; import org.keycloak.models.RoleContainerModel; import org.keycloak.models.RoleModel; +import org.keycloak.models.cache.CachedObject; import org.keycloak.models.cache.infinispan.entities.CachedClient; import java.security.MessageDigest; @@ -36,7 +37,7 @@ import java.util.Set; * @author Bill Burke * @version $Revision: 1 $ */ -public class ClientAdapter implements ClientModel { +public class ClientAdapter implements ClientModel, CachedObject { protected RealmCacheSession cacheSession; protected RealmModel cachedRealm; @@ -69,6 +70,11 @@ public class ClientAdapter implements ClientModel { return true; } + @Override + public long getCacheTimestamp() { + return cached.getCacheTimestamp(); + } + @Override public void updateClient() { if (updated != null) updated.updateClient(); diff --git a/model/infinispan/src/main/java/org/keycloak/models/cache/infinispan/RealmAdapter.java b/model/infinispan/src/main/java/org/keycloak/models/cache/infinispan/RealmAdapter.java index 17cacc8b4a..dd623775f7 100755 --- a/model/infinispan/src/main/java/org/keycloak/models/cache/infinispan/RealmAdapter.java +++ b/model/infinispan/src/main/java/org/keycloak/models/cache/infinispan/RealmAdapter.java @@ -1336,8 +1336,14 @@ public class RealmAdapter implements CachedRealmModel { public void executeEvictions(ComponentModel model) { if (model == null) return; - // test that this is a realm component - if (model.getParentId() != null && !model.getParentId().equals(getId())) return; + // If not realm component, check to see if it is a user storage provider child component (i.e. LDAP mapper) + if (model.getParentId() != null && !model.getParentId().equals(getId())) { + ComponentModel parent = getComponent(model.getParentId()); + if (parent != null && UserStorageProvider.class.getName().equals(parent.getProviderType())) { + session.userCache().evict(this); + } + return; + } // invalidate entire user cache if we're dealing with user storage SPI if (UserStorageProvider.class.getName().equals(model.getProviderType())) { diff --git a/testsuite/integration-arquillian/tests/base/src/test/java/org/keycloak/testsuite/federation/storage/ClientStorageTest.java b/testsuite/integration-arquillian/tests/base/src/test/java/org/keycloak/testsuite/federation/storage/ClientStorageTest.java index c8c2f529f1..eb231db4e1 100644 --- a/testsuite/integration-arquillian/tests/base/src/test/java/org/keycloak/testsuite/federation/storage/ClientStorageTest.java +++ b/testsuite/integration-arquillian/tests/base/src/test/java/org/keycloak/testsuite/federation/storage/ClientStorageTest.java @@ -228,49 +228,6 @@ public class ClientStorageTest extends AbstractTestRealmKeycloakTest { events.clear(); } - /* - - @Test - public void testDailyEviction() { - - // set eviction to 1 hour from now - Calendar eviction = Calendar.getInstance(); - eviction.add(Calendar.HOUR, 1); - ComponentRepresentation propProviderRW = testRealmResource().components().component(propProviderRWId).toRepresentation(); - propProviderRW.getConfig().putSingle(CACHE_POLICY, CacheableStorageProviderModel.CachePolicy.EVICT_DAILY.name()); - propProviderRW.getConfig().putSingle(EVICTION_HOUR, Integer.toString(eviction.get(HOUR_OF_DAY))); - propProviderRW.getConfig().putSingle(EVICTION_MINUTE, Integer.toString(eviction.get(MINUTE))); - testRealmResource().components().component(propProviderRWId).update(propProviderRW); - - // now - testingClient.server().run(session -> { - RealmModel realm = session.realms().getRealmByName("test"); - UserModel user = session.users().getUserByUsername("thor", realm); - }); - - // run twice to make sure its in cache. - testingClient.server().run(session -> { - RealmModel realm = session.realms().getRealmByName("test"); - UserModel user = session.users().getUserByUsername("thor", realm); - System.out.println("User class: " + user.getClass()); - Assert.assertTrue(user instanceof CachedUserModel); // should still be cached - }); - - setTimeOffset(2 * 60 * 60); // 2 hours in future - - testingClient.server().run(session -> { - RealmModel realm = session.realms().getRealmByName("test"); - UserModel user = session.users().getUserByUsername("thor", realm); - System.out.println("User class: " + user.getClass()); - Assert.assertFalse(user instanceof CachedUserModel); // should be evicted - }); - - } - - - */ - - @Test public void testDailyEviction() { testIsCached(); diff --git a/testsuite/integration-deprecated/src/test/java/org/keycloak/testsuite/federation/storage/UserStorageTest.java b/testsuite/integration-deprecated/src/test/java/org/keycloak/testsuite/federation/storage/UserStorageTest.java index 48228a24af..d446985339 100644 --- a/testsuite/integration-deprecated/src/test/java/org/keycloak/testsuite/federation/storage/UserStorageTest.java +++ b/testsuite/integration-deprecated/src/test/java/org/keycloak/testsuite/federation/storage/UserStorageTest.java @@ -167,16 +167,36 @@ public class UserStorageTest { KeycloakSession session = keycloakRule.startSession(); RealmModel realm = session.realms().getRealmByName("test"); CachedUserModel thor = (CachedUserModel)session.users().getUserByUsername("thor", realm); - long thorTimestamp = thor.getCacheTimestamp(); + long lastTimestamp = thor.getCacheTimestamp(); realm.updateComponent(model); keycloakRule.stopSession(session, true); + session = keycloakRule.startSession(); + realm = session.realms().getRealmByName("test"); + thor = (CachedUserModel)session.users().getUserByUsername("thor", realm); + lastTimestamp = thor.getCacheTimestamp(); + realm.updateComponent(model); + keycloakRule.stopSession(session, true); + + // test is cached + session = keycloakRule.startSession(); + realm = session.realms().getRealmByName("test"); + thor = (CachedUserModel)session.users().getUserByUsername("thor", realm); + // thor should be evicted because we changed the model + Assert.assertTrue(thor.getCacheTimestamp() > lastTimestamp); + lastTimestamp = thor.getCacheTimestamp(); + keycloakRule.stopSession(session, true); + + Time.setOffset(60 * 2 * 60); // 2 hours session = keycloakRule.startSession(); realm = session.realms().getRealmByName("test"); UserModel thor2 = session.users().getUserByUsername("thor", realm); - Assert.assertFalse(thor2 instanceof CachedUserModel); + // thor should be evicted because we put it 2 hours in the future + if (thor2 instanceof CachedUserModel) { + Assert.assertTrue(((CachedUserModel)thor2).getCacheTimestamp() > lastTimestamp); + } model.getConfig().remove("cachePolicy"); model.getConfig().remove("evictionHour"); model.getConfig().remove("evictionMinute"); @@ -199,24 +219,46 @@ public class UserStorageTest { KeycloakSession session = keycloakRule.startSession(); RealmModel realm = session.realms().getRealmByName("test"); - CachedUserModel thor = (CachedUserModel)session.users().getUserByUsername("thor", realm); realm.updateComponent(model); keycloakRule.stopSession(session, true); + session = keycloakRule.startSession(); + realm = session.realms().getRealmByName("test"); + CachedUserModel thor = (CachedUserModel)session.users().getUserByUsername("thor", realm); + long lastTimestamp = thor.getCacheTimestamp(); + keycloakRule.stopSession(session, true); + + session = keycloakRule.startSession(); + realm = session.realms().getRealmByName("test"); + thor = (CachedUserModel)session.users().getUserByUsername("thor", realm); + lastTimestamp = thor.getCacheTimestamp(); + keycloakRule.stopSession(session, true); + + session = keycloakRule.startSession(); + realm = session.realms().getRealmByName("test"); + thor = (CachedUserModel)session.users().getUserByUsername("thor", realm); + lastTimestamp = thor.getCacheTimestamp(); + keycloakRule.stopSession(session, true); + Time.setOffset(60 * 60 * 24 * 2); // 2 days in future, should be cached still session = keycloakRule.startSession(); realm = session.realms().getRealmByName("test"); // test still - UserModel thor2 = session.users().getUserByUsername("thor", realm); - Assert.assertTrue(thor2 instanceof CachedUserModel); + thor = (CachedUserModel)session.users().getUserByUsername("thor", realm); + Assert.assertEquals(thor.getCacheTimestamp(), lastTimestamp); + lastTimestamp = thor.getCacheTimestamp(); keycloakRule.stopSession(session, true); + Time.setOffset(Time.getOffset() + 60 * 60 * 24 * 3); // 3 days into future, cache will be invalidated session = keycloakRule.startSession(); realm = session.realms().getRealmByName("test"); - thor2 = session.users().getUserByUsername("thor", realm); - Assert.assertFalse(thor2 instanceof CachedUserModel); + UserModel thor2 = session.users().getUserByUsername("thor", realm); + // thor should be evicted because we put it 2 hours in the future + if (thor2 instanceof CachedUserModel) { + Assert.assertTrue(((CachedUserModel)thor2).getCacheTimestamp() > lastTimestamp); + } model.getConfig().remove("cachePolicy"); model.getConfig().remove("evictionHour"); model.getConfig().remove("evictionMinute"); @@ -233,24 +275,44 @@ public class UserStorageTest { KeycloakSession session = keycloakRule.startSession(); RealmModel realm = session.realms().getRealmByName("test"); - CachedUserModel thor = (CachedUserModel)session.users().getUserByUsername("thor", realm); realm.updateComponent(model); keycloakRule.stopSession(session, true); - Time.setOffset(60 * 5); // 5 minutes in future, should be cached still + session = keycloakRule.startSession(); + realm = session.realms().getRealmByName("test"); + CachedUserModel thor = (CachedUserModel)session.users().getUserByUsername("thor", realm); + long lastTimestamp = thor.getCacheTimestamp(); + keycloakRule.stopSession(session, true); + session = keycloakRule.startSession(); realm = session.realms().getRealmByName("test"); - // test still - UserModel thor2 = session.users().getUserByUsername("thor", realm); - Assert.assertTrue(thor2 instanceof CachedUserModel); + thor = (CachedUserModel)session.users().getUserByUsername("thor", realm); + lastTimestamp = thor.getCacheTimestamp(); keycloakRule.stopSession(session, true); + + + Time.setOffset(60 * 5); // 5 minutes in future, should be cached still + session = keycloakRule.startSession(); + realm = session.realms().getRealmByName("test"); + thor = (CachedUserModel)session.users().getUserByUsername("thor", realm); + Assert.assertEquals(thor.getCacheTimestamp(), lastTimestamp); + lastTimestamp = thor.getCacheTimestamp(); + keycloakRule.stopSession(session, true); + Time.setOffset(60 * 20); // 20 minutes into future, cache will be invalidated session = keycloakRule.startSession(); realm = session.realms().getRealmByName("test"); - thor2 = session.users().getUserByUsername("thor", realm); - Assert.assertFalse(thor2 instanceof CachedUserModel); + UserModel thor2 = session.users().getUserByUsername("thor", realm); + // thor should be evicted because we put it 2 hours in the future + if (thor2 instanceof CachedUserModel) { + Assert.assertTrue(((CachedUserModel)thor2).getCacheTimestamp() > lastTimestamp); + } + keycloakRule.stopSession(session, true); + + session = keycloakRule.startSession(); + realm = session.realms().getRealmByName("test"); model.getConfig().remove("cachePolicy"); model.getConfig().remove("maxLifespan"); realm.updateComponent(model);