Skip to content

Commit e9d0455

Browse files
committed
Ensure user email is always in email address table
In order to avoid email duplication, ensure that once an user is created or updated his new email address is added to the email address table.
1 parent 65dc4d0 commit e9d0455

File tree

5 files changed

+185
-13
lines changed

5 files changed

+185
-13
lines changed

models/migrations/migrations.go

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -250,6 +250,8 @@ var migrations = []Migration{
250250
NewMigration("fix publisher ID for tag releases", fixPublisherIDforTagReleases),
251251
// v157 -> v158
252252
NewMigration("ensure repo topics are up-to-date", fixRepoTopics),
253+
// v158 -> v159
254+
NewMigration("Ensure user primary email address is in email address table", addUserPrimaryEmailToUserMails),
253255
}
254256

255257
// GetCurrentDBVersion returns the current db version

models/migrations/v158.go

Lines changed: 73 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,73 @@
1+
// Copyright 2020 The Gitea Authors. All rights reserved.
2+
// Use of this source code is governed by a MIT-style
3+
// license that can be found in the LICENSE file.
4+
5+
package migrations
6+
7+
import (
8+
"xorm.io/xorm"
9+
)
10+
11+
func addUserPrimaryEmailToUserMails(x *xorm.Engine) error {
12+
type User struct {
13+
ID int64 `xorm:"pk autoincr"`
14+
Email string `xorm:"NOT NULL"`
15+
IsActive bool `xorm:"INDEX"`
16+
}
17+
type EmailAddress struct {
18+
ID int64 `xorm:"pk autoincr"`
19+
UID int64 `xorm:"INDEX NOT NULL"`
20+
Email string `xorm:"UNIQUE NOT NULL"`
21+
IsActivated bool
22+
}
23+
24+
if err := x.Sync2(new(User), new(EmailAddress)); err != nil {
25+
return err
26+
}
27+
28+
updateUsers := func(users []*User) error {
29+
sess := x.NewSession()
30+
defer sess.Close()
31+
if err := sess.Begin(); err != nil {
32+
return err
33+
}
34+
for _, user := range users {
35+
if has, err := sess.Get(&EmailAddress{UID: user.ID, Email: user.Email}); err != nil {
36+
return err
37+
} else if has {
38+
continue
39+
}
40+
email := &EmailAddress{
41+
Email: user.Email,
42+
UID: user.ID,
43+
IsActivated: user.IsActive,
44+
}
45+
if _, err := sess.Insert(email); err != nil {
46+
return err
47+
}
48+
}
49+
50+
return sess.Commit()
51+
}
52+
53+
var start = 0
54+
var batchSize = 100
55+
for {
56+
var users = make([]*User, 0, batchSize)
57+
if err := x.Limit(batchSize, start).Find(&users); err != nil {
58+
return err
59+
}
60+
61+
if err := updateUsers(users); err != nil {
62+
return err
63+
}
64+
65+
start += len(users)
66+
67+
if len(users) < batchSize {
68+
break
69+
}
70+
}
71+
72+
return nil
73+
}

models/user.go

