mirror of
https://github.com/jbcr/core.git
synced 2026-04-02 14:22:22 +02:00
262 lines
10 KiB
PHP
262 lines
10 KiB
PHP
<?php
|
|
|
|
declare(strict_types=1);
|
|
|
|
namespace Bolt\Command;
|
|
|
|
use Bolt\Entity\User;
|
|
use Bolt\Repository\UserRepository;
|
|
use Bolt\Utils\Str;
|
|
use Bolt\Utils\Validator;
|
|
use Doctrine\ORM\EntityManagerInterface;
|
|
use Symfony\Component\Console\Command\Command;
|
|
use Symfony\Component\Console\Exception\RuntimeException;
|
|
use Symfony\Component\Console\Input\InputArgument;
|
|
use Symfony\Component\Console\Input\InputInterface;
|
|
use Symfony\Component\Console\Input\InputOption;
|
|
use Symfony\Component\Console\Output\OutputInterface;
|
|
use Symfony\Component\Console\Style\SymfonyStyle;
|
|
use Symfony\Component\Security\Core\Encoder\UserPasswordEncoderInterface;
|
|
use Symfony\Component\Stopwatch\Stopwatch;
|
|
|
|
/**
|
|
* A console command that creates users and stores them in the database.
|
|
*
|
|
* To use this command, open a terminal window, enter into your project
|
|
* directory and execute the following:
|
|
*
|
|
* $ php bin/console app:add-user
|
|
*
|
|
* To output detailed information, increase the command verbosity:
|
|
*
|
|
* $ php bin/console app:add-user -vv
|
|
*
|
|
* See https://symfony.com/doc/current/cookbook/console/console_command.html
|
|
* For more advanced uses, commands can be defined as services too. See
|
|
* https://symfony.com/doc/current/console/commands_as_services.html
|
|
*
|
|
* @author Javier Eguiluz <javier.eguiluz@gmail.com>
|
|
* @author Yonel Ceruto <yonelceruto@gmail.com>
|
|
*/
|
|
class AddUserCommand extends Command
|
|
{
|
|
// to make your command lazily loaded, configure the $defaultName static property,
|
|
// so it will be instantiated only when the command is actually called.
|
|
protected static $defaultName = 'bolt:add-user';
|
|
|
|
/** @var SymfonyStyle */
|
|
private $io;
|
|
|
|
/** @var EntityManagerInterface */
|
|
private $entityManager;
|
|
|
|
/** @var UserPasswordEncoderInterface */
|
|
private $passwordEncoder;
|
|
|
|
/** @var Validator */
|
|
private $validator;
|
|
|
|
/** @var UserRepository */
|
|
private $users;
|
|
|
|
public function __construct(EntityManagerInterface $em, UserPasswordEncoderInterface $encoder, Validator $validator, UserRepository $users)
|
|
{
|
|
parent::__construct();
|
|
|
|
$this->entityManager = $em;
|
|
$this->passwordEncoder = $encoder;
|
|
$this->validator = $validator;
|
|
$this->users = $users;
|
|
}
|
|
|
|
/**
|
|
* {@inheritdoc}
|
|
*/
|
|
protected function configure(): void
|
|
{
|
|
$this
|
|
->setDescription('Creates users and stores them in the database')
|
|
->setHelp($this->getCommandHelp())
|
|
// commands can optionally define arguments and/or options (mandatory and optional)
|
|
// see https://symfony.com/doc/current/components/console/console_arguments.html
|
|
->addArgument('username', InputArgument::OPTIONAL, 'The username of the new user')
|
|
->addArgument('password', InputArgument::OPTIONAL, 'The plain password of the new user')
|
|
->addArgument('email', InputArgument::OPTIONAL, 'The email of the new user')
|
|
->addArgument('display-name', InputArgument::OPTIONAL, 'The display name of the new user')
|
|
->addOption('admin', null, InputOption::VALUE_NONE, 'If set, the user is created as an administrator');
|
|
}
|
|
|
|
/**
|
|
* This optional method is the first one executed for a command after configure()
|
|
* and is useful to initialize properties based on the input arguments and options.
|
|
*/
|
|
protected function initialize(InputInterface $input, OutputInterface $output): void
|
|
{
|
|
// SymfonyStyle is an optional feature that Symfony provides so you can
|
|
// apply a consistent look to the commands of your application.
|
|
// See https://symfony.com/doc/current/console/style.html
|
|
$this->io = new SymfonyStyle($input, $output);
|
|
}
|
|
|
|
/**
|
|
* This method is executed after initialize() and before execute(). Its purpose
|
|
* is to check if some of the options/arguments are missing and interactively
|
|
* ask the user for those values.
|
|
*
|
|
* This method is completely optional. If you are developing an internal console
|
|
* command, you probably should not implement this method because it requires
|
|
* quite a lot of work. However, if the command is meant to be used by external
|
|
* users, this method is a nice way to fall back and prevent errors.
|
|
*/
|
|
protected function interact(InputInterface $input, OutputInterface $output): void
|
|
{
|
|
if ($input->getArgument('username') !== null && $input->getArgument('password') !== null && $input->getArgument('email') !== null && $input->getArgument('display-name') !== null) {
|
|
return;
|
|
}
|
|
|
|
$this->io->title('Add Bolt User Command');
|
|
$this->io->text([
|
|
'If you prefer to not use this interactive wizard, provide the',
|
|
'arguments required by this command as follows:',
|
|
'',
|
|
' $ php bin/console app:add-user username password email@example.com',
|
|
'',
|
|
'Now we\'ll ask you for the value of all the missing command arguments.',
|
|
]);
|
|
|
|
// Ask for the username if it's not defined
|
|
$username = $input->getArgument('username');
|
|
if ($username !== null) {
|
|
$this->io->text(' > <info>Username</info>: ' . $username);
|
|
} else {
|
|
$username = $this->io->ask('Username', null, [$this->validator, 'validateUsername']);
|
|
$input->setArgument('username', $username);
|
|
}
|
|
|
|
// Ask for the password if it's not defined
|
|
$password = $input->getArgument('password');
|
|
if ($password !== null) {
|
|
$this->io->text(' > <info>Password</info>: ' . str_repeat('*', mb_strlen($password)));
|
|
} else {
|
|
$password = $this->io->ask('Password', Str::generatePassword(), [$this->validator, 'validatePassword']);
|
|
$input->setArgument('password', $password);
|
|
}
|
|
|
|
// Ask for the email if it's not defined
|
|
$email = $input->getArgument('email');
|
|
if ($email !== null) {
|
|
$this->io->text(' > <info>Email</info>: ' . $email);
|
|
} else {
|
|
$email = $this->io->ask('Email', null, [$this->validator, 'validateEmail']);
|
|
$input->setArgument('email', $email);
|
|
}
|
|
|
|
// Ask for the display name if it's not defined
|
|
$displayName = $input->getArgument('display-name');
|
|
if ($displayName !== null) {
|
|
$this->io->text(' > <info>Display Name</info>: ' . $displayName);
|
|
} else {
|
|
$displayName = $this->io->ask('Display Name', null, [$this->validator, 'validateDisplayName']);
|
|
$input->setArgument('display-name', $displayName);
|
|
}
|
|
}
|
|
|
|
/**
|
|
* This method is executed after interact() and initialize(). It usually
|
|
* contains the logic to execute to complete this command task.
|
|
*/
|
|
protected function execute(InputInterface $input, OutputInterface $output)
|
|
{
|
|
$stopwatch = new Stopwatch();
|
|
$stopwatch->start('add-user-command');
|
|
|
|
$username = $input->getArgument('username');
|
|
$plainPassword = $input->getArgument('password');
|
|
$email = $input->getArgument('email');
|
|
$displayName = $input->getArgument('display-name');
|
|
$isAdmin = $input->getOption('admin');
|
|
|
|
// make sure to validate the user data is correct
|
|
$this->validateUserData($username, $plainPassword, $email, $displayName);
|
|
|
|
// create the user and encode its password
|
|
$user = new User();
|
|
$user->setDisplayName($displayName);
|
|
$user->setUsername($username);
|
|
$user->setEmail($email);
|
|
$user->setRoles([$isAdmin ? 'ROLE_ADMIN' : 'ROLE_USER']);
|
|
|
|
// See https://symfony.com/doc/current/book/security.html#security-encoding-password
|
|
$encodedPassword = $this->passwordEncoder->encodePassword($user, $plainPassword);
|
|
$user->setPassword($encodedPassword);
|
|
|
|
$this->entityManager->persist($user);
|
|
$this->entityManager->flush();
|
|
|
|
$this->io->success(sprintf('%s was successfully created: %s (%s)', $isAdmin ? 'Administrator user' : 'User', $user->getUsername(), $user->getEmail()));
|
|
|
|
$event = $stopwatch->stop('add-user-command');
|
|
if ($output->isVerbose()) {
|
|
$this->io->comment(sprintf('New user database id: %d / Elapsed time: %.2f ms / Consumed memory: %.2f MB', $user->getId(), $event->getDuration(), $event->getMemory() / (1024 ** 2)));
|
|
}
|
|
|
|
return null;
|
|
}
|
|
|
|
private function validateUserData(string $username, string $plainPassword, string $email, string $displayName): void
|
|
{
|
|
// first check if a user with the same username already exists.
|
|
$existingUser = $this->users->findOneBy(['username' => $username]);
|
|
|
|
if ($existingUser !== null) {
|
|
throw new RuntimeException(sprintf('There is already a user registered with the "%s" username.', $username));
|
|
}
|
|
|
|
// @todo Validation must be moved to a separate UserValidator
|
|
|
|
// validate password and email if is not this input means interactive.
|
|
$this->validator->validatePassword($plainPassword);
|
|
$this->validator->validateEmail($email);
|
|
$this->validator->validateDisplayName($displayName);
|
|
|
|
// check if a user with the same email already exists.
|
|
$existingEmail = $this->users->findOneBy(['email' => $email]);
|
|
|
|
if ($existingEmail !== null) {
|
|
throw new RuntimeException(sprintf('There is already a user registered with the "%s" email.', $email));
|
|
}
|
|
}
|
|
|
|
/**
|
|
* The command help is usually included in the configure() method, but when
|
|
* it's too long, it's better to define a separate method to maintain the
|
|
* code readability.
|
|
*/
|
|
private function getCommandHelp(): string
|
|
{
|
|
return <<<'HELP'
|
|
The <info>%command.name%</info> command creates new users and saves them in the database:
|
|
|
|
<info>php %command.full_name%</info> <comment>username password email</comment>
|
|
|
|
By default the command creates regular users. To create administrator users,
|
|
add the <comment>--admin</comment> option:
|
|
|
|
<info>php %command.full_name%</info> username password email <comment>--admin</comment>
|
|
|
|
If you omit any of the three required arguments, the command will ask you to
|
|
provide the missing values:
|
|
|
|
# command will ask you for the email
|
|
<info>php %command.full_name%</info> <comment>username password</comment>
|
|
|
|
# command will ask you for the email and password
|
|
<info>php %command.full_name%</info> <comment>username</comment>
|
|
|
|
# command will ask you for all arguments
|
|
<info>php %command.full_name%</info>
|
|
|
|
HELP;
|
|
}
|
|
}
|