diff --git a/apps/provisioning_api/lib/Controller/AUserData.php b/apps/provisioning_api/lib/Controller/AUserData.php index b7b31b18b5..73909a319f 100644 --- a/apps/provisioning_api/lib/Controller/AUserData.php +++ b/apps/provisioning_api/lib/Controller/AUserData.php @@ -50,6 +50,7 @@ use OCP\User\Backend\ISetDisplayNameBackend; use OCP\User\Backend\ISetPasswordBackend; abstract class AUserData extends OCSController { + public const SCOPE_SUFFIX = 'Scope'; /** @var IUserManager */ protected $userManager; @@ -86,12 +87,13 @@ abstract class AUserData extends OCSController { * creates a array with all user data * * @param string $userId + * @param bool $includeScopes * @return array * @throws NotFoundException * @throws OCSException * @throws OCSNotFoundException */ - protected function getUserData(string $userId): array { + protected function getUserData(string $userId, bool $includeScopes = false): array { $currentLoggedInUser = $this->userSession->getUser(); $data = []; @@ -114,7 +116,7 @@ abstract class AUserData extends OCSController { } // Get groups data - $userAccount = $this->accountManager->getUser($targetUserObject); + $userAccount = $this->accountManager->getAccount($targetUserObject); $groups = $this->groupManager->getUserGroups($targetUserObject); $gids = []; foreach ($groups as $group) { @@ -137,11 +139,26 @@ abstract class AUserData extends OCSController { $data['subadmin'] = $this->getUserSubAdminGroupsData($targetUserObject->getUID()); $data['quota'] = $this->fillStorageInfo($targetUserObject->getUID()); $data[IAccountManager::PROPERTY_EMAIL] = $targetUserObject->getEMailAddress(); + if ($includeScopes) { + $data[IAccountManager::PROPERTY_EMAIL . self::SCOPE_SUFFIX] = $userAccount->getProperty(IAccountManager::PROPERTY_EMAIL)->getScope(); + } $data[IAccountManager::PROPERTY_DISPLAYNAME] = $targetUserObject->getDisplayName(); - $data[IAccountManager::PROPERTY_PHONE] = $userAccount[IAccountManager::PROPERTY_PHONE]['value']; - $data[IAccountManager::PROPERTY_ADDRESS] = $userAccount[IAccountManager::PROPERTY_ADDRESS]['value']; - $data[IAccountManager::PROPERTY_WEBSITE] = $userAccount[IAccountManager::PROPERTY_WEBSITE]['value']; - $data[IAccountManager::PROPERTY_TWITTER] = $userAccount[IAccountManager::PROPERTY_TWITTER]['value']; + if ($includeScopes) { + $data[IAccountManager::PROPERTY_DISPLAYNAME . self::SCOPE_SUFFIX] = $userAccount->getProperty(IAccountManager::PROPERTY_DISPLAYNAME)->getScope(); + } + + foreach ([ + IAccountManager::PROPERTY_PHONE, + IAccountManager::PROPERTY_ADDRESS, + IAccountManager::PROPERTY_WEBSITE, + IAccountManager::PROPERTY_TWITTER, + ] as $propertyName) { + $property = $userAccount->getProperty($propertyName); + $data[$propertyName] = $property->getValue(); + if ($includeScopes) { + $data[$propertyName . self::SCOPE_SUFFIX] = $property->getScope(); + } + } $data['groups'] = $gids; $data['language'] = $this->l10nFactory->getUserLanguage($targetUserObject); $data['locale'] = $this->config->getUserValue($targetUserObject->getUID(), 'core', 'locale'); diff --git a/apps/provisioning_api/lib/Controller/UsersController.php b/apps/provisioning_api/lib/Controller/UsersController.php index 1c9b63a01c..eb0bf978df 100644 --- a/apps/provisioning_api/lib/Controller/UsersController.php +++ b/apps/provisioning_api/lib/Controller/UsersController.php @@ -470,7 +470,13 @@ class UsersController extends AUserData { * @throws OCSException */ public function getUser(string $userId): DataResponse { - $data = $this->getUserData($userId); + $includeScopes = false; + $currentUser = $this->userSession->getUser(); + if ($currentUser && $currentUser->getUID() === $userId) { + $includeScopes = true; + } + + $data = $this->getUserData($userId, $includeScopes); // getUserData returns empty array if not enough permissions if (empty($data)) { throw new OCSException('', \OCP\API::RESPOND_UNAUTHORISED); @@ -490,7 +496,7 @@ class UsersController extends AUserData { public function getCurrentUser(): DataResponse { $user = $this->userSession->getUser(); if ($user) { - $data = $this->getUserData($user->getUID()); + $data = $this->getUserData($user->getUID(), true); // rename "displayname" to "display-name" only for this call to keep // the API stable. $data['display-name'] = $data['displayname']; @@ -552,6 +558,9 @@ class UsersController extends AUserData { $permittedFields[] = IAccountManager::PROPERTY_EMAIL; } + $permittedFields[] = IAccountManager::PROPERTY_DISPLAYNAME . self::SCOPE_SUFFIX; + $permittedFields[] = IAccountManager::PROPERTY_EMAIL . self::SCOPE_SUFFIX; + $permittedFields[] = 'password'; if ($this->config->getSystemValue('force_language', false) === false || $this->groupManager->isAdmin($currentLoggedInUser->getUID())) { @@ -567,6 +576,10 @@ class UsersController extends AUserData { $permittedFields[] = IAccountManager::PROPERTY_ADDRESS; $permittedFields[] = IAccountManager::PROPERTY_WEBSITE; $permittedFields[] = IAccountManager::PROPERTY_TWITTER; + $permittedFields[] = IAccountManager::PROPERTY_PHONE . self::SCOPE_SUFFIX; + $permittedFields[] = IAccountManager::PROPERTY_ADDRESS . self::SCOPE_SUFFIX; + $permittedFields[] = IAccountManager::PROPERTY_WEBSITE . self::SCOPE_SUFFIX; + $permittedFields[] = IAccountManager::PROPERTY_TWITTER . self::SCOPE_SUFFIX; // If admin they can edit their own quota if ($this->groupManager->isAdmin($currentLoggedInUser->getUID())) { @@ -671,6 +684,23 @@ class UsersController extends AUserData { } } break; + case IAccountManager::PROPERTY_DISPLAYNAME . self::SCOPE_SUFFIX: + case IAccountManager::PROPERTY_EMAIL . self::SCOPE_SUFFIX: + case IAccountManager::PROPERTY_PHONE . self::SCOPE_SUFFIX: + case IAccountManager::PROPERTY_ADDRESS . self::SCOPE_SUFFIX: + case IAccountManager::PROPERTY_WEBSITE . self::SCOPE_SUFFIX: + case IAccountManager::PROPERTY_TWITTER . self::SCOPE_SUFFIX: + $propertyName = substr($key, 0, strlen($key) - strlen(self::SCOPE_SUFFIX)); + $userAccount = $this->accountManager->getUser($targetUser); + if ($userAccount[$propertyName]['scope'] !== $value) { + $userAccount[$propertyName]['scope'] = $value; + try { + $this->accountManager->updateUser($targetUser, $userAccount, true); + } catch (\InvalidArgumentException $e) { + throw new OCSException('Invalid ' . $e->getMessage(), 102); + } + } + break; default: throw new OCSException('', 103); } diff --git a/lib/private/Accounts/AccountManager.php b/lib/private/Accounts/AccountManager.php index ff3b04d839..74ba53737c 100644 --- a/lib/private/Accounts/AccountManager.php +++ b/lib/private/Accounts/AccountManager.php @@ -144,6 +144,37 @@ class AccountManager implements IAccountManager { } } + $allowedScopes = [ + self::SCOPE_PRIVATE, + self::SCOPE_LOCAL, + self::SCOPE_FEDERATED, + self::SCOPE_PUBLISHED, + self::VISIBILITY_PRIVATE, + self::VISIBILITY_CONTACTS_ONLY, + self::VISIBILITY_PUBLIC, + ]; + + // validate and convert scope values + foreach ($data as $propertyName => $propertyData) { + if (isset($propertyData['scope'])) { + if ($throwOnData && !in_array($propertyData['scope'], $allowedScopes, true)) { + throw new \InvalidArgumentException('scope'); + } + + if ( + $propertyData['scope'] === self::SCOPE_PRIVATE + && ($propertyName === self::PROPERTY_DISPLAYNAME || $propertyName === self::PROPERTY_EMAIL) + ) { + // v2-private is not available for these fields + throw new \InvalidArgumentException('scope'); + } + + // migrate scope values to the new format + // invalid scopes are mapped to a default value + $data[$propertyName]['scope'] = AccountProperty::mapScopeToV2($propertyData['scope']); + } + } + if (empty($userData)) { $this->insertNewUser($user, $data); } elseif ($userData !== $data) { @@ -405,7 +436,7 @@ class AccountManager implements IAccountManager { } $query->setParameter('name', $propertyName) - ->setParameter('value', $property['value']); + ->setParameter('value', $property['value'] ?? ''); $query->execute(); } } diff --git a/lib/private/Accounts/AccountProperty.php b/lib/private/Accounts/AccountProperty.php index 4c75ad8541..850f39df9e 100644 --- a/lib/private/Accounts/AccountProperty.php +++ b/lib/private/Accounts/AccountProperty.php @@ -128,7 +128,7 @@ class AccountProperty implements IAccountProperty { return $this->scope; } - private function mapScopeToV2($scope) { + public static function mapScopeToV2($scope) { if (strpos($scope, 'v2-') === 0) { return $scope; }