Skip to content

Commit

Permalink
passing tests
Browse files Browse the repository at this point in the history
  • Loading branch information
patriot1burke committed Jun 5, 2015
1 parent 9ab023c commit 021b01f
Show file tree
Hide file tree
Showing 9 changed files with 64 additions and 36 deletions.
@@ -1,10 +1,20 @@
package org.keycloak.models;

import java.util.Comparator;

/**
* @author <a href="mailto:bill@burkecentral.com">Bill Burke</a>
* @version $Revision: 1 $
*/
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 authenticator;
Expand Down
Expand Up @@ -20,6 +20,7 @@

import javax.ws.rs.core.Response;
import javax.ws.rs.core.UriInfo;
import java.util.Collections;
import java.util.List;

/**
Expand Down Expand Up @@ -332,6 +333,7 @@ public boolean isSuccessful(AuthenticationExecutionModel model) {
}

public Response authenticate() throws AuthException {
logger.info("AUTHENTICATE");
event.event(EventType.LOGIN);
event.client(clientSession.getClient().getClientId())
.detail(Details.REDIRECT_URI, clientSession.getRedirectUri())
Expand Down Expand Up @@ -381,7 +383,7 @@ public Response authenticateOnly() throws AuthException {
public Response finishAuthentication() {
event.success();
RealmModel realm = clientSession.getRealm();
return AuthenticationManager.redirectAfterSuccessfulFlow(session, realm , userSession, clientSession, request, uriInfo, connection);
return AuthenticationManager.redirectAfterSuccessfulFlow(session, realm, userSession, clientSession, request, uriInfo, connection);

}

Expand All @@ -393,11 +395,13 @@ public Response processFlow(String flowId) {
}
List<AuthenticationExecutionModel> executions = realm.getAuthenticationExecutions(flowId);
if (executions == null) return null;
Collections.sort(executions, AuthenticationExecutionModel.ExecutionComparator.SINGLETON);
Response alternativeChallenge = null;
AuthenticationExecutionModel challengedAlternativeExecution = null;
boolean alternativeSuccessful = false;
for (AuthenticationExecutionModel model : executions) {
if (isProcessed(model)) {
logger.info("execution is processed");
if (!alternativeSuccessful && model.isAlternative() && isSuccessful(model)) alternativeSuccessful = true;
continue;
}
Expand All @@ -421,21 +425,23 @@ public Response processFlow(String flowId) {
AuthenticatorModel authenticatorModel = realm.getAuthenticatorById(model.getAuthenticator());
AuthenticatorFactory factory = (AuthenticatorFactory)session.getKeycloakSessionFactory().getProviderFactory(Authenticator.class, authenticatorModel.getProviderId());
Authenticator authenticator = factory.create(authenticatorModel);
logger.info("authenticator: " + authenticatorModel.getProviderId());
UserModel authUser = clientSession.getAuthenticatedUser();

if (authenticator.requiresUser() && authUser == null){
if (alternativeChallenge != null) {
clientSession.setAuthenticatorStatus(challengedAlternativeExecution.getId(), UserSessionModel.AuthenticatorStatus.CHALLENGED);
return alternativeChallenge;
}
throw new AuthException(Error.UNKNOWN_USER);
throw new AuthException("authenticator: " + authenticatorModel.getProviderId(), Error.UNKNOWN_USER);
}
boolean configuredFor = false;
if (authenticator.requiresUser() && authUser != null) {
configuredFor = authenticator.configuredFor(session, realm, authUser);
if (!configuredFor) {
if (model.isRequired()) {
if (model.isUserSetupAllowed()) {
logger.info("authenticator SETUP_REQUIRED: " + authenticatorModel.getProviderId());
clientSession.setAuthenticatorStatus(model.getId(), UserSessionModel.AuthenticatorStatus.SETUP_REQUIRED);
String requiredAction = authenticator.getRequiredAction();
if (!authUser.getRequiredActions().contains(requiredAction)) {
Expand All @@ -455,15 +461,18 @@ public Response processFlow(String flowId) {
authenticator.authenticate(context);
Status result = context.getStatus();
if (result == Status.SUCCESS){
logger.info("authenticator SUCCESS: " + authenticatorModel.getProviderId());
clientSession.setAuthenticatorStatus(model.getId(), UserSessionModel.AuthenticatorStatus.SUCCESS);
if (model.isAlternative()) alternativeSuccessful = true;
continue;
} else if (result == Status.FAILED) {
logger.info("authenticator FAILED: " + authenticatorModel.getProviderId());
logUserFailure();
clientSession.setAuthenticatorStatus(model.getId(), UserSessionModel.AuthenticatorStatus.FAILED);
if (context.challenge != null) return context.challenge;
throw new AuthException(context.error);
} else if (result == Status.CHALLENGE) {
logger.info("authenticator CHALLENGE: " + authenticatorModel.getProviderId());
if (model.isRequired() || (model.isOptional() && configuredFor)) {
clientSession.setAuthenticatorStatus(model.getId(), UserSessionModel.AuthenticatorStatus.CHALLENGED);
return context.challenge;
Expand All @@ -476,16 +485,19 @@ else if (model.isAlternative()) {
}
continue;
} else if (result == Status.FAILURE_CHALLENGE) {
logger.info("authenticator FAILURE_CHALLENGE: " + authenticatorModel.getProviderId());
logUserFailure();
clientSession.setAuthenticatorStatus(model.getId(), UserSessionModel.AuthenticatorStatus.CHALLENGED);
return context.challenge;
} else if (result == Status.ATTEMPTED) {
logger.info("authenticator ATTEMPTED: " + authenticatorModel.getProviderId());
if (model.getRequirement() == AuthenticationExecutionModel.Requirement.REQUIRED) {
throw new AuthException(Error.INVALID_CREDENTIALS);
}
clientSession.setAuthenticatorStatus(model.getId(), UserSessionModel.AuthenticatorStatus.ATTEMPTED);
continue;
} else {
logger.info("authenticator INTERNAL_ERROR: " + authenticatorModel.getProviderId());
logger.error("Unknown result status");
throw new AuthException(Error.INTERNAL_ERROR);
}
Expand All @@ -508,10 +520,15 @@ public void validateUser(UserModel authenticatedUser) {

protected Response authenticationComplete() {
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
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);
}
if (remember) {
event.detail(Details.REMEMBER_ME, "true");
}
TokenManager.attachClientSession(userSession, clientSession);
event.user(userSession.getUser())
.detail(Details.USERNAME, username)
Expand Down
Expand Up @@ -15,5 +15,4 @@ public interface Authenticator extends Provider {
boolean configuredFor(KeycloakSession session, RealmModel realm, UserModel user);
String getRequiredAction();


}
Expand Up @@ -24,6 +24,7 @@
* @version $Revision: 1 $
*/
public class LoginFormUsernameAuthenticator extends AbstractFormAuthenticator implements Authenticator {
public static final String FORM_USERNAME = "FORM_USERNAME";
protected AuthenticatorModel model;

public LoginFormUsernameAuthenticator(AuthenticatorModel model) {
Expand All @@ -35,9 +36,14 @@ public void authenticate(AuthenticatorContext context) {
if (!isActionUrl(context)) {
MultivaluedMap<String, String> formData = new MultivaluedMapImpl<>();
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) {
formData.add(AuthenticationManager.FORM_USERNAME, loginHint);
} else {
formData.add(AuthenticationManager.FORM_USERNAME, rememberMeUsername);
formData.add("rememberMe", "on");
}
}
Expand Down Expand Up @@ -83,9 +89,15 @@ public void validateUser(AuthenticatorContext context, MultivaluedMap<String, St
return;
}
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);
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.success();
}
Expand Down
Expand Up @@ -216,8 +216,9 @@ public void changePassword() {
changePasswordPage.open();
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");

Assert.assertEquals("Please specify password.", profilePage.getError());
Expand All @@ -241,7 +242,7 @@ public void changePassword() {

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.login("test-user@localhost", "new-password");
Expand Down
Expand Up @@ -26,11 +26,15 @@
import org.junit.ClassRule;
import org.junit.Rule;
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.Event;
import org.keycloak.events.EventType;
import org.keycloak.models.AuthenticationExecutionModel;
import org.keycloak.models.RealmModel;
import org.keycloak.models.UserModel;
import org.keycloak.models.utils.DefaultAuthenticationFlows;
import org.keycloak.services.managers.RealmManager;
import org.keycloak.testsuite.AssertEvents;
import org.keycloak.testsuite.MailUtil;
Expand All @@ -46,6 +50,7 @@
import org.keycloak.testsuite.rule.KeycloakRule.KeycloakSetup;
import org.keycloak.testsuite.rule.WebResource;
import org.keycloak.testsuite.rule.WebRule;
import org.keycloak.testsuite.utils.CredentialHelper;
import org.openqa.selenium.WebDriver;

import javax.mail.MessagingException;
Expand Down
Expand Up @@ -144,28 +144,4 @@ public void loginWithTotpInvalidPassword() throws Exception {

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);
}
}

}
Expand Up @@ -123,7 +123,7 @@ public void logoutMultipleSessions() throws IOException {

// Check session 1 logged-in
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
driver.navigate().to(oauth.getLogoutUrl(AppPage.baseUrl, null));
Expand All @@ -140,7 +140,7 @@ public void logoutMultipleSessions() throws IOException {

// Check session 3 logged-in
oauth.openLoginForm();
events.expectLogin().session(sessionId3).detail(Details.AUTH_METHOD, "sso").removeDetail(Details.USERNAME).assertEvent();
events.expectLogin().session(sessionId3).removeDetail(Details.USERNAME).assertEvent();
}

}
Expand Up @@ -34,6 +34,14 @@ public static void setRequiredCredential(String type, RealmModel realm) {
}
}

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) {
AuthenticationExecutionModel.Requirement requirement = AuthenticationExecutionModel.Requirement.REQUIRED;
authenticationRequirement(realm, authenticatorProviderId, flowAlias, requirement);
Expand Down

0 comments on commit 021b01f

Please sign in to comment.