brute force fixes, code cleanup, tests
This commit is contained in:
parent
576db8e0e1
commit
d9b0415047
7 changed files with 65 additions and 407 deletions
|
@ -596,9 +596,8 @@ public class AuthenticationProcessor {
|
||||||
}
|
}
|
||||||
|
|
||||||
public void validateUser(UserModel authenticatedUser) {
|
public void validateUser(UserModel authenticatedUser) {
|
||||||
if (authenticatedUser != null) {
|
if (authenticatedUser == null) return;
|
||||||
if (!authenticatedUser.isEnabled()) throw new AuthException(Error.USER_DISABLED);
|
if (!authenticatedUser.isEnabled()) throw new AuthException(Error.USER_DISABLED);
|
||||||
}
|
|
||||||
if (realm.isBruteForceProtected()) {
|
if (realm.isBruteForceProtected()) {
|
||||||
if (protector.isTemporarilyDisabled(session, realm, authenticatedUser.getUsername())) {
|
if (protector.isTemporarilyDisabled(session, realm, authenticatedUser.getUsername())) {
|
||||||
throw new AuthException(Error.USER_TEMPORARILY_DISABLED);
|
throw new AuthException(Error.USER_TEMPORARILY_DISABLED);
|
||||||
|
|
|
@ -592,121 +592,6 @@ public class AuthenticationManager {
|
||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
|
|
||||||
public AuthenticationStatus authenticateForm(KeycloakSession session, ClientConnection clientConnection, RealmModel realm, MultivaluedMap<String, String> formData) {
|
|
||||||
String username = formData.getFirst(FORM_USERNAME);
|
|
||||||
if (username == null) {
|
|
||||||
logger.debug("Username not provided");
|
|
||||||
return AuthenticationStatus.INVALID_USER;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (realm.isBruteForceProtected()) {
|
|
||||||
if (protector.isTemporarilyDisabled(session, realm, username)) {
|
|
||||||
return AuthenticationStatus.ACCOUNT_TEMPORARILY_DISABLED;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
AuthenticationStatus status = authenticateInternal(session, realm, formData, username);
|
|
||||||
if (realm.isBruteForceProtected()) {
|
|
||||||
switch (status) {
|
|
||||||
case SUCCESS:
|
|
||||||
protector.successfulLogin(realm, username, clientConnection);
|
|
||||||
break;
|
|
||||||
case FAILED:
|
|
||||||
case MISSING_TOTP:
|
|
||||||
case MISSING_PASSWORD:
|
|
||||||
case INVALID_CREDENTIALS:
|
|
||||||
protector.failedLogin(realm, username, clientConnection);
|
|
||||||
break;
|
|
||||||
case INVALID_USER:
|
|
||||||
protector.invalidUser(realm, username, clientConnection);
|
|
||||||
break;
|
|
||||||
default:
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
return status;
|
|
||||||
}
|
|
||||||
|
|
||||||
protected AuthenticationStatus authenticateInternal(KeycloakSession session, RealmModel realm, MultivaluedMap<String, String> formData, String username) {
|
|
||||||
UserModel user = KeycloakModelUtils.findUserByNameOrEmail(session, realm, username);
|
|
||||||
|
|
||||||
if (user == null) {
|
|
||||||
logger.debugv("User {0} not found", username);
|
|
||||||
return AuthenticationStatus.INVALID_USER;
|
|
||||||
}
|
|
||||||
|
|
||||||
Set<String> types = new HashSet<String>();
|
|
||||||
|
|
||||||
for (RequiredCredentialModel credential : realm.getRequiredCredentials()) {
|
|
||||||
types.add(credential.getType());
|
|
||||||
}
|
|
||||||
|
|
||||||
if (types.contains(CredentialRepresentation.PASSWORD)) {
|
|
||||||
List<UserCredentialModel> credentials = new LinkedList<UserCredentialModel>();
|
|
||||||
|
|
||||||
String password = formData.getFirst(CredentialRepresentation.PASSWORD);
|
|
||||||
if (password != null) {
|
|
||||||
credentials.add(UserCredentialModel.password(password));
|
|
||||||
}
|
|
||||||
|
|
||||||
String passwordToken = formData.getFirst(CredentialRepresentation.PASSWORD_TOKEN);
|
|
||||||
if (passwordToken != null) {
|
|
||||||
credentials.add(UserCredentialModel.passwordToken(passwordToken));
|
|
||||||
}
|
|
||||||
|
|
||||||
String totp = formData.getFirst(CredentialRepresentation.TOTP);
|
|
||||||
if (totp != null) {
|
|
||||||
credentials.add(UserCredentialModel.totp(totp));
|
|
||||||
}
|
|
||||||
|
|
||||||
if ((password == null || password.isEmpty()) && (passwordToken == null || passwordToken.isEmpty())) {
|
|
||||||
logger.debug("Password not provided");
|
|
||||||
return AuthenticationStatus.MISSING_PASSWORD;
|
|
||||||
}
|
|
||||||
|
|
||||||
logger.debugv("validating password for user: {0}", username);
|
|
||||||
|
|
||||||
if (!session.users().validCredentials(realm, user, credentials)) {
|
|
||||||
return AuthenticationStatus.INVALID_CREDENTIALS;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (!user.isEnabled()) {
|
|
||||||
return AuthenticationStatus.ACCOUNT_DISABLED;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (user.isTotp() && totp == null) {
|
|
||||||
return AuthenticationStatus.MISSING_TOTP;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (!user.getRequiredActions().isEmpty()) {
|
|
||||||
return AuthenticationStatus.ACTIONS_REQUIRED;
|
|
||||||
} else {
|
|
||||||
return AuthenticationStatus.SUCCESS;
|
|
||||||
}
|
|
||||||
} else if (types.contains(CredentialRepresentation.SECRET)) {
|
|
||||||
String secret = formData.getFirst(CredentialRepresentation.SECRET);
|
|
||||||
if (secret == null) {
|
|
||||||
logger.debug("Secret not provided");
|
|
||||||
return AuthenticationStatus.MISSING_PASSWORD;
|
|
||||||
}
|
|
||||||
if (!session.users().validCredentials(realm, user, UserCredentialModel.secret(secret))) {
|
|
||||||
return AuthenticationStatus.INVALID_CREDENTIALS;
|
|
||||||
}
|
|
||||||
if (!user.isEnabled()) {
|
|
||||||
return AuthenticationStatus.ACCOUNT_DISABLED;
|
|
||||||
}
|
|
||||||
if (!user.getRequiredActions().isEmpty()) {
|
|
||||||
return AuthenticationStatus.ACTIONS_REQUIRED;
|
|
||||||
} else {
|
|
||||||
return AuthenticationStatus.SUCCESS;
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
logger.warn("Do not know how to authenticate user");
|
|
||||||
return AuthenticationStatus.FAILED;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
public enum AuthenticationStatus {
|
public enum AuthenticationStatus {
|
||||||
SUCCESS, ACCOUNT_TEMPORARILY_DISABLED, ACCOUNT_DISABLED, ACTIONS_REQUIRED, INVALID_USER, INVALID_CREDENTIALS, MISSING_PASSWORD, MISSING_TOTP, FAILED
|
SUCCESS, ACCOUNT_TEMPORARILY_DISABLED, ACCOUNT_DISABLED, ACTIONS_REQUIRED, INVALID_USER, INVALID_CREDENTIALS, MISSING_PASSWORD, MISSING_TOTP, FAILED
|
||||||
}
|
}
|
||||||
|
|
|
@ -137,6 +137,21 @@ public class LoginTest {
|
||||||
.assertEvent();
|
.assertEvent();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void loginMissingPassword() {
|
||||||
|
loginPage.open();
|
||||||
|
loginPage.missingPassword("login-test");
|
||||||
|
|
||||||
|
loginPage.assertCurrent();
|
||||||
|
|
||||||
|
Assert.assertEquals("Invalid username or password.", loginPage.getError());
|
||||||
|
|
||||||
|
events.expectLogin().user(userId).session((String) null).error("invalid_user_credentials")
|
||||||
|
.detail(Details.USERNAME, "login-test")
|
||||||
|
.removeDetail(Details.CONSENT)
|
||||||
|
.assertEvent();
|
||||||
|
}
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
public void loginInvalidPasswordDisabledUser() {
|
public void loginInvalidPasswordDisabledUser() {
|
||||||
keycloakRule.configure(new KeycloakRule.KeycloakSetup() {
|
keycloakRule.configure(new KeycloakRule.KeycloakSetup() {
|
||||||
|
@ -214,6 +229,20 @@ public class LoginTest {
|
||||||
.assertEvent();
|
.assertEvent();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void loginMissingUsername() {
|
||||||
|
loginPage.open();
|
||||||
|
loginPage.missingUsername();
|
||||||
|
|
||||||
|
loginPage.assertCurrent();
|
||||||
|
|
||||||
|
Assert.assertEquals("Invalid username or password.", loginPage.getError());
|
||||||
|
|
||||||
|
events.expectLogin().user((String) null).session((String) null).error("user_not_found")
|
||||||
|
.removeDetail(Details.CONSENT)
|
||||||
|
.assertEvent();
|
||||||
|
}
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
public void loginSuccess() {
|
public void loginSuccess() {
|
||||||
loginPage.open();
|
loginPage.open();
|
||||||
|
|
|
@ -121,6 +121,25 @@ public class LoginTotpTest {
|
||||||
.assertEvent();
|
.assertEvent();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void loginWithMissingTotp() throws Exception {
|
||||||
|
loginPage.open();
|
||||||
|
loginPage.login("test-user@localhost", "password");
|
||||||
|
|
||||||
|
loginTotpPage.assertCurrent();
|
||||||
|
|
||||||
|
loginTotpPage.login(null);
|
||||||
|
loginTotpPage.assertCurrent();
|
||||||
|
Assert.assertEquals("Invalid authenticator code.", loginPage.getError());
|
||||||
|
|
||||||
|
//loginPage.assertCurrent(); // Invalid authenticator code.
|
||||||
|
//Assert.assertEquals("Invalid username or password.", loginPage.getError());
|
||||||
|
|
||||||
|
events.expectLogin().error("invalid_user_credentials").session((String) null)
|
||||||
|
.removeDetail(Details.CONSENT)
|
||||||
|
.assertEvent();
|
||||||
|
}
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
public void loginWithTotpSuccess() throws Exception {
|
public void loginWithTotpSuccess() throws Exception {
|
||||||
loginPage.open();
|
loginPage.open();
|
||||||
|
|
|
@ -1,288 +0,0 @@
|
||||||
package org.keycloak.testsuite.model;
|
|
||||||
|
|
||||||
import org.jboss.resteasy.specimpl.MultivaluedMapImpl;
|
|
||||||
import org.jboss.resteasy.spi.ResteasyProviderFactory;
|
|
||||||
import org.junit.Assert;
|
|
||||||
import org.junit.Before;
|
|
||||||
import org.junit.Test;
|
|
||||||
import org.keycloak.ClientConnection;
|
|
||||||
import org.keycloak.jose.jws.JWSBuilder;
|
|
||||||
import org.keycloak.models.RealmModel;
|
|
||||||
import org.keycloak.models.UserCredentialModel;
|
|
||||||
import org.keycloak.models.UserModel;
|
|
||||||
import org.keycloak.models.UserModel.RequiredAction;
|
|
||||||
import org.keycloak.models.utils.KeycloakModelUtils;
|
|
||||||
import org.keycloak.models.utils.TimeBasedOTP;
|
|
||||||
import org.keycloak.representations.PasswordToken;
|
|
||||||
import org.keycloak.representations.idm.CredentialRepresentation;
|
|
||||||
import org.keycloak.services.managers.AuthenticationManager;
|
|
||||||
import org.keycloak.services.managers.AuthenticationManager.AuthenticationStatus;
|
|
||||||
import org.keycloak.services.managers.BruteForceProtector;
|
|
||||||
import org.keycloak.util.Time;
|
|
||||||
|
|
||||||
import javax.ws.rs.core.MultivaluedMap;
|
|
||||||
import java.util.UUID;
|
|
||||||
|
|
||||||
public class AuthenticationManagerTest extends AbstractModelTest {
|
|
||||||
|
|
||||||
private AuthenticationManager am;
|
|
||||||
private MultivaluedMap<String, String> formData;
|
|
||||||
private TimeBasedOTP otp;
|
|
||||||
private RealmModel realm;
|
|
||||||
private UserModel user;
|
|
||||||
private BruteForceProtector protector;
|
|
||||||
private ClientConnection dummyConnection = new ClientConnection() {
|
|
||||||
@Override
|
|
||||||
public String getRemoteAddr() {
|
|
||||||
return "127.0.0.1";
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public String getRemoteHost() {
|
|
||||||
return "localhost";
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public int getReportPort() {
|
|
||||||
return 8080;
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
@Test
|
|
||||||
public void authForm() {
|
|
||||||
AuthenticationStatus status = am.authenticateForm(session, dummyConnection, realm, formData);
|
|
||||||
Assert.assertEquals(AuthenticationStatus.SUCCESS, status);
|
|
||||||
}
|
|
||||||
|
|
||||||
@Test
|
|
||||||
public void authFormInvalidPassword() {
|
|
||||||
formData.remove(CredentialRepresentation.PASSWORD);
|
|
||||||
formData.add(CredentialRepresentation.PASSWORD, "invalid");
|
|
||||||
|
|
||||||
AuthenticationStatus status = am.authenticateForm(session, dummyConnection, realm, formData);
|
|
||||||
Assert.assertEquals(AuthenticationStatus.INVALID_CREDENTIALS, status);
|
|
||||||
}
|
|
||||||
|
|
||||||
@Test
|
|
||||||
public void authFormMissingUsername() {
|
|
||||||
formData.remove("username");
|
|
||||||
|
|
||||||
AuthenticationStatus status = am.authenticateForm(session, dummyConnection, realm, formData);
|
|
||||||
Assert.assertEquals(AuthenticationStatus.INVALID_USER, status);
|
|
||||||
}
|
|
||||||
|
|
||||||
@Test
|
|
||||||
public void authFormMissingPassword() {
|
|
||||||
formData.remove(CredentialRepresentation.PASSWORD);
|
|
||||||
|
|
||||||
AuthenticationStatus status = am.authenticateForm(session, dummyConnection, realm, formData);
|
|
||||||
Assert.assertEquals(AuthenticationStatus.MISSING_PASSWORD, status);
|
|
||||||
}
|
|
||||||
|
|
||||||
@Test
|
|
||||||
public void authFormRequiredAction() {
|
|
||||||
realm.addRequiredCredential(CredentialRepresentation.TOTP);
|
|
||||||
user.addRequiredAction(RequiredAction.CONFIGURE_TOTP);
|
|
||||||
|
|
||||||
AuthenticationStatus status = am.authenticateForm(session, dummyConnection, realm, formData);
|
|
||||||
Assert.assertEquals(AuthenticationStatus.ACTIONS_REQUIRED, status);
|
|
||||||
}
|
|
||||||
|
|
||||||
@Test
|
|
||||||
public void authFormUserDisabled() {
|
|
||||||
user.setEnabled(false);
|
|
||||||
|
|
||||||
AuthenticationStatus status = am.authenticateForm(session, dummyConnection, realm, formData);
|
|
||||||
Assert.assertEquals(AuthenticationStatus.ACCOUNT_DISABLED, status);
|
|
||||||
}
|
|
||||||
|
|
||||||
@Test
|
|
||||||
public void authFormWithTotp() {
|
|
||||||
realm.addRequiredCredential(CredentialRepresentation.TOTP);
|
|
||||||
|
|
||||||
String totpSecret = UUID.randomUUID().toString();
|
|
||||||
|
|
||||||
UserCredentialModel credential = new UserCredentialModel();
|
|
||||||
credential.setType(CredentialRepresentation.TOTP);
|
|
||||||
credential.setValue(totpSecret);
|
|
||||||
|
|
||||||
user.updateCredential(credential);
|
|
||||||
|
|
||||||
user.setTotp(true);
|
|
||||||
|
|
||||||
String token = otp.generate(totpSecret);
|
|
||||||
|
|
||||||
formData.add(CredentialRepresentation.TOTP, token);
|
|
||||||
|
|
||||||
AuthenticationStatus status = am.authenticateForm(session, dummyConnection, realm, formData);
|
|
||||||
Assert.assertEquals(AuthenticationStatus.SUCCESS, status);
|
|
||||||
}
|
|
||||||
|
|
||||||
@Test
|
|
||||||
public void authFormWithTotpInvalidPassword() {
|
|
||||||
authFormWithTotp();
|
|
||||||
|
|
||||||
formData.remove(CredentialRepresentation.PASSWORD);
|
|
||||||
formData.add(CredentialRepresentation.PASSWORD, "invalid");
|
|
||||||
|
|
||||||
AuthenticationStatus status = am.authenticateForm(session, dummyConnection, realm, formData);
|
|
||||||
Assert.assertEquals(AuthenticationStatus.INVALID_CREDENTIALS, status);
|
|
||||||
}
|
|
||||||
|
|
||||||
@Test
|
|
||||||
public void authFormWithTotpMissingPassword() {
|
|
||||||
authFormWithTotp();
|
|
||||||
|
|
||||||
formData.remove(CredentialRepresentation.PASSWORD);
|
|
||||||
|
|
||||||
AuthenticationStatus status = am.authenticateForm(session, dummyConnection, realm, formData);
|
|
||||||
Assert.assertEquals(AuthenticationStatus.MISSING_PASSWORD, status);
|
|
||||||
}
|
|
||||||
|
|
||||||
@Test
|
|
||||||
public void authFormWithTotpInvalidTotp() {
|
|
||||||
authFormWithTotp();
|
|
||||||
|
|
||||||
formData.remove(CredentialRepresentation.TOTP);
|
|
||||||
formData.add(CredentialRepresentation.TOTP, "invalid");
|
|
||||||
|
|
||||||
AuthenticationStatus status = am.authenticateForm(session, dummyConnection, realm, formData);
|
|
||||||
Assert.assertEquals(AuthenticationStatus.INVALID_CREDENTIALS, status);
|
|
||||||
}
|
|
||||||
|
|
||||||
@Test
|
|
||||||
public void authFormWithTotpMissingTotp() {
|
|
||||||
authFormWithTotp();
|
|
||||||
|
|
||||||
formData.remove(CredentialRepresentation.TOTP);
|
|
||||||
|
|
||||||
AuthenticationStatus status = am.authenticateForm(session, dummyConnection, realm, formData);
|
|
||||||
Assert.assertEquals(AuthenticationStatus.MISSING_TOTP, status);
|
|
||||||
}
|
|
||||||
|
|
||||||
@Test
|
|
||||||
public void authFormWithTotpPasswordToken() {
|
|
||||||
realm.addRequiredCredential(CredentialRepresentation.TOTP);
|
|
||||||
|
|
||||||
String totpSecret = UUID.randomUUID().toString();
|
|
||||||
|
|
||||||
UserCredentialModel credential = new UserCredentialModel();
|
|
||||||
credential.setType(CredentialRepresentation.TOTP);
|
|
||||||
credential.setValue(totpSecret);
|
|
||||||
|
|
||||||
user.updateCredential(credential);
|
|
||||||
|
|
||||||
user.setTotp(true);
|
|
||||||
|
|
||||||
String token = otp.generate(totpSecret);
|
|
||||||
|
|
||||||
formData.add(CredentialRepresentation.TOTP, token);
|
|
||||||
formData.remove(CredentialRepresentation.PASSWORD);
|
|
||||||
|
|
||||||
String passwordToken = new JWSBuilder().jsonContent(new PasswordToken(realm.getName(), user.getId())).rsa256(realm.getPrivateKey());
|
|
||||||
formData.add(CredentialRepresentation.PASSWORD_TOKEN, passwordToken);
|
|
||||||
|
|
||||||
AuthenticationStatus status = am.authenticateForm(session, dummyConnection, realm, formData);
|
|
||||||
Assert.assertEquals(AuthenticationStatus.SUCCESS, status);
|
|
||||||
}
|
|
||||||
|
|
||||||
@Test
|
|
||||||
public void authFormWithTotpPasswordTokenInvalidKey() {
|
|
||||||
authFormWithTotpPasswordToken();
|
|
||||||
|
|
||||||
formData.remove(CredentialRepresentation.PASSWORD_TOKEN);
|
|
||||||
String passwordToken = new JWSBuilder().jsonContent(new PasswordToken(realm.getName(), user.getId())).rsa256(realm.getPrivateKey());
|
|
||||||
formData.add(CredentialRepresentation.PASSWORD_TOKEN, passwordToken);
|
|
||||||
|
|
||||||
KeycloakModelUtils.generateRealmKeys(realm);
|
|
||||||
|
|
||||||
AuthenticationStatus status = am.authenticateForm(session, dummyConnection, realm, formData);
|
|
||||||
Assert.assertEquals(AuthenticationStatus.INVALID_CREDENTIALS, status);
|
|
||||||
}
|
|
||||||
|
|
||||||
@Test
|
|
||||||
public void authFormWithTotpPasswordTokenInvalidRealm() {
|
|
||||||
authFormWithTotpPasswordToken();
|
|
||||||
|
|
||||||
formData.remove(CredentialRepresentation.PASSWORD_TOKEN);
|
|
||||||
String passwordToken = new JWSBuilder().jsonContent(new PasswordToken("invalid", user.getId())).rsa256(realm.getPrivateKey());
|
|
||||||
formData.add(CredentialRepresentation.PASSWORD_TOKEN, passwordToken);
|
|
||||||
|
|
||||||
AuthenticationStatus status = am.authenticateForm(session, dummyConnection, realm, formData);
|
|
||||||
Assert.assertEquals(AuthenticationStatus.INVALID_CREDENTIALS, status);
|
|
||||||
}
|
|
||||||
|
|
||||||
@Test
|
|
||||||
public void authFormWithTotpPasswordTokenInvalidUser() {
|
|
||||||
authFormWithTotpPasswordToken();
|
|
||||||
|
|
||||||
formData.remove(CredentialRepresentation.PASSWORD_TOKEN);
|
|
||||||
String passwordToken = new JWSBuilder().jsonContent(new PasswordToken(realm.getName(), "invalid")).rsa256(realm.getPrivateKey());
|
|
||||||
formData.add(CredentialRepresentation.PASSWORD_TOKEN, passwordToken);
|
|
||||||
|
|
||||||
AuthenticationStatus status = am.authenticateForm(session, dummyConnection, realm, formData);
|
|
||||||
Assert.assertEquals(AuthenticationStatus.INVALID_CREDENTIALS, status);
|
|
||||||
}
|
|
||||||
|
|
||||||
@Test
|
|
||||||
public void authFormWithTotpPasswordTokenExpired() throws InterruptedException {
|
|
||||||
int lifespan = realm.getAccessCodeLifespanUserAction();
|
|
||||||
|
|
||||||
try {
|
|
||||||
authFormWithTotpPasswordToken();
|
|
||||||
|
|
||||||
realm.setAccessCodeLifespanUserAction(1);
|
|
||||||
|
|
||||||
formData.remove(CredentialRepresentation.PASSWORD_TOKEN);
|
|
||||||
String passwordToken = new JWSBuilder().jsonContent(new PasswordToken(realm.getName(), "invalid")).rsa256(realm.getPrivateKey());
|
|
||||||
formData.add(CredentialRepresentation.PASSWORD_TOKEN, passwordToken);
|
|
||||||
|
|
||||||
Time.setOffset(2);
|
|
||||||
|
|
||||||
AuthenticationStatus status = am.authenticateForm(session, dummyConnection, realm, formData);
|
|
||||||
Assert.assertEquals(AuthenticationStatus.INVALID_CREDENTIALS, status);
|
|
||||||
|
|
||||||
Time.setOffset(0);
|
|
||||||
} finally {
|
|
||||||
realm.setAccessCodeLifespanUserAction(lifespan);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
@Before
|
|
||||||
@Override
|
|
||||||
public void before() throws Exception {
|
|
||||||
super.before();
|
|
||||||
|
|
||||||
realm = realmManager.createRealm("TestAuth");
|
|
||||||
realm.setAccessCodeLifespan(100);
|
|
||||||
realm.setAccessCodeLifespanUserAction(100);
|
|
||||||
realm.setEnabled(true);
|
|
||||||
realm.setName("TestAuth");
|
|
||||||
|
|
||||||
KeycloakModelUtils.generateRealmKeys(realm);
|
|
||||||
|
|
||||||
realm.setAccessTokenLifespan(1000);
|
|
||||||
realm.addRequiredCredential(CredentialRepresentation.PASSWORD);
|
|
||||||
|
|
||||||
protector = ResteasyProviderFactory.getContextData(BruteForceProtector.class);
|
|
||||||
am = new AuthenticationManager(protector);
|
|
||||||
|
|
||||||
user = realmManager.getSession().users().addUser(realm, "test");
|
|
||||||
user.setEnabled(true);
|
|
||||||
|
|
||||||
UserCredentialModel credential = new UserCredentialModel();
|
|
||||||
credential.setType(CredentialRepresentation.PASSWORD);
|
|
||||||
credential.setValue("password");
|
|
||||||
|
|
||||||
user.updateCredential(credential);
|
|
||||||
|
|
||||||
formData = new MultivaluedMapImpl<String, String>();
|
|
||||||
formData.add("username", "test");
|
|
||||||
formData.add(CredentialRepresentation.PASSWORD, "password");
|
|
||||||
|
|
||||||
otp = new TimeBasedOTP();
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
|
13
testsuite/integration/src/test/java/org/keycloak/testsuite/pages/LoginPage.java
Normal file → Executable file
13
testsuite/integration/src/test/java/org/keycloak/testsuite/pages/LoginPage.java
Normal file → Executable file
|
@ -91,6 +91,19 @@ public class LoginPage extends AbstractPage {
|
||||||
submitButton.click();
|
submitButton.click();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public void missingPassword(String username) {
|
||||||
|
usernameInput.clear();
|
||||||
|
usernameInput.sendKeys(username);
|
||||||
|
passwordInput.clear();
|
||||||
|
submitButton.click();
|
||||||
|
|
||||||
|
}
|
||||||
|
public void missingUsername() {
|
||||||
|
usernameInput.clear();
|
||||||
|
submitButton.click();
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
public String getUsername() {
|
public String getUsername() {
|
||||||
return usernameInput.getAttribute("value");
|
return usernameInput.getAttribute("value");
|
||||||
}
|
}
|
||||||
|
|
3
testsuite/integration/src/test/java/org/keycloak/testsuite/pages/LoginTotpPage.java
Normal file → Executable file
3
testsuite/integration/src/test/java/org/keycloak/testsuite/pages/LoginTotpPage.java
Normal file → Executable file
|
@ -43,7 +43,8 @@ public class LoginTotpPage extends AbstractPage {
|
||||||
private WebElement loginErrorMessage;
|
private WebElement loginErrorMessage;
|
||||||
|
|
||||||
public void login(String totp) {
|
public void login(String totp) {
|
||||||
totpInput.sendKeys(totp);
|
totpInput.clear();
|
||||||
|
if (totp != null) totpInput.sendKeys(totp);
|
||||||
|
|
||||||
submitButton.click();
|
submitButton.click();
|
||||||
}
|
}
|
||||||
|
|
Loading…
Reference in a new issue