Extract AdminUser class that enforces invariants

This commit is contained in:
Franz Liedke 2019-01-26 01:13:38 +01:00
parent cf56157ec7
commit 1e056b2c50
8 changed files with 104 additions and 45 deletions

View File

@ -0,0 +1,58 @@
<?php
/*
* This file is part of Flarum.
*
* (c) Toby Zerner <toby.zerner@gmail.com>
*
* For the full copyright and license information, please view the LICENSE
* file that was distributed with this source code.
*/
namespace Flarum\Install;
use Carbon\Carbon;
use Illuminate\Hashing\BcryptHasher;
class AdminUser
{
private $username;
private $password;
private $email;
public function __construct($username, $password, $email)
{
$this->username = $username;
$this->password = $password;
$this->email = $email;
$this->validate();
}
public function getUsername()
{
return $this->username;
}
public function getAttributes(): array
{
return [
'username' => $this->username,
'email' => $this->email,
'password' => (new BcryptHasher)->make($this->password),
'joined_at' => Carbon::now(),
'is_email_confirmed' => 1,
];
}
private function validate()
{
if (! filter_var($this->email, FILTER_VALIDATE_EMAIL)) {
throw new ValidationFailed('You must enter a valid email.');
}
if (! $this->username || preg_match('/[^a-z0-9_-]/i', $this->username)) {
throw new ValidationFailed('Username can only contain letters, numbers, underscores, and dashes.');
}
}
}

View File

@ -11,13 +11,15 @@
namespace Flarum\Install\Console; namespace Flarum\Install\Console;
use Flarum\Install\AdminUser;
interface DataProviderInterface interface DataProviderInterface
{ {
public function getDatabaseConfiguration(); public function getDatabaseConfiguration();
public function getBaseUrl(); public function getBaseUrl();
public function getAdminUser(); public function getAdminUser(): AdminUser;
public function getSettings(); public function getSettings();

View File

@ -12,6 +12,7 @@
namespace Flarum\Install\Console; namespace Flarum\Install\Console;
use Exception; use Exception;
use Flarum\Install\AdminUser;
use Symfony\Component\Console\Input\InputInterface; use Symfony\Component\Console\Input\InputInterface;
use Symfony\Component\Yaml\Yaml; use Symfony\Component\Yaml\Yaml;
@ -72,13 +73,13 @@ class FileDataProvider implements DataProviderInterface
return $this->baseUrl ?? 'http://flarum.local'; return $this->baseUrl ?? 'http://flarum.local';
} }
public function getAdminUser() public function getAdminUser(): AdminUser
{ {
return $this->adminUser + [ return new AdminUser(
'username' => 'admin', $this->adminUser['username'] ?? 'admin',
'password' => 'password', $this->adminUser['password'] ?? 'password',
'email' => 'admin@example.com', $this->adminUser['email'] ?? 'admin@example.com'
]; );
} }
public function getSettings() public function getSettings()

View File

@ -118,24 +118,13 @@ class InstallCommand extends AbstractCommand
$validation->getMessageBag()->toArray()))); $validation->getMessageBag()->toArray())));
} }
$admin = $this->dataSource->getAdminUser();
if (! filter_var($admin['email'], FILTER_VALIDATE_EMAIL)) {
throw new Exception('You must enter a valid email.');
}
if (! $admin['username'] || preg_match('/[^a-z0-9_-]/i',
$admin['username'])) {
throw new Exception('Username can only contain letters, numbers, underscores, and dashes.');
}
$this->runPipeline( $this->runPipeline(
$this->installation $this->installation
->configPath($this->input->getOption('config')) ->configPath($this->input->getOption('config'))
->debugMode($this->dataSource->isDebugMode()) ->debugMode($this->dataSource->isDebugMode())
->baseUrl($this->dataSource->getBaseUrl()) ->baseUrl($this->dataSource->getBaseUrl())
->databaseConfig($dbConfig) ->databaseConfig($dbConfig)
->adminUser($admin) ->adminUser($this->dataSource->getAdminUser())
->settings($this->dataSource->getSettings()) ->settings($this->dataSource->getSettings())
->build() ->build()
); );

View File

@ -11,6 +11,7 @@
namespace Flarum\Install\Console; namespace Flarum\Install\Console;
use Flarum\Install\AdminUser;
use Symfony\Component\Console\Helper\QuestionHelper; use Symfony\Component\Console\Helper\QuestionHelper;
use Symfony\Component\Console\Input\InputInterface; use Symfony\Component\Console\Input\InputInterface;
use Symfony\Component\Console\Output\OutputInterface; use Symfony\Component\Console\Output\OutputInterface;
@ -61,13 +62,13 @@ class UserDataProvider implements DataProviderInterface
return $this->baseUrl = rtrim($this->ask('Base URL:'), '/'); return $this->baseUrl = rtrim($this->ask('Base URL:'), '/');
} }
public function getAdminUser() public function getAdminUser(): AdminUser
{ {
return [ return new AdminUser(
'username' => $this->ask('Admin username:'), $this->ask('Admin username:'),
'password' => $this->askForAdminPassword(), $this->askForAdminPassword(),
'email' => $this->ask('Admin email address:'), $this->ask('Admin email address:')
]; );
} }
private function askForAdminPassword() private function askForAdminPassword()

