Skip to content

Commit 27629b5

Browse files
committed
feat(files_sharing): add config option for extending link-share permissions
This allows the admin to control the behavior whether link shares with READ permissions should be extended to also gain SHARE permissions, allowing users (public share receivers) to add the share to their cloud. Signed-off-by: Ferdinand Thiessen <opensource@fthiessen.de>
1 parent 85c141e commit 27629b5

File tree

9 files changed

+92
-4
lines changed

9 files changed

+92
-4
lines changed

apps/files_sharing/lib/Controller/ShareAPIController.php

Lines changed: 5 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -10,6 +10,7 @@
1010
namespace OCA\Files_Sharing\Controller;
1111

1212
use Exception;
13+
use OC\Core\ConfigLexicon;
1314
use OC\Files\Storage\Wrapper\Wrapper;
1415
use OCA\Circles\Api\v1\Circles;
1516
use OCA\Files\Helper;
@@ -39,6 +40,7 @@
3940
use OCP\Files\Node;
4041
use OCP\Files\NotFoundException;
4142
use OCP\HintException;
43+
use OCP\IAppConfig;
4244
use OCP\IConfig;
4345
use OCP\IDateTimeZone;
4446
use OCP\IGroupManager;
@@ -86,6 +88,7 @@ public function __construct(
8688
private IURLGenerator $urlGenerator,
8789
private IL10N $l,
8890
private IConfig $config,
91+
private IAppConfig $appConfig,
8992
private IAppManager $appManager,
9093
private ContainerInterface $serverContainer,
9194
private IUserStatusManager $userStatusManager,
@@ -967,9 +970,9 @@ private function getLinkSharePermissions(?int $permissions, ?bool $legacyPublicU
967970
: Constants::PERMISSION_READ;
968971
}
969972

970-
// TODO: It might make sense to have a dedicated setting to allow/deny converting link shares into federated ones
971973
if ($this->hasPermission($permissions, Constants::PERMISSION_READ)
972-
&& $this->shareManager->outgoingServer2ServerSharesAllowed()) {
974+
&& $this->shareManager->outgoingServer2ServerSharesAllowed()
975+
&& $this->appConfig->getValueBool('core', ConfigLexicon::SHAREAPI_ALLOW_FEDERATION_ON_PUBLIC_SHARES, true)) {
973976
$permissions |= Constants::PERMISSION_SHARE;
974977
}
975978

apps/files_sharing/tests/Controller/ShareAPIControllerTest.php

Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -21,6 +21,7 @@
2121
use OCP\Files\Mount\IShareOwnerlessMount;
2222
use OCP\Files\NotFoundException;
2323
use OCP\Files\Storage\IStorage;
24+
use OCP\IAppConfig;
2425
use OCP\IConfig;
2526
use OCP\IDateTimeZone;
2627
use OCP\IGroup;
@@ -70,6 +71,7 @@ class ShareAPIControllerTest extends TestCase {
7071
private IURLGenerator&MockObject $urlGenerator;
7172
private IL10N&MockObject $l;
7273
private IConfig&MockObject $config;
74+
private IAppConfig&MockObject $appConfig;
7375
private IAppManager&MockObject $appManager;
7476
private ContainerInterface&MockObject $serverContainer;
7577
private IUserStatusManager&MockObject $userStatusManager;
@@ -102,6 +104,7 @@ protected function setUp(): void {
102104
return vsprintf($text, $parameters);
103105
});
104106
$this->config = $this->createMock(IConfig::class);
107+
$this->appConfig = $this->createMock(IAppConfig::class);
105108
$this->appManager = $this->createMock(IAppManager::class);
106109
$this->serverContainer = $this->createMock(ContainerInterface::class);
107110
$this->userStatusManager = $this->createMock(IUserStatusManager::class);
@@ -126,6 +129,7 @@ protected function setUp(): void {
126129
$this->urlGenerator,
127130
$this->l,
128131
$this->config,
132+
$this->appConfig,
129133
$this->appManager,
130134
$this->serverContainer,
131135
$this->userStatusManager,
@@ -154,6 +158,7 @@ private function mockFormatShare() {
154158
$this->urlGenerator,
155159
$this->l,
156160
$this->config,
161+
$this->appConfig,
157162
$this->appManager,
158163
$this->serverContainer,
159164
$this->userStatusManager,
@@ -839,6 +844,7 @@ public function testGetShare(IShare $share, array $result): void {
839844
$this->urlGenerator,
840845
$this->l,
841846
$this->config,
847+
$this->appConfig,
842848
$this->appManager,
843849
$this->serverContainer,
844850
$this->userStatusManager,
@@ -1472,6 +1478,7 @@ public function testGetShares(array $getSharesParameters, array $shares, array $
14721478
$this->urlGenerator,
14731479
$this->l,
14741480
$this->config,
1481+
$this->appConfig,
14751482
$this->appManager,
14761483
$this->serverContainer,
14771484
$this->userStatusManager,
@@ -1815,6 +1822,7 @@ public function testCreateShareUser(): void {
18151822
$this->urlGenerator,
18161823
$this->l,
18171824
$this->config,
1825+
$this->appConfig,
18181826
$this->appManager,
18191827
$this->serverContainer,
18201828
$this->userStatusManager,
@@ -1913,6 +1921,7 @@ public function testCreateShareGroup(): void {
19131921
$this->urlGenerator,
19141922
$this->l,
19151923
$this->config,
1924+
$this->appConfig,
19161925
$this->appManager,
19171926
$this->serverContainer,
19181927
$this->userStatusManager,
@@ -2339,6 +2348,7 @@ public function testCreateShareRemote(): void {
23392348
$this->urlGenerator,
23402349
$this->l,
23412350
$this->config,
2351+
$this->appConfig,
23422352
$this->appManager,
23432353
$this->serverContainer,
23442354
$this->userStatusManager,
@@ -2410,6 +2420,7 @@ public function testCreateShareRemoteGroup(): void {
24102420
$this->urlGenerator,
24112421
$this->l,
24122422
$this->config,
2423+
$this->appConfig,
24132424
$this->appManager,
24142425
$this->serverContainer,
24152426
$this->userStatusManager,
@@ -2642,6 +2653,7 @@ public function testCreateReshareOfFederatedMountNoDeletePermissions(): void {
26422653
$this->urlGenerator,
26432654
$this->l,
26442655
$this->config,
2656+
$this->appConfig,
26452657
$this->appManager,
26462658
$this->serverContainer,
26472659
$this->userStatusManager,

apps/settings/lib/Settings/Admin/Sharing.php

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -9,6 +9,7 @@
99
use OCP\AppFramework\Http\TemplateResponse;
1010
use OCP\AppFramework\Services\IInitialState;
1111
use OCP\Constants;
12+
use OCP\IAppConfig;
1213
use OCP\IConfig;
1314
use OCP\IL10N;
1415
use OCP\IURLGenerator;
@@ -19,6 +20,7 @@
1920
class Sharing implements IDelegatedSettings {
2021
public function __construct(
2122
private IConfig $config,
23+
private IAppConfig $appConfig,
2224
private IL10N $l,
2325
private IManager $shareManager,
2426
private IAppManager $appManager,
@@ -46,6 +48,7 @@ public function getForm() {
4648
'allowPublicUpload' => $this->getHumanBooleanConfig('core', 'shareapi_allow_public_upload', true),
4749
'allowResharing' => $this->getHumanBooleanConfig('core', 'shareapi_allow_resharing', true),
4850
'allowShareDialogUserEnumeration' => $this->getHumanBooleanConfig('core', 'shareapi_allow_share_dialog_user_enumeration', true),
51+
'allowFederationOnPublicShares' => $this->appConfig->getValueBool('core', 'shareapi_allow_federation_on_public_shares', true),
4952
'restrictUserEnumerationToGroup' => $this->getHumanBooleanConfig('core', 'shareapi_restrict_user_enumeration_to_group'),
5053
'restrictUserEnumerationToPhone' => $this->getHumanBooleanConfig('core', 'shareapi_restrict_user_enumeration_to_phone'),
5154
'restrictUserEnumerationFullMatch' => $this->getHumanBooleanConfig('core', 'shareapi_restrict_user_enumeration_full_match', true),

apps/settings/src/components/AdminSettingsSharingForm.vue

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -39,6 +39,10 @@
3939
<NcCheckboxRadioSwitch :checked.sync="settings.allowPublicUpload">
4040
{{ t('settings', 'Allow public uploads') }}
4141
</NcCheckboxRadioSwitch>
42+
<NcCheckboxRadioSwitch v-model="settings.allowFederationOnPublicShares">
43+
{{ t('settings', 'Allow public shares to be added to other clouds by federation.') }}
44+
{{ t('settings', 'This will add share permissions to all newly created link shares.') }}
45+
</NcCheckboxRadioSwitch>
4246
<NcCheckboxRadioSwitch :checked.sync="settings.enableLinkPasswordByDefault">
4347
{{ t('settings', 'Always ask for a password') }}
4448
</NcCheckboxRadioSwitch>
@@ -232,6 +236,7 @@ interface IShareSettings {
232236
allowPublicUpload: boolean
233237
allowResharing: boolean
234238
allowShareDialogUserEnumeration: boolean
239+
allowFederationOnPublicShares: boolean
235240
restrictUserEnumerationToGroup: boolean
236241
restrictUserEnumerationToPhone: boolean
237242
restrictUserEnumerationFullMatch: boolean

apps/settings/tests/Settings/Admin/SharingTest.php

Lines changed: 14 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -10,6 +10,7 @@
1010
use OCP\AppFramework\Http\TemplateResponse;
1111
use OCP\AppFramework\Services\IInitialState;
1212
use OCP\Constants;
13+
use OCP\IAppConfig;
1314
use OCP\IConfig;
1415
use OCP\IL10N;
1516
use OCP\IURLGenerator;
@@ -18,25 +19,30 @@
1819
use Test\TestCase;
1920

2021
class SharingTest extends TestCase {
22+
private Sharing $admin;
23+
2124
private IConfig&MockObject $config;
25+
private IAppConfig&MockObject $appConfig;
2226
private IL10N&MockObject $l10n;
2327
private IManager&MockObject $shareManager;
2428
private IAppManager&MockObject $appManager;
2529
private IURLGenerator&MockObject $urlGenerator;
2630
private IInitialState&MockObject $initialState;
27-
private Sharing $admin;
2831

2932
protected function setUp(): void {
3033
parent::setUp();
3134
$this->config = $this->createMock(IConfig::class);
35+
$this->appConfig = $this->createMock(IAppConfig::class);
3236
$this->l10n = $this->createMock(IL10N::class);
37+
3338
$this->shareManager = $this->createMock(IManager::class);
3439
$this->appManager = $this->createMock(IAppManager::class);
3540
$this->urlGenerator = $this->createMock(IURLGenerator::class);
3641
$this->initialState = $this->createMock(IInitialState::class);
3742

3843
$this->admin = new Sharing(
3944
$this->config,
45+
$this->appConfig,
4046
$this->l10n,
4147
$this->shareManager,
4248
$this->appManager,
@@ -47,6 +53,12 @@ protected function setUp(): void {
4753
}
4854

4955
public function testGetFormWithoutExcludedGroups(): void {
56+
$this->appConfig
57+
->method('getValueBool')
58+
->willReturnMap([
59+
['core', 'shareapi_allow_federation_on_public_shares', true, false, true],
60+
]);
61+
5062
$this->config
5163
->method('getAppValue')
5264
->willReturnMap([
@@ -102,6 +114,7 @@ public function testGetFormWithoutExcludedGroups(): void {
102114
'allowPublicUpload' => true,
103115
'allowResharing' => true,
104116
'allowShareDialogUserEnumeration' => true,
117+
'allowFederationOnPublicShares' => true,
105118
'restrictUserEnumerationToGroup' => false,
106119
'restrictUserEnumerationToPhone' => false,
107120
'restrictUserEnumerationFullMatch' => true,

core/Application.php

Lines changed: 14 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -17,10 +17,14 @@
1717
use OC\Authentication\Listeners\UserDeletedTokenCleanupListener;
1818
use OC\Authentication\Listeners\UserDeletedWebAuthnCleanupListener;
1919
use OC\Authentication\Notifications\Notifier as AuthenticationNotifier;
20+
use OC\Core\Config\ConfigLexicon;
2021
use OC\Core\Listener\BeforeTemplateRenderedListener;
2122
use OC\Core\Notification\CoreNotifier;
2223
use OC\TagManager;
2324
use OCP\AppFramework\App;
25+
use OCP\AppFramework\Bootstrap\IBootContext;
26+
use OCP\AppFramework\Bootstrap\IBootstrap;
27+
use OCP\AppFramework\Bootstrap\IRegistrationContext;
2428
use OCP\AppFramework\Http\Events\BeforeLoginTemplateRenderedEvent;
2529
use OCP\AppFramework\Http\Events\BeforeTemplateRenderedEvent;
2630
use OCP\DB\Events\AddMissingIndicesEvent;
@@ -36,7 +40,7 @@
3640
*
3741
* @package OC\Core
3842
*/
39-
class Application extends App {
43+
class Application extends App implements IBootstrap {
4044
public function __construct() {
4145
parent::__construct('core');
4246

@@ -305,4 +309,13 @@ public function __construct() {
305309
// Tags
306310
$eventDispatcher->addServiceListener(UserDeletedEvent::class, TagManager::class);
307311
}
312+
313+
public function register(IRegistrationContext $context): void {
314+
$context->registerConfigLexicon(ConfigLexicon::class);
315+
}
316+
317+
public function boot(IBootContext $context): void {
318+
// ...
319+
}
320+
308321
}

core/ConfigLexicon.php

Lines changed: 37 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,37 @@
1+
<?php
2+
3+
declare(strict_types=1);
4+
/**
5+
* SPDX-FileCopyrightText: 2025 Nextcloud GmbH and Nextcloud contributors
6+
* SPDX-License-Identifier: AGPL-3.0-or-later
7+
*/
8+
9+
namespace OC\Core;
10+
11+
use NCU\Config\Lexicon\ConfigLexiconEntry;
12+
use NCU\Config\Lexicon\ConfigLexiconStrictness;
13+
use NCU\Config\Lexicon\IConfigLexicon;
14+
use NCU\Config\ValueType;
15+
16+
/**
17+
* Config Lexicon for core.
18+
*
19+
* Please Add & Manage your Config Keys in that file and keep the Lexicon up to date!
20+
*/
21+
class ConfigLexicon implements IConfigLexicon {
22+
public const SHAREAPI_ALLOW_FEDERATION_ON_PUBLIC_SHARES = 'shareapi_allow_federation_on_public_shares';
23+
24+
public function getStrictness(): ConfigLexiconStrictness {
25+
return ConfigLexiconStrictness::IGNORE;
26+
}
27+
28+
public function getAppConfigs(): array {
29+
return [
30+
new ConfigLexiconEntry(self::SHAREAPI_ALLOW_FEDERATION_ON_PUBLIC_SHARES, ValueType::BOOL, true, 'adds share permission to public shares to allow adding them to your Nextcloud (federation)'),
31+
];
32+
}
33+
34+
public function getUserConfigs(): array {
35+
return [];
36+
}
37+
}

lib/composer/composer/autoload_classmap.php

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1334,6 +1334,7 @@
13341334
'OC\\Core\\Command\\User\\Setting' => $baseDir . '/core/Command/User/Setting.php',
13351335
'OC\\Core\\Command\\User\\SyncAccountDataCommand' => $baseDir . '/core/Command/User/SyncAccountDataCommand.php',
13361336
'OC\\Core\\Command\\User\\Welcome' => $baseDir . '/core/Command/User/Welcome.php',
1337+
'OC\\Core\\ConfigLexicon' => $baseDir . '/core/ConfigLexicon.php',
13371338
'OC\\Core\\Controller\\AppPasswordController' => $baseDir . '/core/Controller/AppPasswordController.php',
13381339
'OC\\Core\\Controller\\AutoCompleteController' => $baseDir . '/core/Controller/AutoCompleteController.php',
13391340
'OC\\Core\\Controller\\AvatarController' => $baseDir . '/core/Controller/AvatarController.php',

lib/composer/composer/autoload_static.php

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1375,6 +1375,7 @@ class ComposerStaticInit749170dad3f5e7f9ca158f5a9f04f6a2
13751375
'OC\\Core\\Command\\User\\Setting' => __DIR__ . '/../../..' . '/core/Command/User/Setting.php',
13761376
'OC\\Core\\Command\\User\\SyncAccountDataCommand' => __DIR__ . '/../../..' . '/core/Command/User/SyncAccountDataCommand.php',
13771377
'OC\\Core\\Command\\User\\Welcome' => __DIR__ . '/../../..' . '/core/Command/User/Welcome.php',
1378+
'OC\\Core\\ConfigLexicon' => __DIR__ . '/../../..' . '/core/ConfigLexicon.php',
13781379
'OC\\Core\\Controller\\AppPasswordController' => __DIR__ . '/../../..' . '/core/Controller/AppPasswordController.php',
13791380
'OC\\Core\\Controller\\AutoCompleteController' => __DIR__ . '/../../..' . '/core/Controller/AutoCompleteController.php',
13801381
'OC\\Core\\Controller\\AvatarController' => __DIR__ . '/../../..' . '/core/Controller/AvatarController.php',

0 commit comments

Comments
 (0)