keycloak-memoizeit
Changes
services/src/main/java/org/keycloak/authentication/authenticators/resetcred/ResetCredentialChooseUser.java 39(+14 -25)
services/src/main/java/org/keycloak/authentication/authenticators/resetcred/ResetCredentialEmail.java 47(+38 -9)
Details
diff --git a/forms/common-themes/src/main/resources/theme/base/login/validate-reset-email.ftl b/forms/common-themes/src/main/resources/theme/base/login/validate-reset-email.ftl
index fd5fa65..5217b04 100755
--- a/forms/common-themes/src/main/resources/theme/base/login/validate-reset-email.ftl
+++ b/forms/common-themes/src/main/resources/theme/base/login/validate-reset-email.ftl
@@ -16,14 +16,9 @@
</div>
<div class="${properties.kcFormGroupClass!}">
- <div id="kc-form-options" class="${properties.kcFormOptionsClass!}">
- <div class="${properties.kcFormOptionsWrapperClass!}">
- <span><a href="${url.loginUrl}">${msg("backToLogin")}</a></span>
- </div>
- </div>
-
<div id="kc-form-buttons" class="${properties.kcFormButtonsClass!}">
- <input class="${properties.kcButtonClass!} ${properties.kcButtonPrimaryClass!} ${properties.kcButtonLargeClass!}" type="submit" value="${msg("doSubmit")}"/>
+ <input class="${properties.kcButtonClass!} ${properties.kcButtonPrimaryClass!} ${properties.kcButtonLargeClass!}" name="login" id="kc-submit" type="submit" value="${msg("doLogIn")}"/>
+ <input class="${properties.kcButtonClass!} ${properties.kcButtonDefaultClass!} ${properties.kcButtonLargeClass!}" name="cancel" id="kc-cancel" type="submit" value="${msg("doCancel")}"/>
</div>
</div>
</form>
diff --git a/model/api/src/main/java/org/keycloak/models/ClientSessionModel.java b/model/api/src/main/java/org/keycloak/models/ClientSessionModel.java
index 105b6ba..52705a2 100755
--- a/model/api/src/main/java/org/keycloak/models/ClientSessionModel.java
+++ b/model/api/src/main/java/org/keycloak/models/ClientSessionModel.java
@@ -80,7 +80,8 @@ public interface ClientSessionModel {
RECOVER_PASSWORD,
AUTHENTICATE,
SOCIAL_CALLBACK,
- LOGGED_OUT
+ LOGGED_OUT,
+ RESET_CREDENTIALS
}
public enum ExecutionStatus {
diff --git a/services/src/main/java/org/keycloak/authentication/AuthenticationFlowContext.java b/services/src/main/java/org/keycloak/authentication/AuthenticationFlowContext.java
index 26ec7bd..f4b4431 100755
--- a/services/src/main/java/org/keycloak/authentication/AuthenticationFlowContext.java
+++ b/services/src/main/java/org/keycloak/authentication/AuthenticationFlowContext.java
@@ -224,4 +224,11 @@ public interface AuthenticationFlowContext {
*
*/
void cancelLogin();
+
+ /**
+ * Abort the current flow and restart it using the realm's browser login
+ *
+ * @return
+ */
+ void resetBrowserLogin();
}
diff --git a/services/src/main/java/org/keycloak/authentication/AuthenticationFlowError.java b/services/src/main/java/org/keycloak/authentication/AuthenticationFlowError.java
index ec60973..e348b67 100755
--- a/services/src/main/java/org/keycloak/authentication/AuthenticationFlowError.java
+++ b/services/src/main/java/org/keycloak/authentication/AuthenticationFlowError.java
@@ -16,5 +16,6 @@ public enum AuthenticationFlowError {
USER_CONFLICT,
USER_TEMPORARILY_DISABLED,
INTERNAL_ERROR,
- UNKNOWN_USER
+ UNKNOWN_USER,
+ RESET_TO_BROWSER_LOGIN
}
diff --git a/services/src/main/java/org/keycloak/authentication/AuthenticationProcessor.java b/services/src/main/java/org/keycloak/authentication/AuthenticationProcessor.java
index 9f3eab9..5caff0b 100755
--- a/services/src/main/java/org/keycloak/authentication/AuthenticationProcessor.java
+++ b/services/src/main/java/org/keycloak/authentication/AuthenticationProcessor.java
@@ -391,6 +391,11 @@ public class AuthenticationProcessor {
Response response = protocol.cancelLogin(getClientSession());
forceChallenge(response);
}
+
+ @Override
+ public void resetBrowserLogin() {
+ this.status = FlowStatus.RESET_BROWSER_LOGIN;
+ }
}
public void logFailure() {
@@ -434,6 +439,21 @@ public class AuthenticationProcessor {
event.error(Errors.EXPIRED_CODE);
return ErrorPage.error(session, Messages.EXPIRED_CODE);
+ } else if (e.getError() == AuthenticationFlowError.RESET_TO_BROWSER_LOGIN) {
+ resetFlow(getClientSession());
+ AuthenticationProcessor processor = new AuthenticationProcessor();
+ processor.setClientSession(clientSession)
+ .setFlowPath(LoginActionsService.AUTHENTICATE_PATH)
+ .setFlowId(realm.getBrowserFlow().getId())
+ .setConnection(connection)
+ .setEventBuilder(event)
+ .setProtector(protector)
+ .setRealm(realm)
+ .setSession(session)
+ .setUriInfo(uriInfo)
+ .setRequest(request);
+ return processor.authenticate();
+
} else {
event.error(Errors.INVALID_USER_CREDENTIALS);
return ErrorPage.error(session, Messages.INVALID_USER);
@@ -530,10 +550,11 @@ public class AuthenticationProcessor {
public void checkClientSession() {
ClientSessionCode code = new ClientSessionCode(realm, clientSession);
- if (!code.isValidAction(ClientSessionModel.Action.AUTHENTICATE.name())) {
+ String action = ClientSessionModel.Action.AUTHENTICATE.name();
+ if (!code.isValidAction(action)) {
throw new AuthenticationFlowException(AuthenticationFlowError.INVALID_CLIENT_SESSION);
}
- if (!code.isActionActive(ClientSessionModel.Action.AUTHENTICATE.name())) {
+ if (!code.isActionActive(action)) {
throw new AuthenticationFlowException(AuthenticationFlowError.EXPIRED_CODE);
}
clientSession.setTimestamp(Time.currentTime());
@@ -564,12 +585,16 @@ public class AuthenticationProcessor {
String username = clientSession.getAuthenticatedUser().getUsername();
String attemptedUsername = clientSession.getNote(AbstractUsernameFormAuthenticator.ATTEMPTED_USERNAME);
if (attemptedUsername != null) username = attemptedUsername;
+ String rememberMe = clientSession.getNote(Details.REMEMBER_ME);
+ boolean remember = rememberMe != null && rememberMe.equalsIgnoreCase("true");
if (userSession == null) { // if no authenticator attached a usersession
- boolean remember = "true".equals(clientSession.getNote(Details.REMEMBER_ME));
- userSession = session.sessions().createUserSession(realm, clientSession.getAuthenticatedUser(), username, connection.getRemoteAddr(), "form", remember, null, null);
+ userSession = session.sessions().createUserSession(realm, clientSession.getAuthenticatedUser(), username, connection.getRemoteAddr(), clientSession.getAuthMethod(), remember, null, null);
userSession.setState(UserSessionModel.State.LOGGING_IN);
userSessionCreated = true;
}
+ if (remember) {
+ event.detail(Details.REMEMBER_ME, "true");
+ }
TokenManager.attachClientSession(userSession, clientSession);
event.user(userSession.getUser())
.detail(Details.USERNAME, username)
@@ -598,21 +623,7 @@ 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(), 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)
- .session(userSession);
-
+ attachSession();
return AuthenticationManager.nextActionAfterAuthentication(session, userSession, clientSession, connection, request, uriInfo, event);
}
diff --git a/services/src/main/java/org/keycloak/authentication/authenticators/resetcred/ResetCredentialChooseUser.java b/services/src/main/java/org/keycloak/authentication/authenticators/resetcred/ResetCredentialChooseUser.java
index 1edd535..a624495 100755
--- a/services/src/main/java/org/keycloak/authentication/authenticators/resetcred/ResetCredentialChooseUser.java
+++ b/services/src/main/java/org/keycloak/authentication/authenticators/resetcred/ResetCredentialChooseUser.java
@@ -5,6 +5,7 @@ import org.keycloak.authentication.AuthenticationFlowContext;
import org.keycloak.authentication.AuthenticationFlowError;
import org.keycloak.authentication.Authenticator;
import org.keycloak.authentication.AuthenticatorFactory;
+import org.keycloak.authentication.authenticators.browser.AbstractUsernameFormAuthenticator;
import org.keycloak.email.EmailException;
import org.keycloak.email.EmailProvider;
import org.keycloak.events.Details;
@@ -62,34 +63,22 @@ public class ResetCredentialChooseUser implements Authenticator, AuthenticatorFa
user = context.getSession().users().getUserByEmail(username, context.getRealm());
}
- if (user == null) {
- event.error(Errors.INVALID_USER_CREDENTIALS);
- Response challenge = context.form()
- .setError(Messages.INVALID_USER)
- .createPasswordReset();
- context.failureChallenge(AuthenticationFlowError.INVALID_USER, challenge);
- return;
- }
+ context.getClientSession().setNote(AbstractUsernameFormAuthenticator.ATTEMPTED_USERNAME, username);
- if (!user.isEnabled()) {
- event.user(user).error(Errors.USER_DISABLED);
- Response challenge = context.form()
- .setError(Messages.ACCOUNT_DISABLED)
- .createPasswordReset();
- context.failureChallenge(AuthenticationFlowError.INVALID_USER, challenge);
- return;
- }
-
- if (user.getEmail() == null || user.getEmail().trim().length() == 0) {
- event.user(user).error(Errors.INVALID_EMAIL);
- Response challenge = context.form()
- .setError(Messages.INVALID_EMAIL)
- .createPasswordReset();
- context.failureChallenge(AuthenticationFlowError.INVALID_USER, challenge);
- return;
+ // we don't want people guessing usernames, so if there is a problem, just continue, but don't set the user
+ // a null user will notify further executions, that this was a failure.
+ if (user == null) {
+ event.clone()
+ .detail(Details.USERNAME, username)
+ .error(Errors.USER_NOT_FOUND);
+ } else if (!user.isEnabled()) {
+ event.clone()
+ .detail(Details.USERNAME, username)
+ .user(user).error(Errors.USER_DISABLED);
+ } else {
+ context.setUser(user);
}
- context.setUser(user);
context.success();
}
diff --git a/services/src/main/java/org/keycloak/authentication/authenticators/resetcred/ResetCredentialEmail.java b/services/src/main/java/org/keycloak/authentication/authenticators/resetcred/ResetCredentialEmail.java
index 5fb3c00..f5e0165 100755
--- a/services/src/main/java/org/keycloak/authentication/authenticators/resetcred/ResetCredentialEmail.java
+++ b/services/src/main/java/org/keycloak/authentication/authenticators/resetcred/ResetCredentialEmail.java
@@ -7,11 +7,13 @@ import org.keycloak.authentication.AuthenticationFlowContext;
import org.keycloak.authentication.AuthenticationFlowError;
import org.keycloak.authentication.Authenticator;
import org.keycloak.authentication.AuthenticatorFactory;
+import org.keycloak.authentication.authenticators.browser.AbstractUsernameFormAuthenticator;
import org.keycloak.email.EmailException;
import org.keycloak.email.EmailProvider;
import org.keycloak.events.Details;
import org.keycloak.events.Errors;
import org.keycloak.events.EventBuilder;
+import org.keycloak.events.EventType;
import org.keycloak.jose.jws.JWSBuilder;
import org.keycloak.jose.jws.JWSInput;
import org.keycloak.login.LoginFormsProvider;
@@ -49,13 +51,29 @@ public class ResetCredentialEmail implements Authenticator, AuthenticatorFactory
@Override
public void authenticate(AuthenticationFlowContext context) {
UserModel user = context.getUser();
+ String username = context.getClientSession().getNote(AbstractUsernameFormAuthenticator.ATTEMPTED_USERNAME);
+
+ // we don't want people guessing usernames, so if there was a problem obtaining the user, the user will be null.
+ // just redisplay this form
+ if (user == null) {
+ Response challenge = context.form()
+ .setSuccess(Messages.EMAIL_SENT)
+ .createForm("validate-reset-email.ftl");
+ context.challenge(challenge);
+ return;
+ }
+
+
EventBuilder event = context.getEvent();
+ // we don't want people guessing usernames, so if there is a problem, just continuously challenge
if (user.getEmail() == null || user.getEmail().trim().length() == 0) {
- event.user(user).error(Errors.INVALID_EMAIL);
+ event.user(user)
+ .detail(Details.USERNAME, username)
+ .error(Errors.INVALID_EMAIL);
Response challenge = context.form()
- .setError(Messages.INVALID_EMAIL)
- .createPasswordReset();
- context.failureChallenge(AuthenticationFlowError.INVALID_USER, challenge);
+ .setSuccess(Messages.EMAIL_SENT)
+ .createForm("validate-reset-email.ftl");
+ context.challenge(challenge);
return;
}
@@ -68,14 +86,19 @@ public class ResetCredentialEmail implements Authenticator, AuthenticatorFactory
try {
context.getSession().getProvider(EmailProvider.class).setRealm(context.getRealm()).setUser(user).sendPasswordReset(secret, link, expiration);
-
- event.detail(Details.EMAIL, user.getEmail()).detail(Details.CODE_ID, context.getClientSession().getId()).success();
+ event.clone().event(EventType.SEND_RESET_PASSWORD)
+ .user(user)
+ .detail(Details.USERNAME, username)
+ .detail(Details.EMAIL, user.getEmail()).detail(Details.CODE_ID, context.getClientSession().getId()).success();
Response challenge = context.form()
.setSuccess(Messages.EMAIL_SENT)
.createForm("validate-reset-email.ftl");
context.challenge(challenge);
} catch (EmailException e) {
- event.error(Errors.EMAIL_SEND_FAILED);
+ event.clone().event(EventType.SEND_RESET_PASSWORD)
+ .detail(Details.USERNAME, username)
+ .user(user)
+ .error(Errors.EMAIL_SEND_FAILED);
logger.error("Failed to send password reset email", e);
Response challenge = context.form()
.setError(Messages.EMAIL_SENT_ERROR)
@@ -92,7 +115,13 @@ public class ResetCredentialEmail implements Authenticator, AuthenticatorFactory
key =context.getUriInfo().getQueryParameters().getFirst(KEY);
} else if (context.getHttpRequest().getHttpMethod().equalsIgnoreCase("POST")) {
- key = context.getHttpRequest().getDecodedFormParameters().getFirst(KEY);
+ MultivaluedMap<String, String> formData = context.getHttpRequest().getDecodedFormParameters();
+ if (formData.containsKey("cancel")) {
+ context.resetBrowserLogin();
+ return;
+ }
+
+ key = formData.getFirst(KEY);
}
// Can only guess once! We remove the note so another guess can't happen
@@ -110,7 +139,7 @@ public class ResetCredentialEmail implements Authenticator, AuthenticatorFactory
@Override
public boolean requiresUser() {
- return true;
+ return false;
}
@Override
diff --git a/services/src/main/java/org/keycloak/authentication/DefaultAuthenticationFlow.java b/services/src/main/java/org/keycloak/authentication/DefaultAuthenticationFlow.java
index 49c48ad..df9a26b 100755
--- a/services/src/main/java/org/keycloak/authentication/DefaultAuthenticationFlow.java
+++ b/services/src/main/java/org/keycloak/authentication/DefaultAuthenticationFlow.java
@@ -166,6 +166,9 @@ public class DefaultAuthenticationFlow implements AuthenticationFlow {
return sendChallenge(result, execution);
}
throw new AuthenticationFlowException(result.getError());
+ } else if (status == FlowStatus.RESET_BROWSER_LOGIN) {
+ AuthenticationProcessor.logger.debugv("reset browser login from authenticator: {0}", execution.getAuthenticator());
+ throw new AuthenticationFlowException(AuthenticationFlowError.RESET_TO_BROWSER_LOGIN);
} else if (status == FlowStatus.FORCE_CHALLENGE) {
processor.getClientSession().setExecutionStatus(execution.getId(), ClientSessionModel.ExecutionStatus.CHALLENGED);
return sendChallenge(result, execution);
diff --git a/services/src/main/java/org/keycloak/authentication/FlowStatus.java b/services/src/main/java/org/keycloak/authentication/FlowStatus.java
index 0acd875..31b7eee 100755
--- a/services/src/main/java/org/keycloak/authentication/FlowStatus.java
+++ b/services/src/main/java/org/keycloak/authentication/FlowStatus.java
@@ -42,6 +42,12 @@ public enum FlowStatus {
* a Kerberos authenticator did not see a negotiate header. There was no error, but the execution was attempted.
*
*/
- ATTEMPTED
+ ATTEMPTED,
+
+ /**
+ * Aborting this flow and starting the realm's browser flow from the beginning
+ *
+ */
+ RESET_BROWSER_LOGIN
}
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 f34bed6..d87ad68 100755
--- a/services/src/main/java/org/keycloak/services/resources/LoginActionsService.java
+++ b/services/src/main/java/org/keycloak/services/resources/LoginActionsService.java
@@ -29,6 +29,7 @@ import org.keycloak.authentication.RequiredActionContext;
import org.keycloak.authentication.RequiredActionContextResult;
import org.keycloak.authentication.RequiredActionFactory;
import org.keycloak.authentication.RequiredActionProvider;
+import org.keycloak.authentication.authenticators.browser.AbstractUsernameFormAuthenticator;
import org.keycloak.email.EmailException;
import org.keycloak.email.EmailProvider;
import org.keycloak.events.Details;
@@ -762,85 +763,6 @@ public class LoginActionsService {
}
}
- private Response sendPasswordReset(@QueryParam("code") String code,
- final MultivaluedMap<String, String> formData) {
- event.event(EventType.SEND_RESET_PASSWORD);
- if (!realm.isResetPasswordAllowed()) {
- event.error(Errors.RESET_CREDENTIAL_DISABLED);
- return ErrorPage.error(session, Messages.RESET_CREDENTIAL_NOT_ALLOWED);
- }
- Checks checks = new Checks();
- if (!checks.verifyCode(code)) {
- return checks.response;
- }
- final ClientSessionCode accessCode = checks.clientCode;
- final ClientSessionModel clientSession = accessCode.getClientSession();
- ClientModel client = clientSession.getClient();
-
-
- String username = formData.getFirst("username");
- if (username == null || username.isEmpty()) {
- event.error(Errors.USERNAME_MISSING);
- return session.getProvider(LoginFormsProvider.class)
- .setError(Messages.MISSING_USERNAME)
- .setClientSessionCode(accessCode.getCode())
- .createPasswordReset();
- }
-
- event.client(client.getClientId())
- .detail(Details.REDIRECT_URI, clientSession.getRedirectUri())
- .detail(Details.RESPONSE_TYPE, "code")
- .detail(Details.AUTH_METHOD, "form")
- .detail(Details.USERNAME, username);
-
- UserModel user = session.users().getUserByUsername(username, realm);
- if (user == null && username.contains("@")) {
- user = session.users().getUserByEmail(username, realm);
- }
-
- if (user == null) {
- event.error(Errors.USER_NOT_FOUND);
- } else if (!user.isEnabled()) {
- event.user(user).error(Errors.USER_DISABLED);
- } else if (user.getEmail() == null || user.getEmail().trim().length() == 0) {
- event.user(user).error(Errors.INVALID_EMAIL);
- } else {
- event.user(user);
-
- UserSessionModel userSession = session.sessions().createUserSession(realm, user, username, clientConnection.getRemoteAddr(), "form", false, null, null);
- event.session(userSession);
- TokenManager.attachClientSession(userSession, clientSession);
-
- accessCode.setAction(ClientSessionModel.Action.RECOVER_PASSWORD.name());
-
- try {
- UriBuilder builder = Urls.loginResetCredentialsBuilder(uriInfo.getBaseUri());
- builder.queryParam("key", accessCode.getCode());
-
- String link = builder.build(realm.getName()).toString();
- long expiration = TimeUnit.SECONDS.toMinutes(realm.getAccessCodeLifespanUserAction());
-
- this.session.getProvider(EmailProvider.class).setRealm(realm).setUser(user).sendChangePassword(link, expiration);
-
- event.detail(Details.EMAIL, user.getEmail()).detail(Details.CODE_ID, clientSession.getId()).success();
- } catch (EmailException e) {
- event.error(Errors.EMAIL_SEND_FAILED);
- logger.error("Failed to send password reset email", e);
- return session.getProvider(LoginFormsProvider.class)
- .setError(Messages.EMAIL_SENT_ERROR)
- .setClientSessionCode(accessCode.getCode())
- .createErrorPage();
- }
-
- createActionCookie(realm, uriInfo, clientConnection, userSession.getId());
- }
-
- return session.getProvider(LoginFormsProvider.class)
- .setSuccess(Messages.EMAIL_SENT)
- .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);
@@ -857,6 +779,7 @@ public class LoginActionsService {
.session(clientSession.getUserSession().getId())
.detail(Details.CODE_ID, clientSession.getId())
.detail(Details.REDIRECT_URI, clientSession.getRedirectUri())
+ .detail(Details.USERNAME, clientSession.getNote(AbstractUsernameFormAuthenticator.ATTEMPTED_USERNAME))
.detail(Details.RESPONSE_TYPE, "code");
UserSessionModel userSession = clientSession.getUserSession();
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 bcaa98e..95d644e 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
@@ -167,7 +167,7 @@ public class AccountTest {
});
}
- @Test
+ //@Test
public void ideTesting() throws Exception {
Thread.sleep(100000000);
}
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 e3926ef..d8ae5fe 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
@@ -44,6 +44,7 @@ import org.keycloak.testsuite.pages.InfoPage;
import org.keycloak.testsuite.pages.LoginPage;
import org.keycloak.testsuite.pages.LoginPasswordResetPage;
import org.keycloak.testsuite.pages.LoginPasswordUpdatePage;
+import org.keycloak.testsuite.pages.ValidatePassworrdEmailResetPage;
import org.keycloak.testsuite.rule.GreenMailRule;
import org.keycloak.testsuite.rule.KeycloakRule;
import org.keycloak.testsuite.rule.WebResource;
@@ -65,6 +66,7 @@ import static org.junit.Assert.*;
*/
public class ResetPasswordTest {
+ static int lifespan = 0;
@ClassRule
public static KeycloakRule keycloakRule = new KeycloakRule((new KeycloakRule.KeycloakSetup() {
@Override
@@ -81,6 +83,7 @@ public class ResetPasswordTest {
user.updateCredential(creds);
appRealm.setEventsListeners(Collections.singleton("dummy"));
+ lifespan = appRealm.getAccessCodeLifespanUserAction();
}
}));
@@ -114,6 +117,9 @@ public class ResetPasswordTest {
protected LoginPasswordResetPage resetPasswordPage;
@WebResource
+ protected ValidatePassworrdEmailResetPage validateResetPage;
+
+ @WebResource
protected LoginPasswordUpdatePage updatePasswordPage;
@Rule
@@ -133,12 +139,13 @@ public class ResetPasswordTest {
resetPasswordPage.changePassword("login-test");
- resetPasswordPage.assertCurrent();
+ validateResetPage.assertCurrent();
- events.expectRequiredAction(EventType.SEND_RESET_PASSWORD).user(userId).detail(Details.USERNAME, "login-test").detail(Details.EMAIL, "login@test.com").assertEvent().getSessionId();
+ events.expectRequiredAction(EventType.SEND_RESET_PASSWORD)
+ .session((String)null)
+ .user(userId).detail(Details.USERNAME, "login-test").detail(Details.EMAIL, "login@test.com").assertEvent().getSessionId();
- String src = driver.getPageSource();
- resetPasswordPage.backToLogin();
+ validateResetPage.cancel();
assertTrue(loginPage.isCurrent());
@@ -169,17 +176,19 @@ public class ResetPasswordTest {
resetPasswordPage.changePassword("test-user@localhost");
- resetPasswordPage.assertCurrent();
+ validateResetPage.assertCurrent();
- events.expectRequiredAction(EventType.SEND_RESET_PASSWORD).detail(Details.USERNAME, "test-user@localhost").detail(Details.EMAIL, "test-user@localhost").assertEvent().getSessionId();
+ events.expectRequiredAction(EventType.SEND_RESET_PASSWORD).detail(Details.USERNAME, "test-user@localhost")
+ .session((String) null)
+ .detail(Details.EMAIL, "test-user@localhost").assertEvent();
- resetPasswordPage.backToLogin();
+ validateResetPage.cancel();
assertTrue(loginPage.isCurrent());
loginPage.login("login@test.com", "password");
- Event loginEvent = events.expectLogin().user(userId).detail(Details.USERNAME, "login-test").assertEvent();
+ Event loginEvent = events.expectLogin().user(userId).detail(Details.USERNAME, "login@test.com").assertEvent();
String code = oauth.getCurrentQuery().get("code");
OAuthClient.AccessTokenResponse tokenResponse = oauth.doAccessTokenRequest(code, "password");
@@ -203,9 +212,14 @@ public class ResetPasswordTest {
resetPasswordPage.changePassword(username);
- resetPasswordPage.assertCurrent();
+ validateResetPage.assertCurrent();
- String sessionId = events.expectRequiredAction(EventType.SEND_RESET_PASSWORD).user(userId).detail(Details.USERNAME, username).detail(Details.EMAIL, "login@test.com").assertEvent().getSessionId();
+ events.expectRequiredAction(EventType.SEND_RESET_PASSWORD)
+ .user(userId)
+ .detail(Details.USERNAME, username)
+ .detail(Details.EMAIL, "login@test.com")
+ .session((String)null)
+ .assertEvent();
assertEquals("You should receive an email shortly with further instructions.", resetPasswordPage.getSuccessMessage());
@@ -221,7 +235,7 @@ public class ResetPasswordTest {
updatePasswordPage.changePassword("resetPassword", "resetPassword");
- events.expectRequiredAction(EventType.UPDATE_PASSWORD).user(userId).session(sessionId).detail(Details.USERNAME, username).assertEvent();
+ String sessionId = events.expectRequiredAction(EventType.UPDATE_PASSWORD).user(userId).detail(Details.USERNAME, username).assertEvent().getSessionId();
assertEquals(RequestType.AUTH_RESPONSE, appPage.getRequestType());
@@ -248,10 +262,10 @@ public class ResetPasswordTest {
resetPasswordPage.changePassword(username);
- resetPasswordPage.assertCurrent();
+ validateResetPage.assertCurrent();
- String sessionId = events.expectRequiredAction(EventType.SEND_RESET_PASSWORD).user(userId)
- .detail(Details.USERNAME, username).detail(Details.EMAIL, "login@test.com").assertEvent().getSessionId();
+ events.expectRequiredAction(EventType.SEND_RESET_PASSWORD).user(userId).session((String)null)
+ .detail(Details.USERNAME, username).detail(Details.EMAIL, "login@test.com").assertEvent();
assertEquals("You should receive an email shortly with further instructions.", resetPasswordPage.getSuccessMessage());
@@ -265,12 +279,12 @@ public class ResetPasswordTest {
updatePasswordPage.changePassword(password, password);
- events.expectRequiredAction(EventType.UPDATE_PASSWORD).user(userId).session(sessionId)
- .detail(Details.USERNAME, username).assertEvent();
+ String sessionId = events.expectRequiredAction(EventType.UPDATE_PASSWORD).user(userId)
+ .detail(Details.USERNAME, username).assertEvent().getSessionId();
assertEquals(RequestType.AUTH_RESPONSE, appPage.getRequestType());
- events.expectLogin().user(userId).detail(Details.USERNAME, username).session(sessionId).assertEvent();
+ events.expectLogin().user(userId).detail(Details.USERNAME, username).assertEvent();
oauth.openLogout();
@@ -285,10 +299,10 @@ public class ResetPasswordTest {
resetPasswordPage.changePassword(username);
- resetPasswordPage.assertCurrent();
+ validateResetPage.assertCurrent();
- events.expectRequiredAction(EventType.SEND_RESET_PASSWORD).user(userId)
- .detail(Details.USERNAME, username).detail(Details.EMAIL, "login@test.com").assertEvent().getSessionId();
+ events.expectRequiredAction(EventType.SEND_RESET_PASSWORD).user(userId).session((String)null)
+ .detail(Details.USERNAME, username).detail(Details.EMAIL, "login@test.com").assertEvent();
assertEquals("You should receive an email shortly with further instructions.", resetPasswordPage.getSuccessMessage());
@@ -315,13 +329,13 @@ public class ResetPasswordTest {
resetPasswordPage.changePassword("invalid");
- resetPasswordPage.assertCurrent();
+ validateResetPage.assertCurrent();
assertEquals("You should receive an email shortly with further instructions.", resetPasswordPage.getSuccessMessage());
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();
+ events.expectRequiredAction(EventType.RESET_PASSWORD).user((String) null).session((String) null).detail(Details.USERNAME, "invalid").removeDetail(Details.EMAIL).removeDetail(Details.CODE_ID).error("user_not_found").assertEvent();
}
@Test
@@ -339,7 +353,7 @@ public class ResetPasswordTest {
assertEquals(0, greenMail.getReceivedMessages().length);
- events.expectRequiredAction(EventType.SEND_RESET_PASSWORD).client((String) null).user((String) null).session((String) null).clearDetails().error("username_missing").assertEvent();
+ events.expectRequiredAction(EventType.RESET_PASSWORD).user((String) null).session((String) null).clearDetails().error("username_missing").assertEvent();
}
@@ -353,9 +367,11 @@ public class ResetPasswordTest {
resetPasswordPage.changePassword("login-test");
- resetPasswordPage.assertCurrent();
+ validateResetPage.assertCurrent();
- String sessionId = events.expectRequiredAction(EventType.SEND_RESET_PASSWORD).user(userId).detail(Details.USERNAME, "login-test").detail(Details.EMAIL, "login@test.com").assertEvent().getSessionId();
+ events.expectRequiredAction(EventType.SEND_RESET_PASSWORD)
+ .session((String)null)
+ .user(userId).detail(Details.USERNAME, "login-test").detail(Details.EMAIL, "login@test.com").assertEvent();
assertEquals("You should receive an email shortly with further instructions.", resetPasswordPage.getSuccessMessage());
@@ -365,13 +381,13 @@ public class ResetPasswordTest {
String changePasswordUrl = getPasswordResetEmailLink(message);
- Time.setOffset(350);
+ Time.setOffset(1800+23);
driver.navigate().to(changePasswordUrl.trim());
- errorPage.assertCurrent();
+ loginPage.assertCurrent();
- assertEquals("Login timeout. Please login again.", errorPage.getError());
+ assertEquals("You took too long to login. Login process starting from beginning.", loginPage.getError());
events.expectRequiredAction(EventType.RESET_PASSWORD).error("expired_code").client("test-app").user((String) null).session((String) null).clearDetails().assertEvent();
} finally {
@@ -396,13 +412,13 @@ public class ResetPasswordTest {
resetPasswordPage.changePassword("login-test");
- resetPasswordPage.assertCurrent();
+ validateResetPage.assertCurrent();
assertEquals("You should receive an email shortly with further instructions.", resetPasswordPage.getSuccessMessage());
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();
+ events.expectRequiredAction(EventType.RESET_PASSWORD).session((String) null).user(userId).detail(Details.USERNAME, "login-test").removeDetail(Details.CODE_ID).error("user_disabled").assertEvent();
} finally {
keycloakRule.configure(new KeycloakRule.KeycloakSetup() {
@Override
@@ -434,13 +450,13 @@ public class ResetPasswordTest {
resetPasswordPage.changePassword("login-test");
- resetPasswordPage.assertCurrent();
+ validateResetPage.assertCurrent();
assertEquals("You should receive an email shortly with further instructions.", resetPasswordPage.getSuccessMessage());
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();
+ events.expectRequiredAction(EventType.RESET_PASSWORD_ERROR).session((String) null).user(userId).detail(Details.USERNAME, "login-test").removeDetail(Details.CODE_ID).error("invalid_email").assertEvent();
} finally {
keycloakRule.configure(new KeycloakRule.KeycloakSetup() {
@Override
@@ -476,7 +492,9 @@ public class ResetPasswordTest {
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();
+ events.expectRequiredAction(EventType.SEND_RESET_PASSWORD_ERROR).user(userId)
+ .session((String)null)
+ .detail(Details.USERNAME, "login-test").removeDetail(Details.CODE_ID).error(Errors.EMAIL_SEND_FAILED).assertEvent();
} finally {
keycloakRule.configure(new KeycloakRule.KeycloakSetup() {
@Override
@@ -503,7 +521,7 @@ public class ResetPasswordTest {
resetPasswordPage.changePassword("login-test");
- resetPasswordPage.assertCurrent();
+ validateResetPage.assertCurrent();
assertEquals("You should receive an email shortly with further instructions.", resetPasswordPage.getSuccessMessage());
@@ -513,7 +531,7 @@ public class ResetPasswordTest {
String changePasswordUrl = getPasswordResetEmailLink(message);
- String sessionId = events.expectRequiredAction(EventType.SEND_RESET_PASSWORD).user(userId).detail(Details.USERNAME, "login-test").detail(Details.EMAIL, "login@test.com").assertEvent().getSessionId();
+ events.expectRequiredAction(EventType.SEND_RESET_PASSWORD).session((String)null).user(userId).detail(Details.USERNAME, "login-test").detail(Details.EMAIL, "login@test.com").assertEvent();
driver.navigate().to(changePasswordUrl.trim());
@@ -525,7 +543,7 @@ public class ResetPasswordTest {
updatePasswordPage.changePassword("resetPasswordWithPasswordPolicy", "resetPasswordWithPasswordPolicy");
- events.expectRequiredAction(EventType.UPDATE_PASSWORD).user(userId).session(sessionId).detail(Details.USERNAME, "login-test").assertEvent();
+ String sessionId = events.expectRequiredAction(EventType.UPDATE_PASSWORD).user(userId).detail(Details.USERNAME, "login-test").assertEvent().getSessionId();
assertEquals(RequestType.AUTH_RESPONSE, appPage.getRequestType());
@@ -585,47 +603,6 @@ public class ResetPasswordTest {
}
}
- @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 changePasswordUrl = getPasswordResetEmailLink(message);
-
- 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("Your password has been updated.", infoPage.getInfo());
-
- loginPage.open();
-
- assertTrue(loginPage.isCurrent());
- }
-
private String getPasswordResetEmailLink(MimeMessage message) throws IOException, MessagingException {
Multipart multipart = (Multipart) message.getContent();
diff --git a/testsuite/integration/src/test/java/org/keycloak/testsuite/pages/ValidatePassworrdEmailResetPage.java b/testsuite/integration/src/test/java/org/keycloak/testsuite/pages/ValidatePassworrdEmailResetPage.java
new file mode 100755
index 0000000..780b704
--- /dev/null
+++ b/testsuite/integration/src/test/java/org/keycloak/testsuite/pages/ValidatePassworrdEmailResetPage.java
@@ -0,0 +1,73 @@
+/*
+ * 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.openqa.selenium.WebElement;
+import org.openqa.selenium.support.FindBy;
+
+/**
+ * @author <a href="mailto:sthorger@redhat.com">Stian Thorgersen</a>
+ */
+public class ValidatePassworrdEmailResetPage extends AbstractPage {
+
+ @FindBy(id = "key")
+ private WebElement keyInput;
+
+ @FindBy(id="kc-submit")
+ private WebElement submitButton;
+
+ @FindBy(id="kc-cancel")
+ private WebElement cancelButton;
+
+ @FindBy(className = "feedback-success")
+ private WebElement emailSuccessMessage;
+
+ @FindBy(className = "feedback-error")
+ private WebElement emailErrorMessage;
+
+ public void submitCode(String code) {
+ keyInput.sendKeys(code);
+
+ submitButton.click();
+ }
+
+ public void cancel() {
+ cancelButton.click();
+ }
+
+ public boolean isCurrent() {
+ return driver.getTitle().equals("Forgot Your Password?");
+ }
+
+ public void open() {
+ throw new UnsupportedOperationException();
+ }
+
+ public String getSuccessMessage() {
+ return emailSuccessMessage != null ? emailSuccessMessage.getText() : null;
+ }
+
+ public String getErrorMessage() {
+ return emailErrorMessage != null ? emailErrorMessage.getText() : null;
+ }
+
+}