[MM-48528] Add support for SendGrid X-SMTPAPI header in email sends (#22296)

* Add support for SendGrid X-SMTPAPI header for categorizing sent emails to track performance

* Implement feedback

* Remove unnecessary struct

---------

Co-authored-by: Mattermost Build <build@mattermost.com>
Этот коммит содержится в:
Nick Misasi
2023-02-14 10:28:48 -05:00
коммит произвёл GitHub
родитель f074c6b729
Коммит 079449e30a
10 изменённых файлов: 83 добавлений и 57 удалений

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

@@ -195,7 +195,7 @@ func (a *App) TestEmail(userID string, cfg *model.Config) *model.AppError {
T := i18n.GetUserTranslations(user.Locale) T := i18n.GetUserTranslations(user.Locale)
license := a.Srv().License() license := a.Srv().License()
mailConfig := a.Srv().MailServiceConfig() mailConfig := a.Srv().MailServiceConfig()
if err := mail.SendMailUsingConfig(user.Email, T("api.admin.test_email.subject"), T("api.admin.test_email.body"), mailConfig, license != nil && *license.Features.Compliance, "", "", "", ""); err != nil { if err := mail.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]any{"Error": err.Error()}, "", http.StatusInternalServerError) return model.NewAppError("testEmail", "app.admin.test_email.failure", map[string]any{"Error": err.Error()}, "", http.StatusInternalServerError)
} }

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

@@ -194,7 +194,7 @@ func (a *App) NotifyAndSetWarnMetricAck(warnMetricId string, sender *model.User,
return model.NewAppError("NotifyAndSetWarnMetricAck", "api.email.send_warn_metric_ack.failure.app_error", map[string]any{"Error": err.Error()}, "", http.StatusInternalServerError) return model.NewAppError("NotifyAndSetWarnMetricAck", "api.email.send_warn_metric_ack.failure.app_error", map[string]any{"Error": err.Error()}, "", http.StatusInternalServerError)
} }
if err := mail.SendMailUsingConfig(model.MmSupportAdvisorAddress, subject, body, mailConfig, false, "", "", "", sender.Email); err != nil { if err := mail.SendMailUsingConfig(model.MmSupportAdvisorAddress, subject, body, mailConfig, false, "", "", "", sender.Email, "NotifyAndSetWarnMetricAck"); err != nil {
return model.NewAppError("NotifyAndSetWarnMetricAck", "api.email.send_warn_metric_ack.failure.app_error", map[string]any{"Error": err.Error()}, "", http.StatusInternalServerError) return model.NewAppError("NotifyAndSetWarnMetricAck", "api.email.send_warn_metric_ack.failure.app_error", map[string]any{"Error": err.Error()}, "", http.StatusInternalServerError)
} }
} }

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

