Skip to content

Commit 4da8f5a

Browse files
authored
Merge pull request #63854 from nextcloud/unified-sharing-per-recipient-permissions
feat(Sharing): Add per-recipient permissions
2 parents e936bc8 + 4d05a09 commit 4da8f5a

29 files changed

Lines changed: 1554 additions & 231 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.1</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: 3 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',
@@ -29,5 +30,7 @@
2930
'OCA\\Sharing\\Migration\\Version1000Date20250929161325' => $baseDir . '/../lib/Migration/Version1000Date20250929161325.php',
3031
'OCA\\Sharing\\Migration\\Version1000Date20260731171922' => $baseDir . '/../lib/Migration/Version1000Date20260731171922.php',
3132
'OCA\\Sharing\\Migration\\Version1000Date20260826073021' => $baseDir . '/../lib/Migration/Version1000Date20260826073021.php',
33+
'OCA\\Sharing\\Migration\\Version1000Date20260826115938' => $baseDir . '/../lib/Migration/Version1000Date20260826115938.php',
34+
'OCA\\Sharing\\Migration\\Version1000Date20260826122228' => $baseDir . '/../lib/Migration/Version1000Date20260826122228.php',
3235
'OCA\\Sharing\\ResponseDefinitions' => $baseDir . '/../lib/ResponseDefinitions.php',
3336
);

apps/sharing/composer/composer/autoload_static.php

