/
UserCommandController.php
402 lines (372 loc) · 17.5 KB
/
UserCommandController.php
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
<?php
namespace Neos\Neos\Command;
/*
* This file is part of the Neos.Neos package.
*
* (c) Contributors of the Neos Project - www.neos.io
*
* This package is Open Source Software. For the full copyright and license
* information, please view the LICENSE file which was distributed with this
* source code.
*/
use Neos\Flow\Annotations as Flow;
use Neos\Flow\Cli\CommandController;
use Neos\Flow\Security\Account;
use Neos\Flow\Security\Exception\NoSuchRoleException;
use Neos\Flow\Security\Policy\Role;
use Neos\Utility\Arrays;
use Neos\Neos\Domain\Model\User;
use Neos\Neos\Domain\Service\UserService;
/**
* The User Command Controller
*
* @Flow\Scope("singleton")
*/
class UserCommandController extends CommandController
{
/**
* @Flow\Inject
* @var UserService
*/
protected $userService;
/**
* @Flow\InjectConfiguration(package="Neos.Flow", path="security.authentication.providers")
* @var array
*/
protected $authenticationProviderSettings;
/**
* List all users
*
* This command lists all existing Neos users.
*
* @return void
*/
public function listCommand()
{
$users = $this->userService->getUsers();
$tableRows = [];
$headerRow = ['Name', 'Email', 'Account(s)', 'Role(s)', 'Active'];
foreach ($users as $user) {
$tableRows[] = $this->getTableRowForUser($user);
}
$this->output->outputTable($tableRows, $headerRow);
}
/**
* Shows the given user
*
* This command shows some basic details about the given user. If such a user does not exist, this command
* will exit with a non-zero status code.
*
* The user will be retrieved by looking for a Neos backend account with the given identifier (ie. the username)
* and then retrieving the user which owns that account. If an authentication provider is specified, this command
* will look for an account identified by "username" for that specific provider.
*
* @param string $username The username of the user to show. Usually refers to the account identifier of the user's Neos backend account.
* @param string $authenticationProvider Name of the authentication provider to use. Example: "Neos.Neos:Backend"
* @return void
*/
public function showCommand($username, $authenticationProvider = null)
{
$user = $this->userService->getUser($username, $authenticationProvider);
if (!$user instanceof User) {
$this->outputLine('The username "%s" is not in use', [$username]);
$this->quit(1);
}
$headerRow = ['Name', 'Email', 'Account(s)', 'Role(s)', 'Active'];
$tableRows = [$this->getTableRowForUser($user)];
$this->output->outputTable($tableRows, $headerRow);
}
/**
* Create a new user
*
* This command creates a new user which has access to the backend user interface.
*
* More specifically, this command will create a new user and a new account at the same time. The created account
* is, by default, a Neos backend account using the the "Neos.Neos:Backend" for authentication. The given username
* will be used as an account identifier for that new account.
*
* If an authentication provider name is specified, the new account will be created for that provider instead.
*
* Roles for the new user can optionally be specified as a comma separated list. For all roles provided by
* Neos, the role namespace "Neos.Neos:" can be omitted.
*
* @param string $username The username of the user to be created, used as an account identifier for the newly created account
* @param string $password Password of the user to be created
* @param string $firstName First name of the user to be created
* @param string $lastName Last name of the user to be created
* @param string $roles A comma separated list of roles to assign. Examples: "Editor, Acme.Foo:Reviewer"
* @param string $authenticationProvider Name of the authentication provider to use for the new account. Example: "Neos.Neos:Backend"
* @return void
*/
public function createCommand($username, $password, $firstName, $lastName, $roles = null, $authenticationProvider = null)
{
$user = $this->userService->getUser($username, $authenticationProvider);
if ($user instanceof User) {
$this->outputLine('The username "%s" is already in use', [$username]);
$this->quit(1);
}
try {
if ($roles === null) {
$user = $this->userService->createUser($username, $password, $firstName, $lastName, null, $authenticationProvider);
} else {
$roleIdentifiers = Arrays::trimExplode(',', $roles);
$user = $this->userService->createUser($username, $password, $firstName, $lastName, $roleIdentifiers, $authenticationProvider);
}
$roleIdentifiers = [];
foreach ($user->getAccounts() as $account) {
/** @var Account $account */
foreach ($account->getRoles() as $role) {
/** @var Role $role */
$roleIdentifiers[$role->getIdentifier()] = true;
}
}
$roleIdentifiers = array_keys($roleIdentifiers);
if (count($roleIdentifiers) === 0) {
$this->outputLine('Created user "%s".', [$username]);
$this->outputLine('<b>Please note that this user currently does not have any roles assigned.</b>');
} else {
$this->outputLine('Created user "%s" and assigned the following role%s: %s.', [$username, (count($roleIdentifiers) > 1 ? 's' : ''), implode(', ', $roleIdentifiers)]);
}
} catch (\Exception $exception) {
$this->outputLine($exception->getMessage());
$this->quit(1);
}
}
/**
* Delete a user (with globbing)
*
* This command deletes an existing Neos user. All content and data directly related to this user, including but
* not limited to draft workspace contents, will be removed as well.
*
* All accounts owned by the given user will be deleted.
*
* If an authentication provider is specified, this command will look for an account with the given username related
* to the given provider. Specifying an authentication provider does <b>not</b> mean that only the account for that
* provider is deleted! If a user was found by the combination of username and authentication provider, <b>all</b>
* related accounts will be deleted.
*
* @param string $username The username of the user to be removed (globbing is supported)
* @param boolean $assumeYes Assume "yes" as the answer to the confirmation dialog
* @param string $authenticationProvider Name of the authentication provider to use. Example: "Neos.Neos:Backend"
* @return void
*/
public function deleteCommand($username, $assumeYes = false, $authenticationProvider = null)
{
$users = $this->findUsersByUsernamePattern($username, $authenticationProvider);
if (empty($users)) {
$this->outputLine('No users that match name-pattern "%s" do exist.', [$username]);
$this->quit(1);
}
foreach ($users as $user) {
$username = $this->userService->getUsername($user, $authenticationProvider);
if ($assumeYes === true) {
$delete = true;
} else {
$delete = $this->output->askConfirmation(sprintf('Are you sure you want to delete the user "%s" (%s) including all directly related data? (y/n) ', $username, $user->getName()));
}
if ($delete) {
$this->userService->deleteUser($user);
$this->outputLine('Deleted user "%s".', [$username]);
}
}
}
/**
* Activate a user (with globbing)
*
* This command reactivates possibly expired accounts for the given user.
*
* If an authentication provider is specified, this command will look for an account with the given username related
* to the given provider. Still, this command will activate <b>all</b> accounts of a user, once such a user has been
* found.
*
* @param string $username The username of the user to be activated (globbing is supported)
* @param string $authenticationProvider Name of the authentication provider to use for finding the user. Example: "Neos.Neos:Backend"
* @return void
*/
public function activateCommand($username, $authenticationProvider = null)
{
$users = $this->findUsersByUsernamePattern($username, $authenticationProvider);
if (empty($users)) {
$this->outputLine('No users that match name-pattern "%s" do exist.', [$username]);
$this->quit(1);
}
foreach ($users as $user) {
$username = $this->userService->getUsername($user, $authenticationProvider);
$this->userService->activateUser($user);
$this->outputLine('Activated user "%s".', [$username]);
}
}
/**
* Deactivate a user (with globbing)
*
* This command deactivates a user by flagging all of its accounts as expired.
*
* If an authentication provider is specified, this command will look for an account with the given username related
* to the given provider. Still, this command will deactivate <b>all</b> accounts of a user, once such a user has been
* found.
*
* @param string $username The username of the user to be deactivated (globbing is supported)
* @param string $authenticationProvider Name of the authentication provider to use for finding the user. Example: "Neos.Neos:Backend"
* @return void
*/
public function deactivateCommand($username, $authenticationProvider = null)
{
$users = $this->findUsersByUsernamePattern($username, $authenticationProvider);
if (empty($users)) {
$this->outputLine('No users that match name-pattern "%s" do exist.', [$username]);
$this->quit(1);
}
foreach ($users as $user) {
$username = $this->userService->getUsername($user, $authenticationProvider);
$this->userService->deactivateUser($user);
$this->outputLine('Deactivated user "%s".', [$username]);
}
}
/**
* Set a new password for the given user
*
* This command sets a new password for an existing user. More specifically, all accounts related to the user
* which are based on a username / password token will receive the new password.
*
* If an authentication provider was specified, the user will be determined by an account identified by "username"
* related to the given provider.
*
* @param string $username Username of the user to modify
* @param string $password The new password
* @param string $authenticationProvider Name of the authentication provider to use for finding the user. Example: "Neos.Neos:Backend"
* @return void
*/
public function setPasswordCommand($username, $password, $authenticationProvider = null)
{
$user = $this->userService->getUser($username, $authenticationProvider);
if (!$user instanceof User) {
$this->outputLine('The user "%s" does not exist.', [$username]);
$this->quit(1);
}
$this->userService->setUserPassword($user, $password);
$this->outputLine('The new password for user "%s" was set.', [$username]);
}
/**
* Add a role to a user
*
* This command allows for adding a specific role to an existing user.
*
* Roles can optionally be specified as a comma separated list. For all roles provided by Neos, the role
* namespace "Neos.Neos:" can be omitted.
*
* If an authentication provider was specified, the user will be determined by an account identified by "username"
* related to the given provider. However, once a user has been found, the new role will be added to <b>all</b>
* existing accounts related to that user, regardless of its authentication provider.
*
* @param string $username The username of the user (globbing is supported)
* @param string $role Role to be added to the user, for example "Neos.Neos:Administrator" or just "Administrator"
* @param string $authenticationProvider Name of the authentication provider to use. Example: "Neos.Neos:Backend"
* @return void
*/
public function addRoleCommand($username, $role, $authenticationProvider = null)
{
$users = $this->findUsersByUsernamePattern($username, $authenticationProvider);
if (empty($users)) {
$this->outputLine('No users that match name-pattern "%s" do exist.', [$username]);
$this->quit(1);
}
foreach ($users as $user) {
$username = $this->userService->getUsername($user, $authenticationProvider);
try {
if ($this->userService->addRoleToUser($user, $role) > 0) {
$this->outputLine('Added role "%s" to accounts of user "%s".', [$role, $username]);
} else {
$this->outputLine('User "%s" already had the role "%s" assigned.', [$username, $role]);
}
} catch (NoSuchRoleException $exception) {
$this->outputLine('The role "%s" does not exist.', [$role]);
$this->quit(2);
}
}
}
/**
* Remove a role from a user
*
* This command allows for removal of a specific role from an existing user.
*
* If an authentication provider was specified, the user will be determined by an account identified by "username"
* related to the given provider. However, once a user has been found, the role will be removed from <b>all</b>
* existing accounts related to that user, regardless of its authentication provider.
*
* @param string $username The username of the user (globbing is supported)
* @param string $role Role to be removed from the user, for example "Neos.Neos:Administrator" or just "Administrator"
* @param string $authenticationProvider Name of the authentication provider to use. Example: "Neos.Neos:Backend"
* @return void
*/
public function removeRoleCommand($username, $role, $authenticationProvider = null)
{
$users = $this->findUsersByUsernamePattern($username, $authenticationProvider);
if (empty($users)) {
$this->outputLine('No users that match name-pattern "%s" do exist.', [$username]);
$this->quit(1);
}
foreach ($users as $user) {
$username = $this->userService->getUsername($user, $authenticationProvider);
try {
if ($this->userService->removeRoleFromUser($user, $role) > 0) {
$this->outputLine('Removed role "%s" from user "%s".', [$role, $username]);
} else {
$this->outputLine('User "%s" did not have the role "%s" assigned.', [$username, $role]);
}
} catch (NoSuchRoleException $exception) {
$this->outputLine('The role "%s" does not exist.', [$role]);
$this->quit(2);
}
}
}
/**
* Find all users the match the given username with globbing support
*
* @param string $usernamePattern pattern for the username of the users to find
* @param string $authenticationProviderName Name of the authentication provider to use
* @return array<User>
*/
protected function findUsersByUsernamePattern($usernamePattern, $authenticationProviderName = null)
{
if (preg_match('/[\\?\\*\\{\\[]/u', $usernamePattern)) {
return array_filter(
$this->userService->getUsers()->toArray(),
function ($user) use ($usernamePattern, $authenticationProviderName) {
return fnmatch($usernamePattern, $this->userService->getUsername($user, $authenticationProviderName));
}
);
} else {
$user = $this->userService->getUser($usernamePattern, $authenticationProviderName);
if ($user instanceof User) {
return [$user];
}
}
return [];
}
/**
* Prepares a table row for output with data of the given User
*
* @param User $user The user
* @return array
*/
protected function getTableRowForUser(User $user)
{
$roleNames = [];
$accountIdentifiers = [];
foreach ($user->getAccounts() as $account) {
/** @var Account $account */
$authenticationProviderName = $account->getAuthenticationProviderName();
if ($authenticationProviderName !== $this->userService->getDefaultAuthenticationProviderName()) {
$authenticationProviderLabel = ' (' . (isset($this->authenticationProviderSettings[$authenticationProviderName]['label']) ? $this->authenticationProviderSettings[$authenticationProviderName]['label'] : $authenticationProviderName) . ')';
} else {
$authenticationProviderLabel = '';
}
$accountIdentifiers[] = $account->getAccountIdentifier() . $authenticationProviderLabel;
foreach ($account->getRoles() as $role) {
/** @var Role $role */
$roleNames[] = $role->getIdentifier();
}
}
return [$user->getName()->getFullName(), $user->getPrimaryElectronicAddress(), implode(', ', $accountIdentifiers), implode(', ', $roleNames), ($user->isActive() ? 'yes' : 'no')];
}
}