diff options
author | Joas Schilling <coding@schilljs.com> | 2024-03-07 12:18:03 +0100 |
---|---|---|
committer | Joas Schilling <coding@schilljs.com> | 2024-03-07 12:55:25 +0100 |
commit | 483659f7d252f6f1510aad42e914885f0f412550 (patch) | |
tree | dbf47c9f7c73f49e99e0c5e0c000d9b46179434e /lib/Controller | |
parent | dac6f7a1a82f18dfd5e55d4d0a6b9b10507fb384 (diff) |
feat(federation): Add endpoint to get the proxied avatar of other users
Signed-off-by: Joas Schilling <coding@schilljs.com>
Diffstat (limited to 'lib/Controller')
-rw-r--r-- | lib/Controller/AvatarController.php | 109 |
1 files changed, 108 insertions, 1 deletions
diff --git a/lib/Controller/AvatarController.php b/lib/Controller/AvatarController.php index ef3116c17..68fc4ca92 100644 --- a/lib/Controller/AvatarController.php +++ b/lib/Controller/AvatarController.php @@ -30,16 +30,21 @@ namespace OCA\Talk\Controller; use InvalidArgumentException; use OCA\Talk\Exceptions\CannotReachRemoteException; use OCA\Talk\Middleware\Attribute\FederationSupported; +use OCA\Talk\Middleware\Attribute\RequireLoggedInParticipant; use OCA\Talk\Middleware\Attribute\RequireModeratorParticipant; use OCA\Talk\Middleware\Attribute\RequireParticipantOrLoggedInAndListedConversation; use OCA\Talk\ResponseDefinitions; use OCA\Talk\Service\AvatarService; use OCA\Talk\Service\RoomFormatter; use OCP\AppFramework\Http; +use OCP\AppFramework\Http\Attribute\BruteForceProtection; use OCP\AppFramework\Http\Attribute\NoCSRFRequired; +use OCP\AppFramework\Http\Attribute\OpenAPI; use OCP\AppFramework\Http\Attribute\PublicPage; use OCP\AppFramework\Http\DataResponse; use OCP\AppFramework\Http\FileDisplayResponse; +use OCP\Federation\ICloudIdManager; +use OCP\IAvatarManager; use OCP\IL10N; use OCP\IRequest; use OCP\IUserSession; @@ -57,6 +62,8 @@ class AvatarController extends AEnvironmentAwareController { protected IUserSession $userSession, protected IL10N $l, protected LoggerInterface $logger, + protected ICloudIdManager $cloudIdManager, + protected IAvatarManager $avatarManager, ) { parent::__construct($appName, $request); } @@ -138,7 +145,7 @@ class AvatarController extends AEnvironmentAwareController { #[NoCSRFRequired] #[RequireParticipantOrLoggedInAndListedConversation] public function getAvatar(bool $darkTheme = false): FileDisplayResponse { - if ($this->room->getRemoteServer()) { + if ($this->room->getRemoteServer() !== '') { /** @var \OCA\Talk\Federation\Proxy\TalkV1\Controller\AvatarController $proxy */ $proxy = \OCP\Server::get(\OCA\Talk\Federation\Proxy\TalkV1\Controller\AvatarController::class); try { @@ -171,6 +178,106 @@ class AvatarController extends AEnvironmentAwareController { } /** + * Get the avatar of a cloudId user + * + * @param int $size Avatar size + * @psalm-param 64|512 $size + * @param string $cloudId Federation CloudID to get the avatar for + * @param bool $darkTheme Theme used for background + * @return FileDisplayResponse<Http::STATUS_OK, array{Content-Type: string}> + * + * 200: User avatar returned + */ + #[FederationSupported] + #[BruteForceProtection(action: 'talkRoomToken')] + #[OpenAPI(scope: OpenAPI::SCOPE_FEDERATION)] + #[PublicPage] + #[NoCSRFRequired] + #[RequireLoggedInParticipant] + public function getUserProxyAvatar(int $size, string $cloudId, bool $darkTheme = false): FileDisplayResponse { + try { + $resolvedCloudId = $this->cloudIdManager->resolveCloudId($cloudId); + } catch (\InvalidArgumentException) { + return $this->getPlaceholderResponse($darkTheme); + } + + $ownId = $this->cloudIdManager->getCloudId($this->userSession->getUser()->getCloudId(), null); + + /** + * Reach out to the remote server to get the avatar + */ + if ($ownId->getRemote() !== $resolvedCloudId->getRemote()) { + /** @var \OCA\Talk\Federation\Proxy\TalkV1\Controller\AvatarController $proxy */ + $proxy = \OCP\Server::get(\OCA\Talk\Federation\Proxy\TalkV1\Controller\AvatarController::class); + try { + return $proxy->getUserProxyAvatar($resolvedCloudId->getRemote(), $resolvedCloudId->getUser(), $size, $darkTheme); + } catch (CannotReachRemoteException) { + // Falling back to a local "user" avatar + return $this->getPlaceholderResponse($darkTheme); + } + } + + /** + * We are the server that hosts the user, so getting it from the avatar manager + */ + try { + $avatar = $this->avatarManager->getAvatar($resolvedCloudId->getUser()); + $avatarFile = $avatar->getFile($size, $darkTheme); + } catch (\Exception) { + return $this->getPlaceholderResponse($darkTheme); + } + + $response = new FileDisplayResponse( + $avatarFile, + Http::STATUS_OK, + ['Content-Type' => $avatarFile->getMimeType()], + ); + // Cache for 1 day + $response->cacheFor(60 * 60 * 24, false, true); + return $response; + } + + /** + * Get the dark mode avatar of a cloudId user + * + * @param int $size Avatar size + * @psalm-param 64|512 $size + * @param string $cloudId Federation CloudID to get the avatar for + * @return FileDisplayResponse<Http::STATUS_OK, array{Content-Type: string}> + * + * 200: User avatar returned + */ + #[FederationSupported] + #[BruteForceProtection(action: 'talkRoomToken')] + #[OpenAPI(scope: OpenAPI::SCOPE_FEDERATION)] + #[PublicPage] + #[NoCSRFRequired] + #[RequireLoggedInParticipant] + public function getUserProxyAvatarDark(int $size, string $cloudId): FileDisplayResponse { + return $this->getUserProxyAvatar($size, $cloudId, true); + } + + /** + * Get the placeholder avatar + * + * @param bool $darkTheme Theme used for background + * @return FileDisplayResponse<Http::STATUS_OK, array{Content-Type: string}> + * + * 200: User avatar returned + */ + protected function getPlaceholderResponse(bool $darkTheme): FileDisplayResponse { + $file = $this->avatarService->getPersonPlaceholder($darkTheme); + $response = new FileDisplayResponse( + $file, + Http::STATUS_OK, + ['Content-Type' => $file->getMimeType()], + ); + $response->cacheFor(60 * 60 * 24, false, true); + return $response; + + } + + /** * Delete the avatar of a room * * @return DataResponse<Http::STATUS_OK, TalkRoom, array{}> |