diff --git a/apps/settings/lib/SetupChecks/DataDirectoryProtected.php b/apps/settings/lib/SetupChecks/DataDirectoryProtected.php index 5afdfaaddd557..447c66d5c5de4 100644 --- a/apps/settings/lib/SetupChecks/DataDirectoryProtected.php +++ b/apps/settings/lib/SetupChecks/DataDirectoryProtected.php @@ -42,13 +42,21 @@ public function getName(): string { public function run(): SetupResult { $datadir = str_replace(\OC::$SERVERROOT . '/', '', $this->config->getSystemValue('datadirectory', '')); - $dataUrl = $this->urlGenerator->getWebroot() . '/' . $datadir . '/.ocdata'; + $dataUrl = $this->urlGenerator->getWebroot() . '/' . $datadir . '/.ncdata'; $noResponse = true; - foreach ($this->runHEAD($dataUrl, httpErrors:false) as $response) { + foreach ($this->runRequest('GET', $dataUrl, [ 'httpErrors' => false ]) as $response) { $noResponse = false; - if ($response->getStatusCode() === 200) { - return SetupResult::error($this->l10n->t('Your data directory and files are probably accessible from the internet. The .htaccess file is not working. It is strongly recommended that you configure your web server so that the data directory is no longer accessible, or move the data directory outside the web server document root.')); + if ($response->getStatusCode() < 400) { + // Read the response body + $body = $response->getBody(); + if (is_resource($body)) { + $body = stream_get_contents($body, 64); + } + + if (str_contains($body, '# Nextcloud data directory')) { + return SetupResult::error($this->l10n->t('Your data directory and files are probably accessible from the internet. The .htaccess file is not working. It is strongly recommended that you configure your web server so that the data directory is no longer accessible, or move the data directory outside the web server document root.')); + } } else { $this->logger->debug('[expected] Could not access data directory from outside.', ['url' => $dataUrl]); } diff --git a/apps/settings/tests/SetupChecks/DataDirectoryProtectedTest.php b/apps/settings/tests/SetupChecks/DataDirectoryProtectedTest.php index 9925ce12c4405..fbee52fcf0f08 100644 --- a/apps/settings/tests/SetupChecks/DataDirectoryProtectedTest.php +++ b/apps/settings/tests/SetupChecks/DataDirectoryProtectedTest.php @@ -45,7 +45,7 @@ protected function setUp(): void { $this->logger = $this->createMock(LoggerInterface::class); $this->setupcheck = $this->getMockBuilder(DataDirectoryProtected::class) - ->onlyMethods(['runHEAD']) + ->onlyMethods(['runRequest']) ->setConstructorArgs([ $this->l10n, $this->config, @@ -59,16 +59,17 @@ protected function setUp(): void { /** * @dataProvider dataTestStatusCode */ - public function testStatusCode(array $status, string $expected): void { - $responses = array_map(function ($state) { + public function testStatusCode(array $status, string $expected, bool $hasBody): void { + $responses = array_map(function ($state) use ($hasBody) { $response = $this->createMock(IResponse::class); $response->expects($this->any())->method('getStatusCode')->willReturn($state); + $response->expects(($this->atMost(1)))->method('getBody')->willReturn($hasBody ? '# Nextcloud data directory' : 'something else'); return $response; }, $status); $this->setupcheck ->expects($this->once()) - ->method('runHEAD') + ->method('runRequest') ->will($this->generate($responses)); $this->config @@ -82,10 +83,11 @@ public function testStatusCode(array $status, string $expected): void { public function dataTestStatusCode(): array { return [ - 'success: forbidden access' => [[403], SetupResult::SUCCESS], - 'error: can access' => [[200], SetupResult::ERROR], - 'error: one forbidden one can access' => [[403, 200], SetupResult::ERROR], - 'warning: connection issue' => [[], SetupResult::WARNING], + 'success: forbidden access' => [[403], SetupResult::SUCCESS, true], + 'success: forbidden access with redirect' => [[200], SetupResult::SUCCESS, false], + 'error: can access' => [[200], SetupResult::ERROR, true], + 'error: one forbidden one can access' => [[403, 200], SetupResult::ERROR, true], + 'warning: connection issue' => [[], SetupResult::WARNING, true], ]; } @@ -95,7 +97,7 @@ public function testNoResponse(): void { $this->setupcheck ->expects($this->once()) - ->method('runHEAD') + ->method('runRequest') ->will($this->generate([])); $this->config diff --git a/lib/composer/composer/autoload_classmap.php b/lib/composer/composer/autoload_classmap.php index 33ee831670e9e..8a6c6f8ccc2b4 100644 --- a/lib/composer/composer/autoload_classmap.php +++ b/lib/composer/composer/autoload_classmap.php @@ -1793,6 +1793,7 @@ 'OC\\Repair\\NC22\\LookupServerSendCheck' => $baseDir . '/lib/private/Repair/NC22/LookupServerSendCheck.php', 'OC\\Repair\\NC24\\AddTokenCleanupJob' => $baseDir . '/lib/private/Repair/NC24/AddTokenCleanupJob.php', 'OC\\Repair\\NC25\\AddMissingSecretJob' => $baseDir . '/lib/private/Repair/NC25/AddMissingSecretJob.php', + 'OC\\Repair\\NC30\\RemoveLegacyDatadirFile' => $baseDir . '/lib/private/Repair/NC30/RemoveLegacyDatadirFile.php', 'OC\\Repair\\OldGroupMembershipShares' => $baseDir . '/lib/private/Repair/OldGroupMembershipShares.php', 'OC\\Repair\\Owncloud\\CleanPreviews' => $baseDir . '/lib/private/Repair/Owncloud/CleanPreviews.php', 'OC\\Repair\\Owncloud\\CleanPreviewsBackgroundJob' => $baseDir . '/lib/private/Repair/Owncloud/CleanPreviewsBackgroundJob.php', diff --git a/lib/composer/composer/autoload_static.php b/lib/composer/composer/autoload_static.php index 4d0981f7fe81e..3b758ff71fe93 100644 --- a/lib/composer/composer/autoload_static.php +++ b/lib/composer/composer/autoload_static.php @@ -1826,6 +1826,7 @@ class ComposerStaticInit749170dad3f5e7f9ca158f5a9f04f6a2 'OC\\Repair\\NC22\\LookupServerSendCheck' => __DIR__ . '/../../..' . '/lib/private/Repair/NC22/LookupServerSendCheck.php', 'OC\\Repair\\NC24\\AddTokenCleanupJob' => __DIR__ . '/../../..' . '/lib/private/Repair/NC24/AddTokenCleanupJob.php', 'OC\\Repair\\NC25\\AddMissingSecretJob' => __DIR__ . '/../../..' . '/lib/private/Repair/NC25/AddMissingSecretJob.php', + 'OC\\Repair\\NC30\\RemoveLegacyDatadirFile' => __DIR__ . '/../../..' . '/lib/private/Repair/NC30/RemoveLegacyDatadirFile.php', 'OC\\Repair\\OldGroupMembershipShares' => __DIR__ . '/../../..' . '/lib/private/Repair/OldGroupMembershipShares.php', 'OC\\Repair\\Owncloud\\CleanPreviews' => __DIR__ . '/../../..' . '/lib/private/Repair/Owncloud/CleanPreviews.php', 'OC\\Repair\\Owncloud\\CleanPreviewsBackgroundJob' => __DIR__ . '/../../..' . '/lib/private/Repair/Owncloud/CleanPreviewsBackgroundJob.php', diff --git a/lib/private/Repair.php b/lib/private/Repair.php index 942cd77e5cb45..d1904e084315e 100644 --- a/lib/private/Repair.php +++ b/lib/private/Repair.php @@ -41,6 +41,7 @@ use OC\Repair\NC22\LookupServerSendCheck; use OC\Repair\NC24\AddTokenCleanupJob; use OC\Repair\NC25\AddMissingSecretJob; +use OC\Repair\NC30\RemoveLegacyDatadirFile; use OC\Repair\OldGroupMembershipShares; use OC\Repair\Owncloud\CleanPreviews; use OC\Repair\Owncloud\DropAccountTermsTable; @@ -187,6 +188,7 @@ public static function getRepairSteps(): array { \OCP\Server::get(AddMetadataGenerationJob::class), \OCP\Server::get(AddAppConfigLazyMigration::class), \OCP\Server::get(RepairLogoDimension::class), + \OCP\Server::get(RemoveLegacyDatadirFile::class), ]; } diff --git a/lib/private/Repair/NC30/RemoveLegacyDatadirFile.php b/lib/private/Repair/NC30/RemoveLegacyDatadirFile.php new file mode 100644 index 0000000000000..623163927bd3f --- /dev/null +++ b/lib/private/Repair/NC30/RemoveLegacyDatadirFile.php @@ -0,0 +1,32 @@ +config->getSystemValueString('datadirectory', \OC::$SERVERROOT . '/data') . '/.ocdata'; + if (file_exists($ocdata)) { + unlink($ocdata); + } + } +} diff --git a/lib/private/Setup.php b/lib/private/Setup.php index a67d74bd032ee..62db4879bbc5b 100644 --- a/lib/private/Setup.php +++ b/lib/private/Setup.php @@ -360,9 +360,12 @@ public function install(array $options, ?IOutput $output = null): array { Installer::installShippedApps(false, $output); // create empty file in data dir, so we can later find - // out that this is indeed an ownCloud data directory + // out that this is indeed a Nextcloud data directory $this->outputDebug($output, 'Setup data directory'); - file_put_contents($config->getSystemValueString('datadirectory', \OC::$SERVERROOT . '/data') . '/.ocdata', ''); + file_put_contents( + $config->getSystemValueString('datadirectory', \OC::$SERVERROOT . '/data') . '/.ncdata', + "# Nextcloud data directory\n# Do not change this file", + ); // Update .htaccess files self::updateHtaccess(); diff --git a/lib/private/Updater.php b/lib/private/Updater.php index 6d23e81aa6383..e26faf86f924a 100644 --- a/lib/private/Updater.php +++ b/lib/private/Updater.php @@ -208,9 +208,12 @@ private function doUpgrade(string $currentVersion, string $installedVersion): vo } // create empty file in data dir, so we can later find - // out that this is indeed an ownCloud data directory + // out that this is indeed a Nextcloud data directory // (in case it didn't exist before) - file_put_contents($this->config->getSystemValueString('datadirectory', \OC::$SERVERROOT . '/data') . '/.ocdata', ''); + file_put_contents( + $this->config->getSystemValueString('datadirectory', \OC::$SERVERROOT . '/data') . '/.ncdata', + "# Nextcloud data directory\n# Do not change this file", + ); // pre-upgrade repairs $repair = \OCP\Server::get(Repair::class); diff --git a/lib/private/User/Manager.php b/lib/private/User/Manager.php index 639ce507f4d35..2c8cc10dc15ae 100644 --- a/lib/private/User/Manager.php +++ b/lib/private/User/Manager.php @@ -783,7 +783,7 @@ private function verifyUid(string $uid, bool $checkDataDirectory = false): bool '.htaccess', 'files_external', '__groupfolders', - '.ocdata', + '.ncdata', 'owncloud.log', 'nextcloud.log', 'updater.log', diff --git a/lib/private/legacy/OC_Util.php b/lib/private/legacy/OC_Util.php index d8045e8343d74..3b5222fee64a4 100644 --- a/lib/private/legacy/OC_Util.php +++ b/lib/private/legacy/OC_Util.php @@ -687,7 +687,7 @@ public static function checkDataDirectoryPermissions($dataDirectory) { /** * Check that the data directory exists and is valid by - * checking the existence of the ".ocdata" file. + * checking the existence of the ".ncdata" file. * * @param string $dataDirectory data directory path * @return array errors found @@ -701,11 +701,11 @@ public static function checkDataDirectoryValidity($dataDirectory) { 'hint' => $l->t('Check the value of "datadirectory" in your configuration.') ]; } - if (!file_exists($dataDirectory . '/.ocdata')) { + + if (!file_exists($dataDirectory . '/.ncdata')) { $errors[] = [ 'error' => $l->t('Your data directory is invalid.'), - 'hint' => $l->t('Ensure there is a file called ".ocdata"' . - ' in the root of the data directory.') + 'hint' => $l->t('Ensure there is a file called "%1$s" in the root of the data directory. It should have the content: "%2$s"', ['.ncdata', '# Nextcloud data directory']), ]; } return $errors; diff --git a/tests/lib/UtilCheckServerTest.php b/tests/lib/UtilCheckServerTest.php index f0cc8a57e6fe3..23da44b6e848e 100644 --- a/tests/lib/UtilCheckServerTest.php +++ b/tests/lib/UtilCheckServerTest.php @@ -39,13 +39,13 @@ protected function setUp(): void { $this->datadir = \OC::$server->getTempManager()->getTemporaryFolder(); - file_put_contents($this->datadir . '/.ocdata', ''); + file_put_contents($this->datadir . '/.ncdata', '# Nextcloud data directory'); \OC::$server->getSession()->set('checkServer_succeeded', false); } protected function tearDown(): void { // clean up - @unlink($this->datadir . '/.ocdata'); + @unlink($this->datadir . '/.ncdata'); parent::tearDown(); } @@ -66,9 +66,9 @@ public function testCheckServer() { */ public function testCheckServerSkipDataDirValidityOnSetup() { // simulate old version that didn't have it - unlink($this->datadir . '/.ocdata'); + unlink($this->datadir . '/.ncdata'); - // even though ".ocdata" is missing, the error isn't + // even though ".ncdata" is missing, the error isn't // triggered to allow setup to run $result = \OC_Util::checkServer($this->getConfig([ 'installed' => false @@ -83,7 +83,7 @@ public function testCheckServerSkipDataDirValidityOnSetup() { */ public function testCheckServerSkipDataDirValidityOnUpgrade() { // simulate old version that didn't have it - unlink($this->datadir . '/.ocdata'); + unlink($this->datadir . '/.ncdata'); $session = \OC::$server->getSession(); $oldCurrentVersion = $session->get('OC_Version'); @@ -91,7 +91,7 @@ public function testCheckServerSkipDataDirValidityOnUpgrade() { // upgrade condition to simulate needUpgrade() === true $session->set('OC_Version', [6, 0, 0, 2]); - // even though ".ocdata" is missing, the error isn't + // even though ".ncdata" is missing, the error isn't // triggered to allow for upgrade $result = \OC_Util::checkServer($this->getConfig([ 'installed' => true, @@ -105,7 +105,7 @@ public function testCheckServerSkipDataDirValidityOnUpgrade() { /** * Test that checkDataDirectoryValidity returns no error - * when ".ocdata" is present. + * when ".ncdata" is present. */ public function testCheckDataDirValidity() { $result = \OC_Util::checkDataDirectoryValidity($this->datadir); @@ -114,10 +114,10 @@ public function testCheckDataDirValidity() { /** * Test that checkDataDirectoryValidity and checkServer - * both return an error when ".ocdata" is missing. + * both return an error when ".ncdata" is missing. */ public function testCheckDataDirValidityWhenFileMissing() { - unlink($this->datadir . '/.ocdata'); + unlink($this->datadir . '/.ncdata'); $result = \OC_Util::checkDataDirectoryValidity($this->datadir); $this->assertEquals(1, count($result));