Passed
Push — master ( 0247f2...f657f0 )
by Morris
13:19 queued 11s
created
apps/settings/lib/Controller/UsersController.php 1 patch
Indentation   +471 added lines, -471 removed lines patch added patch discarded remove patch
@@ -66,475 +66,475 @@
 block discarded – undo
66 66
 use function in_array;
67 67
 
68 68
 class UsersController extends Controller {
69
-	/** @var UserManager */
70
-	private $userManager;
71
-	/** @var GroupManager */
72
-	private $groupManager;
73
-	/** @var IUserSession */
74
-	private $userSession;
75
-	/** @var IConfig */
76
-	private $config;
77
-	/** @var bool */
78
-	private $isAdmin;
79
-	/** @var IL10N */
80
-	private $l10n;
81
-	/** @var IMailer */
82
-	private $mailer;
83
-	/** @var Factory */
84
-	private $l10nFactory;
85
-	/** @var IAppManager */
86
-	private $appManager;
87
-	/** @var AccountManager */
88
-	private $accountManager;
89
-	/** @var Manager */
90
-	private $keyManager;
91
-	/** @var IJobList */
92
-	private $jobList;
93
-	/** @var IManager */
94
-	private $encryptionManager;
95
-	/** @var IEventDispatcher */
96
-	private $dispatcher;
97
-
98
-
99
-	public function __construct(
100
-		string $appName,
101
-		IRequest $request,
102
-		IUserManager $userManager,
103
-		IGroupManager $groupManager,
104
-		IUserSession $userSession,
105
-		IConfig $config,
106
-		bool $isAdmin,
107
-		IL10N $l10n,
108
-		IMailer $mailer,
109
-		IFactory $l10nFactory,
110
-		IAppManager $appManager,
111
-		AccountManager $accountManager,
112
-		Manager $keyManager,
113
-		IJobList $jobList,
114
-		IManager $encryptionManager,
115
-		IEventDispatcher $dispatcher
116
-	) {
117
-		parent::__construct($appName, $request);
118
-		$this->userManager = $userManager;
119
-		$this->groupManager = $groupManager;
120
-		$this->userSession = $userSession;
121
-		$this->config = $config;
122
-		$this->isAdmin = $isAdmin;
123
-		$this->l10n = $l10n;
124
-		$this->mailer = $mailer;
125
-		$this->l10nFactory = $l10nFactory;
126
-		$this->appManager = $appManager;
127
-		$this->accountManager = $accountManager;
128
-		$this->keyManager = $keyManager;
129
-		$this->jobList = $jobList;
130
-		$this->encryptionManager = $encryptionManager;
131
-		$this->dispatcher = $dispatcher;
132
-	}
133
-
134
-
135
-	/**
136
-	 * @NoCSRFRequired
137
-	 * @NoAdminRequired
138
-	 *
139
-	 * Display users list template
140
-	 *
141
-	 * @return TemplateResponse
142
-	 */
143
-	public function usersListByGroup() {
144
-		return $this->usersList();
145
-	}
146
-
147
-	/**
148
-	 * @NoCSRFRequired
149
-	 * @NoAdminRequired
150
-	 *
151
-	 * Display users list template
152
-	 *
153
-	 * @return TemplateResponse
154
-	 */
155
-	public function usersList() {
156
-		$user = $this->userSession->getUser();
157
-		$uid = $user->getUID();
158
-
159
-		\OC::$server->getNavigationManager()->setActiveEntry('core_users');
160
-
161
-		/* SORT OPTION: SORT_USERCOUNT or SORT_GROUPNAME */
162
-		$sortGroupsBy = \OC\Group\MetaData::SORT_USERCOUNT;
163
-		$isLDAPUsed = false;
164
-		if ($this->config->getSystemValue('sort_groups_by_name', false)) {
165
-			$sortGroupsBy = \OC\Group\MetaData::SORT_GROUPNAME;
166
-		} else {
167
-			if ($this->appManager->isEnabledForUser('user_ldap')) {
168
-				$isLDAPUsed =
169
-					$this->groupManager->isBackendUsed('\OCA\User_LDAP\Group_Proxy');
170
-				if ($isLDAPUsed) {
171
-					// LDAP user count can be slow, so we sort by group name here
172
-					$sortGroupsBy = \OC\Group\MetaData::SORT_GROUPNAME;
173
-				}
174
-			}
175
-		}
176
-
177
-		$canChangePassword = $this->canAdminChangeUserPasswords();
178
-
179
-		/* GROUPS */
180
-		$groupsInfo = new \OC\Group\MetaData(
181
-			$uid,
182
-			$this->isAdmin,
183
-			$this->groupManager,
184
-			$this->userSession
185
-		);
186
-
187
-		$groupsInfo->setSorting($sortGroupsBy);
188
-		list($adminGroup, $groups) = $groupsInfo->get();
189
-
190
-		if (!$isLDAPUsed && $this->appManager->isEnabledForUser('user_ldap')) {
191
-			$isLDAPUsed = (bool)array_reduce($this->userManager->getBackends(), function ($ldapFound, $backend) {
192
-				return $ldapFound || $backend instanceof User_Proxy;
193
-			});
194
-		}
195
-
196
-		$disabledUsers = -1;
197
-		$userCount = 0;
198
-
199
-		if (!$isLDAPUsed) {
200
-			if ($this->isAdmin) {
201
-				$disabledUsers = $this->userManager->countDisabledUsers();
202
-				$userCount = array_reduce($this->userManager->countUsers(), function ($v, $w) {
203
-					return $v + (int)$w;
204
-				}, 0);
205
-			} else {
206
-				// User is subadmin !
207
-				// Map group list to names to retrieve the countDisabledUsersOfGroups
208
-				$userGroups = $this->groupManager->getUserGroups($user);
209
-				$groupsNames = [];
210
-
211
-				foreach ($groups as $key => $group) {
212
-					// $userCount += (int)$group['usercount'];
213
-					array_push($groupsNames, $group['name']);
214
-					// we prevent subadmins from looking up themselves
215
-					// so we lower the count of the groups he belongs to
216
-					if (array_key_exists($group['id'], $userGroups)) {
217
-						$groups[$key]['usercount']--;
218
-						$userCount -= 1; // we also lower from one the total count
219
-					}
220
-				}
221
-				$userCount += $this->userManager->countUsersOfGroups($groupsInfo->getGroups());
222
-				$disabledUsers = $this->userManager->countDisabledUsersOfGroups($groupsNames);
223
-			}
224
-
225
-			$userCount -= $disabledUsers;
226
-		}
227
-
228
-		$disabledUsersGroup = [
229
-			'id' => 'disabled',
230
-			'name' => 'Disabled users',
231
-			'usercount' => $disabledUsers
232
-		];
233
-
234
-		/* QUOTAS PRESETS */
235
-		$quotaPreset = $this->parseQuotaPreset($this->config->getAppValue('files', 'quota_preset', '1 GB, 5 GB, 10 GB'));
236
-		$defaultQuota = $this->config->getAppValue('files', 'default_quota', 'none');
237
-
238
-		$event = new BeforeTemplateRenderedEvent();
239
-		$this->dispatcher->dispatch('OC\Settings\Users::loadAdditionalScripts', $event);
240
-		$this->dispatcher->dispatchTyped($event);
241
-
242
-		/* LANGUAGES */
243
-		$languages = $this->l10nFactory->getLanguages();
244
-
245
-		/* FINAL DATA */
246
-		$serverData = [];
247
-		// groups
248
-		$serverData['groups'] = array_merge_recursive($adminGroup, [$disabledUsersGroup], $groups);
249
-		// Various data
250
-		$serverData['isAdmin'] = $this->isAdmin;
251
-		$serverData['sortGroups'] = $sortGroupsBy;
252
-		$serverData['quotaPreset'] = $quotaPreset;
253
-		$serverData['userCount'] = $userCount;
254
-		$serverData['languages'] = $languages;
255
-		$serverData['defaultLanguage'] = $this->config->getSystemValue('default_language', 'en');
256
-		$serverData['forceLanguage'] = $this->config->getSystemValue('force_language', false);
257
-		// Settings
258
-		$serverData['defaultQuota'] = $defaultQuota;
259
-		$serverData['canChangePassword'] = $canChangePassword;
260
-		$serverData['newUserGenerateUserID'] = $this->config->getAppValue('core', 'newUser.generateUserID', 'no') === 'yes';
261
-		$serverData['newUserRequireEmail'] = $this->config->getAppValue('core', 'newUser.requireEmail', 'no') === 'yes';
262
-		$serverData['newUserSendEmail'] = $this->config->getAppValue('core', 'newUser.sendEmail', 'yes') === 'yes';
263
-
264
-		return new TemplateResponse('settings', 'settings-vue', ['serverData' => $serverData]);
265
-	}
266
-
267
-	/**
268
-	 * @param string $key
269
-	 * @param string $value
270
-	 *
271
-	 * @return JSONResponse
272
-	 */
273
-	public function setPreference(string $key, string $value): JSONResponse {
274
-		$allowed = ['newUser.sendEmail'];
275
-		if (!in_array($key, $allowed, true)) {
276
-			return new JSONResponse([], Http::STATUS_FORBIDDEN);
277
-		}
278
-
279
-		$this->config->setAppValue('core', $key, $value);
280
-
281
-		return new JSONResponse([]);
282
-	}
283
-
284
-	/**
285
-	 * Parse the app value for quota_present
286
-	 *
287
-	 * @param string $quotaPreset
288
-	 * @return array
289
-	 */
290
-	protected function parseQuotaPreset(string $quotaPreset): array {
291
-		// 1 GB, 5 GB, 10 GB => [1 GB, 5 GB, 10 GB]
292
-		$presets = array_filter(array_map('trim', explode(',', $quotaPreset)));
293
-		// Drop default and none, Make array indexes numerically
294
-		return array_values(array_diff($presets, ['default', 'none']));
295
-	}
296
-
297
-	/**
298
-	 * check if the admin can change the users password
299
-	 *
300
-	 * The admin can change the passwords if:
301
-	 *
302
-	 *   - no encryption module is loaded and encryption is disabled
303
-	 *   - encryption module is loaded but it doesn't require per user keys
304
-	 *
305
-	 * The admin can not change the passwords if:
306
-	 *
307
-	 *   - an encryption module is loaded and it uses per-user keys
308
-	 *   - encryption is enabled but no encryption modules are loaded
309
-	 *
310
-	 * @return bool
311
-	 */
312
-	protected function canAdminChangeUserPasswords() {
313
-		$isEncryptionEnabled = $this->encryptionManager->isEnabled();
314
-		try {
315
-			$noUserSpecificEncryptionKeys = !$this->encryptionManager->getEncryptionModule()->needDetailedAccessList();
316
-			$isEncryptionModuleLoaded = true;
317
-		} catch (ModuleDoesNotExistsException $e) {
318
-			$noUserSpecificEncryptionKeys = true;
319
-			$isEncryptionModuleLoaded = false;
320
-		}
321
-		$canChangePassword = ($isEncryptionModuleLoaded && $noUserSpecificEncryptionKeys)
322
-			|| (!$isEncryptionModuleLoaded && !$isEncryptionEnabled);
323
-
324
-		return $canChangePassword;
325
-	}
326
-
327
-	/**
328
-	 * @NoAdminRequired
329
-	 * @NoSubAdminRequired
330
-	 * @PasswordConfirmationRequired
331
-	 *
332
-	 * @param string $avatarScope
333
-	 * @param string $displayname
334
-	 * @param string $displaynameScope
335
-	 * @param string $phone
336
-	 * @param string $phoneScope
337
-	 * @param string $email
338
-	 * @param string $emailScope
339
-	 * @param string $website
340
-	 * @param string $websiteScope
341
-	 * @param string $address
342
-	 * @param string $addressScope
343
-	 * @param string $twitter
344
-	 * @param string $twitterScope
345
-	 * @return DataResponse
346
-	 */
347
-	public function setUserSettings($avatarScope,
348
-									$displayname,
349
-									$displaynameScope,
350
-									$phone,
351
-									$phoneScope,
352
-									$email,
353
-									$emailScope,
354
-									$website,
355
-									$websiteScope,
356
-									$address,
357
-									$addressScope,
358
-									$twitter,
359
-									$twitterScope
360
-	) {
361
-		$email = strtolower($email);
362
-		if (!empty($email) && !$this->mailer->validateMailAddress($email)) {
363
-			return new DataResponse(
364
-				[
365
-					'status' => 'error',
366
-					'data' => [
367
-						'message' => $this->l10n->t('Invalid mail address')
368
-					]
369
-				],
370
-				Http::STATUS_UNPROCESSABLE_ENTITY
371
-			);
372
-		}
373
-		$user = $this->userSession->getUser();
374
-		$data = $this->accountManager->getUser($user);
375
-		$data[AccountManager::PROPERTY_AVATAR] = ['scope' => $avatarScope];
376
-		if ($this->config->getSystemValue('allow_user_to_change_display_name', true) !== false) {
377
-			$data[AccountManager::PROPERTY_DISPLAYNAME] = ['value' => $displayname, 'scope' => $displaynameScope];
378
-			$data[AccountManager::PROPERTY_EMAIL] = ['value' => $email, 'scope' => $emailScope];
379
-		}
380
-		if ($this->appManager->isEnabledForUser('federatedfilesharing')) {
381
-			$shareProvider = \OC::$server->query(FederatedShareProvider::class);
382
-			if ($shareProvider->isLookupServerUploadEnabled()) {
383
-				$data[AccountManager::PROPERTY_WEBSITE] = ['value' => $website, 'scope' => $websiteScope];
384
-				$data[AccountManager::PROPERTY_ADDRESS] = ['value' => $address, 'scope' => $addressScope];
385
-				$data[AccountManager::PROPERTY_PHONE] = ['value' => $phone, 'scope' => $phoneScope];
386
-				$data[AccountManager::PROPERTY_TWITTER] = ['value' => $twitter, 'scope' => $twitterScope];
387
-			}
388
-		}
389
-		try {
390
-			$this->saveUserSettings($user, $data);
391
-			return new DataResponse(
392
-				[
393
-					'status' => 'success',
394
-					'data' => [
395
-						'userId' => $user->getUID(),
396
-						'avatarScope' => $data[AccountManager::PROPERTY_AVATAR]['scope'],
397
-						'displayname' => $data[AccountManager::PROPERTY_DISPLAYNAME]['value'],
398
-						'displaynameScope' => $data[AccountManager::PROPERTY_DISPLAYNAME]['scope'],
399
-						'email' => $data[AccountManager::PROPERTY_EMAIL]['value'],
400
-						'emailScope' => $data[AccountManager::PROPERTY_EMAIL]['scope'],
401
-						'website' => $data[AccountManager::PROPERTY_WEBSITE]['value'],
402
-						'websiteScope' => $data[AccountManager::PROPERTY_WEBSITE]['scope'],
403
-						'address' => $data[AccountManager::PROPERTY_ADDRESS]['value'],
404
-						'addressScope' => $data[AccountManager::PROPERTY_ADDRESS]['scope'],
405
-						'message' => $this->l10n->t('Settings saved')
406
-					]
407
-				],
408
-				Http::STATUS_OK
409
-			);
410
-		} catch (ForbiddenException $e) {
411
-			return new DataResponse([
412
-				'status' => 'error',
413
-				'data' => [
414
-					'message' => $e->getMessage()
415
-				],
416
-			]);
417
-		}
418
-	}
419
-	/**
420
-	 * update account manager with new user data
421
-	 *
422
-	 * @param IUser $user
423
-	 * @param array $data
424
-	 * @throws ForbiddenException
425
-	 */
426
-	protected function saveUserSettings(IUser $user, array $data) {
427
-		// keep the user back-end up-to-date with the latest display name and email
428
-		// address
429
-		$oldDisplayName = $user->getDisplayName();
430
-		$oldDisplayName = is_null($oldDisplayName) ? '' : $oldDisplayName;
431
-		if (isset($data[AccountManager::PROPERTY_DISPLAYNAME]['value'])
432
-			&& $oldDisplayName !== $data[AccountManager::PROPERTY_DISPLAYNAME]['value']
433
-		) {
434
-			$result = $user->setDisplayName($data[AccountManager::PROPERTY_DISPLAYNAME]['value']);
435
-			if ($result === false) {
436
-				throw new ForbiddenException($this->l10n->t('Unable to change full name'));
437
-			}
438
-		}
439
-		$oldEmailAddress = $user->getEMailAddress();
440
-		$oldEmailAddress = is_null($oldEmailAddress) ? '' : strtolower($oldEmailAddress);
441
-		if (isset($data[AccountManager::PROPERTY_EMAIL]['value'])
442
-			&& $oldEmailAddress !== $data[AccountManager::PROPERTY_EMAIL]['value']
443
-		) {
444
-			// this is the only permission a backend provides and is also used
445
-			// for the permission of setting a email address
446
-			if (!$user->canChangeDisplayName()) {
447
-				throw new ForbiddenException($this->l10n->t('Unable to change email address'));
448
-			}
449
-			$user->setEMailAddress($data[AccountManager::PROPERTY_EMAIL]['value']);
450
-		}
451
-		$this->accountManager->updateUser($user, $data);
452
-	}
453
-
454
-	/**
455
-	 * Set the mail address of a user
456
-	 *
457
-	 * @NoAdminRequired
458
-	 * @NoSubAdminRequired
459
-	 * @PasswordConfirmationRequired
460
-	 *
461
-	 * @param string $account
462
-	 * @param bool $onlyVerificationCode only return verification code without updating the data
463
-	 * @return DataResponse
464
-	 */
465
-	public function getVerificationCode(string $account, bool $onlyVerificationCode): DataResponse {
466
-		$user = $this->userSession->getUser();
467
-
468
-		if ($user === null) {
469
-			return new DataResponse([], Http::STATUS_BAD_REQUEST);
470
-		}
471
-
472
-		$accountData = $this->accountManager->getUser($user);
473
-		$cloudId = $user->getCloudId();
474
-		$message = 'Use my Federated Cloud ID to share with me: ' . $cloudId;
475
-		$signature = $this->signMessage($user, $message);
476
-
477
-		$code = $message . ' ' . $signature;
478
-		$codeMd5 = $message . ' ' . md5($signature);
479
-
480
-		switch ($account) {
481
-			case 'verify-twitter':
482
-				$accountData[AccountManager::PROPERTY_TWITTER]['verified'] = AccountManager::VERIFICATION_IN_PROGRESS;
483
-				$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):');
484
-				$code = $codeMd5;
485
-				$type = AccountManager::PROPERTY_TWITTER;
486
-				$data = $accountData[AccountManager::PROPERTY_TWITTER]['value'];
487
-				$accountData[AccountManager::PROPERTY_TWITTER]['signature'] = $signature;
488
-				break;
489
-			case 'verify-website':
490
-				$accountData[AccountManager::PROPERTY_WEBSITE]['verified'] = AccountManager::VERIFICATION_IN_PROGRESS;
491
-				$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):');
492
-				$type = AccountManager::PROPERTY_WEBSITE;
493
-				$data = $accountData[AccountManager::PROPERTY_WEBSITE]['value'];
494
-				$accountData[AccountManager::PROPERTY_WEBSITE]['signature'] = $signature;
495
-				break;
496
-			default:
497
-				return new DataResponse([], Http::STATUS_BAD_REQUEST);
498
-		}
499
-
500
-		if ($onlyVerificationCode === false) {
501
-			$this->accountManager->updateUser($user, $accountData);
502
-
503
-			$this->jobList->add(VerifyUserData::class,
504
-				[
505
-					'verificationCode' => $code,
506
-					'data' => $data,
507
-					'type' => $type,
508
-					'uid' => $user->getUID(),
509
-					'try' => 0,
510
-					'lastRun' => $this->getCurrentTime()
511
-				]
512
-			);
513
-		}
514
-
515
-		return new DataResponse(['msg' => $msg, 'code' => $code]);
516
-	}
517
-
518
-	/**
519
-	 * get current timestamp
520
-	 *
521
-	 * @return int
522
-	 */
523
-	protected function getCurrentTime(): int {
524
-		return time();
525
-	}
526
-
527
-	/**
528
-	 * sign message with users private key
529
-	 *
530
-	 * @param IUser $user
531
-	 * @param string $message
532
-	 *
533
-	 * @return string base64 encoded signature
534
-	 */
535
-	protected function signMessage(IUser $user, string $message): string {
536
-		$privateKey = $this->keyManager->getKey($user)->getPrivate();
537
-		openssl_sign(json_encode($message), $signature, $privateKey, OPENSSL_ALGO_SHA512);
538
-		return base64_encode($signature);
539
-	}
69
+    /** @var UserManager */
70
+    private $userManager;
71
+    /** @var GroupManager */
72
+    private $groupManager;
73
+    /** @var IUserSession */
74
+    private $userSession;
75
+    /** @var IConfig */
76
+    private $config;
77
+    /** @var bool */
78
+    private $isAdmin;
79
+    /** @var IL10N */
80
+    private $l10n;
81
+    /** @var IMailer */
82
+    private $mailer;
83
+    /** @var Factory */
84
+    private $l10nFactory;
85
+    /** @var IAppManager */
86
+    private $appManager;
87
+    /** @var AccountManager */
88
+    private $accountManager;
89
+    /** @var Manager */
90
+    private $keyManager;
91
+    /** @var IJobList */
92
+    private $jobList;
93
+    /** @var IManager */
94
+    private $encryptionManager;
95
+    /** @var IEventDispatcher */
96
+    private $dispatcher;
97
+
98
+
99
+    public function __construct(
100
+        string $appName,
101
+        IRequest $request,
102
+        IUserManager $userManager,
103
+        IGroupManager $groupManager,
104
+        IUserSession $userSession,
105
+        IConfig $config,
106
+        bool $isAdmin,
107
+        IL10N $l10n,
108
+        IMailer $mailer,
109
+        IFactory $l10nFactory,
110
+        IAppManager $appManager,
111
+        AccountManager $accountManager,
112
+        Manager $keyManager,
113
+        IJobList $jobList,
114
+        IManager $encryptionManager,
115
+        IEventDispatcher $dispatcher
116
+    ) {
117
+        parent::__construct($appName, $request);
118
+        $this->userManager = $userManager;
119
+        $this->groupManager = $groupManager;
120
+        $this->userSession = $userSession;
121
+        $this->config = $config;
122
+        $this->isAdmin = $isAdmin;
123
+        $this->l10n = $l10n;
124
+        $this->mailer = $mailer;
125
+        $this->l10nFactory = $l10nFactory;
126
+        $this->appManager = $appManager;
127
+        $this->accountManager = $accountManager;
128
+        $this->keyManager = $keyManager;
129
+        $this->jobList = $jobList;
130
+        $this->encryptionManager = $encryptionManager;
131
+        $this->dispatcher = $dispatcher;
132
+    }
133
+
134
+
135
+    /**
136
+     * @NoCSRFRequired
137
+     * @NoAdminRequired
138
+     *
139
+     * Display users list template
140
+     *
141
+     * @return TemplateResponse
142
+     */
143
+    public function usersListByGroup() {
144
+        return $this->usersList();
145
+    }
146
+
147
+    /**
148
+     * @NoCSRFRequired
149
+     * @NoAdminRequired
150
+     *
151
+     * Display users list template
152
+     *
153
+     * @return TemplateResponse
154
+     */
155
+    public function usersList() {
156
+        $user = $this->userSession->getUser();
157
+        $uid = $user->getUID();
158
+
159
+        \OC::$server->getNavigationManager()->setActiveEntry('core_users');
160
+
161
+        /* SORT OPTION: SORT_USERCOUNT or SORT_GROUPNAME */
162
+        $sortGroupsBy = \OC\Group\MetaData::SORT_USERCOUNT;
163
+        $isLDAPUsed = false;
164
+        if ($this->config->getSystemValue('sort_groups_by_name', false)) {
165
+            $sortGroupsBy = \OC\Group\MetaData::SORT_GROUPNAME;
166
+        } else {
167
+            if ($this->appManager->isEnabledForUser('user_ldap')) {
168
+                $isLDAPUsed =
169
+                    $this->groupManager->isBackendUsed('\OCA\User_LDAP\Group_Proxy');
170
+                if ($isLDAPUsed) {
171
+                    // LDAP user count can be slow, so we sort by group name here
172
+                    $sortGroupsBy = \OC\Group\MetaData::SORT_GROUPNAME;
173
+                }
174
+            }
175
+        }
176
+
177
+        $canChangePassword = $this->canAdminChangeUserPasswords();
178
+
179
+        /* GROUPS */
180
+        $groupsInfo = new \OC\Group\MetaData(
181
+            $uid,
182
+            $this->isAdmin,
183
+            $this->groupManager,
184
+            $this->userSession
185
+        );
186
+
187
+        $groupsInfo->setSorting($sortGroupsBy);
188
+        list($adminGroup, $groups) = $groupsInfo->get();
189
+
190
+        if (!$isLDAPUsed && $this->appManager->isEnabledForUser('user_ldap')) {
191
+            $isLDAPUsed = (bool)array_reduce($this->userManager->getBackends(), function ($ldapFound, $backend) {
192
+                return $ldapFound || $backend instanceof User_Proxy;
193
+            });
194
+        }
195
+
196
+        $disabledUsers = -1;
197
+        $userCount = 0;
198
+
199
+        if (!$isLDAPUsed) {
200
+            if ($this->isAdmin) {
201
+                $disabledUsers = $this->userManager->countDisabledUsers();
202
+                $userCount = array_reduce($this->userManager->countUsers(), function ($v, $w) {
203
+                    return $v + (int)$w;
204
+                }, 0);
205
+            } else {
206
+                // User is subadmin !
207
+                // Map group list to names to retrieve the countDisabledUsersOfGroups
208
+                $userGroups = $this->groupManager->getUserGroups($user);
209
+                $groupsNames = [];
210
+
211
+                foreach ($groups as $key => $group) {
212
+                    // $userCount += (int)$group['usercount'];
213
+                    array_push($groupsNames, $group['name']);
214
+                    // we prevent subadmins from looking up themselves
215
+                    // so we lower the count of the groups he belongs to
216
+                    if (array_key_exists($group['id'], $userGroups)) {
217
+                        $groups[$key]['usercount']--;
218
+                        $userCount -= 1; // we also lower from one the total count
219
+                    }
220
+                }
221
+                $userCount += $this->userManager->countUsersOfGroups($groupsInfo->getGroups());
222
+                $disabledUsers = $this->userManager->countDisabledUsersOfGroups($groupsNames);
223
+            }
224
+
225
+            $userCount -= $disabledUsers;
226
+        }
227
+
228
+        $disabledUsersGroup = [
229
+            'id' => 'disabled',
230
+            'name' => 'Disabled users',
231
+            'usercount' => $disabledUsers
232
+        ];
233
+
234
+        /* QUOTAS PRESETS */
235
+        $quotaPreset = $this->parseQuotaPreset($this->config->getAppValue('files', 'quota_preset', '1 GB, 5 GB, 10 GB'));
236
+        $defaultQuota = $this->config->getAppValue('files', 'default_quota', 'none');
237
+
238
+        $event = new BeforeTemplateRenderedEvent();
239
+        $this->dispatcher->dispatch('OC\Settings\Users::loadAdditionalScripts', $event);
240
+        $this->dispatcher->dispatchTyped($event);
241
+
242
+        /* LANGUAGES */
243
+        $languages = $this->l10nFactory->getLanguages();
244
+
245
+        /* FINAL DATA */
246
+        $serverData = [];
247
+        // groups
248
+        $serverData['groups'] = array_merge_recursive($adminGroup, [$disabledUsersGroup], $groups);
249
+        // Various data
250
+        $serverData['isAdmin'] = $this->isAdmin;
251
+        $serverData['sortGroups'] = $sortGroupsBy;
252
+        $serverData['quotaPreset'] = $quotaPreset;
253
+        $serverData['userCount'] = $userCount;
254
+        $serverData['languages'] = $languages;
255
+        $serverData['defaultLanguage'] = $this->config->getSystemValue('default_language', 'en');
256
+        $serverData['forceLanguage'] = $this->config->getSystemValue('force_language', false);
257
+        // Settings
258
+        $serverData['defaultQuota'] = $defaultQuota;
259
+        $serverData['canChangePassword'] = $canChangePassword;
260
+        $serverData['newUserGenerateUserID'] = $this->config->getAppValue('core', 'newUser.generateUserID', 'no') === 'yes';
261
+        $serverData['newUserRequireEmail'] = $this->config->getAppValue('core', 'newUser.requireEmail', 'no') === 'yes';
262
+        $serverData['newUserSendEmail'] = $this->config->getAppValue('core', 'newUser.sendEmail', 'yes') === 'yes';
263
+
264
+        return new TemplateResponse('settings', 'settings-vue', ['serverData' => $serverData]);
265
+    }
266
+
267
+    /**
268
+     * @param string $key
269
+     * @param string $value
270
+     *
271
+     * @return JSONResponse
272
+     */
273
+    public function setPreference(string $key, string $value): JSONResponse {
274
+        $allowed = ['newUser.sendEmail'];
275
+        if (!in_array($key, $allowed, true)) {
276
+            return new JSONResponse([], Http::STATUS_FORBIDDEN);
277
+        }
278
+
279
+        $this->config->setAppValue('core', $key, $value);
280
+
281
+        return new JSONResponse([]);
282
+    }
283
+
284
+    /**
285
+     * Parse the app value for quota_present
286
+     *
287
+     * @param string $quotaPreset
288
+     * @return array
289
+     */
290
+    protected function parseQuotaPreset(string $quotaPreset): array {
291
+        // 1 GB, 5 GB, 10 GB => [1 GB, 5 GB, 10 GB]
292
+        $presets = array_filter(array_map('trim', explode(',', $quotaPreset)));
293
+        // Drop default and none, Make array indexes numerically
294
+        return array_values(array_diff($presets, ['default', 'none']));
295
+    }
296
+
297
+    /**
298
+     * check if the admin can change the users password
299
+     *
300
+     * The admin can change the passwords if:
301
+     *
302
+     *   - no encryption module is loaded and encryption is disabled
303
+     *   - encryption module is loaded but it doesn't require per user keys
304
+     *
305
+     * The admin can not change the passwords if:
306
+     *
307
+     *   - an encryption module is loaded and it uses per-user keys
308
+     *   - encryption is enabled but no encryption modules are loaded
309
+     *
310
+     * @return bool
311
+     */
312
+    protected function canAdminChangeUserPasswords() {
313
+        $isEncryptionEnabled = $this->encryptionManager->isEnabled();
314
+        try {
315
+            $noUserSpecificEncryptionKeys = !$this->encryptionManager->getEncryptionModule()->needDetailedAccessList();
316
+            $isEncryptionModuleLoaded = true;
317
+        } catch (ModuleDoesNotExistsException $e) {
318
+            $noUserSpecificEncryptionKeys = true;
319
+            $isEncryptionModuleLoaded = false;
320
+        }
321
+        $canChangePassword = ($isEncryptionModuleLoaded && $noUserSpecificEncryptionKeys)
322
+            || (!$isEncryptionModuleLoaded && !$isEncryptionEnabled);
323
+
324
+        return $canChangePassword;
325
+    }
326
+
327
+    /**
328
+     * @NoAdminRequired
329
+     * @NoSubAdminRequired
330
+     * @PasswordConfirmationRequired
331
+     *
332
+     * @param string $avatarScope
333
+     * @param string $displayname
334
+     * @param string $displaynameScope
335
+     * @param string $phone
336
+     * @param string $phoneScope
337
+     * @param string $email
338
+     * @param string $emailScope
339
+     * @param string $website
340
+     * @param string $websiteScope
341
+     * @param string $address
342
+     * @param string $addressScope
343
+     * @param string $twitter
344
+     * @param string $twitterScope
345
+     * @return DataResponse
346
+     */
347
+    public function setUserSettings($avatarScope,
348
+                                    $displayname,
349
+                                    $displaynameScope,
350
+                                    $phone,
351
+                                    $phoneScope,
352
+                                    $email,
353
+                                    $emailScope,
354
+                                    $website,
355
+                                    $websiteScope,
356
+                                    $address,
357
+                                    $addressScope,
358
+                                    $twitter,
359
+                                    $twitterScope
360
+    ) {
361
+        $email = strtolower($email);
362
+        if (!empty($email) && !$this->mailer->validateMailAddress($email)) {
363
+            return new DataResponse(
364
+                [
365
+                    'status' => 'error',
366
+                    'data' => [
367
+                        'message' => $this->l10n->t('Invalid mail address')
368
+                    ]
369
+                ],
370
+                Http::STATUS_UNPROCESSABLE_ENTITY
371
+            );
372
+        }
373
+        $user = $this->userSession->getUser();
374
+        $data = $this->accountManager->getUser($user);
375
+        $data[AccountManager::PROPERTY_AVATAR] = ['scope' => $avatarScope];
376
+        if ($this->config->getSystemValue('allow_user_to_change_display_name', true) !== false) {
377
+            $data[AccountManager::PROPERTY_DISPLAYNAME] = ['value' => $displayname, 'scope' => $displaynameScope];
378
+            $data[AccountManager::PROPERTY_EMAIL] = ['value' => $email, 'scope' => $emailScope];
379
+        }
380
+        if ($this->appManager->isEnabledForUser('federatedfilesharing')) {
381
+            $shareProvider = \OC::$server->query(FederatedShareProvider::class);
382
+            if ($shareProvider->isLookupServerUploadEnabled()) {
383
+                $data[AccountManager::PROPERTY_WEBSITE] = ['value' => $website, 'scope' => $websiteScope];
384
+                $data[AccountManager::PROPERTY_ADDRESS] = ['value' => $address, 'scope' => $addressScope];
385
+                $data[AccountManager::PROPERTY_PHONE] = ['value' => $phone, 'scope' => $phoneScope];
386
+                $data[AccountManager::PROPERTY_TWITTER] = ['value' => $twitter, 'scope' => $twitterScope];
387
+            }
388
+        }
389
+        try {
390
+            $this->saveUserSettings($user, $data);
391
+            return new DataResponse(
392
+                [
393
+                    'status' => 'success',
394
+                    'data' => [
395
+                        'userId' => $user->getUID(),
396
+                        'avatarScope' => $data[AccountManager::PROPERTY_AVATAR]['scope'],
397
+                        'displayname' => $data[AccountManager::PROPERTY_DISPLAYNAME]['value'],
398
+                        'displaynameScope' => $data[AccountManager::PROPERTY_DISPLAYNAME]['scope'],
399
+                        'email' => $data[AccountManager::PROPERTY_EMAIL]['value'],
400
+                        'emailScope' => $data[AccountManager::PROPERTY_EMAIL]['scope'],
401
+                        'website' => $data[AccountManager::PROPERTY_WEBSITE]['value'],
402
+                        'websiteScope' => $data[AccountManager::PROPERTY_WEBSITE]['scope'],
403
+                        'address' => $data[AccountManager::PROPERTY_ADDRESS]['value'],
404
+                        'addressScope' => $data[AccountManager::PROPERTY_ADDRESS]['scope'],
405
+                        'message' => $this->l10n->t('Settings saved')
406
+                    ]
407
+                ],
408
+                Http::STATUS_OK
409
+            );
410
+        } catch (ForbiddenException $e) {
411
+            return new DataResponse([
412
+                'status' => 'error',
413
+                'data' => [
414
+                    'message' => $e->getMessage()
415
+                ],
416
+            ]);
417
+        }
418
+    }
419
+    /**
420
+     * update account manager with new user data
421
+     *
422
+     * @param IUser $user
423
+     * @param array $data
424
+     * @throws ForbiddenException
425
+     */
426
+    protected function saveUserSettings(IUser $user, array $data) {
427
+        // keep the user back-end up-to-date with the latest display name and email
428
+        // address
429
+        $oldDisplayName = $user->getDisplayName();
430
+        $oldDisplayName = is_null($oldDisplayName) ? '' : $oldDisplayName;
431
+        if (isset($data[AccountManager::PROPERTY_DISPLAYNAME]['value'])
432
+            && $oldDisplayName !== $data[AccountManager::PROPERTY_DISPLAYNAME]['value']
433
+        ) {
434
+            $result = $user->setDisplayName($data[AccountManager::PROPERTY_DISPLAYNAME]['value']);
435
+            if ($result === false) {
436
+                throw new ForbiddenException($this->l10n->t('Unable to change full name'));
437
+            }
438
+        }
439
+        $oldEmailAddress = $user->getEMailAddress();
440
+        $oldEmailAddress = is_null($oldEmailAddress) ? '' : strtolower($oldEmailAddress);
441
+        if (isset($data[AccountManager::PROPERTY_EMAIL]['value'])
442
+            && $oldEmailAddress !== $data[AccountManager::PROPERTY_EMAIL]['value']
443
+        ) {
444
+            // this is the only permission a backend provides and is also used
445
+            // for the permission of setting a email address
446
+            if (!$user->canChangeDisplayName()) {
447
+                throw new ForbiddenException($this->l10n->t('Unable to change email address'));
448
+            }
449
+            $user->setEMailAddress($data[AccountManager::PROPERTY_EMAIL]['value']);
450
+        }
451
+        $this->accountManager->updateUser($user, $data);
452
+    }
453
+
454
+    /**
455
+     * Set the mail address of a user
456
+     *
457
+     * @NoAdminRequired
458
+     * @NoSubAdminRequired
459
+     * @PasswordConfirmationRequired
460
+     *
461
+     * @param string $account
462
+     * @param bool $onlyVerificationCode only return verification code without updating the data
463
+     * @return DataResponse
464
+     */
465
+    public function getVerificationCode(string $account, bool $onlyVerificationCode): DataResponse {
466
+        $user = $this->userSession->getUser();
467
+
468
+        if ($user === null) {
469
+            return new DataResponse([], Http::STATUS_BAD_REQUEST);
470
+        }
471
+
472
+        $accountData = $this->accountManager->getUser($user);
473
+        $cloudId = $user->getCloudId();
474
+        $message = 'Use my Federated Cloud ID to share with me: ' . $cloudId;
475
+        $signature = $this->signMessage($user, $message);
476
+
477
+        $code = $message . ' ' . $signature;
478
+        $codeMd5 = $message . ' ' . md5($signature);
479
+
480
+        switch ($account) {
481
+            case 'verify-twitter':
482
+                $accountData[AccountManager::PROPERTY_TWITTER]['verified'] = AccountManager::VERIFICATION_IN_PROGRESS;
483
+                $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):');
484
+                $code = $codeMd5;
485
+                $type = AccountManager::PROPERTY_TWITTER;
486
+                $data = $accountData[AccountManager::PROPERTY_TWITTER]['value'];
487
+                $accountData[AccountManager::PROPERTY_TWITTER]['signature'] = $signature;
488
+                break;
489
+            case 'verify-website':
490
+                $accountData[AccountManager::PROPERTY_WEBSITE]['verified'] = AccountManager::VERIFICATION_IN_PROGRESS;
491
+                $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):');
492
+                $type = AccountManager::PROPERTY_WEBSITE;
493
+                $data = $accountData[AccountManager::PROPERTY_WEBSITE]['value'];
494
+                $accountData[AccountManager::PROPERTY_WEBSITE]['signature'] = $signature;
495
+                break;
496
+            default:
497
+                return new DataResponse([], Http::STATUS_BAD_REQUEST);
498
+        }
499
+
500
+        if ($onlyVerificationCode === false) {
501
+            $this->accountManager->updateUser($user, $accountData);
502
+
503
+            $this->jobList->add(VerifyUserData::class,
504
+                [
505
+                    'verificationCode' => $code,
506
+                    'data' => $data,
507
+                    'type' => $type,
508
+                    'uid' => $user->getUID(),
509
+                    'try' => 0,
510
+                    'lastRun' => $this->getCurrentTime()
511
+                ]
512
+            );
513
+        }
514
+
515
+        return new DataResponse(['msg' => $msg, 'code' => $code]);
516
+    }
517
+
518
+    /**
519
+     * get current timestamp
520
+     *
521
+     * @return int
522
+     */
523
+    protected function getCurrentTime(): int {
524
+        return time();
525
+    }
526
+
527
+    /**
528
+     * sign message with users private key
529
+     *
530
+     * @param IUser $user
531
+     * @param string $message
532
+     *
533
+     * @return string base64 encoded signature
534
+     */
535
+    protected function signMessage(IUser $user, string $message): string {
536
+        $privateKey = $this->keyManager->getKey($user)->getPrivate();
537
+        openssl_sign(json_encode($message), $signature, $privateKey, OPENSSL_ALGO_SHA512);
538
+        return base64_encode($signature);
539
+    }
540 540
 }
Please login to merge, or discard this patch.