package useCases import ( "RenewCMS/internal/domain/audit" "RenewCMS/internal/domain/role" "RenewCMS/internal/domain/user" "context" "errors" ) type TransferOwnershipUseCase struct { users user.Repository roles role.Repository recorder audit.Recorder } type TransferOwnershipCommand struct { Caller ContentCaller UserID uint32 ConfirmEmail string } type OwnershipSuccessor struct { ID uint32 `json:"id"` Name string `json:"name"` } type OwnershipPredecessor struct { ID uint32 `json:"id"` Name string `json:"name"` Role RoleRef `json:"role"` } type OwnershipTransfer struct { Owner OwnershipSuccessor `json:"owner"` FormerOwner OwnershipPredecessor `json:"former_owner"` } func NewTransferOwnershipUseCase(users user.Repository, roles role.Repository, recorder audit.Recorder) *TransferOwnershipUseCase { return &TransferOwnershipUseCase{users: users, roles: roles, recorder: recorder} } var transferRefusals = []error{user.ErrRoleChanged, user.ErrInvalidTransfer, user.ErrUserNotFound} func (u *TransferOwnershipUseCase) Transfer(ctx context.Context, command TransferOwnershipCommand) (OwnershipTransfer, error) { successor, err := u.successor(command) if err != nil { return OwnershipTransfer{}, err } former, err := u.users.Get(command.Caller.UserID) if err != nil { return OwnershipTransfer{}, err } ownerRole, err := builtInRole(u.roles, role.OwnerName) if err != nil { return OwnershipTransfer{}, err } adminRole, err := builtInRole(u.roles, role.AdminName) if err != nil { return OwnershipTransfer{}, err } event := audit.NewEvent(audit.ActionUserTransferOwnership, audit.CategoryUsers) event.Security = true event.Target = audit.NewTarget(audit.TargetUser, successor.ID, successor.Username) if err := u.users.TransferOwnership(former.ID, successor.ID, ownerRole.ID, adminRole.ID); err != nil { u.recordWriteFailure(ctx, event, err) return OwnershipTransfer{}, err } event.Params[audit.ParamFrom] = former.ID event.Params[audit.ParamTo] = successor.ID u.recorder.Record(ctx, event) return OwnershipTransfer{ Owner: OwnershipSuccessor{ID: successor.ID, Name: successor.NameOrUsername()}, FormerOwner: OwnershipPredecessor{ID: former.ID, Name: former.NameOrUsername(), Role: NewRoleRef(adminRole)}, }, nil } func (u *TransferOwnershipUseCase) successor(command TransferOwnershipCommand) (user.User, error) { successor, err := u.users.Get(command.UserID) if errors.Is(err, user.ErrUserNotFound) { return user.User{}, user.ErrInvalidTransfer } if err != nil { return user.User{}, err } if successor.ID == command.Caller.UserID || !successor.IsVerified { return user.User{}, user.ErrInvalidTransfer } if successor.Email != command.ConfirmEmail { return user.User{}, user.ErrConfirmationMismatch } return successor, nil } func (u *TransferOwnershipUseCase) recordWriteFailure(ctx context.Context, event audit.Event, err error) { for _, refusal := range transferRefusals { if errors.Is(err, refusal) { return } } recordFailure(ctx, u.recorder, event, err) }