Completed
Pull Request — master (#5174)
by Björn
17:50
created

UsersController::formatUserForIndex()   C

Complexity

Conditions 10
Paths 96

Size

Total Lines 71
Code Lines 42

Duplication

Lines 0
Ratio 0 %

Importance

Changes 0
Metric Value
cc 10
eloc 42
nc 96
nop 2
dl 0
loc 71
rs 5.9513
c 0
b 0
f 0

How to fix   Long Method    Complexity   

Long Method

Small methods make your code easier to understand, in particular if combined with a good name. Besides, if your method is small, finding a good name is usually much easier.

For example, if you find yourself adding comments to a method's body, this is usually a good sign to extract the commented part to a new method, and use the comment as a starting point when coming up with a good name for this new method.

Commonly applied refactorings include:

1
<?php
2
/**
3
 * @copyright Copyright (c) 2016, ownCloud, Inc.
4
 *
5
 * @author Arthur Schiwon <[email protected]>
6
 * @author Clark Tomlinson <[email protected]>
7
 * @author Joas Schilling <[email protected]>
8
 * @author Lukas Reschke <[email protected]>
9
 * @author Morris Jobke <[email protected]>
10
 * @author Robin Appelman <[email protected]>
11
 * @author Roeland Jago Douma <[email protected]>
12
 * @author Thomas Müller <[email protected]>
13
 * @author Vincent Petry <[email protected]>
14
 *
15
 * @license AGPL-3.0
16
 *
17
 * This code is free software: you can redistribute it and/or modify
18
 * it under the terms of the GNU Affero General Public License, version 3,
19
 * as published by the Free Software Foundation.
20
 *
21
 * This program is distributed in the hope that it will be useful,
22
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
23
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
24
 * GNU Affero General Public License for more details.
25
 *
26
 * You should have received a copy of the GNU Affero General Public License, version 3,
27
 * along with this program.  If not, see <http://www.gnu.org/licenses/>
28
 *
29
 */
30
31
namespace OC\Settings\Controller;
32
33
use OC\Accounts\AccountManager;
34
use OC\AppFramework\Http;
35
use OC\ForbiddenException;
36
use OC\HintException;
37
use OC\Settings\Mailer\NewUserMailHelper;
38
use OC\Security\IdentityProof\Manager;
39
use OCP\App\IAppManager;
40
use OCP\AppFramework\Controller;
41
use OCP\AppFramework\Http\DataResponse;
42
use OCP\AppFramework\Utility\ITimeFactory;
43
use OCP\BackgroundJob\IJobList;
44
use OCP\Files\Config\IUserMountCache;
45
use OCP\Encryption\IEncryptionModule;
46
use OCP\Encryption\IManager;
47
use OCP\IConfig;
48
use OCP\IGroupManager;
49
use OCP\IL10N;
50
use OCP\ILogger;
51
use OCP\IRequest;
52
use OCP\IURLGenerator;
53
use OCP\IUser;
54
use OCP\IUserManager;
55
use OCP\IUserSession;
56
use OCP\Mail\IMailer;
57
use OCP\IAvatarManager;
58
use OCP\Security\ICrypto;
59
use OCP\Security\ISecureRandom;
60
use OCP\Util;
61
62
/**
63
 * @package OC\Settings\Controller
64
 */
65
class UsersController extends Controller {
66
	/** @var IL10N */
67
	private $l10n;
68
	/** @var IUserSession */
69
	private $userSession;
70
	/** @var bool */
71
	private $isAdmin;
72
	/** @var IUserManager */
73
	private $userManager;
74
	/** @var IGroupManager */
75
	private $groupManager;
76
	/** @var IConfig */
77
	private $config;
78
	/** @var ILogger */
79
	private $log;
80
	/** @var IMailer */
81
	private $mailer;
82
	/** @var bool contains the state of the encryption app */
83
	private $isEncryptionAppEnabled;
84
	/** @var bool contains the state of the admin recovery setting */
85
	private $isRestoreEnabled = false;
86
	/** @var IAppManager */
87
	private $appManager;
88
	/** @var IAvatarManager */
89
	private $avatarManager;
90
	/** @var AccountManager */
91
	private $accountManager;
92
	/** @var ISecureRandom */
93
	private $secureRandom;
94
	/** @var NewUserMailHelper */
95
	private $newUserMailHelper;
96
	/** @var ITimeFactory */
97
	private $timeFactory;
98
	/** @var ICrypto */
99
	private $crypto;
100
	/** @var Manager */
101
	private $keyManager;
102
	/** @var IJobList */
103
	private $jobList;
104
105
	/** @var IUserMountCache */
106
	private $userMountCache;
107
108
	/** @var IManager */
109
	private $encryptionManager;
110
111
112
	/**
113
	 * @param string $appName
114
	 * @param IRequest $request
115
	 * @param IUserManager $userManager
116
	 * @param IGroupManager $groupManager
117
	 * @param IUserSession $userSession
118
	 * @param IConfig $config
119
	 * @param bool $isAdmin
120
	 * @param IL10N $l10n
121
	 * @param ILogger $log
122
	 * @param IMailer $mailer
123
	 * @param IURLGenerator $urlGenerator
124
	 * @param IAppManager $appManager
125
	 * @param IAvatarManager $avatarManager
126
	 * @param AccountManager $accountManager
127
	 * @param ISecureRandom $secureRandom
128
	 * @param NewUserMailHelper $newUserMailHelper
129
	 * @param ITimeFactory $timeFactory
130
	 * @param ICrypto $crypto
131
	 * @param Manager $keyManager
132
	 * @param IJobList $jobList
133
	 * @param IUserMountCache $userMountCache
134
	 * @param IManager $encryptionManager
135
	 */
136
	public function __construct($appName,
137
								IRequest $request,
138
								IUserManager $userManager,
139
								IGroupManager $groupManager,
140
								IUserSession $userSession,
141
								IConfig $config,
142
								$isAdmin,
143
								IL10N $l10n,
144
								ILogger $log,
145
								IMailer $mailer,
146
								IURLGenerator $urlGenerator,
0 ignored issues
show
Unused Code introduced by
The parameter $urlGenerator is not used and could be removed.

This check looks from parameters that have been defined for a function or method, but which are not used in the method body.

Loading history...
147
								IAppManager $appManager,
148
								IAvatarManager $avatarManager,
149
								AccountManager $accountManager,
150
								ISecureRandom $secureRandom,
151
								NewUserMailHelper $newUserMailHelper,
152
								ITimeFactory $timeFactory,
153
								ICrypto $crypto,
154
								Manager $keyManager,
155
								IJobList $jobList,
156
								IUserMountCache $userMountCache,
157
								IManager $encryptionManager) {
158
		parent::__construct($appName, $request);
159
		$this->userManager = $userManager;
160
		$this->groupManager = $groupManager;
161
		$this->userSession = $userSession;
162
		$this->config = $config;
163
		$this->isAdmin = $isAdmin;
164
		$this->l10n = $l10n;
165
		$this->log = $log;
166
		$this->mailer = $mailer;
167
		$this->appManager = $appManager;
168
		$this->avatarManager = $avatarManager;
169
		$this->accountManager = $accountManager;
170
		$this->secureRandom = $secureRandom;
171
		$this->newUserMailHelper = $newUserMailHelper;
172
		$this->timeFactory = $timeFactory;
173
		$this->crypto = $crypto;
174
		$this->keyManager = $keyManager;
175
		$this->jobList = $jobList;
176
		$this->userMountCache = $userMountCache;
177
		$this->encryptionManager = $encryptionManager;
178
179
		// check for encryption state - TODO see formatUserForIndex
180
		$this->isEncryptionAppEnabled = $appManager->isEnabledForUser('encryption');
181
		if ($this->isEncryptionAppEnabled) {
182
			// putting this directly in empty is possible in PHP 5.5+
183
			$result = $config->getAppValue('encryption', 'recoveryAdminEnabled', 0);
184
			$this->isRestoreEnabled = !empty($result);
185
		}
186
	}
187
188
	/**
189
	 * @param IUser $user
190
	 * @param array $userGroups
191
	 * @return array
192
	 */
193
	private function formatUserForIndex(IUser $user, array $userGroups = null) {
194
195
		// TODO: eliminate this encryption specific code below and somehow
196
		// hook in additional user info from other apps
197
198
		// recovery isn't possible if admin or user has it disabled and encryption
199
		// is enabled - so we eliminate the else paths in the conditional tree
200
		// below
201
		$restorePossible = false;
202
203
		if ($this->isEncryptionAppEnabled) {
204
			if ($this->isRestoreEnabled) {
205
				// check for the users recovery setting
206
				$recoveryMode = $this->config->getUserValue($user->getUID(), 'encryption', 'recoveryEnabled', '0');
207
				// method call inside empty is possible with PHP 5.5+
208
				$recoveryModeEnabled = !empty($recoveryMode);
209
				if ($recoveryModeEnabled) {
210
					// user also has recovery mode enabled
211
					$restorePossible = true;
212
				}
213
			} else {
214
				$modules = $this->encryptionManager->getEncryptionModules();
215
				$restorePossible = true;
216
				foreach ($modules as $id => $module) {
217
					/* @var IEncryptionModule $instance */
218
					$instance = call_user_func($module['callback']);
219
					if ($instance->needDetailedAccessList()) {
220
						$restorePossible = false;
221
						break;
222
					}
223
				}
224
			}
225
		} else {
226
			// recovery is possible if encryption is disabled (plain files are
227
			// available)
228
			$restorePossible = true;
229
		}
230
231
		$subAdminGroups = $this->groupManager->getSubAdmin()->getSubAdminsGroups($user);
232
		foreach ($subAdminGroups as $key => $subAdminGroup) {
233
			$subAdminGroups[$key] = $subAdminGroup->getGID();
234
		}
235
236
		$displayName = $user->getEMailAddress();
237
		if (is_null($displayName)) {
238
			$displayName = '';
239
		}
240
241
		$avatarAvailable = false;
242
		try {
243
			$avatarAvailable = $this->avatarManager->getAvatar($user->getUID())->exists();
244
		} catch (\Exception $e) {
245
			//No avatar yet
246
		}
247
248
		return [
249
			'name' => $user->getUID(),
250
			'displayname' => $user->getDisplayName(),
251
			'groups' => (empty($userGroups)) ? $this->groupManager->getUserGroupIds($user) : $userGroups,
252
			'subadmin' => $subAdminGroups,
253
			'quota' => $user->getQuota(),
254
			'quota_bytes' => Util::computerFileSize($user->getQuota()),
255
			'storageLocation' => $user->getHome(),
256
			'lastLogin' => $user->getLastLogin() * 1000,
257
			'backend' => $user->getBackendClassName(),
258
			'email' => $displayName,
259
			'isRestoreDisabled' => !$restorePossible,
260
			'isAvatarAvailable' => $avatarAvailable,
261
			'isEnabled' => $user->isEnabled(),
262
		];
263
	}
264
265
	/**
266
	 * @param array $userIDs Array with schema [$uid => $displayName]
267
	 * @return IUser[]
268
	 */
269
	private function getUsersForUID(array $userIDs) {
270
		$users = [];
271
		foreach ($userIDs as $uid => $displayName) {
272
			$users[$uid] = $this->userManager->get($uid);
273
		}
274
		return $users;
275
	}
276
277
	/**
278
	 * @NoAdminRequired
279
	 *
280
	 * @param int $offset
281
	 * @param int $limit
282
	 * @param string $gid GID to filter for
283
	 * @param string $pattern Pattern to search for in the username
284
	 * @param string $backend Backend to filter for (class-name)
285
	 * @return DataResponse
286
	 *
287
	 * TODO: Tidy up and write unit tests - code is mainly static method calls
288
	 */
289
	public function index($offset = 0, $limit = 10, $gid = '', $pattern = '', $backend = '') {
290
		// Remove backends
291
		if (!empty($backend)) {
292
			$activeBackends = $this->userManager->getBackends();
293
			$this->userManager->clearBackends();
294
			foreach ($activeBackends as $singleActiveBackend) {
295
				if ($backend === get_class($singleActiveBackend)) {
296
					$this->userManager->registerBackend($singleActiveBackend);
297
					break;
298
				}
299
			}
300
		}
301
302
		$userObjects = [];
303
		$users = [];
304
		if ($this->isAdmin) {
305
			if ($gid !== '' && $gid !== '_disabledUsers') {
306
				$batch = $this->getUsersForUID($this->groupManager->displayNamesInGroup($gid, $pattern, $limit, $offset));
307
			} else {
308
				$batch = $this->userManager->search($pattern, $limit, $offset);
309
			}
310
311
			foreach ($batch as $user) {
312 View Code Duplication
				if (($gid !== '_disabledUsers' && $user->isEnabled()) ||
313
					($gid === '_disabledUsers' && !$user->isEnabled())
314
				) {
315
					$userObjects[] = $user;
316
					$users[] = $this->formatUserForIndex($user);
317
				}
318
			}
319
320
		} else {
321
			$subAdminOfGroups = $this->groupManager->getSubAdmin()->getSubAdminsGroups($this->userSession->getUser());
322
			// New class returns IGroup[] so convert back
323
			$gids = [];
324
			foreach ($subAdminOfGroups as $group) {
325
				$gids[] = $group->getGID();
326
			}
327
			$subAdminOfGroups = $gids;
328
329
			// Set the $gid parameter to an empty value if the subadmin has no rights to access a specific group
330
			if ($gid !== '' && $gid !== '_disabledUsers' && !in_array($gid, $subAdminOfGroups)) {
331
				$gid = '';
332
			}
333
334
			// Batch all groups the user is subadmin of when a group is specified
335
			$batch = [];
336
			if ($gid === '') {
337
				foreach ($subAdminOfGroups as $group) {
338
					$groupUsers = $this->groupManager->displayNamesInGroup($group, $pattern, $limit, $offset);
339
340
					foreach ($groupUsers as $uid => $displayName) {
341
						$batch[$uid] = $displayName;
342
					}
343
				}
344
			} else {
345
				$batch = $this->groupManager->displayNamesInGroup($gid, $pattern, $limit, $offset);
346
			}
347
			$batch = $this->getUsersForUID($batch);
348
349
			foreach ($batch as $user) {
350
				// Only add the groups, this user is a subadmin of
351
				$userGroups = array_values(array_intersect(
352
					$this->groupManager->getUserGroupIds($user),
353
					$subAdminOfGroups
354
				));
355 View Code Duplication
				if (($gid !== '_disabledUsers' && $user->isEnabled()) ||
356
					($gid === '_disabledUsers' && !$user->isEnabled())
357
				) {
358
					$userObjects[] = $user;
359
					$users[] = $this->formatUserForIndex($user, $userGroups);
360
				}
361
			}
362
		}
363
364
		$usedSpace = $this->userMountCache->getUsedSpaceForUsers($userObjects);
365
366
		foreach ($users as &$userData) {
367
			$userData['size'] = isset($usedSpace[$userData['name']]) ? $usedSpace[$userData['name']] : 0;
368
		}
369
370
		return new DataResponse($users);
371
	}
372
373
	/**
374
	 * @NoAdminRequired
375
	 * @PasswordConfirmationRequired
376
	 *
377
	 * @param string $username
378
	 * @param string $password
379
	 * @param array $groups
380
	 * @param string $email
381
	 * @return DataResponse
382
	 */
383
	public function create($username, $password, array $groups = [], $email = '') {
384
		if ($email !== '' && !$this->mailer->validateMailAddress($email)) {
385
			return new DataResponse(
386
				[
387
					'message' => (string)$this->l10n->t('Invalid mail address')
388
				],
389
				Http::STATUS_UNPROCESSABLE_ENTITY
390
			);
391
		}
392
393
		$currentUser = $this->userSession->getUser();
394
395
		if (!$this->isAdmin) {
396
			if (!empty($groups)) {
397
				foreach ($groups as $key => $group) {
398
					$groupObject = $this->groupManager->get($group);
399
					if ($groupObject === null) {
400
						unset($groups[$key]);
401
						continue;
402
					}
403
404
					if (!$this->groupManager->getSubAdmin()->isSubAdminofGroup($currentUser, $groupObject)) {
405
						unset($groups[$key]);
406
					}
407
				}
408
			}
409
410
			if (empty($groups)) {
411
				return new DataResponse(
412
					[
413
						'message' => $this->l10n->t('No valid group selected'),
414
					],
415
					Http::STATUS_FORBIDDEN
416
				);
417
			}
418
		}
419
420
		if ($this->userManager->userExists($username)) {
421
			return new DataResponse(
422
				[
423
					'message' => (string)$this->l10n->t('A user with that name already exists.')
424
				],
425
				Http::STATUS_CONFLICT
426
			);
427
		}
428
429
		$generatePasswordResetToken = false;
430
		if ($password === '') {
431
			if ($email === '') {
432
				return new DataResponse(
433
					[
434
						'message' => (string)$this->l10n->t('To send a password link to the user an email address is required.')
435
					],
436
					Http::STATUS_UNPROCESSABLE_ENTITY
437
				);
438
			}
439
440
			$password = $this->secureRandom->generate(32);
441
			$generatePasswordResetToken = true;
442
		}
443
444
		try {
445
			$user = $this->userManager->createUser($username, $password);
446
		} catch (\Exception $exception) {
447
			$message = $exception->getMessage();
448
			if ($exception instanceof HintException && $exception->getHint()) {
449
				$message = $exception->getHint();
450
			}
451
			if (!$message) {
452
				$message = $this->l10n->t('Unable to create user.');
453
			}
454
			return new DataResponse(
455
				[
456
					'message' => (string)$message,
457
				],
458
				Http::STATUS_FORBIDDEN
459
			);
460
		}
461
462
		if ($user instanceof IUser) {
463
			if ($groups !== null) {
464
				foreach ($groups as $groupName) {
465
					$group = $this->groupManager->get($groupName);
466
467
					if (empty($group)) {
468
						$group = $this->groupManager->createGroup($groupName);
469
					}
470
					$group->addUser($user);
471
				}
472
			}
473
			/**
474
			 * Send new user mail only if a mail is set
475
			 */
476
			if ($email !== '') {
477
				$user->setEMailAddress($email);
478
				try {
479
					$emailTemplate = $this->newUserMailHelper->generateTemplate($user, $generatePasswordResetToken);
480
					$this->newUserMailHelper->sendMail($user, $emailTemplate);
481
				} catch (\Exception $e) {
482
					$this->log->error("Can't send new user mail to $email: " . $e->getMessage(), ['app' => 'settings']);
483
				}
484
			}
485
			// fetch users groups
486
			$userGroups = $this->groupManager->getUserGroupIds($user);
487
488
			return new DataResponse(
489
				$this->formatUserForIndex($user, $userGroups),
490
				Http::STATUS_CREATED
491
			);
492
		}
493
494
		return new DataResponse(
495
			[
496
				'message' => (string)$this->l10n->t('Unable to create user.')
497
			],
498
			Http::STATUS_FORBIDDEN
499
		);
500
501
	}
502
503
	/**
504
	 * @NoAdminRequired
505
	 * @PasswordConfirmationRequired
506
	 *
507
	 * @param string $id
508
	 * @return DataResponse
509
	 */
510
	public function destroy($id) {
511
		$userId = $this->userSession->getUser()->getUID();
512
		$user = $this->userManager->get($id);
513
514
		if ($userId === $id) {
515
			return new DataResponse(
516
				[
517
					'status' => 'error',
518
					'data' => [
519
						'message' => (string)$this->l10n->t('Unable to delete user.')
520
					]
521
				],
522
				Http::STATUS_FORBIDDEN
523
			);
524
		}
525
526 View Code Duplication
		if (!$this->isAdmin && !$this->groupManager->getSubAdmin()->isUserAccessible($this->userSession->getUser(), $user)) {
527
			return new DataResponse(
528
				[
529
					'status' => 'error',
530
					'data' => [
531
						'message' => (string)$this->l10n->t('Authentication error')
532
					]
533
				],
534
				Http::STATUS_FORBIDDEN
535
			);
536
		}
537
538
		if ($user) {
539
			if ($user->delete()) {
540
				return new DataResponse(
541
					[
542
						'status' => 'success',
543
						'data' => [
544
							'username' => $id
545
						]
546
					],
547
					Http::STATUS_NO_CONTENT
548
				);
549
			}
550
		}
551
552
		return new DataResponse(
553
			[
554
				'status' => 'error',
555
				'data' => [
556
					'message' => (string)$this->l10n->t('Unable to delete user.')
557
				]
558
			],
559
			Http::STATUS_FORBIDDEN
560
		);
561
	}
562
563
	/**
564
	 * @NoAdminRequired
565
	 *
566
	 * @param string $id
567
	 * @param int $enabled
568
	 * @return DataResponse
569
	 */
570
	public function setEnabled($id, $enabled) {
571
		$enabled = (bool)$enabled;
572
		if ($enabled) {
573
			$errorMsgGeneral = (string)$this->l10n->t('Error while enabling user.');
574
		} else {
575
			$errorMsgGeneral = (string)$this->l10n->t('Error while disabling user.');
576
		}
577
578
		$userId = $this->userSession->getUser()->getUID();
579
		$user = $this->userManager->get($id);
580
581 View Code Duplication
		if ($userId === $id) {
582
			return new DataResponse(
583
				[
584
					'status' => 'error',
585
					'data' => [
586
						'message' => $errorMsgGeneral
587
					]
588
				], Http::STATUS_FORBIDDEN
589
			);
590
		}
591
592
		if ($user) {
593 View Code Duplication
			if (!$this->isAdmin && !$this->groupManager->getSubAdmin()->isUserAccessible($this->userSession->getUser(), $user)) {
594
				return new DataResponse(
595
					[
596
						'status' => 'error',
597
						'data' => [
598
							'message' => (string)$this->l10n->t('Authentication error')
599
						]
600
					],
601
					Http::STATUS_FORBIDDEN
602
				);
603
			}
604
605
			$user->setEnabled($enabled);
606
			return new DataResponse(
607
				[
608
					'status' => 'success',
609
					'data' => [
610
						'username' => $id,
611
						'enabled' => $enabled
612
					]
613
				]
614
			);
615 View Code Duplication
		} else {
616
			return new DataResponse(
617
				[
618
					'status' => 'error',
619
					'data' => [
620
						'message' => $errorMsgGeneral
621
					]
622
				],
623
				Http::STATUS_FORBIDDEN
624
			);
625
		}
626
627
	}
628
629
	/**
630
	 * Set the mail address of a user
631
	 *
632
	 * @NoAdminRequired
633
	 * @NoSubadminRequired
634
	 * @PasswordConfirmationRequired
635
	 *
636
	 * @param string $account
637
	 * @param bool $onlyVerificationCode only return verification code without updating the data
638
	 * @return DataResponse
639
	 */
640
	public function getVerificationCode($account, $onlyVerificationCode) {
641
642
		$user = $this->userSession->getUser();
643
644
		if ($user === null) {
645
			return new DataResponse([], Http::STATUS_BAD_REQUEST);
646
		}
647
648
		$accountData = $this->accountManager->getUser($user);
649
		$cloudId = $user->getCloudId();
650
		$message = "Use my Federated Cloud ID to share with me: " . $cloudId;
651
		$signature = $this->signMessage($user, $message);
652
653
		$code = $message . ' ' . $signature;
654
		$codeMd5 = $message . ' ' . md5($signature);
655
656
		switch ($account) {
657 View Code Duplication
			case 'verify-twitter':
658
				$accountData[AccountManager::PROPERTY_TWITTER]['verified'] = AccountManager::VERIFICATION_IN_PROGRESS;
659
				$msg = $this->l10n->t('In order to verify your Twitter account, post the following tweet on Twitter (please make sure to post it without any line breaks):');
660
				$code = $codeMd5;
661
				$type = AccountManager::PROPERTY_TWITTER;
662
				$data = $accountData[AccountManager::PROPERTY_TWITTER]['value'];
663
				$accountData[AccountManager::PROPERTY_TWITTER]['signature'] = $signature;
664
				break;
665 View Code Duplication
			case 'verify-website':
666
				$accountData[AccountManager::PROPERTY_WEBSITE]['verified'] = AccountManager::VERIFICATION_IN_PROGRESS;
667
				$msg = $this->l10n->t('In order to verify your Website, store the following content in your web-root at \'.well-known/CloudIdVerificationCode.txt\' (please make sure that the complete text is in one line):');
668
				$type = AccountManager::PROPERTY_WEBSITE;
669
				$data = $accountData[AccountManager::PROPERTY_WEBSITE]['value'];
670
				$accountData[AccountManager::PROPERTY_WEBSITE]['signature'] = $signature;
671
				break;
672
			default:
673
				return new DataResponse([], Http::STATUS_BAD_REQUEST);
674
		}
675
676
		if ($onlyVerificationCode === false) {
677
			$this->accountManager->updateUser($user, $accountData);
678
679
			$this->jobList->add('OC\Settings\BackgroundJobs\VerifyUserData',
680
				[
681
					'verificationCode' => $code,
682
					'data' => $data,
683
					'type' => $type,
684
					'uid' => $user->getUID(),
685
					'try' => 0,
686
					'lastRun' => $this->getCurrentTime()
687
				]
688
			);
689
		}
690
691
		return new DataResponse(['msg' => $msg, 'code' => $code]);
692
	}
693
694
	/**
695
	 * get current timestamp
696
	 *
697
	 * @return int
698
	 */
699
	protected function getCurrentTime() {
700
		return time();
701
	}
702
703
	/**
704
	 * sign message with users private key
705
	 *
706
	 * @param IUser $user
707
	 * @param string $message
708
	 *
709
	 * @return string base64 encoded signature
710
	 */
711
	protected function signMessage(IUser $user, $message) {
712
		$privateKey = $this->keyManager->getKey($user)->getPrivate();
713
		openssl_sign(json_encode($message), $signature, $privateKey, OPENSSL_ALGO_SHA512);
714
		$signatureBase64 = base64_encode($signature);
715
716
		return $signatureBase64;
717
	}
718
719
	/**
720
	 * @NoAdminRequired
721
	 * @NoSubadminRequired
722
	 * @PasswordConfirmationRequired
723
	 *
724
	 * @param string $avatarScope
725
	 * @param string $displayname
726
	 * @param string $displaynameScope
727
	 * @param string $phone
728
	 * @param string $phoneScope
729
	 * @param string $email
730
	 * @param string $emailScope
731
	 * @param string $website
732
	 * @param string $websiteScope
733
	 * @param string $address
734
	 * @param string $addressScope
735
	 * @param string $twitter
736
	 * @param string $twitterScope
737
	 * @return DataResponse
738
	 */
739
	public function setUserSettings($avatarScope,
740
									$displayname,
741
									$displaynameScope,
742
									$phone,
743
									$phoneScope,
744
									$email,
745
									$emailScope,
746
									$website,
747
									$websiteScope,
748
									$address,
749
									$addressScope,
750
									$twitter,
751
									$twitterScope
752
	) {
753
754
		if (!empty($email) && !$this->mailer->validateMailAddress($email)) {
755
			return new DataResponse(
756
				[
757
					'status' => 'error',
758
					'data' => [
759
						'message' => (string)$this->l10n->t('Invalid mail address')
760
					]
761
				],
762
				Http::STATUS_UNPROCESSABLE_ENTITY
763
			);
764
		}
765
766
		$user = $this->userSession->getUser();
767
768
		$data = $this->accountManager->getUser($user);
0 ignored issues
show
Bug introduced by
It seems like $user defined by $this->userSession->getUser() on line 766 can be null; however, OC\Accounts\AccountManager::getUser() does not accept null, maybe add an additional type check?

Unless you are absolutely sure that the expression can never be null because of other conditions, we strongly recommend to add an additional type check to your code:

/** @return stdClass|null */
function mayReturnNull() { }

function doesNotAcceptNull(stdClass $x) { }

// With potential error.
function withoutCheck() {
    $x = mayReturnNull();
    doesNotAcceptNull($x); // Potential error here.
}

// Safe - Alternative 1
function withCheck1() {
    $x = mayReturnNull();
    if ( ! $x instanceof stdClass) {
        throw new \LogicException('$x must be defined.');
    }
    doesNotAcceptNull($x);
}

// Safe - Alternative 2
function withCheck2() {
    $x = mayReturnNull();
    if ($x instanceof stdClass) {
        doesNotAcceptNull($x);
    }
}
Loading history...
769
770
		$data[AccountManager::PROPERTY_AVATAR] = ['scope' => $avatarScope];
771
		if ($this->config->getSystemValue('allow_user_to_change_display_name', true) !== false) {
772
			$data[AccountManager::PROPERTY_DISPLAYNAME] = ['value' => $displayname, 'scope' => $displaynameScope];
773
			$data[AccountManager::PROPERTY_EMAIL] = ['value' => $email, 'scope' => $emailScope];
774
		}
775
776
		if ($this->appManager->isEnabledForUser('federatedfilesharing')) {
777
			$federatedFileSharing = new \OCA\FederatedFileSharing\AppInfo\Application();
778
			$shareProvider = $federatedFileSharing->getFederatedShareProvider();
779
			if ($shareProvider->isLookupServerUploadEnabled()) {
780
				$data[AccountManager::PROPERTY_WEBSITE] = ['value' => $website, 'scope' => $websiteScope];
781
				$data[AccountManager::PROPERTY_ADDRESS] = ['value' => $address, 'scope' => $addressScope];
782
				$data[AccountManager::PROPERTY_PHONE] = ['value' => $phone, 'scope' => $phoneScope];
783
				$data[AccountManager::PROPERTY_TWITTER] = ['value' => $twitter, 'scope' => $twitterScope];
784
			}
785
		}
786
787
		try {
788
			$this->saveUserSettings($user, $data);
0 ignored issues
show
Bug introduced by
It seems like $user defined by $this->userSession->getUser() on line 766 can be null; however, OC\Settings\Controller\U...ler::saveUserSettings() does not accept null, maybe add an additional type check?

Unless you are absolutely sure that the expression can never be null because of other conditions, we strongly recommend to add an additional type check to your code:

/** @return stdClass|null */
function mayReturnNull() { }

function doesNotAcceptNull(stdClass $x) { }

// With potential error.
function withoutCheck() {
    $x = mayReturnNull();
    doesNotAcceptNull($x); // Potential error here.
}

// Safe - Alternative 1
function withCheck1() {
    $x = mayReturnNull();
    if ( ! $x instanceof stdClass) {
        throw new \LogicException('$x must be defined.');
    }
    doesNotAcceptNull($x);
}

// Safe - Alternative 2
function withCheck2() {
    $x = mayReturnNull();
    if ($x instanceof stdClass) {
        doesNotAcceptNull($x);
    }
}
Loading history...
789
			return new DataResponse(
790
				[
791
					'status' => 'success',
792
					'data' => [
793
						'userId' => $user->getUID(),
794
						'avatarScope' => $data[AccountManager::PROPERTY_AVATAR]['scope'],
795
						'displayname' => $data[AccountManager::PROPERTY_DISPLAYNAME]['value'],
796
						'displaynameScope' => $data[AccountManager::PROPERTY_DISPLAYNAME]['scope'],
797
						'email' => $data[AccountManager::PROPERTY_EMAIL]['value'],
798
						'emailScope' => $data[AccountManager::PROPERTY_EMAIL]['scope'],
799
						'website' => $data[AccountManager::PROPERTY_WEBSITE]['value'],
800
						'websiteScope' => $data[AccountManager::PROPERTY_WEBSITE]['scope'],
801
						'address' => $data[AccountManager::PROPERTY_ADDRESS]['value'],
802
						'addressScope' => $data[AccountManager::PROPERTY_ADDRESS]['scope'],
803
						'message' => (string)$this->l10n->t('Settings saved')
804
					]
805
				],
806
				Http::STATUS_OK
807
			);
808
		} catch (ForbiddenException $e) {
809
			return new DataResponse([
810
				'status' => 'error',
811
				'data' => [
812
					'message' => $e->getMessage()
813
				],
814
			]);
815
		}
816
817
	}
818
819
820
	/**
821
	 * update account manager with new user data
822
	 *
823
	 * @param IUser $user
824
	 * @param array $data
825
	 * @throws ForbiddenException
826
	 */
827
	protected function saveUserSettings(IUser $user, $data) {
828
829
		// keep the user back-end up-to-date with the latest display name and email
830
		// address
831
		$oldDisplayName = $user->getDisplayName();
832
		$oldDisplayName = is_null($oldDisplayName) ? '' : $oldDisplayName;
833 View Code Duplication
		if (isset($data[AccountManager::PROPERTY_DISPLAYNAME]['value'])
834
			&& $oldDisplayName !== $data[AccountManager::PROPERTY_DISPLAYNAME]['value']
835
		) {
836
			$result = $user->setDisplayName($data[AccountManager::PROPERTY_DISPLAYNAME]['value']);
837
			if ($result === false) {
838
				throw new ForbiddenException($this->l10n->t('Unable to change full name'));
839
			}
840
		}
841
842
		$oldEmailAddress = $user->getEMailAddress();
843
		$oldEmailAddress = is_null($oldEmailAddress) ? '' : $oldEmailAddress;
844 View Code Duplication
		if (isset($data[AccountManager::PROPERTY_EMAIL]['value'])
845
			&& $oldEmailAddress !== $data[AccountManager::PROPERTY_EMAIL]['value']
846
		) {
847
			// this is the only permission a backend provides and is also used
848
			// for the permission of setting a email address
849
			if (!$user->canChangeDisplayName()) {
850
				throw new ForbiddenException($this->l10n->t('Unable to change email address'));
851
			}
852
			$user->setEMailAddress($data[AccountManager::PROPERTY_EMAIL]['value']);
853
		}
854
855
		$this->accountManager->updateUser($user, $data);
856
	}
857
858
	/**
859
	 * Count all unique users visible for the current admin/subadmin.
860
	 *
861
	 * @NoAdminRequired
862
	 *
863
	 * @return DataResponse
864
	 */
865
	public function stats() {
866
		$userCount = 0;
867
		if ($this->isAdmin) {
868
			$countByBackend = $this->userManager->countUsers();
869
870
			if (!empty($countByBackend)) {
871
				foreach ($countByBackend as $count) {
872
					$userCount += $count;
873
				}
874
			}
875
		} else {
876
			$groups = $this->groupManager->getSubAdmin()->getSubAdminsGroups($this->userSession->getUser());
877
878
			$uniqueUsers = [];
879
			foreach ($groups as $group) {
880
				foreach ($group->getUsers() as $uid => $displayName) {
881
					$uniqueUsers[$uid] = true;
882
				}
883
			}
884
885
			$userCount = count($uniqueUsers);
886
		}
887
888
		return new DataResponse(
889
			[
890
				'totalUsers' => $userCount
891
			]
892
		);
893
	}
894
895
896
	/**
897
	 * Set the displayName of a user
898
	 *
899
	 * @NoAdminRequired
900
	 * @NoSubadminRequired
901
	 * @PasswordConfirmationRequired
902
	 * @todo merge into saveUserSettings
903
	 *
904
	 * @param string $username
905
	 * @param string $displayName
906
	 * @return DataResponse
907
	 */
908
	public function setDisplayName($username, $displayName) {
909
		$currentUser = $this->userSession->getUser();
910
		$user = $this->userManager->get($username);
911
912
		if ($user === null ||
913
			!$user->canChangeDisplayName() ||
914
			(
915
				!$this->groupManager->isAdmin($currentUser->getUID()) &&
916
				!$this->groupManager->getSubAdmin()->isUserAccessible($currentUser, $user) &&
917
				$currentUser->getUID() !== $username
918
919
			)
920
		) {
921
			return new DataResponse([
922
				'status' => 'error',
923
				'data' => [
924
					'message' => $this->l10n->t('Authentication error'),
925
				],
926
			]);
927
		}
928
929
		$userData = $this->accountManager->getUser($user);
930
		$userData[AccountManager::PROPERTY_DISPLAYNAME]['value'] = $displayName;
931
932
933
		try {
934
			$this->saveUserSettings($user, $userData);
935
			return new DataResponse([
936
				'status' => 'success',
937
				'data' => [
938
					'message' => $this->l10n->t('Your full name has been changed.'),
939
					'username' => $username,
940
					'displayName' => $displayName,
941
				],
942
			]);
943
		} catch (ForbiddenException $e) {
944
			return new DataResponse([
945
				'status' => 'error',
946
				'data' => [
947
					'message' => $e->getMessage(),
948
					'displayName' => $user->getDisplayName(),
949
				],
950
			]);
951
		}
952
	}
953
954
	/**
955
	 * Set the mail address of a user
956
	 *
957
	 * @NoAdminRequired
958
	 * @NoSubadminRequired
959
	 * @PasswordConfirmationRequired
960
	 *
961
	 * @param string $id
962
	 * @param string $mailAddress
963
	 * @return DataResponse
964
	 */
965
	public function setEMailAddress($id, $mailAddress) {
966
		$user = $this->userManager->get($id);
967
		if (!$this->isAdmin
968
			&& !$this->groupManager->getSubAdmin()->isUserAccessible($this->userSession->getUser(), $user)
969
		) {
970
			return new DataResponse(
971
				[
972
					'status' => 'error',
973
					'data' => [
974
						'message' => (string)$this->l10n->t('Forbidden')
975
					]
976
				],
977
				Http::STATUS_FORBIDDEN
978
			);
979
		}
980
981
		if ($mailAddress !== '' && !$this->mailer->validateMailAddress($mailAddress)) {
982
			return new DataResponse(
983
				[
984
					'status' => 'error',
985
					'data' => [
986
						'message' => (string)$this->l10n->t('Invalid mail address')
987
					]
988
				],
989
				Http::STATUS_UNPROCESSABLE_ENTITY
990
			);
991
		}
992
993
		if (!$user) {
994
			return new DataResponse(
995
				[
996
					'status' => 'error',
997
					'data' => [
998
						'message' => (string)$this->l10n->t('Invalid user')
999
					]
1000
				],
1001
				Http::STATUS_UNPROCESSABLE_ENTITY
1002
			);
1003
		}
1004
		// this is the only permission a backend provides and is also used
1005
		// for the permission of setting a email address
1006
		if (!$user->canChangeDisplayName()) {
1007
			return new DataResponse(
1008
				[
1009
					'status' => 'error',
1010
					'data' => [
1011
						'message' => (string)$this->l10n->t('Unable to change mail address')
1012
					]
1013
				],
1014
				Http::STATUS_FORBIDDEN
1015
			);
1016
		}
1017
1018
		$userData = $this->accountManager->getUser($user);
1019
		$userData[AccountManager::PROPERTY_EMAIL]['value'] = $mailAddress;
1020
1021
		try {
1022
			$this->saveUserSettings($user, $userData);
1023
			return new DataResponse(
1024
				[
1025
					'status' => 'success',
1026
					'data' => [
1027
						'username' => $id,
1028
						'mailAddress' => $mailAddress,
1029
						'message' => (string)$this->l10n->t('Email saved')
1030
					]
1031
				],
1032
				Http::STATUS_OK
1033
			);
1034
		} catch (ForbiddenException $e) {
1035
			return new DataResponse([
1036
				'status' => 'error',
1037
				'data' => [
1038
					'message' => $e->getMessage()
1039
				],
1040
			]);
1041
		}
1042
	}
1043
1044
}
1045