reset password refactor
This commit is contained in:
parent
c0f3d851db
commit
c7b5975ac1
13 changed files with 237 additions and 222 deletions
|
@ -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>
|
||||
|
|
|
@ -80,7 +80,8 @@ public interface ClientSessionModel {
|
|||
RECOVER_PASSWORD,
|
||||
AUTHENTICATE,
|
||||
SOCIAL_CALLBACK,
|
||||
LOGGED_OUT
|
||||
LOGGED_OUT,
|
||||
RESET_CREDENTIALS
|
||||
}
|
||||
|
||||
public enum ExecutionStatus {
|
||||
|
|
|
@ -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();
|
||||
}
|
||||
|
|
|
@ -16,5 +16,6 @@ public enum AuthenticationFlowError {
|
|||
USER_CONFLICT,
|
||||
USER_TEMPORARILY_DISABLED,
|
||||
INTERNAL_ERROR,
|
||||
UNKNOWN_USER
|
||||
UNKNOWN_USER,
|
||||
RESET_TO_BROWSER_LOGIN
|
||||
}
|
||||
|
|
|
@ -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);
|
||||
|
||||
}
|
||||
|
|
|
@ -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);
|
||||
|
|
|
@ -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
|
||||
|
||||
}
|
||||
|
|
|
@ -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());
|
||||
}
|
||||
|
||||
context.getClientSession().setNote(AbstractUsernameFormAuthenticator.ATTEMPTED_USERNAME, username);
|
||||
|
||||
// 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.error(Errors.INVALID_USER_CREDENTIALS);
|
||||
Response challenge = context.form()
|
||||
.setError(Messages.INVALID_USER)
|
||||
.createPasswordReset();
|
||||
context.failureChallenge(AuthenticationFlowError.INVALID_USER, challenge);
|
||||
return;
|
||||
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);
|
||||
}
|
||||
|
||||
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;
|
||||
}
|
||||
|
||||
context.setUser(user);
|
||||
context.success();
|
||||
}
|
||||
|
||||
|
|
|
@ -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();
|
||||
EventBuilder event = context.getEvent();
|
||||
if (user.getEmail() == null || user.getEmail().trim().length() == 0) {
|
||||
event.user(user).error(Errors.INVALID_EMAIL);
|
||||
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()
|
||||
.setError(Messages.INVALID_EMAIL)
|
||||
.createPasswordReset();
|
||||
context.failureChallenge(AuthenticationFlowError.INVALID_USER, challenge);
|
||||
.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)
|
||||
.detail(Details.USERNAME, username)
|
||||
.error(Errors.INVALID_EMAIL);
|
||||
Response challenge = context.form()
|
||||
.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
|
||||
|
|
|
@ -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();
|
||||
|
|
|
@ -167,7 +167,7 @@ public class AccountTest {
|
|||
});
|
||||
}
|
||||
|
||||
@Test
|
||||
//@Test
|
||||
public void ideTesting() throws Exception {
|
||||
Thread.sleep(100000000);
|
||||
}
|
||||
|
|
|
@ -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();
|
||||
}
|
||||
}));
|
||||
|
||||
|
@ -113,6 +116,9 @@ public class ResetPasswordTest {
|
|||
@WebResource
|
||||
protected LoginPasswordResetPage resetPasswordPage;
|
||||
|
||||
@WebResource
|
||||
protected ValidatePassworrdEmailResetPage validateResetPage;
|
||||
|
||||
@WebResource
|
||||
protected LoginPasswordUpdatePage updatePasswordPage;
|
||||
|
||||
|
@ -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();
|
||||
|
||||
|
|
|
@ -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;
|
||||
}
|
||||
|
||||
}
|
Loading…
Reference in a new issue