Skip to content

Commit c853833

Browse files
Merge pull request #56796 from nextcloud/backport/55811/stable30
[stable30] fix(sharing): Allow public share access for everyone
2 parents 6561423 + 37c21ce commit c853833

3 files changed

Lines changed: 118 additions & 5 deletions

File tree

lib/private/Share20/Manager.php

Lines changed: 25 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1402,7 +1402,7 @@ public function getShareByToken($token) {
14021402
}
14031403
$share = null;
14041404
try {
1405-
if ($this->shareApiAllowLinks()) {
1405+
if ($this->config->getAppValue('core', 'shareapi_allow_links', 'yes') === 'yes') {
14061406
$provider = $this->factory->getProviderForType(IShare::TYPE_LINK);
14071407
$share = $provider->getShareByToken($token);
14081408
}
@@ -1485,6 +1485,17 @@ protected function checkShare(IShare $share): void {
14851485
}
14861486
}
14871487
}
1488+
1489+
// For link and email shares, verify the share owner can still create such shares
1490+
if ($share->getShareType() === IShare::TYPE_LINK || $share->getShareType() === IShare::TYPE_EMAIL) {
1491+
$shareOwner = $this->userManager->get($share->getShareOwner());
1492+
if ($shareOwner === null) {
1493+
throw new ShareNotFound($this->l->t('The requested share does not exist anymore'));
1494+
}
1495+
if (!$this->userCanCreateLinkShares($shareOwner)) {
1496+
throw new ShareNotFound($this->l->t('The requested share does not exist anymore'));
1497+
}
1498+
}
14881499
}
14891500

