From 162c32af7ec9cc434895079cfc7bfc683feb4d4c Mon Sep 17 00:00:00 2001
From: zeripath <art27@cantab.net>
Date: Thu, 12 Aug 2021 08:26:33 +0100
Subject: [PATCH] Send registration email on user autoregistration (#16523)

When users login and are autoregistered send email notification.

Fix #16178

* Protect public functions within the mailer by testing if the mailer is configured

Signed-off-by: Andrew Thornton <art27@cantab.net>

Co-authored-by: 6543 <6543@obermui.de>
---
 services/auth/reverseproxy.go                 |  3 ++
 .../auth/source/ldap/source_authenticate.go   |  8 +++++-
 .../auth/source/pam/source_authenticate.go    | 10 ++++++-
 .../auth/source/smtp/source_authenticate.go   | 10 ++++++-
 services/auth/sspi_windows.go                 |  4 +++
 services/mailer/mail.go                       | 28 +++++++++++++++++++
 services/mailer/mail_comment.go               | 11 ++++++++
 services/mailer/mail_issue.go                 |  6 ++++
 services/mailer/mail_release.go               |  5 ++++
 services/mailer/mail_repo.go                  |  6 ++++
 10 files changed, 88 insertions(+), 3 deletions(-)

diff --git a/services/auth/reverseproxy.go b/services/auth/reverseproxy.go
index 46d8d3fa63..7ff226077c 100644
--- a/services/auth/reverseproxy.go
+++ b/services/auth/reverseproxy.go
@@ -13,6 +13,7 @@ import (
 	"code.gitea.io/gitea/modules/log"
 	"code.gitea.io/gitea/modules/setting"
 	"code.gitea.io/gitea/modules/web/middleware"
+	"code.gitea.io/gitea/services/mailer"
 
 	gouuid "github.com/google/uuid"
 )
@@ -112,5 +113,7 @@ func (r *ReverseProxy) newUser(req *http.Request) *models.User {
 		return nil
 	}
 
+	mailer.SendRegisterNotifyMail(user)
+
 	return user
 }
diff --git a/services/auth/source/ldap/source_authenticate.go b/services/auth/source/ldap/source_authenticate.go
index 1d5e69539b..ecc95fbd56 100644
--- a/services/auth/source/ldap/source_authenticate.go
+++ b/services/auth/source/ldap/source_authenticate.go
@@ -9,6 +9,7 @@ import (
 	"strings"
 
 	"code.gitea.io/gitea/models"
+	"code.gitea.io/gitea/services/mailer"
 )
 
 // Authenticate queries if login/password is valid against the LDAP directory pool,
@@ -84,8 +85,13 @@ func (source *Source) Authenticate(user *models.User, login, password string) (*
 	}
 
 	err := models.CreateUser(user)
+	if err != nil {
+		return user, err
+	}
 
-	if err == nil && isAttributeSSHPublicKeySet && models.AddPublicKeysBySource(user, source.loginSource, sr.SSHPublicKey) {
+	mailer.SendRegisterNotifyMail(user)
+
+	if isAttributeSSHPublicKeySet && models.AddPublicKeysBySource(user, source.loginSource, sr.SSHPublicKey) {
 		err = models.RewriteAllPublicKeys()
 	}
 
diff --git a/services/auth/source/pam/source_authenticate.go b/services/auth/source/pam/source_authenticate.go
index 6ca0642904..8241aed725 100644
--- a/services/auth/source/pam/source_authenticate.go
+++ b/services/auth/source/pam/source_authenticate.go
@@ -11,6 +11,7 @@ import (
 	"code.gitea.io/gitea/models"
 	"code.gitea.io/gitea/modules/auth/pam"
 	"code.gitea.io/gitea/modules/setting"
+	"code.gitea.io/gitea/services/mailer"
 
 	"github.com/google/uuid"
 )
@@ -58,5 +59,12 @@ func (source *Source) Authenticate(user *models.User, login, password string) (*
 		LoginName:   login, // This is what the user typed in
 		IsActive:    true,
 	}
-	return user, models.CreateUser(user)
+
+	if err := models.CreateUser(user); err != nil {
+		return user, err
+	}
+
+	mailer.SendRegisterNotifyMail(user)
+
+	return user, nil
 }
diff --git a/services/auth/source/smtp/source_authenticate.go b/services/auth/source/smtp/source_authenticate.go
index e3dcd83222..cff64c69d2 100644
--- a/services/auth/source/smtp/source_authenticate.go
+++ b/services/auth/source/smtp/source_authenticate.go
@@ -12,6 +12,7 @@ import (
 
 	"code.gitea.io/gitea/models"
 	"code.gitea.io/gitea/modules/util"
+	"code.gitea.io/gitea/services/mailer"
 )
 
 // Authenticate queries if the provided login/password is authenticates against the SMTP server
