Skip to content

Commit 5daea4e

Browse files
authored
Merge pull request #154 from os2display/feature/SUPP0RT-1109_user_tenants_commands
DISPLAY-986: Updated add user command to ask which tenants user belongs to
2 parents 0fe2eb2 + 60883aa commit 5daea4e

File tree

5 files changed

+115
-25
lines changed

5 files changed

+115
-25
lines changed

CHANGELOG.md

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -3,14 +3,15 @@
33
All notable changes to this project will be documented in this file.
44

55
## [Unreleased]
6-
6+
- Update docker build to publish to "os2display" org on docker hub. Update github workflow to latest actions.
7+
- Set up separate image builds for itkdev and os2display
78
- Updated `EventDatabaseApiFeedType` query ensuring started
89
but not finished events are found.
910
- Refactored all feed related classes and services
1011
- Minor update of composer packages
1112
- Updated psalm to version 5.x
1213
- Fixed feed data provider id issue [#151](https://github.com/os2display/display-api-service/pull/151)
13-
- Set up separate image builds for itkdev and os2display
14+
- Updated add user command to ask which tenants user belongs to
1415

1516
## [1.2.8] - 2023-05-25
1617

src/Command/Users/AddTenantCommand.php renamed to src/Command/Tenant/AddTenantCommand.php

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -9,7 +9,7 @@
99
* file that was distributed with this source code.
1010
*/
1111

12-
namespace App\Command\Users;
12+
namespace App\Command\Tenant;
1313

1414
use App\Entity\Tenant;
1515
use App\Repository\TenantRepository;

src/Command/Users/ConfigureTenantCommand.php renamed to src/Command/Tenant/ConfigureTenantCommand.php

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
<?php
22

3-
namespace App\Command\Users;
3+
namespace App\Command\Tenant;
44

55
use App\Entity\Tenant;
66
use App\Repository\TenantRepository;

src/Command/Users/AddUserCommand.php renamed to src/Command/User/AddUserCommand.php

Lines changed: 69 additions & 21 deletions
Original file line numberDiff line numberDiff line change
@@ -9,8 +9,9 @@
99
* file that was distributed with this source code.
1010
*/
1111

12-
namespace App\Command\Users;
12+
namespace App\Command\User;
1313

14+
use App\Entity\Tenant;
1415
use App\Entity\User;
1516
use App\Entity\UserRoleTenant;
1617
use App\Repository\TenantRepository;
@@ -22,8 +23,8 @@
2223
use Symfony\Component\Console\Exception\RuntimeException;
2324
use Symfony\Component\Console\Input\InputArgument;
2425
use Symfony\Component\Console\Input\InputInterface;
25-
use Symfony\Component\Console\Input\InputOption;
2626
use Symfony\Component\Console\Output\OutputInterface;
27+
use Symfony\Component\Console\Question\ChoiceQuestion;
2728
use Symfony\Component\Console\Style\SymfonyStyle;
2829
use Symfony\Component\PasswordHasher\Hasher\UserPasswordHasherInterface;
2930
use Symfony\Component\Stopwatch\Stopwatch;
@@ -63,7 +64,7 @@ public function __construct(
6364
private UserPasswordHasherInterface $passwordHasher,
6465
private CommandInputValidator $validator,
6566
private UserRepository $users,
66-
private TenantRepository $tenants
67+
private TenantRepository $tenantRepository
6768
) {
6869
parent::__construct();
6970
}
@@ -80,7 +81,8 @@ protected function configure(): void
8081
->addArgument('email', InputArgument::OPTIONAL, 'The email of the new user')
8182
->addArgument('password', InputArgument::OPTIONAL, 'The plain password of the new user')
8283
->addArgument('full-name', InputArgument::OPTIONAL, 'The full name of the new user')
83-
->addOption('admin', null, InputOption::VALUE_NONE, 'If set, the user is created as an administrator')
84+
->addArgument('role', InputArgument::OPTIONAL, 'The role of the user [editor|admin]')
85+
->addArgument('tenant-keys', InputArgument::IS_ARRAY | InputArgument::OPTIONAL, 'The keys of the tenants the user should belong to (separate multiple keys with a space)')
8486
;
8587
}
8688

