keycloak-memoizeit
Changes
model/sessions-infinispan/src/main/java/org/keycloak/models/sessions/infinispan/entities/LoginFailureEntity.java 4(+0 -4)
model/sessions-infinispan/src/main/java/org/keycloak/models/sessions/infinispan/entities/LoginFailureKey.java 36(+36 -0)
model/sessions-infinispan/src/main/java/org/keycloak/models/sessions/infinispan/InfinispanUserSessionProvider.java 30(+18 -12)
model/sessions-infinispan/src/main/java/org/keycloak/models/sessions/infinispan/InfinispanUserSessionProviderFactory.java 3(+2 -1)
model/sessions-infinispan/src/main/java/org/keycloak/models/sessions/infinispan/UsernameLoginFailureAdapter.java 9(+6 -3)
model/sessions-jpa/src/main/java/org/keycloak/models/sessions/jpa/entities/UsernameLoginFailureEntity.java 2(+1 -1)
model/sessions-jpa/src/main/java/org/keycloak/models/sessions/jpa/JpaUserSessionProvider.java 2(+1 -1)
model/sessions-mem/src/main/java/org/keycloak/models/sessions/mem/MemUserSessionProvider.java 5(+3 -2)
Details
diff --git a/model/sessions-infinispan/src/main/java/org/keycloak/models/sessions/infinispan/entities/LoginFailureEntity.java b/model/sessions-infinispan/src/main/java/org/keycloak/models/sessions/infinispan/entities/LoginFailureEntity.java
index 09bfca6..e742bbc 100644
--- a/model/sessions-infinispan/src/main/java/org/keycloak/models/sessions/infinispan/entities/LoginFailureEntity.java
+++ b/model/sessions-infinispan/src/main/java/org/keycloak/models/sessions/infinispan/entities/LoginFailureEntity.java
@@ -12,10 +12,6 @@ public class LoginFailureEntity {
private long lastFailure;
private String lastIPFailure;
- public String getId() {
- return realm + ":" + username;
- }
-
public String getUsername() {
return username;
}
diff --git a/model/sessions-infinispan/src/main/java/org/keycloak/models/sessions/infinispan/entities/LoginFailureKey.java b/model/sessions-infinispan/src/main/java/org/keycloak/models/sessions/infinispan/entities/LoginFailureKey.java
new file mode 100644
index 0000000..f8dbe87
--- /dev/null
+++ b/model/sessions-infinispan/src/main/java/org/keycloak/models/sessions/infinispan/entities/LoginFailureKey.java
@@ -0,0 +1,36 @@
+package org.keycloak.models.sessions.infinispan.entities;
+
+/**
+ * @author <a href="mailto:sthorger@redhat.com">Stian Thorgersen</a>
+ */
+public class LoginFailureKey {
+
+ private final String realm;
+ private final String username;
+
+ public LoginFailureKey(String realm, String username) {
+ this.realm = realm;
+ this.username = username;
+ }
+
+ @Override
+ public boolean equals(Object o) {
+ if (this == o) return true;
+ if (o == null || getClass() != o.getClass()) return false;
+
+ LoginFailureKey key = (LoginFailureKey) o;
+
+ if (realm != null ? !realm.equals(key.realm) : key.realm != null) return false;
+ if (username != null ? !username.equals(key.username) : key.username != null) return false;
+
+ return true;
+ }
+
+ @Override
+ public int hashCode() {
+ int result = realm != null ? realm.hashCode() : 0;
+ result = 31 * result + (username != null ? username.hashCode() : 0);
+ return result;
+ }
+
+}
diff --git a/model/sessions-infinispan/src/main/java/org/keycloak/models/sessions/infinispan/InfinispanUserSessionProvider.java b/model/sessions-infinispan/src/main/java/org/keycloak/models/sessions/infinispan/InfinispanUserSessionProvider.java
index 60bcb59..617f686 100644
--- a/model/sessions-infinispan/src/main/java/org/keycloak/models/sessions/infinispan/InfinispanUserSessionProvider.java
+++ b/model/sessions-infinispan/src/main/java/org/keycloak/models/sessions/infinispan/InfinispanUserSessionProvider.java
@@ -13,6 +13,7 @@ import org.keycloak.models.UserSessionProvider;
import org.keycloak.models.UsernameLoginFailureModel;
import org.keycloak.models.sessions.infinispan.entities.ClientSessionEntity;
import org.keycloak.models.sessions.infinispan.entities.LoginFailureEntity;
+import org.keycloak.models.sessions.infinispan.entities.LoginFailureKey;
import org.keycloak.models.sessions.infinispan.entities.SessionEntity;
import org.keycloak.models.sessions.infinispan.entities.UserSessionEntity;
import org.keycloak.models.sessions.infinispan.mapreduce.ClientSessionMapper;
@@ -39,10 +40,10 @@ public class InfinispanUserSessionProvider implements UserSessionProvider {
private final KeycloakSession session;
private final Cache<String, SessionEntity> sessionCache;
- private final Cache<String, LoginFailureEntity> loginFailureCache;
+ private final Cache<LoginFailureKey, LoginFailureEntity> loginFailureCache;
private final InfinispanKeycloakTransaction tx;
- public InfinispanUserSessionProvider(KeycloakSession session, Cache<String, SessionEntity> sessionCache, Cache<String, LoginFailureEntity> loginFailureCache) {
+ public InfinispanUserSessionProvider(KeycloakSession session, Cache<String, SessionEntity> sessionCache, Cache<LoginFailureKey, LoginFailureEntity> loginFailureCache) {
this.session = session;
this.sessionCache = sessionCache;
this.loginFailureCache = loginFailureCache;
@@ -225,16 +226,18 @@ public class InfinispanUserSessionProvider implements UserSessionProvider {
@Override
public UsernameLoginFailureModel getUserLoginFailure(RealmModel realm, String username) {
- return wrap(loginFailureCache.get(realm.getId() + ":" + username));
+ LoginFailureKey key = new LoginFailureKey(realm.getId(), username);
+ return wrap(key, loginFailureCache.get(key));
}
@Override
public UsernameLoginFailureModel addUserLoginFailure(RealmModel realm, String username) {
+ LoginFailureKey key = new LoginFailureKey(realm.getId(), username);
LoginFailureEntity entity = new LoginFailureEntity();
entity.setRealm(realm.getId());
entity.setUsername(username);
- tx.put(loginFailureCache, entity.getId(), entity);
- return wrap(entity);
+ tx.put(loginFailureCache, key, entity);
+ return wrap(key, entity);
}
@Override
@@ -257,6 +260,9 @@ public class InfinispanUserSessionProvider implements UserSessionProvider {
@Override
public void onUserRemoved(RealmModel realm, UserModel user) {
removeUserSessions(realm, user);
+
+ loginFailureCache.remove(new LoginFailureKey(realm.getId(), user.getUsername()));
+ loginFailureCache.remove(new LoginFailureKey(realm.getId(), user.getEmail()));
}
@Override
@@ -321,8 +327,8 @@ public class InfinispanUserSessionProvider implements UserSessionProvider {
}
- UsernameLoginFailureModel wrap(LoginFailureEntity entity) {
- return entity != null ? new UsernameLoginFailureAdapter(this, loginFailureCache, entity) : null;
+ UsernameLoginFailureModel wrap(LoginFailureKey key, LoginFailureEntity entity) {
+ return entity != null ? new UsernameLoginFailureAdapter(this, loginFailureCache, key, entity) : null;
}
List<ClientSessionModel> wrapClientSessions(RealmModel realm, Collection<ClientSessionEntity> entities) {
@@ -337,7 +343,7 @@ public class InfinispanUserSessionProvider implements UserSessionProvider {
private boolean active;
private boolean rollback;
- private Map<String, CacheTask> tasks = new HashMap<String, CacheTask>();
+ private Map<Object, CacheTask> tasks = new HashMap<Object, CacheTask>();
@Override
public void begin() {
@@ -375,7 +381,7 @@ public class InfinispanUserSessionProvider implements UserSessionProvider {
return active;
}
- public void put(Cache cache, String key, Object value) {
+ public void put(Cache cache, Object key, Object value) {
if (tasks.containsKey(key)) {
throw new IllegalStateException("Can't add session: task in progress for session");
} else {
@@ -383,7 +389,7 @@ public class InfinispanUserSessionProvider implements UserSessionProvider {
}
}
- public void replace(Cache cache, String key, Object value) {
+ public void replace(Cache cache, Object key, Object value) {
CacheTask current = tasks.get(key);
if (current != null) {
switch (current.operation) {
@@ -406,10 +412,10 @@ public class InfinispanUserSessionProvider implements UserSessionProvider {
public class CacheTask {
private Cache cache;
private CacheOperation operation;
- private String key;
+ private Object key;
private Object value;
- public CacheTask(Cache cache, CacheOperation operation, String key, Object value) {
+ public CacheTask(Cache cache, CacheOperation operation, Object key, Object value) {
this.cache = cache;
this.operation = operation;
this.key = key;
diff --git a/model/sessions-infinispan/src/main/java/org/keycloak/models/sessions/infinispan/InfinispanUserSessionProviderFactory.java b/model/sessions-infinispan/src/main/java/org/keycloak/models/sessions/infinispan/InfinispanUserSessionProviderFactory.java
index 6098f3f..977eb3d 100755
--- a/model/sessions-infinispan/src/main/java/org/keycloak/models/sessions/infinispan/InfinispanUserSessionProviderFactory.java
+++ b/model/sessions-infinispan/src/main/java/org/keycloak/models/sessions/infinispan/InfinispanUserSessionProviderFactory.java
@@ -7,6 +7,7 @@ import org.keycloak.models.KeycloakSession;
import org.keycloak.models.UserSessionProvider;
import org.keycloak.models.UserSessionProviderFactory;
import org.keycloak.models.sessions.infinispan.entities.LoginFailureEntity;
+import org.keycloak.models.sessions.infinispan.entities.LoginFailureKey;
import org.keycloak.models.sessions.infinispan.entities.SessionEntity;
/**
@@ -21,7 +22,7 @@ public class InfinispanUserSessionProviderFactory implements UserSessionProvider
public UserSessionProvider create(KeycloakSession session) {
InfinispanConnectionProvider connections = session.getProvider(InfinispanConnectionProvider.class);
Cache<String, SessionEntity> cache = connections.getCache(SESSION_CACHE_NAME);
- Cache<String, LoginFailureEntity> loginFailures = connections.getCache(LOGIN_FAILURE_CACHE_NAME);
+ Cache<LoginFailureKey, LoginFailureEntity> loginFailures = connections.getCache(LOGIN_FAILURE_CACHE_NAME);
return new InfinispanUserSessionProvider(session, cache, loginFailures);
}
diff --git a/model/sessions-infinispan/src/main/java/org/keycloak/models/sessions/infinispan/UsernameLoginFailureAdapter.java b/model/sessions-infinispan/src/main/java/org/keycloak/models/sessions/infinispan/UsernameLoginFailureAdapter.java
index 2de7089..fed8f28 100755
--- a/model/sessions-infinispan/src/main/java/org/keycloak/models/sessions/infinispan/UsernameLoginFailureAdapter.java
+++ b/model/sessions-infinispan/src/main/java/org/keycloak/models/sessions/infinispan/UsernameLoginFailureAdapter.java
@@ -3,6 +3,7 @@ package org.keycloak.models.sessions.infinispan;
import org.infinispan.Cache;
import org.keycloak.models.UsernameLoginFailureModel;
import org.keycloak.models.sessions.infinispan.entities.LoginFailureEntity;
+import org.keycloak.models.sessions.infinispan.entities.LoginFailureKey;
/**
* @author <a href="mailto:sthorger@redhat.com">Stian Thorgersen</a>
@@ -10,12 +11,14 @@ import org.keycloak.models.sessions.infinispan.entities.LoginFailureEntity;
public class UsernameLoginFailureAdapter implements UsernameLoginFailureModel {
private InfinispanUserSessionProvider provider;
- private Cache<String, LoginFailureEntity> cache;
+ private Cache<LoginFailureKey, LoginFailureEntity> cache;
+ private LoginFailureKey key;
private LoginFailureEntity entity;
- public UsernameLoginFailureAdapter(InfinispanUserSessionProvider provider, Cache<String, LoginFailureEntity> cache, LoginFailureEntity entity) {
+ public UsernameLoginFailureAdapter(InfinispanUserSessionProvider provider, Cache<LoginFailureKey, LoginFailureEntity> cache, LoginFailureKey key, LoginFailureEntity entity) {
this.provider = provider;
this.cache = cache;
+ this.key = key;
this.entity = entity;
}
@@ -75,7 +78,7 @@ public class UsernameLoginFailureAdapter implements UsernameLoginFailureModel {
}
void update() {
- provider.getTx().replace(cache, entity.getId(), entity);
+ provider.getTx().replace(cache, key, entity);
}
}
diff --git a/model/sessions-jpa/src/main/java/org/keycloak/models/sessions/jpa/entities/UsernameLoginFailureEntity.java b/model/sessions-jpa/src/main/java/org/keycloak/models/sessions/jpa/entities/UsernameLoginFailureEntity.java
index 899d80e..c62e474 100755
--- a/model/sessions-jpa/src/main/java/org/keycloak/models/sessions/jpa/entities/UsernameLoginFailureEntity.java
+++ b/model/sessions-jpa/src/main/java/org/keycloak/models/sessions/jpa/entities/UsernameLoginFailureEntity.java
@@ -18,7 +18,7 @@ import java.io.Serializable;
@NamedQueries({
@NamedQuery(name="getAllFailures", query="select failure from UsernameLoginFailureEntity failure"),
@NamedQuery(name = "removeLoginFailuresByRealm", query = "delete from UsernameLoginFailureEntity f where f.realmId = :realmId"),
- @NamedQuery(name = "removeLoginFailuresByUser", query = "delete from UsernameLoginFailureEntity f where f.realmId = :realmId and f.username = :username")
+ @NamedQuery(name = "removeLoginFailuresByUser", query = "delete from UsernameLoginFailureEntity f where f.realmId = :realmId and (f.username = :username or f.username = :email)")
})
@IdClass(UsernameLoginFailureEntity.Key.class)
public class UsernameLoginFailureEntity {
diff --git a/model/sessions-jpa/src/main/java/org/keycloak/models/sessions/jpa/JpaUserSessionProvider.java b/model/sessions-jpa/src/main/java/org/keycloak/models/sessions/jpa/JpaUserSessionProvider.java
index a54891d..6708a2d 100755
--- a/model/sessions-jpa/src/main/java/org/keycloak/models/sessions/jpa/JpaUserSessionProvider.java
+++ b/model/sessions-jpa/src/main/java/org/keycloak/models/sessions/jpa/JpaUserSessionProvider.java
@@ -242,7 +242,7 @@ public class JpaUserSessionProvider implements UserSessionProvider {
@Override
public void onUserRemoved(RealmModel realm, UserModel user) {
removeUserSessions(realm, user);
- em.createNamedQuery("removeLoginFailuresByUser").setParameter("username", user.getUsername()).executeUpdate();
+ em.createNamedQuery("removeLoginFailuresByUser").setParameter("realmId", realm.getId()).setParameter("username", user.getUsername()).setParameter("email", user.getEmail()).executeUpdate();
}
@Override
diff --git a/model/sessions-mem/src/main/java/org/keycloak/models/sessions/mem/MemUserSessionProvider.java b/model/sessions-mem/src/main/java/org/keycloak/models/sessions/mem/MemUserSessionProvider.java
index 53cf7f3..a19af58 100755
--- a/model/sessions-mem/src/main/java/org/keycloak/models/sessions/mem/MemUserSessionProvider.java
+++ b/model/sessions-mem/src/main/java/org/keycloak/models/sessions/mem/MemUserSessionProvider.java
@@ -224,13 +224,13 @@ public class MemUserSessionProvider implements UserSessionProvider {
@Override
public UsernameLoginFailureModel getUserLoginFailure(RealmModel realm, String username) {
- UsernameLoginFailureEntity entity = loginFailures.get(new UsernameLoginFailureKey(username, realm.getId()));
+ UsernameLoginFailureEntity entity = loginFailures.get(new UsernameLoginFailureKey(realm.getId(), username));
return entity != null ? new UsernameLoginFailureAdapter(entity) : null;
}
@Override
public UsernameLoginFailureModel addUserLoginFailure(RealmModel realm, String username) {
- UsernameLoginFailureKey key = new UsernameLoginFailureKey(username, realm.getId());
+ UsernameLoginFailureKey key = new UsernameLoginFailureKey(realm.getId(), username);
UsernameLoginFailureEntity entity = new UsernameLoginFailureEntity(username, realm.getId());
if (loginFailures.putIfAbsent(key, entity) != null) {
throw new ModelDuplicateException();
@@ -265,6 +265,7 @@ public class MemUserSessionProvider implements UserSessionProvider {
removeUserSessions(realm, user);
loginFailures.remove(new UsernameLoginFailureKey(realm.getId(), user.getUsername()));
+ loginFailures.remove(new UsernameLoginFailureKey(realm.getId(), user.getEmail()));
}
@Override
diff --git a/model/sessions-mongo/src/main/java/org/keycloak/models/sessions/mongo/MongoUserSessionProvider.java b/model/sessions-mongo/src/main/java/org/keycloak/models/sessions/mongo/MongoUserSessionProvider.java
index e2e9478..6ddebb9 100755
--- a/model/sessions-mongo/src/main/java/org/keycloak/models/sessions/mongo/MongoUserSessionProvider.java
+++ b/model/sessions-mongo/src/main/java/org/keycloak/models/sessions/mongo/MongoUserSessionProvider.java
@@ -250,6 +250,12 @@ public class MongoUserSessionProvider implements UserSessionProvider {
@Override
public void onUserRemoved(RealmModel realm, UserModel user) {
removeUserSessions(realm, user);
+
+ DBObject query = new QueryBuilder()
+ .or(new BasicDBObject("username", user.getUsername()), new BasicDBObject("username", user.getEmail()))
+ .and("realmId").is(realm.getId())
+ .get();
+ mongoStore.removeEntities(MongoUsernameLoginFailureEntity.class, query, invocationContext);
}
@Override
diff --git a/testsuite/integration/src/test/java/org/keycloak/testsuite/model/UserSessionProviderTest.java b/testsuite/integration/src/test/java/org/keycloak/testsuite/model/UserSessionProviderTest.java
index 2d9983e..c68ae7d 100755
--- a/testsuite/integration/src/test/java/org/keycloak/testsuite/model/UserSessionProviderTest.java
+++ b/testsuite/integration/src/test/java/org/keycloak/testsuite/model/UserSessionProviderTest.java
@@ -12,6 +12,7 @@ import org.keycloak.models.UserModel;
import org.keycloak.models.UserSessionModel;
import org.keycloak.models.UsernameLoginFailureModel;
import org.keycloak.protocol.oidc.OpenIDConnect;
+import org.keycloak.services.managers.UserManager;
import org.keycloak.testsuite.rule.KeycloakRule;
import org.keycloak.util.Time;
@@ -38,8 +39,8 @@ public class UserSessionProviderTest {
public void before() {
session = kc.startSession();
realm = session.realms().getRealm("test");
- session.users().addUser(realm, "user1");
- session.users().addUser(realm, "user2");
+ session.users().addUser(realm, "user1").setEmail("user1@localhost");
+ session.users().addUser(realm, "user2").setEmail("user2@localhost");
}
@After
@@ -48,8 +49,10 @@ public class UserSessionProviderTest {
session.sessions().removeUserSessions(realm);
UserModel user1 = session.users().getUserByUsername("user1", realm);
UserModel user2 = session.users().getUserByUsername("user2", realm);
- session.users().removeUser(realm, user1);
- session.users().removeUser(realm, user2);
+
+ UserManager um = new UserManager(session);
+ um.removeUser(realm, user1);
+ um.removeUser(realm, user2);
kc.stopSession(session, true);
}
@@ -360,6 +363,28 @@ public class UserSessionProviderTest {
assertEquals(0, failure1.getNumFailures());
}
+ @Test
+ public void testOnUserRemoved() {
+ createSessions();
+
+ session.sessions().addUserLoginFailure(realm, "user1");
+ session.sessions().addUserLoginFailure(realm, "user1@localhost");
+ session.sessions().addUserLoginFailure(realm, "user2");
+
+ resetSession();
+
+ session.sessions().onUserRemoved(realm, session.users().getUserByUsername("user1", realm));
+
+ resetSession();
+
+ assertTrue(session.sessions().getUserSessions(realm, session.users().getUserByUsername("user1", realm)).isEmpty());
+ assertFalse(session.sessions().getUserSessions(realm, session.users().getUserByUsername("user2", realm)).isEmpty());
+
+ assertNull(session.sessions().getUserLoginFailure(realm, "user1"));
+ assertNull(session.sessions().getUserLoginFailure(realm, "user1@localhost"));
+ assertNotNull(session.sessions().getUserLoginFailure(realm, "user2"));
+ }
+
private ClientSessionModel createClientSession(ClientModel client, UserSessionModel userSession, String redirect, String state, Set<String> roles) {
ClientSessionModel clientSession = session.sessions().createClientSession(realm, client);
if (userSession != null) clientSession.setUserSession(userSession);