Skip to content

Commit 8f87b4e

Browse files
Merge pull request #64638 from nextcloud/fix/sharing/validate-source-with-owner
fix(Sharing): Enforce source to be accessible by the owner
2 parents ac9b70c + f146c5b commit 8f87b4e

5 files changed

Lines changed: 40 additions & 19 deletions

File tree

‎apps/files/lib/Sharing/Source/NodeShareSourceType.php‎

Lines changed: 7 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -56,12 +56,17 @@ public function getDisplayName(IFactory $l10nFactory): string {
5656
}
5757

5858
#[\Override]
59-
public function validateSource(string $source): bool {
59+
public function validateSource(IUser $owner, string $source): bool {
6060
if ((string)(int)$source !== $source) {
6161
return false;
6262
}
6363

64-
return $this->rootFolder->getFirstNodeById((int)$source) instanceof Node;
64+
$node = $this->rootFolder->getUserFolder($owner->getUID())->getFirstNodeById((int)$source);
65+
if (!$node instanceof Node) {
66+
return false;
67+
}
68+
69+
return $node->isReadable() && $node->isShareable();
6570
}
6671

6772
#[\Override]

‎apps/files/tests/Sharing/Source/NodeShareSourceTypeTest.php‎

Lines changed: 10 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -37,6 +37,8 @@ final class NodeShareSourceTypeTest extends TestCase {
3737

3838
private IUser $user1;
3939

40+
private IUser $user2;
41+
4042
private Node $node;
4143

4244
private NodeShareSourceType $sourceType;
@@ -49,8 +51,8 @@ public function setUp(): void {
4951

5052
$this->manager = Server::get(ISharingManager::class);
5153

52-
$user1 = $this->createUser('user1', 'password');
53-
$this->user1 = $user1;
54+
$this->user1 = $this->createUser('user1', 'password');
55+
$this->user2 = $this->createUser('user2', 'password');
5456

5557
$userFolder = Server::get(IRootFolder::class)->getUserFolder($this->user1->getUID());
5658
$this->node = $userFolder->newFile('foo.txt', 'bar');
@@ -75,11 +77,12 @@ protected function tearDown(): void {
7577
}
7678

7779
public function testValidateSource(): void {
78-
$this->assertTrue($this->sourceType->validateSource((string)$this->node->getId()));
79-
$this->assertFalse($this->sourceType->validateSource('-1'));
80-
$this->assertFalse($this->sourceType->validateSource('000123'));
81-
$this->assertFalse($this->sourceType->validateSource('123abcdef'));
82-
$this->assertFalse($this->sourceType->validateSource('000123abcdef'));
80+
$this->assertTrue($this->sourceType->validateSource($this->user1, (string)$this->node->getId()));
81+
$this->assertFalse($this->sourceType->validateSource($this->user2, (string)$this->node->getId()));
82+
$this->assertFalse($this->sourceType->validateSource($this->user1, '-1'));
83+
$this->assertFalse($this->sourceType->validateSource($this->user1, '000123'));
84+
$this->assertFalse($this->sourceType->validateSource($this->user1, '123abcdef'));
85+
$this->assertFalse($this->sourceType->validateSource($this->user1, '000123abcdef'));
8386
}
8487

8588
public function testGetSourceDisplayName(): void {

‎lib/private/Sharing/SharingManager.php‎

Lines changed: 20 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -259,14 +259,23 @@ public function updateShareUserStatus(ShareAccessContext $accessContext, Share $
259259

260260
#[\Override]
261261
public function addShareSource(ShareAccessContext $accessContext, Share $share, ShareSource $source): Share {
262+
if ($share->owner->instance !== null) {
263+
throw new ShareOperationForbiddenException();
264+
}
265+
262266
// only the owner can add sources, otherwise a user could add sources others don't have access to, which would remove their access
263267
$this->validateShareEditPermissions($accessContext, $share, true);
264268

265269
if (($sourceType = $this->registry->getSourceTypes()[$source->class] ?? null) === null) {
266270
throw new RuntimeException('The source type is not registered: ' . $source->class);
267271
}
268272

269-
if (!$sourceType->validateSource($source->value)) {
273+
$ownerUser = $this->userManager->get($share->owner->userId);
274+
if (!$ownerUser instanceof IUser) {
275+
throw new RuntimeException('Owner does not exist.');
276+
}
277+
278+
if (!$sourceType->validateSource($ownerUser, $source->value)) {
270279
throw new ShareInvalidException('Invalid source: ' . $source->value . ' ' . $source->class, $this->l10n->t('The source does not exist.'));
271280
}
272281

@@ -870,11 +879,17 @@ private function validateInteraction(ShareAccessContext $accessContext, Share $s
870879
null, array_values(array_map(static fn (SharePermission $permission): string => $permission->class, $share->getEffectiveEnabledPermissions($accessContext)))
871880
);
872881

873-
$usersToCheck = [];
874-
if ($share->owner->instance === null && ($ownerUser = $this->userManager->get($share->owner->userId)) instanceof IUser) {
875-
$usersToCheck[] = $ownerUser;
882+
if ($share->owner->instance !== null) {
883+
throw new ShareOperationForbiddenException();
884+
}
885+
886+
$ownerUser = $this->userManager->get($share->owner->userId);
887+
if (!$ownerUser instanceof IUser) {
888+
throw new RuntimeException('Owner does not exist.');
876889
}
877890

891+
$usersToCheck = [$ownerUser];
892+
878893
if ($accessContext->currentUser instanceof IUser && !$share->owner->isCurrentUser($accessContext)) {
879894
$usersToCheck[] = $accessContext->currentUser;
880895
}
@@ -902,7 +917,7 @@ private function validateInteraction(ShareAccessContext $accessContext, Share $s
902917
throw new RuntimeException('The source type is not registered: ' . $source->class);
903918
}
904919

905-
if (!$sourceType->validateSource($source->value)) {
920+
if (!$sourceType->validateSource($ownerUser, $source->value)) {
906921
continue;
907922
}
908923

‎lib/unstable/Sharing/Source/IShareSourceType.php‎

Lines changed: 2 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -28,14 +28,12 @@ interface IShareSourceType {
2828
public function getDisplayName(IFactory $l10nFactory): string;
2929

3030
/**
31-
* Validate that a source exists.
32-
*
33-
* Any check if the source is allowed to be accessed and shared, must be implemented through {@see RestrictInteractionEvent}.
31+
* Validate that a source exists and is accessible by the owner.
3432
*
3533
* @param non-empty-string $source
3634
* @experimental 35.0.0
3735
*/
38-
public function validateSource(string $source): bool;
36+
public function validateSource(IUser $owner, string $source): bool;
3937

4038
/**
4139
* @param non-empty-string $source

‎tests/lib/Sharing/TestShareSourceType1.php‎

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -34,7 +34,7 @@ public function getDisplayName(IFactory $l10nFactory): string {
3434
}
3535

3636
#[\Override]
37-
public function validateSource(string $source): bool {
37+
public function validateSource(IUser $owner, string $source): bool {
3838
return array_key_exists($source, $this->validSources);
3939
}
4040

0 commit comments

Comments
 (0)