diff --git a/app/Console/Commands/CreateAdmin.php b/app/Console/Commands/CreateAdmin.php new file mode 100644 index 000000000..c7a9969e8 --- /dev/null +++ b/app/Console/Commands/CreateAdmin.php @@ -0,0 +1,85 @@ +userRepo = $userRepo; + parent::__construct(); + } + + /** + * Execute the console command. + * + * @return mixed + * @throws \BookStack\Exceptions\NotFoundException + */ + public function handle() + { + $email = trim($this->option('email')); + if (empty($email)) { + $email = $this->ask('Please specify an email address for the new admin user'); + } + if (strlen($email) < 5 || !filter_var($email, FILTER_VALIDATE_EMAIL)) { + return $this->error('Invalid email address provided'); + } + + if ($this->userRepo->getByEmail($email) !== null) { + return $this->error('A user with the provided email already exists!'); + } + + $name = trim($this->option('name')); + if (empty($name)) { + $name = $this->ask('Please specify an name for the new admin user'); + } + if (strlen($name) < 2) { + return $this->error('Invalid name provided'); + } + + $password = trim($this->option('password')); + if (empty($password)) { + $password = $this->secret('Please specify a password for the new admin user'); + } + if (strlen($password) < 5) { + return $this->error('Invalid password provided, Must be at least 5 characters'); + } + + + $user = $this->userRepo->create(['email' => $email, 'name' => $name, 'password' => $password]); + $this->userRepo->attachSystemRole($user, 'admin'); + $this->userRepo->downloadGravatarToUserAvatar($user); + $user->email_confirmed = true; + $user->save(); + + $this->info("Admin account with email \"{$user->email}\" successfully created!"); + } +} diff --git a/app/Http/Controllers/UserController.php b/app/Http/Controllers/UserController.php index 7aa7b31d2..d50baa86f 100644 --- a/app/Http/Controllers/UserController.php +++ b/app/Http/Controllers/UserController.php @@ -93,16 +93,7 @@ class UserController extends Controller $user->roles()->sync($roles); } - // Get avatar from gravatar and save - if (!config('services.disable_services')) { - try { - $avatar = \Images::saveUserGravatar($user); - $user->avatar()->associate($avatar); - $user->save(); - } catch (Exception $e) { - \Log::error('Failed to save user gravatar image'); - } - } + $this->userRepo->downloadGravatarToUserAvatar($user); return redirect('/settings/users'); } diff --git a/app/Repos/UserRepo.php b/app/Repos/UserRepo.php index d329c307c..3cfd61d27 100644 --- a/app/Repos/UserRepo.php +++ b/app/Repos/UserRepo.php @@ -1,6 +1,7 @@ attachDefaultRole($user); // Get avatar from gravatar and save - if (!config('services.disable_services')) { - try { - $avatar = Images::saveUserGravatar($user); - $user->avatar()->associate($avatar); - $user->save(); - } catch (Exception $e) { - $user->save(); - \Log::error('Failed to save user gravatar image'); - } - } + $this->downloadGravatarToUserAvatar($user); return $user; } @@ -113,6 +105,21 @@ class UserRepo $user->attachRoleId($roleId); } + /** + * Assign a user to a system-level role. + * @param User $user + * @param $systemRoleName + * @throws NotFoundException + */ + public function attachSystemRole(User $user, $systemRoleName) + { + $role = $this->role->newQuery()->where('system_name', '=', $systemRoleName)->first(); + if ($role === null) { + throw new NotFoundException("Role '{$systemRoleName}' not found"); + } + $user->attachRole($role); + } + /** * Checks if the give user is the only admin. * @param User $user @@ -228,4 +235,28 @@ class UserRepo { return $this->role->where('system_name', '!=', 'admin')->get(); } + + /** + * Get a gravatar image for a user and set it as their avatar. + * Does not run if gravatar disabled in config. + * @param User $user + * @return bool + */ + public function downloadGravatarToUserAvatar(User $user) + { + // Get avatar from gravatar and save + if (!config('services.gravatar')) { + return false; + } + + try { + $avatar = Images::saveUserGravatar($user); + $user->avatar()->associate($avatar); + $user->save(); + return true; + } catch (Exception $e) { + \Log::error('Failed to save user gravatar image'); + return false; + } + } } diff --git a/config/services.php b/config/services.php index 8695ea91c..18649c093 100644 --- a/config/services.php +++ b/config/services.php @@ -16,6 +16,7 @@ return [ // Single option to disable non-auth external services such as Gravatar and Draw.io 'disable_services' => env('DISABLE_EXTERNAL_SERVICES', false), + 'gravatar' => env('GRAVATAR', !env('DISABLE_EXTERNAL_SERVICES', false)), 'drawio' => env('DRAWIO', !env('DISABLE_EXTERNAL_SERVICES', false)), diff --git a/tests/CommandsTest.php b/tests/CommandsTest.php index 5df82ee51..25516c317 100644 --- a/tests/CommandsTest.php +++ b/tests/CommandsTest.php @@ -3,6 +3,7 @@ use BookStack\JointPermission; use BookStack\Page; use BookStack\Repos\EntityRepo; +use BookStack\User; class CommandsTest extends TestCase { @@ -99,4 +100,22 @@ class CommandsTest extends TestCase $this->assertDatabaseHas('joint_permissions', ['entity_id' => $page->id]); } + + public function test_add_admin_command() + { + $exitCode = \Artisan::call('bookstack:create-admin', [ + '--email' => '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::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'); + } }