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