mirror of
https://github.com/projectsend/projectsend.git
synced 2026-09-17 00:55:07 +00:00
6560346280
The last two paths that passed email_verified_at into User::create() and lost it: the setup screen, and projectsend:admin for a container that comes up from environment variables. It is deliberately absent from $fillable, so mass assignment drops it without a word, and both meant to set it. The intent is plain in both cases — the first administrator typed their own address into the form in front of them, and whoever provisioned the container supplied it themselves. There is nobody to confirm it to. Inert today, since MustVerifyEmail is not enabled on the model, but the column is what a later switch would read: turning verification on would have locked out the one account that cannot be helped by another administrator. Both are now pinned by a test that fails when the forceFill is removed. StaffAccounts had already fixed this for staff and named the rest; with client accounts done earlier today, that list is empty. Also says on User::$fillable what absence from it buys and what it does not. It stops a request smuggling a value in; it does not tell code that meant to set the value that it failed. Four separate paths made the same mistake against the same comment.
93 lines
3.4 KiB
PHP
93 lines
3.4 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,
|
|
]);
|
|
|
|
// forceFill, for the reason SetupController gives beside it:
|
|
// email_verified_at is not in User::$fillable, so passing it into
|
|
// create() lost it without a word. Whoever provisioned this
|
|
// container supplied the address themselves.
|
|
$user->forceFill(['email_verified_at' => now()])->save();
|
|
|
|
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;
|
|
}
|
|
}
|