Makes it possible to store download permission Signed-off-by: Vincent Petry <vincent@nextcloud.com>pull/32482/head
parent
ee23f41abe
commit
a95c19e14b
@ -0,0 +1,113 @@ |
||||
<?php |
||||
/** |
||||
* @author Piotr Mrowczynski piotr@owncloud.com |
||||
* |
||||
* @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 <http://www.gnu.org/licenses/> |
||||
* |
||||
*/ |
||||
|
||||
namespace OCA\DAV\DAV; |
||||
|
||||
use OCA\DAV\Connector\Sabre\Exception\Forbidden; |
||||
use OCA\DAV\Connector\Sabre\File as DavFile; |
||||
use OCA\DAV\Meta\MetaFile; |
||||
use OCP\Files\FileInfo; |
||||
use OCP\Files\NotFoundException; |
||||
use OCP\ILogger; |
||||
use Sabre\DAV\Server; |
||||
use Sabre\DAV\ServerPlugin; |
||||
use Sabre\HTTP\RequestInterface; |
||||
use Sabre\DAV\Exception\NotFound; |
||||
|
||||
/** |
||||
* Sabre plugin for restricting file share receiver download: |
||||
*/ |
||||
class ViewOnlyPlugin extends ServerPlugin { |
||||
|
||||
/** @var Server $server */ |
||||
private $server; |
||||
|
||||
/** @var ILogger $logger */ |
||||
private $logger; |
||||
|
||||
/** |
||||
* @param ILogger $logger |
||||
*/ |
||||
public function __construct(ILogger $logger) { |
||||
$this->logger = $logger; |
||||
} |
||||
|
||||
/** |
||||
* This initializes the plugin. |
||||
* |
||||
* This function is called by Sabre\DAV\Server, after |
||||
* addPlugin is called. |
||||
* |
||||
* This method should set up the required event subscriptions. |
||||
* |
||||
* @param Server $server |
||||
* @return void |
||||
*/ |
||||
public function initialize(Server $server) { |
||||
$this->server = $server; |
||||
//priority 90 to make sure the plugin is called before |
||||
//Sabre\DAV\CorePlugin::httpGet |
||||
$this->server->on('method:GET', [$this, 'checkViewOnly'], 90); |
||||
} |
||||
|
||||
/** |
||||
* Disallow download via DAV Api in case file being received share |
||||
* and having special permission |
||||
* |
||||
* @param RequestInterface $request request object |
||||
* @return boolean |
||||
* @throws Forbidden |
||||
* @throws NotFoundException |
||||
*/ |
||||
public function checkViewOnly( |
||||
RequestInterface $request |
||||
) { |
||||
$path = $request->getPath(); |
||||
|
||||
try { |
||||
$davNode = $this->server->tree->getNodeForPath($path); |
||||
if (!($davNode instanceof DavFile)) { |
||||
return true; |
||||
} |
||||
// Restrict view-only to nodes which are shared |
||||
$node = $davNode->getNode(); |
||||
|
||||
$storage = $node->getStorage(); |
||||
// using string as we have no guarantee that "files_sharing" app is loaded |
||||
if (!$storage->instanceOfStorage('OCA\Files_Sharing\SharedStorage')) { |
||||
return true; |
||||
} |
||||
// Extract extra permissions |
||||
/** @var \OCA\Files_Sharing\SharedStorage $storage */ |
||||
$share = $storage->getShare(); |
||||
|
||||
// Check if read-only and on whether permission can download is both set and disabled. |
||||
$canDownload = $share->getAttributes()->getAttribute('permissions', 'download'); |
||||
if ($canDownload !== null && !$canDownload) { |
||||
throw new Forbidden('Access to this resource has been denied because it is in view-only mode.'); |
||||
} |
||||
} catch (NotFound $e) { |
||||
$this->logger->warning($e->getMessage()); |
||||
} |
||||
|
||||
return true; |
||||
} |
||||
} |
||||
@ -0,0 +1,118 @@ |
||||
<?php |
||||
/** |
||||
* @author Piotr Mrowczynski piotr@owncloud.com |
||||
* |
||||
* @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 <http://www.gnu.org/licenses/> |
||||
* |
||||
*/ |
||||
namespace OCA\DAV\Tests\unit\DAV; |
||||
|
||||
use OCA\DAV\DAV\ViewOnlyPlugin; |
||||
use OCA\Files_Sharing\SharedStorage; |
||||
use OCA\DAV\Connector\Sabre\File as DavFile; |
||||
use OCP\Files\File; |
||||
use OCP\Files\Storage\IStorage; |
||||
use OCP\ILogger; |
||||
use OCP\Share\IAttributes; |
||||
use OCP\Share\IShare; |
||||
use Sabre\DAV\Server; |
||||
use Sabre\DAV\Tree; |
||||
use Test\TestCase; |
||||
use Sabre\HTTP\RequestInterface; |
||||
use OCA\DAV\Connector\Sabre\Exception\Forbidden; |
||||
|
||||
class ViewOnlyPluginTest extends TestCase { |
||||
|
||||
/** @var ViewOnlyPlugin */ |
||||
private $plugin; |
||||
/** @var Tree | \PHPUnit\Framework\MockObject\MockObject */ |
||||
private $tree; |
||||
/** @var RequestInterface | \PHPUnit\Framework\MockObject\MockObject */ |
||||
private $request; |
||||
|
||||
public function setUp(): void { |
||||
$this->plugin = new ViewOnlyPlugin( |
||||
$this->createMock(ILogger::class) |
||||
); |
||||
$this->request = $this->createMock(RequestInterface::class); |
||||
$this->tree = $this->createMock(Tree::class); |
||||
|
||||
$server = $this->createMock(Server::class); |
||||
$server->tree = $this->tree; |
||||
|
||||
$this->plugin->initialize($server); |
||||
} |
||||
|
||||
public function testCanGetNonDav() { |
||||
$this->request->expects($this->once())->method('getPath')->willReturn('files/test/target'); |
||||
$this->tree->method('getNodeForPath')->willReturn(null); |
||||
|
||||
$this->assertTrue($this->plugin->checkViewOnly($this->request)); |
||||
} |
||||
|
||||
public function testCanGetNonShared() { |
||||
$this->request->expects($this->once())->method('getPath')->willReturn('files/test/target'); |
||||
$davNode = $this->createMock(DavFile::class); |
||||
$this->tree->method('getNodeForPath')->willReturn($davNode); |
||||
|
||||
$file = $this->createMock(File::class); |
||||
$davNode->method('getNode')->willReturn($file); |
||||
|
||||
$storage = $this->createMock(IStorage::class); |
||||
$file->method('getStorage')->willReturn($storage); |
||||
$storage->method('instanceOfStorage')->with(SharedStorage::class)->willReturn(false); |
||||
|
||||
$this->assertTrue($this->plugin->checkViewOnly($this->request)); |
||||
} |
||||
|
||||
public function providesDataForCanGet() { |
||||
return [ |
||||
// has attribute permissions-download enabled - can get file |
||||
[ $this->createMock(File::class), true, true], |
||||
// has no attribute permissions-download - can get file |
||||
[ $this->createMock(File::class), null, true], |
||||
// has attribute permissions-download disabled- cannot get the file |
||||
[ $this->createMock(File::class), false, false], |
||||
]; |
||||
} |
||||
|
||||
/** |
||||
* @dataProvider providesDataForCanGet |
||||
*/ |
||||
public function testCanGet($nodeInfo, $attrEnabled, $expectCanDownloadFile) { |
||||
$this->request->expects($this->once())->method('getPath')->willReturn('files/test/target'); |
||||
|
||||
$davNode = $this->createMock(DavFile::class); |
||||
$this->tree->method('getNodeForPath')->willReturn($davNode); |
||||
|
||||
$davNode->method('getNode')->willReturn($nodeInfo); |
||||
|
||||
$storage = $this->createMock(SharedStorage::class); |
||||
$share = $this->createMock(IShare::class); |
||||
$nodeInfo->method('getStorage')->willReturn($storage); |
||||
$storage->method('instanceOfStorage')->with(SharedStorage::class)->willReturn(true); |
||||
$storage->method('getShare')->willReturn($share); |
||||
|
||||
$extAttr = $this->createMock(IAttributes::class); |
||||
$share->method('getAttributes')->willReturn($extAttr); |
||||
$extAttr->method('getAttribute')->with('permissions', 'download')->willReturn($attrEnabled); |
||||
|
||||
if (!$expectCanDownloadFile) { |
||||
$this->expectException(Forbidden::class); |
||||
} |
||||
$this->plugin->checkViewOnly($this->request); |
||||
} |
||||
} |
||||
@ -0,0 +1,116 @@ |
||||
<?php |
||||
/** |
||||
* @author Piotr Mrowczynski piotr@owncloud.com |
||||
* |
||||
* @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 <http://www.gnu.org/licenses/> |
||||
* |
||||
*/ |
||||
|
||||
namespace OCA\Files_Sharing; |
||||
|
||||
use OCP\Files\File; |
||||
use OCP\Files\Folder; |
||||
use OCP\Files\Node; |
||||
use OCP\Files\NotFoundException; |
||||
|
||||
/** |
||||
* Handles restricting for download of files |
||||
*/ |
||||
class ViewOnly { |
||||
|
||||
/** @var Folder */ |
||||
private $userFolder; |
||||
|
||||
public function __construct(Folder $userFolder) { |
||||
$this->userFolder = $userFolder; |
||||
} |
||||
|
||||
/** |
||||
* @param string[] $pathsToCheck |
||||
* @return bool |
||||
*/ |
||||
public function check($pathsToCheck) { |
||||
// If any of elements cannot be downloaded, prevent whole download |
||||
foreach ($pathsToCheck as $file) { |
||||
try { |
||||
$info = $this->userFolder->get($file); |
||||
if ($info instanceof File) { |
||||
// access to filecache is expensive in the loop |
||||
if (!$this->checkFileInfo($info)) { |
||||
return false; |
||||
} |
||||
} elseif ($info instanceof Folder) { |
||||
// get directory content is rather cheap query |
||||
if (!$this->dirRecursiveCheck($info)) { |
||||
return false; |
||||
} |
||||
} |
||||
} catch (NotFoundException $e) { |
||||
continue; |
||||
} |
||||
} |
||||
return true; |
||||
} |
||||
|
||||
/** |
||||
* @param Folder $dirInfo |
||||
* @return bool |
||||
* @throws NotFoundException |
||||
*/ |
||||
private function dirRecursiveCheck(Folder $dirInfo) { |
||||
if (!$this->checkFileInfo($dirInfo)) { |
||||
return false; |
||||
} |
||||
// If any of elements cannot be downloaded, prevent whole download |
||||
$files = $dirInfo->getDirectoryListing(); |
||||
foreach ($files as $file) { |
||||
if ($file instanceof File) { |
||||
if (!$this->checkFileInfo($file)) { |
||||
return false; |
||||
} |
||||
} elseif ($file instanceof Folder) { |
||||
return $this->dirRecursiveCheck($file); |
||||
} |
||||
} |
||||
|
||||
return true; |
||||
} |
||||
|
||||
/** |
||||
* @param Node $fileInfo |
||||
* @return bool |
||||
* @throws NotFoundException |
||||
*/ |
||||
private function checkFileInfo(Node $fileInfo) { |
||||
// Restrict view-only to nodes which are shared |
||||
$storage = $fileInfo->getStorage(); |
||||
if (!$storage->instanceOfStorage(SharedStorage::class)) { |
||||
return true; |
||||
} |
||||
|
||||
// Extract extra permissions |
||||
/** @var \OCA\Files_Sharing\SharedStorage $storage */ |
||||
$share = $storage->getShare(); |
||||
|
||||
// Check if read-only and on whether permission can download is both set and disabled. |
||||
|
||||
$canDownload = $share->getAttributes()->getAttribute('permissions', 'download'); |
||||
if ($canDownload !== null && !$canDownload) { |
||||
return false; |
||||
} |
||||
return true; |
||||
} |
||||
} |
||||
@ -0,0 +1,238 @@ |
||||
<?php |
||||
/** |
||||
* @copyright 2022, Vincent Petry <vincent@nextcloud.com> |
||||
* |
||||
* @author Vincent Petry <vincent@nextcloud.com> |
||||
* |
||||
* @license GNU AGPL version 3 or any later version |
||||
* |
||||
* This program is free software: you can redistribute it and/or modify |
||||
* it under the terms of the GNU Affero General Public License as |
||||
* published by the Free Software Foundation, either version 3 of the |
||||
* License, or (at your option) any later version. |
||||
* |
||||
* This program is distributed in the hope that it will be useful, |
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of |
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the |
||||
* GNU Affero General Public License for more details. |
||||
* |
||||
* You should have received a copy of the GNU Affero General Public License |
||||
* along with this program. If not, see <http://www.gnu.org/licenses/>. |
||||
* |
||||
*/ |
||||
namespace OCA\Files_Sharing\Tests; |
||||
|
||||
use Psr\Log\LoggerInterface; |
||||
use OC\Share20\LegacyHooks; |
||||
use OC\Share20\Manager; |
||||
use OC\EventDispatcher\EventDispatcher; |
||||
use OCA\Files_Sharing\AppInfo\Application; |
||||
use OCA\Files_Sharing\SharedStorage; |
||||
use OCP\Constants; |
||||
use OCP\EventDispatcher\GenericEvent; |
||||
use OCP\EventDispatcher\IEventDispatcher; |
||||
use OCP\Files\Cache\ICacheEntry; |
||||
use OCP\Files\File; |
||||
use OCP\Files\Folder; |
||||
use OCP\Files\IRootFolder; |
||||
use OCP\Files\Storage\IStorage; |
||||
use OCP\IServerContainer; |
||||
use OCP\IUser; |
||||
use OCP\IUserSession; |
||||
use OCP\Share\IAttributes; |
||||
use OCP\Share\IShare; |
||||
use Symfony\Component\EventDispatcher\EventDispatcher as SymfonyDispatcher; |
||||
use Test\TestCase; |
||||
|
||||
class ApplicationTest extends TestCase { |
||||
|
||||
/** @var Application */ |
||||
private $application; |
||||
|
||||
/** @var IEventDispatcher */ |
||||
private $eventDispatcher; |
||||
|
||||
/** @var IUserSession */ |
||||
private $userSession; |
||||
|
||||
/** @var IRootFolder */ |
||||
private $rootFolder; |
||||
|
||||
/** @var Manager */ private $manager; |
||||
|
||||
protected function setUp(): void { |
||||
parent::setUp(); |
||||
|
||||
$this->application = new Application([]); |
||||
|
||||
// FIXME: how to mock this one ?? |
||||
$symfonyDispatcher = $this->createMock(SymfonyDispatcher::class); |
||||
$this->eventDispatcher = new EventDispatcher( |
||||
$symfonyDispatcher, |
||||
$this->createMock(IServerContainer::class), |
||||
$this->createMock(LoggerInterface::class) |
||||
); |
||||
$this->userSession = $this->createMock(IUserSession::class); |
||||
$this->rootFolder = $this->createMock(IRootFolder::class); |
||||
|
||||
$this->application->registerDownloadEvents( |
||||
$this->eventDispatcher, |
||||
$this->userSession, |
||||
$this->rootFolder |
||||
); |
||||
} |
||||
|
||||
public function providesDataForCanGet() { |
||||
// normal file (sender) - can download directly |
||||
$senderFileStorage = $this->createMock(IStorage::class); |
||||
$senderFileStorage->method('instanceOfStorage')->with(SharedStorage::class)->willReturn(false); |
||||
$senderFile = $this->createMock(File::class); |
||||
$senderFile->method('getStorage')->willReturn($senderFileStorage); |
||||
$senderUserFolder = $this->createMock(Folder::class); |
||||
$senderUserFolder->method('get')->willReturn($senderFile); |
||||
|
||||
$result[] = [ '/bar.txt', $senderUserFolder, true ]; |
||||
|
||||
// shared file (receiver) with attribute secure-view-enabled set false - |
||||
// can download directly |
||||
$receiverFileShareAttributes = $this->createMock(IAttributes::class); |
||||
$receiverFileShareAttributes->method('getAttribute')->with('permissions', 'download')->willReturn(true); |
||||
$receiverFileShare = $this->createMock(IShare::class); |
||||
$receiverFileShare->method('getAttributes')->willReturn($receiverFileShareAttributes); |
||||
$receiverFileStorage = $this->createMock(SharedStorage::class); |
||||
$receiverFileStorage->method('instanceOfStorage')->with(SharedStorage::class)->willReturn(true); |
||||
$receiverFileStorage->method('getShare')->willReturn($receiverFileShare); |
||||
$receiverFile = $this->createMock(File::class); |
||||
$receiverFile->method('getStorage')->willReturn($receiverFileStorage); |
||||
$receiverUserFolder = $this->createMock(Folder::class); |
||||
$receiverUserFolder->method('get')->willReturn($receiverFile); |
||||
|
||||
$result[] = [ '/share-bar.txt', $receiverUserFolder, true ]; |
||||
|
||||
// shared file (receiver) with attribute secure-view-enabled set true - |
||||
// cannot download directly |
||||
$secureReceiverFileShareAttributes = $this->createMock(IAttributes::class); |
||||
$secureReceiverFileShareAttributes->method('getAttribute')->with('permissions', 'download')->willReturn(false); |
||||
$secureReceiverFileShare = $this->createMock(IShare::class); |
||||
$secureReceiverFileShare->method('getAttributes')->willReturn($secureReceiverFileShareAttributes); |
||||
$secureReceiverFileStorage = $this->createMock(SharedStorage::class); |
||||
$secureReceiverFileStorage->method('instanceOfStorage')->with(SharedStorage::class)->willReturn(true); |
||||
$secureReceiverFileStorage->method('getShare')->willReturn($secureReceiverFileShare); |
||||
$secureReceiverFile = $this->createMock(File::class); |
||||
$secureReceiverFile->method('getStorage')->willReturn($secureReceiverFileStorage); |
||||
$secureReceiverUserFolder = $this->createMock(Folder::class); |
||||
$secureReceiverUserFolder->method('get')->willReturn($secureReceiverFile); |
||||
|
||||
$result[] = [ '/secure-share-bar.txt', $secureReceiverUserFolder, false ]; |
||||
|
||||
return $result; |
||||
} |
||||
|
||||
/** |
||||
* @dataProvider providesDataForCanGet |
||||
*/ |
||||
public function testCheckDirectCanBeDownloaded($path, $userFolder, $run) { |
||||
$user = $this->createMock(IUser::class); |
||||
$user->method("getUID")->willReturn("test"); |
||||
$this->userSession->method("getUser")->willReturn($user); |
||||
$this->userSession->method("isLoggedIn")->willReturn(true); |
||||
$this->rootFolder->method('getUserFolder')->willReturn($userFolder); |
||||
|
||||
// Simulate direct download of file |
||||
$event = new GenericEvent(null, [ 'path' => $path ]); |
||||
$this->eventDispatcher->dispatch('file.beforeGetDirect', $event); |
||||
|
||||
$this->assertEquals($run, !$event->hasArgument('errorMessage')); |
||||
} |
||||
|
||||
public function providesDataForCanZip() { |
||||
// Mock: Normal file/folder storage |
||||
$nonSharedStorage = $this->createMock(IStorage::class); |
||||
$nonSharedStorage->method('instanceOfStorage')->with(SharedStorage::class)->willReturn(false); |
||||
|
||||
// Mock: Secure-view file/folder shared storage |
||||
$secureReceiverFileShareAttributes = $this->createMock(IAttributes::class); |
||||
$secureReceiverFileShareAttributes->method('getAttribute')->with('permissions', 'download')->willReturn(false); |
||||
$secureReceiverFileShare = $this->createMock(IShare::class); |
||||
$secureReceiverFileShare->method('getAttributes')->willReturn($secureReceiverFileShareAttributes); |
||||
$secureSharedStorage = $this->createMock(SharedStorage::class); |
||||
$secureSharedStorage->method('instanceOfStorage')->with(SharedStorage::class)->willReturn(true); |
||||
$secureSharedStorage->method('getShare')->willReturn($secureReceiverFileShare); |
||||
|
||||
// 1. can download zipped 2 non-shared files inside non-shared folder |
||||
// 2. can download zipped non-shared folder |
||||
$sender1File = $this->createMock(File::class); |
||||
$sender1File->method('getStorage')->willReturn($nonSharedStorage); |
||||
$sender1Folder = $this->createMock(Folder::class); |
||||
$sender1Folder->method('getStorage')->willReturn($nonSharedStorage); |
||||
$sender1Folder->method('getDirectoryListing')->willReturn([$sender1File, $sender1File]); |
||||
$sender1RootFolder = $this->createMock(Folder::class); |
||||
$sender1RootFolder->method('getStorage')->willReturn($nonSharedStorage); |
||||
$sender1RootFolder->method('getDirectoryListing')->willReturn([$sender1Folder]); |
||||
$sender1UserFolder = $this->createMock(Folder::class); |
||||
$sender1UserFolder->method('get')->willReturn($sender1RootFolder); |
||||
|
||||
$return[] = [ '/folder', ['bar1.txt', 'bar2.txt'], $sender1UserFolder, true ]; |
||||
$return[] = [ '/', 'folder', $sender1UserFolder, true ]; |
||||
|
||||
// 3. cannot download zipped 1 non-shared file and 1 secure-shared inside non-shared folder |
||||
$receiver1File = $this->createMock(File::class); |
||||
$receiver1File->method('getStorage')->willReturn($nonSharedStorage); |
||||
$receiver1SecureFile = $this->createMock(File::class); |
||||
$receiver1SecureFile->method('getStorage')->willReturn($secureSharedStorage); |
||||
$receiver1Folder = $this->createMock(Folder::class); |
||||
$receiver1Folder->method('getStorage')->willReturn($nonSharedStorage); |
||||
$receiver1Folder->method('getDirectoryListing')->willReturn([$receiver1File, $receiver1SecureFile]); |
||||
$receiver1RootFolder = $this->createMock(Folder::class); |
||||
$receiver1RootFolder->method('getStorage')->willReturn($nonSharedStorage); |
||||
$receiver1RootFolder->method('getDirectoryListing')->willReturn([$receiver1Folder]); |
||||
$receiver1UserFolder = $this->createMock(Folder::class); |
||||
$receiver1UserFolder->method('get')->willReturn($receiver1RootFolder); |
||||
|
||||
$return[] = [ '/folder', ['secured-bar1.txt', 'bar2.txt'], $receiver1UserFolder, false ]; |
||||
|
||||
// 4. cannot download zipped secure-shared folder |
||||
$receiver2Folder = $this->createMock(Folder::class); |
||||
$receiver2Folder->method('getStorage')->willReturn($secureSharedStorage); |
||||
$receiver2RootFolder = $this->createMock(Folder::class); |
||||
$receiver2RootFolder->method('getStorage')->willReturn($nonSharedStorage); |
||||
$receiver2RootFolder->method('getDirectoryListing')->willReturn([$receiver2Folder]); |
||||
$receiver2UserFolder = $this->createMock(Folder::class); |
||||
$receiver2UserFolder->method('get')->willReturn($receiver2RootFolder); |
||||
|
||||
$return[] = [ '/', 'secured-folder', $receiver2UserFolder, false ]; |
||||
|
||||
return $return; |
||||
} |
||||
|
||||
/** |
||||
* @dataProvider providesDataForCanZip |
||||
*/ |
||||
public function testCheckZipCanBeDownloaded($dir, $files, $userFolder, $run) { |
||||
$user = $this->createMock(IUser::class); |
||||
$user->method("getUID")->willReturn("test"); |
||||
$this->userSession->method("getUser")->willReturn($user); |
||||
$this->userSession->method("isLoggedIn")->willReturn(true); |
||||
|
||||
$this->rootFolder->method('getUserFolder')->with("test")->willReturn($userFolder); |
||||
|
||||
// Simulate zip download of folder folder |
||||
$event = new GenericEvent(null, ['dir' => $dir, 'files' => $files, 'run' => true]); |
||||
$this->eventDispatcher->dispatch('file.beforeCreateZip', $event); |
||||
|
||||
$this->assertEquals($run, $event->getArgument('run')); |
||||
$this->assertEquals($run, !$event->hasArgument('errorMessage')); |
||||
} |
||||
|
||||
public function testCheckFileUserNotFound() { |
||||
$this->userSession->method("isLoggedIn")->willReturn(false); |
||||
|
||||
// Simulate zip download of folder folder |
||||
$event = new GenericEvent(null, ['dir' => '/test', 'files' => ['test.txt'], 'run' => true]); |
||||
$this->eventDispatcher->dispatch('file.beforeCreateZip', $event); |
||||
|
||||
// It should run as this would restrict e.g. share links otherwise |
||||
$this->assertTrue($event->getArgument('run')); |
||||
$this->assertFalse($event->hasArgument('errorMessage')); |
||||
} |
||||
} |
||||
@ -0,0 +1,73 @@ |
||||
<?php |
||||
/** |
||||
* @author Piotr Mrowczynski <piotr@owncloud.com> |
||||
* |
||||
* @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 <http://www.gnu.org/licenses/> |
||||
* |
||||
*/ |
||||
namespace OC\Share20; |
||||
|
||||
use OCP\Share\IAttributes; |
||||
|
||||
class ShareAttributes implements IAttributes { |
||||
|
||||
/** @var array */ |
||||
private $attributes; |
||||
|
||||
public function __construct() { |
||||
$this->attributes = []; |
||||
} |
||||
|
||||
/** |
||||
* @inheritdoc |
||||
*/ |
||||
public function setAttribute($scope, $key, $enabled) { |
||||
if (!\array_key_exists($scope, $this->attributes)) { |
||||
$this->attributes[$scope] = []; |
||||
} |
||||
$this->attributes[$scope][$key] = $enabled; |
||||
return $this; |
||||
} |
||||
|
||||
/** |
||||
* @inheritdoc |
||||
*/ |
||||
public function getAttribute($scope, $key) { |
||||
if (\array_key_exists($scope, $this->attributes) && |
||||
\array_key_exists($key, $this->attributes[$scope])) { |
||||
return $this->attributes[$scope][$key]; |
||||
} |
||||
return null; |
||||
} |
||||
|
||||
/** |
||||
* @inheritdoc |
||||
*/ |
||||
public function toArray() { |
||||
$result = []; |
||||
foreach ($this->attributes as $scope => $keys) { |
||||
foreach ($keys as $key => $enabled) { |
||||
$result[] = [ |
||||
"scope" => $scope, |
||||
"key" => $key, |
||||
"enabled" => $enabled |
||||
]; |
||||
} |
||||
} |
||||
|
||||
return $result; |
||||
} |
||||
} |
||||
@ -0,0 +1,68 @@ |
||||
<?php |
||||
/** |
||||
* @author Piotr Mrowczynski <piotr@owncloud.com> |
||||
* |
||||
* @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 <http://www.gnu.org/licenses/> |
||||
* |
||||
*/ |
||||
namespace OCP\Share; |
||||
|
||||
/** |
||||
* Interface IAttributes |
||||
* |
||||
* @package OCP\Share |
||||
* @since 10.2.0 |
||||
*/ |
||||
interface IAttributes { |
||||
|
||||
/** |
||||
* Sets an attribute enabled/disabled. If the key did not exist before it will be created. |
||||
* |
||||
* @param string $scope scope |
||||
* @param string $key key |
||||
* @param bool $enabled enabled |
||||
* @return IAttributes The modified object |
||||
* @since 10.2.0 |
||||
*/ |
||||
public function setAttribute($scope, $key, $enabled); |
||||
|
||||
/** |
||||
* Returns if attribute is enabled/disabled for given scope id and key. |
||||
* If attribute does not exist, returns null |
||||
* |
||||
* @param string $scope scope |
||||
* @param string $key key |
||||
* @return bool|null |
||||
* @since 10.2.0 |
||||
*/ |
||||
public function getAttribute($scope, $key); |
||||
|
||||
/** |
||||
* Formats the IAttributes object to array with the following format: |
||||
* [ |
||||
* 0 => [ |
||||
* "scope" => <string>, |
||||
* "key" => <string>, |
||||
* "enabled" => <bool> |
||||
* ], |
||||
* ... |
||||
* ] |
||||
* |
||||
* @return array formatted IAttributes |
||||
* @since 10.2.0 |
||||
*/ |
||||
public function toArray(); |
||||
} |
||||
Loading…
Reference in new issue