Skip to content

Commit 47701b0

Browse files
committed
feat(Sharing): Add per-recipient permissions
1 parent 3e9f34e commit 47701b0

24 files changed

Lines changed: 1379 additions & 87 deletions

apps/sharing/appinfo/info.xml

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -9,7 +9,7 @@
99
<name>Sharing</name>
1010
<summary>TODO</summary>
1111
<description>TODO</description>
12-
<version>2.0.0-dev.2</version>
12+
<version>2.0.0-dev.3</version>
1313
<licence>AGPL-3.0-or-later</licence>
1414
<author>Kate Döen</author>
1515
<namespace>Sharing</namespace>
@@ -30,6 +30,7 @@
3030
<command>\OCA\Sharing\Command\SelectSharePermissionPreset</command>
3131
<command>\OCA\Sharing\Command\UpdateSharePermission</command>
3232
<command>\OCA\Sharing\Command\UpdateShareProperty</command>
33+
<command>\OCA\Sharing\Command\UpdateShareRecipientPermission</command>
3334
<command>\OCA\Sharing\Command\UpdateShareRecipientSecret</command>
3435
<command>\OCA\Sharing\Command\UpdateShareState</command>
3536
<command>\OCA\Sharing\Command\UpdateShareUserStatus</command>

apps/sharing/composer/composer/autoload_classmap.php

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -21,6 +21,7 @@
2121
'OCA\\Sharing\\Command\\SharingBase' => $baseDir . '/../lib/Command/SharingBase.php',
2222
'OCA\\Sharing\\Command\\UpdateSharePermission' => $baseDir . '/../lib/Command/UpdateSharePermission.php',
2323
'OCA\\Sharing\\Command\\UpdateShareProperty' => $baseDir . '/../lib/Command/UpdateShareProperty.php',
24+
'OCA\\Sharing\\Command\\UpdateShareRecipientPermission' => $baseDir . '/../lib/Command/UpdateShareRecipientPermission.php',
2425
'OCA\\Sharing\\Command\\UpdateShareRecipientSecret' => $baseDir . '/../lib/Command/UpdateShareRecipientSecret.php',
2526
'OCA\\Sharing\\Command\\UpdateShareState' => $baseDir . '/../lib/Command/UpdateShareState.php',
2627
'OCA\\Sharing\\Command\\UpdateShareUserStatus' => $baseDir . '/../lib/Command/UpdateShareUserStatus.php',
@@ -30,5 +31,6 @@
3031
'OCA\\Sharing\\Migration\\Version1000Date20260731171922' => $baseDir . '/../lib/Migration/Version1000Date20260731171922.php',
3132
'OCA\\Sharing\\Migration\\Version1000Date20260826073021' => $baseDir . '/../lib/Migration/Version1000Date20260826073021.php',
3233
'OCA\\Sharing\\Migration\\Version1000Date20260826115938' => $baseDir . '/../lib/Migration/Version1000Date20260826115938.php',
34+
'OCA\\Sharing\\Migration\\Version1000Date20260826122228' => $baseDir . '/../lib/Migration/Version1000Date20260826122228.php',
3335
'OCA\\Sharing\\ResponseDefinitions' => $baseDir . '/../lib/ResponseDefinitions.php',
3436
);

apps/sharing/composer/composer/autoload_static.php

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -36,6 +36,7 @@ class ComposerStaticInitSharing
3636
'OCA\\Sharing\\Command\\SharingBase' => __DIR__ . '/..' . '/../lib/Command/SharingBase.php',
3737
'OCA\\Sharing\\Command\\UpdateSharePermission' => __DIR__ . '/..' . '/../lib/Command/UpdateSharePermission.php',
3838
'OCA\\Sharing\\Command\\UpdateShareProperty' => __DIR__ . '/..' . '/../lib/Command/UpdateShareProperty.php',
39+
'OCA\\Sharing\\Command\\UpdateShareRecipientPermission' => __DIR__ . '/..' . '/../lib/Command/UpdateShareRecipientPermission.php',
3940
'OCA\\Sharing\\Command\\UpdateShareRecipientSecret' => __DIR__ . '/..' . '/../lib/Command/UpdateShareRecipientSecret.php',
4041
'OCA\\Sharing\\Command\\UpdateShareState' => __DIR__ . '/..' . '/../lib/Command/UpdateShareState.php',
4142
'OCA\\Sharing\\Command\\UpdateShareUserStatus' => __DIR__ . '/..' . '/../lib/Command/UpdateShareUserStatus.php',
@@ -45,6 +46,7 @@ class ComposerStaticInitSharing
4546
'OCA\\Sharing\\Migration\\Version1000Date20260731171922' => __DIR__ . '/..' . '/../lib/Migration/Version1000Date20260731171922.php',
4647
'OCA\\Sharing\\Migration\\Version1000Date20260826073021' => __DIR__ . '/..' . '/../lib/Migration/Version1000Date20260826073021.php',
4748
'OCA\\Sharing\\Migration\\Version1000Date20260826115938' => __DIR__ . '/..' . '/../lib/Migration/Version1000Date20260826115938.php',
49+
'OCA\\Sharing\\Migration\\Version1000Date20260826122228' => __DIR__ . '/..' . '/../lib/Migration/Version1000Date20260826122228.php',
4850
'OCA\\Sharing\\ResponseDefinitions' => __DIR__ . '/..' . '/../lib/ResponseDefinitions.php',
4951
);
5052

