diff --git a/apps/federatedfilesharing/lib/Controller/RequestHandlerController.php b/apps/federatedfilesharing/lib/Controller/RequestHandlerController.php
index 63de8ff605e..90e7f53da80 100644
--- a/apps/federatedfilesharing/lib/Controller/RequestHandlerController.php
+++ b/apps/federatedfilesharing/lib/Controller/RequestHandlerController.php
@@ -39,9 +39,6 @@ use Psr\Log\LoggerInterface;
#[OpenAPI(scope: OpenAPI::SCOPE_FEDERATION)]
class RequestHandlerController extends OCSController {
- /** @var string */
- private $shareTable = 'share';
-
public function __construct(
string $appName,
IRequest $request,
diff --git a/apps/federatedfilesharing/lib/FederatedShareProvider.php b/apps/federatedfilesharing/lib/FederatedShareProvider.php
index 139c873b0d6..4d2157c1b44 100644
--- a/apps/federatedfilesharing/lib/FederatedShareProvider.php
+++ b/apps/federatedfilesharing/lib/FederatedShareProvider.php
@@ -999,6 +999,11 @@ class FederatedShareProvider implements IShareProvider {
return ($result === 'yes');
}
+ public function isFederatedTrustedShareAutoAccept() {
+ $result = $this->config->getAppValue('files_sharing', 'federatedTrustedShareAutoAccept', 'yes');
+ return ($result === 'yes');
+ }
+
/**
* @inheritdoc
*/
diff --git a/apps/federatedfilesharing/lib/OCM/CloudFederationProviderFiles.php b/apps/federatedfilesharing/lib/OCM/CloudFederationProviderFiles.php
index 5c633c0fbbf..d5082eafc56 100644
--- a/apps/federatedfilesharing/lib/OCM/CloudFederationProviderFiles.php
+++ b/apps/federatedfilesharing/lib/OCM/CloudFederationProviderFiles.php
@@ -10,6 +10,7 @@ use OC\AppFramework\Http;
use OC\Files\Filesystem;
use OCA\FederatedFileSharing\AddressHandler;
use OCA\FederatedFileSharing\FederatedShareProvider;
+use OCA\Federation\TrustedServers;
use OCA\Files_Sharing\Activity\Providers\RemoteShares;
use OCA\Files_Sharing\External\Manager;
use OCA\GlobalSiteSelector\Service\SlaveService;
@@ -66,6 +67,7 @@ class CloudFederationProviderFiles implements ISignedCloudFederationProvider {
private LoggerInterface $logger,
private IFilenameValidator $filenameValidator,
private readonly IProviderFactory $shareProviderFactory,
+ private TrustedServers $trustedServers,
) {
}
@@ -163,6 +165,11 @@ class CloudFederationProviderFiles implements ISignedCloudFederationProvider {
->setObject('remote_share', $shareId, $name);
\OC::$server->getActivityManager()->publish($event);
$this->notifyAboutNewShare($shareWith, $shareId, $ownerFederatedId, $sharedByFederatedId, $name, $ownerDisplayName);
+
+ // If auto-accept is enabled, accept the share
+ if ($this->federatedShareProvider->isFederatedTrustedShareAutoAccept() && $this->trustedServers->isTrustedServer($remote)) {
+ $this->externalShareManager->acceptShare($shareId, $shareWith);
+ }
} else {
$groupMembers = $this->groupManager->get($shareWith)->getUsers();
foreach ($groupMembers as $user) {
@@ -174,8 +181,14 @@ class CloudFederationProviderFiles implements ISignedCloudFederationProvider {
->setObject('remote_share', $shareId, $name);
\OC::$server->getActivityManager()->publish($event);
$this->notifyAboutNewShare($user->getUID(), $shareId, $ownerFederatedId, $sharedByFederatedId, $name, $ownerDisplayName);
+
+ // If auto-accept is enabled, accept the share
+ if ($this->federatedShareProvider->isFederatedTrustedShareAutoAccept() && $this->trustedServers->isTrustedServer($remote)) {
+ $this->externalShareManager->acceptShare($shareId, $user->getUID());
+ }
}
}
+
return $shareId;
} catch (\Exception $e) {
$this->logger->error('Server can not add remote share.', [
diff --git a/apps/federatedfilesharing/lib/Settings/Admin.php b/apps/federatedfilesharing/lib/Settings/Admin.php
index 1343513e65a..e21c34638ad 100644
--- a/apps/federatedfilesharing/lib/Settings/Admin.php
+++ b/apps/federatedfilesharing/lib/Settings/Admin.php
@@ -40,6 +40,7 @@ class Admin implements IDelegatedSettings {
$this->initialState->provideInitialState('incomingServer2serverGroupShareEnabled', $this->fedShareProvider->isIncomingServer2serverGroupShareEnabled());
$this->initialState->provideInitialState('lookupServerEnabled', $this->fedShareProvider->isLookupServerQueriesEnabled());
$this->initialState->provideInitialState('lookupServerUploadEnabled', $this->fedShareProvider->isLookupServerUploadEnabled());
+ $this->initialState->provideInitialState('federatedTrustedShareAutoAccept', $this->fedShareProvider->isFederatedTrustedShareAutoAccept());
return new TemplateResponse('federatedfilesharing', 'settings-admin', [], '');
}
@@ -76,6 +77,7 @@ class Admin implements IDelegatedSettings {
'incomingServer2serverGroupShareEnabled',
'lookupServerEnabled',
'lookupServerUploadEnabled',
+ 'federatedTrustedShareAutoAccept',
],
];
}
diff --git a/apps/federatedfilesharing/src/components/AdminSettings.vue b/apps/federatedfilesharing/src/components/AdminSettings.vue
index dfafe64c062..4f2049942d5 100644
--- a/apps/federatedfilesharing/src/components/AdminSettings.vue
+++ b/apps/federatedfilesharing/src/components/AdminSettings.vue
@@ -43,6 +43,12 @@
@update:checked="update('lookupServerUploadEnabled', lookupServerUploadEnabled)">
{{ t('federatedfilesharing', 'Allow people to publish their data to a global and public address book') }}
+
+
+ {{ t('federatedfilesharing', 'Automatically accept shares from federated accounts and groups by default') }}
+
@@ -74,6 +80,7 @@ export default {
federatedGroupSharingSupported: loadState('federatedfilesharing', 'federatedGroupSharingSupported'),
lookupServerEnabled: loadState('federatedfilesharing', 'lookupServerEnabled'),
lookupServerUploadEnabled: loadState('federatedfilesharing', 'lookupServerUploadEnabled'),
+ federatedTrustedShareAutoAccept: loadState('federatedfilesharing', 'federatedTrustedShareAutoAccept'),
internalOnly: loadState('federatedfilesharing', 'internalOnly'),
sharingFederatedDocUrl: loadState('federatedfilesharing', 'sharingFederatedDocUrl'),
}
diff --git a/apps/federation/lib/BackgroundJob/GetSharedSecret.php b/apps/federation/lib/BackgroundJob/GetSharedSecret.php
index 01dbf7b80b6..fe3b360dd18 100644
--- a/apps/federation/lib/BackgroundJob/GetSharedSecret.php
+++ b/apps/federation/lib/BackgroundJob/GetSharedSecret.php
@@ -44,7 +44,7 @@ class GetSharedSecret extends Job {
private LoggerInterface $logger,
private IDiscoveryService $ocsDiscoveryService,
ITimeFactory $timeFactory,
- private IConfig $config
+ private IConfig $config,
) {
parent::__construct($timeFactory);
$this->httpClient = $httpClientService->newClient();
diff --git a/apps/federation/lib/BackgroundJob/RequestSharedSecret.php b/apps/federation/lib/BackgroundJob/RequestSharedSecret.php
index 6691e39e682..cc50ee75ca0 100644
--- a/apps/federation/lib/BackgroundJob/RequestSharedSecret.php
+++ b/apps/federation/lib/BackgroundJob/RequestSharedSecret.php
@@ -48,7 +48,7 @@ class RequestSharedSecret extends Job {
private IDiscoveryService $ocsDiscoveryService,
private LoggerInterface $logger,
ITimeFactory $timeFactory,
- private IConfig $config
+ private IConfig $config,
) {
parent::__construct($timeFactory);
$this->httpClient = $httpClientService->newClient();
diff --git a/apps/federation/openapi-administration.json.license b/apps/federation/openapi-administration.json.license
new file mode 100644
index 00000000000..5dcb9c9e84b
--- /dev/null
+++ b/apps/federation/openapi-administration.json.license
@@ -0,0 +1,2 @@
+SPDX-FileCopyrightText: 2024 Nextcloud GmbH and Nextcloud contributors
+SPDX-License-Identifier: AGPL-3.0-or-later
\ No newline at end of file
diff --git a/apps/federation/openapi-federation.json.license b/apps/federation/openapi-federation.json.license
new file mode 100644
index 00000000000..5dcb9c9e84b
--- /dev/null
+++ b/apps/federation/openapi-federation.json.license
@@ -0,0 +1,2 @@
+SPDX-FileCopyrightText: 2024 Nextcloud GmbH and Nextcloud contributors
+SPDX-License-Identifier: AGPL-3.0-or-later
\ No newline at end of file
diff --git a/apps/federation/openapi-full.json.license b/apps/federation/openapi-full.json.license
new file mode 100644
index 00000000000..5dcb9c9e84b
--- /dev/null
+++ b/apps/federation/openapi-full.json.license
@@ -0,0 +1,2 @@
+SPDX-FileCopyrightText: 2024 Nextcloud GmbH and Nextcloud contributors
+SPDX-License-Identifier: AGPL-3.0-or-later
\ No newline at end of file
diff --git a/apps/files_sharing/lib/External/Manager.php b/apps/files_sharing/lib/External/Manager.php
index a69755edf2c..1ea7775641e 100644
--- a/apps/files_sharing/lib/External/Manager.php
+++ b/apps/files_sharing/lib/External/Manager.php
@@ -129,7 +129,7 @@ class Manager {
'mountpoint' => $mountPoint,
'owner' => $owner
];
- return $this->mountShare($options);
+ return $this->mountShare($options, $user);
}
/**
@@ -214,11 +214,12 @@ class Manager {
* @param int $id share id
* @return mixed share of false
*/
- public function getShare($id) {
+ public function getShare(int $id, ?string $user = null): array|false {
+ $user = $user ?? $this->uid;
$share = $this->fetchShare($id);
// check if the user is allowed to access it
- if ($this->canAccessShare($share)) {
+ if ($this->canAccessShare($share, $user)) {
return $share;
}
@@ -243,7 +244,7 @@ class Manager {
return $share;
}
- private function canAccessShare(array $share): bool {
+ private function canAccessShare(array $share, string $user): bool {
$validShare = isset($share['share_type']) && isset($share['user']);
if (!$validShare) {
@@ -252,7 +253,7 @@ class Manager {
// If the share is a user share, check if the user is the recipient
if ((int)$share['share_type'] === IShare::TYPE_USER
- && $share['user'] === $this->uid) {
+ && $share['user'] === $user) {
return true;
}
@@ -266,7 +267,7 @@ class Manager {
$groupShare = $share;
}
- $user = $this->userManager->get($this->uid);
+ $user = $this->userManager->get($user);
if ($this->groupManager->get($groupShare['user'])->inGroup($user)) {
return true;
}
@@ -295,13 +296,22 @@ class Manager {
* @param int $id
* @return bool True if the share could be accepted, false otherwise
*/
- public function acceptShare($id) {
- $share = $this->getShare($id);
+ public function acceptShare(int $id, ?string $user = null) {
+ // If we're auto-accepting a share, we need to know the user id
+ // as there is no session available while processing the share
+ // from the remote server request.
+ $user = $user ?? $this->uid;
+ if ($user === null) {
+ $this->logger->error('No user specified for accepting share');
+ return false;
+ }
+
+ $share = $this->getShare($id, $user);
$result = false;
if ($share) {
- \OC_Util::setupFS($this->uid);
- $shareFolder = Helper::getShareFolder(null, $this->uid);
+ \OC_Util::setupFS($user);
+ $shareFolder = Helper::getShareFolder(null, $user);
$mountPoint = Files::buildNotExistingFileName($shareFolder, $share['name']);
$mountPoint = Filesystem::normalizePath($mountPoint);
$hash = md5($mountPoint);
@@ -314,14 +324,14 @@ class Manager {
`mountpoint` = ?,
`mountpoint_hash` = ?
WHERE `id` = ? AND `user` = ?');
- $userShareAccepted = $acceptShare->execute([1, $mountPoint, $hash, $id, $this->uid]);
+ $userShareAccepted = $acceptShare->execute([1, $mountPoint, $hash, $id, $user]);
} else {
$parentId = (int)$share['parent'];
if ($parentId !== -1) {
// this is the sub-share
$subshare = $share;
} else {
- $subshare = $this->fetchUserShare($id, $this->uid);
+ $subshare = $this->fetchUserShare($id, $user);
}
if ($subshare !== null) {
@@ -332,7 +342,7 @@ class Manager {
`mountpoint` = ?,
`mountpoint_hash` = ?
WHERE `id` = ? AND `user` = ?');
- $acceptShare->execute([1, $mountPoint, $hash, $subshare['id'], $this->uid]);
+ $acceptShare->execute([1, $mountPoint, $hash, $subshare['id'], $user]);
$result = true;
} catch (Exception $e) {
$this->logger->emergency('Could not update share', ['exception' => $e]);
@@ -346,7 +356,7 @@ class Manager {
$share['password'],
$share['name'],
$share['owner'],
- $this->uid,
+ $user,
$mountPoint, $hash, 1,
$share['remote_id'],
$id,
@@ -358,17 +368,18 @@ class Manager {
}
}
}
+
if ($userShareAccepted !== false) {
$this->sendFeedbackToRemote($share['remote'], $share['share_token'], $share['remote_id'], 'accept');
$event = new FederatedShareAddedEvent($share['remote']);
$this->eventDispatcher->dispatchTyped($event);
- $this->eventDispatcher->dispatchTyped(new InvalidateMountCacheEvent($this->userManager->get($this->uid)));
+ $this->eventDispatcher->dispatchTyped(new InvalidateMountCacheEvent($this->userManager->get($user)));
$result = true;
}
}
// Make sure the user has no notification for something that does not exist anymore.
- $this->processNotification($id);
+ $this->processNotification($id, $user);
return $result;
}
@@ -379,17 +390,23 @@ class Manager {
* @param int $id
* @return bool True if the share could be declined, false otherwise
*/
- public function declineShare($id) {
- $share = $this->getShare($id);
+ public function declineShare(int $id, ?string $user = null) {
+ $user = $user ?? $this->uid;
+ if ($user === null) {
+ $this->logger->error('No user specified for declining share');
+ return false;
+ }
+
+ $share = $this->getShare($id, $user);
$result = false;
if ($share && (int)$share['share_type'] === IShare::TYPE_USER) {
$removeShare = $this->connection->prepare('
DELETE FROM `*PREFIX*share_external` WHERE `id` = ? AND `user` = ?');
- $removeShare->execute([$id, $this->uid]);
+ $removeShare->execute([$id, $user]);
$this->sendFeedbackToRemote($share['remote'], $share['share_token'], $share['remote_id'], 'decline');
- $this->processNotification($id);
+ $this->processNotification($id, $user);
$result = true;
} elseif ($share && (int)$share['share_type'] === IShare::TYPE_GROUP) {
$parentId = (int)$share['parent'];
@@ -397,7 +414,7 @@ class Manager {
// this is the sub-share
$subshare = $share;
} else {
- $subshare = $this->fetchUserShare($id, $this->uid);
+ $subshare = $this->fetchUserShare($id, $user);
}
if ($subshare !== null) {
@@ -416,7 +433,7 @@ class Manager {
$share['password'],
$share['name'],
$share['owner'],
- $this->uid,
+ $user,
$share['mountpoint'],
$share['mountpoint_hash'],
0,
@@ -429,16 +446,27 @@ class Manager {
$result = false;
}
}
- $this->processNotification($id);
+ $this->processNotification($id, $user);
}
return $result;
}
- public function processNotification(int $remoteShare): void {
+ public function processNotification(int $remoteShare, ?string $user = null): void {
+ $user = $user ?? $this->uid;
+ if ($user === null) {
+ $this->logger->error('No user specified for processing notification');
+ return;
+ }
+
+ $share = $this->fetchShare($remoteShare);
+ if ($share === false) {
+ return;
+ }
+
$filter = $this->notificationManager->createNotification();
$filter->setApp('files_sharing')
- ->setUser($this->uid)
+ ->setUser($user)
->setObject('remote_share', (string)$remoteShare);
$this->notificationManager->markProcessed($filter);
}
@@ -538,9 +566,10 @@ class Manager {
return rtrim(substr($path, strlen($prefix)), '/');
}
- public function getMount($data) {
+ public function getMount($data, ?string $user = null) {
+ $user = $user ?? $this->uid;
$data['manager'] = $this;
- $mountPoint = '/' . $this->uid . '/files' . $data['mountpoint'];
+ $mountPoint = '/' . $user . '/files' . $data['mountpoint'];
$data['mountpoint'] = $mountPoint;
$data['certificateManager'] = \OC::$server->getCertificateManager();
return new Mount(self::STORAGE, $mountPoint, $data, $this, $this->storageLoader);
@@ -550,8 +579,8 @@ class Manager {
* @param array $data
* @return Mount
*/
- protected function mountShare($data) {
- $mount = $this->getMount($data);
+ protected function mountShare($data, ?string $user = null) {
+ $mount = $this->getMount($data, $user);
$this->mountManager->addMount($mount);
return $mount;
}
@@ -768,6 +797,8 @@ class Manager {
* @return list list of open server-to-server shares
*/
private function getShares($accepted) {
+ // Not allowing providing a user here,
+ // as we only want to retrieve shares for the current user.
$user = $this->userManager->get($this->uid);
$groups = $this->groupManager->getUserGroups($user);
$userGroups = [];
diff --git a/apps/files_sharing/tests/External/ManagerTest.php b/apps/files_sharing/tests/External/ManagerTest.php
index 2bd0a1e14bf..29a873a72eb 100644
--- a/apps/files_sharing/tests/External/ManagerTest.php
+++ b/apps/files_sharing/tests/External/ManagerTest.php
@@ -645,10 +645,10 @@ class ManagerTest extends TestCase {
'user' => 'user2',
'remoteId' => '2342'
];
- $this->assertSame(null, call_user_func_array([$manager2, 'addShare'], $shareData2));
- $user2Shares = $manager2->getOpenShares();
- $this->assertCount(2, $user2Shares);
+ $this->assertCount(1, $manager2->getOpenShares());
+ $this->assertSame(null, call_user_func_array([$manager2, 'addShare'], $shareData2));
+ $this->assertCount(2, $manager2->getOpenShares());
$this->manager->expects($this->once())->method('tryOCMEndPoint')->with('http://localhost', 'token1', '2342', 'decline')->willReturn([]);
$this->manager->removeUserShares($this->uid);
@@ -690,10 +690,10 @@ class ManagerTest extends TestCase {
'user' => 'user2',
'remoteId' => '2342'
];
- $this->assertSame(null, call_user_func_array([$manager2, 'addShare'], $shareData2));
- $user2Shares = $manager2->getOpenShares();
- $this->assertCount(2, $user2Shares);
+ $this->assertCount(1, $manager2->getOpenShares());
+ $this->assertSame(null, call_user_func_array([$manager2, 'addShare'], $shareData2));
+ $this->assertCount(2, $manager2->getOpenShares());
$this->manager->expects($this->never())->method('tryOCMEndPoint');
$this->manager->removeGroupShares('group1');