Completed
Push — master ( fde08a...208e38 )
by Blizzz
17:49
created
apps/provisioning_api/lib/Controller/UsersController.php 1 patch
Indentation   +826 added lines, -826 removed lines patch added patch discarded remove patch
@@ -53,830 +53,830 @@
 block discarded – undo
53 53
 
54 54
 class UsersController extends OCSController {
55 55
 
56
-	/** @var IUserManager */
57
-	private $userManager;
58
-	/** @var IConfig */
59
-	private $config;
60
-	/** @var IAppManager */
61
-	private $appManager;
62
-	/** @var IGroupManager|\OC\Group\Manager */ // FIXME Requires a method that is not on the interface
63
-	private $groupManager;
64
-	/** @var IUserSession */
65
-	private $userSession;
66
-	/** @var AccountManager */
67
-	private $accountManager;
68
-	/** @var ILogger */
69
-	private $logger;
70
-	/** @var IFactory */
71
-	private $l10nFactory;
72
-	/** @var NewUserMailHelper */
73
-	private $newUserMailHelper;
74
-	/** @var FederatedFileSharingFactory */
75
-	private $federatedFileSharingFactory;
76
-
77
-	/**
78
-	 * @param string $appName
79
-	 * @param IRequest $request
80
-	 * @param IUserManager $userManager
81
-	 * @param IConfig $config
82
-	 * @param IAppManager $appManager
83
-	 * @param IGroupManager $groupManager
84
-	 * @param IUserSession $userSession
85
-	 * @param AccountManager $accountManager
86
-	 * @param ILogger $logger
87
-	 * @param IFactory $l10nFactory
88
-	 * @param NewUserMailHelper $newUserMailHelper
89
-	 * @param FederatedFileSharingFactory $federatedFileSharingFactory
90
-	 */
91
-	public function __construct(string $appName,
92
-								IRequest $request,
93
-								IUserManager $userManager,
94
-								IConfig $config,
95
-								IAppManager $appManager,
96
-								IGroupManager $groupManager,
97
-								IUserSession $userSession,
98
-								AccountManager $accountManager,
99
-								ILogger $logger,
100
-								IFactory $l10nFactory,
101
-								NewUserMailHelper $newUserMailHelper,
102
-								FederatedFileSharingFactory $federatedFileSharingFactory) {
103
-		parent::__construct($appName, $request);
104
-
105
-		$this->userManager = $userManager;
106
-		$this->config = $config;
107
-		$this->appManager = $appManager;
108
-		$this->groupManager = $groupManager;
109
-		$this->userSession = $userSession;
110
-		$this->accountManager = $accountManager;
111
-		$this->logger = $logger;
112
-		$this->l10nFactory = $l10nFactory;
113
-		$this->newUserMailHelper = $newUserMailHelper;
114
-		$this->federatedFileSharingFactory = $federatedFileSharingFactory;
115
-	}
116
-
117
-	/**
118
-	 * @NoAdminRequired
119
-	 *
120
-	 * returns a list of users
121
-	 *
122
-	 * @param string $search
123
-	 * @param int $limit
124
-	 * @param int $offset
125
-	 * @return DataResponse
126
-	 */
127
-	public function getUsers(string $search = '', $limit = null, $offset = null): DataResponse {
128
-		$user = $this->userSession->getUser();
129
-		$users = [];
130
-
131
-		// Admin? Or SubAdmin?
132
-		$uid = $user->getUID();
133
-		$subAdminManager = $this->groupManager->getSubAdmin();
134
-		if($this->groupManager->isAdmin($uid)){
135
-			$users = $this->userManager->search($search, $limit, $offset);
136
-		} else if ($subAdminManager->isSubAdmin($user)) {
137
-			$subAdminOfGroups = $subAdminManager->getSubAdminsGroups($user);
138
-			foreach ($subAdminOfGroups as $key => $group) {
139
-				$subAdminOfGroups[$key] = $group->getGID();
140
-			}
141
-
142
-			if($offset === null) {
143
-				$offset = 0;
144
-			}
145
-
146
-			$users = [];
147
-			foreach ($subAdminOfGroups as $group) {
148
-				$users = array_merge($users, $this->groupManager->displayNamesInGroup($group, $search));
149
-			}
150
-
151
-			$users = array_slice($users, $offset, $limit);
152
-		}
153
-
154
-		$users = array_keys($users);
155
-
156
-		return new DataResponse([
157
-			'users' => $users
158
-		]);
159
-	}
160
-
161
-	/**
162
-	 * @PasswordConfirmationRequired
163
-	 * @NoAdminRequired
164
-	 *
165
-	 * @param string $userid
166
-	 * @param string $password
167
-	 * @param array $groups
168
-	 * @return DataResponse
169
-	 * @throws OCSException
170
-	 */
171
-	public function addUser(string $userid, string $password, array $groups = []): DataResponse {
172
-		$user = $this->userSession->getUser();
173
-		$isAdmin = $this->groupManager->isAdmin($user->getUID());
174
-		$subAdminManager = $this->groupManager->getSubAdmin();
175
-
176
-		if($this->userManager->userExists($userid)) {
177
-			$this->logger->error('Failed addUser attempt: User already exists.', ['app' => 'ocs_api']);
178
-			throw new OCSException('User already exists', 102);
179
-		}
180
-
181
-		if($groups !== []) {
182
-			foreach ($groups as $group) {
183
-				if(!$this->groupManager->groupExists($group)) {
184
-					throw new OCSException('group '.$group.' does not exist', 104);
185
-				}
186
-				if(!$isAdmin && !$subAdminManager->isSubAdminofGroup($user, $this->groupManager->get($group))) {
187
-					throw new OCSException('insufficient privileges for group '. $group, 105);
188
-				}
189
-			}
190
-		} else {
191
-			if(!$isAdmin) {
192
-				throw new OCSException('no group specified (required for subadmins)', 106);
193
-			}
194
-		}
195
-
196
-		try {
197
-			$newUser = $this->userManager->createUser($userid, $password);
198
-			$this->logger->info('Successful addUser call with userid: ' . $userid, ['app' => 'ocs_api']);
199
-
200
-			foreach ($groups as $group) {
201
-				$this->groupManager->get($group)->addUser($newUser);
202
-				$this->logger->info('Added userid ' . $userid . ' to group ' . $group, ['app' => 'ocs_api']);
203
-			}
204
-
205
-			return new DataResponse();
206
-		} catch (HintException $e ) {
207
-			$this->logger->logException($e, [
208
-				'message' => 'Failed addUser attempt with hint exception.',
209
-				'level' => \OCP\Util::WARN,
210
-				'app' => 'ocs_api',
211
-			]);
212
-			throw new OCSException($e->getHint(), 107);
213
-		} catch (\Exception $e) {
214
-			$this->logger->logException($e, [
215
-				'message' => 'Failed addUser attempt with exception.',
216
-				'level' => \OCP\Util::ERROR,
217
-				'app' => 'ocs_api',
218
-			]);
219
-			throw new OCSException('Bad request', 101);
220
-		}
221
-	}
222
-
223
-	/**
224
-	 * @NoAdminRequired
225
-	 * @NoSubAdminRequired
226
-	 *
227
-	 * gets user info
228
-	 *
229
-	 * @param string $userId
230
-	 * @return DataResponse
231
-	 * @throws OCSException
232
-	 */
233
-	public function getUser(string $userId): DataResponse {
234
-		$data = $this->getUserData($userId);
235
-		return new DataResponse($data);
236
-	}
237
-
238
-	/**
239
-	 * @NoAdminRequired
240
-	 * @NoSubAdminRequired
241
-	 *
242
-	 * gets user info from the currently logged in user
243
-	 *
244
-	 * @return DataResponse
245
-	 * @throws OCSException
246
-	 */
247
-	public function getCurrentUser(): DataResponse {
248
-		$user = $this->userSession->getUser();
249
-		if ($user) {
250
-			$data =  $this->getUserData($user->getUID());
251
-			// rename "displayname" to "display-name" only for this call to keep
252
-			// the API stable.
253
-			$data['display-name'] = $data['displayname'];
254
-			unset($data['displayname']);
255
-			return new DataResponse($data);
256
-
257
-		}
258
-
259
-		throw new OCSException('', \OCP\API::RESPOND_UNAUTHORISED);
260
-	}
261
-
262
-	/**
263
-	 * creates a array with all user data
264
-	 *
265
-	 * @param $userId
266
-	 * @return array
267
-	 * @throws OCSException
268
-	 */
269
-	protected function getUserData(string $userId): array {
270
-		$currentLoggedInUser = $this->userSession->getUser();
271
-
272
-		$data = [];
273
-
274
-		// Check if the target user exists
275
-		$targetUserObject = $this->userManager->get($userId);
276
-		if($targetUserObject === null) {
277
-			throw new OCSException('The requested user could not be found', \OCP\API::RESPOND_NOT_FOUND);
278
-		}
279
-
280
-		// Admin? Or SubAdmin?
281
-		if($this->groupManager->isAdmin($currentLoggedInUser->getUID())
282
-			|| $this->groupManager->getSubAdmin()->isUserAccessible($currentLoggedInUser, $targetUserObject)) {
283
-			$data['enabled'] = $this->config->getUserValue($targetUserObject->getUID(), 'core', 'enabled', 'true');
284
-		} else {
285
-			// Check they are looking up themselves
286
-			if($currentLoggedInUser->getUID() !== $targetUserObject->getUID()) {
287
-				throw new OCSException('', \OCP\API::RESPOND_UNAUTHORISED);
288
-			}
289
-		}
290
-
291
-		$userAccount = $this->accountManager->getUser($targetUserObject);
292
-		$groups = $this->groupManager->getUserGroups($targetUserObject);
293
-		$gids = [];
294
-		foreach ($groups as $group) {
295
-			$gids[] = $group->getDisplayName();
296
-		}
297
-
298
-		// Find the data
299
-		$data['id'] = $targetUserObject->getUID();
300
-		$data['quota'] = $this->fillStorageInfo($targetUserObject->getUID());
301
-		$data[AccountManager::PROPERTY_EMAIL] = $targetUserObject->getEMailAddress();
302
-		$data[AccountManager::PROPERTY_DISPLAYNAME] = $targetUserObject->getDisplayName();
303
-		$data[AccountManager::PROPERTY_PHONE] = $userAccount[AccountManager::PROPERTY_PHONE]['value'];
304
-		$data[AccountManager::PROPERTY_ADDRESS] = $userAccount[AccountManager::PROPERTY_ADDRESS]['value'];
305
-		$data[AccountManager::PROPERTY_WEBSITE] = $userAccount[AccountManager::PROPERTY_WEBSITE]['value'];
306
-		$data[AccountManager::PROPERTY_TWITTER] = $userAccount[AccountManager::PROPERTY_TWITTER]['value'];
307
-		$data['groups'] = $gids;
308
-		$data['language'] = $this->config->getUserValue($targetUserObject->getUID(), 'core', 'lang');
309
-
310
-		return $data;
311
-	}
312
-
313
-	/**
314
-	 * @NoAdminRequired
315
-	 * @NoSubAdminRequired
316
-	 */
317
-	public function getEditableFields(): DataResponse {
318
-		$permittedFields = [];
319
-
320
-		// Editing self (display, email)
321
-		if ($this->config->getSystemValue('allow_user_to_change_display_name', true) !== false) {
322
-			$permittedFields[] = AccountManager::PROPERTY_DISPLAYNAME;
323
-			$permittedFields[] = AccountManager::PROPERTY_EMAIL;
324
-		}
325
-
326
-		if ($this->appManager->isEnabledForUser('federatedfilesharing')) {
327
-			$federatedFileSharing = $this->federatedFileSharingFactory->get();
328
-			$shareProvider = $federatedFileSharing->getFederatedShareProvider();
329
-			if ($shareProvider->isLookupServerUploadEnabled()) {
330
-				$permittedFields[] = AccountManager::PROPERTY_PHONE;
331
-				$permittedFields[] = AccountManager::PROPERTY_ADDRESS;
332
-				$permittedFields[] = AccountManager::PROPERTY_WEBSITE;
333
-				$permittedFields[] = AccountManager::PROPERTY_TWITTER;
334
-			}
335
-		}
336
-
337
-		return new DataResponse($permittedFields);
338
-	}
339
-
340
-	/**
341
-	 * @NoAdminRequired
342
-	 * @NoSubAdminRequired
343
-	 * @PasswordConfirmationRequired
344
-	 *
345
-	 * edit users
346
-	 *
347
-	 * @param string $userId
348
-	 * @param string $key
349
-	 * @param string $value
350
-	 * @return DataResponse
351
-	 * @throws OCSException
352
-	 */
353
-	public function editUser(string $userId, string $key, string $value): DataResponse {
354
-		$currentLoggedInUser = $this->userSession->getUser();
355
-
356
-		$targetUser = $this->userManager->get($userId);
357
-		if($targetUser === null) {
358
-			throw new OCSException('', \OCP\API::RESPOND_UNAUTHORISED);
359
-		}
360
-
361
-		$permittedFields = [];
362
-		if($targetUser->getUID() === $currentLoggedInUser->getUID()) {
363
-			// Editing self (display, email)
364
-			if ($this->config->getSystemValue('allow_user_to_change_display_name', true) !== false) {
365
-				$permittedFields[] = 'display';
366
-				$permittedFields[] = AccountManager::PROPERTY_DISPLAYNAME;
367
-				$permittedFields[] = AccountManager::PROPERTY_EMAIL;
368
-			}
369
-
370
-			$permittedFields[] = 'password';
371
-			if ($this->config->getSystemValue('force_language', false) === false ||
372
-				$this->groupManager->isAdmin($currentLoggedInUser->getUID())) {
373
-				$permittedFields[] = 'language';
374
-			}
375
-
376
-			if ($this->appManager->isEnabledForUser('federatedfilesharing')) {
377
-				$federatedFileSharing = new \OCA\FederatedFileSharing\AppInfo\Application();
378
-				$shareProvider = $federatedFileSharing->getFederatedShareProvider();
379
-				if ($shareProvider->isLookupServerUploadEnabled()) {
380
-					$permittedFields[] = AccountManager::PROPERTY_PHONE;
381
-					$permittedFields[] = AccountManager::PROPERTY_ADDRESS;
382
-					$permittedFields[] = AccountManager::PROPERTY_WEBSITE;
383
-					$permittedFields[] = AccountManager::PROPERTY_TWITTER;
384
-				}
385
-			}
386
-
387
-			// If admin they can edit their own quota
388
-			if($this->groupManager->isAdmin($currentLoggedInUser->getUID())) {
389
-				$permittedFields[] = 'quota';
390
-			}
391
-		} else {
392
-			// Check if admin / subadmin
393
-			$subAdminManager = $this->groupManager->getSubAdmin();
394
-			if($subAdminManager->isUserAccessible($currentLoggedInUser, $targetUser)
395
-			|| $this->groupManager->isAdmin($currentLoggedInUser->getUID())) {
396
-				// They have permissions over the user
397
-				$permittedFields[] = 'display';
398
-				$permittedFields[] = AccountManager::PROPERTY_DISPLAYNAME;
399
-				$permittedFields[] = AccountManager::PROPERTY_EMAIL;
400
-				$permittedFields[] = 'password';
401
-				$permittedFields[] = 'language';
402
-				$permittedFields[] = AccountManager::PROPERTY_PHONE;
403
-				$permittedFields[] = AccountManager::PROPERTY_ADDRESS;
404
-				$permittedFields[] = AccountManager::PROPERTY_WEBSITE;
405
-				$permittedFields[] = AccountManager::PROPERTY_TWITTER;
406
-				$permittedFields[] = 'quota';
407
-			} else {
408
-				// No rights
409
-				throw new OCSException('', \OCP\API::RESPOND_UNAUTHORISED);
410
-			}
411
-		}
412
-		// Check if permitted to edit this field
413
-		if(!in_array($key, $permittedFields)) {
414
-			throw new OCSException('', \OCP\API::RESPOND_UNAUTHORISED);
415
-		}
416
-		// Process the edit
417
-		switch($key) {
418
-			case 'display':
419
-			case AccountManager::PROPERTY_DISPLAYNAME:
420
-				$targetUser->setDisplayName($value);
421
-				break;
422
-			case 'quota':
423
-				$quota = $value;
424
-				if($quota !== 'none' && $quota !== 'default') {
425
-					if (is_numeric($quota)) {
426
-						$quota = (float) $quota;
427
-					} else {
428
-						$quota = \OCP\Util::computerFileSize($quota);
429
-					}
430
-					if ($quota === false) {
431
-						throw new OCSException('Invalid quota value '.$value, 103);
432
-					}
433
-					if($quota === 0) {
434
-						$quota = 'default';
435
-					}else if($quota === -1) {
436
-						$quota = 'none';
437
-					} else {
438
-						$quota = \OCP\Util::humanFileSize($quota);
439
-					}
440
-				}
441
-				$targetUser->setQuota($quota);
442
-				break;
443
-			case 'password':
444
-				$targetUser->setPassword($value);
445
-				break;
446
-			case 'language':
447
-				$languagesCodes = $this->l10nFactory->findAvailableLanguages();
448
-				if (!in_array($value, $languagesCodes, true) && $value !== 'en') {
449
-					throw new OCSException('Invalid language', 102);
450
-				}
451
-				$this->config->setUserValue($targetUser->getUID(), 'core', 'lang', $value);
452
-				break;
453
-			case AccountManager::PROPERTY_EMAIL:
454
-				if(filter_var($value, FILTER_VALIDATE_EMAIL)) {
455
-					$targetUser->setEMailAddress($value);
456
-				} else {
457
-					throw new OCSException('', 102);
458
-				}
459
-				break;
460
-			case AccountManager::PROPERTY_PHONE:
461
-			case AccountManager::PROPERTY_ADDRESS:
462
-			case AccountManager::PROPERTY_WEBSITE:
463
-			case AccountManager::PROPERTY_TWITTER:
464
-				$userAccount = $this->accountManager->getUser($targetUser);
465
-				if ($userAccount[$key]['value'] !== $value) {
466
-					$userAccount[$key]['value'] = $value;
467
-					$this->accountManager->updateUser($targetUser, $userAccount);
468
-				}
469
-				break;
470
-			default:
471
-				throw new OCSException('', 103);
472
-		}
473
-		return new DataResponse();
474
-	}
475
-
476
-	/**
477
-	 * @PasswordConfirmationRequired
478
-	 * @NoAdminRequired
479
-	 *
480
-	 * @param string $userId
481
-	 * @return DataResponse
482
-	 * @throws OCSException
483
-	 */
484
-	public function deleteUser(string $userId): DataResponse {
485
-		$currentLoggedInUser = $this->userSession->getUser();
486
-
487
-		$targetUser = $this->userManager->get($userId);
488
-
489
-		if($targetUser === null || $targetUser->getUID() === $currentLoggedInUser->getUID()) {
490
-			throw new OCSException('', 101);
491
-		}
492
-
493
-		// If not permitted
494
-		$subAdminManager = $this->groupManager->getSubAdmin();
495
-		if(!$this->groupManager->isAdmin($currentLoggedInUser->getUID()) && !$subAdminManager->isUserAccessible($currentLoggedInUser, $targetUser)) {
496
-			throw new OCSException('', \OCP\API::RESPOND_UNAUTHORISED);
497
-		}
498
-
499
-		// Go ahead with the delete
500
-		if($targetUser->delete()) {
501
-			return new DataResponse();
502
-		} else {
503
-			throw new OCSException('', 101);
504
-		}
505
-	}
506
-
507
-	/**
508
-	 * @PasswordConfirmationRequired
509
-	 * @NoAdminRequired
510
-	 *
511
-	 * @param string $userId
512
-	 * @return DataResponse
513
-	 * @throws OCSException
514
-	 * @throws OCSForbiddenException
515
-	 */
516
-	public function disableUser(string $userId): DataResponse {
517
-		return $this->setEnabled($userId, false);
518
-	}
519
-
520
-	/**
521
-	 * @PasswordConfirmationRequired
522
-	 * @NoAdminRequired
523
-	 *
524
-	 * @param string $userId
525
-	 * @return DataResponse
526
-	 * @throws OCSException
527
-	 * @throws OCSForbiddenException
528
-	 */
529
-	public function enableUser(string $userId): DataResponse {
530
-		return $this->setEnabled($userId, true);
531
-	}
532
-
533
-	/**
534
-	 * @param string $userId
535
-	 * @param bool $value
536
-	 * @return DataResponse
537
-	 * @throws OCSException
538
-	 */
539
-	private function setEnabled(string $userId, bool $value): DataResponse {
540
-		$currentLoggedInUser = $this->userSession->getUser();
541
-
542
-		$targetUser = $this->userManager->get($userId);
543
-		if($targetUser === null || $targetUser->getUID() === $currentLoggedInUser->getUID()) {
544
-			throw new OCSException('', 101);
545
-		}
546
-
547
-		// If not permitted
548
-		$subAdminManager = $this->groupManager->getSubAdmin();
549
-		if(!$this->groupManager->isAdmin($currentLoggedInUser->getUID()) && !$subAdminManager->isUserAccessible($currentLoggedInUser, $targetUser)) {
550
-			throw new OCSException('', \OCP\API::RESPOND_UNAUTHORISED);
551
-		}
552
-
553
-		// enable/disable the user now
554
-		$targetUser->setEnabled($value);
555
-		return new DataResponse();
556
-	}
557
-
558
-	/**
559
-	 * @NoAdminRequired
560
-	 * @NoSubAdminRequired
561
-	 *
562
-	 * @param string $userId
563
-	 * @return DataResponse
564
-	 * @throws OCSException
565
-	 */
566
-	public function getUsersGroups(string $userId): DataResponse {
567
-		$loggedInUser = $this->userSession->getUser();
568
-
569
-		$targetUser = $this->userManager->get($userId);
570
-		if($targetUser === null) {
571
-			throw new OCSException('', \OCP\API::RESPOND_NOT_FOUND);
572
-		}
573
-
574
-		if($targetUser->getUID() === $loggedInUser->getUID() || $this->groupManager->isAdmin($loggedInUser->getUID())) {
575
-			// Self lookup or admin lookup
576
-			return new DataResponse([
577
-				'groups' => $this->groupManager->getUserGroupIds($targetUser)
578
-			]);
579
-		} else {
580
-			$subAdminManager = $this->groupManager->getSubAdmin();
581
-
582
-			// Looking up someone else
583
-			if($subAdminManager->isUserAccessible($loggedInUser, $targetUser)) {
584
-				// Return the group that the method caller is subadmin of for the user in question
585
-				/** @var IGroup[] $getSubAdminsGroups */
586
-				$getSubAdminsGroups = $subAdminManager->getSubAdminsGroups($loggedInUser);
587
-				foreach ($getSubAdminsGroups as $key => $group) {
588
-					$getSubAdminsGroups[$key] = $group->getGID();
589
-				}
590
-				$groups = array_intersect(
591
-					$getSubAdminsGroups,
592
-					$this->groupManager->getUserGroupIds($targetUser)
593
-				);
594
-				return new DataResponse(['groups' => $groups]);
595
-			} else {
596
-				// Not permitted
597
-				throw new OCSException('', \OCP\API::RESPOND_UNAUTHORISED);
598
-			}
599
-		}
600
-
601
-	}
602
-
603
-	/**
604
-	 * @PasswordConfirmationRequired
605
-	 * @NoAdminRequired
606
-	 *
607
-	 * @param string $userId
608
-	 * @param string $groupid
609
-	 * @return DataResponse
610
-	 * @throws OCSException
611
-	 */
612
-	public function addToGroup(string $userId, string $groupid = ''): DataResponse {
613
-		if($groupid === '') {
614
-			throw new OCSException('', 101);
615
-		}
616
-
617
-		$group = $this->groupManager->get($groupid);
618
-		$targetUser = $this->userManager->get($userId);
619
-		if($group === null) {
620
-			throw new OCSException('', 102);
621
-		}
622
-		if($targetUser === null) {
623
-			throw new OCSException('', 103);
624
-		}
625
-
626
-		// If they're not an admin, check they are a subadmin of the group in question
627
-		$loggedInUser = $this->userSession->getUser();
628
-		$subAdminManager = $this->groupManager->getSubAdmin();
629
-		if (!$this->groupManager->isAdmin($loggedInUser->getUID()) && !$subAdminManager->isSubAdminOfGroup($loggedInUser, $group)) {
630
-			throw new OCSException('', 104);
631
-		}
632
-
633
-		// Add user to group
634
-		$group->addUser($targetUser);
635
-		return new DataResponse();
636
-	}
637
-
638
-	/**
639
-	 * @PasswordConfirmationRequired
640
-	 * @NoAdminRequired
641
-	 *
642
-	 * @param string $userId
643
-	 * @param string $groupid
644
-	 * @return DataResponse
645
-	 * @throws OCSException
646
-	 */
647
-	public function removeFromGroup(string $userId, string $groupid): DataResponse {
648
-		$loggedInUser = $this->userSession->getUser();
649
-
650
-		if($groupid === null || trim($groupid) === '') {
651
-			throw new OCSException('', 101);
652
-		}
653
-
654
-		$group = $this->groupManager->get($groupid);
655
-		if($group === null) {
656
-			throw new OCSException('', 102);
657
-		}
658
-
659
-		$targetUser = $this->userManager->get($userId);
660
-		if($targetUser === null) {
661
-			throw new OCSException('', 103);
662
-		}
663
-
664
-		// If they're not an admin, check they are a subadmin of the group in question
665
-		$subAdminManager = $this->groupManager->getSubAdmin();
666
-		if (!$this->groupManager->isAdmin($loggedInUser->getUID()) && !$subAdminManager->isSubAdminOfGroup($loggedInUser, $group)) {
667
-			throw new OCSException('', 104);
668
-		}
669
-
670
-		// Check they aren't removing themselves from 'admin' or their 'subadmin; group
671
-		if ($targetUser->getUID() === $loggedInUser->getUID()) {
672
-			if ($this->groupManager->isAdmin($loggedInUser->getUID())) {
673
-				if ($group->getGID() === 'admin') {
674
-					throw new OCSException('Cannot remove yourself from the admin group', 105);
675
-				}
676
-			} else {
677
-				// Not an admin, so the user must be a subadmin of this group, but that is not allowed.
678
-				throw new OCSException('Cannot remove yourself from this group as you are a SubAdmin', 105);
679
-			}
680
-
681
-		} else if (!$this->groupManager->isAdmin($loggedInUser->getUID())) {
682
-			/** @var IGroup[] $subAdminGroups */
683
-			$subAdminGroups = $subAdminManager->getSubAdminsGroups($loggedInUser);
684
-			$subAdminGroups = array_map(function (IGroup $subAdminGroup) {
685
-				return $subAdminGroup->getGID();
686
-			}, $subAdminGroups);
687
-			$userGroups = $this->groupManager->getUserGroupIds($targetUser);
688
-			$userSubAdminGroups = array_intersect($subAdminGroups, $userGroups);
689
-
690
-			if (count($userSubAdminGroups) <= 1) {
691
-				// Subadmin must not be able to remove a user from all their subadmin groups.
692
-				throw new OCSException('Cannot remove user from this group as this is the only remaining group you are a SubAdmin of', 105);
693
-			}
694
-		}
695
-
696
-		// Remove user from group
697
-		$group->removeUser($targetUser);
698
-		return new DataResponse();
699
-	}
700
-
701
-	/**
702
-	 * Creates a subadmin
703
-	 *
704
-	 * @PasswordConfirmationRequired
705
-	 *
706
-	 * @param string $userId
707
-	 * @param string $groupid
708
-	 * @return DataResponse
709
-	 * @throws OCSException
710
-	 */
711
-	public function addSubAdmin(string $userId, string $groupid): DataResponse {
712
-		$group = $this->groupManager->get($groupid);
713
-		$user = $this->userManager->get($userId);
714
-
715
-		// Check if the user exists
716
-		if($user === null) {
717
-			throw new OCSException('User does not exist', 101);
718
-		}
719
-		// Check if group exists
720
-		if($group === null) {
721
-			throw new OCSException('Group does not exist',  102);
722
-		}
723
-		// Check if trying to make subadmin of admin group
724
-		if($group->getGID() === 'admin') {
725
-			throw new OCSException('Cannot create subadmins for admin group', 103);
726
-		}
727
-
728
-		$subAdminManager = $this->groupManager->getSubAdmin();
729
-
730
-		// We cannot be subadmin twice
731
-		if ($subAdminManager->isSubAdminofGroup($user, $group)) {
732
-			return new DataResponse();
733
-		}
734
-		// Go
735
-		if($subAdminManager->createSubAdmin($user, $group)) {
736
-			return new DataResponse();
737
-		} else {
738
-			throw new OCSException('Unknown error occurred', 103);
739
-		}
740
-	}
741
-
742
-	/**
743
-	 * Removes a subadmin from a group
744
-	 *
745
-	 * @PasswordConfirmationRequired
746
-	 *
747
-	 * @param string $userId
748
-	 * @param string $groupid
749
-	 * @return DataResponse
750
-	 * @throws OCSException
751
-	 */
752
-	public function removeSubAdmin(string $userId, string $groupid): DataResponse {
753
-		$group = $this->groupManager->get($groupid);
754
-		$user = $this->userManager->get($userId);
755
-		$subAdminManager = $this->groupManager->getSubAdmin();
756
-
757
-		// Check if the user exists
758
-		if($user === null) {
759
-			throw new OCSException('User does not exist', 101);
760
-		}
761
-		// Check if the group exists
762
-		if($group === null) {
763
-			throw new OCSException('Group does not exist', 101);
764
-		}
765
-		// Check if they are a subadmin of this said group
766
-		if(!$subAdminManager->isSubAdminOfGroup($user, $group)) {
767
-			throw new OCSException('User is not a subadmin of this group', 102);
768
-		}
769
-
770
-		// Go
771
-		if($subAdminManager->deleteSubAdmin($user, $group)) {
772
-			return new DataResponse();
773
-		} else {
774
-			throw new OCSException('Unknown error occurred', 103);
775
-		}
776
-	}
777
-
778
-	/**
779
-	 * Get the groups a user is a subadmin of
780
-	 *
781
-	 * @param string $userId
782
-	 * @return DataResponse
783
-	 * @throws OCSException
784
-	 */
785
-	public function getUserSubAdminGroups(string $userId): DataResponse {
786
-		$user = $this->userManager->get($userId);
787
-		// Check if the user exists
788
-		if($user === null) {
789
-			throw new OCSException('User does not exist', 101);
790
-		}
791
-
792
-		// Get the subadmin groups
793
-		$subAdminGroups = $this->groupManager->getSubAdmin()->getSubAdminsGroups($user);
794
-		$groups = [];
795
-		foreach ($subAdminGroups as $key => $group) {
796
-			$groups[] = $group->getGID();
797
-		}
798
-
799
-		if(!$groups) {
800
-			throw new OCSException('Unknown error occurred', 102);
801
-		} else {
802
-			return new DataResponse($groups);
803
-		}
804
-	}
805
-
806
-	/**
807
-	 * @param string $userId
808
-	 * @return array
809
-	 * @throws \OCP\Files\NotFoundException
810
-	 */
811
-	protected function fillStorageInfo(string $userId): array {
812
-		try {
813
-			\OC_Util::tearDownFS();
814
-			\OC_Util::setupFS($userId);
815
-			$storage = OC_Helper::getStorageInfo('/');
816
-			$data = [
817
-				'free' => $storage['free'],
818
-				'used' => $storage['used'],
819
-				'total' => $storage['total'],
820
-				'relative' => $storage['relative'],
821
-				'quota' => $storage['quota'],
822
-			];
823
-		} catch (NotFoundException $ex) {
824
-			$data = [];
825
-		}
826
-		return $data;
827
-	}
828
-
829
-	/**
830
-	 * @NoAdminRequired
831
-	 * @PasswordConfirmationRequired
832
-	 *
833
-	 * resend welcome message
834
-	 *
835
-	 * @param string $userId
836
-	 * @return DataResponse
837
-	 * @throws OCSException
838
-	 */
839
-	public function resendWelcomeMessage(string $userId): DataResponse {
840
-		$currentLoggedInUser = $this->userSession->getUser();
841
-
842
-		$targetUser = $this->userManager->get($userId);
843
-		if($targetUser === null) {
844
-			throw new OCSException('', \OCP\API::RESPOND_NOT_FOUND);
845
-		}
846
-
847
-		// Check if admin / subadmin
848
-		$subAdminManager = $this->groupManager->getSubAdmin();
849
-		if(!$subAdminManager->isUserAccessible($currentLoggedInUser, $targetUser)
850
-			&& !$this->groupManager->isAdmin($currentLoggedInUser->getUID())) {
851
-			// No rights
852
-			throw new OCSException('', \OCP\API::RESPOND_UNAUTHORISED);
853
-		}
854
-
855
-		$email = $targetUser->getEMailAddress();
856
-		if ($email === '' || $email === null) {
857
-			throw new OCSException('Email address not available', 101);
858
-		}
859
-		$username = $targetUser->getUID();
860
-		$lang = $this->config->getUserValue($username, 'core', 'lang', 'en');
861
-		if (!$this->l10nFactory->languageExists('settings', $lang)) {
862
-			$lang = 'en';
863
-		}
864
-
865
-		$l10n = $this->l10nFactory->get('settings', $lang);
866
-
867
-		try {
868
-			$this->newUserMailHelper->setL10N($l10n);
869
-			$emailTemplate = $this->newUserMailHelper->generateTemplate($targetUser, false);
870
-			$this->newUserMailHelper->sendMail($targetUser, $emailTemplate);
871
-		} catch(\Exception $e) {
872
-			$this->logger->logException($e, [
873
-				'message' => "Can't send new user mail to $email",
874
-				'level' => \OCP\Util::ERROR,
875
-				'app' => 'settings',
876
-			]);
877
-			throw new OCSException('Sending email failed', 102);
878
-		}
879
-
880
-		return new DataResponse();
881
-	}
56
+    /** @var IUserManager */
57
+    private $userManager;
58
+    /** @var IConfig */
59
+    private $config;
60
+    /** @var IAppManager */
61
+    private $appManager;
62
+    /** @var IGroupManager|\OC\Group\Manager */ // FIXME Requires a method that is not on the interface
63
+    private $groupManager;
64
+    /** @var IUserSession */
65
+    private $userSession;
66
+    /** @var AccountManager */
67
+    private $accountManager;
68
+    /** @var ILogger */
69
+    private $logger;
70
+    /** @var IFactory */
71
+    private $l10nFactory;
72
+    /** @var NewUserMailHelper */
73
+    private $newUserMailHelper;
74
+    /** @var FederatedFileSharingFactory */
75
+    private $federatedFileSharingFactory;
76
+
77
+    /**
78
+     * @param string $appName
79
+     * @param IRequest $request
80
+     * @param IUserManager $userManager
81
+     * @param IConfig $config
82
+     * @param IAppManager $appManager
83
+     * @param IGroupManager $groupManager
84
+     * @param IUserSession $userSession
85
+     * @param AccountManager $accountManager
86
+     * @param ILogger $logger
87
+     * @param IFactory $l10nFactory
88
+     * @param NewUserMailHelper $newUserMailHelper
89
+     * @param FederatedFileSharingFactory $federatedFileSharingFactory
90
+     */
91
+    public function __construct(string $appName,
92
+                                IRequest $request,
93
+                                IUserManager $userManager,
94
+                                IConfig $config,
95
+                                IAppManager $appManager,
96
+                                IGroupManager $groupManager,
97
+                                IUserSession $userSession,
98
+                                AccountManager $accountManager,
99
+                                ILogger $logger,
100
+                                IFactory $l10nFactory,
101
+                                NewUserMailHelper $newUserMailHelper,
102
+                                FederatedFileSharingFactory $federatedFileSharingFactory) {
103
+        parent::__construct($appName, $request);
104
+
105
+        $this->userManager = $userManager;
106
+        $this->config = $config;
107
+        $this->appManager = $appManager;
108
+        $this->groupManager = $groupManager;
109
+        $this->userSession = $userSession;
110
+        $this->accountManager = $accountManager;
111
+        $this->logger = $logger;
112
+        $this->l10nFactory = $l10nFactory;
113
+        $this->newUserMailHelper = $newUserMailHelper;
114
+        $this->federatedFileSharingFactory = $federatedFileSharingFactory;
115
+    }
116
+
117
+    /**
118
+     * @NoAdminRequired
119
+     *
120
+     * returns a list of users
121
+     *
122
+     * @param string $search
123
+     * @param int $limit
124
+     * @param int $offset
125
+     * @return DataResponse
126
+     */
127
+    public function getUsers(string $search = '', $limit = null, $offset = null): DataResponse {
128
+        $user = $this->userSession->getUser();
129
+        $users = [];
130
+
131
+        // Admin? Or SubAdmin?
132
+        $uid = $user->getUID();
133
+        $subAdminManager = $this->groupManager->getSubAdmin();
134
+        if($this->groupManager->isAdmin($uid)){
135
+            $users = $this->userManager->search($search, $limit, $offset);
136
+        } else if ($subAdminManager->isSubAdmin($user)) {
137
+            $subAdminOfGroups = $subAdminManager->getSubAdminsGroups($user);
138
+            foreach ($subAdminOfGroups as $key => $group) {
139
+                $subAdminOfGroups[$key] = $group->getGID();
140
+            }
141
+
142
+            if($offset === null) {
143
+                $offset = 0;
144
+            }
145
+
146
+            $users = [];
147
+            foreach ($subAdminOfGroups as $group) {
148
+                $users = array_merge($users, $this->groupManager->displayNamesInGroup($group, $search));
149
+            }
150
+
151
+            $users = array_slice($users, $offset, $limit);
152
+        }
153
+
154
+        $users = array_keys($users);
155
+
156
+        return new DataResponse([
157
+            'users' => $users
158
+        ]);
159
+    }
160
+
161
+    /**
162
+     * @PasswordConfirmationRequired
163
+     * @NoAdminRequired
164
+     *
165
+     * @param string $userid
166
+     * @param string $password
167
+     * @param array $groups
168
+     * @return DataResponse
169
+     * @throws OCSException
170
+     */
171
+    public function addUser(string $userid, string $password, array $groups = []): DataResponse {
172
+        $user = $this->userSession->getUser();
173
+        $isAdmin = $this->groupManager->isAdmin($user->getUID());
174
+        $subAdminManager = $this->groupManager->getSubAdmin();
175
+
176
+        if($this->userManager->userExists($userid)) {
177
+            $this->logger->error('Failed addUser attempt: User already exists.', ['app' => 'ocs_api']);
178
+            throw new OCSException('User already exists', 102);
179
+        }
180
+
181
+        if($groups !== []) {
182
+            foreach ($groups as $group) {
183
+                if(!$this->groupManager->groupExists($group)) {
184
+                    throw new OCSException('group '.$group.' does not exist', 104);
185
+                }
186
+                if(!$isAdmin && !$subAdminManager->isSubAdminofGroup($user, $this->groupManager->get($group))) {
187
+                    throw new OCSException('insufficient privileges for group '. $group, 105);
188
+                }
189
+            }
190
+        } else {
191
+            if(!$isAdmin) {
192
+                throw new OCSException('no group specified (required for subadmins)', 106);
193
+            }
194
+        }
195
+
196
+        try {
197
+            $newUser = $this->userManager->createUser($userid, $password);
198
+            $this->logger->info('Successful addUser call with userid: ' . $userid, ['app' => 'ocs_api']);
199
+
200
+            foreach ($groups as $group) {
201
+                $this->groupManager->get($group)->addUser($newUser);
202
+                $this->logger->info('Added userid ' . $userid . ' to group ' . $group, ['app' => 'ocs_api']);
203
+            }
204
+
205
+            return new DataResponse();
206
+        } catch (HintException $e ) {
207
+            $this->logger->logException($e, [
208
+                'message' => 'Failed addUser attempt with hint exception.',
209
+                'level' => \OCP\Util::WARN,
210
+                'app' => 'ocs_api',
211
+            ]);
212
+            throw new OCSException($e->getHint(), 107);
213
+        } catch (\Exception $e) {
214
+            $this->logger->logException($e, [
215
+                'message' => 'Failed addUser attempt with exception.',
216
+                'level' => \OCP\Util::ERROR,
217
+                'app' => 'ocs_api',
218
+            ]);
219
+            throw new OCSException('Bad request', 101);
220
+        }
221
+    }
222
+
223
+    /**
224
+     * @NoAdminRequired
225
+     * @NoSubAdminRequired
226
+     *
227
+     * gets user info
228
+     *
229
+     * @param string $userId
230
+     * @return DataResponse
231
+     * @throws OCSException
232
+     */
233
+    public function getUser(string $userId): DataResponse {
234
+        $data = $this->getUserData($userId);
235
+        return new DataResponse($data);
236
+    }
237
+
238
+    /**
239
+     * @NoAdminRequired
240
+     * @NoSubAdminRequired
241
+     *
242
+     * gets user info from the currently logged in user
243
+     *
244
+     * @return DataResponse
245
+     * @throws OCSException
246
+     */
247
+    public function getCurrentUser(): DataResponse {
248
+        $user = $this->userSession->getUser();
249
+        if ($user) {
250
+            $data =  $this->getUserData($user->getUID());
251
+            // rename "displayname" to "display-name" only for this call to keep
252
+            // the API stable.
253
+            $data['display-name'] = $data['displayname'];
254
+            unset($data['displayname']);
255
+            return new DataResponse($data);
256
+
257
+        }
258
+
259
+        throw new OCSException('', \OCP\API::RESPOND_UNAUTHORISED);
260
+    }
261
+
262
+    /**
263
+     * creates a array with all user data
264
+     *
265
+     * @param $userId
266
+     * @return array
267
+     * @throws OCSException
268
+     */
269
+    protected function getUserData(string $userId): array {
270
+        $currentLoggedInUser = $this->userSession->getUser();
271
+
272
+        $data = [];
273
+
274
+        // Check if the target user exists
275
+        $targetUserObject = $this->userManager->get($userId);
276
+        if($targetUserObject === null) {
277
+            throw new OCSException('The requested user could not be found', \OCP\API::RESPOND_NOT_FOUND);
278
+        }
279
+
280
+        // Admin? Or SubAdmin?
281
+        if($this->groupManager->isAdmin($currentLoggedInUser->getUID())
282
+            || $this->groupManager->getSubAdmin()->isUserAccessible($currentLoggedInUser, $targetUserObject)) {
283
+            $data['enabled'] = $this->config->getUserValue($targetUserObject->getUID(), 'core', 'enabled', 'true');
284
+        } else {
285
+            // Check they are looking up themselves
286
+            if($currentLoggedInUser->getUID() !== $targetUserObject->getUID()) {
287
+                throw new OCSException('', \OCP\API::RESPOND_UNAUTHORISED);
288
+            }
289
+        }
290
+
291
+        $userAccount = $this->accountManager->getUser($targetUserObject);
292
+        $groups = $this->groupManager->getUserGroups($targetUserObject);
293
+        $gids = [];
294
+        foreach ($groups as $group) {
295
+            $gids[] = $group->getDisplayName();
296
+        }
297
+
298
+        // Find the data
299
+        $data['id'] = $targetUserObject->getUID();
300
+        $data['quota'] = $this->fillStorageInfo($targetUserObject->getUID());
301
+        $data[AccountManager::PROPERTY_EMAIL] = $targetUserObject->getEMailAddress();
302
+        $data[AccountManager::PROPERTY_DISPLAYNAME] = $targetUserObject->getDisplayName();
303
+        $data[AccountManager::PROPERTY_PHONE] = $userAccount[AccountManager::PROPERTY_PHONE]['value'];
304
+        $data[AccountManager::PROPERTY_ADDRESS] = $userAccount[AccountManager::PROPERTY_ADDRESS]['value'];
305
+        $data[AccountManager::PROPERTY_WEBSITE] = $userAccount[AccountManager::PROPERTY_WEBSITE]['value'];
306
+        $data[AccountManager::PROPERTY_TWITTER] = $userAccount[AccountManager::PROPERTY_TWITTER]['value'];
307
+        $data['groups'] = $gids;
308
+        $data['language'] = $this->config->getUserValue($targetUserObject->getUID(), 'core', 'lang');
309
+
310
+        return $data;
311
+    }
312
+
313
+    /**
314
+     * @NoAdminRequired
315
+     * @NoSubAdminRequired
316
+     */
317
+    public function getEditableFields(): DataResponse {
318
+        $permittedFields = [];
319
+
320
+        // Editing self (display, email)
321
+        if ($this->config->getSystemValue('allow_user_to_change_display_name', true) !== false) {
322
+            $permittedFields[] = AccountManager::PROPERTY_DISPLAYNAME;
323
+            $permittedFields[] = AccountManager::PROPERTY_EMAIL;
324
+        }
325
+
326
+        if ($this->appManager->isEnabledForUser('federatedfilesharing')) {
327
+            $federatedFileSharing = $this->federatedFileSharingFactory->get();
328
+            $shareProvider = $federatedFileSharing->getFederatedShareProvider();
329
+            if ($shareProvider->isLookupServerUploadEnabled()) {
330
+                $permittedFields[] = AccountManager::PROPERTY_PHONE;
331
+                $permittedFields[] = AccountManager::PROPERTY_ADDRESS;
332
+                $permittedFields[] = AccountManager::PROPERTY_WEBSITE;
333
+                $permittedFields[] = AccountManager::PROPERTY_TWITTER;
334
+            }
335
+        }
336
+
337
+        return new DataResponse($permittedFields);
338
+    }
339
+
340
+    /**
341
+     * @NoAdminRequired
342
+     * @NoSubAdminRequired
343
+     * @PasswordConfirmationRequired
344
+     *
345
+     * edit users
346
+     *
347
+     * @param string $userId
348
+     * @param string $key
349
+     * @param string $value
350
+     * @return DataResponse
351
+     * @throws OCSException
352
+     */
353
+    public function editUser(string $userId, string $key, string $value): DataResponse {
354
+        $currentLoggedInUser = $this->userSession->getUser();
355
+
356
+        $targetUser = $this->userManager->get($userId);
357
+        if($targetUser === null) {
358
+            throw new OCSException('', \OCP\API::RESPOND_UNAUTHORISED);
359
+        }
360
+
361
+        $permittedFields = [];
362
+        if($targetUser->getUID() === $currentLoggedInUser->getUID()) {
363
+            // Editing self (display, email)
364
+            if ($this->config->getSystemValue('allow_user_to_change_display_name', true) !== false) {
365
+                $permittedFields[] = 'display';
366
+                $permittedFields[] = AccountManager::PROPERTY_DISPLAYNAME;
367
+                $permittedFields[] = AccountManager::PROPERTY_EMAIL;
368
+            }
369
+
370
+            $permittedFields[] = 'password';
371
+            if ($this->config->getSystemValue('force_language', false) === false ||
372
+                $this->groupManager->isAdmin($currentLoggedInUser->getUID())) {
373
+                $permittedFields[] = 'language';
374
+            }
375
+
376
+            if ($this->appManager->isEnabledForUser('federatedfilesharing')) {
377
+                $federatedFileSharing = new \OCA\FederatedFileSharing\AppInfo\Application();
378
+                $shareProvider = $federatedFileSharing->getFederatedShareProvider();
379
+                if ($shareProvider->isLookupServerUploadEnabled()) {
380
+                    $permittedFields[] = AccountManager::PROPERTY_PHONE;
381
+                    $permittedFields[] = AccountManager::PROPERTY_ADDRESS;
382
+                    $permittedFields[] = AccountManager::PROPERTY_WEBSITE;
383
+                    $permittedFields[] = AccountManager::PROPERTY_TWITTER;
384
+                }
385
+            }
386
+
387
+            // If admin they can edit their own quota
388
+            if($this->groupManager->isAdmin($currentLoggedInUser->getUID())) {
389
+                $permittedFields[] = 'quota';
390
+            }
391
+        } else {
392
+            // Check if admin / subadmin
393
+            $subAdminManager = $this->groupManager->getSubAdmin();
394
+            if($subAdminManager->isUserAccessible($currentLoggedInUser, $targetUser)
395
+            || $this->groupManager->isAdmin($currentLoggedInUser->getUID())) {
396
+                // They have permissions over the user
397
+                $permittedFields[] = 'display';
398
+                $permittedFields[] = AccountManager::PROPERTY_DISPLAYNAME;
399
+                $permittedFields[] = AccountManager::PROPERTY_EMAIL;
400
+                $permittedFields[] = 'password';
401
+                $permittedFields[] = 'language';
402
+                $permittedFields[] = AccountManager::PROPERTY_PHONE;
403
+                $permittedFields[] = AccountManager::PROPERTY_ADDRESS;
404
+                $permittedFields[] = AccountManager::PROPERTY_WEBSITE;
405
+                $permittedFields[] = AccountManager::PROPERTY_TWITTER;
406
+                $permittedFields[] = 'quota';
407
+            } else {
408
+                // No rights
409
+                throw new OCSException('', \OCP\API::RESPOND_UNAUTHORISED);
410
+            }
411
+        }
412
+        // Check if permitted to edit this field
413
+        if(!in_array($key, $permittedFields)) {
414
+            throw new OCSException('', \OCP\API::RESPOND_UNAUTHORISED);
415
+        }
416
+        // Process the edit
417
+        switch($key) {
418
+            case 'display':
419
+            case AccountManager::PROPERTY_DISPLAYNAME:
420
+                $targetUser->setDisplayName($value);
421
+                break;
422
+            case 'quota':
423
+                $quota = $value;
424
+                if($quota !== 'none' && $quota !== 'default') {
425
+                    if (is_numeric($quota)) {
426
+                        $quota = (float) $quota;
427
+                    } else {
428
+                        $quota = \OCP\Util::computerFileSize($quota);
429
+                    }
430
+                    if ($quota === false) {
431
+                        throw new OCSException('Invalid quota value '.$value, 103);
432
+                    }
433
+                    if($quota === 0) {
434
+                        $quota = 'default';
435
+                    }else if($quota === -1) {
436
+                        $quota = 'none';
437
+                    } else {
438
+                        $quota = \OCP\Util::humanFileSize($quota);
439
+                    }
440
+                }
441
+                $targetUser->setQuota($quota);
442
+                break;
443
+            case 'password':
444
+                $targetUser->setPassword($value);
445
+                break;
446
+            case 'language':
447
+                $languagesCodes = $this->l10nFactory->findAvailableLanguages();
448
+                if (!in_array($value, $languagesCodes, true) && $value !== 'en') {
449
+                    throw new OCSException('Invalid language', 102);
450
+                }
451
+                $this->config->setUserValue($targetUser->getUID(), 'core', 'lang', $value);
452
+                break;
453
+            case AccountManager::PROPERTY_EMAIL:
454
+                if(filter_var($value, FILTER_VALIDATE_EMAIL)) {
455
+                    $targetUser->setEMailAddress($value);
456
+                } else {
457
+                    throw new OCSException('', 102);
458
+                }
459
+                break;
460
+            case AccountManager::PROPERTY_PHONE:
461
+            case AccountManager::PROPERTY_ADDRESS:
462
+            case AccountManager::PROPERTY_WEBSITE:
463
+            case AccountManager::PROPERTY_TWITTER:
464
+                $userAccount = $this->accountManager->getUser($targetUser);
465
+                if ($userAccount[$key]['value'] !== $value) {
466
+                    $userAccount[$key]['value'] = $value;
467
+                    $this->accountManager->updateUser($targetUser, $userAccount);
468
+                }
469
+                break;
470
+            default:
471
+                throw new OCSException('', 103);
472
+        }
473
+        return new DataResponse();
474
+    }
475
+
476
+    /**
477
+     * @PasswordConfirmationRequired
478
+     * @NoAdminRequired
479
+     *
480
+     * @param string $userId
481
+     * @return DataResponse
482
+     * @throws OCSException
483
+     */
484
+    public function deleteUser(string $userId): DataResponse {
485
+        $currentLoggedInUser = $this->userSession->getUser();
486
+
487
+        $targetUser = $this->userManager->get($userId);
488
+
489
+        if($targetUser === null || $targetUser->getUID() === $currentLoggedInUser->getUID()) {
490
+            throw new OCSException('', 101);
491
+        }
492
+
493
+        // If not permitted
494
+        $subAdminManager = $this->groupManager->getSubAdmin();
495
+        if(!$this->groupManager->isAdmin($currentLoggedInUser->getUID()) && !$subAdminManager->isUserAccessible($currentLoggedInUser, $targetUser)) {
496
+            throw new OCSException('', \OCP\API::RESPOND_UNAUTHORISED);
497
+        }
498
+
499
+        // Go ahead with the delete
500
+        if($targetUser->delete()) {
501
+            return new DataResponse();
502
+        } else {
503
+            throw new OCSException('', 101);
504
+        }
505
+    }
506
+
507
+    /**
508
+     * @PasswordConfirmationRequired
509
+     * @NoAdminRequired
510
+     *
511
+     * @param string $userId
512
+     * @return DataResponse
513
+     * @throws OCSException
514
+     * @throws OCSForbiddenException
515
+     */
516
+    public function disableUser(string $userId): DataResponse {
517
+        return $this->setEnabled($userId, false);
518
+    }
519
+
520
+    /**
521
+     * @PasswordConfirmationRequired
522
+     * @NoAdminRequired
523
+     *
524
+     * @param string $userId
525
+     * @return DataResponse
526
+     * @throws OCSException
527
+     * @throws OCSForbiddenException
528
+     */
529
+    public function enableUser(string $userId): DataResponse {
530
+        return $this->setEnabled($userId, true);
531
+    }
532
+
533
+    /**
534
+     * @param string $userId
535
+     * @param bool $value
536
+     * @return DataResponse
537
+     * @throws OCSException
538
+     */
539
+    private function setEnabled(string $userId, bool $value): DataResponse {
540
+        $currentLoggedInUser = $this->userSession->getUser();
541
+
542
+        $targetUser = $this->userManager->get($userId);
543
+        if($targetUser === null || $targetUser->getUID() === $currentLoggedInUser->getUID()) {
544
+            throw new OCSException('', 101);
545
+        }
546
+
547
+        // If not permitted
548
+        $subAdminManager = $this->groupManager->getSubAdmin();
549
+        if(!$this->groupManager->isAdmin($currentLoggedInUser->getUID()) && !$subAdminManager->isUserAccessible($currentLoggedInUser, $targetUser)) {
550
+            throw new OCSException('', \OCP\API::RESPOND_UNAUTHORISED);
551
+        }
552
+
553
+        // enable/disable the user now
554
+        $targetUser->setEnabled($value);
555
+        return new DataResponse();
556
+    }
557
+
558
+    /**
559
+     * @NoAdminRequired
560
+     * @NoSubAdminRequired
561
+     *
562
+     * @param string $userId
563
+     * @return DataResponse
564
+     * @throws OCSException
565
+     */
566
+    public function getUsersGroups(string $userId): DataResponse {
567
+        $loggedInUser = $this->userSession->getUser();
568
+
569
+        $targetUser = $this->userManager->get($userId);
570
+        if($targetUser === null) {
571
+            throw new OCSException('', \OCP\API::RESPOND_NOT_FOUND);
572
+        }
573
+
574
+        if($targetUser->getUID() === $loggedInUser->getUID() || $this->groupManager->isAdmin($loggedInUser->getUID())) {
575
+            // Self lookup or admin lookup
576
+            return new DataResponse([
577
+                'groups' => $this->groupManager->getUserGroupIds($targetUser)
578
+            ]);
579
+        } else {
580
+            $subAdminManager = $this->groupManager->getSubAdmin();
581
+
582
+            // Looking up someone else
583
+            if($subAdminManager->isUserAccessible($loggedInUser, $targetUser)) {
584
+                // Return the group that the method caller is subadmin of for the user in question
585
+                /** @var IGroup[] $getSubAdminsGroups */
586
+                $getSubAdminsGroups = $subAdminManager->getSubAdminsGroups($loggedInUser);
587
+                foreach ($getSubAdminsGroups as $key => $group) {
588
+                    $getSubAdminsGroups[$key] = $group->getGID();
589
+                }
590
+                $groups = array_intersect(
591
+                    $getSubAdminsGroups,
592
+                    $this->groupManager->getUserGroupIds($targetUser)
593
+                );
594
+                return new DataResponse(['groups' => $groups]);
595
+            } else {
596
+                // Not permitted
597
+                throw new OCSException('', \OCP\API::RESPOND_UNAUTHORISED);
598
+            }
599
+        }
600
+
601
+    }
602
+
603
+    /**
604
+     * @PasswordConfirmationRequired
605
+     * @NoAdminRequired
606
+     *
607
+     * @param string $userId
608
+     * @param string $groupid
609
+     * @return DataResponse
610
+     * @throws OCSException
611
+     */
612
+    public function addToGroup(string $userId, string $groupid = ''): DataResponse {
613
+        if($groupid === '') {
614
+            throw new OCSException('', 101);
615
+        }
616
+
617
+        $group = $this->groupManager->get($groupid);
618
+        $targetUser = $this->userManager->get($userId);
619
+        if($group === null) {
620
+            throw new OCSException('', 102);
621
+        }
622
+        if($targetUser === null) {
623
+            throw new OCSException('', 103);
624
+        }
625
+
626
+        // If they're not an admin, check they are a subadmin of the group in question
627
+        $loggedInUser = $this->userSession->getUser();
628
+        $subAdminManager = $this->groupManager->getSubAdmin();
629
+        if (!$this->groupManager->isAdmin($loggedInUser->getUID()) && !$subAdminManager->isSubAdminOfGroup($loggedInUser, $group)) {
630
+            throw new OCSException('', 104);
631
+        }
632
+
633
+        // Add user to group
634
+        $group->addUser($targetUser);
635
+        return new DataResponse();
636
+    }
637
+
638
+    /**
639
+     * @PasswordConfirmationRequired
640
+     * @NoAdminRequired
641
+     *
642
+     * @param string $userId
643
+     * @param string $groupid
644
+     * @return DataResponse
645
+     * @throws OCSException
646
+     */
647
+    public function removeFromGroup(string $userId, string $groupid): DataResponse {
648
+        $loggedInUser = $this->userSession->getUser();
649
+
650
+        if($groupid === null || trim($groupid) === '') {
651
+            throw new OCSException('', 101);
652
+        }
653
+
654
+        $group = $this->groupManager->get($groupid);
655
+        if($group === null) {
656
+            throw new OCSException('', 102);
657
+        }
658
+
659
+        $targetUser = $this->userManager->get($userId);
660
+        if($targetUser === null) {
661
+            throw new OCSException('', 103);
662
+        }
663
+
664
+        // If they're not an admin, check they are a subadmin of the group in question
665
+        $subAdminManager = $this->groupManager->getSubAdmin();
666
+        if (!$this->groupManager->isAdmin($loggedInUser->getUID()) && !$subAdminManager->isSubAdminOfGroup($loggedInUser, $group)) {
667
+            throw new OCSException('', 104);
668
+        }
669
+
670
+        // Check they aren't removing themselves from 'admin' or their 'subadmin; group
671
+        if ($targetUser->getUID() === $loggedInUser->getUID()) {
672
+            if ($this->groupManager->isAdmin($loggedInUser->getUID())) {
673
+                if ($group->getGID() === 'admin') {
674
+                    throw new OCSException('Cannot remove yourself from the admin group', 105);
675
+                }
676
+            } else {
677
+                // Not an admin, so the user must be a subadmin of this group, but that is not allowed.
678
+                throw new OCSException('Cannot remove yourself from this group as you are a SubAdmin', 105);
679
+            }
680
+
681
+        } else if (!$this->groupManager->isAdmin($loggedInUser->getUID())) {
682
+            /** @var IGroup[] $subAdminGroups */
683
+            $subAdminGroups = $subAdminManager->getSubAdminsGroups($loggedInUser);
684
+            $subAdminGroups = array_map(function (IGroup $subAdminGroup) {
685
+                return $subAdminGroup->getGID();
686
+            }, $subAdminGroups);
687
+            $userGroups = $this->groupManager->getUserGroupIds($targetUser);
688
+            $userSubAdminGroups = array_intersect($subAdminGroups, $userGroups);
689
+
690
+            if (count($userSubAdminGroups) <= 1) {
691
+                // Subadmin must not be able to remove a user from all their subadmin groups.
692
+                throw new OCSException('Cannot remove user from this group as this is the only remaining group you are a SubAdmin of', 105);
693
+            }
694
+        }
695
+
696
+        // Remove user from group
697
+        $group->removeUser($targetUser);
698
+        return new DataResponse();
699
+    }
700
+
701
+    /**
702
+     * Creates a subadmin
703
+     *
704
+     * @PasswordConfirmationRequired
705
+     *
706
+     * @param string $userId
707
+     * @param string $groupid
708
+     * @return DataResponse
709
+     * @throws OCSException
710
+     */
711
+    public function addSubAdmin(string $userId, string $groupid): DataResponse {
712
+        $group = $this->groupManager->get($groupid);
713
+        $user = $this->userManager->get($userId);
714
+
715
+        // Check if the user exists
716
+        if($user === null) {
717
+            throw new OCSException('User does not exist', 101);
718
+        }
719
+        // Check if group exists
720
+        if($group === null) {
721
+            throw new OCSException('Group does not exist',  102);
722
+        }
723
+        // Check if trying to make subadmin of admin group
724
+        if($group->getGID() === 'admin') {
725
+            throw new OCSException('Cannot create subadmins for admin group', 103);
726
+        }
727
+
728
+        $subAdminManager = $this->groupManager->getSubAdmin();
729
+
730
+        // We cannot be subadmin twice
731
+        if ($subAdminManager->isSubAdminofGroup($user, $group)) {
732
+            return new DataResponse();
733
+        }
734
+        // Go
735
+        if($subAdminManager->createSubAdmin($user, $group)) {
736
+            return new DataResponse();
737
+        } else {
738
+            throw new OCSException('Unknown error occurred', 103);
739
+        }
740
+    }
741
+
742
+    /**
743
+     * Removes a subadmin from a group
744
+     *
745
+     * @PasswordConfirmationRequired
746
+     *
747
+     * @param string $userId
748
+     * @param string $groupid
749
+     * @return DataResponse
750
+     * @throws OCSException
751
+     */
752
+    public function removeSubAdmin(string $userId, string $groupid): DataResponse {
753
+        $group = $this->groupManager->get($groupid);
754
+        $user = $this->userManager->get($userId);
755
+        $subAdminManager = $this->groupManager->getSubAdmin();
756
+
757
+        // Check if the user exists
758
+        if($user === null) {
759
+            throw new OCSException('User does not exist', 101);
760
+        }
761
+        // Check if the group exists
762
+        if($group === null) {
763
+            throw new OCSException('Group does not exist', 101);
764
+        }
765
+        // Check if they are a subadmin of this said group
766
+        if(!$subAdminManager->isSubAdminOfGroup($user, $group)) {
767
+            throw new OCSException('User is not a subadmin of this group', 102);
768
+        }
769
+
770
+        // Go
771
+        if($subAdminManager->deleteSubAdmin($user, $group)) {
772
+            return new DataResponse();
773
+        } else {
774
+            throw new OCSException('Unknown error occurred', 103);
775
+        }
776
+    }
777
+
778
+    /**
779
+     * Get the groups a user is a subadmin of
780
+     *
781
+     * @param string $userId
782
+     * @return DataResponse
783
+     * @throws OCSException
784
+     */
785
+    public function getUserSubAdminGroups(string $userId): DataResponse {
786
+        $user = $this->userManager->get($userId);
787
+        // Check if the user exists
788
+        if($user === null) {
789
+            throw new OCSException('User does not exist', 101);
790
+        }
791
+
792
+        // Get the subadmin groups
793
+        $subAdminGroups = $this->groupManager->getSubAdmin()->getSubAdminsGroups($user);
794
+        $groups = [];
795
+        foreach ($subAdminGroups as $key => $group) {
796
+            $groups[] = $group->getGID();
797
+        }
798
+
799
+        if(!$groups) {
800
+            throw new OCSException('Unknown error occurred', 102);
801
+        } else {
802
+            return new DataResponse($groups);
803
+        }
804
+    }
805
+
806
+    /**
807
+     * @param string $userId
808
+     * @return array
809
+     * @throws \OCP\Files\NotFoundException
810
+     */
811
+    protected function fillStorageInfo(string $userId): array {
812
+        try {
813
+            \OC_Util::tearDownFS();
814
+            \OC_Util::setupFS($userId);
815
+            $storage = OC_Helper::getStorageInfo('/');
816
+            $data = [
817
+                'free' => $storage['free'],
818
+                'used' => $storage['used'],
819
+                'total' => $storage['total'],
820
+                'relative' => $storage['relative'],
821
+                'quota' => $storage['quota'],
822
+            ];
823
+        } catch (NotFoundException $ex) {
824
+            $data = [];
825
+        }
826
+        return $data;
827
+    }
828
+
829
+    /**
830
+     * @NoAdminRequired
831
+     * @PasswordConfirmationRequired
832
+     *
833
+     * resend welcome message
834
+     *
835
+     * @param string $userId
836
+     * @return DataResponse
837
+     * @throws OCSException
838
+     */
839
+    public function resendWelcomeMessage(string $userId): DataResponse {
840
+        $currentLoggedInUser = $this->userSession->getUser();
841
+
842
+        $targetUser = $this->userManager->get($userId);
843
+        if($targetUser === null) {
844
+            throw new OCSException('', \OCP\API::RESPOND_NOT_FOUND);
845
+        }
846
+
847
+        // Check if admin / subadmin
848
+        $subAdminManager = $this->groupManager->getSubAdmin();
849
+        if(!$subAdminManager->isUserAccessible($currentLoggedInUser, $targetUser)
850
+            && !$this->groupManager->isAdmin($currentLoggedInUser->getUID())) {
851
+            // No rights
852
+            throw new OCSException('', \OCP\API::RESPOND_UNAUTHORISED);
853
+        }
854
+
855
+        $email = $targetUser->getEMailAddress();
856
+        if ($email === '' || $email === null) {
857
+            throw new OCSException('Email address not available', 101);
858
+        }
859
+        $username = $targetUser->getUID();
860
+        $lang = $this->config->getUserValue($username, 'core', 'lang', 'en');
861
+        if (!$this->l10nFactory->languageExists('settings', $lang)) {
862
+            $lang = 'en';
863
+        }
864
+
865
+        $l10n = $this->l10nFactory->get('settings', $lang);
866
+
867
+        try {
868
+            $this->newUserMailHelper->setL10N($l10n);
869
+            $emailTemplate = $this->newUserMailHelper->generateTemplate($targetUser, false);
870
+            $this->newUserMailHelper->sendMail($targetUser, $emailTemplate);
871
+        } catch(\Exception $e) {
872
+            $this->logger->logException($e, [
873
+                'message' => "Can't send new user mail to $email",
874
+                'level' => \OCP\Util::ERROR,
875
+                'app' => 'settings',
876
+            ]);
877
+            throw new OCSException('Sending email failed', 102);
878
+        }
879
+
880
+        return new DataResponse();
881
+    }
882 882
 }
