diff --git a/examples/demo-template/testrealm.json b/examples/demo-template/testrealm.json index 2e35b1218d..81af756756 100755 --- a/examples/demo-template/testrealm.json +++ b/examples/demo-template/testrealm.json @@ -7,7 +7,6 @@ "sslNotRequired": true, "registrationAllowed": false, "social": false, - "bruteForceProtected": true, "updateProfileOnInitialSocialLogin": false, "privateKey": "MIICXAIBAAKBgQCrVrCuTtArbgaZzL1hvh0xtL5mc7o0NqPVnYXkLvgcwiC3BjLGw1tGEGoJaXDuSaRllobm53JBhjx33UNv+5z/UMG4kytBWxheNVKnL6GgqlNabMaFfPLPCF8kAgKnsi79NMo+n6KnSY8YeUmec/p2vjO2NjsSAVcWEQMVhJ31LwIDAQABAoGAfmO8gVhyBxdqlxmIuglbz8bcjQbhXJLR2EoS8ngTXmN1bo2L90M0mUKSdc7qF10LgETBzqL8jYlQIbt+e6TH8fcEpKCjUlyq0Mf/vVbfZSNaVycY13nTzo27iPyWQHK5NLuJzn1xvxxrUeXI6A2WFpGEBLbHjwpx5WQG9A+2scECQQDvdn9NE75HPTVPxBqsEd2z10TKkl9CZxu10Qby3iQQmWLEJ9LNmy3acvKrE3gMiYNWb6xHPKiIqOR1as7L24aTAkEAtyvQOlCvr5kAjVqrEKXalj0Tzewjweuxc0pskvArTI2Oo070h65GpoIKLc9jf+UA69cRtquwP93aZKtW06U8dQJAF2Y44ks/mK5+eyDqik3koCI08qaC8HYq2wVl7G2QkJ6sbAaILtcvD92ToOvyGyeE0flvmDZxMYlvaZnaQ0lcSQJBAKZU6umJi3/xeEbkJqMfeLclD27XGEFoPeNrmdx0q10Azp4NfJAY+Z8KRyQCR2BEG+oNitBOZ+YXF9KCpH3cdmECQHEigJhYg+ykOvr1aiZUMFT72HU0jnmQe2FVekuG+LJUt2Tm7GtMjTFoGpf0JwrVuZN39fOYAlo+nTixgeW7X8Y=", "publicKey": "MIGfMA0GCSqGSIb3DQEBAQUAA4GNADCBiQKBgQCrVrCuTtArbgaZzL1hvh0xtL5mc7o0NqPVnYXkLvgcwiC3BjLGw1tGEGoJaXDuSaRllobm53JBhjx33UNv+5z/UMG4kytBWxheNVKnL6GgqlNabMaFfPLPCF8kAgKnsi79NMo+n6KnSY8YeUmec/p2vjO2NjsSAVcWEQMVhJ31LwIDAQAB", diff --git a/services/src/main/java/org/keycloak/services/managers/BruteForceProtector.java b/services/src/main/java/org/keycloak/services/managers/BruteForceProtector.java index 5645c57c04..a941f89ad7 100755 --- a/services/src/main/java/org/keycloak/services/managers/BruteForceProtector.java +++ b/services/src/main/java/org/keycloak/services/managers/BruteForceProtector.java @@ -1,7 +1,7 @@ package org.keycloak.services.managers; -import org.jboss.resteasy.logging.Logger; +import org.jboss.logging.Logger; import org.keycloak.models.KeycloakSession; import org.keycloak.models.KeycloakSessionFactory; import org.keycloak.models.RealmModel; @@ -78,10 +78,13 @@ public class BruteForceProtector implements Runnable { } public void failure(KeycloakSession session, LoginEvent event) { + logger.debug("failure"); RealmModel realm = getRealmModel(session, event); logFailure(event); UsernameLoginFailureModel user = getUserModel(session, event); - if (user == null) return; + if (user == null) { + user = realm.addUserLoginFailure(event.username); + } user.setLastIPFailure(event.ip); long currentTime = System.currentTimeMillis(); long last = user.getLastFailure(); @@ -97,16 +100,22 @@ public class BruteForceProtector implements Runnable { } } user.incrementFailures(); + logger.debugv("new num failures: {0}" , user.getNumFailures()); int waitSeconds = realm.getWaitIncrementSeconds() * (user.getNumFailures() / realm.getFailureFactor()); + logger.debugv("waitSeconds: {0}", waitSeconds); + logger.debugv("deltaTime: {0}", deltaTime); if (waitSeconds == 0) { - if (deltaTime > realm.getQuickLoginCheckMilliSeconds()) { + if (last > 0 && deltaTime < realm.getQuickLoginCheckMilliSeconds()) { + logger.debugv("quick login, set min wait seconds"); waitSeconds = realm.getMinimumQuickLoginWaitSeconds(); } } - waitSeconds = Math.min(realm.getMaxFailureWaitSeconds(), waitSeconds); if (waitSeconds > 0) { - user.setFailedLoginNotBefore((int) (currentTime / 1000) + waitSeconds); + waitSeconds = Math.min(realm.getMaxFailureWaitSeconds(), waitSeconds); + int notBefore = (int) (currentTime / 1000) + waitSeconds; + logger.debugv("set notBefore: {0}", notBefore); + user.setFailedLoginNotBefore(notBefore); } } @@ -152,6 +161,7 @@ public class BruteForceProtector implements Runnable { queue.drainTo(events, TRANSACTION_SIZE); Collections.sort(events); // we sort to avoid deadlock due to ordered updates. Maybe I'm overthinking this. KeycloakSession session = factory.createSession(); + session.getTransaction().begin(); try { for (LoginEvent event : events) { if (event instanceof FailedLogin) { @@ -231,6 +241,7 @@ public class BruteForceProtector implements Runnable { int currTime = (int)(System.currentTimeMillis()/1000); if (currTime < failure.getFailedLoginNotBefore()) { + logger.debugv("Current: {0} notBefore: {1}", currTime , failure.getFailedLoginNotBefore()); return true; } return false; diff --git a/testsuite/integration/src/test/resources/testcomposite.json b/testsuite/integration/src/test/resources/testcomposite.json index dd4e378a43..61038ea65b 100755 --- a/testsuite/integration/src/test/resources/testcomposite.json +++ b/testsuite/integration/src/test/resources/testcomposite.json @@ -6,7 +6,6 @@ "accessCodeLifespan": 600, "accessCodeLifespanUserAction": 600, "sslNotRequired": true, - "bruteForceProtected": true, "registrationAllowed": true, "resetPasswordAllowed": true, "requiredCredentials": [ "password" ], diff --git a/testsuite/integration/src/test/resources/testrealm.json b/testsuite/integration/src/test/resources/testrealm.json index d3ea49f5a4..a7cf0d49e1 100755 --- a/testsuite/integration/src/test/resources/testrealm.json +++ b/testsuite/integration/src/test/resources/testrealm.json @@ -5,7 +5,6 @@ "sslNotRequired": true, "registrationAllowed": true, "resetPasswordAllowed": true, - "bruteForceProtected": true, "privateKey": "MIICXAIBAAKBgQCrVrCuTtArbgaZzL1hvh0xtL5mc7o0NqPVnYXkLvgcwiC3BjLGw1tGEGoJaXDuSaRllobm53JBhjx33UNv+5z/UMG4kytBWxheNVKnL6GgqlNabMaFfPLPCF8kAgKnsi79NMo+n6KnSY8YeUmec/p2vjO2NjsSAVcWEQMVhJ31LwIDAQABAoGAfmO8gVhyBxdqlxmIuglbz8bcjQbhXJLR2EoS8ngTXmN1bo2L90M0mUKSdc7qF10LgETBzqL8jYlQIbt+e6TH8fcEpKCjUlyq0Mf/vVbfZSNaVycY13nTzo27iPyWQHK5NLuJzn1xvxxrUeXI6A2WFpGEBLbHjwpx5WQG9A+2scECQQDvdn9NE75HPTVPxBqsEd2z10TKkl9CZxu10Qby3iQQmWLEJ9LNmy3acvKrE3gMiYNWb6xHPKiIqOR1as7L24aTAkEAtyvQOlCvr5kAjVqrEKXalj0Tzewjweuxc0pskvArTI2Oo070h65GpoIKLc9jf+UA69cRtquwP93aZKtW06U8dQJAF2Y44ks/mK5+eyDqik3koCI08qaC8HYq2wVl7G2QkJ6sbAaILtcvD92ToOvyGyeE0flvmDZxMYlvaZnaQ0lcSQJBAKZU6umJi3/xeEbkJqMfeLclD27XGEFoPeNrmdx0q10Azp4NfJAY+Z8KRyQCR2BEG+oNitBOZ+YXF9KCpH3cdmECQHEigJhYg+ykOvr1aiZUMFT72HU0jnmQe2FVekuG+LJUt2Tm7GtMjTFoGpf0JwrVuZN39fOYAlo+nTixgeW7X8Y=", "publicKey": "MIGfMA0GCSqGSIb3DQEBAQUAA4GNADCBiQKBgQCrVrCuTtArbgaZzL1hvh0xtL5mc7o0NqPVnYXkLvgcwiC3BjLGw1tGEGoJaXDuSaRllobm53JBhjx33UNv+5z/UMG4kytBWxheNVKnL6GgqlNabMaFfPLPCF8kAgKnsi79NMo+n6KnSY8YeUmec/p2vjO2NjsSAVcWEQMVhJ31LwIDAQAB", "requiredCredentials": [ "password" ],