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