Details
diff --git a/forms/src/main/resources/org/keycloak/forms/messages.properties b/forms/src/main/resources/org/keycloak/forms/messages.properties
index aa13cf8..5353df4 100644
--- a/forms/src/main/resources/org/keycloak/forms/messages.properties
+++ b/forms/src/main/resources/org/keycloak/forms/messages.properties
@@ -59,6 +59,7 @@ errorHeader=Error!
emailForgotHeader=Forgot Your Password?
emailUpdateHeader=Update password
emailSent=You should receive an email shortly with further instructions.
+emailSendError=Failed to send email, please try again later
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.
diff --git a/services/src/main/java/org/keycloak/services/email/EmailException.java b/services/src/main/java/org/keycloak/services/email/EmailException.java
new file mode 100644
index 0000000..d15c022
--- /dev/null
+++ b/services/src/main/java/org/keycloak/services/email/EmailException.java
@@ -0,0 +1,12 @@
+package org.keycloak.services.email;
+
+/**
+ * @author <a href="mailto:sthorger@redhat.com">Stian Thorgersen</a>
+ */
+public class EmailException extends Exception {
+
+ public EmailException(Throwable cause) {
+ super(cause);
+ }
+
+}
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 185ca83..b2f72ce 100755
--- a/services/src/main/java/org/keycloak/services/email/EmailSender.java
+++ b/services/src/main/java/org/keycloak/services/email/EmailSender.java
@@ -54,7 +54,7 @@ public class EmailSender {
this.config = config;
}
- public void send(String address, String subject, String body) throws MessagingException {
+ public void send(String address, String subject, String body) throws EmailException {
Properties props = new Properties();
props.setProperty("mail.smtp.host", config.get("host"));
@@ -81,24 +81,28 @@ public class EmailSender {
String from = config.get("from");
- Session session = Session.getInstance(props);
-
- Message msg = new MimeMessage(session);
- msg.setFrom(new InternetAddress(from));
- msg.setSubject(subject);
- msg.setText(body);
- msg.saveChanges();
-
- Transport transport = session.getTransport("smtp");
- if (auth) {
- transport.connect(config.get("user"), config.get("password"));
- } else {
- transport.connect();
+ try {
+ Session session = Session.getInstance(props);
+
+ Message msg = new MimeMessage(session);
+ msg.setFrom(new InternetAddress(from));
+ msg.setSubject(subject);
+ msg.setText(body);
+ msg.saveChanges();
+
+ Transport transport = session.getTransport("smtp");
+ if (auth) {
+ transport.connect(config.get("user"), config.get("password"));
+ } else {
+ transport.connect();
+ }
+ transport.sendMessage(msg, new InternetAddress[]{new InternetAddress(address)});
+ } catch (Exception e) {
+ throw new EmailException(e);
}
- transport.sendMessage(msg, new InternetAddress[] { new InternetAddress(address) });
}
- public void sendEmailVerification(UserModel user, RealmModel realm, AccessCodeEntry accessCode, UriInfo uriInfo) {
+ public void sendEmailVerification(UserModel user, RealmModel realm, AccessCodeEntry accessCode, UriInfo uriInfo) throws EmailException {
UriBuilder builder = Urls.loginActionEmailVerificationBuilder(uriInfo.getBaseUri());
builder.queryParam("key", accessCode.getId());
@@ -115,14 +119,10 @@ public class EmailSender {
sb.append("Thanks,\n");
sb.append("The Keycloak Team");
- try {
- send(user.getEmail(), "Verify email", sb.toString());
- } catch (Exception e1) {
- log.warn("Failed to send email verification");
- }
+ send(user.getEmail(), "Verify email", sb.toString());
}
- public void sendPasswordReset(UserModel user, RealmModel realm, AccessCodeEntry accessCode, UriInfo uriInfo) {
+ public void sendPasswordReset(UserModel user, RealmModel realm, AccessCodeEntry accessCode, UriInfo uriInfo) throws EmailException {
UriBuilder builder = Urls.loginPasswordResetBuilder(uriInfo.getBaseUri());
builder.queryParam("key", accessCode.getId());
@@ -140,11 +140,7 @@ public class EmailSender {
sb.append("Thanks,\n");
sb.append("The Keycloak Team");
- try {
- send(user.getEmail(), "Reset password link", sb.toString());
- } catch (Exception e) {
- log.warn("Failed to send reset password link", e);
- }
+ send(user.getEmail(), "Reset password link", sb.toString());
}
}
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 a62a064..a9fb6de 100755
--- a/services/src/main/java/org/keycloak/services/resources/AccountService.java
+++ b/services/src/main/java/org/keycloak/services/resources/AccountService.java
@@ -46,7 +46,6 @@ import javax.ws.rs.*;
import javax.ws.rs.core.*;
import javax.ws.rs.ext.Providers;
import java.net.URI;
-import java.net.URISyntaxException;
import java.util.List;
/**
@@ -175,8 +174,7 @@ public class AccountService {
user.setLastName(formData.getFirst("lastName"));
user.setEmail(formData.getFirst("email"));
- return Flows.forms(realm, request, uriInfo).setUser(user).setError("accountUpdated")
- .setErrorType(FormFlows.MessageType.SUCCESS).forwardToAccount();
+ return Flows.forms(realm, request, uriInfo).setUser(user).setSuccess("accountUpdated").forwardToAccount();
}
@Path("totp-remove")
@@ -190,8 +188,7 @@ public class AccountService {
UserModel user = auth.getUser();
user.setTotp(false);
- return Flows.forms(realm, request, uriInfo).setError("successTotpRemoved").setErrorType(FormFlows.MessageType.SUCCESS)
- .setUser(user).forwardToTotp();
+ return Flows.forms(realm, request, uriInfo).setSuccess("successTotpRemoved").setUser(user).forwardToTotp();
}
@Path("totp")
@@ -222,8 +219,7 @@ public class AccountService {
user.setTotp(true);
- return Flows.forms(realm, request, uriInfo).setError("successTotp").setErrorType(FormFlows.MessageType.SUCCESS)
- .setUser(user).forwardToTotp();
+ return Flows.forms(realm, request, uriInfo).setSuccess("successTotp").setUser(user).forwardToTotp();
}
@Path("password")
@@ -266,8 +262,7 @@ public class AccountService {
realm.updateCredential(user, credentials);
- return Flows.forms(realm, request, uriInfo).setUser(user).setError("accountPasswordUpdated")
- .setErrorType(FormFlows.MessageType.SUCCESS).forwardToPassword();
+ return Flows.forms(realm, request, uriInfo).setUser(user).setSuccess("accountPasswordUpdated").forwardToPassword();
}
@Path("login-redirect")
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 e95c3aa..fea4f98 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
@@ -28,6 +28,7 @@ import org.keycloak.models.RoleModel;
import org.keycloak.models.UserModel;
import org.keycloak.models.UserModel.RequiredAction;
import org.keycloak.services.FormService;
+import org.keycloak.services.email.EmailException;
import org.keycloak.services.email.EmailSender;
import org.keycloak.services.managers.AccessCodeEntry;
import org.keycloak.services.messages.Messages;
@@ -51,8 +52,7 @@ public class FormFlows {
public static final String CODE = "code";
- // TODO refactor/rename "error" to "message" everywhere where it makes sense
- private String error;
+ private String message;
public static enum MessageType {SUCCESS, WARNING, ERROR};
private MessageType messageType = MessageType.ERROR;
@@ -86,7 +86,11 @@ public class FormFlows {
case UPDATE_PASSWORD:
return forwardToActionForm(Pages.LOGIN_UPDATE_PASSWORD, Messages.ACTION_WARN_PASSWD);
case VERIFY_EMAIL:
- new EmailSender(realm.getSmtpConfig()).sendEmailVerification(userModel, realm, accessCode, uriInfo);
+ try {
+ new EmailSender(realm.getSmtpConfig()).sendEmailVerification(userModel, realm, accessCode, uriInfo);
+ } catch (EmailException e) {
+ return setError("emailSendError").forwardToErrorPage();
+ }
return forwardToActionForm(Pages.LOGIN_VERIFY_EMAIL, Messages.ACTION_WARN_EMAIL);
default:
return Response.serverError().build();
@@ -146,7 +150,7 @@ public class FormFlows {
public Response forwardToForm(String template) {
- FormService.FormServiceDataBean formDataBean = new FormService.FormServiceDataBean(realm, userModel, formData, queryParams, error);
+ FormService.FormServiceDataBean formDataBean = new FormService.FormServiceDataBean(realm, userModel, formData, queryParams, message);
formDataBean.setMessageType(messageType);
return forwardToForm(template, formDataBean);
@@ -156,9 +160,9 @@ public class FormFlows {
// If no other message is set, notify user about required action in the warning window
// so it's clear that this is a req. action form not a login form
- if (error == null){
+ if (message == null){
messageType = MessageType.WARNING;
- error = warningSummary;
+ message = warningSummary;
}
return forwardToForm(template);
@@ -198,7 +202,7 @@ public class FormFlows {
public Response forwardToOAuthGrant(){
- FormService.FormServiceDataBean formDataBean = new FormService.FormServiceDataBean(realm, userModel, formData, queryParams, error);
+ FormService.FormServiceDataBean formDataBean = new FormService.FormServiceDataBean(realm, userModel, formData, queryParams, message);
formDataBean.setOAuthRealmRolesRequested((List<RoleModel>) request.getAttribute("realmRolesRequested"));
formDataBean.setOAuthResourceRolesRequested((MultivaluedMap<String, RoleModel>) request.getAttribute("resourceRolesRequested"));
@@ -222,13 +226,21 @@ public class FormFlows {
return this;
}
- public FormFlows setError(String error) {
- this.error = error;
+ public FormFlows setError(String message) {
+ this.message = message;
+ this.messageType = MessageType.ERROR;
return this;
}
- public FormFlows setErrorType(MessageType errorType) {
- this.messageType = errorType;
+ public FormFlows setSuccess(String message) {
+ this.message = message;
+ this.messageType = MessageType.SUCCESS;
+ return this;
+ }
+
+ public FormFlows setWarning(String message) {
+ this.message = message;
+ this.messageType = MessageType.WARNING;
return this;
}
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 3199c01..8faeb01 100755
--- a/services/src/main/java/org/keycloak/services/resources/RequiredActionsService.java
+++ b/services/src/main/java/org/keycloak/services/resources/RequiredActionsService.java
@@ -31,6 +31,7 @@ import org.keycloak.models.UserModel;
import org.keycloak.models.UserModel.RequiredAction;
import org.keycloak.models.utils.TimeBasedOTP;
import org.keycloak.representations.idm.CredentialRepresentation;
+import org.keycloak.services.email.EmailException;
import org.keycloak.services.email.EmailSender;
import org.keycloak.services.managers.AccessCodeEntry;
import org.keycloak.services.managers.AuthenticationManager;
@@ -265,10 +266,14 @@ public class RequiredActionsService {
accessCode.setRequiredActions(requiredActions);
accessCode.setExpiration(System.currentTimeMillis() / 1000 + realm.getAccessCodeLifespanUserAction());
- new EmailSender(realm.getSmtpConfig()).sendPasswordReset(user, realm, accessCode, uriInfo);
+ try {
+ new EmailSender(realm.getSmtpConfig()).sendPasswordReset(user, realm, accessCode, uriInfo);
+ } catch (EmailException e) {
+ logger.error("Failed to send password reset email", e);
+ return Flows.forms(realm, request, uriInfo).setError("emailSendError").forwardToErrorPage();
+ }
- return Flows.forms(realm, request, uriInfo).setError("emailSent").setErrorType(FormFlows.MessageType.SUCCESS)
- .forwardToPasswordReset();
+ return Flows.forms(realm, request, uriInfo).setSuccess("emailSent").forwardToPasswordReset();
}
private AccessCodeEntry getAccessCodeEntry(RequiredAction requiredAction) {
diff --git a/services/src/main/java/org/keycloak/services/resources/SocialResource.java b/services/src/main/java/org/keycloak/services/resources/SocialResource.java
index 908844c..9877bee 100755
--- a/services/src/main/java/org/keycloak/services/resources/SocialResource.java
+++ b/services/src/main/java/org/keycloak/services/resources/SocialResource.java
@@ -27,7 +27,6 @@ import org.jboss.resteasy.spi.HttpRequest;
import org.jboss.resteasy.spi.HttpResponse;
import org.keycloak.models.KeycloakSession;
import org.keycloak.models.RealmModel;
-import org.keycloak.models.RoleModel;
import org.keycloak.models.SocialLinkModel;
import org.keycloak.models.UserModel;
import org.keycloak.services.managers.AuthenticationManager;
diff --git a/services/src/test/java/org/keycloak/services/email/EmailSenderTest.java b/services/src/test/java/org/keycloak/services/email/EmailSenderTest.java
index e749f16..fa76623 100755
--- a/services/src/test/java/org/keycloak/services/email/EmailSenderTest.java
+++ b/services/src/test/java/org/keycloak/services/email/EmailSenderTest.java
@@ -55,7 +55,7 @@ public class EmailSenderTest {
}
@Test
- public void sendMail() throws MessagingException, IOException {
+ public void sendMail() throws Exception {
emailSender.send("test@test.com", "Test subject", "Test body");
MimeMessage[] receivedMessages = greenMail.getReceivedMessages();