You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

145 lines
5.0 KiB

  1. <?php
  2. declare(strict_types=1);
  3. /**
  4. * SPDX-FileCopyrightText: 2021 Nextcloud GmbH and Nextcloud contributors
  5. * SPDX-License-Identifier: AGPL-3.0-or-later
  6. */
  7. namespace OC\Core\Controller;
  8. use OC\Core\Db\ProfileConfigMapper;
  9. use OC\Core\ResponseDefinitions;
  10. use OC\Profile\ProfileManager;
  11. use OCP\AppFramework\Http;
  12. use OCP\AppFramework\Http\Attribute\ApiRoute;
  13. use OCP\AppFramework\Http\Attribute\BruteForceProtection;
  14. use OCP\AppFramework\Http\Attribute\NoAdminRequired;
  15. use OCP\AppFramework\Http\Attribute\PasswordConfirmationRequired;
  16. use OCP\AppFramework\Http\Attribute\UserRateLimit;
  17. use OCP\AppFramework\Http\DataResponse;
  18. use OCP\AppFramework\OCS\OCSBadRequestException;
  19. use OCP\AppFramework\OCS\OCSForbiddenException;
  20. use OCP\AppFramework\OCS\OCSNotFoundException;
  21. use OCP\AppFramework\OCSController;
  22. use OCP\AppFramework\Utility\ITimeFactory;
  23. use OCP\IConfig;
  24. use OCP\IRequest;
  25. use OCP\IUser;
  26. use OCP\IUserManager;
  27. use OCP\IUserSession;
  28. use OCP\Share\IManager;
  29. /**
  30. * @psalm-import-type CoreProfileData from ResponseDefinitions
  31. */
  32. class ProfileApiController extends OCSController {
  33. public function __construct(
  34. IRequest $request,
  35. private IConfig $config,
  36. private ITimeFactory $timeFactory,
  37. private ProfileConfigMapper $configMapper,
  38. private ProfileManager $profileManager,
  39. private IUserManager $userManager,
  40. private IUserSession $userSession,
  41. private IManager $shareManager,
  42. ) {
  43. parent::__construct('core', $request);
  44. }
  45. /**
  46. * @NoSubAdminRequired
  47. *
  48. * Update the visibility of a parameter
  49. *
  50. * @param string $targetUserId ID of the user
  51. * @param string $paramId ID of the parameter
  52. * @param string $visibility New visibility
  53. * @return DataResponse<Http::STATUS_OK, list<empty>, array{}>
  54. * @throws OCSBadRequestException Updating visibility is not possible
  55. * @throws OCSForbiddenException Not allowed to edit other users visibility
  56. * @throws OCSNotFoundException Account not found
  57. *
  58. * 200: Visibility updated successfully
  59. */
  60. #[NoAdminRequired]
  61. #[PasswordConfirmationRequired]
  62. #[UserRateLimit(limit: 40, period: 600)]
  63. #[ApiRoute(verb: 'PUT', url: '/{targetUserId}', root: '/profile')]
  64. public function setVisibility(string $targetUserId, string $paramId, string $visibility): DataResponse {
  65. $requestingUser = $this->userSession->getUser();
  66. if ($requestingUser->getUID() !== $targetUserId) {
  67. throw new OCSForbiddenException('People can only edit their own visibility settings');
  68. }
  69. $targetUser = $this->userManager->get($targetUserId);
  70. if (!$targetUser instanceof IUser) {
  71. throw new OCSNotFoundException('Account does not exist');
  72. }
  73. // Ensure that a profile config is created in the database
  74. $this->profileManager->getProfileConfig($targetUser, $targetUser);
  75. $config = $this->configMapper->get($targetUserId);
  76. if (!in_array($paramId, array_keys($config->getVisibilityMap()), true)) {
  77. throw new OCSBadRequestException('Account does not have a profile parameter with ID: ' . $paramId);
  78. }
  79. $config->setVisibility($paramId, $visibility);
  80. $this->configMapper->update($config);
  81. return new DataResponse();
  82. }
  83. /**
  84. * Get profile fields for another user
  85. *
  86. * @param string $targetUserId ID of the user
  87. * @return DataResponse<Http::STATUS_OK, CoreProfileData, array{}>|DataResponse<Http::STATUS_BAD_REQUEST|Http::STATUS_NOT_FOUND, null, array{}>
  88. *
  89. * 200: Profile data returned successfully
  90. * 400: Profile is disabled
  91. * 404: Account not found or disabled
  92. */
  93. #[NoAdminRequired]
  94. #[ApiRoute(verb: 'GET', url: '/{targetUserId}', root: '/profile')]
  95. #[BruteForceProtection(action: 'user')]
  96. #[UserRateLimit(limit: 30, period: 120)]
  97. public function getProfileFields(string $targetUserId): DataResponse {
  98. $targetUser = $this->userManager->get($targetUserId);
  99. if (!$targetUser instanceof IUser) {
  100. $response = new DataResponse(null, Http::STATUS_NOT_FOUND);
  101. $response->throttle();
  102. return $response;
  103. }
  104. if (!$targetUser->isEnabled()) {
  105. return new DataResponse(null, Http::STATUS_NOT_FOUND);
  106. }
  107. if (!$this->profileManager->isProfileEnabled($targetUser)) {
  108. return new DataResponse(null, Http::STATUS_BAD_REQUEST);
  109. }
  110. $requestingUser = $this->userSession->getUser();
  111. if ($targetUser !== $requestingUser) {
  112. if (!$this->shareManager->currentUserCanEnumerateTargetUser($requestingUser, $targetUser)) {
  113. return new DataResponse(null, Http::STATUS_NOT_FOUND);
  114. }
  115. }
  116. $profileFields = $this->profileManager->getProfileFields($targetUser, $requestingUser);
  117. // Extend the profile information with timezone of the user
  118. $timezoneStringTarget = $this->config->getUserValue($targetUser->getUID(), 'core', 'timezone') ?: $this->config->getSystemValueString('default_timezone', 'UTC');
  119. try {
  120. $timezoneTarget = new \DateTimeZone($timezoneStringTarget);
  121. } catch (\Throwable) {
  122. $timezoneTarget = new \DateTimeZone('UTC');
  123. }
  124. $profileFields['timezone'] = $timezoneTarget->getName(); // E.g. Europe/Berlin
  125. $profileFields['timezoneOffset'] = $timezoneTarget->getOffset($this->timeFactory->now()); // In seconds E.g. 7200
  126. return new DataResponse($profileFields);
  127. }
  128. }