github.com/jlevesy/mattermost-server@v5.3.2-0.20181003190404-7468f35cb0c8+incompatible/app/email.go (about) 1 // Copyright (c) 2017-present Mattermost, Inc. All Rights Reserved. 2 // See License.txt for license information. 3 4 package app 5 6 import ( 7 "fmt" 8 "net/url" 9 10 "net/http" 11 12 "github.com/nicksnyder/go-i18n/i18n" 13 "github.com/pkg/errors" 14 "github.com/throttled/throttled" 15 "github.com/throttled/throttled/store/memstore" 16 17 "github.com/mattermost/mattermost-server/mlog" 18 "github.com/mattermost/mattermost-server/model" 19 "github.com/mattermost/mattermost-server/services/mailservice" 20 "github.com/mattermost/mattermost-server/utils" 21 ) 22 23 const ( 24 emailRateLimitingMemstoreSize = 65536 25 emailRateLimitingPerHour = 20 26 emailRateLimitingMaxBurst = 20 27 ) 28 29 func (a *App) SetupInviteEmailRateLimiting() error { 30 store, err := memstore.New(emailRateLimitingMemstoreSize) 31 if err != nil { 32 return errors.Wrap(err, "Unable to setup email rate limiting memstore.") 33 } 34 35 quota := throttled.RateQuota{ 36 MaxRate: throttled.PerHour(emailRateLimitingPerHour), 37 MaxBurst: emailRateLimitingMaxBurst, 38 } 39 40 rateLimiter, err := throttled.NewGCRARateLimiter(store, quota) 41 if err != nil || rateLimiter == nil { 42 return errors.Wrap(err, "Unable to setup email rate limiting GCRA rate limiter.") 43 } 44 45 a.EmailRateLimiter = rateLimiter 46 return nil 47 } 48 49 func (a *App) SendChangeUsernameEmail(oldUsername, newUsername, email, locale, siteURL string) *model.AppError { 50 T := utils.GetUserTranslations(locale) 51 52 subject := T("api.templates.username_change_subject", 53 map[string]interface{}{"SiteName": a.ClientConfig()["SiteName"], 54 "TeamDisplayName": a.Config().TeamSettings.SiteName}) 55 56 bodyPage := a.NewEmailTemplate("email_change_body", locale) 57 bodyPage.Props["SiteURL"] = siteURL 58 bodyPage.Props["Title"] = T("api.templates.username_change_body.title") 59 bodyPage.Props["Info"] = T("api.templates.username_change_body.info", 60 map[string]interface{}{"TeamDisplayName": a.Config().TeamSettings.SiteName, "NewUsername": newUsername}) 61 bodyPage.Props["Warning"] = T("api.templates.email_warning") 62 63 if err := a.SendMail(email, subject, bodyPage.Render()); err != nil { 64 return model.NewAppError("SendChangeUsernameEmail", "api.user.send_email_change_username_and_forget.error", nil, err.Error(), http.StatusInternalServerError) 65 } 66 67 return nil 68 } 69 70 func (a *App) SendEmailChangeVerifyEmail(newUserEmail, locale, siteURL, token string) *model.AppError { 71 T := utils.GetUserTranslations(locale) 72 73 link := fmt.Sprintf("%s/do_verify_email?token=%s&email=%s", siteURL, token, url.QueryEscape(newUserEmail)) 74 75 subject := T("api.templates.email_change_verify_subject", 76 map[string]interface{}{"SiteName": a.ClientConfig()["SiteName"], 77 "TeamDisplayName": a.Config().TeamSettings.SiteName}) 78 79 bodyPage := a.NewEmailTemplate("email_change_verify_body", locale) 80 bodyPage.Props["SiteURL"] = siteURL 81 bodyPage.Props["Title"] = T("api.templates.email_change_verify_body.title") 82 bodyPage.Props["Info"] = T("api.templates.email_change_verify_body.info", 83 map[string]interface{}{"TeamDisplayName": a.Config().TeamSettings.SiteName}) 84 bodyPage.Props["VerifyUrl"] = link 85 bodyPage.Props["VerifyButton"] = T("api.templates.email_change_verify_body.button") 86 87 if err := a.SendMail(newUserEmail, subject, bodyPage.Render()); err != nil { 88 return model.NewAppError("SendEmailChangeVerifyEmail", "api.user.send_email_change_verify_email_and_forget.error", nil, err.Error(), http.StatusInternalServerError) 89 } 90 91 return nil 92 } 93 94 func (a *App) SendEmailChangeEmail(oldEmail, newEmail, locale, siteURL string) *model.AppError { 95 T := utils.GetUserTranslations(locale) 96 97 subject := T("api.templates.email_change_subject", 98 map[string]interface{}{"SiteName": a.ClientConfig()["SiteName"], 99 "TeamDisplayName": a.Config().TeamSettings.SiteName}) 100 101 bodyPage := a.NewEmailTemplate("email_change_body", locale) 102 bodyPage.Props["SiteURL"] = siteURL 103 bodyPage.Props["Title"] = T("api.templates.email_change_body.title") 104 bodyPage.Props["Info"] = T("api.templates.email_change_body.info", 105 map[string]interface{}{"TeamDisplayName": a.Config().TeamSettings.SiteName, "NewEmail": newEmail}) 106 bodyPage.Props["Warning"] = T("api.templates.email_warning") 107 108 if err := a.SendMail(oldEmail, subject, bodyPage.Render()); err != nil { 109 return model.NewAppError("SendEmailChangeEmail", "api.user.send_email_change_email_and_forget.error", nil, err.Error(), http.StatusInternalServerError) 110 } 111 112 return nil 113 } 114 115 func (a *App) SendVerifyEmail(userEmail, locale, siteURL, token string) *model.AppError { 116 T := utils.GetUserTranslations(locale) 117 118 link := fmt.Sprintf("%s/do_verify_email?token=%s&email=%s", siteURL, token, url.QueryEscape(userEmail)) 119 120 url, _ := url.Parse(siteURL) 121 122 subject := T("api.templates.verify_subject", 123 map[string]interface{}{"SiteName": a.ClientConfig()["SiteName"]}) 124 125 bodyPage := a.NewEmailTemplate("verify_body", locale) 126 bodyPage.Props["SiteURL"] = siteURL 127 bodyPage.Props["Title"] = T("api.templates.verify_body.title", map[string]interface{}{"ServerURL": url.Host}) 128 bodyPage.Props["Info"] = T("api.templates.verify_body.info") 129 bodyPage.Props["VerifyUrl"] = link 130 bodyPage.Props["Button"] = T("api.templates.verify_body.button") 131 132 if err := a.SendMail(userEmail, subject, bodyPage.Render()); err != nil { 133 return model.NewAppError("SendVerifyEmail", "api.user.send_verify_email_and_forget.failed.error", nil, err.Error(), http.StatusInternalServerError) 134 } 135 136 return nil 137 } 138 139 func (a *App) SendSignInChangeEmail(email, method, locale, siteURL string) *model.AppError { 140 T := utils.GetUserTranslations(locale) 141 142 subject := T("api.templates.signin_change_email.subject", 143 map[string]interface{}{"SiteName": a.ClientConfig()["SiteName"]}) 144 145 bodyPage := a.NewEmailTemplate("signin_change_body", locale) 146 bodyPage.Props["SiteURL"] = siteURL 147 bodyPage.Props["Title"] = T("api.templates.signin_change_email.body.title") 148 bodyPage.Props["Info"] = T("api.templates.signin_change_email.body.info", 149 map[string]interface{}{"SiteName": a.ClientConfig()["SiteName"], "Method": method}) 150 bodyPage.Props["Warning"] = T("api.templates.email_warning") 151 152 if err := a.SendMail(email, subject, bodyPage.Render()); err != nil { 153 return model.NewAppError("SendSignInChangeEmail", "api.user.send_sign_in_change_email_and_forget.error", nil, err.Error(), http.StatusInternalServerError) 154 } 155 156 return nil 157 } 158 159 func (a *App) SendWelcomeEmail(userId string, email string, verified bool, locale, siteURL string) *model.AppError { 160 T := utils.GetUserTranslations(locale) 161 162 rawUrl, _ := url.Parse(siteURL) 163 164 subject := T("api.templates.welcome_subject", 165 map[string]interface{}{"SiteName": a.ClientConfig()["SiteName"], 166 "ServerURL": rawUrl.Host}) 167 168 bodyPage := a.NewEmailTemplate("welcome_body", locale) 169 bodyPage.Props["SiteURL"] = siteURL 170 bodyPage.Props["Title"] = T("api.templates.welcome_body.title", map[string]interface{}{"ServerURL": rawUrl.Host}) 171 bodyPage.Props["Info"] = T("api.templates.welcome_body.info") 172 bodyPage.Props["Button"] = T("api.templates.welcome_body.button") 173 bodyPage.Props["Info2"] = T("api.templates.welcome_body.info2") 174 bodyPage.Props["Info3"] = T("api.templates.welcome_body.info3") 175 bodyPage.Props["SiteURL"] = siteURL 176 177 if *a.Config().NativeAppSettings.AppDownloadLink != "" { 178 bodyPage.Props["AppDownloadInfo"] = T("api.templates.welcome_body.app_download_info") 179 bodyPage.Props["AppDownloadLink"] = *a.Config().NativeAppSettings.AppDownloadLink 180 } 181 182 if !verified { 183 token, err := a.CreateVerifyEmailToken(userId) 184 if err != nil { 185 return err 186 } 187 link := fmt.Sprintf("%s/do_verify_email?token=%s&email=%s", siteURL, token.Token, url.QueryEscape(email)) 188 bodyPage.Props["VerifyUrl"] = link 189 } 190 191 if err := a.SendMail(email, subject, bodyPage.Render()); err != nil { 192 return model.NewAppError("SendWelcomeEmail", "api.user.send_welcome_email_and_forget.failed.error", nil, err.Error(), http.StatusInternalServerError) 193 } 194 195 return nil 196 } 197 198 func (a *App) SendPasswordChangeEmail(email, method, locale, siteURL string) *model.AppError { 199 T := utils.GetUserTranslations(locale) 200 201 subject := T("api.templates.password_change_subject", 202 map[string]interface{}{"SiteName": a.ClientConfig()["SiteName"], 203 "TeamDisplayName": a.Config().TeamSettings.SiteName}) 204 205 bodyPage := a.NewEmailTemplate("password_change_body", locale) 206 bodyPage.Props["SiteURL"] = siteURL 207 bodyPage.Props["Title"] = T("api.templates.password_change_body.title") 208 bodyPage.Props["Info"] = T("api.templates.password_change_body.info", 209 map[string]interface{}{"TeamDisplayName": a.Config().TeamSettings.SiteName, "TeamURL": siteURL, "Method": method}) 210 bodyPage.Props["Warning"] = T("api.templates.email_warning") 211 212 if err := a.SendMail(email, subject, bodyPage.Render()); err != nil { 213 return model.NewAppError("SendPasswordChangeEmail", "api.user.send_password_change_email_and_forget.error", nil, err.Error(), http.StatusInternalServerError) 214 } 215 216 return nil 217 } 218 219 func (a *App) SendUserAccessTokenAddedEmail(email, locale, siteURL string) *model.AppError { 220 T := utils.GetUserTranslations(locale) 221 222 subject := T("api.templates.user_access_token_subject", 223 map[string]interface{}{"SiteName": a.ClientConfig()["SiteName"]}) 224 225 bodyPage := a.NewEmailTemplate("password_change_body", locale) 226 bodyPage.Props["SiteURL"] = siteURL 227 bodyPage.Props["Title"] = T("api.templates.user_access_token_body.title") 228 bodyPage.Props["Info"] = T("api.templates.user_access_token_body.info", 229 map[string]interface{}{"SiteName": a.ClientConfig()["SiteName"], "SiteURL": siteURL}) 230 bodyPage.Props["Warning"] = T("api.templates.email_warning") 231 232 if err := a.SendMail(email, subject, bodyPage.Render()); err != nil { 233 return model.NewAppError("SendUserAccessTokenAddedEmail", "api.user.send_user_access_token.error", nil, err.Error(), http.StatusInternalServerError) 234 } 235 236 return nil 237 } 238 239 func (a *App) SendPasswordResetEmail(email string, token *model.Token, locale, siteURL string) (bool, *model.AppError) { 240 241 T := utils.GetUserTranslations(locale) 242 243 link := fmt.Sprintf("%s/reset_password_complete?token=%s", siteURL, url.QueryEscape(token.Token)) 244 245 subject := T("api.templates.reset_subject", 246 map[string]interface{}{"SiteName": a.ClientConfig()["SiteName"]}) 247 248 bodyPage := a.NewEmailTemplate("reset_body", locale) 249 bodyPage.Props["SiteURL"] = siteURL 250 bodyPage.Props["Title"] = T("api.templates.reset_body.title") 251 bodyPage.Props["Info1"] = utils.TranslateAsHtml(T, "api.templates.reset_body.info1", nil) 252 bodyPage.Props["Info2"] = T("api.templates.reset_body.info2") 253 bodyPage.Props["ResetUrl"] = link 254 bodyPage.Props["Button"] = T("api.templates.reset_body.button") 255 256 if err := a.SendMail(email, subject, bodyPage.Render()); err != nil { 257 return false, model.NewAppError("SendPasswordReset", "api.user.send_password_reset.send.app_error", nil, "err="+err.Message, http.StatusInternalServerError) 258 } 259 260 return true, nil 261 } 262 263 func (a *App) SendMfaChangeEmail(email string, activated bool, locale, siteURL string) *model.AppError { 264 T := utils.GetUserTranslations(locale) 265 266 subject := T("api.templates.mfa_change_subject", 267 map[string]interface{}{"SiteName": a.ClientConfig()["SiteName"]}) 268 269 bodyPage := a.NewEmailTemplate("mfa_change_body", locale) 270 bodyPage.Props["SiteURL"] = siteURL 271 272 if activated { 273 bodyPage.Props["Info"] = T("api.templates.mfa_activated_body.info", map[string]interface{}{"SiteURL": siteURL}) 274 bodyPage.Props["Title"] = T("api.templates.mfa_activated_body.title") 275 } else { 276 bodyPage.Props["Info"] = T("api.templates.mfa_deactivated_body.info", map[string]interface{}{"SiteURL": siteURL}) 277 bodyPage.Props["Title"] = T("api.templates.mfa_deactivated_body.title") 278 } 279 bodyPage.Props["Warning"] = T("api.templates.email_warning") 280 281 if err := a.SendMail(email, subject, bodyPage.Render()); err != nil { 282 return model.NewAppError("SendMfaChangeEmail", "api.user.send_mfa_change_email.error", nil, err.Error(), http.StatusInternalServerError) 283 } 284 285 return nil 286 } 287 288 func (a *App) SendInviteEmails(team *model.Team, senderName string, senderUserId string, invites []string, siteURL string) { 289 if a.EmailRateLimiter == nil { 290 a.Log.Error("Email invite not sent, rate limiting could not be setup.", mlog.String("user_id", senderUserId), mlog.String("team_id", team.Id)) 291 return 292 } 293 rateLimited, result, err := a.EmailRateLimiter.RateLimit(senderUserId, len(invites)) 294 if err != nil { 295 a.Log.Error("Error rate limiting invite email.", mlog.String("user_id", senderUserId), mlog.String("team_id", team.Id), mlog.Err(err)) 296 return 297 } 298 299 if rateLimited { 300 a.Log.Error("Invite emails rate limited.", 301 mlog.String("user_id", senderUserId), 302 mlog.String("team_id", team.Id), 303 mlog.String("retry_after", result.RetryAfter.String()), 304 mlog.Err(err)) 305 return 306 } 307 308 for _, invite := range invites { 309 if len(invite) > 0 { 310 senderRole := utils.T("api.team.invite_members.member") 311 312 subject := utils.T("api.templates.invite_subject", 313 map[string]interface{}{"SenderName": senderName, 314 "TeamDisplayName": team.DisplayName, 315 "SiteName": a.ClientConfig()["SiteName"]}) 316 317 bodyPage := a.NewEmailTemplate("invite_body", model.DEFAULT_LOCALE) 318 bodyPage.Props["SiteURL"] = siteURL 319 bodyPage.Props["Title"] = utils.T("api.templates.invite_body.title") 320 bodyPage.Html["Info"] = utils.TranslateAsHtml(utils.T, "api.templates.invite_body.info", 321 map[string]interface{}{"SenderStatus": senderRole, "SenderName": senderName, "TeamDisplayName": team.DisplayName}) 322 bodyPage.Props["Info"] = map[string]interface{}{} 323 bodyPage.Props["Button"] = utils.T("api.templates.invite_body.button") 324 bodyPage.Html["ExtraInfo"] = utils.TranslateAsHtml(utils.T, "api.templates.invite_body.extra_info", 325 map[string]interface{}{"TeamDisplayName": team.DisplayName}) 326 bodyPage.Props["TeamURL"] = siteURL + "/" + team.Name 327 328 token := model.NewToken( 329 TOKEN_TYPE_TEAM_INVITATION, 330 model.MapToJson(map[string]string{"teamId": team.Id, "email": invite}), 331 ) 332 333 props := make(map[string]string) 334 props["email"] = invite 335 props["display_name"] = team.DisplayName 336 props["name"] = team.Name 337 data := model.MapToJson(props) 338 339 if result := <-a.Srv.Store.Token().Save(token); result.Err != nil { 340 mlog.Error(fmt.Sprintf("Failed to send invite email successfully err=%v", result.Err)) 341 continue 342 } 343 bodyPage.Props["Link"] = fmt.Sprintf("%s/signup_user_complete/?d=%s&t=%s", siteURL, url.QueryEscape(data), url.QueryEscape(token.Token)) 344 345 if !a.Config().EmailSettings.SendEmailNotifications { 346 mlog.Info(fmt.Sprintf("sending invitation to %v %v", invite, bodyPage.Props["Link"])) 347 } 348 349 if err := a.SendMail(invite, subject, bodyPage.Render()); err != nil { 350 mlog.Error(fmt.Sprintf("Failed to send invite email successfully err=%v", err)) 351 } 352 } 353 } 354 } 355 356 func (a *App) NewEmailTemplate(name, locale string) *utils.HTMLTemplate { 357 t := utils.NewHTMLTemplate(a.HTMLTemplates(), name) 358 359 var localT i18n.TranslateFunc 360 if locale != "" { 361 localT = utils.GetUserTranslations(locale) 362 } else { 363 localT = utils.T 364 } 365 366 t.Props["Footer"] = localT("api.templates.email_footer") 367 368 if *a.Config().EmailSettings.FeedbackOrganization != "" { 369 t.Props["Organization"] = localT("api.templates.email_organization") + *a.Config().EmailSettings.FeedbackOrganization 370 } else { 371 t.Props["Organization"] = "" 372 } 373 374 t.Props["EmailInfo1"] = localT("api.templates.email_info1") 375 t.Props["EmailInfo2"] = localT("api.templates.email_info2") 376 t.Props["EmailInfo3"] = localT("api.templates.email_info3", 377 map[string]interface{}{"SiteName": a.Config().TeamSettings.SiteName}) 378 t.Props["SupportEmail"] = *a.Config().SupportSettings.SupportEmail 379 380 return t 381 } 382 383 func (a *App) SendDeactivateAccountEmail(email string, locale, siteURL string) *model.AppError { 384 T := utils.GetUserTranslations(locale) 385 386 rawUrl, _ := url.Parse(siteURL) 387 388 subject := T("api.templates.deactivate_subject", 389 map[string]interface{}{"SiteName": a.ClientConfig()["SiteName"], 390 "ServerURL": rawUrl.Host}) 391 392 bodyPage := a.NewEmailTemplate("deactivate_body", locale) 393 bodyPage.Props["SiteURL"] = siteURL 394 bodyPage.Props["Title"] = T("api.templates.deactivate_body.title", map[string]interface{}{"ServerURL": rawUrl.Host}) 395 bodyPage.Props["Info"] = T("api.templates.deactivate_body.info", 396 map[string]interface{}{"SiteURL": siteURL}) 397 bodyPage.Props["Warning"] = T("api.templates.deactivate_body.warning") 398 399 if err := a.SendMail(email, subject, bodyPage.Render()); err != nil { 400 return model.NewAppError("SendDeactivateEmail", "api.user.send_deactivate_email_and_forget.failed.error", nil, err.Error(), http.StatusInternalServerError) 401 } 402 403 return nil 404 } 405 406 func (a *App) SendMail(to, subject, htmlBody string) *model.AppError { 407 license := a.License() 408 return mailservice.SendMailUsingConfig(to, subject, htmlBody, a.Config(), license != nil && *license.Features.Compliance) 409 }