Skip to content

Commit 02d3c12

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 (cherry picked from commit 7d2d997) (cherry picked from commit 435a54f14039408b315c99063bdce28c7ef6fe2f) (cherry picked from commit 8ec7b3e4484383445fa2622a28bb4f5c990dd4f2) [GITEA] notifies admins on new user registration (squash) performance bottleneck Refs: https://codeberg.org/forgejo/forgejo/issues/1479 (cherry picked from commit 97ac914) (cherry picked from commit 19f295c16bd392aa438477fa3c42038d63d1a06a) (cherry picked from commit 3367dcb2cf5328e2afc89f7d5a008b64ede1c987) [GITEA] notifies admins on new user registration (squash) cosmetic changes Co-authored-by: delvh <[email protected]> (cherry picked from commit 9f1670e) (cherry picked from commit de5bb2a224ab2ae9be891de1ee88a7454a07f7e9) (cherry picked from commit 8f8e52f31a4da080465521747a2c5c0c51ed65e3) (cherry picked from commit e0d51303129fe8763d87ed5f859eeae8f0cc6188) (cherry picked from commit f1288d6d9bfc9150596cb2f7ddb7300cf7ab6952) (cherry picked from commit 1db4736fd7cd75027f3cdf805e0f86c3a5f69c9d) (cherry picked from commit e8dcbb6cd68064209cdbe054d5886710cbe2925d) (cherry picked from commit 09625d647629b85397270e14dfe22258df2bcc43) [GITEA] notifies admins on new user registration (squash) ctx.Locale (cherry picked from commit dab7212fad44a252a1acf8da71b254b1a6715121) (cherry picked from commit 9b7bbae8c4cd5dc4d36726f10870462c8985e543) (cherry picked from commit f750b71d3db9a24dc2722effb8bbc2dded657cbb) (cherry picked from commit f79af366796a8ab581bbfa1f5609dc721798ae68) (cherry picked from commit e76eee334e446a45d841caf19a7c18eab89ca457) [GITEA] notifies admins on new user registration (squash) fix locale (cherry picked from commit 54cd100d8da37ccb0a545e2545995066f92180f0) (cherry picked from commit 053dbd3d50d3c7d1afae8d31c25bda92ceb8f8c0) [GITEA] notifies admins on new user registration (squash) fix URL 1. Use absolute URL in the admin panel link sent on new registrations 2. Include absolute URL of the newly signed-up user's profile. New email looks like this: <details><summary>Please click to expand</summary> ``` --153937b1864f158f4fd145c4b5d4a513568681dd489021dd466a8ad7b770 Content-Transfer-Encoding: quoted-printable Content-Type: text/plain; charset=UTF-8 User Information: @realaravinth ( http://localhost:3000/realaravinth ) ---------------------------------------------------------------------- * Created: 2023-12-13 19:36:50 +05:30 Please click here ( http://localhost:3000/admin/users/9 ) to manage the use= r from the admin panel. --153937b1864f158f4fd145c4b5d4a513568681dd489021dd466a8ad7b770 Content-Transfer-Encoding: quoted-printable Content-Type: text/html; charset=UTF-8 <!DOCTYPE html> <html> <head> <meta http-equiv=3D"Content-Type" content=3D"text/html; charset=3Dutf-8"> <title>New user realaravinth just signed up</title> <style> blockquote { padding-left: 1em; margin: 1em 0; border-left: 1px solid gre= y; color: go-gitea#777} .footer { font-size:small; color:go-gitea#666;} </style> </head> <body> <ul> <h3>User Information: <a href=3D"http://localhost:3000/realaravinth">@rea= laravinth</a></h3> <li>Created: <relative-time format=3D"datetime" weekday=3D"" year=3D"nume= ric" month=3D"short" day=3D"numeric" hour=3D"numeric" minute=3D"numeric" se= cond=3D"numeric" datetime=3D"2023-12-13T19:36:50+05:30">2023-12-13 19:36:50= +05:30</relative-time></li> </ul> <p> Please <a href=3D"http://localhost:3000/admin/users/9" rel=3D"nofollow= ">click here</a> to manage the user from the admin panel. </p> </body> </html> --153937b1864f158f4fd145c4b5d4a513568681dd489021dd466a8ad7b770-- ``` </details> fixes: https://codeberg.org/forgejo/forgejo/issues/1927 Reviewed-on: https://codeberg.org/forgejo/forgejo/pulls/1940 Reviewed-by: Earl Warren <[email protected]> Reviewed-by: Gusted <[email protected]> Co-authored-by: Aravinth Manivannan <[email protected]> Co-committed-by: Aravinth Manivannan <[email protected]> (cherry picked from commit b8d764e36a0cd8e60627805f87b84bb04152e9c1) (cherry picked from commit d48b84f623e369222e5e15965f22e27d74ff4243) Conflicts: routers/web/auth/auth.go https://codeberg.org/forgejo/forgejo/pulls/2034
1 parent 358222a commit 02d3c12

