Merge pull request #57523 from nextcloud/authoritative-share-provider

Make share mount provider authoritative
This commit is contained in:
Robin Appelman 2026-02-17 13:31:10 +01:00 committed by GitHub
commit 867ef52cfb
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
2 changed files with 32 additions and 12 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;
@ -28,7 +29,7 @@ use Psr\Log\LoggerInterface;
use function count;
class MountProvider implements IMountProvider, IPartialMountProvider {
class MountProvider implements IMountProvider, IAuthoritativeMountProvider, IPartialMountProvider {
public function __construct(
protected readonly IConfig $config,
protected readonly IManager $shareManager,
@ -46,9 +47,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),
@ -58,7 +60,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);
}
@ -318,14 +321,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;
}