Skip to content
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.

Commit c59a057

Browse files
authoredMay 21, 2023
Refactor rename user and rename organization (#24052)
This PR is a refactor at the beginning. And now it did 4 things. - [x] Move renaming organizaiton and user logics into services layer and merged as one function - [x] Support rename a user capitalization only. For example, rename the user from `Lunny` to `lunny`. We just need to change one table `user` and others should not be touched. - [x] Before this PR, some renaming were missed like `agit` - [x] Fix bug the API reutrned from `http.StatusNoContent` to `http.StatusOK`
1 parent 64f6a5d commit c59a057

File tree

12 files changed

+267
-188
lines changed

12 files changed

+267
-188
lines changed
 

‎models/repo/repo.go

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -832,3 +832,11 @@ func FixNullArchivedRepository(ctx context.Context) (int64, error) {
832832
IsArchived: false,
833833
})
834834
}
835+
836+
// UpdateRepositoryOwnerName updates the owner name of all repositories owned by the user
837+
func UpdateRepositoryOwnerName(ctx context.Context, oldUserName, newUserName string) error {
838+
if _, err := db.GetEngine(ctx).Exec("UPDATE `repository` SET owner_name=? WHERE owner_name=?", newUserName, oldUserName); err != nil {
839+
return fmt.Errorf("change repo owner name: %w", err)
840+
}
841+
return nil
842+
}

‎models/user/error.go

Lines changed: 31 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -9,13 +9,6 @@ import (
99
"code.gitea.io/gitea/modules/util"
1010
)
1111

