Skip to content

Commit 371ebce

Browse files
authored
Fix database inconsistent when admin change user email (#17549)
1 parent f23851f commit 371ebce

File tree

6 files changed

+62
-15
lines changed

6 files changed

+62
-15
lines changed

models/user/user.go

Lines changed: 35 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -796,18 +796,48 @@ func validateUser(u *User) error {
796796
return ValidateEmail(u.Email)
797797
}
798798

799-
func updateUser(e db.Engine, u *User) error {
799+
func updateUser(ctx context.Context, u *User, changePrimaryEmail bool) error {
800800
if err := validateUser(u); err != nil {
801801
return err
802802
}
803803

804+
e := db.GetEngine(ctx)
805+
806+
if changePrimaryEmail {
807+
var emailAddress EmailAddress
808+
has, err := e.Where("lower_email=?", strings.ToLower(u.Email)).Get(&emailAddress)
809+
if err != nil {
810+
return err
811+
}
812+
if !has {
813+
// 1. Update old primary email
814+
if _, err = e.Where("uid=? AND is_primary=?", u.ID, true).Cols("is_primary").Update(&EmailAddress{
815+
IsPrimary: false,
816+
}); err != nil {
817+
return err
818+
}
819+
820+
emailAddress.Email = u.Email
821+
emailAddress.UID = u.ID
822+
emailAddress.IsActivated = true
823+
emailAddress.IsPrimary = true
824+
if _, err := e.Insert(&emailAddress); err != nil {
825+
return err
826+
}
827+
} else if _, err := e.ID(emailAddress).Cols("is_primary").Update(&EmailAddress{
828+
IsPrimary: true,
829+
}); err != nil {
830+
return err
831+
}
832+
}
833+
804834
_, err := e.ID(u.ID).AllCols().Update(u)
805835
return err
806836
}
807837

808838
// UpdateUser updates user's information.
809-
func UpdateUser(u *User) error {
810-
return updateUser(db.GetEngine(db.DefaultContext), u)
839+
func UpdateUser(u *User, emailChanged bool) error {
840+
return updateUser(db.DefaultContext, u, emailChanged)
811841
}
812842

813843
// UpdateUserCols update user according special columns
@@ -836,14 +866,13 @@ func UpdateUserSetting(u *User) (err error) {
836866
return err
837867
}
838868
defer committer.Close()
839-
sess := db.GetEngine(ctx)
840869

841870
if !u.IsOrganization() {
842-
if err = checkDupEmail(sess, u); err != nil {
871+
if err = checkDupEmail(db.GetEngine(ctx), u); err != nil {
843872
return err
844873
}
845874
}
846-
if err = updateUser(sess, u); err != nil {
875+
if err = updateUser(ctx, u, false); err != nil {
847876
return err
848877
}
849878
return committer.Commit()

models/user/user_test.go

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -273,19 +273,19 @@ func TestUpdateUser(t *testing.T) {
273273
user := unittest.AssertExistsAndLoadBean(t, &User{ID: 2}).(*User)
274274

275275
user.KeepActivityPrivate = true
276-
assert.NoError(t, UpdateUser(user))
276+
assert.NoError(t, UpdateUser(user, false))
277277
user = unittest.AssertExistsAndLoadBean(t, &User{ID: 2}).(*User)
278278
assert.True(t, user.KeepActivityPrivate)
279279

280280
setting.Service.AllowedUserVisibilityModesSlice = []bool{true, false, false}
281281
user.KeepActivityPrivate = false
282282
user.Visibility = structs.VisibleTypePrivate
283-
assert.Error(t, UpdateUser(user))
283+
assert.Error(t, UpdateUser(user, false))
284284
user = unittest.AssertExistsAndLoadBean(t, &User{ID: 2}).(*User)
285285
assert.True(t, user.KeepActivityPrivate)
286286

287287
user.Email = "no [email protected]"
288-
assert.Error(t, UpdateUser(user))
288+
assert.Error(t, UpdateUser(user, true))
289289
}
290290

291291
func TestNewUserRedirect(t *testing.T) {

routers/api/v1/admin/user.go

Lines changed: 13 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -9,6 +9,7 @@ import (
99
"errors"
1010
"fmt"
1111
"net/http"
12+
"strings"
1213

1314
"code.gitea.io/gitea/models"
1415
"code.gitea.io/gitea/models/db"
@@ -203,12 +204,21 @@ func EditUser(ctx *context.APIContext) {
203204
if form.FullName != nil {
204205
u.FullName = *form.FullName
205206
}
207+
var emailChanged bool
206208
if form.Email != nil {
207-
u.Email = *form.Email
208-
if len(u.Email) == 0 {
209+
email := strings.TrimSpace(*form.Email)
210+
if len(email) == 0 {
209211
ctx.Error(http.StatusUnprocessableEntity, "", fmt.Errorf("email is not allowed to be empty string"))
210212
return
211213
}
214+
215+
if err := user_model.ValidateEmail(email); err != nil {
216+
ctx.InternalServerError(err)
217+
return
218+
}
219+
220+
emailChanged = !strings.EqualFold(u.Email, email)
221+
u.Email = email
212222
}
213223
if form.Website != nil {
214224
u.Website = *form.Website
@@ -247,7 +257,7 @@ func EditUser(ctx *context.APIContext) {
247257
u.IsRestricted = *form.Restricted
248258
}
249259

250-
if err := user_model.UpdateUser(u); err != nil {
260+
if err := user_model.UpdateUser(u, emailChanged); err != nil {
251261
if user_model.IsErrEmailAlreadyUsed(err) || user_model.IsErrEmailInvalid(err) {
252262
ctx.Error(http.StatusUnprocessableEntity, "", err)
253263
} else {

routers/api/v1/user/settings.go

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -74,7 +74,7 @@ func UpdateUserSettings(ctx *context.APIContext) {
7474
ctx.User.KeepActivityPrivate = *form.HideActivity
7575
}
7676

77-
if err := user_model.UpdateUser(ctx.User); err != nil {
77+
if err := user_model.UpdateUser(ctx.User, false); err != nil {
7878
ctx.InternalServerError(err)
7979
return
8080
}

routers/web/admin/users.go

Lines changed: 9 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -298,6 +298,13 @@ func EditUserPost(ctx *context.Context) {
298298
ctx.RenderWithErr(errMsg, tplUserNew, &form)
299299
return
300300
}
301+
302+
if err := user_model.ValidateEmail(form.Email); err != nil {
303+
ctx.Data["Err_Email"] = true
304+
ctx.RenderWithErr(ctx.Tr("form.email_error"), tplUserNew, &form)
305+
return
306+
}
307+
301308
if u.Salt, err = user_model.GetUserSalt(); err != nil {
302309
ctx.ServerError("UpdateUser", err)
303310
return
@@ -332,6 +339,7 @@ func EditUserPost(ctx *context.Context) {
332339

333340
u.LoginName = form.LoginName
334341
u.FullName = form.FullName
342+
emailChanged := !strings.EqualFold(u.Email, form.Email)
335343
u.Email = form.Email
336344
u.Website = form.Website
337345
u.Location = form.Location
@@ -352,7 +360,7 @@ func EditUserPost(ctx *context.Context) {
352360
u.ProhibitLogin = form.ProhibitLogin
353361
}
354362

355-
if err := user_model.UpdateUser(u); err != nil {
363+
if err := user_model.UpdateUser(u, emailChanged); err != nil {
356364
if user_model.IsErrEmailAlreadyUsed(err) {
357365
ctx.Data["Err_Email"] = true
358366
ctx.RenderWithErr(ctx.Tr("form.email_been_used"), tplUserEdit, &form)

routers/web/org/setting.go

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -104,7 +104,7 @@ func SettingsPost(ctx *context.Context) {
104104
visibilityChanged := form.Visibility != org.Visibility
105105
org.Visibility = form.Visibility
106106

107-
if err := user_model.UpdateUser(org.AsUser()); err != nil {
107+
if err := user_model.UpdateUser(org.AsUser(), false); err != nil {
108108
ctx.ServerError("UpdateUser", err)
109109
return
110110
}

0 commit comments

Comments
 (0)