File tree

14 files changed

+244
-2
lines changed

14 files changed

+244
-2
lines changed

custom/conf/app.example.ini

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1476,6 +1476,8 @@ LEVEL = Info
14761476
;;
14771477
;; Default configuration for email notifications for users (user configurable). Options: enabled, onmention, disabled
14781478
;DEFAULT_EMAIL_NOTIFICATIONS = enabled
1479+
;; Send an email to all admins when a new user signs up to inform the admins about this act. Options: true, false
1480+
;SEND_NOTIFICATION_EMAIL_ON_NEW_USER = false
14791481

14801482
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
14811483
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;

docs/content/administration/config-cheat-sheet.en-us.md

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -517,6 +517,7 @@ And the following unique queues:
517517

518518
- `DEFAULT_EMAIL_NOTIFICATIONS`: **enabled**: Default configuration for email notifications for users (user configurable). Options: enabled, onmention, disabled
519519
- `DISABLE_REGULAR_ORG_CREATION`: **false**: Disallow regular (non-admin) users from creating organizations.
520+
- `SEND_NOTIFICATION_EMAIL_ON_NEW_USER`: **false**: Send an email to all admins when a new user signs up to inform the admins about this act.
520521

521522
## Security (`security`)
522523

models/user/user.go

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -223,6 +223,12 @@ func GetAllUsers(ctx context.Context) ([]*User, error) {
223223
return users, db.GetEngine(ctx).OrderBy("id").Where("type = ?", UserTypeIndividual).Find(&users)
224224
}
225225

226+
// GetAllAdmins returns a slice of all adminusers found in DB.
227+
func GetAllAdmins(ctx context.Context) ([]*User, error) {
228+
users := make([]*User, 0)
229+
return users, db.GetEngine(ctx).OrderBy("id").Where("type = ?", UserTypeIndividual).And("is_admin = ?", true).Find(&users)
230+
}
231+
226232
// IsLocal returns true if user login type is LoginPlain.
227233
func (u *User) IsLocal() bool {
228234
return u.LoginType <= auth.Plain

models/user/user_test.go

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -544,3 +544,13 @@ func Test_ValidateUser(t *testing.T) {
544544
assert.EqualValues(t, expected, err == nil, fmt.Sprintf("case: %+v", kase))
545545
}
546546
}
547+
548+
func TestGetAllAdmins(t *testing.T) {
549+
assert.NoError(t, unittest.PrepareTestDatabase())
550+
551+
admins, err := user_model.GetAllAdmins(db.DefaultContext)
552+
assert.NoError(t, err)
553+
554+
assert.Len(t, admins, 1)
555+
assert.Equal(t, int64(1), admins[0].ID)
556+
}

modules/setting/admin.go

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -5,8 +5,9 @@ package setting
55

