Skip to content
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions apps/settings/composer/composer/autoload_classmap.php
Original file line number Diff line number Diff line change
Expand Up @@ -114,6 +114,7 @@
'OCA\\Settings\\SetupChecks\\SystemIs64bit' => $baseDir . '/../lib/SetupChecks/SystemIs64bit.php',
'OCA\\Settings\\SetupChecks\\TempSpaceAvailable' => $baseDir . '/../lib/SetupChecks/TempSpaceAvailable.php',
'OCA\\Settings\\SetupChecks\\TransactionIsolation' => $baseDir . '/../lib/SetupChecks/TransactionIsolation.php',
'OCA\\Settings\\SetupChecks\\WellKnownUrls' => $baseDir . '/../lib/SetupChecks/WellKnownUrls.php',
'OCA\\Settings\\UserMigration\\AccountMigrator' => $baseDir . '/../lib/UserMigration/AccountMigrator.php',
'OCA\\Settings\\UserMigration\\AccountMigratorException' => $baseDir . '/../lib/UserMigration/AccountMigratorException.php',
'OCA\\Settings\\WellKnown\\ChangePasswordHandler' => $baseDir . '/../lib/WellKnown/ChangePasswordHandler.php',
Expand Down
1 change: 1 addition & 0 deletions apps/settings/composer/composer/autoload_static.php
Original file line number Diff line number Diff line change
Expand Up @@ -129,6 +129,7 @@ class ComposerStaticInitSettings
'OCA\\Settings\\SetupChecks\\SystemIs64bit' => __DIR__ . '/..' . '/../lib/SetupChecks/SystemIs64bit.php',
'OCA\\Settings\\SetupChecks\\TempSpaceAvailable' => __DIR__ . '/..' . '/../lib/SetupChecks/TempSpaceAvailable.php',
'OCA\\Settings\\SetupChecks\\TransactionIsolation' => __DIR__ . '/..' . '/../lib/SetupChecks/TransactionIsolation.php',
'OCA\\Settings\\SetupChecks\\WellKnownUrls' => __DIR__ . '/..' . '/../lib/SetupChecks/WellKnownUrls.php',
'OCA\\Settings\\UserMigration\\AccountMigrator' => __DIR__ . '/..' . '/../lib/UserMigration/AccountMigrator.php',
'OCA\\Settings\\UserMigration\\AccountMigratorException' => __DIR__ . '/..' . '/../lib/UserMigration/AccountMigratorException.php',
'OCA\\Settings\\WellKnown\\ChangePasswordHandler' => __DIR__ . '/..' . '/../lib/WellKnown/ChangePasswordHandler.php',
Expand Down
2 changes: 2 additions & 0 deletions apps/settings/lib/AppInfo/Application.php
Original file line number Diff line number Diff line change
Expand Up @@ -86,6 +86,7 @@
use OCA\Settings\SetupChecks\SystemIs64bit;
use OCA\Settings\SetupChecks\TempSpaceAvailable;
use OCA\Settings\SetupChecks\TransactionIsolation;
use OCA\Settings\SetupChecks\WellKnownUrls;
use OCA\Settings\UserMigration\AccountMigrator;
use OCA\Settings\WellKnown\ChangePasswordHandler;
use OCA\Settings\WellKnown\SecurityTxtHandler;
Expand Down Expand Up @@ -213,6 +214,7 @@ public function register(IRegistrationContext $context): void {
$context->registerSetupCheck(TempSpaceAvailable::class);
$context->registerSetupCheck(TransactionIsolation::class);
$context->registerSetupCheck(PushService::class);
$context->registerSetupCheck(WellKnownUrls::class);

$context->registerUserMigrator(AccountMigrator::class);
}
Expand Down
118 changes: 118 additions & 0 deletions apps/settings/lib/SetupChecks/WellKnownUrls.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,118 @@
<?php

declare(strict_types=1);

/**
* @copyright Copyright (c) 2024 Côme Chilliet <[email protected]>
*
* @author Côme Chilliet <[email protected]>
*
* @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 <http://www.gnu.org/licenses/>.
*
*/

namespace OCA\Settings\SetupChecks;

use OCP\Http\Client\IClientService;
use OCP\IConfig;
use OCP\IL10N;
use OCP\IRequest;
use OCP\IURLGenerator;
use OCP\SetupCheck\ISetupCheck;
use OCP\SetupCheck\SetupResult;

