Unify user update methods (#28733)
Fixes #28660 Fixes an admin api bug related to `user.LoginSource` Fixed `/user/emails` response not identical to GitHub api This PR unifies the user update methods. The goal is to keep the logic only at one place (having audit logs in mind). For example, do the password checks only in one method not everywhere a password is updated. After that PR is merged, the user creation should be next.
This commit is contained in:
parent
b4513f48ce
commit
f8b471ace1
42 changed files with 1383 additions and 1068 deletions
|
@ -57,7 +57,7 @@ func DeleteAvatar(ctx context.Context, u *user_model.User) error {
|
|||
u.UseCustomAvatar = false
|
||||
u.Avatar = ""
|
||||
if _, err := db.GetEngine(ctx).ID(u.ID).Cols("avatar, use_custom_avatar").Update(u); err != nil {
|
||||
return fmt.Errorf("UpdateUser: %w", err)
|
||||
return fmt.Errorf("DeleteAvatar: %w", err)
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
|
166
services/user/email.go
Normal file
166
services/user/email.go
Normal file
|
@ -0,0 +1,166 @@
|
|||
// Copyright 2024 The Gitea Authors. All rights reserved.
|
||||
// SPDX-License-Identifier: MIT
|
||||
|
||||
package user
|
||||
|
||||
import (
|
||||
"context"
|
||||
"errors"
|
||||
"strings"
|
||||
|
||||
"code.gitea.io/gitea/models/db"
|
||||
user_model "code.gitea.io/gitea/models/user"
|
||||
"code.gitea.io/gitea/modules/setting"
|
||||
"code.gitea.io/gitea/modules/util"
|
||||
)
|
||||
|
||||
func AddOrSetPrimaryEmailAddress(ctx context.Context, u *user_model.User, emailStr string) error {
|
||||
if strings.EqualFold(u.Email, emailStr) {
|
||||
return nil
|
||||
}
|
||||
|
||||
if err := user_model.ValidateEmail(emailStr); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
// Check if address exists already
|
||||
email, err := user_model.GetEmailAddressByEmail(ctx, emailStr)
|
||||
if err != nil && !errors.Is(err, util.ErrNotExist) {
|
||||
return err
|
||||
}
|
||||
if email != nil && email.UID != u.ID {
|
||||
return user_model.ErrEmailAlreadyUsed{Email: emailStr}
|
||||
}
|
||||
|
||||
// Update old primary address
|
||||
primary, err := user_model.GetPrimaryEmailAddressOfUser(ctx, u.ID)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
primary.IsPrimary = false
|
||||
if err := user_model.UpdateEmailAddress(ctx, primary); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
// Insert new or update existing address
|
||||
if email != nil {
|
||||
email.IsPrimary = true
|
||||
email.IsActivated = true
|
||||
if err := user_model.UpdateEmailAddress(ctx, email); err != nil {
|
||||
return err
|
||||
}
|
||||
} else {
|
||||
email = &user_model.EmailAddress{
|
||||
UID: u.ID,
|
||||
Email: emailStr,
|
||||
IsActivated: true,
|
||||
IsPrimary: true,
|
||||
}
|
||||
if _, err := user_model.InsertEmailAddress(ctx, email); err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
|
||||
u.Email = emailStr
|
||||
|
||||
return user_model.UpdateUserCols(ctx, u, "email")
|
||||
}
|
||||
|
||||
func ReplacePrimaryEmailAddress(ctx context.Context, u *user_model.User, emailStr string) error {
|
||||
if strings.EqualFold(u.Email, emailStr) {
|
||||
return nil
|
||||
}
|
||||
|
||||
if err := user_model.ValidateEmail(emailStr); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
if !u.IsOrganization() {
|
||||
// Check if address exists already
|
||||
email, err := user_model.GetEmailAddressByEmail(ctx, emailStr)
|
||||
if err != nil && !errors.Is(err, util.ErrNotExist) {
|
||||
return err
|
||||
}
|
||||
if email != nil {
|
||||
if email.IsPrimary && email.UID == u.ID {
|
||||
return nil
|
||||
}
|
||||
return user_model.ErrEmailAlreadyUsed{Email: emailStr}
|
||||
}
|
||||
|
||||
// Remove old primary address
|
||||
primary, err := user_model.GetPrimaryEmailAddressOfUser(ctx, u.ID)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
if _, err := db.DeleteByID[user_model.EmailAddress](ctx, primary.ID); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
// Insert new primary address
|
||||
email = &user_model.EmailAddress{
|
||||
UID: u.ID,
|
||||
Email: emailStr,
|
||||
IsActivated: true,
|
||||
IsPrimary: true,
|
||||
}
|
||||
if _, err := user_model.InsertEmailAddress(ctx, email); err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
|
||||
u.Email = emailStr
|
||||
|
||||
return user_model.UpdateUserCols(ctx, u, "email")
|
||||
}
|
||||
|
||||
func AddEmailAddresses(ctx context.Context, u *user_model.User, emails []string) error {
|
||||
for _, emailStr := range emails {
|
||||
if err := user_model.ValidateEmail(emailStr); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
// Check if address exists already
|
||||
email, err := user_model.GetEmailAddressByEmail(ctx, emailStr)
|
||||
if err != nil && !errors.Is(err, util.ErrNotExist) {
|
||||
return err
|
||||
}
|
||||
if email != nil {
|
||||
return user_model.ErrEmailAlreadyUsed{Email: emailStr}
|
||||
}
|
||||
|
||||
// Insert new address
|
||||
email = &user_model.EmailAddress{
|
||||
UID: u.ID,
|
||||
Email: emailStr,
|
||||
IsActivated: !setting.Service.RegisterEmailConfirm,
|
||||
IsPrimary: false,
|
||||
}
|
||||
if _, err := user_model.InsertEmailAddress(ctx, email); err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
func DeleteEmailAddresses(ctx context.Context, u *user_model.User, emails []string) error {
|
||||
for _, emailStr := range emails {
|
||||
// Check if address exists
|
||||
email, err := user_model.GetEmailAddressOfUser(ctx, emailStr, u.ID)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
if email.IsPrimary {
|
||||
return user_model.ErrPrimaryEmailCannotDelete{Email: emailStr}
|
||||
}
|
||||
|
||||
// Remove address
|
||||
if _, err := db.DeleteByID[user_model.EmailAddress](ctx, email.ID); err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
129
services/user/email_test.go
Normal file
129
services/user/email_test.go
Normal file
|
@ -0,0 +1,129 @@
|
|||
// Copyright 2024 The Gitea Authors. All rights reserved.
|
||||
// SPDX-License-Identifier: MIT
|
||||
|
||||
package user
|
||||
|
||||
import (
|
||||
"testing"
|
||||
|
||||
"code.gitea.io/gitea/models/db"
|
||||
organization_model "code.gitea.io/gitea/models/organization"
|
||||
"code.gitea.io/gitea/models/unittest"
|
||||
user_model "code.gitea.io/gitea/models/user"
|
||||
|
||||
"github.com/stretchr/testify/assert"
|
||||
)
|
||||
|
||||
func TestAddOrSetPrimaryEmailAddress(t *testing.T) {
|
||||
assert.NoError(t, unittest.PrepareTestDatabase())
|
||||
|
||||
user := unittest.AssertExistsAndLoadBean(t, &user_model.User{ID: 27})
|
||||
|
||||
emails, err := user_model.GetEmailAddresses(db.DefaultContext, user.ID)
|
||||
assert.NoError(t, err)
|
||||
assert.Len(t, emails, 1)
|
||||
|
||||
primary, err := user_model.GetPrimaryEmailAddressOfUser(db.DefaultContext, user.ID)
|
||||
assert.NoError(t, err)
|
||||
assert.NotEqual(t, "new-primary@example.com", primary.Email)
|
||||
assert.Equal(t, user.Email, primary.Email)
|
||||
|
||||
assert.NoError(t, AddOrSetPrimaryEmailAddress(db.DefaultContext, user, "new-primary@example.com"))
|
||||
|
||||
primary, err = user_model.GetPrimaryEmailAddressOfUser(db.DefaultContext, user.ID)
|
||||
assert.NoError(t, err)
|
||||
assert.Equal(t, "new-primary@example.com", primary.Email)
|
||||
assert.Equal(t, user.Email, primary.Email)
|
||||
|
||||
emails, err = user_model.GetEmailAddresses(db.DefaultContext, user.ID)
|
||||
assert.NoError(t, err)
|
||||
assert.Len(t, emails, 2)
|
||||
|
||||
assert.NoError(t, AddOrSetPrimaryEmailAddress(db.DefaultContext, user, "user27@example.com"))
|
||||
|
||||
primary, err = user_model.GetPrimaryEmailAddressOfUser(db.DefaultContext, user.ID)
|
||||
assert.NoError(t, err)
|
||||
assert.Equal(t, "user27@example.com", primary.Email)
|
||||
assert.Equal(t, user.Email, primary.Email)
|
||||
|
||||
emails, err = user_model.GetEmailAddresses(db.DefaultContext, user.ID)
|
||||
assert.NoError(t, err)
|
||||
assert.Len(t, emails, 2)
|
||||
}
|
||||
|
||||
func TestReplacePrimaryEmailAddress(t *testing.T) {
|
||||
assert.NoError(t, unittest.PrepareTestDatabase())
|
||||
|
||||
t.Run("User", func(t *testing.T) {
|
||||
user := unittest.AssertExistsAndLoadBean(t, &user_model.User{ID: 13})
|
||||
|
||||
emails, err := user_model.GetEmailAddresses(db.DefaultContext, user.ID)
|
||||
assert.NoError(t, err)
|
||||
assert.Len(t, emails, 1)
|
||||
|
||||
primary, err := user_model.GetPrimaryEmailAddressOfUser(db.DefaultContext, user.ID)
|
||||
assert.NoError(t, err)
|
||||
assert.NotEqual(t, "primary-13@example.com", primary.Email)
|
||||
assert.Equal(t, user.Email, primary.Email)
|
||||
|
||||
assert.NoError(t, ReplacePrimaryEmailAddress(db.DefaultContext, user, "primary-13@example.com"))
|
||||
|
||||
primary, err = user_model.GetPrimaryEmailAddressOfUser(db.DefaultContext, user.ID)
|
||||
assert.NoError(t, err)
|
||||
assert.Equal(t, "primary-13@example.com", primary.Email)
|
||||
assert.Equal(t, user.Email, primary.Email)
|
||||
|
||||
emails, err = user_model.GetEmailAddresses(db.DefaultContext, user.ID)
|
||||
assert.NoError(t, err)
|
||||
assert.Len(t, emails, 1)
|
||||
|
||||
assert.NoError(t, ReplacePrimaryEmailAddress(db.DefaultContext, user, "primary-13@example.com"))
|
||||
})
|
||||
|
||||
t.Run("Organization", func(t *testing.T) {
|
||||
org := unittest.AssertExistsAndLoadBean(t, &organization_model.Organization{ID: 3})
|
||||
|
||||
assert.Equal(t, "org3@example.com", org.Email)
|
||||
|
||||
assert.NoError(t, ReplacePrimaryEmailAddress(db.DefaultContext, org.AsUser(), "primary-org@example.com"))
|
||||
|
||||
assert.Equal(t, "primary-org@example.com", org.Email)
|
||||
})
|
||||
}
|
||||
|
||||
func TestAddEmailAddresses(t *testing.T) {
|
||||
assert.NoError(t, unittest.PrepareTestDatabase())
|
||||
|
||||
user := unittest.AssertExistsAndLoadBean(t, &user_model.User{ID: 2})
|
||||
|
||||
assert.Error(t, AddEmailAddresses(db.DefaultContext, user, []string{" invalid email "}))
|
||||
|
||||
emails := []string{"user1234@example.com", "user5678@example.com"}
|
||||
|
||||
assert.NoError(t, AddEmailAddresses(db.DefaultContext, user, emails))
|
||||
|
||||
err := AddEmailAddresses(db.DefaultContext, user, emails)
|
||||
assert.Error(t, err)
|
||||
assert.True(t, user_model.IsErrEmailAlreadyUsed(err))
|
||||
}
|
||||
|
||||
func TestDeleteEmailAddresses(t *testing.T) {
|
||||
assert.NoError(t, unittest.PrepareTestDatabase())
|
||||
|
||||
user := unittest.AssertExistsAndLoadBean(t, &user_model.User{ID: 2})
|
||||
|
||||
emails := []string{"user2-2@example.com"}
|
||||
|
||||
err := DeleteEmailAddresses(db.DefaultContext, user, emails)
|
||||
assert.NoError(t, err)
|
||||
|
||||
err = DeleteEmailAddresses(db.DefaultContext, user, emails)
|
||||
assert.Error(t, err)
|
||||
assert.True(t, user_model.IsErrEmailAddressNotExist(err))
|
||||
|
||||
emails = []string{"user2@example.com"}
|
||||
|
||||
err = DeleteEmailAddresses(db.DefaultContext, user, emails)
|
||||
assert.Error(t, err)
|
||||
assert.True(t, user_model.IsErrPrimaryEmailCannotDelete(err))
|
||||
}
|
212
services/user/update.go
Normal file
212
services/user/update.go
Normal file
|
@ -0,0 +1,212 @@
|
|||
// Copyright 2024 The Gitea Authors. All rights reserved.
|
||||
// SPDX-License-Identifier: MIT
|
||||
|
||||
package user
|
||||
|
||||
import (
|
||||
"context"
|
||||
"fmt"
|
||||
|
||||
"code.gitea.io/gitea/models"
|
||||
auth_model "code.gitea.io/gitea/models/auth"
|
||||
user_model "code.gitea.io/gitea/models/user"
|
||||
password_module "code.gitea.io/gitea/modules/auth/password"
|
||||
"code.gitea.io/gitea/modules/optional"
|
||||
"code.gitea.io/gitea/modules/setting"
|
||||
"code.gitea.io/gitea/modules/structs"
|
||||
)
|
||||
|
||||
type UpdateOptions struct {
|
||||
KeepEmailPrivate optional.Option[bool]
|
||||
FullName optional.Option[string]
|
||||
Website optional.Option[string]
|
||||
Location optional.Option[string]
|
||||
Description optional.Option[string]
|
||||
AllowGitHook optional.Option[bool]
|
||||
AllowImportLocal optional.Option[bool]
|
||||
MaxRepoCreation optional.Option[int]
|
||||
IsRestricted optional.Option[bool]
|
||||
Visibility optional.Option[structs.VisibleType]
|
||||
KeepActivityPrivate optional.Option[bool]
|
||||
Language optional.Option[string]
|
||||
Theme optional.Option[string]
|
||||
DiffViewStyle optional.Option[string]
|
||||
AllowCreateOrganization optional.Option[bool]
|
||||
IsActive optional.Option[bool]
|
||||
IsAdmin optional.Option[bool]
|
||||
EmailNotificationsPreference optional.Option[string]
|
||||
SetLastLogin bool
|
||||
RepoAdminChangeTeamAccess optional.Option[bool]
|
||||
}
|
||||
|
||||
func UpdateUser(ctx context.Context, u *user_model.User, opts *UpdateOptions) error {
|
||||
cols := make([]string, 0, 20)
|
||||
|
||||
if opts.KeepEmailPrivate.Has() {
|
||||
u.KeepEmailPrivate = opts.KeepEmailPrivate.Value()
|
||||
|
||||
cols = append(cols, "keep_email_private")
|
||||
}
|
||||
|
||||
if opts.FullName.Has() {
|
||||
u.FullName = opts.FullName.Value()
|
||||
|
||||
cols = append(cols, "full_name")
|
||||
}
|
||||
if opts.Website.Has() {
|
||||
u.Website = opts.Website.Value()
|
||||
|
||||
cols = append(cols, "website")
|
||||
}
|
||||
if opts.Location.Has() {
|
||||
u.Location = opts.Location.Value()
|
||||
|
||||
cols = append(cols, "location")
|
||||
}
|
||||
if opts.Description.Has() {
|
||||
u.Description = opts.Description.Value()
|
||||
|
||||
cols = append(cols, "description")
|
||||
}
|
||||
if opts.Language.Has() {
|
||||
u.Language = opts.Language.Value()
|
||||
|
||||
cols = append(cols, "language")
|
||||
}
|
||||
if opts.Theme.Has() {
|
||||
u.Theme = opts.Theme.Value()
|
||||
|
||||
cols = append(cols, "theme")
|
||||
}
|
||||
if opts.DiffViewStyle.Has() {
|
||||
u.DiffViewStyle = opts.DiffViewStyle.Value()
|
||||
|
||||
cols = append(cols, "diff_view_style")
|
||||
}
|
||||
|
||||
if opts.AllowGitHook.Has() {
|
||||
u.AllowGitHook = opts.AllowGitHook.Value()
|
||||
|
||||
cols = append(cols, "allow_git_hook")
|
||||
}
|
||||
if opts.AllowImportLocal.Has() {
|
||||
u.AllowImportLocal = opts.AllowImportLocal.Value()
|
||||
|
||||
cols = append(cols, "allow_import_local")
|
||||
}
|
||||
|
||||
if opts.MaxRepoCreation.Has() {
|
||||
u.MaxRepoCreation = opts.MaxRepoCreation.Value()
|
||||
|
||||
cols = append(cols, "max_repo_creation")
|
||||
}
|
||||
|
||||
if opts.IsActive.Has() {
|
||||
u.IsActive = opts.IsActive.Value()
|
||||
|
||||
cols = append(cols, "is_active")
|
||||
}
|
||||
if opts.IsRestricted.Has() {
|
||||
u.IsRestricted = opts.IsRestricted.Value()
|
||||
|
||||
cols = append(cols, "is_restricted")
|
||||
}
|
||||
if opts.IsAdmin.Has() {
|
||||
if !opts.IsAdmin.Value() && user_model.IsLastAdminUser(ctx, u) {
|
||||
return models.ErrDeleteLastAdminUser{UID: u.ID}
|
||||
}
|
||||
|
||||
u.IsAdmin = opts.IsAdmin.Value()
|
||||
|
||||
cols = append(cols, "is_admin")
|
||||
}
|
||||
|
||||
if opts.Visibility.Has() {
|
||||
if !u.IsOrganization() && !setting.Service.AllowedUserVisibilityModesSlice.IsAllowedVisibility(opts.Visibility.Value()) {
|
||||
return fmt.Errorf("visibility mode not allowed: %s", opts.Visibility.Value().String())
|
||||
}
|
||||
u.Visibility = opts.Visibility.Value()
|
||||
|
||||
cols = append(cols, "visibility")
|
||||
}
|
||||
if opts.KeepActivityPrivate.Has() {
|
||||
u.KeepActivityPrivate = opts.KeepActivityPrivate.Value()
|
||||
|
||||
cols = append(cols, "keep_activity_private")
|
||||
}
|
||||
|
||||
if opts.AllowCreateOrganization.Has() {
|
||||
u.AllowCreateOrganization = opts.AllowCreateOrganization.Value()
|
||||
|
||||
cols = append(cols, "allow_create_organization")
|
||||
}
|
||||
if opts.RepoAdminChangeTeamAccess.Has() {
|
||||
u.RepoAdminChangeTeamAccess = opts.RepoAdminChangeTeamAccess.Value()
|
||||
|
||||
cols = append(cols, "repo_admin_change_team_access")
|
||||
}
|
||||
|
||||
if opts.EmailNotificationsPreference.Has() {
|
||||
u.EmailNotificationsPreference = opts.EmailNotificationsPreference.Value()
|
||||
|
||||
cols = append(cols, "email_notifications_preference")
|
||||
}
|
||||
|
||||
if opts.SetLastLogin {
|
||||
u.SetLastLogin()
|
||||
|
||||
cols = append(cols, "last_login_unix")
|
||||
}
|
||||
|
||||
return user_model.UpdateUserCols(ctx, u, cols...)
|
||||
}
|
||||
|
||||
type UpdateAuthOptions struct {
|
||||
LoginSource optional.Option[int64]
|
||||
LoginName optional.Option[string]
|
||||
Password optional.Option[string]
|
||||
MustChangePassword optional.Option[bool]
|
||||
ProhibitLogin optional.Option[bool]
|
||||
}
|
||||
|
||||
func UpdateAuth(ctx context.Context, u *user_model.User, opts *UpdateAuthOptions) error {
|
||||
if opts.LoginSource.Has() {
|
||||
source, err := auth_model.GetSourceByID(ctx, opts.LoginSource.Value())
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
u.LoginType = source.Type
|
||||
u.LoginSource = source.ID
|
||||
}
|
||||
if opts.LoginName.Has() {
|
||||
u.LoginName = opts.LoginName.Value()
|
||||
}
|
||||
|
||||
if opts.Password.Has() && (u.IsLocal() || u.IsOAuth2()) {
|
||||
password := opts.Password.Value()
|
||||
|
||||
if len(password) < setting.MinPasswordLength {
|
||||
return password_module.ErrMinLength
|
||||
}
|
||||
if !password_module.IsComplexEnough(password) {
|
||||
return password_module.ErrComplexity
|
||||
}
|
||||
if err := password_module.IsPwned(ctx, password); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
if err := u.SetPassword(password); err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
|
||||
if opts.MustChangePassword.Has() {
|
||||
u.MustChangePassword = opts.MustChangePassword.Value()
|
||||
}
|
||||
if opts.ProhibitLogin.Has() {
|
||||
u.ProhibitLogin = opts.ProhibitLogin.Value()
|
||||
}
|
||||
|
||||
return user_model.UpdateUserCols(ctx, u, "login_type", "login_source", "login_name", "passwd", "passwd_hash_algo", "salt", "must_change_password", "prohibit_login")
|
||||
}
|
120
services/user/update_test.go
Normal file
120
services/user/update_test.go
Normal file
|
@ -0,0 +1,120 @@
|
|||
// Copyright 2024 The Gitea Authors. All rights reserved.
|
||||
// SPDX-License-Identifier: MIT
|
||||
|
||||
package user
|
||||
|
||||
import (
|
||||
"testing"
|
||||
|
||||
"code.gitea.io/gitea/models/db"
|
||||
"code.gitea.io/gitea/models/unittest"
|
||||
user_model "code.gitea.io/gitea/models/user"
|
||||
password_module "code.gitea.io/gitea/modules/auth/password"
|
||||
"code.gitea.io/gitea/modules/optional"
|
||||
"code.gitea.io/gitea/modules/structs"
|
||||
|
||||
"github.com/stretchr/testify/assert"
|
||||
)
|
||||
|
||||
func TestUpdateUser(t *testing.T) {
|
||||
assert.NoError(t, unittest.PrepareTestDatabase())
|
||||
|
||||
admin := unittest.AssertExistsAndLoadBean(t, &user_model.User{ID: 1})
|
||||
|
||||
assert.Error(t, UpdateUser(db.DefaultContext, admin, &UpdateOptions{
|
||||
IsAdmin: optional.Some(false),
|
||||
}))
|
||||
|
||||
user := unittest.AssertExistsAndLoadBean(t, &user_model.User{ID: 28})
|
||||
|
||||
opts := &UpdateOptions{
|
||||
KeepEmailPrivate: optional.Some(false),
|
||||
FullName: optional.Some("Changed Name"),
|
||||
Website: optional.Some("https://gitea.com/"),
|
||||
Location: optional.Some("location"),
|
||||
Description: optional.Some("description"),
|
||||
AllowGitHook: optional.Some(true),
|
||||
AllowImportLocal: optional.Some(true),
|
||||
MaxRepoCreation: optional.Some[int](10),
|
||||
IsRestricted: optional.Some(true),
|
||||
IsActive: optional.Some(false),
|
||||
IsAdmin: optional.Some(true),
|
||||
Visibility: optional.Some(structs.VisibleTypePrivate),
|
||||
KeepActivityPrivate: optional.Some(true),
|
||||
Language: optional.Some("lang"),
|
||||
Theme: optional.Some("theme"),
|
||||
DiffViewStyle: optional.Some("split"),
|
||||
AllowCreateOrganization: optional.Some(false),
|
||||
EmailNotificationsPreference: optional.Some("disabled"),
|
||||
SetLastLogin: true,
|
||||
}
|
||||
assert.NoError(t, UpdateUser(db.DefaultContext, user, opts))
|
||||
|
||||
assert.Equal(t, opts.KeepEmailPrivate.Value(), user.KeepEmailPrivate)
|
||||
assert.Equal(t, opts.FullName.Value(), user.FullName)
|
||||
assert.Equal(t, opts.Website.Value(), user.Website)
|
||||
assert.Equal(t, opts.Location.Value(), user.Location)
|
||||
assert.Equal(t, opts.Description.Value(), user.Description)
|
||||
assert.Equal(t, opts.AllowGitHook.Value(), user.AllowGitHook)
|
||||
assert.Equal(t, opts.AllowImportLocal.Value(), user.AllowImportLocal)
|
||||
assert.Equal(t, opts.MaxRepoCreation.Value(), user.MaxRepoCreation)
|
||||
assert.Equal(t, opts.IsRestricted.Value(), user.IsRestricted)
|
||||
assert.Equal(t, opts.IsActive.Value(), user.IsActive)
|
||||
assert.Equal(t, opts.IsAdmin.Value(), user.IsAdmin)
|
||||
assert.Equal(t, opts.Visibility.Value(), user.Visibility)
|
||||
assert.Equal(t, opts.KeepActivityPrivate.Value(), user.KeepActivityPrivate)
|
||||
assert.Equal(t, opts.Language.Value(), user.Language)
|
||||
assert.Equal(t, opts.Theme.Value(), user.Theme)
|
||||
assert.Equal(t, opts.DiffViewStyle.Value(), user.DiffViewStyle)
|
||||
assert.Equal(t, opts.AllowCreateOrganization.Value(), user.AllowCreateOrganization)
|
||||
assert.Equal(t, opts.EmailNotificationsPreference.Value(), user.EmailNotificationsPreference)
|
||||
|
||||
user = unittest.AssertExistsAndLoadBean(t, &user_model.User{ID: 28})
|
||||
assert.Equal(t, opts.KeepEmailPrivate.Value(), user.KeepEmailPrivate)
|
||||
assert.Equal(t, opts.FullName.Value(), user.FullName)
|
||||
assert.Equal(t, opts.Website.Value(), user.Website)
|
||||
assert.Equal(t, opts.Location.Value(), user.Location)
|
||||
assert.Equal(t, opts.Description.Value(), user.Description)
|
||||
assert.Equal(t, opts.AllowGitHook.Value(), user.AllowGitHook)
|
||||
assert.Equal(t, opts.AllowImportLocal.Value(), user.AllowImportLocal)
|
||||
assert.Equal(t, opts.MaxRepoCreation.Value(), user.MaxRepoCreation)
|
||||
assert.Equal(t, opts.IsRestricted.Value(), user.IsRestricted)
|
||||
assert.Equal(t, opts.IsActive.Value(), user.IsActive)
|
||||
assert.Equal(t, opts.IsAdmin.Value(), user.IsAdmin)
|
||||
assert.Equal(t, opts.Visibility.Value(), user.Visibility)
|
||||
assert.Equal(t, opts.KeepActivityPrivate.Value(), user.KeepActivityPrivate)
|
||||
assert.Equal(t, opts.Language.Value(), user.Language)
|
||||
assert.Equal(t, opts.Theme.Value(), user.Theme)
|
||||
assert.Equal(t, opts.DiffViewStyle.Value(), user.DiffViewStyle)
|
||||
assert.Equal(t, opts.AllowCreateOrganization.Value(), user.AllowCreateOrganization)
|
||||
assert.Equal(t, opts.EmailNotificationsPreference.Value(), user.EmailNotificationsPreference)
|
||||
}
|
||||
|
||||
func TestUpdateAuth(t *testing.T) {
|
||||
assert.NoError(t, unittest.PrepareTestDatabase())
|
||||
|
||||
user := unittest.AssertExistsAndLoadBean(t, &user_model.User{ID: 28})
|
||||
copy := *user
|
||||
|
||||
assert.NoError(t, UpdateAuth(db.DefaultContext, user, &UpdateAuthOptions{
|
||||
LoginName: optional.Some("new-login"),
|
||||
}))
|
||||
assert.Equal(t, "new-login", user.LoginName)
|
||||
|
||||
assert.NoError(t, UpdateAuth(db.DefaultContext, user, &UpdateAuthOptions{
|
||||
Password: optional.Some("%$DRZUVB576tfzgu"),
|
||||
MustChangePassword: optional.Some(true),
|
||||
}))
|
||||
assert.True(t, user.MustChangePassword)
|
||||
assert.NotEqual(t, copy.Passwd, user.Passwd)
|
||||
assert.NotEqual(t, copy.Salt, user.Salt)
|
||||
|
||||
assert.NoError(t, UpdateAuth(db.DefaultContext, user, &UpdateAuthOptions{
|
||||
ProhibitLogin: optional.Some(true),
|
||||
}))
|
||||
assert.True(t, user.ProhibitLogin)
|
||||
|
||||
assert.ErrorIs(t, UpdateAuth(db.DefaultContext, user, &UpdateAuthOptions{
|
||||
Password: optional.Some("aaaa"),
|
||||
}), password_module.ErrMinLength)
|
||||
}
|
|
@ -41,10 +41,7 @@ func RenameUser(ctx context.Context, u *user_model.User, newUserName string) err
|
|||
}
|
||||
|
||||
if newUserName == u.Name {
|
||||
return user_model.ErrUsernameNotChanged{
|
||||
UID: u.ID,
|
||||
Name: u.Name,
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
if err := user_model.IsUsableUsername(newUserName); err != nil {
|
||||
|
|
|
@ -107,7 +107,7 @@ func TestRenameUser(t *testing.T) {
|
|||
})
|
||||
|
||||
t.Run("Same username", func(t *testing.T) {
|
||||
assert.ErrorIs(t, RenameUser(db.DefaultContext, user, user.Name), user_model.ErrUsernameNotChanged{UID: user.ID, Name: user.Name})
|
||||
assert.NoError(t, RenameUser(db.DefaultContext, user, user.Name))
|
||||
})
|
||||
|
||||
t.Run("Non usable username", func(t *testing.T) {
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue