commit
4276328794
4 changed files with 29 additions and 40 deletions
|
@ -53,12 +53,6 @@ public class BruteForceProtector implements Runnable {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
protected class SuccessfulLogin extends LoginEvent {
|
|
||||||
public SuccessfulLogin(String realmId, String userId, String ip) {
|
|
||||||
super(realmId, userId, ip);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
protected class ShutdownEvent extends LoginEvent {
|
protected class ShutdownEvent extends LoginEvent {
|
||||||
public ShutdownEvent() {
|
public ShutdownEvent() {
|
||||||
super(null, null, null);
|
super(null, null, null);
|
||||||
|
@ -83,7 +77,7 @@ public class BruteForceProtector implements Runnable {
|
||||||
logFailure(event);
|
logFailure(event);
|
||||||
UsernameLoginFailureModel user = getUserModel(session, event);
|
UsernameLoginFailureModel user = getUserModel(session, event);
|
||||||
if (user == null) {
|
if (user == null) {
|
||||||
user = session.sessions().addUserLoginFailure(realm, event.username);
|
user = session.sessions().addUserLoginFailure(realm, event.username.toLowerCase());
|
||||||
}
|
}
|
||||||
user.setLastIPFailure(event.ip);
|
user.setLastIPFailure(event.ip);
|
||||||
long currentTime = System.currentTimeMillis();
|
long currentTime = System.currentTimeMillis();
|
||||||
|
@ -122,7 +116,7 @@ public class BruteForceProtector implements Runnable {
|
||||||
protected UsernameLoginFailureModel getUserModel(KeycloakSession session, LoginEvent event) {
|
protected UsernameLoginFailureModel getUserModel(KeycloakSession session, LoginEvent event) {
|
||||||
RealmModel realm = getRealmModel(session, event);
|
RealmModel realm = getRealmModel(session, event);
|
||||||
if (realm == null) return null;
|
if (realm == null) return null;
|
||||||
UsernameLoginFailureModel user = session.sessions().getUserLoginFailure(realm, event.username);
|
UsernameLoginFailureModel user = session.sessions().getUserLoginFailure(realm, event.username.toLowerCase());
|
||||||
if (user == null) return null;
|
if (user == null) return null;
|
||||||
return user;
|
return user;
|
||||||
}
|
}
|
||||||
|
@ -147,7 +141,6 @@ public class BruteForceProtector implements Runnable {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
public void run() {
|
public void run() {
|
||||||
final ArrayList<LoginEvent> events = new ArrayList<LoginEvent>(TRANSACTION_SIZE + 1);
|
final ArrayList<LoginEvent> events = new ArrayList<LoginEvent>(TRANSACTION_SIZE + 1);
|
||||||
try {
|
try {
|
||||||
|
@ -196,10 +189,6 @@ public class BruteForceProtector implements Runnable {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
protected void logSuccess(LoginEvent event) {
|
|
||||||
logger.warn("login success for user " + event.username + " from ip " + event.ip);
|
|
||||||
}
|
|
||||||
|
|
||||||
protected void logFailure(LoginEvent event) {
|
protected void logFailure(LoginEvent event) {
|
||||||
logger.warn("login failure for user " + event.username + " from ip " + event.ip);
|
logger.warn("login failure for user " + event.username + " from ip " + event.ip);
|
||||||
failures++;
|
failures++;
|
||||||
|
@ -215,15 +204,6 @@ public class BruteForceProtector implements Runnable {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
public void successfulLogin(RealmModel realm, String username, ClientConnection clientConnection) {
|
|
||||||
logger.info("successful login user: " + username + " from ip " + clientConnection.getRemoteAddr());
|
|
||||||
}
|
|
||||||
|
|
||||||
public void invalidUser(RealmModel realm, String username, ClientConnection clientConnection) {
|
|
||||||
logger.warn("invalid user: " + username + " from ip " + clientConnection.getRemoteAddr());
|
|
||||||
// todo more?
|
|
||||||
}
|
|
||||||
|
|
||||||
public void failedLogin(RealmModel realm, String username, ClientConnection clientConnection) {
|
public void failedLogin(RealmModel realm, String username, ClientConnection clientConnection) {
|
||||||
try {
|
try {
|
||||||
FailedLogin event = new FailedLogin(realm.getId(), username, clientConnection.getRemoteAddr());
|
FailedLogin event = new FailedLogin(realm.getId(), username, clientConnection.getRemoteAddr());
|
||||||
|
@ -238,7 +218,7 @@ public class BruteForceProtector implements Runnable {
|
||||||
}
|
}
|
||||||
|
|
||||||
public boolean isTemporarilyDisabled(KeycloakSession session, RealmModel realm, String username) {
|
public boolean isTemporarilyDisabled(KeycloakSession session, RealmModel realm, String username) {
|
||||||
UsernameLoginFailureModel failure = session.sessions().getUserLoginFailure(realm, username);
|
UsernameLoginFailureModel failure = session.sessions().getUserLoginFailure(realm, username.toLowerCase());
|
||||||
if (failure == null) {
|
if (failure == null) {
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
@ -251,13 +231,4 @@ public class BruteForceProtector implements Runnable {
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
public long getFailures() {
|
|
||||||
return failures;
|
|
||||||
}
|
|
||||||
|
|
||||||
public long getLastFailure() {
|
|
||||||
return lastFailure;
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
|
@ -75,7 +75,7 @@ public class AttackDetectionResource {
|
||||||
data.put("lastIPFailure", "n/a");
|
data.put("lastIPFailure", "n/a");
|
||||||
if (!realm.isBruteForceProtected()) return data;
|
if (!realm.isBruteForceProtected()) return data;
|
||||||
|
|
||||||
UsernameLoginFailureModel model = session.sessions().getUserLoginFailure(realm, username);
|
UsernameLoginFailureModel model = session.sessions().getUserLoginFailure(realm, username.toLowerCase());
|
||||||
if (model == null) return data;
|
if (model == null) return data;
|
||||||
if (protector.isTemporarilyDisabled(session, realm, username)) {
|
if (protector.isTemporarilyDisabled(session, realm, username)) {
|
||||||
data.put("disabled", true);
|
data.put("disabled", true);
|
||||||
|
@ -97,7 +97,7 @@ public class AttackDetectionResource {
|
||||||
@DELETE
|
@DELETE
|
||||||
public void clearBruteForceForUser(@PathParam("username") String username) {
|
public void clearBruteForceForUser(@PathParam("username") String username) {
|
||||||
auth.requireManage();
|
auth.requireManage();
|
||||||
UsernameLoginFailureModel model = session.sessions().getUserLoginFailure(realm, username);
|
UsernameLoginFailureModel model = session.sessions().getUserLoginFailure(realm, username.toLowerCase());
|
||||||
if (model != null) {
|
if (model != null) {
|
||||||
session.sessions().removeUserLoginFailure(realm, username);
|
session.sessions().removeUserLoginFailure(realm, username);
|
||||||
adminEvent.operation(OperationType.DELETE).success();
|
adminEvent.operation(OperationType.DELETE).success();
|
||||||
|
|
|
@ -144,7 +144,7 @@ public class UsersResource {
|
||||||
}
|
}
|
||||||
|
|
||||||
if (rep.isEnabled() != null && rep.isEnabled()) {
|
if (rep.isEnabled() != null && rep.isEnabled()) {
|
||||||
UsernameLoginFailureModel failureModel = session.sessions().getUserLoginFailure(realm, rep.getUsername());
|
UsernameLoginFailureModel failureModel = session.sessions().getUserLoginFailure(realm, rep.getUsername().toLowerCase());
|
||||||
if (failureModel != null) {
|
if (failureModel != null) {
|
||||||
failureModel.clearFailures();
|
failureModel.clearFailures();
|
||||||
}
|
}
|
||||||
|
|
|
@ -302,6 +302,15 @@ public class BruteForceTest {
|
||||||
loginSuccess();
|
loginSuccess();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void testBrowserInvalidPasswordDifferentCase() throws Exception {
|
||||||
|
loginSuccess("test-user@localhost");
|
||||||
|
loginInvalidPassword("test-User@localhost");
|
||||||
|
loginInvalidPassword("Test-user@localhost");
|
||||||
|
expectTemporarilyDisabled();
|
||||||
|
clearAllUserFailures();
|
||||||
|
}
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
public void testBrowserMissingPassword() throws Exception {
|
public void testBrowserMissingPassword() throws Exception {
|
||||||
loginSuccess();
|
loginSuccess();
|
||||||
|
@ -333,8 +342,12 @@ public class BruteForceTest {
|
||||||
}
|
}
|
||||||
|
|
||||||
public void expectTemporarilyDisabled() throws Exception {
|
public void expectTemporarilyDisabled() throws Exception {
|
||||||
|
expectTemporarilyDisabled("test-user@localhost");
|
||||||
|
}
|
||||||
|
|
||||||
|
public void expectTemporarilyDisabled(String username) throws Exception {
|
||||||
loginPage.open();
|
loginPage.open();
|
||||||
loginPage.login("test-user@localhost", "password");
|
loginPage.login(username, "password");
|
||||||
|
|
||||||
loginPage.assertCurrent();
|
loginPage.assertCurrent();
|
||||||
String src = driver.getPageSource();
|
String src = driver.getPageSource();
|
||||||
|
@ -345,9 +358,11 @@ public class BruteForceTest {
|
||||||
.assertEvent();
|
.assertEvent();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
public void loginSuccess() throws Exception {
|
public void loginSuccess() throws Exception {
|
||||||
|
loginSuccess("test-user@localhost");
|
||||||
|
}
|
||||||
|
|
||||||
|
public void loginSuccess(String username) throws Exception {
|
||||||
loginPage.open();
|
loginPage.open();
|
||||||
loginPage.login("test-user@localhost", "password");
|
loginPage.login("test-user@localhost", "password");
|
||||||
|
|
||||||
|
@ -391,10 +406,13 @@ public class BruteForceTest {
|
||||||
events.clear();
|
events.clear();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
public void loginInvalidPassword() throws Exception {
|
public void loginInvalidPassword() throws Exception {
|
||||||
|
loginInvalidPassword("test-user@localhost");
|
||||||
|
}
|
||||||
|
|
||||||
|
public void loginInvalidPassword(String username) throws Exception {
|
||||||
loginPage.open();
|
loginPage.open();
|
||||||
loginPage.login("test-user@localhost", "invalid");
|
loginPage.login(username, "invalid");
|
||||||
|
|
||||||
loginPage.assertCurrent();
|
loginPage.assertCurrent();
|
||||||
|
|
||||||
|
|
Loading…
Reference in a new issue