Skip to content

Commit 7d2d997

Browse files
realaravinthearl-warren
authored andcommitted
[GITEA] notifies admins on new user registration
Sends email with information on the new user (time of creation and time of last sign-in) and a link to manage the new user from the admin panel closes: https://codeberg.org/forgejo/forgejo/issues/480 Reviewed-on: https://codeberg.org/forgejo/forgejo/pulls/1371 Co-authored-by: Aravinth Manivannan <[email protected]> Co-committed-by: Aravinth Manivannan <[email protected]> (cherry picked from commit c721aa828ba6aec5ef95459cfc632a0a1f7463e9) (cherry picked from commit 6487efcb9da61be1f802f1cd8007330153322770) Conflicts: modules/notification/base/notifier.go modules/notification/base/null.go modules/notification/notification.go https://codeberg.org/forgejo/forgejo/pulls/1422 (cherry picked from commit 7ea66ee1c5dd21d9e6a43f961e8adc71ec79b806) Conflicts: services/notify/notifier.go services/notify/notify.go services/notify/null.go https://codeberg.org/forgejo/forgejo/pulls/1469
1 parent 589e7d3 commit 7d2d997

File tree

11 files changed

+218
-1
lines changed

11 files changed

+218
-1
lines changed

custom/conf/app.example.ini

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1455,6 +1455,8 @@ LEVEL = Info
14551455
;;
14561456
;; Default configuration for email notifications for users (user configurable). Options: enabled, onmention, disabled
14571457
;DEFAULT_EMAIL_NOTIFICATIONS = enabled
1458+
;; Send email notifications to all instance admins on new user sign-ups. Options: enabled, true, false
1459+
;NOTIFY_NEW_SIGN_UPS = false
14581460

14591461
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
14601462
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;

modules/setting/admin.go

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -7,6 +7,7 @@ package setting
77
var Admin struct {
88
DisableRegularOrgCreation bool
99
DefaultEmailNotification string
10+
NotifyNewSignUps bool
1011
}
1112

1213
func loadAdminFrom(rootCfg ConfigProvider) {

options/locale/locale_en-US.ini

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -439,6 +439,10 @@ activate_email = Verify your email address
439439
activate_email.title = %s, please verify your email address
440440
activate_email.text = Please click the following link to verify your email address within <b>%s</b>:
441441

442+
admin.new_user.subject = New user %s
443+
admin.new_user.user_info = User Information
444+
admin.new_user.text = Please <a href="%s">click here</a> to manage the user from the admin panel.
445+
442446
register_notify = Welcome to Gitea
443447
register_notify.title = %[1]s, welcome to %[2]s
444448
register_notify.text_1 = this is your registration confirmation email for %s!

routers/web/auth/auth.go

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -30,6 +30,7 @@ import (
3030
"code.gitea.io/gitea/services/externalaccount"
3131
"code.gitea.io/gitea/services/forms"
3232
"code.gitea.io/gitea/services/mailer"
33+
notify_service "code.gitea.io/gitea/services/notify"
3334

3435
"github.com/markbates/goth"
3536
)
@@ -586,6 +587,7 @@ func handleUserCreated(ctx *context.Context, u *user_model.User, gothUser *goth.
586587
}
587588
}
588589

