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