diff --git a/apps/dav/lib/DAV/PublicAuth.php b/apps/dav/lib/DAV/PublicAuth.php
index 8b854691cc84..6ae6aeecab54 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/IPublicSharedNode.php b/apps/dav/lib/Files/PublicFiles/IPublicSharedNode.php
new file mode 100644
index 000000000000..4bd32313812f
--- /dev/null
+++ b/apps/dav/lib/Files/PublicFiles/IPublicSharedNode.php
@@ -0,0 +1,48 @@
+
+ *
+ * @copyright Copyright (c) 2019, 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\Files\Node;
+use OCP\Share\IShare;
+
+/**
+ * Interface IPublicSharedNode - common interface of all files and folders
+ * in a shared node
+ *
+ * @package OCA\DAV\Files\PublicFiles
+ */
+interface IPublicSharedNode {
+ /**
+ * @return IShare
+ */
+ public function getShare();
+
+ /**
+ * @return Node
+ */
+ public function getNode();
+
+ /**
+ * @return string
+ */
+ public function getDavPermissions();
+}
diff --git a/apps/dav/lib/Files/PublicFiles/NodeFactoryTrait.php b/apps/dav/lib/Files/PublicFiles/NodeFactoryTrait.php
new file mode 100644
index 000000000000..bdaceb7944e7
--- /dev/null
+++ b/apps/dav/lib/Files/PublicFiles/NodeFactoryTrait.php
@@ -0,0 +1,44 @@
+
+ *
+ * @copyright Copyright (c) 2019, 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\Files\File;
+use OCP\Files\Folder;
+use OCP\Files\Node;
+use OCP\Share\IShare;
+
+/**
+ * Trait NodeFactoryTrait
+ *
+ * @package OCA\DAV\Files\PublicFiles
+ */
+trait NodeFactoryTrait {
+ private function nodeFactory(Node $node, IShare $share) {
+ if ($node instanceof Folder) {
+ return new SharedFolder($node, $share);
+ }
+ if ($node instanceof File) {
+ return new SharedFile($node, $share);
+ }
+ throw new \InvalidArgumentException();
+ }
+}
diff --git a/apps/dav/lib/Files/PublicFiles/PublicFilesPlugin.php b/apps/dav/lib/Files/PublicFiles/PublicFilesPlugin.php
new file mode 100644
index 000000000000..f4a98e7c3c7b
--- /dev/null
+++ b/apps/dav/lib/Files/PublicFiles/PublicFilesPlugin.php
@@ -0,0 +1,108 @@
+
+ *
+ * @copyright Copyright (c) 2019, 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 OCA\DAV\Connector\Sabre\FilesPlugin;
+use Sabre\DAV\INode;
+use Sabre\DAV\PropFind;
+use Sabre\DAV\Server;
+use Sabre\DAV\ServerPlugin;
+use Sabre\DAV\Xml\Property\GetLastModified;
+
+/**
+ * Class PublicFilesPlugin - additional PROPFIND properties for public shared
+ * files and folders are handled with this plugin
+ *
+ * @package OCA\DAV\Files\PublicFiles
+ */
+class PublicFilesPlugin extends ServerPlugin {
+ const PUBLIC_LINK_ITEM_TYPE = '{http://owncloud.org/ns}public-link-item-type';
+ const PUBLIC_LINK_PERMISSION = '{http://owncloud.org/ns}public-link-permission';
+ const PUBLIC_LINK_EXPIRATION = '{http://owncloud.org/ns}public-link-expiration';
+ const PUBLIC_LINK_SHARE_DATETIME = '{http://owncloud.org/ns}public-link-share-datetime';
+ const PUBLIC_LINK_SHARE_OWNER = '{http://owncloud.org/ns}public-link-share-owner';
+
+ /** @var Server */
+ private $server;
+
+ public function initialize(Server $server) {
+ $this->server = $server;
+
+ $this->server->on('propFind', [$this, 'propFind']);
+ }
+
+ public function propFind(PropFind $propFind, INode $node) {
+ // properties about the share
+ if ($node instanceof PublicSharedRootNode) {
+ $propFind->handle(self::PUBLIC_LINK_ITEM_TYPE, static function () use ($node) {
+ return $node->getShare()->getNodeType();
+ });
+
+ $propFind->handle(self::PUBLIC_LINK_PERMISSION, static function () use ($node) {
+ return $node->getShare()->getPermissions();
+ });
+
+ $propFind->handle(self::PUBLIC_LINK_EXPIRATION, static function () use ($node) {
+ $expire = $node->getShare()->getExpirationDate();
+ if ($expire) {
+ return new GetLastModified($expire);
+ }
+ return null;
+ });
+
+ $propFind->handle(self::PUBLIC_LINK_SHARE_DATETIME, static function () use ($node) {
+ return new GetLastModified($node->getShare()->getShareTime());
+ });
+
+ $propFind->handle(self::PUBLIC_LINK_SHARE_OWNER, static function () use ($node) {
+ return $node->getShare()->getShareOwner();
+ });
+
+ $propFind->handle(FilesPlugin::PERMISSIONS_PROPERTYNAME, static function () use ($node) {
+ return $node->getPermissions();
+ });
+ }
+
+ // properties about the resources within the public link
+ if ($node instanceof IPublicSharedNode) {
+ $propFind->handle(FilesPlugin::INTERNAL_FILEID_PROPERTYNAME, static function () use ($node) {
+ return $node->getNode()->getId();
+ });
+
+ $propFind->handle(FilesPlugin::PERMISSIONS_PROPERTYNAME, static function () use ($node) {
+ return $node->getDavPermissions();
+ });
+
+ $propFind->handle(FilesPlugin::OWNER_ID_PROPERTYNAME, static function () use ($node) {
+ $owner = $node->getNode()->getOwner();
+ return $owner->getUID();
+ });
+ $propFind->handle(FilesPlugin::OWNER_DISPLAY_NAME_PROPERTYNAME, static function () use ($node) {
+ $owner = $node->getNode()->getOwner();
+ return $owner->getDisplayName();
+ });
+ $propFind->handle(FilesPlugin::SIZE_PROPERTYNAME, static function () use ($node) {
+ return $node->getNode()->getSize();
+ });
+ }
+ }
+}
diff --git a/apps/dav/lib/Files/PublicFiles/PublicSharedRootNode.php b/apps/dav/lib/Files/PublicFiles/PublicSharedRootNode.php
new file mode 100644
index 000000000000..6ba63c86d714
--- /dev/null
+++ b/apps/dav/lib/Files/PublicFiles/PublicSharedRootNode.php
@@ -0,0 +1,154 @@
+
+ *
+ * @copyright Copyright (c) 2019, 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\FileInfo;
+use OCP\Files\InvalidPathException;
+use OCP\Files\Node;
+use OCP\Files\NotFoundException;
+use OCP\Files\NotPermittedException;
+use OCP\Share\IShare;
+use Sabre\DAV\Collection;
+use Sabre\DAV\Exception\Forbidden;
+use Sabre\DAV\INode;
+
+/**
+ * Class PublicSharedRootNode - root node of a public share
+ *
+ * @package OCA\DAV\Files\PublicFiles
+ */
+class PublicSharedRootNode extends Collection {
+ use NodeFactoryTrait;
+
+ /** @var IShare */
+ private $share;
+
+ /**
+ * PublicSharedRootNode constructor.
+ *
+ * @param IShare $share
+ */
+ public function __construct(IShare $share) {
+ $this->share = $share;
+ }
+ /**
+ * Returns an array with all the child nodes
+ *
+ * @return INode[]
+ */
+ public function getChildren() {
+ if ($this->share->getNodeType() === 'folder') {
+ $nodes = $this->share->getNode()->getDirectoryListing();
+ } else {
+ $nodes = [$this->share->getNode()];
+ }
+ return \array_map(function (Node $node) {
+ return $this->nodeFactory($node, $this->share);
+ }, $nodes);
+ }
+
+ public function createDirectory($name) {
+ if (!$this->checkPermissions(Constants::PERMISSION_CREATE)) {
+ throw new Forbidden('Permission denied to create directory');
+ }
+ if ($this->share->getNodeType() !== 'folder') {
+ throw new Forbidden('Creating a folder in a file is not allowed');
+ }
+ try {
+ $this->share->getNode()->newFolder($name);
+ } catch (NotPermittedException $ex) {
+ throw new Forbidden('Permission denied to create directory');
+ }
+ }
+
+ /**
+ * @param string $name
+ * @param resource|string|null $data
+ * @return string|null - the quoted etag - see base class
+ * @throws Forbidden
+ * @throws NotFoundException
+ */
+ public function createFile($name, $data = null) {
+ if (!$this->checkPermissions(Constants::PERMISSION_CREATE)) {
+ throw new Forbidden('Permission denied to create file');
+ }
+ if ($this->share->getNodeType() !== 'folder') {
+ throw new Forbidden('Permission denied to create file');
+ }
+ try {
+ $file = $this->share->getNode()->newFile($name);
+ $file->putContent(data);
+ return $file->getEtag();
+ } catch (NotPermittedException $ex) {
+ throw new Forbidden('Permission denied to create file');
+ } catch (InvalidPathException $ex) {
+ throw new Forbidden('Permission denied to create file');
+ } catch (NotFoundException $ex) {
+ throw new Forbidden('Permission denied to create file');
+ }
+ }
+
+ public function delete() {
+ if (!$this->checkPermissions(Constants::PERMISSION_DELETE)) {
+ throw new Forbidden('Permission denied to delete a resource');
+ }
+ try {
+ $this->share->getNode()->delete();
+ } catch (NotPermittedException $ex) {
+ throw new Forbidden('Permission denied to create directory');
+ }
+ }
+
+ /**
+ * Returns the name of the node.
+ *
+ * This is used to generate the url.
+ *
+ * @return string
+ */
+ public function getName() {
+ return $this->share->getToken();
+ }
+
+ public function getShare() {
+ return $this->share;
+ }
+
+ public function getPermissions() {
+ $p = '';
+ if ($this->checkPermissions(Constants::PERMISSION_DELETE)) {
+ $p .= 'D';
+ }
+ if ($this->checkPermissions(Constants::PERMISSION_UPDATE)) {
+ $p .= 'NV'; // Renameable, Moveable
+ }
+ if ($this->checkPermissions(Constants::PERMISSION_CREATE)) {
+ $p .= 'CK';
+ }
+ return $p;
+ }
+
+ protected function checkPermissions($permissions) {
+ return ($this->share->getPermissions() & $permissions) === $permissions;
+ }
+}
diff --git a/apps/dav/lib/Files/PublicFiles/PublicSharingAuth.php b/apps/dav/lib/Files/PublicFiles/PublicSharingAuth.php
new file mode 100644
index 000000000000..dc1744df8cc4
--- /dev/null
+++ b/apps/dav/lib/Files/PublicFiles/PublicSharingAuth.php
@@ -0,0 +1,141 @@
+
+ *
+ * @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\Exception\NotFound;
+use Sabre\DAV\INode;
+use Sabre\DAV\Server;
+use Sabre\HTTP\RequestInterface;
+use Sabre\HTTP\ResponseInterface;
+use function explode;
+
+/**
+ * Class PublicSharingAuth - sabre dav auth backend to handle password for
+ * public shared files and folders
+ *
+ * @package OCA\DAV\Files\PublicFiles
+ */
+class PublicSharingAuth extends AbstractBasic {
+
+ /** @var Server */
+ private $server;
+ /** @var IShare */
+ private $share;
+ /** @var IManager */
+ private $shareManager;
+
+ /**
+ * PublicSharingAuth constructor.
+ *
+ * @param Server $server
+ * @param IManager $manager
+ */
+ 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
+ * @throws NotFound
+ */
+ public function check(RequestInterface $request, ResponseInterface $response) {
+ $node = $this->resolveShare($request->getPath());
+ if (!$node instanceof PublicSharedRootNode) {
+ 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
+ */
+ public function challenge(RequestInterface $request, ResponseInterface $response) {
+ // intentionally left empty - no need to challenge the user here
+ }
+
+ /**
+ * 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);
+ }
+
+ /**
+ * @param string $path
+ * @return INode|null
+ * @throws NotFound
+ */
+ private function resolveShare($path) {
+ $elements = \explode('/', $path);
+ if ($elements[0] !== 'public-files') {
+ return null;
+ }
+
+ return $this->server->tree->getNodeForPath($elements[0] .'/' . $elements[1]);
+ }
+}
diff --git a/apps/dav/lib/Files/PublicFiles/RootCollection.php b/apps/dav/lib/Files/PublicFiles/RootCollection.php
new file mode 100644
index 000000000000..f502a72da244
--- /dev/null
+++ b/apps/dav/lib/Files/PublicFiles/RootCollection.php
@@ -0,0 +1,93 @@
+
+ *
+ * @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 - represents the list of public shares.
+ * Only in debug mode all shares will be listed.
+ * In production enumerating public shares is considered an information disclosure.
+ *
+ * @package OCA\DAV\Files\PublicFiles
+ */
+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;
+
+ public function __construct() {
+ $this->l10n = \OC::$server->getL10N('dav');
+ $this->shareManager = \OC::$server->getShareManager();
+ }
+
+ /**
+ * @inheritdoc
+ */
+ public function getName() {
+ return 'public-files';
+ }
+
+ /**
+ * @inheritdoc
+ */
+ public function getChild($name) {
+ try {
+ $share = $this->shareManager->getShareByToken($name);
+ return new PublicSharedRootNode($share);
+ } catch (ShareNotFound $ex) {
+ throw new NotFound();
+ }
+ }
+
+ /**
+ * @inheritdoc
+ */
+ public 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(static function (IShare $share) {
+ return new PublicSharedRootNode($share);
+ }, $shares);
+ }
+}
diff --git a/apps/dav/lib/Files/PublicFiles/SharedFile.php b/apps/dav/lib/Files/PublicFiles/SharedFile.php
new file mode 100644
index 000000000000..dc09bd35c9ae
--- /dev/null
+++ b/apps/dav/lib/Files/PublicFiles/SharedFile.php
@@ -0,0 +1,74 @@
+
+ *
+ * @copyright Copyright (c) 2019, 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 OCA\DAV\Files\IFileNode;
+use OCP\Files\NotPermittedException;
+use OCP\Share\IShare;
+use Sabre\DAV\Exception\Forbidden;
+use Sabre\DAV\File;
+use Sabre\DAVACL\IACL;
+
+/**
+ * Class SharedFile - represents a file living in a public shared folder
+ *
+ * @package OCA\DAV\Files\PublicFiles
+ */
+class SharedFile extends File implements IACL, IFileNode, IPublicSharedNode {
+ use SharedNodeTrait;
+
+ /** @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;
+ $this->node = $file;
+ $this->share = $share;
+ }
+
+ public function get() {
+ try {
+ return $this->file->fopen('r');
+ } catch (NotPermittedException $ex) {
+ throw new Forbidden('Permission denied to read this file');
+ }
+ }
+
+ public function getContentType() {
+ return $this->file->getMimeType();
+ }
+
+ public function put($data) {
+ try {
+ $this->file->putContent($data);
+ return $this->file->getEtag();
+ } catch (NotPermittedException $ex) {
+ throw new Forbidden('Permission denied to change data');
+ }
+ }
+}
diff --git a/apps/dav/lib/Files/PublicFiles/SharedFolder.php b/apps/dav/lib/Files/PublicFiles/SharedFolder.php
new file mode 100644
index 000000000000..39a069c6d6ca
--- /dev/null
+++ b/apps/dav/lib/Files/PublicFiles/SharedFolder.php
@@ -0,0 +1,111 @@
+
+ *
+ * @copyright Copyright (c) 2019, 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\Folder;
+use OCP\Files\NotPermittedException;
+use OCP\Share\IShare;
+use Sabre\DAV\Collection;
+use Sabre\DAV\Exception\Forbidden;
+use Sabre\DAVACL\IACL;
+
+/**
+ * Class SharedFolder - represents a folder living in a public shared folder
+ *
+ * @package OCA\DAV\Files\PublicFiles
+ */
+class SharedFolder extends Collection implements IACL, IPublicSharedNode {
+ use SharedNodeTrait, NodeFactoryTrait;
+
+ /** @var Folder */
+ private $folder;
+
+ /**
+ * SharedFolder constructor.
+ *
+ * @param Folder $folder
+ * @param IShare $share
+ */
+ public function __construct(Folder $folder, IShare $share) {
+ $this->folder = $folder;
+ $this->node = $folder;
+ $this->share = $share;
+ }
+
+ /**
+ * @inheritdoc
+ */
+ public function getChildren() {
+ $nodes = $this->folder->getDirectoryListing();
+ return \array_map(function ($node) {
+ return $this->nodeFactory($node, $this->share);
+ }, $nodes);
+ }
+
+ public function createDirectory($name) {
+ try {
+ $this->folder->newFolder($name);
+ } catch (NotPermittedException $ex) {
+ throw new Forbidden('Permission denied to create directory');
+ }
+ }
+
+ public function createFile($name, $data = null) {
+ $file = $this->folder->newFile($name);
+ $file->putContent($data);
+ }
+
+ public function getACL() {
+ $acl = [
+ [
+ 'privilege' => '{DAV:}all',
+ 'principal' => '{DAV:}owner',
+ 'protected' => true,
+ ],
+ [
+ 'privilege' => '{DAV:}read',
+ 'principal' => 'principals/system/public',
+ 'protected' => true,
+ ]
+ ];
+
+ if ($this->checkSharePermissions(Constants::PERMISSION_DELETE)) {
+ $acl[] =
+ [
+ 'privilege' => '{DAV:}unbind',
+ 'principal' => 'principals/system/public',
+ 'protected' => true,
+ ];
+ }
+ if ($this->checkSharePermissions(Constants::PERMISSION_CREATE)) {
+ $acl[] =
+ [
+ 'privilege' => '{DAV:}bind',
+ 'principal' => 'principals/system/public',
+ 'protected' => true,
+ ];
+ }
+
+ return $acl;
+ }
+}
diff --git a/apps/dav/lib/Files/PublicFiles/SharedNodeTrait.php b/apps/dav/lib/Files/PublicFiles/SharedNodeTrait.php
new file mode 100644
index 000000000000..e402cfc51aa2
--- /dev/null
+++ b/apps/dav/lib/Files/PublicFiles/SharedNodeTrait.php
@@ -0,0 +1,220 @@
+
+ *
+ * @copyright Copyright (c) 2019, 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\Files\NotPermittedException;
+use OCP\Share\IShare;
+use Sabre\DAV\Collection;
+use Sabre\DAV\Exception\Forbidden;
+use Sabre\DAVACL\ACLTrait;
+use Sabre\DAVACL\IACL;
+
+/**
+ * Trait SharedNodeTrait - common method implementations of SharedFile and SharedFolder
+ *
+ * @package OCA\DAV\Files\PublicFiles
+ */
+trait SharedNodeTrait {
+
+ /**@var Node */
+ private $node;
+ /** @var IShare */
+ private $share;
+
+ public function getName() {
+ return $this->node->getName();
+ }
+
+ public function getLastModified() {
+ return $this->node->getMTime();
+ }
+
+ public function getETag() {
+ return $this->node->getETag();
+ }
+
+ public function delete() {
+ try {
+ $this->node->delete();
+ } catch (NotPermittedException $ex) {
+ throw new Forbidden('Permission denied to delete node');
+ }
+ }
+
+ public function setName($name) {
+ try {
+ $newPath = $this->node->getParent()->getPath() . '/' . $name;
+ $this->node->move($newPath);
+ } catch (NotPermittedException $ex) {
+ throw new Forbidden('Permission denied to rename node');
+ }
+ }
+
+ /**
+ * Returns the owner principal.
+ *
+ * This must be a url to a principal, or null if there's no owner
+ *
+ * @return string|null
+ */
+ public function getOwner() {
+ return null;
+ }
+
+ /**
+ * Returns a group principal.
+ *
+ * This must be a url to a principal, or null if there's no owner
+ *
+ * @return string|null
+ */
+ public function getGroup() {
+ return null;
+ }
+
+ /**
+ * Returns a list of ACE's for this node.
+ *
+ * Each ACE has the following properties:
+ * * 'privilege', a string such as {DAV:}read or {DAV:}write. These are
+ * currently the only supported privileges
+ * * 'principal', a url to the principal who owns the node
+ * * 'protected' (optional), indicating that this ACE is not allowed to
+ * be updated.
+ *
+ * @return array
+ */
+ public function getACL() {
+ $acl = [
+ [
+ 'privilege' => '{DAV:}all',
+ 'principal' => '{DAV:}owner',
+ 'protected' => true,
+ ],
+ [
+ 'privilege' => '{DAV:}read',
+ 'principal' => 'principals/system/public',
+ 'protected' => true,
+ ]
+ ];
+
+ if (($this->node instanceof File) && $this->checkSharePermissions(Constants::PERMISSION_UPDATE)) {
+ $acl[] =
+ [
+ 'privilege' => '{DAV:}write-content',
+ 'principal' => 'principals/system/public',
+ 'protected' => true,
+ ];
+ }
+ if ($this->node instanceof Folder) {
+ if ($this->checkSharePermissions(Constants::PERMISSION_DELETE)) {
+ $acl[] =
+ [
+ 'privilege' => '{DAV:}unbind',
+ 'principal' => 'principals/system/public',
+ 'protected' => true,
+ ];
+ }
+ if ($this->checkSharePermissions(Constants::PERMISSION_CREATE)) {
+ $acl[] =
+ [
+ 'privilege' => '{DAV:}bind',
+ 'principal' => 'principals/system/public',
+ 'protected' => true,
+ ];
+ }
+ }
+
+ return $acl;
+ }
+
+ /**
+ * Updates the ACL.
+ *
+ * This method will receive a list of new ACE's as an array argument.
+ *
+ * @param array $acl
+ */
+ public function setACL(array $acl) {
+ throw new \Sabre\DAV\Exception\Forbidden('Setting ACL is not supported on this node');
+ }
+
+ /**
+ * Returns the list of supported privileges for this node.
+ *
+ * The returned data structure is a list of nested privileges.
+ * See Sabre\DAVACL\Plugin::getDefaultSupportedPrivilegeSet for a simple
+ * standard structure.
+ *
+ * If null is returned from this method, the default privilege set is used,
+ * which is fine for most common usecases.
+ *
+ * @return array|null
+ */
+ public function getSupportedPrivilegeSet() {
+ return null;
+ }
+ public function getShare() {
+ return $this->share;
+ }
+
+ /**
+ * @return Node
+ */
+ public function getNode() {
+ return $this->node;
+ }
+
+ /**
+ * @return string
+ * @throws \OCP\Files\InvalidPathException
+ * @throws \OCP\Files\NotFoundException
+ */
+ public function getDavPermissions() {
+ $node = $this->getNode();
+ $p = '';
+ if ($node->isDeletable() && $this->checkSharePermissions(Constants::PERMISSION_DELETE)) {
+ $p .= 'D';
+ }
+ if ($node->isUpdateable() && $this->checkSharePermissions(Constants::PERMISSION_UPDATE)) {
+ $p .= 'NV'; // Renameable, Moveable
+ }
+ if ($node->getType() === \OCP\Files\FileInfo::TYPE_FILE) {
+ if ($node->isUpdateable() && $this->checkSharePermissions(Constants::PERMISSION_UPDATE)) {
+ $p .= 'W';
+ }
+ } else {
+ if ($node->isCreatable() && $this->checkSharePermissions(Constants::PERMISSION_CREATE)) {
+ $p .= 'CK';
+ }
+ }
+ return $p;
+ }
+
+ protected function checkSharePermissions($permissions) {
+ return ($this->share->getPermissions() & $permissions) === $permissions;
+ }
+}
diff --git a/apps/dav/lib/RootCollection.php b/apps/dav/lib/RootCollection.php
index 4d10aaad038a..ac731a37ff77 100644
--- a/apps/dav/lib/RootCollection.php
+++ b/apps/dav/lib/RootCollection.php
@@ -65,6 +65,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(),
@@ -112,8 +114,9 @@ public function __construct() {
$systemTagRelationsCollection,
$uploadCollection,
$avatarCollection,
- new \OCA\DAV\Meta\RootCollection(\OC::$server->getLazyRootFolder()),
- $queueCollection
+ new Meta\RootCollection(\OC::$server->getLazyRootFolder()),
+ $queueCollection,
+ $publicFilesRoot
];
parent::__construct('root', $children);
diff --git a/apps/dav/lib/Server.php b/apps/dav/lib/Server.php
index 01c4100eb73f..31e791273e79 100644
--- a/apps/dav/lib/Server.php
+++ b/apps/dav/lib/Server.php
@@ -26,8 +26,10 @@
*/
namespace OCA\DAV;
+use OC;
use OC\Files\Filesystem;
use OCA\DAV\AppInfo\PluginManager;
+use OCA\DAV\CalDAV\Publishing\PublishPlugin;
use OCA\DAV\CalDAV\Schedule\IMipPlugin;
use OCA\DAV\CardDAV\ImageExportPlugin;
use OCA\DAV\Connector\Sabre\Auth;
@@ -37,8 +39,11 @@
use OCA\DAV\Connector\Sabre\CorsPlugin;
use OCA\DAV\Connector\Sabre\DavAclPlugin;
use OCA\DAV\Connector\Sabre\DummyGetResponsePlugin;
+use OCA\DAV\Connector\Sabre\ExceptionLoggerPlugin;
use OCA\DAV\Connector\Sabre\FilesPlugin;
use OCA\DAV\Connector\Sabre\FilesReportPlugin;
+use OCA\DAV\Connector\Sabre\FilesSearchReportPlugin;
+use OCA\DAV\Connector\Sabre\LockPlugin;
use OCA\DAV\Connector\Sabre\MaintenancePlugin;
use OCA\DAV\Connector\Sabre\QuotaPlugin;
use OCA\DAV\Connector\Sabre\SharesPlugin;
@@ -53,15 +58,21 @@
use OCA\DAV\Files\BrowserErrorPagePlugin;
use OCA\DAV\Files\FileLocksBackend;
use OCA\DAV\Files\PreviewPlugin;
+use OCA\DAV\Files\PublicFiles\PublicFilesPlugin;
+use OCA\DAV\Files\Sharing\PublicLinkEventsPlugin;
use OCA\DAV\JobStatus\Entity\JobStatusMapper;
use OCA\DAV\Meta\MetaPlugin;
+use OCA\DAV\Files\PublicFiles\PublicSharingAuth;
use OCA\DAV\SystemTag\SystemTagPlugin;
use OCA\DAV\TrashBin\TrashBinPlugin;
use OCA\DAV\Upload\ChunkingPlugin;
+use OCP\AppFramework\QueryException;
use OCP\IRequest;
use OCP\SabrePluginEvent;
+use Sabre\CalDAV\ICSExportPlugin;
use Sabre\CardDAV\VCFExportPlugin;
use Sabre\DAV\Auth\Plugin;
+use Sabre\DAV\Exception;
class Server {
@@ -78,37 +89,37 @@ class Server {
*
* @param IRequest $request
* @param string $baseUri
- * @throws \OCP\AppFramework\QueryException
- * @throws \Sabre\DAV\Exception
+ * @throws QueryException
+ * @throws Exception
*/
public function __construct(IRequest $request, $baseUri) {
$this->request = $request;
$this->baseUri = $baseUri;
- $logger = \OC::$server->getLogger();
- $dispatcher = \OC::$server->getEventDispatcher();
+ $logger = OC::$server->getLogger();
+ $dispatcher = OC::$server->getEventDispatcher();
$root = new RootCollection();
- $tree = new \OCA\DAV\Tree($root);
- $this->server = new \OCA\DAV\Connector\Sabre\Server($tree);
+ $tree = new Tree($root);
+ $this->server = new Connector\Sabre\Server($tree);
- $config = \OC::$server->getConfig();
+ $config = OC::$server->getConfig();
if ($config->getSystemValue('dav.enable.async', false)) {
$this->server->addPlugin(new LazyOpsPlugin(
- \OC::$server->getUserSession(),
- \OC::$server->getURLGenerator(),
- \OC::$server->getShutdownHandler(),
- \OC::$server->query(JobStatusMapper::class),
- \OC::$server->getLogger()
+ OC::$server->getUserSession(),
+ OC::$server->getURLGenerator(),
+ OC::$server->getShutdownHandler(),
+ OC::$server->query(JobStatusMapper::class),
+ OC::$server->getLogger()
));
}
// Backends
$authBackend = new Auth(
- \OC::$server->getSession(),
- \OC::$server->getUserSession(),
- \OC::$server->getRequest(),
- \OC::$server->getTwoFactorAuthManager(),
- \OC::$server->getAccountModuleManager()
+ OC::$server->getSession(),
+ OC::$server->getUserSession(),
+ OC::$server->getRequest(),
+ OC::$server->getTwoFactorAuthManager(),
+ OC::$server->getAccountModuleManager()
);
// Set URL explicitly due to reverse-proxy situations
@@ -118,8 +129,12 @@ public function __construct(IRequest $request, $baseUri) {
$this->server->addPlugin(new MaintenancePlugin($config));
$this->server->addPlugin(new ValidateRequestPlugin('dav'));
$this->server->addPlugin(new BlockLegacyClientPlugin($config));
- $this->server->addPlugin(new CorsPlugin(\OC::$server->getUserSession()));
+ $this->server->addPlugin(new CorsPlugin(OC::$server->getUserSession()));
$authPlugin = new Plugin();
+ if ($this->isRequestForSubtree(['public-files'])) {
+ $authPlugin->addBackend(new PublicSharingAuth($this->server, OC::$server->getShareManager()));
+ $this->server->addPlugin(new PublicLinkEventsPlugin(\OC::$server->getEventDispatcher()));
+ }
$authPlugin->addBackend(new PublicAuth());
$this->server->addPlugin($authPlugin);
@@ -131,16 +146,17 @@ public function __construct(IRequest $request, $baseUri) {
$authPlugin->addBackend($authBackend);
// debugging
- if (\OC::$server->getConfig()->getSystemValue('debug', false)) {
+ if (OC::$server->getConfig()->getSystemValue('debug', false)) {
$this->server->addPlugin(new \Sabre\DAV\Browser\Plugin());
} else {
$this->server->addPlugin(new DummyGetResponsePlugin());
}
- $this->server->addPlugin(new \OCA\DAV\Connector\Sabre\ExceptionLoggerPlugin('webdav', $logger));
- $this->server->addPlugin(new \OCA\DAV\Connector\Sabre\LockPlugin());
+ $this->server->addPlugin(new ExceptionLoggerPlugin('webdav', $logger));
+ $this->server->addPlugin(new LockPlugin());
$this->server->addPlugin(new \Sabre\DAV\Sync\Plugin());
- $this->server->addPlugin(new \Sabre\DAV\Locks\Plugin(new FileLocksBackend($this->server->tree, false, \OC::$server->getTimeFactory())));
+ $this->server->addPlugin(new \Sabre\DAV\Locks\Plugin(new FileLocksBackend($this->server->tree, false, OC::$server->getTimeFactory())));
+ $this->server->addPlugin(new PublicFilesPlugin());
// ACL plugin not used in files subtree, also it causes issues
// with performance and locking issues because it will query
@@ -158,67 +174,67 @@ public function __construct(IRequest $request, $baseUri) {
// calendar plugins
if ($this->isRequestForSubtree(['calendars', 'public-calendars', 'principals'])) {
- $mailer = \OC::$server->getMailer();
- $this->server->addPlugin(new \OCA\DAV\CalDAV\Plugin());
- $this->server->addPlugin(new \Sabre\CalDAV\ICSExportPlugin());
- $this->server->addPlugin(new \OCA\DAV\CalDAV\Schedule\Plugin());
+ $mailer = OC::$server->getMailer();
+ $this->server->addPlugin(new CalDAV\Plugin());
+ $this->server->addPlugin(new ICSExportPlugin());
+ $this->server->addPlugin(new CalDAV\Schedule\Plugin());
$this->server->addPlugin(new IMipPlugin($mailer, $logger, $request));
$this->server->addPlugin(new \Sabre\CalDAV\Subscriptions\Plugin());
$this->server->addPlugin(new \Sabre\CalDAV\Notifications\Plugin());
- $this->server->addPlugin(new DAV\Sharing\Plugin($authBackend, \OC::$server->getRequest()));
- $this->server->addPlugin(new \OCA\DAV\CalDAV\Publishing\PublishPlugin(
- \OC::$server->getConfig(),
- \OC::$server->getURLGenerator()
+ $this->server->addPlugin(new DAV\Sharing\Plugin($authBackend, OC::$server->getRequest()));
+ $this->server->addPlugin(new PublishPlugin(
+ OC::$server->getConfig(),
+ OC::$server->getURLGenerator()
));
}
// addressbook plugins
if ($this->isRequestForSubtree(['addressbooks', 'principals'])) {
- $this->server->addPlugin(new DAV\Sharing\Plugin($authBackend, \OC::$server->getRequest()));
- $this->server->addPlugin(new \OCA\DAV\CardDAV\Plugin());
+ $this->server->addPlugin(new DAV\Sharing\Plugin($authBackend, OC::$server->getRequest()));
+ $this->server->addPlugin(new CardDAV\Plugin());
$this->server->addPlugin(new VCFExportPlugin());
- $this->server->addPlugin(new ImageExportPlugin(\OC::$server->getLogger()));
+ $this->server->addPlugin(new ImageExportPlugin(OC::$server->getLogger()));
}
// system tags plugins
$this->server->addPlugin(new SystemTagPlugin(
- \OC::$server->getSystemTagManager(),
- \OC::$server->getGroupManager(),
- \OC::$server->getUserSession()
+ OC::$server->getSystemTagManager(),
+ OC::$server->getGroupManager(),
+ OC::$server->getUserSession()
));
$this->server->addPlugin(new CopyEtagHeaderPlugin());
$this->server->addPlugin(new ChunkingPlugin());
$this->server->addPlugin(new TrashBinPlugin());
$this->server->addPlugin(new MetaPlugin(
- \OC::$server->getUserSession(),
- \OC::$server->getLazyRootFolder()
+ OC::$server->getUserSession(),
+ OC::$server->getLazyRootFolder()
));
// Allow view-only plugin for webdav requests
$this->server->addPlugin(new ViewOnlyPlugin(
- \OC::$server->getLogger()
+ OC::$server->getLogger()
));
if (BrowserErrorPagePlugin::isBrowserRequest($request)) {
$this->server->addPlugin(new BrowserErrorPagePlugin());
}
- $this->server->addPlugin(new PreviewPlugin(\OC::$server->getTimeFactory(), \OC::$server->getPreviewManager()));
+ $this->server->addPlugin(new PreviewPlugin(OC::$server->getTimeFactory(), OC::$server->getPreviewManager()));
// wait with registering these until auth is handled and the filesystem is setup
$this->server->on('beforeMethod:*', function () use ($root) {
// custom properties plugin must be the last one
- $userSession = \OC::$server->getUserSession();
+ $userSession = OC::$server->getUserSession();
$user = $userSession->getUser();
if ($user !== null) {
$view = Filesystem::getView();
$this->server->addPlugin(
new FilesPlugin(
$this->server->tree,
- \OC::$server->getConfig(),
+ OC::$server->getConfig(),
$this->request,
false,
- !\OC::$server->getConfig()->getSystemValue('debug', false)
+ !OC::$server->getConfig()->getSystemValue('debug', false)
)
);
@@ -227,9 +243,9 @@ public function __construct(IRequest $request, $baseUri) {
$filePropertiesPlugin = new FileCustomPropertiesPlugin(
new FileCustomPropertiesBackend(
$this->server->tree,
- \OC::$server->getDatabaseConnection(),
- \OC::$server->getUserSession()->getUser(),
- \OC::$server->getRootFolder()
+ OC::$server->getDatabaseConnection(),
+ OC::$server->getUserSession()->getUser(),
+ OC::$server->getRootFolder()
)
);
$this->server->addPlugin($filePropertiesPlugin);
@@ -237,9 +253,9 @@ public function __construct(IRequest $request, $baseUri) {
$miscPropertiesPlugin = new \Sabre\DAV\PropertyStorage\Plugin(
new MiscCustomPropertiesBackend(
$this->server->tree,
- \OC::$server->getDatabaseConnection(),
- \OC::$server->getUserSession()->getUser(),
- \OC::$server->getRootFolder()
+ OC::$server->getDatabaseConnection(),
+ OC::$server->getUserSession()->getUser(),
+ OC::$server->getRootFolder()
)
);
$this->server->addPlugin($miscPropertiesPlugin);
@@ -251,18 +267,18 @@ public function __construct(IRequest $request, $baseUri) {
}
$this->server->addPlugin(
new TagsPlugin(
- $this->server->tree, \OC::$server->getTagManager()
+ $this->server->tree, OC::$server->getTagManager()
)
);
// TODO: switch to LazyUserFolder
- $userFolder = \OC::$server->getUserFolder();
+ $userFolder = OC::$server->getUserFolder();
$this->server->addPlugin(new SharesPlugin(
$this->server->tree,
$userSession,
- \OC::$server->getShareManager()
+ OC::$server->getShareManager()
));
$this->server->addPlugin(new CommentPropertiesPlugin(
- \OC::$server->getCommentsManager(),
+ OC::$server->getCommentsManager(),
$userSession
));
@@ -270,25 +286,25 @@ public function __construct(IRequest $request, $baseUri) {
$this->server->addPlugin(new FilesReportPlugin(
$this->server->tree,
$view,
- \OC::$server->getSystemTagManager(),
- \OC::$server->getSystemTagObjectMapper(),
- \OC::$server->getTagManager(),
+ OC::$server->getSystemTagManager(),
+ OC::$server->getSystemTagObjectMapper(),
+ OC::$server->getTagManager(),
$userSession,
- \OC::$server->getGroupManager(),
+ OC::$server->getGroupManager(),
$userFolder
));
}
$this->server->addPlugin(
- new \OCA\DAV\Connector\Sabre\FilesSearchReportPlugin(
- \OC::$server->getSearch()
+ new FilesSearchReportPlugin(
+ OC::$server->getSearch()
)
);
}
// register plugins from apps
$pluginManager = new PluginManager(
- \OC::$server,
- \OC::$server->getAppManager()
+ OC::$server,
+ OC::$server->getAppManager()
);
foreach ($pluginManager->getAppPlugins() as $appPlugin) {
$this->server->addPlugin($appPlugin);
@@ -300,7 +316,7 @@ public function __construct(IRequest $request, $baseUri) {
}
public function exec() {
- $this->server->exec();
+ $this->server->start();
}
/**
@@ -309,7 +325,7 @@ public function exec() {
*/
private function isRequestForSubtree(array $subTrees) {
foreach ($subTrees as $subTree) {
- $subTree = \trim($subTree, " /");
+ $subTree = \trim($subTree, ' /');
if (\strpos($this->server->getRequestUri(), "$subTree/") === 0) {
return true;
}
diff --git a/apps/dav/tests/unit/Files/PublicFiles/PublicFilesPluginTest.php b/apps/dav/tests/unit/Files/PublicFiles/PublicFilesPluginTest.php
new file mode 100644
index 000000000000..e803434896ea
--- /dev/null
+++ b/apps/dav/tests/unit/Files/PublicFiles/PublicFilesPluginTest.php
@@ -0,0 +1,68 @@
+
+ *
+ * @copyright Copyright (c) 2019, 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\Tests\Unit\Files\PublicFiles;
+
+use OCA\DAV\Files\PublicFiles\PublicFilesPlugin;
+use OCA\DAV\Files\PublicFiles\PublicSharedRootNode;
+use OCP\Share\IShare;
+use Sabre\DAV\PropFind;
+use Sabre\DAV\Server;
+use Sabre\DAV\Xml\Property\GetLastModified;
+use Test\TestCase;
+
+class PublicFilesPluginTest extends TestCase {
+ public function testInit() {
+ $server = $this->createMock(Server::class);
+ $server->expects($this->once())->method('on')->with('propFind');
+
+ $plugin = new PublicFilesPlugin();
+ $plugin->initialize($server);
+ }
+
+ /**
+ * @dataProvider providesMethods
+ */
+ public function testPropFindPublicSharedRootNode($expectedMethod, $expectedMethodReturn, $prop, $methodReturnValue = null) {
+ if ($methodReturnValue === null) {
+ $methodReturnValue = $expectedMethodReturn;
+ }
+ $node = $this->createMock(PublicSharedRootNode::class);
+ $share = $this->createMock(IShare::class);
+ $node->method('getShare')->willReturn($share);
+ $share->expects(self::once())->method($expectedMethod)->willReturn($methodReturnValue);
+ $propFind = new PropFind('', [$prop]);
+ $plugin = new PublicFilesPlugin();
+ $plugin->propFind($propFind, $node);
+
+ self::assertEquals($expectedMethodReturn, $propFind->get($prop));
+ }
+
+ public function providesMethods() {
+ return [
+ ['getShareOwner', 'alice', PublicFilesPlugin::PUBLIC_LINK_SHARE_OWNER],
+ ['getExpirationDate', new GetLastModified(123456), PublicFilesPlugin::PUBLIC_LINK_EXPIRATION, 123456],
+ ['getPermissions', 1, PublicFilesPlugin::PUBLIC_LINK_PERMISSION],
+ ['getNodeType', 'file', PublicFilesPlugin::PUBLIC_LINK_ITEM_TYPE],
+ ['getShareTime', new GetLastModified(123456), PublicFilesPlugin::PUBLIC_LINK_SHARE_DATETIME, 123456],
+ ];
+ }
+}
diff --git a/apps/dav/tests/unit/Files/PublicFiles/PublicSharingAuthTest.php b/apps/dav/tests/unit/Files/PublicFiles/PublicSharingAuthTest.php
new file mode 100644
index 000000000000..5b908bbb54b7
--- /dev/null
+++ b/apps/dav/tests/unit/Files/PublicFiles/PublicSharingAuthTest.php
@@ -0,0 +1,99 @@
+
+ *
+ * @copyright Copyright (c) 2019, 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\Tests\Unit\Files\PublicFiles;
+
+use OCA\DAV\Files\PublicFiles\PublicFilesPlugin;
+use OCA\DAV\Files\PublicFiles\PublicSharingAuth;
+use OCA\DAV\Files\PublicFiles\PublicSharedRootNode;
+use OCP\Share\IManager;
+use OCP\Share\IShare;
+use PHPUnit\Framework\MockObject\MockObject;
+use Sabre\DAV\PropFind;
+use Sabre\DAV\Server;
+use Sabre\DAV\Tree;
+use Sabre\DAV\Xml\Property\GetLastModified;
+use Sabre\HTTP\RequestInterface;
+use Sabre\HTTP\ResponseInterface;
+use Test\TestCase;
+
+class PublicSharingAuthTest extends TestCase {
+ /**
+ * @dataProvider providesCheckData
+ */
+ public function testCheck($expectedResult, $shareNode, $authHeader = null) {
+ $tree = $this->createMock(Tree::class);
+ $server = $this->createMock(Server::class);
+ $server->tree = $tree;
+ $manager = $this->createMock(IManager::class);
+ $manager->method('checkPassword')->willReturnCallback(static function ($share, $password) {
+ return $share->getPassword() === $password;
+ });
+
+ $tree->method('getNodeForPath')->willReturn($shareNode);
+ $auth = new PublicSharingAuth($server, $manager);
+
+ $req = $this->createMock(RequestInterface::class);
+ $req->method('getPath')->willReturn('public-files/123456');
+ if ($auth) {
+ $req->method('getHeader')->willReturnCallback(static function ($key) use ($authHeader) {
+ if ($key === 'Authorization') {
+ return $authHeader;
+ }
+ return null;
+ });
+ }
+ $resp = $this->createMock(ResponseInterface::class);
+ $result = $auth->check($req, $resp);
+
+ self::assertEquals($expectedResult, $result);
+ }
+
+ public function providesCheckData() {
+ $validResult = [true, 'principals/system/public'];
+ $authHeaderMissing = [false, 'No \'Authorization: Basic\' header found. Either the client didn\'t send one, or the server is misconfigured'];
+ $wrongUserOrPassword = [false, 'Username or password was incorrect'];
+ $shareWithoutPassword = $this->createPublicSharedRootNode();
+ $shareWithPassword = $this->createPublicSharedRootNode('123456');
+
+ return [
+ 'not a share node' => [$validResult, null],
+ 'no password' => [$validResult, $shareWithoutPassword],
+ 'with password - but no auth header' => [$authHeaderMissing, $shareWithPassword],
+ 'with password - and valid auth header' => [$validResult, $shareWithPassword, 'Basic cHVibGljOjEyMzQ1Ng=='],
+ 'with password - and wrong password in auth header' => [$wrongUserOrPassword, $shareWithPassword, 'Basic cHViaWM6MTIzNDU2'],
+ 'with password - and invalid auth header' => [$authHeaderMissing, $shareWithPassword, 'Basic 1111111'],
+ ];
+ }
+
+ /**
+ * @return MockObject
+ */
+ private function createPublicSharedRootNode($password = null) {
+ $shareWithoutPassword = $this->createMock(PublicSharedRootNode::class);
+ $share = $this->createMock(IShare::class);
+ if ($password) {
+ $share->method('getPassword')->willReturn($password);
+ }
+ $shareWithoutPassword->method('getShare')->willReturn($share);
+ return $shareWithoutPassword;
+ }
+}
diff --git a/core/routes.php b/core/routes.php
index 97e109a8c69b..9429fb62e077 100644
--- a/core/routes.php
+++ b/core/routes.php
@@ -92,7 +92,13 @@
});
// Sharing routes
-$this->create('files_sharing.sharecontroller.showShare', '/s/{token}')->action(function ($urlParams) {
+$this->create('files_sharing.sharecontroller.showShare', '/s/{token}')->action(static function ($urlParams) {
+ $phoenixBaseUrl = \OC::$server->getConfig()->getSystemValue('phoenix.baseUrl', null);
+ if ($phoenixBaseUrl) {
+ $token = $urlParams['token'];
+ \OC_Response::redirect("$phoenixBaseUrl/index.html#/s/$token");
+ return;
+ }
$app = new \OCA\Files_Sharing\AppInfo\Application($urlParams);
$app->dispatch('ShareController', 'showShare');
});
diff --git a/lib/private/Files/Node/Node.php b/lib/private/Files/Node/Node.php
index 8bb9a4b3b515..ecdf175a3284 100644
--- a/lib/private/Files/Node/Node.php
+++ b/lib/private/Files/Node/Node.php
@@ -424,4 +424,8 @@ public function move($targetPath) {
throw new NotPermittedException('No permission to move to path ' . $targetPath);
}
}
+
+ public function getView() {
+ return $this->view;
+ }
}
diff --git a/lib/private/Share/Constants.php b/lib/private/Share/Constants.php
index 75007efcda63..aa99210a7147 100644
--- a/lib/private/Share/Constants.php
+++ b/lib/private/Share/Constants.php
@@ -43,7 +43,7 @@ class Constants {
const FORMAT_STATUSES = -2;
const FORMAT_SOURCES = -3; // ToDo Check if it is still in use otherwise remove it
- const RESPONSE_FORMAT = 'json'; // default resonse format for ocs calls
+ const RESPONSE_FORMAT = 'json'; // default response format for ocs calls
const TOKEN_LENGTH = 15; // old (oc7) length is 32, keep token length in db at least that for compatibility
diff --git a/lib/public/Files/Node.php b/lib/public/Files/Node.php
index c391095be404..b69a5c90f76c 100644
--- a/lib/public/Files/Node.php
+++ b/lib/public/Files/Node.php
@@ -52,6 +52,7 @@ public function move($targetPath);
/**
* Delete the file or folder
* @return void
+ * @throws NotPermittedException
* @since 6.0.0
*/
public function delete();