Passed
Push — master ( a1aa44...b14ae5 )
by Blizzz
24:39 queued 14s
created
apps/provisioning_api/lib/Controller/UsersController.php 1 patch
Indentation   +1343 added lines, -1343 removed lines patch added patch discarded remove patch
@@ -78,1347 +78,1347 @@
 block discarded – undo
78 78
 use Psr\Log\LoggerInterface;
79 79
 
80 80
 class UsersController extends AUserData {
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(
99
-		string $appName,
100
-		IRequest $request,
101
-		IUserManager $userManager,
102
-		IConfig $config,
103
-		IGroupManager $groupManager,
104
-		IUserSession $userSession,
105
-		IAccountManager $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
-	) {
115
-		parent::__construct(
116
-			$appName,
117
-			$request,
118
-			$userManager,
119
-			$config,
120
-			$groupManager,
121
-			$userSession,
122
-			$accountManager,
123
-			$l10nFactory
124
-		);
125
-
126
-		$this->urlGenerator = $urlGenerator;
127
-		$this->logger = $logger;
128
-		$this->l10nFactory = $l10nFactory;
129
-		$this->newUserMailHelper = $newUserMailHelper;
130
-		$this->secureRandom = $secureRandom;
131
-		$this->remoteWipe = $remoteWipe;
132
-		$this->knownUserService = $knownUserService;
133
-		$this->eventDispatcher = $eventDispatcher;
134
-	}
135
-
136
-	/**
137
-	 * @NoAdminRequired
138
-	 *
139
-	 * returns a list of users
140
-	 *
141
-	 * @param string $search
142
-	 * @param int $limit
143
-	 * @param int $offset
144
-	 * @return DataResponse
145
-	 */
146
-	public function getUsers(string $search = '', int $limit = null, int $offset = 0): DataResponse {
147
-		$user = $this->userSession->getUser();
148
-		$users = [];
149
-
150
-		// Admin? Or SubAdmin?
151
-		$uid = $user->getUID();
152
-		$subAdminManager = $this->groupManager->getSubAdmin();
153
-		if ($this->groupManager->isAdmin($uid)) {
154
-			$users = $this->userManager->search($search, $limit, $offset);
155
-		} elseif ($subAdminManager->isSubAdmin($user)) {
156
-			$subAdminOfGroups = $subAdminManager->getSubAdminsGroups($user);
157
-			foreach ($subAdminOfGroups as $key => $group) {
158
-				$subAdminOfGroups[$key] = $group->getGID();
159
-			}
160
-
161
-			$users = [];
162
-			foreach ($subAdminOfGroups as $group) {
163
-				$users = array_merge($users, $this->groupManager->displayNamesInGroup($group, $search, $limit, $offset));
164
-			}
165
-		}
166
-
167
-		$users = array_keys($users);
168
-
169
-		return new DataResponse([
170
-			'users' => $users
171
-		]);
172
-	}
173
-
174
-	/**
175
-	 * @NoAdminRequired
176
-	 *
177
-	 * returns a list of users and their data
178
-	 */
179
-	public function getUsersDetails(string $search = '', int $limit = null, int $offset = 0): DataResponse {
180
-		$currentUser = $this->userSession->getUser();
181
-		$users = [];
182
-
183
-		// Admin? Or SubAdmin?
184
-		$uid = $currentUser->getUID();
185
-		$subAdminManager = $this->groupManager->getSubAdmin();
186
-		if ($this->groupManager->isAdmin($uid)) {
187
-			$users = $this->userManager->search($search, $limit, $offset);
188
-			$users = array_keys($users);
189
-		} elseif ($subAdminManager->isSubAdmin($currentUser)) {
190
-			$subAdminOfGroups = $subAdminManager->getSubAdminsGroups($currentUser);
191
-			foreach ($subAdminOfGroups as $key => $group) {
192
-				$subAdminOfGroups[$key] = $group->getGID();
193
-			}
194
-
195
-			$users = [];
196
-			foreach ($subAdminOfGroups as $group) {
197
-				$users[] = array_keys($this->groupManager->displayNamesInGroup($group, $search, $limit, $offset));
198
-			}
199
-			$users = array_merge(...$users);
200
-		}
201
-
202
-		$usersDetails = [];
203
-		foreach ($users as $userId) {
204
-			$userId = (string) $userId;
205
-			$userData = $this->getUserData($userId);
206
-			// Do not insert empty entry
207
-			if (!empty($userData)) {
208
-				$usersDetails[$userId] = $userData;
209
-			} else {
210
-				// Logged user does not have permissions to see this user
211
-				// only showing its id
212
-				$usersDetails[$userId] = ['id' => $userId];
213
-			}
214
-		}
215
-
216
-		return new DataResponse([
217
-			'users' => $usersDetails
218
-		]);
219
-	}
220
-
221
-
222
-	/**
223
-	 * @NoAdminRequired
224
-	 * @NoSubAdminRequired
225
-	 *
226
-	 * @param string $location
227
-	 * @param array $search
228
-	 * @return DataResponse
229
-	 */
230
-	public function searchByPhoneNumbers(string $location, array $search): DataResponse {
231
-		$phoneUtil = PhoneNumberUtil::getInstance();
232
-
233
-		if ($phoneUtil->getCountryCodeForRegion($location) === 0) {
234
-			// Not a valid region code
235
-			return new DataResponse([], Http::STATUS_BAD_REQUEST);
236
-		}
237
-
238
-		/** @var IUser $user */
239
-		$user = $this->userSession->getUser();
240
-		$knownTo = $user->getUID();
241
-		$defaultPhoneRegion = $this->config->getSystemValueString('default_phone_region');
242
-
243
-		$normalizedNumberToKey = [];
244
-		foreach ($search as $key => $phoneNumbers) {
245
-			foreach ($phoneNumbers as $phone) {
246
-				try {
247
-					$phoneNumber = $phoneUtil->parse($phone, $location);
248
-					if ($phoneNumber instanceof PhoneNumber && $phoneUtil->isValidNumber($phoneNumber)) {
249
-						$normalizedNumber = $phoneUtil->format($phoneNumber, PhoneNumberFormat::E164);
250
-						$normalizedNumberToKey[$normalizedNumber] = (string) $key;
251
-					}
252
-				} catch (NumberParseException $e) {
253
-				}
254
-
255
-				if ($defaultPhoneRegion !== '' && $defaultPhoneRegion !== $location && strpos($phone, '0') === 0) {
256
-					// If the number has a leading zero (no country code),
257
-					// we also check the default phone region of the instance,
258
-					// when it's different to the user's given region.
259
-					try {
260
-						$phoneNumber = $phoneUtil->parse($phone, $defaultPhoneRegion);
261
-						if ($phoneNumber instanceof PhoneNumber && $phoneUtil->isValidNumber($phoneNumber)) {
262
-							$normalizedNumber = $phoneUtil->format($phoneNumber, PhoneNumberFormat::E164);
263
-							$normalizedNumberToKey[$normalizedNumber] = (string) $key;
264
-						}
265
-					} catch (NumberParseException $e) {
266
-					}
267
-				}
268
-			}
269
-		}
270
-
271
-		$phoneNumbers = array_keys($normalizedNumberToKey);
272
-
273
-		if (empty($phoneNumbers)) {
274
-			return new DataResponse();
275
-		}
276
-
277
-		// Cleanup all previous entries and only allow new matches
278
-		$this->knownUserService->deleteKnownTo($knownTo);
279
-
280
-		$userMatches = $this->accountManager->searchUsers(IAccountManager::PROPERTY_PHONE, $phoneNumbers);
281
-
282
-		if (empty($userMatches)) {
283
-			return new DataResponse();
284
-		}
285
-
286
-		$cloudUrl = rtrim($this->urlGenerator->getAbsoluteURL('/'), '/');
287
-		if (strpos($cloudUrl, 'http://') === 0) {
288
-			$cloudUrl = substr($cloudUrl, strlen('http://'));
289
-		} elseif (strpos($cloudUrl, 'https://') === 0) {
290
-			$cloudUrl = substr($cloudUrl, strlen('https://'));
291
-		}
292
-
293
-		$matches = [];
294
-		foreach ($userMatches as $phone => $userId) {
295
-			// Not using the ICloudIdManager as that would run a search for each contact to find the display name in the address book
296
-			$matches[$normalizedNumberToKey[$phone]] = $userId . '@' . $cloudUrl;
297
-			$this->knownUserService->storeIsKnownToUser($knownTo, $userId);
298
-		}
299
-
300
-		return new DataResponse($matches);
301
-	}
302
-
303
-	/**
304
-	 * @throws OCSException
305
-	 */
306
-	private function createNewUserId(): string {
307
-		$attempts = 0;
308
-		do {
309
-			$uidCandidate = $this->secureRandom->generate(10, ISecureRandom::CHAR_HUMAN_READABLE);
310
-			if (!$this->userManager->userExists($uidCandidate)) {
311
-				return $uidCandidate;
312
-			}
313
-			$attempts++;
314
-		} while ($attempts < 10);
315
-		throw new OCSException('Could not create non-existing user id', 111);
316
-	}
317
-
318
-	/**
319
-	 * @PasswordConfirmationRequired
320
-	 * @NoAdminRequired
321
-	 *
322
-	 * @param string $userid
323
-	 * @param string $password
324
-	 * @param string $displayName
325
-	 * @param string $email
326
-	 * @param array $groups
327
-	 * @param array $subadmin
328
-	 * @param string $quota
329
-	 * @param string $language
330
-	 * @return DataResponse
331
-	 * @throws OCSException
332
-	 */
333
-	public function addUser(
334
-		string $userid,
335
-		string $password = '',
336
-		string $displayName = '',
337
-		string $email = '',
338
-		array $groups = [],
339
-		array $subadmin = [],
340
-		string $quota = '',
341
-		string $language = ''
342
-	): DataResponse {
343
-		$user = $this->userSession->getUser();
344
-		$isAdmin = $this->groupManager->isAdmin($user->getUID());
345
-		$subAdminManager = $this->groupManager->getSubAdmin();
346
-
347
-		if (empty($userid) && $this->config->getAppValue('core', 'newUser.generateUserID', 'no') === 'yes') {
348
-			$userid = $this->createNewUserId();
349
-		}
350
-
351
-		if ($this->userManager->userExists($userid)) {
352
-			$this->logger->error('Failed addUser attempt: User already exists.', ['app' => 'ocs_api']);
353
-			throw new OCSException($this->l10nFactory->get('provisioning_api')->t('User already exists'), 102);
354
-		}
355
-
356
-		if ($groups !== []) {
357
-			foreach ($groups as $group) {
358
-				if (!$this->groupManager->groupExists($group)) {
359
-					throw new OCSException('group ' . $group . ' does not exist', 104);
360
-				}
361
-				if (!$isAdmin && !$subAdminManager->isSubAdminOfGroup($user, $this->groupManager->get($group))) {
362
-					throw new OCSException('insufficient privileges for group ' . $group, 105);
363
-				}
364
-			}
365
-		} else {
366
-			if (!$isAdmin) {
367
-				throw new OCSException('no group specified (required for subadmins)', 106);
368
-			}
369
-		}
370
-
371
-		$subadminGroups = [];
372
-		if ($subadmin !== []) {
373
-			foreach ($subadmin as $groupid) {
374
-				$group = $this->groupManager->get($groupid);
375
-				// Check if group exists
376
-				if ($group === null) {
377
-					throw new OCSException('Subadmin group does not exist', 102);
378
-				}
379
-				// Check if trying to make subadmin of admin group
380
-				if ($group->getGID() === 'admin') {
381
-					throw new OCSException('Cannot create subadmins for admin group', 103);
382
-				}
383
-				// Check if has permission to promote subadmins
384
-				if (!$subAdminManager->isSubAdminOfGroup($user, $group) && !$isAdmin) {
385
-					throw new OCSForbiddenException('No permissions to promote subadmins');
386
-				}
387
-				$subadminGroups[] = $group;
388
-			}
389
-		}
390
-
391
-		$generatePasswordResetToken = false;
392
-		if (strlen($password) > IUserManager::MAX_PASSWORD_LENGTH) {
393
-			throw new OCSException('Invalid password value', 101);
394
-		}
395
-		if ($password === '') {
396
-			if ($email === '') {
397
-				throw new OCSException('To send a password link to the user an email address is required.', 108);
398
-			}
399
-
400
-			$passwordEvent = new GenerateSecurePasswordEvent();
401
-			$this->eventDispatcher->dispatchTyped($passwordEvent);
402
-
403
-			$password = $passwordEvent->getPassword();
404
-			if ($password === null) {
405
-				// Fallback: ensure to pass password_policy in any case
406
-				$password = $this->secureRandom->generate(10)
407
-					. $this->secureRandom->generate(1, ISecureRandom::CHAR_UPPER)
408
-					. $this->secureRandom->generate(1, ISecureRandom::CHAR_LOWER)
409
-					. $this->secureRandom->generate(1, ISecureRandom::CHAR_DIGITS)
410
-					. $this->secureRandom->generate(1, ISecureRandom::CHAR_SYMBOLS);
411
-			}
412
-			$generatePasswordResetToken = true;
413
-		}
414
-
415
-		if ($email === '' && $this->config->getAppValue('core', 'newUser.requireEmail', 'no') === 'yes') {
416
-			throw new OCSException('Required email address was not provided', 110);
417
-		}
418
-
419
-		try {
420
-			$newUser = $this->userManager->createUser($userid, $password);
421
-			$this->logger->info('Successful addUser call with userid: ' . $userid, ['app' => 'ocs_api']);
422
-
423
-			foreach ($groups as $group) {
424
-				$this->groupManager->get($group)->addUser($newUser);
425
-				$this->logger->info('Added userid ' . $userid . ' to group ' . $group, ['app' => 'ocs_api']);
426
-			}
427
-			foreach ($subadminGroups as $group) {
428
-				$subAdminManager->createSubAdmin($newUser, $group);
429
-			}
430
-
431
-			if ($displayName !== '') {
432
-				try {
433
-					$this->editUser($userid, self::USER_FIELD_DISPLAYNAME, $displayName);
434
-				} catch (OCSException $e) {
435
-					if ($newUser instanceof IUser) {
436
-						$newUser->delete();
437
-					}
438
-					throw $e;
439
-				}
440
-			}
441
-
442
-			if ($quota !== '') {
443
-				$this->editUser($userid, self::USER_FIELD_QUOTA, $quota);
444
-			}
445
-
446
-			if ($language !== '') {
447
-				$this->editUser($userid, self::USER_FIELD_LANGUAGE, $language);
448
-			}
449
-
450
-			// Send new user mail only if a mail is set
451
-			if ($email !== '') {
452
-				$newUser->setEMailAddress($email);
453
-				if ($this->config->getAppValue('core', 'newUser.sendEmail', 'yes') === 'yes') {
454
-					try {
455
-						$emailTemplate = $this->newUserMailHelper->generateTemplate($newUser, $generatePasswordResetToken);
456
-						$this->newUserMailHelper->sendMail($newUser, $emailTemplate);
457
-					} catch (\Exception $e) {
458
-						// Mail could be failing hard or just be plain not configured
459
-						// Logging error as it is the hardest of the two
460
-						$this->logger->error(
461
-							"Unable to send the invitation mail to $email",
462
-							[
463
-								'app' => 'ocs_api',
464
-								'exception' => $e,
465
-							]
466
-						);
467
-					}
468
-				}
469
-			}
470
-
471
-			return new DataResponse(['id' => $userid]);
472
-		} catch (HintException $e) {
473
-			$this->logger->warning(
474
-				'Failed addUser attempt with hint exception.',
475
-				[
476
-					'app' => 'ocs_api',
477
-					'exception' => $e,
478
-				]
479
-			);
480
-			throw new OCSException($e->getHint(), 107);
481
-		} catch (OCSException $e) {
482
-			$this->logger->warning(
483
-				'Failed addUser attempt with ocs exception.',
484
-				[
485
-					'app' => 'ocs_api',
486
-					'exception' => $e,
487
-				]
488
-			);
489
-			throw $e;
490
-		} catch (InvalidArgumentException $e) {
491
-			$this->logger->error(
492
-				'Failed addUser attempt with invalid argument exception.',
493
-				[
494
-					'app' => 'ocs_api',
495
-					'exception' => $e,
496
-				]
497
-			);
498
-			throw new OCSException($e->getMessage(), 101);
499
-		} catch (\Exception $e) {
500
-			$this->logger->error(
501
-				'Failed addUser attempt with exception.',
502
-				[
503
-					'app' => 'ocs_api',
504
-					'exception' => $e
505
-				]
506
-			);
507
-			throw new OCSException('Bad request', 101);
508
-		}
509
-	}
510
-
511
-	/**
512
-	 * @NoAdminRequired
513
-	 * @NoSubAdminRequired
514
-	 *
515
-	 * gets user info
516
-	 *
517
-	 * @param string $userId
518
-	 * @return DataResponse
519
-	 * @throws OCSException
520
-	 */
521
-	public function getUser(string $userId): DataResponse {
522
-		$includeScopes = false;
523
-		$currentUser = $this->userSession->getUser();
524
-		if ($currentUser && $currentUser->getUID() === $userId) {
525
-			$includeScopes = true;
526
-		}
527
-
528
-		$data = $this->getUserData($userId, $includeScopes);
529
-		// getUserData returns empty array if not enough permissions
530
-		if (empty($data)) {
531
-			throw new OCSException('', OCSController::RESPOND_NOT_FOUND);
532
-		}
533
-		return new DataResponse($data);
534
-	}
535
-
536
-	/**
537
-	 * @NoAdminRequired
538
-	 * @NoSubAdminRequired
539
-	 *
540
-	 * gets user info from the currently logged in user
541
-	 *
542
-	 * @return DataResponse
543
-	 * @throws OCSException
544
-	 */
545
-	public function getCurrentUser(): DataResponse {
546
-		$user = $this->userSession->getUser();
547
-		if ($user) {
548
-			$data = $this->getUserData($user->getUID(), true);
549
-			return new DataResponse($data);
550
-		}
551
-
552
-		throw new OCSException('', OCSController::RESPOND_UNAUTHORISED);
553
-	}
554
-
555
-	/**
556
-	 * @NoAdminRequired
557
-	 * @NoSubAdminRequired
558
-	 *
559
-	 * @return DataResponse
560
-	 * @throws OCSException
561
-	 */
562
-	public function getEditableFields(): DataResponse {
563
-		$currentLoggedInUser = $this->userSession->getUser();
564
-		if (!$currentLoggedInUser instanceof IUser) {
565
-			throw new OCSException('', OCSController::RESPOND_NOT_FOUND);
566
-		}
567
-
568
-		return $this->getEditableFieldsForUser($currentLoggedInUser->getUID());
569
-	}
570
-
571
-	/**
572
-	 * @NoAdminRequired
573
-	 * @NoSubAdminRequired
574
-	 *
575
-	 * @param string $userId
576
-	 * @return DataResponse
577
-	 * @throws OCSException
578
-	 */
579
-	public function getEditableFieldsForUser(string $userId): DataResponse {
580
-		$currentLoggedInUser = $this->userSession->getUser();
581
-		if (!$currentLoggedInUser instanceof IUser) {
582
-			throw new OCSException('', OCSController::RESPOND_NOT_FOUND);
583
-		}
584
-
585
-		$permittedFields = [];
586
-
587
-		if ($userId !== $currentLoggedInUser->getUID()) {
588
-			$targetUser = $this->userManager->get($userId);
589
-			if (!$targetUser instanceof IUser) {
590
-				throw new OCSException('', OCSController::RESPOND_NOT_FOUND);
591
-			}
592
-
593
-			$subAdminManager = $this->groupManager->getSubAdmin();
594
-			if (
595
-				!$this->groupManager->isAdmin($currentLoggedInUser->getUID())
596
-				&& !$subAdminManager->isUserAccessible($currentLoggedInUser, $targetUser)
597
-			) {
598
-				throw new OCSException('', OCSController::RESPOND_NOT_FOUND);
599
-			}
600
-		} else {
601
-			$targetUser = $currentLoggedInUser;
602
-		}
603
-
604
-		// Editing self (display, email)
605
-		if ($this->config->getSystemValue('allow_user_to_change_display_name', true) !== false) {
606
-			if (
607
-				$targetUser->getBackend() instanceof ISetDisplayNameBackend
608
-				|| $targetUser->getBackend()->implementsActions(Backend::SET_DISPLAYNAME)
609
-			) {
610
-				$permittedFields[] = IAccountManager::PROPERTY_DISPLAYNAME;
611
-			}
612
-			$permittedFields[] = IAccountManager::PROPERTY_EMAIL;
613
-		}
614
-
615
-		$permittedFields[] = IAccountManager::COLLECTION_EMAIL;
616
-		$permittedFields[] = IAccountManager::PROPERTY_PHONE;
617
-		$permittedFields[] = IAccountManager::PROPERTY_ADDRESS;
618
-		$permittedFields[] = IAccountManager::PROPERTY_WEBSITE;
619
-		$permittedFields[] = IAccountManager::PROPERTY_TWITTER;
620
-		$permittedFields[] = IAccountManager::PROPERTY_FEDIVERSE;
621
-		$permittedFields[] = IAccountManager::PROPERTY_ORGANISATION;
622
-		$permittedFields[] = IAccountManager::PROPERTY_ROLE;
623
-		$permittedFields[] = IAccountManager::PROPERTY_HEADLINE;
624
-		$permittedFields[] = IAccountManager::PROPERTY_BIOGRAPHY;
625
-		$permittedFields[] = IAccountManager::PROPERTY_PROFILE_ENABLED;
626
-
627
-		return new DataResponse($permittedFields);
628
-	}
629
-
630
-	/**
631
-	 * @NoAdminRequired
632
-	 * @NoSubAdminRequired
633
-	 * @PasswordConfirmationRequired
634
-	 * @UserRateThrottle(limit=5, period=60)
635
-	 *
636
-	 * @throws OCSException
637
-	 */
638
-	public function editUserMultiValue(
639
-		string $userId,
640
-		string $collectionName,
641
-		string $key,
642
-		string $value
643
-	): DataResponse {
644
-		$currentLoggedInUser = $this->userSession->getUser();
645
-		if ($currentLoggedInUser === null) {
646
-			throw new OCSException('', OCSController::RESPOND_UNAUTHORISED);
647
-		}
648
-
649
-		$targetUser = $this->userManager->get($userId);
650
-		if ($targetUser === null) {
651
-			throw new OCSException('', OCSController::RESPOND_NOT_FOUND);
652
-		}
653
-
654
-		$subAdminManager = $this->groupManager->getSubAdmin();
655
-		$isAdminOrSubadmin = $this->groupManager->isAdmin($currentLoggedInUser->getUID())
656
-			|| $subAdminManager->isUserAccessible($currentLoggedInUser, $targetUser);
657
-
658
-		$permittedFields = [];
659
-		if ($targetUser->getUID() === $currentLoggedInUser->getUID()) {
660
-			// Editing self (display, email)
661
-			$permittedFields[] = IAccountManager::COLLECTION_EMAIL;
662
-			$permittedFields[] = IAccountManager::COLLECTION_EMAIL . self::SCOPE_SUFFIX;
663
-		} else {
664
-			// Check if admin / subadmin
665
-			if ($isAdminOrSubadmin) {
666
-				// They have permissions over the user
667
-				$permittedFields[] = IAccountManager::COLLECTION_EMAIL;
668
-			} else {
669
-				// No rights
670
-				throw new OCSException('', OCSController::RESPOND_NOT_FOUND);
671
-			}
672
-		}
673
-
674
-		// Check if permitted to edit this field
675
-		if (!in_array($collectionName, $permittedFields)) {
676
-			throw new OCSException('', 103);
677
-		}
678
-
679
-		switch ($collectionName) {
680
-			case IAccountManager::COLLECTION_EMAIL:
681
-				$userAccount = $this->accountManager->getAccount($targetUser);
682
-				$mailCollection = $userAccount->getPropertyCollection(IAccountManager::COLLECTION_EMAIL);
683
-				$mailCollection->removePropertyByValue($key);
684
-				if ($value !== '') {
685
-					$mailCollection->addPropertyWithDefaults($value);
686
-					$property = $mailCollection->getPropertyByValue($key);
687
-					if ($isAdminOrSubadmin && $property) {
688
-						// admin set mails are auto-verified
689
-						$property->setLocallyVerified(IAccountManager::VERIFIED);
690
-					}
691
-				}
692
-				$this->accountManager->updateAccount($userAccount);
693
-				break;
694
-
695
-			case IAccountManager::COLLECTION_EMAIL . self::SCOPE_SUFFIX:
696
-				$userAccount = $this->accountManager->getAccount($targetUser);
697
-				$mailCollection = $userAccount->getPropertyCollection(IAccountManager::COLLECTION_EMAIL);
698
-				$targetProperty = null;
699
-				foreach ($mailCollection->getProperties() as $property) {
700
-					if ($property->getValue() === $key) {
701
-						$targetProperty = $property;
702
-						break;
703
-					}
704
-				}
705
-				if ($targetProperty instanceof IAccountProperty) {
706
-					try {
707
-						$targetProperty->setScope($value);
708
-						$this->accountManager->updateAccount($userAccount);
709
-					} catch (InvalidArgumentException $e) {
710
-						throw new OCSException('', 102);
711
-					}
712
-				} else {
713
-					throw new OCSException('', 102);
714
-				}
715
-				break;
716
-
717
-			default:
718
-				throw new OCSException('', 103);
719
-		}
720
-		return new DataResponse();
721
-	}
722
-
723
-	/**
724
-	 * @NoAdminRequired
725
-	 * @NoSubAdminRequired
726
-	 * @PasswordConfirmationRequired
727
-	 * @UserRateThrottle(limit=50, period=600)
728
-	 *
729
-	 * edit users
730
-	 *
731
-	 * @param string $userId
732
-	 * @param string $key
733
-	 * @param string $value
734
-	 * @return DataResponse
735
-	 * @throws OCSException
736
-	 */
737
-	public function editUser(string $userId, string $key, string $value): DataResponse {
738
-		$currentLoggedInUser = $this->userSession->getUser();
739
-
740
-		$targetUser = $this->userManager->get($userId);
741
-		if ($targetUser === null) {
742
-			throw new OCSException('', OCSController::RESPOND_NOT_FOUND);
743
-		}
744
-
745
-		$permittedFields = [];
746
-		if ($targetUser->getUID() === $currentLoggedInUser->getUID()) {
747
-			// Editing self (display, email)
748
-			if ($this->config->getSystemValue('allow_user_to_change_display_name', true) !== false) {
749
-				if (
750
-					$targetUser->getBackend() instanceof ISetDisplayNameBackend
751
-					|| $targetUser->getBackend()->implementsActions(Backend::SET_DISPLAYNAME)
752
-				) {
753
-					$permittedFields[] = self::USER_FIELD_DISPLAYNAME;
754
-					$permittedFields[] = IAccountManager::PROPERTY_DISPLAYNAME;
755
-				}
756
-				$permittedFields[] = IAccountManager::PROPERTY_EMAIL;
757
-			}
758
-
759
-			$permittedFields[] = IAccountManager::PROPERTY_DISPLAYNAME . self::SCOPE_SUFFIX;
760
-			$permittedFields[] = IAccountManager::PROPERTY_EMAIL . self::SCOPE_SUFFIX;
761
-
762
-			$permittedFields[] = IAccountManager::COLLECTION_EMAIL;
763
-
764
-			$permittedFields[] = self::USER_FIELD_PASSWORD;
765
-			$permittedFields[] = self::USER_FIELD_NOTIFICATION_EMAIL;
766
-			if (
767
-				$this->config->getSystemValue('force_language', false) === false ||
768
-				$this->groupManager->isAdmin($currentLoggedInUser->getUID())
769
-			) {
770
-				$permittedFields[] = self::USER_FIELD_LANGUAGE;
771
-			}
772
-
773
-			if (
774
-				$this->config->getSystemValue('force_locale', false) === false ||
775
-				$this->groupManager->isAdmin($currentLoggedInUser->getUID())
776
-			) {
777
-				$permittedFields[] = self::USER_FIELD_LOCALE;
778
-			}
779
-
780
-			$permittedFields[] = IAccountManager::PROPERTY_PHONE;
781
-			$permittedFields[] = IAccountManager::PROPERTY_ADDRESS;
782
-			$permittedFields[] = IAccountManager::PROPERTY_WEBSITE;
783
-			$permittedFields[] = IAccountManager::PROPERTY_TWITTER;
784
-			$permittedFields[] = IAccountManager::PROPERTY_FEDIVERSE;
785
-			$permittedFields[] = IAccountManager::PROPERTY_ORGANISATION;
786
-			$permittedFields[] = IAccountManager::PROPERTY_ROLE;
787
-			$permittedFields[] = IAccountManager::PROPERTY_HEADLINE;
788
-			$permittedFields[] = IAccountManager::PROPERTY_BIOGRAPHY;
789
-			$permittedFields[] = IAccountManager::PROPERTY_PROFILE_ENABLED;
790
-			$permittedFields[] = IAccountManager::PROPERTY_PHONE . self::SCOPE_SUFFIX;
791
-			$permittedFields[] = IAccountManager::PROPERTY_ADDRESS . self::SCOPE_SUFFIX;
792
-			$permittedFields[] = IAccountManager::PROPERTY_WEBSITE . self::SCOPE_SUFFIX;
793
-			$permittedFields[] = IAccountManager::PROPERTY_TWITTER . self::SCOPE_SUFFIX;
794
-			$permittedFields[] = IAccountManager::PROPERTY_FEDIVERSE . self::SCOPE_SUFFIX;
795
-			$permittedFields[] = IAccountManager::PROPERTY_ORGANISATION . self::SCOPE_SUFFIX;
796
-			$permittedFields[] = IAccountManager::PROPERTY_ROLE . self::SCOPE_SUFFIX;
797
-			$permittedFields[] = IAccountManager::PROPERTY_HEADLINE . self::SCOPE_SUFFIX;
798
-			$permittedFields[] = IAccountManager::PROPERTY_BIOGRAPHY . self::SCOPE_SUFFIX;
799
-			$permittedFields[] = IAccountManager::PROPERTY_PROFILE_ENABLED . self::SCOPE_SUFFIX;
800
-
801
-			$permittedFields[] = IAccountManager::PROPERTY_AVATAR . self::SCOPE_SUFFIX;
802
-
803
-			// If admin they can edit their own quota
804
-			if ($this->groupManager->isAdmin($currentLoggedInUser->getUID())) {
805
-				$permittedFields[] = self::USER_FIELD_QUOTA;
806
-			}
807
-		} else {
808
-			// Check if admin / subadmin
809
-			$subAdminManager = $this->groupManager->getSubAdmin();
810
-			if (
811
-				$this->groupManager->isAdmin($currentLoggedInUser->getUID())
812
-				|| $subAdminManager->isUserAccessible($currentLoggedInUser, $targetUser)
813
-			) {
814
-				// They have permissions over the user
815
-				if (
816
-					$targetUser->getBackend() instanceof ISetDisplayNameBackend
817
-					|| $targetUser->getBackend()->implementsActions(Backend::SET_DISPLAYNAME)
818
-				) {
819
-					$permittedFields[] = self::USER_FIELD_DISPLAYNAME;
820
-					$permittedFields[] = IAccountManager::PROPERTY_DISPLAYNAME;
821
-				}
822
-				$permittedFields[] = IAccountManager::PROPERTY_EMAIL;
823
-				$permittedFields[] = IAccountManager::COLLECTION_EMAIL;
824
-				$permittedFields[] = self::USER_FIELD_PASSWORD;
825
-				$permittedFields[] = self::USER_FIELD_LANGUAGE;
826
-				$permittedFields[] = self::USER_FIELD_LOCALE;
827
-				$permittedFields[] = IAccountManager::PROPERTY_PHONE;
828
-				$permittedFields[] = IAccountManager::PROPERTY_ADDRESS;
829
-				$permittedFields[] = IAccountManager::PROPERTY_WEBSITE;
830
-				$permittedFields[] = IAccountManager::PROPERTY_TWITTER;
831
-				$permittedFields[] = IAccountManager::PROPERTY_FEDIVERSE;
832
-				$permittedFields[] = IAccountManager::PROPERTY_ORGANISATION;
833
-				$permittedFields[] = IAccountManager::PROPERTY_ROLE;
834
-				$permittedFields[] = IAccountManager::PROPERTY_HEADLINE;
835
-				$permittedFields[] = IAccountManager::PROPERTY_BIOGRAPHY;
836
-				$permittedFields[] = IAccountManager::PROPERTY_PROFILE_ENABLED;
837
-				$permittedFields[] = self::USER_FIELD_QUOTA;
838
-				$permittedFields[] = self::USER_FIELD_NOTIFICATION_EMAIL;
839
-			} else {
840
-				// No rights
841
-				throw new OCSException('', OCSController::RESPOND_NOT_FOUND);
842
-			}
843
-		}
844
-		// Check if permitted to edit this field
845
-		if (!in_array($key, $permittedFields)) {
846
-			throw new OCSException('', 103);
847
-		}
848
-		// Process the edit
849
-		switch ($key) {
850
-			case self::USER_FIELD_DISPLAYNAME:
851
-			case IAccountManager::PROPERTY_DISPLAYNAME:
852
-				try {
853
-					$targetUser->setDisplayName($value);
854
-				} catch (InvalidArgumentException $e) {
855
-					throw new OCSException($e->getMessage(), 101);
856
-				}
857
-				break;
858
-			case self::USER_FIELD_QUOTA:
859
-				$quota = $value;
860
-				if ($quota !== 'none' && $quota !== 'default') {
861
-					if (is_numeric($quota)) {
862
-						$quota = (float) $quota;
863
-					} else {
864
-						$quota = \OCP\Util::computerFileSize($quota);
865
-					}
866
-					if ($quota === false) {
867
-						throw new OCSException('Invalid quota value ' . $value, 102);
868
-					}
869
-					if ($quota === -1) {
870
-						$quota = 'none';
871
-					} else {
872
-						$maxQuota = (int) $this->config->getAppValue('files', 'max_quota', '-1');
873
-						if ($maxQuota !== -1 && $quota > $maxQuota) {
874
-							throw new OCSException('Invalid quota value. ' . $value . ' is exceeding the maximum quota', 102);
875
-						}
876
-						$quota = \OCP\Util::humanFileSize($quota);
877
-					}
878
-				}
879
-				// no else block because quota can be set to 'none' in previous if
880
-				if ($quota === 'none') {
881
-					$allowUnlimitedQuota = $this->config->getAppValue('files', 'allow_unlimited_quota', '1') === '1';
882
-					if (!$allowUnlimitedQuota) {
883
-						throw new OCSException('Unlimited quota is forbidden on this instance', 102);
884
-					}
885
-				}
886
-				$targetUser->setQuota($quota);
887
-				break;
888
-			case self::USER_FIELD_PASSWORD:
889
-				try {
890
-					if (strlen($value) > IUserManager::MAX_PASSWORD_LENGTH) {
891
-						throw new OCSException('Invalid password value', 102);
892
-					}
893
-					if (!$targetUser->canChangePassword()) {
894
-						throw new OCSException('Setting the password is not supported by the users backend', 103);
895
-					}
896
-					$targetUser->setPassword($value);
897
-				} catch (HintException $e) { // password policy error
898
-					throw new OCSException($e->getMessage(), 103);
899
-				}
900
-				break;
901
-			case self::USER_FIELD_LANGUAGE:
902
-				$languagesCodes = $this->l10nFactory->findAvailableLanguages();
903
-				if (!in_array($value, $languagesCodes, true) && $value !== 'en') {
904
-					throw new OCSException('Invalid language', 102);
905
-				}
906
-				$this->config->setUserValue($targetUser->getUID(), 'core', 'lang', $value);
907
-				break;
908
-			case self::USER_FIELD_LOCALE:
909
-				if (!$this->l10nFactory->localeExists($value)) {
910
-					throw new OCSException('Invalid locale', 102);
911
-				}
912
-				$this->config->setUserValue($targetUser->getUID(), 'core', 'locale', $value);
913
-				break;
914
-			case self::USER_FIELD_NOTIFICATION_EMAIL:
915
-				$success = false;
916
-				if ($value === '' || filter_var($value, FILTER_VALIDATE_EMAIL)) {
917
-					try {
918
-						$targetUser->setPrimaryEMailAddress($value);
919
-						$success = true;
920
-					} catch (InvalidArgumentException $e) {
921
-						$this->logger->info(
922
-							'Cannot set primary email, because provided address is not verified',
923
-							[
924
-								'app' => 'provisioning_api',
925
-								'exception' => $e,
926
-							]
927
-						);
928
-					}
929
-				}
930
-				if (!$success) {
931
-					throw new OCSException('', 102);
932
-				}
933
-				break;
934
-			case IAccountManager::PROPERTY_EMAIL:
935
-				if (filter_var($value, FILTER_VALIDATE_EMAIL) || $value === '') {
936
-					$targetUser->setEMailAddress($value);
937
-				} else {
938
-					throw new OCSException('', 102);
939
-				}
940
-				break;
941
-			case IAccountManager::COLLECTION_EMAIL:
942
-				if (filter_var($value, FILTER_VALIDATE_EMAIL) && $value !== $targetUser->getSystemEMailAddress()) {
943
-					$userAccount = $this->accountManager->getAccount($targetUser);
944
-					$mailCollection = $userAccount->getPropertyCollection(IAccountManager::COLLECTION_EMAIL);
945
-					foreach ($mailCollection->getProperties() as $property) {
946
-						if ($property->getValue() === $value) {
947
-							break;
948
-						}
949
-					}
950
-					$mailCollection->addPropertyWithDefaults($value);
951
-					$this->accountManager->updateAccount($userAccount);
952
-				} else {
953
-					throw new OCSException('', 102);
954
-				}
955
-				break;
956
-			case IAccountManager::PROPERTY_PHONE:
957
-			case IAccountManager::PROPERTY_ADDRESS:
958
-			case IAccountManager::PROPERTY_WEBSITE:
959
-			case IAccountManager::PROPERTY_TWITTER:
960
-			case IAccountManager::PROPERTY_FEDIVERSE:
961
-			case IAccountManager::PROPERTY_ORGANISATION:
962
-			case IAccountManager::PROPERTY_ROLE:
963
-			case IAccountManager::PROPERTY_HEADLINE:
964
-			case IAccountManager::PROPERTY_BIOGRAPHY:
965
-				$userAccount = $this->accountManager->getAccount($targetUser);
966
-				try {
967
-					$userProperty = $userAccount->getProperty($key);
968
-					if ($userProperty->getValue() !== $value) {
969
-						try {
970
-							$userProperty->setValue($value);
971
-							if ($userProperty->getName() === IAccountManager::PROPERTY_PHONE) {
972
-								$this->knownUserService->deleteByContactUserId($targetUser->getUID());
973
-							}
974
-						} catch (InvalidArgumentException $e) {
975
-							throw new OCSException('Invalid ' . $e->getMessage(), 102);
976
-						}
977
-					}
978
-				} catch (PropertyDoesNotExistException $e) {
979
-					$userAccount->setProperty($key, $value, IAccountManager::SCOPE_PRIVATE, IAccountManager::NOT_VERIFIED);
980
-				}
981
-				try {
982
-					$this->accountManager->updateAccount($userAccount);
983
-				} catch (InvalidArgumentException $e) {
984
-					throw new OCSException('Invalid ' . $e->getMessage(), 102);
985
-				}
986
-				break;
987
-			case IAccountManager::PROPERTY_PROFILE_ENABLED:
988
-				$userAccount = $this->accountManager->getAccount($targetUser);
989
-				try {
990
-					$userProperty = $userAccount->getProperty($key);
991
-					if ($userProperty->getValue() !== $value) {
992
-						$userProperty->setValue($value);
993
-					}
994
-				} catch (PropertyDoesNotExistException $e) {
995
-					$userAccount->setProperty($key, $value, IAccountManager::SCOPE_LOCAL, IAccountManager::NOT_VERIFIED);
996
-				}
997
-				$this->accountManager->updateAccount($userAccount);
998
-				break;
999
-			case IAccountManager::PROPERTY_DISPLAYNAME . self::SCOPE_SUFFIX:
1000
-			case IAccountManager::PROPERTY_EMAIL . self::SCOPE_SUFFIX:
1001
-			case IAccountManager::PROPERTY_PHONE . self::SCOPE_SUFFIX:
1002
-			case IAccountManager::PROPERTY_ADDRESS . self::SCOPE_SUFFIX:
1003
-			case IAccountManager::PROPERTY_WEBSITE . self::SCOPE_SUFFIX:
1004
-			case IAccountManager::PROPERTY_TWITTER . self::SCOPE_SUFFIX:
1005
-			case IAccountManager::PROPERTY_FEDIVERSE . self::SCOPE_SUFFIX:
1006
-			case IAccountManager::PROPERTY_ORGANISATION . self::SCOPE_SUFFIX:
1007
-			case IAccountManager::PROPERTY_ROLE . self::SCOPE_SUFFIX:
1008
-			case IAccountManager::PROPERTY_HEADLINE . self::SCOPE_SUFFIX:
1009
-			case IAccountManager::PROPERTY_BIOGRAPHY . self::SCOPE_SUFFIX:
1010
-			case IAccountManager::PROPERTY_PROFILE_ENABLED . self::SCOPE_SUFFIX:
1011
-			case IAccountManager::PROPERTY_AVATAR . self::SCOPE_SUFFIX:
1012
-				$propertyName = substr($key, 0, strlen($key) - strlen(self::SCOPE_SUFFIX));
1013
-				$userAccount = $this->accountManager->getAccount($targetUser);
1014
-				$userProperty = $userAccount->getProperty($propertyName);
1015
-				if ($userProperty->getScope() !== $value) {
1016
-					try {
1017
-						$userProperty->setScope($value);
1018
-						$this->accountManager->updateAccount($userAccount);
1019
-					} catch (InvalidArgumentException $e) {
1020
-						throw new OCSException('Invalid ' . $e->getMessage(), 102);
1021
-					}
1022
-				}
1023
-				break;
1024
-			default:
1025
-				throw new OCSException('', 103);
1026
-		}
1027
-		return new DataResponse();
1028
-	}
1029
-
1030
-	/**
1031
-	 * @PasswordConfirmationRequired
1032
-	 * @NoAdminRequired
1033
-	 *
1034
-	 * @param string $userId
1035
-	 *
1036
-	 * @return DataResponse
1037
-	 *
1038
-	 * @throws OCSException
1039
-	 */
1040
-	public function wipeUserDevices(string $userId): DataResponse {
1041
-		/** @var IUser $currentLoggedInUser */
1042
-		$currentLoggedInUser = $this->userSession->getUser();
1043
-
1044
-		$targetUser = $this->userManager->get($userId);
1045
-
1046
-		if ($targetUser === null) {
1047
-			throw new OCSException('', OCSController::RESPOND_NOT_FOUND);
1048
-		}
1049
-
1050
-		if ($targetUser->getUID() === $currentLoggedInUser->getUID()) {
1051
-			throw new OCSException('', 101);
1052
-		}
1053
-
1054
-		// If not permitted
1055
-		$subAdminManager = $this->groupManager->getSubAdmin();
1056
-		if (!$this->groupManager->isAdmin($currentLoggedInUser->getUID()) && !$subAdminManager->isUserAccessible($currentLoggedInUser, $targetUser)) {
1057
-			throw new OCSException('', OCSController::RESPOND_NOT_FOUND);
1058
-		}
1059
-
1060
-		$this->remoteWipe->markAllTokensForWipe($targetUser);
1061
-
1062
-		return new DataResponse();
1063
-	}
1064
-
1065
-	/**
1066
-	 * @PasswordConfirmationRequired
1067
-	 * @NoAdminRequired
1068
-	 *
1069
-	 * @param string $userId
1070
-	 * @return DataResponse
1071
-	 * @throws OCSException
1072
-	 */
1073
-	public function deleteUser(string $userId): DataResponse {
1074
-		$currentLoggedInUser = $this->userSession->getUser();
1075
-
1076
-		$targetUser = $this->userManager->get($userId);
1077
-
1078
-		if ($targetUser === null) {
1079
-			throw new OCSException('', OCSController::RESPOND_NOT_FOUND);
1080
-		}
1081
-
1082
-		if ($targetUser->getUID() === $currentLoggedInUser->getUID()) {
1083
-			throw new OCSException('', 101);
1084
-		}
1085
-
1086
-		// If not permitted
1087
-		$subAdminManager = $this->groupManager->getSubAdmin();
1088
-		if (!$this->groupManager->isAdmin($currentLoggedInUser->getUID()) && !$subAdminManager->isUserAccessible($currentLoggedInUser, $targetUser)) {
1089
-			throw new OCSException('', OCSController::RESPOND_NOT_FOUND);
1090
-		}
1091
-
1092
-		// Go ahead with the delete
1093
-		if ($targetUser->delete()) {
1094
-			return new DataResponse();
1095
-		} else {
1096
-			throw new OCSException('', 101);
1097
-		}
1098
-	}
1099
-
1100
-	/**
1101
-	 * @PasswordConfirmationRequired
1102
-	 * @NoAdminRequired
1103
-	 *
1104
-	 * @param string $userId
1105
-	 * @return DataResponse
1106
-	 * @throws OCSException
1107
-	 * @throws OCSForbiddenException
1108
-	 */
1109
-	public function disableUser(string $userId): DataResponse {
1110
-		return $this->setEnabled($userId, false);
1111
-	}
1112
-
1113
-	/**
1114
-	 * @PasswordConfirmationRequired
1115
-	 * @NoAdminRequired
1116
-	 *
1117
-	 * @param string $userId
1118
-	 * @return DataResponse
1119
-	 * @throws OCSException
1120
-	 * @throws OCSForbiddenException
1121
-	 */
1122
-	public function enableUser(string $userId): DataResponse {
1123
-		return $this->setEnabled($userId, true);
1124
-	}
1125
-
1126
-	/**
1127
-	 * @param string $userId
1128
-	 * @param bool $value
1129
-	 * @return DataResponse
1130
-	 * @throws OCSException
1131
-	 */
1132
-	private function setEnabled(string $userId, bool $value): DataResponse {
1133
-		$currentLoggedInUser = $this->userSession->getUser();
1134
-
1135
-		$targetUser = $this->userManager->get($userId);
1136
-		if ($targetUser === null || $targetUser->getUID() === $currentLoggedInUser->getUID()) {
1137
-			throw new OCSException('', 101);
1138
-		}
1139
-
1140
-		// If not permitted
1141
-		$subAdminManager = $this->groupManager->getSubAdmin();
1142
-		if (!$this->groupManager->isAdmin($currentLoggedInUser->getUID()) && !$subAdminManager->isUserAccessible($currentLoggedInUser, $targetUser)) {
1143
-			throw new OCSException('', OCSController::RESPOND_NOT_FOUND);
1144
-		}
1145
-
1146
-		// enable/disable the user now
1147
-		$targetUser->setEnabled($value);
1148
-		return new DataResponse();
1149
-	}
1150
-
1151
-	/**
1152
-	 * @NoAdminRequired
1153
-	 * @NoSubAdminRequired
1154
-	 *
1155
-	 * @param string $userId
1156
-	 * @return DataResponse
1157
-	 * @throws OCSException
1158
-	 */
1159
-	public function getUsersGroups(string $userId): DataResponse {
1160
-		$loggedInUser = $this->userSession->getUser();
1161
-
1162
-		$targetUser = $this->userManager->get($userId);
1163
-		if ($targetUser === null) {
1164
-			throw new OCSException('', OCSController::RESPOND_NOT_FOUND);
1165
-		}
1166
-
1167
-		if ($targetUser->getUID() === $loggedInUser->getUID() || $this->groupManager->isAdmin($loggedInUser->getUID())) {
1168
-			// Self lookup or admin lookup
1169
-			return new DataResponse([
1170
-				'groups' => $this->groupManager->getUserGroupIds($targetUser)
1171
-			]);
1172
-		} else {
1173
-			$subAdminManager = $this->groupManager->getSubAdmin();
1174
-
1175
-			// Looking up someone else
1176
-			if ($subAdminManager->isUserAccessible($loggedInUser, $targetUser)) {
1177
-				// Return the group that the method caller is subadmin of for the user in question
1178
-				/** @var IGroup[] $getSubAdminsGroups */
1179
-				$getSubAdminsGroups = $subAdminManager->getSubAdminsGroups($loggedInUser);
1180
-				foreach ($getSubAdminsGroups as $key => $group) {
1181
-					$getSubAdminsGroups[$key] = $group->getGID();
1182
-				}
1183
-				$groups = array_intersect(
1184
-					$getSubAdminsGroups,
1185
-					$this->groupManager->getUserGroupIds($targetUser)
1186
-				);
1187
-				return new DataResponse(['groups' => $groups]);
1188
-			} else {
1189
-				// Not permitted
1190
-				throw new OCSException('', OCSController::RESPOND_NOT_FOUND);
1191
-			}
1192
-		}
1193
-	}
1194
-
1195
-	/**
1196
-	 * @PasswordConfirmationRequired
1197
-	 * @NoAdminRequired
1198
-	 *
1199
-	 * @param string $userId
1200
-	 * @param string $groupid
1201
-	 * @return DataResponse
1202
-	 * @throws OCSException
1203
-	 */
1204
-	public function addToGroup(string $userId, string $groupid = ''): DataResponse {
1205
-		if ($groupid === '') {
1206
-			throw new OCSException('', 101);
1207
-		}
1208
-
1209
-		$group = $this->groupManager->get($groupid);
1210
-		$targetUser = $this->userManager->get($userId);
1211
-		if ($group === null) {
1212
-			throw new OCSException('', 102);
1213
-		}
1214
-		if ($targetUser === null) {
1215
-			throw new OCSException('', 103);
1216
-		}
1217
-
1218
-		// If they're not an admin, check they are a subadmin of the group in question
1219
-		$loggedInUser = $this->userSession->getUser();
1220
-		$subAdminManager = $this->groupManager->getSubAdmin();
1221
-		if (!$this->groupManager->isAdmin($loggedInUser->getUID()) && !$subAdminManager->isSubAdminOfGroup($loggedInUser, $group)) {
1222
-			throw new OCSException('', 104);
1223
-		}
1224
-
1225
-		// Add user to group
1226
-		$group->addUser($targetUser);
1227
-		return new DataResponse();
1228
-	}
1229
-
1230
-	/**
1231
-	 * @PasswordConfirmationRequired
1232
-	 * @NoAdminRequired
1233
-	 *
1234
-	 * @param string $userId
1235
-	 * @param string $groupid
1236
-	 * @return DataResponse
1237
-	 * @throws OCSException
1238
-	 */
1239
-	public function removeFromGroup(string $userId, string $groupid): DataResponse {
1240
-		$loggedInUser = $this->userSession->getUser();
1241
-
1242
-		if ($groupid === null || trim($groupid) === '') {
1243
-			throw new OCSException('', 101);
1244
-		}
1245
-
1246
-		$group = $this->groupManager->get($groupid);
1247
-		if ($group === null) {
1248
-			throw new OCSException('', 102);
1249
-		}
1250
-
1251
-		$targetUser = $this->userManager->get($userId);
1252
-		if ($targetUser === null) {
1253
-			throw new OCSException('', 103);
1254
-		}
1255
-
1256
-		// If they're not an admin, check they are a subadmin of the group in question
1257
-		$subAdminManager = $this->groupManager->getSubAdmin();
1258
-		if (!$this->groupManager->isAdmin($loggedInUser->getUID()) && !$subAdminManager->isSubAdminOfGroup($loggedInUser, $group)) {
1259
-			throw new OCSException('', 104);
1260
-		}
1261
-
1262
-		// Check they aren't removing themselves from 'admin' or their 'subadmin; group
1263
-		if ($targetUser->getUID() === $loggedInUser->getUID()) {
1264
-			if ($this->groupManager->isAdmin($loggedInUser->getUID())) {
1265
-				if ($group->getGID() === 'admin') {
1266
-					throw new OCSException('Cannot remove yourself from the admin group', 105);
1267
-				}
1268
-			} else {
1269
-				// Not an admin, so the user must be a subadmin of this group, but that is not allowed.
1270
-				throw new OCSException('Cannot remove yourself from this group as you are a SubAdmin', 105);
1271
-			}
1272
-		} elseif (!$this->groupManager->isAdmin($loggedInUser->getUID())) {
1273
-			/** @var IGroup[] $subAdminGroups */
1274
-			$subAdminGroups = $subAdminManager->getSubAdminsGroups($loggedInUser);
1275
-			$subAdminGroups = array_map(function (IGroup $subAdminGroup) {
1276
-				return $subAdminGroup->getGID();
1277
-			}, $subAdminGroups);
1278
-			$userGroups = $this->groupManager->getUserGroupIds($targetUser);
1279
-			$userSubAdminGroups = array_intersect($subAdminGroups, $userGroups);
1280
-
1281
-			if (count($userSubAdminGroups) <= 1) {
1282
-				// Subadmin must not be able to remove a user from all their subadmin groups.
1283
-				throw new OCSException('Not viable to remove user from the last group you are SubAdmin of', 105);
1284
-			}
1285
-		}
1286
-
1287
-		// Remove user from group
1288
-		$group->removeUser($targetUser);
1289
-		return new DataResponse();
1290
-	}
1291
-
1292
-	/**
1293
-	 * Creates a subadmin
1294
-	 *
1295
-	 * @PasswordConfirmationRequired
1296
-	 *
1297
-	 * @param string $userId
1298
-	 * @param string $groupid
1299
-	 * @return DataResponse
1300
-	 * @throws OCSException
1301
-	 */
1302
-	public function addSubAdmin(string $userId, string $groupid): DataResponse {
1303
-		$group = $this->groupManager->get($groupid);
1304
-		$user = $this->userManager->get($userId);
1305
-
1306
-		// Check if the user exists
1307
-		if ($user === null) {
1308
-			throw new OCSException('User does not exist', 101);
1309
-		}
1310
-		// Check if group exists
1311
-		if ($group === null) {
1312
-			throw new OCSException('Group does not exist', 102);
1313
-		}
1314
-		// Check if trying to make subadmin of admin group
1315
-		if ($group->getGID() === 'admin') {
1316
-			throw new OCSException('Cannot create subadmins for admin group', 103);
1317
-		}
1318
-
1319
-		$subAdminManager = $this->groupManager->getSubAdmin();
1320
-
1321
-		// We cannot be subadmin twice
1322
-		if ($subAdminManager->isSubAdminOfGroup($user, $group)) {
1323
-			return new DataResponse();
1324
-		}
1325
-		// Go
1326
-		$subAdminManager->createSubAdmin($user, $group);
1327
-		return new DataResponse();
1328
-	}
1329
-
1330
-	/**
1331
-	 * Removes a subadmin from a group
1332
-	 *
1333
-	 * @PasswordConfirmationRequired
1334
-	 *
1335
-	 * @param string $userId
1336
-	 * @param string $groupid
1337
-	 * @return DataResponse
1338
-	 * @throws OCSException
1339
-	 */
1340
-	public function removeSubAdmin(string $userId, string $groupid): DataResponse {
1341
-		$group = $this->groupManager->get($groupid);
1342
-		$user = $this->userManager->get($userId);
1343
-		$subAdminManager = $this->groupManager->getSubAdmin();
1344
-
1345
-		// Check if the user exists
1346
-		if ($user === null) {
1347
-			throw new OCSException('User does not exist', 101);
1348
-		}
1349
-		// Check if the group exists
1350
-		if ($group === null) {
1351
-			throw new OCSException('Group does not exist', 101);
1352
-		}
1353
-		// Check if they are a subadmin of this said group
1354
-		if (!$subAdminManager->isSubAdminOfGroup($user, $group)) {
1355
-			throw new OCSException('User is not a subadmin of this group', 102);
1356
-		}
1357
-
1358
-		// Go
1359
-		$subAdminManager->deleteSubAdmin($user, $group);
1360
-		return new DataResponse();
1361
-	}
1362
-
1363
-	/**
1364
-	 * Get the groups a user is a subadmin of
1365
-	 *
1366
-	 * @param string $userId
1367
-	 * @return DataResponse
1368
-	 * @throws OCSException
1369
-	 */
1370
-	public function getUserSubAdminGroups(string $userId): DataResponse {
1371
-		$groups = $this->getUserSubAdminGroupsData($userId);
1372
-		return new DataResponse($groups);
1373
-	}
1374
-
1375
-	/**
1376
-	 * @NoAdminRequired
1377
-	 * @PasswordConfirmationRequired
1378
-	 *
1379
-	 * resend welcome message
1380
-	 *
1381
-	 * @param string $userId
1382
-	 * @return DataResponse
1383
-	 * @throws OCSException
1384
-	 */
1385
-	public function resendWelcomeMessage(string $userId): DataResponse {
1386
-		$currentLoggedInUser = $this->userSession->getUser();
1387
-
1388
-		$targetUser = $this->userManager->get($userId);
1389
-		if ($targetUser === null) {
1390
-			throw new OCSException('', OCSController::RESPOND_NOT_FOUND);
1391
-		}
1392
-
1393
-		// Check if admin / subadmin
1394
-		$subAdminManager = $this->groupManager->getSubAdmin();
1395
-		if (
1396
-			!$subAdminManager->isUserAccessible($currentLoggedInUser, $targetUser)
1397
-			&& !$this->groupManager->isAdmin($currentLoggedInUser->getUID())
1398
-		) {
1399
-			// No rights
1400
-			throw new OCSException('', OCSController::RESPOND_NOT_FOUND);
1401
-		}
1402
-
1403
-		$email = $targetUser->getEMailAddress();
1404
-		if ($email === '' || $email === null) {
1405
-			throw new OCSException('Email address not available', 101);
1406
-		}
1407
-
1408
-		try {
1409
-			$emailTemplate = $this->newUserMailHelper->generateTemplate($targetUser, false);
1410
-			$this->newUserMailHelper->sendMail($targetUser, $emailTemplate);
1411
-		} catch (\Exception $e) {
1412
-			$this->logger->error(
1413
-				"Can't send new user mail to $email",
1414
-				[
1415
-					'app' => 'settings',
1416
-					'exception' => $e,
1417
-				]
1418
-			);
1419
-			throw new OCSException('Sending email failed', 102);
1420
-		}
1421
-
1422
-		return new DataResponse();
1423
-	}
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(
99
+        string $appName,
100
+        IRequest $request,
101
+        IUserManager $userManager,
102
+        IConfig $config,
103
+        IGroupManager $groupManager,
104
+        IUserSession $userSession,
105
+        IAccountManager $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
+    ) {
115
+        parent::__construct(
116
+            $appName,
117
+            $request,
118
+            $userManager,
119
+            $config,
120
+            $groupManager,
121
+            $userSession,
122
+            $accountManager,
123
+            $l10nFactory
124
+        );
125
+
126
+        $this->urlGenerator = $urlGenerator;
127
+        $this->logger = $logger;
128
+        $this->l10nFactory = $l10nFactory;
129
+        $this->newUserMailHelper = $newUserMailHelper;
130
+        $this->secureRandom = $secureRandom;
131
+        $this->remoteWipe = $remoteWipe;
132
+        $this->knownUserService = $knownUserService;
133
+        $this->eventDispatcher = $eventDispatcher;
134
+    }
135
+
136
+    /**
137
+     * @NoAdminRequired
138
+     *
139
+     * returns a list of users
140
+     *
141
+     * @param string $search
142
+     * @param int $limit
143
+     * @param int $offset
144
+     * @return DataResponse
145
+     */
146
+    public function getUsers(string $search = '', int $limit = null, int $offset = 0): DataResponse {
147
+        $user = $this->userSession->getUser();
148
+        $users = [];
149
+
150
+        // Admin? Or SubAdmin?
151
+        $uid = $user->getUID();
152
+        $subAdminManager = $this->groupManager->getSubAdmin();
153
+        if ($this->groupManager->isAdmin($uid)) {
154
+            $users = $this->userManager->search($search, $limit, $offset);
155
+        } elseif ($subAdminManager->isSubAdmin($user)) {
156
+            $subAdminOfGroups = $subAdminManager->getSubAdminsGroups($user);
157
+            foreach ($subAdminOfGroups as $key => $group) {
158
+                $subAdminOfGroups[$key] = $group->getGID();
159
+            }
160
+
161
+            $users = [];
162
+            foreach ($subAdminOfGroups as $group) {
163
+                $users = array_merge($users, $this->groupManager->displayNamesInGroup($group, $search, $limit, $offset));
164
+            }
165
+        }
166
+
167
+        $users = array_keys($users);
168
+
169
+        return new DataResponse([
170
+            'users' => $users
171
+        ]);
172
+    }
173
+
174
+    /**
175
+     * @NoAdminRequired
176
+     *
177
+     * returns a list of users and their data
178
+     */
179
+    public function getUsersDetails(string $search = '', int $limit = null, int $offset = 0): DataResponse {
180
+        $currentUser = $this->userSession->getUser();
181
+        $users = [];
182
+
183
+        // Admin? Or SubAdmin?
184
+        $uid = $currentUser->getUID();
185
+        $subAdminManager = $this->groupManager->getSubAdmin();
186
+        if ($this->groupManager->isAdmin($uid)) {
187
+            $users = $this->userManager->search($search, $limit, $offset);
188
+            $users = array_keys($users);
189
+        } elseif ($subAdminManager->isSubAdmin($currentUser)) {
190
+            $subAdminOfGroups = $subAdminManager->getSubAdminsGroups($currentUser);
191
+            foreach ($subAdminOfGroups as $key => $group) {
192
+                $subAdminOfGroups[$key] = $group->getGID();
193
+            }
194
+
195
+            $users = [];
196
+            foreach ($subAdminOfGroups as $group) {
197
+                $users[] = array_keys($this->groupManager->displayNamesInGroup($group, $search, $limit, $offset));
198
+            }
199
+            $users = array_merge(...$users);
200
+        }
201
+
202
+        $usersDetails = [];
203
+        foreach ($users as $userId) {
204
+            $userId = (string) $userId;
205
+            $userData = $this->getUserData($userId);
206
+            // Do not insert empty entry
207
+            if (!empty($userData)) {
208
+                $usersDetails[$userId] = $userData;
209
+            } else {
210
+                // Logged user does not have permissions to see this user
211
+                // only showing its id
212
+                $usersDetails[$userId] = ['id' => $userId];
213
+            }
214
+        }
215
+
216
+        return new DataResponse([
217
+            'users' => $usersDetails
218
+        ]);
219
+    }
220
+
221
+
222
+    /**
223
+     * @NoAdminRequired
224
+     * @NoSubAdminRequired
225
+     *
226
+     * @param string $location
227
+     * @param array $search
228
+     * @return DataResponse
229
+     */
230
+    public function searchByPhoneNumbers(string $location, array $search): DataResponse {
231
+        $phoneUtil = PhoneNumberUtil::getInstance();
232
+
233
+        if ($phoneUtil->getCountryCodeForRegion($location) === 0) {
234
+            // Not a valid region code
235
+            return new DataResponse([], Http::STATUS_BAD_REQUEST);
236
+        }
237
+
238
+        /** @var IUser $user */
239
+        $user = $this->userSession->getUser();
240
+        $knownTo = $user->getUID();
241
+        $defaultPhoneRegion = $this->config->getSystemValueString('default_phone_region');
242
+
243
+        $normalizedNumberToKey = [];
244
+        foreach ($search as $key => $phoneNumbers) {
245
+            foreach ($phoneNumbers as $phone) {
246
+                try {
247
+                    $phoneNumber = $phoneUtil->parse($phone, $location);
248
+                    if ($phoneNumber instanceof PhoneNumber && $phoneUtil->isValidNumber($phoneNumber)) {
249
+                        $normalizedNumber = $phoneUtil->format($phoneNumber, PhoneNumberFormat::E164);
250
+                        $normalizedNumberToKey[$normalizedNumber] = (string) $key;
251
+                    }
252
+                } catch (NumberParseException $e) {
253
+                }
254
+
255
+                if ($defaultPhoneRegion !== '' && $defaultPhoneRegion !== $location && strpos($phone, '0') === 0) {
256
+                    // If the number has a leading zero (no country code),
257
+                    // we also check the default phone region of the instance,
258
+                    // when it's different to the user's given region.
259
+                    try {
260
+                        $phoneNumber = $phoneUtil->parse($phone, $defaultPhoneRegion);
261
+                        if ($phoneNumber instanceof PhoneNumber && $phoneUtil->isValidNumber($phoneNumber)) {
262
+                            $normalizedNumber = $phoneUtil->format($phoneNumber, PhoneNumberFormat::E164);
263
+                            $normalizedNumberToKey[$normalizedNumber] = (string) $key;
264
+                        }
265
+                    } catch (NumberParseException $e) {
266
+                    }
267
+                }
268
+            }
269
+        }
270
+
271
+        $phoneNumbers = array_keys($normalizedNumberToKey);
272
+
273
+        if (empty($phoneNumbers)) {
274
+            return new DataResponse();
275
+        }
276
+
277
+        // Cleanup all previous entries and only allow new matches
278
+        $this->knownUserService->deleteKnownTo($knownTo);
279
+
280
+        $userMatches = $this->accountManager->searchUsers(IAccountManager::PROPERTY_PHONE, $phoneNumbers);
281
+
282
+        if (empty($userMatches)) {
283
+            return new DataResponse();
284
+        }
285
+
286
+        $cloudUrl = rtrim($this->urlGenerator->getAbsoluteURL('/'), '/');
287
+        if (strpos($cloudUrl, 'http://') === 0) {
288
+            $cloudUrl = substr($cloudUrl, strlen('http://'));
289
+        } elseif (strpos($cloudUrl, 'https://') === 0) {
290
+            $cloudUrl = substr($cloudUrl, strlen('https://'));
291
+        }
292
+
293
+        $matches = [];
294
+        foreach ($userMatches as $phone => $userId) {
295
+            // Not using the ICloudIdManager as that would run a search for each contact to find the display name in the address book
296
+            $matches[$normalizedNumberToKey[$phone]] = $userId . '@' . $cloudUrl;
297
+            $this->knownUserService->storeIsKnownToUser($knownTo, $userId);
298
+        }
299
+
300
+        return new DataResponse($matches);
301
+    }
302
+
303
+    /**
304
+     * @throws OCSException
305
+     */
306
+    private function createNewUserId(): string {
307
+        $attempts = 0;
308
+        do {
309
+            $uidCandidate = $this->secureRandom->generate(10, ISecureRandom::CHAR_HUMAN_READABLE);
310
+            if (!$this->userManager->userExists($uidCandidate)) {
311
+                return $uidCandidate;
312
+            }
313
+            $attempts++;
314
+        } while ($attempts < 10);
315
+        throw new OCSException('Could not create non-existing user id', 111);
316
+    }
317
+
318
+    /**
319
+     * @PasswordConfirmationRequired
320
+     * @NoAdminRequired
321
+     *
322
+     * @param string $userid
323
+     * @param string $password
324
+     * @param string $displayName
325
+     * @param string $email
326
+     * @param array $groups
327
+     * @param array $subadmin
328
+     * @param string $quota
329
+     * @param string $language
330
+     * @return DataResponse
331
+     * @throws OCSException
332
+     */
333
+    public function addUser(
334
+        string $userid,
335
+        string $password = '',
336
+        string $displayName = '',
337
+        string $email = '',
338
+        array $groups = [],
339
+        array $subadmin = [],
340
+        string $quota = '',
341
+        string $language = ''
342
+    ): DataResponse {
343
+        $user = $this->userSession->getUser();
344
+        $isAdmin = $this->groupManager->isAdmin($user->getUID());
345
+        $subAdminManager = $this->groupManager->getSubAdmin();
346
+
347
+        if (empty($userid) && $this->config->getAppValue('core', 'newUser.generateUserID', 'no') === 'yes') {
348
+            $userid = $this->createNewUserId();
349
+        }
350
+
351
+        if ($this->userManager->userExists($userid)) {
352
+            $this->logger->error('Failed addUser attempt: User already exists.', ['app' => 'ocs_api']);
353
+            throw new OCSException($this->l10nFactory->get('provisioning_api')->t('User already exists'), 102);
354
+        }
355
+
356
+        if ($groups !== []) {
357
+            foreach ($groups as $group) {
358
+                if (!$this->groupManager->groupExists($group)) {
359
+                    throw new OCSException('group ' . $group . ' does not exist', 104);
360
+                }
361
+                if (!$isAdmin && !$subAdminManager->isSubAdminOfGroup($user, $this->groupManager->get($group))) {
362
+                    throw new OCSException('insufficient privileges for group ' . $group, 105);
363
+                }
364
+            }
365
+        } else {
366
+            if (!$isAdmin) {
367
+                throw new OCSException('no group specified (required for subadmins)', 106);
368
+            }
369
+        }
370
+
371
+        $subadminGroups = [];
372
+        if ($subadmin !== []) {
373
+            foreach ($subadmin as $groupid) {
374
+                $group = $this->groupManager->get($groupid);
375
+                // Check if group exists
376
+                if ($group === null) {
377
+                    throw new OCSException('Subadmin group does not exist', 102);
378
+                }
379
+                // Check if trying to make subadmin of admin group
380
+                if ($group->getGID() === 'admin') {
381
+                    throw new OCSException('Cannot create subadmins for admin group', 103);
382
+                }
383
+                // Check if has permission to promote subadmins
384
+                if (!$subAdminManager->isSubAdminOfGroup($user, $group) && !$isAdmin) {
385
+                    throw new OCSForbiddenException('No permissions to promote subadmins');
386
+                }
387
+                $subadminGroups[] = $group;
388
+            }
389
+        }
390
+
391
+        $generatePasswordResetToken = false;
392
+        if (strlen($password) > IUserManager::MAX_PASSWORD_LENGTH) {
393
+            throw new OCSException('Invalid password value', 101);
394
+        }
395
+        if ($password === '') {
396
+            if ($email === '') {
397
+                throw new OCSException('To send a password link to the user an email address is required.', 108);
398
+            }
399
+
400
+            $passwordEvent = new GenerateSecurePasswordEvent();
401
+            $this->eventDispatcher->dispatchTyped($passwordEvent);
402
+
403
+            $password = $passwordEvent->getPassword();
404
+            if ($password === null) {
405
+                // Fallback: ensure to pass password_policy in any case
406
+                $password = $this->secureRandom->generate(10)
407
+                    . $this->secureRandom->generate(1, ISecureRandom::CHAR_UPPER)
408
+                    . $this->secureRandom->generate(1, ISecureRandom::CHAR_LOWER)
409
+                    . $this->secureRandom->generate(1, ISecureRandom::CHAR_DIGITS)
410
+                    . $this->secureRandom->generate(1, ISecureRandom::CHAR_SYMBOLS);
411
+            }
412
+            $generatePasswordResetToken = true;
413
+        }
414
+
415
+        if ($email === '' && $this->config->getAppValue('core', 'newUser.requireEmail', 'no') === 'yes') {
416
+            throw new OCSException('Required email address was not provided', 110);
417
+        }
418
+
419
+        try {
420
+            $newUser = $this->userManager->createUser($userid, $password);
421
+            $this->logger->info('Successful addUser call with userid: ' . $userid, ['app' => 'ocs_api']);
422
+
423
+            foreach ($groups as $group) {
424
+                $this->groupManager->get($group)->addUser($newUser);
425
+                $this->logger->info('Added userid ' . $userid . ' to group ' . $group, ['app' => 'ocs_api']);
426
+            }
427
+            foreach ($subadminGroups as $group) {
428
+                $subAdminManager->createSubAdmin($newUser, $group);
429
+            }
430
+
431
+            if ($displayName !== '') {
432
+                try {
433
+                    $this->editUser($userid, self::USER_FIELD_DISPLAYNAME, $displayName);
434
+                } catch (OCSException $e) {
435
+                    if ($newUser instanceof IUser) {
436
+                        $newUser->delete();
437
+                    }
438
+                    throw $e;
439
+                }
440
+            }
441
+
442
+            if ($quota !== '') {
443
+                $this->editUser($userid, self::USER_FIELD_QUOTA, $quota);
444
+            }
445
+
446
+            if ($language !== '') {
447
+                $this->editUser($userid, self::USER_FIELD_LANGUAGE, $language);
448
+            }
449
+
450
+            // Send new user mail only if a mail is set
451
+            if ($email !== '') {
452
+                $newUser->setEMailAddress($email);
453
+                if ($this->config->getAppValue('core', 'newUser.sendEmail', 'yes') === 'yes') {
454
+                    try {
455
+                        $emailTemplate = $this->newUserMailHelper->generateTemplate($newUser, $generatePasswordResetToken);
456
+                        $this->newUserMailHelper->sendMail($newUser, $emailTemplate);
457
+                    } catch (\Exception $e) {
458
+                        // Mail could be failing hard or just be plain not configured
459
+                        // Logging error as it is the hardest of the two
460
+                        $this->logger->error(
461
+                            "Unable to send the invitation mail to $email",
462
+                            [
463
+                                'app' => 'ocs_api',
464
+                                'exception' => $e,
465
+                            ]
466
+                        );
467
+                    }
468
+                }
469
+            }
470
+
471
+            return new DataResponse(['id' => $userid]);
472
+        } catch (HintException $e) {
473
+            $this->logger->warning(
474
+                'Failed addUser attempt with hint exception.',
475
+                [
476
+                    'app' => 'ocs_api',
477
+                    'exception' => $e,
478
+                ]
479
+            );
480
+            throw new OCSException($e->getHint(), 107);
481
+        } catch (OCSException $e) {
482
+            $this->logger->warning(
483
+                'Failed addUser attempt with ocs exception.',
484
+                [
485
+                    'app' => 'ocs_api',
486
+                    'exception' => $e,
487
+                ]
488
+            );
489
+            throw $e;
490
+        } catch (InvalidArgumentException $e) {
491
+            $this->logger->error(
492
+                'Failed addUser attempt with invalid argument exception.',
493
+                [
494
+                    'app' => 'ocs_api',
495
+                    'exception' => $e,
496
+                ]
497
+            );
498
+            throw new OCSException($e->getMessage(), 101);
499
+        } catch (\Exception $e) {
500
+            $this->logger->error(
501
+                'Failed addUser attempt with exception.',
502
+                [
503
+                    'app' => 'ocs_api',
504
+                    'exception' => $e
505
+                ]
506
+            );
507
+            throw new OCSException('Bad request', 101);
508
+        }
509
+    }
510
+
511
+    /**
512
+     * @NoAdminRequired
513
+     * @NoSubAdminRequired
514
+     *
515
+     * gets user info
516
+     *
517
+     * @param string $userId
518
+     * @return DataResponse
519
+     * @throws OCSException
520
+     */
521
+    public function getUser(string $userId): DataResponse {
522
+        $includeScopes = false;
523
+        $currentUser = $this->userSession->getUser();
524
+        if ($currentUser && $currentUser->getUID() === $userId) {
525
+            $includeScopes = true;
526
+        }
527
+
528
+        $data = $this->getUserData($userId, $includeScopes);
529
+        // getUserData returns empty array if not enough permissions
530
+        if (empty($data)) {
531
+            throw new OCSException('', OCSController::RESPOND_NOT_FOUND);
532
+        }
533
+        return new DataResponse($data);
534
+    }
535
+
536
+    /**
537
+     * @NoAdminRequired
538
+     * @NoSubAdminRequired
539
+     *
540
+     * gets user info from the currently logged in user
541
+     *
542
+     * @return DataResponse
543
+     * @throws OCSException
544
+     */
545
+    public function getCurrentUser(): DataResponse {
546
+        $user = $this->userSession->getUser();
547
+        if ($user) {
548
+            $data = $this->getUserData($user->getUID(), true);
549
+            return new DataResponse($data);
550
+        }
551
+
552
+        throw new OCSException('', OCSController::RESPOND_UNAUTHORISED);
553
+    }
554
+
555
+    /**
556
+     * @NoAdminRequired
557
+     * @NoSubAdminRequired
558
+     *
559
+     * @return DataResponse
560
+     * @throws OCSException
561
+     */
562
+    public function getEditableFields(): DataResponse {
563
+        $currentLoggedInUser = $this->userSession->getUser();
564
+        if (!$currentLoggedInUser instanceof IUser) {
565
+            throw new OCSException('', OCSController::RESPOND_NOT_FOUND);
566
+        }
567
+
568
+        return $this->getEditableFieldsForUser($currentLoggedInUser->getUID());
569
+    }
570
+
571
+    /**
572
+     * @NoAdminRequired
573
+     * @NoSubAdminRequired
574
+     *
575
+     * @param string $userId
576
+     * @return DataResponse
577
+     * @throws OCSException
578
+     */
579
+    public function getEditableFieldsForUser(string $userId): DataResponse {
580
+        $currentLoggedInUser = $this->userSession->getUser();
581
+        if (!$currentLoggedInUser instanceof IUser) {
582
+            throw new OCSException('', OCSController::RESPOND_NOT_FOUND);
583
+        }
584
+
585
+        $permittedFields = [];
586
+
587
+        if ($userId !== $currentLoggedInUser->getUID()) {
588
+            $targetUser = $this->userManager->get($userId);
589
+            if (!$targetUser instanceof IUser) {
590
+                throw new OCSException('', OCSController::RESPOND_NOT_FOUND);
591
+            }
592
+
593
+            $subAdminManager = $this->groupManager->getSubAdmin();
594
+            if (
595
+                !$this->groupManager->isAdmin($currentLoggedInUser->getUID())
596
+                && !$subAdminManager->isUserAccessible($currentLoggedInUser, $targetUser)
597
+            ) {
598
+                throw new OCSException('', OCSController::RESPOND_NOT_FOUND);
599
+            }
600
+        } else {
601
+            $targetUser = $currentLoggedInUser;
602
+        }
603
+
604
+        // Editing self (display, email)
605
+        if ($this->config->getSystemValue('allow_user_to_change_display_name', true) !== false) {
606
+            if (
607
+                $targetUser->getBackend() instanceof ISetDisplayNameBackend
608
+                || $targetUser->getBackend()->implementsActions(Backend::SET_DISPLAYNAME)
609
+            ) {
610
+                $permittedFields[] = IAccountManager::PROPERTY_DISPLAYNAME;
611
+            }
612
+            $permittedFields[] = IAccountManager::PROPERTY_EMAIL;
613
+        }
614
+
615
+        $permittedFields[] = IAccountManager::COLLECTION_EMAIL;
616
+        $permittedFields[] = IAccountManager::PROPERTY_PHONE;
617
+        $permittedFields[] = IAccountManager::PROPERTY_ADDRESS;
618
+        $permittedFields[] = IAccountManager::PROPERTY_WEBSITE;
619
+        $permittedFields[] = IAccountManager::PROPERTY_TWITTER;
620
+        $permittedFields[] = IAccountManager::PROPERTY_FEDIVERSE;
621
+        $permittedFields[] = IAccountManager::PROPERTY_ORGANISATION;
622
+        $permittedFields[] = IAccountManager::PROPERTY_ROLE;
623
+        $permittedFields[] = IAccountManager::PROPERTY_HEADLINE;
624
+        $permittedFields[] = IAccountManager::PROPERTY_BIOGRAPHY;
625
+        $permittedFields[] = IAccountManager::PROPERTY_PROFILE_ENABLED;
626
+
627
+        return new DataResponse($permittedFields);
628
+    }
629
+
630
+    /**
631
+     * @NoAdminRequired
632
+     * @NoSubAdminRequired
633
+     * @PasswordConfirmationRequired
634
+     * @UserRateThrottle(limit=5, period=60)
635
+     *
636
+     * @throws OCSException
637
+     */
638
+    public function editUserMultiValue(
639
+        string $userId,
640
+        string $collectionName,
641
+        string $key,
642
+        string $value
643
+    ): DataResponse {
644
+        $currentLoggedInUser = $this->userSession->getUser();
645
+        if ($currentLoggedInUser === null) {
646
+            throw new OCSException('', OCSController::RESPOND_UNAUTHORISED);
647
+        }
648
+
649
+        $targetUser = $this->userManager->get($userId);
650
+        if ($targetUser === null) {
651
+            throw new OCSException('', OCSController::RESPOND_NOT_FOUND);
652
+        }
653
+
654
+        $subAdminManager = $this->groupManager->getSubAdmin();
655
+        $isAdminOrSubadmin = $this->groupManager->isAdmin($currentLoggedInUser->getUID())
656
+            || $subAdminManager->isUserAccessible($currentLoggedInUser, $targetUser);
657
+
658
+        $permittedFields = [];
659
+        if ($targetUser->getUID() === $currentLoggedInUser->getUID()) {
660
+            // Editing self (display, email)
661
+            $permittedFields[] = IAccountManager::COLLECTION_EMAIL;
662
+            $permittedFields[] = IAccountManager::COLLECTION_EMAIL . self::SCOPE_SUFFIX;
663
+        } else {
664
+            // Check if admin / subadmin
665
+            if ($isAdminOrSubadmin) {
666
+                // They have permissions over the user
667
+                $permittedFields[] = IAccountManager::COLLECTION_EMAIL;
668
+            } else {
669
+                // No rights
670
+                throw new OCSException('', OCSController::RESPOND_NOT_FOUND);
671
+            }
672
+        }
673
+
674
+        // Check if permitted to edit this field
675
+        if (!in_array($collectionName, $permittedFields)) {
676
+            throw new OCSException('', 103);
677
+        }
678
+
679
+        switch ($collectionName) {
680
+            case IAccountManager::COLLECTION_EMAIL:
681
+                $userAccount = $this->accountManager->getAccount($targetUser);
682
+                $mailCollection = $userAccount->getPropertyCollection(IAccountManager::COLLECTION_EMAIL);
683
+                $mailCollection->removePropertyByValue($key);
684
+                if ($value !== '') {
685
+                    $mailCollection->addPropertyWithDefaults($value);
686
+                    $property = $mailCollection->getPropertyByValue($key);
687
+                    if ($isAdminOrSubadmin && $property) {
688
+                        // admin set mails are auto-verified
689
+                        $property->setLocallyVerified(IAccountManager::VERIFIED);
690
+                    }
691
+                }
692
+                $this->accountManager->updateAccount($userAccount);
693
+                break;
694
+
695
+            case IAccountManager::COLLECTION_EMAIL . self::SCOPE_SUFFIX:
696
+                $userAccount = $this->accountManager->getAccount($targetUser);
697
+                $mailCollection = $userAccount->getPropertyCollection(IAccountManager::COLLECTION_EMAIL);
698
+                $targetProperty = null;
699
+                foreach ($mailCollection->getProperties() as $property) {
700
+                    if ($property->getValue() === $key) {
701
+                        $targetProperty = $property;
702
+                        break;
703
+                    }
704
+                }
705
+                if ($targetProperty instanceof IAccountProperty) {
706
+                    try {
707
+                        $targetProperty->setScope($value);
708
+                        $this->accountManager->updateAccount($userAccount);
709
+                    } catch (InvalidArgumentException $e) {
710
+                        throw new OCSException('', 102);
711
+                    }
712
+                } else {
713
+                    throw new OCSException('', 102);
714
+                }
715
+                break;
716
+
717
+            default:
718
+                throw new OCSException('', 103);
719
+        }
720
+        return new DataResponse();
721
+    }
722
+
723
+    /**
724
+     * @NoAdminRequired
725
+     * @NoSubAdminRequired
726
+     * @PasswordConfirmationRequired
727
+     * @UserRateThrottle(limit=50, period=600)
728
+     *
729
+     * edit users
730
+     *
731
+     * @param string $userId
732
+     * @param string $key
733
+     * @param string $value
734
+     * @return DataResponse
735
+     * @throws OCSException
736
+     */
737
+    public function editUser(string $userId, string $key, string $value): DataResponse {
738
+        $currentLoggedInUser = $this->userSession->getUser();
739
+
740
+        $targetUser = $this->userManager->get($userId);
741
+        if ($targetUser === null) {
742
+            throw new OCSException('', OCSController::RESPOND_NOT_FOUND);
743
+        }
744
+
745
+        $permittedFields = [];
746
+        if ($targetUser->getUID() === $currentLoggedInUser->getUID()) {
747
+            // Editing self (display, email)
748
+            if ($this->config->getSystemValue('allow_user_to_change_display_name', true) !== false) {
749
+                if (
750
+                    $targetUser->getBackend() instanceof ISetDisplayNameBackend
751
+                    || $targetUser->getBackend()->implementsActions(Backend::SET_DISPLAYNAME)
752
+                ) {
753
+                    $permittedFields[] = self::USER_FIELD_DISPLAYNAME;
754
+                    $permittedFields[] = IAccountManager::PROPERTY_DISPLAYNAME;
755
+                }
756
+                $permittedFields[] = IAccountManager::PROPERTY_EMAIL;
757
+            }
758
+
759
+            $permittedFields[] = IAccountManager::PROPERTY_DISPLAYNAME . self::SCOPE_SUFFIX;
760
+            $permittedFields[] = IAccountManager::PROPERTY_EMAIL . self::SCOPE_SUFFIX;
761
+
762
+            $permittedFields[] = IAccountManager::COLLECTION_EMAIL;
763
+
764
+            $permittedFields[] = self::USER_FIELD_PASSWORD;
765
+            $permittedFields[] = self::USER_FIELD_NOTIFICATION_EMAIL;
766
+            if (
767
+                $this->config->getSystemValue('force_language', false) === false ||
768
+                $this->groupManager->isAdmin($currentLoggedInUser->getUID())
769
+            ) {
770
+                $permittedFields[] = self::USER_FIELD_LANGUAGE;
771
+            }
772
+
773
+            if (
774
+                $this->config->getSystemValue('force_locale', false) === false ||
775
+                $this->groupManager->isAdmin($currentLoggedInUser->getUID())
776
+            ) {
777
+                $permittedFields[] = self::USER_FIELD_LOCALE;
778
+            }
779
+
780
+            $permittedFields[] = IAccountManager::PROPERTY_PHONE;
781
+            $permittedFields[] = IAccountManager::PROPERTY_ADDRESS;
782
+            $permittedFields[] = IAccountManager::PROPERTY_WEBSITE;
783
+            $permittedFields[] = IAccountManager::PROPERTY_TWITTER;
784
+            $permittedFields[] = IAccountManager::PROPERTY_FEDIVERSE;
785
+            $permittedFields[] = IAccountManager::PROPERTY_ORGANISATION;
786
+            $permittedFields[] = IAccountManager::PROPERTY_ROLE;
787
+            $permittedFields[] = IAccountManager::PROPERTY_HEADLINE;
788
+            $permittedFields[] = IAccountManager::PROPERTY_BIOGRAPHY;
789
+            $permittedFields[] = IAccountManager::PROPERTY_PROFILE_ENABLED;
790
+            $permittedFields[] = IAccountManager::PROPERTY_PHONE . self::SCOPE_SUFFIX;
791
+            $permittedFields[] = IAccountManager::PROPERTY_ADDRESS . self::SCOPE_SUFFIX;
792
+            $permittedFields[] = IAccountManager::PROPERTY_WEBSITE . self::SCOPE_SUFFIX;
793
+            $permittedFields[] = IAccountManager::PROPERTY_TWITTER . self::SCOPE_SUFFIX;
794
+            $permittedFields[] = IAccountManager::PROPERTY_FEDIVERSE . self::SCOPE_SUFFIX;
795
+            $permittedFields[] = IAccountManager::PROPERTY_ORGANISATION . self::SCOPE_SUFFIX;
796
+            $permittedFields[] = IAccountManager::PROPERTY_ROLE . self::SCOPE_SUFFIX;
797
+            $permittedFields[] = IAccountManager::PROPERTY_HEADLINE . self::SCOPE_SUFFIX;
798
+            $permittedFields[] = IAccountManager::PROPERTY_BIOGRAPHY . self::SCOPE_SUFFIX;
799
+            $permittedFields[] = IAccountManager::PROPERTY_PROFILE_ENABLED . self::SCOPE_SUFFIX;
800
+
801
+            $permittedFields[] = IAccountManager::PROPERTY_AVATAR . self::SCOPE_SUFFIX;
802
+
803
+            // If admin they can edit their own quota
804
+            if ($this->groupManager->isAdmin($currentLoggedInUser->getUID())) {
805
+                $permittedFields[] = self::USER_FIELD_QUOTA;
806
+            }
807
+        } else {
808
+            // Check if admin / subadmin
809
+            $subAdminManager = $this->groupManager->getSubAdmin();
810
+            if (
811
+                $this->groupManager->isAdmin($currentLoggedInUser->getUID())
812
+                || $subAdminManager->isUserAccessible($currentLoggedInUser, $targetUser)
813
+            ) {
814
+                // They have permissions over the user
815
+                if (
816
+                    $targetUser->getBackend() instanceof ISetDisplayNameBackend
817
+                    || $targetUser->getBackend()->implementsActions(Backend::SET_DISPLAYNAME)
818
+                ) {
819
+                    $permittedFields[] = self::USER_FIELD_DISPLAYNAME;
820
+                    $permittedFields[] = IAccountManager::PROPERTY_DISPLAYNAME;
821
+                }
822
+                $permittedFields[] = IAccountManager::PROPERTY_EMAIL;
823
+                $permittedFields[] = IAccountManager::COLLECTION_EMAIL;
824
+                $permittedFields[] = self::USER_FIELD_PASSWORD;
825
+                $permittedFields[] = self::USER_FIELD_LANGUAGE;
826
+                $permittedFields[] = self::USER_FIELD_LOCALE;
827
+                $permittedFields[] = IAccountManager::PROPERTY_PHONE;
828
+                $permittedFields[] = IAccountManager::PROPERTY_ADDRESS;
829
+                $permittedFields[] = IAccountManager::PROPERTY_WEBSITE;
830
+                $permittedFields[] = IAccountManager::PROPERTY_TWITTER;
831
+                $permittedFields[] = IAccountManager::PROPERTY_FEDIVERSE;
832
+                $permittedFields[] = IAccountManager::PROPERTY_ORGANISATION;
833
+                $permittedFields[] = IAccountManager::PROPERTY_ROLE;
834
+                $permittedFields[] = IAccountManager::PROPERTY_HEADLINE;
835
+                $permittedFields[] = IAccountManager::PROPERTY_BIOGRAPHY;
836
+                $permittedFields[] = IAccountManager::PROPERTY_PROFILE_ENABLED;
837
+                $permittedFields[] = self::USER_FIELD_QUOTA;
838
+                $permittedFields[] = self::USER_FIELD_NOTIFICATION_EMAIL;
839
+            } else {
840
+                // No rights
841
+                throw new OCSException('', OCSController::RESPOND_NOT_FOUND);
842
+            }
843
+        }
844
+        // Check if permitted to edit this field
845
+        if (!in_array($key, $permittedFields)) {
846
+            throw new OCSException('', 103);
847
+        }
848
+        // Process the edit
849
+        switch ($key) {
850
+            case self::USER_FIELD_DISPLAYNAME:
851
+            case IAccountManager::PROPERTY_DISPLAYNAME:
852
+                try {
853
+                    $targetUser->setDisplayName($value);
854
+                } catch (InvalidArgumentException $e) {
855
+                    throw new OCSException($e->getMessage(), 101);
856
+                }
857
+                break;
858
+            case self::USER_FIELD_QUOTA:
859
+                $quota = $value;
860
+                if ($quota !== 'none' && $quota !== 'default') {
861
+                    if (is_numeric($quota)) {
862
+                        $quota = (float) $quota;
863
+                    } else {
864
+                        $quota = \OCP\Util::computerFileSize($quota);
865
+                    }
866
+                    if ($quota === false) {
867
+                        throw new OCSException('Invalid quota value ' . $value, 102);
868
+                    }
869
+                    if ($quota === -1) {
870
+                        $quota = 'none';
871
+                    } else {
872
+                        $maxQuota = (int) $this->config->getAppValue('files', 'max_quota', '-1');
873
+                        if ($maxQuota !== -1 && $quota > $maxQuota) {
874
+                            throw new OCSException('Invalid quota value. ' . $value . ' is exceeding the maximum quota', 102);
875
+                        }
876
+                        $quota = \OCP\Util::humanFileSize($quota);
877
+                    }
878
+                }
879
+                // no else block because quota can be set to 'none' in previous if
880
+                if ($quota === 'none') {
881
+                    $allowUnlimitedQuota = $this->config->getAppValue('files', 'allow_unlimited_quota', '1') === '1';
882
+                    if (!$allowUnlimitedQuota) {
883
+                        throw new OCSException('Unlimited quota is forbidden on this instance', 102);
884
+                    }
885
+                }
886
+                $targetUser->setQuota($quota);
887
+                break;
888
+            case self::USER_FIELD_PASSWORD:
889
+                try {
890
+                    if (strlen($value) > IUserManager::MAX_PASSWORD_LENGTH) {
891
+                        throw new OCSException('Invalid password value', 102);
892
+                    }
893
+                    if (!$targetUser->canChangePassword()) {
894
+                        throw new OCSException('Setting the password is not supported by the users backend', 103);
895
+                    }
896
+                    $targetUser->setPassword($value);
897
+                } catch (HintException $e) { // password policy error
898
+                    throw new OCSException($e->getMessage(), 103);
899
+                }
900
+                break;
901
+            case self::USER_FIELD_LANGUAGE:
902
+                $languagesCodes = $this->l10nFactory->findAvailableLanguages();
903
+                if (!in_array($value, $languagesCodes, true) && $value !== 'en') {
904
+                    throw new OCSException('Invalid language', 102);
905
+                }
906
+                $this->config->setUserValue($targetUser->getUID(), 'core', 'lang', $value);
907
+                break;
908
+            case self::USER_FIELD_LOCALE:
909
+                if (!$this->l10nFactory->localeExists($value)) {
910
+                    throw new OCSException('Invalid locale', 102);
911
+                }
912
+                $this->config->setUserValue($targetUser->getUID(), 'core', 'locale', $value);
913
+                break;
914
+            case self::USER_FIELD_NOTIFICATION_EMAIL:
915
+                $success = false;
916
+                if ($value === '' || filter_var($value, FILTER_VALIDATE_EMAIL)) {
917
+                    try {
918
+                        $targetUser->setPrimaryEMailAddress($value);
919
+                        $success = true;
920
+                    } catch (InvalidArgumentException $e) {
921
+                        $this->logger->info(
922
+                            'Cannot set primary email, because provided address is not verified',
923
+                            [
924
+                                'app' => 'provisioning_api',
925
+                                'exception' => $e,
926
+                            ]
927
+                        );
928
+                    }
929
+                }
930
+                if (!$success) {
931
+                    throw new OCSException('', 102);
932
+                }
933
+                break;
934
+            case IAccountManager::PROPERTY_EMAIL:
935
+                if (filter_var($value, FILTER_VALIDATE_EMAIL) || $value === '') {
936
+                    $targetUser->setEMailAddress($value);
937
+                } else {
938
+                    throw new OCSException('', 102);
939
+                }
940
+                break;
941
+            case IAccountManager::COLLECTION_EMAIL:
942
+                if (filter_var($value, FILTER_VALIDATE_EMAIL) && $value !== $targetUser->getSystemEMailAddress()) {
943
+                    $userAccount = $this->accountManager->getAccount($targetUser);
944
+                    $mailCollection = $userAccount->getPropertyCollection(IAccountManager::COLLECTION_EMAIL);
945
+                    foreach ($mailCollection->getProperties() as $property) {
946
+                        if ($property->getValue() === $value) {
947
+                            break;
948
+                        }
949
+                    }
950
+                    $mailCollection->addPropertyWithDefaults($value);
951
+                    $this->accountManager->updateAccount($userAccount);
952
+                } else {
953
+                    throw new OCSException('', 102);
954
+                }
955
+                break;
956
+            case IAccountManager::PROPERTY_PHONE:
957
+            case IAccountManager::PROPERTY_ADDRESS:
958
+            case IAccountManager::PROPERTY_WEBSITE:
959
+            case IAccountManager::PROPERTY_TWITTER:
960
+            case IAccountManager::PROPERTY_FEDIVERSE:
961
+            case IAccountManager::PROPERTY_ORGANISATION:
962
+            case IAccountManager::PROPERTY_ROLE:
963
+            case IAccountManager::PROPERTY_HEADLINE:
964
+            case IAccountManager::PROPERTY_BIOGRAPHY:
965
+                $userAccount = $this->accountManager->getAccount($targetUser);
966
+                try {
967
+                    $userProperty = $userAccount->getProperty($key);
968
+                    if ($userProperty->getValue() !== $value) {
969
+                        try {
970
+                            $userProperty->setValue($value);
971
+                            if ($userProperty->getName() === IAccountManager::PROPERTY_PHONE) {
972
+                                $this->knownUserService->deleteByContactUserId($targetUser->getUID());
973
+                            }
974
+                        } catch (InvalidArgumentException $e) {
975
+                            throw new OCSException('Invalid ' . $e->getMessage(), 102);
976
+                        }
977
+                    }
978
+                } catch (PropertyDoesNotExistException $e) {
979
+                    $userAccount->setProperty($key, $value, IAccountManager::SCOPE_PRIVATE, IAccountManager::NOT_VERIFIED);
980
+                }
981
+                try {
982
+                    $this->accountManager->updateAccount($userAccount);
983
+                } catch (InvalidArgumentException $e) {
984
+                    throw new OCSException('Invalid ' . $e->getMessage(), 102);
985
+                }
986
+                break;
987
+            case IAccountManager::PROPERTY_PROFILE_ENABLED:
988
+                $userAccount = $this->accountManager->getAccount($targetUser);
989
+                try {
990
+                    $userProperty = $userAccount->getProperty($key);
991
+                    if ($userProperty->getValue() !== $value) {
992
+                        $userProperty->setValue($value);
993
+                    }
994
+                } catch (PropertyDoesNotExistException $e) {
995
+                    $userAccount->setProperty($key, $value, IAccountManager::SCOPE_LOCAL, IAccountManager::NOT_VERIFIED);
996
+                }
997
+                $this->accountManager->updateAccount($userAccount);
998
+                break;
999
+            case IAccountManager::PROPERTY_DISPLAYNAME . self::SCOPE_SUFFIX:
1000
+            case IAccountManager::PROPERTY_EMAIL . self::SCOPE_SUFFIX:
1001
+            case IAccountManager::PROPERTY_PHONE . self::SCOPE_SUFFIX:
1002
+            case IAccountManager::PROPERTY_ADDRESS . self::SCOPE_SUFFIX:
1003
+            case IAccountManager::PROPERTY_WEBSITE . self::SCOPE_SUFFIX:
1004
+            case IAccountManager::PROPERTY_TWITTER . self::SCOPE_SUFFIX:
1005
+            case IAccountManager::PROPERTY_FEDIVERSE . self::SCOPE_SUFFIX:
1006
+            case IAccountManager::PROPERTY_ORGANISATION . self::SCOPE_SUFFIX:
1007
+            case IAccountManager::PROPERTY_ROLE . self::SCOPE_SUFFIX:
1008
+            case IAccountManager::PROPERTY_HEADLINE . self::SCOPE_SUFFIX:
1009
+            case IAccountManager::PROPERTY_BIOGRAPHY . self::SCOPE_SUFFIX:
1010
+            case IAccountManager::PROPERTY_PROFILE_ENABLED . self::SCOPE_SUFFIX:
1011
+            case IAccountManager::PROPERTY_AVATAR . self::SCOPE_SUFFIX:
1012
+                $propertyName = substr($key, 0, strlen($key) - strlen(self::SCOPE_SUFFIX));
1013
+                $userAccount = $this->accountManager->getAccount($targetUser);
1014
+                $userProperty = $userAccount->getProperty($propertyName);
1015
+                if ($userProperty->getScope() !== $value) {
1016
+                    try {
1017
+                        $userProperty->setScope($value);
1018
+                        $this->accountManager->updateAccount($userAccount);
1019
+                    } catch (InvalidArgumentException $e) {
1020
+                        throw new OCSException('Invalid ' . $e->getMessage(), 102);
1021
+                    }
1022
+                }
1023
+                break;
1024
+            default:
1025
+                throw new OCSException('', 103);
1026
+        }
1027
+        return new DataResponse();
1028
+    }
1029
+
1030
+    /**
1031
+     * @PasswordConfirmationRequired
1032
+     * @NoAdminRequired
1033
+     *
1034
+     * @param string $userId
1035
+     *
1036
+     * @return DataResponse
1037
+     *
1038
+     * @throws OCSException
1039
+     */
1040
+    public function wipeUserDevices(string $userId): DataResponse {
1041
+        /** @var IUser $currentLoggedInUser */
1042
+        $currentLoggedInUser = $this->userSession->getUser();
1043
+
1044
+        $targetUser = $this->userManager->get($userId);
1045
+
1046
+        if ($targetUser === null) {
1047
+            throw new OCSException('', OCSController::RESPOND_NOT_FOUND);
1048
+        }
1049
+
1050
+        if ($targetUser->getUID() === $currentLoggedInUser->getUID()) {
1051
+            throw new OCSException('', 101);
1052
+        }
1053
+
1054
+        // If not permitted
1055
+        $subAdminManager = $this->groupManager->getSubAdmin();
1056
+        if (!$this->groupManager->isAdmin($currentLoggedInUser->getUID()) && !$subAdminManager->isUserAccessible($currentLoggedInUser, $targetUser)) {
1057
+            throw new OCSException('', OCSController::RESPOND_NOT_FOUND);
1058
+        }
1059
+
1060
+        $this->remoteWipe->markAllTokensForWipe($targetUser);
1061
+
1062
+        return new DataResponse();
1063
+    }
1064
+
1065
+    /**
1066
+     * @PasswordConfirmationRequired
1067
+     * @NoAdminRequired
1068
+     *
1069
+     * @param string $userId
1070
+     * @return DataResponse
1071
+     * @throws OCSException
1072
+     */
1073
+    public function deleteUser(string $userId): DataResponse {
1074
+        $currentLoggedInUser = $this->userSession->getUser();
1075
+
1076
+        $targetUser = $this->userManager->get($userId);
1077
+
1078
+        if ($targetUser === null) {
1079
+            throw new OCSException('', OCSController::RESPOND_NOT_FOUND);
1080
+        }
1081
+
1082
+        if ($targetUser->getUID() === $currentLoggedInUser->getUID()) {
1083
+            throw new OCSException('', 101);
1084
+        }
1085
+
1086
+        // If not permitted
1087
+        $subAdminManager = $this->groupManager->getSubAdmin();
1088
+        if (!$this->groupManager->isAdmin($currentLoggedInUser->getUID()) && !$subAdminManager->isUserAccessible($currentLoggedInUser, $targetUser)) {
1089
+            throw new OCSException('', OCSController::RESPOND_NOT_FOUND);
1090
+        }
1091
+
1092
+        // Go ahead with the delete
1093
+        if ($targetUser->delete()) {
1094
+            return new DataResponse();
1095
+        } else {
1096
+            throw new OCSException('', 101);
1097
+        }
1098
+    }
1099
+
1100
+    /**
1101
+     * @PasswordConfirmationRequired
1102
+     * @NoAdminRequired
1103
+     *
1104
+     * @param string $userId
1105
+     * @return DataResponse
1106
+     * @throws OCSException
1107
+     * @throws OCSForbiddenException
1108
+     */
1109
+    public function disableUser(string $userId): DataResponse {
1110
+        return $this->setEnabled($userId, false);
1111
+    }
1112
+
1113
+    /**
1114
+     * @PasswordConfirmationRequired
1115
+     * @NoAdminRequired
1116
+     *
1117
+     * @param string $userId
1118
+     * @return DataResponse
1119
+     * @throws OCSException
1120
+     * @throws OCSForbiddenException
1121
+     */
1122
+    public function enableUser(string $userId): DataResponse {
1123
+        return $this->setEnabled($userId, true);
1124
+    }
1125
+
1126
+    /**
1127
+     * @param string $userId
1128
+     * @param bool $value
1129
+     * @return DataResponse
1130
+     * @throws OCSException
1131
+     */
1132
+    private function setEnabled(string $userId, bool $value): DataResponse {
1133
+        $currentLoggedInUser = $this->userSession->getUser();
1134
+
1135
+        $targetUser = $this->userManager->get($userId);
1136
+        if ($targetUser === null || $targetUser->getUID() === $currentLoggedInUser->getUID()) {
1137
+            throw new OCSException('', 101);
1138
+        }
1139
+
1140
+        // If not permitted
1141
+        $subAdminManager = $this->groupManager->getSubAdmin();
1142
+        if (!$this->groupManager->isAdmin($currentLoggedInUser->getUID()) && !$subAdminManager->isUserAccessible($currentLoggedInUser, $targetUser)) {
1143
+            throw new OCSException('', OCSController::RESPOND_NOT_FOUND);
1144
+        }
1145
+
1146
+        // enable/disable the user now
1147
+        $targetUser->setEnabled($value);
1148
+        return new DataResponse();
1149
+    }
1150
+
1151
+    /**
1152
+     * @NoAdminRequired
1153
+     * @NoSubAdminRequired
1154
+     *
1155
+     * @param string $userId
1156
+     * @return DataResponse
1157
+     * @throws OCSException
1158
+     */
1159
+    public function getUsersGroups(string $userId): DataResponse {
1160
+        $loggedInUser = $this->userSession->getUser();
1161
+
1162
+        $targetUser = $this->userManager->get($userId);
1163
+        if ($targetUser === null) {
1164
+            throw new OCSException('', OCSController::RESPOND_NOT_FOUND);
1165
+        }
1166
+
1167
+        if ($targetUser->getUID() === $loggedInUser->getUID() || $this->groupManager->isAdmin($loggedInUser->getUID())) {
1168
+            // Self lookup or admin lookup
1169
+            return new DataResponse([
1170
+                'groups' => $this->groupManager->getUserGroupIds($targetUser)
1171
+            ]);
1172
+        } else {
1173
+            $subAdminManager = $this->groupManager->getSubAdmin();
1174
+
1175
+            // Looking up someone else
1176
+            if ($subAdminManager->isUserAccessible($loggedInUser, $targetUser)) {
1177
+                // Return the group that the method caller is subadmin of for the user in question
1178
+                /** @var IGroup[] $getSubAdminsGroups */
1179
+                $getSubAdminsGroups = $subAdminManager->getSubAdminsGroups($loggedInUser);
1180
+                foreach ($getSubAdminsGroups as $key => $group) {
1181
+                    $getSubAdminsGroups[$key] = $group->getGID();
1182
+                }
1183
+                $groups = array_intersect(
1184
+                    $getSubAdminsGroups,
1185
+                    $this->groupManager->getUserGroupIds($targetUser)
1186
+                );
1187
+                return new DataResponse(['groups' => $groups]);
1188
+            } else {
1189
+                // Not permitted
1190
+                throw new OCSException('', OCSController::RESPOND_NOT_FOUND);
1191
+            }
1192
+        }
1193
+    }
1194
+
1195
+    /**
1196
+     * @PasswordConfirmationRequired
1197
+     * @NoAdminRequired
1198
+     *
1199
+     * @param string $userId
1200
+     * @param string $groupid
1201
+     * @return DataResponse
1202
+     * @throws OCSException
1203
+     */
1204
+    public function addToGroup(string $userId, string $groupid = ''): DataResponse {
1205
+        if ($groupid === '') {
1206
+            throw new OCSException('', 101);
1207
+        }
1208
+
1209
+        $group = $this->groupManager->get($groupid);
1210
+        $targetUser = $this->userManager->get($userId);
1211
+        if ($group === null) {
1212
+            throw new OCSException('', 102);
1213
+        }
1214
+        if ($targetUser === null) {
1215
+            throw new OCSException('', 103);
1216
+        }
1217
+
1218
+        // If they're not an admin, check they are a subadmin of the group in question
1219
+        $loggedInUser = $this->userSession->getUser();
1220
+        $subAdminManager = $this->groupManager->getSubAdmin();
1221
+        if (!$this->groupManager->isAdmin($loggedInUser->getUID()) && !$subAdminManager->isSubAdminOfGroup($loggedInUser, $group)) {
1222
+            throw new OCSException('', 104);
1223
+        }
1224
+
1225
+        // Add user to group
1226
+        $group->addUser($targetUser);
1227
+        return new DataResponse();
1228
+    }
1229
+
1230
+    /**
1231
+     * @PasswordConfirmationRequired
1232
+     * @NoAdminRequired
1233
+     *
1234
+     * @param string $userId
1235
+     * @param string $groupid
1236
+     * @return DataResponse
1237
+     * @throws OCSException
1238
+     */
1239
+    public function removeFromGroup(string $userId, string $groupid): DataResponse {
1240
+        $loggedInUser = $this->userSession->getUser();
1241
+
1242
+        if ($groupid === null || trim($groupid) === '') {
1243
+            throw new OCSException('', 101);
1244
+        }
1245
+
1246
+        $group = $this->groupManager->get($groupid);
1247
+        if ($group === null) {
1248
+            throw new OCSException('', 102);
1249
+        }
1250
+
1251
+        $targetUser = $this->userManager->get($userId);
1252
+        if ($targetUser === null) {
1253
+            throw new OCSException('', 103);
1254
+        }
1255
+
1256
+        // If they're not an admin, check they are a subadmin of the group in question
1257
+        $subAdminManager = $this->groupManager->getSubAdmin();
1258
+        if (!$this->groupManager->isAdmin($loggedInUser->getUID()) && !$subAdminManager->isSubAdminOfGroup($loggedInUser, $group)) {
1259
+            throw new OCSException('', 104);
1260
+        }
1261
+
1262
+        // Check they aren't removing themselves from 'admin' or their 'subadmin; group
1263
+        if ($targetUser->getUID() === $loggedInUser->getUID()) {
1264
+            if ($this->groupManager->isAdmin($loggedInUser->getUID())) {
1265
+                if ($group->getGID() === 'admin') {
1266
+                    throw new OCSException('Cannot remove yourself from the admin group', 105);
1267
+                }
1268
+            } else {
1269
+                // Not an admin, so the user must be a subadmin of this group, but that is not allowed.
1270
+                throw new OCSException('Cannot remove yourself from this group as you are a SubAdmin', 105);
1271
+            }
1272
+        } elseif (!$this->groupManager->isAdmin($loggedInUser->getUID())) {
1273
+            /** @var IGroup[] $subAdminGroups */
1274
+            $subAdminGroups = $subAdminManager->getSubAdminsGroups($loggedInUser);
1275
+            $subAdminGroups = array_map(function (IGroup $subAdminGroup) {
1276
+                return $subAdminGroup->getGID();
1277
+            }, $subAdminGroups);
1278
+            $userGroups = $this->groupManager->getUserGroupIds($targetUser);
1279
+            $userSubAdminGroups = array_intersect($subAdminGroups, $userGroups);
1280
+
1281
+            if (count($userSubAdminGroups) <= 1) {
1282
+                // Subadmin must not be able to remove a user from all their subadmin groups.
1283
+                throw new OCSException('Not viable to remove user from the last group you are SubAdmin of', 105);
1284
+            }
1285
+        }
1286
+
1287
+        // Remove user from group
1288
+        $group->removeUser($targetUser);
1289
+        return new DataResponse();
1290
+    }
1291
+
1292
+    /**
1293
+     * Creates a subadmin
1294
+     *
1295
+     * @PasswordConfirmationRequired
1296
+     *
1297
+     * @param string $userId
1298
+     * @param string $groupid
1299
+     * @return DataResponse
1300
+     * @throws OCSException
1301
+     */
1302
+    public function addSubAdmin(string $userId, string $groupid): DataResponse {
1303
+        $group = $this->groupManager->get($groupid);
1304
+        $user = $this->userManager->get($userId);
1305
+
1306
+        // Check if the user exists
1307
+        if ($user === null) {
1308
+            throw new OCSException('User does not exist', 101);
1309
+        }
1310
+        // Check if group exists
1311
+        if ($group === null) {
1312
+            throw new OCSException('Group does not exist', 102);
1313
+        }
1314
+        // Check if trying to make subadmin of admin group
1315
+        if ($group->getGID() === 'admin') {
1316
+            throw new OCSException('Cannot create subadmins for admin group', 103);
1317
+        }
1318
+
1319
+        $subAdminManager = $this->groupManager->getSubAdmin();
1320
+
1321
+        // We cannot be subadmin twice
1322
+        if ($subAdminManager->isSubAdminOfGroup($user, $group)) {
1323
+            return new DataResponse();
1324
+        }
1325
+        // Go
1326
+        $subAdminManager->createSubAdmin($user, $group);
1327
+        return new DataResponse();
1328
+    }
1329
+
1330
+    /**
1331
+     * Removes a subadmin from a group
1332
+     *
1333
+     * @PasswordConfirmationRequired
1334
+     *
1335
+     * @param string $userId
1336
+     * @param string $groupid
1337
+     * @return DataResponse
1338
+     * @throws OCSException
1339
+     */
1340
+    public function removeSubAdmin(string $userId, string $groupid): DataResponse {
1341
+        $group = $this->groupManager->get($groupid);
1342
+        $user = $this->userManager->get($userId);
1343
+        $subAdminManager = $this->groupManager->getSubAdmin();
1344
+
1345
+        // Check if the user exists
1346
+        if ($user === null) {
1347
+            throw new OCSException('User does not exist', 101);
1348
+        }
1349
+        // Check if the group exists
1350
+        if ($group === null) {
1351
+            throw new OCSException('Group does not exist', 101);
1352
+        }
1353
+        // Check if they are a subadmin of this said group
1354
+        if (!$subAdminManager->isSubAdminOfGroup($user, $group)) {
1355
+            throw new OCSException('User is not a subadmin of this group', 102);
1356
+        }
1357
+
1358
+        // Go
1359
+        $subAdminManager->deleteSubAdmin($user, $group);
1360
+        return new DataResponse();
1361
+    }
1362
+
1363
+    /**
1364
+     * Get the groups a user is a subadmin of
1365
+     *
1366
+     * @param string $userId
1367
+     * @return DataResponse
1368
+     * @throws OCSException
1369
+     */
1370
+    public function getUserSubAdminGroups(string $userId): DataResponse {
1371
+        $groups = $this->getUserSubAdminGroupsData($userId);
1372
+        return new DataResponse($groups);
1373
+    }
1374
+
1375
+    /**
1376
+     * @NoAdminRequired
1377
+     * @PasswordConfirmationRequired
1378
+     *
1379
+     * resend welcome message
1380
+     *
1381
+     * @param string $userId
1382
+     * @return DataResponse
1383
+     * @throws OCSException
1384
+     */
1385
+    public function resendWelcomeMessage(string $userId): DataResponse {
1386
+        $currentLoggedInUser = $this->userSession->getUser();
1387
+
1388
+        $targetUser = $this->userManager->get($userId);
1389
+        if ($targetUser === null) {
1390
+            throw new OCSException('', OCSController::RESPOND_NOT_FOUND);
1391
+        }
1392
+
1393
+        // Check if admin / subadmin
1394
+        $subAdminManager = $this->groupManager->getSubAdmin();
1395
+        if (
1396
+            !$subAdminManager->isUserAccessible($currentLoggedInUser, $targetUser)
1397
+            && !$this->groupManager->isAdmin($currentLoggedInUser->getUID())
1398
+        ) {
1399
+            // No rights
1400
+            throw new OCSException('', OCSController::RESPOND_NOT_FOUND);
1401
+        }
1402
+
1403
+        $email = $targetUser->getEMailAddress();
1404
+        if ($email === '' || $email === null) {
1405
+            throw new OCSException('Email address not available', 101);
1406
+        }
1407
+
1408
+        try {
1409
+            $emailTemplate = $this->newUserMailHelper->generateTemplate($targetUser, false);
1410
+            $this->newUserMailHelper->sendMail($targetUser, $emailTemplate);
1411
+        } catch (\Exception $e) {
1412
+            $this->logger->error(
1413
+                "Can't send new user mail to $email",
1414
+                [
1415
+                    'app' => 'settings',
1416
+                    'exception' => $e,
1417
+                ]
1418
+            );
1419
+            throw new OCSException('Sending email failed', 102);
1420
+        }
1421
+
1422
+        return new DataResponse();
1423
+    }
1424 1424
 }
