Skip to content
Draft
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
4 changes: 4 additions & 0 deletions apps/files/lib/Service/OwnershipTransferService.php
Original file line number Diff line number Diff line change
Expand Up @@ -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;
Expand All @@ -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;
Expand All @@ -52,6 +54,7 @@ public function __construct(
private IUserManager $userManager,
private IFactory $l10nFactory,
private IRootFolder $rootFolder,
private IEventDispatcher $eventDispatcher,
) {
}

Expand Down Expand Up @@ -544,6 +547,7 @@ private function restoreShares(
} catch (\Throwable $e) {
$output->writeln('<error>Could not restore share with id ' . $share->getId() . ':' . $e->getMessage() . ' : ' . $e->getTraceAsString() . '</error>');
}
$this->eventDispatcher->dispatchTyped(new ShareTransferredEvent($share));
$progress->advance();
}
$progress->finish();
Expand Down
4 changes: 2 additions & 2 deletions apps/files_sharing/lib/AppInfo/Application.php
Original file line number Diff line number Diff line change
Expand Up @@ -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;
Expand All @@ -57,6 +56,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;
Expand Down Expand Up @@ -117,10 +117,10 @@ 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);
$context->registerEventListener(FilesystemTornDownEvent::class, SharesUpdatedListener::class);

$context->registerConfigLexicon(ConfigLexicon::class);
}
Expand Down
50 changes: 29 additions & 21 deletions apps/files_sharing/lib/Listener/SharesUpdatedListener.php
Original file line number Diff line number Diff line change
Expand Up @@ -11,73 +11,81 @@
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\Files\Storage\IStorageFactory;
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\Events\ShareTransferredEvent;
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>
* @template-implements IEventListener<UserAddedEvent|UserRemovedEvent|ShareCreatedEvent|ShareTransferredEvent|BeforeShareDeletedEvent|UserShareAccessUpdatedEvent>
*/
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,
private readonly IStorageFactory $storageFactory,
) {
$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);
$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) {
if (
$event instanceof ShareCreatedEvent ||
$event instanceof ShareTransferredEvent
) {
foreach ($this->shareManager->getUsersForShare($event->getShare()) as $user) {
$this->updateForUser($user);
$this->updateForUser($user, true);
}
}
}

private function updateForUser(IUser $user): void {
if (isset($this->updatedUsers[$user->getUID()])) {
return;
if ($event instanceof BeforeShareDeletedEvent) {
foreach ($this->shareManager->getUsersForShare($event->getShare()) as $user) {
$this->updateForUser($user, false, [$event->getShare()]);
}
}
$this->updatedUsers[$user->getUID()] = true;
}

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]);
}
}
}
13 changes: 9 additions & 4 deletions apps/files_sharing/lib/MountProvider.php
Original file line number Diff line number Diff line change
Expand Up @@ -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;
Expand All @@ -28,7 +29,7 @@

use function count;

class MountProvider implements IMountProvider, IPartialMountProvider {
class MountProvider implements IMountProvider, IAuthoritativeMountProvider, IPartialMountProvider {
/**
* @param IConfig $config
* @param IManager $shareManager
Expand Down Expand Up @@ -57,9 +58,10 @@ public function getMountsForUser(IUser $user, IStorageFactory $loader) {

/**
* @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),
Expand All @@ -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);
}

Expand Down Expand Up @@ -340,14 +343,16 @@ public function getMountsFromSuperShares(
* 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;
}
Expand Down
33 changes: 33 additions & 0 deletions lib/public/Share/Events/ShareTransferredEvent.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,33 @@
<?php

declare(strict_types=1);

/**
* SPDX-FileCopyrightText: 2019 Nextcloud GmbH and Nextcloud contributors
* SPDX-License-Identifier: AGPL-3.0-or-later
*/
namespace OCP\Share\Events;

use OCP\EventDispatcher\Event;
use OCP\Share\IShare;

/**
* @since 33.0.0
*/
class ShareTransferredEvent extends Event {
/**
* @since 33.0.0
*/
public function __construct(
private readonly IShare $share
) {
parent::__construct();
}

/**
* @since 33.0.0
*/
public function getShare(): IShare {
return $this->share;
}
}
Loading