diff options
Diffstat (limited to 'services')
-rw-r--r-- | services/mailer/mail_admin_new_user.go | 81 | ||||
-rw-r--r-- | services/mailer/mail_admin_new_user_test.go | 98 | ||||
-rw-r--r-- | services/mailer/notify.go | 4 | ||||
-rw-r--r-- | services/notify/notifier.go | 2 | ||||
-rw-r--r-- | services/notify/notify.go | 7 | ||||
-rw-r--r-- | services/notify/null.go | 3 |
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) { } |