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