keycloak-aplcache
Changes
forms/login-freemarker/src/main/java/org/keycloak/login/freemarker/FreeMarkerLoginFormsProvider.java 11(+8 -3)
testsuite/integration/src/test/java/org/keycloak/testsuite/actions/RequiredActionEmailVerificationTest.java 44(+44 -0)
Details
diff --git a/forms/common-themes/src/main/resources/theme/login/base/info.ftl b/forms/common-themes/src/main/resources/theme/login/base/info.ftl
new file mode 100755
index 0000000..d303e5b
--- /dev/null
+++ b/forms/common-themes/src/main/resources/theme/login/base/info.ftl
@@ -0,0 +1,15 @@
+<#import "template.ftl" as layout>
+<@layout.registrationLayout displayMessage=false; section>
+ <#if section = "title">
+ ${message.summary}
+ <#elseif section = "header">
+ ${message.summary}
+ <#elseif section = "form">
+ <div id="kc-info-message">
+ <p class="instruction">${message.summary}</p>
+ <#if client.baseUrl??>
+ <p><a href="${client.baseUrl}">${rb.backToApplication}</a></p>
+ </#if>
+ </div>
+ </#if>
+</@layout.registrationLayout>
\ No newline at end of file
diff --git a/forms/common-themes/src/main/resources/theme/login/base/messages/messages.properties b/forms/common-themes/src/main/resources/theme/login/base/messages/messages.properties
index 3a1c041..6899731 100755
--- a/forms/common-themes/src/main/resources/theme/login/base/messages/messages.properties
+++ b/forms/common-themes/src/main/resources/theme/login/base/messages/messages.properties
@@ -20,6 +20,7 @@ rememberMe=Remember me
passwordConfirm=Confirm password
passwordNew=New Password
passwordNewConfirm=New Password confirmation
+passwordUpdated=Password updated
cancel=Cancel
accept=Accept
submit=Submit
@@ -85,6 +86,7 @@ emailVerifyInstr=An email with instructions to verify your email address has bee
emailVerifyInstrQ=Haven't received a verification code in your email?
emailVerifyClick=Click here
emailVerifyResend=to re-send the email.
+emailVerified=Email verified
error=A system error has occured, contact admin
errorTitle=We're sorry...
@@ -106,6 +108,7 @@ errorHeader=Error!
emailForgotHeader=Forgot Your Password?
backToLogin=« Back to Login
+backToApplication=« Back to Application
emailUpdateHeader=Update password
emailSent=You should receive an email shortly with further instructions.
emailSendError=Failed to send email, please try again later
diff --git a/forms/login-api/src/main/java/org/keycloak/login/LoginFormsPages.java b/forms/login-api/src/main/java/org/keycloak/login/LoginFormsPages.java
index 2b0cd23..e34b0ef 100644
--- a/forms/login-api/src/main/java/org/keycloak/login/LoginFormsPages.java
+++ b/forms/login-api/src/main/java/org/keycloak/login/LoginFormsPages.java
@@ -5,6 +5,6 @@ package org.keycloak.login;
*/
public enum LoginFormsPages {
- LOGIN, LOGIN_TOTP, LOGIN_CONFIG_TOTP, LOGIN_VERIFY_EMAIL, OAUTH_GRANT, LOGIN_RESET_PASSWORD, LOGIN_UPDATE_PASSWORD, REGISTER, ERROR, LOGIN_UPDATE_PROFILE, CODE;
+ LOGIN, LOGIN_TOTP, LOGIN_CONFIG_TOTP, LOGIN_VERIFY_EMAIL, OAUTH_GRANT, LOGIN_RESET_PASSWORD, LOGIN_UPDATE_PASSWORD, REGISTER, INFO, ERROR, LOGIN_UPDATE_PROFILE, CODE;
}
diff --git a/forms/login-api/src/main/java/org/keycloak/login/LoginFormsProvider.java b/forms/login-api/src/main/java/org/keycloak/login/LoginFormsProvider.java
index 45f3adb..f9a9c48 100755
--- a/forms/login-api/src/main/java/org/keycloak/login/LoginFormsProvider.java
+++ b/forms/login-api/src/main/java/org/keycloak/login/LoginFormsProvider.java
@@ -31,6 +31,8 @@ public interface LoginFormsProvider extends Provider {
public Response createRegistration();
+ public Response createInfoPage();
+
public Response createErrorPage();
public Response createOAuthGrant();
diff --git a/forms/login-freemarker/src/main/java/org/keycloak/login/freemarker/FreeMarkerLoginFormsProvider.java b/forms/login-freemarker/src/main/java/org/keycloak/login/freemarker/FreeMarkerLoginFormsProvider.java
index 44a7585..f8b85c6 100755
--- a/forms/login-freemarker/src/main/java/org/keycloak/login/freemarker/FreeMarkerLoginFormsProvider.java
+++ b/forms/login-freemarker/src/main/java/org/keycloak/login/freemarker/FreeMarkerLoginFormsProvider.java
@@ -29,6 +29,7 @@ import org.keycloak.models.RealmModel;
import org.keycloak.models.RoleModel;
import org.keycloak.models.UserModel;
import org.keycloak.services.messages.Messages;
+import org.keycloak.services.resources.LoginActionsService;
import org.keycloak.services.resources.flows.Urls;
import javax.ws.rs.core.MediaType;
@@ -51,7 +52,8 @@ public class FreeMarkerLoginFormsProvider implements LoginFormsProvider {
private static final Logger logger = Logger.getLogger(FreeMarkerLoginFormsProvider.class);
- private String message;
+ public static enum MessageType {SUCCESS, WARNING, ERROR}
+
private String accessCode;
private Response.Status status;
private List<RoleModel> realmRolesRequested;
@@ -61,8 +63,7 @@ public class FreeMarkerLoginFormsProvider implements LoginFormsProvider {
private String accessRequestMessage;
private URI actionUri;
- public static enum MessageType {SUCCESS, WARNING, ERROR}
-
+ private String message;
private MessageType messageType = MessageType.ERROR;
private MultivaluedMap<String, String> formData;
@@ -253,6 +254,10 @@ public class FreeMarkerLoginFormsProvider implements LoginFormsProvider {
return createResponse(LoginFormsPages.REGISTER);
}
+ public Response createInfoPage() {
+ return createResponse(LoginFormsPages.INFO);
+ }
+
public Response createErrorPage() {
if (status == null) {
status = Response.Status.INTERNAL_SERVER_ERROR;
diff --git a/forms/login-freemarker/src/main/java/org/keycloak/login/freemarker/model/ClientBean.java b/forms/login-freemarker/src/main/java/org/keycloak/login/freemarker/model/ClientBean.java
index 4507762..fcd9e11 100755
--- a/forms/login-freemarker/src/main/java/org/keycloak/login/freemarker/model/ClientBean.java
+++ b/forms/login-freemarker/src/main/java/org/keycloak/login/freemarker/model/ClientBean.java
@@ -26,4 +26,12 @@ public class ClientBean {
public String getClientId() {
return client.getClientId();
}
+
+ public String getBaseUrl() {
+ if (client instanceof ApplicationModel) {
+ return ((ApplicationModel) client).getBaseUrl();
+ }
+ return null;
+ }
+
}
diff --git a/forms/login-freemarker/src/main/java/org/keycloak/login/freemarker/Templates.java b/forms/login-freemarker/src/main/java/org/keycloak/login/freemarker/Templates.java
index 02e20ec..785b186 100644
--- a/forms/login-freemarker/src/main/java/org/keycloak/login/freemarker/Templates.java
+++ b/forms/login-freemarker/src/main/java/org/keycloak/login/freemarker/Templates.java
@@ -25,6 +25,8 @@ public class Templates {
return "login-update-password.ftl";
case REGISTER:
return "register.ftl";
+ case INFO:
+ return "info.ftl";
case ERROR:
return "error.ftl";
case LOGIN_UPDATE_PROFILE:
diff --git a/services/src/main/java/org/keycloak/services/managers/AuthenticationManager.java b/services/src/main/java/org/keycloak/services/managers/AuthenticationManager.java
index d02ad2b..25fce1f 100755
--- a/services/src/main/java/org/keycloak/services/managers/AuthenticationManager.java
+++ b/services/src/main/java/org/keycloak/services/managers/AuthenticationManager.java
@@ -26,6 +26,7 @@ import org.keycloak.protocol.LoginProtocol;
import org.keycloak.protocol.oidc.TokenManager;
import org.keycloak.representations.AccessToken;
import org.keycloak.representations.idm.CredentialRepresentation;
+import org.keycloak.services.resources.LoginActionsService;
import org.keycloak.services.resources.RealmsResource;
import org.keycloak.services.resources.flows.Flows;
import org.keycloak.services.util.CookieHelper;
@@ -366,6 +367,7 @@ public class AuthenticationManager {
LoginFormsProvider loginFormsProvider = Flows.forms(session, realm, client, uriInfo).setClientSessionCode(accessCode.getCode()).setUser(user);
if (action.equals(UserModel.RequiredAction.VERIFY_EMAIL)) {
event.clone().event(EventType.SEND_VERIFY_EMAIL).detail(Details.EMAIL, user.getEmail()).success();
+ LoginActionsService.createActionCookie(realm, uriInfo, clientConnection, userSession.getId());
}
return loginFormsProvider
diff --git a/services/src/main/java/org/keycloak/services/resources/LoginActionsService.java b/services/src/main/java/org/keycloak/services/resources/LoginActionsService.java
index 3590f71..8a3abec 100755
--- a/services/src/main/java/org/keycloak/services/resources/LoginActionsService.java
+++ b/services/src/main/java/org/keycloak/services/resources/LoginActionsService.java
@@ -53,6 +53,7 @@ import org.keycloak.services.managers.ClientSessionCode;
import org.keycloak.services.messages.Messages;
import org.keycloak.services.resources.flows.Flows;
import org.keycloak.services.resources.flows.Urls;
+import org.keycloak.services.util.CookieHelper;
import org.keycloak.services.validation.Validation;
import javax.ws.rs.Consumes;
@@ -61,6 +62,7 @@ import javax.ws.rs.POST;
import javax.ws.rs.Path;
import javax.ws.rs.QueryParam;
import javax.ws.rs.core.Context;
+import javax.ws.rs.core.Cookie;
import javax.ws.rs.core.HttpHeaders;
import javax.ws.rs.core.MediaType;
import javax.ws.rs.core.MultivaluedMap;
@@ -79,6 +81,8 @@ public class LoginActionsService {
protected static final Logger logger = Logger.getLogger(LoginActionsService.class);
+ public static final String ACTION_COOKIE = "KEYCLOAK_ACTION";
+
private RealmModel realm;
@Context
@@ -163,6 +167,18 @@ public class LoginActionsService {
}
}
+ boolean check(String code, ClientSessionModel.Action requiredAction, ClientSessionModel.Action alternativeRequiredAction) {
+ if (!check(code)) {
+ return false;
+ } else if (!(clientCode.isValid(requiredAction) || clientCode.isValid(alternativeRequiredAction))) {
+ event.error(Errors.INVALID_CODE);
+ response = Flows.forwardToSecurityFailurePage(session, realm, uriInfo, "Invalid code, please login again through your application.");
+ return false;
+ } else {
+ return true;
+ }
+ }
+
public boolean check(String code) {
if (!checkSsl()) {
event.error(Errors.SSL_REQUIRED);
@@ -690,7 +706,7 @@ public class LoginActionsService {
final MultivaluedMap<String, String> formData) {
event.event(EventType.UPDATE_PASSWORD);
Checks checks = new Checks();
- if (!checks.check(code, ClientSessionModel.Action.UPDATE_PASSWORD)) {
+ if (!checks.check(code, ClientSessionModel.Action.UPDATE_PASSWORD, ClientSessionModel.Action.RECOVER_PASSWORD)) {
return checks.response;
}
ClientSessionCode accessCode = checks.clientCode;
@@ -724,7 +740,17 @@ public class LoginActionsService {
user.removeRequiredAction(RequiredAction.UPDATE_PASSWORD);
- event.clone().event(EventType.UPDATE_PASSWORD).success();
+ event.event(EventType.UPDATE_PASSWORD).success();
+
+ if (clientSession.getAction().equals(ClientSessionModel.Action.RECOVER_PASSWORD)) {
+ String actionCookieValue = getActionCookie();
+ if (actionCookieValue == null || !actionCookieValue.equals(userSession.getId())) {
+ return Flows.forms(session, realm, clientSession.getClient(), uriInfo).setSuccess("passwordUpdated").createInfoPage();
+ }
+ }
+
+ event = event.clone().event(EventType.LOGIN);
+
return redirectOauth(user, accessCode, clientSession, userSession);
}
@@ -747,7 +773,14 @@ public class LoginActionsService {
user.removeRequiredAction(RequiredAction.VERIFY_EMAIL);
- event.clone().event(EventType.VERIFY_EMAIL).detail(Details.EMAIL, user.getEmail()).success();
+ event.event(EventType.VERIFY_EMAIL).detail(Details.EMAIL, user.getEmail()).success();
+
+ String actionCookieValue = getActionCookie();
+ if (actionCookieValue == null || !actionCookieValue.equals(userSession.getId())) {
+ return Flows.forms(session, realm, clientSession.getClient(), uriInfo).setSuccess("emailVerified").createInfoPage();
+ }
+
+ event = event.clone().removeDetail(Details.EMAIL).event(EventType.LOGIN);
return redirectOauth(user, accessCode, clientSession, userSession);
} else {
@@ -760,6 +793,8 @@ public class LoginActionsService {
UserSessionModel userSession = clientSession.getUserSession();
initEvent(clientSession);
+ createActionCookie(realm, uriInfo, clientConnection, userSession.getId());
+
return Flows.forms(session, realm, null, uriInfo)
.setClientSessionCode(accessCode.getCode())
.setUser(userSession.getUser())
@@ -777,7 +812,6 @@ public class LoginActionsService {
return checks.response;
}
ClientSessionCode accessCode = checks.clientCode;
- accessCode.setRequiredAction(RequiredAction.UPDATE_PASSWORD);
return Flows.forms(session, realm, null, uriInfo)
.setClientSessionCode(accessCode.getCode())
.createResponse(RequiredAction.UPDATE_PASSWORD);
@@ -864,11 +898,23 @@ public class LoginActionsService {
.setClientSessionCode(accessCode.getCode())
.createErrorPage();
}
+
+ createActionCookie(realm, uriInfo, clientConnection, userSession.getId());
}
return Flows.forms(session, realm, client, uriInfo).setSuccess("emailSent").setClientSessionCode(accessCode.getCode()).createPasswordReset();
}
+ private String getActionCookie() {
+ Cookie cookie = headers.getCookies().get(ACTION_COOKIE);
+ AuthenticationManager.expireCookie(realm, ACTION_COOKIE, AuthenticationManager.getRealmCookiePath(realm, uriInfo), realm.getSslRequired().isRequired(clientConnection), clientConnection);
+ return cookie != null ? cookie.getValue() : null;
+ }
+
+ public static void createActionCookie(RealmModel realm, UriInfo uriInfo, ClientConnection clientConnection, String sessionId) {
+ CookieHelper.addCookie(ACTION_COOKIE, sessionId, AuthenticationManager.getRealmCookiePath(realm, uriInfo), null, null, -1, realm.getSslRequired().isRequired(clientConnection), true);
+ }
+
private Response redirectOauth(UserModel user, ClientSessionCode accessCode, ClientSessionModel clientSession, UserSessionModel userSession) {
return AuthenticationManager.nextActionAfterAuthentication(session, userSession, clientSession, clientConnection, request, uriInfo, event);
}
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 0f2b13f..1d66651 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
@@ -37,6 +37,7 @@ import org.keycloak.testsuite.MailUtil;
import org.keycloak.testsuite.OAuthClient;
import org.keycloak.testsuite.pages.AppPage;
import org.keycloak.testsuite.pages.AppPage.RequestType;
+import org.keycloak.testsuite.pages.InfoPage;
import org.keycloak.testsuite.pages.LoginPage;
import org.keycloak.testsuite.pages.RegisterPage;
import org.keycloak.testsuite.pages.VerifyEmailPage;
@@ -51,6 +52,9 @@ import javax.mail.MessagingException;
import javax.mail.internet.MimeMessage;
import java.io.IOException;
+import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertTrue;
+
/**
* @author <a href="mailto:sthorger@redhat.com">Stian Thorgersen</a>
*/
@@ -86,6 +90,9 @@ public class RequiredActionEmailVerificationTest {
@WebResource
protected RegisterPage registerPage;
+ @WebResource
+ protected InfoPage infoPage;
+
@Before
public void before() {
oauth.state("mystate"); // have to set this as keycloak validates that state is sent
@@ -200,4 +207,41 @@ public class RequiredActionEmailVerificationTest {
events.expectLogin().session(sessionId).assertEvent();
}
+ @Test
+ public void verifyEmailNewBrowserSession() throws IOException, MessagingException {
+ loginPage.open();
+ loginPage.login("test-user@localhost", "password");
+
+ Assert.assertTrue(verifyEmailPage.isCurrent());
+
+ Assert.assertEquals(1, greenMail.getReceivedMessages().length);
+
+ MimeMessage message = greenMail.getReceivedMessages()[0];
+
+ String body = (String) message.getContent();
+ String verificationUrl = MailUtil.getLink(body);
+
+ AssertEvents.ExpectedEvent emailEvent = events.expectRequiredAction(EventType.SEND_VERIFY_EMAIL).detail("email", "test-user@localhost");
+ Event sendEvent = emailEvent.assertEvent();
+ String sessionId = sendEvent.getSessionId();
+
+ String mailCodeId = sendEvent.getDetails().get(Details.CODE_ID);
+
+ Assert.assertEquals(mailCodeId, verificationUrl.split("key=")[1].split("\\.")[1]);
+
+ driver.manage().deleteAllCookies();
+
+ driver.navigate().to(verificationUrl.trim());
+
+ events.expectRequiredAction(EventType.VERIFY_EMAIL).session(sessionId).detail("email", "test-user@localhost").detail(Details.CODE_ID, mailCodeId).assertEvent();
+
+ assertTrue(infoPage.isCurrent());
+ assertEquals("Email verified", infoPage.getInfo());
+
+ loginPage.open();
+
+ assertTrue(loginPage.isCurrent());
+ }
+
+
}
diff --git a/testsuite/integration/src/test/java/org/keycloak/testsuite/forms/ResetPasswordTest.java b/testsuite/integration/src/test/java/org/keycloak/testsuite/forms/ResetPasswordTest.java
index 20926f4..7582e20 100755
--- a/testsuite/integration/src/test/java/org/keycloak/testsuite/forms/ResetPasswordTest.java
+++ b/testsuite/integration/src/test/java/org/keycloak/testsuite/forms/ResetPasswordTest.java
@@ -21,7 +21,6 @@
*/
package org.keycloak.testsuite.forms;
-import org.junit.Assert;
import org.junit.ClassRule;
import org.junit.Rule;
import org.junit.Test;
@@ -41,6 +40,7 @@ import org.keycloak.testsuite.OAuthClient;
import org.keycloak.testsuite.pages.AppPage;
import org.keycloak.testsuite.pages.AppPage.RequestType;
import org.keycloak.testsuite.pages.ErrorPage;
+import org.keycloak.testsuite.pages.InfoPage;
import org.keycloak.testsuite.pages.LoginPage;
import org.keycloak.testsuite.pages.LoginPasswordResetPage;
import org.keycloak.testsuite.pages.LoginPasswordUpdatePage;
@@ -56,6 +56,9 @@ import javax.mail.internet.MimeMessage;
import java.io.IOException;
import java.util.Collections;
+import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertTrue;
+
/**
* @author <a href="mailto:sthorger@redhat.com">Stian Thorgersen</a>
*/
@@ -104,6 +107,9 @@ public class ResetPasswordTest {
protected ErrorPage errorPage;
@WebResource
+ protected InfoPage infoPage;
+
+ @WebResource
protected LoginPasswordResetPage resetPasswordPage;
@WebResource
@@ -132,13 +138,13 @@ public class ResetPasswordTest {
resetPasswordPage.backToLogin();
- Assert.assertTrue(loginPage.isCurrent());
+ assertTrue(loginPage.isCurrent());
loginPage.login("login-test", "password");
events.expectLogin().user(userId).detail(Details.USERNAME, "login-test").assertEvent();
- Assert.assertEquals(1, greenMail.getReceivedMessages().length);
+ assertEquals(1, greenMail.getReceivedMessages().length);
MimeMessage message = greenMail.getReceivedMessages()[0];
@@ -149,8 +155,8 @@ public class ResetPasswordTest {
events.expect(EventType.RESET_PASSWORD_ERROR).client((String) null).user((String) null).error("invalid_code").clearDetails().assertEvent();
- Assert.assertTrue(errorPage.isCurrent());
- Assert.assertEquals("Unknown code, please login again through your application.", errorPage.getError());
+ assertTrue(errorPage.isCurrent());
+ assertEquals("Unknown code, please login again through your application.", errorPage.getError());
}
@Test
@@ -168,7 +174,7 @@ public class ResetPasswordTest {
resetPasswordPage.backToLogin();
- Assert.assertTrue(loginPage.isCurrent());
+ assertTrue(loginPage.isCurrent());
loginPage.login("login@test.com", "password");
@@ -177,8 +183,8 @@ public class ResetPasswordTest {
String code = oauth.getCurrentQuery().get("code");
OAuthClient.AccessTokenResponse tokenResponse = oauth.doAccessTokenRequest(code, "password");
- Assert.assertEquals(200, tokenResponse.getStatusCode());
- Assert.assertEquals(userId, oauth.verifyToken(tokenResponse.getAccessToken()).getSubject());
+ assertEquals(200, tokenResponse.getStatusCode());
+ assertEquals(userId, oauth.verifyToken(tokenResponse.getAccessToken()).getSubject());
events.expectCodeToToken(loginEvent.getDetails().get(Details.CODE_ID), loginEvent.getSessionId()).user(userId).assertEvent();
}
@@ -200,9 +206,9 @@ public class ResetPasswordTest {
String sessionId = events.expectRequiredAction(EventType.SEND_RESET_PASSWORD).user(userId).detail(Details.USERNAME, username).detail(Details.EMAIL, "login@test.com").assertEvent().getSessionId();
- Assert.assertEquals("You should receive an email shortly with further instructions.", resetPasswordPage.getSuccessMessage());
+ assertEquals("You should receive an email shortly with further instructions.", resetPasswordPage.getSuccessMessage());
- Assert.assertEquals(1, greenMail.getReceivedMessages().length);
+ assertEquals(1, greenMail.getReceivedMessages().length);
MimeMessage message = greenMail.getReceivedMessages()[0];
@@ -217,7 +223,7 @@ public class ResetPasswordTest {
events.expectRequiredAction(EventType.UPDATE_PASSWORD).user(userId).session(sessionId).detail(Details.USERNAME, username).assertEvent();
- Assert.assertEquals(RequestType.AUTH_RESPONSE, appPage.getRequestType());
+ assertEquals(RequestType.AUTH_RESPONSE, appPage.getRequestType());
events.expectLogin().user(userId).detail(Details.USERNAME, username).session(sessionId).assertEvent();
@@ -231,7 +237,7 @@ public class ResetPasswordTest {
events.expectLogin().user(userId).detail(Details.USERNAME, "login-test").assertEvent();
- Assert.assertEquals(RequestType.AUTH_RESPONSE, appPage.getRequestType());
+ assertEquals(RequestType.AUTH_RESPONSE, appPage.getRequestType());
}
@Test
@@ -245,11 +251,11 @@ public class ResetPasswordTest {
resetPasswordPage.assertCurrent();
- Assert.assertEquals("You should receive an email shortly with further instructions.", resetPasswordPage.getSuccessMessage());
+ assertEquals("You should receive an email shortly with further instructions.", resetPasswordPage.getSuccessMessage());
Thread.sleep(1000);
- Assert.assertEquals(0, greenMail.getReceivedMessages().length);
+ assertEquals(0, greenMail.getReceivedMessages().length);
events.expectRequiredAction(EventType.SEND_RESET_PASSWORD).user((String) null).session((String) null).detail(Details.USERNAME, "invalid").removeDetail(Details.EMAIL).removeDetail(Details.CODE_ID).error("user_not_found").assertEvent();
}
@@ -268,9 +274,9 @@ public class ResetPasswordTest {
String sessionId = events.expectRequiredAction(EventType.SEND_RESET_PASSWORD).user(userId).detail(Details.USERNAME, "login-test").detail(Details.EMAIL, "login@test.com").assertEvent().getSessionId();
- Assert.assertEquals("You should receive an email shortly with further instructions.", resetPasswordPage.getSuccessMessage());
+ assertEquals("You should receive an email shortly with further instructions.", resetPasswordPage.getSuccessMessage());
- Assert.assertEquals(1, greenMail.getReceivedMessages().length);
+ assertEquals(1, greenMail.getReceivedMessages().length);
MimeMessage message = greenMail.getReceivedMessages()[0];
@@ -283,7 +289,7 @@ public class ResetPasswordTest {
errorPage.assertCurrent();
- Assert.assertEquals("Invalid code, please login again through your application.", errorPage.getError());
+ assertEquals("Invalid code, please login again through your application.", errorPage.getError());
events.expectRequiredAction(EventType.RESET_PASSWORD).error("invalid_code").client((String) null).user((String) null).session((String) null).clearDetails().assertEvent();
} finally {
@@ -310,11 +316,11 @@ public class ResetPasswordTest {
resetPasswordPage.assertCurrent();
- Assert.assertEquals("You should receive an email shortly with further instructions.", resetPasswordPage.getSuccessMessage());
+ assertEquals("You should receive an email shortly with further instructions.", resetPasswordPage.getSuccessMessage());
Thread.sleep(1000);
- Assert.assertEquals(0, greenMail.getReceivedMessages().length);
+ assertEquals(0, greenMail.getReceivedMessages().length);
events.expectRequiredAction(EventType.SEND_RESET_PASSWORD).session((String) null).user(userId).detail(Details.USERNAME, "login-test").removeDetail(Details.CODE_ID).error("user_disabled").assertEvent();
} finally {
@@ -350,11 +356,11 @@ public class ResetPasswordTest {
resetPasswordPage.assertCurrent();
- Assert.assertEquals("You should receive an email shortly with further instructions.", resetPasswordPage.getSuccessMessage());
+ assertEquals("You should receive an email shortly with further instructions.", resetPasswordPage.getSuccessMessage());
Thread.sleep(1000);
- Assert.assertEquals(0, greenMail.getReceivedMessages().length);
+ assertEquals(0, greenMail.getReceivedMessages().length);
events.expectRequiredAction(EventType.SEND_RESET_PASSWORD_ERROR).session((String) null).user(userId).detail(Details.USERNAME, "login-test").removeDetail(Details.CODE_ID).error("invalid_email").assertEvent();
} finally {
@@ -388,11 +394,11 @@ public class ResetPasswordTest {
errorPage.assertCurrent();
- Assert.assertEquals("Failed to send email, please try again later", errorPage.getError());
+ assertEquals("Failed to send email, please try again later", errorPage.getError());
Thread.sleep(1000);
- Assert.assertEquals(0, greenMail.getReceivedMessages().length);
+ assertEquals(0, greenMail.getReceivedMessages().length);
events.expectRequiredAction(EventType.SEND_RESET_PASSWORD_ERROR).user(userId).detail(Details.USERNAME, "login-test").removeDetail(Details.CODE_ID).error(Errors.EMAIL_SEND_FAILED).assertEvent();
} finally {
@@ -423,9 +429,9 @@ public class ResetPasswordTest {
resetPasswordPage.assertCurrent();
- Assert.assertEquals("You should receive an email shortly with further instructions.", resetPasswordPage.getSuccessMessage());
+ assertEquals("You should receive an email shortly with further instructions.", resetPasswordPage.getSuccessMessage());
- Assert.assertEquals(1, greenMail.getReceivedMessages().length);
+ assertEquals(1, greenMail.getReceivedMessages().length);
MimeMessage message = greenMail.getReceivedMessages()[0];
@@ -440,13 +446,13 @@ public class ResetPasswordTest {
updatePasswordPage.changePassword("invalid", "invalid");
- Assert.assertEquals("Invalid password: minimum length 8", resetPasswordPage.getErrorMessage());
+ assertEquals("Invalid password: minimum length 8", resetPasswordPage.getErrorMessage());
updatePasswordPage.changePassword("resetPasswordWithPasswordPolicy", "resetPasswordWithPasswordPolicy");
events.expectRequiredAction(EventType.UPDATE_PASSWORD).user(userId).session(sessionId).detail(Details.USERNAME, "login-test").assertEvent();
- Assert.assertEquals(RequestType.AUTH_RESPONSE, appPage.getRequestType());
+ assertEquals(RequestType.AUTH_RESPONSE, appPage.getRequestType());
events.expectLogin().user(userId).detail(Details.USERNAME, "login-test").session(sessionId).assertEvent();
@@ -458,9 +464,51 @@ public class ResetPasswordTest {
loginPage.login("login-test", "resetPasswordWithPasswordPolicy");
- Assert.assertEquals(RequestType.AUTH_RESPONSE, appPage.getRequestType());
+ assertEquals(RequestType.AUTH_RESPONSE, appPage.getRequestType());
events.expectLogin().user(userId).detail(Details.USERNAME, "login-test").assertEvent();
}
+ @Test
+ public void resetPasswordNewBrowserSession() throws IOException, MessagingException {
+ String username = "login-test";
+
+ loginPage.open();
+ loginPage.resetPassword();
+
+ resetPasswordPage.assertCurrent();
+
+ resetPasswordPage.changePassword(username);
+
+ resetPasswordPage.assertCurrent();
+
+ String sessionId = events.expectRequiredAction(EventType.SEND_RESET_PASSWORD).user(userId).detail(Details.USERNAME, username).detail(Details.EMAIL, "login@test.com").assertEvent().getSessionId();
+
+ assertEquals("You should receive an email shortly with further instructions.", resetPasswordPage.getSuccessMessage());
+
+ assertEquals(1, greenMail.getReceivedMessages().length);
+
+ MimeMessage message = greenMail.getReceivedMessages()[0];
+
+ String body = (String) message.getContent();
+ String changePasswordUrl = MailUtil.getLink(body);
+
+ driver.manage().deleteAllCookies();
+
+ driver.navigate().to(changePasswordUrl.trim());
+
+ updatePasswordPage.assertCurrent();
+
+ updatePasswordPage.changePassword("resetPassword", "resetPassword");
+
+ events.expectRequiredAction(EventType.UPDATE_PASSWORD).user(userId).session(sessionId).detail(Details.USERNAME, username).assertEvent();
+
+ assertTrue(infoPage.isCurrent());
+ assertEquals("Password updated", infoPage.getInfo());
+
+ loginPage.open();
+
+ assertTrue(loginPage.isCurrent());
+ }
+
}
diff --git a/testsuite/integration/src/test/java/org/keycloak/testsuite/pages/InfoPage.java b/testsuite/integration/src/test/java/org/keycloak/testsuite/pages/InfoPage.java
new file mode 100644
index 0000000..516d625
--- /dev/null
+++ b/testsuite/integration/src/test/java/org/keycloak/testsuite/pages/InfoPage.java
@@ -0,0 +1,53 @@
+/*
+ * JBoss, Home of Professional Open Source.
+ * Copyright 2012, Red Hat, Inc., and individual contributors
+ * as indicated by the @author tags. See the copyright.txt file in the
+ * distribution for a full listing of individual contributors.
+ *
+ * This is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU Lesser General Public License as
+ * published by the Free Software Foundation; either version 2.1 of
+ * the License, or (at your option) any later version.
+ *
+ * This software is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this software; if not, write to the Free
+ * Software Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA
+ * 02110-1301 USA, or see the FSF site: http://www.fsf.org.
+ */
+package org.keycloak.testsuite.pages;
+
+import org.keycloak.testsuite.OAuthClient;
+import org.keycloak.testsuite.rule.WebResource;
+import org.openqa.selenium.WebElement;
+import org.openqa.selenium.support.FindBy;
+
+/**
+ * @author <a href="mailto:sthorger@redhat.com">Stian Thorgersen</a>
+ */
+public class InfoPage extends AbstractPage {
+
+ @WebResource
+ protected OAuthClient oauth;
+
+ @FindBy(className = "instruction")
+ private WebElement infoMessage;
+
+ public String getInfo() {
+ return infoMessage.getText();
+ }
+
+ public boolean isCurrent() {
+ return driver.getPageSource().contains("kc-info-message");
+ }
+
+ @Override
+ public void open() {
+ throw new UnsupportedOperationException();
+ }
+
+}