mirror of
https://github.com/projectsend/projectsend.git
synced 2026-09-17 09:05:08 +00:00
4806b81dc3
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.
88 lines
3.1 KiB
PHP
88 lines
3.1 KiB
PHP
<?php
|
|
|
|
declare(strict_types=1);
|
|
|
|
namespace App\Modules\Identity\Console;
|
|
|
|
use App\Models\User;
|
|
use App\Modules\Audit\Action;
|
|
use App\Modules\Audit\ActivityLogger;
|
|
use App\Modules\Identity\Erasure\AvailableEmailRule;
|
|
use App\Modules\Identity\Models\Role;
|
|
use App\Modules\Identity\Permissions\SystemRole;
|
|
use App\Modules\Identity\UserType;
|
|
use App\Modules\Platform\Onboarding\InstallationWelcome;
|
|
use App\Modules\Platform\Settings\Setting;
|
|
use App\Modules\Platform\Settings\Settings;
|
|
use Illuminate\Console\Command;
|
|
use Illuminate\Support\Facades\Validator;
|
|
use Illuminate\Validation\Rules\Password;
|
|
|
|
class CreateAdminCommand extends Command
|
|
{
|
|
protected $signature = 'projectsend:admin
|
|
{--name= : Full name of the administrator}
|
|
{--email= : Email address (used to log in)}
|
|
{--password= : Password (prompted interactively when omitted)}
|
|
{--if-none : Do nothing when a staff user already exists (idempotent provisioning)}';
|
|
|
|
protected $description = 'Create a staff administrator account';
|
|
|
|
public function handle(): int
|
|
{
|
|
if ($this->option('if-none') && User::query()->where('type', UserType::Staff)->exists()) {
|
|
$this->info('A staff user already exists; nothing to do.');
|
|
|
|
return self::SUCCESS;
|
|
}
|
|
|
|
$name = $this->option('name') ?? $this->ask('Name');
|
|
$email = $this->option('email') ?? $this->ask('Email address');
|
|
$password = $this->option('password') ?? $this->secret('Password');
|
|
|
|
$validator = Validator::make(
|
|
['name' => $name, 'email' => $email, 'password' => $password],
|
|
[
|
|
'name' => ['required', 'string', 'max:255'],
|
|
'email' => ['required', 'string', 'email', 'max:255', new AvailableEmailRule],
|
|
'password' => ['required', Password::defaults()],
|
|
],
|
|
);
|
|
|
|
if ($validator->fails()) {
|
|
foreach ($validator->errors()->all() as $error) {
|
|
$this->error($error);
|
|
}
|
|
|
|
return self::FAILURE;
|
|
}
|
|
|
|
$user = User::create([
|
|
'type' => UserType::Staff,
|
|
'active' => true,
|
|
'role_id' => Role::query()->where('name', SystemRole::SystemAdministrator->value)->value('id'),
|
|
'name' => $name,
|
|
'email' => $email,
|
|
'password' => $password,
|
|
'email_verified_at' => now(),
|
|
]);
|
|
|
|
app(ActivityLogger::class)->log(Action::UserCreated, null, $user);
|
|
|
|
$settings = app(Settings::class);
|
|
if ($settings->get(Setting::AdminNotificationEmails) === []) {
|
|
$settings->set(Setting::AdminNotificationEmails, [$user->email]);
|
|
}
|
|
|
|
// Unattended provisioning skips the setup screen entirely, so this
|
|
// is the only place that can record "somebody just installed this"
|
|
// for a container that came up from environment variables. They
|
|
// still deserve showing around on their first visit.
|
|
app(InstallationWelcome::class)->raise();
|
|
|
|
$this->info("Administrator {$user->email} created.");
|
|
|
|
return self::SUCCESS;
|
|
}
|
|
}
|