feat: implement authoritative mount provider for share provider

Signed-off-by: Robin Appelman <robin@icewind.nl>
This commit is contained in:
Robin Appelman 2026-01-13 15:14:36 +01:00 committed by Louis Chmn
parent 563ff9f431
commit d718d68e26
3 changed files with 38 additions and 21 deletions

View file

@ -15,6 +15,7 @@ use OCP\EventDispatcher\Event;
use OCP\EventDispatcher\IEventListener;
use OCP\Files\Config\ICachedMountInfo;
use OCP\Files\Config\IUserMountCache;
use OCP\Files\Storage\IStorageFactory;
use OCP\Group\Events\UserAddedEvent;
use OCP\Group\Events\UserRemovedEvent;
use OCP\IUser;
@ -36,50 +37,64 @@ class SharesUpdatedListener implements IEventListener {
private readonly IUserMountCache $userMountCache,
private readonly MountProvider $shareMountProvider,
private readonly ShareTargetValidator $shareTargetValidator,
private readonly IStorageFactory $storageFactory,
) {
}
public function handle(Event $event): void {
if ($event instanceof UserShareAccessUpdatedEvent) {
foreach ($event->getUsers() as $user) {
$this->updateForUser($user);
$this->updateForUser($user, true);
}
}
if ($event instanceof UserAddedEvent || $event instanceof UserRemovedEvent) {
$this->updateForUser($event->getUser());
$this->updateForUser($event->getUser(), true);
}
if (
$event instanceof ShareCreatedEvent
|| $event instanceof BeforeShareDeletedEvent
|| $event instanceof ShareTransferredEvent
) {
foreach ($this->shareManager->getUsersForShare($event->getShare()) as $user) {
$this->updateForUser($user);
$this->updateForUser($user, true);
}
}
if ($event instanceof BeforeShareDeletedEvent) {
foreach ($this->shareManager->getUsersForShare($event->getShare()) as $user) {
$this->updateForUser($user, false, [$event->getShare()]);
}
}
}
private function updateForUser(IUser $user): void {
private function updateForUser(IUser $user, bool $verifyMountPoints, array $ignoreShares = []): void {
// prevent recursion
if (isset($this->inUpdate[$user->getUID()])) {
return;
}
$this->inUpdate[$user->getUID()] = true;
$cachedMounts = $this->userMountCache->getMountsForUser($user);
$shareMounts = array_filter($cachedMounts, fn (ICachedMountInfo $mount) => $mount->getMountProvider() === MountProvider::class);
$mountPoints = array_map(fn (ICachedMountInfo $mount) => $mount->getMountPoint(), $cachedMounts);
$mountsByPath = array_combine($mountPoints, $cachedMounts);
$shares = $this->shareMountProvider->getSuperSharesForUser($user);
$shares = $this->shareMountProvider->getSuperSharesForUser($user, $ignoreShares);
$mountsChanged = count($shares) !== count($shareMounts);
foreach ($shares as &$share) {
[$parentShare, $groupedShares] = $share;
$mountPoint = '/' . $user->getUID() . '/files/' . trim($parentShare->getTarget(), '/') . '/';
$mountKey = $parentShare->getNodeId() . '::' . $mountPoint;
if (!isset($cachedMounts[$mountKey])) {
$this->shareTargetValidator->verifyMountPoint($user, $parentShare, $mountsByPath, $groupedShares);
$mountsChanged = true;
if ($verifyMountPoints) {
$this->shareTargetValidator->verifyMountPoint($user, $parentShare, $mountsByPath, $groupedShares);
}
}
}
if ($mountsChanged) {
$newMounts = $this->shareMountProvider->getMountsFromSuperShares($user, $shares, $this->storageFactory);
$this->userMountCache->registerMounts($user, $newMounts, [MountProvider::class]);
}
unset($this->inUpdate[$user->getUID()]);
}
}

View file

