diff options
author | Lunny Xiao <xiaolunwen@gmail.com> | 2023-05-21 23:13:47 +0800 |
---|---|---|
committer | GitHub <noreply@github.com> | 2023-05-21 23:13:47 +0800 |
commit | c59a057297c782f44a81a3e630b5094a58099edb (patch) | |
tree | 58c3020b0da9755fa769619bb3ad80e656f25cd9 /services | |
parent | 64f6a5d113da0d5d187752c9398d6e8d22d24b79 (diff) | |
download | gitea-c59a057297c782f44a81a3e630b5094a58099edb.tar.gz gitea-c59a057297c782f44a81a3e630b5094a58099edb.zip |
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`
Diffstat (limited to 'services')
-rw-r--r-- | services/org/org.go | 17 | ||||
-rw-r--r-- | services/user/avatar.go | 62 | ||||
-rw-r--r-- | services/user/rename.go | 41 | ||||
-rw-r--r-- | services/user/user.go | 134 |
4 files changed, 156 insertions, 98 deletions
diff --git a/services/org/org.go b/services/org/org.go index e45fb305de..a62e5b6fc8 100644 --- a/services/org/org.go +++ b/services/org/org.go @@ -4,20 +4,22 @@ package org import ( + "context" "fmt" "code.gitea.io/gitea/models" "code.gitea.io/gitea/models/db" - "code.gitea.io/gitea/models/organization" + org_model "code.gitea.io/gitea/models/organization" packages_model "code.gitea.io/gitea/models/packages" repo_model "code.gitea.io/gitea/models/repo" user_model "code.gitea.io/gitea/models/user" "code.gitea.io/gitea/modules/storage" "code.gitea.io/gitea/modules/util" + user_service "code.gitea.io/gitea/services/user" ) // DeleteOrganization completely and permanently deletes everything of organization. -func DeleteOrganization(org *organization.Organization) error { +func DeleteOrganization(org *org_model.Organization) error { ctx, commiter, err := db.TxContext(db.DefaultContext) if err != nil { return err @@ -39,7 +41,7 @@ func DeleteOrganization(org *organization.Organization) error { return models.ErrUserOwnPackages{UID: org.ID} } - if err := organization.DeleteOrganization(ctx, org); err != nil { + if err := org_model.DeleteOrganization(ctx, org); err != nil { return fmt.Errorf("DeleteOrganization: %w", err) } @@ -53,15 +55,20 @@ func DeleteOrganization(org *organization.Organization) error { path := user_model.UserPath(org.Name) if err := util.RemoveAll(path); err != nil { - return fmt.Errorf("Failed to RemoveAll %s: %w", path, err) + return fmt.Errorf("failed to RemoveAll %s: %w", path, err) } if len(org.Avatar) > 0 { avatarPath := org.CustomAvatarRelativePath() if err := storage.Avatars.Delete(avatarPath); err != nil { - return fmt.Errorf("Failed to remove %s: %w", avatarPath, err) + return fmt.Errorf("failed to remove %s: %w", avatarPath, err) } } return nil } + +// RenameOrganization renames an organization. +func RenameOrganization(ctx context.Context, org *org_model.Organization, newName string) error { + return user_service.RenameUser(ctx, org.AsUser(), newName) +} diff --git a/services/user/avatar.go b/services/user/avatar.go new file mode 100644 index 0000000000..26c100abdb --- /dev/null +++ b/services/user/avatar.go @@ -0,0 +1,62 @@ +// Copyright 2023 The Gitea Authors. All rights reserved. +// SPDX-License-Identifier: MIT + +package user + +import ( + "fmt" + "io" + + "code.gitea.io/gitea/models/db" + user_model "code.gitea.io/gitea/models/user" + "code.gitea.io/gitea/modules/avatar" + "code.gitea.io/gitea/modules/log" + "code.gitea.io/gitea/modules/storage" +) + +// UploadAvatar saves custom avatar for user. +func UploadAvatar(u *user_model.User, data []byte) error { + avatarData, err := avatar.ProcessAvatarImage(data) + if err != nil { + return err + } + + ctx, committer, err := db.TxContext(db.DefaultContext) + if err != nil { + return err + } + defer committer.Close() + + u.UseCustomAvatar = true + u.Avatar = avatar.HashAvatar(u.ID, data) + if err = user_model.UpdateUserCols(ctx, u, "use_custom_avatar", "avatar"); err != nil { + return fmt.Errorf("updateUser: %w", err) + } + + if err := storage.SaveFrom(storage.Avatars, u.CustomAvatarRelativePath(), func(w io.Writer) error { + _, err := w.Write(avatarData) + return err + }); err != nil { + return fmt.Errorf("Failed to create dir %s: %w", u.CustomAvatarRelativePath(), err) + } + + return committer.Commit() +} + +// DeleteAvatar deletes the user's custom avatar. +func DeleteAvatar(u *user_model.User) error { + aPath := u.CustomAvatarRelativePath() + log.Trace("DeleteAvatar[%d]: %s", u.ID, aPath) + if len(u.Avatar) > 0 { + if err := storage.Avatars.Delete(aPath); err != nil { + return fmt.Errorf("Failed to remove %s: %w", aPath, err) + } + } + + u.UseCustomAvatar = false + u.Avatar = "" + if _, err := db.GetEngine(db.DefaultContext).ID(u.ID).Cols("avatar, use_custom_avatar").Update(u); err != nil { + return fmt.Errorf("UpdateUser: %w", err) + } + return nil +} diff --git a/services/user/rename.go b/services/user/rename.go deleted file mode 100644 index af195d7d76..0000000000 --- a/services/user/rename.go +++ /dev/null @@ -1,41 +0,0 @@ -// Copyright 2023 The Gitea Authors. All rights reserved. -// SPDX-License-Identifier: MIT - -package user - -import ( - "context" - "fmt" - "strings" - - user_model "code.gitea.io/gitea/models/user" - "code.gitea.io/gitea/modules/log" - "code.gitea.io/gitea/services/agit" - container_service "code.gitea.io/gitea/services/packages/container" -) - -func renameUser(ctx context.Context, u *user_model.User, newUserName string) error { - if u.IsOrganization() { - return fmt.Errorf("cannot rename organization") - } - - if err := user_model.ChangeUserName(ctx, u, newUserName); err != nil { - return err - } - - if err := agit.UserNameChanged(ctx, u, newUserName); err != nil { - return err - } - if err := container_service.UpdateRepositoryNames(ctx, u, newUserName); err != nil { - return err - } - - u.Name = newUserName - u.LowerName = strings.ToLower(newUserName) - if err := user_model.UpdateUser(ctx, u, false); err != nil { - return err - } - - log.Trace("User name changed: %s -> %s", u.Name, newUserName) - return nil -} diff --git a/services/user/user.go b/services/user/user.go index 5148f2168d..e0815bd860 100644 --- a/services/user/user.go +++ b/services/user/user.go @@ -6,7 +6,8 @@ package user import ( "context" "fmt" - "io" + "os" + "strings" "time" "code.gitea.io/gitea/models" @@ -17,29 +18,105 @@ import ( repo_model "code.gitea.io/gitea/models/repo" system_model "code.gitea.io/gitea/models/system" user_model "code.gitea.io/gitea/models/user" - "code.gitea.io/gitea/modules/avatar" "code.gitea.io/gitea/modules/eventsource" "code.gitea.io/gitea/modules/log" "code.gitea.io/gitea/modules/setting" "code.gitea.io/gitea/modules/storage" "code.gitea.io/gitea/modules/util" + "code.gitea.io/gitea/services/agit" "code.gitea.io/gitea/services/packages" + container_service "code.gitea.io/gitea/services/packages/container" ) // RenameUser renames a user func RenameUser(ctx context.Context, u *user_model.User, newUserName string) error { + // Non-local users are not allowed to change their username. + if !u.IsOrganization() && !u.IsLocal() { + return user_model.ErrUserIsNotLocal{ + UID: u.ID, + Name: u.Name, + } + } + + if newUserName == u.Name { + return user_model.ErrUsernameNotChanged{ + UID: u.ID, + Name: u.Name, + } + } + + if err := user_model.IsUsableUsername(newUserName); err != nil { + return err + } + + onlyCapitalization := strings.EqualFold(newUserName, u.Name) + oldUserName := u.Name + + if onlyCapitalization { + u.Name = newUserName + if err := user_model.UpdateUserCols(ctx, u, "name"); err != nil { + u.Name = oldUserName + return err + } + return nil + } + ctx, committer, err := db.TxContext(ctx) if err != nil { return err } defer committer.Close() - if err := renameUser(ctx, u, newUserName); err != nil { + + isExist, err := user_model.IsUserExist(ctx, u.ID, newUserName) + if err != nil { return err } - if err := committer.Commit(); err != nil { + if isExist { + return user_model.ErrUserAlreadyExist{ + Name: newUserName, + } + } + + if err = repo_model.UpdateRepositoryOwnerName(ctx, oldUserName, newUserName); err != nil { + return err + } + + if err = user_model.NewUserRedirect(ctx, u.ID, oldUserName, newUserName); err != nil { + return err + } + + if err := agit.UserNameChanged(ctx, u, newUserName); err != nil { + return err + } + if err := container_service.UpdateRepositoryNames(ctx, u, newUserName); err != nil { + return err + } + + u.Name = newUserName + u.LowerName = strings.ToLower(newUserName) + if err := user_model.UpdateUserCols(ctx, u, "name", "lower_name"); err != nil { + u.Name = oldUserName + u.LowerName = strings.ToLower(oldUserName) return err } - return err + + // Do not fail if directory does not exist + if err = util.Rename(user_model.UserPath(oldUserName), user_model.UserPath(newUserName)); err != nil && !os.IsNotExist(err) { + u.Name = oldUserName + u.LowerName = strings.ToLower(oldUserName) + return fmt.Errorf("rename user directory: %w", err) + } + + if err = committer.Commit(); err != nil { + u.Name = oldUserName + u.LowerName = strings.ToLower(oldUserName) + if err2 := util.Rename(user_model.UserPath(newUserName), user_model.UserPath(oldUserName)); err2 != nil && !os.IsNotExist(err2) { + 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) + 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) + } + return err + } + return nil } // DeleteUser completely and permanently deletes everything of a user, @@ -240,50 +317,3 @@ func DeleteInactiveUsers(ctx context.Context, olderThan time.Duration) error { return user_model.DeleteInactiveEmailAddresses(ctx) } - -// UploadAvatar saves custom avatar for user. -func UploadAvatar(u *user_model.User, data []byte) error { - avatarData, err := avatar.ProcessAvatarImage(data) - if err != nil { - return err - } - - ctx, committer, err := db.TxContext(db.DefaultContext) - if err != nil { - return err - } - defer committer.Close() - - u.UseCustomAvatar = true - u.Avatar = avatar.HashAvatar(u.ID, data) - if err = user_model.UpdateUserCols(ctx, u, "use_custom_avatar", "avatar"); err != nil { - return fmt.Errorf("updateUser: %w", err) - } - - if err := storage.SaveFrom(storage.Avatars, u.CustomAvatarRelativePath(), func(w io.Writer) error { - _, err := w.Write(avatarData) - return err - }); err != nil { - return fmt.Errorf("Failed to create dir %s: %w", u.CustomAvatarRelativePath(), err) - } - - return committer.Commit() -} - -// DeleteAvatar deletes the user's custom avatar. -func DeleteAvatar(u *user_model.User) error { - aPath := u.CustomAvatarRelativePath() - log.Trace("DeleteAvatar[%d]: %s", u.ID, aPath) - if len(u.Avatar) > 0 { - if err := storage.Avatars.Delete(aPath); err != nil { - return fmt.Errorf("Failed to remove %s: %w", aPath, err) - } - } - - u.UseCustomAvatar = false - u.Avatar = "" - if _, err := db.GetEngine(db.DefaultContext).ID(u.ID).Cols("avatar, use_custom_avatar").Update(u); err != nil { - return fmt.Errorf("UpdateUser: %w", err) - } - return nil -} |