Passed
Push — master ( e1a300...aa651f )
by Joas
15:36 queued 12s
created

UsersController::__construct()   A

Complexity

Conditions 1
Paths 1

Size

Total Lines 35
Code Lines 16

Duplication

Lines 0
Ratio 0 %

Importance

Changes 0
Metric Value
cc 1
eloc 16
nc 1
nop 17
dl 0
loc 35
rs 9.7333
c 0
b 0
f 0

How to fix   Many Parameters   

Many Parameters

Methods with many parameters are not only hard to understand, but their parameters also often become inconsistent when you need more, or different data.

There are several approaches to avoid long parameter lists:

1
<?php
2
3
declare(strict_types=1);
4
5
/**
6
 * @copyright Copyright (c) 2016, ownCloud, Inc.
7
 *
8
 * @author Arthur Schiwon <[email protected]>
9
 * @author Bjoern Schiessle <[email protected]>
10
 * @author Christoph Wurst <[email protected]>
11
 * @author Daniel Kesselberg <[email protected]>
12
 * @author GretaD <[email protected]>
13
 * @author Joas Schilling <[email protected]>
14
 * @author John Molakvoæ (skjnldsv) <[email protected]>
15
 * @author Morris Jobke <[email protected]>
16
 * @author Robin Appelman <[email protected]>
17
 * @author Roeland Jago Douma <[email protected]>
18
 *
19
 * @license AGPL-3.0
20
 *
21
 * This code is free software: you can redistribute it and/or modify
22
 * it under the terms of the GNU Affero General Public License, version 3,
23
 * as published by the Free Software Foundation.
24
 *
25
 * This program is distributed in the hope that it will be useful,
26
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
27
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
28
 * GNU Affero General Public License for more details.
29
 *
30
 * You should have received a copy of the GNU Affero General Public License, version 3,
31
 * along with this program. If not, see <http://www.gnu.org/licenses/>
32
 *
33
 */
