You can not select more than 25 topics
			Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
		
		
		
		
		
			
		
			
				
					
					
						
							405 lines
						
					
					
						
							13 KiB
						
					
					
				
			
		
		
		
			
			
			
		
		
	
	
							405 lines
						
					
					
						
							13 KiB
						
					
					
				| <?php | |
| /** | |
|  * @copyright Copyright (c) 2016, ownCloud, Inc. | |
|  * | |
|  * @author Christoph Wurst <christoph@winzerhof-wurst.at> | |
|  * @author Daniel Calviño Sánchez <danxuliu@gmail.com> | |
|  * @author Joas Schilling <coding@schilljs.com> | |
|  * @author Julius Härtl <jus@bitgrid.net> | |
|  * @author Maxence Lange <maxence@nextcloud.com> | |
|  * @author Morris Jobke <hey@morrisjobke.de> | |
|  * @author Roeland Jago Douma <roeland@famdouma.nl> | |
|  * @author Thomas Müller <thomas.mueller@tmit.eu> | |
|  * @author Vincent Petry <vincent@nextcloud.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/> | |
|  * | |
|  */ | |
| namespace OCA\Files_Sharing\Tests; | |
| 
 | |
| use OC\Memcache\NullCache; | |
| use OCA\Files_Sharing\MountProvider; | |
| use OCP\EventDispatcher\IEventDispatcher; | |
| use OCP\Files\IRootFolder; | |
| use OCP\Files\Storage\IStorageFactory; | |
| use OCP\ICacheFactory; | |
| use OCP\IConfig; | |
| use OCP\ILogger; | |
| use OCP\IUser; | |
| use OCP\IUserManager; | |
| use OCP\Share\IManager; | |
| use OCP\Share\IShare; | |
| 
 | |
