aboutsummaryrefslogtreecommitdiffstats
path: root/core/Controller/ProfileApiController.php
blob: 02979cb1649796b017b7afcb724024b2ee2645ba (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
<?php

declare(strict_types=1);

/**
 * 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 OCP\Share\IManager;

/**
 * @psalm-import-type CoreProfileData from ResponseDefinitions
 */
class ProfileApiController extends OCSController {
	public function __construct(
		IRequest $request,
		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);
	}

	/**
	 * @NoSubAdminRequired
	 *
	 * 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();
		if ($requestingUser->getUID() !== $targetUserId) {
			throw new OCSForbiddenException('People 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
		$this->profileManager->getProfileConfig($targetUser, $targetUser);
		$config = $this->configMapper->get($targetUserId);

		if (!in_array($paramId, array_keys($config->getVisibilityMap()), true)) {
			throw new OCSBadRequestException('Account does not have a profile parameter with ID: ' . $paramId);
		}

		$config->setVisibility($paramId, $visibility);
		$this->configMapper->update($config);

		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);
	}
}