diff options
-rw-r--r-- | apps/files_sharing/lib/Config/ConfigLexicon.php | 2 | ||||
-rw-r--r-- | apps/files_sharing/lib/Controller/ShareAPIController.php | 19 | ||||
-rw-r--r-- | apps/files_sharing/lib/Listener/LoadSidebarListener.php | 1 | ||||
-rw-r--r-- | apps/files_sharing/src/models/Share.ts | 7 | ||||
-rw-r--r-- | apps/files_sharing/src/services/ConfigService.ts | 8 | ||||
-rw-r--r-- | apps/files_sharing/src/views/SharingTab.vue | 8 | ||||
-rw-r--r-- | apps/files_sharing/tests/ApiTest.php | 3 | ||||
-rw-r--r-- | apps/files_sharing/tests/Controller/ShareAPIControllerTest.php | 151 |
8 files changed, 197 insertions, 2 deletions
diff --git a/apps/files_sharing/lib/Config/ConfigLexicon.php b/apps/files_sharing/lib/Config/ConfigLexicon.php index a463b4e7ef2..fb2821e7f56 100644 --- a/apps/files_sharing/lib/Config/ConfigLexicon.php +++ b/apps/files_sharing/lib/Config/ConfigLexicon.php @@ -22,6 +22,7 @@ use NCU\Config\ValueType; */ class ConfigLexicon implements IConfigLexicon { public const SHOW_FEDERATED_AS_INTERNAL = 'show_federated_shares_as_internal'; + public const SHOW_FEDERATED_TO_TRUSTED_AS_INTERNAL = 'show_federated_shares_to_trusted_servers_as_internal'; public function getStrictness(): ConfigLexiconStrictness { return ConfigLexiconStrictness::IGNORE; @@ -30,6 +31,7 @@ class ConfigLexicon implements IConfigLexicon { public function getAppConfigs(): array { return [ new ConfigLexiconEntry(self::SHOW_FEDERATED_AS_INTERNAL, ValueType::BOOL, false, 'shows federated shares as internal shares', true), + new ConfigLexiconEntry(self::SHOW_FEDERATED_TO_TRUSTED_AS_INTERNAL, ValueType::BOOL, false, 'shows federated shares to trusted servers as internal shares', true), ]; } diff --git a/apps/files_sharing/lib/Controller/ShareAPIController.php b/apps/files_sharing/lib/Controller/ShareAPIController.php index 23ba9da1568..f544732b227 100644 --- a/apps/files_sharing/lib/Controller/ShareAPIController.php +++ b/apps/files_sharing/lib/Controller/ShareAPIController.php @@ -60,6 +60,8 @@ use OCP\Share\IProviderFactory; use OCP\Share\IShare; use OCP\Share\IShareProviderWithNotification; use OCP\UserStatus\IManager as IUserStatusManager; +use OCA\Federation\TrustedServers; +use OCP\User\IUserSession; use Psr\Container\ContainerExceptionInterface; use Psr\Container\ContainerInterface; use Psr\Log\LoggerInterface; @@ -95,6 +97,7 @@ class ShareAPIController extends OCSController { private IProviderFactory $factory, private IMailer $mailer, private ITagManager $tagManager, + private ?TrustedServers $trustedServers, private ?string $userId = null, ) { parent::__construct($appName, $request); @@ -197,6 +200,22 @@ class ShareAPIController extends OCSController { $result['item_size'] = $node->getSize(); $result['item_mtime'] = $node->getMTime(); + if ($this->trustedServers !== null && in_array($share->getShareType(), [IShare::TYPE_REMOTE, IShare::TYPE_REMOTE_GROUP])) { + $result['is_trusted_server'] = false; + $sharedWith = $share->getSharedWith(); + $remoteIdentifier = is_string($sharedWith) ? strrchr($sharedWith, '@') : false; + if ($remoteIdentifier !== false) { + $remote = substr($remoteIdentifier, 1); + try { + if ($this->trustedServers->isTrustedServer($remote)) { + $result['is_trusted_server'] = true; + } + } catch (\Exception $e) { + // Server not found or other issue, we consider it not trusted + } + } + } + $expiration = $share->getExpirationDate(); if ($expiration !== null) { $expiration->setTimezone($this->dateTimeZone->getTimeZone()); diff --git a/apps/files_sharing/lib/Listener/LoadSidebarListener.php b/apps/files_sharing/lib/Listener/LoadSidebarListener.php index 9f0eee9159a..88c39f38545 100644 --- a/apps/files_sharing/lib/Listener/LoadSidebarListener.php +++ b/apps/files_sharing/lib/Listener/LoadSidebarListener.php @@ -38,6 +38,7 @@ class LoadSidebarListener implements IEventListener { $appConfig = Server::get(IAppConfig::class); $this->initialState->provideInitialState('showFederatedSharesAsInternal', $appConfig->getValueBool('files_sharing', ConfigLexicon::SHOW_FEDERATED_AS_INTERNAL)); + $this->initialState->provideInitialState('showFederatedSharesToTrustedServersAsInternal', $appConfig->getValueBool('files_sharing', ConfigLexicon::SHOW_FEDERATED_TO_TRUSTED_AS_INTERNAL)); Util::addScript(Application::APP_ID, 'files_sharing_tab', 'files'); } } diff --git a/apps/files_sharing/src/models/Share.ts b/apps/files_sharing/src/models/Share.ts index fb76a655d53..b0638b29448 100644 --- a/apps/files_sharing/src/models/Share.ts +++ b/apps/files_sharing/src/models/Share.ts @@ -486,4 +486,11 @@ export default class Share { return this._share.status } + /** + * Is the share from a trusted server + */ + get isTrustedServer(): boolean { + return !!this._share.is_trusted_server + } + } diff --git a/apps/files_sharing/src/services/ConfigService.ts b/apps/files_sharing/src/services/ConfigService.ts index 2114e2d1bae..f75f34c7936 100644 --- a/apps/files_sharing/src/services/ConfigService.ts +++ b/apps/files_sharing/src/services/ConfigService.ts @@ -315,4 +315,12 @@ export default class Config { return loadState('files_sharing', 'showFederatedSharesAsInternal', false) } + /** + * Show federated shares to trusted servers as internal shares + * @return {boolean} + */ + get showFederatedSharesToTrustedServersAsInternal(): boolean { + return loadState('files_sharing', 'showFederatedSharesToTrustedServersAsInternal', false) + } + } diff --git a/apps/files_sharing/src/views/SharingTab.vue b/apps/files_sharing/src/views/SharingTab.vue index 82a11dea2e0..b06a9ae5ee6 100644 --- a/apps/files_sharing/src/views/SharingTab.vue +++ b/apps/files_sharing/src/views/SharingTab.vue @@ -402,7 +402,13 @@ export default { if ([ShareType.Link, ShareType.Email].includes(share.type)) { this.linkShares.push(share) } else if ([ShareType.Remote, ShareType.RemoteGroup].includes(share.type)) { - if (this.config.showFederatedSharesAsInternal) { + if (this.config.showFederatedSharesToTrustedServersAsInternal) { + if (share.isTrustedServer) { + this.shares.push(share) + } else { + this.externalShares.push(share) + } + } else if (this.config.showFederatedSharesAsInternal) { this.shares.push(share) } else { this.externalShares.push(share) diff --git a/apps/files_sharing/tests/ApiTest.php b/apps/files_sharing/tests/ApiTest.php index a712903d768..970aa97cadc 100644 --- a/apps/files_sharing/tests/ApiTest.php +++ b/apps/files_sharing/tests/ApiTest.php @@ -34,6 +34,7 @@ use OCP\Server; use OCP\Share\IProviderFactory; use OCP\Share\IShare; use OCP\UserStatus\IManager as IUserStatusManager; +use OCA\Federation\TrustedServers; use Psr\Container\ContainerInterface; use Psr\Log\LoggerInterface; @@ -113,6 +114,7 @@ class ApiTest extends TestCase { $providerFactory = $this->createMock(IProviderFactory::class); $mailer = $this->createMock(IMailer::class); $tagManager = $this->createMock(ITagManager::class); + $trustedServers = $this->createMock(TrustedServers::class); $dateTimeZone->method('getTimeZone')->willReturn(new \DateTimeZone(date_default_timezone_get())); return new ShareAPIController( @@ -134,6 +136,7 @@ class ApiTest extends TestCase { $providerFactory, $mailer, $tagManager, + $trustedServers, $userId, ); } diff --git a/apps/files_sharing/tests/Controller/ShareAPIControllerTest.php b/apps/files_sharing/tests/Controller/ShareAPIControllerTest.php index f245f0a25c7..62e7971e115 100644 --- a/apps/files_sharing/tests/Controller/ShareAPIControllerTest.php +++ b/apps/files_sharing/tests/Controller/ShareAPIControllerTest.php @@ -44,6 +44,7 @@ use OCP\Share\IManager; use OCP\Share\IProviderFactory; use OCP\Share\IShare; use OCP\UserStatus\IManager as IUserStatusManager; +use OCA\Federation\TrustedServers; use PHPUnit\Framework\MockObject\MockObject; use Psr\Container\ContainerInterface; use Psr\Log\LoggerInterface; @@ -79,6 +80,7 @@ class ShareAPIControllerTest extends TestCase { private IProviderFactory&MockObject $factory; private IMailer&MockObject $mailer; private ITagManager&MockObject $tagManager; + private TrustedServers&MockObject $trustedServers; protected function setUp(): void { $this->shareManager = $this->createMock(IManager::class); @@ -115,6 +117,7 @@ class ShareAPIControllerTest extends TestCase { $this->factory = $this->createMock(IProviderFactory::class); $this->mailer = $this->createMock(IMailer::class); $this->tagManager = $this->createMock(ITagManager::class); + $this->trustedServers = $this->createMock(TrustedServers::class); $this->ocs = new ShareAPIController( $this->appName, @@ -135,8 +138,10 @@ class ShareAPIControllerTest extends TestCase { $this->factory, $this->mailer, $this->tagManager, - $this->currentUser, + $this->trustedServers, + $this->currentUser ); + } /** @@ -163,6 +168,7 @@ class ShareAPIControllerTest extends TestCase { $this->factory, $this->mailer, $this->tagManager, + $this->trustedServers, $this->currentUser, ])->onlyMethods(['formatShare']) ->getMock(); @@ -848,6 +854,7 @@ class ShareAPIControllerTest extends TestCase { $this->factory, $this->mailer, $this->tagManager, + $this->trustedServers, $this->currentUser, ]) ->onlyMethods(['canAccessShare']) @@ -1481,6 +1488,7 @@ class ShareAPIControllerTest extends TestCase { $this->factory, $this->mailer, $this->tagManager, + $this->trustedServers, $this->currentUser, ]) ->onlyMethods(['formatShare']) @@ -1872,6 +1880,7 @@ class ShareAPIControllerTest extends TestCase { $this->factory, $this->mailer, $this->tagManager, + $this->trustedServers, $this->currentUser, ])->onlyMethods(['formatShare']) ->getMock(); @@ -1970,6 +1979,7 @@ class ShareAPIControllerTest extends TestCase { $this->factory, $this->mailer, $this->tagManager, + $this->trustedServers, $this->currentUser, ])->onlyMethods(['formatShare']) ->getMock(); @@ -2396,6 +2406,7 @@ class ShareAPIControllerTest extends TestCase { $this->factory, $this->mailer, $this->tagManager, + $this->trustedServers, $this->currentUser, ])->onlyMethods(['formatShare']) ->getMock(); @@ -2467,6 +2478,7 @@ class ShareAPIControllerTest extends TestCase { $this->factory, $this->mailer, $this->tagManager, + $this->trustedServers, $this->currentUser, ])->onlyMethods(['formatShare']) ->getMock(); @@ -2705,6 +2717,7 @@ class ShareAPIControllerTest extends TestCase { $this->factory, $this->mailer, $this->tagManager, + $this->trustedServers, $this->currentUser, ])->onlyMethods(['formatShare']) ->getMock(); @@ -4497,6 +4510,7 @@ class ShareAPIControllerTest extends TestCase { 'mount-type' => '', 'attributes' => null, 'item_permissions' => 1, + 'is_trusted_server' => false, ], $share, [], false ]; @@ -4550,6 +4564,7 @@ class ShareAPIControllerTest extends TestCase { 'mount-type' => '', 'attributes' => null, 'item_permissions' => 1, + 'is_trusted_server' => false, ], $share, [], false ]; @@ -5233,4 +5248,138 @@ class ShareAPIControllerTest extends TestCase { ['file_source' => 42, 'x' => 'y', 'tags' => ['tag1', 'tag2']], ], $result); } + + public function trustedServerProvider(): array { + return [ + 'Trusted server' => [true, true], + 'Untrusted server' => [false, false], + ]; + } + + /** + * @dataProvider trustedServerProvider + */ + public function testFormatShareWithFederatedShare(bool $isKnownServer, bool $isTrusted): void { + $nodeId = 12; + $nodePath = '/test.txt'; + $share = $this->createShare( + 1, + IShare::TYPE_REMOTE, + 'recipient@remoteserver.com', // shared with + 'sender@testserver.com', // shared by + 'shareOwner', // share owner + $nodePath, // path + Constants::PERMISSION_READ, + time(), + null, + null, + $nodePath, + $nodeId + ); + + $node = $this->createMock(\OCP\Files\File::class); + $node->method('getId')->willReturn($nodeId); + $node->method('getPath')->willReturn($nodePath); + $node->method('getInternalPath')->willReturn(ltrim($nodePath, '/')); + $mountPoint = $this->createMock(\OCP\Files\Mount\IMountPoint::class); + $mountPoint->method('getMountType')->willReturn('local'); + $node->method('getMountPoint')->willReturn($mountPoint); + $node->method('getMimetype')->willReturn('text/plain'); + $storage = $this->createMock(\OCP\Files\Storage\IStorage::class); + $storageCache = $this->createMock(\OCP\Files\Cache\ICache::class); + $storageCache->method('getNumericStorageId')->willReturn(1); + $storage->method('getCache')->willReturn($storageCache); + $storage->method('getId')->willReturn('home::shareOwner'); + $node->method('getStorage')->willReturn($storage); + $parent = $this->createMock(\OCP\Files\Folder::class); + $parent->method('getId')->willReturn(2); + $node->method('getParent')->willReturn($parent); + $node->method('getSize')->willReturn(1234); + $node->method('getMTime')->willReturn(1234567890); + + $this->previewManager->method('isAvailable')->with($node)->willReturn(false); + + $this->rootFolder->method('getUserFolder') + ->with($this->currentUser) + ->willReturnSelf(); + + $this->rootFolder->method('getFirstNodeById') + ->with($share->getNodeId()) + ->willReturn($node); + + $this->rootFolder->method('getRelativePath') + ->with($node->getPath()) + ->willReturnArgument(0); + + $serverName = 'remoteserver.com'; + $this->trustedServers->method('isTrustedServer') + ->with($serverName) + ->willReturn($isKnownServer); + + $result = $this->invokePrivate($this->ocs, 'formatShare', [$share]); + + $this->assertSame($isTrusted, $result['is_trusted_server']); + } + + public function testFormatShareWithFederatedShareWithAtInUsername(): void { + $nodeId = 12; + $nodePath = '/test.txt'; + $share = $this->createShare( + 1, + IShare::TYPE_REMOTE, + 'recipient@domain.com@remoteserver.com', // shared with + 'sender@testserver.com', // shared by + 'shareOwner', // share owner + $nodePath, // path + Constants::PERMISSION_READ, + time(), + null, + null, + $nodePath, + $nodeId + ); + + $node = $this->createMock(\OCP\Files\File::class); + $node->method('getId')->willReturn($nodeId); + $node->method('getPath')->willReturn($nodePath); + $node->method('getInternalPath')->willReturn(ltrim($nodePath, '/')); + $mountPoint = $this->createMock(\OCP\Files\Mount\IMountPoint::class); + $mountPoint->method('getMountType')->willReturn('local'); + $node->method('getMountPoint')->willReturn($mountPoint); + $node->method('getMimetype')->willReturn('text/plain'); + $storage = $this->createMock(\OCP\Files\Storage\IStorage::class); + $storageCache = $this->createMock(\OCP\Files\Cache\ICache::class); + $storageCache->method('getNumericStorageId')->willReturn(1); + $storage->method('getCache')->willReturn($storageCache); + $storage->method('getId')->willReturn('home::shareOwner'); + $node->method('getStorage')->willReturn($storage); + $parent = $this->createMock(\OCP\Files\Folder::class); + $parent->method('getId')->willReturn(2); + $node->method('getParent')->willReturn($parent); + $node->method('getSize')->willReturn(1234); + $node->method('getMTime')->willReturn(1234567890); + + $this->previewManager->method('isAvailable')->with($node)->willReturn(false); + + $this->rootFolder->method('getUserFolder') + ->with($this->currentUser) + ->willReturnSelf(); + + $this->rootFolder->method('getFirstNodeById') + ->with($share->getNodeId()) + ->willReturn($node); + + $this->rootFolder->method('getRelativePath') + ->with($node->getPath()) + ->willReturnArgument(0); + + $serverName = 'remoteserver.com'; + $this->trustedServers->method('isTrustedServer') + ->with($serverName) + ->willReturn(true); + + $result = $this->invokePrivate($this->ocs, 'formatShare', [$share]); + + $this->assertTrue($result['is_trusted_server']); + } } |