Please login to merge, or discard this patch.
settings/Controller/UsersController.php 1 patch
Indentation   +943 added lines, -943 removed lines patch added patch discarded remove patch
@@ -71,948 +71,948 @@
 block discarded – undo
71 71
  * @package OC\Settings\Controller
72 72
  */
73 73
 class UsersController extends Controller {
74
-	/** @var IL10N */
75
-	private $l10n;
76
-	/** @var IUserSession */
77
-	private $userSession;
78
-	/** @var bool */
79
-	private $isAdmin;
80
-	/** @var IUserManager */
81
-	private $userManager;
82
-	/** @var IGroupManager */
83
-	private $groupManager;
84
-	/** @var IConfig */
85
-	private $config;
86
-	/** @var ILogger */
87
-	private $log;
88
-	/** @var IMailer */
89
-	private $mailer;
90
-	/** @var bool contains the state of the encryption app */
91
-	private $isEncryptionAppEnabled;
92
-	/** @var bool contains the state of the admin recovery setting */
93
-	private $isRestoreEnabled = false;
94
-	/** @var IAppManager */
95
-	private $appManager;
96
-	/** @var IAvatarManager */
97
-	private $avatarManager;
98
-	/** @var AccountManager */
99
-	private $accountManager;
100
-	/** @var ISecureRandom */
101
-	private $secureRandom;
102
-	/** @var NewUserMailHelper */
103
-	private $newUserMailHelper;
104
-	/** @var Manager */
105
-	private $keyManager;
106
-	/** @var IJobList */
107
-	private $jobList;
108
-
109
-	/** @var IUserMountCache */
110
-	private $userMountCache;
111
-
112
-	/** @var IManager */
113
-	private $encryptionManager;
114
-
115
-	public function __construct(string $appName,
116
-								IRequest $request,
117
-								IUserManager $userManager,
118
-								IGroupManager $groupManager,
119
-								IUserSession $userSession,
120
-								IConfig $config,
121
-								bool $isAdmin,
122
-								IL10N $l10n,
123
-								ILogger $log,
124
-								IMailer $mailer,
125
-								IURLGenerator $urlGenerator,
126
-								IAppManager $appManager,
127
-								IAvatarManager $avatarManager,
128
-								AccountManager $accountManager,
129
-								ISecureRandom $secureRandom,
130
-								NewUserMailHelper $newUserMailHelper,
131
-								Manager $keyManager,
132
-								IJobList $jobList,
133
-								IUserMountCache $userMountCache,
134
-								IManager $encryptionManager) {
135
-		parent::__construct($appName, $request);
136
-		$this->userManager = $userManager;
137
-		$this->groupManager = $groupManager;
138
-		$this->userSession = $userSession;
139
-		$this->config = $config;
140
-		$this->isAdmin = $isAdmin;
141
-		$this->l10n = $l10n;
142
-		$this->log = $log;
143
-		$this->mailer = $mailer;
144
-		$this->appManager = $appManager;
145
-		$this->avatarManager = $avatarManager;
146
-		$this->accountManager = $accountManager;
147
-		$this->secureRandom = $secureRandom;
148
-		$this->newUserMailHelper = $newUserMailHelper;
149
-		$this->keyManager = $keyManager;
150
-		$this->jobList = $jobList;
151
-		$this->userMountCache = $userMountCache;
152
-		$this->encryptionManager = $encryptionManager;
153
-
154
-		// check for encryption state - TODO see formatUserForIndex
155
-		$this->isEncryptionAppEnabled = $appManager->isEnabledForUser('encryption');
156
-		if ($this->isEncryptionAppEnabled) {
157
-			// putting this directly in empty is possible in PHP 5.5+
158
-			$result = $config->getAppValue('encryption', 'recoveryAdminEnabled', '0');
159
-			$this->isRestoreEnabled = !empty($result);
160
-		}
161
-	}
162
-
163
-	/**
164
-	 * @param IUser $user
165
-	 * @param array|null $userGroups
166
-	 * @return array
167
-	 */
168
-	private function formatUserForIndex(IUser $user, array $userGroups = null): array {
169
-
170
-		// TODO: eliminate this encryption specific code below and somehow
171
-		// hook in additional user info from other apps
172
-
173
-		// recovery isn't possible if admin or user has it disabled and encryption
174
-		// is enabled - so we eliminate the else paths in the conditional tree
175
-		// below
176
-		$restorePossible = false;
177
-
178
-		if ($this->isEncryptionAppEnabled) {
179
-			if ($this->isRestoreEnabled) {
180
-				// check for the users recovery setting
181
-				$recoveryMode = $this->config->getUserValue($user->getUID(), 'encryption', 'recoveryEnabled', '0');
182
-				// method call inside empty is possible with PHP 5.5+
183
-				$recoveryModeEnabled = !empty($recoveryMode);
184
-				if ($recoveryModeEnabled) {
185
-					// user also has recovery mode enabled
186
-					$restorePossible = true;
187
-				}
188
-			} else {
189
-				$modules = $this->encryptionManager->getEncryptionModules();
190
-				$restorePossible = true;
191
-				foreach ($modules as $id => $module) {
192
-					/* @var IEncryptionModule $instance */
193
-					$instance = call_user_func($module['callback']);
194
-					if ($instance->needDetailedAccessList()) {
195
-						$restorePossible = false;
196
-						break;
197
-					}
198
-				}
199
-			}
200
-		} else {
201
-			// recovery is possible if encryption is disabled (plain files are
202
-			// available)
203
-			$restorePossible = true;
204
-		}
205
-
206
-		$subAdminGroups = $this->groupManager->getSubAdmin()->getSubAdminsGroupsName($user);
207
-
208
-		$displayName = $user->getEMailAddress();
209
-		if (is_null($displayName)) {
210
-			$displayName = '';
211
-		}
212
-
213
-		$avatarAvailable = false;
214
-		try {
215
-			$avatarAvailable = $this->avatarManager->getAvatar($user->getUID())->exists();
216
-		} catch (\Exception $e) {
217
-			//No avatar yet
218
-		}
219
-
220
-		return [
221
-			'name' => $user->getUID(),
222
-			'displayname' => $user->getDisplayName(),
223
-			'groups' => empty($userGroups) ? $this->groupManager->getUserGroupNames($user) : $userGroups,
224
-			'subadmin' => $subAdminGroups,
225
-			'quota' => $user->getQuota(),
226
-			'quota_bytes' => Util::computerFileSize($user->getQuota()),
227
-			'storageLocation' => $user->getHome(),
228
-			'lastLogin' => $user->getLastLogin() * 1000,
229
-			'backend' => $user->getBackendClassName(),
230
-			'email' => $displayName,
231
-			'isRestoreDisabled' => !$restorePossible,
232
-			'isAvatarAvailable' => $avatarAvailable,
233
-			'isEnabled' => $user->isEnabled(),
234
-		];
235
-	}
236
-
237
-	/**
238
-	 * @param array $userIDs Array with schema [$uid => $displayName]
239
-	 * @return IUser[]
240
-	 */
241
-	private function getUsersForUID(array $userIDs): array {
242
-		$users = [];
243
-		foreach ($userIDs as $uid => $displayName) {
244
-			$users[$uid] = $this->userManager->get($uid);
245
-		}
246
-		return $users;
247
-	}
248
-
249
-	/**
250
-	 * @NoAdminRequired
251
-	 *
252
-	 * @param int $offset
253
-	 * @param int $limit
254
-	 * @param string $gid GID to filter for
255
-	 * @param string $pattern Pattern to search for in the username
256
-	 * @param string $backend Backend to filter for (class-name)
257
-	 * @return DataResponse
258
-	 *
259
-	 * TODO: Tidy up and write unit tests - code is mainly static method calls
260
-	 */
261
-	public function index(int $offset = 0, int $limit = 10, string $gid = '', string $pattern = '', string $backend = ''): DataResponse {
262
-		// Remove backends
263
-		if (!empty($backend)) {
264
-			$activeBackends = $this->userManager->getBackends();
265
-			$this->userManager->clearBackends();
266
-			foreach ($activeBackends as $singleActiveBackend) {
267
-				if ($backend === get_class($singleActiveBackend)) {
268
-					$this->userManager->registerBackend($singleActiveBackend);
269
-					break;
270
-				}
271
-			}
272
-		}
273
-
274
-		$userObjects = [];
275
-		$users = [];
276
-		if ($this->isAdmin) {
277
-			if ($gid !== '' && $gid !== '_disabledUsers' && $gid !== '_everyone') {
278
-				$batch = $this->getUsersForUID($this->groupManager->displayNamesInGroup($gid, $pattern, $limit, $offset));
279
-			} else {
280
-				$batch = $this->userManager->search($pattern, $limit, $offset);
281
-			}
282
-
283
-			foreach ($batch as $user) {
284
-				if (($gid !== '_disabledUsers' && $user->isEnabled()) ||
285
-					($gid === '_disabledUsers' && !$user->isEnabled())
286
-				) {
287
-					$userObjects[] = $user;
288
-					$users[] = $this->formatUserForIndex($user);
289
-				}
290
-			}
291
-
292
-		} else {
293
-			$subAdminOfGroups = $this->groupManager->getSubAdmin()->getSubAdminsGroups($this->userSession->getUser());
294
-			// New class returns IGroup[] so convert back
295
-			$gids = [];
296
-			foreach ($subAdminOfGroups as $group) {
297
-				$gids[] = $group->getGID();
298
-			}
299
-			$subAdminOfGroups = $gids;
300
-
301
-			// Set the $gid parameter to an empty value if the subadmin has no rights to access a specific group
302
-			if ($gid !== '' && $gid !== '_disabledUsers' && !in_array($gid, $subAdminOfGroups)) {
303
-				$gid = '';
304
-			}
305
-
306
-			// Batch all groups the user is subadmin of when a group is specified
307
-			$batch = [];
308
-			if ($gid !== '' && $gid !== '_disabledUsers' && $gid !== '_everyone') {
309
-				$batch = $this->groupManager->displayNamesInGroup($gid, $pattern, $limit, $offset);
310
-			} else {
311
-				foreach ($subAdminOfGroups as $group) {
312
-					$groupUsers = $this->groupManager->displayNamesInGroup($group, $pattern, $limit, $offset);
313
-
314
-					foreach ($groupUsers as $uid => $displayName) {
315
-						$batch[$uid] = $displayName;
316
-					}
317
-				}
318
-			}
319
-			$batch = $this->getUsersForUID($batch);
320
-
321
-			foreach ($batch as $user) {
322
-				// Only add the groups, this user is a subadmin of
323
-				$userGroups = array_values(array_intersect(
324
-					$this->groupManager->getUserGroupIds($user),
325
-					$subAdminOfGroups
326
-				));
327
-				if (($gid !== '_disabledUsers' && $user->isEnabled()) ||
328
-					($gid === '_disabledUsers' && !$user->isEnabled())
329
-				) {
330
-					$userObjects[] = $user;
331
-					$users[] = $this->formatUserForIndex($user, $userGroups);
332
-				}
333
-			}
334
-		}
335
-
336
-		$usedSpace = $this->userMountCache->getUsedSpaceForUsers($userObjects);
337
-
338
-		foreach ($users as &$userData) {
339
-			$userData['size'] = isset($usedSpace[$userData['name']]) ? $usedSpace[$userData['name']] : 0;
340
-		}
341
-
342
-		return new DataResponse($users);
343
-	}
344
-
345
-	/**
346
-	 * @NoAdminRequired
347
-	 * @PasswordConfirmationRequired
348
-	 *
349
-	 * @param string $username
350
-	 * @param string $password
351
-	 * @param array $groups
352
-	 * @param string $email
353
-	 * @return DataResponse
354
-	 */
355
-	public function create(string $username, string $password, array $groups = [], $email = ''): DataResponse {
356
-		if ($email !== '' && !$this->mailer->validateMailAddress($email)) {
357
-			return new DataResponse(
358
-				[
359
-					'message' => $this->l10n->t('Invalid mail address')
360
-				],
361
-				Http::STATUS_UNPROCESSABLE_ENTITY
362
-			);
363
-		}
364
-
365
-		$currentUser = $this->userSession->getUser();
366
-
367
-		if (!$this->isAdmin) {
368
-			if (!empty($groups)) {
369
-				foreach ($groups as $key => $group) {
370
-					$groupObject = $this->groupManager->get($group);
371
-					if ($groupObject === null) {
372
-						unset($groups[$key]);
373
-						continue;
374
-					}
375
-
376
-					if (!$this->groupManager->getSubAdmin()->isSubAdminOfGroup($currentUser, $groupObject)) {
377
-						unset($groups[$key]);
378
-					}
379
-				}
380
-			}
381
-
382
-			if (empty($groups)) {
383
-				return new DataResponse(
384
-					[
385
-						'message' => $this->l10n->t('No valid group selected'),
386
-					],
387
-					Http::STATUS_FORBIDDEN
388
-				);
389
-			}
390
-		}
391
-
392
-		if ($this->userManager->userExists($username)) {
393
-			return new DataResponse(
394
-				[
395
-					'message' => $this->l10n->t('A user with that name already exists.')
396
-				],
397
-				Http::STATUS_CONFLICT
398
-			);
399
-		}
400
-
401
-		$generatePasswordResetToken = false;
402
-		if ($password === '') {
403
-			if ($email === '') {
404
-				return new DataResponse(
405
-					[
406
-						'message' => $this->l10n->t('To send a password link to the user an email address is required.')
407
-					],
408
-					Http::STATUS_UNPROCESSABLE_ENTITY
409
-				);
410
-			}
411
-
412
-			$password = $this->secureRandom->generate(30);
413
-			// Make sure we pass the password_policy
414
-			$password .= $this->secureRandom->generate(2, '$!.,;:-~+*[]{}()');
415
-			$generatePasswordResetToken = true;
416
-		}
417
-
418
-		try {
419
-			$user = $this->userManager->createUser($username, $password);
420
-		} catch (\Exception $exception) {
421
-			$message = $exception->getMessage();
422
-			if ($exception instanceof HintException && $exception->getHint()) {
423
-				$message = $exception->getHint();
424
-			}
425
-			if (!$message) {
426
-				$message = $this->l10n->t('Unable to create user.');
427
-			}
428
-			return new DataResponse(
429
-				[
430
-					'message' => (string)$message,
431
-				],
432
-				Http::STATUS_FORBIDDEN
433
-			);
434
-		}
435
-
436
-		if ($user instanceof IUser) {
437
-			if ($groups !== null) {
438
-				foreach ($groups as $groupName) {
439
-					$group = $this->groupManager->get($groupName);
440
-
441
-					if (empty($group)) {
442
-						$group = $this->groupManager->createGroup($groupName);
443
-					}
444
-					$group->addUser($user);
445
-				}
446
-			}
447
-			/**
448
-			 * Send new user mail only if a mail is set
449
-			 */
450
-			if ($email !== '') {
451
-				$user->setEMailAddress($email);
452
-				try {
453
-					$emailTemplate = $this->newUserMailHelper->generateTemplate($user, $generatePasswordResetToken);
454
-					$this->newUserMailHelper->sendMail($user, $emailTemplate);
455
-				} catch (\Exception $e) {
456
-					$this->log->logException($e, [
457
-						'message' => "Can't send new user mail to $email",
458
-						'level' => \OCP\Util::ERROR,
459
-						'app' => 'settings',
460
-					]);
461
-				}
462
-			}
463
-			// fetch users groups
464
-			$userGroups = $this->groupManager->getUserGroupNames($user);
465
-
466
-			return new DataResponse(
467
-				$this->formatUserForIndex($user, $userGroups),
468
-				Http::STATUS_CREATED
469
-			);
470
-		}
471
-
472
-		return new DataResponse(
473
-			[
474
-				'message' => $this->l10n->t('Unable to create user.')
475
-			],
476
-			Http::STATUS_FORBIDDEN
477
-		);
478
-
479
-	}
480
-
481
-	/**
482
-	 * @NoAdminRequired
483
-	 * @PasswordConfirmationRequired
484
-	 *
485
-	 * @param string $id
486
-	 * @return DataResponse
487
-	 */
488
-	public function destroy(string $id): DataResponse {
489
-		$userId = $this->userSession->getUser()->getUID();
490
-		$user = $this->userManager->get($id);
491
-
492
-		if ($userId === $id) {
493
-			return new DataResponse(
494
-				[
495
-					'status' => 'error',
496
-					'data' => [
497
-						'message' => $this->l10n->t('Unable to delete user.')
498
-					]
499
-				],
500
-				Http::STATUS_FORBIDDEN
501
-			);
502
-		}
503
-
504
-		if (!$this->isAdmin && !$this->groupManager->getSubAdmin()->isUserAccessible($this->userSession->getUser(), $user)) {
505
-			return new DataResponse(
506
-				[
507
-					'status' => 'error',
508
-					'data' => [
509
-						'message' => $this->l10n->t('Authentication error')
510
-					]
511
-				],
512
-				Http::STATUS_FORBIDDEN
513
-			);
514
-		}
515
-
516
-		if ($user && $user->delete()) {
517
-			return new DataResponse(
518
-				[
519
-					'status' => 'success',
520
-					'data' => [
521
-						'username' => $id
522
-					]
523
-				],
524
-				Http::STATUS_NO_CONTENT
525
-			);
526
-		}
527
-
528
-		return new DataResponse(
529
-			[
530
-				'status' => 'error',
531
-				'data' => [
532
-					'message' => $this->l10n->t('Unable to delete user.')
533
-				]
534
-			],
535
-			Http::STATUS_FORBIDDEN
536
-		);
537
-	}
538
-
539
-	/**
540
-	 * @NoAdminRequired
541
-	 *
542
-	 * @param string $id
543
-	 * @param int $enabled
544
-	 * @return DataResponse
545
-	 */
546
-	public function setEnabled(string $id, int $enabled): DataResponse {
547
-		$enabled = (bool)$enabled;
548
-		if ($enabled) {
549
-			$errorMsgGeneral = $this->l10n->t('Error while enabling user.');
550
-		} else {
551
-			$errorMsgGeneral = $this->l10n->t('Error while disabling user.');
552
-		}
553
-
554
-		$userId = $this->userSession->getUser()->getUID();
555
-		$user = $this->userManager->get($id);
556
-
557
-		if ($userId === $id) {
558
-			return new DataResponse(
559
-				[
560
-					'status' => 'error',
561
-					'data' => [
562
-						'message' => $errorMsgGeneral
563
-					]
564
-				], Http::STATUS_FORBIDDEN
565
-			);
566
-		}
567
-
568
-		if ($user) {
569
-			if (!$this->isAdmin && !$this->groupManager->getSubAdmin()->isUserAccessible($this->userSession->getUser(), $user)) {
570
-				return new DataResponse(
571
-					[
572
-						'status' => 'error',
573
-						'data' => [
574
-							'message' => $this->l10n->t('Authentication error')
575
-						]
576
-					],
577
-					Http::STATUS_FORBIDDEN
578
-				);
579
-			}
580
-
581
-			$user->setEnabled($enabled);
582
-			return new DataResponse(
583
-				[
584
-					'status' => 'success',
585
-					'data' => [
586
-						'username' => $id,
587
-						'enabled' => $enabled
588
-					]
589
-				]
590
-			);
591
-		} else {
592
-			return new DataResponse(
593
-				[
594
-					'status' => 'error',
595
-					'data' => [
596
-						'message' => $errorMsgGeneral
597
-					]
598
-				],
599
-				Http::STATUS_FORBIDDEN
600
-			);
601
-		}
602
-
603
-	}
604
-
605
-	/**
606
-	 * Set the mail address of a user
607
-	 *
608
-	 * @NoAdminRequired
609
-	 * @NoSubadminRequired
610
-	 * @PasswordConfirmationRequired
611
-	 *
612
-	 * @param string $account
613
-	 * @param bool $onlyVerificationCode only return verification code without updating the data
614
-	 * @return DataResponse
615
-	 */
616
-	public function getVerificationCode(string $account, bool $onlyVerificationCode): DataResponse {
617
-
618
-		$user = $this->userSession->getUser();
619
-
620
-		if ($user === null) {
621
-			return new DataResponse([], Http::STATUS_BAD_REQUEST);
622
-		}
623
-
624
-		$accountData = $this->accountManager->getUser($user);
625
-		$cloudId = $user->getCloudId();
626
-		$message = 'Use my Federated Cloud ID to share with me: ' . $cloudId;
627
-		$signature = $this->signMessage($user, $message);
628
-
629
-		$code = $message . ' ' . $signature;
630
-		$codeMd5 = $message . ' ' . md5($signature);
631
-
632
-		switch ($account) {
633
-			case 'verify-twitter':
634
-				$accountData[AccountManager::PROPERTY_TWITTER]['verified'] = AccountManager::VERIFICATION_IN_PROGRESS;
635
-				$msg = $this->l10n->t('In order to verify your Twitter account, post the following tweet on Twitter (please make sure to post it without any line breaks):');
636
-				$code = $codeMd5;
637
-				$type = AccountManager::PROPERTY_TWITTER;
638
-				$data = $accountData[AccountManager::PROPERTY_TWITTER]['value'];
639
-				$accountData[AccountManager::PROPERTY_TWITTER]['signature'] = $signature;
640
-				break;
641
-			case 'verify-website':
642
-				$accountData[AccountManager::PROPERTY_WEBSITE]['verified'] = AccountManager::VERIFICATION_IN_PROGRESS;
643
-				$msg = $this->l10n->t('In order to verify your Website, store the following content in your web-root at \'.well-known/CloudIdVerificationCode.txt\' (please make sure that the complete text is in one line):');
644
-				$type = AccountManager::PROPERTY_WEBSITE;
645
-				$data = $accountData[AccountManager::PROPERTY_WEBSITE]['value'];
646
-				$accountData[AccountManager::PROPERTY_WEBSITE]['signature'] = $signature;
647
-				break;
648
-			default:
649
-				return new DataResponse([], Http::STATUS_BAD_REQUEST);
650
-		}
651
-
652
-		if ($onlyVerificationCode === false) {
653
-			$this->accountManager->updateUser($user, $accountData);
654
-
655
-			$this->jobList->add(VerifyUserData::class,
656
-				[
657
-					'verificationCode' => $code,
658
-					'data' => $data,
659
-					'type' => $type,
660
-					'uid' => $user->getUID(),
661
-					'try' => 0,
662
-					'lastRun' => $this->getCurrentTime()
663
-				]
664
-			);
665
-		}
666
-
667
-		return new DataResponse(['msg' => $msg, 'code' => $code]);
668
-	}
669
-
670
-	/**
671
-	 * get current timestamp
672
-	 *
673
-	 * @return int
674
-	 */
675
-	protected function getCurrentTime(): int {
676
-		return time();
677
-	}
678
-
679
-	/**
680
-	 * sign message with users private key
681
-	 *
682
-	 * @param IUser $user
683
-	 * @param string $message
684
-	 *
685
-	 * @return string base64 encoded signature
686
-	 */
687
-	protected function signMessage(IUser $user, string $message): string {
688
-		$privateKey = $this->keyManager->getKey($user)->getPrivate();
689
-		openssl_sign(json_encode($message), $signature, $privateKey, OPENSSL_ALGO_SHA512);
690
-		return base64_encode($signature);
691
-	}
692
-
693
-	/**
694
-	 * @NoAdminRequired
695
-	 * @NoSubadminRequired
696
-	 * @PasswordConfirmationRequired
697
-	 *
698
-	 * @param string $avatarScope
699
-	 * @param string $displayname
700
-	 * @param string $displaynameScope
701
-	 * @param string $phone
702
-	 * @param string $phoneScope
703
-	 * @param string $email
704
-	 * @param string $emailScope
705
-	 * @param string $website
706
-	 * @param string $websiteScope
707
-	 * @param string $address
708
-	 * @param string $addressScope
709
-	 * @param string $twitter
710
-	 * @param string $twitterScope
711
-	 * @return DataResponse
712
-	 */
713
-	public function setUserSettings($avatarScope,
714
-									$displayname,
715
-									$displaynameScope,
716
-									$phone,
717
-									$phoneScope,
718
-									$email,
719
-									$emailScope,
720
-									$website,
721
-									$websiteScope,
722
-									$address,
723
-									$addressScope,
724
-									$twitter,
725
-									$twitterScope
726
-	) {
727
-
728
-		if (!empty($email) && !$this->mailer->validateMailAddress($email)) {
729
-			return new DataResponse(
730
-				[
731
-					'status' => 'error',
732
-					'data' => [
733
-						'message' => $this->l10n->t('Invalid mail address')
734
-					]
735
-				],
736
-				Http::STATUS_UNPROCESSABLE_ENTITY
737
-			);
738
-		}
739
-
740
-		$user = $this->userSession->getUser();
741
-
742
-		$data = $this->accountManager->getUser($user);
743
-
744
-		$data[AccountManager::PROPERTY_AVATAR] = ['scope' => $avatarScope];
745
-		if ($this->config->getSystemValue('allow_user_to_change_display_name', true) !== false) {
746
-			$data[AccountManager::PROPERTY_DISPLAYNAME] = ['value' => $displayname, 'scope' => $displaynameScope];
747
-			$data[AccountManager::PROPERTY_EMAIL] = ['value' => $email, 'scope' => $emailScope];
748
-		}
749
-
750
-		if ($this->appManager->isEnabledForUser('federatedfilesharing')) {
751
-			$federatedFileSharing = new \OCA\FederatedFileSharing\AppInfo\Application();
752
-			$shareProvider = $federatedFileSharing->getFederatedShareProvider();
753
-			if ($shareProvider->isLookupServerUploadEnabled()) {
754
-				$data[AccountManager::PROPERTY_WEBSITE] = ['value' => $website, 'scope' => $websiteScope];
755
-				$data[AccountManager::PROPERTY_ADDRESS] = ['value' => $address, 'scope' => $addressScope];
756
-				$data[AccountManager::PROPERTY_PHONE] = ['value' => $phone, 'scope' => $phoneScope];
757
-				$data[AccountManager::PROPERTY_TWITTER] = ['value' => $twitter, 'scope' => $twitterScope];
758
-			}
759
-		}
760
-
761
-		try {
762
-			$this->saveUserSettings($user, $data);
763
-			return new DataResponse(
764
-				[
765
-					'status' => 'success',
766
-					'data' => [
767
-						'userId' => $user->getUID(),
768
-						'avatarScope' => $data[AccountManager::PROPERTY_AVATAR]['scope'],
769
-						'displayname' => $data[AccountManager::PROPERTY_DISPLAYNAME]['value'],
770
-						'displaynameScope' => $data[AccountManager::PROPERTY_DISPLAYNAME]['scope'],
771
-						'email' => $data[AccountManager::PROPERTY_EMAIL]['value'],
772
-						'emailScope' => $data[AccountManager::PROPERTY_EMAIL]['scope'],
773
-						'website' => $data[AccountManager::PROPERTY_WEBSITE]['value'],
774
-						'websiteScope' => $data[AccountManager::PROPERTY_WEBSITE]['scope'],
775
-						'address' => $data[AccountManager::PROPERTY_ADDRESS]['value'],
776
-						'addressScope' => $data[AccountManager::PROPERTY_ADDRESS]['scope'],
777
-						'message' => $this->l10n->t('Settings saved')
778
-					]
779
-				],
780
-				Http::STATUS_OK
781
-			);
782
-		} catch (ForbiddenException $e) {
783
-			return new DataResponse([
784
-				'status' => 'error',
785
-				'data' => [
786
-					'message' => $e->getMessage()
787
-				],
788
-			]);
789
-		}
790
-
791
-	}
792
-
793
-
794
-	/**
795
-	 * update account manager with new user data
796
-	 *
797
-	 * @param IUser $user
798
-	 * @param array $data
799
-	 * @throws ForbiddenException
800
-	 */
801
-	protected function saveUserSettings(IUser $user, array $data) {
802
-
803
-		// keep the user back-end up-to-date with the latest display name and email
804
-		// address
805
-		$oldDisplayName = $user->getDisplayName();
806
-		$oldDisplayName = is_null($oldDisplayName) ? '' : $oldDisplayName;
807
-		if (isset($data[AccountManager::PROPERTY_DISPLAYNAME]['value'])
808
-			&& $oldDisplayName !== $data[AccountManager::PROPERTY_DISPLAYNAME]['value']
809
-		) {
810
-			$result = $user->setDisplayName($data[AccountManager::PROPERTY_DISPLAYNAME]['value']);
811
-			if ($result === false) {
812
-				throw new ForbiddenException($this->l10n->t('Unable to change full name'));
813
-			}
814
-		}
815
-
816
-		$oldEmailAddress = $user->getEMailAddress();
817
-		$oldEmailAddress = is_null($oldEmailAddress) ? '' : $oldEmailAddress;
818
-		if (isset($data[AccountManager::PROPERTY_EMAIL]['value'])
819
-			&& $oldEmailAddress !== $data[AccountManager::PROPERTY_EMAIL]['value']
820
-		) {
821
-			// this is the only permission a backend provides and is also used
822
-			// for the permission of setting a email address
823
-			if (!$user->canChangeDisplayName()) {
824
-				throw new ForbiddenException($this->l10n->t('Unable to change email address'));
825
-			}
826
-			$user->setEMailAddress($data[AccountManager::PROPERTY_EMAIL]['value']);
827
-		}
828
-
829
-		$this->accountManager->updateUser($user, $data);
830
-	}
831
-
832
-	/**
833
-	 * Count all unique users visible for the current admin/subadmin.
834
-	 *
835
-	 * @NoAdminRequired
836
-	 *
837
-	 * @return DataResponse
838
-	 */
839
-	public function stats(): DataResponse {
840
-		$userCount = 0;
841
-		if ($this->isAdmin) {
842
-			$countByBackend = $this->userManager->countUsers();
843
-
844
-			if (!empty($countByBackend)) {
845
-				foreach ($countByBackend as $count) {
846
-					$userCount += $count;
847
-				}
848
-			}
849
-		} else {
850
-			$groups = $this->groupManager->getSubAdmin()->getSubAdminsGroups($this->userSession->getUser());
851
-
852
-			$uniqueUsers = [];
853
-			foreach ($groups as $group) {
854
-				foreach ($group->getUsers() as $uid => $displayName) {
855
-					$uniqueUsers[$uid] = true;
856
-				}
857
-			}
858
-
859
-			$userCount = count($uniqueUsers);
860
-		}
861
-
862
-		return new DataResponse(
863
-			[
864
-				'totalUsers' => $userCount
865
-			]
866
-		);
867
-	}
868
-
869
-
870
-	/**
871
-	 * Set the displayName of a user
872
-	 *
873
-	 * @NoAdminRequired
874
-	 * @NoSubadminRequired
875
-	 * @PasswordConfirmationRequired
876
-	 * @todo merge into saveUserSettings
877
-	 *
878
-	 * @param string $username
879
-	 * @param string $displayName
880
-	 * @return DataResponse
881
-	 */
882
-	public function setDisplayName(string $username, string $displayName) {
883
-		$currentUser = $this->userSession->getUser();
884
-		$user = $this->userManager->get($username);
885
-
886
-		if ($user === null ||
887
-			!$user->canChangeDisplayName() ||
888
-			(
889
-				!$this->groupManager->isAdmin($currentUser->getUID()) &&
890
-				!$this->groupManager->getSubAdmin()->isUserAccessible($currentUser, $user) &&
891
-				$currentUser->getUID() !== $username
892
-
893
-			)
894
-		) {
895
-			return new DataResponse([
896
-				'status' => 'error',
897
-				'data' => [
898
-					'message' => $this->l10n->t('Authentication error'),
899
-				],
900
-			]);
901
-		}
902
-
903
-		$userData = $this->accountManager->getUser($user);
904
-		$userData[AccountManager::PROPERTY_DISPLAYNAME]['value'] = $displayName;
905
-
906
-
907
-		try {
908
-			$this->saveUserSettings($user, $userData);
909
-			return new DataResponse([
910
-				'status' => 'success',
911
-				'data' => [
912
-					'message' => $this->l10n->t('Your full name has been changed.'),
913
-					'username' => $username,
914
-					'displayName' => $displayName,
915
-				],
916
-			]);
917
-		} catch (ForbiddenException $e) {
918
-			return new DataResponse([
919
-				'status' => 'error',
920
-				'data' => [
921
-					'message' => $e->getMessage(),
922
-					'displayName' => $user->getDisplayName(),
923
-				],
924
-			]);
925
-		}
926
-	}
927
-
928
-	/**
929
-	 * Set the mail address of a user
930
-	 *
931
-	 * @NoAdminRequired
932
-	 * @NoSubadminRequired
933
-	 * @PasswordConfirmationRequired
934
-	 *
935
-	 * @param string $id
936
-	 * @param string $mailAddress
937
-	 * @return DataResponse
938
-	 */
939
-	public function setEMailAddress(string $id, string $mailAddress) {
940
-		$user = $this->userManager->get($id);
941
-		if (!$this->isAdmin
942
-			&& !$this->groupManager->getSubAdmin()->isUserAccessible($this->userSession->getUser(), $user)
943
-		) {
944
-			return new DataResponse(
945
-				[
946
-					'status' => 'error',
947
-					'data' => [
948
-						'message' => $this->l10n->t('Forbidden')
949
-					]
950
-				],
951
-				Http::STATUS_FORBIDDEN
952
-			);
953
-		}
954
-
955
-		if ($mailAddress !== '' && !$this->mailer->validateMailAddress($mailAddress)) {
956
-			return new DataResponse(
957
-				[
958
-					'status' => 'error',
959
-					'data' => [
960
-						'message' => $this->l10n->t('Invalid mail address')
961
-					]
962
-				],
963
-				Http::STATUS_UNPROCESSABLE_ENTITY
964
-			);
965
-		}
966
-
967
-		if (!$user) {
968
-			return new DataResponse(
969
-				[
970
-					'status' => 'error',
971
-					'data' => [
972
-						'message' => $this->l10n->t('Invalid user')
973
-					]
974
-				],
975
-				Http::STATUS_UNPROCESSABLE_ENTITY
976
-			);
977
-		}
978
-		// this is the only permission a backend provides and is also used
979
-		// for the permission of setting a email address
980
-		if (!$user->canChangeDisplayName()) {
981
-			return new DataResponse(
982
-				[
983
-					'status' => 'error',
984
-					'data' => [
985
-						'message' => $this->l10n->t('Unable to change mail address')
986
-					]
987
-				],
988
-				Http::STATUS_FORBIDDEN
989
-			);
990
-		}
991
-
992
-		$userData = $this->accountManager->getUser($user);
993
-		$userData[AccountManager::PROPERTY_EMAIL]['value'] = $mailAddress;
994
-
995
-		try {
996
-			$this->saveUserSettings($user, $userData);
997
-			return new DataResponse(
998
-				[
999
-					'status' => 'success',
1000
-					'data' => [
1001
-						'username' => $id,
1002
-						'mailAddress' => $mailAddress,
1003
-						'message' => $this->l10n->t('Email saved')
1004
-					]
1005
-				],
1006
-				Http::STATUS_OK
1007
-			);
1008
-		} catch (ForbiddenException $e) {
1009
-			return new DataResponse([
1010
-				'status' => 'error',
1011
-				'data' => [
1012
-					'message' => $e->getMessage()
1013
-				],
1014
-			]);
1015
-		}
1016
-	}
74
+    /** @var IL10N */
75
+    private $l10n;
76
+    /** @var IUserSession */
77
+    private $userSession;
78
+    /** @var bool */
79
+    private $isAdmin;
80
+    /** @var IUserManager */
81
+    private $userManager;
82
+    /** @var IGroupManager */
83
+    private $groupManager;
84
+    /** @var IConfig */
85
+    private $config;
86
+    /** @var ILogger */
87
+    private $log;
88
+    /** @var IMailer */
89
+    private $mailer;
90
+    /** @var bool contains the state of the encryption app */
91
+    private $isEncryptionAppEnabled;
92
+    /** @var bool contains the state of the admin recovery setting */
93
+    private $isRestoreEnabled = false;
94
+    /** @var IAppManager */
95
+    private $appManager;
96
+    /** @var IAvatarManager */
97
+    private $avatarManager;
98
+    /** @var AccountManager */
99
+    private $accountManager;
100
+    /** @var ISecureRandom */
101
+    private $secureRandom;
102
+    /** @var NewUserMailHelper */
103
+    private $newUserMailHelper;
104
+    /** @var Manager */
105
+    private $keyManager;
106
+    /** @var IJobList */
107
+    private $jobList;
108
+
109
+    /** @var IUserMountCache */
110
+    private $userMountCache;
111
+
112
+    /** @var IManager */
113
+    private $encryptionManager;
114
+
115
+    public function __construct(string $appName,
116
+                                IRequest $request,
117
+                                IUserManager $userManager,
118
+                                IGroupManager $groupManager,
119
+                                IUserSession $userSession,
120
+                                IConfig $config,
121
+                                bool $isAdmin,
122
+                                IL10N $l10n,
123
+                                ILogger $log,
124
+                                IMailer $mailer,
125
+                                IURLGenerator $urlGenerator,
126
+                                IAppManager $appManager,
127
+                                IAvatarManager $avatarManager,
128
+                                AccountManager $accountManager,
129
+                                ISecureRandom $secureRandom,
130
+                                NewUserMailHelper $newUserMailHelper,
131
+                                Manager $keyManager,
132
+                                IJobList $jobList,
133
+                                IUserMountCache $userMountCache,
134
+                                IManager $encryptionManager) {
135
+        parent::__construct($appName, $request);
136
+        $this->userManager = $userManager;
137
+        $this->groupManager = $groupManager;
138
+        $this->userSession = $userSession;
139
+        $this->config = $config;
140
+        $this->isAdmin = $isAdmin;
141
+        $this->l10n = $l10n;
142
+        $this->log = $log;
143
+        $this->mailer = $mailer;
144
+        $this->appManager = $appManager;
145
+        $this->avatarManager = $avatarManager;
146
+        $this->accountManager = $accountManager;
147
+        $this->secureRandom = $secureRandom;
148
+        $this->newUserMailHelper = $newUserMailHelper;
149
+        $this->keyManager = $keyManager;
150
+        $this->jobList = $jobList;
151
+        $this->userMountCache = $userMountCache;
152
+        $this->encryptionManager = $encryptionManager;
153
+
154
+        // check for encryption state - TODO see formatUserForIndex
155
+        $this->isEncryptionAppEnabled = $appManager->isEnabledForUser('encryption');
156
+        if ($this->isEncryptionAppEnabled) {
157
+            // putting this directly in empty is possible in PHP 5.5+
158
+            $result = $config->getAppValue('encryption', 'recoveryAdminEnabled', '0');
159
+            $this->isRestoreEnabled = !empty($result);
160
+        }
161
+    }
162
+
163
+    /**
164
+     * @param IUser $user
165
+     * @param array|null $userGroups
166
+     * @return array
167
+     */
168
+    private function formatUserForIndex(IUser $user, array $userGroups = null): array {
169
+
170
+        // TODO: eliminate this encryption specific code below and somehow
171
+        // hook in additional user info from other apps
172
+
173
+        // recovery isn't possible if admin or user has it disabled and encryption
174
+        // is enabled - so we eliminate the else paths in the conditional tree
175
+        // below
176
+        $restorePossible = false;
177
+
178
+        if ($this->isEncryptionAppEnabled) {
179
+            if ($this->isRestoreEnabled) {
180
+                // check for the users recovery setting
181
+                $recoveryMode = $this->config->getUserValue($user->getUID(), 'encryption', 'recoveryEnabled', '0');
182
+                // method call inside empty is possible with PHP 5.5+
183
+                $recoveryModeEnabled = !empty($recoveryMode);
184
+                if ($recoveryModeEnabled) {
185
+                    // user also has recovery mode enabled
186
+                    $restorePossible = true;
187
+                }
188
+            } else {
189
+                $modules = $this->encryptionManager->getEncryptionModules();
190
+                $restorePossible = true;
191
+                foreach ($modules as $id => $module) {
192
+                    /* @var IEncryptionModule $instance */
193
+                    $instance = call_user_func($module['callback']);
194
+                    if ($instance->needDetailedAccessList()) {
195
+                        $restorePossible = false;
196
+                        break;
197
+                    }
198
+                }
199
+            }
200
+        } else {
201
+            // recovery is possible if encryption is disabled (plain files are
202
+            // available)
203
+            $restorePossible = true;
204
+        }
205
+
206
+        $subAdminGroups = $this->groupManager->getSubAdmin()->getSubAdminsGroupsName($user);
207
+
208
+        $displayName = $user->getEMailAddress();
209
+        if (is_null($displayName)) {
210
+            $displayName = '';
211
+        }
212
+
213
+        $avatarAvailable = false;
214
+        try {
215
+            $avatarAvailable = $this->avatarManager->getAvatar($user->getUID())->exists();
216
+        } catch (\Exception $e) {
217
+            //No avatar yet
218
+        }
219
+
220
+        return [
221
+            'name' => $user->getUID(),
222
+            'displayname' => $user->getDisplayName(),
223
+            'groups' => empty($userGroups) ? $this->groupManager->getUserGroupNames($user) : $userGroups,
224
+            'subadmin' => $subAdminGroups,
225
+            'quota' => $user->getQuota(),
226
+            'quota_bytes' => Util::computerFileSize($user->getQuota()),
227
+            'storageLocation' => $user->getHome(),
228
+            'lastLogin' => $user->getLastLogin() * 1000,
229
+            'backend' => $user->getBackendClassName(),
230
+            'email' => $displayName,
231
+            'isRestoreDisabled' => !$restorePossible,
232
+            'isAvatarAvailable' => $avatarAvailable,
233
+            'isEnabled' => $user->isEnabled(),
234
+        ];
235
+    }
236
+
237
+    /**
238
+     * @param array $userIDs Array with schema [$uid => $displayName]
239
+     * @return IUser[]
240
+     */
241
+    private function getUsersForUID(array $userIDs): array {
242
+        $users = [];
243
+        foreach ($userIDs as $uid => $displayName) {
244
+            $users[$uid] = $this->userManager->get($uid);
245
+        }
246
+        return $users;
247
+    }
248
+
249
+    /**
250
+     * @NoAdminRequired
251
+     *
252
+     * @param int $offset
253
+     * @param int $limit
254
+     * @param string $gid GID to filter for
255
+     * @param string $pattern Pattern to search for in the username
256
+     * @param string $backend Backend to filter for (class-name)
257
+     * @return DataResponse
258
+     *
259
+     * TODO: Tidy up and write unit tests - code is mainly static method calls
260
+     */
261
+    public function index(int $offset = 0, int $limit = 10, string $gid = '', string $pattern = '', string $backend = ''): DataResponse {
262
+        // Remove backends
263
+        if (!empty($backend)) {
264
+            $activeBackends = $this->userManager->getBackends();
265
+            $this->userManager->clearBackends();
266
+            foreach ($activeBackends as $singleActiveBackend) {
267
+                if ($backend === get_class($singleActiveBackend)) {
268
+                    $this->userManager->registerBackend($singleActiveBackend);
269
+                    break;
270
+                }
271
+            }
272
+        }
273
+
274
+        $userObjects = [];
275
+        $users = [];
276
+        if ($this->isAdmin) {
277
+            if ($gid !== '' && $gid !== '_disabledUsers' && $gid !== '_everyone') {
278
+                $batch = $this->getUsersForUID($this->groupManager->displayNamesInGroup($gid, $pattern, $limit, $offset));
279
+            } else {
280
+                $batch = $this->userManager->search($pattern, $limit, $offset);
281
+            }
282
+
283
+            foreach ($batch as $user) {
284
+                if (($gid !== '_disabledUsers' && $user->isEnabled()) ||
285
+                    ($gid === '_disabledUsers' && !$user->isEnabled())
286
+                ) {
287
+                    $userObjects[] = $user;
288
+                    $users[] = $this->formatUserForIndex($user);
289
+                }
290
+            }
291
+
292
+        } else {
293
+            $subAdminOfGroups = $this->groupManager->getSubAdmin()->getSubAdminsGroups($this->userSession->getUser());
294
+            // New class returns IGroup[] so convert back
295
+            $gids = [];
296
+            foreach ($subAdminOfGroups as $group) {
297
+                $gids[] = $group->getGID();
298
+            }
299
+            $subAdminOfGroups = $gids;
300
+
301
+            // Set the $gid parameter to an empty value if the subadmin has no rights to access a specific group
302
+            if ($gid !== '' && $gid !== '_disabledUsers' && !in_array($gid, $subAdminOfGroups)) {
303
+                $gid = '';
304
+            }
305
+
306
+            // Batch all groups the user is subadmin of when a group is specified
307
+            $batch = [];
308
+            if ($gid !== '' && $gid !== '_disabledUsers' && $gid !== '_everyone') {
309
+                $batch = $this->groupManager->displayNamesInGroup($gid, $pattern, $limit, $offset);
310
+            } else {
311
+                foreach ($subAdminOfGroups as $group) {
312
+                    $groupUsers = $this->groupManager->displayNamesInGroup($group, $pattern, $limit, $offset);
313
+
314
+                    foreach ($groupUsers as $uid => $displayName) {
315
+                        $batch[$uid] = $displayName;
316
+                    }
317
+                }
318
+            }
319
+            $batch = $this->getUsersForUID($batch);
320
+
321
+            foreach ($batch as $user) {
322
+                // Only add the groups, this user is a subadmin of
323
+                $userGroups = array_values(array_intersect(
324
+                    $this->groupManager->getUserGroupIds($user),
325
+                    $subAdminOfGroups
326
+                ));
327
+                if (($gid !== '_disabledUsers' && $user->isEnabled()) ||
328
+                    ($gid === '_disabledUsers' && !$user->isEnabled())
329
+                ) {
330
+                    $userObjects[] = $user;
331
+                    $users[] = $this->formatUserForIndex($user, $userGroups);
332
+                }
333
+            }
334
+        }
335
+
336
+        $usedSpace = $this->userMountCache->getUsedSpaceForUsers($userObjects);
337
+
338
+        foreach ($users as &$userData) {
339
+            $userData['size'] = isset($usedSpace[$userData['name']]) ? $usedSpace[$userData['name']] : 0;
340
+        }
341
+
342
+        return new DataResponse($users);
343
+    }
344
+
345
+    /**
346
+     * @NoAdminRequired
347
+     * @PasswordConfirmationRequired
348
+     *
349
+     * @param string $username
350
+     * @param string $password
351
+     * @param array $groups
352
+     * @param string $email
353
+     * @return DataResponse
354
+     */
355
+    public function create(string $username, string $password, array $groups = [], $email = ''): DataResponse {
356
+        if ($email !== '' && !$this->mailer->validateMailAddress($email)) {
357
+            return new DataResponse(
358
+                [
359
+                    'message' => $this->l10n->t('Invalid mail address')
360
+                ],
361
+                Http::STATUS_UNPROCESSABLE_ENTITY
362
+            );
363
+        }
364
+
365
+        $currentUser = $this->userSession->getUser();
366
+
367
+        if (!$this->isAdmin) {
368
+            if (!empty($groups)) {
369
+                foreach ($groups as $key => $group) {
370
+                    $groupObject = $this->groupManager->get($group);
371
+                    if ($groupObject === null) {
372
+                        unset($groups[$key]);
373
+                        continue;
374
+                    }
375
+
376
+                    if (!$this->groupManager->getSubAdmin()->isSubAdminOfGroup($currentUser, $groupObject)) {
377
+                        unset($groups[$key]);
378
+                    }
379
+                }
380
+            }
381
+
382
+            if (empty($groups)) {
383
+                return new DataResponse(
384
+                    [
385
+                        'message' => $this->l10n->t('No valid group selected'),
386
+                    ],
387
+                    Http::STATUS_FORBIDDEN
388
+                );
389
+            }
390
+        }
391
+
392
+        if ($this->userManager->userExists($username)) {
393
+            return new DataResponse(
394
+                [
395
+                    'message' => $this->l10n->t('A user with that name already exists.')
396
+                ],
397
+                Http::STATUS_CONFLICT
398
+            );
399
+        }
400
+
401
+        $generatePasswordResetToken = false;
402
+        if ($password === '') {
403
+            if ($email === '') {
404
+                return new DataResponse(
405
+                    [
406
+                        'message' => $this->l10n->t('To send a password link to the user an email address is required.')
407
+                    ],
408
+                    Http::STATUS_UNPROCESSABLE_ENTITY
409
+                );
410
+            }
411
+
412
+            $password = $this->secureRandom->generate(30);
413
+            // Make sure we pass the password_policy
414
+            $password .= $this->secureRandom->generate(2, '$!.,;:-~+*[]{}()');
415
+            $generatePasswordResetToken = true;
416
+        }
417
+
418
+        try {
419
+            $user = $this->userManager->createUser($username, $password);
420
+        } catch (\Exception $exception) {
421
+            $message = $exception->getMessage();
422
+            if ($exception instanceof HintException && $exception->getHint()) {
423
+                $message = $exception->getHint();
424
+            }
425
+            if (!$message) {
426
+                $message = $this->l10n->t('Unable to create user.');
427
+            }
428
+            return new DataResponse(
429
+                [
430
+                    'message' => (string)$message,
431
+                ],
432
+                Http::STATUS_FORBIDDEN
433
+            );
434
+        }
435
+
436
+        if ($user instanceof IUser) {
437
+            if ($groups !== null) {
438
+                foreach ($groups as $groupName) {
439
+                    $group = $this->groupManager->get($groupName);
440
+
441
+                    if (empty($group)) {
442
+                        $group = $this->groupManager->createGroup($groupName);
443
+                    }
444
+                    $group->addUser($user);
445
+                }
446
+            }
447
+            /**
448
+             * Send new user mail only if a mail is set
449
+             */
450
+            if ($email !== '') {
451
+                $user->setEMailAddress($email);
452
+                try {
453
+                    $emailTemplate = $this->newUserMailHelper->generateTemplate($user, $generatePasswordResetToken);
454
+                    $this->newUserMailHelper->sendMail($user, $emailTemplate);
455
+                } catch (\Exception $e) {
456
+                    $this->log->logException($e, [
457
+                        'message' => "Can't send new user mail to $email",
458
+                        'level' => \OCP\Util::ERROR,
459
+                        'app' => 'settings',
460
+                    ]);
461
+                }
462
+            }
463
+            // fetch users groups
464
+            $userGroups = $this->groupManager->getUserGroupNames($user);
465
+
466
+            return new DataResponse(
467
+                $this->formatUserForIndex($user, $userGroups),
468
+                Http::STATUS_CREATED
469
+            );
470
+        }
471
+
472
+        return new DataResponse(
473
+            [
474
+                'message' => $this->l10n->t('Unable to create user.')
475
+            ],
476
+            Http::STATUS_FORBIDDEN
477
+        );
478
+
479
+    }
480
+
481
+    /**
482
+     * @NoAdminRequired
483
+     * @PasswordConfirmationRequired
484
+     *
485
+     * @param string $id
486
+     * @return DataResponse
487
+     */
488
+    public function destroy(string $id): DataResponse {
489
+        $userId = $this->userSession->getUser()->getUID();
490
+        $user = $this->userManager->get($id);
491
+
492
+        if ($userId === $id) {
493
+            return new DataResponse(
494
+                [
495
+                    'status' => 'error',
496
+                    'data' => [
497
+                        'message' => $this->l10n->t('Unable to delete user.')
498
+                    ]
499
+                ],
500
+                Http::STATUS_FORBIDDEN
501
+            );
502
+        }
503
+
504
+        if (!$this->isAdmin && !$this->groupManager->getSubAdmin()->isUserAccessible($this->userSession->getUser(), $user)) {
505
+            return new DataResponse(
506
+                [
507
+                    'status' => 'error',
508
+                    'data' => [
509
+                        'message' => $this->l10n->t('Authentication error')
510
+                    ]
511
+                ],
512
+                Http::STATUS_FORBIDDEN
513
+            );
514
+        }
515
+
516
+        if ($user && $user->delete()) {
517
+            return new DataResponse(
518
+                [
519
+                    'status' => 'success',
520
+                    'data' => [
521
+                        'username' => $id
522
+                    ]
523
+                ],
524
+                Http::STATUS_NO_CONTENT
525
+            );
526
+        }
527
+
528
+        return new DataResponse(
529
+            [
530
+                'status' => 'error',
531
+                'data' => [
532
+                    'message' => $this->l10n->t('Unable to delete user.')
533
+                ]
534
+            ],
535
+            Http::STATUS_FORBIDDEN
536
+        );
537
+    }
538
+
539
+    /**
540
+     * @NoAdminRequired
541
+     *
542
+     * @param string $id
543
+     * @param int $enabled
544
+     * @return DataResponse
545
+     */
546
+    public function setEnabled(string $id, int $enabled): DataResponse {
547
+        $enabled = (bool)$enabled;
548
+        if ($enabled) {
549
+            $errorMsgGeneral = $this->l10n->t('Error while enabling user.');
550
+        } else {
551
+            $errorMsgGeneral = $this->l10n->t('Error while disabling user.');
552
+        }
553
+
554
+        $userId = $this->userSession->getUser()->getUID();
555
+        $user = $this->userManager->get($id);
556
+
557
+        if ($userId === $id) {
558
+            return new DataResponse(
559
+                [
560
+                    'status' => 'error',
561
+                    'data' => [
562
+                        'message' => $errorMsgGeneral
563
+                    ]
564
+                ], Http::STATUS_FORBIDDEN
565
+            );
566
+        }
567
+
568
+        if ($user) {
569
+            if (!$this->isAdmin && !$this->groupManager->getSubAdmin()->isUserAccessible($this->userSession->getUser(), $user)) {
570
+                return new DataResponse(
571
+                    [
572
+                        'status' => 'error',
573
+                        'data' => [
574
+                            'message' => $this->l10n->t('Authentication error')
575
+                        ]
576
+                    ],
577
+                    Http::STATUS_FORBIDDEN
578
+                );
579
+            }
580
+
581
+            $user->setEnabled($enabled);
582
+            return new DataResponse(
583
+                [
584
+                    'status' => 'success',
585
+                    'data' => [
586
+                        'username' => $id,
587
+                        'enabled' => $enabled
588
+                    ]
589
+                ]
590
+            );
591
+        } else {
592
+            return new DataResponse(
593
+                [
594
+                    'status' => 'error',
595
+                    'data' => [
596
+                        'message' => $errorMsgGeneral
597
+                    ]
598
+                ],
599
+                Http::STATUS_FORBIDDEN
600
+            );
601
+        }
602
+
603
+    }
604
+
605
+    /**
606
+     * Set the mail address of a user
607
+     *
608
+     * @NoAdminRequired
609
+     * @NoSubadminRequired
610
+     * @PasswordConfirmationRequired
611
+     *
612
+     * @param string $account
613
+     * @param bool $onlyVerificationCode only return verification code without updating the data
614
+     * @return DataResponse
615
+     */
616
+    public function getVerificationCode(string $account, bool $onlyVerificationCode): DataResponse {
617
+
618
+        $user = $this->userSession->getUser();
619
+
620
+        if ($user === null) {
621
+            return new DataResponse([], Http::STATUS_BAD_REQUEST);
622
+        }
623
+
624
+        $accountData = $this->accountManager->getUser($user);
625
+        $cloudId = $user->getCloudId();
626
+        $message = 'Use my Federated Cloud ID to share with me: ' . $cloudId;
627
+        $signature = $this->signMessage($user, $message);
628
+
629
+        $code = $message . ' ' . $signature;
630
+        $codeMd5 = $message . ' ' . md5($signature);
631
+
632
+        switch ($account) {
633
+            case 'verify-twitter':
634
+                $accountData[AccountManager::PROPERTY_TWITTER]['verified'] = AccountManager::VERIFICATION_IN_PROGRESS;
635
+                $msg = $this->l10n->t('In order to verify your Twitter account, post the following tweet on Twitter (please make sure to post it without any line breaks):');
636
+                $code = $codeMd5;
637
+                $type = AccountManager::PROPERTY_TWITTER;
638
+                $data = $accountData[AccountManager::PROPERTY_TWITTER]['value'];
639
+                $accountData[AccountManager::PROPERTY_TWITTER]['signature'] = $signature;
640
+                break;
641
+            case 'verify-website':
642
+                $accountData[AccountManager::PROPERTY_WEBSITE]['verified'] = AccountManager::VERIFICATION_IN_PROGRESS;
643
+                $msg = $this->l10n->t('In order to verify your Website, store the following content in your web-root at \'.well-known/CloudIdVerificationCode.txt\' (please make sure that the complete text is in one line):');
644
+                $type = AccountManager::PROPERTY_WEBSITE;
645
+                $data = $accountData[AccountManager::PROPERTY_WEBSITE]['value'];
646
+                $accountData[AccountManager::PROPERTY_WEBSITE]['signature'] = $signature;
647
+                break;
648
+            default:
649
+                return new DataResponse([], Http::STATUS_BAD_REQUEST);
650
+        }
651
+
652
+        if ($onlyVerificationCode === false) {
653
+            $this->accountManager->updateUser($user, $accountData);
654
+
655
+            $this->jobList->add(VerifyUserData::class,
656
+                [
657
+                    'verificationCode' => $code,
658
+                    'data' => $data,
659
+                    'type' => $type,
660
+                    'uid' => $user->getUID(),
661
+                    'try' => 0,
662
+                    'lastRun' => $this->getCurrentTime()
663
+                ]
664
+            );
665
+        }
666
+
667
+        return new DataResponse(['msg' => $msg, 'code' => $code]);
668
+    }
669
+
670
+    /**
671
+     * get current timestamp
672
+     *
673
+     * @return int
674
+     */
675
+    protected function getCurrentTime(): int {
676
+        return time();
677
+    }
678
+
679
+    /**
680
+     * sign message with users private key
681
+     *
682
+     * @param IUser $user
683
+     * @param string $message
684
+     *
685
+     * @return string base64 encoded signature
686
+     */
687
+    protected function signMessage(IUser $user, string $message): string {
688
+        $privateKey = $this->keyManager->getKey($user)->getPrivate();
689
+        openssl_sign(json_encode($message), $signature, $privateKey, OPENSSL_ALGO_SHA512);
690
+        return base64_encode($signature);
691
+    }
692
+
693
+    /**
694
+     * @NoAdminRequired
695
+     * @NoSubadminRequired
696
+     * @PasswordConfirmationRequired
697
+     *
698
+     * @param string $avatarScope
699
+     * @param string $displayname
700
+     * @param string $displaynameScope
701
+     * @param string $phone
702
+     * @param string $phoneScope
703
+     * @param string $email
704
+     * @param string $emailScope
705
+     * @param string $website
706
+     * @param string $websiteScope
707
+     * @param string $address
708
+     * @param string $addressScope
709
+     * @param string $twitter
710
+     * @param string $twitterScope
711
+     * @return DataResponse
712
+     */
713
+    public function setUserSettings($avatarScope,
714
+                                    $displayname,
715
+                                    $displaynameScope,
716
+                                    $phone,
717
+                                    $phoneScope,
718
+                                    $email,
719
+                                    $emailScope,
720
+                                    $website,
721
+                                    $websiteScope,
722
+                                    $address,
723
+                                    $addressScope,
724
+                                    $twitter,
725
+                                    $twitterScope
726
+    ) {
727
+
728
+        if (!empty($email) && !$this->mailer->validateMailAddress($email)) {
729
+            return new DataResponse(
730
+                [
731
+                    'status' => 'error',
732
+                    'data' => [
733
+                        'message' => $this->l10n->t('Invalid mail address')
734
+                    ]
735
+                ],
736
+                Http::STATUS_UNPROCESSABLE_ENTITY
737
+            );
738
+        }
739
+
740
+        $user = $this->userSession->getUser();
741
+
742
+        $data = $this->accountManager->getUser($user);
743
+
744
+        $data[AccountManager::PROPERTY_AVATAR] = ['scope' => $avatarScope];
745
+        if ($this->config->getSystemValue('allow_user_to_change_display_name', true) !== false) {
746
+            $data[AccountManager::PROPERTY_DISPLAYNAME] = ['value' => $displayname, 'scope' => $displaynameScope];
747
+            $data[AccountManager::PROPERTY_EMAIL] = ['value' => $email, 'scope' => $emailScope];
748
+        }
749
+
750
+        if ($this->appManager->isEnabledForUser('federatedfilesharing')) {
751
+            $federatedFileSharing = new \OCA\FederatedFileSharing\AppInfo\Application();
752
+            $shareProvider = $federatedFileSharing->getFederatedShareProvider();
753
+            if ($shareProvider->isLookupServerUploadEnabled()) {
754
+                $data[AccountManager::PROPERTY_WEBSITE] = ['value' => $website, 'scope' => $websiteScope];
755
+                $data[AccountManager::PROPERTY_ADDRESS] = ['value' => $address, 'scope' => $addressScope];
756
+                $data[AccountManager::PROPERTY_PHONE] = ['value' => $phone, 'scope' => $phoneScope];
757
+                $data[AccountManager::PROPERTY_TWITTER] = ['value' => $twitter, 'scope' => $twitterScope];
758
+            }
759
+        }
760
+
761
+        try {
762
+            $this->saveUserSettings($user, $data);
763
+            return new DataResponse(
764
+                [
765
+                    'status' => 'success',
766
+                    'data' => [
767
+                        'userId' => $user->getUID(),
768
+                        'avatarScope' => $data[AccountManager::PROPERTY_AVATAR]['scope'],
769
+                        'displayname' => $data[AccountManager::PROPERTY_DISPLAYNAME]['value'],
770
+                        'displaynameScope' => $data[AccountManager::PROPERTY_DISPLAYNAME]['scope'],
771
+                        'email' => $data[AccountManager::PROPERTY_EMAIL]['value'],
772
+                        'emailScope' => $data[AccountManager::PROPERTY_EMAIL]['scope'],
773
+                        'website' => $data[AccountManager::PROPERTY_WEBSITE]['value'],
774
+                        'websiteScope' => $data[AccountManager::PROPERTY_WEBSITE]['scope'],
775
+                        'address' => $data[AccountManager::PROPERTY_ADDRESS]['value'],
776
+                        'addressScope' => $data[AccountManager::PROPERTY_ADDRESS]['scope'],
777
+                        'message' => $this->l10n->t('Settings saved')
778
+                    ]
779
+                ],
780
+                Http::STATUS_OK
781
+            );
782
+        } catch (ForbiddenException $e) {
783
+            return new DataResponse([
784
+                'status' => 'error',
785
+                'data' => [
786
+                    'message' => $e->getMessage()
787
+                ],
788
+            ]);
789
+        }
790
+
791
+    }
792
+
793
+
794
+    /**
795
+     * update account manager with new user data
796
+     *
797
+     * @param IUser $user
798
+     * @param array $data
799
+     * @throws ForbiddenException
800
+     */
801
+    protected function saveUserSettings(IUser $user, array $data) {
802
+
803
+        // keep the user back-end up-to-date with the latest display name and email
804
+        // address
805
+        $oldDisplayName = $user->getDisplayName();
806
+        $oldDisplayName = is_null($oldDisplayName) ? '' : $oldDisplayName;
807
+        if (isset($data[AccountManager::PROPERTY_DISPLAYNAME]['value'])
808
+            && $oldDisplayName !== $data[AccountManager::PROPERTY_DISPLAYNAME]['value']
809
+        ) {
810
+            $result = $user->setDisplayName($data[AccountManager::PROPERTY_DISPLAYNAME]['value']);
811
+            if ($result === false) {
812
+                throw new ForbiddenException($this->l10n->t('Unable to change full name'));
813
+            }
814
+        }
815
+
816
+        $oldEmailAddress = $user->getEMailAddress();
817
+        $oldEmailAddress = is_null($oldEmailAddress) ? '' : $oldEmailAddress;
818
+        if (isset($data[AccountManager::PROPERTY_EMAIL]['value'])
819
+            && $oldEmailAddress !== $data[AccountManager::PROPERTY_EMAIL]['value']
820
+        ) {
821
+            // this is the only permission a backend provides and is also used
822
+            // for the permission of setting a email address
823
+            if (!$user->canChangeDisplayName()) {
824
+                throw new ForbiddenException($this->l10n->t('Unable to change email address'));
825
+            }
826
+            $user->setEMailAddress($data[AccountManager::PROPERTY_EMAIL]['value']);
827
+        }
828
+
829
+        $this->accountManager->updateUser($user, $data);
830
+    }
831
+
832
+    /**
833
+     * Count all unique users visible for the current admin/subadmin.
834
+     *
835
+     * @NoAdminRequired
836
+     *
837
+     * @return DataResponse
838
+     */
839
+    public function stats(): DataResponse {
840
+        $userCount = 0;
841
+        if ($this->isAdmin) {
842
+            $countByBackend = $this->userManager->countUsers();
843
+
844
+            if (!empty($countByBackend)) {
845
+                foreach ($countByBackend as $count) {
846
+                    $userCount += $count;
847
+                }
848
+            }
849
+        } else {
850
+            $groups = $this->groupManager->getSubAdmin()->getSubAdminsGroups($this->userSession->getUser());
851
+
852
+            $uniqueUsers = [];
853
+            foreach ($groups as $group) {
854
+                foreach ($group->getUsers() as $uid => $displayName) {
855
+                    $uniqueUsers[$uid] = true;
856
+                }
857
+            }
858
+
859
+            $userCount = count($uniqueUsers);
860
+        }
861
+
862
+        return new DataResponse(
863
+            [
864
+                'totalUsers' => $userCount
865
+            ]
866
+        );
867
+    }
868
+
869
+
870
+    /**
871
+     * Set the displayName of a user
872
+     *
873
+     * @NoAdminRequired
874
+     * @NoSubadminRequired
875
+     * @PasswordConfirmationRequired
876
+     * @todo merge into saveUserSettings
877
+     *
878
+     * @param string $username
879
+     * @param string $displayName
880
+     * @return DataResponse
881
+     */
882
+    public function setDisplayName(string $username, string $displayName) {
883
+        $currentUser = $this->userSession->getUser();
884
+        $user = $this->userManager->get($username);
885
+
886
+        if ($user === null ||
887
+            !$user->canChangeDisplayName() ||
888
+            (
889
+                !$this->groupManager->isAdmin($currentUser->getUID()) &&
890
+                !$this->groupManager->getSubAdmin()->isUserAccessible($currentUser, $user) &&
891
+                $currentUser->getUID() !== $username
892
+
893
+            )
894
+        ) {
895
+            return new DataResponse([
896
+                'status' => 'error',
897
+                'data' => [
898
+                    'message' => $this->l10n->t('Authentication error'),
899
+                ],
900
+            ]);
901
+        }
902
+
903
+        $userData = $this->accountManager->getUser($user);
904
+        $userData[AccountManager::PROPERTY_DISPLAYNAME]['value'] = $displayName;
905
+
906
+
907
+        try {
908
+            $this->saveUserSettings($user, $userData);
909
+            return new DataResponse([
910
+                'status' => 'success',
911
+                'data' => [
912
+                    'message' => $this->l10n->t('Your full name has been changed.'),
913
+                    'username' => $username,
914
+                    'displayName' => $displayName,
915
+                ],
916
+            ]);
917
+        } catch (ForbiddenException $e) {
918
+            return new DataResponse([
919
+                'status' => 'error',
920
+                'data' => [
921
+                    'message' => $e->getMessage(),
922
+                    'displayName' => $user->getDisplayName(),
923
+                ],
924
+            ]);
925
+        }
926
+    }
927
+
928
+    /**
929
+     * Set the mail address of a user
930
+     *
931
+     * @NoAdminRequired
932
+     * @NoSubadminRequired
933
+     * @PasswordConfirmationRequired
934
+     *
935
+     * @param string $id
936
+     * @param string $mailAddress
937
+     * @return DataResponse
938
+     */
939
+    public function setEMailAddress(string $id, string $mailAddress) {
940
+        $user = $this->userManager->get($id);
941
+        if (!$this->isAdmin
942
+            && !$this->groupManager->getSubAdmin()->isUserAccessible($this->userSession->getUser(), $user)
943
+        ) {
944
+            return new DataResponse(
945
+                [
946
+                    'status' => 'error',
947
+                    'data' => [
948
+                        'message' => $this->l10n->t('Forbidden')
949
+                    ]
950
+                ],
951
+                Http::STATUS_FORBIDDEN
952
+            );
953
+        }
954
+
955
+        if ($mailAddress !== '' && !$this->mailer->validateMailAddress($mailAddress)) {
956
+            return new DataResponse(
957
+                [
958
+                    'status' => 'error',
959
+                    'data' => [
960
+                        'message' => $this->l10n->t('Invalid mail address')
961
+                    ]
962
+                ],
963
+                Http::STATUS_UNPROCESSABLE_ENTITY
964
+            );
965
+        }
966
+
967
+        if (!$user) {
968
+            return new DataResponse(
969
+                [
970
+                    'status' => 'error',
971
+                    'data' => [
972
+                        'message' => $this->l10n->t('Invalid user')
973
+                    ]
974
+                ],
975
+                Http::STATUS_UNPROCESSABLE_ENTITY
976
+            );
977
+        }
978
+        // this is the only permission a backend provides and is also used
979
+        // for the permission of setting a email address
980
+        if (!$user->canChangeDisplayName()) {
981
+            return new DataResponse(
982
+                [
983
+                    'status' => 'error',
984
+                    'data' => [
985
+                        'message' => $this->l10n->t('Unable to change mail address')
986
+                    ]
987
+                ],
988
+                Http::STATUS_FORBIDDEN
989
+            );
990
+        }
991
+
992
+        $userData = $this->accountManager->getUser($user);
993
+        $userData[AccountManager::PROPERTY_EMAIL]['value'] = $mailAddress;
994
+
995
+        try {
996
+            $this->saveUserSettings($user, $userData);
997
+            return new DataResponse(
998
+                [
999
+                    'status' => 'success',
1000
+                    'data' => [
1001
+                        'username' => $id,
1002
+                        'mailAddress' => $mailAddress,
1003
+                        'message' => $this->l10n->t('Email saved')
1004
+                    ]
1005
+                ],
1006
+                Http::STATUS_OK
1007
+            );
1008
+        } catch (ForbiddenException $e) {
1009
+            return new DataResponse([
1010
+                'status' => 'error',
1011
+                'data' => [
1012
+                    'message' => $e->getMessage()
1013
+                ],
1014
+            ]);
1015
+        }
1016
+    }
1017 1017
 
1018 1018
 }
Please login to merge, or discard this patch.