From dc3bc01e63a5f3c17a3cb3600af75c4393517333 Mon Sep 17 00:00:00 2001 From: Robin Appelman Date: Thu, 22 Jan 2026 19:15:29 +0100 Subject: [PATCH 1/3] feat: add event for tranfered shares Signed-off-by: Robin Appelman --- .../lib/Service/OwnershipTransferService.php | 4 +++ .../files_sharing/lib/AppInfo/Application.php | 2 ++ .../lib/Listener/SharesUpdatedListener.php | 9 +++-- .../Share/Events/ShareTransferredEvent.php | 33 +++++++++++++++++++ 4 files changed, 46 insertions(+), 2 deletions(-) create mode 100644 lib/public/Share/Events/ShareTransferredEvent.php diff --git a/apps/files/lib/Service/OwnershipTransferService.php b/apps/files/lib/Service/OwnershipTransferService.php index e4a4e8f595aef..581d5a1294801 100644 --- a/apps/files/lib/Service/OwnershipTransferService.php +++ b/apps/files/lib/Service/OwnershipTransferService.php @@ -18,6 +18,7 @@ use OCA\Files\Exception\TransferOwnershipException; use OCA\Files_External\Config\ConfigAdapter; use OCP\Encryption\IManager as IEncryptionManager; +use OCP\EventDispatcher\IEventDispatcher; use OCP\Files\Config\IHomeMountProvider; use OCP\Files\Config\IUserMountCache; use OCP\Files\File; @@ -30,6 +31,7 @@ use OCP\IUserManager; use OCP\L10N\IFactory; use OCP\Server; +use OCP\Share\Events\ShareTransferredEvent; use OCP\Share\IManager as IShareManager; use OCP\Share\IShare; use Symfony\Component\Console\Helper\ProgressBar; @@ -52,6 +54,7 @@ public function __construct( private IUserManager $userManager, private IFactory $l10nFactory, private IRootFolder $rootFolder, + private IEventDispatcher $eventDispatcher, ) { } @@ -544,6 +547,7 @@ private function restoreShares( } catch (\Throwable $e) { $output->writeln('Could not restore share with id ' . $share->getId() . ':' . $e->getMessage() . ' : ' . $e->getTraceAsString() . ''); } + $this->eventDispatcher->dispatchTyped(new ShareTransferredEvent($share)); $progress->advance(); } $progress->finish(); diff --git a/apps/files_sharing/lib/AppInfo/Application.php b/apps/files_sharing/lib/AppInfo/Application.php index 4f0848b60bd33..c760994f1642f 100644 --- a/apps/files_sharing/lib/AppInfo/Application.php +++ b/apps/files_sharing/lib/AppInfo/Application.php @@ -57,6 +57,7 @@ use OCP\IGroup; use OCP\Share\Events\BeforeShareDeletedEvent; use OCP\Share\Events\ShareCreatedEvent; +use OCP\Share\Events\ShareTransferredEvent; use OCP\User\Events\UserChangedEvent; use OCP\User\Events\UserDeletedEvent; use OCP\Util; @@ -117,6 +118,7 @@ function () use ($c) { // Update mounts $context->registerEventListener(ShareCreatedEvent::class, SharesUpdatedListener::class); $context->registerEventListener(BeforeShareDeletedEvent::class, SharesUpdatedListener::class); + $context->registerEventListener(ShareTransferredEvent::class, SharesUpdatedListener::class); $context->registerEventListener(UserAddedEvent::class, SharesUpdatedListener::class); $context->registerEventListener(UserRemovedEvent::class, SharesUpdatedListener::class); $context->registerEventListener(UserShareAccessUpdatedEvent::class, SharesUpdatedListener::class); diff --git a/apps/files_sharing/lib/Listener/SharesUpdatedListener.php b/apps/files_sharing/lib/Listener/SharesUpdatedListener.php index b7b85689f8ad7..6987627cb50ed 100644 --- a/apps/files_sharing/lib/Listener/SharesUpdatedListener.php +++ b/apps/files_sharing/lib/Listener/SharesUpdatedListener.php @@ -22,12 +22,13 @@ use OCP\IUser; use OCP\Share\Events\BeforeShareDeletedEvent; use OCP\Share\Events\ShareCreatedEvent; +use OCP\Share\Events\ShareTransferredEvent; use OCP\Share\IManager; /** * Listen to various events that can change what shares a user has access to * - * @template-implements IEventListener + * @template-implements IEventListener */ class SharesUpdatedListener implements IEventListener { private CappedMemoryCache $updatedUsers; @@ -52,7 +53,11 @@ public function handle(Event $event): void { if ($event instanceof UserAddedEvent || $event instanceof UserRemovedEvent) { $this->updateForUser($event->getUser()); } - if ($event instanceof ShareCreatedEvent || $event instanceof BeforeShareDeletedEvent) { + if ( + $event instanceof ShareCreatedEvent || + $event instanceof BeforeShareDeletedEvent || + $event instanceof ShareTransferredEvent + ) { foreach ($this->shareManager->getUsersForShare($event->getShare()) as $user) { $this->updateForUser($user); } diff --git a/lib/public/Share/Events/ShareTransferredEvent.php b/lib/public/Share/Events/ShareTransferredEvent.php new file mode 100644 index 0000000000000..d5964b780b156 --- /dev/null +++ b/lib/public/Share/Events/ShareTransferredEvent.php @@ -0,0 +1,33 @@ +share; + } +} From b3fc17ed3f7c1313072a294fdde66dc7956f023a Mon Sep 17 00:00:00 2001 From: Robin Appelman Date: Thu, 22 Jan 2026 19:30:19 +0100 Subject: [PATCH 2/3] fix: remove validate-user-shares-once optimization Signed-off-by: Robin Appelman --- apps/files_sharing/lib/AppInfo/Application.php | 2 -- .../lib/Listener/SharesUpdatedListener.php | 15 +-------------- 2 files changed, 1 insertion(+), 16 deletions(-) diff --git a/apps/files_sharing/lib/AppInfo/Application.php b/apps/files_sharing/lib/AppInfo/Application.php index c760994f1642f..da4984d378533 100644 --- a/apps/files_sharing/lib/AppInfo/Application.php +++ b/apps/files_sharing/lib/AppInfo/Application.php @@ -48,7 +48,6 @@ use OCP\Files\Events\BeforeDirectFileDownloadEvent; use OCP\Files\Events\BeforeZipCreatedEvent; use OCP\Files\Events\Node\BeforeNodeReadEvent; -use OCP\Files\Events\Node\FilesystemTornDownEvent; use OCP\Group\Events\GroupChangedEvent; use OCP\Group\Events\GroupDeletedEvent; use OCP\Group\Events\UserAddedEvent; @@ -122,7 +121,6 @@ function () use ($c) { $context->registerEventListener(UserAddedEvent::class, SharesUpdatedListener::class); $context->registerEventListener(UserRemovedEvent::class, SharesUpdatedListener::class); $context->registerEventListener(UserShareAccessUpdatedEvent::class, SharesUpdatedListener::class); - $context->registerEventListener(FilesystemTornDownEvent::class, SharesUpdatedListener::class); $context->registerConfigLexicon(ConfigLexicon::class); } diff --git a/apps/files_sharing/lib/Listener/SharesUpdatedListener.php b/apps/files_sharing/lib/Listener/SharesUpdatedListener.php index 6987627cb50ed..591fd92b477c6 100644 --- a/apps/files_sharing/lib/Listener/SharesUpdatedListener.php +++ b/apps/files_sharing/lib/Listener/SharesUpdatedListener.php @@ -11,12 +11,10 @@ use OCA\Files_Sharing\Event\UserShareAccessUpdatedEvent; use OCA\Files_Sharing\MountProvider; use OCA\Files_Sharing\ShareTargetValidator; -use OCP\Cache\CappedMemoryCache; use OCP\EventDispatcher\Event; use OCP\EventDispatcher\IEventListener; use OCP\Files\Config\ICachedMountInfo; use OCP\Files\Config\IUserMountCache; -use OCP\Files\Events\Node\FilesystemTornDownEvent; use OCP\Group\Events\UserAddedEvent; use OCP\Group\Events\UserRemovedEvent; use OCP\IUser; @@ -28,23 +26,17 @@ /** * Listen to various events that can change what shares a user has access to * - * @template-implements IEventListener + * @template-implements IEventListener */ class SharesUpdatedListener implements IEventListener { - private CappedMemoryCache $updatedUsers; - public function __construct( private readonly IManager $shareManager, private readonly IUserMountCache $userMountCache, private readonly MountProvider $shareMountProvider, private readonly ShareTargetValidator $shareTargetValidator, ) { - $this->updatedUsers = new CappedMemoryCache(); } public function handle(Event $event): void { - if ($event instanceof FilesystemTornDownEvent) { - $this->updatedUsers = new CappedMemoryCache(); - } if ($event instanceof UserShareAccessUpdatedEvent) { foreach ($event->getUsers() as $user) { $this->updateForUser($user); @@ -65,11 +57,6 @@ public function handle(Event $event): void { } private function updateForUser(IUser $user): void { - if (isset($this->updatedUsers[$user->getUID()])) { - return; - } - $this->updatedUsers[$user->getUID()] = true; - $cachedMounts = $this->userMountCache->getMountsForUser($user); $mountPoints = array_map(fn (ICachedMountInfo $mount) => $mount->getMountPoint(), $cachedMounts); $mountsByPath = array_combine($mountPoints, $cachedMounts); From 0ecb616f8370a441f93f06d30f07df4624f05421 Mon Sep 17 00:00:00 2001 From: Robin Appelman Date: Tue, 13 Jan 2026 15:14:36 +0100 Subject: [PATCH 3/3] feat: implement authoritative mount provider for share provider Signed-off-by: Robin Appelman --- .../lib/Listener/SharesUpdatedListener.php | 30 ++++++++++++++----- apps/files_sharing/lib/MountProvider.php | 13 +++++--- 2 files changed, 32 insertions(+), 11 deletions(-) diff --git a/apps/files_sharing/lib/Listener/SharesUpdatedListener.php b/apps/files_sharing/lib/Listener/SharesUpdatedListener.php index 591fd92b477c6..9cbea36dbaa56 100644 --- a/apps/files_sharing/lib/Listener/SharesUpdatedListener.php +++ b/apps/files_sharing/lib/Listener/SharesUpdatedListener.php @@ -15,6 +15,7 @@ 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; @@ -34,42 +35,57 @@ public function __construct( 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 { $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]); + } } } diff --git a/apps/files_sharing/lib/MountProvider.php b/apps/files_sharing/lib/MountProvider.php index e9575dbac26c3..befb4fd0603a7 100644 --- a/apps/files_sharing/lib/MountProvider.php +++ b/apps/files_sharing/lib/MountProvider.php @@ -13,6 +13,7 @@ use OCA\Files_Sharing\Event\ShareMountedEvent; use OCP\Cache\CappedMemoryCache; 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 function count; -class MountProvider implements IMountProvider, IPartialMountProvider { +class MountProvider implements IMountProvider, IAuthoritativeMountProvider, IPartialMountProvider { /** * @param IConfig $config * @param IManager $shareManager @@ -57,9 +58,10 @@ public function getMountsForUser(IUser $user, IStorageFactory $loader) { /** * @param IUser $user + * @param list $excludeShares * @return list}> 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), @@ -69,7 +71,8 @@ public function getSuperSharesForUser(IUser $user): array { $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); } @@ -340,14 +343,16 @@ public function getMountsFromSuperShares( * user has no permissions. * * @param iterable $shares + * @param list $excludeShareIds * @return iterable */ - 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; }