Fix a bug user could change another user's primary email (#36586)

This commit is contained in:
Lunny Xiao
2026-02-12 12:34:38 -08:00
committed by GitHub
parent 514f322dcf
commit 8d26ea9373
5 changed files with 51 additions and 12 deletions

View File

@@ -276,17 +276,22 @@ func updateActivation(ctx context.Context, email *EmailAddress, activate bool) e
return UpdateUserCols(ctx, user, "rands") return UpdateUserCols(ctx, user, "rands")
} }
func MakeActiveEmailPrimary(ctx context.Context, emailID int64) error { func MakeActiveEmailPrimary(ctx context.Context, ownerID, emailID int64) error {
return makeEmailPrimaryInternal(ctx, emailID, true) return makeEmailPrimaryInternal(ctx, ownerID, emailID, true)
} }
func MakeInactiveEmailPrimary(ctx context.Context, emailID int64) error { func MakeInactiveEmailPrimary(ctx context.Context, ownerID, emailID int64) error {
return makeEmailPrimaryInternal(ctx, emailID, false) return makeEmailPrimaryInternal(ctx, ownerID, emailID, false)
} }
func makeEmailPrimaryInternal(ctx context.Context, emailID int64, isActive bool) error { func makeEmailPrimaryInternal(ctx context.Context, ownerID, emailID int64, isActive bool) error {
email := &EmailAddress{} email := &EmailAddress{}
if has, err := db.GetEngine(ctx).ID(emailID).Where(builder.Eq{"is_activated": isActive}).Get(email); err != nil { if has, err := db.GetEngine(ctx).ID(emailID).
Where(builder.Eq{
"uid": ownerID,
"is_activated": isActive,
}).
Get(email); err != nil {
return err return err
} else if !has { } else if !has {
return ErrEmailAddressNotExist{} return ErrEmailAddressNotExist{}
@@ -336,7 +341,7 @@ func ChangeInactivePrimaryEmail(ctx context.Context, uid int64, oldEmailAddr, ne
if err != nil { if err != nil {
return err return err
} }
return MakeInactiveEmailPrimary(ctx, newEmail.ID) return MakeInactiveEmailPrimary(ctx, uid, newEmail.ID)
}) })
} }

View File

