From ecb111cec74d96ed66e0880bef093d1156afa256 Mon Sep 17 00:00:00 2001 From: Joas Schilling Date: Wed, 28 Feb 2024 10:06:19 +0100 Subject: [PATCH] fix(federation): Allow cloud federation providers to handle unsuccessful return codes Otherwise they are put to retry and will immediately trigger bruteforce protection infinitely Signed-off-by: Joas Schilling --- .../CloudFederationProviderManager.php | 61 +++++++++++++++++++ lib/private/Http/Client/Client.php | 16 +++++ .../ICloudFederationProviderManager.php | 22 +++++++ lib/public/Http/Client/IClient.php | 10 +++ 4 files changed, 109 insertions(+) diff --git a/lib/private/Federation/CloudFederationProviderManager.php b/lib/private/Federation/CloudFederationProviderManager.php index ea2f0dd7575f0..07df3e7a2090a 100644 --- a/lib/private/Federation/CloudFederationProviderManager.php +++ b/lib/private/Federation/CloudFederationProviderManager.php @@ -36,6 +36,7 @@ use OCP\Federation\ICloudFederationShare; use OCP\Federation\ICloudIdManager; use OCP\Http\Client\IClientService; +use OCP\Http\Client\IResponse; use OCP\IConfig; use OCP\OCM\Exceptions\OCMProviderException; use OCP\OCM\IOCMDiscoveryService; @@ -111,6 +112,9 @@ public function getCloudFederationProvider($resourceType) { } } + /** + * @deprecated 29.0.0 - Use {@see sendCloudShare()} instead and handle errors manually + */ public function sendShare(ICloudFederationShare $share) { $cloudID = $this->cloudIdManager->resolveCloudId($share->getShareWith()); try { @@ -147,10 +151,39 @@ public function sendShare(ICloudFederationShare $share) { return false; } + /** + * @param ICloudFederationShare $share + * @return IResponse + * @throws OCMProviderException + */ + public function sendCloudShare(ICloudFederationShare $share): IResponse { + $cloudID = $this->cloudIdManager->resolveCloudId($share->getShareWith()); + $ocmProvider = $this->discoveryService->discover($cloudID->getRemote()); + + $client = $this->httpClientService->newClient(); + try { + return $client->post($ocmProvider->getEndPoint() . '/shares', [ + 'body' => json_encode($share->getShare()), + 'headers' => ['content-type' => 'application/json'], + 'verify' => !$this->config->getSystemValueBool('sharing.federation.allowSelfSignedCertificates', false), + 'timeout' => 10, + 'connect_timeout' => 10, + ]); + } catch (\Throwable $e) { + $this->logger->error('Error while sending share to federation server: ' . $e->getMessage(), ['exception' => $e]); + try { + return $client->getResponseFromThrowable($e); + } catch (\Throwable $e) { + throw new OCMProviderException($e->getMessage(), $e->getCode(), $e); + } + } + } + /** * @param string $url * @param ICloudFederationNotification $notification * @return array|false + * @deprecated 29.0.0 - Use {@see sendCloudNotification()} instead and handle errors manually */ public function sendNotification($url, ICloudFederationNotification $notification) { try { @@ -180,6 +213,34 @@ public function sendNotification($url, ICloudFederationNotification $notificatio return false; } + /** + * @param string $url + * @param ICloudFederationNotification $notification + * @return IResponse + * @throws OCMProviderException + */ + public function sendCloudNotification(string $url, ICloudFederationNotification $notification): IResponse { + $ocmProvider = $this->discoveryService->discover($url); + + $client = $this->httpClientService->newClient(); + try { + return $client->post($ocmProvider->getEndPoint() . '/notifications', [ + 'body' => json_encode($notification->getMessage()), + 'headers' => ['content-type' => 'application/json'], + 'verify' => !$this->config->getSystemValueBool('sharing.federation.allowSelfSignedCertificates', false), + 'timeout' => 10, + 'connect_timeout' => 10, + ]); + } catch (\Throwable $e) { + $this->logger->error('Error while sending notification to federation server: ' . $e->getMessage(), ['exception' => $e]); + try { + return $client->getResponseFromThrowable($e); + } catch (\Throwable $e) { + throw new OCMProviderException($e->getMessage(), $e->getCode(), $e); + } + } + } + /** * check if the new cloud federation API is ready to be used * diff --git a/lib/private/Http/Client/Client.php b/lib/private/Http/Client/Client.php index 26a0a63378b94..b43583ba09a97 100644 --- a/lib/private/Http/Client/Client.php +++ b/lib/private/Http/Client/Client.php @@ -408,6 +408,22 @@ public function options(string $uri, array $options = []): IResponse { return new Response($response); } + /** + * Get the response of a Throwable thrown by the request methods when possible + * + * @param \Throwable $e + * @return IResponse + * @throws \Throwable When $e did not have a response + * @since 29.0.0 + */ + public function getResponseFromThrowable(\Throwable $e): IResponse { + if (method_exists($e, 'hasResponse') && method_exists($e, 'getResponse') && $e->hasResponse()) { + return new Response($e->getResponse()); + } + + throw $e; + } + protected function wrapGuzzlePromise(PromiseInterface $promise): IPromise { return new GuzzlePromiseAdapter( $promise, diff --git a/lib/public/Federation/ICloudFederationProviderManager.php b/lib/public/Federation/ICloudFederationProviderManager.php index 7272653b14de4..14ac59f3b0bf1 100644 --- a/lib/public/Federation/ICloudFederationProviderManager.php +++ b/lib/public/Federation/ICloudFederationProviderManager.php @@ -23,6 +23,9 @@ */ namespace OCP\Federation; +use OCP\Http\Client\IResponse; +use OCP\OCM\Exceptions\OCMProviderException; + /** * Class ICloudFederationProviderManager * @@ -80,9 +83,18 @@ public function getCloudFederationProvider($resourceType); * @return mixed * * @since 14.0.0 + * @deprecated 29.0.0 - Use {@see sendCloudShare()} instead and handle errors manually */ public function sendShare(ICloudFederationShare $share); + /** + * @param ICloudFederationShare $share + * @return IResponse + * @throws OCMProviderException + * @since 29.0.0 + */ + public function sendCloudShare(ICloudFederationShare $share): IResponse; + /** * send notification about existing share * @@ -91,9 +103,19 @@ public function sendShare(ICloudFederationShare $share); * @return array|false * * @since 14.0.0 + * @deprecated 29.0.0 - Use {@see sendCloudNotification()} instead and handle errors manually */ public function sendNotification($url, ICloudFederationNotification $notification); + /** + * @param string $url + * @param ICloudFederationNotification $notification + * @return IResponse + * @throws OCMProviderException + * @since 29.0.0 + */ + public function sendCloudNotification(string $url, ICloudFederationNotification $notification): IResponse; + /** * check if the new cloud federation API is ready to be used * diff --git a/lib/public/Http/Client/IClient.php b/lib/public/Http/Client/IClient.php index fb1760c25f216..9268eb6292060 100644 --- a/lib/public/Http/Client/IClient.php +++ b/lib/public/Http/Client/IClient.php @@ -207,6 +207,16 @@ public function delete(string $uri, array $options = []): IResponse; */ public function options(string $uri, array $options = []): IResponse; + /** + * Get the response of a Throwable thrown by the request methods when possible + * + * @param \Throwable $e + * @return IResponse + * @throws \Throwable When $e did not have a response + * @since 29.0.0 + */ + public function getResponseFromThrowable(\Throwable $e): IResponse; + /** * Sends an asynchronous GET request * @param string $uri