Skip to content

Commit 2716b0f

Browse files
authored
Merge pull request #57292 from nextcloud/feature/54562/files-sharing-authoritative
feat(files_sharing): implement partial mount providers
2 parents 5db140d + b6e90ac commit 2716b0f

File tree

11 files changed

+268
-44
lines changed

11 files changed

+268
-44
lines changed

apps/files_sharing/appinfo/info.xml

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -14,7 +14,7 @@
1414
Turning the feature off removes shared files and folders on the server for all share recipients, and also on the sync clients and mobile apps. More information is available in the Nextcloud Documentation.
1515

1616
</description>
17-
<version>1.25.1</version>
17+
<version>1.25.2</version>
1818
<licence>agpl</licence>
1919
<author>Michael Gapczynski</author>
2020
<author>Bjoern Schiessle</author>

apps/files_sharing/lib/External/MountProvider.php

Lines changed: 52 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,54 @@ public function getMountsForUser(IUser $user, IStorageFactory $loader): array {
6970
$result->closeCursor();
7071
return $mounts;
7172
}
73+
74+
public function getMountsForPath(
75+
string $setupPathHint,
76+
bool $forChildren,
77+
array $mountProviderArgs,
78+
IStorageFactory $loader,
79+
): array {
80+
$user = $mountProviderArgs[0]->mountInfo->getUser();
81+
$userId = $user->getUID();
82+
83+
if (!$forChildren) {
84+
// override path with mount point when fetching without children
85+
$setupPathHint = $mountProviderArgs[0]->mountInfo->getMountPoint();
86+
}
87+
88+
// remove /uid/files as the target is stored without
89+
$setupPathHint = \substr($setupPathHint, \strlen('/' . $userId . '/files'));
90+
// remove trailing slash
91+
$setupPathHint = \rtrim($setupPathHint, '/');
92+
93+
// make sure trailing slash is present when loading children
94+
if ($forChildren || $setupPathHint === '') {
95+
$setupPathHint .= '/';
96+
}
97+
98+
$qb = $this->connection->getQueryBuilder();
99+
$qb->select('id', 'remote', 'share_token', 'password', 'mountpoint', 'owner')
100+
->from('share_external')
101+
->where($qb->expr()->eq('user', $qb->createNamedParameter($user->getUID())))
102+
->andWhere($qb->expr()->eq('accepted', $qb->createNamedParameter(IShare::STATUS_ACCEPTED, IQueryBuilder::PARAM_INT)));
103+
104+
if ($forChildren) {
105+
$qb->andWhere($qb->expr()->like('mountpoint', $qb->createNamedParameter($this->connection->escapeLikeParameter($setupPathHint) . '_%')));
106+
} else {
107+
$qb->andWhere($qb->expr()->eq('mountpoint', $qb->createNamedParameter($setupPathHint)));
108+
}
109+
110+
$result = $qb->executeQuery();
111+
112+
$mounts = [];
113+
while ($row = $result->fetchAssociative()) {
114+
$row['manager'] = $this;
115+
$row['token'] = $row['share_token'];
116+
$mount = $this->getMount($user, $row, $loader);
117+
$mounts[$mount->getMountPoint()] = $mount;
118+
}
119+
$result->closeCursor();
120+
121+
return $mounts;
122+
}
72123
}

apps/files_sharing/lib/Migration/Version11300Date20201120141438.php

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -85,6 +85,7 @@ public function changeSchema(IOutput $output, Closure $schemaClosure, array $opt
8585
]);
8686
$table->setPrimaryKey(['id']);
8787
$table->addUniqueIndex(['user', 'mountpoint_hash'], 'sh_external_mp');
88+
$table->addIndex(['user', 'mountpoint'], 'user_mountpoint_index', [], ['lengths' => [null, 128]]);
8889
} else {
8990
$table = $schema->getTable('share_external');
9091
$remoteIdColumn = $table->getColumn('remote_id');

apps/files_sharing/lib/MountProvider.php

Lines changed: 83 additions & 29 deletions
Original file line numberDiff line numberDiff line change
@@ -14,6 +14,7 @@
1414
use OCP\Cache\CappedMemoryCache;
1515
use OCP\EventDispatcher\IEventDispatcher;
1616
use OCP\Files\Config\IMountProvider;
17+
use OCP\Files\Config\IPartialMountProvider;
1718
use OCP\Files\Mount\IMountManager;
1819
use OCP\Files\Mount\IMountPoint;
1920
use OCP\Files\Storage\IStorageFactory;
@@ -24,9 +25,10 @@
2425
use OCP\Share\IManager;
2526
use OCP\Share\IShare;
2627
use Psr\Log\LoggerInterface;
28+
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,24 @@ 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(
68+
$this->getMountsFromSuperShares(
69+
$userId,
70+
$superShares,
71+
$loader,
72+
$user,
73+
),
74+
);
6675
}
6776

