Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion apps/files_sharing/appinfo/info.xml
Original file line number Diff line number Diff line change
Expand Up @@ -14,7 +14,7 @@
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.

</description>
<version>1.25.1</version>
<version>1.25.2</version>
<licence>agpl</licence>
<author>Michael Gapczynski</author>
<author>Bjoern Schiessle</author>
Expand Down
53 changes: 52 additions & 1 deletion apps/files_sharing/lib/External/MountProvider.php
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,7 @@
use OCP\DB\QueryBuilder\IQueryBuilder;
use OCP\Federation\ICloudIdManager;
use OCP\Files\Config\IMountProvider;
use OCP\Files\Config\IPartialMountProvider;
use OCP\Files\Storage\IStorageFactory;
use OCP\Http\Client\IClientService;
use OCP\ICertificateManager;
Expand All @@ -21,7 +22,7 @@
use OCP\Server;
use OCP\Share\IShare;

class MountProvider implements IMountProvider {
class MountProvider implements IMountProvider, IPartialMountProvider {
public const STORAGE = ExternalShareStorage::class;

/**
Expand Down Expand Up @@ -69,4 +70,54 @@ public function getMountsForUser(IUser $user, IStorageFactory $loader): array {
$result->closeCursor();
return $mounts;
}

public function getMountsForPath(
string $setupPathHint,
bool $forChildren,
array $mountProviderArgs,
IStorageFactory $loader,
): array {
$user = $mountProviderArgs[0]->mountInfo->getUser();
$userId = $user->getUID();

if (!$forChildren) {
// override path with mount point when fetching without children
$setupPathHint = $mountProviderArgs[0]->mountInfo->getMountPoint();
}

// remove /uid/files as the target is stored without
$setupPathHint = \substr($setupPathHint, \strlen('/' . $userId . '/files'));
// remove trailing slash
$setupPathHint = \rtrim($setupPathHint, '/');

// make sure trailing slash is present when loading children
if ($forChildren || $setupPathHint === '') {
$setupPathHint .= '/';
}

$qb = $this->connection->getQueryBuilder();
$qb->select('id', 'remote', 'share_token', 'password', 'mountpoint', 'owner')
->from('share_external')
->where($qb->expr()->eq('user', $qb->createNamedParameter($user->getUID())))
->andWhere($qb->expr()->eq('accepted', $qb->createNamedParameter(IShare::STATUS_ACCEPTED, IQueryBuilder::PARAM_INT)));

if ($forChildren) {
$qb->andWhere($qb->expr()->like('mountpoint', $qb->createNamedParameter($this->connection->escapeLikeParameter($setupPathHint) . '_%')));
} else {
$qb->andWhere($qb->expr()->eq('mountpoint', $qb->createNamedParameter($setupPathHint)));
}

$result = $qb->executeQuery();

$mounts = [];
while ($row = $result->fetchAssociative()) {
$row['manager'] = $this;
$row['token'] = $row['share_token'];
$mount = $this->getMount($user, $row, $loader);
$mounts[$mount->getMountPoint()] = $mount;
}
$result->closeCursor();

return $mounts;
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -85,6 +85,7 @@ public function changeSchema(IOutput $output, Closure $schemaClosure, array $opt
]);
$table->setPrimaryKey(['id']);
$table->addUniqueIndex(['user', 'mountpoint_hash'], 'sh_external_mp');
$table->addIndex(['user', 'mountpoint'], 'user_mountpoint_index', [], ['lengths' => [null, 128]]);
} else {
$table = $schema->getTable('share_external');
$remoteIdColumn = $table->getColumn('remote_id');
Expand Down
112 changes: 83 additions & 29 deletions apps/files_sharing/lib/MountProvider.php
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,7 @@
use OCP\Cache\CappedMemoryCache;
use OCP\EventDispatcher\IEventDispatcher;
use OCP\Files\Config\IMountProvider;
use OCP\Files\Config\IPartialMountProvider;
use OCP\Files\Mount\IMountManager;
use OCP\Files\Mount\IMountPoint;
use OCP\Files\Storage\IStorageFactory;
Expand All @@ -24,9 +25,10 @@
use OCP\Share\IManager;
use OCP\Share\IShare;
use Psr\Log\LoggerInterface;

use function count;

class MountProvider implements IMountProvider {
class MountProvider implements IMountProvider, IPartialMountProvider {
/**
* @param IConfig $config
* @param IManager $shareManager
Expand All @@ -51,7 +53,7 @@ public function __construct(
*/
public function getMountsForUser(IUser $user, IStorageFactory $loader) {
$userId = $user->getUID();
$shares = array_merge(
$shares = $this->mergeIterables(
$this->shareManager->getSharedWith($userId, IShare::TYPE_USER, null, -1),
$this->shareManager->getSharedWith($userId, IShare::TYPE_GROUP, null, -1),
$this->shareManager->getSharedWith($userId, IShare::TYPE_CIRCLE, null, -1),
Expand All @@ -62,17 +64,24 @@ public function getMountsForUser(IUser $user, IStorageFactory $loader) {
$shares = $this->filterShares($shares, $userId);
$superShares = $this->buildSuperShares($shares, $user);

return $this->getMountsFromSuperShares($userId, $superShares, $loader, $user);
return array_values(
$this->getMountsFromSuperShares(
$userId,
$superShares,
$loader,
$user,
),
);
}

/**
* Groups shares by path (nodeId) and target path
*
* @param IShare[] $shares
* @param iterable<IShare> $shares
* @return IShare[][] array of grouped shares, each element in the
* array is a group which itself is an array of shares
*/
private function groupShares(array $shares) {
private function groupShares(iterable $shares): array {
$tmp = [];

foreach ($shares as $share) {
Expand Down Expand Up @@ -108,11 +117,11 @@ private function groupShares(array $shares) {
* the shares in the group, forming the most permissive combination
* possible.
*
* @param IShare[] $allShares
* @param iterable<IShare> $allShares
* @param IUser $user user
* @return list<array{IShare, array<IShare>}> Tuple of [superShare, groupedShares]
*/
private function buildSuperShares(array $allShares, IUser $user) {
private function buildSuperShares(iterable $allShares, IUser $user): array {
$result = [];

$groupedShares = $this->groupShares($allShares);
Expand Down Expand Up @@ -237,8 +246,7 @@ private function adjustTarget(
// null groups which usually appear with group backend
// caching inconsistencies
$this->logger->debug(
'Could not adjust share target for share ' . $share->getId(
) . ' to make it consistent: ' . $e->getMessage(),
'Could not adjust share target for share ' . $share->getId() . ' to make it consistent: ' . $e->getMessage(),
['app' => 'files_sharing']
);
}
Expand All @@ -248,7 +256,7 @@ private function adjustTarget(
* @param array $superShares
* @param IStorageFactory $loader
* @param IUser $user
* @return array
* @return array IMountPoint indexed by mount point
* @throws Exception
*/
private function getMountsFromSuperShares(
Expand All @@ -261,13 +269,11 @@ private function getMountsFromSuperShares(
$mounts = [];
$view = new View('/' . $userId . '/files');
$ownerViews = [];
$sharingDisabledForUser
= $this->shareManager->sharingDisabledForUser($userId);
$sharingDisabledForUser = $this->shareManager->sharingDisabledForUser($userId);
/** @var CappedMemoryCache<bool> $folderExistCache */
$foldersExistCache = new CappedMemoryCache();

$validShareCache
= $this->cacheFactory->createLocal('share-valid-mountpoint-max');
$validShareCache = $this->cacheFactory->createLocal('share-valid-mountpoint-max');
$maxValidatedShare = $validShareCache->get($userId) ?? 0;
$newMaxValidatedShare = $maxValidatedShare;

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

$mounts[$mount->getMountPoint()]
= $allMounts[$mount->getMountPoint()] = $mount;
$mounts[$mount->getMountPoint()] = $allMounts[$mount->getMountPoint()] = $mount;
foreach ($event->getAdditionalMounts() as $additionalMount) {
$allMounts[$additionalMount->getMountPoint()]
= $mounts[$additionalMount->getMountPoint()]
= $additionalMount;
$mounts[$additionalMount->getMountPoint()] = $additionalMount;
$allMounts[$additionalMount->getMountPoint()] = $additionalMount;
}
} catch (Exception $e) {
$this->logger->error(
Expand All @@ -333,24 +337,74 @@ private function getMountsFromSuperShares(
$validShareCache->set($userId, $newMaxValidatedShare, 24 * 60 * 60);

// array_filter removes the null values from the array
return array_values(array_filter($mounts));
return array_filter($mounts);
}

/**
* Filters out shares owned or shared by the user and ones for which the
* user has no permissions.
*
* @param IShare[] $shares
* @return IShare[]
* @param iterable<IShare> $shares
* @return iterable<IShare>
*/
private function filterShares(array $shares, string $userId): array {
return array_filter(
$shares,
static function (IShare $share) use ($userId) {
return $share->getPermissions() > 0
&& $share->getShareOwner() !== $userId
&& $share->getSharedBy() !== $userId;
private function filterShares(iterable $shares, string $userId): iterable {
foreach ($shares as $share) {
if (
$share->getPermissions() > 0
&& $share->getShareOwner() !== $userId
&& $share->getSharedBy() !== $userId
) {
yield $share;
}
}
}

public function getMountsForPath(
string $setupPathHint,
bool $forChildren,
array $mountProviderArgs,
IStorageFactory $loader,
): array {
$limit = -1;
$user = $mountProviderArgs[0]->mountInfo->getUser();
$userId = $user->getUID();

if (!$forChildren) {
// override path with mount point when fetching without children
$setupPathHint = $mountProviderArgs[0]->mountInfo->getMountPoint();
}

// remove /uid/files as the target is stored without
$setupPathHint = \substr($setupPathHint, \strlen('/' . $userId . '/files'));
// remove trailing slash
$setupPathHint = \rtrim($setupPathHint, '/');

// make sure trailing slash is present when loading children
if ($forChildren || $setupPathHint === '') {
$setupPathHint .= '/';
}

$shares = $this->mergeIterables(
$this->shareManager->getSharedWithByPath($userId, IShare::TYPE_USER, $setupPathHint, $forChildren, $limit),
$this->shareManager->getSharedWithByPath($userId, IShare::TYPE_GROUP, $setupPathHint, $forChildren, $limit),
$this->shareManager->getSharedWithByPath($userId, IShare::TYPE_CIRCLE, $setupPathHint, $forChildren, $limit),
$this->shareManager->getSharedWithByPath($userId, IShare::TYPE_ROOM, $setupPathHint, $forChildren, $limit),
$this->shareManager->getSharedWithByPath($userId, IShare::TYPE_DECK, $setupPathHint, $forChildren, $limit),
);

$shares = $this->filterShares($shares, $userId);
$superShares = $this->buildSuperShares($shares, $user);

return $this->getMountsFromSuperShares($userId, $superShares, $loader, $user);
}

/**
* @param iterable ...$iterables
* @return iterable
*/
private function mergeIterables(...$iterables): iterable {
foreach ($iterables as $iterable) {
yield from $iterable;
}
}
}
Loading
Loading