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 92e416b3b5..4c51b96b41 100644 --- 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 @@ -243,7 +243,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-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 899d80e7ca..c62e4745c0 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-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 56d475586a..6bd79f9023 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 @@ -207,13 +207,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(); @@ -259,6 +259,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 3fdfdba3fc..bc2e8d093c 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 @@ -262,6 +262,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 f2c168cd3d..3a57cc9586 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 @@ -10,6 +10,8 @@ import org.keycloak.models.KeycloakSession; import org.keycloak.models.RealmModel; import org.keycloak.models.UserModel; import org.keycloak.models.UserSessionModel; +import org.keycloak.models.UsernameLoginFailureModel; +import org.keycloak.services.managers.UserManager; import org.keycloak.testsuite.rule.KeycloakRule; import org.keycloak.util.Time; @@ -36,8 +38,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 @@ -46,8 +48,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); } @@ -280,6 +284,56 @@ public class UserSessionProviderTest { assertEquals(1, session.sessions().getActiveUserSessions(realm, realm.findClient("third-party"))); } + @Test + public void loginFailures() { + UsernameLoginFailureModel failure1 = session.sessions().addUserLoginFailure(realm, "user1"); + failure1.incrementFailures(); + + UsernameLoginFailureModel failure2 = session.sessions().addUserLoginFailure(realm, "user2"); + failure2.incrementFailures(); + failure2.incrementFailures(); + + resetSession(); + + failure1 = session.sessions().getUserLoginFailure(realm, "user1"); + assertEquals(1, failure1.getNumFailures()); + + failure2 = session.sessions().getUserLoginFailure(realm, "user2"); + assertEquals(2, failure2.getNumFailures()); + + resetSession(); + + failure1 = session.sessions().getUserLoginFailure(realm, "user1"); + failure1.clearFailures(); + + resetSession(); + + failure1 = session.sessions().getUserLoginFailure(realm, "user1"); + 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 UserSessionModel[] createSessions() { UserSessionModel[] sessions = new UserSessionModel[3]; sessions[0] = session.sessions().createUserSession(realm, session.users().getUserByUsername("user1", realm), "user1", "127.0.0.1", "form", true);