From 65bf87bc9bfb9cf2ffc96e79a6a1e624ea018424 Mon Sep 17 00:00:00 2001 From: Joas Schilling Date: Fri, 16 Feb 2024 10:15:30 +0100 Subject: [PATCH] feat(federation): Abstract away the proxying Signed-off-by: Joas Schilling --- lib/Exceptions/RemoteClientException.php | 30 ++++++ lib/Federation/Proxy/TalkV1/ChatService.php | 48 ++++------ lib/Federation/Proxy/TalkV1/ProxyRequest.php | 97 ++++++++++++++++++++ 3 files changed, 143 insertions(+), 32 deletions(-) create mode 100644 lib/Exceptions/RemoteClientException.php create mode 100644 lib/Federation/Proxy/TalkV1/ProxyRequest.php diff --git a/lib/Exceptions/RemoteClientException.php b/lib/Exceptions/RemoteClientException.php new file mode 100644 index 000000000000..699a196bdda8 --- /dev/null +++ b/lib/Exceptions/RemoteClientException.php @@ -0,0 +1,30 @@ + + * + * @author Joas Schilling + * + * @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 . + * + */ + +namespace OCA\Talk\Exceptions; + +class RemoteClientException extends \Exception { +} diff --git a/lib/Federation/Proxy/TalkV1/ChatService.php b/lib/Federation/Proxy/TalkV1/ChatService.php index 72944216ee49..7ee630858f87 100644 --- a/lib/Federation/Proxy/TalkV1/ChatService.php +++ b/lib/Federation/Proxy/TalkV1/ChatService.php @@ -26,13 +26,13 @@ namespace OCA\Talk\Federation\Proxy\TalkV1; +use OCA\Talk\Exceptions\CannotReachRemoteException; +use OCA\Talk\Exceptions\RemoteClientException; use OCA\Talk\Model\Attendee; use OCA\Talk\Participant; use OCA\Talk\ResponseDefinitions; use OCA\Talk\Room; use OCP\Federation\ICloudIdManager; -use OCP\Http\Client\IClientService; -use OCP\IConfig; use OCP\IUserManager; use OCP\Security\ITrustedDomainHelper; @@ -43,47 +43,31 @@ class ChatService { protected ?Room $room = null; public function __construct( - protected IConfig $config, - protected IClientService $clientService, protected ICloudIdManager $cloudIdManager, protected IUserManager $userManager, protected ITrustedDomainHelper $trustedDomainHelper, + protected ProxyRequest $proxy, ) { } /** - * * @return TalkChatMentionSuggestion[] + * @throws CannotReachRemoteException + * @throws RemoteClientException */ - public function mentions(Room $room, Participant $participant, string $search, int $limit = 20, bool $includeStatus = false): array { + public function mentions(Room $room, Participant $participant, string $search, int $limit, bool $includeStatus): array { $this->room = $room; - $headers = [ - 'Accept' => 'application/json', - 'X-Nextcloud-Federation' => 'true', - 'OCS-APIRequest' => 'true', - ]; - $client = $this->clientService->newClient(); - try { - $response = $client->get($room->getRemoteServer() . '/ocs/v2.php/apps/spreed/api/v1/chat/' . $room->getRemoteToken() . '/mentions', [ - 'json' => [ - 'search' => $search, - 'limit' => $limit, - 'includeStatus' => $includeStatus, - ], - 'verify' => !$this->config->getSystemValueBool('sharing.federation.allowSelfSignedCertificates', false), - 'nextcloud' => [ - 'allow_local_address' => $this->config->getSystemValueBool('allow_local_remote_servers'), - ], - 'headers' => $headers, - 'timeout' => 5, - 'auth' => [urlencode($participant->getAttendee()->getInvitedCloudId()), $participant->getAttendee()->getAccessToken()] - ]); - } catch (\Exception $e) { - \OC::$server->getLogger()->error($e->getMessage(), ['exception' => $e]); - // FIXME Error handling - return []; - } + $response = $this->proxy->get( + $participant->getAttendee()->getInvitedCloudId(), + $participant->getAttendee()->getAccessToken(), + $room->getRemoteServer() . '/ocs/v2.php/apps/spreed/api/v1/chat/' . $room->getRemoteToken() . '/mentions', + [ + 'search' => $search, + 'limit' => $limit, + 'includeStatus' => $includeStatus, + ], + ); $content = $response->getBody(); $data = json_decode($content, true); diff --git a/lib/Federation/Proxy/TalkV1/ProxyRequest.php b/lib/Federation/Proxy/TalkV1/ProxyRequest.php new file mode 100644 index 000000000000..a5d629612197 --- /dev/null +++ b/lib/Federation/Proxy/TalkV1/ProxyRequest.php @@ -0,0 +1,97 @@ + + * + * @author Joas Schilling + * + * @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 . + * + */ + +namespace OCA\Talk\Federation\Proxy\TalkV1; + +use GuzzleHttp\Exception\ClientException; +use GuzzleHttp\Exception\GuzzleException; +use GuzzleHttp\Exception\ServerException; +use OCA\Talk\Exceptions\CannotReachRemoteException; +use OCA\Talk\Exceptions\RemoteClientException; +use OCP\Http\Client\IClientService; +use OCP\Http\Client\IResponse; +use OCP\IConfig; +use Psr\Log\LoggerInterface; +use SensitiveParameter; + +class ProxyRequest { + public function __construct( + protected IConfig $config, + protected IClientService $clientService, + protected LoggerInterface $logger, + ) { + } + + protected function generateDefaultRequestOptions( + string $cloudId, + #[SensitiveParameter] + string $accessToken, + ): array { + return [ + 'verify' => !$this->config->getSystemValueBool('sharing.federation.allowSelfSignedCertificates'), + 'nextcloud' => [ + 'allow_local_address' => $this->config->getSystemValueBool('allow_local_remote_servers'), + ], + 'headers' => [ + 'Accept' => 'application/json', + 'X-Nextcloud-Federation' => 'true', + 'OCS-APIRequest' => 'true', + ], + 'timeout' => 5, + 'auth' => [urlencode($cloudId), $accessToken], + ]; + } + + /** + * @throws CannotReachRemoteException + * @throws RemoteClientException + */ + public function get( + string $cloudId, + #[SensitiveParameter] + string $accessToken, + string $url, + array $parameters = [], + ): IResponse { + $requestOptions = $this->generateDefaultRequestOptions($cloudId, $accessToken); + if (!empty($parameters)) { + $requestOptions['json'] = $parameters; + } + + try { + return $this->clientService->newClient()->get($url, $requestOptions); + } catch (ClientException $e) { + // FIXME Improve error handling: read error attribute and status code + $clientException = new RemoteClientException($e->getMessage(), $e->getCode(), $e); + $this->logger->error('Client error from remote', ['exception' => $clientException]); + throw $clientException; + } catch (ServerException|GuzzleException $e) { + $serverException = new CannotReachRemoteException($e->getMessage(), $e->getCode(), $e); + $this->logger->error('Could not reach remote', ['exception' => $serverException]); + throw $serverException; + } + } +}