diff --git a/services/src/main/java/org/keycloak/email/DefaultEmailSenderProvider.java b/services/src/main/java/org/keycloak/email/DefaultEmailSenderProvider.java index 311627a6bf..7477d843fa 100644 --- a/services/src/main/java/org/keycloak/email/DefaultEmailSenderProvider.java +++ b/services/src/main/java/org/keycloak/email/DefaultEmailSenderProvider.java @@ -17,6 +17,7 @@ package org.keycloak.email; +import com.sun.mail.smtp.SMTPMessage; import org.jboss.logging.Logger; import org.keycloak.models.KeycloakSession; import org.keycloak.models.RealmModel; @@ -25,15 +26,17 @@ import org.keycloak.services.ServicesLogger; import org.keycloak.truststore.HostnameVerificationPolicy; import org.keycloak.truststore.JSSETruststoreConfigurator; +import javax.mail.Address; import javax.mail.MessagingException; import javax.mail.Multipart; import javax.mail.Session; import javax.mail.Transport; +import javax.mail.internet.AddressException; import javax.mail.internet.InternetAddress; import javax.mail.internet.MimeBodyPart; -import javax.mail.internet.MimeMessage; import javax.mail.internet.MimeMultipart; import javax.net.ssl.SSLSocketFactory; +import java.io.UnsupportedEncodingException; import java.security.KeyManagementException; import java.security.NoSuchAlgorithmException; import java.util.Date; @@ -91,6 +94,10 @@ public class DefaultEmailSenderProvider implements EmailSenderProvider { props.setProperty("mail.smtp.connectiontimeout", "10000"); String from = config.get("from"); + String fromDisplayName = config.get("fromDisplayName"); + String replyTo = config.get("replyTo"); + String replyToDisplayName = config.get("replyToDisplayName"); + String envelopeFrom = config.get("envelopeFrom"); Session session = Session.getInstance(props); @@ -108,8 +115,17 @@ public class DefaultEmailSenderProvider implements EmailSenderProvider { multipart.addBodyPart(htmlPart); } - MimeMessage msg = new MimeMessage(session); - msg.setFrom(new InternetAddress(from)); + SMTPMessage msg = new SMTPMessage(session); + msg.setFrom(toInternetAddress(from, fromDisplayName)); + + msg.setReplyTo(new Address[]{toInternetAddress(from, fromDisplayName)}); + if (replyTo != null) { + msg.setReplyTo(new Address[]{toInternetAddress(replyTo, replyToDisplayName)}); + } + if (envelopeFrom != null) { + msg.setEnvelopeFrom(envelopeFrom); + } + msg.setHeader("To", address); msg.setSubject(subject, "utf-8"); msg.setContent(multipart); @@ -136,6 +152,13 @@ public class DefaultEmailSenderProvider implements EmailSenderProvider { } } } + + protected InternetAddress toInternetAddress(String email, String displayName) throws UnsupportedEncodingException, AddressException { + if (displayName == null || "".equals(displayName.trim())) { + return new InternetAddress(email); + } + return new InternetAddress(email, displayName, "utf-8"); + } protected String retrieveEmailAddress(UserModel user) { return user.getEmail(); diff --git a/testsuite/integration-arquillian/tests/base/src/test/java/org/keycloak/testsuite/actions/RequiredActionEmailVerificationTest.java b/testsuite/integration-arquillian/tests/base/src/test/java/org/keycloak/testsuite/actions/RequiredActionEmailVerificationTest.java index 539267fa9e..eb719d884b 100755 --- a/testsuite/integration-arquillian/tests/base/src/test/java/org/keycloak/testsuite/actions/RequiredActionEmailVerificationTest.java +++ b/testsuite/integration-arquillian/tests/base/src/test/java/org/keycloak/testsuite/actions/RequiredActionEmailVerificationTest.java @@ -46,7 +46,6 @@ import javax.mail.MessagingException; import javax.mail.Multipart; import javax.mail.internet.MimeMessage; import java.io.IOException; -import java.util.Collections; import static org.junit.Assert.assertEquals; import static org.junit.Assert.assertTrue; @@ -98,6 +97,28 @@ public class RequiredActionEmailVerificationTest extends AbstractTestRealmKeyclo ApiUtil.createUserAndResetPasswordWithAdminClient(testRealm(), user, "password"); } + /** + * see KEYCLOAK-4163 + */ + @Test + public void verifyEmailConfig() throws IOException, MessagingException { + + loginPage.open(); + loginPage.login("test-user@localhost", "password"); + + Assert.assertTrue(verifyEmailPage.isCurrent()); + + Assert.assertEquals(1, greenMail.getReceivedMessages().length); + + MimeMessage message = greenMail.getReceivedMessages()[0]; + + // see testsuite/integration-arquillian/tests/base/src/test/resources/testrealm.json + Assert.assertEquals("", message.getHeader("Return-Path")[0]); + // displayname + Assert.assertEquals("Keycloak SSO ", message.getHeader("From")[0]); + Assert.assertEquals("Keycloak no-reply ", message.getHeader("Reply-To")[0]); + } + @Test public void verifyEmailExisting() throws IOException, MessagingException { loginPage.open(); diff --git a/testsuite/integration-arquillian/tests/base/src/test/resources/testrealm.json b/testsuite/integration-arquillian/tests/base/src/test/resources/testrealm.json index 6a090ced66..edb0f61585 100644 --- a/testsuite/integration-arquillian/tests/base/src/test/resources/testrealm.json +++ b/testsuite/integration-arquillian/tests/base/src/test/resources/testrealm.json @@ -13,7 +13,11 @@ "smtpServer": { "from": "auto@keycloak.org", "host": "localhost", - "port":"3025" + "port":"3025", + "fromDisplayName": "Keycloak SSO", + "replyTo":"reply-to@keycloak.org", + "replyToDisplayName": "Keycloak no-reply", + "envelopeFrom": "auto+bounces@keycloak.org" }, "users" : [ { diff --git a/themes/src/main/resources/theme/base/admin/messages/admin-messages_en.properties b/themes/src/main/resources/theme/base/admin/messages/admin-messages_en.properties index ce986d10bb..14708d82c3 100644 --- a/themes/src/main/resources/theme/base/admin/messages/admin-messages_en.properties +++ b/themes/src/main/resources/theme/base/admin/messages/admin-messages_en.properties @@ -55,7 +55,18 @@ smtp-host=SMTP Host port=Port smtp-port=SMTP Port (defaults to 25) from=From +fromDisplayName=From Display Name +fromDisplayName.tooltip=A user-friendly name for the 'From' address (optional). +replyTo=Reply To +replyToDisplayName=Reply To Display Name +replyToDisplayName.tooltip=A user-friendly name for the 'Reply-To' address (optional). +envelopeFrom=Envelope From +envelopeFrom.tooltip=An email address used for bounces (optional). sender-email-addr=Sender Email Address +sender-email-addr-display=Display Name for Sender Email Address +reply-to-email-addr=Reply To Email Address +reply-to-email-addr-display=Display Name for Reply To Email Address +sender-envelope-email-addr=Sender Envelope Email Address enable-ssl=Enable SSL enable-start-tls=Enable StartTLS enable-auth=Enable Authentication diff --git a/themes/src/main/resources/theme/base/admin/resources/partials/realm-smtp.html b/themes/src/main/resources/theme/base/admin/resources/partials/realm-smtp.html index 7d778f5962..5d3c68eedb 100755 --- a/themes/src/main/resources/theme/base/admin/resources/partials/realm-smtp.html +++ b/themes/src/main/resources/theme/base/admin/resources/partials/realm-smtp.html @@ -17,12 +17,39 @@ +
+ +
+ +
+ {{:: 'fromDisplayName.tooltip' | translate}} +
+
+ +
+ +
+ {{:: 'replyToDisplayName.tooltip' | translate}} +
+
+ +
+ +
+
+
+ +
+ +
+ {{:: 'envelopeFrom.tooltip' | translate}} +