class WellKnownUrls implements ISetupCheck {
public function __construct(
private IConfig $config,
private IL10N $l10n,
private IURLGenerator $urlGenerator,
private IRequest $request,
private IClientService $httpClientService,
) {
}

public function getCategory(): string {
return 'system';
}

public function getName(): string {
return $this->l10n->t('.well-known URLs');
}

/**
* @param 'get'|'propfind' $verb
*/
private function checkGetUrl(string $verb, string $url, array $validStatuses, bool $checkCustomHeader): bool {
$client = $this->httpClientService->newClient();
$response = $client->$verb($this->urlGenerator->getAbsoluteURL($url), ['verify' => false, 'http_errors' => false]);
if (!in_array($response->getStatusCode(), $validStatuses)) {
return false;
}
if ($checkCustomHeader && empty($response->getHeader('X-NEXTCLOUD-WELL-KNOWN'))) {
return false;
}
return true;
}

public function run(): SetupResult {
if (!$this->config->getSystemValueBool('check_for_working_wellknown_setup', true)) {
return SetupResult::success($this->l10n->t('`check_for_working_wellknown_setup` is set to false in your configuration, so this check was skipped.'));
}
try {
$checkList = '';
$level = 'success';
$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]) {
if (!$this->checkGetUrl($verb, $url, $validStatuses, $checkCustomHeader)) {
$level = 'info';
$checkList .= ' - '.strtoupper($verb).' '.$url.': failure'."\n";
} else {
$checkList .= ' - '.strtoupper($verb).' '.$url.': success'."\n";
}
}
return match($level) {
'success' => SetupResult::success(
$this->l10n->t("Your web server is correctly configured to serve `.well-known` URLs:\n%s", [$checkList]),
$this->urlGenerator->linkToDocs('admin-setup-well-known-URL')
),
'info' => SetupResult::info(
$this->l10n->t("Your web server is not properly set up to resolve well-known URLs:\n%s", [$checkList]),
$this->urlGenerator->linkToDocs('admin-setup-well-known-URL')
),
};
} catch (\Exception $e) {
return SetupResult::error(
$this->l10n->t('Failed to test .well-known URLs: "%s".', [$e->getMessage()]),
);
}
/*
* TODO:
// OC.SetupChecks.checkWellKnownUrl('PROPFIND', '/.well-known/caldav', OC.theme.docPlaceholderUrl),
// OC.SetupChecks.checkWellKnownUrl('PROPFIND', '/.well-known/carddav', OC.theme.docPlaceholderUrl),
OC.SetupChecks.checkProviderUrl(OC.getRootPath() + '/ocm-provider/', OC.theme.docPlaceholderUrl, $('#postsetupchecks').data('check-wellknown') === true),
OC.SetupChecks.checkProviderUrl(OC.getRootPath() + '/ocs-provider/', OC.theme.docPlaceholderUrl, $('#postsetupchecks').data('check-wellknown') === true),
Valid status is 207
*/
return SetupResult::success(
$this->l10n->t('Your server is correctly configured to serve `.well-known` URLs.')
);
}
}
36 changes: 36 additions & 0 deletions lib/private/Http/Client/Client.php
Original file line number Diff line number Diff line change
Expand Up @@ -408,6 +408,42 @@ public function options(string $uri, array $options = []): IResponse {
return new Response($response);
}

/**
* Sends a PROPFIND request
*
* @param string $uri
* @param array $options Array such as
* 'query' => [
* 'field' => 'abc',
* 'other_field' => '123',
* 'file_name' => fopen('/path/to/file', 'r'),
* ],
* 'headers' => [
* 'foo' => 'bar',
* ],
* 'cookies' => [
* 'foo' => 'bar',
* ],
* 'allow_redirects' => [
* 'max' => 10, // allow at most 10 redirects.
* 'strict' => true, // use "strict" RFC compliant redirects.
* 'referer' => true, // add a Referer header
* 'protocols' => ['https'] // only allow https URLs
* ],
* 'sink' => '/path/to/file', // save to a file or a stream
* 'verify' => true, // bool or string to CA file
* 'debug' => true,
* 'timeout' => 5,
* @return IResponse
* @throws \Exception If the request could not get completed
*/
public function propfind(string $uri, array $options = []): IResponse {
$this->preventLocalAddress($uri, $options);
$response = $this->client->request('propfind', $uri, $this->buildRequestOptions($options));
$isStream = isset($options['stream']) && $options['stream'];
return new Response($response, $isStream);
}

protected function wrapGuzzlePromise(PromiseInterface $promise): IPromise {
return new GuzzlePromiseAdapter(
$promise,
Expand Down
30 changes: 30 additions & 0 deletions lib/public/Http/Client/IClient.php
Original file line number Diff line number Diff line change
Expand Up @@ -207,6 +207,36 @@ public function delete(string $uri, array $options = []): IResponse;
*/
public function options(string $uri, array $options = []): IResponse;

/**
* Sends a PROPFIND request
* @param string $uri
* @param array $options Array such as
* 'query' => [
* 'field' => 'abc',
* 'other_field' => '123',
* 'file_name' => fopen('/path/to/file', 'r'),
* ],
* 'headers' => [
* 'foo' => 'bar',
* ],
* 'cookies' => [
* 'foo' => 'bar',
* ],
* 'allow_redirects' => [
* 'max' => 10, // allow at most 10 redirects.
* 'strict' => true, // use "strict" RFC compliant redirects.
* 'referer' => true, // add a Referer header
* 'protocols' => ['https'] // only allow https URLs
* ],
* 'sink' => '/path/to/file', // save to a file or a stream
* 'verify' => true, // bool or string to CA file
* 'debug' => true,
* @return IResponse
* @throws \Exception If the request could not get completed
* @since 29.0.0
*/
public function propfind(string $uri, array $options = []): IResponse;
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Can we wrap the generic request from guzzle? Than we do not need any API additions for other non standard HTTP methods like copy move propstat search etc in the future.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I’m fine with both solution, my idea was to keep the OCP change minimal.


/**
* Sends an asynchronous GET request
* @param string $uri
Expand Down