Skip to content

Create Admin Command: New Flags #5749

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Open
wants to merge 3 commits into
base: development
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
116 changes: 94 additions & 22 deletions app/Console/Commands/CreateAdminCommand.php
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,6 @@
use Illuminate\Support\Facades\Validator;
use Illuminate\Support\Str;
use Illuminate\Validation\Rules\Password;
use Illuminate\Validation\Rules\Unique;

class CreateAdminCommand extends Command
{
Expand All @@ -21,7 +20,9 @@ class CreateAdminCommand extends Command
{--email= : The email address for the new admin user}
{--name= : The name of the new admin user}
{--password= : The password to assign to the new admin user}
{--external-auth-id= : The external authentication system id for the new admin user (SAML2/LDAP/OIDC)}';
{--external-auth-id= : The external authentication system id for the new admin user (SAML2/LDAP/OIDC)}
{--generate-password : Generate a random password for the new admin user}
{--initial : Indicate if this should set/update the details of the initial admin user}';

/**
* The console command description.
Expand All @@ -35,26 +36,12 @@ class CreateAdminCommand extends Command
*/
public function handle(UserRepo $userRepo): int
{
$details = $this->snakeCaseOptions();

if (empty($details['email'])) {
$details['email'] = $this->ask('Please specify an email address for the new admin user');
}

if (empty($details['name'])) {
$details['name'] = $this->ask('Please specify a name for the new admin user');
}

if (empty($details['password'])) {
if (empty($details['external_auth_id'])) {
$details['password'] = $this->ask('Please specify a password for the new admin user (8 characters min)');
} else {
$details['password'] = Str::random(32);
}
}
$initialAdminOnly = $this->option('initial');
$shouldGeneratePassword = $this->option('generate-password');
$details = $this->gatherDetails($shouldGeneratePassword, $initialAdminOnly);

$validator = Validator::make($details, [
'email' => ['required', 'email', 'min:5', new Unique('users', 'email')],
'email' => ['required', 'email', 'min:5'],
'name' => ['required', 'min:2'],
'password' => ['required_without:external_auth_id', Password::default()],
'external_auth_id' => ['required_without:password'],
Expand All @@ -68,16 +55,101 @@ public function handle(UserRepo $userRepo): int
return 1;
}

$adminRole = Role::getSystemRole('admin');

if ($initialAdminOnly) {
$handled = $this->handleInitialAdminIfExists($userRepo, $details, $shouldGeneratePassword, $adminRole);
if ($handled !== null) {
return $handled;
}
}

$emailUsed = $userRepo->getByEmail($details['email']) !== null;
if ($emailUsed) {
$this->error("Could not create admin account.");
$this->error("An account with the email address \"{$details['email']}\" already exists.");
return 1;
}

$user = $userRepo->createWithoutActivity($validator->validated());
$user->attachRole(Role::getSystemRole('admin'));
$user->attachRole($adminRole);
$user->email_confirmed = true;
$user->save();

$this->info("Admin account with email \"{$user->email}\" successfully created!");
if ($shouldGeneratePassword) {
$this->line($details['password']);
} else {
$this->info("Admin account with email \"{$user->email}\" successfully created!");
}

return 0;
}

/**
* Handle updates to the original admin account if it exists.
* Returns an int return status if handled, otherwise returns null if not handled (new user to be created).
*/
protected function handleInitialAdminIfExists(UserRepo $userRepo, array $data, bool $generatePassword, Role $adminRole): int|null
{
$defaultAdmin = $userRepo->getByEmail('[email protected]');
if ($defaultAdmin && $defaultAdmin->hasSystemRole('admin')) {
if ($defaultAdmin->email !== $data['email'] && $userRepo->getByEmail($data['email']) !== null) {
$this->error("Could not create admin account.");
$this->error("An account with the email address \"{$data['email']}\" already exists.");
return 1;
}

$userRepo->updateWithoutActivity($defaultAdmin, $data, true);
if ($generatePassword) {
$this->line($data['password']);
} else {
$this->info("The default admin user has been updated with the provided details!");
}

return 0;
} else if ($adminRole->users()->count() > 0) {
$this->warn('Non-default admin user already exists. Skipping creation of new admin user.');
return 2;
}

return null;
}

protected function gatherDetails(bool $generatePassword, bool $initialAdmin): array
{
$details = $this->snakeCaseOptions();

if (empty($details['email'])) {
if ($initialAdmin) {
$details['email'] = '[email protected]';
} else {
$details['email'] = $this->ask('Please specify an email address for the new admin user');
}
}

if (empty($details['name'])) {
if ($initialAdmin) {
$details['name'] = 'Admin';
} else {
$details['name'] = $this->ask('Please specify a name for the new admin user');
}
}

if (empty($details['password'])) {
if (empty($details['external_auth_id'])) {
if ($generatePassword) {
$details['password'] = Str::random(32);
} else {
$details['password'] = $this->ask('Please specify a password for the new admin user (8 characters min)');
}
} else {
$details['password'] = Str::random(32);
}
}

return $details;
}

protected function snakeCaseOptions(): array
{
$returnOpts = [];
Expand Down
19 changes: 17 additions & 2 deletions app/Users/UserRepo.php
Original file line number Diff line number Diff line change
Expand Up @@ -100,13 +100,13 @@ public function create(array $data, bool $sendInvite = false): User
}

/**
* Update the given user with the given data.
* Update the given user with the given data, but do not create an activity.
*
* @param array{name: ?string, email: ?string, external_auth_id: ?string, password: ?string, roles: ?array<int>, language: ?string} $data
*
* @throws UserUpdateException
*/
public function update(User $user, array $data, bool $manageUsersAllowed): User
public function updateWithoutActivity(User $user, array $data, bool $manageUsersAllowed): User
{
if (!empty($data['name'])) {
$user->name = $data['name'];
Expand Down Expand Up @@ -134,6 +134,21 @@ public function update(User $user, array $data, bool $manageUsersAllowed): User
}

$user->save();

return $user;
}

/**
* Update the given user with the given data.
*
* @param array{name: ?string, email: ?string, external_auth_id: ?string, password: ?string, roles: ?array<int>, language: ?string} $data
*
* @throws UserUpdateException
*/
public function update(User $user, array $data, bool $manageUsersAllowed): User
{
$user = $this->updateWithoutActivity($user, $data, $manageUsersAllowed);

Activity::add(ActivityType::USER_UPDATE, $user);

return $user;
Expand Down
Loading