Please login to merge, or discard this patch.
core/Controller/ProfileApiController.php 1 patch
Indentation   +42 added lines, -42 removed lines patch added patch discarded remove patch
@@ -38,54 +38,54 @@
 block discarded – undo
38 38
 use OC\Profile\ProfileManager;
39 39
 
40 40
 class ProfileApiController extends OCSController {
41
-	private ProfileConfigMapper $configMapper;
42
-	private ProfileManager $profileManager;
43
-	private IUserManager $userManager;
44
-	private IUserSession $userSession;
41
+    private ProfileConfigMapper $configMapper;
42
+    private ProfileManager $profileManager;
43
+    private IUserManager $userManager;
44
+    private IUserSession $userSession;
45 45
 
46
-	public function __construct(
47
-		IRequest $request,
48
-		ProfileConfigMapper $configMapper,
49
-		ProfileManager $profileManager,
50
-		IUserManager $userManager,
51
-		IUserSession $userSession
52
-	) {
53
-		parent::__construct('core', $request);
54
-		$this->configMapper = $configMapper;
55
-		$this->profileManager = $profileManager;
56
-		$this->userManager = $userManager;
57
-		$this->userSession = $userSession;
58
-	}
46
+    public function __construct(
47
+        IRequest $request,
48
+        ProfileConfigMapper $configMapper,
49
+        ProfileManager $profileManager,
50
+        IUserManager $userManager,
51
+        IUserSession $userSession
52
+    ) {
53
+        parent::__construct('core', $request);
54
+        $this->configMapper = $configMapper;
55
+        $this->profileManager = $profileManager;
56
+        $this->userManager = $userManager;
57
+        $this->userSession = $userSession;
58
+    }
59 59
 
60
-	/**
61
-	 * @NoAdminRequired
62
-	 * @NoSubAdminRequired
63
-	 * @PasswordConfirmationRequired
64
-	 * @UserRateThrottle(limit=40, period=600)
65
-	 */
66
-	public function setVisibility(string $targetUserId, string $paramId, string $visibility): DataResponse {
67
-		$requestingUser = $this->userSession->getUser();
68
-		$targetUser = $this->userManager->get($targetUserId);
60
+    /**
61
+     * @NoAdminRequired
62
+     * @NoSubAdminRequired
63
+     * @PasswordConfirmationRequired
64
+     * @UserRateThrottle(limit=40, period=600)
65
+     */
66
+    public function setVisibility(string $targetUserId, string $paramId, string $visibility): DataResponse {
67
+        $requestingUser = $this->userSession->getUser();
68
+        $targetUser = $this->userManager->get($targetUserId);
69 69
 
70
-		if (!$this->userManager->userExists($targetUserId)) {
71
-			throw new OCSNotFoundException('User does not exist');
72
-		}
70
+        if (!$this->userManager->userExists($targetUserId)) {
71
+            throw new OCSNotFoundException('User does not exist');
72
+        }
73 73
 
74
-		if ($requestingUser !== $targetUser) {
75
-			throw new OCSForbiddenException('Users can only edit their own visibility settings');
76
-		}
74
+        if ($requestingUser !== $targetUser) {
75
+            throw new OCSForbiddenException('Users can only edit their own visibility settings');
76
+        }
77 77
 
78
-		// Ensure that a profile config is created in the database
79
-		$this->profileManager->getProfileConfig($targetUser, $targetUser);
80
-		$config = $this->configMapper->get($targetUserId);
78
+        // Ensure that a profile config is created in the database
79
+        $this->profileManager->getProfileConfig($targetUser, $targetUser);
80
+        $config = $this->configMapper->get($targetUserId);
81 81
 
82
-		if (!in_array($paramId, array_keys($config->getVisibilityMap()), true)) {
83
-			throw new OCSBadRequestException('User does not have a profile parameter with ID: ' . $paramId);
84
-		}
82
+        if (!in_array($paramId, array_keys($config->getVisibilityMap()), true)) {
83
+            throw new OCSBadRequestException('User does not have a profile parameter with ID: ' . $paramId);
84
+        }
85 85
 
86
-		$config->setVisibility($paramId, $visibility);
87
-		$this->configMapper->update($config);
86
+        $config->setVisibility($paramId, $visibility);
87
+        $this->configMapper->update($config);
88 88
 
89
-		return new DataResponse();
90
-	}
89
+        return new DataResponse();
90
+    }
91 91
 }
Please login to merge, or discard this patch.