diff options
Diffstat (limited to 'core/Controller/ProfileApiController.php')
-rw-r--r-- | core/Controller/ProfileApiController.php | 147 |
1 files changed, 97 insertions, 50 deletions
diff --git a/core/Controller/ProfileApiController.php b/core/Controller/ProfileApiController.php index d9e20701eaa..02979cb1649 100644 --- a/core/Controller/ProfileApiController.php +++ b/core/Controller/ProfileApiController.php @@ -3,83 +3,79 @@ declare(strict_types=1); /** - * @copyright 2021 Christopher Ng <chrng8@gmail.com> - * - * @author Christopher Ng <chrng8@gmail.com> - * - * @license GNU AGPL version 3 or any later version - * - * This program is free software: you can redistribute it and/or modify - * it under the terms of the GNU Affero General Public License as - * published by the Free Software Foundation, either version 3 of the - * License, or (at your option) any later version. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU Affero General Public License for more details. - * - * You should have received a copy of the GNU Affero General Public License - * along with this program. If not, see <http://www.gnu.org/licenses/>. - * + * SPDX-FileCopyrightText: 2021 Nextcloud GmbH and Nextcloud contributors + * SPDX-License-Identifier: AGPL-3.0-or-later */ namespace OC\Core\Controller; use OC\Core\Db\ProfileConfigMapper; +use OC\Core\ResponseDefinitions; +use OC\Profile\ProfileManager; +use OCP\AppFramework\Http; +use OCP\AppFramework\Http\Attribute\ApiRoute; +use OCP\AppFramework\Http\Attribute\BruteForceProtection; +use OCP\AppFramework\Http\Attribute\NoAdminRequired; +use OCP\AppFramework\Http\Attribute\PasswordConfirmationRequired; +use OCP\AppFramework\Http\Attribute\UserRateLimit; use OCP\AppFramework\Http\DataResponse; use OCP\AppFramework\OCS\OCSBadRequestException; use OCP\AppFramework\OCS\OCSForbiddenException; use OCP\AppFramework\OCS\OCSNotFoundException; use OCP\AppFramework\OCSController; +use OCP\AppFramework\Utility\ITimeFactory; +use OCP\IConfig; use OCP\IRequest; +use OCP\IUser; use OCP\IUserManager; use OCP\IUserSession; -use OC\Profile\ProfileManager; +use OCP\Share\IManager; +/** + * @psalm-import-type CoreProfileData from ResponseDefinitions + */ class ProfileApiController extends OCSController { - - /** @var ProfileConfigMapper */ - private $configMapper; - - /** @var ProfileManager */ - private $profileManager; - - /** @var IUserManager */ - private $userManager; - - /** @var IUserSession */ - private $userSession; - public function __construct( IRequest $request, - ProfileConfigMapper $configMapper, - ProfileManager $profileManager, - IUserManager $userManager, - IUserSession $userSession + private IConfig $config, + private ITimeFactory $timeFactory, + private ProfileConfigMapper $configMapper, + private ProfileManager $profileManager, + private IUserManager $userManager, + private IUserSession $userSession, + private IManager $shareManager, ) { parent::__construct('core', $request); - $this->configMapper = $configMapper; - $this->profileManager = $profileManager; - $this->userManager = $userManager; - $this->userSession = $userSession; } /** - * @NoAdminRequired * @NoSubAdminRequired - * @PasswordConfirmationRequired + * + * Update the visibility of a parameter + * + * @param string $targetUserId ID of the user + * @param string $paramId ID of the parameter + * @param string $visibility New visibility + * @return DataResponse<Http::STATUS_OK, list<empty>, array{}> + * @throws OCSBadRequestException Updating visibility is not possible + * @throws OCSForbiddenException Not allowed to edit other users visibility + * @throws OCSNotFoundException Account not found + * + * 200: Visibility updated successfully */ + #[NoAdminRequired] + #[PasswordConfirmationRequired] + #[UserRateLimit(limit: 40, period: 600)] + #[ApiRoute(verb: 'PUT', url: '/{targetUserId}', root: '/profile')] public function setVisibility(string $targetUserId, string $paramId, string $visibility): DataResponse { $requestingUser = $this->userSession->getUser(); - $targetUser = $this->userManager->get($targetUserId); - - if (!$this->userManager->userExists($targetUserId)) { - throw new OCSNotFoundException('User does not exist'); + if ($requestingUser->getUID() !== $targetUserId) { + throw new OCSForbiddenException('People can only edit their own visibility settings'); } - if ($requestingUser !== $targetUser) { - throw new OCSForbiddenException('Users can only edit their own visibility settings'); + $targetUser = $this->userManager->get($targetUserId); + if (!$targetUser instanceof IUser) { + throw new OCSNotFoundException('Account does not exist'); } // Ensure that a profile config is created in the database @@ -87,7 +83,7 @@ class ProfileApiController extends OCSController { $config = $this->configMapper->get($targetUserId); if (!in_array($paramId, array_keys($config->getVisibilityMap()), true)) { - throw new OCSBadRequestException('User does not have a profile parameter with ID: ' . $paramId); + throw new OCSBadRequestException('Account does not have a profile parameter with ID: ' . $paramId); } $config->setVisibility($paramId, $visibility); @@ -95,4 +91,55 @@ class ProfileApiController extends OCSController { return new DataResponse(); } + + /** + * Get profile fields for another user + * + * @param string $targetUserId ID of the user + * @return DataResponse<Http::STATUS_OK, CoreProfileData, array{}>|DataResponse<Http::STATUS_BAD_REQUEST|Http::STATUS_NOT_FOUND, null, array{}> + * + * 200: Profile data returned successfully + * 400: Profile is disabled + * 404: Account not found or disabled + */ + #[NoAdminRequired] + #[ApiRoute(verb: 'GET', url: '/{targetUserId}', root: '/profile')] + #[BruteForceProtection(action: 'user')] + #[UserRateLimit(limit: 30, period: 120)] + public function getProfileFields(string $targetUserId): DataResponse { + $targetUser = $this->userManager->get($targetUserId); + if (!$targetUser instanceof IUser) { + $response = new DataResponse(null, Http::STATUS_NOT_FOUND); + $response->throttle(); + return $response; + } + if (!$targetUser->isEnabled()) { + return new DataResponse(null, Http::STATUS_NOT_FOUND); + } + + if (!$this->profileManager->isProfileEnabled($targetUser)) { + return new DataResponse(null, Http::STATUS_BAD_REQUEST); + } + + $requestingUser = $this->userSession->getUser(); + if ($targetUser !== $requestingUser) { + if (!$this->shareManager->currentUserCanEnumerateTargetUser($requestingUser, $targetUser)) { + return new DataResponse(null, Http::STATUS_NOT_FOUND); + } + } + + $profileFields = $this->profileManager->getProfileFields($targetUser, $requestingUser); + + // Extend the profile information with timezone of the user + $timezoneStringTarget = $this->config->getUserValue($targetUser->getUID(), 'core', 'timezone') ?: $this->config->getSystemValueString('default_timezone', 'UTC'); + try { + $timezoneTarget = new \DateTimeZone($timezoneStringTarget); + } catch (\Throwable) { + $timezoneTarget = new \DateTimeZone('UTC'); + } + $profileFields['timezone'] = $timezoneTarget->getName(); // E.g. Europe/Berlin + $profileFields['timezoneOffset'] = $timezoneTarget->getOffset($this->timeFactory->now()); // In seconds E.g. 7200 + + return new DataResponse($profileFields); + } } |