Skip to content

Commit 8f6d6a4

Browse files
committed
feat(files_sharing): implement partial mount providers
Signed-off-by: Salvatore Martire <[email protected]>
1 parent 239647d commit 8f6d6a4

File tree

5 files changed

+161
-19
lines changed

5 files changed

+161
-19
lines changed

apps/files_sharing/lib/External/MountProvider.php

Lines changed: 55 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -13,6 +13,7 @@
1313
use OCP\DB\QueryBuilder\IQueryBuilder;
1414
use OCP\Federation\ICloudIdManager;
1515
use OCP\Files\Config\IMountProvider;
16+
use OCP\Files\Config\IPartialMountProvider;
1617
use OCP\Files\Storage\IStorageFactory;
1718
use OCP\Http\Client\IClientService;
1819
use OCP\ICertificateManager;
@@ -21,7 +22,7 @@
2122
use OCP\Server;
2223
use OCP\Share\IShare;
2324

24-
class MountProvider implements IMountProvider {
25+
class MountProvider implements IMountProvider, IPartialMountProvider {
2526
public const STORAGE = ExternalShareStorage::class;
2627

2728
/**
@@ -69,4 +70,57 @@ public function getMountsForUser(IUser $user, IStorageFactory $loader): array {
6970
$result->closeCursor();
7071
return $mounts;
7172
}
73+
74+
/**
75+
* @inheritDoc
76+
*/
77+
public function getMountsForPath(
78+
string $path,
79+
bool $forChildren,
80+
array $mountProviderArgs,
81+
IStorageFactory $loader,
82+
): array {
83+
if (empty($mountProviderArgs)) {
84+
return [];
85+
}
86+
87+
$userId = null;
88+
$user = null;
89+
foreach ($mountProviderArgs as $mountProviderArg) {
90+
if ($userId === null) {
91+
$user = $mountProviderArg->mountInfo->getUser();
92+
$userId = $user->getUID();
93+
} elseif ($userId !== $mountProviderArg->getUser()->getUID()) {
94+
throw new \LogicException('Mounts must belong to the same user!');
95+
}
96+
}
97+
98+
$path = \substr($path, \strlen('/' . $userId . '/files'));
99+
100+
// TODO filter mounts by mount point received in the mountProviderArgs
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('file_target', $qb->createNamedParameter($this->connection->escapeLikeParameter($path) . '_%')));
110+
} else {
111+
$qb->andWhere($qb->expr()->eq('file_target', $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+
$mounts[] = $this->getMount($user, $row, $loader);
121+
}
122+
$result->closeCursor();
123+
124+
return $mounts;
125+
}
72126
}

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),
@@ -63,17 +65,19 @@ public function getMountsForUser(IUser $user, IStorageFactory $loader) {
6365
$shares = $this->filterShares($shares, $userId);
6466
$superShares = $this->buildSuperShares($shares, $user);
6567

66-
return $this->getMountsFromSuperShares($userId, $superShares, $loader, $user);
68+
return array_values($this->getMountsFromSuperShares($userId,
69+
$superShares,
70+
$loader, $user));
6771
}
6872

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

7983
foreach ($shares as $share) {
@@ -109,11 +113,11 @@ private function groupShares(array $shares) {
109113
* the shares in the group, forming the most permissive combination
110114
* possible.
111115
*
112-
* @param IShare[] $allShares
116+
* @param iterable<IShare> $allShares
113117
* @param IUser $user user
114118
* @return list<array{IShare, array<IShare>}> Tuple of [superShare, groupedShares]
115119
*/
116-
private function buildSuperShares(array $allShares, IUser $user) {
120+
private function buildSuperShares(iterable $allShares, IUser $user): array {
117121
$result = [];
118122

119123
$groupedShares = $this->groupShares($allShares);
@@ -249,7 +253,7 @@ private function adjustTarget(
249253
* @param array $superShares
250254
* @param IStorageFactory $loader
251255
* @param IUser $user
252-
* @return array
256+
* @return array IMountPoint indexed by mount point
253257
* @throws Exception
254258
*/
255259
private function getMountsFromSuperShares(
@@ -334,24 +338,102 @@ private function getMountsFromSuperShares(
334338
$validShareCache->set($userId, $newMaxValidatedShare, 24 * 60 * 60);
335339

336340
// array_filter removes the null values from the array
337-
return array_values(array_filter($mounts));
341+
return array_filter($mounts);
338342
}
339343

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

lib/private/Files/Config/MountProviderCollection.php

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -91,6 +91,7 @@ public function getMountsForUser(IUser $user): array {
9191
public function getUserMountsFromProviderByPath(
9292
string $providerClass,
9393
string $path,
94+
bool $forChildren,
9495
array $mountProviderArgs,
9596
): array {
9697
$provider = $this->providers[$providerClass] ?? null;
@@ -107,6 +108,7 @@ public function getUserMountsFromProviderByPath(
107108
/** @var IPartialMountProvider $provider */
108109
return $provider->getMountsForPath(
109110
$path,
111+
$forChildren,
110112
$mountProviderArgs,
111113
$this->loader,
112114
);

lib/private/Files/SetupManager.php

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -489,6 +489,7 @@ public function setupForPath(string $path, bool $includeChildren = false): void
489489
$this->mountProviderCollection->getUserMountsFromProviderByPath(
490490
$mountProvider,
491491
$path,
492+
false,
492493
[$providerArgs]
493494
)
494495
);
@@ -576,6 +577,7 @@ static function (ICachedMountInfo $info) use ($rootsMetadata) {
576577
= $this->mountProviderCollection->getUserMountsFromProviderByPath(
577578
$providerClass,
578579
$path,
580+
true,
579581
$providerArgs,
580582
);
581583
}

lib/public/Files/Config/IPartialMountProvider.php

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -30,13 +30,15 @@ interface IPartialMountProvider extends IMountProvider {
3030
* corresponding IMountPoint instances.
3131
*
3232
* @param string $path path for which the mounts are set up
33+
* @param bool $forChildren when true, only child mounts for path should be returned
3334
* @param IMountProviderArgs[] $mountProviderArgs
3435
* @param IStorageFactory $loader
3536
* @return array<string, IMountPoint> IMountPoint instances, indexed by
3637
* mount-point
3738
*/
3839
public function getMountsForPath(
3940
string $path,
41+
bool $forChildren,
4042
array $mountProviderArgs,
4143
IStorageFactory $loader,
4244
): array;

0 commit comments

Comments
 (0)