diff --git a/apps/dav/lib/DAV/PublicAuth.php b/apps/dav/lib/DAV/PublicAuth.php index c637e7929b01..ea1160fa4ab3 100644 --- a/apps/dav/lib/DAV/PublicAuth.php +++ b/apps/dav/lib/DAV/PublicAuth.php @@ -29,9 +29,6 @@ class PublicAuth implements BackendInterface { /** @var string[] */ private $publicURLs; - /** - * @param string[] $publicURLs - */ public function __construct() { $this->publicURLs = [ 'public-calendars', diff --git a/apps/dav/lib/Files/PublicFiles/PublicSharingAuth.php b/apps/dav/lib/Files/PublicFiles/PublicSharingAuth.php new file mode 100644 index 000000000000..e449d174473b --- /dev/null +++ b/apps/dav/lib/Files/PublicFiles/PublicSharingAuth.php @@ -0,0 +1,116 @@ + + * + * @copyright Copyright (c) 2017, ownCloud GmbH + * @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 OCA\DAV\Files\PublicFiles; + +use OCP\Share\IManager; +use OCP\Share\IShare; +use Sabre\DAV\Auth\Backend\AbstractBasic; +use Sabre\DAV\Server; +use Sabre\HTTP\RequestInterface; +use Sabre\HTTP\ResponseInterface; + +class PublicSharingAuth extends AbstractBasic { + + /** @var Server */ + private $server; + /** @var IShare */ + private $share; + /** @var IManager */ + private $shareManager; + + /** + * PublicSharingAuth constructor. + * + * @param Server $server + */ + public function __construct(Server $server, IManager $manager) { + $this->server = $server; + $this->shareManager = $manager; + $this->principalPrefix = 'principals/system/'; + $this->setRealm('owncloud/share'); + } + + /** + * When this method is called, the backend must check if authentication was + * successful. + * + * The returned value must be one of the following + * + * [true, "principals/username"] + * [false, "reason for failure"] + * + * If authentication was successful, it's expected that the authentication + * backend returns a so-called principal url. + * + * Examples of a principal url: + * + * principals/admin + * principals/user1 + * principals/users/joe + * principals/uid/123457 + * + * If you don't use WebDAV ACL (RFC3744) we recommend that you simply + * return a string such as: + * + * principals/users/[username] + * + * @param RequestInterface $request + * @param ResponseInterface $response + * @return array + */ + function check(RequestInterface $request, ResponseInterface $response) { + + $node = $this->server->tree->getNodeForPath($request->getPath()); + if (!$node instanceof ShareNode && !$node instanceof SharedFile && !$node instanceof SharedFolder) { + return [true, "principals/system/public"]; + } + $this->share = $node->getShare(); + $password = $this->share->getPassword(); + if ($password === null) { + return [true, "principals/system/public"]; + } + + return parent::check($request, $response); + } + + /** + * @inheritdoc + */ + function challenge(RequestInterface $request, ResponseInterface $response) { + } + + /** + * Validates a username and password + * + * This method should return true or false depending on if login + * succeeded. + * + * @param string $username + * @param string $password + * @return bool + */ + protected function validateUserPass($username, $password) { + if ($username !== 'public') { + return false; + } + return $this->shareManager->checkPassword($this->share, $password); + } +} diff --git a/apps/dav/lib/Files/PublicFiles/RootCollection.php b/apps/dav/lib/Files/PublicFiles/RootCollection.php new file mode 100644 index 000000000000..fb361b3cf6f2 --- /dev/null +++ b/apps/dav/lib/Files/PublicFiles/RootCollection.php @@ -0,0 +1,87 @@ + + * + * @copyright Copyright (c) 2017, ownCloud GmbH + * @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 OCA\DAV\Files\PublicFiles; + +use OC\Share\Constants; +use OCP\Share\Exceptions\ShareNotFound; +use OCP\Share\IManager; +use OCP\Share\IShare; +use Sabre\DAV\Collection; +use Sabre\DAV\Exception\MethodNotAllowed; +use Sabre\DAV\Exception\NotFound; +use Sabre\DAV\SimpleCollection; +use Sabre\DAV\SimpleFile; + +class RootCollection extends Collection { + + /** @var IManager */ + private $shareManager; + /** @var \OCP\IL10N */ + protected $l10n; + + /** + * If this value is set to true, it effectively disables listing of users + * it still allows user to find other users if they have an exact url. + * + * @var bool + */ + public $disableListing = false; + + function __construct() { + $this->l10n = \OC::$server->getL10N('dav'); + $this->shareManager = \OC::$server->getShareManager(); + } + + /** + * @inheritdoc + */ + function getName() { + return 'public-files'; + } + + /** + * @inheritdoc + */ + function getChild($name) { + try { + $share = $this->shareManager->getShareByToken($name); + $password = $share->getPassword(); + return new ShareNode($share); + } catch (ShareNotFound $ex) { + throw new NotFound(); + } + } + + /** + * @inheritdoc + */ + function getChildren() { + if ($this->disableListing) { + throw new MethodNotAllowed('Listing members of this collection is disabled'); + } + + $shares = $this->shareManager->getAllSharedWith(null, [Constants::SHARE_TYPE_LINK]); + return array_map(function(IShare $share) { + return new ShareNode($share); + }, $shares); + } +} diff --git a/apps/dav/lib/Files/PublicFiles/ShareNode.php b/apps/dav/lib/Files/PublicFiles/ShareNode.php new file mode 100644 index 000000000000..76b13a42404b --- /dev/null +++ b/apps/dav/lib/Files/PublicFiles/ShareNode.php @@ -0,0 +1,59 @@ +share = $share; + } + /** + * Returns an array with all the child nodes + * + * @return INode[] + */ + function getChildren() { + if ($this->share->getNodeType() === 'folder') { + $nodes = $this->share->getNode()->getDirectoryListing(); + } else { + $nodes = [$this->share->getNode()]; + } + return array_map(function(Node $node) { + if ($node->getType() === FileInfo::TYPE_FOLDER) { + return new SharedFolder($node, $this->share); + } + return new SharedFile($node, $this->share); + }, $nodes); + } + + /** + * Returns the name of the node. + * + * This is used to generate the url. + * + * @return string + */ + function getName() { + return $this->share->getToken(); + } + + function getShare() { + return $this->share; + } +} \ No newline at end of file diff --git a/apps/dav/lib/Files/PublicFiles/SharedFile.php b/apps/dav/lib/Files/PublicFiles/SharedFile.php new file mode 100644 index 000000000000..e67087287d66 --- /dev/null +++ b/apps/dav/lib/Files/PublicFiles/SharedFile.php @@ -0,0 +1,109 @@ + + * + * @copyright Copyright (c) 2017, ownCloud GmbH + * @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 OCA\DAV\Files\PublicFiles; + + +use OCP\Share\IShare; +use Sabre\DAV\File; +use Sabre\DAVACL\ACLTrait; +use Sabre\DAVACL\IACL; + +/** + * Class MetaFile + * This is a Sabre based implementation of a file living in the /meta resource. + * + * @package OCA\DAV\Meta + */ +class SharedFile extends File implements IACL { + + use ACLTrait; + + /** @var \OCP\Files\File */ + private $file; + + /** + * MetaFolder constructor. + * + * @param \OCP\Files\File $file + * @param IShare $share + */ + public function __construct(\OCP\Files\File $file, IShare $share) { + $this->file = $file; + } + + /** + * @inheritdoc + */ + function getName() { + return $this->file->getName(); + } + + public function getSize() { + return $this->file->getSize(); + } + + public function getContentType() { + return $this->file->getMimeType(); + } + + public function getETag() { + return $this->file->getETag(); + } + + function getLastModified() { + return $this->file->getMTime(); + } + + function delete() { + // TODO: check permissions - via ACL? + $this->file->delete(); + } + +// function setName($name) { +// $this->file->setName($name); +// } + + function getOwner() { + return ''; + } + + function getACL() { + return [ + [ + 'privilege' => '{DAV:}all', + 'principal' => '{DAV:}owner', + 'protected' => true, + ], + [ + 'privilege' => '{DAV:}read', + 'principal' => 'principals/system/public', + 'protected' => true, + ] + ]; + } + + function getShare() { + return $this->share; + } + +} diff --git a/apps/dav/lib/Files/PublicFiles/SharedFolder.php b/apps/dav/lib/Files/PublicFiles/SharedFolder.php new file mode 100644 index 000000000000..8136be752e61 --- /dev/null +++ b/apps/dav/lib/Files/PublicFiles/SharedFolder.php @@ -0,0 +1,132 @@ + + * + * @copyright Copyright (c) 2017, ownCloud GmbH + * @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 OCA\DAV\Files\PublicFiles; + + +use OCP\Constants; +use OCP\Files\File; +use OCP\Files\Folder; +use OCP\Files\Node; +use OCP\Share\IShare; +use Sabre\DAV\Collection; +use Sabre\DAVACL\ACLTrait; +use Sabre\DAVACL\IACL; + +/** + * Class MetaFolder + * This is a Sabre based implementation of a folder living in the /meta resource. + * + * @package OCA\DAV\Meta + */ +class SharedFolder extends Collection implements IACL { + use ACLTrait; + + /** @var Folder */ + private $folder; + /** @var IShare */ + private $share; + + /** + * MetaFolder constructor. + * + * @param Folder $folder + */ + public function __construct(Folder $folder, IShare $share) { + $this->folder = $folder; + $this->share = $share; + } + + /** + * @inheritdoc + */ + function getChildren() { + $nodes = $this->folder->getDirectoryListing(); + return array_map(function($node) { + return $this->nodeFactory($node); + }, $nodes); + } + + /** + * @inheritdoc + */ + function getName() { + return $this->folder->getName(); + } + + function getLastModified() { + return $this->folder->getMTime(); + } + + function createDirectory($name) { + $this->folder->newFolder($name); + } + + function createFile($name, $data = null) { + $file = $this->folder->newFile($name); + $file->putContent($data); + + } + + private function nodeFactory(Node $node) { + if ($node instanceof Folder) { + return new SharedFolder($node, $this->share); + } + if ($node instanceof File) { + return new SharedFile($node, $this->share); + } + throw new \InvalidArgumentException(); + } + + function getACL() { + $acl = [ + [ + 'privilege' => '{DAV:}all', + 'principal' => '{DAV:}owner', + 'protected' => true, + ], + [ + 'privilege' => '{DAV:}read', + 'principal' => 'principals/system/public', + 'protected' => true, + ] + ]; + + // TODO: add more acl to convert the logic + if ($this->share->getPermissions() & Constants::PERMISSION_DELETE === Constants::PERMISSION_DELETE) { + $acl[]= [ + [ + 'privilege' => '{DAV:}unbind', + 'principal' => 'principals/system/public', + 'protected' => true, + ] + ]; + } + + return $acl; + } + + function getShare() { + return $this->share; + } + +} diff --git a/apps/dav/lib/RootCollection.php b/apps/dav/lib/RootCollection.php index 3f5ab59d2c8f..29e5031f8314 100644 --- a/apps/dav/lib/RootCollection.php +++ b/apps/dav/lib/RootCollection.php @@ -64,6 +64,8 @@ public function __construct() { $calendarRoot->disableListing = $disableListing; $publicCalendarRoot = new PublicCalendarRoot($caldavBackend); $publicCalendarRoot->disableListing = $disableListing; + $publicFilesRoot = new Files\PublicFiles\RootCollection(); + $publicFilesRoot->disableListing = $disableListing; $systemTagCollection = new SystemTag\SystemTagsByIdCollection( \OC::$server->getSystemTagManager(), @@ -107,7 +109,8 @@ public function __construct() { $systemTagRelationsCollection, $uploadCollection, $avatarCollection, - new \OCA\DAV\Meta\RootCollection(\OC::$server->getRootFolder()) + new \OCA\DAV\Meta\RootCollection(\OC::$server->getRootFolder()), + $publicFilesRoot ]; parent::__construct('root', $children); diff --git a/apps/dav/lib/Server.php b/apps/dav/lib/Server.php index f0ccc621b8e7..53c2032bebd9 100644 --- a/apps/dav/lib/Server.php +++ b/apps/dav/lib/Server.php @@ -51,6 +51,7 @@ use OCA\DAV\Files\BrowserErrorPagePlugin; use OCA\DAV\Files\PreviewPlugin; use OCA\DAV\Files\ZsyncPlugin; +use OCA\DAV\Files\PublicFiles\PublicSharingAuth; use OCA\DAV\SystemTag\SystemTagPlugin; use OCA\DAV\Upload\ChunkingPlugin; use OCA\DAV\Upload\ChunkingPluginZsync; @@ -103,6 +104,7 @@ public function __construct(IRequest $request, $baseUri) { $this->server->addPlugin(new BlockLegacyClientPlugin($config)); $this->server->addPlugin(new CorsPlugin(\OC::$server->getUserSession())); $authPlugin = new Plugin(); + $authPlugin->addBackend(new PublicSharingAuth($this->server, \OC::$server->getShareManager())); $authPlugin->addBackend(new PublicAuth()); $this->server->addPlugin($authPlugin); diff --git a/lib/private/Files/Node/Node.php b/lib/private/Files/Node/Node.php index a4af937fe7c8..f90dfaabfd25 100644 --- a/lib/private/Files/Node/Node.php +++ b/lib/private/Files/Node/Node.php @@ -425,4 +425,7 @@ public function move($targetPath) { } } + public function getView() { + return $this->view; + } }