Files
denkfabrik-li 4806b81dc3 Let a deleted account's email address come back into use
An account deleted by an administrator was soft-deleted with erase_after
null, so projectsend:purge-erasures — which filters on
whereNotNull('erase_after') — never reached it, and the unique index on
users.email kept the address reserved forever. Anyone re-creating the
account got "The email has already been taken", naming a conflict nothing
on any screen could show or clear (#1648).

Both halves of the issue's option 3:

Every deletion path now schedules the erasure. The stamp lives in
ErasureSchedule — self-deletion switched to it, and StaffAccounts::delete
(shared by the web screen and the API) and both client controllers call
it right before delete(). Same grace period, same purge, whoever deleted
the account. Deliberately no backfill for rows deleted before this
change: stamping them during an update would start a countdown to data
erasure that nobody chose at deletion time; the message below covers
them instead.

The staff creation paths swap unique:users,email for AvailableEmailRule,
which refuses exactly the same things but can explain the one refusal
the stock message can't: an address held by a deleted account now names
the date it becomes available, and one deleted before scheduling existed
points at projectsend:erase-account. A living account keeps the stock
message, and public registration keeps the stock rule — telling an
anonymous visitor the address belongs to a deleted account would confirm
it had an account here.
2026-08-26 04:04:48 +02:00

35 lines
1.0 KiB
PHP

<?php
declare(strict_types=1);
namespace App\Modules\Identity\Erasure;
use App\Models\User;
use App\Modules\Platform\Settings\Setting;
use App\Modules\Platform\Settings\Settings;
/**
* Stamps the moment a soft-deleted account graduates to permanent
* erasure: now plus the installation's grace period.
*
* Every deletion path calls this right before delete(), self-service and
* administrative alike, so PurgeErasuresCommand eventually reaches every
* deleted account — and the email address its row keeps reserved under
* the unique index is freed. Only self-deletion did this at first, which
* left admin-deleted accounts trashed forever and their addresses
* unusable (#1648).
*/
class ErasureSchedule
{
public function __construct(
private readonly Settings $settings,
) {}
public function apply(User $user): void
{
$graceDays = (int) $this->settings->get(Setting::AccountErasureGraceDays);
$user->forceFill(['erase_after' => now()->addDays($graceDays)])->save();
}
}