From 8c5302847b9ac3d491c1b62411e62f509573fe7a Mon Sep 17 00:00:00 2001 From: Robin Appelman Date: Fri, 7 Aug 2015 16:04:27 +0200 Subject: [PATCH 1/5] add test framework for doing full request webdav tests --- .../connector/sabre/exceptionloggerplugin.php | 2 +- lib/private/connector/sabre/file.php | 2 +- .../lib/connector/sabre/requesttest/auth.php | 69 +++++++ .../sabre/requesttest/exceptionplugin.php | 32 ++++ .../sabre/requesttest/requesttest.php | 174 ++++++++++++++++++ .../lib/connector/sabre/requesttest/sapi.php | 53 ++++++ .../sabre/requesttest/uploadtest.php | 88 +++++++++ 7 files changed, 418 insertions(+), 2 deletions(-) create mode 100644 tests/lib/connector/sabre/requesttest/auth.php create mode 100644 tests/lib/connector/sabre/requesttest/exceptionplugin.php create mode 100644 tests/lib/connector/sabre/requesttest/requesttest.php create mode 100644 tests/lib/connector/sabre/requesttest/sapi.php create mode 100644 tests/lib/connector/sabre/requesttest/uploadtest.php diff --git a/lib/private/connector/sabre/exceptionloggerplugin.php b/lib/private/connector/sabre/exceptionloggerplugin.php index 741ba4d3e0..53a1f738ea 100644 --- a/lib/private/connector/sabre/exceptionloggerplugin.php +++ b/lib/private/connector/sabre/exceptionloggerplugin.php @@ -28,7 +28,7 @@ use Sabre\DAV\Exception; use Sabre\HTTP\Response; class ExceptionLoggerPlugin extends \Sabre\DAV\ServerPlugin { - private $nonFatalExceptions = array( + protected $nonFatalExceptions = array( 'Sabre\DAV\Exception\NotAuthenticated' => true, // the sync client uses this to find out whether files exist, // so it is not always an error, log it as debug diff --git a/lib/private/connector/sabre/file.php b/lib/private/connector/sabre/file.php index fa2f5ce18d..b7d0c547f2 100644 --- a/lib/private/connector/sabre/file.php +++ b/lib/private/connector/sabre/file.php @@ -332,7 +332,7 @@ class File extends Node implements IFile { $info = \OC_FileChunking::decodeName($name); if (empty($info)) { - throw new NotImplemented(); + throw new NotImplemented('Invalid chunk name'); } $chunk_handler = new \OC_FileChunking($info); $bytesWritten = $chunk_handler->store($info['index'], $data); diff --git a/tests/lib/connector/sabre/requesttest/auth.php b/tests/lib/connector/sabre/requesttest/auth.php new file mode 100644 index 0000000000..7cab4da526 --- /dev/null +++ b/tests/lib/connector/sabre/requesttest/auth.php @@ -0,0 +1,69 @@ + + * This file is licensed under the Affero General Public License version 3 or + * later. + * See the COPYING-README file. + */ + +namespace Test\Connector\Sabre\RequestTest; + +use Sabre\DAV\Auth\Backend\BackendInterface; + +class Auth implements BackendInterface { + /** + * @var string + */ + private $user; + + /** + * @var string + */ + private $password; + + /** + * Auth constructor. + * + * @param string $user + * @param string $password + */ + public function __construct($user, $password) { + $this->user = $user; + $this->password = $password; + } + + + /** + * Authenticates the user based on the current request. + * + * If authentication is successful, true must be returned. + * If authentication fails, an exception must be thrown. + * + * @param \Sabre\DAV\Server $server + * @param string $realm + * @return bool + */ + function authenticate(\Sabre\DAV\Server $server, $realm) { + $userSession = \OC::$server->getUserSession(); + $result = $userSession->login($this->user, $this->password); + if ($result) { + //we need to pass the user name, which may differ from login name + $user = $userSession->getUser()->getUID(); + \OC_Util::setupFS($user); + //trigger creation of user home and /files folder + \OC::$server->getUserFolder($user); + } + return $result; + } + + /** + * Returns information about the currently logged in username. + * + * If nobody is currently logged in, this method should return null. + * + * @return string|null + */ + function getCurrentUser() { + return $this->user; + } +} diff --git a/tests/lib/connector/sabre/requesttest/exceptionplugin.php b/tests/lib/connector/sabre/requesttest/exceptionplugin.php new file mode 100644 index 0000000000..2b9e5d6d46 --- /dev/null +++ b/tests/lib/connector/sabre/requesttest/exceptionplugin.php @@ -0,0 +1,32 @@ + + * This file is licensed under the Affero General Public License version 3 or + * later. + * See the COPYING-README file. + */ + +namespace Test\Connector\Sabre\RequestTest; + +use Sabre\DAV\Exception; + +class ExceptionPlugin extends \OC\Connector\Sabre\ExceptionLoggerPlugin { + /** + * @var \Exception[] + */ + protected $exceptions = []; + + public function logException(\Exception $ex) { + $exceptionClass = get_class($ex); + if (!isset($this->nonFatalExceptions[$exceptionClass])) { + $this->exceptions[] = $ex; + } + } + + /** + * @return \Exception[] + */ + public function getExceptions() { + return $this->exceptions; + } +} diff --git a/tests/lib/connector/sabre/requesttest/requesttest.php b/tests/lib/connector/sabre/requesttest/requesttest.php new file mode 100644 index 0000000000..7a4f44ee71 --- /dev/null +++ b/tests/lib/connector/sabre/requesttest/requesttest.php @@ -0,0 +1,174 @@ + + * This file is licensed under the Affero General Public License version 3 or + * later. + * See the COPYING-README file. + */ + +namespace Test\Connector\Sabre\RequestTest; + +use OC\Connector\Sabre\Server; +use OC\Files\Mount\MountPoint; +use OC\Files\Storage\Temporary; +use OC\Files\View; +use OCP\IUser; +use Sabre\HTTP\Request; +use Test\TestCase; + +abstract class RequestTest extends TestCase { + /** + * @var \OC_User_Dummy + */ + protected $userBackend; + + /** + * @var \OCP\Files\Config\IMountProvider[] + */ + protected $mountProviders; + + protected function getStream($string) { + $stream = fopen('php://temp', 'r+'); + fwrite($stream, $string); + fseek($stream, 0); + return $stream; + } + + /** + * @param $userId + * @param $storages + * @return \OCP\Files\Config\IMountProvider + */ + protected function getMountProvider($userId, $storages) { + $mounts = []; + foreach ($storages as $mountPoint => $storage) { + $mounts[] = new MountPoint($storage, $mountPoint); + } + $provider = $this->getMock('\OCP\Files\Config\IMountProvider'); + $provider->expects($this->any()) + ->method('getMountsForUser') + ->will($this->returnCallback(function (IUser $user) use ($userId, $mounts) { + if ($user->getUID() === $userId) { + return $mounts; + } else { + return []; + } + })); + return $provider; + } + + protected function setUp() { + $this->userBackend = new \OC_User_Dummy(); + \OC::$server->getUserManager()->registerBackend($this->userBackend); + } + + protected function tearDown() { + \OC::$server->getUserManager()->removeBackend($this->userBackend); + } + + protected function setupUser($name, $password) { + $this->userBackend->createUser($name, $password); + \OC::$server->getMountProviderCollection()->registerProvider($this->getMountProvider($name, [ + '/' . $name => new Temporary() + ])); + $this->loginAsUser($name); + return new View('/' . $name . '/files'); + } + + /** + * @param \OC\Files\View $view the view to run the webdav server against + * @param string $user + * @param string $password + * @param string $method + * @param string $url + * @param resource|string|null $body + * @param array|null $headers + * @return \Sabre\HTTP\Response + */ + protected function request($view, $user, $password, $method, $url, $body = null, $headers = null) { + if (is_string($body)) { + $body = $this->getStream($body); + } + $this->logout(); + $exceptionPlugin = new ExceptionPlugin('webdav', null); + $server = $this->getSabreServer($view, $user, $password, $exceptionPlugin); + $request = new Request($method, $url, $headers, $body); + + // since sabre catches all exceptions we need to save them and throw them from outside the sabre server + + $originalServer = $_SERVER; + + if (is_array($headers)) { + foreach ($headers as $header => $value) { + $_SERVER['HTTP_' . strtoupper(str_replace('-', '_', $header))] = $value; + } + } + + $result = $this->makeRequest($server, $request); + + foreach ($exceptionPlugin->getExceptions() as $exception) { + throw $exception; + } + $_SERVER = $originalServer; + return $result; + } + + /** + * @param Server $server + * @param Request $request + * @return \Sabre\HTTP\Response + */ + protected function makeRequest(Server $server, Request $request) { + $sapi = new Sapi($request); + $server->sapi = $sapi; + $server->httpRequest = $request; + $server->exec(); + return $sapi->getResponse(); + } + + /** + * @param View $view + * @param string $user + * @param string $password + * @param ExceptionPlugin $exceptionPlugin + * @return Server + */ + protected function getSabreServer(View $view, $user, $password, ExceptionPlugin $exceptionPlugin) { + $authBackend = new Auth($user, $password); + $objectTree = new \OC\Connector\Sabre\ObjectTree(); + $server = new \OC\Connector\Sabre\Server($objectTree); + + $server->setBaseUri('/'); + + // Load plugins + $server->addPlugin(new \Sabre\DAV\Auth\Plugin($authBackend, 'oc-test')); + $server->addPlugin(new \OC\Connector\Sabre\DummyGetResponsePlugin()); + $server->addPlugin(new \OC\Connector\Sabre\FilesPlugin($objectTree)); + $server->addPlugin($exceptionPlugin); + + // wait with registering these until auth is handled and the filesystem is setup + $server->on('beforeMethod', function () use ($server, $objectTree, $view) { + $rootInfo = $view->getFileInfo(''); + + // Create ownCloud Dir + $mountManager = \OC\Files\Filesystem::getMountManager(); + $rootDir = new \OC\Connector\Sabre\Directory($view, $rootInfo); + $objectTree->init($rootDir, $view, $mountManager); + + $server->addPlugin(new \OC\Connector\Sabre\QuotaPlugin($view)); + + // custom properties plugin must be the last one + $server->addPlugin( + new \Sabre\DAV\PropertyStorage\Plugin( + new \OC\Connector\Sabre\CustomPropertiesBackend( + $objectTree, + \OC::$server->getDatabaseConnection(), + \OC::$server->getUserSession()->getUser() + ) + ) + ); + $server->addPlugin(new \OC\Connector\Sabre\CopyEtagHeaderPlugin()); + }, 30); // priority 30: after auth (10) and acl(20), before lock(50) and handling the request + return $server; + } +} diff --git a/tests/lib/connector/sabre/requesttest/sapi.php b/tests/lib/connector/sabre/requesttest/sapi.php new file mode 100644 index 0000000000..7072b8bd28 --- /dev/null +++ b/tests/lib/connector/sabre/requesttest/sapi.php @@ -0,0 +1,53 @@ + + * This file is licensed under the Affero General Public License version 3 or + * later. + * See the COPYING-README file. + */ + +namespace Test\Connector\Sabre\RequestTest; + +use Sabre\HTTP\Request; +use Sabre\HTTP\Response; + +class Sapi { + /** + * @var \Sabre\HTTP\Request + */ + private $request; + + /** + * @var \Sabre\HTTP\Response + */ + private $response; + + /** + * This static method will create a new Request object, based on the + * current PHP request. + * + * @return \Sabre\HTTP\Request + */ + public function getRequest() { + return $this->request; + } + + public function __construct(Request $request) { + $this->request = $request; + } + + /** + * @param \Sabre\HTTP\Response $response + * @return void + */ + public function sendResponse(Response $response) { + $this->response = $response; + } + + /** + * @return \Sabre\HTTP\Response + */ + public function getResponse() { + return $this->response; + } +} diff --git a/tests/lib/connector/sabre/requesttest/uploadtest.php b/tests/lib/connector/sabre/requesttest/uploadtest.php new file mode 100644 index 0000000000..2cc912d07f --- /dev/null +++ b/tests/lib/connector/sabre/requesttest/uploadtest.php @@ -0,0 +1,88 @@ + + * This file is licensed under the Affero General Public License version 3 or + * later. + * See the COPYING-README file. + */ + +namespace Test\Connector\Sabre\RequestTest; + +class UploadTest extends RequestTest { + public function testBasicUpload() { + $user = $this->getUniqueID(); + $view = $this->setupUser($user, 'pass'); + + $this->assertFalse($view->file_exists('foo.txt')); + $response = $this->request($view, $user, 'pass', 'PUT', '/foo.txt', 'asd'); + + $this->assertEquals(201, $response->getStatus()); + $this->assertTrue($view->file_exists('foo.txt')); + $this->assertEquals('asd', $view->file_get_contents('foo.txt')); + } + + public function testUploadOverWrite() { + $user = $this->getUniqueID(); + $view = $this->setupUser($user, 'pass'); + + $view->file_put_contents('foo.txt', 'bar'); + + $response = $this->request($view, $user, 'pass', 'PUT', '/foo.txt', 'asd'); + + $this->assertEquals(204, $response->getStatus()); + $this->assertEquals('asd', $view->file_get_contents('foo.txt')); + } + + public function testChunkedUpload() { + $user = $this->getUniqueID(); + $view = $this->setupUser($user, 'pass'); + + $this->assertFalse($view->file_exists('foo.txt')); + $response = $this->request($view, $user, 'pass', 'PUT', '/foo.txt-chunking-123-2-0', 'asd', ['OC-Chunked' => '1']); + + $this->assertEquals(201, $response->getStatus()); + $this->assertFalse($view->file_exists('foo.txt')); + + $response = $this->request($view, $user, 'pass', 'PUT', '/foo.txt-chunking-123-2-1', 'bar', ['OC-Chunked' => '1']); + + $this->assertEquals(201, $response->getStatus()); + $this->assertTrue($view->file_exists('foo.txt')); + + $this->assertEquals('asdbar', $view->file_get_contents('foo.txt')); + } + + public function testChunkedUploadOverWrite() { + $user = $this->getUniqueID(); + $view = $this->setupUser($user, 'pass'); + + $view->file_put_contents('foo.txt', 'bar'); + $response = $this->request($view, $user, 'pass', 'PUT', '/foo.txt-chunking-123-2-0', 'asd', ['OC-Chunked' => '1']); + + $this->assertEquals(201, $response->getStatus()); + $this->assertEquals('bar', $view->file_get_contents('foo.txt')); + + $response = $this->request($view, $user, 'pass', 'PUT', '/foo.txt-chunking-123-2-1', 'bar', ['OC-Chunked' => '1']); + + $this->assertEquals(201, $response->getStatus()); + + $this->assertEquals('asdbar', $view->file_get_contents('foo.txt')); + } + + public function testChunkedUploadOutOfOrder() { + $user = $this->getUniqueID(); + $view = $this->setupUser($user, 'pass'); + + $this->assertFalse($view->file_exists('foo.txt')); + $response = $this->request($view, $user, 'pass', 'PUT', '/foo.txt-chunking-123-2-1', 'bar', ['OC-Chunked' => '1']); + + $this->assertEquals(201, $response->getStatus()); + $this->assertFalse($view->file_exists('foo.txt')); + + $response = $this->request($view, $user, 'pass', 'PUT', '/foo.txt-chunking-123-2-0', 'asd', ['OC-Chunked' => '1']); + + $this->assertEquals(201, $response->getStatus()); + $this->assertTrue($view->file_exists('foo.txt')); + + $this->assertEquals('asdbar', $view->file_get_contents('foo.txt')); + } +} From b0c8654f9eb8cd71aa575d3eb760fceb2a3942b1 Mon Sep 17 00:00:00 2001 From: Robin Appelman Date: Fri, 7 Aug 2015 17:36:14 +0200 Subject: [PATCH 2/5] split out creating the sabre dav server to it's own factory --- apps/files/appinfo/remote.php | 56 +++------- apps/files_sharing/publicwebdav.php | 42 ++----- lib/private/connector/sabre/objecttree.php | 6 +- lib/private/connector/sabre/serverfactory.php | 103 ++++++++++++++++++ .../sabre/requesttest/requesttest.php | 44 +++----- 5 files changed, 145 insertions(+), 106 deletions(-) create mode 100644 lib/private/connector/sabre/serverfactory.php diff --git a/apps/files/appinfo/remote.php b/apps/files/appinfo/remote.php index fff3332ef4..36479ae13d 100644 --- a/apps/files/appinfo/remote.php +++ b/apps/files/appinfo/remote.php @@ -33,51 +33,23 @@ set_time_limit(0); // Turn off output buffering to prevent memory problems \OC_Util::obEnd(); +$serverFactory = new \OC\Connector\Sabre\ServerFactory( + \OC::$server->getConfig(), + \OC::$server->getLogger(), + \OC::$server->getDatabaseConnection(), + \OC::$server->getUserSession(), + \OC::$server->getMountManager(), + \OC::$server->getTagManager() +); + // Backends $authBackend = new \OC\Connector\Sabre\Auth(); +$requestUri = \OC::$server->getRequest()->getRequestUri(); -// Fire up server -$objectTree = new \OC\Connector\Sabre\ObjectTree(); -$server = new \OC\Connector\Sabre\Server($objectTree); -// Set URL explicitly due to reverse-proxy situations -$server->httpRequest->setUrl(\OC::$server->getRequest()->getRequestUri()); -$server->setBaseUri($baseuri); - -// Load plugins -$defaults = new OC_Defaults(); -$server->addPlugin(new \OC\Connector\Sabre\BlockLegacyClientPlugin(\OC::$server->getConfig())); -$server->addPlugin(new \Sabre\DAV\Auth\Plugin($authBackend, $defaults->getName())); -// FIXME: The following line is a workaround for legacy components relying on being able to send a GET to / -$server->addPlugin(new \OC\Connector\Sabre\DummyGetResponsePlugin()); -$server->addPlugin(new \OC\Connector\Sabre\FilesPlugin($objectTree)); -$server->addPlugin(new \OC\Connector\Sabre\MaintenancePlugin(\OC::$server->getConfig())); -$server->addPlugin(new \OC\Connector\Sabre\ExceptionLoggerPlugin('webdav', \OC::$server->getLogger())); - -// wait with registering these until auth is handled and the filesystem is setup -$server->on('beforeMethod', function () use ($server, $objectTree) { - $view = \OC\Files\Filesystem::getView(); - $rootInfo = $view->getFileInfo(''); - - // Create ownCloud Dir - $mountManager = \OC\Files\Filesystem::getMountManager(); - $rootDir = new \OC\Connector\Sabre\Directory($view, $rootInfo); - $objectTree->init($rootDir, $view, $mountManager); - - $server->addPlugin(new \OC\Connector\Sabre\TagsPlugin($objectTree, \OC::$server->getTagManager())); - $server->addPlugin(new \OC\Connector\Sabre\QuotaPlugin($view)); - - // custom properties plugin must be the last one - $server->addPlugin( - new \Sabre\DAV\PropertyStorage\Plugin( - new \OC\Connector\Sabre\CustomPropertiesBackend( - $objectTree, - \OC::$server->getDatabaseConnection(), - \OC::$server->getUserSession()->getUser() - ) - ) - ); - $server->addPlugin(new \OC\Connector\Sabre\CopyEtagHeaderPlugin()); -}, 30); // priority 30: after auth (10) and acl(20), before lock(50) and handling the request +$server = $serverFactory->createServer($baseuri, $requestUri, $authBackend, function() { + // use the view for the logged in user + return \OC\Files\Filesystem::getView(); +}); // And off we go! $server->exec(); diff --git a/apps/files_sharing/publicwebdav.php b/apps/files_sharing/publicwebdav.php index 5bde908109..eec158dd4b 100644 --- a/apps/files_sharing/publicwebdav.php +++ b/apps/files_sharing/publicwebdav.php @@ -33,24 +33,18 @@ OC_Util::obEnd(); // Backends $authBackend = new OCA\Files_Sharing\Connector\PublicAuth(\OC::$server->getConfig()); -// Fire up server -$objectTree = new \OC\Connector\Sabre\ObjectTree(); -$server = new \OC\Connector\Sabre\Server($objectTree); -// Set URL explicitly due to reverse-proxy situations -$server->httpRequest->setUrl(\OC::$server->getRequest()->getRequestUri()); -$server->setBaseUri($baseuri); +$serverFactory = new \OC\Connector\Sabre\ServerFactory( + \OC::$server->getConfig(), + \OC::$server->getLogger(), + \OC::$server->getDatabaseConnection(), + \OC::$server->getUserSession(), + \OC::$server->getMountManager(), + \OC::$server->getTagManager() +); -// Load plugins -$defaults = new OC_Defaults(); -$server->addPlugin(new \Sabre\DAV\Auth\Plugin($authBackend, $defaults->getName())); -// FIXME: The following line is a workaround for legacy components relying on being able to send a GET to / -$server->addPlugin(new \OC\Connector\Sabre\DummyGetResponsePlugin()); -$server->addPlugin(new \OC\Connector\Sabre\FilesPlugin($objectTree, true)); -$server->addPlugin(new \OC\Connector\Sabre\MaintenancePlugin(\OC::$server->getConfig())); -$server->addPlugin(new \OC\Connector\Sabre\ExceptionLoggerPlugin('webdav', \OC::$server->getLogger())); +$requestUri = \OC::$server->getRequest()->getRequestUri(); -// wait with registering these until auth is handled and the filesystem is setup -$server->on('beforeMethod', function () use ($server, $objectTree, $authBackend) { +$server = $serverFactory->createServer($baseuri, $requestUri, $authBackend, function () use ($authBackend) { if (OCA\Files_Sharing\Helper::isOutgoingServer2serverShareEnabled() === false) { // this is what is thrown when trying to access a non-existing share throw new \Sabre\DAV\Exception\NotAuthenticated(); @@ -72,20 +66,8 @@ $server->on('beforeMethod', function () use ($server, $objectTree, $authBackend) $ownerView = \OC\Files\Filesystem::getView(); $path = $ownerView->getPath($fileId); - $view = new \OC\Files\View($ownerView->getAbsolutePath($path)); - $rootInfo = $view->getFileInfo(''); - - // Create ownCloud Dir - if ($rootInfo->getType() === 'dir') { - $root = new \OC\Connector\Sabre\Directory($view, $rootInfo); - } else { - $root = new \OC\Connector\Sabre\File($view, $rootInfo); - } - $mountManager = \OC\Files\Filesystem::getMountManager(); - $objectTree->init($root, $view, $mountManager); - - $server->addPlugin(new \OC\Connector\Sabre\QuotaPlugin($view)); -}, 30); // priority 30: after auth (10) and acl(20), before lock(50) and handling the request + return new \OC\Files\View($ownerView->getAbsolutePath($path)); +}); // And off we go! $server->exec(); diff --git a/lib/private/connector/sabre/objecttree.php b/lib/private/connector/sabre/objecttree.php index 1e9b9ba59e..18d3c1dcf2 100644 --- a/lib/private/connector/sabre/objecttree.php +++ b/lib/private/connector/sabre/objecttree.php @@ -41,7 +41,7 @@ class ObjectTree extends \Sabre\DAV\Tree { protected $fileView; /** - * @var \OC\Files\Mount\Manager + * @var \OCP\Files\Mount\IMountManager */ protected $mountManager; @@ -54,9 +54,9 @@ class ObjectTree extends \Sabre\DAV\Tree { /** * @param \Sabre\DAV\INode $rootNode * @param \OC\Files\View $view - * @param \OC\Files\Mount\Manager $mountManager + * @param \OCP\Files\Mount\IMountManager $mountManager */ - public function init(\Sabre\DAV\INode $rootNode, \OC\Files\View $view, \OC\Files\Mount\Manager $mountManager) { + public function init(\Sabre\DAV\INode $rootNode, \OC\Files\View $view, \OCP\Files\Mount\IMountManager $mountManager) { $this->rootNode = $rootNode; $this->fileView = $view; $this->mountManager = $mountManager; diff --git a/lib/private/connector/sabre/serverfactory.php b/lib/private/connector/sabre/serverfactory.php new file mode 100644 index 0000000000..7d3ed338ad --- /dev/null +++ b/lib/private/connector/sabre/serverfactory.php @@ -0,0 +1,103 @@ + + * + * @copyright Copyright (c) 2015, ownCloud, Inc. + * @license AGPL-3.0 + * + * This code is free software: you can redistribute it and/or modify + * it under the terms of the GNU Affero General Public License, version 3, + * as published by the Free Software Foundation. + * + * 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, version 3, + * along with this program. If not, see + * + */ + +namespace OC\Connector\Sabre; + +use OCP\Files\Mount\IMountManager; +use OCP\IConfig; +use OCP\IDBConnection; +use OCP\ILogger; +use OCP\ITagManager; +use OCP\IUserSession; +use Sabre\DAV\Auth\Backend\BackendInterface; + +class ServerFactory { + public function __construct( + IConfig $config, + ILogger $logger, + IDBConnection $databaseConnection, + IUserSession $userSession, + IMountManager $mountManager, + ITagManager $tagManager + ) { + $this->config = $config; + $this->logger = $logger; + $this->databaseConnection = $databaseConnection; + $this->userSession = $userSession; + $this->mountManager = $mountManager; + $this->tagManager = $tagManager; + } + + /** + * @param string $baseUri + * @param string $requestUri + * @param BackendInterface $authBackend + * @param callable $viewCallBack callback that should return the view for the dav endpoint + * @return Server + */ + public function createServer($baseUri, $requestUri, BackendInterface $authBackend, callable $viewCallBack) { + // Fire up server + $objectTree = new \OC\Connector\Sabre\ObjectTree(); + $server = new \OC\Connector\Sabre\Server($objectTree); + // Set URL explicitly due to reverse-proxy situations + $server->httpRequest->setUrl($requestUri); + $server->setBaseUri($baseUri); + + // Load plugins + $defaults = new \OC_Defaults(); + $server->addPlugin(new \OC\Connector\Sabre\BlockLegacyClientPlugin($this->config)); + $server->addPlugin(new \Sabre\DAV\Auth\Plugin($authBackend, $defaults->getName())); + // FIXME: The following line is a workaround for legacy components relying on being able to send a GET to / + $server->addPlugin(new \OC\Connector\Sabre\DummyGetResponsePlugin()); + $server->addPlugin(new \OC\Connector\Sabre\FilesPlugin($objectTree)); + $server->addPlugin(new \OC\Connector\Sabre\MaintenancePlugin($this->config)); + $server->addPlugin(new \OC\Connector\Sabre\ExceptionLoggerPlugin('webdav', $this->logger)); + + // wait with registering these until auth is handled and the filesystem is setup + $server->on('beforeMethod', function () use ($server, $objectTree, $viewCallBack) { + /** @var \OC\Files\View $view */ + $view = $viewCallBack(); + $rootInfo = $view->getFileInfo(''); + + // Create ownCloud Dir + $rootDir = new \OC\Connector\Sabre\Directory($view, $rootInfo); + $objectTree->init($rootDir, $view, $this->mountManager); + + $server->addPlugin(new \OC\Connector\Sabre\TagsPlugin($objectTree, $this->tagManager)); + $server->addPlugin(new \OC\Connector\Sabre\QuotaPlugin($view)); + + // custom properties plugin must be the last one + if($this->userSession->isLoggedIn()) { + $server->addPlugin( + new \Sabre\DAV\PropertyStorage\Plugin( + new \OC\Connector\Sabre\CustomPropertiesBackend( + $objectTree, + $this->databaseConnection, + $this->userSession->getUser() + ) + ) + ); + } + $server->addPlugin(new \OC\Connector\Sabre\CopyEtagHeaderPlugin()); + }, 30); // priority 30: after auth (10) and acl(20), before lock(50) and handling the request + return $server; + } +} diff --git a/tests/lib/connector/sabre/requesttest/requesttest.php b/tests/lib/connector/sabre/requesttest/requesttest.php index 7a4f44ee71..91dd92c460 100644 --- a/tests/lib/connector/sabre/requesttest/requesttest.php +++ b/tests/lib/connector/sabre/requesttest/requesttest.php @@ -134,41 +134,23 @@ abstract class RequestTest extends TestCase { * @return Server */ protected function getSabreServer(View $view, $user, $password, ExceptionPlugin $exceptionPlugin) { + $serverFactory = new \OC\Connector\Sabre\ServerFactory( + \OC::$server->getConfig(), + \OC::$server->getLogger(), + \OC::$server->getDatabaseConnection(), + \OC::$server->getUserSession(), + \OC::$server->getMountManager(), + \OC::$server->getTagManager() + ); + + $authBackend = new Auth($user, $password); - $objectTree = new \OC\Connector\Sabre\ObjectTree(); - $server = new \OC\Connector\Sabre\Server($objectTree); - $server->setBaseUri('/'); - - // Load plugins - $server->addPlugin(new \Sabre\DAV\Auth\Plugin($authBackend, 'oc-test')); - $server->addPlugin(new \OC\Connector\Sabre\DummyGetResponsePlugin()); - $server->addPlugin(new \OC\Connector\Sabre\FilesPlugin($objectTree)); + $server = $serverFactory->createServer('/', 'dummy', $authBackend, function () use ($view) { + return $view; + }); $server->addPlugin($exceptionPlugin); - // wait with registering these until auth is handled and the filesystem is setup - $server->on('beforeMethod', function () use ($server, $objectTree, $view) { - $rootInfo = $view->getFileInfo(''); - - // Create ownCloud Dir - $mountManager = \OC\Files\Filesystem::getMountManager(); - $rootDir = new \OC\Connector\Sabre\Directory($view, $rootInfo); - $objectTree->init($rootDir, $view, $mountManager); - - $server->addPlugin(new \OC\Connector\Sabre\QuotaPlugin($view)); - - // custom properties plugin must be the last one - $server->addPlugin( - new \Sabre\DAV\PropertyStorage\Plugin( - new \OC\Connector\Sabre\CustomPropertiesBackend( - $objectTree, - \OC::$server->getDatabaseConnection(), - \OC::$server->getUserSession()->getUser() - ) - ) - ); - $server->addPlugin(new \OC\Connector\Sabre\CopyEtagHeaderPlugin()); - }, 30); // priority 30: after auth (10) and acl(20), before lock(50) and handling the request return $server; } } From d15870b998e4c022106e5d467d30bd5cf990357d Mon Sep 17 00:00:00 2001 From: Robin Appelman Date: Fri, 7 Aug 2015 17:38:12 +0200 Subject: [PATCH 3/5] fix setup/teardown --- .../sabre/requesttest/requesttest.php | 29 ++++++++++++------- 1 file changed, 18 insertions(+), 11 deletions(-) diff --git a/tests/lib/connector/sabre/requesttest/requesttest.php b/tests/lib/connector/sabre/requesttest/requesttest.php index 91dd92c460..c7739aefcd 100644 --- a/tests/lib/connector/sabre/requesttest/requesttest.php +++ b/tests/lib/connector/sabre/requesttest/requesttest.php @@ -9,6 +9,7 @@ namespace Test\Connector\Sabre\RequestTest; use OC\Connector\Sabre\Server; +use OC\Connector\Sabre\ServerFactory; use OC\Files\Mount\MountPoint; use OC\Files\Storage\Temporary; use OC\Files\View; @@ -27,6 +28,11 @@ abstract class RequestTest extends TestCase { */ protected $mountProviders; + /** + * @var \OC\Connector\Sabre\ServerFactory + */ + protected $serverFactory; + protected function getStream($string) { $stream = fopen('php://temp', 'r+'); fwrite($stream, $string); @@ -58,11 +64,22 @@ abstract class RequestTest extends TestCase { } protected function setUp() { + parent::setUp(); $this->userBackend = new \OC_User_Dummy(); \OC::$server->getUserManager()->registerBackend($this->userBackend); + + $this->serverFactory = new ServerFactory( + \OC::$server->getConfig(), + \OC::$server->getLogger(), + \OC::$server->getDatabaseConnection(), + \OC::$server->getUserSession(), + \OC::$server->getMountManager(), + \OC::$server->getTagManager() + ); } protected function tearDown() { + parent::tearDown(); \OC::$server->getUserManager()->removeBackend($this->userBackend); } @@ -134,19 +151,9 @@ abstract class RequestTest extends TestCase { * @return Server */ protected function getSabreServer(View $view, $user, $password, ExceptionPlugin $exceptionPlugin) { - $serverFactory = new \OC\Connector\Sabre\ServerFactory( - \OC::$server->getConfig(), - \OC::$server->getLogger(), - \OC::$server->getDatabaseConnection(), - \OC::$server->getUserSession(), - \OC::$server->getMountManager(), - \OC::$server->getTagManager() - ); - - $authBackend = new Auth($user, $password); - $server = $serverFactory->createServer('/', 'dummy', $authBackend, function () use ($view) { + $server = $this->serverFactory->createServer('/', 'dummy', $authBackend, function () use ($view) { return $view; }); $server->addPlugin($exceptionPlugin); From fd353650414f634adca5f1d55fc7d3470ef88122 Mon Sep 17 00:00:00 2001 From: Robin Appelman Date: Tue, 11 Aug 2015 13:28:33 +0200 Subject: [PATCH 4/5] only add tags plugin when loggedin --- lib/private/connector/sabre/serverfactory.php | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/lib/private/connector/sabre/serverfactory.php b/lib/private/connector/sabre/serverfactory.php index 7d3ed338ad..e9ec6b46f7 100644 --- a/lib/private/connector/sabre/serverfactory.php +++ b/lib/private/connector/sabre/serverfactory.php @@ -81,11 +81,11 @@ class ServerFactory { $rootDir = new \OC\Connector\Sabre\Directory($view, $rootInfo); $objectTree->init($rootDir, $view, $this->mountManager); - $server->addPlugin(new \OC\Connector\Sabre\TagsPlugin($objectTree, $this->tagManager)); $server->addPlugin(new \OC\Connector\Sabre\QuotaPlugin($view)); - // custom properties plugin must be the last one if($this->userSession->isLoggedIn()) { + $server->addPlugin(new \OC\Connector\Sabre\TagsPlugin($objectTree, $this->tagManager)); + // custom properties plugin must be the last one $server->addPlugin( new \Sabre\DAV\PropertyStorage\Plugin( new \OC\Connector\Sabre\CustomPropertiesBackend( From b7cbff23d93a24f391b39682fc7581c85d8e0017 Mon Sep 17 00:00:00 2001 From: Robin Appelman Date: Tue, 11 Aug 2015 14:46:46 +0200 Subject: [PATCH 5/5] handle single file dav endpoints --- lib/private/connector/sabre/serverfactory.php | 8 ++++++-- 1 file changed, 6 insertions(+), 2 deletions(-) diff --git a/lib/private/connector/sabre/serverfactory.php b/lib/private/connector/sabre/serverfactory.php index e9ec6b46f7..525ff0104c 100644 --- a/lib/private/connector/sabre/serverfactory.php +++ b/lib/private/connector/sabre/serverfactory.php @@ -78,8 +78,12 @@ class ServerFactory { $rootInfo = $view->getFileInfo(''); // Create ownCloud Dir - $rootDir = new \OC\Connector\Sabre\Directory($view, $rootInfo); - $objectTree->init($rootDir, $view, $this->mountManager); + if ($rootInfo->getType() === 'dir') { + $root = new \OC\Connector\Sabre\Directory($view, $rootInfo); + } else { + $root = new \OC\Connector\Sabre\File($view, $rootInfo); + } + $objectTree->init($root, $view, $this->mountManager); $server->addPlugin(new \OC\Connector\Sabre\QuotaPlugin($view));