View File

@ -12,6 +12,7 @@
namespace Flarum\Install\Controller; namespace Flarum\Install\Controller;
use Flarum\Http\SessionAuthenticator; use Flarum\Http\SessionAuthenticator;
use Flarum\Install\AdminUser;
use Flarum\Install\Installation; use Flarum\Install\Installation;
use Flarum\Install\StepFailed; use Flarum\Install\StepFailed;
use Flarum\Install\ValidationFailed; use Flarum\Install\ValidationFailed;
@ -75,11 +76,7 @@ class InstallController implements RequestHandlerInterface
'prefix' => array_get($input, 'tablePrefix'), 'prefix' => array_get($input, 'tablePrefix'),
'strict' => false, 'strict' => false,
]) ])
->adminUser([ ->adminUser($this->makeAdminUser($input))
'username' => array_get($input, 'adminUsername'),
'password' => $this->getConfirmedAdminPassword($input),
'email' => array_get($input, 'adminEmail'),
])
->settings([ ->settings([
'forum_title' => array_get($input, 'forumTitle'), 'forum_title' => array_get($input, 'forumTitle'),
'mail_from' => 'noreply@'.preg_replace('/^www\./i', '', parse_url($baseUrl, PHP_URL_HOST)), 'mail_from' => 'noreply@'.preg_replace('/^www\./i', '', parse_url($baseUrl, PHP_URL_HOST)),
@ -102,7 +99,21 @@ class InstallController implements RequestHandlerInterface
return new Response\EmptyResponse; return new Response\EmptyResponse;
} }
private function getConfirmedAdminPassword(array $input) /**
* @param array $input
* @return AdminUser
* @throws ValidationFailed
*/
private function makeAdminUser(array $input): AdminUser
{
return new AdminUser(
array_get($input, 'adminUsername'),
$this->getConfirmedAdminPassword($input),
array_get($input, 'adminEmail')
);
}
private function getConfirmedAdminPassword(array $input): string
{ {
$password = array_get($input, 'adminPassword'); $password = array_get($input, 'adminPassword');
$confirmation = array_get($input, 'adminPasswordConfirmation'); $confirmation = array_get($input, 'adminPasswordConfirmation');

View File

@ -22,7 +22,9 @@ class Installation
private $dbConfig = []; private $dbConfig = [];
private $baseUrl; private $baseUrl;
private $customSettings = []; private $customSettings = [];
private $adminUser = [];
/** @var AdminUser */
private $adminUser;
// A few instance variables to persist objects between steps. // A few instance variables to persist objects between steps.
// Could also be local variables in build(), but this way // Could also be local variables in build(), but this way
@ -73,7 +75,7 @@ class Installation
return $this; return $this;
} }
public function adminUser($admin) public function adminUser(AdminUser $admin)
{ {
$this->adminUser = $admin; $this->adminUser = $admin;

View File

@ -11,11 +11,10 @@
namespace Flarum\Install\Steps; namespace Flarum\Install\Steps;
use Carbon\Carbon;
use Flarum\Group\Group; use Flarum\Group\Group;
use Flarum\Install\AdminUser;
use Flarum\Install\Step; use Flarum\Install\Step;
use Illuminate\Database\ConnectionInterface; use Illuminate\Database\ConnectionInterface;
use Illuminate\Hashing\BcryptHasher;
class CreateAdminUser implements Step class CreateAdminUser implements Step
{ {
@ -25,11 +24,11 @@ class CreateAdminUser implements Step
private $database; private $database;
/** /**
* @var array * @var AdminUser
*/ */
private $admin; private $admin;
public function __construct(ConnectionInterface $database, array $admin) public function __construct(ConnectionInterface $database, AdminUser $admin)
{ {
$this->database = $database; $this->database = $database;
$this->admin = $admin; $this->admin = $admin;
@ -37,18 +36,14 @@ class CreateAdminUser implements Step
public function getMessage() public function getMessage()
{ {
return 'Creating admin user '.$this->admin['username']; return 'Creating admin user '.$this->admin->getUsername();
} }
public function run() public function run()
{ {
$uid = $this->database->table('users')->insertGetId([ $uid = $this->database->table('users')->insertGetId(
'username' => $this->admin['username'], $this->admin->getAttributes()
'email' => $this->admin['email'], );
'password' => (new BcryptHasher)->make($this->admin['password']),
'joined_at' => Carbon::now(),
'is_email_confirmed' => 1,
]);
$this->database->table('group_user')->insert([ $this->database->table('group_user')->insert([
'user_id' => $uid, 'user_id' => $uid,