Completed
Push — master ( d4a0af...760b28 )
by Blizzz
25:12 queued 24:34
created

UsersController::usersList()   D

Complexity

Conditions 13
Paths 144

Size

Total Lines 103
Code Lines 64

Duplication

Lines 0
Ratio 0 %

Importance

Changes 0
Metric Value
cc 13
eloc 64
nc 144
nop 0
dl 0
loc 103
rs 4.6605
c 0
b 0
f 0

How to fix   Long Method    Complexity   

Long Method

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

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

Commonly applied refactorings include:

1
<?php
2
// FIXME: disabled for now to be able to inject IGroupManager and also use
3
// getSubAdmin()
4
//declare(strict_types=1);
5
/**
6
 * @copyright Copyright (c) 2016, ownCloud, Inc.
7
 *
8
 * @author Arthur Schiwon <[email protected]>
9
 * @author Bjoern Schiessle <[email protected]>
10
 * @author Björn Schießle <[email protected]>
11
 * @author Christoph Wurst <[email protected]>
12
 * @author Clark Tomlinson <[email protected]>
13
 * @author Joas Schilling <[email protected]>
14
 * @author Lukas Reschke <[email protected]>
15
 * @author Morris Jobke <[email protected]>
16
 * @author Robin Appelman <[email protected]>
17
 * @author Roeland Jago Douma <[email protected]>
18
 * @author Thomas Müller <[email protected]>
19
 * @author Thomas Pulzer <[email protected]>
20
 * @author Tobia De Koninck <[email protected]>
21
 * @author Tobias Kaminsky <[email protected]>
22
 * @author Vincent Petry <[email protected]>
23
 *
24
 * @license AGPL-3.0
25
 *
26
 * This code is free software: you can redistribute it and/or modify
27
 * it under the terms of the GNU Affero General Public License, version 3,
28
 * as published by the Free Software Foundation.
29
 *
30
 * This program is distributed in the hope that it will be useful,
31
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
32
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
33
 * GNU Affero General Public License for more details.
34
 *
35
 * You should have received a copy of the GNU Affero General Public License, version 3,
36
 * along with this program.  If not, see <http://www.gnu.org/licenses/>
37
 *
38
 */
39
40
namespace OC\Settings\Controller;
41
42
use OC\Accounts\AccountManager;
43
use OC\AppFramework\Http;
44
use OC\ForbiddenException;
45
use OC\Security\IdentityProof\Manager;
46
use OCP\App\IAppManager;
47
use OCP\AppFramework\Controller;
48
use OCP\AppFramework\Http\DataResponse;
49
use OCP\AppFramework\Http\TemplateResponse;
50
use OCP\BackgroundJob\IJobList;
51
use OCP\Encryption\IManager;
52
use OCP\IConfig;
53
use OCP\IGroupManager;
54
use OCP\IL10N;
55
use OCP\IRequest;
56
use OCP\IURLGenerator;
57
use OCP\IUser;
58
use OCP\IUserManager;
59
use OCP\IUserSession;
60
use OCP\L10N\IFactory;
61
use OCP\Mail\IMailer;
62
use OC\Settings\BackgroundJobs\VerifyUserData;
63
64
/**
65
 * @package OC\Settings\Controller
66
 */