Lines changed: 38 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -834,6 +834,9 @@ func CreateUser(u *User) (err error) {
834834
if _, err = sess.Insert(u); err != nil {
835835
return err
836836
}
837+
if err = addEmailAddress(sess, &EmailAddress{UID: u.ID, Email: u.Email}); err != nil {
838+
return err
839+
}
837840

838841
return sess.Commit()
839842
}
@@ -937,16 +940,24 @@ func ChangeUserName(u *User, newUserName string) (err error) {
937940
// checkDupEmail checks whether there are the same email with the user
938941
func checkDupEmail(e Engine, u *User) error {
939942
u.Email = strings.ToLower(u.Email)
940-
has, err := e.
943+
if has, err := e.
941944
Where("id!=?", u.ID).
945+
And("email=?", u.Email).
942946
And("type=?", u.Type).
947+
Get(new(User)); err != nil {
948+
return err
949+
} else if has {
950+
return ErrEmailAlreadyUsed{u.Email}
951+
}
952+
if has, err := e.
953+
Where("uid!=?", u.ID).
943954
And("email=?", u.Email).
944-
Get(new(User))
945-
if err != nil {
955+
Get(new(EmailAddress)); err != nil {
946956
return err
947957
} else if has {
948958
return ErrEmailAlreadyUsed{u.Email}
949959
}
960+
950961
return nil
951962
}
952963

@@ -972,12 +983,34 @@ func updateUserCols(e Engine, u *User, cols ...string) error {
972983

973984
// UpdateUserSetting updates user's settings.
974985
func UpdateUserSetting(u *User) error {
986+
isEmailFound := false
987+
sess := x.NewSession()
988+
975989
if !u.IsOrganization() {
976-
if err := checkDupEmail(x, u); err != nil {
990+
if err := checkDupEmail(sess, u); err != nil {
977991
return err
978992
}
993+
emails, err := getEmailAddresses(sess, u.ID)
994+
if err != nil {
995+
return err
996+
}
997+
for _, email := range emails {
998+
if email.Email == u.Email {
999+
isEmailFound = true
1000+
break
1001+
}
1002+
}
9791003
}
980-
return updateUser(x, u)
1004+
1005+
if err := updateUser(sess, u); err != nil {
1006+
return err
1007+
}
1008+
if !isEmailFound {
1009+
if err := addEmailAddress(sess, &EmailAddress{UID: u.ID, Email: u.Email}); err != nil {
1010+
return err
1011+
}
1012+
}
1013+
return sess.Commit()
9811014
}
9821015

9831016
// deleteBeans deletes all given beans, beans should contain delete conditions.

models/user_mail.go

Lines changed: 14 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -34,8 +34,12 @@ type EmailAddress struct {
3434

3535
// GetEmailAddresses returns all email addresses belongs to given user.
3636
func GetEmailAddresses(uid int64) ([]*EmailAddress, error) {
37+
return getEmailAddresses(x, uid)
38+
}
39+
40+
func getEmailAddresses(e Engine, uid int64) ([]*EmailAddress, error) {
3741
emails := make([]*EmailAddress, 0, 5)
38-
if err := x.
42+
if err := e.
3943
Where("uid=?", uid).
4044
Find(&emails); err != nil {
4145
return nil, err
@@ -136,19 +140,21 @@ func IsEmailUsed(email string) (bool, error) {
136140

137141
func addEmailAddress(e Engine, email *EmailAddress) error {
138142
email.Email = strings.ToLower(strings.TrimSpace(email.Email))
139-
used, err := isEmailUsed(e, email.Email)
143+
_, err := e.Insert(email)
144+
return err
145+
}
146+
147+
// AddEmailAddress adds an email address to given user.
148+
func AddEmailAddress(email *EmailAddress) error {
149+
email.Email = strings.ToLower(strings.TrimSpace(email.Email))
150+
151+
used, err := isEmailUsed(x, email.Email)
140152
if err != nil {
141153
return err
142154
} else if used {
143155
return ErrEmailAlreadyUsed{email.Email}
144156
}
145157

146-
_, err = e.Insert(email)
147-
return err
148-
}
149-
150-
// AddEmailAddress adds an email address to given user.
151-
func AddEmailAddress(email *EmailAddress) error {
152158
return addEmailAddress(x, email)
153159
}
154160

models/user_test.go

Lines changed: 58 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -329,6 +329,64 @@ func TestCreateUser(t *testing.T) {
329329
assert.NoError(t, DeleteUser(user))
330330
}
331331

332+
func TestCreateUserInsertsEmailAddressRecord(t *testing.T) {
333+
sess := x.NewSession()
334+
defer sess.Close()
335+
336+
validUser := &User{
337+
Name: "GiteaBot",
338+
339+
}
340+
invalidUser := &User{
341+
Name: "GiteaBot2",
342+
343+
}
344+
assert.NoError(t, CreateUser(validUser))
345+
email := new(EmailAddress)
346+
found, err := sess.Where("email=?", validUser.Email).Get(email)
347+
assert.NoError(t, err)
348+
assert.True(t, found)
349+
assert.EqualValues(t, email.UID, validUser.ID)
350+
assert.Error(t, CreateUser(invalidUser))
351+
352+
assert.NoError(t, DeleteUser(validUser))
353+
}
354+
355+
func TestUpdateUser(t *testing.T) {
356+
sess := x.NewSession()
357+
defer sess.Close()
358+
359+
users := []*User{
360+
&User{
361+
Name: "GiteaBot",
362+
363+
},
364+
&User{
365+
Name: "GiteaBot2",
366+
367+
},
368+
}
369+
for _, u := range users {
370+
assert.NoError(t, CreateUser(u))
371+
}
372+
373+
users[0].Email = "[email protected]"
374+
assert.Error(t, AddEmailAddress(&EmailAddress{UID: users[0].ID, Email: users[0].Email}))
375+
assert.Error(t, UpdateUserSetting(users[0]))
376+
users[0].Email = "[email protected]"
377+
assert.NoError(t, UpdateUserSetting(users[0]))
378+
379+
email := new(EmailAddress)
380+
found, err := sess.Where("email=?", users[0].Email).Get(email)
381+
assert.NoError(t, err)
382+
assert.True(t, found)
383+
assert.EqualValues(t, email.UID, users[0].ID)
384+
385+
for _, u := range users {
386+
assert.NoError(t, DeleteUser(u))
387+
}
388+
}
389+
332390
func TestCreateUser_Issue5882(t *testing.T) {
333391

334392
// Init settings

0 commit comments

Comments
 (0)