keycloak-aplcache

Details

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 92e416b..4c51b96 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-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 56d4755..6bd79f9 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 3fdfdba..bc2e8d0 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 f2c168c..3a57cc9 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);