fix(auth): allow admins to change any username including their own
ci / contract / contract (сгенерированный код совпадает с openapi.yaml) (push) Successful in 1m6s
ci / ts / ts (packages + web) (push) Successful in 2m59s
ci / api / api (push) Successful in 49s
ci / db / db (push) Successful in 1m17s
ci / mobile / mobile (Metro-бандл Android) (push) Successful in 1m25s

This commit is contained in:
mrleo1nid committed 2026-10-21 17:33:19 +03:00
1 parent b0cbd0f149
commit c34272d508
16 files changed
+431 -266

No files matched your search

+18 -1
View File
@@ -150,13 +150,14 @@ func displayName(s string) (*string, error) {
// UserPatch — изменения пользователя из админки. nil — не менять.
type UserPatch struct {
Username *string
DisplayName *string
Role *Role
IsBlocked *bool
ReviewsUploads *bool
}
// UpdateUser меняет имя, роль и блокировку с проверкой прав по каждому изменению.
// UpdateUser меняет логин, имя, роль и блокировку с проверкой прав по каждому изменению.
func (s *Service) UpdateUser(ctx context.Context, p Principal, id uuid.UUID, patch UserPatch) (User, error) {
if patch.Role != nil && !patch.Role.Valid() {
return User{}, apperr.Validation("role", CodeRoleInvalid, "unknown role", nil)
@@ -178,6 +179,19 @@ func (s *Service) UpdateUser(ctx context.Context, p Principal, id uuid.UUID, pat
})
}
if patch.Username != nil {
if p.Role != RoleAdmin {
return forbidden()
}
username := NormalizeUsername(*patch.Username)
if err := ValidateUsername("username", username); err != nil {
return err
}
if username != target.Username {
upd.Username = &username
audit("user.username_change", map[string]any{"from": target.Username, "to": username})
}
}
if patch.DisplayName != nil {
if !CanManageUser(actor, tgt, UserEdit) {
return forbidden()
@@ -247,6 +261,9 @@ func (s *Service) UpdateUser(ctx context.Context, p Principal, id uuid.UUID, pat
}
return nil
})
if errors.Is(err, ErrUsernameTaken) {
return User{}, apperr.Validation("username", CodeUsernameTaken, "username is taken", nil)
}
return out, err
}