@@ -109,7 +111,12 @@ protected function initialize(InputInterface $input, OutputInterface $output): v
109111
*/
110112
protected function interact(InputInterface $input, OutputInterface $output): void
111113
{
112-
if (null !== $input->getArgument('email') && null !== $input->getArgument('password') && null !== $input->getArgument('full-name')) {
114+
if (null !== $input->getArgument('email') &&
115+
null !== $input->getArgument('password') &&
116+
null !== $input->getArgument('full-name') &&
117+
null !== $input->getArgument('role') &&
118+
null !== $input->getArgument('tenant-keys')
119+
) {
113120
return;
114121
}
115122

@@ -149,6 +156,39 @@ protected function interact(InputInterface $input, OutputInterface $output): voi
149156
$fullName = $this->io->ask('Full Name', null, [$this->validator, 'validateFullName']);
150157
$input->setArgument('full-name', $fullName);
151158
}
159+
160+
$helper = $this->getHelper('question');
161+
162+
// Ask for the role if it's not defined
163+
$role = $input->getArgument('role');
164+
if (null !== $role) {
165+
$this->io->text(' > <info>Role</info>: '.$role);
166+
} else {
167+
$question = new ChoiceQuestion(
168+
'Please select the user\'s role (defaults to editor)',
169+
CommandInputValidator::ALLOWED_USER_ROLES,
170+
0
171+
);
172+
$question->setErrorMessage('Role %s is invalid.');
173+
174+
$role = $helper->ask($input, $output, $question);
175+
$output->writeln('You have just selected: '.$role);
176+
}
177+
178+
// Ask for the tenant keys if it's not defined
179+
$tenantKeys = $input->getArgument('tenant-keys');
180+
if (0 < \count($tenantKeys)) {
181+
$this->io->text(' > <info>Tenant Keys</info>: '.$tenantKeys);
182+
} else {
183+
$question = new ChoiceQuestion(
184+
'Please select the tenant(s) the user should belong to (to select multiple answer with a list. E.g: "key1, key3")',
185+
$this->getTenantsChoiceList(),
186+
);
187+
$question->setMultiselect(true);
188+
189+
$tenantKeys = $helper->ask($input, $output, $question);
190+
$output->writeln('You have just selected: '.implode(', ', $tenantKeys));
191+
}
152192
}
153193

154194
/**
@@ -163,10 +203,11 @@ protected function execute(InputInterface $input, OutputInterface $output): int
163203
$email = $input->getArgument('email');
164204
$plainPassword = $input->getArgument('password');
165205
$fullName = $input->getArgument('full-name');
166-
$isAdmin = $input->getOption('admin');
206+
$role = $input->getArgument('role');
207+
$tenantKeys = $input->getArgument('tenant-keys');
167208

168209
// make sure to validate the user data is correct
169-
$this->validateUserData($email, $plainPassword, $fullName);
210+
$this->validateUserData($email, $plainPassword, $fullName, $role, $tenantKeys);
170211

171212
// create the user and hash its password
172213
$user = new User();
@@ -179,19 +220,18 @@ protected function execute(InputInterface $input, OutputInterface $output): int
179220
$hashedPassword = $this->passwordHasher->hashPassword($user, $plainPassword);
180221
$user->setPassword($hashedPassword);
181222

182-
// @TODO Make it possible to only select specific Tenants
183-
$tenants = $this->tenants->findAll();
184-
foreach ($tenants as $tenant) {
223+
foreach ($tenantKeys as $tenantKey) {
224+
$tenant = $this->tenantRepository->findOneBy(['tenantKey' => $tenantKey]);
185225
$userRoleTenant = new UserRoleTenant();
186226
$userRoleTenant->setTenant($tenant);
187-
$userRoleTenant->setRoles([$isAdmin ? 'ROLE_ADMIN' : 'ROLE_EDITOR']);
227+
$userRoleTenant->setRoles(['ROLE_'.strtoupper($role)]);
188228
$user->addUserRoleTenant($userRoleTenant);
189229
}
190230

191231
$this->entityManager->persist($user);
192232
$this->entityManager->flush();
193233

194-
$this->io->success(sprintf('%s was successfully created: %s', $isAdmin ? 'Administrator user' : 'User', $user->getUserIdentifier()));
234+
$this->io->success(sprintf('%s was successfully created: %s', ucfirst($role), $user->getUserIdentifier()));
195235

196236
$event = $stopwatch->stop('add-user-command');
197237
if ($output->isVerbose()) {
@@ -201,7 +241,7 @@ protected function execute(InputInterface $input, OutputInterface $output): int
201241
return Command::SUCCESS;
202242
}
203243

204-
private function validateUserData($email, $plainPassword, $fullName): void
244+
private function validateUserData($email, $plainPassword, $fullName, $role, $tenantKeys): void
205245
{
206246
// first check if a user with the same username already exists.
207247
$existingUser = $this->users->findOneBy(['email' => $email]);
@@ -214,6 +254,19 @@ private function validateUserData($email, $plainPassword, $fullName): void
214254
$this->validator->validatePassword($plainPassword);
215255
$this->validator->validateEmail($email);
216256
$this->validator->validateFullName($fullName);
257+
$this->validator->validateRole($role);
258+
$this->validator->validateTenantKeys($tenantKeys);
259+
}
260+
261+
private function getTenantsChoiceList(): array
262+
{
263+
$tenants = [];
264+
/** @var Tenant $tenant */
265+
foreach ($this->tenantRepository->findBy([], ['tenantKey' => 'ASC']) as $tenant) {
266+
$tenants[$tenant->getTenantKey()] = $tenant->getDescription();
267+
}
268+
269+
return $tenants;
217270
}
218271