@@ -74,5 +75,12 @@ func (source *Source) Authenticate(user *models.User, login, password string) (*
 		LoginName:   login,
 		IsActive:    true,
 	}
-	return user, models.CreateUser(user)
+
+	if err := models.CreateUser(user); err != nil {
+		return user, err
+	}
+
+	mailer.SendRegisterNotifyMail(user)
+
+	return user, nil
 }
diff --git a/services/auth/sspi_windows.go b/services/auth/sspi_windows.go
index 8420d43071..d7e0f55242 100644
--- a/services/auth/sspi_windows.go
+++ b/services/auth/sspi_windows.go
@@ -16,6 +16,7 @@ import (
 	"code.gitea.io/gitea/modules/templates"
 	"code.gitea.io/gitea/modules/web/middleware"
 	"code.gitea.io/gitea/services/auth/source/sspi"
+	"code.gitea.io/gitea/services/mailer"
 
 	gouuid "github.com/google/uuid"
 	"github.com/quasoft/websspi"
@@ -197,6 +198,9 @@ func (s *SSPI) newUser(username string, cfg *sspi.Source) (*models.User, error)
 	if err := models.CreateUser(user); err != nil {
 		return nil, err
 	}
+
+	mailer.SendRegisterNotifyMail(user)
+
 	return user, nil
 }
 
