Signed-off-by: Joas Schilling <coding@schilljs.com>tags/v12.0.0beta1
@@ -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]; |
@@ -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 |
@@ -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; | |||
} | |||
} |
@@ -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; | |||
} | |||
@@ -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; | |||
} | |||
} |
@@ -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 |
@@ -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); | |||
} |
@@ -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); |