34
35
// FIXME: disabled for now to be able to inject IGroupManager and also use
36
// getSubAdmin()
37
38
namespace OCA\Settings\Controller;
39
40
use OC\Accounts\AccountManager;
41
use OC\AppFramework\Http;
42
use OC\Encryption\Exceptions\ModuleDoesNotExistsException;
43
use OC\ForbiddenException;
44
use OC\Group\Manager as GroupManager;
45
use OC\KnownUser\KnownUserService;
46
use OC\L10N\Factory;
47
use OC\Security\IdentityProof\Manager;
48
use OC\User\Manager as UserManager;
49
use OCA\Settings\BackgroundJobs\VerifyUserData;
50
use OCA\Settings\Events\BeforeTemplateRenderedEvent;
51
use OCA\User_LDAP\User_Proxy;
52
use OCP\Accounts\IAccountManager;
53
use OCP\App\IAppManager;
54
use OCP\AppFramework\Controller;
55
use OCP\AppFramework\Http\DataResponse;
56
use OCP\AppFramework\Http\JSONResponse;
57
use OCP\AppFramework\Http\TemplateResponse;
58
use OCP\BackgroundJob\IJobList;
59
use OCP\Encryption\IManager;
60
use OCP\EventDispatcher\IEventDispatcher;
61
use OCP\IConfig;
62
use OCP\IGroupManager;
63
use OCP\IL10N;
64
use OCP\IRequest;
65
use OCP\IUser;
66
use OCP\IUserManager;
67
use OCP\IUserSession;
68
use OCP\L10N\IFactory;
69
use OCP\Mail\IMailer;
70
use function in_array;
71
72
class UsersController extends Controller {
73
	/** @var UserManager */
74
	private $userManager;
75
	/** @var GroupManager */
76
	private $groupManager;
77
	/** @var IUserSession */
78
	private $userSession;
79
	/** @var IConfig */
80
	private $config;
81
	/** @var bool */
82
	private $isAdmin;
83
	/** @var IL10N */
84
	private $l10n;
85
	/** @var IMailer */
86
	private $mailer;
87
	/** @var Factory */
88
	private $l10nFactory;
89
	/** @var IAppManager */
90
	private $appManager;
91
	/** @var AccountManager */
92
	private $accountManager;
93
	/** @var Manager */
94
	private $keyManager;
95
	/** @var IJobList */
96
	private $jobList;
97
	/** @var IManager */
98
	private $encryptionManager;
99
	/** @var KnownUserService */
100
	private $knownUserService;
101
	/** @var IEventDispatcher */
102
	private $dispatcher;
103
104
105
	public function __construct(
106
		string $appName,
107
		IRequest $request,
108
		IUserManager $userManager,
109
		IGroupManager $groupManager,
110
		IUserSession $userSession,
111
		IConfig $config,
112
		bool $isAdmin,
113
		IL10N $l10n,
114
		IMailer $mailer,
115
		IFactory $l10nFactory,
116
		IAppManager $appManager,
117
		AccountManager $accountManager,
118
		Manager $keyManager,
119
		IJobList $jobList,
120
		IManager $encryptionManager,
121
		KnownUserService $knownUserService,
122
		IEventDispatcher $dispatcher
123
	) {
124
		parent::__construct($appName, $request);
125
		$this->userManager = $userManager;
0 ignored issues
show
Documentation Bug introduced by
$userManager is of type OCP\IUserManager, but the property $userManager was declared to be of type OC\User\Manager. Are you sure that you always receive this specific sub-class here, or does it make sense to add an instanceof check?

Our type inference engine has found a suspicous assignment of a value to a property. This check raises an issue when a value that can be of a given class or a super-class is assigned to a property that is type hinted more strictly.

Either this assignment is in error or an instanceof check should be added for that assignment.

class Alien {}

class Dalek extends Alien {}

class Plot
{
    /** @var  Dalek */
    public $villain;
}

$alien = new Alien();
$plot = new Plot();
if ($alien instanceof Dalek) {
    $plot->villain = $alien;
}
Loading history...
126
		$this->groupManager = $groupManager;
0 ignored issues
show
Documentation Bug introduced by
$groupManager is of type OCP\IGroupManager, but the property $groupManager was declared to be of type OC\Group\Manager. Are you sure that you always receive this specific sub-class here, or does it make sense to add an instanceof check?

Our type inference engine has found a suspicous assignment of a value to a property. This check raises an issue when a value that can be of a given class or a super-class is assigned to a property that is type hinted more strictly.

Either this assignment is in error or an instanceof check should be added for that assignment.

class Alien {}

class Dalek extends Alien {}

class Plot
{
    /** @var  Dalek */
    public $villain;
}

$alien = new Alien();
$plot = new Plot();
if ($alien instanceof Dalek) {
    $plot->villain = $alien;
}
Loading history...
127
		$this->userSession = $userSession;
128
		$this->config = $config;
129
		$this->isAdmin = $isAdmin;
130
		$this->l10n = $l10n;
131
		$this->mailer = $mailer;
132
		$this->l10nFactory = $l10nFactory;
0 ignored issues
show
Documentation Bug introduced by
$l10nFactory is of type OCP\L10N\IFactory, but the property $l10nFactory was declared to be of type OC\L10N\Factory. Are you sure that you always receive this specific sub-class here, or does it make sense to add an instanceof check?

Our type inference engine has found a suspicous assignment of a value to a property. This check raises an issue when a value that can be of a given class or a super-class is assigned to a property that is type hinted more strictly.

Either this assignment is in error or an instanceof check should be added for that assignment.

class Alien {}

class Dalek extends Alien {}

class Plot
{
    /** @var  Dalek */
    public $villain;
}

$alien = new Alien();
$plot = new Plot();
if ($alien instanceof Dalek) {
    $plot->villain = $alien;
}
Loading history...
133
		$this->appManager = $appManager;
134
		$this->accountManager = $accountManager;
135
		$this->keyManager = $keyManager;
136
		$this->jobList = $jobList;
137
		$this->encryptionManager = $encryptionManager;
138
		$this->knownUserService = $knownUserService;
139
		$this->dispatcher = $dispatcher;
140
	}
141
142
143
	/**
144
	 * @NoCSRFRequired
145
	 * @NoAdminRequired
146
	 *
147
	 * Display users list template
148
	 *
149
	 * @return TemplateResponse
150
	 */
151
	public function usersListByGroup(): TemplateResponse {
152
		return $this->usersList();
153
	}
154
155
	/**
156
	 * @NoCSRFRequired
157
	 * @NoAdminRequired
158
	 *
159
	 * Display users list template
160
	 *
161
	 * @return TemplateResponse
162
	 */
163
	public function usersList(): TemplateResponse {
164
		$user = $this->userSession->getUser();
165
		$uid = $user->getUID();
166
167
		\OC::$server->getNavigationManager()->setActiveEntry('core_users');
168
169
		/* SORT OPTION: SORT_USERCOUNT or SORT_GROUPNAME */
170
		$sortGroupsBy = \OC\Group\MetaData::SORT_USERCOUNT;
171
		$isLDAPUsed = false;
172
		if ($this->config->getSystemValue('sort_groups_by_name', false)) {
173
			$sortGroupsBy = \OC\Group\MetaData::SORT_GROUPNAME;
174
		} else {
175
			if ($this->appManager->isEnabledForUser('user_ldap')) {
176
				$isLDAPUsed =
177
					$this->groupManager->isBackendUsed('\OCA\User_LDAP\Group_Proxy');
178
				if ($isLDAPUsed) {
179
					// LDAP user count can be slow, so we sort by group name here
180
					$sortGroupsBy = \OC\Group\MetaData::SORT_GROUPNAME;
181
				}
182
			}
183
		}
184
185
		$canChangePassword = $this->canAdminChangeUserPasswords();
186
187
		/* GROUPS */
188
		$groupsInfo = new \OC\Group\MetaData(
189
			$uid,
190
			$this->isAdmin,
191
			$this->groupManager,
192
			$this->userSession
193
		);
194
195
		$groupsInfo->setSorting($sortGroupsBy);
196
		[$adminGroup, $groups] = $groupsInfo->get();
197
198
		if (!$isLDAPUsed && $this->appManager->isEnabledForUser('user_ldap')) {
199
			$isLDAPUsed = (bool)array_reduce($this->userManager->getBackends(), function ($ldapFound, $backend) {
200
				return $ldapFound || $backend instanceof User_Proxy;
201
			});
202
		}
203
204
		$disabledUsers = -1;
205
		$userCount = 0;
206
207
		if (!$isLDAPUsed) {
208
			if ($this->isAdmin) {
209
				$disabledUsers = $this->userManager->countDisabledUsers();
210
				$userCount = array_reduce($this->userManager->countUsers(), function ($v, $w) {
211
					return $v + (int)$w;
212
				}, 0);
213
			} else {
214
				// User is subadmin !
215
				// Map group list to names to retrieve the countDisabledUsersOfGroups
216
				$userGroups = $this->groupManager->getUserGroups($user);
217
				$groupsNames = [];
218
219
				foreach ($groups as $key => $group) {
220
					// $userCount += (int)$group['usercount'];
221
					array_push($groupsNames, $group['name']);
222
					// we prevent subadmins from looking up themselves
223
					// so we lower the count of the groups he belongs to
224
					if (array_key_exists($group['id'], $userGroups)) {
225
						$groups[$key]['usercount']--;
226
						$userCount -= 1; // we also lower from one the total count
227
					}
228
				}
229
				$userCount += $this->userManager->countUsersOfGroups($groupsInfo->getGroups());
230
				$disabledUsers = $this->userManager->countDisabledUsersOfGroups($groupsNames);
231
			}
232
233
			$userCount -= $disabledUsers;
234
		}
235
236
		$disabledUsersGroup = [
237
			'id' => 'disabled',
238
			'name' => 'Disabled users',
239
			'usercount' => $disabledUsers
240
		];
241
242
		/* QUOTAS PRESETS */
243
		$quotaPreset = $this->parseQuotaPreset($this->config->getAppValue('files', 'quota_preset', '1 GB, 5 GB, 10 GB'));
244
		$defaultQuota = $this->config->getAppValue('files', 'default_quota', 'none');
245
246
		$event = new BeforeTemplateRenderedEvent();
247
		$this->dispatcher->dispatch('OC\Settings\Users::loadAdditionalScripts', $event);
248
		$this->dispatcher->dispatchTyped($event);
249
250
		/* LANGUAGES */
251
		$languages = $this->l10nFactory->getLanguages();
252
253
		/* FINAL DATA */
254
		$serverData = [];
255
		// groups
256
		$serverData['groups'] = array_merge_recursive($adminGroup, [$disabledUsersGroup], $groups);
257
		// Various data
258
		$serverData['isAdmin'] = $this->isAdmin;
259
		$serverData['sortGroups'] = $sortGroupsBy;
260
		$serverData['quotaPreset'] = $quotaPreset;
261
		$serverData['userCount'] = $userCount;
262
		$serverData['languages'] = $languages;
263
		$serverData['defaultLanguage'] = $this->config->getSystemValue('default_language', 'en');
264
		$serverData['forceLanguage'] = $this->config->getSystemValue('force_language', false);
265
		// Settings
266
		$serverData['defaultQuota'] = $defaultQuota;
267
		$serverData['canChangePassword'] = $canChangePassword;
268
		$serverData['newUserGenerateUserID'] = $this->config->getAppValue('core', 'newUser.generateUserID', 'no') === 'yes';
269
		$serverData['newUserRequireEmail'] = $this->config->getAppValue('core', 'newUser.requireEmail', 'no') === 'yes';
270
		$serverData['newUserSendEmail'] = $this->config->getAppValue('core', 'newUser.sendEmail', 'yes') === 'yes';
271
272
		return new TemplateResponse('settings', 'settings-vue', ['serverData' => $serverData]);
273
	}
274
275
	/**
276
	 * @param string $key
277
	 * @param string $value
278
	 *
279
	 * @return JSONResponse
280
	 */
281
	public function setPreference(string $key, string $value): JSONResponse {
282
		$allowed = ['newUser.sendEmail'];
283
		if (!in_array($key, $allowed, true)) {
284
			return new JSONResponse([], Http::STATUS_FORBIDDEN);
285
		}
286
287
		$this->config->setAppValue('core', $key, $value);
288
289
		return new JSONResponse([]);
290
	}
291
292
	/**
293
	 * Parse the app value for quota_present
294
	 *
295
	 * @param string $quotaPreset
296
	 * @return array
297
	 */
298
	protected function parseQuotaPreset(string $quotaPreset): array {
299
		// 1 GB, 5 GB, 10 GB => [1 GB, 5 GB, 10 GB]
300
		$presets = array_filter(array_map('trim', explode(',', $quotaPreset)));
301
		// Drop default and none, Make array indexes numerically
302
		return array_values(array_diff($presets, ['default', 'none']));
303
	}
304
305
	/**
306
	 * check if the admin can change the users password
307
	 *
308
	 * The admin can change the passwords if:
309
	 *
310
	 *   - no encryption module is loaded and encryption is disabled
311
	 *   - encryption module is loaded but it doesn't require per user keys
312
	 *
313
	 * The admin can not change the passwords if:
314
	 *
315
	 *   - an encryption module is loaded and it uses per-user keys
316
	 *   - encryption is enabled but no encryption modules are loaded
317
	 *
318
	 * @return bool
319
	 */
320
	protected function canAdminChangeUserPasswords(): bool {
321
		$isEncryptionEnabled = $this->encryptionManager->isEnabled();
322
		try {
323
			$noUserSpecificEncryptionKeys = !$this->encryptionManager->getEncryptionModule()->needDetailedAccessList();
324
			$isEncryptionModuleLoaded = true;
325
		} catch (ModuleDoesNotExistsException $e) {
326
			$noUserSpecificEncryptionKeys = true;
327
			$isEncryptionModuleLoaded = false;
328
		}
329
		$canChangePassword = ($isEncryptionModuleLoaded && $noUserSpecificEncryptionKeys)
330
			|| (!$isEncryptionModuleLoaded && !$isEncryptionEnabled);
331
332
		return $canChangePassword;
333
	}
334
335
	/**
336
	 * @NoAdminRequired
337
	 * @NoSubAdminRequired
338
	 * @PasswordConfirmationRequired
339
	 *
340
	 * @param string|null $avatarScope
341
	 * @param string|null $displayname
342
	 * @param string|null $displaynameScope
343
	 * @param string|null $phone
344
	 * @param string|null $phoneScope
345
	 * @param string|null $email
346
	 * @param string|null $emailScope
347
	 * @param string|null $website
348
	 * @param string|null $websiteScope
349
	 * @param string|null $address
350
	 * @param string|null $addressScope
351
	 * @param string|null $twitter
352
	 * @param string|null $twitterScope
353
	 *
354
	 * @return DataResponse
355
	 */
356
	public function setUserSettings(?string $avatarScope = null,
357
									?string $displayname = null,
358
									?string $displaynameScope = null,
359
									?string $phone = null,
360
									?string $phoneScope = null,
361
									?string $email = null,
362
									?string $emailScope = null,
363
									?string $website = null,
364
									?string $websiteScope = null,
365
									?string $address = null,
366
									?string $addressScope = null,
367
									?string $twitter = null,
368
									?string $twitterScope = null
369
	) {
370
		$user = $this->userSession->getUser();
371
		if (!$user instanceof IUser) {
372
			return new DataResponse(
373
				[
374
					'status' => 'error',
375
					'data' => [
376
						'message' => $this->l10n->t('Invalid user')
377
					]
378
				],
379
				Http::STATUS_UNAUTHORIZED
380
			);
381
		}
382
383
		$email = !is_null($email) ? strtolower($email) : $email;
384
		if (!empty($email) && !$this->mailer->validateMailAddress($email)) {
385
			return new DataResponse(
386
				[
387
					'status' => 'error',
388
					'data' => [
389
						'message' => $this->l10n->t('Invalid mail address')
390
					]
391
				],
392
				Http::STATUS_UNPROCESSABLE_ENTITY
393
			);
394
		}
395
396
		$data = $this->accountManager->getUser($user);
397
		$beforeData = $data;
398
		if (!is_null($avatarScope)) {
399
			$data[IAccountManager::PROPERTY_AVATAR]['scope'] = $avatarScope;
400
		}
401
		if ($this->config->getSystemValue('allow_user_to_change_display_name', true) !== false) {
402
			if (!is_null($displayname)) {
403
				$data[IAccountManager::PROPERTY_DISPLAYNAME]['value'] = $displayname;
404
			}
405
			if (!is_null($displaynameScope)) {
406
				$data[IAccountManager::PROPERTY_DISPLAYNAME]['scope'] = $displaynameScope;
407
			}
408
			if (!is_null($email)) {
409
				$data[IAccountManager::PROPERTY_EMAIL]['value'] = $email;
410
			}
411
			if (!is_null($emailScope)) {
412
				$data[IAccountManager::PROPERTY_EMAIL]['scope'] = $emailScope;
413
			}
414
		}
415
		if (!is_null($website)) {
416
			$data[IAccountManager::PROPERTY_WEBSITE]['value'] = $website;
417
		}
418
		if (!is_null($websiteScope)) {
419
			$data[IAccountManager::PROPERTY_WEBSITE]['scope'] = $websiteScope;
420
		}
421
		if (!is_null($address)) {
422
			$data[IAccountManager::PROPERTY_ADDRESS]['value'] = $address;
423
		}
424
		if (!is_null($addressScope)) {
425
			$data[IAccountManager::PROPERTY_ADDRESS]['scope'] = $addressScope;
426
		}
427
		if (!is_null($phone)) {
428
			$data[IAccountManager::PROPERTY_PHONE]['value'] = $phone;
429
		}
430
		if (!is_null($phoneScope)) {
431
			$data[IAccountManager::PROPERTY_PHONE]['scope'] = $phoneScope;
432
		}
433
		if (!is_null($twitter)) {
434
			$data[IAccountManager::PROPERTY_TWITTER]['value'] = $twitter;
435
		}
436
		if (!is_null($twitterScope)) {
437
			$data[IAccountManager::PROPERTY_TWITTER]['scope'] = $twitterScope;
438
		}
439
440
		try {
441
			$data = $this->saveUserSettings($user, $data);
442
			if ($beforeData[IAccountManager::PROPERTY_PHONE]['value'] !== $data[IAccountManager::PROPERTY_PHONE]['value']) {
443
				$this->knownUserService->deleteByContactUserId($user->getUID());
444
			}
445
			return new DataResponse(
446
				[
447
					'status' => 'success',
448
					'data' => [
449
						'userId' => $user->getUID(),
450
						'avatarScope' => $data[IAccountManager::PROPERTY_AVATAR]['scope'],
451
						'displayname' => $data[IAccountManager::PROPERTY_DISPLAYNAME]['value'],
452
						'displaynameScope' => $data[IAccountManager::PROPERTY_DISPLAYNAME]['scope'],
453
						'phone' => $data[IAccountManager::PROPERTY_PHONE]['value'],
454
						'phoneScope' => $data[IAccountManager::PROPERTY_PHONE]['scope'],
455
						'email' => $data[IAccountManager::PROPERTY_EMAIL]['value'],
456
						'emailScope' => $data[IAccountManager::PROPERTY_EMAIL]['scope'],
457
						'website' => $data[IAccountManager::PROPERTY_WEBSITE]['value'],
458
						'websiteScope' => $data[IAccountManager::PROPERTY_WEBSITE]['scope'],
459
						'address' => $data[IAccountManager::PROPERTY_ADDRESS]['value'],
460
						'addressScope' => $data[IAccountManager::PROPERTY_ADDRESS]['scope'],
461
						'twitter' => $data[IAccountManager::PROPERTY_TWITTER]['value'],
462
						'twitterScope' => $data[IAccountManager::PROPERTY_TWITTER]['scope'],
463
						'message' => $this->l10n->t('Settings saved')
464
					]
465
				],
466
				Http::STATUS_OK
467
			);
468
		} catch (ForbiddenException $e) {
469
			return new DataResponse([
470
				'status' => 'error',
471
				'data' => [
472
					'message' => $e->getMessage()
473
				],
474
			]);
475
		} catch (\InvalidArgumentException $e) {
476
			return new DataResponse([
477
				'status' => 'error',
478
				'data' => [
479
					'message' => $e->getMessage()
480
				],
481
			]);
482
		}
483
	}
484
	/**
485
	 * update account manager with new user data
486
	 *
487
	 * @param IUser $user
488
	 * @param array $data
489
	 * @return array
490
	 * @throws ForbiddenException
491
	 * @throws \InvalidArgumentException
492
	 */
493
	protected function saveUserSettings(IUser $user, array $data): array {
494
		// keep the user back-end up-to-date with the latest display name and email
495
		// address
496
		$oldDisplayName = $user->getDisplayName();
497
		$oldDisplayName = is_null($oldDisplayName) ? '' : $oldDisplayName;
0 ignored issues
show
introduced by
The condition is_null($oldDisplayName) is always false.
Loading history...
498
		if (isset($data[IAccountManager::PROPERTY_DISPLAYNAME]['value'])
499
			&& $oldDisplayName !== $data[IAccountManager::PROPERTY_DISPLAYNAME]['value']
500
		) {
501
			$result = $user->setDisplayName($data[IAccountManager::PROPERTY_DISPLAYNAME]['value']);
502
			if ($result === false) {
503
				throw new ForbiddenException($this->l10n->t('Unable to change full name'));
504
			}
505
		}
506
507
		$oldEmailAddress = $user->getEMailAddress();
508
		$oldEmailAddress = is_null($oldEmailAddress) ? '' : strtolower($oldEmailAddress);
509
		if (isset($data[IAccountManager::PROPERTY_EMAIL]['value'])
510
			&& $oldEmailAddress !== $data[IAccountManager::PROPERTY_EMAIL]['value']
511
		) {
512
			// this is the only permission a backend provides and is also used
513
			// for the permission of setting a email address
514
			if (!$user->canChangeDisplayName()) {
515
				throw new ForbiddenException($this->l10n->t('Unable to change email address'));
516
			}
517
			$user->setEMailAddress($data[IAccountManager::PROPERTY_EMAIL]['value']);
518
		}
519
520
		try {
521
			return $this->accountManager->updateUser($user, $data, true);
522
		} catch (\InvalidArgumentException $e) {
523
			if ($e->getMessage() === IAccountManager::PROPERTY_PHONE) {
524
				throw new \InvalidArgumentException($this->l10n->t('Unable to set invalid phone number'));
525
			}
526
			if ($e->getMessage() === IAccountManager::PROPERTY_WEBSITE) {
527
				throw new \InvalidArgumentException($this->l10n->t('Unable to set invalid website'));
528
			}
529
			throw new \InvalidArgumentException($this->l10n->t('Some account data was invalid'));
530
		}
531
	}
532
533
	/**
534
	 * Set the mail address of a user
535
	 *
536
	 * @NoAdminRequired
537
	 * @NoSubAdminRequired
538
	 * @PasswordConfirmationRequired
539
	 *
540
	 * @param string $account
541
	 * @param bool $onlyVerificationCode only return verification code without updating the data
542
	 * @return DataResponse
543
	 */
544
	public function getVerificationCode(string $account, bool $onlyVerificationCode): DataResponse {
545
		$user = $this->userSession->getUser();
546
547
		if ($user === null) {
548
			return new DataResponse([], Http::STATUS_BAD_REQUEST);
549
		}
550
551
		$accountData = $this->accountManager->getUser($user);
552
		$cloudId = $user->getCloudId();
553
		$message = 'Use my Federated Cloud ID to share with me: ' . $cloudId;
554
		$signature = $this->signMessage($user, $message);
555
556
		$code = $message . ' ' . $signature;
557
		$codeMd5 = $message . ' ' . md5($signature);
558
559
		switch ($account) {
560
			case 'verify-twitter':
561
				$accountData[IAccountManager::PROPERTY_TWITTER]['verified'] = IAccountManager::VERIFICATION_IN_PROGRESS;
562
				$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):');
563
				$code = $codeMd5;
564
				$type = IAccountManager::PROPERTY_TWITTER;
565
				$accountData[IAccountManager::PROPERTY_TWITTER]['signature'] = $signature;
566
				break;
567
			case 'verify-website':
568
				$accountData[IAccountManager::PROPERTY_WEBSITE]['verified'] = IAccountManager::VERIFICATION_IN_PROGRESS;
569
				$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):');
570
				$type = IAccountManager::PROPERTY_WEBSITE;
571
				$accountData[IAccountManager::PROPERTY_WEBSITE]['signature'] = $signature;
572
				break;
573
			default:
574
				return new DataResponse([], Http::STATUS_BAD_REQUEST);
575
		}
