Passed
Push — master ( d93b00...b971f6 )
by Morris
13:15 queued 11s
created
apps/provisioning_api/lib/Controller/UsersController.php 1 patch
Indentation   +1079 added lines, -1079 removed lines patch added patch discarded remove patch
@@ -76,1083 +76,1083 @@
 block discarded – undo
76 76
 
77 77
 class UsersController extends AUserData {
78 78
 
79
-	/** @var IAppManager */
80
-	private $appManager;
81
-	/** @var IURLGenerator */
82
-	protected $urlGenerator;
83
-	/** @var LoggerInterface */
84
-	private $logger;
85
-	/** @var IFactory */
86
-	protected $l10nFactory;
87
-	/** @var NewUserMailHelper */
88
-	private $newUserMailHelper;
89
-	/** @var ISecureRandom */
90
-	private $secureRandom;
91
-	/** @var RemoteWipe */
92
-	private $remoteWipe;
93
-	/** @var KnownUserService */
94
-	private $knownUserService;
95
-	/** @var IEventDispatcher */
96
-	private $eventDispatcher;
97
-
98
-	public function __construct(string $appName,
99
-								IRequest $request,
100
-								IUserManager $userManager,
101
-								IConfig $config,
102
-								IAppManager $appManager,
103
-								IGroupManager $groupManager,
104
-								IUserSession $userSession,
105
-								AccountManager $accountManager,
106
-								IURLGenerator $urlGenerator,
107
-								LoggerInterface $logger,
108
-								IFactory $l10nFactory,
109
-								NewUserMailHelper $newUserMailHelper,
110
-								ISecureRandom $secureRandom,
111
-								RemoteWipe $remoteWipe,
112
-								KnownUserService $knownUserService,
113
-								IEventDispatcher $eventDispatcher) {
114
-		parent::__construct($appName,
115
-							$request,
116
-							$userManager,
117
-							$config,
118
-							$groupManager,
119
-							$userSession,
120
-							$accountManager,
121
-							$l10nFactory);
122
-
123
-		$this->appManager = $appManager;
124
-		$this->urlGenerator = $urlGenerator;
125
-		$this->logger = $logger;
126
-		$this->l10nFactory = $l10nFactory;
127
-		$this->newUserMailHelper = $newUserMailHelper;
128
-		$this->secureRandom = $secureRandom;
129
-		$this->remoteWipe = $remoteWipe;
130
-		$this->knownUserService = $knownUserService;
131
-		$this->eventDispatcher = $eventDispatcher;
132
-	}
133
-
134
-	/**
135
-	 * @NoAdminRequired
136
-	 *
137
-	 * returns a list of users
138
-	 *
139
-	 * @param string $search
140
-	 * @param int $limit
141
-	 * @param int $offset
142
-	 * @return DataResponse
143
-	 */
144
-	public function getUsers(string $search = '', int $limit = null, int $offset = 0): DataResponse {
145
-		$user = $this->userSession->getUser();
146
-		$users = [];
147
-
148
-		// Admin? Or SubAdmin?
149
-		$uid = $user->getUID();
150
-		$subAdminManager = $this->groupManager->getSubAdmin();
151
-		if ($this->groupManager->isAdmin($uid)) {
152
-			$users = $this->userManager->search($search, $limit, $offset);
153
-		} elseif ($subAdminManager->isSubAdmin($user)) {
154
-			$subAdminOfGroups = $subAdminManager->getSubAdminsGroups($user);
155
-			foreach ($subAdminOfGroups as $key => $group) {
156
-				$subAdminOfGroups[$key] = $group->getGID();
157
-			}
158
-
159
-			$users = [];
160
-			foreach ($subAdminOfGroups as $group) {
161
-				$users = array_merge($users, $this->groupManager->displayNamesInGroup($group, $search, $limit, $offset));
162
-			}
163
-		}
164
-
165
-		$users = array_keys($users);
166
-
167
-		return new DataResponse([
168
-			'users' => $users
169
-		]);
170
-	}
171
-
172
-	/**
173
-	 * @NoAdminRequired
174
-	 *
175
-	 * returns a list of users and their data
176
-	 */
177
-	public function getUsersDetails(string $search = '', int $limit = null, int $offset = 0): DataResponse {
178
-		$currentUser = $this->userSession->getUser();
179
-		$users = [];
180
-
181
-		// Admin? Or SubAdmin?
182
-		$uid = $currentUser->getUID();
183
-		$subAdminManager = $this->groupManager->getSubAdmin();
184
-		if ($this->groupManager->isAdmin($uid)) {
185
-			$users = $this->userManager->search($search, $limit, $offset);
186
-			$users = array_keys($users);
187
-		} elseif ($subAdminManager->isSubAdmin($currentUser)) {
188
-			$subAdminOfGroups = $subAdminManager->getSubAdminsGroups($currentUser);
189
-			foreach ($subAdminOfGroups as $key => $group) {
190
-				$subAdminOfGroups[$key] = $group->getGID();
191
-			}
192
-
193
-			$users = [];
194
-			foreach ($subAdminOfGroups as $group) {
195
-				$users[] = array_keys($this->groupManager->displayNamesInGroup($group, $search, $limit, $offset));
196
-			}
197
-			$users = array_merge(...$users);
198
-		}
199
-
200
-		$usersDetails = [];
201
-		foreach ($users as $userId) {
202
-			$userId = (string) $userId;
203
-			$userData = $this->getUserData($userId);
204
-			// Do not insert empty entry
205
-			if (!empty($userData)) {
206
-				$usersDetails[$userId] = $userData;
207
-			} else {
208
-				// Logged user does not have permissions to see this user
209
-				// only showing its id
210
-				$usersDetails[$userId] = ['id' => $userId];
211
-			}
212
-		}
213
-
214
-		return new DataResponse([
215
-			'users' => $usersDetails
216
-		]);
217
-	}
218
-
219
-
220
-	/**
221
-	 * @NoAdminRequired
222
-	 * @NoSubAdminRequired
223
-	 *
224
-	 * @param string $location
225
-	 * @param array $search
226
-	 * @return DataResponse
227
-	 */
228
-	public function searchByPhoneNumbers(string $location, array $search): DataResponse {
229
-		$phoneUtil = PhoneNumberUtil::getInstance();
230
-
231
-		if ($phoneUtil->getCountryCodeForRegion($location) === 0) {
232
-			// Not a valid region code
233
-			return new DataResponse([], Http::STATUS_BAD_REQUEST);
234
-		}
235
-
236
-		/** @var IUser $user */
237
-		$user = $this->userSession->getUser();
238
-		$knownTo = $user->getUID();
239
-		$defaultPhoneRegion = $this->config->getSystemValueString('default_phone_region');
240
-
241
-		$normalizedNumberToKey = [];
242
-		foreach ($search as $key => $phoneNumbers) {
243
-			foreach ($phoneNumbers as $phone) {
244
-				try {
245
-					$phoneNumber = $phoneUtil->parse($phone, $location);
246
-					if ($phoneNumber instanceof PhoneNumber && $phoneUtil->isValidNumber($phoneNumber)) {
247
-						$normalizedNumber = $phoneUtil->format($phoneNumber, PhoneNumberFormat::E164);
248
-						$normalizedNumberToKey[$normalizedNumber] = (string) $key;
249
-					}
250
-				} catch (NumberParseException $e) {
251
-				}
252
-
253
-				if ($defaultPhoneRegion !== '' && $defaultPhoneRegion !== $location && strpos($phone, '0') === 0) {
254
-					// If the number has a leading zero (no country code),
255
-					// we also check the default phone region of the instance,
256
-					// when it's different to the user's given region.
257
-					try {
258
-						$phoneNumber = $phoneUtil->parse($phone, $defaultPhoneRegion);
259
-						if ($phoneNumber instanceof PhoneNumber && $phoneUtil->isValidNumber($phoneNumber)) {
260
-							$normalizedNumber = $phoneUtil->format($phoneNumber, PhoneNumberFormat::E164);
261
-							$normalizedNumberToKey[$normalizedNumber] = (string) $key;
262
-						}
263
-					} catch (NumberParseException $e) {
264
-					}
265
-				}
266
-			}
267
-		}
268
-
269
-		$phoneNumbers = array_keys($normalizedNumberToKey);
270
-
271
-		if (empty($phoneNumbers)) {
272
-			return new DataResponse();
273
-		}
274
-
275
-		// Cleanup all previous entries and only allow new matches
276
-		$this->knownUserService->deleteKnownTo($knownTo);
277
-
278
-		$userMatches = $this->accountManager->searchUsers(IAccountManager::PROPERTY_PHONE, $phoneNumbers);
279
-
280
-		if (empty($userMatches)) {
281
-			return new DataResponse();
282
-		}
283
-
284
-		$cloudUrl = rtrim($this->urlGenerator->getAbsoluteURL('/'), '/');
285
-		if (strpos($cloudUrl, 'http://') === 0) {
286
-			$cloudUrl = substr($cloudUrl, strlen('http://'));
287
-		} elseif (strpos($cloudUrl, 'https://') === 0) {
288
-			$cloudUrl = substr($cloudUrl, strlen('https://'));
289
-		}
290
-
291
-		$matches = [];
292
-		foreach ($userMatches as $phone => $userId) {
293
-			// Not using the ICloudIdManager as that would run a search for each contact to find the display name in the address book
294
-			$matches[$normalizedNumberToKey[$phone]] = $userId . '@' . $cloudUrl;
295
-			$this->knownUserService->storeIsKnownToUser($knownTo, $userId);
296
-		}
297
-
298
-		return new DataResponse($matches);
299
-	}
300
-
301
-	/**
302
-	 * @throws OCSException
303
-	 */
304
-	private function createNewUserId(): string {
305
-		$attempts = 0;
306
-		do {
307
-			$uidCandidate = $this->secureRandom->generate(10, ISecureRandom::CHAR_HUMAN_READABLE);
308
-			if (!$this->userManager->userExists($uidCandidate)) {
309
-				return $uidCandidate;
310
-			}
311
-			$attempts++;
312
-		} while ($attempts < 10);
313
-		throw new OCSException('Could not create non-existing user id', 111);
314
-	}
315
-
316
-	/**
317
-	 * @PasswordConfirmationRequired
318
-	 * @NoAdminRequired
319
-	 *
320
-	 * @param string $userid
321
-	 * @param string $password
322
-	 * @param string $displayName
323
-	 * @param string $email
324
-	 * @param array $groups
325
-	 * @param array $subadmin
326
-	 * @param string $quota
327
-	 * @param string $language
328
-	 * @return DataResponse
329
-	 * @throws OCSException
330
-	 */
331
-	public function addUser(string $userid,
332
-							string $password = '',
333
-							string $displayName = '',
334
-							string $email = '',
335
-							array $groups = [],
336
-							array $subadmin = [],
337
-							string $quota = '',
338
-							string $language = ''): DataResponse {
339
-		$user = $this->userSession->getUser();
340
-		$isAdmin = $this->groupManager->isAdmin($user->getUID());
341
-		$subAdminManager = $this->groupManager->getSubAdmin();
342
-
343
-		if (empty($userid) && $this->config->getAppValue('core', 'newUser.generateUserID', 'no') === 'yes') {
344
-			$userid = $this->createNewUserId();
345
-		}
346
-
347
-		if ($this->userManager->userExists($userid)) {
348
-			$this->logger->error('Failed addUser attempt: User already exists.', ['app' => 'ocs_api']);
349
-			throw new OCSException('User already exists', 102);
350
-		}
351
-
352
-		if ($groups !== []) {
353
-			foreach ($groups as $group) {
354
-				if (!$this->groupManager->groupExists($group)) {
355
-					throw new OCSException('group '.$group.' does not exist', 104);
356
-				}
357
-				if (!$isAdmin && !$subAdminManager->isSubAdminOfGroup($user, $this->groupManager->get($group))) {
358
-					throw new OCSException('insufficient privileges for group '. $group, 105);
359
-				}
360
-			}
361
-		} else {
362
-			if (!$isAdmin) {
363
-				throw new OCSException('no group specified (required for subadmins)', 106);
364
-			}
365
-		}
366
-
367
-		$subadminGroups = [];
368
-		if ($subadmin !== []) {
369
-			foreach ($subadmin as $groupid) {
370
-				$group = $this->groupManager->get($groupid);
371
-				// Check if group exists
372
-				if ($group === null) {
373
-					throw new OCSException('Subadmin group does not exist',  102);
374
-				}
375
-				// Check if trying to make subadmin of admin group
376
-				if ($group->getGID() === 'admin') {
377
-					throw new OCSException('Cannot create subadmins for admin group', 103);
378
-				}
379
-				// Check if has permission to promote subadmins
380
-				if (!$subAdminManager->isSubAdminOfGroup($user, $group) && !$isAdmin) {
381
-					throw new OCSForbiddenException('No permissions to promote subadmins');
382
-				}
383
-				$subadminGroups[] = $group;
384
-			}
385
-		}
386
-
387
-		$generatePasswordResetToken = false;
388
-		if ($password === '') {
389
-			if ($email === '') {
390
-				throw new OCSException('To send a password link to the user an email address is required.', 108);
391
-			}
392
-
393
-			$passwordEvent = new GenerateSecurePasswordEvent();
394
-			$this->eventDispatcher->dispatchTyped($passwordEvent);
395
-
396
-			$password = $passwordEvent->getPassword();
397
-			if ($password === null) {
398
-				// Fallback: ensure to pass password_policy in any case
399
-				$password = $this->secureRandom->generate(10)
400
-					. $this->secureRandom->generate(1, ISecureRandom::CHAR_UPPER)
401
-					. $this->secureRandom->generate(1, ISecureRandom::CHAR_LOWER)
402
-					. $this->secureRandom->generate(1, ISecureRandom::CHAR_DIGITS)
403
-					. $this->secureRandom->generate(1, ISecureRandom::CHAR_SYMBOLS);
404
-			}
405
-			$generatePasswordResetToken = true;
406
-		}
407
-
408
-		if ($email === '' && $this->config->getAppValue('core', 'newUser.requireEmail', 'no') === 'yes') {
409
-			throw new OCSException('Required email address was not provided', 110);
410
-		}
411
-
412
-		try {
413
-			$newUser = $this->userManager->createUser($userid, $password);
414
-			$this->logger->info('Successful addUser call with userid: ' . $userid, ['app' => 'ocs_api']);
415
-
416
-			foreach ($groups as $group) {
417
-				$this->groupManager->get($group)->addUser($newUser);
418
-				$this->logger->info('Added userid ' . $userid . ' to group ' . $group, ['app' => 'ocs_api']);
419
-			}
420
-			foreach ($subadminGroups as $group) {
421
-				$subAdminManager->createSubAdmin($newUser, $group);
422
-			}
423
-
424
-			if ($displayName !== '') {
425
-				$this->editUser($userid, 'display', $displayName);
426
-			}
427
-
428
-			if ($quota !== '') {
429
-				$this->editUser($userid, 'quota', $quota);
430
-			}
431
-
432
-			if ($language !== '') {
433
-				$this->editUser($userid, 'language', $language);
434
-			}
435
-
436
-			// Send new user mail only if a mail is set
437
-			if ($email !== '') {
438
-				$newUser->setEMailAddress($email);
439
-				if ($this->config->getAppValue('core', 'newUser.sendEmail', 'yes') === 'yes') {
440
-					try {
441
-						$emailTemplate = $this->newUserMailHelper->generateTemplate($newUser, $generatePasswordResetToken);
442
-						$this->newUserMailHelper->sendMail($newUser, $emailTemplate);
443
-					} catch (\Exception $e) {
444
-						// Mail could be failing hard or just be plain not configured
445
-						// Logging error as it is the hardest of the two
446
-						$this->logger->error("Unable to send the invitation mail to $email",
447
-							[
448
-								'app' => 'ocs_api',
449
-								'exception' => $e,
450
-							]
451
-						);
452
-					}
453
-				}
454
-			}
455
-
456
-			return new DataResponse(['id' => $userid]);
457
-		} catch (HintException $e) {
458
-			$this->logger->warning('Failed addUser attempt with hint exception.',
459
-				[
460
-					'app' => 'ocs_api',
461
-					'exception' => $e,
462
-				]
463
-			);
464
-			throw new OCSException($e->getHint(), 107);
465
-		} catch (OCSException $e) {
466
-			$this->logger->warning('Failed addUser attempt with ocs exeption.',
467
-				[
468
-					'app' => 'ocs_api',
469
-					'exception' => $e,
470
-				]
471
-			);
472
-			throw $e;
473
-		} catch (\InvalidArgumentException $e) {
474
-			$this->logger->error('Failed addUser attempt with invalid argument exeption.',
475
-				[
476
-					'app' => 'ocs_api',
477
-					'exception' => $e,
478
-				]
479
-			);
480
-			throw new OCSException($e->getMessage(), 101);
481
-		} catch (\Exception $e) {
482
-			$this->logger->error('Failed addUser attempt with exception.',
483
-				[
484
-					'app' => 'ocs_api',
485
-					'exception' => $e
486
-				]
487
-			);
488
-			throw new OCSException('Bad request', 101);
489
-		}
490
-	}
491
-
492
-	/**
493
-	 * @NoAdminRequired
494
-	 * @NoSubAdminRequired
495
-	 *
496
-	 * gets user info
497
-	 *
498
-	 * @param string $userId
499
-	 * @return DataResponse
500
-	 * @throws OCSException
501
-	 */
502
-	public function getUser(string $userId): DataResponse {
503
-		$includeScopes = false;
504
-		$currentUser = $this->userSession->getUser();
505
-		if ($currentUser && $currentUser->getUID() === $userId) {
506
-			$includeScopes = true;
507
-		}
508
-
509
-		$data = $this->getUserData($userId, $includeScopes);
510
-		// getUserData returns empty array if not enough permissions
511
-		if (empty($data)) {
512
-			throw new OCSException('', OCSController::RESPOND_UNAUTHORISED);
513
-		}
514
-		return new DataResponse($data);
515
-	}
516
-
517
-	/**
518
-	 * @NoAdminRequired
519
-	 * @NoSubAdminRequired
520
-	 *
521
-	 * gets user info from the currently logged in user
522
-	 *
523
-	 * @return DataResponse
524
-	 * @throws OCSException
525
-	 */
526
-	public function getCurrentUser(): DataResponse {
527
-		$user = $this->userSession->getUser();
528
-		if ($user) {
529
-			$data = $this->getUserData($user->getUID(), true);
530
-			// rename "displayname" to "display-name" only for this call to keep
531
-			// the API stable.
532
-			$data['display-name'] = $data['displayname'];
533
-			unset($data['displayname']);
534
-			return new DataResponse($data);
535
-		}
536
-
537
-		throw new OCSException('', OCSController::RESPOND_UNAUTHORISED);
538
-	}
539
-
540
-	/**
541
-	 * @NoAdminRequired
542
-	 * @NoSubAdminRequired
543
-	 *
544
-	 * @return DataResponse
545
-	 * @throws OCSException
546
-	 */
547
-	public function getEditableFields(?string $userId = null): DataResponse {
548
-		$currentLoggedInUser = $this->userSession->getUser();
549
-		if (!$currentLoggedInUser instanceof IUser) {
550
-			throw new OCSException('', OCSController::RESPOND_NOT_FOUND);
551
-		}
552
-
553
-		$permittedFields = [];
554
-
555
-		if ($userId !== $currentLoggedInUser->getUID()) {
556
-			$targetUser = $this->userManager->get($userId);
557
-			if (!$targetUser instanceof IUser) {
558
-				throw new OCSException('', OCSController::RESPOND_NOT_FOUND);
559
-			}
560
-
561
-			$subAdminManager = $this->groupManager->getSubAdmin();
562
-			if (!$this->groupManager->isAdmin($currentLoggedInUser->getUID())
563
-				&& !$subAdminManager->isUserAccessible($currentLoggedInUser, $targetUser)) {
564
-				throw new OCSException('', OCSController::RESPOND_NOT_FOUND);
565
-			}
566
-		} else {
567
-			$targetUser = $currentLoggedInUser;
568
-		}
569
-
570
-		// Editing self (display, email)
571
-		if ($this->config->getSystemValue('allow_user_to_change_display_name', true) !== false) {
572
-			if ($targetUser->getBackend() instanceof ISetDisplayNameBackend
573
-				|| $targetUser->getBackend()->implementsActions(Backend::SET_DISPLAYNAME)) {
574
-				$permittedFields[] = IAccountManager::PROPERTY_DISPLAYNAME;
575
-			}
576
-			$permittedFields[] = IAccountManager::PROPERTY_EMAIL;
577
-		}
578
-
579
-		$permittedFields[] = IAccountManager::PROPERTY_PHONE;
580
-		$permittedFields[] = IAccountManager::PROPERTY_ADDRESS;
581
-		$permittedFields[] = IAccountManager::PROPERTY_WEBSITE;
582
-		$permittedFields[] = IAccountManager::PROPERTY_TWITTER;
583
-
584
-		return new DataResponse($permittedFields);
585
-	}
586
-
587
-	/**
588
-	 * @NoAdminRequired
589
-	 * @NoSubAdminRequired
590
-	 * @PasswordConfirmationRequired
591
-	 *
592
-	 * edit users
593
-	 *
594
-	 * @param string $userId
595
-	 * @param string $key
596
-	 * @param string $value
597
-	 * @return DataResponse
598
-	 * @throws OCSException
599
-	 */
600
-	public function editUser(string $userId, string $key, string $value): DataResponse {
601
-		$currentLoggedInUser = $this->userSession->getUser();
602
-
603
-		$targetUser = $this->userManager->get($userId);
604
-		if ($targetUser === null) {
605
-			throw new OCSException('', OCSController::RESPOND_UNAUTHORISED);
606
-		}
607
-
608
-		$permittedFields = [];
609
-		if ($targetUser->getUID() === $currentLoggedInUser->getUID()) {
610
-			// Editing self (display, email)
611
-			if ($this->config->getSystemValue('allow_user_to_change_display_name', true) !== false) {
612
-				if ($targetUser->getBackend() instanceof ISetDisplayNameBackend
613
-					|| $targetUser->getBackend()->implementsActions(Backend::SET_DISPLAYNAME)) {
614
-					$permittedFields[] = 'display';
615
-					$permittedFields[] = IAccountManager::PROPERTY_DISPLAYNAME;
616
-				}
617
-				$permittedFields[] = IAccountManager::PROPERTY_EMAIL;
618
-			}
619
-
620
-			$permittedFields[] = IAccountManager::PROPERTY_DISPLAYNAME . self::SCOPE_SUFFIX;
621
-			$permittedFields[] = IAccountManager::PROPERTY_EMAIL . self::SCOPE_SUFFIX;
622
-
623
-			$permittedFields[] = 'password';
624
-			if ($this->config->getSystemValue('force_language', false) === false ||
625
-				$this->groupManager->isAdmin($currentLoggedInUser->getUID())) {
626
-				$permittedFields[] = 'language';
627
-			}
628
-
629
-			if ($this->config->getSystemValue('force_locale', false) === false ||
630
-				$this->groupManager->isAdmin($currentLoggedInUser->getUID())) {
631
-				$permittedFields[] = 'locale';
632
-			}
633
-
634
-			$permittedFields[] = IAccountManager::PROPERTY_PHONE;
635
-			$permittedFields[] = IAccountManager::PROPERTY_ADDRESS;
636
-			$permittedFields[] = IAccountManager::PROPERTY_WEBSITE;
637
-			$permittedFields[] = IAccountManager::PROPERTY_TWITTER;
638
-			$permittedFields[] = IAccountManager::PROPERTY_PHONE . self::SCOPE_SUFFIX;
639
-			$permittedFields[] = IAccountManager::PROPERTY_ADDRESS . self::SCOPE_SUFFIX;
640
-			$permittedFields[] = IAccountManager::PROPERTY_WEBSITE . self::SCOPE_SUFFIX;
641
-			$permittedFields[] = IAccountManager::PROPERTY_TWITTER . self::SCOPE_SUFFIX;
642
-
643
-			$permittedFields[] = IAccountManager::PROPERTY_AVATAR . self::SCOPE_SUFFIX;
644
-
645
-			// If admin they can edit their own quota
646
-			if ($this->groupManager->isAdmin($currentLoggedInUser->getUID())) {
647
-				$permittedFields[] = 'quota';
648
-			}
649
-		} else {
650
-			// Check if admin / subadmin
651
-			$subAdminManager = $this->groupManager->getSubAdmin();
652
-			if ($this->groupManager->isAdmin($currentLoggedInUser->getUID())
653
-			|| $subAdminManager->isUserAccessible($currentLoggedInUser, $targetUser)) {
654
-				// They have permissions over the user
655
-				if ($targetUser->getBackend() instanceof ISetDisplayNameBackend
656
-					|| $targetUser->getBackend()->implementsActions(Backend::SET_DISPLAYNAME)) {
657
-					$permittedFields[] = 'display';
658
-					$permittedFields[] = IAccountManager::PROPERTY_DISPLAYNAME;
659
-				}
660
-				$permittedFields[] = IAccountManager::PROPERTY_EMAIL;
661
-				$permittedFields[] = 'password';
662
-				$permittedFields[] = 'language';
663
-				$permittedFields[] = 'locale';
664
-				$permittedFields[] = IAccountManager::PROPERTY_PHONE;
665
-				$permittedFields[] = IAccountManager::PROPERTY_ADDRESS;
666
-				$permittedFields[] = IAccountManager::PROPERTY_WEBSITE;
667
-				$permittedFields[] = IAccountManager::PROPERTY_TWITTER;
668
-				$permittedFields[] = 'quota';
669
-			} else {
670
-				// No rights
671
-				throw new OCSException('', OCSController::RESPOND_UNAUTHORISED);
672
-			}
673
-		}
674
-		// Check if permitted to edit this field
675
-		if (!in_array($key, $permittedFields)) {
676
-			throw new OCSException('', OCSController::RESPOND_UNAUTHORISED);
677
-		}
678
-		// Process the edit
679
-		switch ($key) {
680
-			case 'display':
681
-			case IAccountManager::PROPERTY_DISPLAYNAME:
682
-				$targetUser->setDisplayName($value);
683
-				break;
684
-			case 'quota':
685
-				$quota = $value;
686
-				if ($quota !== 'none' && $quota !== 'default') {
687
-					if (is_numeric($quota)) {
688
-						$quota = (float) $quota;
689
-					} else {
690
-						$quota = \OCP\Util::computerFileSize($quota);
691
-					}
692
-					if ($quota === false) {
693
-						throw new OCSException('Invalid quota value '.$value, 103);
694
-					}
695
-					if ($quota === -1) {
696
-						$quota = 'none';
697
-					} else {
698
-						$quota = \OCP\Util::humanFileSize($quota);
699
-					}
700
-				}
701
-				$targetUser->setQuota($quota);
702
-				break;
703
-			case 'password':
704
-				try {
705
-					if (!$targetUser->canChangePassword()) {
706
-						throw new OCSException('Setting the password is not supported by the users backend', 103);
707
-					}
708
-					$targetUser->setPassword($value);
709
-				} catch (HintException $e) { // password policy error
710
-					throw new OCSException($e->getMessage(), 103);
711
-				}
712
-				break;
713
-			case 'language':
714
-				$languagesCodes = $this->l10nFactory->findAvailableLanguages();
715
-				if (!in_array($value, $languagesCodes, true) && $value !== 'en') {
716
-					throw new OCSException('Invalid language', 102);
717
-				}
718
-				$this->config->setUserValue($targetUser->getUID(), 'core', 'lang', $value);
719
-				break;
720
-			case 'locale':
721
-				if (!$this->l10nFactory->localeExists($value)) {
722
-					throw new OCSException('Invalid locale', 102);
723
-				}
724
-				$this->config->setUserValue($targetUser->getUID(), 'core', 'locale', $value);
725
-				break;
726
-			case IAccountManager::PROPERTY_EMAIL:
727
-				if (filter_var($value, FILTER_VALIDATE_EMAIL) || $value === '') {
728
-					$targetUser->setEMailAddress($value);
729
-				} else {
730
-					throw new OCSException('', 102);
731
-				}
732
-				break;
733
-			case IAccountManager::PROPERTY_PHONE:
734
-			case IAccountManager::PROPERTY_ADDRESS:
735
-			case IAccountManager::PROPERTY_WEBSITE:
736
-			case IAccountManager::PROPERTY_TWITTER:
737
-				$userAccount = $this->accountManager->getUser($targetUser);
738
-				if ($userAccount[$key]['value'] !== $value) {
739
-					$userAccount[$key]['value'] = $value;
740
-					try {
741
-						$this->accountManager->updateUser($targetUser, $userAccount, true);
742
-
743
-						if ($key === IAccountManager::PROPERTY_PHONE) {
744
-							$this->knownUserService->deleteByContactUserId($targetUser->getUID());
745
-						}
746
-					} catch (\InvalidArgumentException $e) {
747
-						throw new OCSException('Invalid ' . $e->getMessage(), 102);
748
-					}
749
-				}
750
-				break;
751
-			case IAccountManager::PROPERTY_DISPLAYNAME . self::SCOPE_SUFFIX:
752
-			case IAccountManager::PROPERTY_EMAIL . self::SCOPE_SUFFIX:
753
-			case IAccountManager::PROPERTY_PHONE . self::SCOPE_SUFFIX:
754
-			case IAccountManager::PROPERTY_ADDRESS . self::SCOPE_SUFFIX:
755
-			case IAccountManager::PROPERTY_WEBSITE . self::SCOPE_SUFFIX:
756
-			case IAccountManager::PROPERTY_TWITTER . self::SCOPE_SUFFIX:
757
-			case IAccountManager::PROPERTY_AVATAR . self::SCOPE_SUFFIX:
758
-				$propertyName = substr($key, 0, strlen($key) - strlen(self::SCOPE_SUFFIX));
759
-				$userAccount = $this->accountManager->getUser($targetUser);
760
-				if ($userAccount[$propertyName]['scope'] !== $value) {
761
-					$userAccount[$propertyName]['scope'] = $value;
762
-					try {
763
-						$this->accountManager->updateUser($targetUser, $userAccount, true);
764
-					} catch (\InvalidArgumentException $e) {
765
-						throw new OCSException('Invalid ' . $e->getMessage(), 102);
766
-					}
767
-				}
768
-				break;
769
-			default:
770
-				throw new OCSException('', 103);
771
-		}
772
-		return new DataResponse();
773
-	}
774
-
775
-	/**
776
-	 * @PasswordConfirmationRequired
777
-	 * @NoAdminRequired
778
-	 *
779
-	 * @param string $userId
780
-	 *
781
-	 * @return DataResponse
782
-	 *
783
-	 * @throws OCSException
784
-	 */
785
-	public function wipeUserDevices(string $userId): DataResponse {
786
-		/** @var IUser $currentLoggedInUser */
787
-		$currentLoggedInUser = $this->userSession->getUser();
788
-
789
-		$targetUser = $this->userManager->get($userId);
790
-
791
-		if ($targetUser === null || $targetUser->getUID() === $currentLoggedInUser->getUID()) {
792
-			throw new OCSException('', 101);
793
-		}
794
-
795
-		// If not permitted
796
-		$subAdminManager = $this->groupManager->getSubAdmin();
797
-		if (!$this->groupManager->isAdmin($currentLoggedInUser->getUID()) && !$subAdminManager->isUserAccessible($currentLoggedInUser, $targetUser)) {
798
-			throw new OCSException('', OCSController::RESPOND_UNAUTHORISED);
799
-		}
800
-
801
-		$this->remoteWipe->markAllTokensForWipe($targetUser);
802
-
803
-		return new DataResponse();
804
-	}
805
-
806
-	/**
807
-	 * @PasswordConfirmationRequired
808
-	 * @NoAdminRequired
809
-	 *
810
-	 * @param string $userId
811
-	 * @return DataResponse
812
-	 * @throws OCSException
813
-	 */
814
-	public function deleteUser(string $userId): DataResponse {
815
-		$currentLoggedInUser = $this->userSession->getUser();
816
-
817
-		$targetUser = $this->userManager->get($userId);
818
-
819
-		if ($targetUser === null || $targetUser->getUID() === $currentLoggedInUser->getUID()) {
820
-			throw new OCSException('', 101);
821
-		}
822
-
823
-		// If not permitted
824
-		$subAdminManager = $this->groupManager->getSubAdmin();
825
-		if (!$this->groupManager->isAdmin($currentLoggedInUser->getUID()) && !$subAdminManager->isUserAccessible($currentLoggedInUser, $targetUser)) {
826
-			throw new OCSException('', OCSController::RESPOND_UNAUTHORISED);
827
-		}
828
-
829
-		// Go ahead with the delete
830
-		if ($targetUser->delete()) {
831
-			return new DataResponse();
832
-		} else {
833
-			throw new OCSException('', 101);
834
-		}
835
-	}
836
-
837
-	/**
838
-	 * @PasswordConfirmationRequired
839
-	 * @NoAdminRequired
840
-	 *
841
-	 * @param string $userId
842
-	 * @return DataResponse
843
-	 * @throws OCSException
844
-	 * @throws OCSForbiddenException
845
-	 */
846
-	public function disableUser(string $userId): DataResponse {
847
-		return $this->setEnabled($userId, false);
848
-	}
849
-
850
-	/**
851
-	 * @PasswordConfirmationRequired
852
-	 * @NoAdminRequired
853
-	 *
854
-	 * @param string $userId
855
-	 * @return DataResponse
856
-	 * @throws OCSException
857
-	 * @throws OCSForbiddenException
858
-	 */
859
-	public function enableUser(string $userId): DataResponse {
860
-		return $this->setEnabled($userId, true);
861
-	}
862
-
863
-	/**
864
-	 * @param string $userId
865
-	 * @param bool $value
866
-	 * @return DataResponse
867
-	 * @throws OCSException
868
-	 */
869
-	private function setEnabled(string $userId, bool $value): DataResponse {
870
-		$currentLoggedInUser = $this->userSession->getUser();
871
-
872
-		$targetUser = $this->userManager->get($userId);
873
-		if ($targetUser === null || $targetUser->getUID() === $currentLoggedInUser->getUID()) {
874
-			throw new OCSException('', 101);
875
-		}
876
-
877
-		// If not permitted
878
-		$subAdminManager = $this->groupManager->getSubAdmin();
879
-		if (!$this->groupManager->isAdmin($currentLoggedInUser->getUID()) && !$subAdminManager->isUserAccessible($currentLoggedInUser, $targetUser)) {
880
-			throw new OCSException('', OCSController::RESPOND_UNAUTHORISED);
881
-		}
882
-
883
-		// enable/disable the user now
884
-		$targetUser->setEnabled($value);
885
-		return new DataResponse();
886
-	}
887
-
888
-	/**
889
-	 * @NoAdminRequired
890
-	 * @NoSubAdminRequired
891
-	 *
892
-	 * @param string $userId
893
-	 * @return DataResponse
894
-	 * @throws OCSException
895
-	 */
896
-	public function getUsersGroups(string $userId): DataResponse {
897
-		$loggedInUser = $this->userSession->getUser();
898
-
899
-		$targetUser = $this->userManager->get($userId);
900
-		if ($targetUser === null) {
901
-			throw new OCSException('', OCSController::RESPOND_NOT_FOUND);
902
-		}
903
-
904
-		if ($targetUser->getUID() === $loggedInUser->getUID() || $this->groupManager->isAdmin($loggedInUser->getUID())) {
905
-			// Self lookup or admin lookup
906
-			return new DataResponse([
907
-				'groups' => $this->groupManager->getUserGroupIds($targetUser)
908
-			]);
909
-		} else {
910
-			$subAdminManager = $this->groupManager->getSubAdmin();
911
-
912
-			// Looking up someone else
913
-			if ($subAdminManager->isUserAccessible($loggedInUser, $targetUser)) {
914
-				// Return the group that the method caller is subadmin of for the user in question
915
-				/** @var IGroup[] $getSubAdminsGroups */
916
-				$getSubAdminsGroups = $subAdminManager->getSubAdminsGroups($loggedInUser);
917
-				foreach ($getSubAdminsGroups as $key => $group) {
918
-					$getSubAdminsGroups[$key] = $group->getGID();
919
-				}
920
-				$groups = array_intersect(
921
-					$getSubAdminsGroups,
922
-					$this->groupManager->getUserGroupIds($targetUser)
923
-				);
924
-				return new DataResponse(['groups' => $groups]);
925
-			} else {
926
-				// Not permitted
927
-				throw new OCSException('', OCSController::RESPOND_UNAUTHORISED);
928
-			}
929
-		}
930
-	}
931
-
932
-	/**
933
-	 * @PasswordConfirmationRequired
934
-	 * @NoAdminRequired
935
-	 *
936
-	 * @param string $userId
937
-	 * @param string $groupid
938
-	 * @return DataResponse
939
-	 * @throws OCSException
940
-	 */
941
-	public function addToGroup(string $userId, string $groupid = ''): DataResponse {
942
-		if ($groupid === '') {
943
-			throw new OCSException('', 101);
944
-		}
945
-
946
-		$group = $this->groupManager->get($groupid);
947
-		$targetUser = $this->userManager->get($userId);
948
-		if ($group === null) {
949
-			throw new OCSException('', 102);
950
-		}
951
-		if ($targetUser === null) {
952
-			throw new OCSException('', 103);
953
-		}
954
-
955
-		// If they're not an admin, check they are a subadmin of the group in question
956
-		$loggedInUser = $this->userSession->getUser();
957
-		$subAdminManager = $this->groupManager->getSubAdmin();
958
-		if (!$this->groupManager->isAdmin($loggedInUser->getUID()) && !$subAdminManager->isSubAdminOfGroup($loggedInUser, $group)) {
959
-			throw new OCSException('', 104);
960
-		}
961
-
962
-		// Add user to group
963
-		$group->addUser($targetUser);
964
-		return new DataResponse();
965
-	}
966
-
967
-	/**
968
-	 * @PasswordConfirmationRequired
969
-	 * @NoAdminRequired
970
-	 *
971
-	 * @param string $userId
972
-	 * @param string $groupid
973
-	 * @return DataResponse
974
-	 * @throws OCSException
975
-	 */
976
-	public function removeFromGroup(string $userId, string $groupid): DataResponse {
977
-		$loggedInUser = $this->userSession->getUser();
978
-
979
-		if ($groupid === null || trim($groupid) === '') {
980
-			throw new OCSException('', 101);
981
-		}
982
-
983
-		$group = $this->groupManager->get($groupid);
984
-		if ($group === null) {
985
-			throw new OCSException('', 102);
986
-		}
987
-
988
-		$targetUser = $this->userManager->get($userId);
989
-		if ($targetUser === null) {
990
-			throw new OCSException('', 103);
991
-		}
992
-
993
-		// If they're not an admin, check they are a subadmin of the group in question
994
-		$subAdminManager = $this->groupManager->getSubAdmin();
995
-		if (!$this->groupManager->isAdmin($loggedInUser->getUID()) && !$subAdminManager->isSubAdminOfGroup($loggedInUser, $group)) {
996
-			throw new OCSException('', 104);
997
-		}
998
-
999
-		// Check they aren't removing themselves from 'admin' or their 'subadmin; group
1000
-		if ($targetUser->getUID() === $loggedInUser->getUID()) {
1001
-			if ($this->groupManager->isAdmin($loggedInUser->getUID())) {
1002
-				if ($group->getGID() === 'admin') {
1003
-					throw new OCSException('Cannot remove yourself from the admin group', 105);
1004
-				}
1005
-			} else {
1006
-				// Not an admin, so the user must be a subadmin of this group, but that is not allowed.
1007
-				throw new OCSException('Cannot remove yourself from this group as you are a SubAdmin', 105);
1008
-			}
1009
-		} elseif (!$this->groupManager->isAdmin($loggedInUser->getUID())) {
1010
-			/** @var IGroup[] $subAdminGroups */
1011
-			$subAdminGroups = $subAdminManager->getSubAdminsGroups($loggedInUser);
1012
-			$subAdminGroups = array_map(function (IGroup $subAdminGroup) {
1013
-				return $subAdminGroup->getGID();
1014
-			}, $subAdminGroups);
1015
-			$userGroups = $this->groupManager->getUserGroupIds($targetUser);
1016
-			$userSubAdminGroups = array_intersect($subAdminGroups, $userGroups);
1017
-
1018
-			if (count($userSubAdminGroups) <= 1) {
1019
-				// Subadmin must not be able to remove a user from all their subadmin groups.
1020
-				throw new OCSException('Not viable to remove user from the last group you are SubAdmin of', 105);
1021
-			}
1022
-		}
1023
-
1024
-		// Remove user from group
1025
-		$group->removeUser($targetUser);
1026
-		return new DataResponse();
1027
-	}
1028
-
1029
-	/**
1030
-	 * Creates a subadmin
1031
-	 *
1032
-	 * @PasswordConfirmationRequired
1033
-	 *
1034
-	 * @param string $userId
1035
-	 * @param string $groupid
1036
-	 * @return DataResponse
1037
-	 * @throws OCSException
1038
-	 */
1039
-	public function addSubAdmin(string $userId, string $groupid): DataResponse {
1040
-		$group = $this->groupManager->get($groupid);
1041
-		$user = $this->userManager->get($userId);
1042
-
1043
-		// Check if the user exists
1044
-		if ($user === null) {
1045
-			throw new OCSException('User does not exist', 101);
1046
-		}
1047
-		// Check if group exists
1048
-		if ($group === null) {
1049
-			throw new OCSException('Group does not exist',  102);
1050
-		}
1051
-		// Check if trying to make subadmin of admin group
1052
-		if ($group->getGID() === 'admin') {
1053
-			throw new OCSException('Cannot create subadmins for admin group', 103);
1054
-		}
1055
-
1056
-		$subAdminManager = $this->groupManager->getSubAdmin();
1057
-
1058
-		// We cannot be subadmin twice
1059
-		if ($subAdminManager->isSubAdminOfGroup($user, $group)) {
1060
-			return new DataResponse();
1061
-		}
1062
-		// Go
1063
-		$subAdminManager->createSubAdmin($user, $group);
1064
-		return new DataResponse();
1065
-	}
1066
-
1067
-	/**
1068
-	 * Removes a subadmin from a group
1069
-	 *
1070
-	 * @PasswordConfirmationRequired
1071
-	 *
1072
-	 * @param string $userId
1073
-	 * @param string $groupid
1074
-	 * @return DataResponse
1075
-	 * @throws OCSException
1076
-	 */
1077
-	public function removeSubAdmin(string $userId, string $groupid): DataResponse {
1078
-		$group = $this->groupManager->get($groupid);
1079
-		$user = $this->userManager->get($userId);
1080
-		$subAdminManager = $this->groupManager->getSubAdmin();
1081
-
1082
-		// Check if the user exists
1083
-		if ($user === null) {
1084
-			throw new OCSException('User does not exist', 101);
1085
-		}
1086
-		// Check if the group exists
1087
-		if ($group === null) {
1088
-			throw new OCSException('Group does not exist', 101);
1089
-		}
1090
-		// Check if they are a subadmin of this said group
1091
-		if (!$subAdminManager->isSubAdminOfGroup($user, $group)) {
1092
-			throw new OCSException('User is not a subadmin of this group', 102);
1093
-		}
1094
-
1095
-		// Go
1096
-		$subAdminManager->deleteSubAdmin($user, $group);
1097
-		return new DataResponse();
1098
-	}
1099
-
1100
-	/**
1101
-	 * Get the groups a user is a subadmin of
1102
-	 *
1103
-	 * @param string $userId
1104
-	 * @return DataResponse
1105
-	 * @throws OCSException
1106
-	 */
1107
-	public function getUserSubAdminGroups(string $userId): DataResponse {
1108
-		$groups = $this->getUserSubAdminGroupsData($userId);
1109
-		return new DataResponse($groups);
1110
-	}
1111
-
1112
-	/**
1113
-	 * @NoAdminRequired
1114
-	 * @PasswordConfirmationRequired
1115
-	 *
1116
-	 * resend welcome message
1117
-	 *
1118
-	 * @param string $userId
1119
-	 * @return DataResponse
1120
-	 * @throws OCSException
1121
-	 */
1122
-	public function resendWelcomeMessage(string $userId): DataResponse {
1123
-		$currentLoggedInUser = $this->userSession->getUser();
1124
-
1125
-		$targetUser = $this->userManager->get($userId);
1126
-		if ($targetUser === null) {
1127
-			throw new OCSException('', OCSController::RESPOND_NOT_FOUND);
1128
-		}
1129
-
1130
-		// Check if admin / subadmin
1131
-		$subAdminManager = $this->groupManager->getSubAdmin();
1132
-		if (!$subAdminManager->isUserAccessible($currentLoggedInUser, $targetUser)
1133
-			&& !$this->groupManager->isAdmin($currentLoggedInUser->getUID())) {
1134
-			// No rights
1135
-			throw new OCSException('', OCSController::RESPOND_UNAUTHORISED);
1136
-		}
1137
-
1138
-		$email = $targetUser->getEMailAddress();
1139
-		if ($email === '' || $email === null) {
1140
-			throw new OCSException('Email address not available', 101);
1141
-		}
1142
-
1143
-		try {
1144
-			$emailTemplate = $this->newUserMailHelper->generateTemplate($targetUser, false);
1145
-			$this->newUserMailHelper->sendMail($targetUser, $emailTemplate);
1146
-		} catch (\Exception $e) {
1147
-			$this->logger->error("Can't send new user mail to $email",
1148
-				[
1149
-					'app' => 'settings',
1150
-					'exception' => $e,
1151
-				]
1152
-			);
1153
-			throw new OCSException('Sending email failed', 102);
1154
-		}
1155
-
1156
-		return new DataResponse();
1157
-	}
79
+    /** @var IAppManager */
80
+    private $appManager;
81
+    /** @var IURLGenerator */
82
+    protected $urlGenerator;
83
+    /** @var LoggerInterface */
84
+    private $logger;
85
+    /** @var IFactory */
86
+    protected $l10nFactory;
87
+    /** @var NewUserMailHelper */
88
+    private $newUserMailHelper;
89
+    /** @var ISecureRandom */
90
+    private $secureRandom;
91
+    /** @var RemoteWipe */
92
+    private $remoteWipe;
93
+    /** @var KnownUserService */
94
+    private $knownUserService;
95
+    /** @var IEventDispatcher */
96
+    private $eventDispatcher;
97
+
98
+    public function __construct(string $appName,
99
+                                IRequest $request,
100
+                                IUserManager $userManager,
101
+                                IConfig $config,
102
+                                IAppManager $appManager,
103
+                                IGroupManager $groupManager,
104
+                                IUserSession $userSession,
105
+                                AccountManager $accountManager,
106
+                                IURLGenerator $urlGenerator,
107
+                                LoggerInterface $logger,
108
+                                IFactory $l10nFactory,
109
+                                NewUserMailHelper $newUserMailHelper,
110
+                                ISecureRandom $secureRandom,
111
+                                RemoteWipe $remoteWipe,
112
+                                KnownUserService $knownUserService,
113
+                                IEventDispatcher $eventDispatcher) {
114
+        parent::__construct($appName,
115
+                            $request,
116
+                            $userManager,
117
+                            $config,
118
+                            $groupManager,
119
+                            $userSession,
120
+                            $accountManager,
121
+                            $l10nFactory);
122
+
123
+        $this->appManager = $appManager;
124
+        $this->urlGenerator = $urlGenerator;
125
+        $this->logger = $logger;
126
+        $this->l10nFactory = $l10nFactory;
127
+        $this->newUserMailHelper = $newUserMailHelper;
128
+        $this->secureRandom = $secureRandom;
129
+        $this->remoteWipe = $remoteWipe;
130
+        $this->knownUserService = $knownUserService;
131
+        $this->eventDispatcher = $eventDispatcher;
132
+    }
133
+
134
+    /**
135
+     * @NoAdminRequired
136
+     *
137
+     * returns a list of users
138
+     *
139
+     * @param string $search
140
+     * @param int $limit
141
+     * @param int $offset
142
+     * @return DataResponse
143
+     */
144
+    public function getUsers(string $search = '', int $limit = null, int $offset = 0): DataResponse {
145
+        $user = $this->userSession->getUser();
146
+        $users = [];
147
+
148
+        // Admin? Or SubAdmin?
149
+        $uid = $user->getUID();
150
+        $subAdminManager = $this->groupManager->getSubAdmin();
151
+        if ($this->groupManager->isAdmin($uid)) {
152
+            $users = $this->userManager->search($search, $limit, $offset);
153
+        } elseif ($subAdminManager->isSubAdmin($user)) {
154
+            $subAdminOfGroups = $subAdminManager->getSubAdminsGroups($user);
155
+            foreach ($subAdminOfGroups as $key => $group) {
156
+                $subAdminOfGroups[$key] = $group->getGID();
157
+            }
158
+
159
+            $users = [];
160
+            foreach ($subAdminOfGroups as $group) {
161
+                $users = array_merge($users, $this->groupManager->displayNamesInGroup($group, $search, $limit, $offset));
162
+            }
163
+        }
164
+
165
+        $users = array_keys($users);
166
+
167
+        return new DataResponse([
168
+            'users' => $users
169
+        ]);
170
+    }
171
+
172
+    /**
173
+     * @NoAdminRequired
174
+     *
175
+     * returns a list of users and their data
176
+     */
177
+    public function getUsersDetails(string $search = '', int $limit = null, int $offset = 0): DataResponse {
178
+        $currentUser = $this->userSession->getUser();
179
+        $users = [];
180
+
181
+        // Admin? Or SubAdmin?
182
+        $uid = $currentUser->getUID();
183
+        $subAdminManager = $this->groupManager->getSubAdmin();
184
+        if ($this->groupManager->isAdmin($uid)) {
185
+            $users = $this->userManager->search($search, $limit, $offset);
186
+            $users = array_keys($users);
187
+        } elseif ($subAdminManager->isSubAdmin($currentUser)) {
188
+            $subAdminOfGroups = $subAdminManager->getSubAdminsGroups($currentUser);
189
+            foreach ($subAdminOfGroups as $key => $group) {
190
+                $subAdminOfGroups[$key] = $group->getGID();
191
+            }
192
+
193
+            $users = [];
194
+            foreach ($subAdminOfGroups as $group) {
195
+                $users[] = array_keys($this->groupManager->displayNamesInGroup($group, $search, $limit, $offset));
196
+            }
197
+            $users = array_merge(...$users);
198
+        }
199
+
200
+        $usersDetails = [];
201
+        foreach ($users as $userId) {
202
+            $userId = (string) $userId;
203
+            $userData = $this->getUserData($userId);
204
+            // Do not insert empty entry
205
+            if (!empty($userData)) {
206
+                $usersDetails[$userId] = $userData;
207
+            } else {
208
+                // Logged user does not have permissions to see this user
209
+                // only showing its id
210
+                $usersDetails[$userId] = ['id' => $userId];
211
+            }
212
+        }
213
+
214
+        return new DataResponse([
215
+            'users' => $usersDetails
216
+        ]);
217
+    }
218
+
219
+
220
+    /**
221
+     * @NoAdminRequired
222
+     * @NoSubAdminRequired
223
+     *
224
+     * @param string $location
225
+     * @param array $search
226
+     * @return DataResponse
227
+     */
228
+    public function searchByPhoneNumbers(string $location, array $search): DataResponse {
229
+        $phoneUtil = PhoneNumberUtil::getInstance();
230
+
231
+        if ($phoneUtil->getCountryCodeForRegion($location) === 0) {
232
+            // Not a valid region code
233
+            return new DataResponse([], Http::STATUS_BAD_REQUEST);
234
+        }
235
+
236
+        /** @var IUser $user */
237
+        $user = $this->userSession->getUser();
238
+        $knownTo = $user->getUID();
239
+        $defaultPhoneRegion = $this->config->getSystemValueString('default_phone_region');
240
+
241
+        $normalizedNumberToKey = [];
242
+        foreach ($search as $key => $phoneNumbers) {
243
+            foreach ($phoneNumbers as $phone) {
244
+                try {
245
+                    $phoneNumber = $phoneUtil->parse($phone, $location);
246
+                    if ($phoneNumber instanceof PhoneNumber && $phoneUtil->isValidNumber($phoneNumber)) {
247
+                        $normalizedNumber = $phoneUtil->format($phoneNumber, PhoneNumberFormat::E164);
248
+                        $normalizedNumberToKey[$normalizedNumber] = (string) $key;
249
+                    }
250
+                } catch (NumberParseException $e) {
251
+                }
252
+
253
+                if ($defaultPhoneRegion !== '' && $defaultPhoneRegion !== $location && strpos($phone, '0') === 0) {
254
+                    // If the number has a leading zero (no country code),
255
+                    // we also check the default phone region of the instance,
256
+                    // when it's different to the user's given region.
257
+                    try {
258
+                        $phoneNumber = $phoneUtil->parse($phone, $defaultPhoneRegion);
259
+                        if ($phoneNumber instanceof PhoneNumber && $phoneUtil->isValidNumber($phoneNumber)) {
260
+                            $normalizedNumber = $phoneUtil->format($phoneNumber, PhoneNumberFormat::E164);
261
+                            $normalizedNumberToKey[$normalizedNumber] = (string) $key;
262
+                        }
263
+                    } catch (NumberParseException $e) {
264
+                    }
265
+                }
266
+            }
267
+        }
268
+
269
+        $phoneNumbers = array_keys($normalizedNumberToKey);
270
+
271
+        if (empty($phoneNumbers)) {
272
+            return new DataResponse();
273
+        }
274
+
275
+        // Cleanup all previous entries and only allow new matches
276
+        $this->knownUserService->deleteKnownTo($knownTo);
277
+
278
+        $userMatches = $this->accountManager->searchUsers(IAccountManager::PROPERTY_PHONE, $phoneNumbers);
279
+
280
+        if (empty($userMatches)) {
281
+            return new DataResponse();
282
+        }
283
+
284
+        $cloudUrl = rtrim($this->urlGenerator->getAbsoluteURL('/'), '/');
285
+        if (strpos($cloudUrl, 'http://') === 0) {
286
+            $cloudUrl = substr($cloudUrl, strlen('http://'));
287
+        } elseif (strpos($cloudUrl, 'https://') === 0) {
288
+            $cloudUrl = substr($cloudUrl, strlen('https://'));
289
+        }
290
+
291
+        $matches = [];
292
+        foreach ($userMatches as $phone => $userId) {
293
+            // Not using the ICloudIdManager as that would run a search for each contact to find the display name in the address book
294
+            $matches[$normalizedNumberToKey[$phone]] = $userId . '@' . $cloudUrl;
295
+            $this->knownUserService->storeIsKnownToUser($knownTo, $userId);
296
+        }
297
+
298
+        return new DataResponse($matches);
299
+    }
300
+
301
+    /**
302
+     * @throws OCSException
303
+     */
304
+    private function createNewUserId(): string {
305
+        $attempts = 0;
306
+        do {
307
+            $uidCandidate = $this->secureRandom->generate(10, ISecureRandom::CHAR_HUMAN_READABLE);
308
+            if (!$this->userManager->userExists($uidCandidate)) {
309
+                return $uidCandidate;
310
+            }
311
+            $attempts++;
312
+        } while ($attempts < 10);
313
+        throw new OCSException('Could not create non-existing user id', 111);
314
+    }
315
+
316
+    /**
317
+     * @PasswordConfirmationRequired
318
+     * @NoAdminRequired
319
+     *
320
+     * @param string $userid
321
+     * @param string $password
322
+     * @param string $displayName
323
+     * @param string $email
324
+     * @param array $groups
325
+     * @param array $subadmin
326
+     * @param string $quota
327
+     * @param string $language
328
+     * @return DataResponse
329
+     * @throws OCSException
330
+     */
331
+    public function addUser(string $userid,
332
+                            string $password = '',
333
+                            string $displayName = '',
334
+                            string $email = '',
335
+                            array $groups = [],
336
+                            array $subadmin = [],
337
+                            string $quota = '',
338
+                            string $language = ''): DataResponse {
339
+        $user = $this->userSession->getUser();
340
+        $isAdmin = $this->groupManager->isAdmin($user->getUID());
341
+        $subAdminManager = $this->groupManager->getSubAdmin();
342
+
343
+        if (empty($userid) && $this->config->getAppValue('core', 'newUser.generateUserID', 'no') === 'yes') {
344
+            $userid = $this->createNewUserId();
345
+        }
346
+
347
+        if ($this->userManager->userExists($userid)) {
348
+            $this->logger->error('Failed addUser attempt: User already exists.', ['app' => 'ocs_api']);
349
+            throw new OCSException('User already exists', 102);
350
+        }
351
+
352
+        if ($groups !== []) {
353
+            foreach ($groups as $group) {
354
+                if (!$this->groupManager->groupExists($group)) {
355
+                    throw new OCSException('group '.$group.' does not exist', 104);
356
+                }
357
+                if (!$isAdmin && !$subAdminManager->isSubAdminOfGroup($user, $this->groupManager->get($group))) {
358
+                    throw new OCSException('insufficient privileges for group '. $group, 105);
359
+                }
360
+            }
361
+        } else {
362
+            if (!$isAdmin) {
363
+                throw new OCSException('no group specified (required for subadmins)', 106);
364
+            }
365
+        }
366
+
367
+        $subadminGroups = [];
368
+        if ($subadmin !== []) {
369
+            foreach ($subadmin as $groupid) {
370
+                $group = $this->groupManager->get($groupid);
371
+                // Check if group exists
372
+                if ($group === null) {
373
+                    throw new OCSException('Subadmin group does not exist',  102);
374
+                }
375
+                // Check if trying to make subadmin of admin group
376
+                if ($group->getGID() === 'admin') {
377
+                    throw new OCSException('Cannot create subadmins for admin group', 103);
378
+                }
379
+                // Check if has permission to promote subadmins
380
+                if (!$subAdminManager->isSubAdminOfGroup($user, $group) && !$isAdmin) {
381
+                    throw new OCSForbiddenException('No permissions to promote subadmins');
382
+                }
383
+                $subadminGroups[] = $group;
384
+            }
385
+        }
386
+
387
+        $generatePasswordResetToken = false;
388
+        if ($password === '') {
389
+            if ($email === '') {
390
+                throw new OCSException('To send a password link to the user an email address is required.', 108);
391
+            }
392
+
393
+            $passwordEvent = new GenerateSecurePasswordEvent();
394
+            $this->eventDispatcher->dispatchTyped($passwordEvent);
395
+
396
+            $password = $passwordEvent->getPassword();
397
+            if ($password === null) {
398
+                // Fallback: ensure to pass password_policy in any case
399
+                $password = $this->secureRandom->generate(10)
400
+                    . $this->secureRandom->generate(1, ISecureRandom::CHAR_UPPER)
401
+                    . $this->secureRandom->generate(1, ISecureRandom::CHAR_LOWER)
402
+                    . $this->secureRandom->generate(1, ISecureRandom::CHAR_DIGITS)
403
+                    . $this->secureRandom->generate(1, ISecureRandom::CHAR_SYMBOLS);
404
+            }
405
+            $generatePasswordResetToken = true;
406
+        }
407
+
408
+        if ($email === '' && $this->config->getAppValue('core', 'newUser.requireEmail', 'no') === 'yes') {
409
+            throw new OCSException('Required email address was not provided', 110);
410
+        }
411
+
412
+        try {
413
+            $newUser = $this->userManager->createUser($userid, $password);
414
+            $this->logger->info('Successful addUser call with userid: ' . $userid, ['app' => 'ocs_api']);
415
+
416
+            foreach ($groups as $group) {
417
+                $this->groupManager->get($group)->addUser($newUser);
418
+                $this->logger->info('Added userid ' . $userid . ' to group ' . $group, ['app' => 'ocs_api']);
419
+            }
420
+            foreach ($subadminGroups as $group) {
421
+                $subAdminManager->createSubAdmin($newUser, $group);
422
+            }
423
+
424
+            if ($displayName !== '') {
425
+                $this->editUser($userid, 'display', $displayName);
426
+            }
427
+
428
+            if ($quota !== '') {
429
+                $this->editUser($userid, 'quota', $quota);
430
+            }
431
+
432
+            if ($language !== '') {
433
+                $this->editUser($userid, 'language', $language);
434
+            }
435
+
436
+            // Send new user mail only if a mail is set
437
+            if ($email !== '') {
438
+                $newUser->setEMailAddress($email);
439
+                if ($this->config->getAppValue('core', 'newUser.sendEmail', 'yes') === 'yes') {
440
+                    try {
441
+                        $emailTemplate = $this->newUserMailHelper->generateTemplate($newUser, $generatePasswordResetToken);
442
+                        $this->newUserMailHelper->sendMail($newUser, $emailTemplate);
443
+                    } catch (\Exception $e) {
444
+                        // Mail could be failing hard or just be plain not configured
445
+                        // Logging error as it is the hardest of the two
446
+                        $this->logger->error("Unable to send the invitation mail to $email",
447
+                            [
448
+                                'app' => 'ocs_api',
449
+                                'exception' => $e,
450
+                            ]
451
+                        );
452
+                    }
453
+                }
454
+            }
455
+
456
+            return new DataResponse(['id' => $userid]);
457
+        } catch (HintException $e) {
458
+            $this->logger->warning('Failed addUser attempt with hint exception.',
459
+                [
460
+                    'app' => 'ocs_api',
461
+                    'exception' => $e,
462
+                ]
463
+            );
464
+            throw new OCSException($e->getHint(), 107);
465
+        } catch (OCSException $e) {
466
+            $this->logger->warning('Failed addUser attempt with ocs exeption.',
467
+                [
468
+                    'app' => 'ocs_api',
469
+                    'exception' => $e,
470
+                ]
471
+            );
472
+            throw $e;
473
+        } catch (\InvalidArgumentException $e) {
474
+            $this->logger->error('Failed addUser attempt with invalid argument exeption.',
475
+                [
476
+                    'app' => 'ocs_api',
477
+                    'exception' => $e,
478
+                ]
479
+            );
480
+            throw new OCSException($e->getMessage(), 101);
481
+        } catch (\Exception $e) {
482
+            $this->logger->error('Failed addUser attempt with exception.',
483
+                [
484
+                    'app' => 'ocs_api',
485
+                    'exception' => $e
486
+                ]
487
+            );
488
+            throw new OCSException('Bad request', 101);
489
+        }
490
+    }
491
+
492
+    /**
493
+     * @NoAdminRequired
494
+     * @NoSubAdminRequired
495
+     *
496
+     * gets user info
497
+     *
498
+     * @param string $userId
499
+     * @return DataResponse
500
+     * @throws OCSException
501
+     */
502
+    public function getUser(string $userId): DataResponse {
503
+        $includeScopes = false;
504
+        $currentUser = $this->userSession->getUser();
505
+        if ($currentUser && $currentUser->getUID() === $userId) {
506
+            $includeScopes = true;
507
+        }
508
+
509
+        $data = $this->getUserData($userId, $includeScopes);
510
+        // getUserData returns empty array if not enough permissions
511
+        if (empty($data)) {
512
+            throw new OCSException('', OCSController::RESPOND_UNAUTHORISED);
513
+        }
514
+        return new DataResponse($data);
515
+    }
516
+
517
+    /**
518
+     * @NoAdminRequired
519
+     * @NoSubAdminRequired
520
+     *
521
+     * gets user info from the currently logged in user
522
+     *
523
+     * @return DataResponse
524
+     * @throws OCSException
525
+     */
526
+    public function getCurrentUser(): DataResponse {
527
+        $user = $this->userSession->getUser();
528
+        if ($user) {
529
+            $data = $this->getUserData($user->getUID(), true);
530
+            // rename "displayname" to "display-name" only for this call to keep
531
+            // the API stable.
532
+            $data['display-name'] = $data['displayname'];
533
+            unset($data['displayname']);
534
+            return new DataResponse($data);
535
+        }
536
+
537
+        throw new OCSException('', OCSController::RESPOND_UNAUTHORISED);
538
+    }
539
+
540
+    /**
541
+     * @NoAdminRequired
542
+     * @NoSubAdminRequired
543
+     *
544
+     * @return DataResponse
545
+     * @throws OCSException
546
+     */
547
+    public function getEditableFields(?string $userId = null): DataResponse {
548
+        $currentLoggedInUser = $this->userSession->getUser();
549
+        if (!$currentLoggedInUser instanceof IUser) {
550
+            throw new OCSException('', OCSController::RESPOND_NOT_FOUND);
551
+        }
552
+
553
+        $permittedFields = [];
554
+
555
+        if ($userId !== $currentLoggedInUser->getUID()) {
556
+            $targetUser = $this->userManager->get($userId);
557
+            if (!$targetUser instanceof IUser) {
558
+                throw new OCSException('', OCSController::RESPOND_NOT_FOUND);
559
+            }
560
+
561
+            $subAdminManager = $this->groupManager->getSubAdmin();
562
+            if (!$this->groupManager->isAdmin($currentLoggedInUser->getUID())
563
+                && !$subAdminManager->isUserAccessible($currentLoggedInUser, $targetUser)) {
564
+                throw new OCSException('', OCSController::RESPOND_NOT_FOUND);
565
+            }
566
+        } else {
567
+            $targetUser = $currentLoggedInUser;
568
+        }
569
+
570
+        // Editing self (display, email)
571
+        if ($this->config->getSystemValue('allow_user_to_change_display_name', true) !== false) {
572
+            if ($targetUser->getBackend() instanceof ISetDisplayNameBackend
573
+                || $targetUser->getBackend()->implementsActions(Backend::SET_DISPLAYNAME)) {
574
+                $permittedFields[] = IAccountManager::PROPERTY_DISPLAYNAME;
575
+            }
576
+            $permittedFields[] = IAccountManager::PROPERTY_EMAIL;
577
+        }
578
+
579
+        $permittedFields[] = IAccountManager::PROPERTY_PHONE;
580
+        $permittedFields[] = IAccountManager::PROPERTY_ADDRESS;
581
+        $permittedFields[] = IAccountManager::PROPERTY_WEBSITE;
582
+        $permittedFields[] = IAccountManager::PROPERTY_TWITTER;
583
+
584
+        return new DataResponse($permittedFields);
585
+    }
586
+
587
+    /**
588
+     * @NoAdminRequired
589
+     * @NoSubAdminRequired
590
+     * @PasswordConfirmationRequired
591
+     *
592
+     * edit users
593
+     *
594
+     * @param string $userId
595
+     * @param string $key
596
+     * @param string $value
597
+     * @return DataResponse
598
+     * @throws OCSException
599
+     */
600
+    public function editUser(string $userId, string $key, string $value): DataResponse {
601
+        $currentLoggedInUser = $this->userSession->getUser();
602
+
603
+        $targetUser = $this->userManager->get($userId);
604
+        if ($targetUser === null) {
605
+            throw new OCSException('', OCSController::RESPOND_UNAUTHORISED);
606
+        }
607
+
608
+        $permittedFields = [];
609
+        if ($targetUser->getUID() === $currentLoggedInUser->getUID()) {
610
+            // Editing self (display, email)
611
+            if ($this->config->getSystemValue('allow_user_to_change_display_name', true) !== false) {
612
+                if ($targetUser->getBackend() instanceof ISetDisplayNameBackend
613
+                    || $targetUser->getBackend()->implementsActions(Backend::SET_DISPLAYNAME)) {
614
+                    $permittedFields[] = 'display';
615
+                    $permittedFields[] = IAccountManager::PROPERTY_DISPLAYNAME;
616
+                }
617
+                $permittedFields[] = IAccountManager::PROPERTY_EMAIL;
618
+            }
619
+
620
+            $permittedFields[] = IAccountManager::PROPERTY_DISPLAYNAME . self::SCOPE_SUFFIX;
621
+            $permittedFields[] = IAccountManager::PROPERTY_EMAIL . self::SCOPE_SUFFIX;
622
+
623
+            $permittedFields[] = 'password';
624
+            if ($this->config->getSystemValue('force_language', false) === false ||
625
+                $this->groupManager->isAdmin($currentLoggedInUser->getUID())) {
626
+                $permittedFields[] = 'language';
627
+            }
628
+
629
+            if ($this->config->getSystemValue('force_locale', false) === false ||
630
+                $this->groupManager->isAdmin($currentLoggedInUser->getUID())) {
631
+                $permittedFields[] = 'locale';
632
+            }
633
+
634
+            $permittedFields[] = IAccountManager::PROPERTY_PHONE;
635
+            $permittedFields[] = IAccountManager::PROPERTY_ADDRESS;
636
+            $permittedFields[] = IAccountManager::PROPERTY_WEBSITE;
637
+            $permittedFields[] = IAccountManager::PROPERTY_TWITTER;
638
+            $permittedFields[] = IAccountManager::PROPERTY_PHONE . self::SCOPE_SUFFIX;
639
+            $permittedFields[] = IAccountManager::PROPERTY_ADDRESS . self::SCOPE_SUFFIX;
640
+            $permittedFields[] = IAccountManager::PROPERTY_WEBSITE . self::SCOPE_SUFFIX;
641
+            $permittedFields[] = IAccountManager::PROPERTY_TWITTER . self::SCOPE_SUFFIX;
642
+
643
+            $permittedFields[] = IAccountManager::PROPERTY_AVATAR . self::SCOPE_SUFFIX;
644
+
645
+            // If admin they can edit their own quota
646
+            if ($this->groupManager->isAdmin($currentLoggedInUser->getUID())) {
647
+                $permittedFields[] = 'quota';
648
+            }
649
+        } else {
650
+            // Check if admin / subadmin
651
+            $subAdminManager = $this->groupManager->getSubAdmin();
652
+            if ($this->groupManager->isAdmin($currentLoggedInUser->getUID())
653
+            || $subAdminManager->isUserAccessible($currentLoggedInUser, $targetUser)) {
654
+                // They have permissions over the user
655
+                if ($targetUser->getBackend() instanceof ISetDisplayNameBackend
656
+                    || $targetUser->getBackend()->implementsActions(Backend::SET_DISPLAYNAME)) {
657
+                    $permittedFields[] = 'display';
658
+                    $permittedFields[] = IAccountManager::PROPERTY_DISPLAYNAME;
659
+                }
660
+                $permittedFields[] = IAccountManager::PROPERTY_EMAIL;
661
+                $permittedFields[] = 'password';
662
+                $permittedFields[] = 'language';
663
+                $permittedFields[] = 'locale';
664
+                $permittedFields[] = IAccountManager::PROPERTY_PHONE;
665
+                $permittedFields[] = IAccountManager::PROPERTY_ADDRESS;
666
+                $permittedFields[] = IAccountManager::PROPERTY_WEBSITE;
667
+                $permittedFields[] = IAccountManager::PROPERTY_TWITTER;
668
+                $permittedFields[] = 'quota';
669
+            } else {
670
+                // No rights
671
+                throw new OCSException('', OCSController::RESPOND_UNAUTHORISED);
672
+            }
673
+        }
674
+        // Check if permitted to edit this field
675
+        if (!in_array($key, $permittedFields)) {
676
+            throw new OCSException('', OCSController::RESPOND_UNAUTHORISED);
677
+        }
678
+        // Process the edit
679
+        switch ($key) {
680
+            case 'display':
681
+            case IAccountManager::PROPERTY_DISPLAYNAME:
682
+                $targetUser->setDisplayName($value);
683
+                break;
684
+            case 'quota':
685
+                $quota = $value;
686
+                if ($quota !== 'none' && $quota !== 'default') {
687
+                    if (is_numeric($quota)) {
688
+                        $quota = (float) $quota;
689
+                    } else {
690
+                        $quota = \OCP\Util::computerFileSize($quota);
691
+                    }
692
+                    if ($quota === false) {
693
+                        throw new OCSException('Invalid quota value '.$value, 103);
694
+                    }
695
+                    if ($quota === -1) {
696
+                        $quota = 'none';
697
+                    } else {
698
+                        $quota = \OCP\Util::humanFileSize($quota);
699
+                    }
700
+                }
701
+                $targetUser->setQuota($quota);
702
+                break;
703
+            case 'password':
704
+                try {
705
+                    if (!$targetUser->canChangePassword()) {
706
+                        throw new OCSException('Setting the password is not supported by the users backend', 103);
707
+                    }
708
+                    $targetUser->setPassword($value);
709
+                } catch (HintException $e) { // password policy error
710
+                    throw new OCSException($e->getMessage(), 103);
711
+                }
712
+                break;
713
+            case 'language':
714
+                $languagesCodes = $this->l10nFactory->findAvailableLanguages();
715
+                if (!in_array($value, $languagesCodes, true) && $value !== 'en') {
716
+                    throw new OCSException('Invalid language', 102);
717
+                }
718
+                $this->config->setUserValue($targetUser->getUID(), 'core', 'lang', $value);
719
+                break;
720
+            case 'locale':
721
+                if (!$this->l10nFactory->localeExists($value)) {
722
+                    throw new OCSException('Invalid locale', 102);
723
+                }
724
+                $this->config->setUserValue($targetUser->getUID(), 'core', 'locale', $value);
725
+                break;
726
+            case IAccountManager::PROPERTY_EMAIL:
727
+                if (filter_var($value, FILTER_VALIDATE_EMAIL) || $value === '') {
728
+                    $targetUser->setEMailAddress($value);
729
+                } else {
730
+                    throw new OCSException('', 102);
731
+                }
732
+                break;
733
+            case IAccountManager::PROPERTY_PHONE:
734
+            case IAccountManager::PROPERTY_ADDRESS:
735
+            case IAccountManager::PROPERTY_WEBSITE:
736
+            case IAccountManager::PROPERTY_TWITTER:
737
+                $userAccount = $this->accountManager->getUser($targetUser);
738
+                if ($userAccount[$key]['value'] !== $value) {
739
+                    $userAccount[$key]['value'] = $value;
740
+                    try {
741
+                        $this->accountManager->updateUser($targetUser, $userAccount, true);
742
+
743
+                        if ($key === IAccountManager::PROPERTY_PHONE) {
744
+                            $this->knownUserService->deleteByContactUserId($targetUser->getUID());
745
+                        }
746
+                    } catch (\InvalidArgumentException $e) {
747
+                        throw new OCSException('Invalid ' . $e->getMessage(), 102);
748
+                    }
749
+                }
750
+                break;
751
+            case IAccountManager::PROPERTY_DISPLAYNAME . self::SCOPE_SUFFIX:
752
+            case IAccountManager::PROPERTY_EMAIL . self::SCOPE_SUFFIX:
753
+            case IAccountManager::PROPERTY_PHONE . self::SCOPE_SUFFIX:
754
+            case IAccountManager::PROPERTY_ADDRESS . self::SCOPE_SUFFIX:
755
+            case IAccountManager::PROPERTY_WEBSITE . self::SCOPE_SUFFIX:
756
+            case IAccountManager::PROPERTY_TWITTER . self::SCOPE_SUFFIX:
757
+            case IAccountManager::PROPERTY_AVATAR . self::SCOPE_SUFFIX:
758
+                $propertyName = substr($key, 0, strlen($key) - strlen(self::SCOPE_SUFFIX));
759
+                $userAccount = $this->accountManager->getUser($targetUser);
760
+                if ($userAccount[$propertyName]['scope'] !== $value) {
761
+                    $userAccount[$propertyName]['scope'] = $value;
762
+                    try {
763
+                        $this->accountManager->updateUser($targetUser, $userAccount, true);
764
+                    } catch (\InvalidArgumentException $e) {
765
+                        throw new OCSException('Invalid ' . $e->getMessage(), 102);
766
+                    }
767
+                }
768
+                break;
769
+            default:
770
+                throw new OCSException('', 103);
771
+        }
772
+        return new DataResponse();
773
+    }
774
+
775
+    /**
776
+     * @PasswordConfirmationRequired
777
+     * @NoAdminRequired
778
+     *
779
+     * @param string $userId
780
+     *
781
+     * @return DataResponse
782
+     *
783
+     * @throws OCSException
784
+     */
785
+    public function wipeUserDevices(string $userId): DataResponse {
786
+        /** @var IUser $currentLoggedInUser */
787
+        $currentLoggedInUser = $this->userSession->getUser();
788
+
789
+        $targetUser = $this->userManager->get($userId);
790
+
791
+        if ($targetUser === null || $targetUser->getUID() === $currentLoggedInUser->getUID()) {
792
+            throw new OCSException('', 101);
793
+        }
794
+
795
+        // If not permitted
796
+        $subAdminManager = $this->groupManager->getSubAdmin();
797
+        if (!$this->groupManager->isAdmin($currentLoggedInUser->getUID()) && !$subAdminManager->isUserAccessible($currentLoggedInUser, $targetUser)) {
798
+            throw new OCSException('', OCSController::RESPOND_UNAUTHORISED);
799
+        }
800
+
801
+        $this->remoteWipe->markAllTokensForWipe($targetUser);
802
+
803
+        return new DataResponse();
804
+    }
805
+
806
+    /**
807
+     * @PasswordConfirmationRequired
808
+     * @NoAdminRequired
809
+     *
810
+     * @param string $userId
811
+     * @return DataResponse
812
+     * @throws OCSException
813
+     */
814
+    public function deleteUser(string $userId): DataResponse {
815
+        $currentLoggedInUser = $this->userSession->getUser();
816
+
817
+        $targetUser = $this->userManager->get($userId);
818
+
819
+        if ($targetUser === null || $targetUser->getUID() === $currentLoggedInUser->getUID()) {
820
+            throw new OCSException('', 101);
821
+        }
822
+
823
+        // If not permitted
824
+        $subAdminManager = $this->groupManager->getSubAdmin();
825
+        if (!$this->groupManager->isAdmin($currentLoggedInUser->getUID()) && !$subAdminManager->isUserAccessible($currentLoggedInUser, $targetUser)) {
826
+            throw new OCSException('', OCSController::RESPOND_UNAUTHORISED);
827
+        }
828
+
829
+        // Go ahead with the delete
830
+        if ($targetUser->delete()) {
831
+            return new DataResponse();
832
+        } else {
833
+            throw new OCSException('', 101);
834
+        }
835
+    }
836
+
837
+    /**
838
+     * @PasswordConfirmationRequired
839
+     * @NoAdminRequired
840
+     *
841
+     * @param string $userId
842
+     * @return DataResponse
843
+     * @throws OCSException
844
+     * @throws OCSForbiddenException
845
+     */
846
+    public function disableUser(string $userId): DataResponse {
847
+        return $this->setEnabled($userId, false);
848
+    }
849
+
850
+    /**
851
+     * @PasswordConfirmationRequired
852
+     * @NoAdminRequired
853
+     *
854
+     * @param string $userId
855
+     * @return DataResponse
856
+     * @throws OCSException
857
+     * @throws OCSForbiddenException
858
+     */
859
+    public function enableUser(string $userId): DataResponse {
860
+        return $this->setEnabled($userId, true);
861
+    }
862
+
863
+    /**
864
+     * @param string $userId
865
+     * @param bool $value
866
+     * @return DataResponse
867
+     * @throws OCSException
868
+     */
869
+    private function setEnabled(string $userId, bool $value): DataResponse {
870
+        $currentLoggedInUser = $this->userSession->getUser();
871
+
872
+        $targetUser = $this->userManager->get($userId);
873
+        if ($targetUser === null || $targetUser->getUID() === $currentLoggedInUser->getUID()) {
874
+            throw new OCSException('', 101);
875
+        }
876
+
877
+        // If not permitted
878
+        $subAdminManager = $this->groupManager->getSubAdmin();
879
+        if (!$this->groupManager->isAdmin($currentLoggedInUser->getUID()) && !$subAdminManager->isUserAccessible($currentLoggedInUser, $targetUser)) {
880
+            throw new OCSException('', OCSController::RESPOND_UNAUTHORISED);
881
+        }
882
+
883
+        // enable/disable the user now
884
+        $targetUser->setEnabled($value);
885
+        return new DataResponse();
886
+    }
887
+
888
+    /**
889
+     * @NoAdminRequired
890
+     * @NoSubAdminRequired
891
+     *
892
+     * @param string $userId
893
+     * @return DataResponse
894
+     * @throws OCSException
895
+     */
896
+    public function getUsersGroups(string $userId): DataResponse {
897
+        $loggedInUser = $this->userSession->getUser();
898
+
899
+        $targetUser = $this->userManager->get($userId);
900
+        if ($targetUser === null) {
901
+            throw new OCSException('', OCSController::RESPOND_NOT_FOUND);
902
+        }
903
+
904
+        if ($targetUser->getUID() === $loggedInUser->getUID() || $this->groupManager->isAdmin($loggedInUser->getUID())) {
905
+            // Self lookup or admin lookup
906
+            return new DataResponse([
907
+                'groups' => $this->groupManager->getUserGroupIds($targetUser)
908
+            ]);
909
+        } else {
910
+            $subAdminManager = $this->groupManager->getSubAdmin();
911
+
912
+            // Looking up someone else
913
+            if ($subAdminManager->isUserAccessible($loggedInUser, $targetUser)) {
914
+                // Return the group that the method caller is subadmin of for the user in question
915
+                /** @var IGroup[] $getSubAdminsGroups */
916
+                $getSubAdminsGroups = $subAdminManager->getSubAdminsGroups($loggedInUser);
917
+                foreach ($getSubAdminsGroups as $key => $group) {
918
+                    $getSubAdminsGroups[$key] = $group->getGID();
919
+                }
920
+                $groups = array_intersect(
921
+                    $getSubAdminsGroups,
922
+                    $this->groupManager->getUserGroupIds($targetUser)
923
+                );
924
+                return new DataResponse(['groups' => $groups]);
925
+            } else {
926
+                // Not permitted
927
+                throw new OCSException('', OCSController::RESPOND_UNAUTHORISED);
928
+            }
929
+        }
930
+    }
931
+
932
+    /**
933
+     * @PasswordConfirmationRequired
934
+     * @NoAdminRequired
935
+     *
936
+     * @param string $userId
937
+     * @param string $groupid
938
+     * @return DataResponse
939
+     * @throws OCSException
940
+     */
941
+    public function addToGroup(string $userId, string $groupid = ''): DataResponse {
942
+        if ($groupid === '') {
943
+            throw new OCSException('', 101);
944
+        }
945
+
946
+        $group = $this->groupManager->get($groupid);
947
+        $targetUser = $this->userManager->get($userId);
948
+        if ($group === null) {
949
+            throw new OCSException('', 102);
950
+        }
951
+        if ($targetUser === null) {
952
+            throw new OCSException('', 103);
953
+        }
954
+
955
+        // If they're not an admin, check they are a subadmin of the group in question
956
+        $loggedInUser = $this->userSession->getUser();
957
+        $subAdminManager = $this->groupManager->getSubAdmin();
958
+        if (!$this->groupManager->isAdmin($loggedInUser->getUID()) && !$subAdminManager->isSubAdminOfGroup($loggedInUser, $group)) {
959
+            throw new OCSException('', 104);
960
+        }
961
+
962
+        // Add user to group
963
+        $group->addUser($targetUser);
964
+        return new DataResponse();
965
+    }
966
+
967
+    /**
968
+     * @PasswordConfirmationRequired
969
+     * @NoAdminRequired
970
+     *
971
+     * @param string $userId
972
+     * @param string $groupid
973
+     * @return DataResponse
974
+     * @throws OCSException
975
+     */
976
+    public function removeFromGroup(string $userId, string $groupid): DataResponse {
977
+        $loggedInUser = $this->userSession->getUser();
978
+
979
+        if ($groupid === null || trim($groupid) === '') {
980
+            throw new OCSException('', 101);
981
+        }
982
+
983
+        $group = $this->groupManager->get($groupid);
984
+        if ($group === null) {
985
+            throw new OCSException('', 102);
986
+        }
987
+
988
+        $targetUser = $this->userManager->get($userId);
989
+        if ($targetUser === null) {
990
+            throw new OCSException('', 103);
991
+        }
992
+
993
+        // If they're not an admin, check they are a subadmin of the group in question
994
+        $subAdminManager = $this->groupManager->getSubAdmin();
995
+        if (!$this->groupManager->isAdmin($loggedInUser->getUID()) && !$subAdminManager->isSubAdminOfGroup($loggedInUser, $group)) {
996
+            throw new OCSException('', 104);
997
+        }
998
+
999
+        // Check they aren't removing themselves from 'admin' or their 'subadmin; group
1000
+        if ($targetUser->getUID() === $loggedInUser->getUID()) {
1001
+            if ($this->groupManager->isAdmin($loggedInUser->getUID())) {
1002
+                if ($group->getGID() === 'admin') {
1003
+                    throw new OCSException('Cannot remove yourself from the admin group', 105);
1004
+                }
1005
+            } else {
1006
+                // Not an admin, so the user must be a subadmin of this group, but that is not allowed.
1007
+                throw new OCSException('Cannot remove yourself from this group as you are a SubAdmin', 105);
1008
+            }
1009
+        } elseif (!$this->groupManager->isAdmin($loggedInUser->getUID())) {
1010
+            /** @var IGroup[] $subAdminGroups */
1011
+            $subAdminGroups = $subAdminManager->getSubAdminsGroups($loggedInUser);
1012
+            $subAdminGroups = array_map(function (IGroup $subAdminGroup) {
1013
+                return $subAdminGroup->getGID();
1014
+            }, $subAdminGroups);
1015
+            $userGroups = $this->groupManager->getUserGroupIds($targetUser);
1016
+            $userSubAdminGroups = array_intersect($subAdminGroups, $userGroups);
1017
+
1018
+            if (count($userSubAdminGroups) <= 1) {
1019
+                // Subadmin must not be able to remove a user from all their subadmin groups.
1020
+                throw new OCSException('Not viable to remove user from the last group you are SubAdmin of', 105);
1021
+            }
1022
+        }
1023
+
1024
+        // Remove user from group
1025
+        $group->removeUser($targetUser);
1026
+        return new DataResponse();
1027
+    }
1028
+
1029
+    /**
1030
+     * Creates a subadmin
1031
+     *
1032
+     * @PasswordConfirmationRequired
1033
+     *
1034
+     * @param string $userId
1035
+     * @param string $groupid
1036
+     * @return DataResponse
1037
+     * @throws OCSException
1038
+     */
1039
+    public function addSubAdmin(string $userId, string $groupid): DataResponse {
1040
+        $group = $this->groupManager->get($groupid);
1041
+        $user = $this->userManager->get($userId);
1042
+
1043
+        // Check if the user exists
1044
+        if ($user === null) {
1045
+            throw new OCSException('User does not exist', 101);
1046
+        }
1047
+        // Check if group exists
1048
+        if ($group === null) {
1049
+            throw new OCSException('Group does not exist',  102);
1050
+        }
1051
+        // Check if trying to make subadmin of admin group
1052
+        if ($group->getGID() === 'admin') {
1053
+            throw new OCSException('Cannot create subadmins for admin group', 103);
1054
+        }
1055
+
1056
+        $subAdminManager = $this->groupManager->getSubAdmin();
1057
+
1058
+        // We cannot be subadmin twice
1059
+        if ($subAdminManager->isSubAdminOfGroup($user, $group)) {
1060
+            return new DataResponse();
1061
+        }
1062
+        // Go
1063
+        $subAdminManager->createSubAdmin($user, $group);
1064
+        return new DataResponse();
1065
+    }
1066
+
1067
+    /**
1068
+     * Removes a subadmin from a group
1069
+     *
1070
+     * @PasswordConfirmationRequired
1071
+     *
1072
+     * @param string $userId
1073
+     * @param string $groupid
1074
+     * @return DataResponse
1075
+     * @throws OCSException
1076
+     */
1077
+    public function removeSubAdmin(string $userId, string $groupid): DataResponse {
1078
+        $group = $this->groupManager->get($groupid);
1079
+        $user = $this->userManager->get($userId);
1080
+        $subAdminManager = $this->groupManager->getSubAdmin();
1081
+
1082
+        // Check if the user exists
1083
+        if ($user === null) {
1084
+            throw new OCSException('User does not exist', 101);
1085
+        }
1086
+        // Check if the group exists
1087
+        if ($group === null) {
1088
+            throw new OCSException('Group does not exist', 101);
1089
+        }
1090
+        // Check if they are a subadmin of this said group
1091
+        if (!$subAdminManager->isSubAdminOfGroup($user, $group)) {
1092
+            throw new OCSException('User is not a subadmin of this group', 102);
1093
+        }
1094
+
1095
+        // Go
1096
+        $subAdminManager->deleteSubAdmin($user, $group);
1097
+        return new DataResponse();
1098
+    }
1099
+
1100
+    /**
1101
+     * Get the groups a user is a subadmin of
1102
+     *
1103
+     * @param string $userId
1104
+     * @return DataResponse
1105
+     * @throws OCSException
1106
+     */
1107
+    public function getUserSubAdminGroups(string $userId): DataResponse {
1108
+        $groups = $this->getUserSubAdminGroupsData($userId);
1109
+        return new DataResponse($groups);
1110
+    }
1111
+
1112
+    /**
1113
+     * @NoAdminRequired
1114
+     * @PasswordConfirmationRequired
1115
+     *
1116
+     * resend welcome message
1117
+     *
1118
+     * @param string $userId
1119
+     * @return DataResponse
1120
+     * @throws OCSException
1121
+     */
1122
+    public function resendWelcomeMessage(string $userId): DataResponse {
1123
+        $currentLoggedInUser = $this->userSession->getUser();
1124
+
1125
+        $targetUser = $this->userManager->get($userId);
1126
+        if ($targetUser === null) {
1127
+            throw new OCSException('', OCSController::RESPOND_NOT_FOUND);
1128
+        }
1129
+
1130
+        // Check if admin / subadmin
1131
+        $subAdminManager = $this->groupManager->getSubAdmin();
1132
+        if (!$subAdminManager->isUserAccessible($currentLoggedInUser, $targetUser)
1133
+            && !$this->groupManager->isAdmin($currentLoggedInUser->getUID())) {
1134
+            // No rights
1135
+            throw new OCSException('', OCSController::RESPOND_UNAUTHORISED);
1136
+        }
1137
+
1138
+        $email = $targetUser->getEMailAddress();
1139
+        if ($email === '' || $email === null) {
1140
+            throw new OCSException('Email address not available', 101);
1141
+        }
1142
+
1143
+        try {
1144
+            $emailTemplate = $this->newUserMailHelper->generateTemplate($targetUser, false);
1145
+            $this->newUserMailHelper->sendMail($targetUser, $emailTemplate);
1146
+        } catch (\Exception $e) {
1147
+            $this->logger->error("Can't send new user mail to $email",
1148
+                [
1149
+                    'app' => 'settings',
1150
+                    'exception' => $e,
1151
+                ]
1152
+            );
1153
+            throw new OCSException('Sending email failed', 102);
1154
+        }
1155
+
1156
+        return new DataResponse();
1157
+    }
1158 1158
 }
