diff --git a/apps/federatedfilesharing/lib/OCM/CloudFederationProviderFiles.php b/apps/federatedfilesharing/lib/OCM/CloudFederationProviderFiles.php index 132b81e468688..91826d610ca51 100644 --- a/apps/federatedfilesharing/lib/OCM/CloudFederationProviderFiles.php +++ b/apps/federatedfilesharing/lib/OCM/CloudFederationProviderFiles.php @@ -209,18 +209,25 @@ public function shareReceived(ICloudFederationShare $share): string { } } + $autoAccept = $this->federatedShareProvider->isFederatedTrustedShareAutoAccept() && $trustedServers?->isTrustedServer($remote) === true; + // Auto-accepted shares show up in "Shared with you", the others wait in "Pending shares" + $link = $this->urlGenerator->linkToRouteAbsolute('files.view.indexView', [ + 'view' => $autoAccept ? 'sharingin' : 'pendingshares', + ]); + if ($shareType === IShare::TYPE_USER) { $event = $this->activityManager->generateEvent(); $event->setApp('files_sharing') ->setType('remote_share') ->setSubject(RemoteShares::SUBJECT_REMOTE_SHARE_RECEIVED, [$ownerFederatedId, trim($name, '/'), $ownerDisplayName]) ->setAffectedUser($shareWith) + ->setLink($link) ->setObject('remote_share', (string)$externalShare->getId(), $name); Server::get(IActivityManager::class)->publish($event); $this->notifyAboutNewShare($shareWith, (string)$externalShare->getId(), $ownerFederatedId, $sharedByFederatedId, $name, $ownerDisplayName); // If auto-accept is enabled, accept the share - if ($this->federatedShareProvider->isFederatedTrustedShareAutoAccept() && $trustedServers?->isTrustedServer($remote) === true) { + if ($autoAccept) { $this->externalShareManager->acceptShare($externalShare, $user); } } else { @@ -231,12 +238,13 @@ public function shareReceived(ICloudFederationShare $share): string { ->setType('remote_share') ->setSubject(RemoteShares::SUBJECT_REMOTE_SHARE_RECEIVED, [$ownerFederatedId, trim($name, '/'), $ownerDisplayName]) ->setAffectedUser($user->getUID()) + ->setLink($link) ->setObject('remote_share', (string)$externalShare->getId(), $name); Server::get(IActivityManager::class)->publish($event); $this->notifyAboutNewShare($user->getUID(), (string)$externalShare->getId(), $ownerFederatedId, $sharedByFederatedId, $name, $ownerDisplayName); // If auto-accept is enabled, accept the share - if ($this->federatedShareProvider->isFederatedTrustedShareAutoAccept() && $trustedServers?->isTrustedServer($remote) === true) { + if ($autoAccept) { $this->externalShareManager->acceptShare($externalShare, $user); } } diff --git a/apps/federatedfilesharing/tests/OCM/CloudFederationProviderFilesTest.php b/apps/federatedfilesharing/tests/OCM/CloudFederationProviderFilesTest.php index 890060211bbbc..9101edae1b747 100644 --- a/apps/federatedfilesharing/tests/OCM/CloudFederationProviderFilesTest.php +++ b/apps/federatedfilesharing/tests/OCM/CloudFederationProviderFilesTest.php @@ -13,8 +13,10 @@ use OCA\FederatedFileSharing\AddressHandler; use OCA\FederatedFileSharing\FederatedShareProvider; use OCA\FederatedFileSharing\OCM\CloudFederationProviderFiles; +use OCA\Federation\TrustedServers; use OCA\Files_Sharing\External\ExternalShareMapper; use OCA\Files_Sharing\External\Manager; +use OCP\Activity\IEvent; use OCP\Activity\IManager as IActivityManager; use OCP\App\IAppManager; use OCP\Federation\Exceptions\ProviderCouldNotAddShareException; @@ -29,8 +31,10 @@ use OCP\Http\Client\IResponse; use OCP\IAppConfig; use OCP\IConfig; +use OCP\IGroup; use OCP\IGroupManager; use OCP\IURLGenerator; +use OCP\IUser; use OCP\IUserManager; use OCP\Notification\IManager as INotificationManager; use OCP\OCM\IOCMDiscoveryService; @@ -39,10 +43,15 @@ use OCP\Security\Signature\ISignatureManager; use OCP\Share\IManager; use OCP\Share\IProviderFactory; +use PHPUnit\Framework\Attributes\Group; use PHPUnit\Framework\MockObject\MockObject; use Psr\Log\LoggerInterface; use Test\TestCase; +/** + * DB group: overwriting TrustedServers resolves the real service first, which needs the database. + */ +#[Group('DB')] class CloudFederationProviderFilesTest extends TestCase { private IAppManager&MockObject $appManager; private FederatedShareProvider&MockObject $federatedShareProvider; @@ -360,4 +369,89 @@ public function testShareReceivedOptionalExchangeStoresAccessTokenOnSuccess(): v $this->provider->shareReceived($share); } + + /** + * Let the share go through to the activity and expect one published event + * with the given link per recipient. + */ + private function expectReceivedActivityLink(string $expectedLink, int $recipients): void { + $this->enableS2S(); + $this->addressHandler->method('splitUserRemote') + ->with('owner@example.com') + ->willReturn(['owner', 'https://example.com/']); + $this->discoveryService->method('discover') + ->willThrowException(new \Exception('network error')); + $this->filenameValidator->method('isFilenameValid')->willReturn(true); + + $this->urlGenerator->method('linkToRouteAbsolute') + ->willReturnCallback(fn (string $route, array $parameters): string => $route . '/' . $parameters['view']); + + $event = $this->createMock(IEvent::class); + foreach (['setApp', 'setType', 'setSubject', 'setAffectedUser', 'setObject'] as $method) { + $event->method($method)->willReturnSelf(); + } + $event->expects($this->exactly($recipients)) + ->method('setLink') + ->with($expectedLink) + ->willReturnSelf(); + $this->activityManager->method('generateEvent')->willReturn($event); + $this->activityManager->expects($this->exactly($recipients)) + ->method('publish') + ->with($event); + $this->overwriteService(IActivityManager::class, $this->activityManager); + } + + public function testShareReceivedActivityLinksToPendingShares(): void { + $this->expectReceivedActivityLink('files.view.indexView/pendingshares', 1); + + $user = $this->createMock(IUser::class); + $this->userManager->method('getUserNameFromLoginName')->with('localuser')->willReturn('localuser'); + $this->userManager->method('get')->with('localuser')->willReturn($user); + $this->externalShareManager->expects($this->never())->method('acceptShare'); + + $this->provider->shareReceived($this->buildShare()); + } + + public function testShareReceivedActivityLinksToPendingSharesForGroupMembers(): void { + $this->expectReceivedActivityLink('files.view.indexView/pendingshares', 2); + + $member1 = $this->createMock(IUser::class); + $member1->method('getUID')->willReturn('member1'); + $member2 = $this->createMock(IUser::class); + $member2->method('getUID')->willReturn('member2'); + $group = $this->createMock(IGroup::class); + $group->method('getUsers')->willReturn([$member1, $member2]); + $this->groupManager->method('get')->with('localgroup')->willReturn($group); + + $share = $this->createMock(ICloudFederationShare::class); + $share->method('getProtocol')->willReturn(['name' => 'webdav', 'webdav' => []]); + $share->method('getOwner')->willReturn('owner@example.com'); + $share->method('getOwnerDisplayName')->willReturn('Owner Name'); + $share->method('getShareSecret')->willReturn('refresh-token-abc'); + $share->method('getResourceName')->willReturn('/SharedFolder'); + $share->method('getShareWith')->willReturn('localgroup'); + $share->method('getProviderId')->willReturn('42'); + $share->method('getSharedBy')->willReturn('owner@example.com'); + $share->method('getShareType')->willReturn('group'); + + $this->provider->shareReceived($share); + } + + public function testShareReceivedActivityLinksToSharedWithYouWhenAutoAccepted(): void { + $this->expectReceivedActivityLink('files.view.indexView/sharingin', 1); + + $this->appManager->method('isEnabledForAnyone') + ->willReturnCallback(fn (string $app): bool => $app === 'federation'); + $trustedServers = $this->createMock(TrustedServers::class); + $trustedServers->method('isTrustedServer')->with('https://example.com/')->willReturn(true); + $this->overwriteService(TrustedServers::class, $trustedServers); + $this->federatedShareProvider->method('isFederatedTrustedShareAutoAccept')->willReturn(true); + + $user = $this->createMock(IUser::class); + $this->userManager->method('getUserNameFromLoginName')->with('localuser')->willReturn('localuser'); + $this->userManager->method('get')->with('localuser')->willReturn($user); + $this->externalShareManager->expects($this->once())->method('acceptShare'); + + $this->provider->shareReceived($this->buildShare()); + } }