diff options
author | Côme Chilliet <come.chilliet@nextcloud.com> | 2024-02-06 17:24:47 +0100 |
---|---|---|
committer | Côme Chilliet <91878298+come-nc@users.noreply.github.com> | 2024-03-07 14:06:08 +0100 |
commit | 1ea88a73f6343a9b39b30736086484d0c072ae46 (patch) | |
tree | 04bf34eb7888cfeb28a28db25ce6cd6ea3a17afd /apps/settings/lib | |
parent | 01d5af66beed08810861e8547fd6cd0cfee6a52a (diff) | |
download | nextcloud-server-1ea88a73f6343a9b39b30736086484d0c072ae46.tar.gz nextcloud-server-1ea88a73f6343a9b39b30736086484d0c072ae46.zip |
feat(settings): Migrate .well-known URL setup check to new API
Co-authored-by: Côme Chilliet <come.chilliet@nextcloud.com>
Co-authored-by: Ferdinand Thiessen <opensource@fthiessen.de>
Signed-off-by: Ferdinand Thiessen <opensource@fthiessen.de>
Diffstat (limited to 'apps/settings/lib')
-rw-r--r-- | apps/settings/lib/AppInfo/Application.php | 2 | ||||
-rw-r--r-- | apps/settings/lib/SetupChecks/CheckServerResponseTrait.php | 35 | ||||
-rw-r--r-- | apps/settings/lib/SetupChecks/WellKnownUrls.php | 111 |
3 files changed, 142 insertions, 6 deletions
diff --git a/apps/settings/lib/AppInfo/Application.php b/apps/settings/lib/AppInfo/Application.php index 12cd21cf17c..0977da398b0 100644 --- a/apps/settings/lib/AppInfo/Application.php +++ b/apps/settings/lib/AppInfo/Application.php @@ -90,6 +90,7 @@ use OCA\Settings\SetupChecks\SupportedDatabase; use OCA\Settings\SetupChecks\SystemIs64bit; use OCA\Settings\SetupChecks\TempSpaceAvailable; use OCA\Settings\SetupChecks\TransactionIsolation; +use OCA\Settings\SetupChecks\WellKnownUrls; use OCA\Settings\SetupChecks\Woff2Loading; use OCA\Settings\UserMigration\AccountMigrator; use OCA\Settings\WellKnown\ChangePasswordHandler; @@ -218,6 +219,7 @@ class Application extends App implements IBootstrap { $context->registerSetupCheck(TempSpaceAvailable::class); $context->registerSetupCheck(TransactionIsolation::class); $context->registerSetupCheck(PushService::class); + $context->registerSetupCheck(WellKnownUrls::class); $context->registerSetupCheck(Woff2Loading::class); $context->registerUserMigrator(AccountMigrator::class); diff --git a/apps/settings/lib/SetupChecks/CheckServerResponseTrait.php b/apps/settings/lib/SetupChecks/CheckServerResponseTrait.php index bc757d27e48..aafade1526a 100644 --- a/apps/settings/lib/SetupChecks/CheckServerResponseTrait.php +++ b/apps/settings/lib/SetupChecks/CheckServerResponseTrait.php @@ -31,6 +31,7 @@ use OCP\Http\Client\IResponse; use OCP\IConfig; use OCP\IL10N; use OCP\IURLGenerator; +use Psr\Log\LoggerInterface; /** * Common trait for setup checks that need to use requests to the same server and check the response @@ -40,6 +41,7 @@ trait CheckServerResponseTrait { protected IURLGenerator $urlGenerator; protected IClientService $clientService; protected IL10N $l10n; + protected LoggerInterface $logger; /** * Common helper string in case a check could not fetch any results @@ -71,25 +73,46 @@ trait CheckServerResponseTrait { } /** - * Run a HEAD request to check header + * Run a HTTP request to check header * @param string $url The relative URL to check - * @param bool $ignoreSSL Ignore SSL certificates - * @param bool $httpErrors Ignore requests with HTTP errors (will not yield if request has a 4xx or 5xx response) + * @param string $method The HTTP method to use + * @param array{ignoreSSL?: bool, httpErrors?: bool, options?: array} $options Additional options, like + * [ + * // Ignore invalid SSL certificates (e.g. self signed) + * 'ignoreSSL' => true, + * // Ignore requests with HTTP errors (will not yield if request has a 4xx or 5xx response) + * 'httpErrors' => true, + * ] + * * @return Generator<int, IResponse> */ - protected function runHEAD(string $url, bool $ignoreSSL = true, bool $httpErrors = true): Generator { + protected function runRequest(string $url, string $method, array $options = []): Generator { + $options = array_merge(['ignoreSSL' => true, 'httpErrors' => true], $options); + $client = $this->clientService->newClient(); - $requestOptions = $this->getRequestOptions($ignoreSSL, $httpErrors); + $requestOptions = $this->getRequestOptions($options['ignoreSSL'], $options['httpErrors']); + $requestOptions = array_merge($requestOptions, $options['options'] ?? []); foreach ($this->getTestUrls($url) as $testURL) { try { - yield $client->head($testURL, $requestOptions); + yield $client->request($testURL, $method, $requestOptions); } catch (\Throwable $e) { $this->logger->debug('Can not connect to local server for running setup checks', ['exception' => $e, 'url' => $testURL]); } } } + /** + * Run a HEAD request to check header + * @param string $url The relative URL to check + * @param bool $ignoreSSL Ignore SSL certificates + * @param bool $httpErrors Ignore requests with HTTP errors (will not yield if request has a 4xx or 5xx response) + * @return Generator<int, IResponse> + */ + protected function runHEAD(string $url, bool $ignoreSSL = true, bool $httpErrors = true): Generator { + return $this->runRequest($url, 'HEAD', ['ignoreSSL' => $ignoreSSL, 'httpErrors' => $httpErrors]); + } + protected function getRequestOptions(bool $ignoreSSL, bool $httpErrors): array { $requestOptions = [ 'connect_timeout' => 10, diff --git a/apps/settings/lib/SetupChecks/WellKnownUrls.php b/apps/settings/lib/SetupChecks/WellKnownUrls.php new file mode 100644 index 00000000000..933f82b614f --- /dev/null +++ b/apps/settings/lib/SetupChecks/WellKnownUrls.php @@ -0,0 +1,111 @@ +<?php + +declare(strict_types=1); + +/** + * @copyright Copyright (c) 2024 Côme Chilliet <come.chilliet@nextcloud.com> + * + * @author Côme Chilliet <come.chilliet@nextcloud.com> + * @author Ferdinand Thiessen <opensource@fthiessen.de> + * + * @license AGPL-3.0-or-later + * + * 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 <http://www.gnu.org/licenses/>. + * + */ + +namespace OCA\Settings\SetupChecks; + +use OCP\Http\Client\IClientService; +use OCP\IConfig; +use OCP\IL10N; +use OCP\IURLGenerator; +use OCP\SetupCheck\ISetupCheck; +use OCP\SetupCheck\SetupResult; +use Psr\Log\LoggerInterface; + +class WellKnownUrls implements ISetupCheck { + + use CheckServerResponseTrait; + + public function __construct( + protected IL10N $l10n, + protected IConfig $config, + protected IURLGenerator $urlGenerator, + protected IClientService $clientService, + protected LoggerInterface $logger, + ) { + } + + public function getCategory(): string { + return 'network'; + } + + public function getName(): string { + return $this->l10n->t('.well-known URLs'); + } + + public function run(): SetupResult { + if (!$this->config->getSystemValueBool('check_for_working_wellknown_setup', true)) { + return SetupResult::info($this->l10n->t('`check_for_working_wellknown_setup` is set to false in your configuration, so this check was skipped.')); + } + + $urls = [ + ['get', '/.well-known/webfinger', [200, 404], true], + ['get', '/.well-known/nodeinfo', [200, 404], true], + ['propfind', '/.well-known/caldav', [207], false], + ['propfind', '/.well-known/carddav', [207], false], + ]; + + foreach ($urls as [$verb,$url,$validStatuses,$checkCustomHeader]) { + $works = null; + foreach ($this->runRequest($url, $verb, ['httpErrors' => false, 'options' => ['allow_redirects' => ['track_redirects' => true]]]) as $response) { + // Check that the response status matches + $works = in_array($response->getStatusCode(), $validStatuses); + // and (if needed) the custom Nextcloud header is set + if ($checkCustomHeader) { + $works = $works && !empty($response->getHeader('X-NEXTCLOUD-WELL-KNOWN')); + } else { + // For default DAV endpoints we lack authorization, but we still can check that the redirect works as expected + if (!$works && $response->getStatusCode() === 401) { + $redirectHops = explode(',', $response->getHeader('X-Guzzle-Redirect-History')); + $effectiveUri = end($redirectHops); + $works = str_ends_with($effectiveUri, '/remote.php/dav/'); + } + } + // Skip the other requests if one works + if ($works === true) { + break; + } + } + // If 'works' is null then we could not connect to the server + if ($works === null) { + return SetupResult::info( + $this->l10n->t('Could not check that your web server serves `.well-known` correctly. Please check manually.') . "\n" . $this->serverConfigHelp(), + $this->urlGenerator->linkToDocs('admin-setup-well-known-URL'), + ); + } + // Otherwise if we fail we can abort here + if ($works === false) { + return SetupResult::warning( + $this->l10n->t("Your web server is not properly set up to resolve `.well-known` URLs, failed on:\n`%s`", [$url]), + $this->urlGenerator->linkToDocs('admin-setup-well-known-URL'), + ); + } + } + return SetupResult::success( + $this->l10n->t('Your server is correctly configured to serve `.well-known` URLs.') + ); + } +} |