@@ -27,6 +27,15 @@ import (
const serverInactivityHours = 100 const serverInactivityHours = 100
// Returns category if enabled is true (default false)
// If "" is returned when enabled is false, the category headers aren't attached to the email
func getSendGridCategory(category string, enabled bool) string {
if enabled {
return category
}
return ""
}
func (es *Service) SendChangeUsernameEmail(newUsername, email, locale, siteURL string) error { func (es *Service) SendChangeUsernameEmail(newUsername, email, locale, siteURL string) error {
T := i18n.GetUserTranslations(locale) T := i18n.GetUserTranslations(locale)
@@ -46,7 +55,7 @@ func (es *Service) SendChangeUsernameEmail(newUsername, email, locale, siteURL s
return err return err
} }
if err := es.sendMail(email, subject, body); err != nil { if err := es.sendMail(email, subject, body, "ChangeUsernameEmail"); err != nil {
return err return err
} }
@@ -79,7 +88,7 @@ func (es *Service) SendEmailChangeVerifyEmail(newUserEmail, locale, siteURL, tok
return err return err
} }
if err := es.sendMail(newUserEmail, subject, body); err != nil { if err := es.sendMail(newUserEmail, subject, body, "EmailChangeVerifyEmail"); err != nil {
return err return err
} }
@@ -105,7 +114,7 @@ func (es *Service) SendEmailChangeEmail(oldEmail, newEmail, locale, siteURL stri
return err return err
} }
if err := es.sendMail(oldEmail, subject, body); err != nil { if err := es.sendMail(oldEmail, subject, body, "EmailChangeEmail"); err != nil {
return err return err
} }
@@ -143,7 +152,7 @@ func (es *Service) SendVerifyEmail(userEmail, locale, siteURL, token, redirect s
return err return err
} }
if err := es.sendMail(userEmail, subject, body); err != nil { if err := es.sendMail(userEmail, subject, body, "VerifyEmail"); err != nil {
return err return err
} }
@@ -168,7 +177,7 @@ func (es *Service) SendSignInChangeEmail(email, method, locale, siteURL string)
return err return err
} }
if err := es.sendMail(email, subject, body); err != nil { if err := es.sendMail(email, subject, body, "SignInChangeEmail"); err != nil {
return err return err
} }
@@ -226,7 +235,7 @@ func (es *Service) SendWelcomeEmail(userID string, email string, verified bool,
return err return err
} }
if err := es.sendMail(email, subject, body); err != nil { if err := es.sendMail(email, subject, body, "WelcomeEmail"); err != nil {
return err return err
} }
@@ -259,11 +268,11 @@ func (es *Service) SendCloudUpgradeConfirmationEmail(userEmail, name, date, loca
} }
if isYearly { if isYearly {
if err := es.SendMailWithEmbeddedFilesAndCustomReplyTo(userEmail, subject, body, *es.config().SupportSettings.SupportEmail, embeddedFiles); err != nil { if err := es.SendMailWithEmbeddedFilesAndCustomReplyTo(userEmail, subject, body, *es.config().SupportSettings.SupportEmail, embeddedFiles, "CloudUpgradeConfirmationEmail"); err != nil {
return err return err
} }
} else { } else {
if err := es.sendEmailWithCustomReplyTo(userEmail, subject, body, *es.config().SupportSettings.SupportEmail); err != nil { if err := es.sendEmailWithCustomReplyTo(userEmail, subject, body, *es.config().SupportSettings.SupportEmail, "CloudUpgradeConfirmationEmail"); err != nil {
return err return err
} }
} }
@@ -304,7 +313,7 @@ func (es *Service) SendCloudWelcomeEmail(userEmail, locale, teamInviteID, workSp
return err return err
} }
if err := es.sendEmailWithCustomReplyTo(userEmail, subject, body, *es.config().SupportSettings.SupportEmail); err != nil { if err := es.sendEmailWithCustomReplyTo(userEmail, subject, body, *es.config().SupportSettings.SupportEmail, "CloudWelcomeEmail"); err != nil {
return err return err
} }
@@ -330,7 +339,7 @@ func (es *Service) SendPasswordChangeEmail(email, method, locale, siteURL string
return err return err
} }
if err := es.sendMail(email, subject, body); err != nil { if err := es.sendMail(email, subject, body, "PasswordChangeEmail"); err != nil {
return err return err
} }
@@ -355,7 +364,7 @@ func (es *Service) SendUserAccessTokenAddedEmail(email, locale, siteURL string)
return err return err
} }
if err := es.sendMail(email, subject, body); err != nil { if err := es.sendMail(email, subject, body, "UserAccessTokenAddedEmail"); err != nil {
return err return err
} }
@@ -385,7 +394,7 @@ func (es *Service) SendPasswordResetEmail(email string, token *model.Token, loca
return false, err return false, err
} }
if err := es.sendMail(email, subject, body); err != nil { if err := es.sendMail(email, subject, body, "PasswordResetEmail"); err != nil {
return false, err return false, err
} }
@@ -415,7 +424,7 @@ func (es *Service) SendMfaChangeEmail(email string, activated bool, locale, site
return err return err
} }
if err := es.sendMail(email, subject, body); err != nil { if err := es.sendMail(email, subject, body, "MfaChangeEmail"); err != nil {
return err return err
} }
@@ -501,7 +510,7 @@ func (es *Service) SendInviteEmails(
mlog.Error("Failed to send invite email successfully ", mlog.Err(err)) mlog.Error("Failed to send invite email successfully ", mlog.Err(err))
} }
if err := es.sendMail(invite, subject, body); err != nil { if err := es.sendMail(invite, subject, body, "InviteEmail"); err != nil {
mlog.Error("Failed to send invite email successfully ", mlog.Err(err)) mlog.Error("Failed to send invite email successfully ", mlog.Err(err))
if errorWhenNotSent { if errorWhenNotSent {
return SendMailError return SendMailError
@@ -617,7 +626,7 @@ func (es *Service) SendGuestInviteEmails(
mlog.Error("Failed to send invite email successfully", mlog.Err(err)) mlog.Error("Failed to send invite email successfully", mlog.Err(err))
} }
if nErr := es.SendMailWithEmbeddedFiles(invite, subject, body, embeddedFiles, "", "", ""); nErr != nil { if nErr := es.SendMailWithEmbeddedFiles(invite, subject, body, embeddedFiles, "", "", "", "InviteEmail"); nErr != nil {
mlog.Error("Failed to send invite email successfully", mlog.Err(nErr)) mlog.Error("Failed to send invite email successfully", mlog.Err(nErr))
if errorWhenNotSent { if errorWhenNotSent {
return SendMailError return SendMailError
@@ -765,7 +774,7 @@ func (es *Service) SendInviteEmailsToTeamAndChannels(
mlog.Error("Failed to send invite email successfully ", mlog.Err(err)) mlog.Error("Failed to send invite email successfully ", mlog.Err(err))
} }
if nErr := es.SendMailWithEmbeddedFiles(invite, subject, body, embeddedFiles, "", "", ""); nErr != nil { if nErr := es.SendMailWithEmbeddedFiles(invite, subject, body, embeddedFiles, "", "", "", "InviteEmailToTeamsAndChannels"); nErr != nil {
mlog.Error("Failed to send invite email successfully", mlog.Err(nErr)) mlog.Error("Failed to send invite email successfully", mlog.Err(nErr))
if errorWhenNotSent { if errorWhenNotSent {
inviteWithError := &model.EmailInviteWithError{ inviteWithError := &model.EmailInviteWithError{
@@ -828,7 +837,7 @@ func (es *Service) SendDeactivateAccountEmail(email string, locale, siteURL stri
return err return err
} }
if err := es.sendMail(email, subject, body); err != nil { // this needs to receive the header options if err := es.sendMail(email, subject, body, "DeactivateAccountEmail"); err != nil { // this needs to receive the header options
return err return err
} }
@@ -839,39 +848,47 @@ func (es *Service) SendNotificationMail(to, subject, htmlBody string) error {
if !*es.config().EmailSettings.SendEmailNotifications { if !*es.config().EmailSettings.SendEmailNotifications {
return nil return nil
} }
return es.sendMail(to, subject, htmlBody) return es.sendMail(to, subject, htmlBody, "NotificationEmail")
} }
func (es *Service) sendMail(to, subject, htmlBody string) error { func (es *Service) sendMail(to, subject, htmlBody, category string) error {
return es.sendMailWithCC(to, subject, htmlBody, "") return es.sendMailWithCC(to, subject, htmlBody, "", category)
} }
func (es *Service) sendEmailWithCustomReplyTo(to, subject, htmlBody, replyToAddress string) error { func (es *Service) sendEmailWithCustomReplyTo(to, subject, htmlBody, replyToAddress, category string) error {
license := es.license() license := es.license()
mailConfig := es.mailServiceConfig(replyToAddress) mailConfig := es.mailServiceConfig(replyToAddress)
return mail.SendMailUsingConfig(to, subject, htmlBody, mailConfig, license != nil && *license.Features.Compliance, "", "", "", "") category = getSendGridCategory(category, license.IsCloud())
return mail.SendMailUsingConfig(to, subject, htmlBody, mailConfig, license != nil && *license.Features.Compliance, "", "", "", "", category)
} }
func (es *Service) sendMailWithCC(to, subject, htmlBody string, ccMail string) error { func (es *Service) sendMailWithCC(to, subject, htmlBody, ccMail, category string) error {
license := es.license() license := es.license()
mailConfig := es.mailServiceConfig("") mailConfig := es.mailServiceConfig("")
return mail.SendMailUsingConfig(to, subject, htmlBody, mailConfig, license != nil && *license.Features.Compliance, "", "", "", ccMail) category = getSendGridCategory(category, license.IsCloud())
return mail.SendMailUsingConfig(to, subject, htmlBody, mailConfig, license != nil && *license.Features.Compliance, "", "", "", ccMail, category)
} }
func (es *Service) SendMailWithEmbeddedFilesAndCustomReplyTo(to, subject, htmlBody, replyToAddress string, embeddedFiles map[string]io.Reader) error { func (es *Service) SendMailWithEmbeddedFilesAndCustomReplyTo(to, subject, htmlBody, replyToAddress string, embeddedFiles map[string]io.Reader, category string) error {
license := es.license() license := es.license()
mailConfig := es.mailServiceConfig(replyToAddress) mailConfig := es.mailServiceConfig(replyToAddress)
return mail.SendMailWithEmbeddedFilesUsingConfig(to, subject, htmlBody, embeddedFiles, mailConfig, license != nil && *license.Features.Compliance, "", "", "", "") category = getSendGridCategory(category, license.IsCloud())
return mail.SendMailWithEmbeddedFilesUsingConfig(to, subject, htmlBody, embeddedFiles, mailConfig, license != nil && *license.Features.Compliance, "", "", "", "", category)
} }
func (es *Service) SendMailWithEmbeddedFiles(to, subject, htmlBody string, embeddedFiles map[string]io.Reader, messageID string, inReplyTo string, references string) error { func (es *Service) SendMailWithEmbeddedFiles(to, subject, htmlBody string, embeddedFiles map[string]io.Reader, messageID string, inReplyTo string, references string, category string) error {
license := es.license() license := es.license()
mailConfig := es.mailServiceConfig("") mailConfig := es.mailServiceConfig("")
return mail.SendMailWithEmbeddedFilesUsingConfig(to, subject, htmlBody, embeddedFiles, mailConfig, license != nil && *license.Features.Compliance, messageID, inReplyTo, references, "") category = getSendGridCategory(category, license.IsCloud())
return mail.SendMailWithEmbeddedFilesUsingConfig(to, subject, htmlBody, embeddedFiles, mailConfig, license != nil && *license.Features.Compliance, messageID, inReplyTo, references, "", category)
} }
func (es *Service) InvalidateVerifyEmailTokensForUser(userID string) *model.AppError { func (es *Service) InvalidateVerifyEmailTokensForUser(userID string) *model.AppError {
@@ -965,7 +982,7 @@ func (es *Service) SendLicenseInactivityEmail(email, name, locale, siteURL strin
return err return err
} }
if err := es.sendMail(email, subject, body); err != nil { if err := es.sendMail(email, subject, body, "LicenseInactivityEmail"); err != nil {
return err return err
} }
@@ -993,7 +1010,7 @@ func (es *Service) SendLicenseUpForRenewalEmail(email, name, locale, siteURL, re
return err return err
} }
if err := es.sendMail(email, subject, body); err != nil { if err := es.sendMail(email, subject, body, "LicenseUpForRenewal"); err != nil {
return err return err
} }
@@ -1026,7 +1043,7 @@ func (es *Service) SendPaymentFailedEmail(email string, locale string, failedPay
return false, err return false, err
} }
if err := es.sendEmailWithCustomReplyTo(email, subject, body, *es.config().SupportSettings.SupportEmail); err != nil { if err := es.sendEmailWithCustomReplyTo(email, subject, body, *es.config().SupportSettings.SupportEmail, "PaymentFailed"); err != nil {
return false, err return false, err
} }
@@ -1053,7 +1070,7 @@ func (es *Service) SendNoCardPaymentFailedEmail(email string, locale string, sit
return err return err
} }
if err := es.sendEmailWithCustomReplyTo(email, subject, body, *es.config().SupportSettings.SupportEmail); err != nil { if err := es.sendEmailWithCustomReplyTo(email, subject, body, *es.config().SupportSettings.SupportEmail, "NoCardPaymentFailed"); err != nil {
return err return err
} }
@@ -1083,7 +1100,7 @@ func (es *Service) SendDelinquencyEmail7(email, locale, siteURL, planName string
return err return err
} }
if err := es.sendEmailWithCustomReplyTo(email, subject, body, *es.config().SupportSettings.SupportEmail); err != nil { if err := es.sendEmailWithCustomReplyTo(email, subject, body, *es.config().SupportSettings.SupportEmail, "Delinquency7"); err != nil {
return err return err
} }
@@ -1112,7 +1129,7 @@ func (es *Service) SendDelinquencyEmail14(email, locale, siteURL, planName strin
return err return err
} }
if err := es.sendEmailWithCustomReplyTo(email, subject, body, *es.config().SupportSettings.SupportEmail); err != nil { if err := es.sendEmailWithCustomReplyTo(email, subject, body, *es.config().SupportSettings.SupportEmail, "Delinquency14"); err != nil {
return err return err
} }
@@ -1142,7 +1159,7 @@ func (es *Service) SendDelinquencyEmail30(email, locale, siteURL, planName strin
return err return err
} }
if err := es.sendEmailWithCustomReplyTo(email, subject, body, *es.config().SupportSettings.SupportEmail); err != nil { if err := es.sendEmailWithCustomReplyTo(email, subject, body, *es.config().SupportSettings.SupportEmail, "Delinquency30"); err != nil {
return err return err
} }
@@ -1174,7 +1191,7 @@ func (es *Service) SendDelinquencyEmail45(email, locale, siteURL, planName, deli
return err return err
} }
if err := es.sendEmailWithCustomReplyTo(email, subject, body, *es.config().SupportSettings.SupportEmail); err != nil { if err := es.sendEmailWithCustomReplyTo(email, subject, body, *es.config().SupportSettings.SupportEmail, "Delinquency45"); err != nil {
return err return err
} }
@@ -1207,7 +1224,7 @@ func (es *Service) SendDelinquencyEmail60(email, locale, siteURL string) error {
return err return err
} }
if err := es.sendEmailWithCustomReplyTo(email, subject, body, *es.config().SupportSettings.SupportEmail); err != nil { if err := es.sendEmailWithCustomReplyTo(email, subject, body, *es.config().SupportSettings.SupportEmail, "Delinquency60"); err != nil {
return err return err
} }
@@ -1240,7 +1257,7 @@ func (es *Service) SendDelinquencyEmail75(email, locale, siteURL, planName, deli
return err return err
} }
if err := es.sendEmailWithCustomReplyTo(email, subject, body, *es.config().SupportSettings.SupportEmail); err != nil { if err := es.sendEmailWithCustomReplyTo(email, subject, body, *es.config().SupportSettings.SupportEmail, "Delinquency75"); err != nil {
return err return err
} }
@@ -1272,7 +1289,7 @@ func (es *Service) SendDelinquencyEmail90(email, locale, siteURL string) error {
return err return err
} }
if err := es.sendEmailWithCustomReplyTo(email, subject, body, *es.config().SupportSettings.SupportEmail); err != nil { if err := es.sendEmailWithCustomReplyTo(email, subject, body, *es.config().SupportSettings.SupportEmail, "Delinquency90"); err != nil {
return err return err
} }
@@ -1297,7 +1314,7 @@ func (es *Service) SendRemoveExpiredLicenseEmail(renewalLink, email string, loca
return err return err
} }
if err := es.sendMail(email, subject, body); err != nil { if err := es.sendMail(email, subject, body, "RemoveExpiredLicense"); err != nil {
return err return err
} }

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