590+
notify_service.NewUserSignUp(ctx, u)
589591
// update external user information
590592
if gothUser != nil {
591593
if err := externalaccount.UpdateExternalUser(u, *gothUser); err != nil {
@@ -609,7 +611,6 @@ func handleUserCreated(ctx *context.Context, u *user_model.User, gothUser *goth.
609611
ctx.Data["Email"] = u.Email
610612
ctx.Data["ActiveCodeLives"] = timeutil.MinutesToFriendly(setting.Service.ActiveCodeLives, ctx.Locale)
611613
ctx.HTML(http.StatusOK, TplActivate)
612-
613614
if setting.CacheService.Enabled {
614615
if err := ctx.Cache.Put("MailResendLimit_"+u.LowerName, u.LowerName, 180); err != nil {
615616
log.Error("Set cache(MailResendLimit) fail: %v", err)
Lines changed: 82 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,82 @@
1+
// Copyright 2023 The Forgejo Authors. All rights reserved.
2+
// SPDX-License-Identifier: MIT
3+
package mailer
4+
5+
import (
6+
"bytes"
7+
"context"
8+
"strconv"
9+
10+
user_model "code.gitea.io/gitea/models/user"
11+
"code.gitea.io/gitea/modules/base"
12+
"code.gitea.io/gitea/modules/log"
13+
"code.gitea.io/gitea/modules/setting"
14+
"code.gitea.io/gitea/modules/templates"
15+
"code.gitea.io/gitea/modules/translation"
16+
)
17+
18+
const (
19+
tplNewUserMail base.TplName = "admin_new_user"
20+
)
21+
22+
var sa = SendAsyncs
23+
24+
// MailNewUser sends notification emails on new user registrations to all admins
25+
func MailNewUser(ctx context.Context, u *user_model.User) {
26+
if !setting.Admin.NotifyNewSignUps {
27+
return
28+
}
29+
30+
if setting.MailService == nil {
31+
// No mail service configured
32+
return
33+
}
34+
35+
recipients, err := user_model.GetAllUsers(ctx)
36+
if err != nil {
37+
log.Error("user_model.GetAllUsers: %v", err)
38+
return
39+
}
40+
41+
langMap := make(map[string][]string)
42+
for _, r := range recipients {
43+
if r.IsAdmin {
44+
langMap[r.Language] = append(langMap[r.Language], r.Email)
45+
}
46+
}
47+
48+
for lang, tos := range langMap {
49+
mailNewUser(ctx, u, lang, tos)
50+
}
51+
}
52+
53+
func mailNewUser(ctx context.Context, u *user_model.User, lang string, tos []string) {
54+
locale := translation.NewLocale(lang)
55+
56+
subject := locale.Tr("mail.admin.new_user.subject", u.Name)
57+
manageUserURL := setting.AppSubURL + "/admin/users/" + strconv.FormatInt(u.ID, 10)
58+
body := locale.Tr("mail.admin.new_user.text", manageUserURL)
59+
mailMeta := map[string]any{
60+
"NewUser": u,
61+
"Subject": subject,
62+
"Body": body,
63+
"Language": locale.Language(),
64+
"locale": locale,
65+
"Str2html": templates.Str2html,
66+
}
67+
68+
var mailBody bytes.Buffer
69+
70+
if err := bodyTemplates.ExecuteTemplate(&mailBody, string(tplNewUserMail), mailMeta); err != nil {
71+
log.Error("ExecuteTemplate [%s]: %v", string(tplNewUserMail)+"/body", err)
72+
return
73+
}
74+
75+
msgs := make([]*Message, 0, len(tos))
76+
for _, to := range tos {
77+
msg := NewMessage(to, subject, mailBody.String())
78+
msg.Info = subject
79+
msgs = append(msgs, msg)
80+
}
81+
sa(msgs)
82+
}
Lines changed: 88 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,88 @@
1+
// Copyright 2023 The Gitea Authors. All rights reserved.
2+
// SPDX-License-Identifier: MIT
3+
4+
package mailer
5+
6+
import (
7+
"context"
8+
"strconv"
9+
"strings"
10+
"testing"
11+
12+
"code.gitea.io/gitea/models/db"
13+
user_model "code.gitea.io/gitea/models/user"
14+
"code.gitea.io/gitea/modules/setting"
15+
16+
"github.com/stretchr/testify/assert"
17+
)
18+
19+
func getTestUsers() []*user_model.User {
20+
admin := new(user_model.User)
21+
admin.Name = "admin"
22+
admin.IsAdmin = true
23+
admin.Language = "en_US"
24+
admin.Email = "[email protected]"
25+
26+
newUser := new(user_model.User)
27+
newUser.Name = "new_user"
28+
newUser.Language = "en_US"
29+
newUser.IsAdmin = false
30+
newUser.Email = "[email protected]"
31+
newUser.LastLoginUnix = 1693648327
32+
newUser.CreatedUnix = 1693648027
33+
34+
user_model.CreateUser(db.DefaultContext, admin)
35+
user_model.CreateUser(db.DefaultContext, newUser)
36+
37+
users := make([]*user_model.User, 0)
38+
users = append(users, admin)
39+
users = append(users, newUser)
40+
41+
return users
42+
}
43+
44+
func cleanUpUsers(ctx context.Context, users []*user_model.User) {
45+
for _, u := range users {
46+
db.DeleteByID(ctx, u.ID, new(user_model.User))
47+
}
48+
}
49+
50+
func TestAdminNotificationMail_test(t *testing.T) {
51+
mailService := setting.Mailer{
52+
53+
Protocol: "dummy",
54+
}
55+
56+
setting.MailService = &mailService
57+
setting.Domain = "localhost"
58+
setting.AppSubURL = "http://localhost"
59+
60+
// test with NOTIFY_NEW_SIGNUPS enabled
61+
setting.Admin.NotifyNewSignUps = true
62+
63+
ctx := context.Background()
64+
NewContext(ctx)
65+
66+
users := getTestUsers()
67+
oldSendAsyncs := sa
68+
defer func() {
69+
sa = oldSendAsyncs
70+
cleanUpUsers(ctx, users)
71+
}()
72+
73+
sa = func(msgs []*Message) {
74+
assert.Equal(t, len(msgs), 1, "Test provides only one admin user, so only one email must be sent")
75+
assert.Equal(t, msgs[0].To, users[0].Email, "checks if the recipient is the admin of the instance")
76+
manageUserURL := "/admin/users/" + strconv.FormatInt(users[1].ID, 10)
77+
assert.True(t, strings.ContainsAny(msgs[0].Body, manageUserURL), "checks if the message contains the link to manage the newly created user from the admin panel")
78+
}
79+
MailNewUser(ctx, users[1])
80+
81+
// test with NOTIFY_NEW_SIGNUPS disabled; emails shouldn't be sent
82+
setting.Admin.NotifyNewSignUps = false
83+
sa = func(msgs []*Message) {
84+
assert.Equal(t, 1, 0, "this shouldn't execute. MailNewUser must exit early since NOTIFY_NEW_SIGNUPS is disabled")
85+
}
86+
87+
MailNewUser(ctx, users[1])
88+
}

services/mailer/notify.go

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -202,3 +202,7 @@ func (m *mailNotifier) RepoPendingTransfer(ctx context.Context, doer, newOwner *
202202
log.Error("SendRepoTransferNotifyMail: %v", err)
203203
}
204204
}
205+
206+
func (m *mailNotifier) NewUserSignUp(ctx context.Context, newUser *user_model.User) {
207+
MailNewUser(ctx, newUser)
208+
}

services/notify/notifier.go

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -59,6 +59,8 @@ type Notifier interface {
5959
EditUncycloPage(ctx context.Context, doer *user_model.User, repo *repo_model.Repository, page, comment string)
6060
DeleteUncycloPage(ctx context.Context, doer *user_model.User, repo *repo_model.Repository, page string)
6161

62+
NewUserSignUp(ctx context.Context, newUser *user_model.User)
63+
6264
NewRelease(ctx context.Context, rel *repo_model.Release)
6365
UpdateRelease(ctx context.Context, doer *user_model.User, rel *repo_model.Release)
6466
DeleteRelease(ctx context.Context, doer *user_model.User, rel *repo_model.Release)

services/notify/notify.go

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -347,6 +347,13 @@ func RepoPendingTransfer(ctx context.Context, doer, newOwner *user_model.User, r
347347
}
348348
}
349349

350+
// NewUserSignUp notifies deletion of a package to notifiers
351+
func NewUserSignUp(ctx context.Context, newUser *user_model.User) {
352+
for _, notifier := range notifiers {
353+
notifier.NewUserSignUp(ctx, newUser)
354+
}
355+
}
356+
350357
// PackageCreate notifies creation of a package to notifiers
351358
func PackageCreate(ctx context.Context, doer *user_model.User, pd *packages_model.PackageDescriptor) {
352359
for _, notifier := range notifiers {

services/notify/null.go

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -197,6 +197,10 @@ func (*NullNotifier) SyncDeleteRef(ctx context.Context, doer *user_model.User, r
197197
func (*NullNotifier) RepoPendingTransfer(ctx context.Context, doer, newOwner *user_model.User, repo *repo_model.Repository) {
198198
}
199199

200+
// NotifyNewUserSignUp notifies deletion of a package to notifiers
201+
func (*NullNotifier) NewUserSignUp(ctx context.Context, newUser *user_model.User) {
202+
}
203+
200204
// PackageCreate places a place holder function
201205
func (*NullNotifier) PackageCreate(ctx context.Context, doer *user_model.User, pd *packages_model.PackageDescriptor) {
202206
}

templates/mail/admin_new_user.tmpl

Lines changed: 22 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,22 @@
1+
<!DOCTYPE html>
2+
<html>
3+
<head>
4+
<meta http-equiv="Content-Type" content="text/html; charset=utf-8">
5+
<title>{{.Subject}}</title>
6+
7+
<style>
8+
blockquote { padding-left: 1em; margin: 1em 0; border-left: 1px solid grey; color: #777}
9+
.footer { font-size:small; color:#666;}
10+
</style>
11+
12+
</head>
13+
14+
<body>
15+
<ul>
16+
<h3>{{.locale.Tr "mail.admin.new_user.user_info"}}</h3>
17+
<li>{{.locale.Tr "admin.users.created"}}: {{DateTime "full" .NewUser.LastLoginUnix}}</li>
18+
<li>{{.locale.Tr "admin.users.last_login"}}: {{DateTime "full" .NewUser.CreatedUnix}}</li>
19+
</ul>
20+
<p> {{.Body | Str2html}} </p>
21+
</body>
22+
</html>

0 commit comments

Comments
 (0)