diff --git a/CHANGELOG.md b/CHANGELOG.md
index 16a29985..b9d49ad3 100644
--- a/CHANGELOG.md
+++ b/CHANGELOG.md
@@ -3,14 +3,15 @@
All notable changes to this project will be documented in this file.
## [Unreleased]
-
+- Update docker build to publish to "os2display" org on docker hub. Update github workflow to latest actions.
+- Set up separate image builds for itkdev and os2display
- 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)
-- Set up separate image builds for itkdev and os2display
+- Updated add user command to ask which tenants user belongs to
## [1.2.8] - 2023-05-25
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 user\'s role (defaults to editor)',
+ CommandInputValidator::ALLOWED_USER_ROLES,
+ 0
+ );
+ $question->setErrorMessage('Role %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..8cbc0e38 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,12 @@
*/
class CommandInputValidator
{
+ public const ALLOWED_USER_ROLES = ['editor', 'admin'];
+
+ public function __construct(
+ private TenantRepository $tenantRepository,
+ ) {}
+
public function validateUsername(?string $username): string
{
if (empty($username)) {
@@ -83,4 +90,38 @@ 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.');
+ }
+
+ if (!in_array($role, self::ALLOWED_USER_ROLES)) {
+ 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;
+ }
}