From 817e2ac52adfa5cd5a59d29f5aa32c92a20eaae7 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Ture=20Gj=C3=B8rup?= Date: Fri, 7 Jul 2023 18:01:18 +0200 Subject: [PATCH 1/3] DISPLAY-986: Updated add user command to ask which tenants user belongs to --- .../{Users => Tenant}/AddTenantCommand.php | 2 +- .../ConfigureTenantCommand.php | 2 +- .../{Users => User}/AddUserCommand.php | 90 ++++++++++++++----- src/Utils/CommandInputValidator.php | 40 +++++++++ 4 files changed, 111 insertions(+), 23 deletions(-) rename src/Command/{Users => Tenant}/AddTenantCommand.php (99%) rename src/Command/{Users => Tenant}/ConfigureTenantCommand.php (98%) rename src/Command/{Users => User}/AddUserCommand.php (73%) diff --git a/src/Command/Users/AddTenantCommand.php b/src/Command/Tenant/AddTenantCommand.php similarity index 99% rename from src/Command/Users/AddTenantCommand.php rename to src/Command/Tenant/AddTenantCommand.php index 17fe4234..c74652f1 100644 --- a/src/Command/Users/AddTenantCommand.php +++ b/src/Command/Tenant/AddTenantCommand.php @@ -9,7 +9,7 @@ * file that was distributed with this source code. */ -namespace App\Command\Users; +namespace App\Command\Tenant; use App\Entity\Tenant; use App\Repository\TenantRepository; diff --git a/src/Command/Users/ConfigureTenantCommand.php b/src/Command/Tenant/ConfigureTenantCommand.php similarity index 98% rename from src/Command/Users/ConfigureTenantCommand.php rename to src/Command/Tenant/ConfigureTenantCommand.php index 40019a4a..a14b2b2e 100644 --- a/src/Command/Users/ConfigureTenantCommand.php +++ b/src/Command/Tenant/ConfigureTenantCommand.php @@ -1,6 +1,6 @@ addArgument('email', InputArgument::OPTIONAL, 'The email of the new user') ->addArgument('password', InputArgument::OPTIONAL, 'The plain password of the new user') ->addArgument('full-name', InputArgument::OPTIONAL, 'The full name of the new user') - ->addOption('admin', null, InputOption::VALUE_NONE, 'If set, the user is created as an administrator') + ->addArgument('role', InputArgument::OPTIONAL, 'The role of the user [editor|admin]') + ->addArgument('tenant-keys', InputArgument::IS_ARRAY | InputArgument::OPTIONAL, 'The keys of the tenants the user should belong to (separate multiple keys with a space)') ; } @@ -109,7 +111,12 @@ protected function initialize(InputInterface $input, OutputInterface $output): v */ protected function interact(InputInterface $input, OutputInterface $output): void { - if (null !== $input->getArgument('email') && null !== $input->getArgument('password') && null !== $input->getArgument('full-name')) { + if (null !== $input->getArgument('email') && + null !== $input->getArgument('password') && + null !== $input->getArgument('full-name') && + null !== $input->getArgument('role') && + null !== $input->getArgument('tenant-keys') + ) { return; } @@ -149,6 +156,39 @@ protected function interact(InputInterface $input, OutputInterface $output): voi $fullName = $this->io->ask('Full Name', null, [$this->validator, 'validateFullName']); $input->setArgument('full-name', $fullName); } + + $helper = $this->getHelper('question'); + + // Ask for the role if it's not defined + $role = $input->getArgument('role'); + if (null !== $role) { + $this->io->text(' > Role: '.$role); + } else { + $question = new ChoiceQuestion( + 'Please select the users role (defaults to editor)', + ['editor', 'admin'], + 0 + ); + $question->setErrorMessage('Color %s is invalid.'); + + $role = $helper->ask($input, $output, $question); + $output->writeln('You have just selected: '.$role); + } + + // Ask for the tenant keys if it's not defined + $tenantKeys = $input->getArgument('tenant-keys'); + if (0 < \count($tenantKeys)) { + $this->io->text(' > Tenant Keys: '.$tenantKeys); + } else { + $question = new ChoiceQuestion( + 'Please select the tenant(s) the user should belong to (to select multiple answer with a list. E.g: "key1, key3")', + $this->getTenantsChoiceList(), + ); + $question->setMultiselect(true); + + $tenantKeys = $helper->ask($input, $output, $question); + $output->writeln('You have just selected: '.implode(', ', $tenantKeys)); + } } /** @@ -163,10 +203,11 @@ protected function execute(InputInterface $input, OutputInterface $output): int $email = $input->getArgument('email'); $plainPassword = $input->getArgument('password'); $fullName = $input->getArgument('full-name'); - $isAdmin = $input->getOption('admin'); + $role = $input->getArgument('role'); + $tenantKeys = $input->getArgument('tenant-keys'); // make sure to validate the user data is correct - $this->validateUserData($email, $plainPassword, $fullName); + $this->validateUserData($email, $plainPassword, $fullName, $role, $tenantKeys); // create the user and hash its password $user = new User(); @@ -179,19 +220,18 @@ protected function execute(InputInterface $input, OutputInterface $output): int $hashedPassword = $this->passwordHasher->hashPassword($user, $plainPassword); $user->setPassword($hashedPassword); - // @TODO Make it possible to only select specific Tenants - $tenants = $this->tenants->findAll(); - foreach ($tenants as $tenant) { + foreach ($tenantKeys as $tenantKey) { + $tenant = $this->tenantRepository->findOneBy(['tenantKey' => $tenantKey]); $userRoleTenant = new UserRoleTenant(); $userRoleTenant->setTenant($tenant); - $userRoleTenant->setRoles([$isAdmin ? 'ROLE_ADMIN' : 'ROLE_EDITOR']); + $userRoleTenant->setRoles(['ROLE_'.strtoupper($role)]); $user->addUserRoleTenant($userRoleTenant); } $this->entityManager->persist($user); $this->entityManager->flush(); - $this->io->success(sprintf('%s was successfully created: %s', $isAdmin ? 'Administrator user' : 'User', $user->getUserIdentifier())); + $this->io->success(sprintf('%s was successfully created: %s', ucfirst($role), $user->getUserIdentifier())); $event = $stopwatch->stop('add-user-command'); if ($output->isVerbose()) { @@ -201,7 +241,7 @@ protected function execute(InputInterface $input, OutputInterface $output): int return Command::SUCCESS; } - private function validateUserData($email, $plainPassword, $fullName): void + private function validateUserData($email, $plainPassword, $fullName, $role, $tenantKeys): void { // first check if a user with the same username already exists. $existingUser = $this->users->findOneBy(['email' => $email]); @@ -214,6 +254,19 @@ private function validateUserData($email, $plainPassword, $fullName): void $this->validator->validatePassword($plainPassword); $this->validator->validateEmail($email); $this->validator->validateFullName($fullName); + $this->validator->validateRole($role); + $this->validator->validateTenantKeys($tenantKeys); + } + + private function getTenantsChoiceList(): array + { + $tenants = []; + /** @var Tenant $tenant */ + foreach ($this->tenantRepository->findBy([], ['tenantKey' => 'ASC']) as $tenant) { + $tenants[$tenant->getTenantKey()] = $tenant->getDescription(); + } + + return $tenants; } /** @@ -226,17 +279,12 @@ private function getCommandHelp(): string return <<<'HELP' The %command.name% command creates new users and saves them in the database: - php %command.full_name% email password - -By default the command creates regular users. To create administrator users, -add the --admin option: - - php %command.full_name% email password --admin + php %command.full_name% email password fullname role tenants -If you omit any of the two required arguments, the command will ask you to +If you omit any of the required arguments, the command will ask you to provide the missing values: - # command will ask you for the password + # command will ask you for the password etc php %command.full_name% email # command will ask you for all arguments diff --git a/src/Utils/CommandInputValidator.php b/src/Utils/CommandInputValidator.php index 339e309d..001747cc 100644 --- a/src/Utils/CommandInputValidator.php +++ b/src/Utils/CommandInputValidator.php @@ -11,6 +11,7 @@ namespace App\Utils; +use App\Repository\TenantRepository; use Symfony\Component\Console\Exception\InvalidArgumentException; use function Symfony\Component\String\u; @@ -23,6 +24,10 @@ */ class CommandInputValidator { + public function __construct( + private TenantRepository $tenantRepository, + ) {} + public function validateUsername(?string $username): string { if (empty($username)) { @@ -83,4 +88,39 @@ public function validateFullName(?string $fullName): string return $fullName; } + + public function validateRole(?string $role): string + { + if (empty($role)) { + throw new InvalidArgumentException('The role can not be empty.'); + } + + $allowedRoles = ['editor', 'admin']; + if (!in_array($role, $allowedRoles)) { + throw new InvalidArgumentException('Unknown role: '.$role); + } + + return $role; + } + + public function validateTenantKeys(?array $tenantKeys): array + { + if (empty($tenantKeys)) { + throw new InvalidArgumentException('The user must belong to at least one tenant.'); + } + + $unknownKeys = []; + foreach ($tenantKeys as $tenantKey) { + $tenant = $this->tenantRepository->findOneBy(['tenantKey' => $tenantKey]); + if (null === $tenant) { + $unknownKeys[] = $tenantKey; + } + } + + if (0 !== \count($unknownKeys)) { + throw new InvalidArgumentException(sprintf('Unknown tenant keys: %s.', implode(', ', $unknownKeys))); + } + + return $tenantKeys; + } } From fed639e76984ffa7c96f0d7add8bdd166e0e4442 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Ture=20Gj=C3=B8rup?= Date: Fri, 7 Jul 2023 18:10:38 +0200 Subject: [PATCH 2/3] SUPP0RT-1109: Updated changelog --- CHANGELOG.md | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 992f575f..788c2f30 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -3,13 +3,14 @@ All notable changes to this project will be documented in this file. ## [Unreleased] - +- Update docker build to publish to "os2display" org on docker hup. Update github workflow to latest actions. - Updated `EventDatabaseApiFeedType` query ensuring started but not finished events are found. - Refactored all feed related classes and services - Minor update of composer packages - Updated psalm to version 5.x - Fixed feed data provider id issue [#151](https://github.com/os2display/display-api-service/pull/151) +- Updated add user command to ask which tenants user belongs to ## [1.2.8] - 2023-05-25 From c1d5319ae308d3387a5d13cc662f8703fd84d84b Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Ture=20Gj=C3=B8rup?= Date: Tue, 11 Jul 2023 11:13:40 +0200 Subject: [PATCH 3/3] DISPLAY-986: Review feedback --- CHANGELOG.md | 2 +- src/Command/User/AddUserCommand.php | 8 ++++---- src/Utils/CommandInputValidator.php | 5 +++-- 3 files changed, 8 insertions(+), 7 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 788c2f30..109eb3e3 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -3,7 +3,7 @@ All notable changes to this project will be documented in this file. ## [Unreleased] -- Update docker build to publish to "os2display" org on docker hup. Update github workflow to latest actions. +- Update docker build to publish to "os2display" org on docker hub. Update github workflow to latest actions. - Updated `EventDatabaseApiFeedType` query ensuring started but not finished events are found. - Refactored all feed related classes and services diff --git a/src/Command/User/AddUserCommand.php b/src/Command/User/AddUserCommand.php index a18aad5c..7f60010c 100644 --- a/src/Command/User/AddUserCommand.php +++ b/src/Command/User/AddUserCommand.php @@ -165,11 +165,11 @@ protected function interact(InputInterface $input, OutputInterface $output): voi $this->io->text(' > Role: '.$role); } else { $question = new ChoiceQuestion( - 'Please select the users role (defaults to editor)', - ['editor', 'admin'], + 'Please select the user\'s role (defaults to editor)', + CommandInputValidator::ALLOWED_USER_ROLES, 0 ); - $question->setErrorMessage('Color %s is invalid.'); + $question->setErrorMessage('Role %s is invalid.'); $role = $helper->ask($input, $output, $question); $output->writeln('You have just selected: '.$role); @@ -284,7 +284,7 @@ private function getCommandHelp(): string If you omit any of the required arguments, the command will ask you to provide the missing values: - # command will ask you for the password etc + # command will ask you for the password etc. php %command.full_name% email # command will ask you for all arguments diff --git a/src/Utils/CommandInputValidator.php b/src/Utils/CommandInputValidator.php index 001747cc..8cbc0e38 100644 --- a/src/Utils/CommandInputValidator.php +++ b/src/Utils/CommandInputValidator.php @@ -24,6 +24,8 @@ */ class CommandInputValidator { + public const ALLOWED_USER_ROLES = ['editor', 'admin']; + public function __construct( private TenantRepository $tenantRepository, ) {} @@ -95,8 +97,7 @@ public function validateRole(?string $role): string throw new InvalidArgumentException('The role can not be empty.'); } - $allowedRoles = ['editor', 'admin']; - if (!in_array($role, $allowedRoles)) { + if (!in_array($role, self::ALLOWED_USER_ROLES)) { throw new InvalidArgumentException('Unknown role: '.$role); }