diff --git a/config/config.sample.php b/config/config.sample.php index d36eac3ed88c1..4d5edf1c645cf 100644 --- a/config/config.sample.php +++ b/config/config.sample.php @@ -827,6 +827,17 @@ */ 'allow_local_remote_servers' => true, + /** + * Add the URL of the Nextcloud server in User-Agent headers HTTP calls. + * + * This helps service providers identifying calls from your server, + * which can be helpful for them, but can be a privacy issue on small + * Nextcloud servers. + * + * Defaults to ``false`` + */ + 'add_url_in_user_agent' => false, + /** * Deleted Items (trash bin) * diff --git a/lib/private/Http/Client/Client.php b/lib/private/Http/Client/Client.php index a50722d55416c..804b8688a47a4 100644 --- a/lib/private/Http/Client/Client.php +++ b/lib/private/Http/Client/Client.php @@ -84,6 +84,10 @@ private function buildRequestOptions(array $options): array { if (!isset($options[RequestOptions::HEADERS]['User-Agent'])) { $userAgent = 'Nextcloud-Server-Crawler/' . $this->serverVersion->getVersionString(); + $overwriteCliUrl = $this->config->getSystemValueString('overwrite.cli.url'); + if ($this->config->getSystemValueBool('add_url_in_user_agent') && !empty($overwriteCliUrl)) { + $userAgent .= '; +' . rtrim($overwriteCliUrl, '/'); + } $options[RequestOptions::HEADERS]['User-Agent'] = $userAgent; } diff --git a/tests/lib/Http/Client/ClientTest.php b/tests/lib/Http/Client/ClientTest.php index dd0e1b7b82217..047ec45d30335 100644 --- a/tests/lib/Http/Client/ClientTest.php +++ b/tests/lib/Http/Client/ClientTest.php @@ -18,6 +18,7 @@ use OCP\IConfig; use OCP\Security\IRemoteHostValidator; use OCP\ServerVersion; +use PHPUnit\Framework\Attributes\DataProvider; use PHPUnit\Framework\MockObject\MockObject; use Psr\Log\LoggerInterface; use function parse_url; @@ -459,11 +460,12 @@ public function testHeadWithOptions(): void { public function testSetDefaultOptionsWithNotInstalled(): void { $this->config - ->expects($this->exactly(2)) + ->expects($this->exactly(3)) ->method('getSystemValueBool') ->willReturnMap([ ['installed', false, false], ['allow_local_remote_servers', false, false], + ['add_url_in_user_agent', false, false], ]); $this->config ->expects($this->once()) @@ -504,11 +506,12 @@ public function testSetDefaultOptionsWithNotInstalled(): void { public function testSetDefaultOptionsWithProxy(): void { $this->config - ->expects($this->exactly(2)) + ->expects($this->exactly(3)) ->method('getSystemValueBool') ->willReturnMap([ ['installed', false, true], ['allow_local_remote_servers', false, false], + ['add_url_in_user_agent', false, false], ]); $this->config ->expects($this->once()) @@ -558,11 +561,12 @@ public function testSetDefaultOptionsWithProxy(): void { public function testSetDefaultOptionsWithProxyAndExclude(): void { $this->config - ->expects($this->exactly(2)) + ->expects($this->exactly(3)) ->method('getSystemValueBool') ->willReturnMap([ ['installed', false, true], ['allow_local_remote_servers', false, false], + ['add_url_in_user_agent', false, false], ]); $this->config ->expects($this->once()) @@ -610,4 +614,59 @@ public function testSetDefaultOptionsWithProxyAndExclude(): void { ], ], self::invokePrivate($this->client, 'buildRequestOptions', [[]])); } + + public static function dataForTestSetServerUrlInUserAgent(): array { + return [ + ['https://example.com/', 'Nextcloud-Server-Crawler/123.45.6; +https://example.com'], + ['', 'Nextcloud-Server-Crawler/123.45.6'], + ]; + } + + #[DataProvider('dataForTestSetServerUrlInUserAgent')] + public function testSetServerUrlInUserAgent(string $url, string $userAgent): void { + $this->config + ->expects($this->exactly(3)) + ->method('getSystemValueBool') + ->willReturnMap([ + ['installed', false, true], + ['allow_local_remote_servers', false, false], + ['add_url_in_user_agent', false, true], + ]); + $this->config + ->expects($this->exactly(3)) + ->method('getSystemValueString') + ->willReturnMap([ + ['proxy', '', ''], + ['proxyuserpwd', '', ''], + ['overwrite.cli.url', null, $url], + ]); + $this->certificateManager + ->expects($this->once()) + ->method('getAbsoluteBundlePath') + ->with() + ->willReturn('/my/path.crt'); + + $this->serverVersion->method('getVersionString') + ->willReturn('123.45.6'); + + $this->assertEquals([ + 'verify' => '/my/path.crt', + 'headers' => [ + 'User-Agent' => $userAgent, + 'Accept-Encoding' => 'gzip', + ], + 'timeout' => 30, + 'nextcloud' => [ + 'allow_local_address' => false, + ], + 'allow_redirects' => [ + 'on_redirect' => function ( + \Psr\Http\Message\RequestInterface $request, + \Psr\Http\Message\ResponseInterface $response, + \Psr\Http\Message\UriInterface $uri, + ): void { + }, + ], + ], self::invokePrivate($this->client, 'buildRequestOptions', [[]])); + } }