/* * JBoss, Home of Professional Open Source * * Copyright 2013 Red Hat, Inc. and/or its affiliates. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package org.keycloak.testsuite.console.realm; import org.jboss.arquillian.graphene.page.Page; import org.junit.Before; import org.junit.Test; import org.keycloak.testsuite.auth.page.account.Account; import org.keycloak.testsuite.console.page.realm.BruteForceDetection; import org.keycloak.testsuite.console.page.users.UserAttributes; import org.keycloak.testsuite.console.page.users.Users; import org.openqa.selenium.WebElement; import org.openqa.selenium.support.FindBy; import static org.junit.Assert.*; import static org.keycloak.representations.idm.CredentialRepresentation.PASSWORD; import static org.keycloak.testsuite.admin.Users.setPasswordFor; import static org.keycloak.testsuite.auth.page.AuthRealm.TEST; import static org.keycloak.testsuite.util.URLAssert.assertCurrentUrlStartsWith; import static org.keycloak.testsuite.util.WaitUtils.*; /** * @author Filip Kiss * @author mhajas * @author Vaclav Muzikar <vmuzikar@redhat.com> */ public class SecurityDefensesTest extends AbstractRealmTest { public static final String INVALID_PWD_MSG = "Invalid username or password."; public static final String ACC_DISABLED_MSG = "Invalid username or password."; public static final short ATTEMPTS_BAD_PWD = 2; public static final short ATTEMPTS_GOOD_PWD = 1; @Page private BruteForceDetection bruteForceDetectionPage; @Page private Account testRealmAccountPage; @Page private Users usersPage; @Page private UserAttributes userAttributesPage; @FindBy(className = "kc-feedback-text") private WebElement feedbackTextElement; @Override public void setDefaultPageUriParameters() { super.setDefaultPageUriParameters(); testRealmAccountPage.setAuthRealm(TEST); } @Before public void beforeSecurityDefensesTest() { bruteForceDetectionPage.navigateTo(); } @Test public void maxLoginFailuresTest() throws InterruptedException { final short secondsToWait = 10; // For slower browsers/webdrivers (like IE) we need higher value final short maxLoginFailures = 2; bruteForceDetectionPage.form().setProtectionEnabled(true); bruteForceDetectionPage.form().setMaxLoginFailures(String.valueOf(maxLoginFailures)); bruteForceDetectionPage.form().setWaitIncrementSelect(BruteForceDetection.TimeSelectValues.SECONDS); bruteForceDetectionPage.form().setWaitIncrementInput(String.valueOf(secondsToWait)); bruteForceDetectionPage.form().setQuickLoginCheckInput("1"); bruteForceDetectionPage.form().save(); assertAlertSuccess(); tryToLogin(secondsToWait * (ATTEMPTS_BAD_PWD + ATTEMPTS_GOOD_PWD) / maxLoginFailures); } @Test public void quickLoginCheck() throws InterruptedException { final short secondsToWait = 10; bruteForceDetectionPage.form().setProtectionEnabled(true); bruteForceDetectionPage.form().setMaxLoginFailures("100"); bruteForceDetectionPage.form().setQuickLoginCheckInput("10000"); bruteForceDetectionPage.form().setMinQuickLoginWaitSelect(BruteForceDetection.TimeSelectValues.SECONDS); bruteForceDetectionPage.form().setMinQuickLoginWaitInput(String.valueOf(secondsToWait)); bruteForceDetectionPage.form().save(); assertAlertSuccess(); tryToLogin(secondsToWait); } @Test public void maxWaitLoginFailures() throws InterruptedException { final short secondsToWait = 15; bruteForceDetectionPage.form().setProtectionEnabled(true); bruteForceDetectionPage.form().setMaxLoginFailures("1"); bruteForceDetectionPage.form().setWaitIncrementSelect(BruteForceDetection.TimeSelectValues.SECONDS); bruteForceDetectionPage.form().setWaitIncrementInput("10"); bruteForceDetectionPage.form().setMaxWaitSelect(BruteForceDetection.TimeSelectValues.SECONDS); bruteForceDetectionPage.form().setMaxWaitInput(String.valueOf(secondsToWait)); bruteForceDetectionPage.form().save(); tryToLogin(secondsToWait); } @Test public void failureResetTime() throws InterruptedException { final short failureResetTime = 3; final short waitIncrement = 5; bruteForceDetectionPage.form().setProtectionEnabled(true); bruteForceDetectionPage.form().setMaxLoginFailures("1"); bruteForceDetectionPage.form().setWaitIncrementSelect(BruteForceDetection.TimeSelectValues.SECONDS); bruteForceDetectionPage.form().setWaitIncrementInput(String.valueOf(waitIncrement)); bruteForceDetectionPage.form().setFailureResetTimeSelect(BruteForceDetection.TimeSelectValues.SECONDS); bruteForceDetectionPage.form().setFailureResetTimeInput(String.valueOf(failureResetTime)); bruteForceDetectionPage.form().save(); assertAlertSuccess(); tryToLogin(failureResetTime, false); testRealmLoginPage.form().login(testUser); assertFeedbackText(ACC_DISABLED_MSG); Thread.sleep(waitIncrement * 1000); testRealmLoginPage.form().login(testUser); assertCurrentUrlStartsWith(testRealmAccountPage); } @Test public void userUnlockTest() { bruteForceDetectionPage.form().setProtectionEnabled(true); bruteForceDetectionPage.form().setMaxLoginFailures("1"); bruteForceDetectionPage.form().setWaitIncrementSelect(BruteForceDetection.TimeSelectValues.MINUTES); bruteForceDetectionPage.form().setWaitIncrementInput("10"); bruteForceDetectionPage.form().save(); assertAlertSuccess(); testRealmAccountPage.navigateTo(); setPasswordFor(testUser, PASSWORD + "-mismatch"); testRealmLoginPage.form().login(testUser); usersPage.navigateTo(); usersPage.table().searchUsers(testUser.getUsername()); usersPage.table().editUser(testUser.getUsername()); assertFalse(userAttributesPage.form().isEnabled()); userAttributesPage.form().unlockUser(); testRealmAccountPage.navigateTo(); setPasswordFor(testUser, PASSWORD); testRealmLoginPage.form().login(testUser); assertCurrentUrlStartsWith(testRealmAccountPage); } private void assertFeedbackText(String text) { waitUntilElement(feedbackTextElement).is().present(); assertEquals(text, feedbackTextElement.getText()); } private void tryToLogin(int wait) throws InterruptedException { tryToLogin(wait, true); } private void tryToLogin(int wait, boolean finalLogin) throws InterruptedException { testRealmAccountPage.navigateTo(); setPasswordFor(testUser, PASSWORD + "-mismatch"); for (int i = 0; i < ATTEMPTS_BAD_PWD; i++) { testRealmLoginPage.form().login(testUser); assertFeedbackText(INVALID_PWD_MSG); } setPasswordFor(testUser, PASSWORD); for (int i = 0; i < ATTEMPTS_GOOD_PWD; i++) { testRealmLoginPage.form().login(testUser); assertFeedbackText(ACC_DISABLED_MSG); } wait *= 1000; pause(wait); if (finalLogin) { testRealmLoginPage.form().login(testUser); assertCurrentUrlStartsWith(testRealmAccountPage); } } }