From 021b01f0bdf7b4a31e79ff1aec76cb66816ed874 Mon Sep 17 00:00:00 2001 From: Bill Burke Date: Fri, 5 Jun 2015 13:49:24 -0400 Subject: [PATCH] passing tests --- .../models/AuthenticationExecutionModel.java | 10 ++++++++ .../AuthenticationProcessor.java | 23 +++++++++++++++--- .../authentication/Authenticator.java | 1 - .../LoginFormUsernameAuthenticator.java | 18 +++++++++++--- .../testsuite/account/AccountTest.java | 7 +++--- .../RequiredActionEmailVerificationTest.java | 5 ++++ .../testsuite/forms/LoginTotpTest.java | 24 ------------------- .../keycloak/testsuite/forms/LogoutTest.java | 4 ++-- .../testsuite/utils/CredentialHelper.java | 8 +++++++ 9 files changed, 64 insertions(+), 36 deletions(-) diff --git a/model/api/src/main/java/org/keycloak/models/AuthenticationExecutionModel.java b/model/api/src/main/java/org/keycloak/models/AuthenticationExecutionModel.java index 79a2b67e77..12cb4fde09 100755 --- a/model/api/src/main/java/org/keycloak/models/AuthenticationExecutionModel.java +++ b/model/api/src/main/java/org/keycloak/models/AuthenticationExecutionModel.java @@ -1,10 +1,20 @@ package org.keycloak.models; +import java.util.Comparator; + /** * @author Bill Burke * @version $Revision: 1 $ */ public class AuthenticationExecutionModel { + public static class ExecutionComparator implements Comparator { + 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; diff --git a/services/src/main/java/org/keycloak/authentication/AuthenticationProcessor.java b/services/src/main/java/org/keycloak/authentication/AuthenticationProcessor.java index bf97994876..2bc6e611d7 100755 --- a/services/src/main/java/org/keycloak/authentication/AuthenticationProcessor.java +++ b/services/src/main/java/org/keycloak/authentication/AuthenticationProcessor.java @@ -20,6 +20,7 @@ import org.keycloak.services.managers.BruteForceProtector; import javax.ws.rs.core.Response; import javax.ws.rs.core.UriInfo; +import java.util.Collections; import java.util.List; /** @@ -332,6 +333,7 @@ public class AuthenticationProcessor { } public Response authenticate() throws AuthException { + logger.info("AUTHENTICATE"); event.event(EventType.LOGIN); event.client(clientSession.getClient().getClientId()) .detail(Details.REDIRECT_URI, clientSession.getRedirectUri()) @@ -381,7 +383,7 @@ public class AuthenticationProcessor { 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); } @@ -393,11 +395,13 @@ public class AuthenticationProcessor { } List 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; } @@ -421,6 +425,7 @@ public class AuthenticationProcessor { 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){ @@ -428,7 +433,7 @@ public class AuthenticationProcessor { 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) { @@ -436,6 +441,7 @@ public class AuthenticationProcessor { 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)) { @@ -455,15 +461,18 @@ public class AuthenticationProcessor { 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; @@ -476,16 +485,19 @@ public class AuthenticationProcessor { } 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); } @@ -508,10 +520,15 @@ public class AuthenticationProcessor { 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) diff --git a/services/src/main/java/org/keycloak/authentication/Authenticator.java b/services/src/main/java/org/keycloak/authentication/Authenticator.java index ee9d43500b..63abd766b3 100755 --- a/services/src/main/java/org/keycloak/authentication/Authenticator.java +++ b/services/src/main/java/org/keycloak/authentication/Authenticator.java @@ -15,5 +15,4 @@ public interface Authenticator extends Provider { boolean configuredFor(KeycloakSession session, RealmModel realm, UserModel user); String getRequiredAction(); - } diff --git a/services/src/main/java/org/keycloak/authentication/authenticators/LoginFormUsernameAuthenticator.java b/services/src/main/java/org/keycloak/authentication/authenticators/LoginFormUsernameAuthenticator.java index 14ae6a1661..6550d138cf 100755 --- a/services/src/main/java/org/keycloak/authentication/authenticators/LoginFormUsernameAuthenticator.java +++ b/services/src/main/java/org/keycloak/authentication/authenticators/LoginFormUsernameAuthenticator.java @@ -24,6 +24,7 @@ import javax.ws.rs.core.Response; * @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) { @@ -35,9 +36,14 @@ public class LoginFormUsernameAuthenticator extends AbstractFormAuthenticator im if (!isActionUrl(context)) { MultivaluedMap 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"); } } @@ -83,9 +89,15 @@ public class LoginFormUsernameAuthenticator extends AbstractFormAuthenticator im 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(); } diff --git a/testsuite/integration/src/test/java/org/keycloak/testsuite/account/AccountTest.java b/testsuite/integration/src/test/java/org/keycloak/testsuite/account/AccountTest.java index 5bfabae083..6585f2dfc4 100755 --- a/testsuite/integration/src/test/java/org/keycloak/testsuite/account/AccountTest.java +++ b/testsuite/integration/src/test/java/org/keycloak/testsuite/account/AccountTest.java @@ -216,8 +216,9 @@ public class AccountTest { 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()); @@ -241,7 +242,7 @@ public class AccountTest { 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"); diff --git a/testsuite/integration/src/test/java/org/keycloak/testsuite/actions/RequiredActionEmailVerificationTest.java b/testsuite/integration/src/test/java/org/keycloak/testsuite/actions/RequiredActionEmailVerificationTest.java index 0d6b4cfefc..b814f628fe 100755 --- a/testsuite/integration/src/test/java/org/keycloak/testsuite/actions/RequiredActionEmailVerificationTest.java +++ b/testsuite/integration/src/test/java/org/keycloak/testsuite/actions/RequiredActionEmailVerificationTest.java @@ -26,11 +26,15 @@ import org.junit.Before; 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; @@ -46,6 +50,7 @@ import org.keycloak.testsuite.rule.KeycloakRule; 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; diff --git a/testsuite/integration/src/test/java/org/keycloak/testsuite/forms/LoginTotpTest.java b/testsuite/integration/src/test/java/org/keycloak/testsuite/forms/LoginTotpTest.java index e13fa0e818..4a86f78f10 100755 --- a/testsuite/integration/src/test/java/org/keycloak/testsuite/forms/LoginTotpTest.java +++ b/testsuite/integration/src/test/java/org/keycloak/testsuite/forms/LoginTotpTest.java @@ -144,28 +144,4 @@ public class LoginTotpTest { 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); - } - } - } diff --git a/testsuite/integration/src/test/java/org/keycloak/testsuite/forms/LogoutTest.java b/testsuite/integration/src/test/java/org/keycloak/testsuite/forms/LogoutTest.java index 561fcd6815..e026c69556 100755 --- a/testsuite/integration/src/test/java/org/keycloak/testsuite/forms/LogoutTest.java +++ b/testsuite/integration/src/test/java/org/keycloak/testsuite/forms/LogoutTest.java @@ -123,7 +123,7 @@ public class LogoutTest { // 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)); @@ -140,7 +140,7 @@ public class LogoutTest { // 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(); } } diff --git a/testsuite/integration/src/test/java/org/keycloak/testsuite/utils/CredentialHelper.java b/testsuite/integration/src/test/java/org/keycloak/testsuite/utils/CredentialHelper.java index 3018fac287..a88d8b1cd6 100755 --- a/testsuite/integration/src/test/java/org/keycloak/testsuite/utils/CredentialHelper.java +++ b/testsuite/integration/src/test/java/org/keycloak/testsuite/utils/CredentialHelper.java @@ -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) { AuthenticationExecutionModel.Requirement requirement = AuthenticationExecutionModel.Requirement.REQUIRED; authenticationRequirement(realm, authenticatorProviderId, flowAlias, requirement);