Please login to merge, or discard this patch.
apps/provisioning_api/appinfo/routes.php 1 patch
Indentation   +44 added lines, -44 removed lines patch added patch discarded remove patch
@@ -27,51 +27,51 @@
 block discarded – undo
27 27
  */
28 28
 
29 29
 return [
30
-	'ocs' => [
31
-		// Apps
32
-		['root' => '/cloud', 'name' => 'Apps#getApps', 'url' => '/apps', 'verb' => 'GET'],
33
-		['root' => '/cloud', 'name' => 'Apps#getAppInfo', 'url' => '/apps/{app}', 'verb' => 'GET'],
34
-		['root' => '/cloud', 'name' => 'Apps#enable', 'url' => '/apps/{app}', 'verb' => 'POST'],
35
-		['root' => '/cloud', 'name' => 'Apps#disable', 'url' => '/apps/{app}', 'verb' => 'DELETE'],
30
+    'ocs' => [
31
+        // Apps
32
+        ['root' => '/cloud', 'name' => 'Apps#getApps', 'url' => '/apps', 'verb' => 'GET'],
33
+        ['root' => '/cloud', 'name' => 'Apps#getAppInfo', 'url' => '/apps/{app}', 'verb' => 'GET'],
34
+        ['root' => '/cloud', 'name' => 'Apps#enable', 'url' => '/apps/{app}', 'verb' => 'POST'],
35
+        ['root' => '/cloud', 'name' => 'Apps#disable', 'url' => '/apps/{app}', 'verb' => 'DELETE'],
36 36
 
37
-		// Groups
38
-		['root' => '/cloud', 'name' => 'Groups#getGroups', 'url' => '/groups', 'verb' => 'GET'],
39
-		['root' => '/cloud', 'name' => 'Groups#getGroupsDetails', 'url' => '/groups/details', 'verb' => 'GET'],
40
-		['root' => '/cloud', 'name' => 'Groups#getGroupUsers', 'url' => '/groups/{groupId}/users', 'verb' => 'GET', 'requirements' => ['groupId' => '.+']],
41
-		['root' => '/cloud', 'name' => 'Groups#getGroupUsersDetails', 'url' => '/groups/{groupId}/users/details', 'verb' => 'GET', 'requirements' => ['groupId' => '.+']],
42
-		['root' => '/cloud', 'name' => 'Groups#getSubAdminsOfGroup', 'url' => '/groups/{groupId}/subadmins', 'verb' => 'GET', 'requirements' => ['groupId' => '.+']],
43
-		['root' => '/cloud', 'name' => 'Groups#addGroup', 'url' => '/groups', 'verb' => 'POST'],
44
-		['root' => '/cloud', 'name' => 'Groups#getGroup', 'url' => '/groups/{groupId}', 'verb' => 'GET', 'requirements' => ['groupId' => '.+']],
45
-		['root' => '/cloud', 'name' => 'Groups#updateGroup', 'url' => '/groups/{groupId}', 'verb' => 'PUT', 'requirements' => ['groupId' => '.+']],
46
-		['root' => '/cloud', 'name' => 'Groups#deleteGroup', 'url' => '/groups/{groupId}', 'verb' => 'DELETE', 'requirements' => ['groupId' => '.+']],
37
+        // Groups
38
+        ['root' => '/cloud', 'name' => 'Groups#getGroups', 'url' => '/groups', 'verb' => 'GET'],
39
+        ['root' => '/cloud', 'name' => 'Groups#getGroupsDetails', 'url' => '/groups/details', 'verb' => 'GET'],
40
+        ['root' => '/cloud', 'name' => 'Groups#getGroupUsers', 'url' => '/groups/{groupId}/users', 'verb' => 'GET', 'requirements' => ['groupId' => '.+']],
41
+        ['root' => '/cloud', 'name' => 'Groups#getGroupUsersDetails', 'url' => '/groups/{groupId}/users/details', 'verb' => 'GET', 'requirements' => ['groupId' => '.+']],
42
+        ['root' => '/cloud', 'name' => 'Groups#getSubAdminsOfGroup', 'url' => '/groups/{groupId}/subadmins', 'verb' => 'GET', 'requirements' => ['groupId' => '.+']],
43
+        ['root' => '/cloud', 'name' => 'Groups#addGroup', 'url' => '/groups', 'verb' => 'POST'],
44
+        ['root' => '/cloud', 'name' => 'Groups#getGroup', 'url' => '/groups/{groupId}', 'verb' => 'GET', 'requirements' => ['groupId' => '.+']],
45
+        ['root' => '/cloud', 'name' => 'Groups#updateGroup', 'url' => '/groups/{groupId}', 'verb' => 'PUT', 'requirements' => ['groupId' => '.+']],
46
+        ['root' => '/cloud', 'name' => 'Groups#deleteGroup', 'url' => '/groups/{groupId}', 'verb' => 'DELETE', 'requirements' => ['groupId' => '.+']],
47 47
 
48
-		// Users
49
-		['root' => '/cloud', 'name' => 'Users#getUsers', 'url' => '/users', 'verb' => 'GET'],
50
-		['root' => '/cloud', 'name' => 'Users#getUsersDetails', 'url' => '/users/details', 'verb' => 'GET'],
51
-		['root' => '/cloud', 'name' => 'Users#searchByPhoneNumbers', 'url' => '/users/search/by-phone', 'verb' => 'POST'],
52
-		['root' => '/cloud', 'name' => 'Users#addUser', 'url' => '/users', 'verb' => 'POST'],
53
-		['root' => '/cloud', 'name' => 'Users#getUser', 'url' => '/users/{userId}', 'verb' => 'GET'],
54
-		['root' => '/cloud', 'name' => 'Users#getCurrentUser', 'url' => '/user', 'verb' => 'GET'],
55
-		['root' => '/cloud', 'name' => 'Users#getEditableFields', 'url' => '/user/fields', 'verb' => 'GET'],
56
-		['root' => '/cloud', 'name' => 'Users#getEditableFields', 'url' => '/user/fields/{userId}', 'verb' => 'GET'],
57
-		['root' => '/cloud', 'name' => 'Users#editUser', 'url' => '/users/{userId}', 'verb' => 'PUT'],
58
-		['root' => '/cloud', 'name' => 'Users#wipeUserDevices', 'url' => '/users/{userId}/wipe', 'verb' => 'POST'],
59
-		['root' => '/cloud', 'name' => 'Users#deleteUser', 'url' => '/users/{userId}', 'verb' => 'DELETE'],
60
-		['root' => '/cloud', 'name' => 'Users#enableUser', 'url' => '/users/{userId}/enable', 'verb' => 'PUT'],
61
-		['root' => '/cloud', 'name' => 'Users#disableUser', 'url' => '/users/{userId}/disable', 'verb' => 'PUT'],
62
-		['root' => '/cloud', 'name' => 'Users#getUsersGroups', 'url' => '/users/{userId}/groups', 'verb' => 'GET'],
63
-		['root' => '/cloud', 'name' => 'Users#addToGroup', 'url' => '/users/{userId}/groups', 'verb' => 'POST'],
64
-		['root' => '/cloud', 'name' => 'Users#removeFromGroup', 'url' => '/users/{userId}/groups', 'verb' => 'DELETE'],
65
-		['root' => '/cloud', 'name' => 'Users#getUserSubAdminGroups', 'url' => '/users/{userId}/subadmins', 'verb' => 'GET'],
66
-		['root' => '/cloud', 'name' => 'Users#addSubAdmin', 'url' => '/users/{userId}/subadmins', 'verb' => 'POST'],
67
-		['root' => '/cloud', 'name' => 'Users#removeSubAdmin', 'url' => '/users/{userId}/subadmins', 'verb' => 'DELETE'],
68
-		['root' => '/cloud', 'name' => 'Users#resendWelcomeMessage', 'url' => '/users/{userId}/welcome', 'verb' => 'POST'],
48
+        // Users
49
+        ['root' => '/cloud', 'name' => 'Users#getUsers', 'url' => '/users', 'verb' => 'GET'],
50
+        ['root' => '/cloud', 'name' => 'Users#getUsersDetails', 'url' => '/users/details', 'verb' => 'GET'],
51
+        ['root' => '/cloud', 'name' => 'Users#searchByPhoneNumbers', 'url' => '/users/search/by-phone', 'verb' => 'POST'],
52
+        ['root' => '/cloud', 'name' => 'Users#addUser', 'url' => '/users', 'verb' => 'POST'],
53
+        ['root' => '/cloud', 'name' => 'Users#getUser', 'url' => '/users/{userId}', 'verb' => 'GET'],
54
+        ['root' => '/cloud', 'name' => 'Users#getCurrentUser', 'url' => '/user', 'verb' => 'GET'],
55
+        ['root' => '/cloud', 'name' => 'Users#getEditableFields', 'url' => '/user/fields', 'verb' => 'GET'],
56
+        ['root' => '/cloud', 'name' => 'Users#getEditableFields', 'url' => '/user/fields/{userId}', 'verb' => 'GET'],
57
+        ['root' => '/cloud', 'name' => 'Users#editUser', 'url' => '/users/{userId}', 'verb' => 'PUT'],
58
+        ['root' => '/cloud', 'name' => 'Users#wipeUserDevices', 'url' => '/users/{userId}/wipe', 'verb' => 'POST'],
59
+        ['root' => '/cloud', 'name' => 'Users#deleteUser', 'url' => '/users/{userId}', 'verb' => 'DELETE'],
60
+        ['root' => '/cloud', 'name' => 'Users#enableUser', 'url' => '/users/{userId}/enable', 'verb' => 'PUT'],
61
+        ['root' => '/cloud', 'name' => 'Users#disableUser', 'url' => '/users/{userId}/disable', 'verb' => 'PUT'],
62
+        ['root' => '/cloud', 'name' => 'Users#getUsersGroups', 'url' => '/users/{userId}/groups', 'verb' => 'GET'],
63
+        ['root' => '/cloud', 'name' => 'Users#addToGroup', 'url' => '/users/{userId}/groups', 'verb' => 'POST'],
64
+        ['root' => '/cloud', 'name' => 'Users#removeFromGroup', 'url' => '/users/{userId}/groups', 'verb' => 'DELETE'],
65
+        ['root' => '/cloud', 'name' => 'Users#getUserSubAdminGroups', 'url' => '/users/{userId}/subadmins', 'verb' => 'GET'],
66
+        ['root' => '/cloud', 'name' => 'Users#addSubAdmin', 'url' => '/users/{userId}/subadmins', 'verb' => 'POST'],
67
+        ['root' => '/cloud', 'name' => 'Users#removeSubAdmin', 'url' => '/users/{userId}/subadmins', 'verb' => 'DELETE'],
68
+        ['root' => '/cloud', 'name' => 'Users#resendWelcomeMessage', 'url' => '/users/{userId}/welcome', 'verb' => 'POST'],
69 69
 
70
-		// Config
71
-		['name' => 'AppConfig#getApps', 'url' => '/api/v1/config/apps', 'verb' => 'GET'],
72
-		['name' => 'AppConfig#getKeys', 'url' => '/api/v1/config/apps/{app}', 'verb' => 'GET'],
73
-		['name' => 'AppConfig#getValue', 'url' => '/api/v1/config/apps/{app}/{key}', 'verb' => 'GET'],
74
-		['name' => 'AppConfig#setValue', 'url' => '/api/v1/config/apps/{app}/{key}', 'verb' => 'POST'],
75
-		['name' => 'AppConfig#deleteKey', 'url' => '/api/v1/config/apps/{app}/{key}', 'verb' => 'DELETE'],
76
-	],
70
+        // Config
71
+        ['name' => 'AppConfig#getApps', 'url' => '/api/v1/config/apps', 'verb' => 'GET'],
72
+        ['name' => 'AppConfig#getKeys', 'url' => '/api/v1/config/apps/{app}', 'verb' => 'GET'],
73
+        ['name' => 'AppConfig#getValue', 'url' => '/api/v1/config/apps/{app}/{key}', 'verb' => 'GET'],
74
+        ['name' => 'AppConfig#setValue', 'url' => '/api/v1/config/apps/{app}/{key}', 'verb' => 'POST'],
75
+        ['name' => 'AppConfig#deleteKey', 'url' => '/api/v1/config/apps/{app}/{key}', 'verb' => 'DELETE'],
76
+    ],
77 77
 ];
Please login to merge, or discard this patch.