| /** | |
|  * @group DB | |
|  */ | |
| class MountProviderTest extends \Test\TestCase { | |
| 
 | |
| 	/** @var MountProvider */ | |
| 	private $provider; | |
| 
 | |
| 	/** @var IConfig|\PHPUnit\Framework\MockObject\MockObject */ | |
| 	private $config; | |
| 
 | |
| 	/** @var IUser|\PHPUnit\Framework\MockObject\MockObject */ | |
| 	private $user; | |
| 
 | |
| 	/** @var IStorageFactory|\PHPUnit\Framework\MockObject\MockObject */ | |
| 	private $loader; | |
| 
 | |
| 	/** @var IManager|\PHPUnit\Framework\MockObject\MockObject */ | |
| 	private $shareManager; | |
| 
 | |
| 	/** @var ILogger | \PHPUnit\Framework\MockObject\MockObject */ | |
| 	private $logger; | |
| 
 | |
| 	protected function setUp(): void { | |
| 		parent::setUp(); | |
| 
 | |
| 		$this->config = $this->getMockBuilder(IConfig::class)->getMock(); | |
| 		$this->user = $this->getMockBuilder(IUser::class)->getMock(); | |
| 		$this->loader = $this->getMockBuilder('OCP\Files\Storage\IStorageFactory')->getMock(); | |
| 		$this->shareManager = $this->getMockBuilder(IManager::class)->getMock(); | |
| 		$this->logger = $this->getMockBuilder(ILogger::class)->getMock(); | |
| 		$eventDispatcher = $this->createMock(IEventDispatcher::class); | |
| 		$cacheFactory = $this->createMock(ICacheFactory::class); | |
| 		$cacheFactory->method('createLocal') | |
| 			->willReturn(new NullCache()); | |
| 
 | |
| 		$this->provider = new MountProvider($this->config, $this->shareManager, $this->logger, $eventDispatcher, $cacheFactory); | |
| 	} | |
| 
 | |
| 	private function makeMockShare($id, $nodeId, $owner = 'user2', $target = null, $permissions = 31) { | |
| 		$share = $this->createMock(IShare::class); | |
| 		$share->expects($this->any()) | |
| 			->method('getPermissions') | |
| 			->willReturn($permissions); | |
| 		$share->expects($this->any()) | |
| 			->method('getShareOwner') | |
| 			->willReturn($owner); | |
| 		$share->expects($this->any()) | |
| 			->method('getTarget') | |
| 			->willReturn($target); | |
| 		$share->expects($this->any()) | |
| 			->method('getId') | |
| 			->willReturn($id); | |
| 		$share->expects($this->any()) | |
| 			->method('getNodeId') | |
| 			->willReturn($nodeId); | |
| 		$share->expects($this->any()) | |
| 			->method('getShareTime') | |
| 			->willReturn( | |
| 				// compute share time based on id, simulating share order | |
| 				new \DateTime('@' . (1469193980 + 1000 * $id)) | |
| 			); | |
| 		return $share; | |
| 	} | |
| 
 | |
| 	/** | |
| 	 * Tests excluding shares from the current view. This includes: | |
| 	 * - shares that were opted out of (permissions === 0) | |
| 	 * - shares with a group in which the owner is already in | |
| 	 */ | |
| 	public function testExcludeShares() { | |
| 		$rootFolder = $this->createMock(IRootFolder::class); | |
| 		$userManager = $this->createMock(IUserManager::class); | |
| 		$userShares = [ | |
| 			$this->makeMockShare(1, 100, 'user2', '/share2', 0), | |
| 			$this->makeMockShare(2, 100, 'user2', '/share2', 31), | |
| 		]; | |
| 		$groupShares = [ | |
| 			$this->makeMockShare(3, 100, 'user2', '/share2', 0), | |
| 			$this->makeMockShare(4, 101, 'user2', '/share4', 31), | |
| 			$this->makeMockShare(5, 100, 'user1', '/share4', 31), | |
| 		]; | |
| 		$roomShares = [ | |
| 			$this->makeMockShare(6, 102, 'user2', '/share6', 0), | |
| 			$this->makeMockShare(7, 102, 'user1', '/share6', 31), | |
| 			$this->makeMockShare(8, 102, 'user2', '/share6', 31), | |
| 			$this->makeMockShare(9, 102, 'user2', '/share6', 31), | |
| 		]; | |
| 		$deckShares = [ | |
| 			$this->makeMockShare(10, 103, 'user2', '/share7', 0), | |
| 			$this->makeMockShare(11, 103, 'user1', '/share7', 31), | |
| 			$this->makeMockShare(12, 103, 'user2', '/share7', 31), | |
| 			$this->makeMockShare(13, 103, 'user2', '/share7', 31), | |
| 		]; | |
| 		// tests regarding circles are made in the app itself. | |
| 		$circleShares = []; | |
| 		$this->user->expects($this->any()) | |
| 			->method('getUID') | |
| 			->willReturn('user1'); | |
| 		$this->shareManager->expects($this->exactly(5)) | |
| 			->method('getSharedWith') | |
| 			->withConsecutive( | |
| 				['user1', IShare::TYPE_USER], | |
| 				['user1', IShare::TYPE_GROUP, null, -1], | |
| 				['user1', IShare::TYPE_CIRCLE, null, -1], | |
| 				['user1', IShare::TYPE_ROOM, null, -1], | |
| 				['user1', IShare::TYPE_DECK, null, -1], | |
| 			)->willReturnOnConsecutiveCalls( | |
| 				$userShares, | |
| 				$groupShares, | |
| 				$circleShares, | |
| 				$roomShares, | |
| 				$deckShares, | |
| 			); | |
| 		$this->shareManager->expects($this->any()) | |
| 			->method('newShare') | |
| 			->willReturnCallback(function () use ($rootFolder, $userManager) { | |
| 				return new \OC\Share20\Share($rootFolder, $userManager); | |
| 			}); | |
| 		$mounts = $this->provider->getMountsForUser($this->user, $this->loader); | |
| 		$this->assertCount(4, $mounts); | |
| 		$this->assertInstanceOf('OCA\Files_Sharing\SharedMount', $mounts[0]); | |
| 		$this->assertInstanceOf('OCA\Files_Sharing\SharedMount', $mounts[1]); | |
| 		$this->assertInstanceOf('OCA\Files_Sharing\SharedMount', $mounts[2]); | |
| 		$this->assertInstanceOf('OCA\Files_Sharing\SharedMount', $mounts[3]); | |
| 		$mountedShare1 = $mounts[0]->getShare(); | |
| 		$this->assertEquals('2', $mountedShare1->getId()); | |
| 		$this->assertEquals('user2', $mountedShare1->getShareOwner()); | |
| 		$this->assertEquals(100, $mountedShare1->getNodeId()); | |
| 		$this->assertEquals('/share2', $mountedShare1->getTarget()); | |
| 		$this->assertEquals(31, $mountedShare1->getPermissions()); | |
| 		$mountedShare2 = $mounts[1]->getShare(); | |
| 		$this->assertEquals('4', $mountedShare2->getId()); | |
| 		$this->assertEquals('user2', $mountedShare2->getShareOwner()); | |
| 		$this->assertEquals(101, $mountedShare2->getNodeId()); | |
| 		$this->assertEquals('/share4', $mountedShare2->getTarget()); | |
| 		$this->assertEquals(31, $mountedShare2->getPermissions()); | |
| 		$mountedShare3 = $mounts[2]->getShare(); | |
| 		$this->assertEquals('8', $mountedShare3->getId()); | |
| 		$this->assertEquals('user2', $mountedShare3->getShareOwner()); | |
| 		$this->assertEquals(102, $mountedShare3->getNodeId()); | |
| 		$this->assertEquals('/share6', $mountedShare3->getTarget()); | |
| 		$this->assertEquals(31, $mountedShare3->getPermissions()); | |
| 		$mountedShare4 = $mounts[3]->getShare(); | |
| 		$this->assertEquals('12', $mountedShare4->getId()); | |
| 		$this->assertEquals('user2', $mountedShare4->getShareOwner()); | |
| 		$this->assertEquals(103, $mountedShare4->getNodeId()); | |
| 		$this->assertEquals('/share7', $mountedShare4->getTarget()); | |
| 		$this->assertEquals(31, $mountedShare4->getPermissions()); | |
| 	} | |
| 
 | |
| 	public function mergeSharesDataProvider() { | |
| 		// note: the user in the specs here is the shareOwner not recipient | |
| 		// the recipient is always "user1" | |
| 		return [ | |
| 			// #0: share as outsider with "group1" and "user1" with same permissions | |
| 			[ | |
| 				[ | |
| 					[1, 100, 'user2', '/share2', 31], | |
| 				], | |
| 				[ | |
| 					[2, 100, 'user2', '/share2', 31], | |
| 				], | |
| 				[ | |
| 					// combined, user share has higher priority | |
| 					['1', 100, 'user2', '/share2', 31], | |
| 				], | |
| 			], | |
| 			// #1: share as outsider with "group1" and "user1" with different permissions | |
| 			[ | |
| 				[ | |
| 					[1, 100, 'user2', '/share', 31], | |
| 				], | |
| 				[ | |
| 					[2, 100, 'user2', '/share', 15], | |
| 				], | |
| 				[ | |
| 					// use highest permissions | |
| 					['1', 100, 'user2', '/share', 31], | |
| 				], | |
| 			], | |
| 			// #2: share as outsider with "group1" and "group2" with same permissions | |
| 			[ | |
| 				[ | |
| 				], | |
| 				[ | |
| 					[1, 100, 'user2', '/share', 31], | |
| 					[2, 100, 'user2', '/share', 31], | |
| 				], | |
| 				[ | |
| 					// combined, first group share has higher priority | |
| 					['1', 100, 'user2', '/share', 31], | |
| 				], | |
| 			], | |
| 			// #3: share as outsider with "group1" and "group2" with different permissions | |
| 			[ | |
| 				[ | |
| 				], | |
| 				[ | |
| 					[1, 100, 'user2', '/share', 31], | |
| 					[2, 100, 'user2', '/share', 15], | |
| 				], | |
| 				[ | |
| 					// use higher permissions | |
| 					['1', 100, 'user2', '/share', 31], | |
| 				], | |
| 			], | |
| 			// #4: share as insider with "group1" | |
| 			[ | |
| 				[ | |
| 				], | |
| 				[ | |
| 					[1, 100, 'user1', '/share', 31], | |
| 				], | |
| 				[ | |
| 					// no received share since "user1" is the sharer/owner | |
| 				], | |
| 			], | |
| 			// #5: share as insider with "group1" and "group2" with different permissions | |
| 			[ | |
| 				[ | |
| 				], | |
| 				[ | |
| 					[1, 100, 'user1', '/share', 31], | |
| 					[2, 100, 'user1', '/share', 15], | |
| 				], | |
| 				[ | |
| 					// no received share since "user1" is the sharer/owner | |
| 				], | |
| 			], | |
| 			// #6: share as outside with "group1", recipient opted out | |
| 			[ | |
| 				[ | |
| 				], | |
| 				[ | |
| 					[1, 100, 'user2', '/share', 0], | |
| 				], | |
| 				[ | |
| 					// no received share since "user1" opted out | |
| 				], | |
| 			], | |
| 			// #7: share as outsider with "group1" and "user1" where recipient renamed in between | |
| 			[ | |
| 				[ | |
| 					[1, 100, 'user2', '/share2-renamed', 31], | |
| 				], | |
| 				[ | |
| 					[2, 100, 'user2', '/share2', 31], | |
| 				], | |
| 				[ | |
| 					// use target of least recent share | |
| 					['1', 100, 'user2', '/share2-renamed', 31], | |
| 				], | |
| 			], | |
| 			// #8: share as outsider with "group1" and "user1" where recipient renamed in between | |
| 			[ | |
| 				[ | |
| 					[2, 100, 'user2', '/share2', 31], | |
| 				], | |
| 				[ | |
| 					[1, 100, 'user2', '/share2-renamed', 31], | |
| 				], | |
| 				[ | |
| 					// use target of least recent share | |
| 					['1', 100, 'user2', '/share2-renamed', 31], | |
| 				], | |
| 			], | |
| 			// #9: share as outsider with "nullgroup" and "user1" where recipient renamed in between | |
| 			[ | |
| 				[ | |
| 					[2, 100, 'user2', '/share2', 31], | |
| 				], | |
| 				[ | |
| 					[1, 100, 'nullgroup', '/share2-renamed', 31], | |
| 				], | |
| 				[ | |
| 					// use target of least recent share | |
| 					['1', 100, 'nullgroup', '/share2-renamed', 31], | |
| 				], | |
| 				true | |
| 			], | |
| 		]; | |
| 	} | |
| 
 | |
| 	/** | |
| 	 * Tests merging shares. | |
| 	 * | |
| 	 * Happens when sharing the same entry to a user through multiple ways, | |
| 	 * like several groups and also direct shares at the same time. | |
| 	 * | |
| 	 * @dataProvider mergeSharesDataProvider | |
| 	 * | |
| 	 * @param array $userShares array of user share specs | |
| 	 * @param array $groupShares array of group share specs | |
| 	 * @param array $expectedShares array of expected supershare specs | |
| 	 */ | |
| 	public function testMergeShares($userShares, $groupShares, $expectedShares, $moveFails = false) { | |
| 		$rootFolder = $this->createMock(IRootFolder::class); | |
| 		$userManager = $this->createMock(IUserManager::class); | |
| 
 | |
| 		$userShares = array_map(function ($shareSpec) { | |
| 			return $this->makeMockShare($shareSpec[0], $shareSpec[1], $shareSpec[2], $shareSpec[3], $shareSpec[4]); | |
| 		}, $userShares); | |
| 		$groupShares = array_map(function ($shareSpec) { | |
| 			return $this->makeMockShare($shareSpec[0], $shareSpec[1], $shareSpec[2], $shareSpec[3], $shareSpec[4]); | |
| 		}, $groupShares); | |
| 
 | |
| 		$this->user->expects($this->any()) | |
| 			->method('getUID') | |
| 			->willReturn('user1'); | |
| 
 | |
| 		// tests regarding circles are made in the app itself. | |
| 		$circleShares = []; | |
| 		$roomShares = []; | |
| 		$deckShares = []; | |
| 		$this->shareManager->expects($this->exactly(5)) | |
| 			->method('getSharedWith') | |
| 			->withConsecutive( | |
| 				['user1', IShare::TYPE_USER], | |
| 				['user1', IShare::TYPE_GROUP, null, -1], | |
| 				['user1', IShare::TYPE_CIRCLE, null, -1], | |
| 				['user1', IShare::TYPE_ROOM, null, -1], | |
| 				['user1', IShare::TYPE_DECK, null, -1], | |
| 			)->willReturnOnConsecutiveCalls( | |
| 				$userShares, | |
| 				$groupShares, | |
| 				$circleShares, | |
| 				$roomShares, | |
| 				$deckShares, | |
| 			); | |
| 		$this->shareManager->expects($this->any()) | |
| 			->method('newShare') | |
| 			->willReturnCallback(function () use ($rootFolder, $userManager) { | |
| 				return new \OC\Share20\Share($rootFolder, $userManager); | |
| 			}); | |
| 
 | |
| 		if ($moveFails) { | |
| 			$this->shareManager->expects($this->any()) | |
| 				->method('moveShare') | |
| 				->will($this->throwException(new \InvalidArgumentException())); | |
| 		} | |
| 
 | |
| 		$mounts = $this->provider->getMountsForUser($this->user, $this->loader); | |
| 
 | |
| 		$this->assertCount(count($expectedShares), $mounts); | |
| 
 | |
| 		foreach ($mounts as $index => $mount) { | |
| 			$expectedShare = $expectedShares[$index]; | |
| 			$this->assertInstanceOf('OCA\Files_Sharing\SharedMount', $mount); | |
| 
 | |
| 			// supershare | |
| 			$share = $mount->getShare(); | |
| 
 | |
| 			$this->assertEquals($expectedShare[0], $share->getId()); | |
| 			$this->assertEquals($expectedShare[1], $share->getNodeId()); | |
| 			$this->assertEquals($expectedShare[2], $share->getShareOwner()); | |
| 			$this->assertEquals($expectedShare[3], $share->getTarget()); | |
| 			$this->assertEquals($expectedShare[4], $share->getPermissions()); | |
| 		} | |
| 	} | |
| }
 |