@ -12,6 +12,7 @@ use InvalidArgumentException;
use OC\Files\View;
use OCA\Files_Sharing\Event\ShareMountedEvent;
use OCP\EventDispatcher\IEventDispatcher;
use OCP\Files\Config\IAuthoritativeMountProvider;
use OCP\Files\Config\IMountProvider;
use OCP\Files\Config\IPartialMountProvider;
use OCP\Files\Mount\IMountManager;
@ -27,7 +28,7 @@ use Psr\Log\LoggerInterface;
use function count;
class MountProvider implements IMountProvider, IPartialMountProvider {
class MountProvider implements IMountProvider, IAuthoritativeMountProvider, IPartialMountProvider {
/**
* @param IConfig $config
@ -58,9 +59,10 @@ class MountProvider implements IMountProvider, IPartialMountProvider {
/**
* @param IUser $user
* @param list<IShare> $excludeShares
* @return list<array{IShare, array<IShare>}> Tuple of [superShare, groupedShares]
*/
public function getSuperSharesForUser(IUser $user): array {
public function getSuperSharesForUser(IUser $user, array $excludeShares = []): array {
$userId = $user->getUID();
$shares = $this->mergeIterables(
$this->shareManager->getSharedWith($userId, IShare::TYPE_USER, null, -1),
@ -70,7 +72,8 @@ class MountProvider implements IMountProvider, IPartialMountProvider {
$this->shareManager->getSharedWith($userId, IShare::TYPE_DECK, null, -1),
);
$shares = $this->filterShares($shares, $userId);
$excludeShareIds = array_map(fn (IShare $share) => $share->getFullId(), $excludeShares);
$shares = $this->filterShares($shares, $userId, $excludeShareIds);
return $this->buildSuperShares($shares, $user);
}
@ -292,12 +295,6 @@ class MountProvider implements IMountProvider, IPartialMountProvider {
}
$shareId = (int)$parentShare->getId();
$absMountPoint = '/' . $user->getUID() . '/files/' . trim($parentShare->getTarget(), '/') . '/';
// after the mountpoint is verified for the first time, only new mountpoints (e.g. groupfolders can overwrite the target)
if ($shareId > $maxValidatedShare || isset($allMounts[$absMountPoint])) {
$this->shareTargetValidator->verifyMountPoint($user, $parentShare, $allMounts, $groupedShares);
}
$mount = new SharedMount(
'\OCA\Files_Sharing\SharedStorage',
@ -347,14 +344,16 @@ class MountProvider implements IMountProvider, IPartialMountProvider {
* user has no permissions.
*
* @param iterable<IShare> $shares
* @param list<string> $excludeShareIds
* @return iterable<IShare>
*/
private function filterShares(iterable $shares, string $userId): iterable {
private function filterShares(iterable $shares, string $userId, array $excludeShareIds = []): iterable {
foreach ($shares as $share) {
if (
$share->getPermissions() > 0
&& $share->getShareOwner() !== $userId
&& $share->getSharedBy() !== $userId
&& !in_array($share->getFullId(), $excludeShareIds)
) {
yield $share;
}

View file

@ -11,8 +11,9 @@ namespace OCA\Files_Sharing\Repair;
use OC\Files\SetupManager;
use OCA\Files_Sharing\ShareTargetValidator;
use OCP\DB\QueryBuilder\IQueryBuilder;
use OCP\Files\Config\ICachedMountInfo;
use OCP\Files\Config\IUserMountCache;
use OCP\Files\IRootFolder;
use OCP\Files\Mount\IMountManager;
use OCP\Files\NotFoundException;
use OCP\ICacheFactory;
use OCP\IDBConnection;
@ -42,7 +43,7 @@ class CleanupShareTarget implements IRepairStep {
private readonly ShareTargetValidator $shareTargetValidator,
private readonly IUserManager $userManager,
private readonly SetupManager $setupManager,
private readonly IMountManager $mountManager,
private readonly IUserMountCache $userMountCache,
private readonly IRootFolder $rootFolder,
private readonly LoggerInterface $logger,
private readonly ICacheFactory $cacheFactory,
@ -85,7 +86,9 @@ class CleanupShareTarget implements IRepairStep {
$this->setupManager->tearDown();
$this->setupManager->setupForUser($recipient);
$userMounts = $this->mountManager->getAll();
$mounts = $this->userMountCache->getMountsForUser($recipient);
$mountPoints = array_map(fn (ICachedMountInfo $mount) => $mount->getMountPoint(), $mounts);
$userMounts = array_combine($mountPoints, $mounts);
$userFolder = $this->rootFolder->getUserFolder($recipient->getUID());
}