refac: DELETE /v1/user/{id}?expected_role_id=N, user.remove audit event, 409 target_role_not_found, GetAll returns its error, Own-scope visibility filter moved into repos, JSON tags removed from use-case types

This commit is contained in:
Florian Sylvain committed 2026-10-29 16:28:25 +01:00
1 parent 5d6f378fce
commit b39a41be88
124 files changed
+8816 -523

No files matched your search

@@ -34,7 +34,8 @@ func (u *ReassignRoleMembersUseCase) Reassign(ctx context.Context, command Reass
if err := u.checkSource(command.Caller, source); err != nil {
return 0, err
}
if err := u.checkTarget(command, source); err != nil {
target, err := u.checkTarget(command, source)
if err != nil {
return 0, err
}
@@ -42,6 +43,7 @@ func (u *ReassignRoleMembersUseCase) Reassign(ctx context.Context, command Reass
event.Target = roleTarget(source)
event.Params[audit.ParamFrom] = source.ID
event.Params[audit.ParamTo] = command.TargetID
event.Params[audit.ParamToName] = target.Name
event.Params[audit.ParamMoved] = 0
moved, err := u.roles.ReassignMembers(source.ID, command.TargetID)
if err != nil {
@@ -68,19 +70,22 @@ func (u *ReassignRoleMembersUseCase) checkSource(caller ContentCaller, source ro
return ensureCovered(caller, u.contentTypes, source)
}
func (u *ReassignRoleMembersUseCase) checkTarget(command ReassignRoleMembersCommand, source role.Role) error {
func (u *ReassignRoleMembersUseCase) checkTarget(command ReassignRoleMembersCommand, source role.Role) (role.Role, error) {
if command.TargetID == 0 || command.TargetID == source.ID {
return role.ErrInvalidReassign
return role.Role{}, role.ErrInvalidReassign
}
target, err := u.roles.Get(command.TargetID)
if errors.Is(err, role.ErrRoleNotFound) {
return role.ErrInvalidReassign
return role.Role{}, role.ErrReassignTargetNotFound
}
if err != nil {
return err
return role.Role{}, err
}
if target.Locked {
return role.ErrInvalidReassign
return role.Role{}, role.ErrInvalidReassign
}
return ensureCovered(command.Caller, u.contentTypes, target)
if err := ensureCovered(command.Caller, u.contentTypes, target); err != nil {
return role.Role{}, err
}
return target, nil
}
@@ -0,0 +1,79 @@
package useCases
import (
"RenewCMS/internal/domain/audit"
"RenewCMS/internal/domain/contenttype"
"RenewCMS/internal/domain/role"
"RenewCMS/internal/domain/user"
"context"
"errors"
)
type RemoveUserUseCase struct {
users user.Repository
roles role.Repository
contentTypes contenttype.Repository
recorder audit.Recorder
}
type RemoveUserCommand struct {
Caller ContentCaller
TargetID uint32
ExpectedRoleID uint32
}
func NewRemoveUserUseCase(users user.Repository, roles role.Repository, contentTypes contenttype.Repository, recorder audit.Recorder) *RemoveUserUseCase {
return &RemoveUserUseCase{users: users, roles: roles, contentTypes: contentTypes, recorder: recorder}
}
func (u *RemoveUserUseCase) Remove(ctx context.Context, command RemoveUserCommand) error {
if command.ExpectedRoleID == 0 {
return role.ErrUnknownRole
}
target, err := u.users.Get(command.TargetID)
if err != nil {
return err
}
if err := u.check(command, target); err != nil {
return err
}
event := audit.NewEvent(audit.ActionUserRemove, audit.CategoryUsers)
event.Target = audit.NewTarget(audit.TargetUser, target.ID, target.Username)
if err := u.users.DeleteIfRole(target.ID, command.ExpectedRoleID); err != nil {
u.recordWriteFailure(ctx, event, err)
return err
}
u.recorder.Record(ctx, event)
return nil
}
func (u *RemoveUserUseCase) check(command RemoveUserCommand, target user.User) error {
if target.ID == command.Caller.UserID {
return role.ErrOwnRole
}
if target.RoleID != command.ExpectedRoleID {
return user.ErrRoleChanged
}
held, err := resolveRole(u.roles, target)
if errors.Is(err, role.ErrRoleNotFound) {
return nil
}
if err != nil {
return err
}
if held.Locked {
return role.ErrRoleLocked
}
typeKeys, err := allowedTypeKeys(u.contentTypes)
if err != nil {
return err
}
return ensureCoveredBy(command.Caller, typeKeys, held)
}
func (u *RemoveUserUseCase) recordWriteFailure(ctx context.Context, event audit.Event, err error) {
if errors.Is(err, user.ErrRoleChanged) || errors.Is(err, user.ErrUserNotFound) {
return
}
recordFailure(ctx, u.recorder, event, err)
}