219272
/**
@@ -226,17 +279,12 @@ private function getCommandHelp(): string
226279
return <<<'HELP'
227280
The <info>%command.name%</info> command creates new users and saves them in the database:
228281
229-
<info>php %command.full_name%</info> <comment>email password</comment>
230-
231-
By default the command creates regular users. To create administrator users,
232-
add the <comment>--admin</comment> option:
233-
234-
<info>php %command.full_name%</info> email password <comment>--admin</comment>
282+
<info>php %command.full_name%</info> <comment>email password fullname role tenants</comment>
235283
236-
If you omit any of the two required arguments, the command will ask you to
284+
If you omit any of the required arguments, the command will ask you to
237285
provide the missing values:
238286
239-
# command will ask you for the password
287+
# command will ask you for the password etc.
240288
<info>php %command.full_name%</info> <comment>email</comment>
241289
242290
# command will ask you for all arguments

src/Utils/CommandInputValidator.php

Lines changed: 41 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -11,6 +11,7 @@
1111

1212
namespace App\Utils;
1313

14+
use App\Repository\TenantRepository;
1415
use Symfony\Component\Console\Exception\InvalidArgumentException;
1516

1617
use function Symfony\Component\String\u;
@@ -23,6 +24,12 @@
2324
*/
2425
class CommandInputValidator
2526
{
27+
public const ALLOWED_USER_ROLES = ['editor', 'admin'];
28+
29+
public function __construct(
30+
private TenantRepository $tenantRepository,
31+
) {}
32+
2633
public function validateUsername(?string $username): string
2734
{
2835
if (empty($username)) {
@@ -83,4 +90,38 @@ public function validateFullName(?string $fullName): string
8390

8491
return $fullName;
8592
}
93+
94+
public function validateRole(?string $role): string
95+
{
96+
if (empty($role)) {
97+
throw new InvalidArgumentException('The role can not be empty.');
98+
}
99+
100+
if (!in_array($role, self::ALLOWED_USER_ROLES)) {
101+
throw new InvalidArgumentException('Unknown role: '.$role);
102+
}
103+
104+
return $role;
105+
}
106+
107+
public function validateTenantKeys(?array $tenantKeys): array
108+
{
109+
if (empty($tenantKeys)) {
110+
throw new InvalidArgumentException('The user must belong to at least one tenant.');
111+
}
112+
113+
$unknownKeys = [];
114+
foreach ($tenantKeys as $tenantKey) {
115+
$tenant = $this->tenantRepository->findOneBy(['tenantKey' => $tenantKey]);
116+
if (null === $tenant) {
117+
$unknownKeys[] = $tenantKey;
118+
}
119+
}
120+
121+
if (0 !== \count($unknownKeys)) {
122+
throw new InvalidArgumentException(sprintf('Unknown tenant keys: %s.', implode(', ', $unknownKeys)));
123+
}
124+
125+
return $tenantKeys;
126+
}
86127
}

0 commit comments

Comments
 (0)