diff --git a/app/Console/Commands/CreateAdmin.php b/app/Console/Commands/CreateAdmin.php index 8c273bc1f..e7aff3f92 100644 --- a/app/Console/Commands/CreateAdmin.php +++ b/app/Console/Commands/CreateAdmin.php @@ -3,8 +3,10 @@ namespace BookStack\Console\Commands; use BookStack\Auth\UserRepo; +use BookStack\Exceptions\NotFoundException; use Illuminate\Console\Command; use Illuminate\Support\Facades\Validator; +use Illuminate\Support\Str; use Illuminate\Validation\Rules\Password; use Illuminate\Validation\Rules\Unique; use Symfony\Component\Console\Command\Command as SymfonyCommand; @@ -19,7 +21,8 @@ class CreateAdmin extends Command protected $signature = 'bookstack:create-admin {--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}'; + {--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)}'; /** * The console command description. @@ -42,28 +45,35 @@ class CreateAdmin extends Command /** * Execute the console command. * - * @throws \BookStack\Exceptions\NotFoundException + * @throws NotFoundException * * @return mixed */ public function handle() { - $details = $this->options(); + $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'])) { - $details['password'] = $this->ask('Please specify a password for the new admin user (8 characters min)'); + 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); + } } $validator = Validator::make($details, [ - 'email' => ['required', 'email', 'min:5', new Unique('users', 'email')], - 'name' => ['required', 'min:2'], - 'password' => ['required', Password::default()], + 'email' => ['required', 'email', 'min:5', new Unique('users', 'email')], + 'name' => ['required', 'min:2'], + 'password' => ['required_without:external_auth_id', Password::default()], + 'external_auth_id' => ['required_without:password'], ]); if ($validator->fails()) { @@ -84,4 +94,13 @@ class CreateAdmin extends Command return SymfonyCommand::SUCCESS; } + + protected function snakeCaseOptions(): array + { + $returnOpts = []; + foreach ($this->options() as $key => $value) { + $returnOpts[str_replace('-', '_', $key)] = $value; + } + return $returnOpts; + } } diff --git a/tests/Commands/AddAdminCommandTest.php b/tests/Commands/AddAdminCommandTest.php deleted file mode 100644 index 0f144246c..000000000 --- a/tests/Commands/AddAdminCommandTest.php +++ /dev/null @@ -1,27 +0,0 @@ - 'admintest@example.com', - '--name' => 'Admin Test', - '--password' => 'testing-4', - ]); - $this->assertTrue($exitCode === 0, 'Command executed successfully'); - - $this->assertDatabaseHas('users', [ - 'email' => 'admintest@example.com', - 'name' => 'Admin Test', - ]); - - $this->assertTrue(User::query()->where('email', '=', 'admintest@example.com')->first()->hasSystemRole('admin'), 'User has admin role as expected'); - $this->assertTrue(\Auth::attempt(['email' => 'admintest@example.com', 'password' => 'testing-4']), 'Password stored as expected'); - } -} diff --git a/tests/Commands/CreateAdminCommandTest.php b/tests/Commands/CreateAdminCommandTest.php new file mode 100644 index 000000000..9aa4b8e38 --- /dev/null +++ b/tests/Commands/CreateAdminCommandTest.php @@ -0,0 +1,63 @@ +artisan('bookstack:create-admin', [ + '--email' => 'admintest@example.com', + '--name' => 'Admin Test', + '--password' => 'testing-4', + ])->assertExitCode(0); + + $this->assertDatabaseHas('users', [ + 'email' => 'admintest@example.com', + 'name' => 'Admin Test', + ]); + + /** @var User $user */ + $user = User::query()->where('email', '=', 'admintest@example.com')->first(); + $this->assertTrue($user->hasSystemRole('admin')); + $this->assertTrue(Auth::attempt(['email' => 'admintest@example.com', 'password' => 'testing-4'])); + } + + public function test_providing_external_auth_id() + { + $this->artisan('bookstack:create-admin', [ + '--email' => 'admintest@example.com', + '--name' => 'Admin Test', + '--external-auth-id' => 'xX_admin_Xx', + ])->assertExitCode(0); + + $this->assertDatabaseHas('users', [ + 'email' => 'admintest@example.com', + 'name' => 'Admin Test', + 'external_auth_id' => 'xX_admin_Xx', + ]); + + /** @var User $user */ + $user = User::query()->where('email', '=', 'admintest@example.com')->first(); + $this->assertNotEmpty($user->password); + } + + public function test_password_required_if_external_auth_id_not_given() + { + $this->artisan('bookstack:create-admin', [ + '--email' => 'admintest@example.com', + '--name' => 'Admin Test', + ])->expectsQuestion('Please specify a password for the new admin user (8 characters min)', 'hunter2000') + ->assertExitCode(0); + + $this->assertDatabaseHas('users', [ + 'email' => 'admintest@example.com', + 'name' => 'Admin Test', + ]); + $this->assertTrue(Auth::attempt(['email' => 'admintest@example.com', 'password' => 'hunter2000'])); + } +}