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: 2 additions & 0 deletions apps/files_sharing/composer/composer/autoload_classmap.php
Original file line number Diff line number Diff line change
Expand Up @@ -70,6 +70,7 @@
'OCA\\Files_Sharing\\Listener\\LoadPublicFileRequestAuthListener' => $baseDir . '/../lib/Listener/LoadPublicFileRequestAuthListener.php',
'OCA\\Files_Sharing\\Listener\\LoadSidebarListener' => $baseDir . '/../lib/Listener/LoadSidebarListener.php',
'OCA\\Files_Sharing\\Listener\\ShareInteractionListener' => $baseDir . '/../lib/Listener/ShareInteractionListener.php',
'OCA\\Files_Sharing\\Listener\\SharesUpdatedListener' => $baseDir . '/../lib/Listener/SharesUpdatedListener.php',
'OCA\\Files_Sharing\\Listener\\UserAddedToGroupListener' => $baseDir . '/../lib/Listener/UserAddedToGroupListener.php',
'OCA\\Files_Sharing\\Listener\\UserShareAcceptanceListener' => $baseDir . '/../lib/Listener/UserShareAcceptanceListener.php',
'OCA\\Files_Sharing\\Middleware\\OCSShareAPIMiddleware' => $baseDir . '/../lib/Middleware/OCSShareAPIMiddleware.php',
Expand All @@ -96,6 +97,7 @@
'OCA\\Files_Sharing\\Settings\\Personal' => $baseDir . '/../lib/Settings/Personal.php',
'OCA\\Files_Sharing\\ShareBackend\\File' => $baseDir . '/../lib/ShareBackend/File.php',
'OCA\\Files_Sharing\\ShareBackend\\Folder' => $baseDir . '/../lib/ShareBackend/Folder.php',
'OCA\\Files_Sharing\\ShareTargetValidator' => $baseDir . '/../lib/ShareTargetValidator.php',
'OCA\\Files_Sharing\\SharedMount' => $baseDir . '/../lib/SharedMount.php',
'OCA\\Files_Sharing\\SharedStorage' => $baseDir . '/../lib/SharedStorage.php',
'OCA\\Files_Sharing\\SharesReminderJob' => $baseDir . '/../lib/SharesReminderJob.php',
Expand Down
2 changes: 2 additions & 0 deletions apps/files_sharing/composer/composer/autoload_static.php
Original file line number Diff line number Diff line change
Expand Up @@ -85,6 +85,7 @@ class ComposerStaticInitFiles_Sharing
'OCA\\Files_Sharing\\Listener\\LoadPublicFileRequestAuthListener' => __DIR__ . '/..' . '/../lib/Listener/LoadPublicFileRequestAuthListener.php',
'OCA\\Files_Sharing\\Listener\\LoadSidebarListener' => __DIR__ . '/..' . '/../lib/Listener/LoadSidebarListener.php',
'OCA\\Files_Sharing\\Listener\\ShareInteractionListener' => __DIR__ . '/..' . '/../lib/Listener/ShareInteractionListener.php',
'OCA\\Files_Sharing\\Listener\\SharesUpdatedListener' => __DIR__ . '/..' . '/../lib/Listener/SharesUpdatedListener.php',
'OCA\\Files_Sharing\\Listener\\UserAddedToGroupListener' => __DIR__ . '/..' . '/../lib/Listener/UserAddedToGroupListener.php',
'OCA\\Files_Sharing\\Listener\\UserShareAcceptanceListener' => __DIR__ . '/..' . '/../lib/Listener/UserShareAcceptanceListener.php',
'OCA\\Files_Sharing\\Middleware\\OCSShareAPIMiddleware' => __DIR__ . '/..' . '/../lib/Middleware/OCSShareAPIMiddleware.php',
Expand All @@ -111,6 +112,7 @@ class ComposerStaticInitFiles_Sharing
'OCA\\Files_Sharing\\Settings\\Personal' => __DIR__ . '/..' . '/../lib/Settings/Personal.php',
'OCA\\Files_Sharing\\ShareBackend\\File' => __DIR__ . '/..' . '/../lib/ShareBackend/File.php',
'OCA\\Files_Sharing\\ShareBackend\\Folder' => __DIR__ . '/..' . '/../lib/ShareBackend/Folder.php',
'OCA\\Files_Sharing\\ShareTargetValidator' => __DIR__ . '/..' . '/../lib/ShareTargetValidator.php',
'OCA\\Files_Sharing\\SharedMount' => __DIR__ . '/..' . '/../lib/SharedMount.php',
'OCA\\Files_Sharing\\SharedStorage' => __DIR__ . '/..' . '/../lib/SharedStorage.php',
'OCA\\Files_Sharing\\SharesReminderJob' => __DIR__ . '/..' . '/../lib/SharesReminderJob.php',
Expand Down
13 changes: 13 additions & 0 deletions apps/files_sharing/lib/AppInfo/Application.php
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,7 @@
use OCA\Files\Event\LoadSidebar;
use OCA\Files_Sharing\Capabilities;
use OCA\Files_Sharing\Config\ConfigLexicon;
use OCA\Files_Sharing\Event\UserShareAccessUpdatedEvent;
use OCA\Files_Sharing\External\Manager;
use OCA\Files_Sharing\External\MountProvider as ExternalMountProvider;
use OCA\Files_Sharing\Helper;
Expand All @@ -24,6 +25,7 @@
use OCA\Files_Sharing\Listener\LoadPublicFileRequestAuthListener;
use OCA\Files_Sharing\Listener\LoadSidebarListener;
use OCA\Files_Sharing\Listener\ShareInteractionListener;
use OCA\Files_Sharing\Listener\SharesUpdatedListener;
use OCA\Files_Sharing\Listener\UserAddedToGroupListener;
use OCA\Files_Sharing\Listener\UserShareAcceptanceListener;
use OCA\Files_Sharing\Middleware\OCSShareAPIMiddleware;
Expand All @@ -46,11 +48,14 @@
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;
use OCP\Group\Events\UserRemovedEvent;
use OCP\IDBConnection;
use OCP\IGroup;
use OCP\Share\Events\BeforeShareDeletedEvent;
use OCP\Share\Events\ShareCreatedEvent;
use OCP\User\Events\UserChangedEvent;
use OCP\User\Events\UserDeletedEvent;
Expand Down Expand Up @@ -109,6 +114,14 @@ function () use ($c) {
// File request auth
$context->registerEventListener(BeforeTemplateRenderedEvent::class, LoadPublicFileRequestAuthListener::class);

// Update mounts
$context->registerEventListener(ShareCreatedEvent::class, SharesUpdatedListener::class);
$context->registerEventListener(BeforeShareDeletedEvent::class, SharesUpdatedListener::class);
$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);
}