@@ -346,7 +346,7 @@ func (es *Service) sendBatchedEmailNotification(userID string, notifications []*
mlog.Error("Unable to render email", mlog.Err(renderErr)) mlog.Error("Unable to render email", mlog.Err(renderErr))
} }
if nErr := es.SendMailWithEmbeddedFiles(user.Email, subject, renderedPage, embeddedFiles, "", "", ""); nErr != nil { if nErr := es.SendMailWithEmbeddedFiles(user.Email, subject, renderedPage, embeddedFiles, "", "", "", "BatchedEmailNotification"); nErr != nil {
mlog.Warn("Unable to send batched email notification", mlog.String("email", user.Email), mlog.Err(nErr)) mlog.Warn("Unable to send batched email notification", mlog.String("email", user.Email), mlog.Err(nErr))
} }
} }

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

@@ -372,13 +372,13 @@ func (_m *ServiceInterface) SendLicenseUpForRenewalEmail(_a0 string, name string
return r0 return r0
} }
// SendMailWithEmbeddedFiles provides a mock function with given fields: to, subject, htmlBody, embeddedFiles, messageID, inReplyTo, references // SendMailWithEmbeddedFiles provides a mock function with given fields: to, subject, htmlBody, embeddedFiles, messageID, inReplyTo, references, category
func (_m *ServiceInterface) SendMailWithEmbeddedFiles(to string, subject string, htmlBody string, embeddedFiles map[string]io.Reader, messageID string, inReplyTo string, references string) error { func (_m *ServiceInterface) SendMailWithEmbeddedFiles(to string, subject string, htmlBody string, embeddedFiles map[string]io.Reader, messageID string, inReplyTo string, references string, category string) error {
ret := _m.Called(to, subject, htmlBody, embeddedFiles, messageID, inReplyTo, references) ret := _m.Called(to, subject, htmlBody, embeddedFiles, messageID, inReplyTo, references, category)
var r0 error var r0 error
if rf, ok := ret.Get(0).(func(string, string, string, map[string]io.Reader, string, string, string) error); ok { if rf, ok := ret.Get(0).(func(string, string, string, map[string]io.Reader, string, string, string, string) error); ok {
r0 = rf(to, subject, htmlBody, embeddedFiles, messageID, inReplyTo, references) r0 = rf(to, subject, htmlBody, embeddedFiles, messageID, inReplyTo, references, category)
} else { } else {
r0 = ret.Error(0) r0 = ret.Error(0)
} }

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

@@ -140,7 +140,7 @@ type ServiceInterface interface {
SendInviteEmailsToTeamAndChannels(team *model.Team, channels []*model.Channel, senderName string, senderUserId string, senderProfileImage []byte, invites []string, siteURL string, reminderData *model.TeamInviteReminderData, message string, errorWhenNotSent bool, isSystemAdmin bool, isFirstAdmin bool) ([]*model.EmailInviteWithError, error) SendInviteEmailsToTeamAndChannels(team *model.Team, channels []*model.Channel, senderName string, senderUserId string, senderProfileImage []byte, invites []string, siteURL string, reminderData *model.TeamInviteReminderData, message string, errorWhenNotSent bool, isSystemAdmin bool, isFirstAdmin bool) ([]*model.EmailInviteWithError, error)
SendDeactivateAccountEmail(email string, locale, siteURL string) error SendDeactivateAccountEmail(email string, locale, siteURL string) error
SendNotificationMail(to, subject, htmlBody string) error SendNotificationMail(to, subject, htmlBody string) error
SendMailWithEmbeddedFiles(to, subject, htmlBody string, embeddedFiles map[string]io.Reader, messageID string, inReplyTo string, references string) error SendMailWithEmbeddedFiles(to, subject, htmlBody string, embeddedFiles map[string]io.Reader, messageID string, inReplyTo string, references string, category string) error
SendLicenseUpForRenewalEmail(email, name, locale, siteURL, renewalLink string, daysToExpiration int) error SendLicenseUpForRenewalEmail(email, name, locale, siteURL, renewalLink string, daysToExpiration int) error
SendPaymentFailedEmail(email string, locale string, failedPayment *model.FailedPayment, planName, siteURL string) (bool, error) SendPaymentFailedEmail(email string, locale string, failedPayment *model.FailedPayment, planName, siteURL string) (bool, error)
// Cloud delinquency email sequence // Cloud delinquency email sequence

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

@@ -130,7 +130,7 @@ func (a *App) sendNotificationEmail(c request.CTX, notification *PostNotificatio
} }
a.Srv().Go(func() { a.Srv().Go(func() {
if nErr := a.Srv().EmailService.SendMailWithEmbeddedFiles(user.Email, html.UnescapeString(subjectText), bodyText, embeddedFiles, messageID, inReplyTo, references); nErr != nil { if nErr := a.Srv().EmailService.SendMailWithEmbeddedFiles(user.Email, html.UnescapeString(subjectText), bodyText, embeddedFiles, messageID, inReplyTo, references, "Notification"); nErr != nil {
mlog.Error("Error while sending the email", mlog.String("user_email", user.Email), mlog.Err(nErr)) mlog.Error("Error while sending the email", mlog.String("user_email", user.Email), mlog.Err(nErr))
} }
}) })

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

@@ -121,7 +121,7 @@ func (s *Server) DoSecurityUpdateCheck() {
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()
mailConfig := s.MailServiceConfig() mailConfig := s.MailServiceConfig()
mail.SendMailUsingConfig(user.Email, i18n.T("mattermost.bulletin.subject"), string(body), mailConfig, license != nil && *license.Features.Compliance, "", "", "", "") mail.SendMailUsingConfig(user.Email, i18n.T("mattermost.bulletin.subject"), string(body), mailConfig, license != nil && *license.Features.Compliance, "", "", "", "", "SecurityUpdateCheck")
} }
bulletinSeen := &model.System{Name: "SecurityBulletin_" + bulletin.Id, Value: bulletin.Id} bulletinSeen := &model.System{Name: "SecurityBulletin_" + bulletin.Id, Value: bulletin.Id}

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

@@ -57,6 +57,7 @@ type mailData struct {
messageID string messageID string
inReplyTo string inReplyTo string
references string references string
category string
} }
// smtpClient is implemented by an smtp.Client. See https://golang.org/pkg/net/smtp/#Client. // smtpClient is implemented by an smtp.Client. See https://golang.org/pkg/net/smtp/#Client.
@@ -234,7 +235,7 @@ func TestConnection(config *SMTPConfig) error {
return nil return nil
} }
func SendMailWithEmbeddedFilesUsingConfig(to, subject, htmlBody string, embeddedFiles map[string]io.Reader, config *SMTPConfig, enableComplianceFeatures bool, messageID string, inReplyTo string, references string, ccMail string) error { func SendMailWithEmbeddedFilesUsingConfig(to, subject, htmlBody string, embeddedFiles map[string]io.Reader, config *SMTPConfig, enableComplianceFeatures bool, messageID string, inReplyTo string, references string, ccMail string, category string) error {
fromMail := mail.Address{Name: config.FeedbackName, Address: config.FeedbackEmail} fromMail := mail.Address{Name: config.FeedbackName, Address: config.FeedbackEmail}
replyTo := mail.Address{Name: config.FeedbackName, Address: config.ReplyToAddress} replyTo := mail.Address{Name: config.FeedbackName, Address: config.ReplyToAddress}
@@ -250,13 +251,14 @@ func SendMailWithEmbeddedFilesUsingConfig(to, subject, htmlBody string, embedded
messageID: messageID, messageID: messageID,
inReplyTo: inReplyTo, inReplyTo: inReplyTo,
references: references, references: references,
category: category,
} }
return sendMailUsingConfigAdvanced(mail, config) return sendMailUsingConfigAdvanced(mail, config)
} }
func SendMailUsingConfig(to, subject, htmlBody string, config *SMTPConfig, enableComplianceFeatures bool, messageID string, inReplyTo string, references string, ccMail string) error { func SendMailUsingConfig(to, subject, htmlBody string, config *SMTPConfig, enableComplianceFeatures bool, messageID string, inReplyTo string, references string, ccMail, category string) error {
return SendMailWithEmbeddedFilesUsingConfig(to, subject, htmlBody, nil, config, enableComplianceFeatures, messageID, inReplyTo, references, ccMail) return SendMailWithEmbeddedFilesUsingConfig(to, subject, htmlBody, nil, config, enableComplianceFeatures, messageID, inReplyTo, references, ccMail, category)
} }
// allows for sending an email with differing MIME/SMTP recipients // allows for sending an email with differing MIME/SMTP recipients
@@ -287,6 +289,8 @@ func sendMailUsingConfigAdvanced(mail mailData, config *SMTPConfig) error {
return sendMail(c, mail, time.Now(), config) return sendMail(c, mail, time.Now(), config)
} }
const SendGridXSMTPAPIHeader = "X-SMTPAPI"
func sendMail(c smtpClient, mail mailData, date time.Time, config *SMTPConfig) error { func sendMail(c smtpClient, mail mailData, date time.Time, config *SMTPConfig) 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))
@@ -307,6 +311,11 @@ func sendMail(c smtpClient, mail mailData, date time.Time, config *SMTPConfig) e
"Precedence": {"bulk"}, "Precedence": {"bulk"},
} }
if mail.category != "" {
sendgridHeader := fmt.Sprintf(`{"category": %q}`, mail.category)
headers[SendGridXSMTPAPIHeader] = []string{sendgridHeader}
}
if mail.replyTo.Address != "" { if mail.replyTo.Address != "" {
headers["Reply-To"] = []string{mail.replyTo.String()} headers["Reply-To"] = []string{mail.replyTo.String()}
} }

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

