keycloak-aplcache
Changes
forms/src/main/resources/META-INF/resources/forms/theme/default/login-username-reminder.ftl 25(+25 -0)
Details
diff --git a/forms/src/main/java/org/keycloak/forms/MessageBean.java b/forms/src/main/java/org/keycloak/forms/MessageBean.java
index c24e66a..85eee93 100644
--- a/forms/src/main/java/org/keycloak/forms/MessageBean.java
+++ b/forms/src/main/java/org/keycloak/forms/MessageBean.java
@@ -47,6 +47,10 @@ public class MessageBean {
return summary;
}
+ public String getType() {
+ return this.type.toString().toLowerCase();
+ }
+
public boolean isSuccess(){
return FormFlows.MessageType.SUCCESS.equals(this.type);
}
diff --git a/forms/src/main/java/org/keycloak/forms/RealmBean.java b/forms/src/main/java/org/keycloak/forms/RealmBean.java
index 7b5e6b2..6eeabab 100755
--- a/forms/src/main/java/org/keycloak/forms/RealmBean.java
+++ b/forms/src/main/java/org/keycloak/forms/RealmBean.java
@@ -62,5 +62,9 @@ public class RealmBean {
public boolean isRegistrationAllowed() {
return realm.isRegistrationAllowed();
}
+
+ public boolean isResetPasswordAllowed() {
+ return realm.isResetPasswordAllowed();
+ }
}
diff --git a/forms/src/main/java/org/keycloak/forms/UrlBean.java b/forms/src/main/java/org/keycloak/forms/UrlBean.java
index 579659d..f6f5f12 100755
--- a/forms/src/main/java/org/keycloak/forms/UrlBean.java
+++ b/forms/src/main/java/org/keycloak/forms/UrlBean.java
@@ -135,6 +135,10 @@ public class UrlBean {
return Urls.loginPasswordReset(baseURI, realm.getId()).toString();
}
+ public String getLoginUsernameReminderUrl() {
+ return Urls.loginUsernameReminder(baseURI, realm.getId()).toString();
+ }
+
public String getLoginEmailVerificationUrl() {
return Urls.loginActionEmailVerification(baseURI, realm.getId()).toString();
}
diff --git a/forms/src/main/java/org/keycloak/service/FormServiceImpl.java b/forms/src/main/java/org/keycloak/service/FormServiceImpl.java
index 596d893..01ca837 100755
--- a/forms/src/main/java/org/keycloak/service/FormServiceImpl.java
+++ b/forms/src/main/java/org/keycloak/service/FormServiceImpl.java
@@ -64,6 +64,7 @@ public class FormServiceImpl implements FormService {
commandMap.put(Pages.LOGIN_UPDATE_PROFILE, new CommandCommon());
commandMap.put(Pages.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.ACCESS, new CommandCommon());
commandMap.put(Pages.SOCIAL, new CommandCommon());
diff --git a/forms/src/main/resources/META-INF/resources/forms/theme/default/login.ftl b/forms/src/main/resources/META-INF/resources/forms/theme/default/login.ftl
index 248f17b..518a92c 100755
--- a/forms/src/main/resources/META-INF/resources/forms/theme/default/login.ftl
+++ b/forms/src/main/resources/META-INF/resources/forms/theme/default/login.ftl
@@ -24,10 +24,6 @@
<input class="btn-primary" name="login" type="submit" value="Log In"/>
<input class="btn-secondary" name="cancel" type="submit" value="Cancel"/>
</div>
-
- <div class="aside-btn">
- <p>Forgot <a href="${url.loginPasswordResetUrl}">Password</a>?</p>
- </div>
</form>
</div>
@@ -37,6 +33,9 @@
<#if realm.registrationAllowed>
<p>${rb.getString('noAccount')} <a href="${url.registrationUrl}">${rb.getString('register')}</a>.</p>
</#if>
+ <#if realm.resetPasswordAllowed>
+ <p>Forgot <a href="${url.loginUsernameReminderUrl}">Username</a> / <a href="${url.loginPasswordResetUrl}">Password</a>?</p>
+ </#if>
</div>
</#if>
diff --git a/forms/src/main/resources/META-INF/resources/forms/theme/default/login-username-reminder.ftl b/forms/src/main/resources/META-INF/resources/forms/theme/default/login-username-reminder.ftl
new file mode 100755
index 0000000..a00d87e
--- /dev/null
+++ b/forms/src/main/resources/META-INF/resources/forms/theme/default/login-username-reminder.ftl
@@ -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>
\ No newline at end of file
diff --git a/forms/src/main/resources/META-INF/resources/forms/theme/default/template-login.ftl b/forms/src/main/resources/META-INF/resources/forms/theme/default/template-login.ftl
index c46227b..30f422c 100644
--- a/forms/src/main/resources/META-INF/resources/forms/theme/default/template-login.ftl
+++ b/forms/src/main/resources/META-INF/resources/forms/theme/default/template-login.ftl
@@ -44,6 +44,13 @@
</p>
</div>
</#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">
</div>
diff --git a/forms/src/main/resources/org/keycloak/forms/messages.properties b/forms/src/main/resources/org/keycloak/forms/messages.properties
index 5353df4..bb08d42 100644
--- a/forms/src/main/resources/org/keycloak/forms/messages.properties
+++ b/forms/src/main/resources/org/keycloak/forms/messages.properties
@@ -64,5 +64,9 @@ emailError=Invalid username or email.
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.
+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
accountPasswordUpdated=Your password has been updated
\ No newline at end of file
diff --git a/model/api/src/main/java/org/keycloak/models/RealmModel.java b/model/api/src/main/java/org/keycloak/models/RealmModel.java
index ce76dcb..7771097 100755
--- a/model/api/src/main/java/org/keycloak/models/RealmModel.java
+++ b/model/api/src/main/java/org/keycloak/models/RealmModel.java
@@ -86,6 +86,8 @@ public interface RealmModel extends RoleContainerModel, RoleMapperModel, ScopeMa
UserModel getUser(String name);
+ UserModel getUserByEmail(String email);
+
UserModel addUser(String username);
boolean removeUser(String name);
diff --git a/model/jpa/src/main/java/org/keycloak/models/jpa/RealmAdapter.java b/model/jpa/src/main/java/org/keycloak/models/jpa/RealmAdapter.java
index 201f286..b6227cd 100755
--- a/model/jpa/src/main/java/org/keycloak/models/jpa/RealmAdapter.java
+++ b/model/jpa/src/main/java/org/keycloak/models/jpa/RealmAdapter.java
@@ -440,6 +440,15 @@ public class RealmAdapter implements RealmModel {
}
@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
public UserModel addUser(String username) {
UserEntity entity = new UserEntity();
entity.setLoginName(username);
diff --git a/model/picketlink/src/main/java/org/keycloak/models/picketlink/RealmAdapter.java b/model/picketlink/src/main/java/org/keycloak/models/picketlink/RealmAdapter.java
index fb2dca6..0c8bfc4 100755
--- a/model/picketlink/src/main/java/org/keycloak/models/picketlink/RealmAdapter.java
+++ b/model/picketlink/src/main/java/org/keycloak/models/picketlink/RealmAdapter.java
@@ -509,6 +509,14 @@ public class RealmAdapter implements RealmModel {
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) {
return SampleModel.getUser(getIdm(), name);
}
diff --git a/services/src/main/java/org/keycloak/services/email/EmailSender.java b/services/src/main/java/org/keycloak/services/email/EmailSender.java
index b2f72ce..ee2d33c 100755
--- a/services/src/main/java/org/keycloak/services/email/EmailSender.java
+++ b/services/src/main/java/org/keycloak/services/email/EmailSender.java
@@ -108,16 +108,17 @@ public class EmailSender {
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("If this was you, click the link below to verify your email address:\n");
sb.append(uri.toString());
sb.append("\n\nThis link will expire within ").append(TimeUnit.SECONDS.toMinutes(realm.getAccessCodeLifespanUserAction()));
sb.append(" minutes.\n\n");
- sb.append("If you didn't create this account, just ignore this message.\n\n");
- sb.append("Thanks,\n");
- sb.append("The Keycloak Team");
+ sb.append("If you didn't create this account, just ignore this message.\n");
+
+ addFooter(sb);
send(user.getEmail(), "Verify email", sb.toString());
}
@@ -128,19 +129,44 @@ public class EmailSender {
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("If this was you, click on the link below to set a new password:\n");
sb.append(uri.toString());
sb.append("\n\nThis link will expire within ").append(TimeUnit.SECONDS.toMinutes(realm.getAccessCodeLifespanUserAction()));
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("Thanks,\n");
- sb.append("The Keycloak Team");
+ sb.append("If you don't want to reset your password, just ignore this message and nothing will be changed.\n");
+
+ addFooter(sb);
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");
+ }
+
+
}
diff --git a/services/src/main/java/org/keycloak/services/resources/flows/FormFlows.java b/services/src/main/java/org/keycloak/services/resources/flows/FormFlows.java
index fea4f98..2059409 100755
--- a/services/src/main/java/org/keycloak/services/resources/flows/FormFlows.java
+++ b/services/src/main/java/org/keycloak/services/resources/flows/FormFlows.java
@@ -176,6 +176,10 @@ public class FormFlows {
return forwardToForm(Pages.LOGIN_RESET_PASSWORD);
}
+ public Response forwardToUsernameReminder() {
+ return forwardToForm(Pages.LOGIN_USERNAME_REMINDER);
+ }
+
public Response forwardToLoginTotp() {
return forwardToForm(Pages.LOGIN_TOTP);
}
diff --git a/services/src/main/java/org/keycloak/services/resources/flows/Pages.java b/services/src/main/java/org/keycloak/services/resources/flows/Pages.java
index 9ee5ad4..72d10e5 100644
--- a/services/src/main/java/org/keycloak/services/resources/flows/Pages.java
+++ b/services/src/main/java/org/keycloak/services/resources/flows/Pages.java
@@ -46,6 +46,8 @@ public class Pages {
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 ERROR = "error.ftl";
diff --git a/services/src/main/java/org/keycloak/services/resources/flows/Urls.java b/services/src/main/java/org/keycloak/services/resources/flows/Urls.java
index 169da3f..15d6d36 100755
--- a/services/src/main/java/org/keycloak/services/resources/flows/Urls.java
+++ b/services/src/main/java/org/keycloak/services/resources/flows/Urls.java
@@ -100,6 +100,14 @@ public class Urls {
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) {
return UriBuilder.fromUri(baseUri).path(RealmsResource.class);
}
diff --git a/services/src/main/java/org/keycloak/services/resources/RequiredActionsService.java b/services/src/main/java/org/keycloak/services/resources/RequiredActionsService.java
index 8faeb01..e8417b3 100755
--- a/services/src/main/java/org/keycloak/services/resources/RequiredActionsService.java
+++ b/services/src/main/java/org/keycloak/services/resources/RequiredActionsService.java
@@ -276,6 +276,45 @@ public class RequiredActionsService {
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) {
String code = uriInfo.getQueryParameters().getFirst(FormFlows.CODE);
if (code == null) {
diff --git a/testsuite/integration/src/test/java/org/keycloak/testsuite/forms/LoginRecoverUsernameTest.java b/testsuite/integration/src/test/java/org/keycloak/testsuite/forms/LoginRecoverUsernameTest.java
new file mode 100755
index 0000000..006bb26
--- /dev/null
+++ b/testsuite/integration/src/test/java/org/keycloak/testsuite/forms/LoginRecoverUsernameTest.java
@@ -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());
+ }
+
+}
diff --git a/testsuite/integration/src/test/java/org/keycloak/testsuite/pages/LoginPage.java b/testsuite/integration/src/test/java/org/keycloak/testsuite/pages/LoginPage.java
index 779873f..3a1f72e 100644
--- a/testsuite/integration/src/test/java/org/keycloak/testsuite/pages/LoginPage.java
+++ b/testsuite/integration/src/test/java/org/keycloak/testsuite/pages/LoginPage.java
@@ -56,6 +56,9 @@ public class LoginPage extends AbstractPage {
@FindBy(linkText = "Password")
private WebElement resetPasswordLink;
+ @FindBy(linkText = "Username")
+ private WebElement recoverUsernameLink;
+
@FindBy(id = "loginError")
private WebElement loginErrorMessage;
@@ -93,6 +96,11 @@ public class LoginPage extends AbstractPage {
resetPasswordLink.click();
}
+ public void recoverUsername() {
+ recoverUsernameLink.click();
+ }
+
+
@Override
public void open() {
oauth.openLoginForm();
diff --git a/testsuite/integration/src/test/java/org/keycloak/testsuite/pages/LoginRecoverUsernamePage.java b/testsuite/integration/src/test/java/org/keycloak/testsuite/pages/LoginRecoverUsernamePage.java
new file mode 100644
index 0000000..5c0050f
--- /dev/null
+++ b/testsuite/integration/src/test/java/org/keycloak/testsuite/pages/LoginRecoverUsernamePage.java
@@ -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;
+ }
+
+}