12-
// ____ ___
13-
// | | \______ ___________
14-
// | | / ___// __ \_ __ \
15-
// | | /\___ \\ ___/| | \/
16-
// |______//____ >\___ >__|
17-
// \/ \/
18-
1912
// ErrUserAlreadyExist represents a "user already exists" error.
2013
type ErrUserAlreadyExist struct {
2114
Name string
@@ -99,3 +92,34 @@ func (err ErrUserInactive) Error() string {
9992
func (err ErrUserInactive) Unwrap() error {
10093
return util.ErrPermissionDenied
10194
}
95+
96+
// ErrUserIsNotLocal represents a "ErrUserIsNotLocal" kind of error.
97+
type ErrUserIsNotLocal struct {
98+
UID int64
99+
Name string
100+
}
101+
102+
func (err ErrUserIsNotLocal) Error() string {
103+
return fmt.Sprintf("user is not local type [uid: %d, name: %s]", err.UID, err.Name)
104+
}
105+
106+
// IsErrUserIsNotLocal
107+
func IsErrUserIsNotLocal(err error) bool {
108+
_, ok := err.(ErrUserIsNotLocal)
109+
return ok
110+
}
111+
112+
type ErrUsernameNotChanged struct {
113+
UID int64
114+
Name string
115+
}
116+
117+
func (err ErrUsernameNotChanged) Error() string {
118+
return fmt.Sprintf("username hasn't been changed[uid: %d, name: %s]", err.UID, err.Name)
119+
}
120+
121+
// IsErrUsernameNotChanged
122+
func IsErrUsernameNotChanged(err error) bool {
123+
_, ok := err.(ErrUsernameNotChanged)
124+
return ok
125+
}

‎models/user/user.go

Lines changed: 0 additions & 45 deletions
Original file line numberDiff line numberDiff line change
@@ -9,7 +9,6 @@ import (
99
"encoding/hex"
1010
"fmt"
1111
"net/url"
12-
"os"
1312
"path/filepath"
1413
"strings"
1514
"time"
@@ -756,50 +755,6 @@ func VerifyUserActiveCode(code string) (user *User) {
756755
return nil
757756
}
758757

759-
// ChangeUserName changes all corresponding setting from old user name to new one.
760-
func ChangeUserName(ctx context.Context, u *User, newUserName string) (err error) {
761-
oldUserName := u.Name
762-
if err = IsUsableUsername(newUserName); err != nil {
763-
return err
764-
}
765-
766-
ctx, committer, err := db.TxContext(ctx)
767-
if err != nil {
768-
return err
769-
}
770-
defer committer.Close()
771-
772-
isExist, err := IsUserExist(ctx, 0, newUserName)
773-
if err != nil {
774-
return err
775-
} else if isExist {
776-
return ErrUserAlreadyExist{newUserName}
777-
}
778-
779-
if _, err = db.GetEngine(ctx).Exec("UPDATE `repository` SET owner_name=? WHERE owner_name=?", newUserName, oldUserName); err != nil {
780-
return fmt.Errorf("Change repo owner name: %w", err)
781-
}
782-
783-
// Do not fail if directory does not exist
784-
if err = util.Rename(UserPath(oldUserName), UserPath(newUserName)); err != nil && !os.IsNotExist(err) {
785-
return fmt.Errorf("Rename user directory: %w", err)
786-
}
787-
788-
if err = NewUserRedirect(ctx, u.ID, oldUserName, newUserName); err != nil {
789-
return err
790-
}
791-
792-
if err = committer.Commit(); err != nil {
793-
if err2 := util.Rename(UserPath(newUserName), UserPath(oldUserName)); err2 != nil && !os.IsNotExist(err2) {
794-
log.Critical("Unable to rollback directory change during failed username change from: %s to: %s. DB Error: %v. Filesystem Error: %v", oldUserName, newUserName, err, err2)
795-
return fmt.Errorf("failed to rollback directory change during failed username change from: %s to: %s. DB Error: %w. Filesystem Error: %v", oldUserName, newUserName, err, err2)
796-
}
797-
return err
798-
}
799-
800-
return nil
801-
}
802-
803758
// checkDupEmail checks whether there are the same email with the user
804759
func checkDupEmail(ctx context.Context, u *User) error {
805760
u.Email = strings.ToLower(u.Email)

‎options/locale/locale_en-US.ini

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -520,6 +520,7 @@ lang_select_error = Select a language from the list.
520520

521521
username_been_taken = The username is already taken.
522522
username_change_not_local_user = Non-local users are not allowed to change their username.
523+
username_has_not_been_changed = Username has not been changed
523524
repo_name_been_taken = The repository name is already used.
524525
repository_force_private = Force Private is enabled: private repositories cannot be made public.
525526
repository_files_already_exist = Files already exist for this repository. Contact the system administrator.

‎routers/api/v1/admin/user.go

Lines changed: 7 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -502,17 +502,15 @@ func RenameUser(ctx *context.APIContext) {
502502
return
503503
}
504504

505+
oldName := ctx.ContextUser.Name
505506
newName := web.GetForm(ctx).(*api.RenameUserOption).NewName
506507

507-
if strings.EqualFold(newName, ctx.ContextUser.Name) {
508-
// Noop as username is not changed
509-
ctx.Status(http.StatusNoContent)
510-
return
511-
}
512-
513508
// Check if user name has been changed
514509
if err := user_service.RenameUser(ctx, ctx.ContextUser, newName); err != nil {
515510
switch {
511+
case user_model.IsErrUsernameNotChanged(err):
512+
// Noop as username is not changed
513+
ctx.Status(http.StatusNoContent)
516514
case user_model.IsErrUserAlreadyExist(err):
517515
ctx.Error(http.StatusUnprocessableEntity, "", ctx.Tr("form.username_been_taken"))
518516
case db.IsErrNameReserved(err):
@@ -526,5 +524,7 @@ func RenameUser(ctx *context.APIContext) {
526524
}
527525
return
528526
}
529-
ctx.Status(http.StatusNoContent)
527+
528+
log.Trace("User name changed: %s -> %s", oldName, newName)
529+
ctx.Status(http.StatusOK)
530530
}

‎routers/web/org/setting.go

Lines changed: 15 additions & 24 deletions
Original file line numberDiff line numberDiff line change
@@ -22,8 +22,7 @@ import (
2222
"code.gitea.io/gitea/modules/web"
2323
user_setting "code.gitea.io/gitea/routers/web/user/setting"
2424
"code.gitea.io/gitea/services/forms"
25-
"code.gitea.io/gitea/services/org"
26-
container_service "code.gitea.io/gitea/services/packages/container"
25+
org_service "code.gitea.io/gitea/services/org"
2726
repo_service "code.gitea.io/gitea/services/repository"
2827
user_service "code.gitea.io/gitea/services/user"
2928
)
@@ -67,31 +66,23 @@ func SettingsPost(ctx *context.Context) {
6766
nameChanged := org.Name != form.Name
6867

6968
// Check if organization name has been changed.
70-
if org.LowerName != strings.ToLower(form.Name) {
71-
isExist, err := user_model.IsUserExist(ctx, org.ID, form.Name)
72-
if err != nil {
73-
ctx.ServerError("IsUserExist", err)
74-
return
75-
} else if isExist {
69+
if nameChanged {
70+
err := org_service.RenameOrganization(ctx, org, form.Name)
71+
switch {
72+
case user_model.IsErrUserAlreadyExist(err):
7673
ctx.Data["OrgName"] = true
7774
ctx.RenderWithErr(ctx.Tr("form.username_been_taken"), tplSettingsOptions, &form)
7875
return
79-
} else if err = user_model.ChangeUserName(ctx, org.AsUser(), form.Name); err != nil {
80-
switch {
81-
case db.IsErrNameReserved(err):
82-
ctx.Data["OrgName"] = true
83-
ctx.RenderWithErr(ctx.Tr("repo.form.name_reserved", err.(db.ErrNameReserved).Name), tplSettingsOptions, &form)
84-
case db.IsErrNamePatternNotAllowed(err):
85-
ctx.Data["OrgName"] = true
86-
ctx.RenderWithErr(ctx.Tr("repo.form.name_pattern_not_allowed", err.(db.ErrNamePatternNotAllowed).Pattern), tplSettingsOptions, &form)
87-
default:
88-
ctx.ServerError("ChangeUserName", err)
89-
}
76+
case db.IsErrNameReserved(err):
77+
ctx.Data["OrgName"] = true
78+
ctx.RenderWithErr(ctx.Tr("repo.form.name_reserved", err.(db.ErrNameReserved).Name), tplSettingsOptions, &form)
9079
return
91-
}
92-
93-
if err := container_service.UpdateRepositoryNames(ctx, org.AsUser(), form.Name); err != nil {
94-
ctx.ServerError("UpdateRepositoryNames", err)
80+
case db.IsErrNamePatternNotAllowed(err):
81+
ctx.Data["OrgName"] = true
82+
ctx.RenderWithErr(ctx.Tr("repo.form.name_pattern_not_allowed", err.(db.ErrNamePatternNotAllowed).Pattern), tplSettingsOptions, &form)
83+
return
84+
case err != nil:
85+
ctx.ServerError("org_service.RenameOrganization", err)
9586
return
9687
}
9788

@@ -186,7 +177,7 @@ func SettingsDelete(ctx *context.Context) {
186177
return
187178
}
188179

189-
if err := org.DeleteOrganization(ctx.Org.Organization); err != nil {
180+
if err := org_service.DeleteOrganization(ctx.Org.Organization); err != nil {
190181
if models.IsErrUserOwnRepos(err) {
191182
ctx.Flash.Error(ctx.Tr("form.org_still_own_repo"))
192183
ctx.Redirect(ctx.Org.OrgLink + "/settings/delete")

‎routers/web/user/setting/profile.go

Lines changed: 8 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -49,15 +49,16 @@ func Profile(ctx *context.Context) {
4949

5050
// HandleUsernameChange handle username changes from user settings and admin interface
5151
func HandleUsernameChange(ctx *context.Context, user *user_model.User, newName string) error {
52-
// Non-local users are not allowed to change their username.
53-
if !user.IsLocal() {
54-
ctx.Flash.Error(ctx.Tr("form.username_change_not_local_user"))
55-
return fmt.Errorf(ctx.Tr("form.username_change_not_local_user"))
56-
}
57-
52+
oldName := user.Name
5853
// rename user
5954
if err := user_service.RenameUser(ctx, user, newName); err != nil {
6055
switch {
56+
// Noop as username is not changed
57+
case user_model.IsErrUsernameNotChanged(err):
58+
ctx.Flash.Error(ctx.Tr("form.username_has_not_been_changed"))
59+
// Non-local users are not allowed to change their username.
60+
case user_model.IsErrUserIsNotLocal(err):
61+
ctx.Flash.Error(ctx.Tr("form.username_change_not_local_user"))
6162
case user_model.IsErrUserAlreadyExist(err):
6263
ctx.Flash.Error(ctx.Tr("form.username_been_taken"))
6364
case user_model.IsErrEmailAlreadyUsed(err):
@@ -73,7 +74,7 @@ func HandleUsernameChange(ctx *context.Context, user *user_model.User, newName s
7374
}
7475
return err
7576
}
76-
77+
log.Trace("User name changed: %s -> %s", oldName, newName)
7778
return nil
7879
}
7980

‎services/org/org.go

Lines changed: 12 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -4,20 +4,22 @@
44
package org
55

66
import (
7+
"context"
78
"fmt"
89

910
"code.gitea.io/gitea/models"
1011
"code.gitea.io/gitea/models/db"
11-
"code.gitea.io/gitea/models/organization"
12+
org_model "code.gitea.io/gitea/models/organization"
1213
packages_model "code.gitea.io/gitea/models/packages"
1314
repo_model "code.gitea.io/gitea/models/repo"
1415
user_model "code.gitea.io/gitea/models/user"
1516
"code.gitea.io/gitea/modules/storage"
1617
"code.gitea.io/gitea/modules/util"
18+
user_service "code.gitea.io/gitea/services/user"
1719
)
1820

1921
// DeleteOrganization completely and permanently deletes everything of organization.
20-
func DeleteOrganization(org *organization.Organization) error {
22+
func DeleteOrganization(org *org_model.Organization) error {
2123
ctx, commiter, err := db.TxContext(db.DefaultContext)
2224
if err != nil {
2325
return err
@@ -39,7 +41,7 @@ func DeleteOrganization(org *organization.Organization) error {
3941
return models.ErrUserOwnPackages{UID: org.ID}
4042
}
4143

42-
if err := organization.DeleteOrganization(ctx, org); err != nil {
44+
if err := org_model.DeleteOrganization(ctx, org); err != nil {
4345
return fmt.Errorf("DeleteOrganization: %w", err)
4446
}
4547

@@ -53,15 +55,20 @@ func DeleteOrganization(org *organization.Organization) error {
5355
path := user_model.UserPath(org.Name)
5456

5557
if err := util.RemoveAll(path); err != nil {
56-
return fmt.Errorf("Failed to RemoveAll %s: %w", path, err)
58+
return fmt.Errorf("failed to RemoveAll %s: %w", path, err)
5759
}
5860

5961
if len(org.Avatar) > 0 {
6062
avatarPath := org.CustomAvatarRelativePath()
6163
if err := storage.Avatars.Delete(avatarPath); err != nil {
62-
return fmt.Errorf("Failed to remove %s: %w", avatarPath, err)
64+
return fmt.Errorf("failed to remove %s: %w", avatarPath, err)
6365
}
6466
}
6567

6668
return nil
6769
}
70+
71+
// RenameOrganization renames an organization.
72+
func RenameOrganization(ctx context.Context, org *org_model.Organization, newName string) error {
73+
return user_service.RenameUser(ctx, org.AsUser(), newName)
74+
}

‎services/user/avatar.go

Lines changed: 62 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,62 @@
1+
// Copyright 2023 The Gitea Authors. All rights reserved.
2+
// SPDX-License-Identifier: MIT
3+
4+
package user
5+
6+
import (
7+
"fmt"
8+
"io"
9+
10+
"code.gitea.io/gitea/models/db"
11+
user_model "code.gitea.io/gitea/models/user"
12+
"code.gitea.io/gitea/modules/avatar"
13+
"code.gitea.io/gitea/modules/log"
14+
"code.gitea.io/gitea/modules/storage"
15+
)
16+
17+
// UploadAvatar saves custom avatar for user.
18+
func UploadAvatar(u *user_model.User, data []byte) error {
19+
avatarData, err := avatar.ProcessAvatarImage(data)
20+
if err != nil {
21+
return err
22+
}
23+
24+
ctx, committer, err := db.TxContext(db.DefaultContext)
25+
if err != nil {
26+
return err
27+
}
28+
defer committer.Close()
29+
30+
u.UseCustomAvatar = true
31+
u.Avatar = avatar.HashAvatar(u.ID, data)
32+
if err = user_model.UpdateUserCols(ctx, u, "use_custom_avatar", "avatar"); err != nil {
33+
return fmt.Errorf("updateUser: %w", err)
34+
}
35+
36+
if err := storage.SaveFrom(storage.Avatars, u.CustomAvatarRelativePath(), func(w io.Writer) error {
37+
_, err := w.Write(avatarData)
38+
return err
39+
}); err != nil {
40+
return fmt.Errorf("Failed to create dir %s: %w", u.CustomAvatarRelativePath(), err)
41+
}
42+
43+
return committer.Commit()
44+
}
45+
46+
// DeleteAvatar deletes the user's custom avatar.
47+
func DeleteAvatar(u *user_model.User) error {
48+
aPath := u.CustomAvatarRelativePath()
49+
log.Trace("DeleteAvatar[%d]: %s", u.ID, aPath)
50+
if len(u.Avatar) > 0 {
51+
if err := storage.Avatars.Delete(aPath); err != nil {
52+
return fmt.Errorf("Failed to remove %s: %w", aPath, err)
53+
}
54+
}
55+
56+
u.UseCustomAvatar = false
57+
u.Avatar = ""
58+
if _, err := db.GetEngine(db.DefaultContext).ID(u.ID).Cols("avatar, use_custom_avatar").Update(u); err != nil {
59+
return fmt.Errorf("UpdateUser: %w", err)
60+
}
61+
return nil
62+
}

‎services/user/rename.go

Lines changed: 0 additions & 41 deletions
This file was deleted.

‎services/user/user.go

Lines changed: 82 additions & 52 deletions
Original file line numberDiff line numberDiff line change
@@ -6,7 +6,8 @@ package user
66
import (
77
"context"
88
"fmt"
9-
"io"
9+
"os"
10+
"strings"
1011
"time"
1112

1213
"code.gitea.io/gitea/models"
@@ -17,29 +18,105 @@ import (
1718
repo_model "code.gitea.io/gitea/models/repo"
1819
system_model "code.gitea.io/gitea/models/system"
1920
user_model "code.gitea.io/gitea/models/user"
20-
"code.gitea.io/gitea/modules/avatar"
2121
"code.gitea.io/gitea/modules/eventsource"
2222
"code.gitea.io/gitea/modules/log"
2323
"code.gitea.io/gitea/modules/setting"
2424
"code.gitea.io/gitea/modules/storage"
2525
"code.gitea.io/gitea/modules/util"
26+
"code.gitea.io/gitea/services/agit"
2627
"code.gitea.io/gitea/services/packages"
28+
container_service "code.gitea.io/gitea/services/packages/container"
2729
)
2830

2931
// RenameUser renames a user
3032
func RenameUser(ctx context.Context, u *user_model.User, newUserName string) error {
33+
// Non-local users are not allowed to change their username.
34+
if !u.IsOrganization() && !u.IsLocal() {
35+
return user_model.ErrUserIsNotLocal{
36+
UID: u.ID,
37+
Name: u.Name,
38+
}
39+
}
40+
41+
if newUserName == u.Name {
42+
return user_model.ErrUsernameNotChanged{
43+
UID: u.ID,
44+
Name: u.Name,
45+
}
46+
}
47+
48+
if err := user_model.IsUsableUsername(newUserName); err != nil {
49+
return err
50+
}
51+
52+
onlyCapitalization := strings.EqualFold(newUserName, u.Name)
53+
oldUserName := u.Name
54+
55+
if onlyCapitalization {
56+
u.Name = newUserName
57+
if err := user_model.UpdateUserCols(ctx, u, "name"); err != nil {
58+
u.Name = oldUserName
59+
return err
60+
}
61+
return nil
62+
}
63+
3164
ctx, committer, err := db.TxContext(ctx)
3265
if err != nil {
3366
return err
3467
}
3568
defer committer.Close()
36-
if err := renameUser(ctx, u, newUserName); err != nil {
69+
70+
isExist, err := user_model.IsUserExist(ctx, u.ID, newUserName)
71+
if err != nil {
3772
return err
3873
}
39-
if err := committer.Commit(); err != nil {
74+
if isExist {
75+
return user_model.ErrUserAlreadyExist{
76+
Name: newUserName,
77+
}
78+
}
79+
80+
if err = repo_model.UpdateRepositoryOwnerName(ctx, oldUserName, newUserName); err != nil {
81+
return err
82+
}
83+
84+
if err = user_model.NewUserRedirect(ctx, u.ID, oldUserName, newUserName); err != nil {
85+
return err
86+
}
87+
88+
if err := agit.UserNameChanged(ctx, u, newUserName); err != nil {
89+
return err
90+
}
91+
if err := container_service.UpdateRepositoryNames(ctx, u, newUserName); err != nil {
92+
return err
93+
}
94+
95+
u.Name = newUserName
96+
u.LowerName = strings.ToLower(newUserName)
97+
if err := user_model.UpdateUserCols(ctx, u, "name", "lower_name"); err != nil {
98+
u.Name = oldUserName
99+
u.LowerName = strings.ToLower(oldUserName)
40100
return err
41101
}
42-
return err
102+
103+
// Do not fail if directory does not exist
104+
if err = util.Rename(user_model.UserPath(oldUserName), user_model.UserPath(newUserName)); err != nil && !os.IsNotExist(err) {
105+
u.Name = oldUserName
106+
u.LowerName = strings.ToLower(oldUserName)
107+
return fmt.Errorf("rename user directory: %w", err)
108+
}
109+
110+
if err = committer.Commit(); err != nil {
111+
u.Name = oldUserName
112+
u.LowerName = strings.ToLower(oldUserName)
113+
if err2 := util.Rename(user_model.UserPath(newUserName), user_model.UserPath(oldUserName)); err2 != nil && !os.IsNotExist(err2) {
114+
log.Critical("Unable to rollback directory change during failed username change from: %s to: %s. DB Error: %v. Filesystem Error: %v", oldUserName, newUserName, err, err2)
115+
return fmt.Errorf("failed to rollback directory change during failed username change from: %s to: %s. DB Error: %w. Filesystem Error: %v", oldUserName, newUserName, err, err2)
116+
}
117+
return err
118+
}
119+
return nil
43120
}
44121

45122
// DeleteUser completely and permanently deletes everything of a user,
@@ -240,50 +317,3 @@ func DeleteInactiveUsers(ctx context.Context, olderThan time.Duration) error {
240317

241318
return user_model.DeleteInactiveEmailAddresses(ctx)
242319
}
243-
244-
// UploadAvatar saves custom avatar for user.
245-
func UploadAvatar(u *user_model.User, data []byte) error {
246-
avatarData, err := avatar.ProcessAvatarImage(data)
247-
if err != nil {
248-
return err
249-
}
250-
251-
ctx, committer, err := db.TxContext(db.DefaultContext)
252-
if err != nil {
253-
return err
254-
}
255-
defer committer.Close()
256-
257-
u.UseCustomAvatar = true
258-
u.Avatar = avatar.HashAvatar(u.ID, data)
259-
if err = user_model.UpdateUserCols(ctx, u, "use_custom_avatar", "avatar"); err != nil {
260-
return fmt.Errorf("updateUser: %w", err)
261-
}
262-
263-
if err := storage.SaveFrom(storage.Avatars, u.CustomAvatarRelativePath(), func(w io.Writer) error {
264-
_, err := w.Write(avatarData)
265-
return err
266-
}); err != nil {
267-
return fmt.Errorf("Failed to create dir %s: %w", u.CustomAvatarRelativePath(), err)
268-
}
269-
270-
return committer.Commit()
271-
}
272-
273-
// DeleteAvatar deletes the user's custom avatar.
274-
func DeleteAvatar(u *user_model.User) error {
275-
aPath := u.CustomAvatarRelativePath()
276-
log.Trace("DeleteAvatar[%d]: %s", u.ID, aPath)
277-
if len(u.Avatar) > 0 {
278-
if err := storage.Avatars.Delete(aPath); err != nil {
279-
return fmt.Errorf("Failed to remove %s: %w", aPath, err)
280-
}
281-
}
282-
283-
u.UseCustomAvatar = false
284-
u.Avatar = ""
285-
if _, err := db.GetEngine(db.DefaultContext).ID(u.ID).Cols("avatar, use_custom_avatar").Update(u); err != nil {
286-
return fmt.Errorf("UpdateUser: %w", err)
287-
}
288-
return nil
289-
}

‎tests/integration/api_admin_test.go

Lines changed: 41 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -241,3 +241,44 @@ func TestAPICreateRepoForUser(t *testing.T) {
241241
)
242242
MakeRequest(t, req, http.StatusCreated)
243243
}
244+
245+
func TestAPIRenameUser(t *testing.T) {
246+
defer tests.PrepareTestEnv(t)()
247+
adminUsername := "user1"
248+
token := getUserToken(t, adminUsername, auth_model.AccessTokenScopeSudo)
249+
urlStr := fmt.Sprintf("/api/v1/admin/users/%s/rename?token=%s", "user2", token)
250+
req := NewRequestWithValues(t, "POST", urlStr, map[string]string{
251+
// required
252+
"new_name": "User2",
253+
})
254+
MakeRequest(t, req, http.StatusOK)
255+
256+
urlStr = fmt.Sprintf("/api/v1/admin/users/%s/rename?token=%s", "User2", token)
257+
req = NewRequestWithValues(t, "POST", urlStr, map[string]string{
258+
// required
259+
"new_name": "User2-2-2",
260+
})
261+
MakeRequest(t, req, http.StatusOK)
262+
263+
urlStr = fmt.Sprintf("/api/v1/admin/users/%s/rename?token=%s", "User2", token)
264+
req = NewRequestWithValues(t, "POST", urlStr, map[string]string{
265+
// required
266+
"new_name": "user1",
267+
})
268+
// the old user name still be used by with a redirect
269+
MakeRequest(t, req, http.StatusTemporaryRedirect)
270+
271+
urlStr = fmt.Sprintf("/api/v1/admin/users/%s/rename?token=%s", "User2-2-2", token)
272+
req = NewRequestWithValues(t, "POST", urlStr, map[string]string{
273+
// required
274+
"new_name": "user1",
275+
})
276+
MakeRequest(t, req, http.StatusUnprocessableEntity)
277+
278+
urlStr = fmt.Sprintf("/api/v1/admin/users/%s/rename?token=%s", "User2-2-2", token)
279+
req = NewRequestWithValues(t, "POST", urlStr, map[string]string{
280+
// required
281+
"new_name": "user2",
282+
})
283+
MakeRequest(t, req, http.StatusOK)
284+
}

0 commit comments

Comments
 (0)
Please sign in to comment.