6877
/**
6978
* Groups shares by path (nodeId) and target path
7079
*
71-
* @param IShare[] $shares
80+
* @param iterable<IShare> $shares
7281
* @return IShare[][] array of grouped shares, each element in the
7382
* array is a group which itself is an array of shares
7483
*/
75-
private function groupShares(array $shares) {
84+
private function groupShares(iterable $shares): array {
7685
$tmp = [];
7786

7887
foreach ($shares as $share) {
@@ -108,11 +117,11 @@ private function groupShares(array $shares) {
108117
* the shares in the group, forming the most permissive combination
109118
* possible.
110119
*
111-
* @param IShare[] $allShares
120+
* @param iterable<IShare> $allShares
112121
* @param IUser $user user
113122
* @return list<array{IShare, array<IShare>}> Tuple of [superShare, groupedShares]
114123
*/
115-
private function buildSuperShares(array $allShares, IUser $user) {
124+
private function buildSuperShares(iterable $allShares, IUser $user): array {
116125
$result = [];
117126

118127
$groupedShares = $this->groupShares($allShares);
@@ -237,8 +246,7 @@ private function adjustTarget(
237246
// null groups which usually appear with group backend
238247
// caching inconsistencies
239248
$this->logger->debug(
240-
'Could not adjust share target for share ' . $share->getId(
241-
) . ' to make it consistent: ' . $e->getMessage(),
249+
'Could not adjust share target for share ' . $share->getId() . ' to make it consistent: ' . $e->getMessage(),
242250
['app' => 'files_sharing']
243251
);
244252
}
@@ -248,7 +256,7 @@ private function adjustTarget(
248256
* @param array $superShares
249257
* @param IStorageFactory $loader
250258
* @param IUser $user
251-
* @return array
259+
* @return array IMountPoint indexed by mount point
252260
* @throws Exception
253261
*/
254262
private function getMountsFromSuperShares(
@@ -261,13 +269,11 @@ private function getMountsFromSuperShares(
261269
$mounts = [];
262270
$view = new View('/' . $userId . '/files');
263271
$ownerViews = [];
264-
$sharingDisabledForUser
265-
= $this->shareManager->sharingDisabledForUser($userId);
272+
$sharingDisabledForUser = $this->shareManager->sharingDisabledForUser($userId);
266273
/** @var CappedMemoryCache<bool> $folderExistCache */
267274
$foldersExistCache = new CappedMemoryCache();
268275

269-
$validShareCache
270-
= $this->cacheFactory->createLocal('share-valid-mountpoint-max');
276+
$validShareCache = $this->cacheFactory->createLocal('share-valid-mountpoint-max');
271277
$maxValidatedShare = $validShareCache->get($userId) ?? 0;
272278
$newMaxValidatedShare = $maxValidatedShare;
273279

@@ -312,12 +318,10 @@ private function getMountsFromSuperShares(
312318
$event = new ShareMountedEvent($mount);
313319
$this->eventDispatcher->dispatchTyped($event);
314320

315-
$mounts[$mount->getMountPoint()]
316-
= $allMounts[$mount->getMountPoint()] = $mount;
321+
$mounts[$mount->getMountPoint()] = $allMounts[$mount->getMountPoint()] = $mount;
317322
foreach ($event->getAdditionalMounts() as $additionalMount) {
318-
$allMounts[$additionalMount->getMountPoint()]
319-
= $mounts[$additionalMount->getMountPoint()]
320-
= $additionalMount;
323+
$mounts[$additionalMount->getMountPoint()] = $additionalMount;
324+
$allMounts[$additionalMount->getMountPoint()] = $additionalMount;
321325
}
322326
} catch (Exception $e) {
323327
$this->logger->error(
@@ -333,24 +337,74 @@ 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 (
353+
$share->getPermissions() > 0
354+
&& $share->getShareOwner() !== $userId
355+
&& $share->getSharedBy() !== $userId
356+
) {
357+
yield $share;
353358
}
359+
}
360+
}
361+
362+
public function getMountsForPath(
363+
string $setupPathHint,
364+
bool $forChildren,
365+
array $mountProviderArgs,
366+
IStorageFactory $loader,
367+
): array {
368+
$limit = -1;
369+
$user = $mountProviderArgs[0]->mountInfo->getUser();
370+
$userId = $user->getUID();
371+
372+
if (!$forChildren) {
373+
// override path with mount point when fetching without children
374+
$setupPathHint = $mountProviderArgs[0]->mountInfo->getMountPoint();
375+
}
376+
377+
// remove /uid/files as the target is stored without
378+
$setupPathHint = \substr($setupPathHint, \strlen('/' . $userId . '/files'));
379+
// remove trailing slash
380+
$setupPathHint = \rtrim($setupPathHint, '/');
381+
382+
// make sure trailing slash is present when loading children
383+
if ($forChildren || $setupPathHint === '') {
384+
$setupPathHint .= '/';
385+
}
386+
387+
$shares = $this->mergeIterables(
388+
$this->shareManager->getSharedWithByPath($userId, IShare::TYPE_USER, $setupPathHint, $forChildren, $limit),
389+
$this->shareManager->getSharedWithByPath($userId, IShare::TYPE_GROUP, $setupPathHint, $forChildren, $limit),
390+
$this->shareManager->getSharedWithByPath($userId, IShare::TYPE_CIRCLE, $setupPathHint, $forChildren, $limit),
391+
$this->shareManager->getSharedWithByPath($userId, IShare::TYPE_ROOM, $setupPathHint, $forChildren, $limit),
392+
$this->shareManager->getSharedWithByPath($userId, IShare::TYPE_DECK, $setupPathHint, $forChildren, $limit),
354393
);
394+
395+
$shares = $this->filterShares($shares, $userId);
396+
$superShares = $this->buildSuperShares($shares, $user);
397+
398+
return $this->getMountsFromSuperShares($userId, $superShares, $loader, $user);
399+
}
400+
401+
/**
402+
* @param iterable ...$iterables
403+
* @return iterable
404+
*/
405+
private function mergeIterables(...$iterables): iterable {
406+
foreach ($iterables as $iterable) {
407+
yield from $iterable;
408+
}
355409
}
356410
}

0 commit comments

Comments
 (0)