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