blob: 4630ee6b16d20b67b8b00ff1faa1a1aa785ed555 [file] [log] [blame]
// Licensed to the Apache Software Foundation (ASF) under one
// or more contributor license agreements. See the NOTICE file
// distributed with this work for additional information
// regarding copyright ownership. The ASF licenses this file
// to you under the Apache License, Version 2.0 (the
// "License"); you may not use this file except in compliance
// with the License. You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing,
// software distributed under the License is distributed on an
// "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
// KIND, either express or implied. See the License for the
// specific language governing permissions and limitations
// under the License.
package org.apache.doris.stack.component;
import org.apache.doris.stack.model.request.config.EmailInfo;
import org.apache.doris.stack.entity.CoreUserEntity;
import org.apache.doris.stack.entity.SettingEntity;
import org.apache.doris.stack.exception.EmailSendException;
import org.apache.doris.stack.service.config.ConfigConstant;
import com.github.mustachejava.DefaultMustacheFactory;
import com.github.mustachejava.Mustache;
import com.github.mustachejava.MustacheFactory;
import lombok.AllArgsConstructor;
import lombok.Data;
import lombok.NoArgsConstructor;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Component;
import javax.mail.Authenticator;
import javax.mail.Message;
import javax.mail.MessagingException;
import javax.mail.PasswordAuthentication;
import javax.mail.Session;
import javax.mail.Transport;
import javax.mail.internet.InternetAddress;
import javax.mail.internet.MimeMessage;
import javax.mail.internet.MimeUtility;
import java.io.StringWriter;
import java.util.Date;
import java.util.List;
import java.util.Properties;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
@Component
@Slf4j
public class MailComponent {
protected static final String LOG_URL = "http://static.metabase.com/logo.png";
@Autowired
protected SettingComponent settingComponent;
public void sendRestPasswordMail(String emailDest, String resetUrl, String hostName) {
try {
EmailInfo emailInfo = getEmailInfo();
if (emailInfo == null) {
log.warn("The email not config.");
return;
}
Session mailSession = getEmailSession(emailInfo);
if (mailSession == null) {
log.warn("send reset password fail,get session error.");
return;
}
Message message = new MimeMessage(mailSession);
message.setFrom(new InternetAddress(emailInfo.getFromAddress()));
message.setRecipient(MimeMessage.RecipientType.TO, new InternetAddress(emailDest));
// TODO:The name is temporarily in Chinese
message.setSubject(MimeUtility.encodeText("[Doris Studio] 密码重置请求", "utf-8", null));
MustacheFactory mf = new DefaultMustacheFactory();
Mustache m = mf.compile("mail/password_reset.mustache");
MailComponent.ResetPassword resetPassword = new MailComponent.ResetPassword();
resetPassword.setHostname(hostName);
String siteUrl = settingComponent.readSetting(ConfigConstant.SITE_URL_KEY).getValue();
log.debug("Get site url {} of doris studio.", siteUrl);
resetPassword.setPasswordResetUrl(siteUrl + resetUrl);
StringWriter writer = new StringWriter();
m.execute(writer, resetPassword).flush();
String htmlBody = writer.toString();
message.setContent(htmlBody, "text/html;charset=utf-8");
log.debug("Send reset password email.");
// Send mail asynchronously
sendEmail(message);
} catch (Exception e) {
log.error("Send reset password error {}.", e.getMessage());
e.printStackTrace();
}
}
public void sendInvitationMail(String emailDest, String invitorName, String joinUrl) {
try {
log.debug("Invitor {} send invitation mail to {}.", invitorName, emailDest);
log.debug("Join url is {}.", joinUrl);
EmailInfo emailInfo = getEmailInfo();
if (emailInfo == null) {
log.warn("The email not config.");
return;
}
Session mailSession = getEmailSession(emailInfo);
if (mailSession == null) {
log.warn("send invitation mail fail,get session error.");
return;
}
Message message = new MimeMessage(mailSession);
if (emailInfo.getFromAddress() == null) {
message.setFrom(new InternetAddress());
} else {
message.setFrom(new InternetAddress(emailInfo.getFromAddress()));
}
message.setRecipient(MimeMessage.RecipientType.TO, new InternetAddress(emailDest));
// TODO:The name is temporarily in Chinese
message.setSubject(MimeUtility.encodeText("[Doris Studio] 新用户邀请", "utf-8", null));
MustacheFactory mf = new DefaultMustacheFactory();
Mustache m = mf.compile("mail/new_user_invite.mustache");
MailComponent.NewUserInvitation userInvitation = new MailComponent.NewUserInvitation();
userInvitation.setInvitorName(invitorName);
String siteUrl = settingComponent.readSetting(ConfigConstant.SITE_URL_KEY).getValue();
log.debug("Get site url {} of doris studio.", siteUrl);
userInvitation.setJoinUrl(siteUrl + joinUrl);
StringWriter writer = new StringWriter();
m.execute(writer, userInvitation).flush();
String htmlBody = writer.toString();
message.setContent(htmlBody, "text/html;charset=utf-8");
log.debug("Send invite email.");
sendEmail(message);
} catch (Exception e) {
log.error("Send reset password error {}.", e.getMessage());
e.printStackTrace();
}
}
public void sendUserJoineNote(List<CoreUserEntity> activeAdminUsers, CoreUserEntity joinUser) {
try {
log.debug("send user join notification mail");
EmailInfo emailInfo = getEmailInfo();
if (emailInfo == null) {
log.warn("The email not config.");
return;
}
Session mailSession = getEmailSession(emailInfo);
if (mailSession == null) {
log.warn("send reset password fail,get session error.");
return;
}
String siteUrl = settingComponent.readSetting(ConfigConstant.SITE_URL_KEY).getValue();
log.debug("Get site url {} of doris studio.", siteUrl);
String joinedUserEidtUrl = siteUrl + "/admin/people";
for (CoreUserEntity adminUser : activeAdminUsers) {
log.debug("Send user joined notification email to admin user {}.", adminUser.getEmail());
Message message = new MimeMessage(mailSession);
if (emailInfo.getFromAddress() == null) {
message.setFrom(new InternetAddress());
} else {
message.setFrom(new InternetAddress(emailInfo.getFromAddress()));
}
message.setRecipient(MimeMessage.RecipientType.TO, new InternetAddress(adminUser.getEmail()));
// TODO:The name is temporarily in Chinese
message.setSubject(MimeUtility.encodeText("[Doris Studio] 用户加入通知", "utf-8", null));
message.setSentDate(new Date());
MustacheFactory mf = new DefaultMustacheFactory();
Mustache m = mf.compile("mail/user_joined_notification.mustache");
MailComponent.UserJoinedNotice joinedNotice = new MailComponent.UserJoinedNotice();
joinedNotice.setAdminEmail(adminUser.getEmail());
joinedNotice.setJoinedUserName(joinUser.getFirstName());
joinedNotice.setJoinedUserEmail(joinUser.getEmail());
joinedNotice.setJoinedUserEditUrl(joinedUserEidtUrl);
StringWriter writer = new StringWriter();
m.execute(writer, joinedNotice).flush();
String htmlBody = writer.toString();
message.setContent(htmlBody, "text/html;charset=utf-8");
log.debug("Send user joined notification email.");
sendEmail(message);
}
} catch (Exception e) {
log.error("Send user join notification error {}.", e.getMessage());
e.printStackTrace();
}
}
public void sendTestEmail(String userMail) throws Exception {
EmailInfo emailInfo = getEmailInfo();
if (emailInfo == null) {
log.error("The email not config.");
throw new EmailSendException();
}
Session mailSession = getEmailSession(emailInfo);
if (mailSession == null) {
log.error("send reset password fail,get session error.");
throw new EmailSendException();
}
Message message = new MimeMessage(mailSession);
if (emailInfo.getFromAddress() == null) {
message.setFrom(new InternetAddress());
} else {
message.setFrom(new InternetAddress(emailInfo.getFromAddress()));
}
message.setRecipient(MimeMessage.RecipientType.TO, new InternetAddress(userMail));
// TODO:The name is temporarily in Chinese
message.setSubject(MimeUtility.encodeText("[Doris Studio] 测试邮件", "utf-8", null));
String testContent = "Your Doris Studio emails are working — hooray!";
// Text content of the message
message.setContent(testContent, "text/html;charset=UTF-8");
Transport.send(message);
log.debug("send mail success.");
}
/**
* Check whether the cache exists
* @return
*/
public boolean checkConfigExist() {
SettingEntity emailConfiguredKey = settingComponent.readSetting(ConfigConstant.EMAIL_CONFIGURED_KEY);
if (emailConfiguredKey == null) {
log.debug("The email config not exist");
return false;
} else {
log.debug("The email config exist");
return true;
}
}
/**
* Get configured mailbox service information
* @return
*/
public EmailInfo getEmailInfo() {
log.debug("Get email info from setting.");
boolean configured = checkConfigExist();
if (!configured) {
return null;
}
EmailInfo emailInfo = new EmailInfo();
SettingEntity hostSettingEntity = settingComponent.readSetting(ConfigConstant.EMAIL_HOST_KEY);
if (hostSettingEntity != null) {
emailInfo.setSmtpHost(hostSettingEntity.getValue());
}
SettingEntity portSettingEntity = settingComponent.readSetting(ConfigConstant.EMAIL_PORT_KEY);
if (portSettingEntity != null) {
emailInfo.setSmtpPort(portSettingEntity.getValue());
}
SettingEntity addressSettingEntity = settingComponent.readSetting(ConfigConstant.EMAIL_ADDRESS_KEY);
if (addressSettingEntity != null) {
emailInfo.setFromAddress(addressSettingEntity.getValue());
}
SettingEntity secSettingEntity = settingComponent.readSetting(ConfigConstant.EMAIL_SECURITY_KEY);
if (secSettingEntity != null) {
emailInfo.setSmtpSecurity(secSettingEntity.getValue());
}
if (emailInfo.getSmtpSecurity() != null
&& !emailInfo.getSmtpSecurity().equals(EmailInfo.SmtpSecurity.none.name())) {
SettingEntity passwdSettingEntity = settingComponent.readSetting(ConfigConstant.EMAIL_USER_NAME_KEY);
if (passwdSettingEntity != null) {
emailInfo.setSmtpUsername(passwdSettingEntity.getValue());
}
SettingEntity userSettingEntity = settingComponent.readSetting(ConfigConstant.EMAIL_PASSWORD_KEY);
if (userSettingEntity != null) {
emailInfo.setSmtpPassword(userSettingEntity.getValue());
}
}
return emailInfo;
}
/**
* Send mail asynchronously
*
* @param message
*/
protected void sendEmail(Message message) {
ExecutorService threadPool = Executors.newFixedThreadPool(1);
MailComponent.MailSender sender = new MailComponent.MailSender(message);
threadPool.submit(sender);
}
/**
* Get send mail session information
* @param emailInfo
* @return
*/
protected Session getEmailSession(EmailInfo emailInfo) {
try {
log.info("Get email session by email info.");
Properties props = new Properties();
props.put("mail.transport.protocol", ConfigConstant.EMAIL_PROTOCOL_KEY);
props.put("mail.smtp.host", emailInfo.getSmtpHost());
props.put("mail.smtp.port", emailInfo.getSmtpPort());
EmailInfo.SmtpSecurity smtpSecurity = EmailInfo.SmtpSecurity.valueOf(emailInfo.getSmtpSecurity());
switch (smtpSecurity) {
case ssl:
props.put("mail.smtp.auth", true);
props.put("mail.smtp.ssl.enable", true);
props.put("mail.smtp.socketFactory.class", "javax.net.ssl.SSLSocketFactory");
break;
case tls:
props.put("mail.smtp.starttls.enable", true);
break;
case starttls:
props.put("mail.smtp.auth", false);
props.put("mail.smtp.socketFactory.port", emailInfo.getSmtpHost());
props.put("mail.smtp.socketFactory.class", "javax.net.ssl.SSLSocketFactory");
props.put("mail.smtp.socketFactory.fallback", false);
props.put("mail.smtp.starttls.enable", true);
break;
case none:
props.put("mail.smtp.auth", false);
break;
default:
break;
}
Session session = null;
if (smtpSecurity == EmailInfo.SmtpSecurity.none) {
session = Session.getDefaultInstance(props);
} else {
Authenticator auth = new Authenticator() {
public PasswordAuthentication getPasswordAuthentication() {
return new PasswordAuthentication(emailInfo.getSmtpUsername(), emailInfo.getSmtpPassword());
}
};
session = Session.getInstance(props, auth);
}
return session;
} catch (Exception e) {
log.error("Get Email session exception {}", e.getMessage());
e.printStackTrace();
return null;
}
}
@Data
@NoArgsConstructor
@AllArgsConstructor
private static class Application {
private String applicationName = "Doris Studio";
private String applicationLogoUrl = LOG_URL;
}
/**
* Reset password message content
*/
@Data
@NoArgsConstructor
@AllArgsConstructor
private static class ResetPassword extends MailComponent.Application {
private String hostname;
private String passwordResetUrl;
}
/**
* Invite new users to add mail content
*/
@Data
@NoArgsConstructor
@AllArgsConstructor
private static class NewUserInvitation extends MailComponent.Application {
private String invitorName;
private String joinUrl;
private Date today = new Date();
}
/**
* User has added reminder email content
*/
@Data
@AllArgsConstructor
@NoArgsConstructor
private static class UserJoinedNotice extends MailComponent.Application {
private String joinedUserName;
private String adminEmail;
private String joinedUserEmail;
private Date joinedDate = new Date();
private String joinedUserEditUrl;
private String joinedViaSSO;
}
protected class MailSender implements Runnable {
private Message message;
public MailSender(Message message) {
this.message = message;
}
@Override
public void run() {
try {
Transport.send(message);
log.debug("send mail success.");
} catch (MessagingException e) {
log.error("Send message exception {}", e.getMessage());
e.printStackTrace();
}
}
}
}