Reduce the coupling of the mailservice with the rest of the application (#16898)

* Reduce the coupling of the mailservice with the rest of the application

* Fixing tests in CI

* Simplifiying mailservice config

* Addressing PR review comments

* Fixing tests

* Removing unnecesary type definition

* Fixing ServerName usage
Этот коммит содержится в:
Jesús Espino
2021-02-16 12:42:03 +01:00
коммит произвёл GitHub
родитель 69ff686667
Коммит d06a62ce64
8 изменённых файлов: 160 добавлений и 242 удалений

Просмотреть файл

@@ -226,7 +226,8 @@ func (a *App) TestEmail(userID string, cfg *model.Config) *model.AppError {
T := utils.GetUserTranslations(user.Locale) T := utils.GetUserTranslations(user.Locale)
license := a.Srv().License() license := a.Srv().License()
if err := mailservice.SendMailUsingConfig(user.Email, T("api.admin.test_email.subject"), T("api.admin.test_email.body"), cfg, license != nil && *license.Features.Compliance, ""); err != nil { mailConfig := a.Srv().MailServiceConfig()
if err := mailservice.SendMailUsingConfig(user.Email, T("api.admin.test_email.subject"), T("api.admin.test_email.body"), mailConfig, license != nil && *license.Features.Compliance, ""); err != nil {
return model.NewAppError("testEmail", "app.admin.test_email.failure", map[string]interface{}{"Error": err.Error()}, "", http.StatusInternalServerError) return model.NewAppError("testEmail", "app.admin.test_email.failure", map[string]interface{}{"Error": err.Error()}, "", http.StatusInternalServerError)
} }

Просмотреть файл

@@ -497,7 +497,8 @@ func (a *App) NotifyAndSetWarnMetricAck(warnMetricId string, sender *model.User,
subject := T("api.templates.warn_metric_ack.subject") subject := T("api.templates.warn_metric_ack.subject")
bodyPage.Props["Title"] = warnMetricDisplayTexts.EmailBody bodyPage.Props["Title"] = warnMetricDisplayTexts.EmailBody
if err := mailservice.SendMailUsingConfig(model.MM_SUPPORT_ADVISOR_ADDRESS, subject, bodyPage.Render(), a.Config(), false, sender.Email); err != nil { mailConfig := a.Srv().MailServiceConfig()
if err := mailservice.SendMailUsingConfig(model.MM_SUPPORT_ADVISOR_ADDRESS, subject, bodyPage.Render(), mailConfig, false, sender.Email); err != nil {
return model.NewAppError("NotifyAndSetWarnMetricAck", "api.email.send_warn_metric_ack.failure.app_error", map[string]interface{}{"Error": err.Error()}, "", http.StatusInternalServerError) return model.NewAppError("NotifyAndSetWarnMetricAck", "api.email.send_warn_metric_ack.failure.app_error", map[string]interface{}{"Error": err.Error()}, "", http.StatusInternalServerError)
} }
} }

Просмотреть файл

@@ -22,6 +22,7 @@ import (
"github.com/mattermost/mattermost-server/v5/config" "github.com/mattermost/mattermost-server/v5/config"
"github.com/mattermost/mattermost-server/v5/mlog" "github.com/mattermost/mattermost-server/v5/mlog"
"github.com/mattermost/mattermost-server/v5/model" "github.com/mattermost/mattermost-server/v5/model"
"github.com/mattermost/mattermost-server/v5/services/mailservice"
"github.com/mattermost/mattermost-server/v5/utils" "github.com/mattermost/mattermost-server/v5/utils"
) )
@@ -450,3 +451,25 @@ func (a *App) HandleMessageExportConfig(cfg *model.Config, appCfg *model.Config)
} }
} }
} }
func (s *Server) MailServiceConfig() *mailservice.SMTPConfig {
emailSettings := s.Config().EmailSettings
hostname := utils.GetHostnameFromSiteURL(*s.Config().ServiceSettings.SiteURL)
cfg := mailservice.SMTPConfig{
Hostname: hostname,
ConnectionSecurity: *emailSettings.ConnectionSecurity,
SkipServerCertificateVerification: *emailSettings.SkipServerCertificateVerification,
ServerName: *emailSettings.SMTPServer,
Server: *emailSettings.SMTPServer,
Port: *emailSettings.SMTPPort,
ServerTimeout: *emailSettings.SMTPServerTimeout,
Username: *emailSettings.SMTPUsername,
Password: *emailSettings.SMTPPassword,
EnableSMTPAuth: *emailSettings.EnableSMTPAuth,
SendEmailNotifications: *emailSettings.SendEmailNotifications,
FeedbackName: *emailSettings.FeedbackName,
FeedbackEmail: *emailSettings.FeedbackEmail,
ReplyToAddress: *emailSettings.ReplyToAddress,
}
return &cfg
}