14901501
/**
@@ -1731,14 +1742,15 @@ public function shareApiEnabled() {
17311742
/**
17321743
* Is public link sharing enabled
17331744
*
1745+
* @param ?IUser $user User to check against group exclusions, defaults to current session user
17341746
* @return bool
17351747
*/
1736-
public function shareApiAllowLinks() {
1748+
public function shareApiAllowLinks(?IUser $user = null) {
17371749
if ($this->config->getAppValue('core', 'shareapi_allow_links', 'yes') !== 'yes') {
17381750
return false;
17391751
}
17401752

1741-
$user = $this->userSession->getUser();
1753+
$user = $user ?? $this->userSession->getUser();
17421754
if ($user) {
17431755
$excludedGroups = json_decode($this->config->getAppValue('core', 'shareapi_allow_links_exclude_groups', '[]'));
17441756
if ($excludedGroups) {
@@ -1750,6 +1762,16 @@ public function shareApiAllowLinks() {
17501762
return true;
17511763
}
17521764

1765+
/**
1766+
* Check if a specific user can create link shares
1767+
*
1768+
* @param IUser $user The user to check
1769+
* @return bool
1770+
*/
1771+
protected function userCanCreateLinkShares(IUser $user): bool {
1772+
return $this->shareApiAllowLinks($user);
1773+
}
1774+
17531775
/**
17541776
* Is password on public link requires
17551777
*

lib/public/Share/IManager.php

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -294,10 +294,12 @@ public function shareApiEnabled();
294294
/**
295295
* Is public link sharing enabled
296296
*
297+
* @param ?IUser $user User to check against group exclusions, defaults to current session user
297298
* @return bool
298299
* @since 9.0.0
300+
* @since 30.0.17 Added optional $user parameter
299301
*/
300-
public function shareApiAllowLinks();
302+
public function shareApiAllowLinks(?IUser $user = null);
301303

302304
/**
303305
* Is password on public link required

tests/lib/Share20/ManagerTest.php

Lines changed: 90 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -3227,21 +3227,29 @@ public function testGetShareByTokenWithPublicLinksDisabled() {
32273227

32283228
public function testGetShareByTokenPublicUploadDisabled() {
32293229
$this->config
3230-
->expects($this->exactly(3))
3230+
->expects($this->exactly(5))
32313231
->method('getAppValue')
32323232
->willReturnMap([
32333233
['core', 'shareapi_allow_links', 'yes', 'yes'],
32343234
['core', 'shareapi_allow_public_upload', 'yes', 'no'],
32353235
['files_sharing', 'hide_disabled_user_shares', 'no', 'no'],
3236+
['core', 'shareapi_allow_links_exclude_groups', '[]', '[]'],
32363237
]);
32373238

32383239
$share = $this->manager->newShare();
32393240
$share->setShareType(IShare::TYPE_LINK)
32403241
->setPermissions(\OCP\Constants::PERMISSION_READ | \OCP\Constants::PERMISSION_CREATE | \OCP\Constants::PERMISSION_UPDATE);
32413242
$share->setSharedWith('sharedWith');
3243+
$share->setShareOwner('shareOwner');
32423244
$folder = $this->createMock(\OC\Files\Node\Folder::class);
32433245
$share->setNode($folder);
32443246

3247+
$shareOwner = $this->createMock(IUser::class);
3248+
$this->userManager->expects($this->once())
3249+
->method('get')
3250+
->with('shareOwner')
3251+
->willReturn($shareOwner);
3252+
32453253
$this->defaultProvider->expects($this->once())
32463254
->method('getShareByToken')
32473255
->willReturn('validToken')
@@ -3252,6 +3260,87 @@ public function testGetShareByTokenPublicUploadDisabled() {
32523260
$this->assertSame(\OCP\Constants::PERMISSION_READ, $res->getPermissions());
32533261
}
32543262

3263+
public function testGetShareByTokenShareOwnerExcludedFromLinkShares(): void {
3264+
$this->expectException(ShareNotFound::class);
3265+
$this->expectExceptionMessage('The requested share does not exist anymore');
3266+
3267+
$this->config
3268+
->expects($this->exactly(4))
3269+
->method('getAppValue')
3270+
->willReturnMap([
3271+
['core', 'shareapi_allow_links', 'yes', 'yes'],
3272+
['files_sharing', 'hide_disabled_user_shares', 'no', 'no'],
3273+
['core', 'shareapi_allow_links_exclude_groups', '[]', '["excludedGroup"]'],
3274+
]);
3275+
3276+
$this->l->expects($this->once())
3277+
->method('t')
3278+
->willReturnArgument(0);
3279+
3280+
$share = $this->manager->newShare();
3281+
$share->setShareType(IShare::TYPE_LINK)
3282+
->setPermissions(\OCP\Constants::PERMISSION_READ);
3283+
$share->setShareOwner('shareOwner');
3284+
$file = $this->createMock(File::class);
3285+
$share->setNode($file);
3286+
3287+
$shareOwner = $this->createMock(IUser::class);
3288+
$this->userManager->expects($this->once())
3289+
->method('get')
3290+
->with('shareOwner')
3291+
->willReturn($shareOwner);
3292+
3293+
$this->groupManager->expects($this->once())
3294+
->method('getUserGroupIds')
3295+
->with($shareOwner)
3296+
->willReturn(['excludedGroup', 'otherGroup']);
3297+
3298+
$this->defaultProvider->expects($this->once())
3299+
->method('getShareByToken')
3300+
->with('token')
3301+
->willReturn($share);
3302+
3303+
$this->manager->getShareByToken('token');
3304+
}
3305+
3306+
public function testGetShareByTokenShareOwnerNotExcludedFromLinkShares(): void {
3307+
$this->config
3308+
->expects($this->exactly(4))
3309+
->method('getAppValue')
3310+
->willReturnMap([
3311+
['core', 'shareapi_allow_links', 'yes', 'yes'],
3312+
['files_sharing', 'hide_disabled_user_shares', 'no', 'no'],
3313+
['core', 'shareapi_allow_links_exclude_groups', '[]', '["excludedGroup"]'],
3314+
]);
3315+
3316+
$share = $this->manager->newShare();
3317+
$share->setShareType(IShare::TYPE_LINK)
3318+
->setPermissions(\OCP\Constants::PERMISSION_READ);
3319+
$share->setShareOwner('shareOwner');
3320+
$file = $this->createMock(File::class);
3321+
$share->setNode($file);
3322+
3323+
$shareOwner = $this->createMock(IUser::class);
3324+
$this->userManager->expects($this->once())
3325+
->method('get')
3326+
->with('shareOwner')
3327+
->willReturn($shareOwner);
3328+
3329+
$this->groupManager->expects($this->once())
3330+
->method('getUserGroupIds')
3331+
->with($shareOwner)
3332+
->willReturn(['allowedGroup', 'otherGroup']);
3333+
3334+
$this->defaultProvider->expects($this->once())
3335+
->method('getShareByToken')
3336+
->with('token')
3337+
->willReturn($share);
3338+
3339+
$res = $this->manager->getShareByToken('token');
3340+
3341+
$this->assertSame($share, $res);
3342+
}
3343+
32553344
public function testCheckPasswordNoLinkShare() {
32563345
$share = $this->createMock(IShare::class);
32573346
$share->method('getShareType')->willReturn(IShare::TYPE_USER);

0 commit comments

Comments
 (0)