Passed
Push — master ( eadce7...28d823 )
by Roeland
38:31 queued 25:52
created
apps/settings/lib/Controller/UsersController.php 1 patch
Indentation   +473 added lines, -473 removed lines patch added patch discarded remove patch
@@ -66,477 +66,477 @@
 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
-
322
-		$canChangePassword = ($isEncryptionEnabled && $isEncryptionModuleLoaded && $noUserSpecificEncryptionKeys)
323
-			|| (!$isEncryptionEnabled && !$isEncryptionModuleLoaded)
324
-			|| (!$isEncryptionEnabled && $isEncryptionModuleLoaded && $noUserSpecificEncryptionKeys);
325
-
326
-		return $canChangePassword;
327
-	}
328
-
329
-	/**
330
-	 * @NoAdminRequired
331
-	 * @NoSubAdminRequired
332
-	 * @PasswordConfirmationRequired
333
-	 *
334
-	 * @param string $avatarScope
335
-	 * @param string $displayname
336
-	 * @param string $displaynameScope
337
-	 * @param string $phone
338
-	 * @param string $phoneScope
339
-	 * @param string $email
340
-	 * @param string $emailScope
341
-	 * @param string $website
342
-	 * @param string $websiteScope
343
-	 * @param string $address
344
-	 * @param string $addressScope
345
-	 * @param string $twitter
346
-	 * @param string $twitterScope
347
-	 * @return DataResponse
348
-	 */
349
-	public function setUserSettings($avatarScope,
350
-									$displayname,
351
-									$displaynameScope,
352
-									$phone,
353
-									$phoneScope,
354
-									$email,
355
-									$emailScope,
356
-									$website,
357
-									$websiteScope,
358
-									$address,
359
-									$addressScope,
360
-									$twitter,
361
-									$twitterScope
362
-	) {
363
-		$email = strtolower($email);
364
-		if (!empty($email) && !$this->mailer->validateMailAddress($email)) {
365
-			return new DataResponse(
366
-				[
367
-					'status' => 'error',
368
-					'data' => [
369
-						'message' => $this->l10n->t('Invalid mail address')
370
-					]
371
-				],
372
-				Http::STATUS_UNPROCESSABLE_ENTITY
373
-			);
374
-		}
375
-		$user = $this->userSession->getUser();
376
-		$data = $this->accountManager->getUser($user);
377
-		$data[AccountManager::PROPERTY_AVATAR] = ['scope' => $avatarScope];
378
-		if ($this->config->getSystemValue('allow_user_to_change_display_name', true) !== false) {
379
-			$data[AccountManager::PROPERTY_DISPLAYNAME] = ['value' => $displayname, 'scope' => $displaynameScope];
380
-			$data[AccountManager::PROPERTY_EMAIL] = ['value' => $email, 'scope' => $emailScope];
381
-		}
382
-		if ($this->appManager->isEnabledForUser('federatedfilesharing')) {
383
-			$shareProvider = \OC::$server->query(FederatedShareProvider::class);
384
-			if ($shareProvider->isLookupServerUploadEnabled()) {
385
-				$data[AccountManager::PROPERTY_WEBSITE] = ['value' => $website, 'scope' => $websiteScope];
386
-				$data[AccountManager::PROPERTY_ADDRESS] = ['value' => $address, 'scope' => $addressScope];
387
-				$data[AccountManager::PROPERTY_PHONE] = ['value' => $phone, 'scope' => $phoneScope];
388
-				$data[AccountManager::PROPERTY_TWITTER] = ['value' => $twitter, 'scope' => $twitterScope];
389
-			}
390
-		}
391
-		try {
392
-			$this->saveUserSettings($user, $data);
393
-			return new DataResponse(
394
-				[
395
-					'status' => 'success',
396
-					'data' => [
397
-						'userId' => $user->getUID(),
398
-						'avatarScope' => $data[AccountManager::PROPERTY_AVATAR]['scope'],
399
-						'displayname' => $data[AccountManager::PROPERTY_DISPLAYNAME]['value'],
400
-						'displaynameScope' => $data[AccountManager::PROPERTY_DISPLAYNAME]['scope'],
401
-						'email' => $data[AccountManager::PROPERTY_EMAIL]['value'],
402
-						'emailScope' => $data[AccountManager::PROPERTY_EMAIL]['scope'],
403
-						'website' => $data[AccountManager::PROPERTY_WEBSITE]['value'],
404
-						'websiteScope' => $data[AccountManager::PROPERTY_WEBSITE]['scope'],
405
-						'address' => $data[AccountManager::PROPERTY_ADDRESS]['value'],
406
-						'addressScope' => $data[AccountManager::PROPERTY_ADDRESS]['scope'],
407
-						'message' => $this->l10n->t('Settings saved')
408
-					]
409
-				],
410
-				Http::STATUS_OK
411
-			);
412
-		} catch (ForbiddenException $e) {
413
-			return new DataResponse([
414
-				'status' => 'error',
415
-				'data' => [
416
-					'message' => $e->getMessage()
417
-				],
418
-			]);
419
-		}
420
-	}
421
-	/**
422
-	 * update account manager with new user data
423
-	 *
424
-	 * @param IUser $user
425
-	 * @param array $data
426
-	 * @throws ForbiddenException
427
-	 */
428
-	protected function saveUserSettings(IUser $user, array $data) {
429
-		// keep the user back-end up-to-date with the latest display name and email
430
-		// address
431
-		$oldDisplayName = $user->getDisplayName();
432
-		$oldDisplayName = is_null($oldDisplayName) ? '' : $oldDisplayName;
433
-		if (isset($data[AccountManager::PROPERTY_DISPLAYNAME]['value'])
434
-			&& $oldDisplayName !== $data[AccountManager::PROPERTY_DISPLAYNAME]['value']
435
-		) {
436
-			$result = $user->setDisplayName($data[AccountManager::PROPERTY_DISPLAYNAME]['value']);
437
-			if ($result === false) {
438
-				throw new ForbiddenException($this->l10n->t('Unable to change full name'));
439
-			}
440
-		}
441
-		$oldEmailAddress = $user->getEMailAddress();
442
-		$oldEmailAddress = is_null($oldEmailAddress) ? '' : strtolower($oldEmailAddress);
443
-		if (isset($data[AccountManager::PROPERTY_EMAIL]['value'])
444
-			&& $oldEmailAddress !== $data[AccountManager::PROPERTY_EMAIL]['value']
445
-		) {
446
-			// this is the only permission a backend provides and is also used
447
-			// for the permission of setting a email address
448
-			if (!$user->canChangeDisplayName()) {
449
-				throw new ForbiddenException($this->l10n->t('Unable to change email address'));
450
-			}
451
-			$user->setEMailAddress($data[AccountManager::PROPERTY_EMAIL]['value']);
452
-		}
453
-		$this->accountManager->updateUser($user, $data);
454
-	}
455
-
456
-	/**
457
-	 * Set the mail address of a user
458
-	 *
459
-	 * @NoAdminRequired
460
-	 * @NoSubAdminRequired
461
-	 * @PasswordConfirmationRequired
462
-	 *
463
-	 * @param string $account
464
-	 * @param bool $onlyVerificationCode only return verification code without updating the data
465
-	 * @return DataResponse
466
-	 */
467
-	public function getVerificationCode(string $account, bool $onlyVerificationCode): DataResponse {
468
-		$user = $this->userSession->getUser();
469
-
470
-		if ($user === null) {
471
-			return new DataResponse([], Http::STATUS_BAD_REQUEST);
472
-		}
473
-
474
-		$accountData = $this->accountManager->getUser($user);
475
-		$cloudId = $user->getCloudId();
476
-		$message = 'Use my Federated Cloud ID to share with me: ' . $cloudId;
477
-		$signature = $this->signMessage($user, $message);
478
-
479
-		$code = $message . ' ' . $signature;
480
-		$codeMd5 = $message . ' ' . md5($signature);
481
-
482
-		switch ($account) {
483
-			case 'verify-twitter':
484
-				$accountData[AccountManager::PROPERTY_TWITTER]['verified'] = AccountManager::VERIFICATION_IN_PROGRESS;
485
-				$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):');
486
-				$code = $codeMd5;
487
-				$type = AccountManager::PROPERTY_TWITTER;
488
-				$data = $accountData[AccountManager::PROPERTY_TWITTER]['value'];
489
-				$accountData[AccountManager::PROPERTY_TWITTER]['signature'] = $signature;
490
-				break;
491
-			case 'verify-website':
492
-				$accountData[AccountManager::PROPERTY_WEBSITE]['verified'] = AccountManager::VERIFICATION_IN_PROGRESS;
493
-				$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):');
494
-				$type = AccountManager::PROPERTY_WEBSITE;
495
-				$data = $accountData[AccountManager::PROPERTY_WEBSITE]['value'];
496
-				$accountData[AccountManager::PROPERTY_WEBSITE]['signature'] = $signature;
497
-				break;
498
-			default:
499
-				return new DataResponse([], Http::STATUS_BAD_REQUEST);
500
-		}
501
-
502
-		if ($onlyVerificationCode === false) {
503
-			$this->accountManager->updateUser($user, $accountData);
504
-
505
-			$this->jobList->add(VerifyUserData::class,
506
-				[
507
-					'verificationCode' => $code,
508
-					'data' => $data,
509
-					'type' => $type,
510
-					'uid' => $user->getUID(),
511
-					'try' => 0,
512
-					'lastRun' => $this->getCurrentTime()
513
-				]
514
-			);
515
-		}
516
-
517
-		return new DataResponse(['msg' => $msg, 'code' => $code]);
518
-	}
519
-
520
-	/**
521
-	 * get current timestamp
522
-	 *
523
-	 * @return int
524
-	 */
525
-	protected function getCurrentTime(): int {
526
-		return time();
527
-	}
528
-
529
-	/**
530
-	 * sign message with users private key
531
-	 *
532
-	 * @param IUser $user
533
-	 * @param string $message
534
-	 *
535
-	 * @return string base64 encoded signature
536
-	 */
537
-	protected function signMessage(IUser $user, string $message): string {
538
-		$privateKey = $this->keyManager->getKey($user)->getPrivate();
539
-		openssl_sign(json_encode($message), $signature, $privateKey, OPENSSL_ALGO_SHA512);
540
-		return base64_encode($signature);
541
-	}
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
+
322
+        $canChangePassword = ($isEncryptionEnabled && $isEncryptionModuleLoaded && $noUserSpecificEncryptionKeys)
323
+            || (!$isEncryptionEnabled && !$isEncryptionModuleLoaded)
324
+            || (!$isEncryptionEnabled && $isEncryptionModuleLoaded && $noUserSpecificEncryptionKeys);
325
+
326
+        return $canChangePassword;
327
+    }
328
+
329
+    /**
330
+     * @NoAdminRequired
331
+     * @NoSubAdminRequired
332
+     * @PasswordConfirmationRequired
333
+     *
334
+     * @param string $avatarScope
335
+     * @param string $displayname
336
+     * @param string $displaynameScope
337
+     * @param string $phone
338
+     * @param string $phoneScope
339
+     * @param string $email
340
+     * @param string $emailScope
341
+     * @param string $website
342
+     * @param string $websiteScope
343
+     * @param string $address
344
+     * @param string $addressScope
345
+     * @param string $twitter
346
+     * @param string $twitterScope
347
+     * @return DataResponse
348
+     */
349
+    public function setUserSettings($avatarScope,
350
+                                    $displayname,
351
+                                    $displaynameScope,
352
+                                    $phone,
353
+                                    $phoneScope,
354
+                                    $email,
355
+                                    $emailScope,
356
+                                    $website,
357
+                                    $websiteScope,
358
+                                    $address,
359
+                                    $addressScope,
360
+                                    $twitter,
361
+                                    $twitterScope
362
+    ) {
363
+        $email = strtolower($email);
364
+        if (!empty($email) && !$this->mailer->validateMailAddress($email)) {
365
+            return new DataResponse(
366
+                [
367
+                    'status' => 'error',
368
+                    'data' => [
369
+                        'message' => $this->l10n->t('Invalid mail address')
370
+                    ]
371
+                ],
372
+                Http::STATUS_UNPROCESSABLE_ENTITY
373
+            );
374
+        }
375
+        $user = $this->userSession->getUser();
376
+        $data = $this->accountManager->getUser($user);
377
+        $data[AccountManager::PROPERTY_AVATAR] = ['scope' => $avatarScope];
378
+        if ($this->config->getSystemValue('allow_user_to_change_display_name', true) !== false) {
379
+            $data[AccountManager::PROPERTY_DISPLAYNAME] = ['value' => $displayname, 'scope' => $displaynameScope];
380
+            $data[AccountManager::PROPERTY_EMAIL] = ['value' => $email, 'scope' => $emailScope];
381
+        }
382
+        if ($this->appManager->isEnabledForUser('federatedfilesharing')) {
383
+            $shareProvider = \OC::$server->query(FederatedShareProvider::class);
384
+            if ($shareProvider->isLookupServerUploadEnabled()) {
385
+                $data[AccountManager::PROPERTY_WEBSITE] = ['value' => $website, 'scope' => $websiteScope];
386
+                $data[AccountManager::PROPERTY_ADDRESS] = ['value' => $address, 'scope' => $addressScope];
387
+                $data[AccountManager::PROPERTY_PHONE] = ['value' => $phone, 'scope' => $phoneScope];
388
+                $data[AccountManager::PROPERTY_TWITTER] = ['value' => $twitter, 'scope' => $twitterScope];
389
+            }
390
+        }
391
+        try {
392
+            $this->saveUserSettings($user, $data);
393
+            return new DataResponse(
394
+                [
395
+                    'status' => 'success',
396
+                    'data' => [
397
+                        'userId' => $user->getUID(),
398
+                        'avatarScope' => $data[AccountManager::PROPERTY_AVATAR]['scope'],
399
+                        'displayname' => $data[AccountManager::PROPERTY_DISPLAYNAME]['value'],
400
+                        'displaynameScope' => $data[AccountManager::PROPERTY_DISPLAYNAME]['scope'],
401
+                        'email' => $data[AccountManager::PROPERTY_EMAIL]['value'],
402
+                        'emailScope' => $data[AccountManager::PROPERTY_EMAIL]['scope'],
403
+                        'website' => $data[AccountManager::PROPERTY_WEBSITE]['value'],
404
+                        'websiteScope' => $data[AccountManager::PROPERTY_WEBSITE]['scope'],
405
+                        'address' => $data[AccountManager::PROPERTY_ADDRESS]['value'],
406
+                        'addressScope' => $data[AccountManager::PROPERTY_ADDRESS]['scope'],
407
+                        'message' => $this->l10n->t('Settings saved')
408
+                    ]
409
+                ],
410
+                Http::STATUS_OK
411
+            );
412
+        } catch (ForbiddenException $e) {
413
+            return new DataResponse([
414
+                'status' => 'error',
415
+                'data' => [
416
+                    'message' => $e->getMessage()
417
+                ],
418
+            ]);
419
+        }
420
+    }
421
+    /**
422
+     * update account manager with new user data
423
+     *
424
+     * @param IUser $user
425
+     * @param array $data
426
+     * @throws ForbiddenException
427
+     */
428
+    protected function saveUserSettings(IUser $user, array $data) {
429
+        // keep the user back-end up-to-date with the latest display name and email
430
+        // address
431
+        $oldDisplayName = $user->getDisplayName();
432
+        $oldDisplayName = is_null($oldDisplayName) ? '' : $oldDisplayName;
433
+        if (isset($data[AccountManager::PROPERTY_DISPLAYNAME]['value'])
434
+            && $oldDisplayName !== $data[AccountManager::PROPERTY_DISPLAYNAME]['value']
435
+        ) {
436
+            $result = $user->setDisplayName($data[AccountManager::PROPERTY_DISPLAYNAME]['value']);
437
+            if ($result === false) {
438
+                throw new ForbiddenException($this->l10n->t('Unable to change full name'));
439
+            }
440
+        }
441
+        $oldEmailAddress = $user->getEMailAddress();
442
+        $oldEmailAddress = is_null($oldEmailAddress) ? '' : strtolower($oldEmailAddress);
443
+        if (isset($data[AccountManager::PROPERTY_EMAIL]['value'])
444
+            && $oldEmailAddress !== $data[AccountManager::PROPERTY_EMAIL]['value']
445
+        ) {
446
+            // this is the only permission a backend provides and is also used
447
+            // for the permission of setting a email address
448
+            if (!$user->canChangeDisplayName()) {
449
+                throw new ForbiddenException($this->l10n->t('Unable to change email address'));
450
+            }
451
+            $user->setEMailAddress($data[AccountManager::PROPERTY_EMAIL]['value']);
452
+        }
453
+        $this->accountManager->updateUser($user, $data);
454
+    }
455
+
456
+    /**
457
+     * Set the mail address of a user
458
+     *
459
+     * @NoAdminRequired
460
+     * @NoSubAdminRequired
461
+     * @PasswordConfirmationRequired
462
+     *
463
+     * @param string $account
464
+     * @param bool $onlyVerificationCode only return verification code without updating the data
465
+     * @return DataResponse
466
+     */
467
+    public function getVerificationCode(string $account, bool $onlyVerificationCode): DataResponse {
468
+        $user = $this->userSession->getUser();
469
+
470
+        if ($user === null) {
471
+            return new DataResponse([], Http::STATUS_BAD_REQUEST);
472
+        }
473
+
474
+        $accountData = $this->accountManager->getUser($user);
475
+        $cloudId = $user->getCloudId();
476
+        $message = 'Use my Federated Cloud ID to share with me: ' . $cloudId;
477
+        $signature = $this->signMessage($user, $message);
478
+
479
+        $code = $message . ' ' . $signature;
480
+        $codeMd5 = $message . ' ' . md5($signature);
481
+
482
+        switch ($account) {
483
+            case 'verify-twitter':
484
+                $accountData[AccountManager::PROPERTY_TWITTER]['verified'] = AccountManager::VERIFICATION_IN_PROGRESS;
485
+                $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):');
486
+                $code = $codeMd5;
487
+                $type = AccountManager::PROPERTY_TWITTER;
488
+                $data = $accountData[AccountManager::PROPERTY_TWITTER]['value'];
489
+                $accountData[AccountManager::PROPERTY_TWITTER]['signature'] = $signature;
490
+                break;
491
+            case 'verify-website':
492
+                $accountData[AccountManager::PROPERTY_WEBSITE]['verified'] = AccountManager::VERIFICATION_IN_PROGRESS;
493
+                $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):');
494
+                $type = AccountManager::PROPERTY_WEBSITE;
495
+                $data = $accountData[AccountManager::PROPERTY_WEBSITE]['value'];
496
+                $accountData[AccountManager::PROPERTY_WEBSITE]['signature'] = $signature;
497
+                break;
498
+            default:
499
+                return new DataResponse([], Http::STATUS_BAD_REQUEST);
500
+        }
501
+
502
+        if ($onlyVerificationCode === false) {
503
+            $this->accountManager->updateUser($user, $accountData);
504
+
505
+            $this->jobList->add(VerifyUserData::class,
506
+                [
507
+                    'verificationCode' => $code,
508
+                    'data' => $data,
509
+                    'type' => $type,
510
+                    'uid' => $user->getUID(),
511
+                    'try' => 0,
512
+                    'lastRun' => $this->getCurrentTime()
513
+                ]
514
+            );
515
+        }
516
+
517
+        return new DataResponse(['msg' => $msg, 'code' => $code]);
518
+    }
519
+
520
+    /**
521
+     * get current timestamp
522
+     *
523
+     * @return int
524
+     */
525
+    protected function getCurrentTime(): int {
526
+        return time();
527
+    }
528
+
529
+    /**
530
+     * sign message with users private key
531
+     *
532
+     * @param IUser $user
533
+     * @param string $message
534
+     *
535
+     * @return string base64 encoded signature
536
+     */
537
+    protected function signMessage(IUser $user, string $message): string {
538
+        $privateKey = $this->keyManager->getKey($user)->getPrivate();
539
+        openssl_sign(json_encode($message), $signature, $privateKey, OPENSSL_ALGO_SHA512);
540
+        return base64_encode($signature);
541
+    }
542 542
 }
Please login to merge, or discard this patch.