Merge pull request #3718 from thomasdarimont/issue/KEYCLOAK-4163-improve-support-for-email-addresses

KEYCLOAK-4163 Improve support for e-mail addresses
This commit is contained in:
Stian Thorgersen 2017-04-06 15:34:30 +02:00 committed by GitHub
commit af4c74f1d9
5 changed files with 91 additions and 5 deletions

View file

@ -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();

View file

@ -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("<auto+bounces@keycloak.org>", message.getHeader("Return-Path")[0]);
// displayname <email@example.org>
Assert.assertEquals("Keycloak SSO <auto@keycloak.org>", message.getHeader("From")[0]);
Assert.assertEquals("Keycloak no-reply <reply-to@keycloak.org>", message.getHeader("Reply-To")[0]);
}
@Test
public void verifyEmailExisting() throws IOException, MessagingException {
loginPage.open();

View file

@ -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" : [
{

View file

@ -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

View file

@ -17,12 +17,39 @@
<input class="form-control" id="smtpPort" type="number" ng-model="realm.smtpServer.port" placeholder="{{:: 'smtp-port' | translate}}">
</div>
</div>
<div class="form-group clearfix">
<label class="col-md-2 control-label" for="smtpFromDisplayName">{{:: 'fromDisplayName' | translate}}</label>
<div class="col-md-6">
<input class="form-control" id="smtpFromDisplayName" type="text" ng-model="realm.smtpServer.fromDisplayName" placeholder="{{:: 'sender-email-addr-display' | translate}}">
</div>
<kc-tooltip>{{:: 'fromDisplayName.tooltip' | translate}}</kc-tooltip>
</div>
<div class="form-group clearfix">
<label class="col-md-2 control-label" for="smtpFrom"><span class="required">*</span> {{:: 'from' | translate}}</label>
<div class="col-md-6">
<input class="form-control" id="smtpFrom" type="email" ng-model="realm.smtpServer.from" placeholder="{{:: 'sender-email-addr' | translate}}" required>
</div>
</div>
<div class="form-group clearfix">
<label class="col-md-2 control-label" for="smtpReplyToDisplayName">{{:: 'replyToDisplayName' | translate}}</label>
<div class="col-md-6">
<input class="form-control" id="smtpReplyToDisplayName" type="text" ng-model="realm.smtpServer.replyToDisplayName" placeholder="{{:: 'reply-to-email-addr-display' | translate}}">
</div>
<kc-tooltip>{{:: 'replyToDisplayName.tooltip' | translate}}</kc-tooltip>
</div>
<div class="form-group clearfix">
<label class="col-md-2 control-label" for="smtpReplyTo">{{:: 'replyTo' | translate}}</label>
<div class="col-md-6">
<input class="form-control" id="smtpReplyTo" type="email" ng-model="realm.smtpServer.replyTo" placeholder="{{:: 'reply-to-email-addr' | translate}}">
</div>
</div>
<div class="form-group clearfix">
<label class="col-md-2 control-label" for="smtpEnvelopeFrom">{{:: 'envelopeFrom' | translate}}</label>
<div class="col-md-6">
<input class="form-control" id="smtpEnvelopeFrom" type="email" ng-model="realm.smtpServer.envelopeFrom" placeholder="{{:: 'sender-envelope-email-addr' | translate}}">
</div>
<kc-tooltip>{{:: 'envelopeFrom.tooltip' | translate}}</kc-tooltip>
</div>
<div class="form-group clearfix">
<label class="col-md-2 control-label" for="smtpSSL">{{:: 'enable-ssl' | translate}}</label>
<div class="col-md-6">