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