66
// Admin settings
77
var Admin struct {
8-
DisableRegularOrgCreation bool
9-
DefaultEmailNotification string
8+
DisableRegularOrgCreation bool
9+
DefaultEmailNotification string
10+
SendNotificationEmailOnNewUser 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
@@ -440,6 +440,10 @@ activate_email = Verify your email address
440440
activate_email.title = %s, please verify your email address
441441
activate_email.text = Please click the following link to verify your email address within <b>%s</b>:
442442

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

routers/web/auth/auth.go

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -32,6 +32,7 @@ import (
3232
"code.gitea.io/gitea/services/externalaccount"
3333
"code.gitea.io/gitea/services/forms"
3434
"code.gitea.io/gitea/services/mailer"
35+
notify_service "code.gitea.io/gitea/services/notify"
3536

3637
"github.com/markbates/goth"
3738
)
@@ -600,6 +601,7 @@ func handleUserCreated(ctx *context.Context, u *user_model.User, gothUser *goth.
600601
}
601602
}
602603

604+
notify_service.NewUserSignUp(ctx, u)
603605
// update external user information
604606
if gothUser != nil {
605607
if err := externalaccount.UpdateExternalUser(ctx, u, *gothUser); err != nil {
Lines changed: 81 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,81 @@
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 = "notify/admin_new_user"
20+
)
21+
22+
var sa = SendAsync
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.SendNotificationEmailOnNewUser {
27+
return
28+
}
29+
30+
if setting.MailService == nil {
31+
// No mail service configured
32+
return
33+
}
34+
35+
recipients, err := user_model.GetAllAdmins(ctx)
36+
if err != nil {
37+
log.Error("user_model.GetAllAdmins: %v", err)
38+
return
39+
}
40+
41+
langMap := make(map[string][]string)
42+
for _, r := range recipients {
43+
langMap[r.Language] = append(langMap[r.Language], r.Email)
44+
}
45+
46+
for lang, tos := range langMap {
47+
mailNewUser(ctx, u, lang, tos)
48+
}
49+
}
50+
51+
func mailNewUser(ctx context.Context, u *user_model.User, lang string, tos []string) {
52+
locale := translation.NewLocale(lang)
53+
54+
manageUserURL := setting.AppURL + "admin/users/" + strconv.FormatInt(u.ID, 10)
55+
subject := locale.Tr("mail.admin.new_user.subject", u.Name)
56+
body := locale.Tr("mail.admin.new_user.text", manageUserURL)
57+
mailMeta := map[string]any{
58+
"NewUser": u,
59+
"NewUserUrl": u.HTMLURL(),
60+
"Subject": subject,
61+
"Body": body,
62+
"Language": locale.Language(),
63+
"Locale": locale,
64+
"Str2html": templates.Str2html,
65+
}
66+
67+
var mailBody bytes.Buffer
68+
69+
if err := bodyTemplates.ExecuteTemplate(&mailBody, string(tplNewUserMail), mailMeta); err != nil {
70+
log.Error("ExecuteTemplate [%s]: %v", string(tplNewUserMail)+"/body", err)
71+
return
72+
}
73+
74+
msgs := make([]*Message, 0, len(tos))
75+
for _, to := range tos {
76+
msg := NewMessage(to, subject, mailBody.String())
77+
msg.Info = subject
78+
msgs = append(msgs, msg)
79+
}
80+
sa(msgs...)
81+
}
Lines changed: 98 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,98 @@
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+
"testing"
10+
11+
"code.gitea.io/gitea/models/db"
12+
user_model "code.gitea.io/gitea/models/user"
13+
"code.gitea.io/gitea/modules/setting"
14+
"code.gitea.io/gitea/modules/translation"
15+
16+
"github.com/stretchr/testify/assert"
17+
"github.com/stretchr/testify/require"
18+
)
19+
20+
func getTestUsers(t *testing.T) []*user_model.User {
21+
t.Helper()
22+
admin := new(user_model.User)
23+
admin.Name = "testadmin"
24+
admin.IsAdmin = true
25+
admin.Language = "en_US"
26+
admin.Email = "[email protected]"
27+
require.NoError(t, user_model.CreateUser(db.DefaultContext, admin))
28+
29+
newUser := new(user_model.User)
30+
newUser.Name = "new_user"
31+
newUser.Language = "en_US"
32+
newUser.IsAdmin = false
33+
newUser.Email = "[email protected]"
34+
newUser.LastLoginUnix = 1693648327
35+
newUser.CreatedUnix = 1693648027
36+
require.NoError(t, user_model.CreateUser(db.DefaultContext, newUser))
37+
38+
return []*user_model.User{admin, newUser}
39+
}
40+
41+
func cleanUpUsers(ctx context.Context, users []*user_model.User) {
42+
for _, u := range users {
43+
db.DeleteByID(ctx, u.ID, new(user_model.User))
44+
}
45+
}
46+
47+
func TestAdminNotificationMail_test(t *testing.T) {
48+
translation.InitLocales(context.Background())
49+
locale := translation.NewLocale("")
50+
key := "mail.admin.new_user.user_info"
51+
translatedKey := locale.Tr(key)
52+
require.NotEqualValues(t, key, translatedKey)
53+
54+
mailService := setting.Mailer{
55+
56+
Protocol: "dummy",
57+
}
58+
59+
setting.MailService = &mailService
60+
61+
// test with SEND_NOTIFICATION_EMAIL_ON_NEW_USER enabled
62+
setting.Admin.SendNotificationEmailOnNewUser = true
63+
64+
ctx := context.Background()
65+
NewContext(ctx)
66+
67+
users := getTestUsers(t)
68+
oldSendAsync := sa
69+
defer func() {
70+
sa = oldSendAsync
71+
cleanUpUsers(ctx, users)
72+
}()
73+
74+
called := false
75+
sa = func(msgs ...*Message) {
76+
assert.Equal(t, len(msgs), 1, "Test provides only one admin user, so only one email must be sent")
77+
assert.Equal(t, msgs[0].To, users[0].Email, "checks if the recipient is the admin of the instance")
78+
manageUserURL := setting.AppURL + "admin/users/" + strconv.FormatInt(users[1].ID, 10)
79+
assert.Contains(t, msgs[0].Body, manageUserURL)
80+
assert.Contains(t, msgs[0].Body, users[1].HTMLURL())
81+
assert.Contains(t, msgs[0].Body, translatedKey, "the .Locale translates to nothing")
82+
assert.Contains(t, msgs[0].Body, users[1].Name, "user name of the newly created user")
83+
for _, untranslated := range []string{"mail.admin", "admin.users"} {
84+
assert.NotContains(t, msgs[0].Body, untranslated, "this is an untranslated placeholder prefix")
85+
}
86+
called = true
87+
}
88+
MailNewUser(ctx, users[1])
89+
assert.True(t, called)
90+
91+
// test with SEND_NOTIFICATION_EMAIL_ON_NEW_USER disabled; emails shouldn't be sent
92+
setting.Admin.SendNotificationEmailOnNewUser = false
93+
sa = func(msgs ...*Message) {
94+
assert.Equal(t, 1, 0, "this shouldn't execute. MailNewUser must exit early since SEND_NOTIFICATION_EMAIL_ON_NEW_USER is disabled")
95+
}
96+
97+
MailNewUser(ctx, users[1])
98+
}

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
EditWikiPage(ctx context.Context, doer *user_model.User, repo *repo_model.Repository, page, comment string)
6060
DeleteWikiPage(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 about a newly signed up user 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: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -197,6 +197,9 @@ 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+
func (*NullNotifier) NewUserSignUp(ctx context.Context, newUser *user_model.User) {
201+
}
202+
200203
// PackageCreate places a place holder function
201204
func (*NullNotifier) PackageCreate(ctx context.Context, doer *user_model.User, pd *packages_model.PackageDescriptor) {
202205
}
Lines changed: 21 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,21 @@
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" | Str2html}}: <a href="{{.NewUserUrl}}">@{{.NewUser.Name}}</a></h3>
17+
<li>{{.Locale.Tr "admin.users.created" | Str2html}}: {{DateTime "full" .NewUser.CreatedUnix}}</li>
18+
</ul>
19+
<p> {{.Body | Str2html}} </p>
20+
</body>
21+
</html>

0 commit comments

Comments
 (0)