67
class UsersController extends Controller {
68
	/** @var IUserManager */
69
	private $userManager;
70
	/** @var IGroupManager */
71
	private $groupManager;
72
	/** @var IUserSession */
73
	private $userSession;
74
	/** @var IConfig */
75
	private $config;
76
	/** @var bool */
77
	private $isAdmin;
78
	/** @var IL10N */
79
	private $l10n;
80
	/** @var IMailer */
81
	private $mailer;
82
	/** @var IFactory */
83
	private $l10nFactory;
84
	/** @var IAppManager */
85
	private $appManager;
86
	/** @var AccountManager */
87
	private $accountManager;
88
	/** @var Manager */
89
	private $keyManager;
90
	/** @var IJobList */
91
	private $jobList;
92
	/** @var IManager */
93
	private $encryptionManager;
94
95
96 View Code Duplication
	public function __construct(string $appName,
97
								IRequest $request,
98
								IUserManager $userManager,
99
								IGroupManager $groupManager,
100
								IUserSession $userSession,
101
								IConfig $config,
102
								bool $isAdmin,
103
								IL10N $l10n,
104
								IMailer $mailer,
105
								IFactory $l10nFactory,
106
								IAppManager $appManager,
107
								AccountManager $accountManager,
108
								Manager $keyManager,
109
								IJobList $jobList,
110
								IManager $encryptionManager) {
111
		parent::__construct($appName, $request);
112
		$this->userManager = $userManager;
113
		$this->groupManager = $groupManager;
114
		$this->userSession = $userSession;
115
		$this->config = $config;
116
		$this->isAdmin = $isAdmin;
117
		$this->l10n = $l10n;
118
		$this->mailer = $mailer;
119
		$this->l10nFactory = $l10nFactory;
120
		$this->appManager = $appManager;
121
		$this->accountManager = $accountManager;
122
		$this->keyManager = $keyManager;
123
		$this->jobList = $jobList;
124
		$this->encryptionManager = $encryptionManager;
125
	}
126
127
128
	/**
129
	 * @NoCSRFRequired
130
	 * @NoAdminRequired
131
	 * 
132
	 * Display users list template
133
	 * 
134
	 * @return TemplateResponse
135
	 */
136
	public function usersListByGroup() {
137
		return $this->usersList();
138
	}
139
140
	/**
141
	 * @NoCSRFRequired
142
	 * @NoAdminRequired
143
	 * 
144
	 * Display users list template
145
	 * 
146
	 * @return TemplateResponse
147
	 */
148
	public function usersList() {
149
		$user = $this->userSession->getUser();
150
		$uid = $user->getUID();
151
152
		\OC::$server->getNavigationManager()->setActiveEntry('core_users');
153
				
154
		/* SORT OPTION: SORT_USERCOUNT or SORT_GROUPNAME */
155
		$sortGroupsBy = \OC\Group\MetaData::SORT_USERCOUNT;
156
		if ($this->config->getSystemValue('sort_groups_by_name', false)) {
157
			$sortGroupsBy = \OC\Group\MetaData::SORT_GROUPNAME;
158
		} else {
159
			$isLDAPUsed = false;
160
			if ($this->appManager->isEnabledForUser('user_ldap')) {
161
				$isLDAPUsed =
162
					$this->groupManager->isBackendUsed('\OCA\User_LDAP\Group_LDAP')
163
					|| $this->groupManager->isBackendUsed('\OCA\User_LDAP\Group_Proxy');
164
				if ($isLDAPUsed) {
165
					// LDAP user count can be slow, so we sort by group name here
166
					$sortGroupsBy = \OC\Group\MetaData::SORT_GROUPNAME;
167
				}
168
			}
169
		}
170
		
171
		/* ENCRYPTION CONFIG */
172
		$isEncryptionEnabled = $this->encryptionManager->isEnabled();
173
		$useMasterKey = $this->config->getAppValue('encryption', 'useMasterKey', true);
0 ignored issues
show
Documentation introduced by
true is of type boolean, but the function expects a string.

It seems like the type of the argument is not accepted by the function/method which you are calling.

In some cases, in particular if PHP’s automatic type-juggling kicks in this might be fine. In other cases, however this might be a bug.

We suggest to add an explicit type cast like in the following example:

function acceptsInteger($int) { }

$x = '123'; // string "123"

// Instead of
acceptsInteger($x);

// we recommend to use
acceptsInteger((integer) $x);
Loading history...
174
		// If masterKey enabled, then you can change password. This is to avoid data loss!
175
		$canChangePassword = ($isEncryptionEnabled && $useMasterKey) || $useMasterKey;
176
		
177
		
178
		/* GROUPS */		
179
		$groupsInfo = new \OC\Group\MetaData(
180
			$uid,
181
			$this->isAdmin,
182
			$this->groupManager,
183
			$this->userSession
184
		);
185
		
186
		$groupsInfo->setSorting($sortGroupsBy);
187
		list($adminGroup, $groups) = $groupsInfo->get();
188
		
189
		if ($this->isAdmin) {
190
			$disabledUsers = $isLDAPUsed ? 0 : $this->userManager->countDisabledUsers();
0 ignored issues
show
Bug introduced by
The variable $isLDAPUsed does not seem to be defined for all execution paths leading up to this point.

If you define a variable conditionally, it can happen that it is not defined for all execution paths.

Let’s take a look at an example:

function myFunction($a) {
    switch ($a) {
        case 'foo':
            $x = 1;
            break;

        case 'bar':
            $x = 2;
            break;
    }

    // $x is potentially undefined here.
    echo $x;
}

In the above example, the variable $x is defined if you pass “foo” or “bar” as argument for $a. However, since the switch statement has no default case statement, if you pass any other value, the variable $x would be undefined.

Available Fixes

  1. Check for existence of the variable explicitly:

    function myFunction($a) {
        switch ($a) {
            case 'foo':
                $x = 1;
                break;
    
            case 'bar':
                $x = 2;
                break;
        }
    
        if (isset($x)) { // Make sure it's always set.
            echo $x;
        }
    }
    
  2. Define a default value for the variable:

    function myFunction($a) {
        $x = ''; // Set a default which gets overridden for certain paths.
        switch ($a) {
            case 'foo':
                $x = 1;
                break;
    
            case 'bar':
                $x = 2;
                break;
        }
    
        echo $x;
    }
    
  3. Add a value for the missing path:

    function myFunction($a) {
        switch ($a) {
            case 'foo':
                $x = 1;
                break;
    
            case 'bar':
                $x = 2;
                break;
    
            // We add support for the missing case.
            default:
                $x = '';
                break;
        }
    
        echo $x;
    }
    
Loading history...
191
			$userCount = array_reduce($this->userManager->countUsers(), function($v, $w) {
192
				return $v + (int)$w;
193
			}, 0);
194
		} else {
195
			// User is subadmin !
196
			// Map group list to names to retrieve the countDisabledUsersOfGroups
197
			$userGroups = $this->groupManager->getUserGroups($user);
198
			$groupsNames = [];
199
			$userCount = 0;
200
201
			foreach($groups as $key => $group) {
202
				// $userCount += (int)$group['usercount'];
0 ignored issues
show
Unused Code Comprehensibility introduced by
59% of this comment could be valid code. Did you maybe forget this after debugging?

Sometimes obsolete code just ends up commented out instead of removed. In this case it is better to remove the code once you have checked you do not need it.

The code might also have been commented out for debugging purposes. In this case it is vital that someone uncomments it again or your project may behave in very unexpected ways in production.

This check looks for comments that seem to be mostly valid code and reports them.

Loading history...
203
				array_push($groupsNames, $group['name']);
204
				// we prevent subadmins from looking up themselves
205
				// so we lower the count of the groups he belongs to
206
				if (array_key_exists($group['id'], $userGroups)) {
207
					$groups[$key]['usercount']--;
208
					$userCount = -1; // we also lower from one the total count
209
				}
210
			};
211
			$userCount += $this->userManager->countUsersOfGroups($groupsInfo->getGroups());
0 ignored issues
show
Bug introduced by
The method countUsersOfGroups() does not exist on OCP\IUserManager. Did you maybe mean countUsers()?

This check marks calls to methods that do not seem to exist on an object.

This is most likely the result of a method being renamed without all references to it being renamed likewise.

Loading history...
212
			$disabledUsers = $isLDAPUsed ? 0 : $this->userManager->countDisabledUsersOfGroups($groupsNames);
0 ignored issues
show
Bug introduced by
The method countDisabledUsersOfGroups() does not exist on OCP\IUserManager. Did you maybe mean countDisabledUsers()?

This check marks calls to methods that do not seem to exist on an object.

This is most likely the result of a method being renamed without all references to it being renamed likewise.

Loading history...
213
		}
214
		$disabledUsersGroup = [
215
			'id' => 'disabled',
216
			'name' => 'Disabled users',
217
			'usercount' => $disabledUsers
218
		];
219
		
220
		/* QUOTAS PRESETS */
221
		$quotaPreset = $this->config->getAppValue('files', 'quota_preset', '1 GB, 5 GB, 10 GB');
222
		$quotaPreset = explode(',', $quotaPreset);
223
		foreach ($quotaPreset as &$preset) {
224
			$preset = trim($preset);
225
		}
226
		$quotaPreset = array_diff($quotaPreset, array('default', 'none'));
227
		$defaultQuota = $this->config->getAppValue('files', 'default_quota', 'none');
228
		
229
		\OC::$server->getEventDispatcher()->dispatch('OC\Settings\Users::loadAdditionalScripts');
230
		
231
		/* LANGUAGES */
232
		$languages = $this->l10nFactory->getLanguages();
233
		
234
		/* FINAL DATA */
235
		$serverData = array();
236
		// groups
237
		$serverData['groups'] = array_merge_recursive($adminGroup, [$disabledUsersGroup], $groups);
238
		// Various data
239
		$serverData['isAdmin'] = $this->isAdmin;
240
		$serverData['sortGroups'] = $sortGroupsBy;
241
		$serverData['quotaPreset'] = $quotaPreset;
242
		$serverData['userCount'] = $userCount - $disabledUsers;
243
		$serverData['languages'] = $languages;
244
		$serverData['defaultLanguage'] = $this->config->getSystemValue('default_language', 'en');
245
		// Settings
246
		$serverData['defaultQuota'] = $defaultQuota;
247
		$serverData['canChangePassword'] = $canChangePassword;
248
249
		return new TemplateResponse('settings', 'settings', ['serverData' => $serverData]);
250
	}
251
252
	/**
253
	 * @NoAdminRequired
254
	 * @NoSubadminRequired
255
	 * @PasswordConfirmationRequired
256
	 *
257
	 * @param string $avatarScope
258
	 * @param string $displayname
259
	 * @param string $displaynameScope
260
	 * @param string $phone
261
	 * @param string $phoneScope
262
	 * @param string $email
263
	 * @param string $emailScope
264
	 * @param string $website
265
	 * @param string $websiteScope
266
	 * @param string $address
267
	 * @param string $addressScope
268
	 * @param string $twitter
269
	 * @param string $twitterScope
270
	 * @return DataResponse
271
	 */
272
	public function setUserSettings($avatarScope,
273
									$displayname,
274
									$displaynameScope,
275
									$phone,
276
									$phoneScope,
277
									$email,
278
									$emailScope,
279
									$website,
280
									$websiteScope,
281
									$address,
282
									$addressScope,
283
									$twitter,
284
									$twitterScope
285
	) {
286
		if (!empty($email) && !$this->mailer->validateMailAddress($email)) {
287
			return new DataResponse(
288
				[
289
					'status' => 'error',
290
					'data' => [
291
						'message' => $this->l10n->t('Invalid mail address')
292
					]
293
				],
294
				Http::STATUS_UNPROCESSABLE_ENTITY
295
			);
296
		}
297
		$user = $this->userSession->getUser();
298
		$data = $this->accountManager->getUser($user);
0 ignored issues
show
Bug introduced by
It seems like $user defined by $this->userSession->getUser() on line 297 can be null; however, OC\Accounts\AccountManager::getUser() does not accept null, maybe add an additional type check?

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

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

function doesNotAcceptNull(stdClass $x) { }

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

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

// Safe - Alternative 2
function withCheck2() {
    $x = mayReturnNull();
    if ($x instanceof stdClass) {
        doesNotAcceptNull($x);
    }
}
Loading history...
299
		$data[AccountManager::PROPERTY_AVATAR] = ['scope' => $avatarScope];
300
		if ($this->config->getSystemValue('allow_user_to_change_display_name', true) !== false) {
301
			$data[AccountManager::PROPERTY_DISPLAYNAME] = ['value' => $displayname, 'scope' => $displaynameScope];
302
			$data[AccountManager::PROPERTY_EMAIL] = ['value' => $email, 'scope' => $emailScope];
303
		}
304
		if ($this->appManager->isEnabledForUser('federatedfilesharing')) {
305
			$federatedFileSharing = new \OCA\FederatedFileSharing\AppInfo\Application();
306
			$shareProvider = $federatedFileSharing->getFederatedShareProvider();
307
			if ($shareProvider->isLookupServerUploadEnabled()) {
308
				$data[AccountManager::PROPERTY_WEBSITE] = ['value' => $website, 'scope' => $websiteScope];
309
				$data[AccountManager::PROPERTY_ADDRESS] = ['value' => $address, 'scope' => $addressScope];
310
				$data[AccountManager::PROPERTY_PHONE] = ['value' => $phone, 'scope' => $phoneScope];
311
				$data[AccountManager::PROPERTY_TWITTER] = ['value' => $twitter, 'scope' => $twitterScope];
312
			}
313
		}
314
		try {
315
			$this->saveUserSettings($user, $data);
0 ignored issues
show
Bug introduced by
It seems like $user defined by $this->userSession->getUser() on line 297 can be null; however, OC\Settings\Controller\U...ler::saveUserSettings() does not accept null, maybe add an additional type check?

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

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

function doesNotAcceptNull(stdClass $x) { }

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

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

// Safe - Alternative 2
function withCheck2() {
    $x = mayReturnNull();
    if ($x instanceof stdClass) {
        doesNotAcceptNull($x);
    }
}
Loading history...
316
			return new DataResponse(
317
				[
318
					'status' => 'success',
319
					'data' => [
320
						'userId' => $user->getUID(),
321
						'avatarScope' => $data[AccountManager::PROPERTY_AVATAR]['scope'],
322
						'displayname' => $data[AccountManager::PROPERTY_DISPLAYNAME]['value'],
323
						'displaynameScope' => $data[AccountManager::PROPERTY_DISPLAYNAME]['scope'],
324
						'email' => $data[AccountManager::PROPERTY_EMAIL]['value'],
325
						'emailScope' => $data[AccountManager::PROPERTY_EMAIL]['scope'],
326
						'website' => $data[AccountManager::PROPERTY_WEBSITE]['value'],
327
						'websiteScope' => $data[AccountManager::PROPERTY_WEBSITE]['scope'],
328
						'address' => $data[AccountManager::PROPERTY_ADDRESS]['value'],
329
						'addressScope' => $data[AccountManager::PROPERTY_ADDRESS]['scope'],
330
						'message' => $this->l10n->t('Settings saved')
331
					]
332
				],
333
				Http::STATUS_OK
334
			);
335
		} catch (ForbiddenException $e) {
336
			return new DataResponse([
337
				'status' => 'error',
338
				'data' => [
339
					'message' => $e->getMessage()
340
				],
341
			]);
342
		}
343
	}
344
	/**
345
	 * update account manager with new user data
346
	 *
347
	 * @param IUser $user
348
	 * @param array $data
349
	 * @throws ForbiddenException
350
	 */
351
	protected function saveUserSettings(IUser $user, array $data) {
352
		// keep the user back-end up-to-date with the latest display name and email
353
		// address
354
		$oldDisplayName = $user->getDisplayName();
355
		$oldDisplayName = is_null($oldDisplayName) ? '' : $oldDisplayName;
356 View Code Duplication
		if (isset($data[AccountManager::PROPERTY_DISPLAYNAME]['value'])
357
			&& $oldDisplayName !== $data[AccountManager::PROPERTY_DISPLAYNAME]['value']
358
		) {
359
			$result = $user->setDisplayName($data[AccountManager::PROPERTY_DISPLAYNAME]['value']);
360
			if ($result === false) {
361
				throw new ForbiddenException($this->l10n->t('Unable to change full name'));
362
			}
363
		}
364
		$oldEmailAddress = $user->getEMailAddress();
365
		$oldEmailAddress = is_null($oldEmailAddress) ? '' : $oldEmailAddress;
366 View Code Duplication
		if (isset($data[AccountManager::PROPERTY_EMAIL]['value'])
367
			&& $oldEmailAddress !== $data[AccountManager::PROPERTY_EMAIL]['value']
368
		) {
369
			// this is the only permission a backend provides and is also used
370
			// for the permission of setting a email address
371
			if (!$user->canChangeDisplayName()) {
372
				throw new ForbiddenException($this->l10n->t('Unable to change email address'));
373
			}
374
			$user->setEMailAddress($data[AccountManager::PROPERTY_EMAIL]['value']);
375
		}
376
		$this->accountManager->updateUser($user, $data);
377
	}
378
379
	/**
380
	 * Set the mail address of a user
381
	 *
382
	 * @NoAdminRequired
383
	 * @NoSubadminRequired
384
	 * @PasswordConfirmationRequired
385
	 *
386
	 * @param string $account
387
	 * @param bool $onlyVerificationCode only return verification code without updating the data
388
	 * @return DataResponse
389
	 */
390
	public function getVerificationCode(string $account, bool $onlyVerificationCode): DataResponse {
391
392
		$user = $this->userSession->getUser();
393
394
		if ($user === null) {
395
			return new DataResponse([], Http::STATUS_BAD_REQUEST);
396
		}
397
398
		$accountData = $this->accountManager->getUser($user);
399
		$cloudId = $user->getCloudId();
400
		$message = 'Use my Federated Cloud ID to share with me: ' . $cloudId;
401
		$signature = $this->signMessage($user, $message);
402
403
		$code = $message . ' ' . $signature;
404
		$codeMd5 = $message . ' ' . md5($signature);
405
406
		switch ($account) {
407 View Code Duplication
			case 'verify-twitter':
408
				$accountData[AccountManager::PROPERTY_TWITTER]['verified'] = AccountManager::VERIFICATION_IN_PROGRESS;
409
				$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):');
410
				$code = $codeMd5;
411
				$type = AccountManager::PROPERTY_TWITTER;
412
				$data = $accountData[AccountManager::PROPERTY_TWITTER]['value'];
413
				$accountData[AccountManager::PROPERTY_TWITTER]['signature'] = $signature;
414
				break;
415 View Code Duplication
			case 'verify-website':
416
				$accountData[AccountManager::PROPERTY_WEBSITE]['verified'] = AccountManager::VERIFICATION_IN_PROGRESS;
417
				$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):');
418
				$type = AccountManager::PROPERTY_WEBSITE;
419
				$data = $accountData[AccountManager::PROPERTY_WEBSITE]['value'];
420
				$accountData[AccountManager::PROPERTY_WEBSITE]['signature'] = $signature;
421
				break;
422
			default:
423
				return new DataResponse([], Http::STATUS_BAD_REQUEST);
424
		}
425
426
		if ($onlyVerificationCode === false) {
427
			$this->accountManager->updateUser($user, $accountData);
428
429
			$this->jobList->add(VerifyUserData::class,
430
				[
431
					'verificationCode' => $code,
432
					'data' => $data,
433
					'type' => $type,
434
					'uid' => $user->getUID(),
435
					'try' => 0,
436
					'lastRun' => $this->getCurrentTime()
437
				]
438
			);
439
		}
440
441
		return new DataResponse(['msg' => $msg, 'code' => $code]);
442
	}
443
444
	/**
445
	 * get current timestamp
446
	 *
447
	 * @return int
448
	 */
449
	protected function getCurrentTime(): int {
450
		return time();
451
	}
452
453
	/**
454
	 * sign message with users private key
455
	 *
456
	 * @param IUser $user
457
	 * @param string $message
458
	 *
459
	 * @return string base64 encoded signature
460
	 */
461
	protected function signMessage(IUser $user, string $message): string {
462
		$privateKey = $this->keyManager->getKey($user)->getPrivate();
463
		openssl_sign(json_encode($message), $signature, $privateKey, OPENSSL_ALGO_SHA512);
464
		return base64_encode($signature);
465
	}
466
}
467