mirror of
https://github.com/nextcloud/server.git
synced 2026-03-04 18:28:08 +01:00
Previously, column visibility settings were stored in localStorage, causing them to be lost when logging out or switching browsers. This change moves the persistence to the database as user preferences. It also refactors the frontend to use clean `userList.*` keys for better consistency between the store and the API. Signed-off-by: Peter Ringelmann <4850521+Pringels@users.noreply.github.com>
611 lines
22 KiB
PHP
611 lines
22 KiB
PHP
<?php
|
|
|
|
declare(strict_types=1);
|
|
|
|
/**
|
|
* SPDX-FileCopyrightText: 2019-2024 Nextcloud GmbH and Nextcloud contributors
|
|
* SPDX-FileCopyrightText: 2016 ownCloud, Inc.
|
|
* SPDX-License-Identifier: AGPL-3.0-only
|
|
*/
|
|
|
|
namespace OCA\Settings\Controller;
|
|
|
|
use InvalidArgumentException;
|
|
use OC\AppFramework\Http;
|
|
use OC\Encryption\Exceptions\ModuleDoesNotExistsException;
|
|
use OC\ForbiddenException;
|
|
use OC\Group\MetaData;
|
|
use OC\KnownUser\KnownUserService;
|
|
use OC\Security\IdentityProof\Manager;
|
|
use OC\User\Manager as UserManager;
|
|
use OCA\Settings\BackgroundJobs\VerifyUserData;
|
|
use OCA\Settings\Events\BeforeTemplateRenderedEvent;
|
|
use OCA\Settings\Settings\Admin\Users;
|
|
use OCA\User_LDAP\User_Proxy;
|
|
use OCP\Accounts\IAccount;
|
|
use OCP\Accounts\IAccountManager;
|
|
use OCP\Accounts\PropertyDoesNotExistException;
|
|
use OCP\App\IAppManager;
|
|
use OCP\AppFramework\Controller;
|
|
use OCP\AppFramework\Http\Attribute\AuthorizedAdminSetting;
|
|
use OCP\AppFramework\Http\Attribute\NoAdminRequired;
|
|
use OCP\AppFramework\Http\Attribute\NoCSRFRequired;
|
|
use OCP\AppFramework\Http\Attribute\OpenAPI;
|
|
use OCP\AppFramework\Http\Attribute\PasswordConfirmationRequired;
|
|
use OCP\AppFramework\Http\Attribute\UserRateLimit;
|
|
use OCP\AppFramework\Http\DataResponse;
|
|
use OCP\AppFramework\Http\JSONResponse;
|
|
use OCP\AppFramework\Http\TemplateResponse;
|
|
use OCP\AppFramework\Services\IInitialState;
|
|
use OCP\BackgroundJob\IJobList;
|
|
use OCP\Config\IUserConfig;
|
|
use OCP\Encryption\IManager;
|
|
use OCP\EventDispatcher\IEventDispatcher;
|
|
use OCP\Group\ISubAdmin;
|
|
use OCP\IAppConfig;
|
|
use OCP\IConfig;
|
|
use OCP\IGroup;
|
|
use OCP\IGroupManager;
|
|
use OCP\IL10N;
|
|
use OCP\INavigationManager;
|
|
use OCP\IRequest;
|
|
use OCP\IUser;
|
|
use OCP\IUserSession;
|
|
use OCP\L10N\IFactory;
|
|
use OCP\Mail\IMailer;
|
|
use OCP\Util;
|
|
use function in_array;
|
|
|
|
#[OpenAPI(scope: OpenAPI::SCOPE_IGNORE)]
|
|
class UsersController extends Controller {
|
|
/** Limit for counting users for subadmins, to avoid spending too much time */
|
|
private const COUNT_LIMIT_FOR_SUBADMINS = 999;
|
|
|
|
public const ALLOWED_USER_PREFERENCES = [
|
|
'user_list_show_storage_path',
|
|
'user_list_show_user_backend',
|
|
'user_list_show_first_login',
|
|
'user_list_show_last_login',
|
|
'user_list_show_new_user_form',
|
|
'user_list_show_languages',
|
|
];
|
|
|
|
public function __construct(
|
|
string $appName,
|
|
IRequest $request,
|
|
private UserManager $userManager,
|
|
private IGroupManager $groupManager,
|
|
private IUserSession $userSession,
|
|
private IConfig $config,
|
|
private IAppConfig $appConfig,
|
|
private IUserConfig $userConfig,
|
|
private IL10N $l10n,
|
|
private IMailer $mailer,
|
|
private IFactory $l10nFactory,
|
|
private IAppManager $appManager,
|
|
private IAccountManager $accountManager,
|
|
private Manager $keyManager,
|
|
private IJobList $jobList,
|
|
private IManager $encryptionManager,
|
|
private KnownUserService $knownUserService,
|
|
private IEventDispatcher $dispatcher,
|
|
private IInitialState $initialState,
|
|
) {
|
|
parent::__construct($appName, $request);
|
|
}
|
|
|
|
|
|
/**
|
|
* Display users list template
|
|
*
|
|
* @return TemplateResponse
|
|
*/
|
|
#[NoAdminRequired]
|
|
#[NoCSRFRequired]
|
|
public function usersListByGroup(INavigationManager $navigationManager, ISubAdmin $subAdmin): TemplateResponse {
|
|
return $this->usersList($navigationManager, $subAdmin);
|
|
}
|
|
|
|
/**
|
|
* Display users list template
|
|
*
|
|
* @return TemplateResponse
|
|
*/
|
|
#[NoAdminRequired]
|
|
#[NoCSRFRequired]
|
|
public function usersList(INavigationManager $navigationManager, ISubAdmin $subAdmin): TemplateResponse {
|
|
$user = $this->userSession->getUser();
|
|
$uid = $user->getUID();
|
|
$isAdmin = $this->groupManager->isAdmin($uid);
|
|
$isDelegatedAdmin = $this->groupManager->isDelegatedAdmin($uid);
|
|
|
|
$navigationManager->setActiveEntry('core_users');
|
|
|
|
/* SORT OPTION: SORT_USERCOUNT or SORT_GROUPNAME */
|
|
$sortGroupsBy = MetaData::SORT_USERCOUNT;
|
|
$isLDAPUsed = false;
|
|
if ($this->config->getSystemValueBool('sort_groups_by_name', false)) {
|
|
$sortGroupsBy = MetaData::SORT_GROUPNAME;
|
|
} else {
|
|
if ($this->appManager->isEnabledForUser('user_ldap')) {
|
|
$isLDAPUsed
|
|
= $this->groupManager->isBackendUsed('\OCA\User_LDAP\Group_Proxy');
|
|
if ($isLDAPUsed) {
|
|
// LDAP user count can be slow, so we sort by group name here
|
|
$sortGroupsBy = MetaData::SORT_GROUPNAME;
|
|
}
|
|
}
|
|
}
|
|
|
|
$canChangePassword = $this->canAdminChangeUserPasswords();
|
|
|
|
/* GROUPS */
|
|
$groupsInfo = new MetaData(
|
|
$uid,
|
|
$isAdmin,
|
|
$isDelegatedAdmin,
|
|
$this->groupManager,
|
|
$this->userSession
|
|
);
|
|
|
|
$adminGroup = $this->groupManager->get('admin');
|
|
$adminGroupData = [
|
|
'id' => $adminGroup->getGID(),
|
|
'name' => $adminGroup->getDisplayName(),
|
|
'usercount' => $sortGroupsBy === MetaData::SORT_USERCOUNT ? $adminGroup->count() : 0,
|
|
'disabled' => $adminGroup->countDisabled(),
|
|
'canAdd' => $adminGroup->canAddUser(),
|
|
'canRemove' => $adminGroup->canRemoveUser(),
|
|
];
|
|
|
|
if (!$isLDAPUsed && $this->appManager->isEnabledForUser('user_ldap')) {
|
|
$isLDAPUsed = (bool)array_reduce($this->userManager->getBackends(), function ($ldapFound, $backend) {
|
|
return $ldapFound || $backend instanceof User_Proxy;
|
|
});
|
|
}
|
|
|
|
$disabledUsers = -1;
|
|
$userCount = 0;
|
|
|
|
if (!$isLDAPUsed) {
|
|
if ($isAdmin || $isDelegatedAdmin) {
|
|
$disabledUsers = $this->userManager->countDisabledUsers();
|
|
$userCount = array_reduce($this->userManager->countUsers(), function ($v, $w) {
|
|
return $v + (int)$w;
|
|
}, 0);
|
|
} else {
|
|
// User is subadmin !
|
|
[$userCount,$disabledUsers] = $this->userManager->countUsersAndDisabledUsersOfGroups($groupsInfo->getGroups(), self::COUNT_LIMIT_FOR_SUBADMINS);
|
|
}
|
|
|
|
if ($disabledUsers > 0) {
|
|
$userCount -= $disabledUsers;
|
|
}
|
|
}
|
|
|
|
$recentUsersGroup = [
|
|
'id' => '__nc_internal_recent',
|
|
'name' => $this->l10n->t('Recently active'),
|
|
'usercount' => $this->userManager->countSeenUsers(),
|
|
];
|
|
|
|
$disabledUsersGroup = [
|
|
'id' => 'disabled',
|
|
'name' => $this->l10n->t('Disabled accounts'),
|
|
'usercount' => $disabledUsers
|
|
];
|
|
|
|
if (!$isAdmin && !$isDelegatedAdmin) {
|
|
$subAdminGroups = array_map(
|
|
fn (IGroup $group) => ['id' => $group->getGID(), 'name' => $group->getDisplayName()],
|
|
$subAdmin->getSubAdminsGroups($user),
|
|
);
|
|
$subAdminGroups = array_values($subAdminGroups);
|
|
}
|
|
|
|
/* QUOTAS PRESETS */
|
|
$quotaPreset = $this->parseQuotaPreset($this->appConfig->getValueString('files', 'quota_preset', '1 GB, 5 GB, 10 GB'));
|
|
$allowUnlimitedQuota = $this->appConfig->getValueBool('files', 'allow_unlimited_quota', true);
|
|
if (!$allowUnlimitedQuota && count($quotaPreset) > 0) {
|
|
$defaultQuota = $this->appConfig->getValueString('files', 'default_quota', $quotaPreset[0]);
|
|
} else {
|
|
$defaultQuota = $this->appConfig->getValueString('files', 'default_quota', 'none');
|
|
}
|
|
|
|
$event = new BeforeTemplateRenderedEvent();
|
|
$this->dispatcher->dispatch('OC\Settings\Users::loadAdditionalScripts', $event);
|
|
$this->dispatcher->dispatchTyped($event);
|
|
|
|
/* LANGUAGES */
|
|
$languages = $this->l10nFactory->getLanguages();
|
|
|
|
/** Using LDAP or admins (system config) can enfore sorting by group name, in this case the frontend setting is overwritten */
|
|
$forceSortGroupByName = $sortGroupsBy === MetaData::SORT_GROUPNAME;
|
|
|
|
/* FINAL DATA */
|
|
$serverData = [];
|
|
// groups
|
|
$serverData['systemGroups'] = [$adminGroupData, $recentUsersGroup, $disabledUsersGroup];
|
|
$serverData['subAdminGroups'] = $subAdminGroups ?? [];
|
|
// Various data
|
|
$serverData['isAdmin'] = $isAdmin;
|
|
$serverData['isDelegatedAdmin'] = $isDelegatedAdmin;
|
|
$serverData['sortGroups'] = $forceSortGroupByName
|
|
? MetaData::SORT_GROUPNAME
|
|
: (int)$this->appConfig->getValueString('core', 'group.sortBy', (string)MetaData::SORT_USERCOUNT);
|
|
$serverData['forceSortGroupByName'] = $forceSortGroupByName;
|
|
$serverData['quotaPreset'] = $quotaPreset;
|
|
$serverData['allowUnlimitedQuota'] = $allowUnlimitedQuota;
|
|
$serverData['userCount'] = $userCount;
|
|
$serverData['languages'] = $languages;
|
|
$serverData['defaultLanguage'] = $this->config->getSystemValue('default_language', 'en');
|
|
$serverData['forceLanguage'] = $this->config->getSystemValue('force_language', false);
|
|
// Settings
|
|
$serverData['defaultQuota'] = $defaultQuota;
|
|
$serverData['canChangePassword'] = $canChangePassword;
|
|
$serverData['newUserGenerateUserID'] = $this->appConfig->getValueBool('core', 'newUser.generateUserID', false);
|
|
$serverData['newUserRequireEmail'] = $this->appConfig->getValueBool('core', 'newUser.requireEmail', false);
|
|
$serverData['newUserSendEmail'] = $this->appConfig->getValueBool('core', 'newUser.sendEmail', true);
|
|
$serverData['showConfig'] = [];
|
|
foreach (self::ALLOWED_USER_PREFERENCES as $key) {
|
|
$serverData['showConfig'][$key] = $this->userConfig->getValueBool($uid, $this->appName, $key, false);
|
|
}
|
|
|
|
$this->initialState->provideInitialState('usersSettings', $serverData);
|
|
|
|
Util::addStyle('settings', 'settings');
|
|
Util::addScript('settings', 'vue-settings-apps-users-management');
|
|
|
|
return new TemplateResponse('settings', 'settings/empty', ['pageTitle' => $this->l10n->t('Settings')]);
|
|
}
|
|
|
|
/**
|
|
* @param string $key
|
|
* @param string $value
|
|
*
|
|
* @return JSONResponse
|
|
*/
|
|
#[AuthorizedAdminSetting(settings:Users::class)]
|
|
public function setPreference(string $key, string $value): JSONResponse {
|
|
switch ($key) {
|
|
case 'newUser.sendEmail':
|
|
$this->appConfig->setValueBool('core', $key, $value === 'yes');
|
|
break;
|
|
case 'group.sortBy':
|
|
$this->appConfig->setValueString('core', $key, $value);
|
|
break;
|
|
default:
|
|
if (in_array($key, self::ALLOWED_USER_PREFERENCES, true)) {
|
|
$this->userConfig->setValueBool($this->userSession->getUser()->getUID(), $this->appName, $key, $value === 'true');
|
|
} else {
|
|
return new JSONResponse([], Http::STATUS_FORBIDDEN);
|
|
}
|
|
break;
|
|
}
|
|
|
|
return new JSONResponse([]);
|
|
}
|
|
|
|
/**
|
|
* Parse the app value for quota_present
|
|
*
|
|
* @param string $quotaPreset
|
|
* @return array
|
|
*/
|
|
protected function parseQuotaPreset(string $quotaPreset): array {
|
|
// 1 GB, 5 GB, 10 GB => [1 GB, 5 GB, 10 GB]
|
|
$presets = array_filter(array_map('trim', explode(',', $quotaPreset)));
|
|
// Drop default and none, Make array indexes numerically
|
|
return array_values(array_diff($presets, ['default', 'none']));
|
|
}
|
|
|
|
/**
|
|
* check if the admin can change the users password
|
|
*
|
|
* The admin can change the passwords if:
|
|
*
|
|
* - no encryption module is loaded and encryption is disabled
|
|
* - encryption module is loaded but it doesn't require per user keys
|
|
*
|
|
* The admin can not change the passwords if:
|
|
*
|
|
* - an encryption module is loaded and it uses per-user keys
|
|
* - encryption is enabled but no encryption modules are loaded
|
|
*
|
|
* @return bool
|
|
*/
|
|
protected function canAdminChangeUserPasswords(): bool {
|
|
$isEncryptionEnabled = $this->encryptionManager->isEnabled();
|
|
try {
|
|
$noUserSpecificEncryptionKeys = !$this->encryptionManager->getEncryptionModule()->needDetailedAccessList();
|
|
$isEncryptionModuleLoaded = true;
|
|
} catch (ModuleDoesNotExistsException $e) {
|
|
$noUserSpecificEncryptionKeys = true;
|
|
$isEncryptionModuleLoaded = false;
|
|
}
|
|
$canChangePassword = ($isEncryptionModuleLoaded && $noUserSpecificEncryptionKeys)
|
|
|| (!$isEncryptionModuleLoaded && !$isEncryptionEnabled);
|
|
|
|
return $canChangePassword;
|
|
}
|
|
|
|
/**
|
|
* @NoSubAdminRequired
|
|
*
|
|
* @param string|null $avatarScope
|
|
* @param string|null $displayname
|
|
* @param string|null $displaynameScope
|
|
* @param string|null $phone
|
|
* @param string|null $phoneScope
|
|
* @param string|null $email
|
|
* @param string|null $emailScope
|
|
* @param string|null $website
|
|
* @param string|null $websiteScope
|
|
* @param string|null $address
|
|
* @param string|null $addressScope
|
|
* @param string|null $twitter
|
|
* @param string|null $twitterScope
|
|
* @param string|null $bluesky
|
|
* @param string|null $blueskyScope
|
|
* @param string|null $fediverse
|
|
* @param string|null $fediverseScope
|
|
* @param string|null $birthdate
|
|
* @param string|null $birthdateScope
|
|
*
|
|
* @return DataResponse
|
|
*/
|
|
#[NoAdminRequired]
|
|
#[PasswordConfirmationRequired]
|
|
#[UserRateLimit(limit: 5, period: 60)]
|
|
public function setUserSettings(?string $avatarScope = null,
|
|
?string $displayname = null,
|
|
?string $displaynameScope = null,
|
|
?string $phone = null,
|
|
?string $phoneScope = null,
|
|
?string $email = null,
|
|
?string $emailScope = null,
|
|
?string $website = null,
|
|
?string $websiteScope = null,
|
|
?string $address = null,
|
|
?string $addressScope = null,
|
|
?string $twitter = null,
|
|
?string $twitterScope = null,
|
|
?string $bluesky = null,
|
|
?string $blueskyScope = null,
|
|
?string $fediverse = null,
|
|
?string $fediverseScope = null,
|
|
?string $birthdate = null,
|
|
?string $birthdateScope = null,
|
|
?string $pronouns = null,
|
|
?string $pronounsScope = null,
|
|
) {
|
|
$user = $this->userSession->getUser();
|
|
if (!$user instanceof IUser) {
|
|
return new DataResponse(
|
|
[
|
|
'status' => 'error',
|
|
'data' => [
|
|
'message' => $this->l10n->t('Invalid account')
|
|
]
|
|
],
|
|
Http::STATUS_UNAUTHORIZED
|
|
);
|
|
}
|
|
|
|
$email = !is_null($email) ? strtolower($email) : $email;
|
|
if (!empty($email) && !$this->mailer->validateMailAddress($email)) {
|
|
return new DataResponse(
|
|
[
|
|
'status' => 'error',
|
|
'data' => [
|
|
'message' => $this->l10n->t('Invalid mail address')
|
|
]
|
|
],
|
|
Http::STATUS_UNPROCESSABLE_ENTITY
|
|
);
|
|
}
|
|
|
|
$userAccount = $this->accountManager->getAccount($user);
|
|
$oldPhoneValue = $userAccount->getProperty(IAccountManager::PROPERTY_PHONE)->getValue();
|
|
|
|
$updatable = [
|
|
IAccountManager::PROPERTY_AVATAR => ['value' => null, 'scope' => $avatarScope],
|
|
IAccountManager::PROPERTY_DISPLAYNAME => ['value' => $displayname, 'scope' => $displaynameScope],
|
|
IAccountManager::PROPERTY_EMAIL => ['value' => $email, 'scope' => $emailScope],
|
|
IAccountManager::PROPERTY_WEBSITE => ['value' => $website, 'scope' => $websiteScope],
|
|
IAccountManager::PROPERTY_ADDRESS => ['value' => $address, 'scope' => $addressScope],
|
|
IAccountManager::PROPERTY_PHONE => ['value' => $phone, 'scope' => $phoneScope],
|
|
IAccountManager::PROPERTY_TWITTER => ['value' => $twitter, 'scope' => $twitterScope],
|
|
IAccountManager::PROPERTY_BLUESKY => ['value' => $bluesky, 'scope' => $blueskyScope],
|
|
IAccountManager::PROPERTY_FEDIVERSE => ['value' => $fediverse, 'scope' => $fediverseScope],
|
|
IAccountManager::PROPERTY_BIRTHDATE => ['value' => $birthdate, 'scope' => $birthdateScope],
|
|
IAccountManager::PROPERTY_PRONOUNS => ['value' => $pronouns, 'scope' => $pronounsScope],
|
|
];
|
|
$allowUserToChangeDisplayName = $this->config->getSystemValueBool('allow_user_to_change_display_name', true);
|
|
foreach ($updatable as $property => $data) {
|
|
if ($allowUserToChangeDisplayName === false
|
|
&& in_array($property, [IAccountManager::PROPERTY_DISPLAYNAME, IAccountManager::PROPERTY_EMAIL], true)) {
|
|
continue;
|
|
}
|
|
$property = $userAccount->getProperty($property);
|
|
if ($data['value'] !== null) {
|
|
$property->setValue($data['value']);
|
|
}
|
|
if ($data['scope'] !== null) {
|
|
$property->setScope($data['scope']);
|
|
}
|
|
}
|
|
|
|
try {
|
|
$this->saveUserSettings($userAccount);
|
|
if ($oldPhoneValue !== $userAccount->getProperty(IAccountManager::PROPERTY_PHONE)->getValue()) {
|
|
$this->knownUserService->deleteByContactUserId($user->getUID());
|
|
}
|
|
return new DataResponse(
|
|
[
|
|
'status' => 'success',
|
|
'data' => [
|
|
'userId' => $user->getUID(),
|
|
'avatarScope' => $userAccount->getProperty(IAccountManager::PROPERTY_AVATAR)->getScope(),
|
|
'displayname' => $userAccount->getProperty(IAccountManager::PROPERTY_DISPLAYNAME)->getValue(),
|
|
'displaynameScope' => $userAccount->getProperty(IAccountManager::PROPERTY_DISPLAYNAME)->getScope(),
|
|
'phone' => $userAccount->getProperty(IAccountManager::PROPERTY_PHONE)->getValue(),
|
|
'phoneScope' => $userAccount->getProperty(IAccountManager::PROPERTY_PHONE)->getScope(),
|
|
'email' => $userAccount->getProperty(IAccountManager::PROPERTY_EMAIL)->getValue(),
|
|
'emailScope' => $userAccount->getProperty(IAccountManager::PROPERTY_EMAIL)->getScope(),
|
|
'website' => $userAccount->getProperty(IAccountManager::PROPERTY_WEBSITE)->getValue(),
|
|
'websiteScope' => $userAccount->getProperty(IAccountManager::PROPERTY_WEBSITE)->getScope(),
|
|
'address' => $userAccount->getProperty(IAccountManager::PROPERTY_ADDRESS)->getValue(),
|
|
'addressScope' => $userAccount->getProperty(IAccountManager::PROPERTY_ADDRESS)->getScope(),
|
|
'twitter' => $userAccount->getProperty(IAccountManager::PROPERTY_TWITTER)->getValue(),
|
|
'twitterScope' => $userAccount->getProperty(IAccountManager::PROPERTY_TWITTER)->getScope(),
|
|
'bluesky' => $userAccount->getProperty(IAccountManager::PROPERTY_BLUESKY)->getValue(),
|
|
'blueskyScope' => $userAccount->getProperty(IAccountManager::PROPERTY_BLUESKY)->getScope(),
|
|
'fediverse' => $userAccount->getProperty(IAccountManager::PROPERTY_FEDIVERSE)->getValue(),
|
|
'fediverseScope' => $userAccount->getProperty(IAccountManager::PROPERTY_FEDIVERSE)->getScope(),
|
|
'birthdate' => $userAccount->getProperty(IAccountManager::PROPERTY_BIRTHDATE)->getValue(),
|
|
'birthdateScope' => $userAccount->getProperty(IAccountManager::PROPERTY_BIRTHDATE)->getScope(),
|
|
'pronouns' => $userAccount->getProperty(IAccountManager::PROPERTY_PRONOUNS)->getValue(),
|
|
'pronounsScope' => $userAccount->getProperty(IAccountManager::PROPERTY_PRONOUNS)->getScope(),
|
|
'message' => $this->l10n->t('Settings saved'),
|
|
],
|
|
],
|
|
Http::STATUS_OK
|
|
);
|
|
} catch (ForbiddenException|InvalidArgumentException|PropertyDoesNotExistException $e) {
|
|
return new DataResponse([
|
|
'status' => 'error',
|
|
'data' => [
|
|
'message' => $e->getMessage()
|
|
],
|
|
]);
|
|
}
|
|
}
|
|
/**
|
|
* update account manager with new user data
|
|
*
|
|
* @throws ForbiddenException
|
|
* @throws InvalidArgumentException
|
|
*/
|
|
protected function saveUserSettings(IAccount $userAccount): void {
|
|
// keep the user back-end up-to-date with the latest display name and email
|
|
// address
|
|
$oldDisplayName = $userAccount->getUser()->getDisplayName();
|
|
if ($oldDisplayName !== $userAccount->getProperty(IAccountManager::PROPERTY_DISPLAYNAME)->getValue()) {
|
|
$result = $userAccount->getUser()->setDisplayName($userAccount->getProperty(IAccountManager::PROPERTY_DISPLAYNAME)->getValue());
|
|
if ($result === false) {
|
|
throw new ForbiddenException($this->l10n->t('Unable to change full name'));
|
|
}
|
|
}
|
|
|
|
$oldEmailAddress = $userAccount->getUser()->getSystemEMailAddress();
|
|
$oldEmailAddress = strtolower((string)$oldEmailAddress);
|
|
if ($oldEmailAddress !== strtolower($userAccount->getProperty(IAccountManager::PROPERTY_EMAIL)->getValue())) {
|
|
// this is the only permission a backend provides and is also used
|
|
// for the permission of setting a email address
|
|
if (!$userAccount->getUser()->canChangeDisplayName()) {
|
|
throw new ForbiddenException($this->l10n->t('Unable to change email address'));
|
|
}
|
|
$userAccount->getUser()->setSystemEMailAddress($userAccount->getProperty(IAccountManager::PROPERTY_EMAIL)->getValue());
|
|
}
|
|
|
|
try {
|
|
$this->accountManager->updateAccount($userAccount);
|
|
} catch (InvalidArgumentException $e) {
|
|
if ($e->getMessage() === IAccountManager::PROPERTY_PHONE) {
|
|
throw new InvalidArgumentException($this->l10n->t('Unable to set invalid phone number'));
|
|
}
|
|
if ($e->getMessage() === IAccountManager::PROPERTY_WEBSITE) {
|
|
throw new InvalidArgumentException($this->l10n->t('Unable to set invalid website'));
|
|
}
|
|
throw new InvalidArgumentException($this->l10n->t('Some account data was invalid'));
|
|
}
|
|
}
|
|
|
|
/**
|
|
* Set the mail address of a user
|
|
*
|
|
* @NoSubAdminRequired
|
|
*
|
|
* @param string $account
|
|
* @param bool $onlyVerificationCode only return verification code without updating the data
|
|
* @return DataResponse
|
|
*/
|
|
#[NoAdminRequired]
|
|
#[PasswordConfirmationRequired]
|
|
public function getVerificationCode(string $account, bool $onlyVerificationCode): DataResponse {
|
|
$user = $this->userSession->getUser();
|
|
|
|
if ($user === null) {
|
|
return new DataResponse([], Http::STATUS_BAD_REQUEST);
|
|
}
|
|
|
|
$userAccount = $this->accountManager->getAccount($user);
|
|
$cloudId = $user->getCloudId();
|
|
$message = 'Use my Federated Cloud ID to share with me: ' . $cloudId;
|
|
$signature = $this->signMessage($user, $message);
|
|
|
|
$code = $message . ' ' . $signature;
|
|
$codeMd5 = $message . ' ' . md5($signature);
|
|
|
|
switch ($account) {
|
|
case 'verify-twitter':
|
|
$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):');
|
|
$code = $codeMd5;
|
|
$type = IAccountManager::PROPERTY_TWITTER;
|
|
break;
|
|
case 'verify-website':
|
|
$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):');
|
|
$type = IAccountManager::PROPERTY_WEBSITE;
|
|
break;
|
|
default:
|
|
return new DataResponse([], Http::STATUS_BAD_REQUEST);
|
|
}
|
|
|
|
$userProperty = $userAccount->getProperty($type);
|
|
$userProperty
|
|
->setVerified(IAccountManager::VERIFICATION_IN_PROGRESS)
|
|
->setVerificationData($signature);
|
|
|
|
if ($onlyVerificationCode === false) {
|
|
$this->accountManager->updateAccount($userAccount);
|
|
|
|
$this->jobList->add(VerifyUserData::class,
|
|
[
|
|
'verificationCode' => $code,
|
|
'data' => $userProperty->getValue(),
|
|
'type' => $type,
|
|
'uid' => $user->getUID(),
|
|
'try' => 0,
|
|
'lastRun' => $this->getCurrentTime()
|
|
]
|
|
);
|
|
}
|
|
|
|
return new DataResponse(['msg' => $msg, 'code' => $code]);
|
|
}
|
|
|
|
/**
|
|
* get current timestamp
|
|
*
|
|
* @return int
|
|
*/
|
|
protected function getCurrentTime(): int {
|
|
return time();
|
|
}
|
|
|
|
/**
|
|
* sign message with users private key
|
|
*
|
|
* @param IUser $user
|
|
* @param string $message
|
|
*
|
|
* @return string base64 encoded signature
|
|
*/
|
|
protected function signMessage(IUser $user, string $message): string {
|
|
$privateKey = $this->keyManager->getKey($user)->getPrivate();
|
|
openssl_sign(json_encode($message), $signature, $privateKey, OPENSSL_ALGO_SHA512);
|
|
return base64_encode($signature);
|
|
}
|
|
}
|