Skip to content

Fix database inconsistent when admin change user email (#17549) #17840

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
merged 1 commit into from
Nov 28, 2021
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
36 changes: 32 additions & 4 deletions models/user.go
Original file line number Diff line number Diff line change
Expand Up @@ -1072,18 +1072,46 @@ func validateUser(u *User) error {
return ValidateEmail(u.Email)
}

func updateUser(e Engine, u *User) error {
func updateUser(e Engine, u *User, changePrimaryEmail bool) error {
if err := validateUser(u); err != nil {
return err
}

if changePrimaryEmail {
var emailAddress EmailAddress
has, err := e.Where("lower_email=?", strings.ToLower(u.Email)).Get(&emailAddress)
if err != nil {
return err
}
if !has {
// 1. Update old primary email
if _, err = e.Where("uid=? AND is_primary=?", u.ID, true).Cols("is_primary").Update(&EmailAddress{
IsPrimary: false,
}); err != nil {
return err
}

emailAddress.Email = u.Email
emailAddress.UID = u.ID
emailAddress.IsActivated = true
emailAddress.IsPrimary = true
if _, err := e.Insert(&emailAddress); err != nil {
return err
}
} else if _, err := e.ID(emailAddress).Cols("is_primary").Update(&EmailAddress{
IsPrimary: true,
}); err != nil {
return err
}
}

_, err := e.ID(u.ID).AllCols().Update(u)
return err
}

// UpdateUser updates user's information.
func UpdateUser(u *User) error {
return updateUser(x, u)
func UpdateUser(u *User, changePrimaryEmail bool) error {
return updateUser(x, u, changePrimaryEmail)
}

// UpdateUserCols update user according special columns
Expand Down Expand Up @@ -1112,7 +1140,7 @@ func UpdateUserSetting(u *User) (err error) {
return err
}
}
if err = updateUser(sess, u); err != nil {
if err = updateUser(sess, u, false); err != nil {
return err
}
return sess.Commit()
Expand Down
6 changes: 3 additions & 3 deletions models/user_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -475,17 +475,17 @@ func TestUpdateUser(t *testing.T) {
user := AssertExistsAndLoadBean(t, &User{ID: 2}).(*User)

user.KeepActivityPrivate = true
assert.NoError(t, UpdateUser(user))
assert.NoError(t, UpdateUser(user, false))
user = AssertExistsAndLoadBean(t, &User{ID: 2}).(*User)
assert.True(t, user.KeepActivityPrivate)

setting.Service.AllowedUserVisibilityModesSlice = []bool{true, false, false}
user.KeepActivityPrivate = false
user.Visibility = structs.VisibleTypePrivate
assert.Error(t, UpdateUser(user))
assert.Error(t, UpdateUser(user, false))
user = AssertExistsAndLoadBean(t, &User{ID: 2}).(*User)
assert.True(t, user.KeepActivityPrivate)

user.Email = "no [email protected]"
assert.Error(t, UpdateUser(user))
assert.Error(t, UpdateUser(user, true))
}
15 changes: 12 additions & 3 deletions routers/api/v1/admin/user.go
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,7 @@ import (
"errors"
"fmt"
"net/http"
"strings"

"code.gitea.io/gitea/models"
"code.gitea.io/gitea/modules/context"
Expand Down Expand Up @@ -199,12 +200,20 @@ func EditUser(ctx *context.APIContext) {
if form.FullName != nil {
u.FullName = *form.FullName
}
var emailChanged bool
if form.Email != nil {
u.Email = *form.Email
if len(u.Email) == 0 {
email := strings.TrimSpace(*form.Email)
if len(email) == 0 {
ctx.Error(http.StatusUnprocessableEntity, "", fmt.Errorf("email is not allowed to be empty string"))
return
}
if err := models.ValidateEmail(email); err != nil {
ctx.InternalServerError(err)
return
}

emailChanged = !strings.EqualFold(u.Email, email)
u.Email = email
}
if form.Website != nil {
u.Website = *form.Website
Expand Down Expand Up @@ -243,7 +252,7 @@ func EditUser(ctx *context.APIContext) {
u.IsRestricted = *form.Restricted
}

if err := models.UpdateUser(u); err != nil {
if err := models.UpdateUser(u, emailChanged); err != nil {
if models.IsErrEmailAlreadyUsed(err) || models.IsErrEmailInvalid(err) {
ctx.Error(http.StatusUnprocessableEntity, "", err)
} else {
Expand Down
2 changes: 1 addition & 1 deletion routers/api/v1/user/settings.go
Original file line number Diff line number Diff line change
Expand Up @@ -74,7 +74,7 @@ func UpdateUserSettings(ctx *context.APIContext) {
ctx.User.KeepActivityPrivate = *form.HideActivity
}

if err := models.UpdateUser(ctx.User); err != nil {
if err := models.UpdateUser(ctx.User, false); err != nil {
ctx.InternalServerError(err)
return
}
Expand Down
11 changes: 9 additions & 2 deletions routers/web/admin/users.go
Original file line number Diff line number Diff line change
Expand Up @@ -273,6 +273,11 @@ func EditUserPost(ctx *context.Context) {
ctx.RenderWithErr(errMsg, tplUserNew, &form)
return
}
if err := models.ValidateEmail(form.Email); err != nil {
ctx.Data["Err_Email"] = true
ctx.RenderWithErr(ctx.Tr("form.email_error"), tplUserNew, &form)
return
}
if u.Salt, err = models.GetUserSalt(); err != nil {
ctx.ServerError("UpdateUser", err)
return
Expand Down Expand Up @@ -307,7 +312,9 @@ func EditUserPost(ctx *context.Context) {

u.LoginName = form.LoginName
u.FullName = form.FullName
u.Email = form.Email
email := strings.TrimSpace(form.Email)
emailChanged := !strings.EqualFold(u.Email, email)
u.Email = email
u.Website = form.Website
u.Location = form.Location
u.MaxRepoCreation = form.MaxRepoCreation
Expand All @@ -327,7 +334,7 @@ func EditUserPost(ctx *context.Context) {
u.ProhibitLogin = form.ProhibitLogin
}

if err := models.UpdateUser(u); err != nil {
if err := models.UpdateUser(u, emailChanged); err != nil {
if models.IsErrEmailAlreadyUsed(err) {
ctx.Data["Err_Email"] = true
ctx.RenderWithErr(ctx.Tr("form.email_been_used"), tplUserEdit, &form)
Expand Down
2 changes: 1 addition & 1 deletion routers/web/org/setting.go
Original file line number Diff line number Diff line change
Expand Up @@ -96,7 +96,7 @@ func SettingsPost(ctx *context.Context) {
visibilityChanged := form.Visibility != org.Visibility
org.Visibility = form.Visibility

if err := models.UpdateUser(org); err != nil {
if err := models.UpdateUser(org, false); err != nil {
ctx.ServerError("UpdateUser", err)
return
}
Expand Down