keycloak-memoizeit
Changes
forms/src/main/resources/META-INF/resources/forms/theme/default/css/img/feedback-success-arrow-down.png 0(+0 -0)
Details
diff --git a/forms/src/main/java/org/keycloak/forms/ErrorBean.java b/forms/src/main/java/org/keycloak/forms/ErrorBean.java
index ff140bd..d6c18b6 100644
--- a/forms/src/main/java/org/keycloak/forms/ErrorBean.java
+++ b/forms/src/main/java/org/keycloak/forms/ErrorBean.java
@@ -21,6 +21,8 @@
*/
package org.keycloak.forms;
+import org.keycloak.services.resources.flows.FormFlows;
+
/**
* @author <a href="mailto:sthorger@redhat.com">Stian Thorgersen</a>
*/
@@ -28,12 +30,32 @@ public class ErrorBean {
private String summary;
+ private FormFlows.ErrorType type;
+
+ // Message is considered ERROR by default
public ErrorBean(String summary) {
+ this(summary, FormFlows.ErrorType.ERROR);
+ }
+
+ public ErrorBean(String summary, FormFlows.ErrorType type) {
this.summary = summary;
+ this.type = type;
}
public String getSummary() {
return summary;
}
+ public boolean isSuccess(){
+ return FormFlows.ErrorType.SUCCESS.equals(this.type);
+ }
+
+ public boolean isWarning(){
+ return FormFlows.ErrorType.WARNING.equals(this.type);
+ }
+
+ public boolean isError(){
+ return FormFlows.ErrorType.ERROR.equals(this.type);
+ }
+
}
\ No newline at end of file
diff --git a/forms/src/main/java/org/keycloak/service/FormServiceImpl.java b/forms/src/main/java/org/keycloak/service/FormServiceImpl.java
index 1df2436..7efa87d 100644
--- a/forms/src/main/java/org/keycloak/service/FormServiceImpl.java
+++ b/forms/src/main/java/org/keycloak/service/FormServiceImpl.java
@@ -42,6 +42,7 @@ import org.keycloak.forms.TotpBean;
import org.keycloak.forms.UrlBean;
import org.keycloak.forms.UserBean;
import org.keycloak.services.FormService;
+import org.keycloak.services.resources.flows.FormFlows;
import org.keycloak.services.resources.flows.Pages;
/**
@@ -149,6 +150,10 @@ public class FormServiceImpl implements FormService {
private class CommandPassword implements Command {
public void exec(Map<String, Object> attributes, FormServiceDataBean dataBean) {
+ if (dataBean.getError() != null){
+ attributes.put("message", new ErrorBean(dataBean.getError(), dataBean.getErrorType()));
+ }
+
RealmBean realm = new RealmBean(dataBean.getRealm());
attributes.put("realm", realm);
diff --git a/forms/src/main/resources/META-INF/resources/forms/theme/default/css/forms.css b/forms/src/main/resources/META-INF/resources/forms/theme/default/css/forms.css
index 062012c..78a16e4 100644
--- a/forms/src/main/resources/META-INF/resources/forms/theme/default/css/forms.css
+++ b/forms/src/main/resources/META-INF/resources/forms/theme/default/css/forms.css
@@ -250,19 +250,19 @@ button.primary:enabled:active {
background-position: 1.27272727272727em 1.63636363636364em;
}
.feedback.error {
- background-image: url(img/feedback-error-arrow-down.svg);
+ background-image: url(img/feedback-error-arrow-down.png);
}
.feedback.error p {
border-color: #b91415;
- background-image: url(img/feedback-error-sign.svg);
+ background-image: url(img/feedback-error-sign.png);
background-color: #f8e7e7;
}
.feedback.success {
- background-image: url(img/feedback-success-arrow-down.svg);
+ background-image: url(img/feedback-success-arrow-down.png);
}
.feedback.success p {
border-color: #4b9e39;
- background-image: url(img/feedback-success-sign.svg);
+ background-image: url(img/feedback-success-sign.png);
background-color: #e4f1e1;
}
.feedback.warning p {
diff --git a/forms/src/main/resources/META-INF/resources/forms/theme/default/css/img/feedback-success-arrow-down.png b/forms/src/main/resources/META-INF/resources/forms/theme/default/css/img/feedback-success-arrow-down.png
new file mode 100644
index 0000000..03cc0c4
Binary files /dev/null and b/forms/src/main/resources/META-INF/resources/forms/theme/default/css/img/feedback-success-arrow-down.png differ
diff --git a/forms/src/main/resources/META-INF/resources/forms/theme/default/css/img/feedback-success-sign.png b/forms/src/main/resources/META-INF/resources/forms/theme/default/css/img/feedback-success-sign.png
new file mode 100644
index 0000000..640bd71
Binary files /dev/null and b/forms/src/main/resources/META-INF/resources/forms/theme/default/css/img/feedback-success-sign.png differ
diff --git a/forms/src/main/resources/META-INF/resources/forms/theme/default/css/login-register.css b/forms/src/main/resources/META-INF/resources/forms/theme/default/css/login-register.css
index 073ff39..8908e52 100644
--- a/forms/src/main/resources/META-INF/resources/forms/theme/default/css/login-register.css
+++ b/forms/src/main/resources/META-INF/resources/forms/theme/default/css/login-register.css
@@ -324,6 +324,12 @@ a.zocial:before {
line-height: 1.3em;
}
+.rcue-login-register.reset p.subtitle {
+ margin-bottom: 10px;
+ position: inherit;
+ text-align: right;
+}
+
.rcue-login-register .background-area p.instruction.instruction.second {
color: #999999;
}
diff --git a/forms/src/main/resources/META-INF/resources/forms/theme/default/login-reset-password.ftl b/forms/src/main/resources/META-INF/resources/forms/theme/default/login-reset-password.ftl
index 6aaddd7..60ae59a 100755
--- a/forms/src/main/resources/META-INF/resources/forms/theme/default/login-reset-password.ftl
+++ b/forms/src/main/resources/META-INF/resources/forms/theme/default/login-reset-password.ftl
@@ -1,30 +1,36 @@
<#import "template-login-action.ftl" as layout>
-<@layout.registrationLayout bodyClass=""; section>
+<@layout.registrationLayout bodyClass="reset"; section>
<#if section = "title">
- Reset password
+ ${rb.getString('emailForgotHeader')}
<#elseif section = "header">
- Reset password
+ ${rb.getString('emailForgotHeader')}
<#elseif section = "form">
<div id="form">
+ <#if message?has_content>
+ <#if message.success>
+ <div class="feedback success bottom-left show"><p><strong>${rb.getString('successHeader')}</strong> ${rb.getString(message.summary)}</p></div>
+ </#if>
+ <#if message.error>
+ <div class="feedback error bottom-left show"><p><strong>${rb.getString('errorHeader')}</strong><br/>${rb.getString(message.summary)}</p></div>
+ </#if>
+ </#if>
+
+ <p class="instruction">${rb.getString('emailInstruction')}</p>
<form action="${url.passwordResetUrl}" method="post">
<div>
- <label for="username">${rb.getString('username')}</label>
- <input id="username" name="username" type="text" />
+ <label for="username">${rb.getString('username')}</label><input id="username" name="username" type="text" />
</div>
<div>
- <label for="email">${rb.getString('email')}</label>
- <input type="text" id="email" name="email" />
+ <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" >
<div id="info">
diff --git a/forms/src/main/resources/META-INF/resources/forms/theme/default/login-update-password.ftl b/forms/src/main/resources/META-INF/resources/forms/theme/default/login-update-password.ftl
index 7a13ecc..a9b1ed2 100755
--- a/forms/src/main/resources/META-INF/resources/forms/theme/default/login-update-password.ftl
+++ b/forms/src/main/resources/META-INF/resources/forms/theme/default/login-update-password.ftl
@@ -1,24 +1,22 @@
<#import "template-login-action.ftl" as layout>
-<@layout.registrationLayout bodyClass=""; section>
+<@layout.registrationLayout bodyClass="reset"; section>
<#if section = "title">
- Update password
+ ${rb.getString('emailUpdateHeader')}
<#elseif section = "header">
- Update password
+ ${rb.getString('emailUpdateHeader')}
<#elseif section = "form">
<div id="form">
<form action="${url.passwordUrl}" method="post">
<div>
- <label for="password-new">${rb.getString('passwordNew')}</label>
- <input type="password" id="password-new" name="password-new" />
+ <label for="password-new">${rb.getString('passwordNew')}</label><input type="password" id="password-new" name="password-new" />
</div>
<div>
- <label for="password-confirm">${rb.getString('passwordConfirm')}</label>
- <input type="password" id="password-confirm" name="password-confirm" />
+ <label for="password-confirm">${rb.getString('passwordConfirm')}</label><input type="password" id="password-confirm" name="password-confirm" />
</div>
<input class="btn-primary" type="submit" value="Submit" />
diff --git a/forms/src/main/resources/org/keycloak/forms/messages.properties b/forms/src/main/resources/org/keycloak/forms/messages.properties
index ce6667a..92722f9 100644
--- a/forms/src/main/resources/org/keycloak/forms/messages.properties
+++ b/forms/src/main/resources/org/keycloak/forms/messages.properties
@@ -17,6 +17,7 @@ email=Email
password=Password
passwordConfirm=Confirm Password
passwordNew=New Password
+passwordNewConfirm=New Password confirmation
authenticatorCode=One-time-password
clientCertificate=Client Certificate
@@ -39,3 +40,14 @@ invalidTotp=Invalid authenticator code
usernameExists=Username already exists
error=A system error has occured, contact admin
+
+successHeader=Success!
+errorHeader=Error!
+
+# Forgot password part
+
+emailForgotHeader=Forgot Your Password?
+emailUpdateHeader=Update password
+emailSent=You should receive an email shortly with further instructions.
+emailError=Invalid username or email.
+emailInstruction=Enter your username and email address and we will send you instructions on how to create a new password.
\ No newline at end of file
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 cdf52d8..8bd181c 100644
--- a/services/src/main/java/org/keycloak/services/email/EmailSender.java
+++ b/services/src/main/java/org/keycloak/services/email/EmailSender.java
@@ -103,9 +103,16 @@ public class EmailSender {
URI uri = builder.build(realm.getId());
StringBuilder sb = new StringBuilder();
+
+ 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");
- sb.append("Expires in " + 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("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");
try {
send(user.getEmail(), "Reset password link", sb.toString());
diff --git a/services/src/main/java/org/keycloak/services/FormService.java b/services/src/main/java/org/keycloak/services/FormService.java
index 8ddf517..50a0959 100644
--- a/services/src/main/java/org/keycloak/services/FormService.java
+++ b/services/src/main/java/org/keycloak/services/FormService.java
@@ -27,6 +27,7 @@ import javax.ws.rs.core.MultivaluedMap;
import org.keycloak.services.models.RealmModel;
import org.keycloak.services.models.UserModel;
+import org.keycloak.services.resources.flows.FormFlows;
/**
* @author <a href="mailto:vrockai@redhat.com">Viliam Rockai</a>
@@ -42,6 +43,9 @@ public interface FormService {
private RealmModel realm;
private UserModel userModel;
private String error;
+
+ private FormFlows.ErrorType errorType;
+
private MultivaluedMap<String, String> formData;
private URI baseURI;
@@ -121,5 +125,13 @@ public interface FormService {
public void setUserModel(UserModel userModel) {
this.userModel = userModel;
}
+
+ public FormFlows.ErrorType getErrorType() {
+ return errorType;
+ }
+
+ public void setErrorType(FormFlows.ErrorType errorType) {
+ this.errorType = errorType;
+ }
}
}
diff --git a/services/src/main/java/org/keycloak/services/resources/AccountService.java b/services/src/main/java/org/keycloak/services/resources/AccountService.java
index d4b643c..94e4682 100755
--- a/services/src/main/java/org/keycloak/services/resources/AccountService.java
+++ b/services/src/main/java/org/keycloak/services/resources/AccountService.java
@@ -240,8 +240,7 @@ public class AccountService {
UserModel user = realm.getUser(username);
if (user == null || !email.equals(user.getEmail())) {
- return Flows.forms(realm, request, uriInfo).setError("Invalid username or email")
- .forwardToAction(RequiredAction.UPDATE_PASSWORD);
+ return Flows.forms(realm, request, uriInfo).setError("emailError").forwardToPasswordReset();
}
Set<RequiredAction> requiredActions = new HashSet<RequiredAction>(user.getRequiredActions());
@@ -253,7 +252,8 @@ public class AccountService {
new EmailSender().sendPasswordReset(user, realm, accessCode, uriInfo);
- return Flows.forms(realm, request, uriInfo).forwardToPasswordReset();
+ return Flows.forms(realm, request, uriInfo).setError("emailSent").setErrorType(FormFlows.ErrorType.SUCCESS)
+ .forwardToPasswordReset();
}
@Path("email-verification")
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 0c106d0..aac8260 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
@@ -53,7 +53,12 @@ public class FormFlows {
public static final String SOCIAL_REGISTRATION = "socialRegistration";
public static final String CODE = "code";
+ // TODO refactor/rename "error" to "message" everywhere where it makes sense
private String error;
+
+ public static enum ErrorType {SUCCESS, WARNING, ERROR};
+ private ErrorType errorType;
+
private MultivaluedMap<String, String> formData;
private RealmModel realm;
@@ -98,6 +103,7 @@ public class FormFlows {
private Response forwardToForm(String template) {
FormService.FormServiceDataBean formDataBean = new FormService.FormServiceDataBean(realm, userModel, formData, error);
+ formDataBean.setErrorType(errorType == null ? ErrorType.ERROR : errorType);
// Getting URI needed by form processing service
ResteasyUriInfo uriInfo = request.getUri();
@@ -172,6 +178,11 @@ public class FormFlows {
return this;
}
+ public FormFlows setErrorType(ErrorType errorType) {
+ this.errorType = errorType;
+ return this;
+ }
+
public FormFlows setUser(UserModel userModel) {
this.userModel = userModel;
return this;
diff --git a/testsuite/src/test/java/org/keycloak/testsuite/forms/ResetPasswordTest.java b/testsuite/src/test/java/org/keycloak/testsuite/forms/ResetPasswordTest.java
index 8dd56e3..9b5ca0f 100644
--- a/testsuite/src/test/java/org/keycloak/testsuite/forms/ResetPasswordTest.java
+++ b/testsuite/src/test/java/org/keycloak/testsuite/forms/ResetPasswordTest.java
@@ -85,12 +85,14 @@ public class ResetPasswordTest {
resetPasswordPage.assertCurrent();
+ Assert.assertEquals("Success!", resetPasswordPage.getMessage());
+
Assert.assertEquals(1, greenMail.getReceivedMessages().length);
MimeMessage message = greenMail.getReceivedMessages()[0];
String body = (String) message.getContent();
- String changePasswordUrl = body.split("\n")[0];
+ String changePasswordUrl = body.split("\n")[3];
driver.navigate().to(changePasswordUrl.trim());
@@ -109,4 +111,34 @@ public class ResetPasswordTest {
Assert.assertEquals(RequestType.AUTH_RESPONSE, appPage.getRequestType());
}
+ @Test
+ public void resetPasswordWrongUsername() throws IOException, MessagingException {
+ loginPage.open();
+ loginPage.resetPassword();
+
+ resetPasswordPage.assertCurrent();
+
+ resetPasswordPage.changePassword("invalid", "test-user@localhost");
+
+ resetPasswordPage.assertCurrent();
+
+ Assert.assertNotEquals("Success!", resetPasswordPage.getMessage());
+ Assert.assertEquals("Error!", resetPasswordPage.getMessage());
+ }
+
+ @Test
+ public void resetPasswordWrongEmail() throws IOException, MessagingException {
+ loginPage.open();
+ loginPage.resetPassword();
+
+ resetPasswordPage.assertCurrent();
+
+ resetPasswordPage.changePassword("test-user@localhost", "invalid");
+
+ resetPasswordPage.assertCurrent();
+
+ Assert.assertNotEquals("Success!", resetPasswordPage.getMessage());
+ Assert.assertEquals("Error!", resetPasswordPage.getMessage());
+ }
+
}
diff --git a/testsuite/src/test/java/org/keycloak/testsuite/pages/LoginPasswordResetPage.java b/testsuite/src/test/java/org/keycloak/testsuite/pages/LoginPasswordResetPage.java
index 94edee8..45fc30a 100644
--- a/testsuite/src/test/java/org/keycloak/testsuite/pages/LoginPasswordResetPage.java
+++ b/testsuite/src/test/java/org/keycloak/testsuite/pages/LoginPasswordResetPage.java
@@ -38,6 +38,9 @@ public class LoginPasswordResetPage extends Page {
@FindBy(css = "input[type=\"submit\"]")
private WebElement submitButton;
+ @FindBy(css = ".feedback > p > strong")
+ private WebElement emailErrorMessage;
+
public void changePassword(String username, String email) {
usernameInput.sendKeys(username);
emailInput.sendKeys(email);
@@ -46,11 +49,15 @@ public class LoginPasswordResetPage extends Page {
}
public boolean isCurrent() {
- return driver.getTitle().equals("Reset password");
+ return driver.getTitle().equals("Forgot Your Password?");
}
public void open() {
throw new UnsupportedOperationException();
}
+ public String getMessage() {
+ return emailErrorMessage != null ? emailErrorMessage.getText() : null;
+ }
+
}