added persistence support for clearing ReplicatingCacheManager and ReplicatingCache; added session garbage collection upon SecurityService construction

Change-Id: I78475411ef01916678e383922d9d0bd11dfd5aec
This commit is contained in:
Axel Uhl
2019-01-18 13:45:12 +01:00
parent eb587c7959
commit 546cac6548
8 changed files with 62 additions and 14 deletions
@@ -84,4 +84,33 @@ public class SessionPersistenceTest {
mof.removeSession(cacheName, session);
assertTrue(dof.loadSessionsByCacheName().isEmpty());
}
@Test
public void testSessionExpiry() throws InterruptedException {
final String cacheName = "shiroSessionCache";
final String host = "myHost";
final String id = UUID.randomUUID().toString();
final Date start = new Date();
final Date last = new Date();
final long timeout = 5000l; // 5s
final SimpleSession session = new SimpleSession();
session.setId(id);
session.setHost(host);
session.setStartTimestamp(start);
session.setLastAccessTime(last);
session.setTimeout(timeout);
mof.storeSession(cacheName, session);
final Map<String, Set<Session>> sessions = dof.loadSessionsByCacheName();
// expecting the session to be still there because the 10s haven't expired yet:
final Session readSession = sessions.get(cacheName).iterator().next();
assertEquals(session, readSession);
assertEquals(id, readSession.getId());
assertEquals(host, readSession.getHost());
assertEquals(start, readSession.getStartTimestamp());
assertEquals(last, readSession.getLastAccessTime());
assertEquals(timeout, readSession.getTimeout());
Thread.sleep(timeout);
final Map<String, Set<Session>> sessionsWithoutExpired = dof.loadSessionsByCacheName();
assertTrue(sessionsWithoutExpired.isEmpty());
}
}
@@ -8,6 +8,6 @@ public interface MongoObjectFactory {
void removeSession(String cacheName, Session session);
void removeAllSessions();
void removeAllSessions(String cacheName);
}
@@ -2,8 +2,10 @@ package com.sap.sse.security.persistence.impl;
import java.io.Serializable;
import java.util.HashMap;
import java.util.HashSet;
import java.util.Map;
import java.util.Set;
import java.util.logging.Logger;
import org.apache.shiro.session.Session;
import org.apache.shiro.session.mgt.SimpleSession;
@@ -13,9 +15,12 @@ import org.bson.Document;
import com.mongodb.client.MongoCollection;
import com.mongodb.client.MongoDatabase;
import com.sap.sse.common.Util;
import com.sap.sse.common.impl.MillisecondsDurationImpl;
import com.sap.sse.common.impl.MillisecondsTimePoint;
import com.sap.sse.security.persistence.DomainObjectFactory;
public class DomainObjectFactoryImpl implements DomainObjectFactory {
private static final Logger logger = Logger.getLogger(DomainObjectFactoryImpl.class.getName());
private final MongoCollection<Document> sessionsCollection;
public DomainObjectFactoryImpl(MongoDatabase mongoDatabase) {
@@ -25,11 +30,19 @@ public class DomainObjectFactoryImpl implements DomainObjectFactory {
@Override
public Map<String, Set<Session>> loadSessionsByCacheName() {
final Map<String, Set<Session>> sessionsByCacheName = new HashMap<>();
final Set<Serializable> expiredSessionIds = new HashSet<>();
sessionsCollection.find().forEach((Document sessionDocument)->{
final String cacheName = sessionDocument.getString(FieldNames.CACHE_NAME.name());
final Session session = loadSession(sessionDocument);
Util.addToValueSet(sessionsByCacheName, cacheName, session);
if (new MillisecondsTimePoint(session.getLastAccessTime()).plus(new MillisecondsDurationImpl(session.getTimeout())).before(MillisecondsTimePoint.now())) {
// expired
logger.info("Session "+session+" expired");
expiredSessionIds.add(session.getId());
} else {
Util.addToValueSet(sessionsByCacheName, cacheName, session);
}
});
expiredSessionIds.forEach(id->sessionsCollection.deleteOne(new Document(FieldNames.SESSION_ID.name(), id)));
return sessionsByCacheName;
}
@@ -82,8 +82,8 @@ public class MongoObjectFactoryImpl implements MongoObjectFactory {
}
@Override
public void removeAllSessions() {
sessionCollection.deleteMany(new Document());
public void removeAllSessions(String cacheName) {
sessionCollection.deleteMany(new Document(FieldNames.CACHE_NAME.name(), cacheName));
}
@Override
@@ -49,6 +49,6 @@ public interface ReplicableSecurityService extends SecurityService {
void removeSession(String cacheName, Session result);
void removeAllSessions();
void removeAllSessions(String cacheName);
}
@@ -109,7 +109,7 @@ public class ReplicatingCache<K, V> implements Cache<K, V>, Named {
securityService.replicate(s->{
s.getCacheManager().getCache(myName).clear(); return null;
});
securityService.removeAllSessions();
securityService.removeAllSessions(getName());
}
@Override
@@ -1,6 +1,7 @@
package com.sap.sse.security.impl;
import java.io.Serializable;
import java.util.Iterator;
import java.util.Map.Entry;
import java.util.concurrent.ConcurrentHashMap;
import java.util.concurrent.ConcurrentMap;
@@ -56,14 +57,14 @@ public class ReplicatingCacheManager implements CacheManager, Serializable {
if (castCache == null) {
this.caches.put(name, otherCache);
} else {
putAll(castCache, otherCache);
putAll(otherCache, castCache);
}
}
private <K, V> void putAll(final ReplicatingCache<K, V> from, ReplicatingCache<K, V> into) {
from.clear();
for (K k:into.keys()) {
from.put(k, into.get(k));
private <K, V> void putAll(ReplicatingCache<K, V> from, final ReplicatingCache<K, V> to) {
to.clear();
for (K k : from.keys()) {
to.put(k, from.get(k));
}
}
@@ -71,6 +72,11 @@ public class ReplicatingCacheManager implements CacheManager, Serializable {
* For test purposes; clears all state held by this cache manager.
*/
public void clear() {
this.caches.clear();
final ReplicableSecurityService securityService = (ReplicableSecurityService) Activator.getSecurityService();
for (final Iterator<Entry<String, ReplicatingCache<?, ?>>> i=caches.entrySet().iterator(); i.hasNext(); ) {
final Entry<String, ReplicatingCache<?, ?>> cacheNameAndCache = i.next();
securityService.removeAllSessions(cacheNameAndCache.getKey());
i.remove();
}
}
}
@@ -1132,7 +1132,7 @@ public class SecurityServiceImpl implements ReplicableSecurityService, ClearStat
}
@Override
public void removeAllSessions() {
PersistenceFactory.INSTANCE.getDefaultMongoObjectFactory().removeAllSessions();
public void removeAllSessions(String cacheName) {
PersistenceFactory.INSTANCE.getDefaultMongoObjectFactory().removeAllSessions(cacheName);
}
}