@@ -124,7 +124,7 @@ func TestSendMailUsingConfig(t *testing.T) {
//Delete all the messages before check the sample email //Delete all the messages before check the sample email
DeleteMailBox(emailTo) DeleteMailBox(emailTo)
err2 := SendMailUsingConfig(emailTo, emailSubject, emailBody, cfg, true, "", "", "", emailCC) err2 := SendMailUsingConfig(emailTo, emailSubject, emailBody, cfg, true, "", "", "", emailCC, "")
require.NoError(t, err2, "Should connect to the SMTP Server") require.NoError(t, err2, "Should connect to the SMTP Server")
//Check if the email was send to the right email address //Check if the email was send to the right email address
@@ -162,7 +162,7 @@ func TestSendMailWithEmbeddedFilesUsingConfig(t *testing.T) {
"test1.png": bytes.NewReader([]byte("xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx")), "test1.png": bytes.NewReader([]byte("xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx")),
"test2.png": bytes.NewReader([]byte("xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx")), "test2.png": bytes.NewReader([]byte("xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx")),
} }
err2 := SendMailWithEmbeddedFilesUsingConfig(emailTo, emailSubject, emailBody, embeddedFiles, cfg, true, "", "", "", emailCC) err2 := SendMailWithEmbeddedFilesUsingConfig(emailTo, emailSubject, emailBody, embeddedFiles, cfg, true, "", "", "", emailCC, "")
require.NoError(t, err2, "Should connect to the SMTP Server") require.NoError(t, err2, "Should connect to the SMTP Server")
//Check if the email was send to the right email address //Check if the email was send to the right email address
@@ -407,7 +407,7 @@ 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, tc.messageID, tc.inReplyTo, tc.references} mail := mailData{"", "", mail.Address{}, "", tc.replyTo, "", "", nil, nil, tc.messageID, tc.inReplyTo, tc.references, ""}
cfg := getConfig() cfg := getConfig()
err = sendMail(mocm, mail, time.Now(), cfg) err = sendMail(mocm, mail, time.Now(), cfg)
require.NoError(t, err) require.NoError(t, err)