1
1
<?php
2
2
namespace Platformsh \Cli \Command \User ;
3
3
4
- use Platformsh \Cli \Command \CommandBase ;
5
4
use Platformsh \Cli \Console \ArrayArgument ;
5
+ use Platformsh \Cli \Util \OsUtil ;
6
6
use Platformsh \Cli \Util \Wildcard ;
7
7
use Platformsh \Client \Model \EnvironmentAccess ;
8
8
use Platformsh \Client \Model \EnvironmentType ;
9
9
use Platformsh \Client \Model \Invitation \AlreadyInvitedException ;
10
10
use Platformsh \Client \Model \Invitation \Permission ;
11
11
use Platformsh \Client \Model \ProjectAccess ;
12
+ use Platformsh \Client \Model \UserAccess \ProjectUserAccess ;
12
13
use Symfony \Component \Console \Exception \InvalidArgumentException ;
13
14
use Symfony \Component \Console \Helper \ProgressBar ;
14
15
use Symfony \Component \Console \Input \InputArgument ;
18
19
use Symfony \Component \Console \Output \OutputInterface ;
19
20
use Symfony \Component \Console \Question \Question ;
20
21
21
- class UserAddCommand extends CommandBase
22
+ class UserAddCommand extends UserCommandBase
22
23
{
23
24
24
25
protected function configure ()
@@ -121,13 +122,10 @@ protected function execute(InputInterface $input, OutputInterface $output)
121
122
$ email = $ input ->getArgument ('email ' );
122
123
if (!$ email ) {
123
124
$ update = stripos ($ input ->getFirstArgument (), ':u ' );
124
- if ($ update && $ input ->isInteractive ()) {
125
- $ choices = [];
126
- foreach ($ this ->api ()->getProjectAccesses ($ project ) as $ access ) {
127
- $ account = $ this ->api ()->getAccount ($ access );
128
- $ choices [$ account ['email ' ]] = $ this ->getUserLabel ($ access );
129
- }
130
- $ email = $ questionHelper ->choose ($ choices , 'Enter a number to choose a user to update: ' );
125
+ if (!$ input ->isInteractive ()) {
126
+ throw new InvalidArgumentException ('An email address is required (in non-interactive mode). ' );
127
+ } elseif ($ update ) {
128
+ $ email = $ questionHelper ->choose ($ this ->listUsers ($ project ), 'Enter a number to choose a user to update: ' );
131
129
} else {
132
130
$ question = new Question ("Enter the user's email address: " );
133
131
$ question ->setValidator (function ($ answer ) {
@@ -140,41 +138,59 @@ protected function execute(InputInterface $input, OutputInterface $output)
140
138
}
141
139
$ this ->validateEmail ($ email );
142
140
143
- // Check the user's existing role on the project.
144
- $ existingProjectAccess = $ this ->api ()->loadProjectAccessByEmail ($ project , $ email );
145
141
$ existingTypeRoles = [];
146
- if ($ existingProjectAccess ) {
147
- // Exit if the user is the owner already.
148
- if ($ existingProjectAccess ->id === $ project ->owner ) {
149
- if ($ hasOutput ) {
150
- $ this ->stdErr ->writeln ('' );
151
- }
152
-
153
- $ this ->stdErr ->writeln (sprintf ('The user %s is the owner of %s. ' , $ this ->getUserLabel ($ existingProjectAccess ), $ this ->api ()->getProjectLabel ($ project )));
154
- if ($ specifiedProjectRole || $ specifiedTypeRoles ) {
155
- $ this ->stdErr ->writeln ('' );
156
- $ this ->stdErr ->writeln ("<comment>The project owner's role(s) cannot be changed.</comment> " );
142
+ $ existingProjectRole = null ;
143
+ $ existingUserLabel = null ;
144
+ $ existingUserId = null ;
145
+
146
+ /**
147
+ * @var ProjectUserAccess|ProjectAccess|null $selection
148
+ */
149
+ $ selection = $ this ->loadProjectUserByEmail ($ project , $ email );
150
+ if ($ selection instanceof ProjectUserAccess) {
151
+ $ existingUserId = $ selection ->user_id ;
152
+ $ existingProjectRole = $ selection ->getProjectRole ();
153
+ $ existingTypeRoles = $ selection ->getEnvironmentTypeRoles ();
154
+ $ userInfo = $ selection ->getUserInfo ();
155
+ $ existingUserLabel = sprintf ('<info>%s</info> (%s) ' , trim ($ userInfo ->first_name . ' ' . $ userInfo ->last_name ), $ userInfo ->email );
156
+ } elseif ($ selection instanceof ProjectAccess) {
157
+ $ existingUserId = $ selection ->id ;
158
+ $ existingProjectRole = $ selection ->role ;
159
+ $ existingUserLabel = $ this ->getUserLabel ($ selection );
160
+ $ existingTypeRoles = $ this ->getTypeRoles ($ selection , $ environmentTypes );
161
+ }
162
+
163
+ if ($ existingUserId !== null ) {
164
+ $ this ->debug (sprintf ('The user %s already exists on the project (user ID: %s) ' , $ email , $ existingUserId ));
165
+ }
166
+
167
+ // Exit if the user is the owner already.
168
+ if ($ existingUserId !== null && $ existingUserId === $ project ->owner ) {
169
+ if ($ hasOutput ) {
170
+ $ this ->stdErr ->writeln ('' );
171
+ }
157
172
158
- return 1 ;
159
- }
173
+ $ this ->stdErr ->writeln (sprintf ('The user %s is the owner of %s. ' , $ existingUserLabel , $ this ->api ()->getProjectLabel ($ project )));
174
+ if ($ specifiedProjectRole || $ specifiedTypeRoles ) {
175
+ $ this ->stdErr ->writeln ('' );
176
+ $ this ->stdErr ->writeln ("<comment>The project owner's role(s) cannot be changed.</comment> " );
160
177
161
- return 0 ;
178
+ return 1 ;
162
179
}
163
180
164
- // Check the user's existing role(s) on the project's environments and types.
165
- $ existingTypeRoles = $ this ->getTypeRoles ($ existingProjectAccess , $ environmentTypes );
181
+ return 0 ;
166
182
}
167
183
168
184
// If the user already exists, print a summary of their roles on the
169
185
// project and environments.
170
- if ($ existingProjectAccess ) {
186
+ if ($ existingUserId !== null ) {
171
187
if ($ hasOutput ) {
172
188
$ this ->stdErr ->writeln ('' );
173
189
}
174
190
175
- $ this ->stdErr ->writeln (sprintf ('Current role(s) of <info>%s</info> on %s: ' , $ this -> getUserLabel ( $ existingProjectAccess ) , $ this ->api ()->getProjectLabel ($ project )));
176
- $ this ->stdErr ->writeln (sprintf (' Project role: <info>%s</info> ' , $ existingProjectAccess -> role ));
177
- if ($ existingProjectAccess -> role !== ProjectAccess::ROLE_ADMIN ) {
191
+ $ this ->stdErr ->writeln (sprintf ('Current role(s) of %s on %s: ' , $ existingUserLabel , $ this ->api ()->getProjectLabel ($ project )));
192
+ $ this ->stdErr ->writeln (sprintf (' Project role: <info>%s</info> ' , $ existingProjectRole ));
193
+ if ($ existingProjectRole !== ProjectAccess::ROLE_ADMIN ) {
178
194
foreach ($ environmentTypes as $ type ) {
179
195
$ role = isset ($ existingTypeRoles [$ type ->id ]) ? $ existingTypeRoles [$ type ->id ] : '[none] ' ;
180
196
$ this ->stdErr ->writeln (sprintf (' Role on environment type <info>%s</info>: %s ' , $ type ->id , $ role ));
@@ -184,7 +200,7 @@ protected function execute(InputInterface $input, OutputInterface $output)
184
200
}
185
201
186
202
// Resolve or merge the project role.
187
- $ desiredProjectRole = $ specifiedProjectRole ?: ($ existingProjectAccess ? $ existingProjectAccess -> role : ProjectAccess::ROLE_VIEWER );
203
+ $ desiredProjectRole = $ specifiedProjectRole ?: ($ existingProjectRole ? : ProjectAccess::ROLE_VIEWER );
188
204
$ provideProjectForm = !$ input ->getOption ('role ' ) && $ input ->isInteractive ();
189
205
if ($ provideProjectForm ) {
190
206
if ($ hasOutput ) {
@@ -219,9 +235,9 @@ protected function execute(InputInterface $input, OutputInterface $output)
219
235
220
236
// Build a list of the changes that are going to be made.
221
237
$ changesText = [];
222
- if ($ existingProjectAccess ) {
223
- if ($ existingProjectAccess -> role !== $ desiredProjectRole ) {
224
- $ changesText [] = sprintf ('Project role: <error>%s</error> -> <info>%s</info> ' , $ existingProjectAccess -> role , $ desiredProjectRole );
238
+ if ($ existingUserId !== null ) {
239
+ if ($ existingProjectRole !== $ desiredProjectRole ) {
240
+ $ changesText [] = sprintf ('Project role: <error>%s</error> -> <info>%s</info> ' , $ existingProjectRole , $ desiredProjectRole );
225
241
}
226
242
} else {
227
243
$ changesText [] = sprintf ('Project role: <info>%s</info> ' , $ desiredProjectRole );
@@ -271,12 +287,12 @@ protected function execute(InputInterface $input, OutputInterface $output)
271
287
$ this ->stdErr ->writeln ('<error>No environment types selected.</error> ' );
272
288
$ this ->stdErr ->writeln ('A non-admin user must be added to at least one environment type. ' );
273
289
274
- if ($ existingProjectAccess ) {
290
+ if ($ existingUserId !== null ) {
275
291
$ this ->stdErr ->writeln ('' );
276
292
$ this ->stdErr ->writeln (sprintf (
277
293
'To delete the user, run: <info>%s user:delete %s</info> ' ,
278
294
$ this ->config ()->get ('application.executable ' ),
279
- $ this -> api ()-> getAccount ( $ existingProjectAccess )[ ' email ' ]
295
+ OsUtil:: escapeShellArg ( $ email)
280
296
));
281
297
}
282
298
@@ -290,7 +306,7 @@ protected function execute(InputInterface $input, OutputInterface $output)
290
306
}
291
307
292
308
// Print a summary of the changes that are about to be made.
293
- if ($ existingProjectAccess ) {
309
+ if ($ existingUserId !== null ) {
294
310
$ this ->stdErr ->writeln ('Summary of changes: ' );
295
311
} else {
296
312
$ this ->stdErr ->writeln (sprintf ('Adding the user <info>%s</info> to %s: ' , $ email , $ this ->api ()->getProjectLabel ($ project )));
@@ -301,7 +317,7 @@ protected function execute(InputInterface $input, OutputInterface $output)
301
317
$ this ->stdErr ->writeln ('' );
302
318
303
319
// Ask for confirmation.
304
- if ($ existingProjectAccess ) {
320
+ if ($ existingUserId !== null ) {
305
321
if (!$ questionHelper ->confirm ('Are you sure you want to make these change(s)? ' )) {
306
322
return 1 ;
307
323
}
@@ -317,7 +333,7 @@ protected function execute(InputInterface $input, OutputInterface $output)
317
333
$ this ->stdErr ->writeln ('' );
318
334
319
335
// If the user does not already exist on the project, then use the Invitations API.
320
- if (! $ existingProjectAccess ) {
336
+ if ($ existingUserId === null ) {
321
337
$ this ->stdErr ->writeln ('Inviting the user to the project... ' );
322
338
$ permissions = [];
323
339
foreach ($ desiredTypeRoles as $ type => $ role ) {
@@ -340,56 +356,68 @@ protected function execute(InputInterface $input, OutputInterface $output)
340
356
return 0 ;
341
357
}
342
358
343
- // Make the desired changes at the project level.
344
- if ($ existingProjectAccess ->role !== $ desiredProjectRole ) {
345
- $ this ->stdErr ->writeln ("Setting the user's project role to: $ desiredProjectRole " );
346
- $ result = $ existingProjectAccess ->update (['role ' => $ desiredProjectRole ]);
347
- $ activities = $ result ->getActivities ();
348
- $ userId = $ existingProjectAccess ->id ;
349
- } else {
350
- $ userId = $ existingProjectAccess ->id ;
351
- $ activities = [];
352
- }
353
-
354
- // Make the desired changes at the environment type level.
355
- if ($ desiredProjectRole !== ProjectAccess::ROLE_ADMIN ) {
356
- foreach ($ typeChanges as $ typeId => $ role ) {
357
- $ type = $ project ->getEnvironmentType ($ typeId );
358
- if (!$ type ) {
359
- $ this ->stdErr ->writeln ('Environment type not found: <comment> ' . $ typeId . '</comment> ' );
360
- continue ;
361
- }
362
- $ access = $ type ->getUser ($ userId );
363
- if ($ role === 'none ' ) {
364
- if ($ access ) {
365
- $ this ->stdErr ->writeln ("Removing the user from the environment type <info> $ typeId</info> " );
366
- $ result = $ access ->delete ();
367
- } else {
359
+ $ activities = [];
360
+ if ($ selection instanceof ProjectUserAccess) {
361
+ $ permissions = [$ desiredProjectRole ];
362
+ foreach ($ desiredTypeRoles as $ typeId => $ role ) {
363
+ $ permissions [] = sprintf ('%s:%s ' , $ typeId , $ role );
364
+ }
365
+ if ($ permissions != $ selection ->permissions ) {
366
+ $ this ->stdErr ->writeln ("Updating the user's project access " );
367
+ $ this ->debug ('New permissions: ' . implode (', ' , $ permissions ));
368
+ $ selection ->update (['permissions ' => $ permissions ]);
369
+ } else {
370
+ $ this ->stdErr ->writeln ('No changes to make ' );
371
+ $ this ->debug ('Permissions match: ' . implode (', ' , $ permissions ));
372
+ }
373
+ } elseif ($ selection instanceof ProjectAccess) {
374
+ // Make the desired changes at the project level.
375
+ if ($ existingProjectRole !== $ desiredProjectRole ) {
376
+ $ this ->stdErr ->writeln ("Setting the user's project role to: $ desiredProjectRole " );
377
+ $ result = $ selection ->update (['role ' => $ desiredProjectRole ]);
378
+ $ activities = $ result ->getActivities ();
379
+ }
380
+
381
+ // Make the desired changes at the environment type level.
382
+ if ($ desiredProjectRole !== ProjectAccess::ROLE_ADMIN ) {
383
+ foreach ($ typeChanges as $ typeId => $ role ) {
384
+ $ type = $ project ->getEnvironmentType ($ typeId );
385
+ if (!$ type ) {
386
+ $ this ->stdErr ->writeln ('Environment type not found: <comment> ' . $ typeId . '</comment> ' );
368
387
continue ;
369
388
}
370
- } elseif ($ access ) {
371
- if ($ access ->role === $ role ) {
372
- continue ;
389
+ $ access = $ type ->getUser ($ existingUserId );
390
+ if ($ role === 'none ' ) {
391
+ if ($ access ) {
392
+ $ this ->stdErr ->writeln ("Removing the user from the environment type <info> $ typeId</info> " );
393
+ $ result = $ access ->delete ();
394
+ } else {
395
+ continue ;
396
+ }
397
+ } elseif ($ access ) {
398
+ if ($ access ->role === $ role ) {
399
+ continue ;
400
+ }
401
+ $ this ->stdErr ->writeln ("Setting the user's role on the environment type <info> $ typeId</info> to: $ role " );
402
+ $ result = $ access ->update (['role ' => $ role ]);
403
+ } else {
404
+ $ this ->stdErr ->writeln ("Adding the user to the environment type: <info> $ typeId</info> " );
405
+ $ result = $ type ->addUser ($ existingUserId , $ role );
373
406
}
374
- $ this ->stdErr ->writeln ("Setting the user's role on the environment type <info> $ typeId</info> to: $ role " );
375
- $ result = $ access ->update (['role ' => $ role ]);
376
- } else {
377
- $ this ->stdErr ->writeln ("Adding the user to the environment type: <info> $ typeId</info> " );
378
- $ result = $ type ->addUser ($ userId , $ role );
407
+ $ activities = array_merge ($ activities , $ result ->getActivities ());
379
408
}
380
- $ activities = array_merge ($ activities , $ result ->getActivities ());
381
409
}
382
410
}
383
411
384
412
// Wait for activities to complete.
385
- if (!$ activities ) {
386
- $ this ->redeployWarning ();
387
- } elseif ($ this ->shouldWait ($ input )) {
413
+ if ($ activities && $ this ->shouldWait ($ input )) {
388
414
/** @var \Platformsh\Cli\Service\ActivityMonitor $activityMonitor */
389
415
$ activityMonitor = $ this ->getService ('activity_monitor ' );
390
416
if (!$ activityMonitor ->waitMultiple ($ activities , $ project )) {
391
417
return 1 ;
392
418
}
419
+ } elseif (!$ this ->config ()->get ('api.centralized_permissions ' )) {
420
+ $ this ->redeployWarning ();
393
421
}
394
422
395
423
return 0 ;
0 commit comments