Skip to content
Closed
Show file tree
Hide file tree
Changes from 1 commit
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
Prev Previous commit
Next Next commit
Add sabre plugin to block dav bind/unbind on the hidden folder
Signed-off-by: Robin Appelman <[email protected]>
  • Loading branch information
icewind1991 committed Aug 23, 2022
commit 5ba07ca265c847f9e2728a046d0a2df4bbbb40c3
1 change: 1 addition & 0 deletions apps/dav/composer/composer/autoload_classmap.php
Original file line number Diff line number Diff line change
Expand Up @@ -198,6 +198,7 @@
'OCA\\DAV\\Files\\BrowserErrorPagePlugin' => $baseDir . '/../lib/Files/BrowserErrorPagePlugin.php',
'OCA\\DAV\\Files\\FileSearchBackend' => $baseDir . '/../lib/Files/FileSearchBackend.php',
'OCA\\DAV\\Files\\FilesHome' => $baseDir . '/../lib/Files/FilesHome.php',
'OCA\\DAV\\Files\\HiddenFolderPlugin' => $baseDir . '/../lib/Files/HiddenFolderPlugin.php',
'OCA\\DAV\\Files\\LazySearchBackend' => $baseDir . '/../lib/Files/LazySearchBackend.php',
'OCA\\DAV\\Files\\RootCollection' => $baseDir . '/../lib/Files/RootCollection.php',
'OCA\\DAV\\Files\\Sharing\\FilesDropPlugin' => $baseDir . '/../lib/Files/Sharing/FilesDropPlugin.php',
Expand Down
1 change: 1 addition & 0 deletions apps/dav/composer/composer/autoload_static.php
Original file line number Diff line number Diff line change
Expand Up @@ -213,6 +213,7 @@ class ComposerStaticInitDAV
'OCA\\DAV\\Files\\BrowserErrorPagePlugin' => __DIR__ . '/..' . '/../lib/Files/BrowserErrorPagePlugin.php',
'OCA\\DAV\\Files\\FileSearchBackend' => __DIR__ . '/..' . '/../lib/Files/FileSearchBackend.php',
'OCA\\DAV\\Files\\FilesHome' => __DIR__ . '/..' . '/../lib/Files/FilesHome.php',
'OCA\\DAV\\Files\\HiddenFolderPlugin' => __DIR__ . '/..' . '/../lib/Files/HiddenFolderPlugin.php',
'OCA\\DAV\\Files\\LazySearchBackend' => __DIR__ . '/..' . '/../lib/Files/LazySearchBackend.php',
'OCA\\DAV\\Files\\RootCollection' => __DIR__ . '/..' . '/../lib/Files/RootCollection.php',
'OCA\\DAV\\Files\\Sharing\\FilesDropPlugin' => __DIR__ . '/..' . '/../lib/Files/Sharing/FilesDropPlugin.php',
Expand Down
1 change: 1 addition & 0 deletions apps/dav/lib/Connector/Sabre/ServerFactory.php
Original file line number Diff line number Diff line change
Expand Up @@ -131,6 +131,7 @@ public function createServer($baseUri,
$server->addPlugin(new \OCA\DAV\Connector\Sabre\DummyGetResponsePlugin());
$server->addPlugin(new \OCA\DAV\Connector\Sabre\ExceptionLoggerPlugin('webdav', $this->logger));
$server->addPlugin(new \OCA\DAV\Connector\Sabre\LockPlugin());
$server->addPlugin(new \OCA\DAV\Files\HiddenFolderPlugin());
// Some WebDAV clients do require Class 2 WebDAV support (locking), since
// we do not provide locking we emulate it using a fake locking plugin.
if ($this->request->isUserAgent([
Expand Down
44 changes: 44 additions & 0 deletions apps/dav/lib/Files/HiddenFolderPlugin.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,44 @@
<?php

declare(strict_types=1);
/**
* @copyright Copyright (c) 2022 Robin Appelman <[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\DAV\Files;

use OC\Files\Filesystem;
use Sabre\DAV\Exception\Forbidden;
use Sabre\DAV\Server;
use Sabre\DAV\ServerPlugin;

class HiddenFolderPlugin extends ServerPlugin {
public function initialize(Server $server) {
$server->on('beforeBind', [$this, 'onBind'], 1000);
$server->on('beforeUnbind', [$this, 'onBind'], 1000);
}

public function onBind($path) {
$hiddenName = Filesystem::getHiddenFolderName();
if (basename($path) === $hiddenName) {
throw new Forbidden("Can't modify hidden base folder");
}
return true;
}
}
1 change: 1 addition & 0 deletions apps/dav/lib/Server.php
Original file line number Diff line number Diff line change
Expand Up @@ -143,6 +143,7 @@ public function __construct(IRequest $request, $baseUri) {

$this->server->addPlugin(new \OCA\DAV\Connector\Sabre\ExceptionLoggerPlugin('webdav', $logger));
$this->server->addPlugin(new \OCA\DAV\Connector\Sabre\LockPlugin());
$this->server->addPlugin(new \OCA\DAV\Files\HiddenFolderPlugin());
$this->server->addPlugin(new \Sabre\DAV\Sync\Plugin());

// acl
Expand Down
9 changes: 9 additions & 0 deletions build/integration/features/bootstrap/CommandLineContext.php
Original file line number Diff line number Diff line change
Expand Up @@ -137,4 +137,13 @@ public function usingTransferFolderAsDavPath($user) {
public function transferFolderNameContains($text) {
Assert::assertContains($text, $this->lastTransferPath);
}

/**
* @Given /^system parameter "([^"]*)" is set to "([^"]*)"$/
* @param string $parameter
* @param string $value
*/
public function setSystemConfig(string $parameter, string $value) {
$this->runOcc(['config:system:set', $parameter, '--value', $value]);
}
}
63 changes: 49 additions & 14 deletions build/integration/features/bootstrap/WebDav.php
Original file line number Diff line number Diff line change
Expand Up @@ -251,11 +251,11 @@ public function downloadedContentShouldStartWith($start) {
* @param string $user
* @param string $elementType
* @param string $path
* @param \Behat\Gherkin\Node\TableNode|null $propertiesTable
* @param TableNode|null $propertiesTable
*/
public function asGetsPropertiesOfFolderWith($user, $elementType, $path, $propertiesTable) {
$properties = null;
if ($propertiesTable instanceof \Behat\Gherkin\Node\TableNode) {
if ($propertiesTable instanceof TableNode) {
foreach ($propertiesTable->getRows() as $row) {
$properties[] = $row[0];
}
Expand All @@ -268,7 +268,7 @@ public function asGetsPropertiesOfFolderWith($user, $elementType, $path, $proper
* @param string $user
* @param string $entry
* @param string $path
* @param \Behat\Gherkin\Node\TableNode|null $propertiesTable
* @param TableNode|null $propertiesTable
*/
public function asTheFileOrFolderDoesNotExist($user, $entry, $path) {
$client = $this->getSabreClient($user);
Expand Down Expand Up @@ -431,12 +431,19 @@ public function getSabreClient($user) {

/**
* @Then /^user "([^"]*)" should see following elements$/
* @Then /^user "([^"]*)" should see following elements in folder "([^"]*)"$/
* @param string $user
* @param \Behat\Gherkin\Node\TableNode|null $expectedElements
* @param string|TableNode|null $folder
* @param TableNode|null $expectedElements
*/
public function checkElementList($user, $expectedElements) {
$elementList = $this->listFolder($user, '/', 3);
if ($expectedElements instanceof \Behat\Gherkin\Node\TableNode) {
public function checkElementList($user, $folder, $expectedElements = null) {
if ($folder instanceof TableNode and $expectedElements === null) {
$expectedElements = $folder;
$folder = null;
}
$path = $folder ?? '/';
$elementList = $this->listFolder($user, $path, 3);
if ($expectedElements instanceof TableNode) {
$elementRows = $expectedElements->getRows();
$elementsSimplified = $this->simplifyArray($elementRows);
foreach ($elementsSimplified as $expectedElement) {
Expand All @@ -448,6 +455,34 @@ public function checkElementList($user, $expectedElements) {
}
}

/**
* @Then /^user "([^"]*)" should not see following elements$/
* @param string $user
* @param TableNode|null $expectedElements
*/
public function checkElementNotList($user, $expectedElements) {
try {
$elementList = $this->listFolder($user, '/', 3);
} catch (\Sabre\HTTP\ClientHttpException $e) {
$status = $e->getResponse()->getStatus();
if ($status === 403 || $status === 404) {
// if listing fails the elements are also not listed, so this is fine
return;
}
}

if ($expectedElements instanceof TableNode) {
$elementRows = $expectedElements->getRows();
$elementsSimplified = $this->simplifyArray($elementRows);
foreach ($elementsSimplified as $expectedElement) {
$webdavPath = "/" . $this->getDavFilesPath($user) . $expectedElement;
if (array_key_exists($webdavPath, $elementList)) {
Assert::fail("$webdavPath" . " is in propfind answer");
}
}
}
}

/**
* @When User :user uploads file :source to :destination
* @param string $user
Expand Down Expand Up @@ -479,7 +514,7 @@ public function userAddsAFileTo($user, $bytes, $destination) {
Assert::assertEquals(1, file_exists("work/$filename"));
$this->userUploadsAFileTo($user, "work/$filename", $destination);
$this->removeFile("work/", $filename);
$expectedElements = new \Behat\Gherkin\Node\TableNode([["$destination"]]);
$expectedElements = new TableNode([["$destination"]]);
$this->checkElementList($user, $expectedElements);
}

Expand Down Expand Up @@ -664,7 +699,7 @@ public function changeFavStateOfAnElement($user, $path, $favOrUnfav, $folderDept
* @Given user :user stores etag of element :path
*/
public function userStoresEtagOfElement($user, $path) {
$propertiesTable = new \Behat\Gherkin\Node\TableNode([['{DAV:}getetag']]);
$propertiesTable = new TableNode([['{DAV:}getetag']]);
$this->asGetsPropertiesOfFolderWith($user, 'entry', $path, $propertiesTable);
$pathETAG[$path] = $this->response['{DAV:}getetag'];
$this->storedETAG[$user] = $pathETAG;
Expand All @@ -674,7 +709,7 @@ public function userStoresEtagOfElement($user, $path) {
* @Then etag of element :path of user :user has not changed
*/
public function checkIfETAGHasNotChanged($path, $user) {
$propertiesTable = new \Behat\Gherkin\Node\TableNode([['{DAV:}getetag']]);
$propertiesTable = new TableNode([['{DAV:}getetag']]);
$this->asGetsPropertiesOfFolderWith($user, 'entry', $path, $propertiesTable);
Assert::assertEquals($this->response['{DAV:}getetag'], $this->storedETAG[$user][$path]);
}
Expand All @@ -683,7 +718,7 @@ public function checkIfETAGHasNotChanged($path, $user) {
* @Then etag of element :path of user :user has changed
*/
public function checkIfETAGHasChanged($path, $user) {
$propertiesTable = new \Behat\Gherkin\Node\TableNode([['{DAV:}getetag']]);
$propertiesTable = new TableNode([['{DAV:}getetag']]);
$this->asGetsPropertiesOfFolderWith($user, 'entry', $path, $propertiesTable);
Assert::assertNotEquals($this->response['{DAV:}getetag'], $this->storedETAG[$user][$path]);
}
Expand Down Expand Up @@ -716,14 +751,14 @@ public function thereAreNoDuplicateHeaders() {
* @Then /^user "([^"]*)" in folder "([^"]*)" should have favorited the following elements$/
* @param string $user
* @param string $folder
* @param \Behat\Gherkin\Node\TableNode|null $expectedElements
* @param TableNode|null $expectedElements
*/
public function checkFavoritedElements($user, $folder, $expectedElements) {
$elementList = $this->reportFolder($user,
$folder,
'<oc:favorite/>',
'<oc:favorite>1</oc:favorite>');
if ($expectedElements instanceof \Behat\Gherkin\Node\TableNode) {
if ($expectedElements instanceof TableNode) {
$elementRows = $expectedElements->getRows();
$elementsSimplified = $this->simplifyArray($elementRows);
foreach ($elementsSimplified as $expectedElement) {
Expand Down Expand Up @@ -760,7 +795,7 @@ public function userDeletesEverythingInFolder($user, $folder) {
* @return int
*/
private function getFileIdForPath($user, $path) {
$propertiesTable = new \Behat\Gherkin\Node\TableNode([["{http://owncloud.org/ns}fileid"]]);
$propertiesTable = new TableNode([["{http://owncloud.org/ns}fileid"]]);
$this->asGetsPropertiesOfFolderWith($user, 'file', $path, $propertiesTable);
return (int)$this->response['{http://owncloud.org/ns}fileid'];
}
Expand Down
33 changes: 33 additions & 0 deletions build/integration/features/hidden-folder.feature
Original file line number Diff line number Diff line change
@@ -0,0 +1,33 @@
Feature: Hidden folder
Background:
Given using api version "1"
And user "user0" exists
And system parameter "instanceid" is set to "dummy"
And User "user0" created a folder "/.hidden_instance"
And system parameter "instanceid" is set to "instance"

Scenario: The hidden folder should not be listed in the root
When User "user0" created a folder "/folder"
Then user "user0" should see following elements
| /folder/ |
And user "user0" should not see following elements
| /.hidden_instance/ |

Scenario: Folders can be created inside the hidden folder
When User "user0" created a folder "/.hidden_instance/sub"
Then the HTTP status code should be "201"
And user "user0" should see following elements in folder "/.hidden_instance/"
| /.hidden_instance/sub/ |

Scenario: Trying to delete the hidden folder should fail
Given User "user0" deletes folder "/.hidden_instance"
Then the HTTP status code should be "403"

Scenario: Trying to rename the hidden folder should fail
Given User "user0" moves folder "/.hidden_instance" to "/foo"
Then the HTTP status code should be "403"

Scenario: Trying to overwrite the hidden folder with a rename should fail
When User "user0" created a folder "/folder"
And User "user0" moves folder "/folder" to "/.hidden_instance"
Then the HTTP status code should be "403"