Expand Down
83 changes: 83 additions & 0 deletions apps/files_sharing/lib/Listener/SharesUpdatedListener.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,83 @@
<?php

declare(strict_types=1);
/**
* SPDX-FileCopyrightText: 2025 Robin Appelman <robin@icewind.nl>
* SPDX-License-Identifier: AGPL-3.0-or-later
*/

namespace OCA\Files_Sharing\Listener;

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;
use OCP\Share\Events\BeforeShareDeletedEvent;
use OCP\Share\Events\ShareCreatedEvent;
use OCP\Share\IManager;

/**
* Listen to various events that can change what shares a user has access to
*
* @template-implements IEventListener<UserAddedEvent|UserRemovedEvent|ShareCreatedEvent|BeforeShareDeletedEvent|UserShareAccessUpdatedEvent|FilesystemTornDownEvent>
*/
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);
}
}
if ($event instanceof UserAddedEvent || $event instanceof UserRemovedEvent) {
$this->updateForUser($event->getUser());
}
if ($event instanceof ShareCreatedEvent || $event instanceof BeforeShareDeletedEvent) {
foreach ($this->shareManager->getUsersForShare($event->getShare()) as $user) {
$this->updateForUser($user);
}
}
}

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);

$shares = $this->shareMountProvider->getSuperSharesForUser($user);
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Am I right that getSuperSharesForUser() and verifyMountPoint() could take a lot of time?
As this been tested on next.perftesting? Like adding a user, or a group to a talk conversation for example?

I am wondering if we should find a way to put that into a background job.

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This is work that was previously done on-mount, so while it can be expensive yes, it's moving the expense from "read" (with some caching) to "write" time.

Putting it in a background job would mean that we can end up mounting the share before we did the conflict resolution. (Unless we do the work to block mounting the share until then)


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);
}
}
}
}
33 changes: 14 additions & 19 deletions apps/files_sharing/lib/MountProvider.php
Original file line number Diff line number Diff line change
Expand Up @@ -52,6 +52,14 @@ public function __construct(
* @return IMountPoint[]
*/
public function getMountsForUser(IUser $user, IStorageFactory $loader) {
return array_values($this->getMountsFromSuperShares($user, $this->getSuperSharesForUser($user), $loader));
}

/**
* @param IUser $user
* @return list<array{IShare, array<IShare>}> Tuple of [superShare, groupedShares]
*/
public function getSuperSharesForUser(IUser $user): array {
$userId = $user->getUID();
$shares = $this->mergeIterables(
$this->shareManager->getSharedWith($userId, IShare::TYPE_USER, null, -1),
Expand All @@ -62,16 +70,7 @@ public function getMountsForUser(IUser $user, IStorageFactory $loader) {
);

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

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

/**
Expand Down Expand Up @@ -253,18 +252,18 @@ private function adjustTarget(
}
/**
* @param string $userId
* @param array $superShares
* @param list<array{IShare, array<IShare>}> $superShares
* @param IStorageFactory $loader
* @param IUser $user
* @return array IMountPoint indexed by mount point
* @throws Exception
*/
private function getMountsFromSuperShares(
string $userId,
public function getMountsFromSuperShares(
IUser $user,
array $superShares,
IStorageFactory $loader,
IUser $user,
): array {
$userId = $user->getUID();
$allMounts = $this->mountManager->getAll();
$mounts = [];
$view = new View('/' . $userId . '/files');
Expand Down Expand Up @@ -295,7 +294,6 @@ private function getMountsFromSuperShares(
$shareId = (int)$parentShare->getId();
$mount = new SharedMount(
'\OCA\Files_Sharing\SharedStorage',
$allMounts,
[
'user' => $userId,
// parent share
Expand All @@ -306,11 +304,8 @@ private function getMountsFromSuperShares(
'sharingDisabledForUser' => $sharingDisabledForUser
],
$loader,
$view,
$foldersExistCache,
$this->eventDispatcher,
$user,
$shareId <= $maxValidatedShare,
);

$newMaxValidatedShare = max($shareId, $newMaxValidatedShare);
Expand Down Expand Up @@ -395,7 +390,7 @@ public function getMountsForPath(
$shares = $this->filterShares($shares, $userId);
$superShares = $this->buildSuperShares($shares, $user);

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

/**
Expand Down
Loading
Loading