passing tests

This commit is contained in:
Bill Burke 2015-06-05 13:49:24 -04:00
parent 9ab023cc6c
commit 021b01f0bd
9 changed files with 64 additions and 36 deletions

View file

@ -1,10 +1,20 @@
package org.keycloak.models; package org.keycloak.models;
import java.util.Comparator;
/** /**
* @author <a href="mailto:bill@burkecentral.com">Bill Burke</a> * @author <a href="mailto:bill@burkecentral.com">Bill Burke</a>
* @version $Revision: 1 $ * @version $Revision: 1 $
*/ */
public class AuthenticationExecutionModel { public class AuthenticationExecutionModel {
public static class ExecutionComparator implements Comparator<AuthenticationExecutionModel> {
public static final ExecutionComparator SINGLETON = new ExecutionComparator();
@Override
public int compare(AuthenticationExecutionModel o1, AuthenticationExecutionModel o2) {
return o1.priority - o2.priority;
}
}
private String id; private String id;
private String authenticator; private String authenticator;

View file

@ -20,6 +20,7 @@ import org.keycloak.services.managers.BruteForceProtector;
import javax.ws.rs.core.Response; import javax.ws.rs.core.Response;
import javax.ws.rs.core.UriInfo; import javax.ws.rs.core.UriInfo;
import java.util.Collections;
import java.util.List; import java.util.List;
/** /**
@ -332,6 +333,7 @@ public class AuthenticationProcessor {
} }
public Response authenticate() throws AuthException { public Response authenticate() throws AuthException {
logger.info("AUTHENTICATE");
event.event(EventType.LOGIN); event.event(EventType.LOGIN);
event.client(clientSession.getClient().getClientId()) event.client(clientSession.getClient().getClientId())
.detail(Details.REDIRECT_URI, clientSession.getRedirectUri()) .detail(Details.REDIRECT_URI, clientSession.getRedirectUri())
@ -393,11 +395,13 @@ public class AuthenticationProcessor {
} }
List<AuthenticationExecutionModel> executions = realm.getAuthenticationExecutions(flowId); List<AuthenticationExecutionModel> executions = realm.getAuthenticationExecutions(flowId);
if (executions == null) return null; if (executions == null) return null;
Collections.sort(executions, AuthenticationExecutionModel.ExecutionComparator.SINGLETON);
Response alternativeChallenge = null; Response alternativeChallenge = null;
AuthenticationExecutionModel challengedAlternativeExecution = null; AuthenticationExecutionModel challengedAlternativeExecution = null;
boolean alternativeSuccessful = false; boolean alternativeSuccessful = false;
for (AuthenticationExecutionModel model : executions) { for (AuthenticationExecutionModel model : executions) {
if (isProcessed(model)) { if (isProcessed(model)) {
logger.info("execution is processed");
if (!alternativeSuccessful && model.isAlternative() && isSuccessful(model)) alternativeSuccessful = true; if (!alternativeSuccessful && model.isAlternative() && isSuccessful(model)) alternativeSuccessful = true;
continue; continue;
} }
@ -421,6 +425,7 @@ public class AuthenticationProcessor {
AuthenticatorModel authenticatorModel = realm.getAuthenticatorById(model.getAuthenticator()); AuthenticatorModel authenticatorModel = realm.getAuthenticatorById(model.getAuthenticator());
AuthenticatorFactory factory = (AuthenticatorFactory)session.getKeycloakSessionFactory().getProviderFactory(Authenticator.class, authenticatorModel.getProviderId()); AuthenticatorFactory factory = (AuthenticatorFactory)session.getKeycloakSessionFactory().getProviderFactory(Authenticator.class, authenticatorModel.getProviderId());
Authenticator authenticator = factory.create(authenticatorModel); Authenticator authenticator = factory.create(authenticatorModel);
logger.info("authenticator: " + authenticatorModel.getProviderId());
UserModel authUser = clientSession.getAuthenticatedUser(); UserModel authUser = clientSession.getAuthenticatedUser();
if (authenticator.requiresUser() && authUser == null){ if (authenticator.requiresUser() && authUser == null){
@ -428,7 +433,7 @@ public class AuthenticationProcessor {
clientSession.setAuthenticatorStatus(challengedAlternativeExecution.getId(), UserSessionModel.AuthenticatorStatus.CHALLENGED); clientSession.setAuthenticatorStatus(challengedAlternativeExecution.getId(), UserSessionModel.AuthenticatorStatus.CHALLENGED);
return alternativeChallenge; return alternativeChallenge;
} }
throw new AuthException(Error.UNKNOWN_USER); throw new AuthException("authenticator: " + authenticatorModel.getProviderId(), Error.UNKNOWN_USER);
} }
boolean configuredFor = false; boolean configuredFor = false;
if (authenticator.requiresUser() && authUser != null) { if (authenticator.requiresUser() && authUser != null) {
@ -436,6 +441,7 @@ public class AuthenticationProcessor {
if (!configuredFor) { if (!configuredFor) {
if (model.isRequired()) { if (model.isRequired()) {
if (model.isUserSetupAllowed()) { if (model.isUserSetupAllowed()) {
logger.info("authenticator SETUP_REQUIRED: " + authenticatorModel.getProviderId());
clientSession.setAuthenticatorStatus(model.getId(), UserSessionModel.AuthenticatorStatus.SETUP_REQUIRED); clientSession.setAuthenticatorStatus(model.getId(), UserSessionModel.AuthenticatorStatus.SETUP_REQUIRED);
String requiredAction = authenticator.getRequiredAction(); String requiredAction = authenticator.getRequiredAction();
if (!authUser.getRequiredActions().contains(requiredAction)) { if (!authUser.getRequiredActions().contains(requiredAction)) {
@ -455,15 +461,18 @@ public class AuthenticationProcessor {
authenticator.authenticate(context); authenticator.authenticate(context);
Status result = context.getStatus(); Status result = context.getStatus();
if (result == Status.SUCCESS){ if (result == Status.SUCCESS){
logger.info("authenticator SUCCESS: " + authenticatorModel.getProviderId());
clientSession.setAuthenticatorStatus(model.getId(), UserSessionModel.AuthenticatorStatus.SUCCESS); clientSession.setAuthenticatorStatus(model.getId(), UserSessionModel.AuthenticatorStatus.SUCCESS);
if (model.isAlternative()) alternativeSuccessful = true; if (model.isAlternative()) alternativeSuccessful = true;
continue; continue;
} else if (result == Status.FAILED) { } else if (result == Status.FAILED) {
logger.info("authenticator FAILED: " + authenticatorModel.getProviderId());
logUserFailure(); logUserFailure();
clientSession.setAuthenticatorStatus(model.getId(), UserSessionModel.AuthenticatorStatus.FAILED); clientSession.setAuthenticatorStatus(model.getId(), UserSessionModel.AuthenticatorStatus.FAILED);
if (context.challenge != null) return context.challenge; if (context.challenge != null) return context.challenge;
throw new AuthException(context.error); throw new AuthException(context.error);
} else if (result == Status.CHALLENGE) { } else if (result == Status.CHALLENGE) {
logger.info("authenticator CHALLENGE: " + authenticatorModel.getProviderId());
if (model.isRequired() || (model.isOptional() && configuredFor)) { if (model.isRequired() || (model.isOptional() && configuredFor)) {
clientSession.setAuthenticatorStatus(model.getId(), UserSessionModel.AuthenticatorStatus.CHALLENGED); clientSession.setAuthenticatorStatus(model.getId(), UserSessionModel.AuthenticatorStatus.CHALLENGED);
return context.challenge; return context.challenge;
@ -476,16 +485,19 @@ public class AuthenticationProcessor {
} }
continue; continue;
} else if (result == Status.FAILURE_CHALLENGE) { } else if (result == Status.FAILURE_CHALLENGE) {
logger.info("authenticator FAILURE_CHALLENGE: " + authenticatorModel.getProviderId());
logUserFailure(); logUserFailure();
clientSession.setAuthenticatorStatus(model.getId(), UserSessionModel.AuthenticatorStatus.CHALLENGED); clientSession.setAuthenticatorStatus(model.getId(), UserSessionModel.AuthenticatorStatus.CHALLENGED);
return context.challenge; return context.challenge;
} else if (result == Status.ATTEMPTED) { } else if (result == Status.ATTEMPTED) {
logger.info("authenticator ATTEMPTED: " + authenticatorModel.getProviderId());
if (model.getRequirement() == AuthenticationExecutionModel.Requirement.REQUIRED) { if (model.getRequirement() == AuthenticationExecutionModel.Requirement.REQUIRED) {
throw new AuthException(Error.INVALID_CREDENTIALS); throw new AuthException(Error.INVALID_CREDENTIALS);
} }
clientSession.setAuthenticatorStatus(model.getId(), UserSessionModel.AuthenticatorStatus.ATTEMPTED); clientSession.setAuthenticatorStatus(model.getId(), UserSessionModel.AuthenticatorStatus.ATTEMPTED);
continue; continue;
} else { } else {
logger.info("authenticator INTERNAL_ERROR: " + authenticatorModel.getProviderId());
logger.error("Unknown result status"); logger.error("Unknown result status");
throw new AuthException(Error.INTERNAL_ERROR); throw new AuthException(Error.INTERNAL_ERROR);
} }
@ -508,10 +520,15 @@ public class AuthenticationProcessor {
protected Response authenticationComplete() { protected Response authenticationComplete() {
String username = clientSession.getAuthenticatedUser().getUsername(); String username = clientSession.getAuthenticatedUser().getUsername();
String rememberMe = clientSession.getNote(Details.REMEMBER_ME);
boolean remember = rememberMe != null && rememberMe.equalsIgnoreCase("true");
if (userSession == null) { // if no authenticator attached a usersession if (userSession == null) { // if no authenticator attached a usersession
userSession = session.sessions().createUserSession(realm, clientSession.getAuthenticatedUser(), username, connection.getRemoteAddr(), "form", false, null, null); userSession = session.sessions().createUserSession(realm, clientSession.getAuthenticatedUser(), username, connection.getRemoteAddr(), clientSession.getAuthMethod(), remember, null, null);
userSession.setState(UserSessionModel.State.LOGGING_IN); userSession.setState(UserSessionModel.State.LOGGING_IN);
} }
if (remember) {
event.detail(Details.REMEMBER_ME, "true");
}
TokenManager.attachClientSession(userSession, clientSession); TokenManager.attachClientSession(userSession, clientSession);
event.user(userSession.getUser()) event.user(userSession.getUser())
.detail(Details.USERNAME, username) .detail(Details.USERNAME, username)

View file

@ -15,5 +15,4 @@ public interface Authenticator extends Provider {
boolean configuredFor(KeycloakSession session, RealmModel realm, UserModel user); boolean configuredFor(KeycloakSession session, RealmModel realm, UserModel user);
String getRequiredAction(); String getRequiredAction();
} }

View file

@ -24,6 +24,7 @@ import javax.ws.rs.core.Response;
* @version $Revision: 1 $ * @version $Revision: 1 $
*/ */
public class LoginFormUsernameAuthenticator extends AbstractFormAuthenticator implements Authenticator { public class LoginFormUsernameAuthenticator extends AbstractFormAuthenticator implements Authenticator {
public static final String FORM_USERNAME = "FORM_USERNAME";
protected AuthenticatorModel model; protected AuthenticatorModel model;
public LoginFormUsernameAuthenticator(AuthenticatorModel model) { public LoginFormUsernameAuthenticator(AuthenticatorModel model) {
@ -35,9 +36,14 @@ public class LoginFormUsernameAuthenticator extends AbstractFormAuthenticator im
if (!isActionUrl(context)) { if (!isActionUrl(context)) {
MultivaluedMap<String, String> formData = new MultivaluedMapImpl<>(); MultivaluedMap<String, String> formData = new MultivaluedMapImpl<>();
String loginHint = context.getClientSession().getNote(OIDCLoginProtocol.LOGIN_HINT_PARAM); String loginHint = context.getClientSession().getNote(OIDCLoginProtocol.LOGIN_HINT_PARAM);
if (loginHint == null) {
loginHint = AuthenticationManager.getRememberMeUsername(context.getRealm(), context.getHttpRequest().getHttpHeaders()); String rememberMeUsername = AuthenticationManager.getRememberMeUsername(context.getRealm(), context.getHttpRequest().getHttpHeaders());
if (loginHint != null || rememberMeUsername != null) {
if (loginHint != null) { if (loginHint != null) {
formData.add(AuthenticationManager.FORM_USERNAME, loginHint);
} else {
formData.add(AuthenticationManager.FORM_USERNAME, rememberMeUsername);
formData.add("rememberMe", "on"); formData.add("rememberMe", "on");
} }
} }
@ -83,9 +89,15 @@ public class LoginFormUsernameAuthenticator extends AbstractFormAuthenticator im
return; return;
} }
context.getEvent().detail(Details.USERNAME, username); context.getEvent().detail(Details.USERNAME, username);
context.getClientSession().setNote("FORM_USERNAME", username); context.getClientSession().setNote(FORM_USERNAME, username);
UserModel user = KeycloakModelUtils.findUserByNameOrEmail(context.getSession(), context.getRealm(), username); UserModel user = KeycloakModelUtils.findUserByNameOrEmail(context.getSession(), context.getRealm(), username);
if (invalidUser(context, user)) return; if (invalidUser(context, user)) return;
String rememberMe = inputData.getFirst("rememberMe");
boolean remember = rememberMe != null && rememberMe.equalsIgnoreCase("on");
if (remember) {
context.getClientSession().setNote(Details.REMEMBER_ME, "true");
context.getEvent().detail(Details.REMEMBER_ME, "true");
}
context.setUser(user); context.setUser(user);
context.success(); context.success();
} }

View file

@ -216,8 +216,9 @@ public class AccountTest {
changePasswordPage.open(); changePasswordPage.open();
loginPage.login("test-user@localhost", "password"); loginPage.login("test-user@localhost", "password");
String sessionId = events.expectLogin().client("account").detail(Details.REDIRECT_URI, ACCOUNT_REDIRECT + "?path=password").assertEvent().getSessionId(); Event event = events.expectLogin().client("account").detail(Details.REDIRECT_URI, ACCOUNT_REDIRECT + "?path=password").assertEvent();
String sessionId = event.getSessionId();
String userId = event.getUserId();
changePasswordPage.changePassword("", "new-password", "new-password"); changePasswordPage.changePassword("", "new-password", "new-password");
Assert.assertEquals("Please specify password.", profilePage.getError()); Assert.assertEquals("Please specify password.", profilePage.getError());
@ -241,7 +242,7 @@ public class AccountTest {
Assert.assertEquals("Invalid username or password.", loginPage.getError()); Assert.assertEquals("Invalid username or password.", loginPage.getError());
events.expectLogin().session((String) null).user((String)null).error("invalid_user_credentials").assertEvent(); events.expectLogin().session((String) null).user(userId).error("invalid_user_credentials").assertEvent();
loginPage.open(); loginPage.open();
loginPage.login("test-user@localhost", "new-password"); loginPage.login("test-user@localhost", "new-password");

View file

@ -26,11 +26,15 @@ import org.junit.Before;
import org.junit.ClassRule; import org.junit.ClassRule;
import org.junit.Rule; import org.junit.Rule;
import org.junit.Test; import org.junit.Test;
import org.keycloak.authentication.authenticators.OTPFormAuthenticator;
import org.keycloak.authentication.authenticators.OTPFormAuthenticatorFactory;
import org.keycloak.events.Details; import org.keycloak.events.Details;
import org.keycloak.events.Event; import org.keycloak.events.Event;
import org.keycloak.events.EventType; import org.keycloak.events.EventType;
import org.keycloak.models.AuthenticationExecutionModel;
import org.keycloak.models.RealmModel; import org.keycloak.models.RealmModel;
import org.keycloak.models.UserModel; import org.keycloak.models.UserModel;
import org.keycloak.models.utils.DefaultAuthenticationFlows;
import org.keycloak.services.managers.RealmManager; import org.keycloak.services.managers.RealmManager;
import org.keycloak.testsuite.AssertEvents; import org.keycloak.testsuite.AssertEvents;
import org.keycloak.testsuite.MailUtil; import org.keycloak.testsuite.MailUtil;
@ -46,6 +50,7 @@ import org.keycloak.testsuite.rule.KeycloakRule;
import org.keycloak.testsuite.rule.KeycloakRule.KeycloakSetup; import org.keycloak.testsuite.rule.KeycloakRule.KeycloakSetup;
import org.keycloak.testsuite.rule.WebResource; import org.keycloak.testsuite.rule.WebResource;
import org.keycloak.testsuite.rule.WebRule; import org.keycloak.testsuite.rule.WebRule;
import org.keycloak.testsuite.utils.CredentialHelper;
import org.openqa.selenium.WebDriver; import org.openqa.selenium.WebDriver;
import javax.mail.MessagingException; import javax.mail.MessagingException;

View file

@ -144,28 +144,4 @@ public class LoginTotpTest {
events.expectLogin().error("invalid_user_credentials").session((String) null).assertEvent(); events.expectLogin().error("invalid_user_credentials").session((String) null).assertEvent();
} }
@Test
public void loginWithTotpExpiredPasswordToken() throws Exception {
try {
loginPage.open();
loginPage.login("test-user@localhost", "password");
loginTotpPage.assertCurrent();
Time.setOffset(350);
loginTotpPage.login(totp.generate("totpSecret"));
loginPage.assertCurrent();
Assert.assertEquals("Invalid username or password.", loginPage.getError());
AssertEvents.ExpectedEvent expectedEvent = events.expectLogin().error("invalid_user_credentials")
.session((String) null);
expectedEvent.assertEvent();
} finally {
Time.setOffset(0);
}
}
} }

View file

@ -123,7 +123,7 @@ public class LogoutTest {
// Check session 1 logged-in // Check session 1 logged-in
oauth.openLoginForm(); oauth.openLoginForm();
events.expectLogin().session(sessionId).detail(Details.AUTH_METHOD, "sso").removeDetail(Details.USERNAME).assertEvent(); events.expectLogin().session(sessionId).removeDetail(Details.USERNAME).assertEvent();
// Logout session 1 by redirect // Logout session 1 by redirect
driver.navigate().to(oauth.getLogoutUrl(AppPage.baseUrl, null)); driver.navigate().to(oauth.getLogoutUrl(AppPage.baseUrl, null));
@ -140,7 +140,7 @@ public class LogoutTest {
// Check session 3 logged-in // Check session 3 logged-in
oauth.openLoginForm(); oauth.openLoginForm();
events.expectLogin().session(sessionId3).detail(Details.AUTH_METHOD, "sso").removeDetail(Details.USERNAME).assertEvent(); events.expectLogin().session(sessionId3).removeDetail(Details.USERNAME).assertEvent();
} }
} }

View file

@ -34,6 +34,14 @@ public class CredentialHelper {
} }
} }
public static AuthenticationExecutionModel.Requirement getRequirement(RealmModel realm, String authenticatorProviderId, String flowAlias) {
AuthenticatorModel authenticator = findAuthenticatorByProviderId(realm, authenticatorProviderId);
AuthenticationFlowModel flow = findAuthenticatorFlowByAlias(realm, flowAlias);
AuthenticationExecutionModel execution = findExecutionByAuthenticator(realm, flow.getId(), authenticator.getId());
return execution.getRequirement();
}
public static void requireAuthentication(RealmModel realm, String authenticatorProviderId, String flowAlias) { public static void requireAuthentication(RealmModel realm, String authenticatorProviderId, String flowAlias) {
AuthenticationExecutionModel.Requirement requirement = AuthenticationExecutionModel.Requirement.REQUIRED; AuthenticationExecutionModel.Requirement requirement = AuthenticationExecutionModel.Requirement.REQUIRED;
authenticationRequirement(realm, authenticatorProviderId, flowAlias, requirement); authenticationRequirement(realm, authenticatorProviderId, flowAlias, requirement);