diff options
Diffstat (limited to 'apps/files_sharing/tests/Controller/ShareControllerTest.php')
-rw-r--r-- | apps/files_sharing/tests/Controller/ShareControllerTest.php | 863 |
1 files changed, 587 insertions, 276 deletions
diff --git a/apps/files_sharing/tests/Controller/ShareControllerTest.php b/apps/files_sharing/tests/Controller/ShareControllerTest.php index 6062ff89065..011210aff42 100644 --- a/apps/files_sharing/tests/Controller/ShareControllerTest.php +++ b/apps/files_sharing/tests/Controller/ShareControllerTest.php @@ -1,56 +1,52 @@ <?php + /** - * @copyright Copyright (c) 2016, ownCloud, Inc. - * - * @author Bjoern Schiessle <bjoern@schiessle.org> - * @author Björn Schießle <bjoern@schiessle.org> - * @author Georg Ehrke <oc.list@georgehrke.com> - * @author Joas Schilling <coding@schilljs.com> - * @author Lukas Reschke <lukas@statuscode.ch> - * @author Morris Jobke <hey@morrisjobke.de> - * @author Robin Appelman <robin@icewind.nl> - * @author Roeland Jago Douma <roeland@famdouma.nl> - * @author Thomas Müller <thomas.mueller@tmit.eu> - * @author Vincent Cloutier <vincent1cloutier@gmail.com> - * - * @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/> - * + * SPDX-FileCopyrightText: 2017-2024 Nextcloud GmbH and Nextcloud contributors + * SPDX-FileCopyrightText: 2016 ownCloud, Inc. + * SPDX-License-Identifier: AGPL-3.0-only */ - namespace OCA\Files_Sharing\Tests\Controllers; use OC\Files\Filesystem; +use OC\Files\Node\Folder; +use OC\Share20\Manager; use OCA\FederatedFileSharing\FederatedShareProvider; use OCA\Files_Sharing\Controller\ShareController; +use OCA\Files_Sharing\DefaultPublicShareTemplateProvider; +use OCA\Files_Sharing\Event\BeforeTemplateRenderedEvent; +use OCP\Accounts\IAccount; +use OCP\Accounts\IAccountManager; +use OCP\Accounts\IAccountProperty; +use OCP\Activity\IManager; +use OCP\AppFramework\Http\ContentSecurityPolicy; use OCP\AppFramework\Http\DataResponse; +use OCP\AppFramework\Http\Template\ExternalShareMenuAction; +use OCP\AppFramework\Http\Template\LinkMenuAction; +use OCP\AppFramework\Http\Template\PublicTemplateResponse; +use OCP\AppFramework\Http\Template\SimpleMenuAction; +use OCP\AppFramework\Services\IInitialState; +use OCP\Constants; +use OCP\Defaults; +use OCP\EventDispatcher\IEventDispatcher; +use OCP\Files\File; +use OCP\Files\IRootFolder; +use OCP\Files\NotFoundException; +use OCP\IAppConfig; use OCP\IConfig; use OCP\IL10N; -use OCP\ILogger; use OCP\IPreview; use OCP\IRequest; -use OCP\IUser; -use OCP\Share\Exceptions\ShareNotFound; -use OCP\AppFramework\Http\NotFoundResponse; -use OCP\AppFramework\Http\RedirectResponse; -use OCP\AppFramework\Http\TemplateResponse; use OCP\ISession; +use OCP\IURLGenerator; +use OCP\IUser; use OCP\IUserManager; use OCP\Security\ISecureRandom; -use OCP\IURLGenerator; +use OCP\Server; +use OCP\Share\Exceptions\ShareNotFound; +use OCP\Share\IAttributes; +use OCP\Share\IPublicShareTemplateFactory; use OCP\Share\IShare; -use Symfony\Component\EventDispatcher\EventDispatcherInterface; +use PHPUnit\Framework\MockObject\MockObject; /** * @group DB @@ -59,66 +55,88 @@ use Symfony\Component\EventDispatcher\EventDispatcherInterface; */ class ShareControllerTest extends \Test\TestCase { - /** @var string */ - private $user; - /** @var string */ - private $oldUser; - - /** @var string */ - private $appName = 'files_sharing'; - /** @var ShareController */ - private $shareController; - /** @var IURLGenerator | \PHPUnit_Framework_MockObject_MockObject */ - private $urlGenerator; - /** @var ISession | \PHPUnit_Framework_MockObject_MockObject */ - private $session; - /** @var \OCP\IPreview | \PHPUnit_Framework_MockObject_MockObject */ - private $previewManager; - /** @var \OCP\IConfig | \PHPUnit_Framework_MockObject_MockObject */ - private $config; - /** @var \OC\Share20\Manager | \PHPUnit_Framework_MockObject_MockObject */ - private $shareManager; - /** @var IUserManager | \PHPUnit_Framework_MockObject_MockObject */ - private $userManager; - /** @var FederatedShareProvider | \PHPUnit_Framework_MockObject_MockObject */ - private $federatedShareProvider; - /** @var EventDispatcherInterface | \PHPUnit_Framework_MockObject_MockObject */ - private $eventDispatcher; - - protected function setUp() { + private string $user; + private string $oldUser; + private string $appName = 'files_sharing'; + private ShareController $shareController; + + private IL10N&MockObject $l10n; + private IConfig&MockObject $config; + private ISession&MockObject $session; + private Defaults&MockObject $defaults; + private IAppConfig&MockObject $appConfig; + private Manager&MockObject $shareManager; + private IPreview&MockObject $previewManager; + private IUserManager&MockObject $userManager; + private IInitialState&MockObject $initialState; + private IURLGenerator&MockObject $urlGenerator; + private ISecureRandom&MockObject $secureRandom; + private IAccountManager&MockObject $accountManager; + private IEventDispatcher&MockObject $eventDispatcher; + private FederatedShareProvider&MockObject $federatedShareProvider; + private IPublicShareTemplateFactory&MockObject $publicShareTemplateFactory; + + protected function setUp(): void { parent::setUp(); $this->appName = 'files_sharing'; - $this->shareManager = $this->getMockBuilder('\OC\Share20\Manager')->disableOriginalConstructor()->getMock(); - $this->urlGenerator = $this->getMockBuilder(IURLGenerator::class)->getMock(); - $this->session = $this->getMockBuilder(ISession::class)->getMock(); - $this->previewManager = $this->getMockBuilder(IPreview::class)->getMock(); - $this->config = $this->getMockBuilder(IConfig::class)->getMock(); - $this->userManager = $this->getMockBuilder(IUserManager::class)->getMock(); - $this->federatedShareProvider = $this->getMockBuilder('OCA\FederatedFileSharing\FederatedShareProvider') - ->disableOriginalConstructor()->getMock(); + $this->shareManager = $this->createMock(Manager::class); + $this->urlGenerator = $this->createMock(IURLGenerator::class); + $this->session = $this->createMock(ISession::class); + $this->previewManager = $this->createMock(IPreview::class); + $this->config = $this->createMock(IConfig::class); + $this->appConfig = $this->createMock(IAppConfig::class); + $this->userManager = $this->createMock(IUserManager::class); + $this->initialState = $this->createMock(IInitialState::class); + $this->federatedShareProvider = $this->createMock(FederatedShareProvider::class); $this->federatedShareProvider->expects($this->any()) ->method('isOutgoingServer2serverShareEnabled')->willReturn(true); $this->federatedShareProvider->expects($this->any()) ->method('isIncomingServer2serverShareEnabled')->willReturn(true); - $this->eventDispatcher = $this->getMockBuilder('Symfony\Component\EventDispatcher\EventDispatcherInterface')->getMock(); + $this->accountManager = $this->createMock(IAccountManager::class); + $this->eventDispatcher = $this->createMock(IEventDispatcher::class); + $this->l10n = $this->createMock(IL10N::class); + $this->secureRandom = $this->createMock(ISecureRandom::class); + $this->defaults = $this->createMock(Defaults::class); + $this->publicShareTemplateFactory = $this->createMock(IPublicShareTemplateFactory::class); + $this->publicShareTemplateFactory + ->expects($this->any()) + ->method('getProvider') + ->willReturn( + new DefaultPublicShareTemplateProvider( + $this->userManager, + $this->accountManager, + $this->previewManager, + $this->federatedShareProvider, + $this->urlGenerator, + $this->eventDispatcher, + $this->l10n, + $this->defaults, + $this->config, + $this->createMock(IRequest::class), + $this->initialState, + $this->appConfig, + ) + ); - $this->shareController = new \OCA\Files_Sharing\Controller\ShareController( + $this->shareController = new ShareController( $this->appName, - $this->getMockBuilder(IRequest::class)->getMock(), + $this->createMock(IRequest::class), $this->config, $this->urlGenerator, $this->userManager, - $this->getMockBuilder(ILogger::class)->getMock(), - $this->getMockBuilder('\OCP\Activity\IManager')->getMock(), + $this->createMock(IManager::class), $this->shareManager, $this->session, $this->previewManager, - $this->getMockBuilder('\OCP\Files\IRootFolder')->getMock(), + $this->createMock(IRootFolder::class), $this->federatedShareProvider, + $this->accountManager, $this->eventDispatcher, - $this->getMockBuilder(IL10N::class)->getMock(), - $this->getMockBuilder('\OCP\Defaults')->getMock() + $this->l10n, + $this->secureRandom, + $this->defaults, + $this->publicShareTemplateFactory, ); @@ -126,22 +144,24 @@ class ShareControllerTest extends \Test\TestCase { $this->oldUser = \OC_User::getUser(); // Create a dummy user - $this->user = \OC::$server->getSecureRandom()->generate(12, ISecureRandom::CHAR_LOWER); + $this->user = Server::get(ISecureRandom::class)->generate(12, ISecureRandom::CHAR_LOWER); - \OC::$server->getUserManager()->createUser($this->user, $this->user); + Server::get(IUserManager::class)->createUser($this->user, $this->user); \OC_Util::tearDownFS(); $this->loginAsUser($this->user); } - protected function tearDown() { + protected function tearDown(): void { \OC_Util::tearDownFS(); \OC_User::setUserId(''); Filesystem::tearDown(); - $user = \OC::$server->getUserManager()->get($this->user); - if ($user !== null) { $user->delete(); } + $user = Server::get(IUserManager::class)->get($this->user); + if ($user !== null) { + $user->delete(); + } \OC_User::setIncognitoMode(false); - \OC::$server->getSession()->set('public_link_authenticated', ''); + Server::get(ISession::class)->set('public_link_authenticated', ''); // Set old user \OC_User::setUserId($this->oldUser); @@ -149,76 +169,118 @@ class ShareControllerTest extends \Test\TestCase { parent::tearDown(); } - public function testShowAuthenticateNotAuthenticated() { - $share = \OC::$server->getShareManager()->newShare(); + public function testShowShareInvalidToken(): void { + $this->shareController->setToken('invalidtoken'); $this->shareManager ->expects($this->once()) ->method('getShareByToken') - ->with('token') - ->willReturn($share); + ->with('invalidtoken') + ->willThrowException(new ShareNotFound()); - $response = $this->shareController->showAuthenticate('token'); - $expectedResponse = new TemplateResponse($this->appName, 'authenticate', [], 'guest'); - $this->assertEquals($expectedResponse, $response); + $this->expectException(NotFoundException::class); + + // Test without a not existing token + $this->shareController->showShare(); } - public function testShowAuthenticateAuthenticatedForDifferentShare() { - $share = \OC::$server->getShareManager()->newShare(); - $share->setId(1); + public function testShowShareNotAuthenticated(): void { + $this->shareController->setToken('validtoken'); + + $share = Server::get(\OCP\Share\IManager::class)->newShare(); + $share->setPassword('password'); $this->shareManager ->expects($this->once()) ->method('getShareByToken') - ->with('token') + ->with('validtoken') ->willReturn($share); - $this->session->method('exists')->with('public_link_authenticated')->willReturn(true); - $this->session->method('get')->with('public_link_authenticated')->willReturn('2'); + $this->expectException(NotFoundException::class); - $response = $this->shareController->showAuthenticate('token'); - $expectedResponse = new TemplateResponse($this->appName, 'authenticate', [], 'guest'); - $this->assertEquals($expectedResponse, $response); + // Test without a not existing token + $this->shareController->showShare(); } - public function testShowAuthenticateCorrectShare() { - $share = \OC::$server->getShareManager()->newShare(); - $share->setId(1); - $this->shareManager - ->expects($this->once()) - ->method('getShareByToken') - ->with('token') - ->willReturn($share); + public function testShowShare(): void { + $note = 'personal note'; + $filename = 'file1.txt'; - $this->session->method('exists')->with('public_link_authenticated')->willReturn(true); - $this->session->method('get')->with('public_link_authenticated')->willReturn('1'); + $this->shareController->setToken('token'); - $this->urlGenerator->expects($this->once()) - ->method('linkToRoute') - ->with('files_sharing.sharecontroller.showShare', ['token' => 'token']) - ->willReturn('redirect'); + $owner = $this->createMock(IUser::class); + $owner->method('getDisplayName')->willReturn('ownerDisplay'); + $owner->method('getUID')->willReturn('ownerUID'); + $owner->method('isEnabled')->willReturn(true); - $response = $this->shareController->showAuthenticate('token'); - $expectedResponse = new RedirectResponse('redirect'); - $this->assertEquals($expectedResponse, $response); - } + $initiator = $this->createMock(IUser::class); + $initiator->method('getDisplayName')->willReturn('initiatorDisplay'); + $initiator->method('getUID')->willReturn('initiatorUID'); + $initiator->method('isEnabled')->willReturn(true); - public function testAuthenticateInvalidToken() { - $this->shareManager - ->expects($this->once()) - ->method('getShareByToken') - ->with('token') - ->will($this->throwException(new \OCP\Share\Exceptions\ShareNotFound())); + $file = $this->createMock(File::class); + $file->method('getName')->willReturn($filename); + $file->method('getMimetype')->willReturn('text/plain'); + $file->method('getSize')->willReturn(33); + $file->method('isReadable')->willReturn(true); + $file->method('isShareable')->willReturn(true); + $file->method('getId')->willReturn(111); + + $accountName = $this->createMock(IAccountProperty::class); + $accountName->method('getScope') + ->willReturn(IAccountManager::SCOPE_PUBLISHED); + $account = $this->createMock(IAccount::class); + $account->method('getProperty') + ->with(IAccountManager::PROPERTY_DISPLAYNAME) + ->willReturn($accountName); + $this->accountManager->expects($this->once()) + ->method('getAccount') + ->with($owner) + ->willReturn($account); + + /** @var Manager */ + $manager = Server::get(Manager::class); + $share = $manager->newShare(); + $share->setId(42) + ->setPermissions(Constants::PERMISSION_READ | Constants::PERMISSION_UPDATE) + ->setPassword('password') + ->setShareOwner('ownerUID') + ->setSharedBy('initiatorUID') + ->setNode($file) + ->setNote($note) + ->setTarget("/$filename") + ->setToken('token'); - $response = $this->shareController->authenticate('token'); - $expectedResponse = new NotFoundResponse(); - $this->assertEquals($expectedResponse, $response); - } + $this->session->method('exists')->with('public_link_authenticated')->willReturn(true); + $this->session->method('get')->with('public_link_authenticated')->willReturn('42'); - public function testAuthenticateValidPassword() { - $share = \OC::$server->getShareManager()->newShare(); - $share->setId(42); + $this->urlGenerator->expects(self::atLeast(2)) + ->method('linkToRouteAbsolute') + ->willReturnMap([ + // every file has the show show share url in the opengraph url prop + ['files_sharing.sharecontroller.showShare', ['token' => 'token'], 'shareUrl'], + // this share is not an image to the default preview is used + ['files_sharing.PublicPreview.getPreview', ['x' => 256, 'y' => 256, 'file' => $share->getTarget(), 'token' => 'token'], 'previewUrl'], + ]); + + $this->urlGenerator->expects($this->once()) + ->method('getAbsoluteURL') + ->willReturnMap([ + ['/public.php/dav/files/token/?accept=zip', 'downloadUrl'], + ]); + + $this->previewManager->method('isMimeSupported')->with('text/plain')->willReturn(true); + + $this->config->method('getSystemValue') + ->willReturnMap( + [ + ['max_filesize_animated_gifs_public_sharing', 10, 10], + ['enable_previews', true, true], + ['preview_max_x', 1024, 1024], + ['preview_max_y', 1024, 1024], + ] + ); $this->shareManager ->expects($this->once()) @@ -226,128 +288,289 @@ class ShareControllerTest extends \Test\TestCase { ->with('token') ->willReturn($share); - $this->shareManager - ->expects($this->once()) - ->method('checkPassword') - ->with($share, 'validpassword') - ->willReturn(true); + $this->userManager->method('get')->willReturnCallback(function (string $uid) use ($owner, $initiator) { + if ($uid === 'ownerUID') { + return $owner; + } + if ($uid === 'initiatorUID') { + return $initiator; + } + return null; + }); + + $this->eventDispatcher->method('dispatchTyped')->with( + $this->callback(function ($event) use ($share) { + if ($event instanceof BeforeTemplateRenderedEvent) { + return $event->getShare() === $share; + } else { + return true; + } + }) + ); - $this->session - ->expects($this->once()) - ->method('set') - ->with('public_link_authenticated', '42'); + $this->l10n->expects($this->any()) + ->method('t') + ->willReturnCallback(function ($text, $parameters) { + return vsprintf($text, $parameters); + }); + + $this->defaults->expects(self::any()) + ->method('getProductName') + ->willReturn('Nextcloud'); + + // Ensure the correct initial state is setup + // Shared node is a file so this is a single file share: + $view = 'public-file-share'; + // Set up initial state + $initialState = []; + $this->initialState->expects(self::any()) + ->method('provideInitialState') + ->willReturnCallback(function ($key, $value) use (&$initialState): void { + $initialState[$key] = $value; + }); + $expectedInitialState = [ + 'isPublic' => true, + 'sharingToken' => 'token', + 'sharePermissions' => (Constants::PERMISSION_READ | Constants::PERMISSION_UPDATE), + 'filename' => $filename, + 'view' => $view, + 'fileId' => 111, + 'owner' => 'ownerUID', + 'ownerDisplayName' => 'ownerDisplay', + 'isFileRequest' => false, + ]; - $this->urlGenerator->expects($this->once()) - ->method('linkToRoute') - ->with('files_sharing.sharecontroller.showShare', ['token'=>'token']) - ->willReturn('redirect'); + $response = $this->shareController->showShare(); + + $this->assertEquals($expectedInitialState, $initialState); + + $csp = new ContentSecurityPolicy(); + $csp->addAllowedFrameDomain('\'self\''); + $expectedResponse = new PublicTemplateResponse('files', 'index'); + $expectedResponse->setParams(['pageTitle' => $filename]); + $expectedResponse->setContentSecurityPolicy($csp); + $expectedResponse->setHeaderTitle($filename); + $expectedResponse->setHeaderDetails('shared by ownerDisplay'); + $expectedResponse->setHeaderActions([ + new SimpleMenuAction('download', $this->l10n->t('Download'), 'icon-download', 'downloadUrl', 0, '33'), + new ExternalShareMenuAction($this->l10n->t('Add to your Nextcloud'), 'icon-external', 'owner', 'ownerDisplay', $filename), + new LinkMenuAction($this->l10n->t('Direct link'), 'icon-public', 'downloadUrl'), + ]); - $response = $this->shareController->authenticate('token', 'validpassword'); - $expectedResponse = new RedirectResponse('redirect'); $this->assertEquals($expectedResponse, $response); } - public function testAuthenticateInvalidPassword() { - $share = \OC::$server->getShareManager()->newShare(); - $share->setNodeId(100) - ->setNodeType('file') - ->setToken('token') - ->setSharedBy('initiator') - ->setId(42); + public function testShowFileDropShare(): void { + $filename = 'folder1'; - $this->shareManager - ->expects($this->once()) - ->method('getShareByToken') - ->with('token') - ->willReturn($share); + $this->shareController->setToken('token'); - $this->shareManager - ->expects($this->once()) - ->method('checkPassword') - ->with($share, 'invalidpassword') - ->willReturn(false); + $owner = $this->createMock(IUser::class); + $owner->method('getDisplayName')->willReturn('ownerDisplay'); + $owner->method('getUID')->willReturn('ownerUID'); + $owner->method('isEnabled')->willReturn(true); - $this->session - ->expects($this->never()) - ->method('set'); - - $hookListner = $this->getMockBuilder('Dummy')->setMethods(['access'])->getMock(); - \OCP\Util::connectHook('OCP\Share', 'share_link_access', $hookListner, 'access'); - - $hookListner->expects($this->once()) - ->method('access') - ->with($this->callback(function(array $data) { - return $data['itemType'] === 'file' && - $data['itemSource'] === 100 && - $data['uidOwner'] === 'initiator' && - $data['token'] === 'token' && - $data['errorCode'] === 403 && - $data['errorMessage'] === 'Wrong password'; - })); - - $response = $this->shareController->authenticate('token', 'invalidpassword'); - $expectedResponse = new TemplateResponse($this->appName, 'authenticate', array('wrongpw' => true), 'guest'); - $expectedResponse->throttle(); - $this->assertEquals($expectedResponse, $response); - } + $initiator = $this->createMock(IUser::class); + $initiator->method('getDisplayName')->willReturn('initiatorDisplay'); + $initiator->method('getUID')->willReturn('initiatorUID'); + $initiator->method('isEnabled')->willReturn(true); - public function testShowShareInvalidToken() { - $this->shareManager + $file = $this->createMock(Folder::class); + $file->method('isReadable')->willReturn(true); + $file->method('isShareable')->willReturn(true); + $file->method('getId')->willReturn(1234); + $file->method('getName')->willReturn($filename); + + $accountName = $this->createMock(IAccountProperty::class); + $accountName->method('getScope') + ->willReturn(IAccountManager::SCOPE_PUBLISHED); + $account = $this->createMock(IAccount::class); + $account->method('getProperty') + ->with(IAccountManager::PROPERTY_DISPLAYNAME) + ->willReturn($accountName); + $this->accountManager->expects($this->once()) + ->method('getAccount') + ->with($owner) + ->willReturn($account); + + /** @var Manager */ + $manager = Server::get(Manager::class); + $share = $manager->newShare(); + $share->setId(42) + ->setPermissions(Constants::PERMISSION_CREATE) + ->setPassword('password') + ->setShareOwner('ownerUID') + ->setSharedBy('initiatorUID') + ->setNote('The note') + ->setLabel('A label') + ->setNode($file) + ->setTarget("/$filename") + ->setToken('token'); + + $this->appConfig ->expects($this->once()) - ->method('getShareByToken') - ->with('invalidtoken') - ->will($this->throwException(new ShareNotFound())); + ->method('getValueString') + ->with('core', 'shareapi_public_link_disclaimertext', '') + ->willReturn('My disclaimer text'); - // Test without a not existing token - $response = $this->shareController->showShare('invalidtoken'); - $expectedResponse = new NotFoundResponse(); - $this->assertEquals($expectedResponse, $response); - } + $this->session->method('exists')->with('public_link_authenticated')->willReturn(true); + $this->session->method('get')->with('public_link_authenticated')->willReturn('42'); - public function testShowShareNotAuthenticated() { - $share = \OC::$server->getShareManager()->newShare(); - $share->setPassword('password'); + $this->urlGenerator->expects(self::atLeastOnce()) + ->method('linkToRouteAbsolute') + ->willReturnMap([ + // every file has the show show share url in the opengraph url prop + ['files_sharing.sharecontroller.showShare', ['token' => 'token'], 'shareUrl'], + // there is no preview or folders so no other link for opengraph + ]); + + $this->config->method('getSystemValue') + ->willReturnMap( + [ + ['max_filesize_animated_gifs_public_sharing', 10, 10], + ['enable_previews', true, true], + ['preview_max_x', 1024, 1024], + ['preview_max_y', 1024, 1024], + ] + ); $this->shareManager ->expects($this->once()) ->method('getShareByToken') - ->with('validtoken') + ->with('token') ->willReturn($share); - $this->urlGenerator->expects($this->once()) - ->method('linkToRoute') - ->with('files_sharing.sharecontroller.authenticate', ['token' => 'validtoken']) - ->willReturn('redirect'); + $this->userManager->method('get')->willReturnCallback(function (string $uid) use ($owner, $initiator) { + if ($uid === 'ownerUID') { + return $owner; + } + if ($uid === 'initiatorUID') { + return $initiator; + } + return null; + }); + + $this->eventDispatcher->method('dispatchTyped')->with( + $this->callback(function ($event) use ($share) { + if ($event instanceof BeforeTemplateRenderedEvent) { + return $event->getShare() === $share; + } else { + return true; + } + }) + ); + + $this->l10n->expects($this->any()) + ->method('t') + ->willReturnCallback(function ($text, $parameters) { + return vsprintf($text, $parameters); + }); + + // Set up initial state + $initialState = []; + $this->initialState->expects(self::any()) + ->method('provideInitialState') + ->willReturnCallback(function ($key, $value) use (&$initialState): void { + $initialState[$key] = $value; + }); + $expectedInitialState = [ + 'isPublic' => true, + 'sharingToken' => 'token', + 'sharePermissions' => Constants::PERMISSION_CREATE, + 'filename' => $filename, + 'view' => 'public-file-drop', + 'disclaimer' => 'My disclaimer text', + 'owner' => 'ownerUID', + 'ownerDisplayName' => 'ownerDisplay', + 'isFileRequest' => false, + 'note' => 'The note', + 'label' => 'A label', + ]; + + $response = $this->shareController->showShare(); + + $this->assertEquals($expectedInitialState, $initialState); + + $csp = new ContentSecurityPolicy(); + $csp->addAllowedFrameDomain('\'self\''); + $expectedResponse = new PublicTemplateResponse('files', 'index'); + $expectedResponse->setParams(['pageTitle' => 'A label']); + $expectedResponse->setContentSecurityPolicy($csp); + $expectedResponse->setHeaderTitle('A label'); + $expectedResponse->setHeaderDetails('shared by ownerDisplay'); + $expectedResponse->setHeaderActions([ + new LinkMenuAction($this->l10n->t('Direct link'), 'icon-public', 'shareUrl'), + ]); - // Test without a not existing token - $response = $this->shareController->showShare('validtoken'); - $expectedResponse = new RedirectResponse('redirect'); $this->assertEquals($expectedResponse, $response); } + public function testShowShareWithPrivateName(): void { + $note = 'personal note'; + $filename = 'file1.txt'; - public function testShowShare() { - $owner = $this->getMockBuilder(IUser::class)->getMock(); + $this->shareController->setToken('token'); + + $owner = $this->createMock(IUser::class); $owner->method('getDisplayName')->willReturn('ownerDisplay'); $owner->method('getUID')->willReturn('ownerUID'); + $owner->method('isEnabled')->willReturn(true); - $file = $this->getMockBuilder('OCP\Files\File')->getMock(); - $file->method('getName')->willReturn('file1.txt'); + $initiator = $this->createMock(IUser::class); + $initiator->method('getDisplayName')->willReturn('initiatorDisplay'); + $initiator->method('getUID')->willReturn('initiatorUID'); + $initiator->method('isEnabled')->willReturn(true); + + $file = $this->createMock(File::class); + $file->method('getName')->willReturn($filename); $file->method('getMimetype')->willReturn('text/plain'); $file->method('getSize')->willReturn(33); $file->method('isReadable')->willReturn(true); $file->method('isShareable')->willReturn(true); - - $share = \OC::$server->getShareManager()->newShare(); + $file->method('getId')->willReturn(111); + + $accountName = $this->createMock(IAccountProperty::class); + $accountName->method('getScope') + ->willReturn(IAccountManager::SCOPE_LOCAL); + $account = $this->createMock(IAccount::class); + $account->method('getProperty') + ->with(IAccountManager::PROPERTY_DISPLAYNAME) + ->willReturn($accountName); + $this->accountManager->expects($this->once()) + ->method('getAccount') + ->with($owner) + ->willReturn($account); + + /** @var IShare */ + $share = Server::get(Manager::class)->newShare(); $share->setId(42); $share->setPassword('password') ->setShareOwner('ownerUID') + ->setSharedBy('initiatorUID') ->setNode($file) - ->setTarget('/file1.txt'); + ->setNote($note) + ->setToken('token') + ->setPermissions(Constants::PERMISSION_ALL & ~Constants::PERMISSION_SHARE) + ->setTarget("/$filename"); $this->session->method('exists')->with('public_link_authenticated')->willReturn(true); $this->session->method('get')->with('public_link_authenticated')->willReturn('42'); + $this->urlGenerator->expects(self::atLeast(2)) + ->method('linkToRouteAbsolute') + ->willReturnMap([ + // every file has the show show share url in the opengraph url prop + ['files_sharing.sharecontroller.showShare', ['token' => 'token'], 'shareUrl'], + // this share is not an image to the default preview is used + ['files_sharing.PublicPreview.getPreview', ['x' => 256, 'y' => 256, 'file' => $share->getTarget(), 'token' => 'token'], 'previewUrl'], + ]); + + $this->urlGenerator->expects($this->once()) + ->method('getAbsoluteURL') + ->willReturnMap([ + ['/public.php/dav/files/token/?accept=zip', 'downloadUrl'], + ]); + $this->previewManager->method('isMimeSupported')->with('text/plain')->willReturn(true); $this->config->method('getSystemValue') @@ -367,75 +590,79 @@ class ShareControllerTest extends \Test\TestCase { ->method('getShareByToken') ->with('token') ->willReturn($share); - $this->config - ->expects($this->once()) - ->method('getAppValue') - ->with('core', 'shareapi_public_link_disclaimertext', null) - ->willReturn('My disclaimer text'); - $this->userManager->method('get')->with('ownerUID')->willReturn($owner); + $this->userManager->method('get')->willReturnCallback(function (string $uid) use ($owner, $initiator) { + if ($uid === 'ownerUID') { + return $owner; + } + if ($uid === 'initiatorUID') { + return $initiator; + } + return null; + }); + + $this->eventDispatcher->method('dispatchTyped')->with( + $this->callback(function ($event) use ($share) { + if ($event instanceof BeforeTemplateRenderedEvent) { + return $event->getShare() === $share; + } else { + return true; + } + }) + ); - $this->eventDispatcher->expects($this->once()) - ->method('dispatch') - ->with('OCA\Files_Sharing::loadAdditionalScripts'); + $this->l10n->expects($this->any()) + ->method('t') + ->willReturnCallback(function ($text, $parameters) { + return vsprintf($text, $parameters); + }); - $response = $this->shareController->showShare('token'); - $sharedTmplParams = array( - 'displayName' => 'ownerDisplay', - 'owner' => 'ownerUID', - 'filename' => 'file1.txt', - 'directory_path' => '/file1.txt', - 'mimetype' => 'text/plain', - 'dirToken' => 'token', - 'sharingToken' => 'token', - 'server2serversharing' => true, - 'protected' => 'true', - 'dir' => '', - 'downloadURL' => null, - 'fileSize' => '33 B', - 'nonHumanFileSize' => 33, - 'maxSizeAnimateGif' => 10, - 'previewSupported' => true, - 'previewEnabled' => true, - 'previewMaxX' => 1024, - 'previewMaxY' => 1024, - 'hideFileList' => false, - 'shareOwner' => 'ownerDisplay', - 'disclaimer' => 'My disclaimer text', - 'shareUrl' => null, - 'previewImage' => null, - 'previewURL' => null, - ); + $this->defaults->expects(self::any()) + ->method('getProductName') + ->willReturn('Nextcloud'); + + $response = $this->shareController->showShare(); - $csp = new \OCP\AppFramework\Http\ContentSecurityPolicy(); + $csp = new ContentSecurityPolicy(); $csp->addAllowedFrameDomain('\'self\''); - $expectedResponse = new TemplateResponse($this->appName, 'public', $sharedTmplParams, 'base'); + $expectedResponse = new PublicTemplateResponse('files', 'index'); + $expectedResponse->setParams(['pageTitle' => $filename]); $expectedResponse->setContentSecurityPolicy($csp); + $expectedResponse->setHeaderTitle($filename); + $expectedResponse->setHeaderDetails(''); + $expectedResponse->setHeaderActions([ + new SimpleMenuAction('download', $this->l10n->t('Download'), 'icon-download', 'downloadUrl', 0, '33'), + new ExternalShareMenuAction($this->l10n->t('Add to your Nextcloud'), 'icon-external', 'owner', 'ownerDisplay', $filename), + new LinkMenuAction($this->l10n->t('Direct link'), 'icon-public', 'downloadUrl'), + ]); $this->assertEquals($expectedResponse, $response); } - /** - * @expectedException \OCP\Files\NotFoundException - */ - public function testShowShareInvalid() { + + public function testShowShareInvalid(): void { + $this->expectException(NotFoundException::class); + + $filename = 'file1.txt'; + $this->shareController->setToken('token'); + $owner = $this->getMockBuilder(IUser::class)->getMock(); $owner->method('getDisplayName')->willReturn('ownerDisplay'); $owner->method('getUID')->willReturn('ownerUID'); $file = $this->getMockBuilder('OCP\Files\File')->getMock(); - $file->method('getName')->willReturn('file1.txt'); + $file->method('getName')->willReturn($filename); $file->method('getMimetype')->willReturn('text/plain'); $file->method('getSize')->willReturn(33); $file->method('isShareable')->willReturn(false); $file->method('isReadable')->willReturn(true); - $share = \OC::$server->getShareManager()->newShare(); + $share = Server::get(\OCP\Share\IManager::class)->newShare(); $share->setId(42); $share->setPassword('password') ->setShareOwner('ownerUID') ->setNode($file) - ->setTarget('/file1.txt'); + ->setTarget("/$filename"); $this->session->method('exists')->with('public_link_authenticated')->willReturn(true); $this->session->method('get')->with('public_link_authenticated')->willReturn('42'); @@ -460,16 +687,16 @@ class ShareControllerTest extends \Test\TestCase { $this->userManager->method('get')->with('ownerUID')->willReturn($owner); - $this->shareController->showShare('token'); + $this->shareController->showShare(); } - public function testDownloadShare() { + public function testDownloadShareWithCreateOnlyShare(): void { $share = $this->getMockBuilder(IShare::class)->getMock(); $share->method('getPassword')->willReturn('password'); $share ->expects($this->once()) ->method('getPermissions') - ->willReturn(\OCP\Constants::PERMISSION_READ); + ->willReturn(Constants::PERMISSION_CREATE); $this->shareManager ->expects($this->once()) @@ -477,35 +704,119 @@ class ShareControllerTest extends \Test\TestCase { ->with('validtoken') ->willReturn($share); - $this->urlGenerator->expects($this->once()) - ->method('linkToRoute') - ->with('files_sharing.sharecontroller.authenticate', ['token' => 'validtoken']) - ->willReturn('redirect'); - // Test with a password protected share and no authentication $response = $this->shareController->downloadShare('validtoken'); - $expectedResponse = new RedirectResponse('redirect'); + $expectedResponse = new DataResponse('Share has no read permission'); $this->assertEquals($expectedResponse, $response); } - public function testDownloadShareWithCreateOnlyShare() { - $share = $this->getMockBuilder(IShare::class)->getMock(); + public function testDownloadShareWithoutDownloadPermission(): void { + $attributes = $this->createMock(IAttributes::class); + $attributes->expects(self::once()) + ->method('getAttribute') + ->with('permissions', 'download') + ->willReturn(false); + + $share = $this->createMock(IShare::class); $share->method('getPassword')->willReturn('password'); - $share - ->expects($this->once()) + $share->expects(self::once()) ->method('getPermissions') - ->willReturn(\OCP\Constants::PERMISSION_CREATE); + ->willReturn(Constants::PERMISSION_READ); + $share->expects(self::once()) + ->method('getAttributes') + ->willReturn($attributes); $this->shareManager - ->expects($this->once()) + ->expects(self::once()) ->method('getShareByToken') ->with('validtoken') ->willReturn($share); // Test with a password protected share and no authentication $response = $this->shareController->downloadShare('validtoken'); - $expectedResponse = new DataResponse('Share is read-only'); + $expectedResponse = new DataResponse('Share has no download permission'); $this->assertEquals($expectedResponse, $response); } + public function testDisabledOwner(): void { + $this->shareController->setToken('token'); + + $owner = $this->getMockBuilder(IUser::class)->getMock(); + $owner->method('isEnabled')->willReturn(false); + + $initiator = $this->createMock(IUser::class); + $initiator->method('isEnabled')->willReturn(false); + + /* @var MockObject|Folder $folder */ + $folder = $this->createMock(Folder::class); + + $share = Server::get(\OCP\Share\IManager::class)->newShare(); + $share->setId(42); + $share->setPermissions(Constants::PERMISSION_CREATE) + ->setShareOwner('ownerUID') + ->setSharedBy('initiatorUID') + ->setNode($folder) + ->setTarget('/share'); + + $this->shareManager + ->expects($this->once()) + ->method('getShareByToken') + ->with('token') + ->willReturn($share); + + $this->userManager->method('get')->willReturnCallback(function (string $uid) use ($owner, $initiator) { + if ($uid === 'ownerUID') { + return $owner; + } + if ($uid === 'initiatorUID') { + return $initiator; + } + return null; + }); + + $this->expectException(NotFoundException::class); + + $this->shareController->showShare(); + } + + public function testDisabledInitiator(): void { + $this->shareController->setToken('token'); + + $owner = $this->getMockBuilder(IUser::class)->getMock(); + $owner->method('isEnabled')->willReturn(false); + + $initiator = $this->createMock(IUser::class); + $initiator->method('isEnabled')->willReturn(true); + + /* @var MockObject|Folder $folder */ + $folder = $this->createMock(Folder::class); + + $share = Server::get(\OCP\Share\IManager::class)->newShare(); + $share->setId(42); + $share->setPermissions(Constants::PERMISSION_CREATE) + ->setShareOwner('ownerUID') + ->setSharedBy('initiatorUID') + ->setNode($folder) + ->setTarget('/share'); + + $this->shareManager + ->expects($this->once()) + ->method('getShareByToken') + ->with('token') + ->willReturn($share); + + $this->userManager->method('get')->willReturnCallback(function (string $uid) use ($owner, $initiator) { + if ($uid === 'ownerUID') { + return $owner; + } + if ($uid === 'initiatorUID') { + return $initiator; + } + return null; + }); + + $this->expectException(NotFoundException::class); + + $this->shareController->showShare(); + } } |