diff --git a/services/mailer/mail.go b/services/mailer/mail.go
index 14512d7d65..979f8aa227 100644
--- a/services/mailer/mail.go
+++ b/services/mailer/mail.go
@@ -57,6 +57,10 @@ func InitMailRender(subjectTpl *texttmpl.Template, bodyTpl *template.Template) {
 
 // SendTestMail sends a test mail
 func SendTestMail(email string) error {
+	if setting.MailService == nil {
+		// No mail service configured
+		return nil
+	}
 	return gomail.Send(Sender, NewMessage([]string{email}, "Gitea Test Email!", "Gitea Test Email!").ToMessage())
 }
 
@@ -90,17 +94,29 @@ func sendUserMail(language string, u *models.User, tpl base.TplName, code, subje
 
 // SendActivateAccountMail sends an activation mail to the user (new user registration)
 func SendActivateAccountMail(locale translation.Locale, u *models.User) {
+	if setting.MailService == nil {
+		// No mail service configured
+		return
+	}
 	sendUserMail(locale.Language(), u, mailAuthActivate, u.GenerateEmailActivateCode(u.Email), locale.Tr("mail.activate_account"), "activate account")
 }
 
 // SendResetPasswordMail sends a password reset mail to the user
 func SendResetPasswordMail(u *models.User) {
+	if setting.MailService == nil {
+		// No mail service configured
+		return
+	}
 	locale := translation.NewLocale(u.Language)
 	sendUserMail(u.Language, u, mailAuthResetPassword, u.GenerateEmailActivateCode(u.Email), locale.Tr("mail.reset_password"), "recover account")
 }
 
 // SendActivateEmailMail sends confirmation email to confirm new email address
 func SendActivateEmailMail(u *models.User, email *models.EmailAddress) {
+	if setting.MailService == nil {
+		// No mail service configured
+		return
+	}
 	locale := translation.NewLocale(u.Language)
 	data := map[string]interface{}{
 		"DisplayName":     u.DisplayName(),
@@ -129,6 +145,10 @@ func SendActivateEmailMail(u *models.User, email *models.EmailAddress) {
 
 // SendRegisterNotifyMail triggers a notify e-mail by admin created a account.
 func SendRegisterNotifyMail(u *models.User) {
+	if setting.MailService == nil {
+		// No mail service configured
+		return
+	}
 	locale := translation.NewLocale(u.Language)
 
 	data := map[string]interface{}{
@@ -156,6 +176,10 @@ func SendRegisterNotifyMail(u *models.User) {
 
 // SendCollaboratorMail sends mail notification to new collaborator.
 func SendCollaboratorMail(u, doer *models.User, repo *models.Repository) {
+	if setting.MailService == nil {
+		// No mail service configured
+		return
+	}
 	locale := translation.NewLocale(u.Language)
 	repoName := repo.FullName()
 
@@ -344,6 +368,10 @@ func sanitizeSubject(subject string) string {
 
 // SendIssueAssignedMail composes and sends issue assigned email
 func SendIssueAssignedMail(issue *models.Issue, doer *models.User, content string, comment *models.Comment, recipients []*models.User) error {
+	if setting.MailService == nil {
+		// No mail service configured
+		return nil
+	}
 	langMap := make(map[string][]*models.User)
 	for _, user := range recipients {
 		langMap[user.Language] = append(langMap[user.Language], user)
diff --git a/services/mailer/mail_comment.go b/services/mailer/mail_comment.go
index eca05cef29..eef71557e7 100644
--- a/services/mailer/mail_comment.go
+++ b/services/mailer/mail_comment.go
@@ -7,10 +7,16 @@ package mailer
 import (
 	"code.gitea.io/gitea/models"
 	"code.gitea.io/gitea/modules/log"
+	"code.gitea.io/gitea/modules/setting"
 )
 
 // MailParticipantsComment sends new comment emails to repository watchers and mentioned people.
 func MailParticipantsComment(c *models.Comment, opType models.ActionType, issue *models.Issue, mentions []*models.User) error {
+	if setting.MailService == nil {
+		// No mail service configured
+		return nil
+	}
+
 	content := c.Content
 	if c.Type == models.CommentTypePullPush {
 		content = ""
@@ -30,6 +36,11 @@ func MailParticipantsComment(c *models.Comment, opType models.ActionType, issue
 
 // MailMentionsComment sends email to users mentioned in a code comment
 func MailMentionsComment(pr *models.PullRequest, c *models.Comment, mentions []*models.User) (err error) {
+	if setting.MailService == nil {
+		// No mail service configured
+		return nil
+	}
+
 	visited := make(map[int64]bool, len(mentions)+1)
 	visited[c.Poster.ID] = true
 	if err = mailIssueCommentBatch(
diff --git a/services/mailer/mail_issue.go b/services/mailer/mail_issue.go
index 6ffc08c8c0..6f401e5f92 100644
--- a/services/mailer/mail_issue.go
+++ b/services/mailer/mail_issue.go
@@ -9,6 +9,7 @@ import (
 
 	"code.gitea.io/gitea/models"
 	"code.gitea.io/gitea/modules/log"
+	"code.gitea.io/gitea/modules/setting"
 )
 
 func fallbackMailSubject(issue *models.Issue) string {
@@ -163,6 +164,11 @@ func mailIssueCommentBatch(ctx *mailCommentContext, users []*models.User, visite
 // MailParticipants sends new issue thread created emails to repository watchers
 // and mentioned people.
 func MailParticipants(issue *models.Issue, doer *models.User, opType models.ActionType, mentions []*models.User) error {
+	if setting.MailService == nil {
+		// No mail service configured
+		return nil
+	}
+
 	content := issue.Content
 	if opType == models.ActionCloseIssue || opType == models.ActionClosePullRequest ||
 		opType == models.ActionReopenIssue || opType == models.ActionReopenPullRequest ||
diff --git a/services/mailer/mail_release.go b/services/mailer/mail_release.go
index f92d3a78fa..a6fc28a5ca 100644
--- a/services/mailer/mail_release.go
+++ b/services/mailer/mail_release.go
@@ -23,6 +23,11 @@ const (
 
 // MailNewRelease send new release notify to all all repo watchers.
 func MailNewRelease(rel *models.Release) {
+	if setting.MailService == nil {
+		// No mail service configured
+		return
+	}
+
 	watcherIDList, err := models.GetRepoWatchersIDs(rel.RepoID)
 	if err != nil {
 		log.Error("GetRepoWatchersIDs(%d): %v", rel.RepoID, err)
diff --git a/services/mailer/mail_repo.go b/services/mailer/mail_repo.go
index 4e629ee5c7..ef85f4aa54 100644
--- a/services/mailer/mail_repo.go
+++ b/services/mailer/mail_repo.go
@@ -9,12 +9,18 @@ import (
 	"fmt"
 
 	"code.gitea.io/gitea/models"
+	"code.gitea.io/gitea/modules/setting"
 	"code.gitea.io/gitea/modules/templates"
 	"code.gitea.io/gitea/modules/translation"
 )
 
 // SendRepoTransferNotifyMail triggers a notification e-mail when a pending repository transfer was created
 func SendRepoTransferNotifyMail(doer, newOwner *models.User, repo *models.Repository) error {
+	if setting.MailService == nil {
+		// No mail service configured
+		return nil
+	}
+
 	if newOwner.IsOrganization() {
 		users, err := models.GetUsersWhoCanCreateOrgRepo(newOwner.ID)
 		if err != nil {