KEYCLOAK-217 Add option to recover username
This commit is contained in:
parent
0dad786b35
commit
cd8c8d52e8
19 changed files with 336 additions and 14 deletions
|
@ -47,6 +47,10 @@ public class MessageBean {
|
||||||
return summary;
|
return summary;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public String getType() {
|
||||||
|
return this.type.toString().toLowerCase();
|
||||||
|
}
|
||||||
|
|
||||||
public boolean isSuccess(){
|
public boolean isSuccess(){
|
||||||
return FormFlows.MessageType.SUCCESS.equals(this.type);
|
return FormFlows.MessageType.SUCCESS.equals(this.type);
|
||||||
}
|
}
|
||||||
|
|
|
@ -63,4 +63,8 @@ public class RealmBean {
|
||||||
return realm.isRegistrationAllowed();
|
return realm.isRegistrationAllowed();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public boolean isResetPasswordAllowed() {
|
||||||
|
return realm.isResetPasswordAllowed();
|
||||||
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
|
@ -135,6 +135,10 @@ public class UrlBean {
|
||||||
return Urls.loginPasswordReset(baseURI, realm.getId()).toString();
|
return Urls.loginPasswordReset(baseURI, realm.getId()).toString();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public String getLoginUsernameReminderUrl() {
|
||||||
|
return Urls.loginUsernameReminder(baseURI, realm.getId()).toString();
|
||||||
|
}
|
||||||
|
|
||||||
public String getLoginEmailVerificationUrl() {
|
public String getLoginEmailVerificationUrl() {
|
||||||
return Urls.loginActionEmailVerification(baseURI, realm.getId()).toString();
|
return Urls.loginActionEmailVerification(baseURI, realm.getId()).toString();
|
||||||
}
|
}
|
||||||
|
|
|
@ -64,6 +64,7 @@ public class FormServiceImpl implements FormService {
|
||||||
commandMap.put(Pages.LOGIN_UPDATE_PROFILE, new CommandCommon());
|
commandMap.put(Pages.LOGIN_UPDATE_PROFILE, new CommandCommon());
|
||||||
commandMap.put(Pages.PASSWORD, new CommandCommon());
|
commandMap.put(Pages.PASSWORD, new CommandCommon());
|
||||||
commandMap.put(Pages.LOGIN_RESET_PASSWORD, new CommandCommon());
|
commandMap.put(Pages.LOGIN_RESET_PASSWORD, new CommandCommon());
|
||||||
|
commandMap.put(Pages.LOGIN_USERNAME_REMINDER, new CommandCommon());
|
||||||
commandMap.put(Pages.LOGIN_UPDATE_PASSWORD, new CommandCommon());
|
commandMap.put(Pages.LOGIN_UPDATE_PASSWORD, new CommandCommon());
|
||||||
commandMap.put(Pages.ACCESS, new CommandCommon());
|
commandMap.put(Pages.ACCESS, new CommandCommon());
|
||||||
commandMap.put(Pages.SOCIAL, new CommandCommon());
|
commandMap.put(Pages.SOCIAL, new CommandCommon());
|
||||||
|
|
|
@ -0,0 +1,25 @@
|
||||||
|
<#import "template-login-action.ftl" as layout>
|
||||||
|
<@layout.registrationLayout bodyClass="reset" isSeparator=true forceSeparator=true; section>
|
||||||
|
<#if section = "title">
|
||||||
|
|
||||||
|
${rb.getString('emailUsernameForgotHeader')}
|
||||||
|
|
||||||
|
<#elseif section = "header">
|
||||||
|
|
||||||
|
${rb.getString('emailUsernameForgotHeader')}
|
||||||
|
|
||||||
|
<#elseif section = "form">
|
||||||
|
|
||||||
|
<div id="form">
|
||||||
|
<p class="instruction">${rb.getString('emailUsernameInstruction')}</p>
|
||||||
|
<form action="${url.loginUsernameReminderUrl}" method="post">
|
||||||
|
<div>
|
||||||
|
<label for="email">${rb.getString('email')}</label><input type="text" id="email" name="email" />
|
||||||
|
</div>
|
||||||
|
<input class="btn-primary" type="submit" value="Submit" />
|
||||||
|
</form>
|
||||||
|
</div>
|
||||||
|
<#elseif section = "info" >
|
||||||
|
<p><a href="${url.loginUrl}">« Back to Login</a></p>
|
||||||
|
</#if>
|
||||||
|
</@layout.registrationLayout>
|
|
@ -24,10 +24,6 @@
|
||||||
<input class="btn-primary" name="login" type="submit" value="Log In"/>
|
<input class="btn-primary" name="login" type="submit" value="Log In"/>
|
||||||
<input class="btn-secondary" name="cancel" type="submit" value="Cancel"/>
|
<input class="btn-secondary" name="cancel" type="submit" value="Cancel"/>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<div class="aside-btn">
|
|
||||||
<p>Forgot <a href="${url.loginPasswordResetUrl}">Password</a>?</p>
|
|
||||||
</div>
|
|
||||||
</form>
|
</form>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
|
@ -37,6 +33,9 @@
|
||||||
<#if realm.registrationAllowed>
|
<#if realm.registrationAllowed>
|
||||||
<p>${rb.getString('noAccount')} <a href="${url.registrationUrl}">${rb.getString('register')}</a>.</p>
|
<p>${rb.getString('noAccount')} <a href="${url.registrationUrl}">${rb.getString('register')}</a>.</p>
|
||||||
</#if>
|
</#if>
|
||||||
|
<#if realm.resetPasswordAllowed>
|
||||||
|
<p>Forgot <a href="${url.loginUsernameReminderUrl}">Username</a> / <a href="${url.loginPasswordResetUrl}">Password</a>?</p>
|
||||||
|
</#if>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
</#if>
|
</#if>
|
||||||
|
|
|
@ -44,6 +44,13 @@
|
||||||
</p>
|
</p>
|
||||||
</div>
|
</div>
|
||||||
</#if>
|
</#if>
|
||||||
|
<#if message?has_content && message.success>
|
||||||
|
<div class="feedback success bottom-left show">
|
||||||
|
<p>
|
||||||
|
<strong>${rb.getString('successHeader')}</strong><br/>${message.summary}
|
||||||
|
</p>
|
||||||
|
</div>
|
||||||
|
</#if>
|
||||||
<#nested "form">
|
<#nested "form">
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
|
|
|
@ -64,5 +64,9 @@ emailError=Invalid username or email.
|
||||||
emailErrorInfo=Please, fill in the fields again.
|
emailErrorInfo=Please, fill in the fields again.
|
||||||
emailInstruction=Enter your username and email address and we will send you instructions on how to create a new password.
|
emailInstruction=Enter your username and email address and we will send you instructions on how to create a new password.
|
||||||
|
|
||||||
|
emailUsernameForgotHeader=Forgot Your Username?
|
||||||
|
emailUsernameInstruction=Enter your email address and we will send you an email with your username.
|
||||||
|
emailUsernameSent=You should receive an email shortly with your username.
|
||||||
|
|
||||||
accountUpdated=Your account has been updated
|
accountUpdated=Your account has been updated
|
||||||
accountPasswordUpdated=Your password has been updated
|
accountPasswordUpdated=Your password has been updated
|
|
@ -86,6 +86,8 @@ public interface RealmModel extends RoleContainerModel, RoleMapperModel, ScopeMa
|
||||||
|
|
||||||
UserModel getUser(String name);
|
UserModel getUser(String name);
|
||||||
|
|
||||||
|
UserModel getUserByEmail(String email);
|
||||||
|
|
||||||
UserModel addUser(String username);
|
UserModel addUser(String username);
|
||||||
|
|
||||||
boolean removeUser(String name);
|
boolean removeUser(String name);
|
||||||
|
|
|
@ -439,6 +439,15 @@ public class RealmAdapter implements RealmModel {
|
||||||
return new UserAdapter(results.get(0));
|
return new UserAdapter(results.get(0));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public UserModel getUserByEmail(String email) {
|
||||||
|
TypedQuery<UserEntity> query = em.createNamedQuery("getRealmUserByEmail", UserEntity.class);
|
||||||
|
query.setParameter("email", email);
|
||||||
|
query.setParameter("realm", realm);
|
||||||
|
List<UserEntity> results = query.getResultList();
|
||||||
|
return results.isEmpty()? null : new UserAdapter(results.get(0));
|
||||||
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public UserModel addUser(String username) {
|
public UserModel addUser(String username) {
|
||||||
UserEntity entity = new UserEntity();
|
UserEntity entity = new UserEntity();
|
||||||
|
|
|
@ -509,6 +509,14 @@ public class RealmAdapter implements RealmModel {
|
||||||
return new UserAdapter(user, getIdm());
|
return new UserAdapter(user, getIdm());
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public UserModel getUserByEmail(String email) {
|
||||||
|
IdentityQuery<User> query = getIdm().createIdentityQuery(User.class);
|
||||||
|
query.setParameter(User.EMAIL, email);
|
||||||
|
List<User> users = query.getResultList();
|
||||||
|
return users.isEmpty() ? null : new UserAdapter(users.get(0), getIdm());
|
||||||
|
}
|
||||||
|
|
||||||
protected User findPicketlinkUser(String name) {
|
protected User findPicketlinkUser(String name) {
|
||||||
return SampleModel.getUser(getIdm(), name);
|
return SampleModel.getUser(getIdm(), name);
|
||||||
}
|
}
|
||||||
|
|
|
@ -108,16 +108,17 @@ public class EmailSender {
|
||||||
|
|
||||||
URI uri = builder.build(realm.getId());
|
URI uri = builder.build(realm.getId());
|
||||||
|
|
||||||
StringBuilder sb = new StringBuilder();
|
|
||||||
sb.append("Hi ").append(user.getFirstName()).append(",\n\n");
|
StringBuilder sb = getHeader(user);
|
||||||
|
|
||||||
sb.append("Someone has created a Keycloak account with this email address. ");
|
sb.append("Someone has created a Keycloak account with this email address. ");
|
||||||
sb.append("If this was you, click the link below to verify your email address:\n");
|
sb.append("If this was you, click the link below to verify your email address:\n");
|
||||||
sb.append(uri.toString());
|
sb.append(uri.toString());
|
||||||
sb.append("\n\nThis link will expire within ").append(TimeUnit.SECONDS.toMinutes(realm.getAccessCodeLifespanUserAction()));
|
sb.append("\n\nThis link will expire within ").append(TimeUnit.SECONDS.toMinutes(realm.getAccessCodeLifespanUserAction()));
|
||||||
sb.append(" minutes.\n\n");
|
sb.append(" minutes.\n\n");
|
||||||
sb.append("If you didn't create this account, just ignore this message.\n\n");
|
sb.append("If you didn't create this account, just ignore this message.\n");
|
||||||
sb.append("Thanks,\n");
|
|
||||||
sb.append("The Keycloak Team");
|
addFooter(sb);
|
||||||
|
|
||||||
send(user.getEmail(), "Verify email", sb.toString());
|
send(user.getEmail(), "Verify email", sb.toString());
|
||||||
}
|
}
|
||||||
|
@ -128,19 +129,44 @@ public class EmailSender {
|
||||||
|
|
||||||
URI uri = builder.build(realm.getId());
|
URI uri = builder.build(realm.getId());
|
||||||
|
|
||||||
StringBuilder sb = new StringBuilder();
|
StringBuilder sb = getHeader(user);
|
||||||
|
|
||||||
sb.append("Hi ").append(user.getFirstName()).append(",\n\n");
|
|
||||||
sb.append("Someone just requested to change your Keycloak account's password. ");
|
sb.append("Someone just requested to change your Keycloak account's password. ");
|
||||||
sb.append("If this was you, click on the link below to set a new password:\n");
|
sb.append("If this was you, click on the link below to set a new password:\n");
|
||||||
sb.append(uri.toString());
|
sb.append(uri.toString());
|
||||||
sb.append("\n\nThis link will expire within ").append(TimeUnit.SECONDS.toMinutes(realm.getAccessCodeLifespanUserAction()));
|
sb.append("\n\nThis link will expire within ").append(TimeUnit.SECONDS.toMinutes(realm.getAccessCodeLifespanUserAction()));
|
||||||
sb.append(" minutes.\n\n");
|
sb.append(" minutes.\n\n");
|
||||||
sb.append("If you don't want to reset your password, just ignore this message and nothing will be changed.\n\n");
|
sb.append("If you don't want to reset your password, just ignore this message and nothing will be changed.\n");
|
||||||
sb.append("Thanks,\n");
|
|
||||||
sb.append("The Keycloak Team");
|
addFooter(sb);
|
||||||
|
|
||||||
send(user.getEmail(), "Reset password link", sb.toString());
|
send(user.getEmail(), "Reset password link", sb.toString());
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public void sendUsernameReminder(UserModel user) throws EmailException {
|
||||||
|
StringBuilder sb = getHeader(user);
|
||||||
|
|
||||||
|
sb.append("The username for your Keycloak account is ").append(user.getLoginName()).append(".\n");
|
||||||
|
|
||||||
|
addFooter(sb);
|
||||||
|
|
||||||
|
send(user.getEmail(), "Username reminder", sb.toString());
|
||||||
|
}
|
||||||
|
|
||||||
|
private StringBuilder getHeader(UserModel user) {
|
||||||
|
StringBuilder sb = new StringBuilder();
|
||||||
|
|
||||||
|
sb.append("Hi");
|
||||||
|
if (user.getFirstName() != null) {
|
||||||
|
sb.append(" ").append(user.getFirstName());
|
||||||
|
}
|
||||||
|
sb.append(",\n\n");
|
||||||
|
return sb;
|
||||||
|
}
|
||||||
|
|
||||||
|
private void addFooter(StringBuilder sb) {
|
||||||
|
sb.append("\nThanks,\nThe Keycloak Team");
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
|
@ -276,6 +276,45 @@ public class RequiredActionsService {
|
||||||
return Flows.forms(realm, request, uriInfo).setSuccess("emailSent").forwardToPasswordReset();
|
return Flows.forms(realm, request, uriInfo).setSuccess("emailSent").forwardToPasswordReset();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
@Path("username-reminder")
|
||||||
|
@GET
|
||||||
|
public Response usernameReminder() {
|
||||||
|
return Flows.forms(realm, request, uriInfo).forwardToUsernameReminder();
|
||||||
|
}
|
||||||
|
|
||||||
|
@Path("username-reminder")
|
||||||
|
@POST
|
||||||
|
@Consumes(MediaType.APPLICATION_FORM_URLENCODED)
|
||||||
|
public Response sendUsernameReminder(final MultivaluedMap<String, String> formData) {
|
||||||
|
String email = formData.getFirst("email");
|
||||||
|
String clientId = uriInfo.getQueryParameters().getFirst("client_id");
|
||||||
|
|
||||||
|
UserModel client = realm.getUser(clientId);
|
||||||
|
if (client == null) {
|
||||||
|
return Flows.oauth(realm, request, uriInfo, authManager, tokenManager).forwardToSecurityFailure(
|
||||||
|
"Unknown login requester.");
|
||||||
|
}
|
||||||
|
if (!client.isEnabled()) {
|
||||||
|
return Flows.oauth(realm, request, uriInfo, authManager, tokenManager).forwardToSecurityFailure(
|
||||||
|
"Login requester not enabled.");
|
||||||
|
}
|
||||||
|
|
||||||
|
UserModel user = realm.getUserByEmail(email);
|
||||||
|
if (user == null) {
|
||||||
|
return Flows.forms(realm, request, uriInfo).setError("emailError").forwardToUsernameReminder();
|
||||||
|
}
|
||||||
|
|
||||||
|
try {
|
||||||
|
new EmailSender(realm.getSmtpConfig()).sendUsernameReminder(user);
|
||||||
|
} catch (EmailException e) {
|
||||||
|
logger.error("Failed to send username reminder email", e);
|
||||||
|
return Flows.forms(realm, request, uriInfo).setError("emailSendError").forwardToErrorPage();
|
||||||
|
}
|
||||||
|
|
||||||
|
return Flows.forms(realm, request, uriInfo).setSuccess("emailUsernameSent").forwardToLogin();
|
||||||
|
}
|
||||||
|
|
||||||
private AccessCodeEntry getAccessCodeEntry(RequiredAction requiredAction) {
|
private AccessCodeEntry getAccessCodeEntry(RequiredAction requiredAction) {
|
||||||
String code = uriInfo.getQueryParameters().getFirst(FormFlows.CODE);
|
String code = uriInfo.getQueryParameters().getFirst(FormFlows.CODE);
|
||||||
if (code == null) {
|
if (code == null) {
|
||||||
|
|
|
@ -176,6 +176,10 @@ public class FormFlows {
|
||||||
return forwardToForm(Pages.LOGIN_RESET_PASSWORD);
|
return forwardToForm(Pages.LOGIN_RESET_PASSWORD);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public Response forwardToUsernameReminder() {
|
||||||
|
return forwardToForm(Pages.LOGIN_USERNAME_REMINDER);
|
||||||
|
}
|
||||||
|
|
||||||
public Response forwardToLoginTotp() {
|
public Response forwardToLoginTotp() {
|
||||||
return forwardToForm(Pages.LOGIN_TOTP);
|
return forwardToForm(Pages.LOGIN_TOTP);
|
||||||
}
|
}
|
||||||
|
|
|
@ -46,6 +46,8 @@ public class Pages {
|
||||||
|
|
||||||
public final static String LOGIN_UPDATE_PASSWORD = "login-update-password.ftl";
|
public final static String LOGIN_UPDATE_PASSWORD = "login-update-password.ftl";
|
||||||
|
|
||||||
|
public final static String LOGIN_USERNAME_REMINDER = "login-username-reminder.ftl";
|
||||||
|
|
||||||
public final static String REGISTER = "register.ftl";
|
public final static String REGISTER = "register.ftl";
|
||||||
|
|
||||||
public final static String ERROR = "error.ftl";
|
public final static String ERROR = "error.ftl";
|
||||||
|
|
|
@ -100,6 +100,14 @@ public class Urls {
|
||||||
return requiredActionsBase(baseUri).path(RequiredActionsService.class, "passwordReset");
|
return requiredActionsBase(baseUri).path(RequiredActionsService.class, "passwordReset");
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public static URI loginUsernameReminder(URI baseUri, String realmId) {
|
||||||
|
return loginUsernameReminderBuilder(baseUri).build(realmId);
|
||||||
|
}
|
||||||
|
|
||||||
|
public static UriBuilder loginUsernameReminderBuilder(URI baseUri) {
|
||||||
|
return requiredActionsBase(baseUri).path(RequiredActionsService.class, "usernameReminder");
|
||||||
|
}
|
||||||
|
|
||||||
private static UriBuilder realmBase(URI baseUri) {
|
private static UriBuilder realmBase(URI baseUri) {
|
||||||
return UriBuilder.fromUri(baseUri).path(RealmsResource.class);
|
return UriBuilder.fromUri(baseUri).path(RealmsResource.class);
|
||||||
}
|
}
|
||||||
|
|
|
@ -0,0 +1,109 @@
|
||||||
|
/*
|
||||||
|
* 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.forms;
|
||||||
|
|
||||||
|
import org.junit.Assert;
|
||||||
|
import org.junit.ClassRule;
|
||||||
|
import org.junit.Rule;
|
||||||
|
import org.junit.Test;
|
||||||
|
import org.keycloak.testsuite.OAuthClient;
|
||||||
|
import org.keycloak.testsuite.pages.AppPage;
|
||||||
|
import org.keycloak.testsuite.pages.AppPage.RequestType;
|
||||||
|
import org.keycloak.testsuite.pages.LoginPage;
|
||||||
|
import org.keycloak.testsuite.pages.LoginPasswordResetPage;
|
||||||
|
import org.keycloak.testsuite.pages.LoginPasswordUpdatePage;
|
||||||
|
import org.keycloak.testsuite.pages.LoginRecoverUsernamePage;
|
||||||
|
import org.keycloak.testsuite.rule.GreenMailRule;
|
||||||
|
import org.keycloak.testsuite.rule.KeycloakRule;
|
||||||
|
import org.keycloak.testsuite.rule.WebResource;
|
||||||
|
import org.keycloak.testsuite.rule.WebRule;
|
||||||
|
import org.openqa.selenium.WebDriver;
|
||||||
|
|
||||||
|
import javax.mail.MessagingException;
|
||||||
|
import javax.mail.internet.MimeMessage;
|
||||||
|
import java.io.IOException;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @author <a href="mailto:sthorger@redhat.com">Stian Thorgersen</a>
|
||||||
|
*/
|
||||||
|
public class LoginRecoverUsernameTest {
|
||||||
|
|
||||||
|
@ClassRule
|
||||||
|
public static KeycloakRule keycloakRule = new KeycloakRule();
|
||||||
|
|
||||||
|
@Rule
|
||||||
|
public WebRule webRule = new WebRule(this);
|
||||||
|
|
||||||
|
@Rule
|
||||||
|
public GreenMailRule greenMail = new GreenMailRule();
|
||||||
|
|
||||||
|
@WebResource
|
||||||
|
protected WebDriver driver;
|
||||||
|
|
||||||
|
@WebResource
|
||||||
|
protected OAuthClient oauth;
|
||||||
|
|
||||||
|
@WebResource
|
||||||
|
protected AppPage appPage;
|
||||||
|
|
||||||
|
@WebResource
|
||||||
|
protected LoginPage loginPage;
|
||||||
|
|
||||||
|
@WebResource
|
||||||
|
protected LoginRecoverUsernamePage recoverUsernamePage;
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void resetPassword() throws IOException, MessagingException {
|
||||||
|
loginPage.open();
|
||||||
|
loginPage.recoverUsername();
|
||||||
|
|
||||||
|
recoverUsernamePage.assertCurrent();
|
||||||
|
|
||||||
|
recoverUsernamePage.recoverUsername("test-user@localhost");
|
||||||
|
|
||||||
|
loginPage.assertCurrent();
|
||||||
|
|
||||||
|
Assert.assertTrue(driver.getPageSource().contains("You should receive an email shortly with your username"));
|
||||||
|
|
||||||
|
Assert.assertEquals(1, greenMail.getReceivedMessages().length);
|
||||||
|
|
||||||
|
MimeMessage message = greenMail.getReceivedMessages()[0];
|
||||||
|
|
||||||
|
String body = (String) message.getContent();
|
||||||
|
Assert.assertTrue(body.contains("The username for your Keycloak account is test-user@localhost"));
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void resetPasswordWrongEmail() throws IOException, MessagingException {
|
||||||
|
loginPage.open();
|
||||||
|
loginPage.recoverUsername();
|
||||||
|
|
||||||
|
recoverUsernamePage.assertCurrent();
|
||||||
|
|
||||||
|
recoverUsernamePage.recoverUsername("invalid");
|
||||||
|
|
||||||
|
recoverUsernamePage.assertCurrent();
|
||||||
|
|
||||||
|
Assert.assertEquals("Invalid username or email.", recoverUsernamePage.getMessage());
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
|
@ -56,6 +56,9 @@ public class LoginPage extends AbstractPage {
|
||||||
@FindBy(linkText = "Password")
|
@FindBy(linkText = "Password")
|
||||||
private WebElement resetPasswordLink;
|
private WebElement resetPasswordLink;
|
||||||
|
|
||||||
|
@FindBy(linkText = "Username")
|
||||||
|
private WebElement recoverUsernameLink;
|
||||||
|
|
||||||
@FindBy(id = "loginError")
|
@FindBy(id = "loginError")
|
||||||
private WebElement loginErrorMessage;
|
private WebElement loginErrorMessage;
|
||||||
|
|
||||||
|
@ -93,6 +96,11 @@ public class LoginPage extends AbstractPage {
|
||||||
resetPasswordLink.click();
|
resetPasswordLink.click();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public void recoverUsername() {
|
||||||
|
recoverUsernameLink.click();
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void open() {
|
public void open() {
|
||||||
oauth.openLoginForm();
|
oauth.openLoginForm();
|
||||||
|
|
|
@ -0,0 +1,59 @@
|
||||||
|
/*
|
||||||
|
* 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 LoginRecoverUsernamePage extends AbstractPage {
|
||||||
|
|
||||||
|
@FindBy(id = "email")
|
||||||
|
private WebElement emailInput;
|
||||||
|
|
||||||
|
@FindBy(css = "input[type=\"submit\"]")
|
||||||
|
private WebElement submitButton;
|
||||||
|
|
||||||
|
@FindBy(css = ".feedback > p > strong")
|
||||||
|
private WebElement emailErrorMessage;
|
||||||
|
|
||||||
|
public void recoverUsername(String email) {
|
||||||
|
emailInput.sendKeys(email);
|
||||||
|
|
||||||
|
submitButton.click();
|
||||||
|
}
|
||||||
|
|
||||||
|
public boolean isCurrent() {
|
||||||
|
return driver.getTitle().equals("Forgot Your Username?");
|
||||||
|
}
|
||||||
|
|
||||||
|
public void open() {
|
||||||
|
throw new UnsupportedOperationException();
|
||||||
|
}
|
||||||
|
|
||||||
|
public String getMessage() {
|
||||||
|
return emailErrorMessage != null ? emailErrorMessage.getText() : null;
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
Loading…
Reference in a new issue