diff options
-rw-r--r-- | apps/federatedfilesharing/lib/FederatedShareProvider.php | 13 | ||||
-rw-r--r-- | lib/private/Encryption/File.php | 4 | ||||
-rw-r--r-- | lib/private/Share20/DefaultShareProvider.php | 54 | ||||
-rw-r--r-- | lib/private/Share20/Manager.php | 16 | ||||
-rw-r--r-- | lib/private/Share20/ShareHelper.php | 118 | ||||
-rw-r--r-- | lib/public/Share/IManager.php | 5 | ||||
-rw-r--r-- | lib/public/Share/IShareHelper.php | 7 | ||||
-rw-r--r-- | lib/public/Share/IShareProvider.php | 7 |
8 files changed, 170 insertions, 54 deletions
diff --git a/apps/federatedfilesharing/lib/FederatedShareProvider.php b/apps/federatedfilesharing/lib/FederatedShareProvider.php index 29aa594df54..9e8d9fd9e47 100644 --- a/apps/federatedfilesharing/lib/FederatedShareProvider.php +++ b/apps/federatedfilesharing/lib/FederatedShareProvider.php @@ -983,18 +983,23 @@ class FederatedShareProvider implements IShareProvider { } $qb = $this->dbConnection->getQueryBuilder(); - $qb->select('share_with') + $qb->select('share_with', 'file_source', 'file_target') ->from('share') ->where($qb->expr()->eq('share_type', $qb->createNamedParameter(\OCP\Share::SHARE_TYPE_REMOTE))) ->andWhere($qb->expr()->in('file_source', $qb->createNamedParameter($ids, IQueryBuilder::PARAM_INT_ARRAY))) ->andWhere($qb->expr()->orX( $qb->expr()->eq('item_type', $qb->createNamedParameter('file')), $qb->expr()->eq('item_type', $qb->createNamedParameter('folder')) - )) - ->setMaxResults(1); + )); $cursor = $qb->execute(); - $remote = $cursor->fetch() !== false; + $remote = []; + while ($row = $cursor->fetch()) { + $remote[$row['share_with']] = [ + 'node_id' => $row['file_source'], + 'node_path' => $row['file_target'], + ]; + } $cursor->closeCursor(); return ['remote' => $remote]; diff --git a/lib/private/Encryption/File.php b/lib/private/Encryption/File.php index 3b6a87ef516..a1fd8e300d4 100644 --- a/lib/private/Encryption/File.php +++ b/lib/private/Encryption/File.php @@ -95,14 +95,14 @@ class File implements \OCP\Encryption\IFile { $this->cache[$parent] = $resultForParents; } $userIds = \array_merge($userIds, $resultForParents['users']); - $public = $resultForParents['public'] || $resultForParents['remote']; + $public = $resultForParents['public'] || !empty($resultForParents['remote']); // Find out who, if anyone, is sharing the file if ($file !== null) { $resultForFile = $this->shareManager->getAccessList($file, false); $userIds = array_merge($userIds, $resultForFile['users']); - $public = $resultForFile['public'] || $resultForFile['remote'] || $public; + $public = $resultForFile['public'] || !empty($resultForFile['remote']) || $public; } // check if it is a group mount diff --git a/lib/private/Share20/DefaultShareProvider.php b/lib/private/Share20/DefaultShareProvider.php index b8f46a1f4d6..49a756f2a10 100644 --- a/lib/private/Share20/DefaultShareProvider.php +++ b/lib/private/Share20/DefaultShareProvider.php @@ -1092,7 +1092,7 @@ class DefaultShareProvider implements IShareProvider { $or->add($qb->expr()->eq('share_type', $qb->createNamedParameter(self::SHARE_TYPE_USERGROUP))); } - $qb->select('share_type', 'share_with') + $qb->select('id', 'parent', 'share_type', 'share_with', 'file_source', 'file_target', 'permissions') ->from('share') ->where( $or @@ -1110,7 +1110,8 @@ class DefaultShareProvider implements IShareProvider { $type = (int)$row['share_type']; if ($type === \OCP\Share::SHARE_TYPE_USER) { $uid = $row['share_with']; - $users[$uid] = isset($users[$uid]) ? $users[$uid] + 1 : 1; + $users[$uid] = isset($users[$uid]) ? $users[$uid] : []; + $users[$uid][$row['id']] = $row; } else if ($type === \OCP\Share::SHARE_TYPE_GROUP) { $gid = $row['share_with']; $group = $this->groupManager->get($gid); @@ -1121,23 +1122,60 @@ class DefaultShareProvider implements IShareProvider { $userList = $group->getUsers(); foreach ($userList as $user) { - $users[$user->getUID()] = isset($users[$user->getUID()]) ? $users[$user->getUID()] + 1 : 1; + $uid = $user->getUID(); + $users[$uid] = isset($users[$uid]) ? $users[$uid] : []; + $users[$uid][$row['id']] = $row; } } else if ($type === \OCP\Share::SHARE_TYPE_LINK) { $link = true; } else if ($type === self::SHARE_TYPE_USERGROUP) { if ($currentAccess === true) { $uid = $row['share_with']; - $users[$uid] = isset($users[$uid]) ? $users[$uid] - 1 : -1; + $users[$uid] = isset($users[$uid]) ? $users[$uid] : []; + $users[$uid][$row['id']] = $row; } } } $cursor->closeCursor(); - $users = array_filter($users, function($count) { - return $count > 0; - }); + $users = array_map([$this, 'filterSharesOfUser'], $users); - return ['users' => array_keys($users), 'public' => $link]; + return ['users' => $users, 'public' => $link]; + } + + /** + * For each user the path with the fewest slashes is returned + * @param array $shares + * @return array + */ + protected function filterSharesOfUser(array $shares) { + // Group shares when the user has a share exception + foreach ($shares as $id => $share) { + $type = (int) $share['share_type']; + $permissions = (int) $share['permissions']; + + if ($type === self::SHARE_TYPE_USERGROUP) { + unset($shares[$share['parent']]); + + if ($permissions === 0) { + unset($shares[$id]); + } + } + } + + $best = []; + $bestDepth = 0; + foreach ($shares as $id => $share) { + $depth = substr_count($share['file_target'], '/'); + if (empty($best) || $depth < $bestDepth) { + $bestDepth = $depth; + $best = [ + 'node_id' => $share['file_source'], + 'node_path' => $share['file_target'], + ]; + } + } + + return $best; } } diff --git a/lib/private/Share20/Manager.php b/lib/private/Share20/Manager.php index f1ce66f008c..91fcb6af8fb 100644 --- a/lib/private/Share20/Manager.php +++ b/lib/private/Share20/Manager.php @@ -1191,9 +1191,10 @@ class Manager implements IManager { * * Then the access list will to '/folder1/folder2/fileA' is: * [ - * users => ['user1', 'user2', 'user4'], + * users => ['user1' => ['node_id' => 42, 'node_path' => '/path'], 'user2' => [...]], + * remote => ['user1' => ['node_id' => 42, 'node_path' => '/path'], 'user2' => [...]], * public => bool - * remote => bool + * mail => bool * ] * * This is required for encryption/activity @@ -1206,7 +1207,7 @@ class Manager implements IManager { public function getAccessList(\OCP\Files\Node $path, $recursive = true, $currentAccess = false) { $owner = $path->getOwner()->getUID(); - $al = ['users' => [], 'public' => false, 'remote' => false]; + $al = ['users' => [], 'remote' => [], 'public' => false]; if (!$this->userManager->userExists($owner)) { return $al; } @@ -1222,7 +1223,12 @@ class Manager implements IManager { /** @var Node[] $nodes */ $nodes = []; - $al['users'][] = $owner; + $ownerPath = $path->getPath(); + list(,,,$ownerPath) = explode('/', $ownerPath, 4); + $al['users'][$owner] = [ + 'node_id' => $path->getId(), + 'node_path' => '/' . $ownerPath, + ]; // Collect all the shares while ($path->getPath() !== $userFolder->getPath()) { @@ -1249,8 +1255,6 @@ class Manager implements IManager { } } - $al['users'] = array_unique($al['users']); - return $al; } diff --git a/lib/private/Share20/ShareHelper.php b/lib/private/Share20/ShareHelper.php index a7d46457cd8..93b32cef3c3 100644 --- a/lib/private/Share20/ShareHelper.php +++ b/lib/private/Share20/ShareHelper.php @@ -22,43 +22,113 @@ */ namespace OC\Share20; -use OCP\Files\IRootFolder; use OCP\Files\Node; -use OCP\Files\NotFoundException; +use OCP\Share\IManager; +use OCP\Share\IShareHelper; -class ShareHelper { - /** @var IRootFolder */ - private $rootFolder; +class ShareHelper implements IShareHelper { - public function __construct(IRootFolder $rootFolder) { - $this->rootFolder = $rootFolder; + /** @var IManager */ + private $shareManager; + + public function __construct(IManager $shareManager) { + $this->shareManager = $shareManager; } /** - * If a user has access to a file - * * @param Node $node - * @param array $users Array of userIds - * @return array Mapping $uid to an array of nodes + * @return array [ users => [Mapping $uid => $path], remotes => [Mapping $cloudId => $path]] */ - public function getPathsForAccessList(Node $node, $users) { - $result = []; - - foreach ($users as $user) { - try { - $userFolder = $this->rootFolder->getUserFolder($user); - } catch (NotFoundException $e) { - continue; + public function getPathsForAccessList(Node $node) { + $result = [ + 'users' => [], + 'remotes' => [], + ]; + + $accessList = $this->shareManager->getAccessList($node, true, true); + if (!empty($accessList['users'])) { + $result['users'] = $this->getPathsForUsers($node, $accessList['users']); + } + if (!empty($accessList['remote'])) { + $result['remotes'] = $this->getPathsForRemotes($node, $accessList['remote']); + } + + return $result; + } + + protected function getPathsForUsers(Node $node, array $users) { + $byId = $results = []; + foreach ($users as $uid => $info) { + if (!isset($byId[$info['node_id']])) { + $byId[$info['node_id']] = []; } + $byId[$info['node_id']][$uid] = $info['node_path']; + } - $nodes = $userFolder->getById($node->getId()); - if ($nodes === []) { - continue; + if (isset($byId[$node->getId()])) { + foreach ($byId[$node->getId()] as $uid => $path) { + $results[$uid] = $path; } + unset($byId[$node->getId()]); + } - $result[$user] = $nodes; + if (empty($byId)) { + return $results; } - return $result; + $item = $node; + $appendix = '/' . $node->getName(); + while (!empty($byId)) { + $item = $item->getParent(); + + if (!empty($byId[$item->getId()])) { + foreach ($byId[$item->getId()] as $uid => $path) { + $results[$uid] = $path . $appendix; + } + unset($byId[$item->getId()]); + } + + $appendix = '/' . $item->getName() . $appendix; + } + + return $results; + } + + protected function getPathsForRemotes(Node $node, array $remotes) { + $byId = $results = []; + foreach ($remotes as $cloudId => $info) { + if (!isset($byId[$info['node_id']])) { + $byId[$info['node_id']] = []; + } + $byId[$info['node_id']][$cloudId] = $info['node_path']; + } + + if (isset($byId[$node->getId()])) { + foreach ($byId[$node->getId()] as $cloudId => $_) { + $results[$cloudId] = '/' . $node->getName(); + } + unset($byId[$node->getId()]); + } + + if (empty($byId)) { + return $results; + } + + $item = $node; + $path = '/' . $node->getName(); + while (!empty($byId)) { + $item = $item->getParent(); + + if (!empty($byId[$item->getId()])) { + foreach ($byId[$item->getId()] as $uid => $_) { + $results[$uid] = $path; + } + unset($byId[$item->getId()]); + } + + $path = '/' . $item->getName() . $path; + } + + return $results; } } diff --git a/lib/public/Share/IManager.php b/lib/public/Share/IManager.php index 26c3f2b42e6..1cbb27cb618 100644 --- a/lib/public/Share/IManager.php +++ b/lib/public/Share/IManager.php @@ -207,9 +207,10 @@ interface IManager { * * Then the access list will to '/folder1/folder2/fileA' is: * [ - * users => ['user1', 'user2', 'user4'], + * users => ['user1' => ['node_id' => 42, 'node_path' => '/path'], 'user2' => [...]], + * remote => ['user1' => ['node_id' => 42, 'node_path' => '/path'], 'user2' => [...]], * public => bool - * remote => bool + * mail => bool * ] * * This is required for encryption/activity diff --git a/lib/public/Share/IShareHelper.php b/lib/public/Share/IShareHelper.php index 5e99f86832d..01202cf477b 100644 --- a/lib/public/Share/IShareHelper.php +++ b/lib/public/Share/IShareHelper.php @@ -33,12 +33,9 @@ use OCP\Files\Node; interface IShareHelper { /** - * If a user has access to a file - * * @param Node $node - * @param array $users Array of userIds - * @return array Mapping $uid to an array of nodes + * @return array [ users => [Mapping $uid => $path], remotes => [Mapping $cloudId => $path]] * @since 12 */ - public function getPathsForAccessList(Node $node, $users); + public function getPathsForAccessList(Node $node); } diff --git a/lib/public/Share/IShareProvider.php b/lib/public/Share/IShareProvider.php index 2533e81abf2..fba432f31e7 100644 --- a/lib/public/Share/IShareProvider.php +++ b/lib/public/Share/IShareProvider.php @@ -196,14 +196,15 @@ interface IShareProvider { * Return will look like: * * [ - * users => ['user1', 'user2', 'user4'], + * users => ['user1' => ['node_id' => 42, 'node_path' => '/path'], 'user2' => [...]], + * remote => ['user1' => ['node_id' => 42, 'node_path' => '/path'], 'user2' => [...]], + * mail => bool * public => bool - * remote => bool * ] * * @param Node[] $nodes The list of nodes to get access for * @param bool $currentAccess If current access is required (like for removed shares that might get revived later) - * @return string[] + * @return array * @since 12 */ public function getAccessList($nodes, $currentAccess); |