@@ -46,22 +46,22 @@ func TestIsEmailUsed(t *testing.T) {
func TestMakeEmailPrimary(t *testing.T) { func TestMakeEmailPrimary(t *testing.T) {
assert.NoError(t, unittest.PrepareTestDatabase()) assert.NoError(t, unittest.PrepareTestDatabase())
err := user_model.MakeActiveEmailPrimary(t.Context(), 9999999) err := user_model.MakeActiveEmailPrimary(t.Context(), 1, 9999999)
assert.Error(t, err) assert.Error(t, err)
assert.ErrorIs(t, err, user_model.ErrEmailAddressNotExist{}) assert.ErrorIs(t, err, user_model.ErrEmailAddressNotExist{})
email := unittest.AssertExistsAndLoadBean(t, &user_model.EmailAddress{Email: "user11@example.com"}) email := unittest.AssertExistsAndLoadBean(t, &user_model.EmailAddress{Email: "user11@example.com"})
err = user_model.MakeActiveEmailPrimary(t.Context(), email.ID) err = user_model.MakeActiveEmailPrimary(t.Context(), email.UID, email.ID)
assert.Error(t, err) assert.Error(t, err)
assert.ErrorIs(t, err, user_model.ErrEmailAddressNotExist{}) // inactive email is considered as not exist for "MakeActiveEmailPrimary" assert.ErrorIs(t, err, user_model.ErrEmailAddressNotExist{}) // inactive email is considered as not exist for "MakeActiveEmailPrimary"
email = unittest.AssertExistsAndLoadBean(t, &user_model.EmailAddress{Email: "user9999999@example.com"}) email = unittest.AssertExistsAndLoadBean(t, &user_model.EmailAddress{Email: "user9999999@example.com"})
err = user_model.MakeActiveEmailPrimary(t.Context(), email.ID) err = user_model.MakeActiveEmailPrimary(t.Context(), email.UID, email.ID)
assert.Error(t, err) assert.Error(t, err)
assert.True(t, user_model.IsErrUserNotExist(err)) assert.True(t, user_model.IsErrUserNotExist(err))
email = unittest.AssertExistsAndLoadBean(t, &user_model.EmailAddress{Email: "user101@example.com"}) email = unittest.AssertExistsAndLoadBean(t, &user_model.EmailAddress{Email: "user101@example.com"})
err = user_model.MakeActiveEmailPrimary(t.Context(), email.ID) err = user_model.MakeActiveEmailPrimary(t.Context(), email.UID, email.ID)
assert.NoError(t, err) assert.NoError(t, err)
user, _ := user_model.GetUserByID(t.Context(), int64(10)) user, _ := user_model.GetUserByID(t.Context(), int64(10))

View File

@@ -758,6 +758,7 @@
"settings.add_email": "Add Email Address", "settings.add_email": "Add Email Address",
"settings.add_openid": "Add OpenID URI", "settings.add_openid": "Add OpenID URI",
"settings.add_email_confirmation_sent": "A confirmation email has been sent to \"%s\". Please check your inbox within the next %s to confirm your email address.", "settings.add_email_confirmation_sent": "A confirmation email has been sent to \"%s\". Please check your inbox within the next %s to confirm your email address.",
"settings.email_primary_not_found": "The selected email address could not be found.",
"settings.add_email_success": "The new email address has been added.", "settings.add_email_success": "The new email address has been added.",
"settings.email_preference_set_success": "Email preference has been set successfully.", "settings.email_preference_set_success": "Email preference has been set successfully.",
"settings.add_openid_success": "The new OpenID address has been added.", "settings.add_openid_success": "The new OpenID address has been added.",

View File

@@ -113,7 +113,12 @@ func EmailPost(ctx *context.Context) {
// Make email address primary. // Make email address primary.
if ctx.FormString("_method") == "PRIMARY" { if ctx.FormString("_method") == "PRIMARY" {
if err := user_model.MakeActiveEmailPrimary(ctx, ctx.FormInt64("id")); err != nil { if err := user_model.MakeActiveEmailPrimary(ctx, ctx.Doer.ID, ctx.FormInt64("id")); err != nil {
if user_model.IsErrEmailAddressNotExist(err) {
ctx.Flash.Error(ctx.Tr("settings.email_primary_not_found"))
ctx.Redirect(setting.AppSubURL + "/user/settings/account")
return
}
ctx.ServerError("MakeEmailPrimary", err) ctx.ServerError("MakeEmailPrimary", err)
return return
} }

View File

@@ -158,6 +158,34 @@ func TestUserSettingsUpdateEmail(t *testing.T) {
req := NewRequest(t, "POST", "/user/settings/account/email") req := NewRequest(t, "POST", "/user/settings/account/email")
session.MakeRequest(t, req, http.StatusNotFound) session.MakeRequest(t, req, http.StatusNotFound)
}) })
t.Run("primary email not found", func(t *testing.T) {
defer tests.PrintCurrentTest(t)()
session := loginUser(t, "user2")
req := NewRequestWithValues(t, "POST", "/user/settings/account/email", map[string]string{
"_method": "PRIMARY",
"id": "9999",
})
resp := session.MakeRequest(t, req, http.StatusSeeOther)
assert.Equal(t, "/user/settings/account", resp.Header().Get("Location"))
flashMsg := session.GetCookieFlashMessage()
assert.Equal(t, "The selected email address could not be found.", flashMsg.ErrorMsg)
})
t.Run("primary email not owned by user", func(t *testing.T) {
defer tests.PrintCurrentTest(t)()
session := loginUser(t, "user2")
req := NewRequestWithValues(t, "POST", "/user/settings/account/email", map[string]string{
"_method": "PRIMARY",
"id": "6",
})
resp := session.MakeRequest(t, req, http.StatusSeeOther)
assert.Equal(t, "/user/settings/account", resp.Header().Get("Location"))
flashMsg := session.GetCookieFlashMessage()
assert.Equal(t, "The selected email address could not be found.", flashMsg.ErrorMsg)
})
} }
func TestUserSettingsDeleteEmail(t *testing.T) { func TestUserSettingsDeleteEmail(t *testing.T) {