Skip to content

Commit 03c02a1

Browse files
committed
feat(files_sharing): implement partial mount providers
Signed-off-by: Salvatore Martire <[email protected]>
1 parent 5de8e11 commit 03c02a1

File tree

2 files changed

+164
-19
lines changed

2 files changed

+164
-19
lines changed

apps/files_sharing/lib/External/MountProvider.php

Lines changed: 64 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -13,6 +13,8 @@
1313
use OCP\DB\QueryBuilder\IQueryBuilder;
1414
use OCP\Federation\ICloudIdManager;
1515
use OCP\Files\Config\IMountProvider;
16+
use OCP\Files\Config\IMountProviderArgs;
17+
use OCP\Files\Config\IPartialMountProvider;
1618
use OCP\Files\Storage\IStorageFactory;
1719
use OCP\Http\Client\IClientService;
1820
use OCP\ICertificateManager;
@@ -21,7 +23,7 @@
2123
use OCP\Server;
2224
use OCP\Share\IShare;
2325

24-
class MountProvider implements IMountProvider {
26+
class MountProvider implements IMountProvider, IPartialMountProvider {
2527
public const STORAGE = ExternalShareStorage::class;
2628

2729
/**
@@ -69,4 +71,65 @@ public function getMountsForUser(IUser $user, IStorageFactory $loader): array {
6971
$result->closeCursor();
7072
return $mounts;
7173
}
74+
75+
/**
76+
* @inheritDoc
77+
*/
78+
public function getMountsForPath(
79+
string $path,
80+
bool $forChildren,
81+
array $mountProviderArgs,
82+
IStorageFactory $loader,
83+
): array {
84+
if (empty($mountProviderArgs)) {
85+
return [];
86+
}
87+
88+
$userId = null;
89+
$user = null;
90+
foreach ($mountProviderArgs as $mountProviderArg) {
91+
if ($userId === null) {
92+
$user = $mountProviderArg->mountInfo->getUser();
93+
$userId = $user->getUID();
94+
} elseif ($userId !== $mountProviderArg->mountInfo->getUser()->getUID()) {
95+
throw new \LogicException('Mounts must belong to the same user!');
96+
}
97+
}
98+
99+
$path = \substr($path, \strlen('/' . $userId . '/files'));
100+
101+
// get all mounts and throw away the ones we are not interested into
102+
$qb = $this->connection->getQueryBuilder();
103+
$qb->select('id', 'remote', 'share_token', 'password', 'mountpoint', 'owner')
104+
->from('share_external')
105+
->where($qb->expr()->eq('user', $qb->createNamedParameter($user->getUID())))
106+
->andWhere($qb->expr()->eq('accepted', $qb->createNamedParameter(IShare::STATUS_ACCEPTED, IQueryBuilder::PARAM_INT)));
107+
108+
if ($forChildren) {
109+
$qb->andWhere($qb->expr()->like('mountpoint', $qb->createNamedParameter($this->connection->escapeLikeParameter($path) . '_%')));
110+
} else {
111+
$qb->andWhere($qb->expr()->eq('mountpoint', $qb->createNamedParameter($path)));
112+
}
113+
114+
$result = $qb->executeQuery();
115+
116+
$mounts = [];
117+
while ($row = $result->fetchAssociative()) {
118+
$row['manager'] = $this;
119+
$row['token'] = $row['share_token'];
120+
$mount = $this->getMount($user, $row, $loader);
121+
122+
$isRequestedMount = array_any($mountProviderArgs, function(IMountProviderArgs $arg) use ($mount) {
123+
return $arg->mountInfo->getMountPoint() === $mount->getMountPoint();
124+
});
125+
if (!$isRequestedMount) {
126+
continue;
127+
}
128+
129+
$mounts[$mount->getMountPoint()] = $mount;
130+
}
131+
$result->closeCursor();
132+
133+
return $mounts;
134+
}
72135
}

apps/files_sharing/lib/MountProvider.php

Lines changed: 100 additions & 18 deletions
Original file line numberDiff line numberDiff line change
@@ -14,6 +14,8 @@
1414
use OCP\Cache\CappedMemoryCache;
1515
use OCP\EventDispatcher\IEventDispatcher;
1616
use OCP\Files\Config\IMountProvider;
17+
use OCP\Files\Config\IMountProviderArgs;
18+
use OCP\Files\Config\IPartialMountProvider;
1719
use OCP\Files\Mount\IMountManager;
1820
use OCP\Files\Mount\IMountPoint;
1921
use OCP\Files\Storage\IStorageFactory;
@@ -26,7 +28,7 @@
2628
use Psr\Log\LoggerInterface;
2729
use function count;
2830

29-
class MountProvider implements IMountProvider {
31+
class MountProvider implements IMountProvider, IPartialMountProvider {
3032
/**
3133
* @param IConfig $config
3234
* @param IManager $shareManager
@@ -51,7 +53,7 @@ public function __construct(
5153
*/
5254
public function getMountsForUser(IUser $user, IStorageFactory $loader) {
5355
$userId = $user->getUID();
54-
$shares = array_merge(
56+
$shares = $this->mergeIterables(
5557
$this->shareManager->getSharedWith($userId, IShare::TYPE_USER, null, -1),
5658
$this->shareManager->getSharedWith($userId, IShare::TYPE_GROUP, null, -1),
5759
$this->shareManager->getSharedWith($userId, IShare::TYPE_CIRCLE, null, -1),
@@ -62,17 +64,19 @@ public function getMountsForUser(IUser $user, IStorageFactory $loader) {
6264
$shares = $this->filterShares($shares, $userId);
6365
$superShares = $this->buildSuperShares($shares, $user);
6466

65-
return $this->getMountsFromSuperShares($userId, $superShares, $loader, $user);
67+
return array_values($this->getMountsFromSuperShares($userId,
68+
$superShares,
69+
$loader, $user));
6670
}
6771

6872
/**
6973
* Groups shares by path (nodeId) and target path
7074
*
71-
* @param IShare[] $shares
75+
* @param iterable<IShare> $shares
7276
* @return IShare[][] array of grouped shares, each element in the
7377
* array is a group which itself is an array of shares
7478
*/
75-
private function groupShares(array $shares) {
79+
private function groupShares(iterable $shares): array {
7680
$tmp = [];
7781

7882
foreach ($shares as $share) {
@@ -108,11 +112,11 @@ private function groupShares(array $shares) {
108112
* the shares in the group, forming the most permissive combination
109113
* possible.
110114
*
111-
* @param IShare[] $allShares
115+
* @param iterable<IShare> $allShares
112116
* @param IUser $user user
113117
* @return list<array{IShare, array<IShare>}> Tuple of [superShare, groupedShares]
114118
*/
115-
private function buildSuperShares(array $allShares, IUser $user) {
119+
private function buildSuperShares(iterable $allShares, IUser $user): array {
116120
$result = [];
117121

118122
$groupedShares = $this->groupShares($allShares);
@@ -248,7 +252,7 @@ private function adjustTarget(
248252
* @param array $superShares
249253
* @param IStorageFactory $loader
250254
* @param IUser $user
251-
* @return array
255+
* @return array IMountPoint indexed by mount point
252256
* @throws Exception
253257
*/
254258
private function getMountsFromSuperShares(
@@ -333,24 +337,102 @@ private function getMountsFromSuperShares(
333337
$validShareCache->set($userId, $newMaxValidatedShare, 24 * 60 * 60);
334338

335339
// array_filter removes the null values from the array
336-
return array_values(array_filter($mounts));
340+
return array_filter($mounts);
337341
}
338342

339343
/**
340344
* Filters out shares owned or shared by the user and ones for which the
341345
* user has no permissions.
342346
*
343-
* @param IShare[] $shares
344-
* @return IShare[]
347+
* @param iterable<IShare> $shares
348+
* @return iterable<IShare>
345349
*/
346-
private function filterShares(array $shares, string $userId): array {
347-
return array_filter(
348-
$shares,
349-
static function (IShare $share) use ($userId) {
350-
return $share->getPermissions() > 0
351-
&& $share->getShareOwner() !== $userId
352-
&& $share->getSharedBy() !== $userId;
350+
private function filterShares(iterable $shares, string $userId): iterable {
351+
foreach ($shares as $share) {
352+
if ($share->getPermissions() > 0 && $share->getShareOwner()
353+
!== $userId && $share->getSharedBy() !== $userId) {
354+
yield $share;
355+
}
356+
}
357+
}
358+
359+
/**
360+
* @inheritdoc
361+
*/
362+
public function getMountsForPath(
363+
string $path,
364+
bool $forChildren,
365+
array $mountProviderArgs,
366+
IStorageFactory $loader,
367+
): array {
368+
if (empty($mountProviderArgs)) {
369+
return [];
370+
}
371+
372+
$limit = -1;
373+
$userId = null;
374+
$user = null;
375+
foreach ($mountProviderArgs as $mountProviderArg) {
376+
if ($userId === null) {
377+
$user = $mountProviderArg->mountInfo->getUser();
378+
$userId = $user->getUID();
379+
} elseif ($userId !== $mountProviderArg->mountInfo->getUser()->getUID()) {
380+
throw new \LogicException('Mounts must belong to the same user!');
353381
}
382+
}
383+
384+
if (!$forChildren) {
385+
// override path with mount point when fetching without children
386+
$path = $mountProviderArgs[0]->mountInfo->getMountPoint();
387+
}
388+
389+
// remove /uid/files and the trailing slash as the target is stored
390+
// without it
391+
$path = \rtrim(\substr($path, \strlen('/' . $userId . '/files')), '/');
392+
393+
$shares = $this->mergeIterables(
394+
$this->shareManager->getSharedWithByPath($userId,
395+
IShare::TYPE_USER, $path, $forChildren, $limit),
396+
$this->shareManager->getSharedWithByPath($userId,
397+
IShare::TYPE_GROUP, $path, $forChildren, $limit),
398+
$this->shareManager->getSharedWithByPath($userId,
399+
IShare::TYPE_CIRCLE, $path, $forChildren, $limit),
400+
$this->shareManager->getSharedWithByPath($userId,
401+
IShare::TYPE_ROOM, $path, $forChildren, $limit),
402+
$this->shareManager->getSharedWithByPath($userId,
403+
IShare::TYPE_DECK, $path, $forChildren, $limit),
404+
$this->shareManager->getSharedWithByPath($userId,
405+
IShare::TYPE_SCIENCEMESH, $path, $forChildren, $limit),
354406
);
407+
408+
$shares = $this->filterShares($shares, $userId);
409+
$superShares = $this->buildSuperShares($shares, $user);
410+
411+
// remove unrequested shares
412+
$superShares = \array_filter($superShares, function (array $shares) use ($mountProviderArgs) {
413+
$superShare = $shares[0];
414+
415+
return array_any($mountProviderArgs,
416+
function (IMountProviderArgs $mountProviderArg) use ($superShare) {
417+
return $superShare->getNodeId() === $mountProviderArg->mountInfo->getRootId();
418+
});
419+
});
420+
421+
return $this->getMountsFromSuperShares($userId,
422+
$superShares,
423+
$loader,
424+
$user);
425+
}
426+
427+
/**
428+
* @param iterable ...$iterables
429+
* @return iterable
430+
*/
431+
private function mergeIterables(...$iterables): iterable {
432+
foreach ($iterables as $iterable) {
433+
foreach ($iterable as $value) {
434+
yield $value;
435+
}
436+
}
355437
}
356438
}

0 commit comments

Comments
 (0)