576
577
		if ($onlyVerificationCode === false) {
578
			$accountData = $this->accountManager->updateUser($user, $accountData);
579
			$data = $accountData[$type]['value'];
580
581
			$this->jobList->add(VerifyUserData::class,
582
				[
583
					'verificationCode' => $code,
584
					'data' => $data,
585
					'type' => $type,
586
					'uid' => $user->getUID(),
587
					'try' => 0,
588
					'lastRun' => $this->getCurrentTime()
589
				]
590
			);
591
		}
592
593
		return new DataResponse(['msg' => $msg, 'code' => $code]);
594
	}
595
596
	/**
597
	 * get current timestamp
598
	 *
599
	 * @return int
600
	 */
601
	protected function getCurrentTime(): int {
602
		return time();
603
	}
604
605
	/**
606
	 * sign message with users private key
607
	 *
608
	 * @param IUser $user
609
	 * @param string $message
610
	 *
611
	 * @return string base64 encoded signature
612
	 */
613
	protected function signMessage(IUser $user, string $message): string {
614
		$privateKey = $this->keyManager->getKey($user)->getPrivate();
615
		openssl_sign(json_encode($message), $signature, $privateKey, OPENSSL_ALGO_SHA512);
616
		return base64_encode($signature);
617
	}
618
}
619