Completed
Pull Request — master (#6136)
by Tobia
15:00
created
settings/Controller/UsersController.php 1 patch
Indentation   +977 added lines, -977 removed lines patch added patch discarded remove patch
@@ -63,982 +63,982 @@
 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(32);
441
-			$generatePasswordResetToken = true;
442
-		}
443
-
444
-		try {
445
-			$user = $this->userManager->createUser($username, $password);
446
-		} catch (\Exception $exception) {
447
-			$message = $exception->getMessage();
448
-			if ($exception instanceof HintException && $exception->getHint()) {
449
-				$message = $exception->getHint();
450
-			}
451
-			if (!$message) {
452
-				$message = $this->l10n->t('Unable to create user.');
453
-			}
454
-			return new DataResponse(
455
-				[
456
-					'message' => (string)$message,
457
-				],
458
-				Http::STATUS_FORBIDDEN
459
-			);
460
-		}
461
-
462
-		if ($user instanceof IUser) {
463
-			if ($groups !== null) {
464
-				foreach ($groups as $groupName) {
465
-					$group = $this->groupManager->get($groupName);
466
-
467
-					if (empty($group)) {
468
-						$group = $this->groupManager->createGroup($groupName);
469
-					}
470
-					$group->addUser($user);
471
-				}
472
-			}
473
-			/**
474
-			 * Send new user mail only if a mail is set
475
-			 */
476
-			if ($email !== '') {
477
-				$user->setEMailAddress($email);
478
-				try {
479
-					$emailTemplate = $this->newUserMailHelper->generateTemplate($user, $generatePasswordResetToken);
480
-					$this->newUserMailHelper->sendMail($user, $emailTemplate);
481
-				} catch (\Exception $e) {
482
-					$this->log->error("Can't send new user mail to $email: " . $e->getMessage(), ['app' => 'settings']);
483
-				}
484
-			}
485
-			// fetch users groups
486
-			$userGroups = $this->groupManager->getUserGroupIds($user);
487
-
488
-			return new DataResponse(
489
-				$this->formatUserForIndex($user, $userGroups),
490
-				Http::STATUS_CREATED
491
-			);
492
-		}
493
-
494
-		return new DataResponse(
495
-			[
496
-				'message' => (string)$this->l10n->t('Unable to create user.')
497
-			],
498
-			Http::STATUS_FORBIDDEN
499
-		);
500
-
501
-	}
502
-
503
-	/**
504
-	 * @NoAdminRequired
505
-	 * @PasswordConfirmationRequired
506
-	 *
507
-	 * @param string $id
508
-	 * @return DataResponse
509
-	 */
510
-	public function destroy($id) {
511
-		$userId = $this->userSession->getUser()->getUID();
512
-		$user = $this->userManager->get($id);
513
-
514
-		if ($userId === $id) {
515
-			return new DataResponse(
516
-				[
517
-					'status' => 'error',
518
-					'data' => [
519
-						'message' => (string)$this->l10n->t('Unable to delete user.')
520
-					]
521
-				],
522
-				Http::STATUS_FORBIDDEN
523
-			);
524
-		}
525
-
526
-		if (!$this->isAdmin && !$this->groupManager->getSubAdmin()->isUserAccessible($this->userSession->getUser(), $user)) {
527
-			return new DataResponse(
528
-				[
529
-					'status' => 'error',
530
-					'data' => [
531
-						'message' => (string)$this->l10n->t('Authentication error')
532
-					]
533
-				],
534
-				Http::STATUS_FORBIDDEN
535
-			);
536
-		}
537
-
538
-		if ($user) {
539
-			if ($user->delete()) {
540
-				return new DataResponse(
541
-					[
542
-						'status' => 'success',
543
-						'data' => [
544
-							'username' => $id
545
-						]
546
-					],
547
-					Http::STATUS_NO_CONTENT
548
-				);
549
-			}
550
-		}
551
-
552
-		return new DataResponse(
553
-			[
554
-				'status' => 'error',
555
-				'data' => [
556
-					'message' => (string)$this->l10n->t('Unable to delete user.')
557
-				]
558
-			],
559
-			Http::STATUS_FORBIDDEN
560
-		);
561
-	}
562
-
563
-	/**
564
-	 * @NoAdminRequired
565
-	 *
566
-	 * @param string $id
567
-	 * @param int $enabled
568
-	 * @return DataResponse
569
-	 */
570
-	public function setEnabled($id, $enabled) {
571
-		$enabled = (bool)$enabled;
572
-		if ($enabled) {
573
-			$errorMsgGeneral = (string)$this->l10n->t('Error while enabling user.');
574
-		} else {
575
-			$errorMsgGeneral = (string)$this->l10n->t('Error while disabling user.');
576
-		}
577
-
578
-		$userId = $this->userSession->getUser()->getUID();
579
-		$user = $this->userManager->get($id);
580
-
581
-		if ($userId === $id) {
582
-			return new DataResponse(
583
-				[
584
-					'status' => 'error',
585
-					'data' => [
586
-						'message' => $errorMsgGeneral
587
-					]
588
-				], Http::STATUS_FORBIDDEN
589
-			);
590
-		}
591
-
592
-		if ($user) {
593
-			if (!$this->isAdmin && !$this->groupManager->getSubAdmin()->isUserAccessible($this->userSession->getUser(), $user)) {
594
-				return new DataResponse(
595
-					[
596
-						'status' => 'error',
597
-						'data' => [
598
-							'message' => (string)$this->l10n->t('Authentication error')
599
-						]
600
-					],
601
-					Http::STATUS_FORBIDDEN
602
-				);
603
-			}
604
-
605
-			$user->setEnabled($enabled);
606
-			return new DataResponse(
607
-				[
608
-					'status' => 'success',
609
-					'data' => [
610
-						'username' => $id,
611
-						'enabled' => $enabled
612
-					]
613
-				]
614
-			);
615
-		} else {
616
-			return new DataResponse(
617
-				[
618
-					'status' => 'error',
619
-					'data' => [
620
-						'message' => $errorMsgGeneral
621
-					]
622
-				],
623
-				Http::STATUS_FORBIDDEN
624
-			);
625
-		}
626
-
627
-	}
628
-
629
-	/**
630
-	 * Set the mail address of a user
631
-	 *
632
-	 * @NoAdminRequired
633
-	 * @NoSubadminRequired
634
-	 * @PasswordConfirmationRequired
635
-	 *
636
-	 * @param string $account
637
-	 * @param bool $onlyVerificationCode only return verification code without updating the data
638
-	 * @return DataResponse
639
-	 */
640
-	public function getVerificationCode($account, $onlyVerificationCode) {
641
-
642
-		$user = $this->userSession->getUser();
643
-
644
-		if ($user === null) {
645
-			return new DataResponse([], Http::STATUS_BAD_REQUEST);
646
-		}
647
-
648
-		$accountData = $this->accountManager->getUser($user);
649
-		$cloudId = $user->getCloudId();
650
-		$message = "Use my Federated Cloud ID to share with me: " . $cloudId;
651
-		$signature = $this->signMessage($user, $message);
652
-
653
-		$code = $message . ' ' . $signature;
654
-		$codeMd5 = $message . ' ' . md5($signature);
655
-
656
-		switch ($account) {
657
-			case 'verify-twitter':
658
-				$accountData[AccountManager::PROPERTY_TWITTER]['verified'] = AccountManager::VERIFICATION_IN_PROGRESS;
659
-				$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):');
660
-				$code = $codeMd5;
661
-				$type = AccountManager::PROPERTY_TWITTER;
662
-				$data = $accountData[AccountManager::PROPERTY_TWITTER]['value'];
663
-				$accountData[AccountManager::PROPERTY_TWITTER]['signature'] = $signature;
664
-				break;
665
-			case 'verify-website':
666
-				$accountData[AccountManager::PROPERTY_WEBSITE]['verified'] = AccountManager::VERIFICATION_IN_PROGRESS;
667
-				$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):');
668
-				$type = AccountManager::PROPERTY_WEBSITE;
669
-				$data = $accountData[AccountManager::PROPERTY_WEBSITE]['value'];
670
-				$accountData[AccountManager::PROPERTY_WEBSITE]['signature'] = $signature;
671
-				break;
672
-			default:
673
-				return new DataResponse([], Http::STATUS_BAD_REQUEST);
674
-		}
675
-
676
-		if ($onlyVerificationCode === false) {
677
-			$this->accountManager->updateUser($user, $accountData);
678
-
679
-			$this->jobList->add('OC\Settings\BackgroundJobs\VerifyUserData',
680
-				[
681
-					'verificationCode' => $code,
682
-					'data' => $data,
683
-					'type' => $type,
684
-					'uid' => $user->getUID(),
685
-					'try' => 0,
686
-					'lastRun' => $this->getCurrentTime()
687
-				]
688
-			);
689
-		}
690
-
691
-		return new DataResponse(['msg' => $msg, 'code' => $code]);
692
-	}
693
-
694
-	/**
695
-	 * get current timestamp
696
-	 *
697
-	 * @return int
698
-	 */
699
-	protected function getCurrentTime() {
700
-		return time();
701
-	}
702
-
703
-	/**
704
-	 * sign message with users private key
705
-	 *
706
-	 * @param IUser $user
707
-	 * @param string $message
708
-	 *
709
-	 * @return string base64 encoded signature
710
-	 */
711
-	protected function signMessage(IUser $user, $message) {
712
-		$privateKey = $this->keyManager->getKey($user)->getPrivate();
713
-		openssl_sign(json_encode($message), $signature, $privateKey, OPENSSL_ALGO_SHA512);
714
-		$signatureBase64 = base64_encode($signature);
715
-
716
-		return $signatureBase64;
717
-	}
718
-
719
-	/**
720
-	 * @NoAdminRequired
721
-	 * @NoSubadminRequired
722
-	 * @PasswordConfirmationRequired
723
-	 *
724
-	 * @param string $avatarScope
725
-	 * @param string $displayname
726
-	 * @param string $displaynameScope
727
-	 * @param string $phone
728
-	 * @param string $phoneScope
729
-	 * @param string $email
730
-	 * @param string $emailScope
731
-	 * @param string $website
732
-	 * @param string $websiteScope
733
-	 * @param string $address
734
-	 * @param string $addressScope
735
-	 * @param string $twitter
736
-	 * @param string $twitterScope
737
-	 * @return DataResponse
738
-	 */
739
-	public function setUserSettings($avatarScope,
740
-									$displayname,
741
-									$displaynameScope,
742
-									$phone,
743
-									$phoneScope,
744
-									$email,
745
-									$emailScope,
746
-									$website,
747
-									$websiteScope,
748
-									$address,
749
-									$addressScope,
750
-									$twitter,
751
-									$twitterScope
752
-	) {
753
-
754
-		if (!empty($email) && !$this->mailer->validateMailAddress($email)) {
755
-			return new DataResponse(
756
-				[
757
-					'status' => 'error',
758
-					'data' => [
759
-						'message' => (string)$this->l10n->t('Invalid mail address')
760
-					]
761
-				],
762
-				Http::STATUS_UNPROCESSABLE_ENTITY
763
-			);
764
-		}
765
-
766
-		$user = $this->userSession->getUser();
767
-
768
-		$data = $this->accountManager->getUser($user);
769
-
770
-		$data[AccountManager::PROPERTY_AVATAR] = ['scope' => $avatarScope];
771
-		if ($this->config->getSystemValue('allow_user_to_change_display_name', true) !== false) {
772
-			$data[AccountManager::PROPERTY_DISPLAYNAME] = ['value' => $displayname, 'scope' => $displaynameScope];
773
-			$data[AccountManager::PROPERTY_EMAIL] = ['value' => $email, 'scope' => $emailScope];
774
-		}
775
-
776
-		if ($this->appManager->isEnabledForUser('federatedfilesharing')) {
777
-			$federatedFileSharing = new \OCA\FederatedFileSharing\AppInfo\Application();
778
-			$shareProvider = $federatedFileSharing->getFederatedShareProvider();
779
-			if ($shareProvider->isLookupServerUploadEnabled()) {
780
-				$data[AccountManager::PROPERTY_WEBSITE] = ['value' => $website, 'scope' => $websiteScope];
781
-				$data[AccountManager::PROPERTY_ADDRESS] = ['value' => $address, 'scope' => $addressScope];
782
-				$data[AccountManager::PROPERTY_PHONE] = ['value' => $phone, 'scope' => $phoneScope];
783
-				$data[AccountManager::PROPERTY_TWITTER] = ['value' => $twitter, 'scope' => $twitterScope];
784
-			}
785
-		}
786
-
787
-		try {
788
-			$this->saveUserSettings($user, $data);
789
-			return new DataResponse(
790
-				[
791
-					'status' => 'success',
792
-					'data' => [
793
-						'userId' => $user->getUID(),
794
-						'avatarScope' => $data[AccountManager::PROPERTY_AVATAR]['scope'],
795
-						'displayname' => $data[AccountManager::PROPERTY_DISPLAYNAME]['value'],
796
-						'displaynameScope' => $data[AccountManager::PROPERTY_DISPLAYNAME]['scope'],
797
-						'email' => $data[AccountManager::PROPERTY_EMAIL]['value'],
798
-						'emailScope' => $data[AccountManager::PROPERTY_EMAIL]['scope'],
799
-						'website' => $data[AccountManager::PROPERTY_WEBSITE]['value'],
800
-						'websiteScope' => $data[AccountManager::PROPERTY_WEBSITE]['scope'],
801
-						'address' => $data[AccountManager::PROPERTY_ADDRESS]['value'],
802
-						'addressScope' => $data[AccountManager::PROPERTY_ADDRESS]['scope'],
803
-						'message' => (string)$this->l10n->t('Settings saved')
804
-					]
805
-				],
806
-				Http::STATUS_OK
807
-			);
808
-		} catch (ForbiddenException $e) {
809
-			return new DataResponse([
810
-				'status' => 'error',
811
-				'data' => [
812
-					'message' => $e->getMessage()
813
-				],
814
-			]);
815
-		}
816
-
817
-	}
818
-
819
-
820
-	/**
821
-	 * update account manager with new user data
822
-	 *
823
-	 * @param IUser $user
824
-	 * @param array $data
825
-	 * @throws ForbiddenException
826
-	 */
827
-	protected function saveUserSettings(IUser $user, $data) {
828
-
829
-		// keep the user back-end up-to-date with the latest display name and email
830
-		// address
831
-		$oldDisplayName = $user->getDisplayName();
832
-		$oldDisplayName = is_null($oldDisplayName) ? '' : $oldDisplayName;
833
-		if (isset($data[AccountManager::PROPERTY_DISPLAYNAME]['value'])
834
-			&& $oldDisplayName !== $data[AccountManager::PROPERTY_DISPLAYNAME]['value']
835
-		) {
836
-			$result = $user->setDisplayName($data[AccountManager::PROPERTY_DISPLAYNAME]['value']);
837
-			if ($result === false) {
838
-				throw new ForbiddenException($this->l10n->t('Unable to change full name'));
839
-			}
840
-		}
841
-
842
-		$oldEmailAddress = $user->getEMailAddress();
843
-		$oldEmailAddress = is_null($oldEmailAddress) ? '' : $oldEmailAddress;
844
-		if (isset($data[AccountManager::PROPERTY_EMAIL]['value'])
845
-			&& $oldEmailAddress !== $data[AccountManager::PROPERTY_EMAIL]['value']
846
-		) {
847
-			// this is the only permission a backend provides and is also used
848
-			// for the permission of setting a email address
849
-			if (!$user->canChangeDisplayName()) {
850
-				throw new ForbiddenException($this->l10n->t('Unable to change email address'));
851
-			}
852
-			$user->setEMailAddress($data[AccountManager::PROPERTY_EMAIL]['value']);
853
-		}
854
-
855
-		$this->accountManager->updateUser($user, $data);
856
-	}
857
-
858
-	/**
859
-	 * Count all unique users visible for the current admin/subadmin.
860
-	 *
861
-	 * @NoAdminRequired
862
-	 *
863
-	 * @return DataResponse
864
-	 */
865
-	public function stats() {
866
-		$userCount = 0;
867
-		if ($this->isAdmin) {
868
-			$countByBackend = $this->userManager->countUsers();
869
-
870
-			if (!empty($countByBackend)) {
871
-				foreach ($countByBackend as $count) {
872
-					$userCount += $count;
873
-				}
874
-			}
875
-		} else {
876
-			$groups = $this->groupManager->getSubAdmin()->getSubAdminsGroups($this->userSession->getUser());
877
-
878
-			$uniqueUsers = [];
879
-			foreach ($groups as $group) {
880
-				foreach ($group->getUsers() as $uid => $displayName) {
881
-					$uniqueUsers[$uid] = true;
882
-				}
883
-			}
884
-
885
-			$userCount = count($uniqueUsers);
886
-		}
887
-
888
-		return new DataResponse(
889
-			[
890
-				'totalUsers' => $userCount
891
-			]
892
-		);
893
-	}
894
-
895
-
896
-	/**
897
-	 * Set the displayName of a user
898
-	 *
899
-	 * @NoAdminRequired
900
-	 * @NoSubadminRequired
901
-	 * @PasswordConfirmationRequired
902
-	 * @todo merge into saveUserSettings
903
-	 *
904
-	 * @param string $username
905
-	 * @param string $displayName
906
-	 * @return DataResponse
907
-	 */
908
-	public function setDisplayName($username, $displayName) {
909
-		$currentUser = $this->userSession->getUser();
910
-		$user = $this->userManager->get($username);
911
-
912
-		if ($user === null ||
913
-			!$user->canChangeDisplayName() ||
914
-			(
915
-				!$this->groupManager->isAdmin($currentUser->getUID()) &&
916
-				!$this->groupManager->getSubAdmin()->isUserAccessible($currentUser, $user) &&
917
-				$currentUser->getUID() !== $username
918
-
919
-			)
920
-		) {
921
-			return new DataResponse([
922
-				'status' => 'error',
923
-				'data' => [
924
-					'message' => $this->l10n->t('Authentication error'),
925
-				],
926
-			]);
927
-		}
928
-
929
-		$userData = $this->accountManager->getUser($user);
930
-		$userData[AccountManager::PROPERTY_DISPLAYNAME]['value'] = $displayName;
931
-
932
-
933
-		try {
934
-			$this->saveUserSettings($user, $userData);
935
-			return new DataResponse([
936
-				'status' => 'success',
937
-				'data' => [
938
-					'message' => $this->l10n->t('Your full name has been changed.'),
939
-					'username' => $username,
940
-					'displayName' => $displayName,
941
-				],
942
-			]);
943
-		} catch (ForbiddenException $e) {
944
-			return new DataResponse([
945
-				'status' => 'error',
946
-				'data' => [
947
-					'message' => $e->getMessage(),
948
-					'displayName' => $user->getDisplayName(),
949
-				],
950
-			]);
951
-		}
952
-	}
953
-
954
-	/**
955
-	 * Set the mail address of a user
956
-	 *
957
-	 * @NoAdminRequired
958
-	 * @NoSubadminRequired
959
-	 * @PasswordConfirmationRequired
960
-	 *
961
-	 * @param string $id
962
-	 * @param string $mailAddress
963
-	 * @return DataResponse
964
-	 */
965
-	public function setEMailAddress($id, $mailAddress) {
966
-		$user = $this->userManager->get($id);
967
-		if (!$this->isAdmin
968
-			&& !$this->groupManager->getSubAdmin()->isUserAccessible($this->userSession->getUser(), $user)
969
-		) {
970
-			return new DataResponse(
971
-				[
972
-					'status' => 'error',
973
-					'data' => [
974
-						'message' => (string)$this->l10n->t('Forbidden')
975
-					]
976
-				],
977
-				Http::STATUS_FORBIDDEN
978
-			);
979
-		}
980
-
981
-		if ($mailAddress !== '' && !$this->mailer->validateMailAddress($mailAddress)) {
982
-			return new DataResponse(
983
-				[
984
-					'status' => 'error',
985
-					'data' => [
986
-						'message' => (string)$this->l10n->t('Invalid mail address')
987
-					]
988
-				],
989
-				Http::STATUS_UNPROCESSABLE_ENTITY
990
-			);
991
-		}
992
-
993
-		if (!$user) {
994
-			return new DataResponse(
995
-				[
996
-					'status' => 'error',
997
-					'data' => [
998
-						'message' => (string)$this->l10n->t('Invalid user')
999
-					]
1000
-				],
1001
-				Http::STATUS_UNPROCESSABLE_ENTITY
1002
-			);
1003
-		}
1004
-		// this is the only permission a backend provides and is also used
1005
-		// for the permission of setting a email address
1006
-		if (!$user->canChangeDisplayName()) {
1007
-			return new DataResponse(
1008
-				[
1009
-					'status' => 'error',
1010
-					'data' => [
1011
-						'message' => (string)$this->l10n->t('Unable to change mail address')
1012
-					]
1013
-				],
1014
-				Http::STATUS_FORBIDDEN
1015
-			);
1016
-		}
1017
-
1018
-		$userData = $this->accountManager->getUser($user);
1019
-		$userData[AccountManager::PROPERTY_EMAIL]['value'] = $mailAddress;
1020
-
1021
-		try {
1022
-			$this->saveUserSettings($user, $userData);
1023
-			return new DataResponse(
1024
-				[
1025
-					'status' => 'success',
1026
-					'data' => [
1027
-						'username' => $id,
1028
-						'mailAddress' => $mailAddress,
1029
-						'message' => (string)$this->l10n->t('Email saved')
1030
-					]
1031
-				],
1032
-				Http::STATUS_OK
1033
-			);
1034
-		} catch (ForbiddenException $e) {
1035
-			return new DataResponse([
1036
-				'status' => 'error',
1037
-				'data' => [
1038
-					'message' => $e->getMessage()
1039
-				],
1040
-			]);
1041
-		}
1042
-	}
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(32);
441
+            $generatePasswordResetToken = true;
442
+        }
443
+
444
+        try {
445
+            $user = $this->userManager->createUser($username, $password);
446
+        } catch (\Exception $exception) {
447
+            $message = $exception->getMessage();
448
+            if ($exception instanceof HintException && $exception->getHint()) {
449
+                $message = $exception->getHint();
450
+            }
451
+            if (!$message) {
452
+                $message = $this->l10n->t('Unable to create user.');
453
+            }
454
+            return new DataResponse(
455
+                [
456
+                    'message' => (string)$message,
457
+                ],
458
+                Http::STATUS_FORBIDDEN
459
+            );
460
+        }
461
+
462
+        if ($user instanceof IUser) {
463
+            if ($groups !== null) {
464
+                foreach ($groups as $groupName) {
465
+                    $group = $this->groupManager->get($groupName);
466
+
467
+                    if (empty($group)) {
468
+                        $group = $this->groupManager->createGroup($groupName);
469
+                    }
470
+                    $group->addUser($user);
471
+                }
472
+            }
473
+            /**
474
+             * Send new user mail only if a mail is set
475
+             */
476
+            if ($email !== '') {
477
+                $user->setEMailAddress($email);
478
+                try {
479
+                    $emailTemplate = $this->newUserMailHelper->generateTemplate($user, $generatePasswordResetToken);
480
+                    $this->newUserMailHelper->sendMail($user, $emailTemplate);
481
+                } catch (\Exception $e) {
482
+                    $this->log->error("Can't send new user mail to $email: " . $e->getMessage(), ['app' => 'settings']);
483
+                }
484
+            }
485
+            // fetch users groups
486
+            $userGroups = $this->groupManager->getUserGroupIds($user);
487
+
488
+            return new DataResponse(
489
+                $this->formatUserForIndex($user, $userGroups),
490
+                Http::STATUS_CREATED
491
+            );
492
+        }
493
+
494
+        return new DataResponse(
495
+            [
496
+                'message' => (string)$this->l10n->t('Unable to create user.')
497
+            ],
498
+            Http::STATUS_FORBIDDEN
499
+        );
500
+
501
+    }
502
+
503
+    /**
504
+     * @NoAdminRequired
505
+     * @PasswordConfirmationRequired
506
+     *
507
+     * @param string $id
508
+     * @return DataResponse
509
+     */
510
+    public function destroy($id) {
511
+        $userId = $this->userSession->getUser()->getUID();
512
+        $user = $this->userManager->get($id);
513
+
514
+        if ($userId === $id) {
515
+            return new DataResponse(
516
+                [
517
+                    'status' => 'error',
518
+                    'data' => [
519
+                        'message' => (string)$this->l10n->t('Unable to delete user.')
520
+                    ]
521
+                ],
522
+                Http::STATUS_FORBIDDEN
523
+            );
524
+        }
525
+
526
+        if (!$this->isAdmin && !$this->groupManager->getSubAdmin()->isUserAccessible($this->userSession->getUser(), $user)) {
527
+            return new DataResponse(
528
+                [
529
+                    'status' => 'error',
530
+                    'data' => [
531
+                        'message' => (string)$this->l10n->t('Authentication error')
532
+                    ]
533
+                ],
534
+                Http::STATUS_FORBIDDEN
535
+            );
536
+        }
537
+
538
+        if ($user) {
539
+            if ($user->delete()) {
540
+                return new DataResponse(
541
+                    [
542
+                        'status' => 'success',
543
+                        'data' => [
544
+                            'username' => $id
545
+                        ]
546
+                    ],
547
+                    Http::STATUS_NO_CONTENT
548
+                );
549
+            }
550
+        }
551
+
552
+        return new DataResponse(
553
+            [
554
+                'status' => 'error',
555
+                'data' => [
556
+                    'message' => (string)$this->l10n->t('Unable to delete user.')
557
+                ]
558
+            ],
559
+            Http::STATUS_FORBIDDEN
560
+        );
561
+    }
562
+
563
+    /**
564
+     * @NoAdminRequired
565
+     *
566
+     * @param string $id
567
+     * @param int $enabled
568
+     * @return DataResponse
569
+     */
570
+    public function setEnabled($id, $enabled) {
571
+        $enabled = (bool)$enabled;
572
+        if ($enabled) {
573
+            $errorMsgGeneral = (string)$this->l10n->t('Error while enabling user.');
574
+        } else {
575
+            $errorMsgGeneral = (string)$this->l10n->t('Error while disabling user.');
576
+        }
577
+
578
+        $userId = $this->userSession->getUser()->getUID();
579
+        $user = $this->userManager->get($id);
580
+
581
+        if ($userId === $id) {
582
+            return new DataResponse(
583
+                [
584
+                    'status' => 'error',
585
+                    'data' => [
586
+                        'message' => $errorMsgGeneral
587
+                    ]
588
+                ], Http::STATUS_FORBIDDEN
589
+            );
590
+        }
591
+
592
+        if ($user) {
593
+            if (!$this->isAdmin && !$this->groupManager->getSubAdmin()->isUserAccessible($this->userSession->getUser(), $user)) {
594
+                return new DataResponse(
595
+                    [
596
+                        'status' => 'error',
597
+                        'data' => [
598
+                            'message' => (string)$this->l10n->t('Authentication error')
599
+                        ]
600
+                    ],
601
+                    Http::STATUS_FORBIDDEN
602
+                );
603
+            }
604
+
605
+            $user->setEnabled($enabled);
606
+            return new DataResponse(
607
+                [
608
+                    'status' => 'success',
609
+                    'data' => [
610
+                        'username' => $id,
611
+                        'enabled' => $enabled
612
+                    ]
613
+                ]
614
+            );
615
+        } else {
616
+            return new DataResponse(
617
+                [
618
+                    'status' => 'error',
619
+                    'data' => [
620
+                        'message' => $errorMsgGeneral
621
+                    ]
622
+                ],
623
+                Http::STATUS_FORBIDDEN
624
+            );
625
+        }
626
+
627
+    }
628
+
629
+    /**
630
+     * Set the mail address of a user
631
+     *
632
+     * @NoAdminRequired
633
+     * @NoSubadminRequired
634
+     * @PasswordConfirmationRequired
635
+     *
636
+     * @param string $account
637
+     * @param bool $onlyVerificationCode only return verification code without updating the data
638
+     * @return DataResponse
639
+     */
640
+    public function getVerificationCode($account, $onlyVerificationCode) {
641
+
642
+        $user = $this->userSession->getUser();
643
+
644
+        if ($user === null) {
645
+            return new DataResponse([], Http::STATUS_BAD_REQUEST);
646
+        }
647
+
648
+        $accountData = $this->accountManager->getUser($user);
649
+        $cloudId = $user->getCloudId();
650
+        $message = "Use my Federated Cloud ID to share with me: " . $cloudId;
651
+        $signature = $this->signMessage($user, $message);
652
+
653
+        $code = $message . ' ' . $signature;
654
+        $codeMd5 = $message . ' ' . md5($signature);
655
+
656
+        switch ($account) {
657
+            case 'verify-twitter':
658
+                $accountData[AccountManager::PROPERTY_TWITTER]['verified'] = AccountManager::VERIFICATION_IN_PROGRESS;
659
+                $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):');
660
+                $code = $codeMd5;
661
+                $type = AccountManager::PROPERTY_TWITTER;
662
+                $data = $accountData[AccountManager::PROPERTY_TWITTER]['value'];
663
+                $accountData[AccountManager::PROPERTY_TWITTER]['signature'] = $signature;
664
+                break;
665
+            case 'verify-website':
666
+                $accountData[AccountManager::PROPERTY_WEBSITE]['verified'] = AccountManager::VERIFICATION_IN_PROGRESS;
667
+                $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):');
668
+                $type = AccountManager::PROPERTY_WEBSITE;
669
+                $data = $accountData[AccountManager::PROPERTY_WEBSITE]['value'];
670
+                $accountData[AccountManager::PROPERTY_WEBSITE]['signature'] = $signature;
671
+                break;
672
+            default:
673
+                return new DataResponse([], Http::STATUS_BAD_REQUEST);
674
+        }
675
+
676
+        if ($onlyVerificationCode === false) {
677
+            $this->accountManager->updateUser($user, $accountData);
678
+
679
+            $this->jobList->add('OC\Settings\BackgroundJobs\VerifyUserData',
680
+                [
681
+                    'verificationCode' => $code,
682
+                    'data' => $data,
683
+                    'type' => $type,
684
+                    'uid' => $user->getUID(),
685
+                    'try' => 0,
686
+                    'lastRun' => $this->getCurrentTime()
687
+                ]
688
+            );
689
+        }
690
+
691
+        return new DataResponse(['msg' => $msg, 'code' => $code]);
692
+    }
693
+
694
+    /**
695
+     * get current timestamp
696
+     *
697
+     * @return int
698
+     */
699
+    protected function getCurrentTime() {
700
+        return time();
701
+    }
702
+
703
+    /**
704
+     * sign message with users private key
705
+     *
706
+     * @param IUser $user
707
+     * @param string $message
708
+     *
709
+     * @return string base64 encoded signature
710
+     */
711
+    protected function signMessage(IUser $user, $message) {
712
+        $privateKey = $this->keyManager->getKey($user)->getPrivate();
713
+        openssl_sign(json_encode($message), $signature, $privateKey, OPENSSL_ALGO_SHA512);
714
+        $signatureBase64 = base64_encode($signature);
715
+
716
+        return $signatureBase64;
717
+    }
718
+
719
+    /**
720
+     * @NoAdminRequired
721
+     * @NoSubadminRequired
722
+     * @PasswordConfirmationRequired
723
+     *
724
+     * @param string $avatarScope
725
+     * @param string $displayname
726
+     * @param string $displaynameScope
727
+     * @param string $phone
728
+     * @param string $phoneScope
729
+     * @param string $email
730
+     * @param string $emailScope
731
+     * @param string $website
732
+     * @param string $websiteScope
733
+     * @param string $address
734
+     * @param string $addressScope
735
+     * @param string $twitter
736
+     * @param string $twitterScope
737
+     * @return DataResponse
738
+     */
739
+    public function setUserSettings($avatarScope,
740
+                                    $displayname,
741
+                                    $displaynameScope,
742
+                                    $phone,
743
+                                    $phoneScope,
744
+                                    $email,
745
+                                    $emailScope,
746
+                                    $website,
747
+                                    $websiteScope,
748
+                                    $address,
749
+                                    $addressScope,
750
+                                    $twitter,
751
+                                    $twitterScope
752
+    ) {
753
+
754
+        if (!empty($email) && !$this->mailer->validateMailAddress($email)) {
755
+            return new DataResponse(
756
+                [
757
+                    'status' => 'error',
758
+                    'data' => [
759
+                        'message' => (string)$this->l10n->t('Invalid mail address')
760
+                    ]
761
+                ],
762
+                Http::STATUS_UNPROCESSABLE_ENTITY
763
+            );
764
+        }
765
+
766
+        $user = $this->userSession->getUser();
767
+
768
+        $data = $this->accountManager->getUser($user);
769
+
770
+        $data[AccountManager::PROPERTY_AVATAR] = ['scope' => $avatarScope];
771
+        if ($this->config->getSystemValue('allow_user_to_change_display_name', true) !== false) {
772
+            $data[AccountManager::PROPERTY_DISPLAYNAME] = ['value' => $displayname, 'scope' => $displaynameScope];
773
+            $data[AccountManager::PROPERTY_EMAIL] = ['value' => $email, 'scope' => $emailScope];
774
+        }
775
+
776
+        if ($this->appManager->isEnabledForUser('federatedfilesharing')) {
777
+            $federatedFileSharing = new \OCA\FederatedFileSharing\AppInfo\Application();
778
+            $shareProvider = $federatedFileSharing->getFederatedShareProvider();
779
+            if ($shareProvider->isLookupServerUploadEnabled()) {
780
+                $data[AccountManager::PROPERTY_WEBSITE] = ['value' => $website, 'scope' => $websiteScope];
781
+                $data[AccountManager::PROPERTY_ADDRESS] = ['value' => $address, 'scope' => $addressScope];
782
+                $data[AccountManager::PROPERTY_PHONE] = ['value' => $phone, 'scope' => $phoneScope];
783
+                $data[AccountManager::PROPERTY_TWITTER] = ['value' => $twitter, 'scope' => $twitterScope];
784
+            }
785
+        }
786
+
787
+        try {
788
+            $this->saveUserSettings($user, $data);
789
+            return new DataResponse(
790
+                [
791
+                    'status' => 'success',
792
+                    'data' => [
793
+                        'userId' => $user->getUID(),
794
+                        'avatarScope' => $data[AccountManager::PROPERTY_AVATAR]['scope'],
795
+                        'displayname' => $data[AccountManager::PROPERTY_DISPLAYNAME]['value'],
796
+                        'displaynameScope' => $data[AccountManager::PROPERTY_DISPLAYNAME]['scope'],
797
+                        'email' => $data[AccountManager::PROPERTY_EMAIL]['value'],
798
+                        'emailScope' => $data[AccountManager::PROPERTY_EMAIL]['scope'],
799
+                        'website' => $data[AccountManager::PROPERTY_WEBSITE]['value'],
800
+                        'websiteScope' => $data[AccountManager::PROPERTY_WEBSITE]['scope'],
801
+                        'address' => $data[AccountManager::PROPERTY_ADDRESS]['value'],
802
+                        'addressScope' => $data[AccountManager::PROPERTY_ADDRESS]['scope'],
803
+                        'message' => (string)$this->l10n->t('Settings saved')
804
+                    ]
805
+                ],
806
+                Http::STATUS_OK
807
+            );
808
+        } catch (ForbiddenException $e) {
809
+            return new DataResponse([
810
+                'status' => 'error',
811
+                'data' => [
812
+                    'message' => $e->getMessage()
813
+                ],
814
+            ]);
815
+        }
816
+
817
+    }
818
+
819
+
820
+    /**
821
+     * update account manager with new user data
822
+     *
823
+     * @param IUser $user
824
+     * @param array $data
825
+     * @throws ForbiddenException
826
+     */
827
+    protected function saveUserSettings(IUser $user, $data) {
828
+
829
+        // keep the user back-end up-to-date with the latest display name and email
830
+        // address
831
+        $oldDisplayName = $user->getDisplayName();
832
+        $oldDisplayName = is_null($oldDisplayName) ? '' : $oldDisplayName;
833
+        if (isset($data[AccountManager::PROPERTY_DISPLAYNAME]['value'])
834
+            && $oldDisplayName !== $data[AccountManager::PROPERTY_DISPLAYNAME]['value']
835
+        ) {
836
+            $result = $user->setDisplayName($data[AccountManager::PROPERTY_DISPLAYNAME]['value']);
837
+            if ($result === false) {
838
+                throw new ForbiddenException($this->l10n->t('Unable to change full name'));
839
+            }
840
+        }
841
+
842
+        $oldEmailAddress = $user->getEMailAddress();
843
+        $oldEmailAddress = is_null($oldEmailAddress) ? '' : $oldEmailAddress;
844
+        if (isset($data[AccountManager::PROPERTY_EMAIL]['value'])
845
+            && $oldEmailAddress !== $data[AccountManager::PROPERTY_EMAIL]['value']
846
+        ) {
847
+            // this is the only permission a backend provides and is also used
848
+            // for the permission of setting a email address
849
+            if (!$user->canChangeDisplayName()) {
850
+                throw new ForbiddenException($this->l10n->t('Unable to change email address'));
851
+            }
852
+            $user->setEMailAddress($data[AccountManager::PROPERTY_EMAIL]['value']);
853
+        }
854
+
855
+        $this->accountManager->updateUser($user, $data);
856
+    }
857
+
858
+    /**
859
+     * Count all unique users visible for the current admin/subadmin.
860
+     *
861
+     * @NoAdminRequired
862
+     *
863
+     * @return DataResponse
864
+     */
865
+    public function stats() {
866
+        $userCount = 0;
867
+        if ($this->isAdmin) {
868
+            $countByBackend = $this->userManager->countUsers();
869
+
870
+            if (!empty($countByBackend)) {
871
+                foreach ($countByBackend as $count) {
872
+                    $userCount += $count;
873
+                }
874
+            }
875
+        } else {
876
+            $groups = $this->groupManager->getSubAdmin()->getSubAdminsGroups($this->userSession->getUser());
877
+
878
+            $uniqueUsers = [];
879
+            foreach ($groups as $group) {
880
+                foreach ($group->getUsers() as $uid => $displayName) {
881
+                    $uniqueUsers[$uid] = true;
882
+                }
883
+            }
884
+
885
+            $userCount = count($uniqueUsers);
886
+        }
887
+
888
+        return new DataResponse(
889
+            [
890
+                'totalUsers' => $userCount
891
+            ]
892
+        );
893
+    }
894
+
895
+
896
+    /**
897
+     * Set the displayName of a user
898
+     *
899
+     * @NoAdminRequired
900
+     * @NoSubadminRequired
901
+     * @PasswordConfirmationRequired
902
+     * @todo merge into saveUserSettings
903
+     *
904
+     * @param string $username
905
+     * @param string $displayName
906
+     * @return DataResponse
907
+     */
908
+    public function setDisplayName($username, $displayName) {
909
+        $currentUser = $this->userSession->getUser();
910
+        $user = $this->userManager->get($username);
911
+
912
+        if ($user === null ||
913
+            !$user->canChangeDisplayName() ||
914
+            (
915
+                !$this->groupManager->isAdmin($currentUser->getUID()) &&
916
+                !$this->groupManager->getSubAdmin()->isUserAccessible($currentUser, $user) &&
917
+                $currentUser->getUID() !== $username
918
+
919
+            )
920
+        ) {
921
+            return new DataResponse([
922
+                'status' => 'error',
923
+                'data' => [
924
+                    'message' => $this->l10n->t('Authentication error'),
925
+                ],
926
+            ]);
927
+        }
928
+
929
+        $userData = $this->accountManager->getUser($user);
930
+        $userData[AccountManager::PROPERTY_DISPLAYNAME]['value'] = $displayName;
931
+
932
+
933
+        try {
934
+            $this->saveUserSettings($user, $userData);
935
+            return new DataResponse([
936
+                'status' => 'success',
937
+                'data' => [
938
+                    'message' => $this->l10n->t('Your full name has been changed.'),
939
+                    'username' => $username,
940
+                    'displayName' => $displayName,
941
+                ],
942
+            ]);
943
+        } catch (ForbiddenException $e) {
944
+            return new DataResponse([
945
+                'status' => 'error',
946
+                'data' => [
947
+                    'message' => $e->getMessage(),
948
+                    'displayName' => $user->getDisplayName(),
949
+                ],
950
+            ]);
951
+        }
952
+    }
953
+
954
+    /**
955
+     * Set the mail address of a user
956
+     *
957
+     * @NoAdminRequired
958
+     * @NoSubadminRequired
959
+     * @PasswordConfirmationRequired
960
+     *
961
+     * @param string $id
962
+     * @param string $mailAddress
963
+     * @return DataResponse
964
+     */
965
+    public function setEMailAddress($id, $mailAddress) {
966
+        $user = $this->userManager->get($id);
967
+        if (!$this->isAdmin
968
+            && !$this->groupManager->getSubAdmin()->isUserAccessible($this->userSession->getUser(), $user)
969
+        ) {
970
+            return new DataResponse(
971
+                [
972
+                    'status' => 'error',
973
+                    'data' => [
974
+                        'message' => (string)$this->l10n->t('Forbidden')
975
+                    ]
976
+                ],
977
+                Http::STATUS_FORBIDDEN
978
+            );
979
+        }
980
+
981
+        if ($mailAddress !== '' && !$this->mailer->validateMailAddress($mailAddress)) {
982
+            return new DataResponse(
983
+                [
984
+                    'status' => 'error',
985
+                    'data' => [
986
+                        'message' => (string)$this->l10n->t('Invalid mail address')
987
+                    ]
988
+                ],
989
+                Http::STATUS_UNPROCESSABLE_ENTITY
990
+            );
991
+        }
992
+
993
+        if (!$user) {
994
+            return new DataResponse(
995
+                [
996
+                    'status' => 'error',
997
+                    'data' => [
998
+                        'message' => (string)$this->l10n->t('Invalid user')
999
+                    ]
1000
+                ],
1001
+                Http::STATUS_UNPROCESSABLE_ENTITY
1002
+            );
1003
+        }
1004
+        // this is the only permission a backend provides and is also used
1005
+        // for the permission of setting a email address
1006
+        if (!$user->canChangeDisplayName()) {
1007
+            return new DataResponse(
1008
+                [
1009
+                    'status' => 'error',
1010
+                    'data' => [
1011
+                        'message' => (string)$this->l10n->t('Unable to change mail address')
1012
+                    ]
1013
+                ],
1014
+                Http::STATUS_FORBIDDEN
1015
+            );
1016
+        }
1017
+
1018
+        $userData = $this->accountManager->getUser($user);
1019
+        $userData[AccountManager::PROPERTY_EMAIL]['value'] = $mailAddress;
1020
+
1021
+        try {
1022
+            $this->saveUserSettings($user, $userData);
1023
+            return new DataResponse(
1024
+                [
1025
+                    'status' => 'success',
1026
+                    'data' => [
1027
+                        'username' => $id,
1028
+                        'mailAddress' => $mailAddress,
1029
+                        'message' => (string)$this->l10n->t('Email saved')
1030
+                    ]
1031
+                ],
1032
+                Http::STATUS_OK
1033
+            );
1034
+        } catch (ForbiddenException $e) {
1035
+            return new DataResponse([
1036
+                'status' => 'error',
1037
+                'data' => [
1038
+                    'message' => $e->getMessage()
1039
+                ],
1040
+            ]);
1041
+        }
1042
+    }
1043 1043
 
1044 1044
 }
Please login to merge, or discard this patch.