Lines changed: 3 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',
@@ -44,6 +45,8 @@ class ComposerStaticInitSharing
4445
'OCA\\Sharing\\Migration\\Version1000Date20250929161325' => __DIR__ . '/..' . '/../lib/Migration/Version1000Date20250929161325.php',
4546
'OCA\\Sharing\\Migration\\Version1000Date20260731171922' => __DIR__ . '/..' . '/../lib/Migration/Version1000Date20260731171922.php',
4647
'OCA\\Sharing\\Migration\\Version1000Date20260826073021' => __DIR__ . '/..' . '/../lib/Migration/Version1000Date20260826073021.php',
48+
'OCA\\Sharing\\Migration\\Version1000Date20260826115938' => __DIR__ . '/..' . '/../lib/Migration/Version1000Date20260826115938.php',
49+
'OCA\\Sharing\\Migration\\Version1000Date20260826122228' => __DIR__ . '/..' . '/../lib/Migration/Version1000Date20260826122228.php',
4750
'OCA\\Sharing\\ResponseDefinitions' => __DIR__ . '/..' . '/../lib/ResponseDefinitions.php',
4851
);
4952

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
@@ -156,7 +156,7 @@ public function createShare(): DataResponse {
156156
$share = $this->manager->createShare($this->accessContext);
157157

158158
$this->dbConnection->commit();
159-
return new DataResponse($share->format($this->registry, $this->l10nFactory, $this->urlGenerator, $this->userManager), Http::STATUS_CREATED);
159+
return new DataResponse($share->format($this->registry, $this->l10nFactory, $this->urlGenerator, $this->userManager, $this->accessContext), Http::STATUS_CREATED);
160160
} catch (Exception $exception) {
161161
$this->dbConnection->rollBack();
162162
throw $exception;
@@ -195,7 +195,7 @@ public function updateShareState(string $id, string $state): DataResponse {
195195
$share = $this->manager->getShare($this->accessContext, $id);
196196
$share = $this->manager->updateShareState($this->accessContext, $share, $shareState);
197197
$this->dbConnection->commit();
198-
return new DataResponse($share->format($this->registry, $this->l10nFactory, $this->urlGenerator, $this->userManager));
198+
return new DataResponse($share->format($this->registry, $this->l10nFactory, $this->urlGenerator, $this->userManager, $this->accessContext));
199199
} catch (Exception $exception) {
200200
$this->dbConnection->rollBack();
201201
throw $exception;
@@ -235,7 +235,7 @@ public function updateShareUserStatus(string $id, string $userStatus): DataRespo
235235
$share = $this->manager->getShare($this->accessContext, $id);
236236
$share = $this->manager->updateShareUserStatus($this->accessContext, $share, $shareUserStatus);
237237
$this->dbConnection->commit();
238-
return new DataResponse($share->format($this->registry, $this->l10nFactory, $this->urlGenerator, $this->userManager));
238+
return new DataResponse($share->format($this->registry, $this->l10nFactory, $this->urlGenerator, $this->userManager, $this->accessContext));
239239
} catch (Exception $exception) {
240240
$this->dbConnection->rollBack();
241241
throw $exception;
@@ -269,7 +269,7 @@ public function addShareSource(string $id, string $class, string $value): DataRe
269269
$share = $this->manager->getShare($this->accessContext, $id);
270270
$share = $this->manager->addShareSource($this->accessContext, $share, new ShareSource($class, $value));
271271
$this->dbConnection->commit();
272-
return new DataResponse($share->format($this->registry, $this->l10nFactory, $this->urlGenerator, $this->userManager));
272+
return new DataResponse($share->format($this->registry, $this->l10nFactory, $this->urlGenerator, $this->userManager, $this->accessContext));
273273
} catch (Exception $exception) {
274274
$this->dbConnection->rollBack();
275275
throw $exception;
@@ -306,7 +306,7 @@ public function removeShareSource(string $id, string $class, string $value): Dat
306306
$share = $this->manager->getShare($this->accessContext, $id);
307307
$share = $this->manager->removeShareSource($this->accessContext, $share, new ShareSource($class, $value));
308308
$this->dbConnection->commit();
309-
return new DataResponse($share->format($this->registry, $this->l10nFactory, $this->urlGenerator, $this->userManager));
309+
return new DataResponse($share->format($this->registry, $this->l10nFactory, $this->urlGenerator, $this->userManager, $this->accessContext));
310310
} catch (Exception $exception) {
311311
$this->dbConnection->rollBack();
312312
throw $exception;
@@ -343,7 +343,7 @@ public function addShareRecipient(string $id, string $class, string $value, ?str
343343
$share = $this->manager->getShare($this->accessContext, $id);
344344
$share = $this->manager->addShareRecipient($this->accessContext, $share, new ShareRecipient($class, $value, $instance));
345345
$this->dbConnection->commit();
346-
return new DataResponse($share->format($this->registry, $this->l10nFactory, $this->urlGenerator, $this->userManager));
346+
return new DataResponse($share->format($this->registry, $this->l10nFactory, $this->urlGenerator, $this->userManager, $this->accessContext));
347347
} catch (Exception $exception) {
348348
$this->dbConnection->rollBack();
349349
throw $exception;
@@ -381,7 +381,7 @@ public function removeShareRecipient(string $id, string $class, string $value, ?
381381
$share = $this->manager->getShare($this->accessContext, $id);
382382
$share = $this->manager->removeShareRecipient($this->accessContext, $share, new ShareRecipient($class, $value, $instance));
383383
$this->dbConnection->commit();
384-
return new DataResponse($share->format($this->registry, $this->l10nFactory, $this->urlGenerator, $this->userManager));
384+
return new DataResponse($share->format($this->registry, $this->l10nFactory, $this->urlGenerator, $this->userManager, $this->accessContext));
385385
} catch (Exception $exception) {
386386
$this->dbConnection->rollBack();
387387
throw $exception;
@@ -419,7 +419,7 @@ public function updateShareRecipientSecret(string $id, string $class, string $va
419419
$share = $this->manager->getShare($this->accessContext, $id);
420420
$share = $this->manager->updateShareRecipientSecret($this->accessContext, $share, new ShareRecipient($class, $value, $instance), $secret);
421421
$this->dbConnection->commit();
422-
return new DataResponse($share->format($this->registry, $this->l10nFactory, $this->urlGenerator, $this->userManager));
422+
return new DataResponse($share->format($this->registry, $this->l10nFactory, $this->urlGenerator, $this->userManager, $this->accessContext));
423423
} catch (Exception $exception) {
424424
$this->dbConnection->rollBack();
425425
throw $exception;
@@ -457,7 +457,7 @@ public function updateShareProperty(string $id, string $class, ?string $value):
457457
$share = $this->manager->getShare($this->accessContext, $id);
458458
$share = $this->manager->updateShareProperty($this->accessContext, $share, new ShareProperty($class, $value));
459459
$this->dbConnection->commit();
460-
return new DataResponse($share->format($this->registry, $this->l10nFactory, $this->urlGenerator, $this->userManager));
460+
return new DataResponse($share->format($this->registry, $this->l10nFactory, $this->urlGenerator, $this->userManager, $this->accessContext));
461461
} catch (Exception $exception) {
462462
$this->dbConnection->rollBack();
463463
throw $exception;
@@ -495,7 +495,7 @@ public function updateSharePermission(string $id, string $class, bool $enabled):
495495
$share = $this->manager->getShare($this->accessContext, $id);
496496
$share = $this->manager->updateSharePermission($this->accessContext, $share, new SharePermission($class, $enabled));
497497
$this->dbConnection->commit();
498-
return new DataResponse($share->format($this->registry, $this->l10nFactory, $this->urlGenerator, $this->userManager));
498+
return new DataResponse($share->format($this->registry, $this->l10nFactory, $this->urlGenerator, $this->userManager, $this->accessContext));
499499
} catch (Exception $exception) {
500500
$this->dbConnection->rollBack();
501501
throw $exception;
@@ -509,6 +509,43 @@ public function updateSharePermission(string $id, string $class, bool $enabled):
509509
}
510510
}
511511

512+
/**
513+
* Update a permission for a recipient of a share.
514+
*
515+
* @param string $id ID of the share
516+
* @param class-string<IShareRecipientType> $recipientClass Type class of the recipient
517+
* @param non-empty-string $recipientValue Value of the recipient
518+
* @param ?non-empty-string $recipientInstance Instance of the recipient
519+
* @param class-string<ISharePermissionType> $permissionClass Type class of the permission
520+
* @param bool $enabled Enabled state of the permission
521+
* @return DataResponse<Http::STATUS_OK, SharingShare, array{}>|DataResponse<Http::STATUS_FORBIDDEN|Http::STATUS_NOT_FOUND, string, array{}>
522+
*
523+
* 200: Share recipient permission updated successfully
524+
* 403: Updating the share recipient permission is not allowed
525+
* 404: Share not found
526+
*/
527+
#[NoAdminRequired]
528+
#[ApiRoute(verb: 'PUT', url: '/api/v1/share/{id}/recipient/permission')]
529+
public function updateShareRecipientPermission(string $id, string $recipientClass, string $recipientValue, ?string $recipientInstance, string $permissionClass, bool $enabled): DataResponse {
530+
try {
531+
try {
532+
$this->dbConnection->beginTransaction();
533+
534+
$share = $this->manager->getShare($this->accessContext, $id);
535+
$share = $this->manager->updateShareRecipientPermission($this->accessContext, $share, new ShareRecipient($recipientClass, $recipientValue, $recipientInstance), new SharePermission($permissionClass, $enabled));
536+
$this->dbConnection->commit();
537+
return new DataResponse($share->format($this->registry, $this->l10nFactory, $this->urlGenerator, $this->userManager, $this->accessContext));
538+
} catch (Exception $exception) {
539+
$this->dbConnection->rollBack();
540+
throw $exception;
541+
}
542+
} catch (ShareOperationForbiddenException $shareOperationForbiddenException) {
543+
return new DataResponse($shareOperationForbiddenException->getHint(), Http::STATUS_FORBIDDEN);
544+
} catch (ShareNotFoundException $shareNotFoundException) {
545+
return new DataResponse($shareNotFoundException->getHint(), Http::STATUS_NOT_FOUND);
546+
}
547+
}
548+
512549
/**
513550
* Select a permission preset for a share.
514551
*
@@ -532,7 +569,7 @@ public function selectSharePermissionPreset(string $id, string $permissionPreset
532569
$share = $this->manager->getShare($this->accessContext, $id);
533570
$share = $this->manager->selectSharePermissionPreset($this->accessContext, $share, $permissionPresetClass);
534571
$this->dbConnection->commit();
535-
return new DataResponse($share->format($this->registry, $this->l10nFactory, $this->urlGenerator, $this->userManager));
572+
return new DataResponse($share->format($this->registry, $this->l10nFactory, $this->urlGenerator, $this->userManager, $this->accessContext));
536573
} catch (Exception $exception) {
537574
$this->dbConnection->rollBack();
538575
throw $exception;
@@ -601,7 +638,7 @@ public function getShare(string $id, ?string $secret = null, array $arguments =
601638

602639
$share = $this->manager->getShare(new ShareAccessContext($this->accessContext->currentUser, $secret, $arguments, $this->accessContext->overrideChecks), $id);
603640
$this->dbConnection->commit();
604-
return new DataResponse($share->format($this->registry, $this->l10nFactory, $this->urlGenerator, $this->userManager));
641+
return new DataResponse($share->format($this->registry, $this->l10nFactory, $this->urlGenerator, $this->userManager, $this->accessContext));
605642
} catch (Exception $exception) {
606643
$this->dbConnection->rollBack();
607644
throw $exception;
@@ -672,7 +709,7 @@ public function getShares(?string $filterSourceTypeClass, ?string $filterSourceT
672709

673710
$shares = $this->manager->getShares($this->accessContext, $filterSourceTypeClass, $filterSourceTypeValue, $filterState, $filterUserStatus, $lastShareID, $limit);
674711
$this->dbConnection->commit();
675-
return new DataResponse(Share::formatMultiple($this->registry, $this->l10nFactory, $this->urlGenerator, $this->userManager, $shares));
712+
return new DataResponse(Share::formatMultiple($this->registry, $this->l10nFactory, $this->urlGenerator, $this->userManager, $this->accessContext, $shares));
676713
} catch (Exception $exception) {
677714
$this->dbConnection->rollBack();
678715
throw $exception;

0 commit comments

Comments
 (0)