Просмотреть файл

@@ -558,14 +558,16 @@ func (es *EmailService) sendMail(to, subject, htmlBody string) error {
func (es *EmailService) sendMailWithCC(to, subject, htmlBody string, ccMail string) error { func (es *EmailService) sendMailWithCC(to, subject, htmlBody string, ccMail string) error {
license := es.srv.License() license := es.srv.License()
return mailservice.SendMailUsingConfig(to, subject, htmlBody, es.srv.Config(), license != nil && *license.Features.Compliance, ccMail) mailConfig := es.srv.MailServiceConfig()
return mailservice.SendMailUsingConfig(to, subject, htmlBody, mailConfig, license != nil && *license.Features.Compliance, ccMail)
} }
func (es *EmailService) sendMailWithEmbeddedFiles(to, subject, htmlBody string, embeddedFiles map[string]io.Reader) error { func (es *EmailService) sendMailWithEmbeddedFiles(to, subject, htmlBody string, embeddedFiles map[string]io.Reader) error {
license := es.srv.License() license := es.srv.License()
config := es.srv.Config() mailConfig := es.srv.MailServiceConfig()
return mailservice.SendMailWithEmbeddedFilesUsingConfig(to, subject, htmlBody, embeddedFiles, config, license != nil && *license.Features.Compliance, "") return mailservice.SendMailWithEmbeddedFilesUsingConfig(to, subject, htmlBody, embeddedFiles, mailConfig, license != nil && *license.Features.Compliance, "")
} }
func (es *EmailService) CreateVerifyEmailToken(userID string, newEmail string) (*model.Token, *model.AppError) { func (es *EmailService) CreateVerifyEmailToken(userID string, newEmail string) (*model.Token, *model.AppError) {

Просмотреть файл

@@ -115,7 +115,9 @@ func (s *Server) DoSecurityUpdateCheck() {
for _, user := range users { for _, user := range users {
mlog.Info("Sending security bulletin", mlog.String("bulletin_id", bulletin.Id), mlog.String("user_email", user.Email)) mlog.Info("Sending security bulletin", mlog.String("bulletin_id", bulletin.Id), mlog.String("user_email", user.Email))
license := s.License() license := s.License()
mailservice.SendMailUsingConfig(user.Email, utils.T("mattermost.bulletin.subject"), string(body), s.Config(), license != nil && *license.Features.Compliance, "") mailConfig := s.MailServiceConfig()
mailservice.SendMailUsingConfig(user.Email, utils.T("mattermost.bulletin.subject"), string(body), mailConfig, license != nil && *license.Features.Compliance, "")
} }
bulletinSeen := &model.System{Name: "SecurityBulletin_" + bulletin.Id, Value: bulletin.Id} bulletinSeen := &model.System{Name: "SecurityBulletin_" + bulletin.Id, Value: bulletin.Id}

Просмотреть файл

@@ -487,7 +487,9 @@ func NewServer(options ...Option) (*Server, error) {
} }
s.WebSocketRouter.app = fakeApp s.WebSocketRouter.app = fakeApp
if nErr := mailservice.TestConnection(s.Config()); nErr != nil { mailConfig := s.MailServiceConfig()
if nErr := mailservice.TestConnection(mailConfig); nErr != nil {
mlog.Error("Mail server connection test is failed", mlog.Err(nErr)) mlog.Error("Mail server connection test is failed", mlog.Err(nErr))
} }

Просмотреть файл

@@ -18,11 +18,30 @@ import (
gomail "gopkg.in/mail.v2" gomail "gopkg.in/mail.v2"
"github.com/mattermost/mattermost-server/v5/mlog" "github.com/mattermost/mattermost-server/v5/mlog"
"github.com/mattermost/mattermost-server/v5/model"
"github.com/mattermost/mattermost-server/v5/services/filesstore"
"github.com/mattermost/mattermost-server/v5/utils"
) )
const (
TLS = "TLS"
StartTLS = "STARTTLS"
)
type SMTPConfig struct {
ConnectionSecurity string
SkipServerCertificateVerification bool
Hostname string
ServerName string
Server string
Port string
ServerTimeout int
Username string
Password string
EnableSMTPAuth bool
SendEmailNotifications bool
FeedbackName string
FeedbackEmail string
ReplyToAddress string
}
type mailData struct { type mailData struct {
mimeTo string mimeTo string
smtpTo string smtpTo string
@@ -31,7 +50,6 @@ type mailData struct {
replyTo mail.Address replyTo mail.Address
subject string subject string
htmlBody string htmlBody string
attachments []*model.FileInfo
embeddedFiles map[string]io.Reader embeddedFiles map[string]io.Reader
mimeHeaders map[string]string mimeHeaders map[string]string
} }
@@ -48,29 +66,17 @@ func encodeRFC2047Word(s string) string {
return mime.BEncoding.Encode("utf-8", s) return mime.BEncoding.Encode("utf-8", s)
} }
type SmtpConnectionInfo struct {
SmtpUsername string
SmtpPassword string
SmtpServerName string
SmtpServerHost string
SmtpPort string
SmtpServerTimeout int
SkipCertVerification bool
ConnectionSecurity string
Auth bool
}
type authChooser struct { type authChooser struct {
smtp.Auth smtp.Auth
connectionInfo *SmtpConnectionInfo config *SMTPConfig
} }
func (a *authChooser) Start(server *smtp.ServerInfo) (string, []byte, error) { func (a *authChooser) Start(server *smtp.ServerInfo) (string, []byte, error) {
smtpAddress := a.connectionInfo.SmtpServerName + ":" + a.connectionInfo.SmtpPort smtpAddress := a.config.ServerName + ":" + a.config.Port
a.Auth = LoginAuth(a.connectionInfo.SmtpUsername, a.connectionInfo.SmtpPassword, smtpAddress) a.Auth = LoginAuth(a.config.Username, a.config.Password, smtpAddress)
for _, method := range server.Auth { for _, method := range server.Auth {
if method == "PLAIN" { if method == "PLAIN" {
a.Auth = smtp.PlainAuth("", a.connectionInfo.SmtpUsername, a.connectionInfo.SmtpPassword, a.connectionInfo.SmtpServerName+":"+a.connectionInfo.SmtpPort) a.Auth = smtp.PlainAuth("", a.config.Username, a.config.Password, a.config.ServerName+":"+a.config.Port)
break break
} }
} }
@@ -111,19 +117,19 @@ func (a *loginAuth) Next(fromServer []byte, more bool) ([]byte, error) {
return nil, nil return nil, nil
} }
func ConnectToSMTPServerAdvanced(connectionInfo *SmtpConnectionInfo) (net.Conn, error) { func ConnectToSMTPServerAdvanced(config *SMTPConfig) (net.Conn, error) {
var conn net.Conn var conn net.Conn
var err error var err error
smtpAddress := connectionInfo.SmtpServerHost + ":" + connectionInfo.SmtpPort smtpAddress := config.Server + ":" + config.Port
dialer := &net.Dialer{ dialer := &net.Dialer{
Timeout: time.Duration(connectionInfo.SmtpServerTimeout) * time.Second, Timeout: time.Duration(config.ServerTimeout) * time.Second,
} }
if connectionInfo.ConnectionSecurity == model.CONN_SECURITY_TLS { if config.ConnectionSecurity == TLS {
tlsconfig := &tls.Config{ tlsconfig := &tls.Config{
InsecureSkipVerify: connectionInfo.SkipCertVerification, InsecureSkipVerify: config.SkipServerCertificateVerification,
ServerName: connectionInfo.SmtpServerName, ServerName: config.ServerName,
} }
conn, err = tls.DialWithDialer(dialer, "tcp", smtpAddress, tlsconfig) conn, err = tls.DialWithDialer(dialer, "tcp", smtpAddress, tlsconfig)
@@ -140,20 +146,11 @@ func ConnectToSMTPServerAdvanced(connectionInfo *SmtpConnectionInfo) (net.Conn,
return conn, nil return conn, nil
} }
func ConnectToSMTPServer(config *model.Config) (net.Conn, error) { func ConnectToSMTPServer(config *SMTPConfig) (net.Conn, error) {
return ConnectToSMTPServerAdvanced( return ConnectToSMTPServerAdvanced(config)
&SmtpConnectionInfo{
ConnectionSecurity: *config.EmailSettings.ConnectionSecurity,
SkipCertVerification: *config.EmailSettings.SkipServerCertificateVerification,
SmtpServerName: *config.EmailSettings.SMTPServer,
SmtpServerHost: *config.EmailSettings.SMTPServer,
SmtpPort: *config.EmailSettings.SMTPPort,
SmtpServerTimeout: *config.EmailSettings.SMTPServerTimeout,
},
)
} }
func NewSMTPClientAdvanced(ctx context.Context, conn net.Conn, hostname string, connectionInfo *SmtpConnectionInfo) (*smtp.Client, error) { func NewSMTPClientAdvanced(ctx context.Context, conn net.Conn, config *SMTPConfig) (*smtp.Client, error) {
ctx, cancel := context.WithCancel(ctx) ctx, cancel := context.WithCancel(ctx)
defer cancel() defer cancel()
@@ -161,7 +158,7 @@ func NewSMTPClientAdvanced(ctx context.Context, conn net.Conn, hostname string,
ec := make(chan error) ec := make(chan error)
go func() { go func() {
var err error var err error
c, err = smtp.NewClient(conn, connectionInfo.SmtpServerName+":"+connectionInfo.SmtpPort) c, err = smtp.NewClient(conn, config.ServerName+":"+config.Port)
if err != nil { if err != nil {
ec <- err ec <- err
return return
@@ -179,50 +176,39 @@ func NewSMTPClientAdvanced(ctx context.Context, conn net.Conn, hostname string,
return nil, errors.Wrap(err, "unable to connect to the SMTP server") return nil, errors.Wrap(err, "unable to connect to the SMTP server")
} }
if hostname != "" { if config.Hostname != "" {
err := c.Hello(hostname) err := c.Hello(config.Hostname)
if err != nil { if err != nil {
return nil, errors.Wrap(err, "unable to send hello message") return nil, errors.Wrap(err, "unable to send hello message")
} }
} }
if connectionInfo.ConnectionSecurity == model.CONN_SECURITY_STARTTLS { if config.ConnectionSecurity == StartTLS {
tlsconfig := &tls.Config{ tlsconfig := &tls.Config{
InsecureSkipVerify: connectionInfo.SkipCertVerification, InsecureSkipVerify: config.SkipServerCertificateVerification,
ServerName: connectionInfo.SmtpServerName, ServerName: config.ServerName,
} }
c.StartTLS(tlsconfig) c.StartTLS(tlsconfig)
} }
if connectionInfo.Auth { if config.EnableSMTPAuth {
if err := c.Auth(&authChooser{connectionInfo: connectionInfo}); err != nil { if err := c.Auth(&authChooser{config: config}); err != nil {
return nil, errors.Wrap(err, "authentication failed") return nil, errors.Wrap(err, "authentication failed")
} }
} }
return c, nil return c, nil
} }
func NewSMTPClient(ctx context.Context, conn net.Conn, config *model.Config) (*smtp.Client, error) { func NewSMTPClient(ctx context.Context, conn net.Conn, config *SMTPConfig) (*smtp.Client, error) {
return NewSMTPClientAdvanced( return NewSMTPClientAdvanced(
ctx, ctx,
conn, conn,
utils.GetHostnameFromSiteURL(*config.ServiceSettings.SiteURL), config,
&SmtpConnectionInfo{
ConnectionSecurity: *config.EmailSettings.ConnectionSecurity,
SkipCertVerification: *config.EmailSettings.SkipServerCertificateVerification,
SmtpServerName: *config.EmailSettings.SMTPServer,
SmtpServerHost: *config.EmailSettings.SMTPServer,
SmtpPort: *config.EmailSettings.SMTPPort,
SmtpServerTimeout: *config.EmailSettings.SMTPServerTimeout,
Auth: *config.EmailSettings.EnableSMTPAuth,
SmtpUsername: *config.EmailSettings.SMTPUsername,
SmtpPassword: *config.EmailSettings.SMTPPassword,
},
) )
} }
func TestConnection(config *model.Config) error { func TestConnection(config *SMTPConfig) error {
if !*config.EmailSettings.SendEmailNotifications { if !config.SendEmailNotifications {
return errors.New("SendEmailNotifications is not true") return errors.New("SendEmailNotifications is not true")
} }
@@ -232,7 +218,7 @@ func TestConnection(config *model.Config) error {
} }
defer conn.Close() defer conn.Close()
sec := *config.EmailSettings.SMTPServerTimeout sec := config.ServerTimeout
ctx := context.Background() ctx := context.Background()
ctx, cancel := context.WithTimeout(ctx, time.Duration(sec)*time.Second) ctx, cancel := context.WithTimeout(ctx, time.Duration(sec)*time.Second)
@@ -248,9 +234,9 @@ func TestConnection(config *model.Config) error {
return nil return nil
} }
func SendMailWithEmbeddedFilesUsingConfig(to, subject, htmlBody string, embeddedFiles map[string]io.Reader, config *model.Config, enableComplianceFeatures bool, ccMail string) error { func SendMailWithEmbeddedFilesUsingConfig(to, subject, htmlBody string, embeddedFiles map[string]io.Reader, config *SMTPConfig, enableComplianceFeatures bool, ccMail string) error {
fromMail := mail.Address{Name: *config.EmailSettings.FeedbackName, Address: *config.EmailSettings.FeedbackEmail} fromMail := mail.Address{Name: config.FeedbackName, Address: config.FeedbackEmail}
replyTo := mail.Address{Name: *config.EmailSettings.FeedbackName, Address: *config.EmailSettings.ReplyToAddress} replyTo := mail.Address{Name: config.FeedbackName, Address: config.ReplyToAddress}
mail := mailData{ mail := mailData{
mimeTo: to, mimeTo: to,
@@ -266,13 +252,13 @@ func SendMailWithEmbeddedFilesUsingConfig(to, subject, htmlBody string, embedded
return sendMailUsingConfigAdvanced(mail, config, enableComplianceFeatures) return sendMailUsingConfigAdvanced(mail, config, enableComplianceFeatures)
} }
func SendMailUsingConfig(to, subject, htmlBody string, config *model.Config, enableComplianceFeatures bool, ccMail string) error { func SendMailUsingConfig(to, subject, htmlBody string, config *SMTPConfig, enableComplianceFeatures bool, ccMail string) error {
return SendMailWithEmbeddedFilesUsingConfig(to, subject, htmlBody, nil, config, enableComplianceFeatures, ccMail) return SendMailWithEmbeddedFilesUsingConfig(to, subject, htmlBody, nil, config, enableComplianceFeatures, ccMail)
} }
// allows for sending an email with attachments and differing MIME/SMTP recipients // allows for sending an email with differing MIME/SMTP recipients
func sendMailUsingConfigAdvanced(mail mailData, config *model.Config, enableComplianceFeatures bool) error { func sendMailUsingConfigAdvanced(mail mailData, config *SMTPConfig, enableComplianceFeatures bool) error {
if *config.EmailSettings.SMTPServer == "" { if config.Server == "" {
return nil return nil
} }
@@ -282,7 +268,7 @@ func sendMailUsingConfigAdvanced(mail mailData, config *model.Config, enableComp
} }
defer conn.Close() defer conn.Close()
sec := *config.EmailSettings.SMTPServerTimeout sec := config.ServerTimeout
ctx := context.Background() ctx := context.Background()
ctx, cancel := context.WithTimeout(ctx, time.Duration(sec)*time.Second) ctx, cancel := context.WithTimeout(ctx, time.Duration(sec)*time.Second)
@@ -295,15 +281,10 @@ func sendMailUsingConfigAdvanced(mail mailData, config *model.Config, enableComp
defer c.Quit() defer c.Quit()
defer c.Close() defer c.Close()
fileBackend, nErr := filesstore.NewFileBackend(config.FileSettings.ToFileBackendSettings(enableComplianceFeatures)) return SendMail(c, mail, time.Now())
if nErr != nil {
return errors.Wrap(nErr, "unable to initialize file backend")
}
return SendMail(c, mail, fileBackend, time.Now())
} }
func SendMail(c smtpClient, mail mailData, fileBackend filesstore.FileBackend, date time.Time) error { func SendMail(c smtpClient, mail mailData, date time.Time) error {
mlog.Debug("sending mail", mlog.String("to", mail.smtpTo), mlog.String("subject", mail.subject)) mlog.Debug("sending mail", mlog.String("to", mail.smtpTo), mlog.String("subject", mail.subject))
htmlMessage := "\r\n<html><body>" + mail.htmlBody + "</body></html>" htmlMessage := "\r\n<html><body>" + mail.htmlBody + "</body></html>"
@@ -345,20 +326,6 @@ func SendMail(c smtpClient, mail mailData, fileBackend filesstore.FileBackend, d
m.EmbedReader(name, reader) m.EmbedReader(name, reader)
} }
for _, fileInfo := range mail.attachments {
bytes, nErr := fileBackend.ReadFile(fileInfo.Path)
if nErr != nil {
return errors.Wrap(err, "failed to read attachment")
}
m.Attach(fileInfo.Name, gomail.SetCopyFunc(func(writer io.Writer) error {
if _, nErr = writer.Write(bytes); nErr != nil {
return errors.Wrap(err, "failed to write attachment to email")
}
return nil
}))
}
if err = c.Mail(mail.from.Address); err != nil { if err = c.Mail(mail.from.Address); err != nil {
return errors.Wrap(err, "failed to set the from address") return errors.Wrap(err, "failed to set the from address")
} }

Просмотреть файл

@@ -6,7 +6,6 @@ package mailservice
import ( import (
"bytes" "bytes"
"context" "context"
"fmt"
"io" "io"
"io/ioutil" "io/ioutil"
"net" "net"
@@ -19,16 +18,38 @@ import (
"github.com/stretchr/testify/assert" "github.com/stretchr/testify/assert"
"github.com/stretchr/testify/require" "github.com/stretchr/testify/require"
"github.com/mattermost/mattermost-server/v5/config"
"github.com/mattermost/mattermost-server/v5/model"
"github.com/mattermost/mattermost-server/v5/services/filesstore"
"github.com/mattermost/mattermost-server/v5/utils"
) )
func getConfig() *SMTPConfig {
server := os.Getenv("MM_EMAILSETTINGS_SMTPSERVER")
if server == "" {
server = "localhost"
}
port := os.Getenv("MM_EMAILSETTINGS_SMTPPORT")
if port == "" {
port = "10025"
}
return &SMTPConfig{
ConnectionSecurity: "",
SkipServerCertificateVerification: false,
Hostname: "localhost",
ServerName: server,
Server: server,
Port: port,
ServerTimeout: 10,
Username: "",
Password: "",
EnableSMTPAuth: false,
SendEmailNotifications: true,
FeedbackName: "",
FeedbackEmail: "test@example.com",
ReplyToAddress: "test@example.com",
}
}
func TestMailConnectionFromConfig(t *testing.T) { func TestMailConnectionFromConfig(t *testing.T) {
store := config.NewTestMemoryStore() cfg := getConfig()
cfg := store.Get()
conn, err := ConnectToSMTPServer(cfg) conn, err := ConnectToSMTPServer(cfg)
require.NoError(t, err, "Should connect to the SMTP Server %v", err) require.NoError(t, err, "Should connect to the SMTP Server %v", err)
@@ -37,8 +58,8 @@ func TestMailConnectionFromConfig(t *testing.T) {
require.NoError(t, err, "Should get new SMTP client") require.NoError(t, err, "Should get new SMTP client")
*cfg.EmailSettings.SMTPServer = "wrongServer" cfg.Server = "wrongServer"
*cfg.EmailSettings.SMTPPort = "553" cfg.Port = "553"
_, err = ConnectToSMTPServer(cfg) _, err = ConnectToSMTPServer(cfg)
@@ -46,56 +67,23 @@ func TestMailConnectionFromConfig(t *testing.T) {
} }
func TestMailConnectionAdvanced(t *testing.T) { func TestMailConnectionAdvanced(t *testing.T) {
store := config.NewTestMemoryStore() cfg := getConfig()
cfg := store.Get()
conn, err := ConnectToSMTPServerAdvanced( conn, err := ConnectToSMTPServerAdvanced(cfg)
&SmtpConnectionInfo{
ConnectionSecurity: *cfg.EmailSettings.ConnectionSecurity,
SkipCertVerification: *cfg.EmailSettings.SkipServerCertificateVerification,
SmtpServerName: *cfg.EmailSettings.SMTPServer,
SmtpServerHost: *cfg.EmailSettings.SMTPServer,
SmtpPort: *cfg.EmailSettings.SMTPPort,
},
)
require.NoError(t, err, "Should connect to the SMTP Server") require.NoError(t, err, "Should connect to the SMTP Server")
defer conn.Close() defer conn.Close()
_, err2 := NewSMTPClientAdvanced( _, err2 := NewSMTPClientAdvanced(context.Background(), conn, cfg)
context.Background(),
conn,
utils.GetHostnameFromSiteURL(*cfg.ServiceSettings.SiteURL),
&SmtpConnectionInfo{
ConnectionSecurity: *cfg.EmailSettings.ConnectionSecurity,
SkipCertVerification: *cfg.EmailSettings.SkipServerCertificateVerification,
SmtpServerName: *cfg.EmailSettings.SMTPServer,
SmtpServerHost: *cfg.EmailSettings.SMTPServer,
SmtpPort: *cfg.EmailSettings.SMTPPort,
Auth: *cfg.EmailSettings.EnableSMTPAuth,
SmtpUsername: *cfg.EmailSettings.SMTPUsername,
SmtpPassword: *cfg.EmailSettings.SMTPPassword,
SmtpServerTimeout: 1,
},
)
require.NoError(t, err2, "Should get new SMTP client") require.NoError(t, err2, "Should get new SMTP client")
l, err3 := net.Listen("tcp", "localhost:") // emulate nc -l <random-port> l, err3 := net.Listen("tcp", "localhost:") // emulate nc -l <random-port>
require.NoError(t, err3, "Should've open a network socket and listen") require.NoError(t, err3, "Should've open a network socket and listen")
defer l.Close() defer l.Close()
cfg.Server = strings.Split(l.Addr().String(), ":")[0]
cfg.Port = strings.Split(l.Addr().String(), ":")[1]
cfg.ServerTimeout = 1
connInfo := &SmtpConnectionInfo{ conn2, err := ConnectToSMTPServerAdvanced(cfg)
ConnectionSecurity: *cfg.EmailSettings.ConnectionSecurity,
SkipCertVerification: *cfg.EmailSettings.SkipServerCertificateVerification,
SmtpServerName: *cfg.EmailSettings.SMTPServer,
SmtpServerHost: strings.Split(l.Addr().String(), ":")[0],
SmtpPort: strings.Split(l.Addr().String(), ":")[1],
Auth: *cfg.EmailSettings.EnableSMTPAuth,
SmtpUsername: *cfg.EmailSettings.SMTPUsername,
SmtpPassword: *cfg.EmailSettings.SMTPPassword,
SmtpServerTimeout: 1,
}
conn2, err := ConnectToSMTPServerAdvanced(connInfo)
require.NoError(t, err, "Should connect to the SMTP Server") require.NoError(t, err, "Should connect to the SMTP Server")
defer conn2.Close() defer conn2.Close()
@@ -106,33 +94,20 @@ func TestMailConnectionAdvanced(t *testing.T) {
_, err4 := NewSMTPClientAdvanced( _, err4 := NewSMTPClientAdvanced(
ctx, ctx,
conn2, conn2,
utils.GetHostnameFromSiteURL(*cfg.ServiceSettings.SiteURL), cfg,
connInfo,
) )
require.Error(t, err4, "Should get a timeout get while creating a new SMTP client") require.Error(t, err4, "Should get a timeout get while creating a new SMTP client")
assert.Contains(t, err4.Error(), "unable to connect to the SMTP server") assert.Contains(t, err4.Error(), "unable to connect to the SMTP server")
_, err5 := ConnectToSMTPServerAdvanced( cfg.Server = "wrongServer"
&SmtpConnectionInfo{ cfg.Port = "553"
ConnectionSecurity: *cfg.EmailSettings.ConnectionSecurity,
SkipCertVerification: *cfg.EmailSettings.SkipServerCertificateVerification, _, err5 := ConnectToSMTPServerAdvanced(cfg)
SmtpServerName: "wrongServer",
SmtpServerHost: "wrongServer",
SmtpPort: "553",
},
)
require.Error(t, err5, "Should not connect to the SMTP Server") require.Error(t, err5, "Should not connect to the SMTP Server")
} }
func TestSendMailUsingConfig(t *testing.T) { func TestSendMailUsingConfig(t *testing.T) {
utils.T = utils.GetUserTranslations("en") cfg := getConfig()
fsInner, err := config.NewFileStore("config.json", false)
require.NoError(t, err)
fs, err := config.NewStoreFromBacking(fsInner, nil, false)
require.NoError(t, err)
cfg := fs.Get()
var emailTo = "test@example.com" var emailTo = "test@example.com"
var emailSubject = "Testing this email" var emailSubject = "Testing this email"
@@ -166,14 +141,7 @@ func TestSendMailUsingConfig(t *testing.T) {
} }
func TestSendMailWithEmbeddedFilesUsingConfig(t *testing.T) { func TestSendMailWithEmbeddedFilesUsingConfig(t *testing.T) {
utils.T = utils.GetUserTranslations("en") cfg := getConfig()
fsInner, err := config.NewFileStore("config.json", false)
require.NoError(t, err)
fs, err := config.NewStoreFromBacking(fsInner, nil, false)
require.NoError(t, err)
cfg := fs.Get()
var emailTo = "test@example.com" var emailTo = "test@example.com"
var emailSubject = "Testing this email" var emailSubject = "Testing this email"
@@ -213,42 +181,23 @@ func TestSendMailWithEmbeddedFilesUsingConfig(t *testing.T) {
} }
func TestSendMailUsingConfigAdvanced(t *testing.T) { func TestSendMailUsingConfigAdvanced(t *testing.T) {
utils.T = utils.GetUserTranslations("en") cfg := getConfig()
fsInner, err := config.NewFileStore("config.json", false)
require.NoError(t, err)
fs, err := config.NewStoreFromBacking(fsInner, nil, false)
require.NoError(t, err)
cfg := fs.Get()
//Delete all the messages before check the sample email //Delete all the messages before check the sample email
DeleteMailBox("test2@example.com") DeleteMailBox("test2@example.com")
fileBackend, err := filesstore.NewFileBackend(cfg.FileSettings.ToFileBackendSettings(true))
assert.NoError(t, err)
// create two files with the same name that will both be attached to the email // create two files with the same name that will both be attached to the email
filePath1 := fmt.Sprintf("test1/%s", "file1.txt") file1, err := ioutil.TempFile("", "*")
filePath2 := fmt.Sprintf("test2/%s", "file2.txt") require.NoError(t, err)
fileContents1 := []byte("hello world") defer os.Remove(file1.Name())
fileContents2 := []byte("foo bar") file1.Write([]byte("hello world"))
_, err = fileBackend.WriteFile(bytes.NewReader(fileContents1), filePath1) file1.Close()
assert.NoError(t, err) file2, err := ioutil.TempFile("", "*")
_, err = fileBackend.WriteFile(bytes.NewReader(fileContents2), filePath2)
assert.NoError(t, err)
defer fileBackend.RemoveFile(filePath1)
defer fileBackend.RemoveFile(filePath2)
attachments := make([]*model.FileInfo, 2) require.NoError(t, err)
attachments[0] = &model.FileInfo{ defer os.Remove(file2.Name())
Name: "file1.txt", file2.Write([]byte("foo bar"))
Path: filePath1, file2.Close()
}
attachments[1] = &model.FileInfo{
Name: "file2.txt",
Path: filePath2,
}
embeddedFiles := map[string]io.Reader{ embeddedFiles := map[string]io.Reader{
"test": bytes.NewReader([]byte("test data")), "test": bytes.NewReader([]byte("test data")),
@@ -264,7 +213,6 @@ func TestSendMailUsingConfigAdvanced(t *testing.T) {
replyTo: mail.Address{Name: "ReplyTo", Address: "reply_to@mattermost.com"}, replyTo: mail.Address{Name: "ReplyTo", Address: "reply_to@mattermost.com"},
subject: "Testing this email", subject: "Testing this email",
htmlBody: "This is a test from autobot", htmlBody: "This is a test from autobot",
attachments: attachments,
embeddedFiles: embeddedFiles, embeddedFiles: embeddedFiles,
mimeHeaders: headers, mimeHeaders: headers,
} }
@@ -297,37 +245,16 @@ func TestSendMailUsingConfigAdvanced(t *testing.T) {
// check that the custom mime headers came through - header case seems to get mutated // check that the custom mime headers came through - header case seems to get mutated
assert.Equal(t, "TestValue", resultsEmail.Header["Testheader"][0]) assert.Equal(t, "TestValue", resultsEmail.Header["Testheader"][0])
// ensure that the attachments were successfully sent
assert.Len(t, resultsEmail.Attachments, 3)
attachmentsFilenames := []string{
resultsEmail.Attachments[0].Filename,
resultsEmail.Attachments[1].Filename,
resultsEmail.Attachments[2].Filename,
}
assert.Contains(t, attachmentsFilenames, "file1.txt")
assert.Contains(t, attachmentsFilenames, "file2.txt")
assert.Contains(t, attachmentsFilenames, "test")
attachment1 := string(resultsEmail.Attachments[0].Bytes)
attachment2 := string(resultsEmail.Attachments[1].Bytes)
attachment3 := string(resultsEmail.Attachments[2].Bytes)
attachmentsData := []string{attachment1, attachment2, attachment3}
assert.Contains(t, attachmentsData, string(fileContents1))
assert.Contains(t, attachmentsData, string(fileContents2))
assert.Contains(t, attachmentsData, "test data")
} }
func TestAuthMethods(t *testing.T) { func TestAuthMethods(t *testing.T) {
auth := &authChooser{ auth := &authChooser{
connectionInfo: &SmtpConnectionInfo{ config: &SMTPConfig{
SmtpUsername: "test", Username: "test",
SmtpPassword: "fakepass", Password: "fakepass",
SmtpServerName: "fakeserver", ServerName: "fakeserver",
SmtpServerHost: "fakeserver", Server: "fakeserver",
SmtpPort: "25", Port: "25",
}, },
} }
tests := []struct { tests := []struct {
@@ -394,13 +321,6 @@ func TestSendMail(t *testing.T) {
dir, err := ioutil.TempDir(".", "mail-test-") dir, err := ioutil.TempDir(".", "mail-test-")
require.NoError(t, err) require.NoError(t, err)
defer os.RemoveAll(dir) defer os.RemoveAll(dir)
settings := model.FileSettings{
DriverName: model.NewString(model.IMAGE_DRIVER_LOCAL),
Directory: &dir,
}
settings.SetDefaults(true)
mockBackend, err := filesstore.NewFileBackend(settings.ToFileBackendSettings(true))
require.NoError(t, err)
mocm := &mockMailer{} mocm := &mockMailer{}
testCases := map[string]struct { testCases := map[string]struct {
@@ -422,8 +342,8 @@ func TestSendMail(t *testing.T) {
for testName, tc := range testCases { for testName, tc := range testCases {
t.Run(testName, func(t *testing.T) { t.Run(testName, func(t *testing.T) {
mail := mailData{"", "", mail.Address{}, "", tc.replyTo, "", "", nil, nil, nil} mail := mailData{"", "", mail.Address{}, "", tc.replyTo, "", "", nil, nil}
err = SendMail(mocm, mail, mockBackend, time.Now()) err = SendMail(mocm, mail, time.Now())
require.NoError(t, err) require.NoError(t, err)
if tc.contains != "" { if tc.contains != "" {
require.Contains(t, string(mocm.data), tc.contains) require.Contains(t, string(mocm.data), tc.contains)