diff --git a/apps/federatedfilesharing/lib/FederatedShareProvider.php b/apps/federatedfilesharing/lib/FederatedShareProvider.php index f66ce2eea680..bcf4c9017b57 100644 --- a/apps/federatedfilesharing/lib/FederatedShareProvider.php +++ b/apps/federatedfilesharing/lib/FederatedShareProvider.php @@ -725,6 +725,14 @@ public function getSharesByPath(Node $path) { return $shares; } + + /** + * @inheritdoc + */ + public function getAllSharedWith($userId, $node) { + return $this->getSharedWith($userId, self::SHARE_TYPE_REMOTE, $node, -1, 0); + } + /** * @inheritdoc */ diff --git a/apps/federatedfilesharing/tests/FederatedShareProviderTest.php b/apps/federatedfilesharing/tests/FederatedShareProviderTest.php index 9facbbb97b42..840ad7951ced 100644 --- a/apps/federatedfilesharing/tests/FederatedShareProviderTest.php +++ b/apps/federatedfilesharing/tests/FederatedShareProviderTest.php @@ -462,6 +462,11 @@ public function datatTestUpdate() { ]; } + public function testGetAllSharedWith() { + $shares = $this->provider->getAllSharedWith('shared', null); + $this->assertCount(0, $shares); + } + public function testGetAllSharesByNodes() { $node = $this->createMock('\OCP\Files\File'); diff --git a/apps/files_sharing/lib/MountProvider.php b/apps/files_sharing/lib/MountProvider.php index d5339d39e771..6688665bfe36 100644 --- a/apps/files_sharing/lib/MountProvider.php +++ b/apps/files_sharing/lib/MountProvider.php @@ -68,8 +68,9 @@ public function __construct(IConfig $config, IManager $shareManager, ILogger $lo * @return \OCP\Files\Mount\IMountPoint[] */ public function getMountsForUser(IUser $user, IStorageFactory $storageFactory) { - $shares = $this->shareManager->getSharedWith($user->getUID(), \OCP\Share::SHARE_TYPE_USER, null, -1); - $shares = array_merge($shares, $this->shareManager->getSharedWith($user->getUID(), \OCP\Share::SHARE_TYPE_GROUP, null, -1)); + $requiredShareTypes = [\OCP\Share::SHARE_TYPE_USER, \OCP\Share::SHARE_TYPE_GROUP]; + $shares = $this->shareManager->getAllSharedWith($user->getUID(), $requiredShareTypes, null); + // filter out excluded shares and group shares that includes self $shares = array_filter($shares, function (\OCP\Share\IShare $share) use ($user) { return $share->getPermissions() > 0 && $share->getShareOwner() !== $user->getUID(); diff --git a/apps/files_sharing/tests/MountProviderTest.php b/apps/files_sharing/tests/MountProviderTest.php index 43bfab98e3d5..a1ae82a155ee 100644 --- a/apps/files_sharing/tests/MountProviderTest.php +++ b/apps/files_sharing/tests/MountProviderTest.php @@ -112,18 +112,19 @@ public function testExcludeShares() { $this->makeMockShare(5, 100, 'user1', '/share4', 31), ]; + $userGroupUserShares = array_merge($userShares, $groupShares); + $this->user->expects($this->any()) ->method('getUID') ->will($this->returnValue('user1')); - $this->shareManager->expects($this->at(0)) - ->method('getSharedWith') - ->with('user1', \OCP\Share::SHARE_TYPE_USER) - ->will($this->returnValue($userShares)); - $this->shareManager->expects($this->at(1)) - ->method('getSharedWith') - ->with('user1', \OCP\Share::SHARE_TYPE_GROUP, null, -1) - ->will($this->returnValue($groupShares)); + $requiredShareTypes = [\OCP\Share::SHARE_TYPE_USER, \OCP\Share::SHARE_TYPE_GROUP]; + $this->shareManager->expects($this->once()) + ->method('getAllSharedWith') + ->with('user1', $requiredShareTypes, null) + ->will($this->returnValue($userGroupUserShares)); + $this->shareManager->expects($this->never()) + ->method('getSharedWith'); $this->shareManager->expects($this->any()) ->method('newShare') ->will($this->returnCallback(function() use ($rootFolder, $userManager) { @@ -311,15 +312,17 @@ public function testMergeShares($userShares, $groupShares, $expectedShares, $mov $this->user->expects($this->any()) ->method('getUID') ->will($this->returnValue('user1')); + + $userGroupUserShares = array_merge($userShares, $groupShares); + $requiredShareTypes = [\OCP\Share::SHARE_TYPE_USER, \OCP\Share::SHARE_TYPE_GROUP]; + $this->shareManager->expects($this->once()) + ->method('getAllSharedWith') + ->with('user1', $requiredShareTypes, null) + ->will($this->returnValue($userGroupUserShares)); + + $this->shareManager->expects($this->never()) + ->method('getSharedWith'); - $this->shareManager->expects($this->at(0)) - ->method('getSharedWith') - ->with('user1', \OCP\Share::SHARE_TYPE_USER) - ->will($this->returnValue($userShares)); - $this->shareManager->expects($this->at(1)) - ->method('getSharedWith') - ->with('user1', \OCP\Share::SHARE_TYPE_GROUP, null, -1) - ->will($this->returnValue($groupShares)); $this->shareManager->expects($this->any()) ->method('newShare') ->will($this->returnCallback(function() use ($rootFolder, $userManager) { diff --git a/lib/private/Share20/DefaultShareProvider.php b/lib/private/Share20/DefaultShareProvider.php index 3a5bfd22c713..ae4c50ece955 100644 --- a/lib/private/Share20/DefaultShareProvider.php +++ b/lib/private/Share20/DefaultShareProvider.php @@ -5,6 +5,7 @@ * @author phisch * @author Roeland Jago Douma * @author Vincent Petry + * @author Piotr Mrowczynski * * @copyright Copyright (c) 2017, ownCloud GmbH * @license AGPL-3.0 @@ -674,6 +675,116 @@ private function isAccessibleResult($data) { return true; } + /* + * Get shared with user shares for the given userId and node + * + * @param string $userId + * @param Node|null $node + * @return DB\QueryBuilder\IQueryBuilder $qb + */ + public function getSharedWithUserQuery($userId, $node) { + $qb = $this->dbConn->getQueryBuilder(); + $qb->select('s.*', 'f.fileid', 'f.path') + ->selectAlias('st.id', 'storage_string_id') + ->from('share', 's') + ->leftJoin('s', 'filecache', 'f', $qb->expr()->eq('s.file_source', 'f.fileid')) + ->leftJoin('f', 'storages', 'st', $qb->expr()->eq('f.storage', 'st.numeric_id')); + + // Order by id + $qb->orderBy('s.id'); + + $qb->where($qb->expr()->eq('share_type', $qb->createNamedParameter(\OCP\Share::SHARE_TYPE_USER))) + ->andWhere($qb->expr()->eq('share_with', $qb->createNamedParameter($userId))) + ->andWhere($qb->expr()->orX( + $qb->expr()->eq('item_type', $qb->createNamedParameter('file')), + $qb->expr()->eq('item_type', $qb->createNamedParameter('folder')) + )); + + // Filter by node if provided + if ($node !== null) { + $qb->andWhere($qb->expr()->eq('file_source', $qb->createNamedParameter($node->getId()))); + } + + return $qb; + } + + /* + * Get shared with group shares for the given groups and node + * + * @param IGroup[] $groups + * @param Node|null $node + * @return DB\QueryBuilder\IQueryBuilder $qb + */ + private function getSharedWithGroupQuery($groups, $node) { + $qb = $this->dbConn->getQueryBuilder(); + $qb->select('s.*', 'f.fileid', 'f.path') + ->selectAlias('st.id', 'storage_string_id') + ->from('share', 's') + ->leftJoin('s', 'filecache', 'f', $qb->expr()->eq('s.file_source', 'f.fileid')) + ->leftJoin('f', 'storages', 'st', $qb->expr()->eq('f.storage', 'st.numeric_id')) + ->orderBy('s.id'); + + // Filter by node if provided + if ($node !== null) { + $qb->andWhere($qb->expr()->eq('file_source', $qb->createNamedParameter($node->getId()))); + } + + $groups = array_map(function(IGroup $group) { return $group->getGID(); }, $groups); + + $qb->andWhere($qb->expr()->eq('share_type', $qb->createNamedParameter(\OCP\Share::SHARE_TYPE_GROUP))) + ->andWhere($qb->expr()->in('share_with', $qb->createNamedParameter( + $groups, + IQueryBuilder::PARAM_STR_ARRAY + ))) + ->andWhere($qb->expr()->orX( + $qb->expr()->eq('item_type', $qb->createNamedParameter('file')), + $qb->expr()->eq('item_type', $qb->createNamedParameter('folder')) + )); + + return $qb; + } + + /* + * Get shared with group and shared with user shares for the given groups, userId and node + * + * @param IGroup[] $groups + * @param string $userId + * @param Node|null $node + * @return DB\QueryBuilder\IQueryBuilder $qb + */ + private function getSharedWithUserGroupQuery($groups, $userId, $node) { + $qb = $this->dbConn->getQueryBuilder(); + $qb->select('s.*', 'f.fileid', 'f.path') + ->selectAlias('st.id', 'storage_string_id') + ->from('share', 's') + ->leftJoin('s', 'filecache', 'f', $qb->expr()->eq('s.file_source', 'f.fileid')) + ->leftJoin('f', 'storages', 'st', $qb->expr()->eq('f.storage', 'st.numeric_id')) + ->orderBy('s.id'); + + // Filter by node if provided + if ($node !== null) { + $qb->andWhere($qb->expr()->eq('file_source', $qb->createNamedParameter($node->getId()))); + } + + $groups = array_map(function(IGroup $group) { return $group->getGID(); }, $groups); + + $qb->andWhere($qb->expr()->orX( + $qb->expr()->andX( + $qb->expr()->eq('share_type', $qb->createNamedParameter(\OCP\Share::SHARE_TYPE_GROUP)), + $qb->expr()->in('share_with', $qb->createNamedParameter( + $groups, + IQueryBuilder::PARAM_STR_ARRAY + )) + ), + $qb->expr()->andX( + $qb->expr()->eq('share_type', $qb->createNamedParameter(\OCP\Share::SHARE_TYPE_USER)), + $qb->expr()->eq('share_with', $qb->createNamedParameter($userId)) + ) + )); + + return $qb; + } + /** * @inheritdoc */ @@ -682,16 +793,8 @@ public function getSharedWith($userId, $shareType, $node, $limit, $offset) { $shares = []; if ($shareType === \OCP\Share::SHARE_TYPE_USER) { - //Get shares directly with this user - $qb = $this->dbConn->getQueryBuilder(); - $qb->select('s.*', 'f.fileid', 'f.path') - ->selectAlias('st.id', 'storage_string_id') - ->from('share', 's') - ->leftJoin('s', 'filecache', 'f', $qb->expr()->eq('s.file_source', 'f.fileid')) - ->leftJoin('f', 'storages', 'st', $qb->expr()->eq('f.storage', 'st.numeric_id')); - - // Order by id - $qb->orderBy('s.id'); + // Create SharedWithUser query + $qb = $this->getSharedWithUserQuery($userId, $node); // Set limit and offset if ($limit !== -1) { @@ -699,18 +802,6 @@ public function getSharedWith($userId, $shareType, $node, $limit, $offset) { } $qb->setFirstResult($offset); - $qb->where($qb->expr()->eq('share_type', $qb->createNamedParameter(\OCP\Share::SHARE_TYPE_USER))) - ->andWhere($qb->expr()->eq('share_with', $qb->createNamedParameter($userId))) - ->andWhere($qb->expr()->orX( - $qb->expr()->eq('item_type', $qb->createNamedParameter('file')), - $qb->expr()->eq('item_type', $qb->createNamedParameter('folder')) - )); - - // Filter by node if provided - if ($node !== null) { - $qb->andWhere($qb->expr()->eq('file_source', $qb->createNamedParameter($node->getId()))); - } - $cursor = $qb->execute(); while($data = $cursor->fetch()) { @@ -736,36 +827,14 @@ public function getSharedWith($userId, $shareType, $node, $limit, $offset) { break; } - $qb = $this->dbConn->getQueryBuilder(); - $qb->select('s.*', 'f.fileid', 'f.path') - ->selectAlias('st.id', 'storage_string_id') - ->from('share', 's') - ->leftJoin('s', 'filecache', 'f', $qb->expr()->eq('s.file_source', 'f.fileid')) - ->leftJoin('f', 'storages', 'st', $qb->expr()->eq('f.storage', 'st.numeric_id')) - ->orderBy('s.id') - ->setFirstResult(0); + // Create SharedWithGroups query + $qb = $this->getSharedWithGroupQuery($groups, $node); + $qb->setFirstResult(0); if ($limit !== -1) { $qb->setMaxResults($limit - count($shares)); } - // Filter by node if provided - if ($node !== null) { - $qb->andWhere($qb->expr()->eq('file_source', $qb->createNamedParameter($node->getId()))); - } - - $groups = array_map(function(IGroup $group) { return $group->getGID(); }, $groups); - - $qb->andWhere($qb->expr()->eq('share_type', $qb->createNamedParameter(\OCP\Share::SHARE_TYPE_GROUP))) - ->andWhere($qb->expr()->in('share_with', $qb->createNamedParameter( - $groups, - IQueryBuilder::PARAM_STR_ARRAY - ))) - ->andWhere($qb->expr()->orX( - $qb->expr()->eq('item_type', $qb->createNamedParameter('file')), - $qb->expr()->eq('item_type', $qb->createNamedParameter('folder')) - )); - $cursor = $qb->execute(); while($data = $cursor->fetch()) { if ($offset > 0) { @@ -792,6 +861,76 @@ public function getSharedWith($userId, $shareType, $node, $limit, $offset) { return $shares; } + /** + * @inheritdoc + */ + public function getAllSharedWith($userId, $node) { + // Create array of sharedWith objects (target user -> $userId or group of which user is a member + $user = $this->userManager->get($userId); + + // Check if user is member of some groups and chunk them + $allGroups = $this->groupManager->getUserGroups($user, 'sharing'); + + // Make chunks + $sharedWithGroupChunks = array_chunk($allGroups, 100); + + // Check how many group chunks do we need + $sharedWithGroupChunksNo = count($sharedWithGroupChunks); + + // If there are not groups, query only user, if there are groups, query both + $chunkedResults = []; + if ($sharedWithGroupChunksNo === 0) { + // There are no groups, query only for user + $qb = $this->getSharedWithUserQuery($userId, $node); + $cursor = $qb->execute(); + $chunkedResults[] = $cursor->fetchAll(); + $cursor->closeCursor(); + } else { + // There are groups, query both for user and for groups + $userSharesRetrieved = false; + for ($chunkNo = 0; $chunkNo < $sharedWithGroupChunksNo; $chunkNo++) { + // Get respective group chunk + $groups = $sharedWithGroupChunks[$chunkNo]; + + // Check if user shares were already retrieved + // One cannot retrieve user shares multiple times, since it will result in duplicated + // user shares with each query + if ($userSharesRetrieved === false) { + $qb = $this->getSharedWithUserGroupQuery($groups, $userId, $node); + $userSharesRetrieved = true; + } else { + $qb = $this->getSharedWithGroupQuery($groups, $node); + } + $cursor = $qb->execute(); + $chunkedResults[] = $cursor->fetchAll(); + $cursor->closeCursor(); + } + } + + $resolvedShares = []; + $groupShares = []; + foreach($chunkedResults as $resultBatch) { + foreach($resultBatch as $data) { + if ($this->isAccessibleResult($data)) { + $share = $this->createShare($data); + if ($share->getShareType() === \OCP\Share::SHARE_TYPE_GROUP){ + $groupShares[] = $share; + } else { + $resolvedShares[] = $share; + } + } + } + } + + //Resolve all group shares to user specific shares + if (!empty($groupShares)) { + $resolvedGroupShares = $this->resolveGroupShares($groupShares, $userId); + $resolvedShares = array_merge($resolvedShares, $resolvedGroupShares); + } + + return $resolvedShares; + } + /** * Get a share by token * diff --git a/lib/private/Share20/Manager.php b/lib/private/Share20/Manager.php index 833d96a697b2..6a85d787617c 100644 --- a/lib/private/Share20/Manager.php +++ b/lib/private/Share20/Manager.php @@ -114,6 +114,23 @@ public function __construct( $this->sharingDisabledForUsersCache = new CappedMemoryCache(); } + /** + * @param int[] $shareTypes - ref \OC\Share\Constants[] + * @return int[] $providerIdMap e.g. { "ocinternal" => { 0, 1 }[2] }[1] + */ + private function shareTypeToProviderMap($shareTypes) { + $providerIdMap = []; + foreach ($shareTypes as $shareType) { + // Get provider and its ID, at this point provider is cached at IProviderFactory instance + $provider = $this->factory->getProviderForType($shareType); + $providerId = $provider->identifier(); + + // Create a key -> multi value map + $providerIdMap[$providerId][] = $shareType; + } + return $providerIdMap; + } + /** * Convert from a full share id to a tuple (providerId, shareId) * @@ -884,7 +901,6 @@ public function moveShare(\OCP\Share\IShare $share, $recipientId) { $provider->move($share, $recipientId); } - /** * @inheritdoc */ @@ -895,18 +911,8 @@ public function getAllSharesBy($userId, $shareTypes, $nodeIDs, $reshares = false } // This will ensure that if there are multiple share providers for the same share type, we will execute it in batches $shares = array(); - $providerIdMap = array(); - foreach ($shareTypes as $shareType) { - // Get provider and its ID, at this point provider is cached at IProviderFactory instance - $provider = $this->factory->getProviderForType($shareType); - $providerId = $provider->identifier(); - // Create a key -> multi value map - if (!isset($providerIdMap[$providerId])) { - $providerIdMap[$providerId] = array(); - } - array_push($providerIdMap[$providerId], $shareType); - } + $providerIdMap = $this->shareTypeToProviderMap($shareTypes); $today = new \DateTime(); foreach ($providerIdMap as $providerId => $shareTypeArray) { @@ -1011,6 +1017,27 @@ public function getSharedWith($userId, $shareType, $node = null, $limit = 50, $o return $provider->getSharedWith($userId, $shareType, $node, $limit, $offset); } + + /** + * @inheritdoc + */ + public function getAllSharedWith($userId, $shareTypes, $node = null) { + $shares = []; + + // Aggregate all required $shareTypes by mapping provider to supported shareTypes + $providerIdMap = $this->shareTypeToProviderMap($shareTypes); + foreach ($providerIdMap as $providerId => $shareTypeArray) { + // Get provider from cache + $provider = $this->factory->getProvider($providerId); + + // Obtain all shares for all the supported provider types + $queriedShares = $provider->getAllSharedWith($userId, $node); + $shares = array_merge($shares, $queriedShares); + } + + return $shares; + } + /** * @inheritdoc */ diff --git a/lib/public/Share/IManager.php b/lib/public/Share/IManager.php index e23d9c1986fb..9efab8215ffe 100644 --- a/lib/public/Share/IManager.php +++ b/lib/public/Share/IManager.php @@ -90,7 +90,7 @@ public function moveShare(IShare $share, $recipientId); * Get all shares shared by (initiated) by the provided user for specific node IDs. * * @param string $userId - * @param int[] $shareTypes + * @param int[] $shareTypes - ref \OC\Share\Constants[] * @param int[] $nodeIDs * @param bool $reshares * @return IShare[] @@ -112,6 +112,19 @@ public function getAllSharesBy($userId, $shareTypes, $nodeIDs, $reshares = false */ public function getSharesBy($userId, $shareType, $path = null, $reshares = false, $limit = 50, $offset = 0); + + /** + * Get shares shared with $userId for specified share types. + * Filter by $node if provided + * + * @param string $userId + * @param int[] $shareTypes - ref \OC\Share\Constants[] + * @param Node|null $node + * @return IShare[] + * @since 10.0.0 + */ + public function getAllSharedWith($userId, $shareTypes, $node = null); + /** * Get shares shared with $user. * Filter by $node if provided diff --git a/lib/public/Share/IShareProvider.php b/lib/public/Share/IShareProvider.php index 7659e71a9fea..595005b4d0db 100644 --- a/lib/public/Share/IShareProvider.php +++ b/lib/public/Share/IShareProvider.php @@ -105,7 +105,7 @@ public function move(\OCP\Share\IShare $share, $recipient); public function getSharesBy($userId, $shareType, $node, $reshares, $limit, $offset); /** - * Get all shares by the given user for specified shareTypes array + * Get all shares by the given user for specified shareTypes array (ref. \OC\Share\Constants) * * @param string $userId * @param int[] $shareTypes @@ -137,8 +137,21 @@ public function getShareById($id, $recipientId = null); */ public function getSharesByPath(Node $path); + + /** + * Get shared with the given user for shares of all supported share types for this share provider, + * with file_source predicate specified ($node is Node) or + * without ($node is null and scan over file_source is performed). + * + * @param string $userId get shares where this user is the recipient + * @param Node|null $node + * @return \OCP\Share\IShare[] + * @since 10.0.0 + */ + public function getAllSharedWith($userId, $node); + /** - * Get shared with the given user + * Get shared with the given user specifying share type predicate for this specific share provider * * @param string $userId get shares where this user is the recipient * @param int $shareType diff --git a/tests/lib/Share20/DefaultShareProviderTest.php b/tests/lib/Share20/DefaultShareProviderTest.php index cd545cf058a4..e815b660ee51 100644 --- a/tests/lib/Share20/DefaultShareProviderTest.php +++ b/tests/lib/Share20/DefaultShareProviderTest.php @@ -1180,6 +1180,262 @@ public function testChunkedGetSharedWithGroupWithNode() { $this->assertEquals(Share::SHARE_TYPE_GROUP, $share->getShareType()); } + /** + * @dataProvider storageAndFileNameProvider + */ + public function testGetAllShared($storageStringId, $fileName1, $fileName2) { + $storageId = $this->createTestStorageEntry($storageStringId); + $fileId1 = $this->createTestFileEntry($fileName1, $storageId); + $fileId2 = $this->createTestFileEntry($fileName2, $storageId); + $id0 = $this->addShareToDB(Share::SHARE_TYPE_USER, 'user0', 'user1', 'user1', + 'file', $fileId1, 'myTarget', 31, null, null, null); + $id1 = $this->addShareToDB(Share::SHARE_TYPE_GROUP, 'group0', 'user1', 'user1', + 'file', $fileId2, 'myTarget', 31, null, null, null); + $id2 = $this->addShareToDB(Share::SHARE_TYPE_USER, 'user0', 'user1', 'user1', + 'file', $fileId2, 'myTarget', 31, null, null, null); + + $user0 = $this->createMock(IUser::class); + $user0->method('getUID')->willReturn('user0'); + $user1 = $this->createMock(IUser::class); + $user1->method('getUID')->willReturn('user1'); + + $this->userManager->method('get')->willReturnMap([ + ['user0', $user0], + ['user1', $user1], + ]); + + $group0 = $this->createMock(IGroup::class); + $group0->method('getGID')->willReturn('group0'); + + $this->groupManager->method('get')->with('group0')->willReturn($group0); + $this->groupManager->method('getUserGroups')->with($user0)->willReturn([$group0]); + + $this->rootFolder->method('getUserFolder')->with('user1')->will($this->returnSelf()); + $node1 = $this->createMock(File::class); + $node1->method('getId')->willReturn($fileId1); + $node2 = $this->createMock(File::class); + $node2->method('getId')->willReturn($fileId2); + $this->rootFolder->method('getById')->willReturnCallback(function ($id) use ($node1, $node2) { + if($node1->getId() === $id){ + return [$node1]; + } + return [$node2]; + }); + + // Check targeting specific node with $node1 + $recShares = $this->provider->getAllSharedWith('user0', $node1); + $this->assertCount(1, $recShares); + $share = $recShares[0]; + $this->assertEquals($id0, $share->getId()); + $this->assertSame('user0', $share->getSharedWith()); + $this->assertSame('user1', $share->getShareOwner()); + $this->assertSame('user1', $share->getSharedBy()); + $this->assertEquals(Share::SHARE_TYPE_USER, $share->getShareType()); + $shareNode = $share->getNode(); + $this->assertSame($node1, $shareNode); + + // Check targeting specific node with $node2 + $recShares = $this->provider->getAllSharedWith('user0', $node2); + $this->assertCount(2, $recShares); + foreach($recShares as $share) { + if ($share->getShareType() === Share::SHARE_TYPE_USER) { + $this->assertEquals($id2, $share->getId()); + $this->assertSame('user0', $share->getSharedWith()); + $this->assertSame('user1', $share->getShareOwner()); + $this->assertSame('user1', $share->getSharedBy()); + $shareNode = $share->getNode(); + $this->assertSame($node2, $shareNode); + } else { + $this->assertEquals($id1, $share->getId()); + $this->assertSame('group0', $share->getSharedWith()); + $this->assertSame('user1', $share->getShareOwner()); + $this->assertSame('user1', $share->getSharedBy()); + $shareNode = $share->getNode(); + $this->assertSame($node2, $shareNode); + } + } + + // Check targeting all nodes with null + $recShares = $this->provider->getAllSharedWith('user0', null); + $this->assertCount(3, $recShares); + } + + /** + * Check scenario with group and user of the same name + * + * 1. create two users "user1" and "user2" + * 2. create a user "meow" (don't add to any group) + * 3. add user "user1" in group "meow" + * 4. login as "user2" + * 5. create a folder "for_meow_user" and share with the user "meow" + * 6. create another folder "for_meow_group" and share with the group "meow" + * 7. login as "user1": only "for_meow_group" must appear + * 8. login as "meow": only "for_meow_user" must appear + * + * @dataProvider storageAndFileNameProvider + */ + public function testGetAllSharedSameUserGroup($storageStringId, $fileName1, $fileName2) { + $storageId = $this->createTestStorageEntry($storageStringId); + + // 1. create two users "user1" and "user2" + $user1 = $this->createMock(IUser::class); + $user1->method('getUID')->willReturn('user1'); + $user2 = $this->createMock(IUser::class); + $user2->method('getUID')->willReturn('user2'); + + // 2. create a user "meow" (don't add to any group) + $userMeow = $this->createMock(IUser::class); + $userMeow->method('getUID')->willReturn('meow'); + + // 3. add user "user1" in group "meow" + $groupMeow = $this->createMock(IGroup::class); + $groupMeow->method('getGID')->willReturn('meow'); + + $this->groupManager->method('get')->with('meow')->willReturn($groupMeow); + $this->groupManager->method('getUserGroups')->willReturnCallback(function ($user) use ($groupMeow) { + $userUID = $user->getUID(); + if($userUID === 'user1'){ + return [$groupMeow]; + } + return []; + }); + + // 4. login as "user2" + // 5. create a file $fileName1 and share with the user "meow" + $fileId1 = $this->createTestFileEntry($fileName1, $storageId); + $id1 = $this->addShareToDB(Share::SHARE_TYPE_USER, 'meow', 'user2', 'user2', + 'file', $fileId1, 'for_meow_user', 31, null, null, null); + + // 6. create another folder "for_meow_group" and share with the group "meow" + $fileId2 = $this->createTestFileEntry($fileName2, $storageId); + $id2 = $this->addShareToDB(Share::SHARE_TYPE_GROUP, 'meow', 'user2', 'user2', + 'file', $fileId2, 'for_meow_group', 31, null, null, null); + + // Setup mocking + $this->userManager->method('get')->willReturnMap([ + ['user1', $user1], + ['user2', $user2], + ['meow', $userMeow], + ]); + + $this->rootFolder->method('getUserFolder')->with('user2')->will($this->returnSelf()); + $node1 = $this->createMock(File::class); + $node1->method('getId')->willReturn($fileId1); + $node2 = $this->createMock(File::class); + $node2->method('getId')->willReturn($fileId2); + $this->rootFolder->method('getById')->willReturnCallback(function ($id) use ($node1, $node2) { + if($node1->getId() === $id){ + return [$node1]; + } + return [$node2]; + }); + + $recShares = $this->provider->getAllSharedWith('meow', null); + $this->assertCount(1, $recShares); + $share = $recShares[0]; + $this->assertEquals($id1, $share->getId()); + $this->assertSame('meow', $share->getSharedWith()); + $this->assertSame('user2', $share->getShareOwner()); + $this->assertSame('user2', $share->getSharedBy()); + $this->assertEquals(Share::SHARE_TYPE_USER, $share->getShareType()); + $shareNode = $share->getNode(); + $this->assertSame($node1, $shareNode); + + $recShares = $this->provider->getAllSharedWith('user1', null); + $this->assertCount(1, $recShares); + $share = $recShares[0]; + $this->assertEquals($id2, $share->getId()); + $this->assertSame('meow', $share->getSharedWith()); + $this->assertSame('user2', $share->getShareOwner()); + $this->assertSame('user2', $share->getSharedBy()); + $this->assertEquals(Share::SHARE_TYPE_GROUP, $share->getShareType()); + $shareNode = $share->getNode(); + $this->assertSame($node2, $shareNode); + + $recShares = $this->provider->getAllSharedWith('user2', null); + $this->assertCount(0, $recShares); + } + + /** + * Check scenario with group chunking + * + * User user1 will share file to user2 and other file to 205 groups, in which user2 is member + * + * @dataProvider storageAndFileNameProvider + */ + public function testGetAllSharedGroupChunking($storageStringId, $fileName1, $fileName2) { + $storageId = $this->createTestStorageEntry($storageStringId); + + $user1 = $this->createMock(IUser::class); + $user1->method('getUID')->willReturn('user1'); + $user2 = $this->createMock(IUser::class); + $user2->method('getUID')->willReturn('user2'); + + $fileId1 = $this->createTestFileEntry($fileName1, $storageId); + $idUser = $this->addShareToDB(Share::SHARE_TYPE_USER, 'user2', 'user1', 'user1', + 'file', $fileId1, 'for_meow_user', 31, null, null, null); + + $fileId2 = $this->createTestFileEntry($fileName2, $storageId); + for($i = 0; $i < 205; $i++) { + $groupId = 'group'.$i; + $group = $this->createMock(IGroup::class); + $group->method('getGID')->willReturn($groupId); + $groupArray[] = $group; + $idGroups[] = $this->addShareToDB(Share::SHARE_TYPE_GROUP, $groupId, 'user1', 'user1', + 'file', $fileId2, 'target'.$groupId, 31, null, null, null); + } + + $this->groupManager->method('get')->with('group')->willReturn($group); + $this->groupManager->method('getUserGroups')->willReturnCallback(function ($user) use ($groupArray) { + $userUID = $user->getUID(); + if($userUID === 'user2'){ + return $groupArray; + } + return []; + }); + + // Setup mocking + $this->userManager->method('get')->willReturnMap([ + ['user1', $user1], + ['user2', $user2], + ]); + + $this->rootFolder->method('getUserFolder')->with('user1')->will($this->returnSelf()); + $node1 = $this->createMock(File::class); + $node1->method('getId')->willReturn($fileId1); + $node2 = $this->createMock(File::class); + $node2->method('getId')->willReturn($fileId2); + $this->rootFolder->method('getById')->willReturnCallback(function ($id) use ($node1, $node2) { + if($node1->getId() === $id){ + return [$node1]; + } + return [$node2]; + }); + + $recShares = $this->provider->getAllSharedWith('user1', null); + $this->assertCount(0, $recShares); + + $recShares = $this->provider->getAllSharedWith('user2', null); + $this->assertCount(206, $recShares); + foreach($recShares as $share) { + if ($share->getShareType() === Share::SHARE_TYPE_USER) { + $this->assertEquals($idUser, $share->getId()); + $this->assertSame('user2', $share->getSharedWith()); + $this->assertSame('user1', $share->getShareOwner()); + $this->assertSame('user1', $share->getSharedBy()); + $shareNode = $share->getNode(); + $this->assertSame($node1, $shareNode); + } else { + $this->assertContains($share->getId(), $idGroups); + $this->assertStringStartsWith('group', $share->getSharedWith()); + $this->assertSame('user1', $share->getShareOwner()); + $this->assertSame('user1', $share->getSharedBy()); + $shareNode = $share->getNode(); + $this->assertSame($node2, $shareNode); + } + } + } + public function shareTypesProvider() { return [ [Share::SHARE_TYPE_USER, false], diff --git a/tests/lib/Share20/ManagerTest.php b/tests/lib/Share20/ManagerTest.php index d614bd8fc5f8..5b8ac7e246e2 100644 --- a/tests/lib/Share20/ManagerTest.php +++ b/tests/lib/Share20/ManagerTest.php @@ -2735,6 +2735,41 @@ public function testMoveShareGroup() { $this->manager->moveShare($share, 'recipient'); } + + public function testGetSharedWith() { + $user = $this->createMock(IUser::class); + + $share = $this->manager->newShare(); + $share->setShareType(\OCP\Share::SHARE_TYPE_GROUP) + ->setId('42') + ->setProviderId('foo'); + $this->defaultProvider->method('getSharedWith')->with($user, \OCP\Share::SHARE_TYPE_GROUP, null, -1, 0)->willReturn([$share]); + + $shares = $this->manager->getSharedWith($user, \OCP\Share::SHARE_TYPE_GROUP, null, -1, 0); + $this->assertCount(1, $shares); + $returnedShare = $shares[0]; + $this->assertSame($returnedShare->getId(), $share->getId()); + } + + public function testGetAllSharedWith() { + $user = $this->createMock(IUser::class); + + $share1 = $this->manager->newShare(); + $share1->setShareType(\OCP\Share::SHARE_TYPE_USER) + ->setId('42') + ->setProviderId('foo'); + $share2 = $this->manager->newShare(); + $share2->setShareType(\OCP\Share::SHARE_TYPE_GROUP) + ->setId('43') + ->setProviderId('foo'); + $this->defaultProvider->method('getAllSharedWith') + ->with($user, null) + ->willReturn([$share1, $share2]); + + $shares = $this->manager->getAllSharedWith($user, [\OCP\Share::SHARE_TYPE_GROUP, \OCP\Share::SHARE_TYPE_USER]); + $this->assertCount(2, $shares); + $this->assertSame($shares, [$share1, $share2]); + } } class DummyPassword {