summaryrefslogtreecommitdiffstats
path: root/services
diff options
context:
space:
mode:
Diffstat (limited to 'services')
-rw-r--r--services/mailer/mail_admin_new_user.go81
-rw-r--r--services/mailer/mail_admin_new_user_test.go98
-rw-r--r--services/mailer/notify.go4
-rw-r--r--services/notify/notifier.go2
-rw-r--r--services/notify/notify.go7
-rw-r--r--services/notify/null.go3
6 files changed, 195 insertions, 0 deletions
diff --git a/services/mailer/mail_admin_new_user.go b/services/mailer/mail_admin_new_user.go
new file mode 100644
index 0000000000..e9610e626a
--- /dev/null
+++ b/services/mailer/mail_admin_new_user.go
@@ -0,0 +1,81 @@
+// Copyright 2023 The Forgejo Authors. All rights reserved.
+// SPDX-License-Identifier: MIT
+package mailer
+
+import (
+ "bytes"
+ "context"
+ "strconv"
+
+ user_model "code.gitea.io/gitea/models/user"
+ "code.gitea.io/gitea/modules/base"
+ "code.gitea.io/gitea/modules/log"
+ "code.gitea.io/gitea/modules/setting"
+ "code.gitea.io/gitea/modules/templates"
+ "code.gitea.io/gitea/modules/translation"
+)
+
+const (
+ tplNewUserMail base.TplName = "notify/admin_new_user"
+)
+
+var sa = SendAsync
+
+// MailNewUser sends notification emails on new user registrations to all admins
+func MailNewUser(ctx context.Context, u *user_model.User) {
+ if !setting.Admin.SendNotificationEmailOnNewUser {
+ return
+ }
+
+ if setting.MailService == nil {
+ // No mail service configured
+ return
+ }
+
+ recipients, err := user_model.GetAllAdmins(ctx)
+ if err != nil {
+ log.Error("user_model.GetAllAdmins: %v", err)
+ return
+ }
+
+ langMap := make(map[string][]string)
+ for _, r := range recipients {
+ langMap[r.Language] = append(langMap[r.Language], r.Email)
+ }
+
+ for lang, tos := range langMap {
+ mailNewUser(ctx, u, lang, tos)
+ }
+}
+
+func mailNewUser(ctx context.Context, u *user_model.User, lang string, tos []string) {
+ locale := translation.NewLocale(lang)
+
+ manageUserURL := setting.AppURL + "admin/users/" + strconv.FormatInt(u.ID, 10)
+ subject := locale.Tr("mail.admin.new_user.subject", u.Name)
+ body := locale.Tr("mail.admin.new_user.text", manageUserURL)
+ mailMeta := map[string]any{
+ "NewUser": u,
+ "NewUserUrl": u.HTMLURL(),
+ "Subject": subject,
+ "Body": body,
+ "Language": locale.Language(),
+ "Locale": locale,
+ "Str2html": templates.Str2html,
+ }
+
+ var mailBody bytes.Buffer
+
+ if err := bodyTemplates.ExecuteTemplate(&mailBody, string(tplNewUserMail), mailMeta); err != nil {
+ log.Error("ExecuteTemplate [%s]: %v", string(tplNewUserMail)+"/body", err)
+ return
+ }
+
+ msgs := make([]*Message, 0, len(tos))
+ for _, to := range tos {
+ msg := NewMessage(to, subject, mailBody.String())
+ msg.Info = subject
+ msgs = append(msgs, msg)
+ }
+ sa(msgs...)
+}
diff --git a/services/mailer/mail_admin_new_user_test.go b/services/mailer/mail_admin_new_user_test.go
new file mode 100644
index 0000000000..b89d888ee1
--- /dev/null
+++ b/services/mailer/mail_admin_new_user_test.go
@@ -0,0 +1,98 @@
+// Copyright 2023 The Gitea Authors. All rights reserved.
+// SPDX-License-Identifier: MIT
+
+package mailer
+
+import (
+ "context"
+ "strconv"
+ "testing"
+
+ "code.gitea.io/gitea/models/db"
+ user_model "code.gitea.io/gitea/models/user"
+ "code.gitea.io/gitea/modules/setting"
+ "code.gitea.io/gitea/modules/translation"
+
+ "github.com/stretchr/testify/assert"
+ "github.com/stretchr/testify/require"
+)
+
+func getTestUsers(t *testing.T) []*user_model.User {
+ t.Helper()
+ admin := new(user_model.User)
+ admin.Name = "testadmin"
+ admin.IsAdmin = true
+ admin.Language = "en_US"
+ admin.Email = "admin@example.com"
+ require.NoError(t, user_model.CreateUser(db.DefaultContext, admin))
+
+ newUser := new(user_model.User)
+ newUser.Name = "new_user"
+ newUser.Language = "en_US"
+ newUser.IsAdmin = false
+ newUser.Email = "new_user@example.com"
+ newUser.LastLoginUnix = 1693648327
+ newUser.CreatedUnix = 1693648027
+ require.NoError(t, user_model.CreateUser(db.DefaultContext, newUser))
+
+ return []*user_model.User{admin, newUser}
+}
+
+func cleanUpUsers(ctx context.Context, users []*user_model.User) {
+ for _, u := range users {
+ db.DeleteByID[user_model.User](ctx, u.ID)
+ }
+}
+
+func TestAdminNotificationMail_test(t *testing.T) {
+ translation.InitLocales(context.Background())
+ locale := translation.NewLocale("")
+ key := "mail.admin.new_user.user_info"
+ translatedKey := locale.Tr(key)
+ require.NotEqualValues(t, key, translatedKey)
+
+ mailService := setting.Mailer{
+ From: "test@example.com",
+ Protocol: "dummy",
+ }
+
+ setting.MailService = &mailService
+
+ // test with SEND_NOTIFICATION_EMAIL_ON_NEW_USER enabled
+ setting.Admin.SendNotificationEmailOnNewUser = true
+
+ ctx := context.Background()
+ NewContext(ctx)
+
+ users := getTestUsers(t)
+ oldSendAsync := sa
+ defer func() {
+ sa = oldSendAsync
+ cleanUpUsers(ctx, users)
+ }()
+
+ called := false
+ sa = func(msgs ...*Message) {
+ assert.Equal(t, len(msgs), 1, "Test provides only one admin user, so only one email must be sent")
+ assert.Equal(t, msgs[0].To, users[0].Email, "checks if the recipient is the admin of the instance")
+ manageUserURL := setting.AppURL + "admin/users/" + strconv.FormatInt(users[1].ID, 10)
+ assert.Contains(t, msgs[0].Body, manageUserURL)
+ assert.Contains(t, msgs[0].Body, users[1].HTMLURL())
+ assert.Contains(t, msgs[0].Body, translatedKey, "the .Locale translates to nothing")
+ assert.Contains(t, msgs[0].Body, users[1].Name, "user name of the newly created user")
+ for _, untranslated := range []string{"mail.admin", "admin.users"} {
+ assert.NotContains(t, msgs[0].Body, untranslated, "this is an untranslated placeholder prefix")
+ }
+ called = true
+ }
+ MailNewUser(ctx, users[1])
+ assert.True(t, called)
+
+ // test with SEND_NOTIFICATION_EMAIL_ON_NEW_USER disabled; emails shouldn't be sent
+ setting.Admin.SendNotificationEmailOnNewUser = false
+ sa = func(msgs ...*Message) {
+ assert.Equal(t, 1, 0, "this shouldn't execute. MailNewUser must exit early since SEND_NOTIFICATION_EMAIL_ON_NEW_USER is disabled")
+ }
+
+ MailNewUser(ctx, users[1])
+}
diff --git a/services/mailer/notify.go b/services/mailer/notify.go
index e48b5d399d..54ab80aab9 100644
--- a/services/mailer/notify.go
+++ b/services/mailer/notify.go
@@ -202,3 +202,7 @@ func (m *mailNotifier) RepoPendingTransfer(ctx context.Context, doer, newOwner *
log.Error("SendRepoTransferNotifyMail: %v", err)
}
}
+
+func (m *mailNotifier) NewUserSignUp(ctx context.Context, newUser *user_model.User) {
+ MailNewUser(ctx, newUser)
+}
diff --git a/services/notify/notifier.go b/services/notify/notifier.go
index ed053a812a..3230a5e5f5 100644
--- a/services/notify/notifier.go
+++ b/services/notify/notifier.go
@@ -59,6 +59,8 @@ type Notifier interface {
EditWikiPage(ctx context.Context, doer *user_model.User, repo *repo_model.Repository, page, comment string)
DeleteWikiPage(ctx context.Context, doer *user_model.User, repo *repo_model.Repository, page string)
+ NewUserSignUp(ctx context.Context, newUser *user_model.User)
+
NewRelease(ctx context.Context, rel *repo_model.Release)
UpdateRelease(ctx context.Context, doer *user_model.User, rel *repo_model.Release)
DeleteRelease(ctx context.Context, doer *user_model.User, rel *repo_model.Release)
diff --git a/services/notify/notify.go b/services/notify/notify.go
index 16fbb6325d..9cb329d302 100644
--- a/services/notify/notify.go
+++ b/services/notify/notify.go
@@ -347,6 +347,13 @@ func RepoPendingTransfer(ctx context.Context, doer, newOwner *user_model.User, r
}
}
+// NewUserSignUp notifies about a newly signed up user to notifiers
+func NewUserSignUp(ctx context.Context, newUser *user_model.User) {
+ for _, notifier := range notifiers {
+ notifier.NewUserSignUp(ctx, newUser)
+ }
+}
+
// PackageCreate notifies creation of a package to notifiers
func PackageCreate(ctx context.Context, doer *user_model.User, pd *packages_model.PackageDescriptor) {
for _, notifier := range notifiers {
diff --git a/services/notify/null.go b/services/notify/null.go
index dddd421bef..894d118eac 100644
--- a/services/notify/null.go
+++ b/services/notify/null.go
@@ -197,6 +197,9 @@ func (*NullNotifier) SyncDeleteRef(ctx context.Context, doer *user_model.User, r
func (*NullNotifier) RepoPendingTransfer(ctx context.Context, doer, newOwner *user_model.User, repo *repo_model.Repository) {
}
+func (*NullNotifier) NewUserSignUp(ctx context.Context, newUser *user_model.User) {
+}
+
// PackageCreate places a place holder function
func (*NullNotifier) PackageCreate(ctx context.Context, doer *user_model.User, pd *packages_model.PackageDescriptor) {
}