KEYCLOAK-12986 BruteForceProtector does not log failures when login failure in PostBroker flow

This commit is contained in:
mposolda 2020-02-19 15:46:28 +01:00 committed by Stian Thorgersen
parent e6e0e6945d
commit 9474dd6208
3 changed files with 79 additions and 39 deletions

View file

@ -639,15 +639,9 @@ public class AuthenticationProcessor {
public void logFailure() { public void logFailure() {
if (realm.isBruteForceProtected()) { if (realm.isBruteForceProtected()) {
String username = authenticationSession.getAuthNote(AbstractUsernameFormAuthenticator.ATTEMPTED_USERNAME); UserModel user = AuthenticationManager.lookupUserForBruteForceLog(session, realm, authenticationSession);
// todo need to handle non form failures if (user != null) {
if (username == null) { getBruteForceProtector().failedLogin(realm, user, connection);
} else {
UserModel user = KeycloakModelUtils.findUserByNameOrEmail(session, realm, username);
if (user != null) {
getBruteForceProtector().failedLogin(realm, user, connection);
}
} }
} }
} }

View file

@ -1316,16 +1316,8 @@ public class AuthenticationManager {
protected static void logSuccess(KeycloakSession session, AuthenticationSessionModel authSession) { protected static void logSuccess(KeycloakSession session, AuthenticationSessionModel authSession) {
RealmModel realm = session.getContext().getRealm(); RealmModel realm = session.getContext().getRealm();
if (realm.isBruteForceProtected()) { if (realm.isBruteForceProtected()) {
String username = authSession.getAuthNote(AbstractUsernameFormAuthenticator.ATTEMPTED_USERNAME); UserModel user = lookupUserForBruteForceLog(session, realm, authSession);
// TODO: as above, need to handle non form success
if(username == null) {
return;
}
UserModel user = KeycloakModelUtils.findUserByNameOrEmail(session, realm, username);
if (user != null) { if (user != null) {
BruteForceProtector bruteForceProtector = session.getProvider(BruteForceProtector.class); BruteForceProtector bruteForceProtector = session.getProvider(BruteForceProtector.class);
bruteForceProtector.successfulLogin(realm, user, session.getContext().getConnection()); bruteForceProtector.successfulLogin(realm, user, session.getContext().getConnection());
@ -1333,4 +1325,16 @@ public class AuthenticationManager {
} }
} }
public static UserModel lookupUserForBruteForceLog(KeycloakSession session, RealmModel realm, AuthenticationSessionModel authenticationSession) {
UserModel user = authenticationSession.getAuthenticatedUser();
if (user != null) return user;
String username = authenticationSession.getAuthNote(AbstractUsernameFormAuthenticator.ATTEMPTED_USERNAME);
if (username != null) {
return KeycloakModelUtils.findUserByNameOrEmail(session, realm, username);
}
return null;
}
} }

View file

@ -28,6 +28,7 @@ import org.keycloak.representations.idm.UserRepresentation;
import org.keycloak.services.Urls; import org.keycloak.services.Urls;
import org.keycloak.storage.UserStorageProvider; import org.keycloak.storage.UserStorageProvider;
import org.keycloak.testsuite.Assert; import org.keycloak.testsuite.Assert;
import org.keycloak.testsuite.admin.ApiUtil;
import org.keycloak.testsuite.federation.DummyUserFederationProviderFactory; import org.keycloak.testsuite.federation.DummyUserFederationProviderFactory;
import org.keycloak.testsuite.util.ClientBuilder; import org.keycloak.testsuite.util.ClientBuilder;
import org.keycloak.testsuite.util.OAuthClient; import org.keycloak.testsuite.util.OAuthClient;
@ -386,13 +387,7 @@ public abstract class AbstractAdvancedBrokerTest extends AbstractBrokerTest {
driver.navigate().to(getAccountUrl(bc.consumerRealmName())); driver.navigate().to(getAccountUrl(bc.consumerRealmName()));
log.debug("Clicking social " + bc.getIDPAlias()); logInWithBroker(bc);
loginPage.clickSocial(bc.getIDPAlias());
waitForPage(driver, "log in to", true);
Assert.assertTrue("Driver should be on the provider realm page right now",
driver.getCurrentUrl().contains("/auth/realms/" + bc.providerRealmName() + "/"));
log.debug("Logging in");
loginPage.login(bc.getUserLogin(), bc.getUserPassword());
totpPage.assertCurrent(); totpPage.assertCurrent();
String totpSecret = totpPage.getTotpSecret(); String totpSecret = totpPage.getTotpSecret();
@ -401,30 +396,77 @@ public abstract class AbstractAdvancedBrokerTest extends AbstractBrokerTest {
assertNumFederatedIdentities(realm.users().search(bc.getUserLogin()).get(0).getId(), 1); assertNumFederatedIdentities(realm.users().search(bc.getUserLogin()).get(0).getId(), 1);
logoutFromRealm(bc.consumerRealmName()); logoutFromRealm(bc.consumerRealmName());
log.debug("Clicking social " + bc.getIDPAlias()); logInWithBroker(bc);
loginPage.clickSocial(bc.getIDPAlias());
waitForPage(driver, "log in to", true);
Assert.assertTrue("Driver should be on the provider realm page right now",
driver.getCurrentUrl().contains("/auth/realms/" + bc.providerRealmName() + "/"));
log.debug("Logging in");
loginPage.login(bc.getUserLogin(), bc.getUserPassword());
loginTotpPage.assertCurrent(); loginTotpPage.assertCurrent();
loginTotpPage.login(totp.generateTOTP(totpSecret)); loginTotpPage.login(totp.generateTOTP(totpSecret));
logoutFromRealm(bc.consumerRealmName()); logoutFromRealm(bc.consumerRealmName());
testingClient.server(bc.consumerRealmName()).run(disablePostBrokerLoginFlow(bc.getIDPAlias())); testingClient.server(bc.consumerRealmName()).run(disablePostBrokerLoginFlow(bc.getIDPAlias()));
log.debug("Clicking social " + bc.getIDPAlias()); logInWithBroker(bc);
loginPage.clickSocial(bc.getIDPAlias());
waitForPage(driver, "log in to", true);
Assert.assertTrue("Driver should be on the provider realm page right now",
driver.getCurrentUrl().contains("/auth/realms/" + bc.providerRealmName() + "/"));
log.debug("Logging in");
loginPage.login(bc.getUserLogin(), bc.getUserPassword());
waitForAccountManagementTitle(); waitForAccountManagementTitle();
accountUpdateProfilePage.assertCurrent(); accountUpdateProfilePage.assertCurrent();
} }
// KEYCLOAK-12986
@Test
public void testPostBrokerLoginFlowWithOTP_bruteForceEnabled() {
updateExecutions(AbstractBrokerTest::disableUpdateProfileOnFirstLogin);
testingClient.server(bc.consumerRealmName()).run(configurePostBrokerLoginWithOTP(bc.getIDPAlias()));
// Enable brute force protector in cosumer realm
RealmResource realm = adminClient.realm(bc.consumerRealmName());
RealmRepresentation consumerRealmRep = realm.toRepresentation();
consumerRealmRep.setBruteForceProtected(true);
consumerRealmRep.setFailureFactor(2);
consumerRealmRep.setMaxDeltaTimeSeconds(20);
consumerRealmRep.setMaxFailureWaitSeconds(100);
consumerRealmRep.setWaitIncrementSeconds(5);
realm.update(consumerRealmRep);
try {
driver.navigate().to(getAccountUrl(bc.consumerRealmName()));
logInWithBroker(bc);
totpPage.assertCurrent();
String totpSecret = totpPage.getTotpSecret();
totpPage.configure(totp.generateTOTP(totpSecret));
assertNumFederatedIdentities(realm.users().search(bc.getUserLogin()).get(0).getId(), 1);
logoutFromRealm(bc.consumerRealmName());
logInWithBroker(bc);
loginTotpPage.assertCurrent();
// Login for 2 times with incorrect TOTP. This should temporarily disable the user
loginTotpPage.login("bad-totp");
Assert.assertEquals("Invalid authenticator code.", loginTotpPage.getError());
loginTotpPage.login("bad-totp");
Assert.assertEquals("Invalid authenticator code.", loginTotpPage.getError());
// Login with valid TOTP. I should not be able to login
loginTotpPage.login(totp.generateTOTP(totpSecret));
Assert.assertEquals("Invalid authenticator code.", loginTotpPage.getError());
// Clear login failures
String userId = ApiUtil.findUserByUsername(realm, bc.getUserLogin()).getId();
realm.attackDetection().clearBruteForceForUser(userId);
loginTotpPage.login(totp.generateTOTP(totpSecret));
waitForAccountManagementTitle();
logoutFromRealm(bc.consumerRealmName());
} finally {
testingClient.server(bc.consumerRealmName()).run(disablePostBrokerLoginFlow(bc.getIDPAlias()));
// Disable brute force protector
consumerRealmRep = realm.toRepresentation();
consumerRealmRep.setBruteForceProtected(false);
realm.update(consumerRealmRep);
}
}
/** /**
* Refers to in old testsuite: org.keycloak.testsuite.broker.OIDCKeyCloakServerBrokerBasicTest#testLogoutWorksWithTokenTimeout() * Refers to in old testsuite: org.keycloak.testsuite.broker.OIDCKeyCloakServerBrokerBasicTest#testLogoutWorksWithTokenTimeout()
*/ */