apps/sharing/lib/Command/GetShares.php

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -84,7 +84,7 @@ public function execute(InputInterface $input, OutputInterface $output): int {
8484
$shares = $this->manager->getShares($this->accessContext, $filterSourceTypeClass, $filterSourceTypeValue, $filterState, $filterUserStatus, $lastShareID, $limit);
8585
$this->dbConnection->commit();
8686

87-
$data = Share::formatMultiple($this->registry, $this->l10nFactory, $this->urlGenerator, $this->userManager, $shares);
87+
$data = Share::formatMultiple($this->registry, $this->l10nFactory, $this->urlGenerator, $this->userManager, $this->accessContext, $shares);
8888
$this->writeArrayInOutputFormat($input, $output, $data);
8989
return Base::SUCCESS;
9090
} catch (Exception $exception) {

apps/sharing/lib/Command/SharingBase.php

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -71,7 +71,7 @@ protected function wrapExecution(InputInterface $input, OutputInterface $output,
7171
$share = $closure();
7272
$this->dbConnection->commit();
7373

74-
$data = $share->format($this->registry, $this->l10nFactory, $this->urlGenerator, $this->userManager);
74+
$data = $share->format($this->registry, $this->l10nFactory, $this->urlGenerator, $this->userManager, $this->accessContext);
7575
$this->writeArrayInOutputFormat($input, $output, $data);
7676

7777
return Base::SUCCESS;
Lines changed: 60 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,60 @@
1+
<?php
2+
3+
/**
4+
* SPDX-FileCopyrightText: 2025 Nextcloud GmbH and Nextcloud contributors
5+
* SPDX-License-Identifier: AGPL-3.0-or-later
6+
*/
7+
8+
declare(strict_types=1);
9+
10+
namespace OCA\Sharing\Command;
11+
12+
use NCU\Sharing\Permission\ISharePermissionType;
13+
use NCU\Sharing\Permission\SharePermission;
14+
use NCU\Sharing\Recipient\IShareRecipientType;
15+
use NCU\Sharing\Recipient\ShareRecipient;
16+
use NCU\Sharing\Share;
17+
use Symfony\Component\Console\Input\InputArgument;
18+
use Symfony\Component\Console\Input\InputInterface;
19+
use Symfony\Component\Console\Output\OutputInterface;
20+
21+
final class UpdateShareRecipientPermission extends SharingBase {
22+
#[\Override]
23+
public function configure(): void {
24+
$this
25+
->setName('sharing:update-share-recipient-permission')
26+
->setDescription('Update a permission for a recipient of a share.')
27+
->addArgument('id', InputArgument::REQUIRED, 'Share ID')
28+
->addArgument('permission-class', InputArgument::REQUIRED, 'Permission class')
29+
->addArgument('permission-enabled', InputArgument::REQUIRED, 'Permission enabled. Only takes "true" or "false".')
30+
->addArgument('recipient-class', InputArgument::REQUIRED, 'Recipient class')
31+
->addArgument('recipient-value', InputArgument::REQUIRED, 'Recipient value')
32+
->addArgument('recipient-instance', InputArgument::OPTIONAL, 'Recipient instance');
33+
parent::configure();
34+
}
35+
36+
#[\Override]
37+
public function execute(InputInterface $input, OutputInterface $output): int {
38+
/** @var string $id */
39+
$id = $input->getArgument('id');
40+
/** @var class-string<ISharePermissionType> $permissionClass */
41+
$permissionClass = $input->getArgument('permission-class');
42+
/** @var string $permissionEnabled */
43+
$permissionEnabled = $input->getArgument('permission-enabled');
44+
$permissionEnabled = $permissionEnabled === 'true';
45+
/** @var class-string<IShareRecipientType> $recipientClass */
46+
$recipientClass = $input->getArgument('recipient-class');
47+
/** @var non-empty-string $recipientValue */
48+
$recipientValue = $input->getArgument('recipient-value');
49+
/** @var ?non-empty-string $recipientInstance */
50+
$recipientInstance = $input->getArgument('recipient-instance');
51+
52+
$recipient = new ShareRecipient($recipientClass, $recipientValue, $recipientInstance);
53+
$permission = new SharePermission($permissionClass, $permissionEnabled);
54+
55+
return $this->wrapExecution($input, $output, function () use ($id, $recipient, $permission): Share {
56+
$share = $this->manager->getShare($this->accessContext, $id);
57+
return $this->manager->updateShareRecipientPermission($this->accessContext, $share, $recipient, $permission);
58+
});
59+
}
60+
}

apps/sharing/lib/Controller/ApiV1Controller.php

Lines changed: 50 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -152,7 +152,7 @@ public function createShare(): DataResponse {
152152
$share = $this->manager->createShare($this->accessContext);
153153

154154
$this->dbConnection->commit();
155-
return new DataResponse($share->format($this->registry, $this->l10nFactory, $this->urlGenerator, $this->userManager), Http::STATUS_CREATED);
155+
return new DataResponse($share->format($this->registry, $this->l10nFactory, $this->urlGenerator, $this->userManager, $this->accessContext), Http::STATUS_CREATED);
156156
} catch (Exception $exception) {
157157
$this->dbConnection->rollBack();
158158
throw $exception;
@@ -190,7 +190,7 @@ public function updateShareState(string $id, string $state): DataResponse {
190190
$share = $this->manager->getShare($this->accessContext, $id);
191191
$share = $this->manager->updateShareState($this->accessContext, $share, $shareState);
192192
$this->dbConnection->commit();
193-
return new DataResponse($share->format($this->registry, $this->l10nFactory, $this->urlGenerator, $this->userManager));
193+
return new DataResponse($share->format($this->registry, $this->l10nFactory, $this->urlGenerator, $this->userManager, $this->accessContext));
194194
} catch (Exception $exception) {
195195
$this->dbConnection->rollBack();
196196
throw $exception;
@@ -229,7 +229,7 @@ public function updateShareUserStatus(string $id, string $userStatus): DataRespo
229229
$share = $this->manager->getShare($this->accessContext, $id);
230230
$share = $this->manager->updateShareUserStatus($this->accessContext, $share, $shareUserStatus);
231231
$this->dbConnection->commit();
232-
return new DataResponse($share->format($this->registry, $this->l10nFactory, $this->urlGenerator, $this->userManager));
232+
return new DataResponse($share->format($this->registry, $this->l10nFactory, $this->urlGenerator, $this->userManager, $this->accessContext));
233233
} catch (Exception $exception) {
234234
$this->dbConnection->rollBack();
235235
throw $exception;
@@ -262,7 +262,7 @@ public function addShareSource(string $id, string $class, string $value): DataRe
262262
$share = $this->manager->getShare($this->accessContext, $id);
263263
$share = $this->manager->addShareSource($this->accessContext, $share, new ShareSource($class, $value));
264264
$this->dbConnection->commit();
265-
return new DataResponse($share->format($this->registry, $this->l10nFactory, $this->urlGenerator, $this->userManager));
265+
return new DataResponse($share->format($this->registry, $this->l10nFactory, $this->urlGenerator, $this->userManager, $this->accessContext));
266266
} catch (Exception $exception) {
267267
$this->dbConnection->rollBack();
268268
throw $exception;
@@ -298,7 +298,7 @@ public function removeShareSource(string $id, string $class, string $value): Dat
298298
$share = $this->manager->getShare($this->accessContext, $id);
299299
$share = $this->manager->removeShareSource($this->accessContext, $share, new ShareSource($class, $value));
300300
$this->dbConnection->commit();
301-
return new DataResponse($share->format($this->registry, $this->l10nFactory, $this->urlGenerator, $this->userManager));
301+
return new DataResponse($share->format($this->registry, $this->l10nFactory, $this->urlGenerator, $this->userManager, $this->accessContext));
302302
} catch (Exception $exception) {
303303
$this->dbConnection->rollBack();
304304
throw $exception;
@@ -334,7 +334,7 @@ public function addShareRecipient(string $id, string $class, string $value, ?str
334334
$share = $this->manager->getShare($this->accessContext, $id);
335335
$share = $this->manager->addShareRecipient($this->accessContext, $share, new ShareRecipient($class, $value, $instance));
336336
$this->dbConnection->commit();
337-
return new DataResponse($share->format($this->registry, $this->l10nFactory, $this->urlGenerator, $this->userManager));
337+
return new DataResponse($share->format($this->registry, $this->l10nFactory, $this->urlGenerator, $this->userManager, $this->accessContext));
338338
} catch (Exception $exception) {
339339
$this->dbConnection->rollBack();
340340
throw $exception;
@@ -371,7 +371,7 @@ public function removeShareRecipient(string $id, string $class, string $value, ?
371371
$share = $this->manager->getShare($this->accessContext, $id);
372372
$share = $this->manager->removeShareRecipient($this->accessContext, $share, new ShareRecipient($class, $value, $instance));
373373
$this->dbConnection->commit();
374-
return new DataResponse($share->format($this->registry, $this->l10nFactory, $this->urlGenerator, $this->userManager));
374+
return new DataResponse($share->format($this->registry, $this->l10nFactory, $this->urlGenerator, $this->userManager, $this->accessContext));
375375
} catch (Exception $exception) {
376376
$this->dbConnection->rollBack();
377377
throw $exception;
@@ -408,7 +408,7 @@ public function updateShareRecipientSecret(string $id, string $class, string $va
408408
$share = $this->manager->getShare($this->accessContext, $id);
409409
$share = $this->manager->updateShareRecipientSecret($this->accessContext, $share, new ShareRecipient($class, $value, $instance), $secret);
410410
$this->dbConnection->commit();
411-
return new DataResponse($share->format($this->registry, $this->l10nFactory, $this->urlGenerator, $this->userManager));
411+
return new DataResponse($share->format($this->registry, $this->l10nFactory, $this->urlGenerator, $this->userManager, $this->accessContext));
412412
} catch (Exception $exception) {
413413
$this->dbConnection->rollBack();
414414
throw $exception;
@@ -445,7 +445,7 @@ public function updateShareProperty(string $id, string $class, ?string $value):
445445
$share = $this->manager->getShare($this->accessContext, $id);
446446
$share = $this->manager->updateShareProperty($this->accessContext, $share, new ShareProperty($class, $value));
447447
$this->dbConnection->commit();
448-
return new DataResponse($share->format($this->registry, $this->l10nFactory, $this->urlGenerator, $this->userManager));
448+
return new DataResponse($share->format($this->registry, $this->l10nFactory, $this->urlGenerator, $this->userManager, $this->accessContext));
449449
} catch (Exception $exception) {
450450
$this->dbConnection->rollBack();
451451
throw $exception;
@@ -482,7 +482,7 @@ public function updateSharePermission(string $id, string $class, bool $enabled):
482482
$share = $this->manager->getShare($this->accessContext, $id);
483483
$share = $this->manager->updateSharePermission($this->accessContext, $share, new SharePermission($class, $enabled));
484484
$this->dbConnection->commit();
485-
return new DataResponse($share->format($this->registry, $this->l10nFactory, $this->urlGenerator, $this->userManager));
485+
return new DataResponse($share->format($this->registry, $this->l10nFactory, $this->urlGenerator, $this->userManager, $this->accessContext));
486486
} catch (Exception $exception) {
487487
$this->dbConnection->rollBack();
488488
throw $exception;
@@ -496,6 +496,43 @@ public function updateSharePermission(string $id, string $class, bool $enabled):
496496
}
497497
}
498498

499+
/**
500+
* Update a permission for a recipient of a share.
501+
*
502+
* @param string $id ID of the share
503+
* @param class-string<IShareRecipientType> $recipientClass Type class of the recipient
504+
* @param non-empty-string $recipientValue Value of the recipient
505+
* @param ?non-empty-string $recipientInstance Instance of the recipient
506+
* @param class-string<ISharePermissionType> $permissionClass Type class of the permission
507+
* @param bool $enabled Enabled state of the permission
508+
* @return DataResponse<Http::STATUS_OK, SharingShare, array{}>|DataResponse<Http::STATUS_FORBIDDEN|Http::STATUS_NOT_FOUND, string, array{}>
509+
*
510+
* 200: Share recipient permission updated successfully
511+
* 403: Updating the share recipient permission is not allowed
512+
* 404: Share not found
513+
*/
514+
#[NoAdminRequired]
515+
#[ApiRoute(verb: 'PUT', url: '/api/v1/share/{id}/recipient/permission')]
516+
public function updateShareRecipientPermission(string $id, string $recipientClass, string $recipientValue, ?string $recipientInstance, string $permissionClass, bool $enabled): DataResponse {
517+
try {
518+
try {
519+
$this->dbConnection->beginTransaction();
520+
521+
$share = $this->manager->getShare($this->accessContext, $id);
522+
$share = $this->manager->updateShareRecipientPermission($this->accessContext, $share, new ShareRecipient($recipientClass, $recipientValue, $recipientInstance), new SharePermission($permissionClass, $enabled));
523+
$this->dbConnection->commit();
524+
return new DataResponse($share->format($this->registry, $this->l10nFactory, $this->urlGenerator, $this->userManager, $this->accessContext));
525+
} catch (Exception $exception) {
526+
$this->dbConnection->rollBack();
527+
throw $exception;
528+
}
529+
} catch (ShareOperationForbiddenException $shareOperationForbiddenException) {
530+
return new DataResponse($shareOperationForbiddenException->getHint(), Http::STATUS_FORBIDDEN);
531+
} catch (ShareNotFoundException $shareNotFoundException) {
532+
return new DataResponse($shareNotFoundException->getHint(), Http::STATUS_NOT_FOUND);
533+
}
534+
}
535+
499536
/**
500537
* Select a permission preset for a share.
501538
*
@@ -518,7 +555,7 @@ public function selectSharePermissionPreset(string $id, string $permissionPreset
518555
$share = $this->manager->getShare($this->accessContext, $id);
519556
$share = $this->manager->selectSharePermissionPreset($this->accessContext, $share, $permissionPresetClass);
520557
$this->dbConnection->commit();
521-
return new DataResponse($share->format($this->registry, $this->l10nFactory, $this->urlGenerator, $this->userManager));
558+
return new DataResponse($share->format($this->registry, $this->l10nFactory, $this->urlGenerator, $this->userManager, $this->accessContext));
522559
} catch (Exception $exception) {
523560
$this->dbConnection->rollBack();
524561
throw $exception;
@@ -583,7 +620,7 @@ public function getShare(string $id, ?string $secret = null, array $arguments =
583620

584621
$share = $this->manager->getShare(new ShareAccessContext($this->accessContext->currentUser, $secret, $arguments, $this->accessContext->overrideChecks), $id);
585622
$this->dbConnection->commit();
586-
return new DataResponse($share->format($this->registry, $this->l10nFactory, $this->urlGenerator, $this->userManager));
623+
return new DataResponse($share->format($this->registry, $this->l10nFactory, $this->urlGenerator, $this->userManager, $this->accessContext));
587624
} catch (Exception $exception) {
588625
$this->dbConnection->rollBack();
589626
throw $exception;
@@ -650,7 +687,7 @@ public function getShares(?string $filterSourceTypeClass, ?string $filterSourceT
650687

651688
$shares = $this->manager->getShares($this->accessContext, $filterSourceTypeClass, $filterSourceTypeValue, $filterState, $filterUserStatus, $lastShareID, $limit);
652689
$this->dbConnection->commit();
653-
return new DataResponse(Share::formatMultiple($this->registry, $this->l10nFactory, $this->urlGenerator, $this->userManager, $shares));
690+
return new DataResponse(Share::formatMultiple($this->registry, $this->l10nFactory, $this->urlGenerator, $this->userManager, $this->accessContext, $shares));
654691
} catch (Exception $exception) {
655692
$this->dbConnection->rollBack();
656693
throw $exception;

apps/sharing/lib/Migration/Version1000Date20250929161325.php

Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -53,7 +53,6 @@ public function changeSchema(IOutput $output, Closure $schemaClosure, array $opt
5353
$sourcesTable->addForeignKeyConstraint($shareTable->getName(), ['share_id'], ['id'], ['onDelete' => 'CASCADE']);
5454
$sourcesTable->addForeignKeyConstraint($mappingTable->getName(), ['source_class_id'], ['class_id']);
5555

56-
// TODO: Add possibility to mask permissions for recipients. For reshares the user may only mask permissions for their child recipients, not their self recipients
5756
$recipientsTable = $schema->createTable('sharing_share_recipients');
5857
$recipientsTable->addColumn('id', Types::BIGINT);
5958
$recipientsTable->addColumn('share_id', Types::BIGINT);

0 commit comments

Comments
 (0)