refactor: use Share for most SharingManager parameters instead of just the id

Signed-off-by: Robin Appelman <robin@icewind.nl>

# Conflicts:
#	tests/lib/Sharing/AbstractSharingManagerTests.php

# Conflicts:
#	apps/sharing/tests/Controller/ApiV1ControllerTest.php
#	tests/lib/Sharing/AbstractSharingManagerTests.php
pull/63140/head
Robin Appelman 1 week ago
parent cbbb991ed5
commit 55e1270b94
No known key found for this signature in database
GPG Key ID: 42B69D8A64526EFB
  1. 8
      apps/files/tests/Sharing/Source/NodeShareSourceTypeTest.php
  2. 7
      apps/sharing/lib/Command/AddShareRecipient.php
  3. 7
      apps/sharing/lib/Command/AddShareSource.php
  4. 3
      apps/sharing/lib/Command/CreateShare.php
  5. 3
      apps/sharing/lib/Command/DeleteShare.php
  6. 3
      apps/sharing/lib/Command/GetShare.php
  7. 7
      apps/sharing/lib/Command/RemoveShareRecipient.php
  8. 7
      apps/sharing/lib/Command/RemoveShareSource.php
  9. 7
      apps/sharing/lib/Command/SelectSharePermissionPreset.php
  10. 6
      apps/sharing/lib/Command/SharingBase.php
  11. 7
      apps/sharing/lib/Command/UpdateSharePermission.php
  12. 7
      apps/sharing/lib/Command/UpdateShareProperty.php
  13. 7
      apps/sharing/lib/Command/UpdateShareRecipientSecret.php
  14. 7
      apps/sharing/lib/Command/UpdateShareState.php
  15. 28
      apps/sharing/lib/Controller/ApiV1Controller.php
  16. 44
      apps/sharing/tests/Command/CommandTest.php
  17. 44
      apps/sharing/tests/Controller/ApiV1ControllerTest.php
  18. 193
      lib/private/Sharing/SharingBackend.php
  19. 387
      lib/private/Sharing/SharingManager.php
  20. 44
      lib/private/Sharing/SharingRegistry.php
  21. 6
      lib/unstable/Sharing/Event/SharesDefaultSetEvent.php
  22. 9
      lib/unstable/Sharing/ISharingBackend.php
  23. 26
      lib/unstable/Sharing/ISharingManager.php
  24. 15
      lib/unstable/Sharing/ISharingRegistry.php
  25. 7
      lib/unstable/Sharing/Recipient/ShareRecipient.php
  26. 7
      lib/unstable/Sharing/Source/ShareSource.php
  27. 8
      tests/Core/Sharing/Recipient/GroupShareRecipientTypeTest.php
  28. 8
      tests/Core/Sharing/Recipient/TeamShareRecipientTypeTest.php
  29. 8
      tests/Core/Sharing/Recipient/UserShareRecipientTypeTest.php
  30. 1105
      tests/lib/Sharing/AbstractSharingManagerTests.php
  31. 113
      tests/lib/Sharing/SharingManagerTest.php

@ -108,8 +108,8 @@ final class NodeShareSourceTypeTest extends TestCase {
$accessContext = new ShareAccessContext(currentUser: $this->user1);
$this->dbConnection->beginTransaction();
$id = $this->manager->createShare($accessContext);
$this->manager->addShareSource($accessContext, $id, new ShareSource($this->sourceType::class, (string)$this->node->getId()));
$share = $this->manager->createShare($accessContext);
$this->manager->addShareSource($accessContext, $share, new ShareSource($this->sourceType::class, (string)$this->node->getId()));
$this->dbConnection->commit();
$before = $this->manager->getTime();
@ -117,12 +117,12 @@ final class NodeShareSourceTypeTest extends TestCase {
$after = $this->manager->getTime();
$this->dbConnection->beginTransaction();
$share = $this->manager->getShare($accessContext, $id);
$share = $this->manager->getShare($accessContext, $share->id);
$this->assertGreaterThanOrEqual(SharingManager::timeToMs($before), SharingManager::timeToMs($share->lastUpdated));
$this->assertLessThanOrEqual(SharingManager::timeToMs($after), SharingManager::timeToMs($share->lastUpdated));
$this->assertEquals([], $share->sources);
$this->manager->deleteShare($accessContext, $id);
$this->manager->deleteShare($accessContext, $share);
$this->dbConnection->commit();
$registry->clear();
}

@ -11,6 +11,7 @@ namespace OCA\Sharing\Command;
use NCU\Sharing\Recipient\IShareRecipientType;
use NCU\Sharing\Recipient\ShareRecipient;
use NCU\Sharing\Share;
use Symfony\Component\Console\Input\InputArgument;
use Symfony\Component\Console\Input\InputInterface;
use Symfony\Component\Console\Output\OutputInterface;
@ -39,9 +40,9 @@ final class AddShareRecipient extends SharingBase {
/** @var ?non-empty-string $instance */
$instance = $input->getArgument('instance');
return $this->wrapExecution($output, function () use ($id, $class, $value, $instance): string {
$this->manager->addShareRecipient($this->accessContext, $id, new ShareRecipient($class, $value, $instance));
return $id;
return $this->wrapExecution($output, function () use ($id, $class, $value, $instance): Share {
$share = $this->manager->getShare($this->accessContext, $id);
return $this->manager->addShareRecipient($this->accessContext, $share, new ShareRecipient($class, $value, $instance));
});
}
}

@ -9,6 +9,7 @@ declare(strict_types=1);
namespace OCA\Sharing\Command;
use NCU\Sharing\Share;
use NCU\Sharing\Source\IShareSourceType;
use NCU\Sharing\Source\ShareSource;
use Symfony\Component\Console\Input\InputArgument;
@ -35,9 +36,9 @@ final class AddShareSource extends SharingBase {
/** @var non-empty-string $value */
$value = $input->getArgument('value');
return $this->wrapExecution($output, function () use ($id, $class, $value): string {
$this->manager->addShareSource($this->accessContext, $id, new ShareSource($class, $value));
return $id;
return $this->wrapExecution($output, function () use ($id, $class, $value): Share {
$share = $this->manager->getShare($this->accessContext, $id);
return $this->manager->addShareSource($this->accessContext, $share, new ShareSource($class, $value));
});
}
}

@ -10,6 +10,7 @@ declare(strict_types=1);
namespace OCA\Sharing\Command;
use NCU\Sharing\Exception\ShareInvalidException;
use NCU\Sharing\Share;
use NCU\Sharing\ShareAccessContext;
use OCP\IUserManager;
use OCP\L10N\IFactory;
@ -36,6 +37,6 @@ final class CreateShare extends SharingBase {
throw new ShareInvalidException('The owner does not exist: ' . $ownerUid, Server::get(IFactory::class)->get('sharing')->t('The owner does not exist: %s', [$ownerUid]));
}
return $this->wrapExecution($output, fn (): string => $this->manager->createShare(new ShareAccessContext($owner)));
return $this->wrapExecution($output, fn (): Share => $this->manager->createShare(new ShareAccessContext($owner)));
}
}

@ -35,7 +35,8 @@ final class DeleteShare extends SharingBase {
try {
$this->dbConnection->beginTransaction();
$this->manager->deleteShare($this->accessContext, $id);
$share = $this->manager->getShare($this->accessContext, $id);
$this->manager->deleteShare($this->accessContext, $share);
$this->dbConnection->commit();
return Base::SUCCESS;
} catch (Exception $exception) {

@ -9,6 +9,7 @@ declare(strict_types=1);
namespace OCA\Sharing\Command;
use NCU\Sharing\Share;
use Symfony\Component\Console\Input\InputArgument;
use Symfony\Component\Console\Input\InputInterface;
use Symfony\Component\Console\Output\OutputInterface;
@ -27,6 +28,6 @@ final class GetShare extends SharingBase {
/** @var string $id */
$id = $input->getArgument('id');
return $this->wrapExecution($output, fn (): string => $id);
return $this->wrapExecution($output, fn (): Share => $this->manager->getShare($this->accessContext, $id));
}
}

@ -11,6 +11,7 @@ namespace OCA\Sharing\Command;
use NCU\Sharing\Recipient\IShareRecipientType;
use NCU\Sharing\Recipient\ShareRecipient;
use NCU\Sharing\Share;
use Symfony\Component\Console\Input\InputArgument;
use Symfony\Component\Console\Input\InputInterface;
use Symfony\Component\Console\Output\OutputInterface;
@ -38,9 +39,9 @@ final class RemoveShareRecipient extends SharingBase {
/** @var ?non-empty-string $instance */
$instance = $input->getArgument('instance');
return $this->wrapExecution($output, function () use ($id, $class, $value, $instance): string {
$this->manager->removeShareRecipient($this->accessContext, $id, new ShareRecipient($class, $value, $instance));
return $id;
return $this->wrapExecution($output, function () use ($id, $class, $value, $instance): Share {
$share = $this->manager->getShare($this->accessContext, $id);
return $this->manager->removeShareRecipient($this->accessContext, $share, new ShareRecipient($class, $value, $instance));
});
}
}

@ -9,6 +9,7 @@ declare(strict_types=1);
namespace OCA\Sharing\Command;
use NCU\Sharing\Share;
use NCU\Sharing\Source\IShareSourceType;
use NCU\Sharing\Source\ShareSource;
use Symfony\Component\Console\Input\InputArgument;
@ -35,9 +36,9 @@ final class RemoveShareSource extends SharingBase {
/** @var non-empty-string $value */
$value = $input->getArgument('value');
return $this->wrapExecution($output, function () use ($id, $class, $value): string {
$this->manager->removeShareSource($this->accessContext, $id, new ShareSource($class, $value));
return $id;
return $this->wrapExecution($output, function () use ($id, $class, $value): Share {
$share = $this->manager->getShare($this->accessContext, $id);
return $this->manager->removeShareSource($this->accessContext, $share, new ShareSource($class, $value));
});
}
}

@ -10,6 +10,7 @@ declare(strict_types=1);
namespace OCA\Sharing\Command;
use NCU\Sharing\Permission\ISharePermissionPreset;
use NCU\Sharing\Share;
use Symfony\Component\Console\Input\InputArgument;
use Symfony\Component\Console\Input\InputInterface;
use Symfony\Component\Console\Output\OutputInterface;
@ -31,9 +32,9 @@ final class SelectSharePermissionPreset extends SharingBase {
/** @var class-string<ISharePermissionPreset> $permissionPresetClass */
$permissionPresetClass = $input->getArgument('permission-preset');
return $this->wrapExecution($output, function () use ($id, $permissionPresetClass): string {
$this->manager->selectSharePermissionPreset($this->accessContext, $id, $permissionPresetClass);
return $id;
return $this->wrapExecution($output, function () use ($id, $permissionPresetClass): Share {
$share = $this->manager->getShare($this->accessContext, $id);
return $this->manager->selectSharePermissionPreset($this->accessContext, $share, $permissionPresetClass);
});
}
}

@ -14,6 +14,7 @@ use Exception;
use NCU\Sharing\Exception\AShareException;
use NCU\Sharing\ISharingManager;
use NCU\Sharing\ISharingRegistry;
use NCU\Sharing\Share;
use NCU\Sharing\ShareAccessContext;
use OC\Core\Command\Base;
use OCP\IDBConnection;
@ -40,7 +41,7 @@ abstract class SharingBase extends Command {
}
/**
* @param Closure():string $closure
* @param Closure():Share $closure
*/
protected function wrapExecution(OutputInterface $output, Closure $closure): int {
@ -48,8 +49,7 @@ abstract class SharingBase extends Command {
try {
$this->dbConnection->beginTransaction();
$id = $closure();
$share = $this->manager->getShare($this->accessContext, $id);
$share = $closure();
$this->dbConnection->commit();
$output->writeln(json_encode($share->format($this->registry, $this->l10nFactory, $this->urlGenerator, $this->userManager), JSON_THROW_ON_ERROR));
return Base::SUCCESS;

@ -11,6 +11,7 @@ namespace OCA\Sharing\Command;
use NCU\Sharing\Permission\ISharePermissionType;
use NCU\Sharing\Permission\SharePermission;
use NCU\Sharing\Share;
use Symfony\Component\Console\Input\InputArgument;
use Symfony\Component\Console\Input\InputInterface;
use Symfony\Component\Console\Output\OutputInterface;
@ -36,9 +37,9 @@ final class UpdateSharePermission extends SharingBase {
$enabled = $input->getArgument('enabled');
$enabled = $enabled === 'true';
return $this->wrapExecution($output, function () use ($id, $class, $enabled): string {
$this->manager->updateSharePermission($this->accessContext, $id, new SharePermission($class, $enabled));
return $id;
return $this->wrapExecution($output, function () use ($id, $class, $enabled): Share {
$share = $this->manager->getShare($this->accessContext, $id);
return $this->manager->updateSharePermission($this->accessContext, $share, new SharePermission($class, $enabled));
});
}
}

@ -11,6 +11,7 @@ namespace OCA\Sharing\Command;
use NCU\Sharing\Property\ISharePropertyType;
use NCU\Sharing\Property\ShareProperty;
use NCU\Sharing\Share;
use Symfony\Component\Console\Input\InputArgument;
use Symfony\Component\Console\Input\InputInterface;
use Symfony\Component\Console\Output\OutputInterface;
@ -35,9 +36,9 @@ final class UpdateShareProperty extends SharingBase {
/** @var ?string $value */
$value = $input->getArgument('value');
return $this->wrapExecution($output, function () use ($id, $class, $value): string {
$this->manager->updateShareProperty($this->accessContext, $id, new ShareProperty($class, $value));
return $id;
return $this->wrapExecution($output, function () use ($id, $class, $value): Share {
$share = $this->manager->getShare($this->accessContext, $id);
return $this->manager->updateShareProperty($this->accessContext, $share, new ShareProperty($class, $value));
});
}
}

@ -11,6 +11,7 @@ namespace OCA\Sharing\Command;
use NCU\Sharing\Recipient\IShareRecipientType;
use NCU\Sharing\Recipient\ShareRecipient;
use NCU\Sharing\Share;
use Symfony\Component\Console\Input\InputArgument;
use Symfony\Component\Console\Input\InputInterface;
use Symfony\Component\Console\Output\OutputInterface;
@ -41,9 +42,9 @@ final class UpdateShareRecipientSecret extends SharingBase {
/** @var non-empty-string $secret */
$secret = $input->getArgument('secret');
return $this->wrapExecution($output, function () use ($id, $class, $value, $instance, $secret): string {
$this->manager->updateShareRecipientSecret($this->accessContext, $id, new ShareRecipient($class, $value, $instance), $secret);
return $id;
return $this->wrapExecution($output, function () use ($id, $class, $value, $instance, $secret): Share {
$share = $this->manager->getShare($this->accessContext, $id);
return $this->manager->updateShareRecipientSecret($this->accessContext, $share, new ShareRecipient($class, $value, $instance), $secret);
});
}
}

@ -9,6 +9,7 @@ declare(strict_types=1);
namespace OCA\Sharing\Command;
use NCU\Sharing\Share;
use NCU\Sharing\ShareState;
use Symfony\Component\Console\Input\InputArgument;
use Symfony\Component\Console\Input\InputInterface;
@ -32,9 +33,9 @@ final class UpdateShareState extends SharingBase {
$state = $input->getArgument('state');
$state = ShareState::from($state);
return $this->wrapExecution($output, function () use ($id, $state): string {
$this->manager->updateShareState($this->accessContext, $id, $state);
return $id;
return $this->wrapExecution($output, function () use ($id, $state): Share {
$share = $this->manager->getShare($this->accessContext, $id);
return $this->manager->updateShareState($this->accessContext, $share, $state);
});
}
}

@ -107,7 +107,8 @@ final class ApiV1Controller extends OCSController {
try {
try {
$this->dbConnection->beginTransaction();
$recipients = $this->manager->searchRecipients($this->accessContext, $filterRecipientTypeClasses, $query, $limit, $offset, $id);
$forShare = ($id === null) ? null : $this->manager->getShare($this->accessContext, $id);
$recipients = $this->manager->searchRecipients($this->accessContext, $filterRecipientTypeClasses, $query, $limit, $offset, $forShare);
$this->dbConnection->commit();
return new DataResponse(ShareRecipient::formatMultiple($this->registry, $this->l10nFactory, $this->urlGenerator, $this->userManager, $recipients));
} catch (Exception $exception) {
@ -146,9 +147,8 @@ final class ApiV1Controller extends OCSController {
try {
$this->dbConnection->beginTransaction();
$id = $this->manager->createShare($this->accessContext);
$share = $this->manager->createShare($this->accessContext);
$share = $this->manager->getShare($this->accessContext, $id);
$this->dbConnection->commit();
return new DataResponse($share->format($this->registry, $this->l10nFactory, $this->urlGenerator, $this->userManager), Http::STATUS_CREATED);
} catch (Exception $exception) {
@ -185,7 +185,8 @@ final class ApiV1Controller extends OCSController {
try {
$this->dbConnection->beginTransaction();
$this->manager->updateShareState($this->accessContext, $id, $shareState);
$share = $this->manager->getShare($this->accessContext, $id);
$this->manager->updateShareState($this->accessContext, $share, $shareState);
$share = $this->manager->getShare($this->accessContext, $id);
$this->dbConnection->commit();
return new DataResponse($share->format($this->registry, $this->l10nFactory, $this->urlGenerator, $this->userManager));
@ -220,8 +221,8 @@ final class ApiV1Controller extends OCSController {
try {
$this->dbConnection->beginTransaction();
$this->manager->addShareSource($this->accessContext, $id, new ShareSource($class, $value));
$share = $this->manager->getShare($this->accessContext, $id);
$share = $this->manager->addShareSource($this->accessContext, $share, new ShareSource($class, $value));
$this->dbConnection->commit();
return new DataResponse($share->format($this->registry, $this->l10nFactory, $this->urlGenerator, $this->userManager));
} catch (Exception $exception) {
@ -256,8 +257,8 @@ final class ApiV1Controller extends OCSController {
try {
$this->dbConnection->beginTransaction();
$this->manager->removeShareSource($this->accessContext, $id, new ShareSource($class, $value));
$share = $this->manager->getShare($this->accessContext, $id);
$share = $this->manager->removeShareSource($this->accessContext, $share, new ShareSource($class, $value));
$this->dbConnection->commit();
return new DataResponse($share->format($this->registry, $this->l10nFactory, $this->urlGenerator, $this->userManager));
} catch (Exception $exception) {
@ -292,8 +293,8 @@ final class ApiV1Controller extends OCSController {
try {
$this->dbConnection->beginTransaction();
$this->manager->addShareRecipient($this->accessContext, $id, new ShareRecipient($class, $value, $instance));
$share = $this->manager->getShare($this->accessContext, $id);
$share = $this->manager->addShareRecipient($this->accessContext, $share, new ShareRecipient($class, $value, $instance));
$this->dbConnection->commit();
return new DataResponse($share->format($this->registry, $this->l10nFactory, $this->urlGenerator, $this->userManager));
} catch (Exception $exception) {
@ -329,8 +330,8 @@ final class ApiV1Controller extends OCSController {
try {
$this->dbConnection->beginTransaction();
$this->manager->removeShareRecipient($this->accessContext, $id, new ShareRecipient($class, $value, $instance));
$share = $this->manager->getShare($this->accessContext, $id);
$share = $this->manager->removeShareRecipient($this->accessContext, $share, new ShareRecipient($class, $value, $instance));
$this->dbConnection->commit();
return new DataResponse($share->format($this->registry, $this->l10nFactory, $this->urlGenerator, $this->userManager));
} catch (Exception $exception) {
@ -366,8 +367,8 @@ final class ApiV1Controller extends OCSController {
try {
$this->dbConnection->beginTransaction();
$this->manager->updateShareRecipientSecret($this->accessContext, $id, new ShareRecipient($class, $value, $instance), $secret);
$share = $this->manager->getShare($this->accessContext, $id);
$share = $this->manager->updateShareRecipientSecret($this->accessContext, $share, new ShareRecipient($class, $value, $instance), $secret);
$this->dbConnection->commit();
return new DataResponse($share->format($this->registry, $this->l10nFactory, $this->urlGenerator, $this->userManager));
} catch (Exception $exception) {
@ -403,8 +404,8 @@ final class ApiV1Controller extends OCSController {
try {
$this->dbConnection->beginTransaction();
$this->manager->updateShareProperty($this->accessContext, $id, new ShareProperty($class, $value));
$share = $this->manager->getShare($this->accessContext, $id);
$share = $this->manager->updateShareProperty($this->accessContext, $share, new ShareProperty($class, $value));
$this->dbConnection->commit();
return new DataResponse($share->format($this->registry, $this->l10nFactory, $this->urlGenerator, $this->userManager));
} catch (Exception $exception) {
@ -440,8 +441,8 @@ final class ApiV1Controller extends OCSController {
try {
$this->dbConnection->beginTransaction();
$this->manager->updateSharePermission($this->accessContext, $id, new SharePermission($class, $enabled));
$share = $this->manager->getShare($this->accessContext, $id);
$share = $this->manager->updateSharePermission($this->accessContext, $share, new SharePermission($class, $enabled));
$this->dbConnection->commit();
return new DataResponse($share->format($this->registry, $this->l10nFactory, $this->urlGenerator, $this->userManager));
} catch (Exception $exception) {
@ -476,8 +477,8 @@ final class ApiV1Controller extends OCSController {
try {
$this->dbConnection->beginTransaction();
$this->manager->selectSharePermissionPreset($this->accessContext, $id, $permissionPresetClass);
$share = $this->manager->getShare($this->accessContext, $id);
$share = $this->manager->selectSharePermissionPreset($this->accessContext, $share, $permissionPresetClass);
$this->dbConnection->commit();
return new DataResponse($share->format($this->registry, $this->l10nFactory, $this->urlGenerator, $this->userManager));
} catch (Exception $exception) {
@ -508,7 +509,8 @@ final class ApiV1Controller extends OCSController {
try {
$this->dbConnection->beginTransaction();
$this->manager->deleteShare($this->accessContext, $id);
$share = $this->manager->getShare($this->accessContext, $id);
$this->manager->deleteShare($this->accessContext, $share);
$this->dbConnection->commit();
return new DataResponse([], Http::STATUS_NO_CONTENT);
} catch (Exception $exception) {

@ -149,12 +149,12 @@ final class CommandTest extends AbstractSharingManagerTests {
}
#[Override]
protected function searchRecipients(ShareAccessContext $accessContext, ?array $filterRecipientTypeClasses, string $query, int $limit, int $offset, ?string $id = null): array {
protected function searchRecipients(ShareAccessContext $accessContext, ?array $filterRecipientTypeClasses, string $query, int $limit, int $offset, ?Share $forShare = null): array {
// We don't have a command for this, so we just call the real manager to make the test pass.
try {
$this->dbConnection->beginTransaction();
/** @psalm-suppress ArgumentTypeCoercion */
$shares = ShareRecipient::formatMultiple($this->registry, Server::get(IFactory::class), Server::get(IURLGenerator::class), Server::get(IUserManager::class), $this->manager->searchRecipients($accessContext, $filterRecipientTypeClasses, $query, $limit, $offset, $id));
$shares = ShareRecipient::formatMultiple($this->registry, Server::get(IFactory::class), Server::get(IURLGenerator::class), Server::get(IUserManager::class), $this->manager->searchRecipients($accessContext, $filterRecipientTypeClasses, $query, $limit, $offset, $forShare));
$this->dbConnection->commit();
return $shares;
} catch (Exception $exception) {
@ -185,12 +185,12 @@ final class CommandTest extends AbstractSharingManagerTests {
* @return SharingShare
*/
#[Override]
protected function updateShareState(ShareAccessContext $accessContext, string $id, ShareState $state): array {
protected function updateShareState(ShareAccessContext $accessContext, Share $share, ShareState $state): array {
$stdout = $this->runCommand(
$accessContext,
UpdateShareState::class,
[
['id', $id],
['id', $share->id],
['state', $state->value],
],
[],
@ -203,12 +203,12 @@ final class CommandTest extends AbstractSharingManagerTests {
* @return SharingShare
*/
#[Override]
protected function addShareSource(ShareAccessContext $accessContext, string $id, ShareSource $source): array {
protected function addShareSource(ShareAccessContext $accessContext, Share $share, ShareSource $source): array {
$stdout = $this->runCommand(
$accessContext,
AddShareSource::class,
[
['id', $id],
['id', $share->id],
['class', $source->class],
['value', $source->value],
],
@ -222,12 +222,12 @@ final class CommandTest extends AbstractSharingManagerTests {
* @return SharingShare
*/
#[Override]
protected function removeShareSource(ShareAccessContext $accessContext, string $id, ShareSource $source): array {
protected function removeShareSource(ShareAccessContext $accessContext, Share $share, ShareSource $source): array {
$stdout = $this->runCommand(
$accessContext,
RemoveShareSource::class,
[
['id', $id],
['id', $share->id],
['class', $source->class],
['value', $source->value],
],
@ -241,12 +241,12 @@ final class CommandTest extends AbstractSharingManagerTests {
* @return SharingShare
*/
#[Override]
protected function addShareRecipient(ShareAccessContext $accessContext, string $id, ShareRecipient $recipient): array {
protected function addShareRecipient(ShareAccessContext $accessContext, Share $share, ShareRecipient $recipient): array {
$stdout = $this->runCommand(
$accessContext,
AddShareRecipient::class,
[
['id', $id],
['id', $share->id],
['class', $recipient->class],
['value', $recipient->value],
['instance', $recipient->instance],
@ -261,12 +261,12 @@ final class CommandTest extends AbstractSharingManagerTests {
* @return SharingShare
*/
#[Override]
protected function removeShareRecipient(ShareAccessContext $accessContext, string $id, ShareRecipient $recipient): array {
protected function removeShareRecipient(ShareAccessContext $accessContext, Share $share, ShareRecipient $recipient): array {
$stdout = $this->runCommand(
$accessContext,
RemoveShareRecipient::class,
[
['id', $id],
['id', $share->id],
['class', $recipient->class],
['value', $recipient->value],
['instance', $recipient->instance],
@ -281,12 +281,12 @@ final class CommandTest extends AbstractSharingManagerTests {
* @return SharingShare
*/
#[Override]
protected function updateShareRecipientSecret(ShareAccessContext $accessContext, string $id, ShareRecipient $recipient, string $secret): array {
protected function updateShareRecipientSecret(ShareAccessContext $accessContext, Share $share, ShareRecipient $recipient, string $secret): array {
$stdout = $this->runCommand(
$accessContext,
UpdateShareRecipientSecret::class,
[
['id', $id],
['id', $share->id],
['class', $recipient->class],
['value', $recipient->value],
['instance', $recipient->instance],
@ -302,12 +302,12 @@ final class CommandTest extends AbstractSharingManagerTests {
* @return SharingShare
*/
#[Override]
protected function updateShareProperty(ShareAccessContext $accessContext, string $id, ShareProperty $property): array {
protected function updateShareProperty(ShareAccessContext $accessContext, Share $share, ShareProperty $property): array {
$stdout = $this->runCommand(
$accessContext,
UpdateShareProperty::class,
[
['id', $id],
['id', $share->id],
['class', $property->class],
['value', $property->value],
],
@ -321,12 +321,12 @@ final class CommandTest extends AbstractSharingManagerTests {
* @return SharingShare
*/
#[Override]
protected function updateSharePermission(ShareAccessContext $accessContext, string $id, SharePermission $permission): array {
protected function updateSharePermission(ShareAccessContext $accessContext, Share $share, SharePermission $permission): array {
$stdout = $this->runCommand(
$accessContext,
UpdateSharePermission::class,
[
['id', $id],
['id', $share->id],
['class', $permission->class],
['enabled', $permission->enabled ? 'true' : 'false'],
],
@ -340,12 +340,12 @@ final class CommandTest extends AbstractSharingManagerTests {
* @return SharingShare
*/
#[Override]
protected function selectSharePermissionPreset(ShareAccessContext $accessContext, string $id, string $permissionPresetClass): array {
protected function selectSharePermissionPreset(ShareAccessContext $accessContext, Share $share, string $permissionPresetClass): array {
$stdout = $this->runCommand(
$accessContext,
SelectSharePermissionPreset::class,
[
['id', $id],
['id', $share->id],
['permission-preset', $permissionPresetClass],
],
[],
@ -355,12 +355,12 @@ final class CommandTest extends AbstractSharingManagerTests {
}
#[Override]
protected function deleteShare(ShareAccessContext $accessContext, string $id): void {
protected function deleteShare(ShareAccessContext $accessContext, Share $share): void {
$this->runCommand(
$accessContext,
DeleteShare::class,
[
['id', $id],
['id', $share->id],
],
[],
);

@ -88,9 +88,9 @@ final class ApiV1ControllerTest extends AbstractSharingManagerTests {
}
#[Override]
protected function searchRecipients(ShareAccessContext $accessContext, ?array $filterRecipientTypeClasses, string $query, int $limit, int $offset, ?string $id = null): array {
protected function searchRecipients(ShareAccessContext $accessContext, ?array $filterRecipientTypeClasses, string $query, int $limit, int $offset, ?Share $forShare = null): array {
/** @psalm-suppress ArgumentTypeCoercion */
return $this->executeRequest($accessContext, fn (ApiV1Controller $controller): DataResponse => $controller->searchRecipients($filterRecipientTypeClasses, $query, $limit, $offset, $id));
return $this->executeRequest($accessContext, fn (ApiV1Controller $controller): DataResponse => $controller->searchRecipients($filterRecipientTypeClasses, $query, $limit, $offset, $forShare?->id));
}
#[Override]
@ -100,64 +100,64 @@ final class ApiV1ControllerTest extends AbstractSharingManagerTests {
}
#[Override]
protected function updateShareState(ShareAccessContext $accessContext, string $id, ShareState $state): array {
protected function updateShareState(ShareAccessContext $accessContext, Share $share, ShareState $state): array {
/** @var SharingShare */
return $this->executeRequest($accessContext, fn (ApiV1Controller $controller): DataResponse => $controller->updateShareState($id, $state->value));
return $this->executeRequest($accessContext, fn (ApiV1Controller $controller): DataResponse => $controller->updateShareState($share->id, $state->value));
}
#[Override]
protected function addShareSource(ShareAccessContext $accessContext, string $id, ShareSource $source): array {
protected function addShareSource(ShareAccessContext $accessContext, Share $share, ShareSource $source): array {
/** @var SharingShare */
return $this->executeRequest($accessContext, fn (ApiV1Controller $controller): DataResponse => $controller->addShareSource($id, $source->class, $source->value));
return $this->executeRequest($accessContext, fn (ApiV1Controller $controller): DataResponse => $controller->addShareSource($share->id, $source->class, $source->value));
}
#[Override]
protected function removeShareSource(ShareAccessContext $accessContext, string $id, ShareSource $source): array {
protected function removeShareSource(ShareAccessContext $accessContext, Share $share, ShareSource $source): array {
/** @var SharingShare */
return $this->executeRequest($accessContext, fn (ApiV1Controller $controller): DataResponse => $controller->removeShareSource($id, $source->class, $source->value));
return $this->executeRequest($accessContext, fn (ApiV1Controller $controller): DataResponse => $controller->removeShareSource($share->id, $source->class, $source->value));
}
#[Override]
protected function addShareRecipient(ShareAccessContext $accessContext, string $id, ShareRecipient $recipient): array {
protected function addShareRecipient(ShareAccessContext $accessContext, Share $share, ShareRecipient $recipient): array {
/** @var SharingShare */
return $this->executeRequest($accessContext, fn (ApiV1Controller $controller): DataResponse => $controller->addShareRecipient($id, $recipient->class, $recipient->value, $recipient->instance));
return $this->executeRequest($accessContext, fn (ApiV1Controller $controller): DataResponse => $controller->addShareRecipient($share->id, $recipient->class, $recipient->value, $recipient->instance));
}
#[Override]
protected function removeShareRecipient(ShareAccessContext $accessContext, string $id, ShareRecipient $recipient): array {
protected function removeShareRecipient(ShareAccessContext $accessContext, Share $share, ShareRecipient $recipient): array {
/** @var SharingShare */
return $this->executeRequest($accessContext, fn (ApiV1Controller $controller): DataResponse => $controller->removeShareRecipient($id, $recipient->class, $recipient->value, $recipient->instance));
return $this->executeRequest($accessContext, fn (ApiV1Controller $controller): DataResponse => $controller->removeShareRecipient($share->id, $recipient->class, $recipient->value, $recipient->instance));
}
#[Override]
protected function updateShareRecipientSecret(ShareAccessContext $accessContext, string $id, ShareRecipient $recipient, string $secret): array {
protected function updateShareRecipientSecret(ShareAccessContext $accessContext, Share $share, ShareRecipient $recipient, string $secret): array {
/** @psalm-suppress ArgumentTypeCoercion */
/** @var SharingShare */
return $this->executeRequest($accessContext, fn (ApiV1Controller $controller): DataResponse => $controller->updateShareRecipientSecret($id, $recipient->class, $recipient->value, $recipient->instance, $secret));
return $this->executeRequest($accessContext, fn (ApiV1Controller $controller): DataResponse => $controller->updateShareRecipientSecret($share->id, $recipient->class, $recipient->value, $recipient->instance, $secret));
}
#[Override]
protected function updateShareProperty(ShareAccessContext $accessContext, string $id, ShareProperty $property): array {
protected function updateShareProperty(ShareAccessContext $accessContext, Share $share, ShareProperty $property): array {
/** @var SharingShare */
return $this->executeRequest($accessContext, fn (ApiV1Controller $controller): DataResponse => $controller->updateShareProperty($id, $property->class, $property->value));
return $this->executeRequest($accessContext, fn (ApiV1Controller $controller): DataResponse => $controller->updateShareProperty($share->id, $property->class, $property->value));
}
#[Override]
protected function updateSharePermission(ShareAccessContext $accessContext, string $id, SharePermission $permission): array {
protected function updateSharePermission(ShareAccessContext $accessContext, Share $share, SharePermission $permission): array {
/** @var SharingShare */
return $this->executeRequest($accessContext, fn (ApiV1Controller $controller): DataResponse => $controller->updateSharePermission($id, $permission->class, $permission->enabled));
return $this->executeRequest($accessContext, fn (ApiV1Controller $controller): DataResponse => $controller->updateSharePermission($share->id, $permission->class, $permission->enabled));
}
#[Override]
protected function selectSharePermissionPreset(ShareAccessContext $accessContext, string $id, string $permissionPresetClass): array {
protected function selectSharePermissionPreset(ShareAccessContext $accessContext, Share $share, string $permissionPresetClass): array {
/** @psalm-suppress ArgumentTypeCoercion */
/** @var SharingShare */
return $this->executeRequest($accessContext, fn (ApiV1Controller $controller): DataResponse => $controller->selectSharePermissionPreset($id, $permissionPresetClass));
return $this->executeRequest($accessContext, fn (ApiV1Controller $controller): DataResponse => $controller->selectSharePermissionPreset($share->id, $permissionPresetClass));
}
#[Override]
protected function deleteShare(ShareAccessContext $accessContext, string $id): void {
$this->executeRequest($accessContext, fn (ApiV1Controller $controller): DataResponse => $controller->deleteShare($id));
protected function deleteShare(ShareAccessContext $accessContext, Share $share): void {
$this->executeRequest($accessContext, fn (ApiV1Controller $controller): DataResponse => $controller->deleteShare($share->id));
}
#[Override]

@ -136,7 +136,10 @@ final readonly class SharingBackend implements ISharingBackend {
->executeStatement();
} catch (Exception $exception) {
if ($exception instanceof \OCP\DB\Exception && $exception->getReason() === \OCP\DB\Exception::REASON_UNIQUE_CONSTRAINT_VIOLATION) {
throw new ShareInvalidException('Tried to add share source that already exists: ' . $source->class . ' ' . $source->value, $this->l10n->t('The share already contains the source.'), previous: $exception);
throw new ShareInvalidException(
'Tried to add share source that already exists: ' . $source->class . ' ' . $source->value,
$this->l10n->t('The share already contains the source.'), previous: $exception
);
}
throw $exception;
@ -214,7 +217,10 @@ final readonly class SharingBackend implements ISharingBackend {
->executeStatement();
} catch (Exception $exception) {
if ($exception instanceof \OCP\DB\Exception && $exception->getReason() === \OCP\DB\Exception::REASON_UNIQUE_CONSTRAINT_VIOLATION) {
throw new ShareInvalidException('Tried to add share recipient that already exists: ' . $recipient->class . ' ' . $recipient->value . ' ' . ($recipient->instance ?? 'local'), $this->l10n->t('The share already contains the recipient.'), previous: $exception);
throw new ShareInvalidException(
'Tried to add share recipient that already exists: ' . $recipient->class . ' ' . $recipient->value . ' ' . ($recipient->instance ?? 'local'),
$this->l10n->t('The share already contains the recipient.'), previous: $exception
);
}
throw $exception;
@ -227,7 +233,9 @@ final readonly class SharingBackend implements ISharingBackend {
$rowCount = $qb
->delete('sharing_share_recipients')
->where($qb->expr()->eq('share_id', $qb->createNamedParameter($id)))
->andWhere($qb->expr()->eq('recipient_class_id', $qb->createNamedParameter($this->classMapper->getClassId($recipient->class), IQueryBuilder::PARAM_INT)))
->andWhere(
$qb->expr()->eq('recipient_class_id', $qb->createNamedParameter($this->classMapper->getClassId($recipient->class), IQueryBuilder::PARAM_INT))
)
->andWhere($qb->expr()->eq('recipient_value', $qb->createNamedParameter($recipient->value)))
->andWhere(
$recipient->instance === null
@ -246,7 +254,9 @@ final readonly class SharingBackend implements ISharingBackend {
$result = $qb
->selectDistinct('share_id')
->from('sharing_share_recipients')
->where($qb->expr()->eq('recipient_class_id', $qb->createNamedParameter($this->classMapper->getClassId($recipient->class), IQueryBuilder::PARAM_INT)))
->where(
$qb->expr()->eq('recipient_class_id', $qb->createNamedParameter($this->classMapper->getClassId($recipient->class), IQueryBuilder::PARAM_INT))
)
->andWhere($qb->expr()->eq('recipient_value', $qb->createNamedParameter($recipient->value)))
->andWhere(
$recipient->instance === null
@ -266,7 +276,9 @@ final readonly class SharingBackend implements ISharingBackend {
$qb = $this->connection->getQueryBuilder();
$qb
->delete('sharing_share_recipients')
->where($qb->expr()->eq('recipient_class_id', $qb->createNamedParameter($this->classMapper->getClassId($recipient->class), IQueryBuilder::PARAM_INT)))
->where(
$qb->expr()->eq('recipient_class_id', $qb->createNamedParameter($this->classMapper->getClassId($recipient->class), IQueryBuilder::PARAM_INT))
)
->andWhere($qb->expr()->eq('recipient_value', $qb->createNamedParameter($recipient->value)))
->andWhere(
$recipient->instance === null
@ -330,7 +342,9 @@ final readonly class SharingBackend implements ISharingBackend {
->update('sharing_share_recipients')
->set('recipient_secret', $qb->createNamedParameter($secret))
->where($qb->expr()->eq('share_id', $qb->createNamedParameter($id)))
->andWhere($qb->expr()->eq('recipient_class_id', $qb->createNamedParameter($this->classMapper->getClassId($recipient->class), IQueryBuilder::PARAM_INT)))
->andWhere(
$qb->expr()->eq('recipient_class_id', $qb->createNamedParameter($this->classMapper->getClassId($recipient->class), IQueryBuilder::PARAM_INT))
)
->andWhere($qb->expr()->eq('recipient_value', $qb->createNamedParameter($recipient->value)))
->andWhere(
$recipient->instance === null
@ -384,7 +398,11 @@ final readonly class SharingBackend implements ISharingBackend {
->select('sp.property_value')
->from('sharing_share_properties', 'sp')
->where($qb->expr()->eq('sp.share_id', $qb->createNamedParameter($id)))
->andWhere($qb->expr()->eq('sp.property_class_id', $qb->createNamedParameter($this->classMapper->getClassId($property->class), IQueryBuilder::PARAM_INT)));
->andWhere(
$qb->expr()->eq(
'sp.property_class_id', $qb->createNamedParameter($this->classMapper->getClassId($property->class), IQueryBuilder::PARAM_INT)
)
);
/** @var string|false $oldValue */
$oldValue = $qb->executeQuery()->fetchOne();
@ -400,7 +418,9 @@ final readonly class SharingBackend implements ISharingBackend {
->update('sharing_share_properties')
->set('property_value', $qb->createNamedParameter($value))
->where($qb->expr()->eq('share_id', $qb->createNamedParameter($id)))
->andWhere($qb->expr()->eq('property_class_id', $qb->createNamedParameter($this->classMapper->getClassId($property->class), IQueryBuilder::PARAM_INT)))
->andWhere(
$qb->expr()->eq('property_class_id', $qb->createNamedParameter($this->classMapper->getClassId($property->class), IQueryBuilder::PARAM_INT))
)
->executeStatement();
if ($rowCount === 0) {
throw new ShareNotFoundException();
@ -435,7 +455,9 @@ final readonly class SharingBackend implements ISharingBackend {
->update('sharing_share_permissions')
->set('permission_enabled', $qb->createNamedParameter($permission->enabled, IQueryBuilder::PARAM_BOOL))
->where($qb->expr()->eq('share_id', $qb->createNamedParameter($id)))
->andWhere($qb->expr()->eq('permission_class_id', $qb->createNamedParameter($this->classMapper->getClassId($permission->class), IQueryBuilder::PARAM_INT)))
->andWhere(
$qb->expr()->eq('permission_class_id', $qb->createNamedParameter($this->classMapper->getClassId($permission->class), IQueryBuilder::PARAM_INT))
)
->executeStatement();
if ($rowCount === 0) {
throw new ShareNotFoundException();
@ -492,7 +514,9 @@ final readonly class SharingBackend implements ISharingBackend {
}
#[\Override]
public function getShares(ShareAccessContext $accessContext, ?string $filterSourceTypeClass, ?string $filterSourceTypeValue, ?string $lastShareID, ?int $limit): array {
public function getShares(
ShareAccessContext $accessContext, ?string $filterSourceTypeClass, ?string $filterSourceTypeValue, ?string $lastShareID, ?int $limit,
): array {
return $this->list($accessContext, null, $filterSourceTypeClass, $filterSourceTypeValue, $lastShareID, $limit);
}
@ -563,7 +587,10 @@ final readonly class SharingBackend implements ISharingBackend {
* @param ?non-empty-string $filterSourceTypeValue
* @return list<Share>
*/
private function list(ShareAccessContext $accessContext, ?string $filterShareID, ?string $filterSourceTypeClass, ?string $filterSourceTypeValue, ?string $lastShareID, ?int $limit): array {
private function list(
ShareAccessContext $accessContext, ?string $filterShareID, ?string $filterSourceTypeClass, ?string $filterSourceTypeValue, ?string $lastShareID,
?int $limit,
): array {
/** @var array<class-string<IShareRecipientType>, list<string>> $recipientTypeValues */
$recipientTypeValues = [];
@ -588,18 +615,25 @@ final readonly class SharingBackend implements ISharingBackend {
// Do not add a query if no recipients matched, otherwise all shares will be returned.
if ($recipientTypeValues !== []) {
$qb = $this->connection->getQueryBuilder();
$qb->innerJoin('s', 'sharing_share_recipients', 'sr', $qb->expr()->andX(
$qb->expr()->eq('s.state', $qb->createNamedParameter(ShareState::Active->value)),
$qb->expr()->eq('s.id', 'sr.share_id'),
));
$qb->innerJoin(
's', 'sharing_share_recipients', 'sr', $qb->expr()->andX(
$qb->expr()->eq('s.state', $qb->createNamedParameter(ShareState::Active->value)),
$qb->expr()->eq('s.id', 'sr.share_id'),
)
);
foreach ($recipientTypeValues as $recipientTypeClass => $recipientValues) {
$qb->orWhere($qb->expr()->andX(
$qb->expr()->eq('sr.recipient_class_id', $qb->createNamedParameter($this->classMapper->getClassId($recipientTypeClass), IQueryBuilder::PARAM_INT)),
// TODO: Add chunking
$qb->expr()->in('sr.recipient_value', $qb->createNamedParameter($recipientValues, IQueryBuilder::PARAM_STR_ARRAY)),
$qb->expr()->isNull('sr.recipient_instance'),
));
$qb->orWhere(
$qb->expr()->andX(
$qb->expr()->eq(
'sr.recipient_class_id',
$qb->createNamedParameter($this->classMapper->getClassId($recipientTypeClass), IQueryBuilder::PARAM_INT)
),
// TODO: Add chunking
$qb->expr()->in('sr.recipient_value', $qb->createNamedParameter($recipientValues, IQueryBuilder::PARAM_STR_ARRAY)),
$qb->expr()->isNull('sr.recipient_instance'),
)
);
}
$queries[] = $qb;
@ -607,11 +641,13 @@ final readonly class SharingBackend implements ISharingBackend {
if ($filterShareID !== null && $accessContext->secret !== null) {
$qb = $this->connection->getQueryBuilder();
$qb->innerJoin('s', 'sharing_share_recipients', 'sr', $qb->expr()->andX(
$qb->expr()->eq('s.state', $qb->createNamedParameter(ShareState::Active->value)),
$qb->expr()->eq('s.id', 'sr.share_id'),
$qb->expr()->eq('sr.recipient_secret', $qb->createNamedParameter($accessContext->secret)),
));
$qb->innerJoin(
's', 'sharing_share_recipients', 'sr', $qb->expr()->andX(
$qb->expr()->eq('s.state', $qb->createNamedParameter(ShareState::Active->value)),
$qb->expr()->eq('s.id', 'sr.share_id'),
$qb->expr()->eq('sr.recipient_secret', $qb->createNamedParameter($accessContext->secret)),
)
);
$queries[] = $qb;
}
@ -639,7 +675,9 @@ final readonly class SharingBackend implements ISharingBackend {
if ($filterSourceTypeClass !== null) {
$sourceTypeFilters = [
$qb->expr()->eq('s.id', 'ss.share_id'),
$qb->expr()->eq('ss.source_class_id', $qb->createNamedParameter($this->classMapper->getClassId($filterSourceTypeClass), IQueryBuilder::PARAM_INT)),
$qb->expr()->eq(
'ss.source_class_id', $qb->createNamedParameter($this->classMapper->getClassId($filterSourceTypeClass), IQueryBuilder::PARAM_INT)
),
];
if ($filterSourceTypeValue !== null) {
@ -666,7 +704,9 @@ final readonly class SharingBackend implements ISharingBackend {
$ownerInstance = $row['owner_instance'];
/** @psalm-suppress PossiblyNullReference The share is automatically deleted, when the owner is deleted. */
if ($ownerInstance === null && !$accessContext->overrideChecks && $this->hideDisabledUserShares() && !$this->userManager->get($ownerUserId)->isEnabled()) {
if ($ownerInstance === null && !$accessContext->overrideChecks && $this->hideDisabledUserShares() && !$this->userManager->get(
$ownerUserId
)->isEnabled()) {
continue;
}
@ -795,7 +835,9 @@ final readonly class SharingBackend implements ISharingBackend {
$initiatorInstance = $row['initiator_instance'];
/** @psalm-suppress PossiblyNullReference The initiator is automatically promoted to the owner, when the initiator is deleted. */
if ($initiatorInstance === null && !$accessContext->overrideChecks && !$shares[$id]['owner']->isCurrentUser($accessContext) && $this->hideDisabledUserShares() && !$this->userManager->get($initiatorUserId)->isEnabled()) {
if ($initiatorInstance === null && !$accessContext->overrideChecks && !$shares[$id]['owner']->isCurrentUser(
$accessContext
) && $this->hideDisabledUserShares() && !$this->userManager->get($initiatorUserId)->isEnabled()) {
continue;
}
@ -912,7 +954,9 @@ final readonly class SharingBackend implements ISharingBackend {
continue;
}
if (array_intersect($registryPropertyTypeCompatibleRecipientTypeClasses[$propertyTypeClass], array_keys($shareRecipientTypeClasses[$id])) === []) {
if (array_intersect(
$registryPropertyTypeCompatibleRecipientTypeClasses[$propertyTypeClass], array_keys($shareRecipientTypeClasses[$id])
) === []) {
// Skip properties that are currently not compatible, but don't remove them.
continue;
}
@ -929,28 +973,10 @@ final readonly class SharingBackend implements ISharingBackend {
}
}
$registrySourceTypePermissionTypeClasses = $this->registry->getSourceTypePermissionTypeClasses();
$registryGenericPermissionTypeClasses = $this->registry->getGenericPermissionTypeClasses();
/** @var array<int, array<class-string<ISharePermissionType>, bool>> $shareCompatiblePermissionTypeClasses */
$shareCompatiblePermissionTypeClasses = [];
foreach (array_keys($shares) as $id) {
$id = (string)$id;
$shareCompatiblePermissionTypeClasses[$id] = [];
foreach ($registryGenericPermissionTypeClasses as $permissionTypeClass) {
$shareCompatiblePermissionTypeClasses[$id][$permissionTypeClass] = true;
}
if (isset($shareSourceTypeClasses[$id])) {
foreach (array_keys($shareSourceTypeClasses[$id]) as $shareSourceTypeClass) {
if (isset($registrySourceTypePermissionTypeClasses[$shareSourceTypeClass])) {
foreach ($registrySourceTypePermissionTypeClasses[$shareSourceTypeClass] as $permissionTypeClass) {
$shareCompatiblePermissionTypeClasses[$id][$permissionTypeClass] = true;
}
}
}
}
}
$shareCompatiblePermissionTypeClasses = array_map(
fn (array $shareData): array => array_flip($this->getShareCompatiblePermissionTypeClasses($shareData['sources'])),
$shares
);
foreach ($chunks as $chunk) {
$qb = $this->connection->getQueryBuilder();
@ -965,8 +991,7 @@ final readonly class SharingBackend implements ISharingBackend {
$result = $qb->executeQuery();
foreach ($result->fetchAll() as $row) {
/** @var non-empty-string $id */
$id = (string)$row['share_id'];
$id = (int)$row['share_id'];
/** @var class-string<ISharePermissionType> $permissionTypeClass */
$permissionTypeClass = $this->classMapper->getClassName((int)$row['permission_class_id']);
@ -992,7 +1017,9 @@ final readonly class SharingBackend implements ISharingBackend {
), $shares);
if (!$accessContext->overrideChecks) {
$filterPropertyTypes = array_filter($registryPropertyTypes, static fn (ISharePropertyType $propertyType): bool => $propertyType instanceof ISharePropertyTypeFilter);
$filterPropertyTypes = array_filter(
$registryPropertyTypes, static fn (ISharePropertyType $propertyType): bool => $propertyType instanceof ISharePropertyTypeFilter
);
if ($filterPropertyTypes !== []) {
$shares = array_filter($shares, static function (Share $share) use ($accessContext, $filterPropertyTypes): bool {
if ($share->owner->isCurrentUser($accessContext)) {
@ -1010,34 +1037,50 @@ final readonly class SharingBackend implements ISharingBackend {
}
}
if ($shares !== []) {
$shares = $this->ensureDefaults($shares);
}
return array_values($shares);
}
/**
* @param ShareSource[] $sources
* @return list<class-string<ISharePermissionType>>
*/
private function getShareCompatiblePermissionTypeClasses(array $sources): array {
$shareSourceTypeClasses = array_map(fn (ShareSource $source): string => $source->class, $sources);
return $this->registry->getCompatiblePermissionTypeClasses($shareSourceTypeClasses);
}
#[\Override]
public function ensureDefaults(array $shares): array {
$defaultSet = false;
foreach (array_keys($shares) as $id) {
$id = (string)$id;
foreach (array_keys($registryPropertyTypes) as $propertyTypeClass) {
$share = $shares[$id];
if (
!isset($share->properties[$propertyTypeClass])
&& isset($shareSourceTypeClasses[$id], $shareRecipientTypeClasses[$id])
&& array_intersect($registryPropertyTypeCompatibleSourceTypeClasses[$propertyTypeClass], array_keys($shareSourceTypeClasses[$id])) !== []
&& array_intersect($registryPropertyTypeCompatibleRecipientTypeClasses[$propertyTypeClass], array_keys($shareRecipientTypeClasses[$id])) !== []) {
$shares[$id] = $this->createSharePropertyDefaultValue($shares[$id], $propertyTypeClass);
foreach ($shares as &$share) {
$shareSourceTypeClasses = array_map(fn (ShareSource $source): string => $source->class, $share->sources);
$shareRecipientTypeClasses = array_map(fn (ShareRecipient $recipient): string => $recipient->class, $share->recipients);
$shareCompatiblePropertyClasses = $this->registry->getCompatiblePropertyTypeClasses($shareSourceTypeClasses, $shareRecipientTypeClasses);
foreach ($shareCompatiblePropertyClasses as $propertyTypeClass) {
if (!isset($share->properties[$propertyTypeClass])) {
$share = $this->createSharePropertyDefaultValue($share, $propertyTypeClass);
$defaultSet = true;
}
}
}
foreach (array_keys($shares) as $id) {
foreach (array_keys($shareCompatiblePermissionTypeClasses[$id]) as $permissionTypeClass) {
$share = $shares[$id];
foreach ($shares as &$share) {
$shareCompatiblePermissionTypeClasses = $this->getShareCompatiblePermissionTypeClasses($share->sources);
foreach ($shareCompatiblePermissionTypeClasses as $permissionTypeClass) {
if (!isset($share->permissions[$permissionTypeClass])) {
$shares[$id] = $this->createSharePermissionDefaultValue($shares[$id], $permissionTypeClass);
$share = $this->createSharePermissionDefaultValue($share, $permissionTypeClass);
$defaultSet = true;
}
}
}
$shares = array_values($shares);
if ($defaultSet && $shares !== []) {
if ($defaultSet) {
$event = new SharesDefaultSetEvent($shares);
$this->eventDispatcher->dispatchTyped($event);
$shares = $event->getShares();
@ -1064,7 +1107,7 @@ final readonly class SharingBackend implements ISharingBackend {
$properties = $share->properties;
$properties[$propertyTypeClass] = $property;
$share = new Share(
return new Share(
$share->id,
$share->owner,
$timestamp,
@ -1074,8 +1117,6 @@ final readonly class SharingBackend implements ISharingBackend {
$properties,
$share->permissions,
);
return $share;
}
/**
@ -1096,7 +1137,7 @@ final readonly class SharingBackend implements ISharingBackend {
$permissions = $share->permissions;
$permissions[$permissionTypeClass] = $permission;
$share = new Share(
return new Share(
$share->id,
$share->owner,
$timestamp,
@ -1106,8 +1147,6 @@ final readonly class SharingBackend implements ISharingBackend {
$share->properties,
$permissions,
);
return $share;
}
private static function parseTimestamp(string $timestampMs): \DateTimeImmutable {

@ -78,7 +78,9 @@ final readonly class SharingManager implements ISharingManager, IEventListener {
}
#[\Override]
public function searchRecipients(ShareAccessContext $accessContext, ?array $filterRecipientTypeClasses, string $query, int $limit, int $offset, ?string $id = null): array {
public function searchRecipients(
ShareAccessContext $accessContext, ?array $filterRecipientTypeClasses, string $query, int $limit, int $offset, ?Share $forShare = null,
): array {
$recipientTypes = $this->registry->getRecipientTypes();
if ($filterRecipientTypeClasses !== null) {
@ -97,28 +99,36 @@ final readonly class SharingManager implements ISharingManager, IEventListener {
$recipientTypes = $filteredRecipientTypes;
} else {
$recipientTypes = array_values(array_filter(
$recipientTypes,
static fn (IShareRecipientType $recipientType): bool => $recipientType instanceof IShareRecipientTypeSearch,
));
$recipientTypes = array_values(
array_filter(
$recipientTypes,
static fn (IShareRecipientType $recipientType): bool => $recipientType instanceof IShareRecipientTypeSearch,
)
);
}
$results = array_merge(...array_map(
static fn (IShareRecipientTypeSearch $recipientType): array => $recipientType->searchRecipients($accessContext, $query, $limit, $offset),
$recipientTypes,
));
$results = array_merge(
...array_map(
static fn (IShareRecipientTypeSearch $recipientType): array => $recipientType->searchRecipients($accessContext, $query, $limit, $offset),
$recipientTypes,
)
);
if ($id !== null) {
if ($forShare instanceof Share) {
// Do not create a new access context with overridden checks, because it could leak the existence of shares and share recipients.
$share = $this->getShare($accessContext, $id);
$recipients = [];
foreach ($share->recipients as $recipient) {
foreach ($forShare->recipients as $recipient) {
$recipients[$recipient->class] ??= [];
$recipients[$recipient->class][$recipient->instance ?? ''] ??= [];
$recipients[$recipient->class][$recipient->instance ?? ''][$recipient->value] = true;
}
$results = array_values(array_filter($results, static fn (ShareRecipient $recipient): bool => !isset($recipients[$recipient->class][$recipient->instance ?? ''][$recipient->value])));
$results = array_values(
array_filter(
$results,
static fn (ShareRecipient $recipient): bool => !isset($recipients[$recipient->class][$recipient->instance ?? ''][$recipient->value])
)
);
}
return $results;
@ -137,7 +147,7 @@ final readonly class SharingManager implements ISharingManager, IEventListener {
}
#[\Override]
public function createShare(ShareAccessContext $accessContext): string {
public function createShare(ShareAccessContext $accessContext): Share {
if (!($currentUser = $accessContext->currentUser) instanceof IUser) {
throw new RuntimeException('No user present to create a share');
}
@ -148,9 +158,9 @@ final readonly class SharingManager implements ISharingManager, IEventListener {
$lastUpdated = $this->getTime();
$this->backend->createShare($id, new ShareUser($currentUser->getUID(), null), $lastUpdated);
$this->processShareUpdates([$id]);
[$share] = $this->processShareUpdates([$id]);
return $id;
return $share;
}
#[\Override]
@ -174,32 +184,40 @@ final readonly class SharingManager implements ISharingManager, IEventListener {
}
#[\Override]
public function updateShareState(ShareAccessContext $accessContext, string $id, ShareState $state): void {
public function updateShareState(ShareAccessContext $accessContext, Share $share, ShareState $state): Share {
$this->assertInTransaction();
$this->backend->setLastUpdated([$id], $this->getTime());
$time = $this->getTime();
$this->backend->setLastUpdated([$share->id], $time);
$owner = $this->backend->getShareOwner($id);
$this->validateShareOwnerOperation($accessContext, $owner);
$this->validateShareOwnerOperation($accessContext, $share->owner);
if ($state === ShareState::Active) {
$share = $this->getShare($accessContext, $id);
$this->assertShareCanBeActive($share);
}
$this->backend->updateShareState($id, $state);
$this->backend->updateShareState($share->id, $state);
$this->processShareUpdates([$id]);
$share = new Share(
$share->id,
$share->owner,
$time,
$state,
$share->sources,
$share->recipients,
$share->properties,
$share->permissions,
);
[$share] = $this->processShareUpdates([$share]);
return $share;
}
#[\Override]
public function addShareSource(ShareAccessContext $accessContext, string $id, ShareSource $source): void {
public function addShareSource(ShareAccessContext $accessContext, Share $share, ShareSource $source): Share {
$this->assertInTransaction();
$this->backend->setLastUpdated([$id], $this->getTime());
$owner = $this->backend->getShareOwner($id);
$this->validateShareOwnerOperation($accessContext, $owner);
$this->validateShareOwnerOperation($accessContext, $share->owner);
if (($sourceType = $this->registry->getSourceTypes()[$source->class] ?? null) === null) {
throw new RuntimeException('The source type is not registered: ' . $source->class);
@ -209,13 +227,14 @@ final readonly class SharingManager implements ISharingManager, IEventListener {
throw new ShareInvalidException('Invalid source: ' . $source->value . ' ' . $source->class, $this->l10n->t('The source does not exist.'));
}
$share = $this->getShare($accessContext, $id);
$time = $this->getTime();
$sources = $share->sources;
$sources[] = $source;
$share = new Share(
$share->id,
$share->owner,
$share->lastUpdated,
$time,
$share->state,
$sources,
$share->recipients,
@ -227,24 +246,50 @@ final readonly class SharingManager implements ISharingManager, IEventListener {
$this->validateInteraction($accessContext, $share);
}
$this->backend->addShareSource($id, $source);
$this->backend->setLastUpdated([$share->id], $time);
$this->backend->addShareSource($share->id, $source);
[$share] = $this->backend->ensureDefaults([$share]);
// The modified share object has to be used instead of fetching the share again, because it would trigger the insertion of default values prematurely.
$this->processShareUpdates([$share]);
[$share] = $this->processShareUpdates([$share]);
return $share;
}
#[\Override]
public function removeShareSource(ShareAccessContext $accessContext, string $id, ShareSource $source): void {
public function removeShareSource(ShareAccessContext $accessContext, Share $share, ShareSource $source): Share {
$this->assertInTransaction();
$this->backend->setLastUpdated([$id], $this->getTime());
$this->validateShareOwnerOperation($accessContext, $share->owner);
$time = $this->getTime();
$this->backend->setLastUpdated([$share->id], $time);
$this->backend->removeShareSource($share->id, $source);
$sources = array_values(array_filter($share->sources, static fn (ShareSource $shareSource): bool => !$shareSource->equals($source)));
$sourceClasses = array_map(static fn (ShareSource $source): string => $source->class, $sources);
$recipientClasses = array_map(static fn (ShareRecipient $recipient): string => $recipient->class, $share->recipients);
$compatiblePropertyClasses = $this->registry->getCompatiblePropertyTypeClasses($sourceClasses, $recipientClasses);
$compatiblePermissionsClasses = $this->registry->getCompatiblePermissionTypeClasses($sourceClasses);
$owner = $this->backend->getShareOwner($id);
$this->validateShareOwnerOperation($accessContext, $owner);
$properties = array_intersect_key($share->properties, array_flip($compatiblePropertyClasses));
$permissions = array_intersect_key($share->permissions, array_flip($compatiblePermissionsClasses));
$this->backend->removeShareSource($id, $source);
$share = new Share(
$share->id,
$share->owner,
$time,
$share->state,
$sources,
$share->recipients,
$properties,
$permissions,
);
$this->processShareUpdates([$id]);
[$share] = $this->processShareUpdates([$share]);
return $share;
}
#[\Override]
@ -268,22 +313,16 @@ final readonly class SharingManager implements ISharingManager, IEventListener {
}
#[\Override]
public function addShareRecipient(ShareAccessContext $accessContext, string $id, ShareRecipient $recipient): void {
public function addShareRecipient(ShareAccessContext $accessContext, Share $share, ShareRecipient $recipient): Share {
if (!($currentUser = $accessContext->currentUser) instanceof IUser) {
throw new RuntimeException('No current user provided in access context.');
}
$this->assertInTransaction();
$this->backend->setLastUpdated([$id], $this->getTime());
$owner = $this->backend->getShareOwner($id);
try {
$this->validateShareOwnerOperation($accessContext, $owner);
$share = null;
$this->validateShareOwnerOperation($accessContext, $share->owner);
} catch (ShareOperationForbiddenException) {
$share = $this->getShare($accessContext, $id);
$this->validatePermission($share, ReshareSharePermissionType::class);
}
@ -292,13 +331,15 @@ final readonly class SharingManager implements ISharingManager, IEventListener {
}
if (!$recipientType->validateRecipient($recipient->value)) {
throw new ShareInvalidException('Invalid recipient: ' . $recipient->value . ' ' . $recipient->class . ' ' . ($recipient->instance ?? 'local'), $this->l10n->t('The recipient does not exist.'));
throw new ShareInvalidException(
'Invalid recipient: ' . $recipient->value . ' ' . $recipient->class . ' ' . ($recipient->instance ?? 'local'),
$this->l10n->t('The recipient does not exist.')
);
}
$share ??= $this->getShare($accessContext, $id);
$recipients = $share->recipients;
$recipients[] = $recipient;
$share = new Share(
$validationShare = new Share(
$share->id,
$share->owner,
$share->lastUpdated,
@ -310,9 +351,12 @@ final readonly class SharingManager implements ISharingManager, IEventListener {
);
if (!$accessContext->overrideChecks) {
$this->validateInteraction($accessContext, $share);
$this->validateInteraction($accessContext, $validationShare);
}
$time = $this->getTime();
$this->backend->setLastUpdated([$share->id], $time);
if ($recipient->secret === null || !$recipient->initiator instanceof ShareUser) {
$secret = $recipient->secret ?? $this->generateSecret();
$initiator = $recipient->initiator ?? new ShareUser($currentUser->getUID(), null);
@ -326,33 +370,66 @@ final readonly class SharingManager implements ISharingManager, IEventListener {
);
}
$this->backend->addShareRecipient($id, $recipient);
$this->backend->addShareRecipient($share->id, $recipient);
$recipients = $share->recipients;
$recipients[] = $recipient;
$share = new Share(
$share->id,
$share->owner,
$time,
$share->state,
$share->sources,
$recipients,
$share->properties,
$share->permissions,
);
[$share] = $this->backend->ensureDefaults([$share]);
// The modified share object has to be used instead of fetching the share again, because it would trigger the insertion of default values prematurely.
$this->processShareUpdates([$share]);
[$share] = $this->processShareUpdates([$share]);
return $share;
}
#[\Override]
public function removeShareRecipient(ShareAccessContext $accessContext, string $id, ShareRecipient $recipient): void {
public function removeShareRecipient(ShareAccessContext $accessContext, Share $share, ShareRecipient $recipient): Share {
$this->assertInTransaction();
$this->assertInTransaction();
$this->backend->setLastUpdated([$id], $this->getTime());
$owner = $this->backend->getShareOwner($id);
try {
$this->validateShareOwnerOperation($accessContext, $owner);
$this->validateShareOwnerOperation($accessContext, $share->owner);
} catch (ShareOperationForbiddenException) {
$share = $this->getShare($accessContext, $id);
// This does not allow removing own recipients. A user can only reject a share, but not remove it for the recipient.
$this->validateReshareOperation($accessContext, $share, $recipient);
}
$this->backend->removeShareRecipient($id, $recipient);
$time = $this->getTime();
$this->backend->setLastUpdated([$share->id], $time);
$this->backend->removeShareRecipient($share->id, $recipient);
$recipients = array_values(array_filter($share->recipients, static fn (ShareRecipient $shareRecipient): bool => !$shareRecipient->equals($recipient)));
$sourceClasses = array_map(static fn (ShareSource $source): string => $source->class, $share->sources);
$recipientClasses = array_map(static fn (ShareRecipient $recipient): string => $recipient->class, $recipients);
$compatiblePropertyClasses = $this->registry->getCompatiblePropertyTypeClasses($sourceClasses, $recipientClasses);
$properties = array_intersect_key($share->properties, array_flip($compatiblePropertyClasses));
$share = new Share(
$share->id,
$share->owner,
$time,
$share->state,
$share->sources,
$recipients,
$properties,
$share->permissions,
);
$this->processShareUpdates([$id]);
[$share] = $this->processShareUpdates([$share]);
return $share;
}
#[\Override]
@ -395,18 +472,20 @@ final readonly class SharingManager implements ISharingManager, IEventListener {
$this->processShareUpdates($updatedIds);
}
/**
* @psalm-assert-if-true non-empty-string $secret
*/
private function validateShareSecret(string $secret): bool {
return (bool)preg_match('/^[a-z0-9-]{1,32}$/i', $secret);
}
#[\Override]
public function updateShareRecipientSecret(ShareAccessContext $accessContext, string $id, ShareRecipient $recipient, string $secret): void {
public function updateShareRecipientSecret(ShareAccessContext $accessContext, Share $share, ShareRecipient $recipient, string $secret): Share {
$this->assertInTransaction();
$this->backend->setLastUpdated([$id], $this->getTime());
$owner = $this->backend->getShareOwner($id);
try {
$this->validateShareOwnerOperation($accessContext, $owner);
$this->validateShareOwnerOperation($accessContext, $share->owner);
} catch (ShareOperationForbiddenException) {
$share = $this->getShare($accessContext, $id);
$this->validateReshareOperation($accessContext, $share, $recipient);
}
@ -418,60 +497,100 @@ final readonly class SharingManager implements ISharingManager, IEventListener {
throw new ShareOperationForbiddenException();
}
if (!preg_match('/^[a-z0-9-]{1,32}$/i', $secret)) {
throw new ShareInvalidException('Invalid secret: ' . $secret, $this->l10n->t('The value must be alphanumeric, 1 to 32 characters long and may contain dashes.'));
if (!$this->validateShareSecret($secret)) {
throw new ShareInvalidException(
'Invalid secret: ' . $secret, $this->l10n->t('The value must be alphanumeric, 1 to 32 characters long and may contain dashes.')
);
}
$time = $this->getTime();
$this->backend->setLastUpdated([$share->id], $time);
$this->backend->updateShareRecipientSecret($share->id, $recipient, $secret);
$recipients = $share->recipients;
foreach ($recipients as &$shareRecipient) {
if ($shareRecipient->equals($recipient)) {
$shareRecipient = new ShareRecipient(
$recipient->class,
$recipient->value,
$recipient->instance,
$secret,
$shareRecipient->initiator,
);
}
}
$this->backend->updateShareRecipientSecret($id, $recipient, $secret);
$share = new Share(
$share->id,
$share->owner,
$time,
$share->state,
$share->sources,
$recipients,
$share->properties,
$share->permissions,
);
$this->processShareUpdates([$id]);
[$share] = $this->processShareUpdates([$share]);
return $share;
}
#[\Override]
public function updateShareProperty(ShareAccessContext $accessContext, string $id, ShareProperty $property): void {
public function updateShareProperty(ShareAccessContext $accessContext, Share $share, ShareProperty $property): Share {
$this->assertInTransaction();
$this->backend->setLastUpdated([$id], $this->getTime());
$owner = $this->backend->getShareOwner($id);
$this->validateShareOwnerOperation($accessContext, $owner);
$this->validateShareOwnerOperation($accessContext, $share->owner);
if (($propertyType = $this->registry->getPropertyTypes()[$property->class] ?? null) === null) {
throw new RuntimeException('The property is not registered: ' . $property->class);
}
if ($property->value !== null) {
$share = $this->getShare($accessContext, $id);
if (($message = $propertyType->validateValue($this->l10nFactory, $share, $property->value)) !== true) {
throw new ShareInvalidException('Invalid property value: ' . $property->value . ' ' . $property->class, $message);
}
if ($property->value !== null && ($message = $propertyType->validateValue($this->l10nFactory, $share, $property->value)) !== true) {
throw new ShareInvalidException('Invalid property value: ' . $property->value . ' ' . $property->class, $message);
}
$this->backend->updateShareProperty($id, $property);
$time = $this->getTime();
$this->backend->setLastUpdated([$share->id], $time);
$this->backend->updateShareProperty($share->id, $property);
$this->processShareUpdates([$id]);
$properties = $share->properties;
$properties[$property->class] = $property;
$share = new Share(
$share->id,
$share->owner,
$time,
$share->state,
$share->sources,
$share->recipients,
$properties,
$share->permissions,
);
[$share] = $this->processShareUpdates([$share->id]);
return $share;
}
#[\Override]
public function updateSharePermission(ShareAccessContext $accessContext, string $id, SharePermission $permission): void {
public function updateSharePermission(ShareAccessContext $accessContext, Share $share, SharePermission $permission): Share {
$this->assertInTransaction();
$this->backend->setLastUpdated([$id], $this->getTime());
$owner = $this->backend->getShareOwner($id);
$this->validateShareOwnerOperation($accessContext, $owner);
$this->validateShareOwnerOperation($accessContext, $share->owner);
if (!isset($this->registry->getPermissionTypes()[$permission->class])) {
throw new RuntimeException('The permission type is not registered: ' . $permission->class);
}
$share = $this->getShare($accessContext, $id);
$time = $this->getTime();
$permissions = $share->permissions;
$permissions[$permission->class] = $permission;
$share = new Share(
$share->id,
$share->owner,
$share->lastUpdated,
$time,
$share->state,
$share->sources,
$share->recipients,
@ -483,45 +602,65 @@ final readonly class SharingManager implements ISharingManager, IEventListener {
$this->validateInteraction($accessContext, $share);
}
$this->backend->updateSharePermission($id, $permission);
$this->backend->setLastUpdated([$share->id], $time);
$this->backend->updateSharePermission($share->id, $permission);
// The modified share object has to be used instead of fetching the share again, because it would trigger the insertion of default values prematurely.
$this->processShareUpdates([$share]);
[$share] = $this->processShareUpdates([$share]);
return $share;
}
#[\Override]
public function selectSharePermissionPreset(ShareAccessContext $accessContext, string $id, string $permissionPresetClass): void {
public function selectSharePermissionPreset(ShareAccessContext $accessContext, Share $share, string $permissionPresetClass): Share {
$this->assertInTransaction();
$this->backend->setLastUpdated([$id], $this->getTime());
$owner = $this->backend->getShareOwner($id);
$this->validateShareOwnerOperation($accessContext, $owner);
$this->validateShareOwnerOperation($accessContext, $share->owner);
if (($this->registry->getPermissionPresetCompatiblePermissionTypeClasses()[$permissionPresetClass] ?? null) === null) {
throw new RuntimeException('The permission preset is not registered: ' . $permissionPresetClass);
}
$this->backend->selectSharePermissionPreset($id, $permissionPresetClass);
$time = $this->getTime();
$this->backend->setLastUpdated([$share->id], $time);
$this->processShareUpdates([$id]);
$this->backend->selectSharePermissionPreset($share->id, $permissionPresetClass);
$sourceClasses = array_map(static fn (ShareSource $source): string => $source->class, $share->sources);
$allPermissionClasses = $this->registry->getCompatiblePermissionTypeClasses($sourceClasses);
$permissionPresetCompatiblePermissionTypeClasses = $this->registry->getPermissionPresetCompatiblePermissionTypeClasses()[$permissionPresetClass];
$presetPermissions = array_combine($allPermissionClasses, array_map(fn (string $class): SharePermission => new SharePermission(
$class,
in_array($class, $permissionPresetCompatiblePermissionTypeClasses),
), $allPermissionClasses));
$share = new Share(
$share->id,
$share->owner,
$time,
$share->state,
$share->sources,
$share->recipients,
$share->properties,
$presetPermissions,
);
[$share] = $this->processShareUpdates([$share]);
return $share;
}
#[\Override]
public function deleteShare(ShareAccessContext $accessContext, string $id): void {
public function deleteShare(ShareAccessContext $accessContext, Share $share): void {
$this->assertInTransaction();
$owner = $this->backend->getShareOwner($id);
// No need to update the last updated timestamp, because the share will be deleted anyway.
$this->validateShareOwnerOperation($accessContext, $owner);
$this->validateShareOwnerOperation($accessContext, $share->owner);
$this->backend->deleteShare($id);
$this->backend->deleteShare($share->id);
$legacyBackend = $this->registry->getLegacyBackend();
if ($legacyBackend instanceof ISharingLegacyBackend) {
$legacyBackend->deleteShare($id);
$legacyBackend->deleteShare($share->id);
}
}
@ -533,7 +672,9 @@ final readonly class SharingManager implements ISharingManager, IEventListener {
}
#[\Override]
public function getShares(ShareAccessContext $accessContext, ?string $filterSourceTypeClass, ?string $filterSourceTypeValue, ?string $lastShareID, ?int $limit): array {
public function getShares(
ShareAccessContext $accessContext, ?string $filterSourceTypeClass, ?string $filterSourceTypeValue, ?string $lastShareID, ?int $limit,
): array {
$this->assertInTransaction();
return $this->backend->getShares($accessContext, $filterSourceTypeClass, $filterSourceTypeValue, $lastShareID, $limit);
@ -542,10 +683,13 @@ final readonly class SharingManager implements ISharingManager, IEventListener {
#[\Override]
public function handle(Event $event): void {
if ($event instanceof SharesDefaultSetEvent) {
$this->processShareUpdates($event->getShares());
$shares = $event->getShares();
$keys = array_keys($shares);
$shares = $this->processShareUpdates(array_values($shares));
$event->setShares(array_combine($keys, $shares));
}
if ($event instanceof BeforeUserDeletedEvent) {
if ($event instanceof BeforeUserDeletedEvent) {
$shareUser = new ShareUser($event->getUser()->getUID(), null);
try {
@ -620,7 +764,9 @@ final readonly class SharingManager implements ISharingManager, IEventListener {
* @throws ShareInvalidException
*/
private function validateInteraction(ShareAccessContext $accessContext, Share $share): void {
$action = new ShareAction(null, array_values(array_map(static fn (SharePermission $permission): string => $permission->class, $share->getEnabledPermissions())));
$action = new ShareAction(
null, array_values(array_map(static fn (SharePermission $permission): string => $permission->class, $share->getEnabledPermissions()))
);
$usersToCheck = [];
if ($share->owner->instance === null && ($ownerUser = $this->userManager->get($share->owner->userId)) instanceof IUser) {
@ -689,7 +835,10 @@ final readonly class SharingManager implements ISharingManager, IEventListener {
foreach ($share->properties as $propertyTypeClass => $property) {
$propertyType = $propertyTypes[$propertyTypeClass];
if ($property->value === null && $propertyType->isRequired($share)) {
throw new ShareInvalidException('Missing value for required property: ' . $propertyTypeClass, $this->l10n->t('You need to set a value for the %s', [$propertyType->getDisplayName($this->l10nFactory)]));
throw new ShareInvalidException(
'Missing value for required property: ' . $propertyTypeClass,
$this->l10n->t('You need to set a value for the %s', [$propertyType->getDisplayName($this->l10nFactory)])
);
}
}
}
@ -739,7 +888,9 @@ final readonly class SharingManager implements ISharingManager, IEventListener {
$compatibleRecipientTypes = array_fill_keys($legacyBackend->getCompatibleRecipientTypes(), true);
foreach ($share->recipients as $recipient) {
if (!isset($compatibleRecipientTypes[$recipient->class])) {
throw new RuntimeException('The legacy backend ' . $legacyBackend::class . ' does not support this recipient type: ' . $recipient->class);
throw new RuntimeException(
'The legacy backend ' . $legacyBackend::class . ' does not support this recipient type: ' . $recipient->class
);
}
}
@ -756,11 +907,7 @@ final readonly class SharingManager implements ISharingManager, IEventListener {
* @return numeric-string
*/
public static function timeToMs(\DateTimeImmutable $time): string {
if (method_exists($time, 'getMicrosecond')) {
$micros = (float)$time->getMicrosecond();
} else {
$micros = (float)$time->format('u');
}
$micros = method_exists($time, 'getMicrosecond') ? (float)$time->getMicrosecond() : (float)$time->format('u');
$time = (string)floor((float)$time->getTimestamp() * 1000.0 + $micros / 1000.0);
if ((float)$time > 0) {

@ -325,4 +325,48 @@ final class SharingRegistry implements ISharingRegistry {
return $out;
}
#[\Override]
public function getCompatiblePermissionTypeClasses(array $shareSourceTypeClasses): array {
/** @var array<class-string<ISharePermissionType>, bool> $shareCompatiblePermissionTypeClasses */
$shareCompatiblePermissionTypeClasses = [];
foreach ($this->genericPermissionTypes as $permissionTypeClass) {
$shareCompatiblePermissionTypeClasses[$permissionTypeClass] = true;
}
foreach ($shareSourceTypeClasses as $shareSourceTypeClass) {
if (isset($this->sourceTypePermissionTypes[$shareSourceTypeClass])) {
foreach ($this->sourceTypePermissionTypes[$shareSourceTypeClass] as $permissionTypeClass) {
$shareCompatiblePermissionTypeClasses[$permissionTypeClass] = true;
}
}
}
return array_keys($shareCompatiblePermissionTypeClasses);
}
/**
* @return list<class-string<ISharePropertyType>>
*/
#[\Override]
public function getCompatiblePropertyTypeClasses(array $shareSourceTypeClasses, array $shareRecipientTypeClasses): array {
$registryPropertyTypes = $this->getPropertyTypes();
$registryPropertyTypeCompatibleSourceTypeClasses = $this->getPropertyTypeCompatibleSourceTypeClasses();
$registryPropertyTypeCompatibleRecipientTypeClasses = $this->getPropertyTypeCompatibleRecipientTypes();
/** @var list<class-string<ISharePropertyType>> $compatiblePropertyTypes */
$compatiblePropertyTypes = [];
foreach (array_keys($registryPropertyTypes) as $propertyTypeClass) {
if (
array_intersect($registryPropertyTypeCompatibleSourceTypeClasses[$propertyTypeClass], $shareSourceTypeClasses) !== []
&& array_intersect(
$registryPropertyTypeCompatibleRecipientTypeClasses[$propertyTypeClass], $shareRecipientTypeClasses
) !== []) {
$compatiblePropertyTypes[] = $propertyTypeClass;
}
}
return $compatiblePropertyTypes;
}
}

@ -19,7 +19,7 @@ use OCP\EventDispatcher\Event;
*/
final class SharesDefaultSetEvent extends Event {
/**
* @param non-empty-list<Share> $shares
* @param non-empty-array<Share> $shares
* @experimental 35.0.0
*/
public function __construct(
@ -29,7 +29,7 @@ final class SharesDefaultSetEvent extends Event {
}
/**
* @return non-empty-list<Share>
* @return non-empty-array<Share>
* @experimental 35.0.0
*/
public function getShares(): array {
@ -37,7 +37,7 @@ final class SharesDefaultSetEvent extends Event {
}
/**
* @param non-empty-list<Share> $shares
* @param non-empty-array<Share> $shares
* @experimental 35.0.0
*/
public function setShares(array $shares): void {

@ -204,4 +204,13 @@ interface ISharingBackend {
* @experimental 35.0.0
*/
public function setLastUpdated(array $ids, \DateTimeImmutable $lastUpdated): void;
/**
* Ensure that all relevant properties and permissions have a value set, inserting defaults when needed
*
* @param non-empty-array<Share> $shares
* @return non-empty-array<Share>
* @experimental 35.0.0
*/
public function ensureDefaults(array $shares): array;
}

@ -32,12 +32,12 @@ interface ISharingManager {
* @param ?list<class-string<IShareRecipientType>> $filterRecipientTypeClasses
* @param positive-int $limit
* @param non-negative-int $offset
* @param ?string $id If provided, recipients that are already part of the share will not be returned.
* @param ?Share $excludeShare If provided, recipients that are already part of the share will not be returned.
* @return list<ShareRecipient>
* @throws ShareNotFoundException
* @experimental 35.0.0
*/
public function searchRecipients(ShareAccessContext $accessContext, ?array $filterRecipientTypeClasses, string $query, int $limit, int $offset, ?string $id = null): array;
public function searchRecipients(ShareAccessContext $accessContext, ?array $filterRecipientTypeClasses, string $query, int $limit, int $offset, ?Share $forShare = null): array;
/**
* Generate a new secret.
@ -59,7 +59,7 @@ interface ISharingManager {
*
* @experimental 35.0.0
*/
public function createShare(ShareAccessContext $accessContext): string;
public function createShare(ShareAccessContext $accessContext): Share;
/**
* Perform all updates when the owner was deleted.
@ -76,7 +76,7 @@ interface ISharingManager {
* @throws ShareOperationForbiddenException
* @experimental 35.0.0
*/
public function updateShareState(ShareAccessContext $accessContext, string $id, ShareState $state): void;
public function updateShareState(ShareAccessContext $accessContext, Share $share, ShareState $state): Share;
/**
* Add a new source to a share.
@ -86,7 +86,7 @@ interface ISharingManager {
* @throws ShareOperationForbiddenException
* @experimental 35.0.0
*/
public function addShareSource(ShareAccessContext $accessContext, string $id, ShareSource $source): void;
public function addShareSource(ShareAccessContext $accessContext, Share $share, ShareSource $source): Share;
/**
* Remove an existing source from a share.
@ -95,7 +95,7 @@ interface ISharingManager {
* @throws ShareOperationForbiddenException
* @experimental 35.0.0
*/
public function removeShareSource(ShareAccessContext $accessContext, string $id, ShareSource $source): void;
public function removeShareSource(ShareAccessContext $accessContext, Share $share, ShareSource $source): Share;
/**
* Perform all updates when the source was deleted.
@ -112,7 +112,7 @@ interface ISharingManager {
* @throws ShareOperationForbiddenException
* @experimental 35.0.0
*/
public function addShareRecipient(ShareAccessContext $accessContext, string $id, ShareRecipient $recipient): void;
public function addShareRecipient(ShareAccessContext $accessContext, Share $share, ShareRecipient $recipient): Share;
/**
* Remove an existing recipient from a share.
@ -121,7 +121,7 @@ interface ISharingManager {
* @throws ShareOperationForbiddenException
* @experimental 35.0.0
*/
public function removeShareRecipient(ShareAccessContext $accessContext, string $id, ShareRecipient $recipient): void;
public function removeShareRecipient(ShareAccessContext $accessContext, Share $share, ShareRecipient $recipient): Share;
/**
* Perform all updates when the recipient was deleted.
@ -145,7 +145,7 @@ interface ISharingManager {
* @throws ShareOperationForbiddenException
* @experimental 35.0.0
*/
public function updateShareRecipientSecret(ShareAccessContext $accessContext, string $id, ShareRecipient $recipient, string $secret): void;
public function updateShareRecipientSecret(ShareAccessContext $accessContext, Share $share, ShareRecipient $recipient, string $secret): Share;
/**
* Update a property of a share.
@ -155,7 +155,7 @@ interface ISharingManager {
* @throws ShareOperationForbiddenException
* @experimental 35.0.0
*/
public function updateShareProperty(ShareAccessContext $accessContext, string $id, ShareProperty $property): void;
public function updateShareProperty(ShareAccessContext $accessContext, Share $share, ShareProperty $property): Share;
/**
* Update a permission of a share.
@ -165,7 +165,7 @@ interface ISharingManager {
* @throws ShareOperationForbiddenException
* @experimental 35.0.0
*/
public function updateSharePermission(ShareAccessContext $accessContext, string $id, SharePermission $permission): void;
public function updateSharePermission(ShareAccessContext $accessContext, Share $share, SharePermission $permission): Share;
/**
* Select a permission preset for a share.
@ -175,7 +175,7 @@ interface ISharingManager {
* @throws ShareOperationForbiddenException
* @experimental 35.0.0
*/
public function selectSharePermissionPreset(ShareAccessContext $accessContext, string $id, string $permissionPresetClass): void;
public function selectSharePermissionPreset(ShareAccessContext $accessContext, Share $share, string $permissionPresetClass): Share;
/**
* Delete a share.
@ -184,7 +184,7 @@ interface ISharingManager {
* @throws ShareOperationForbiddenException
* @experimental 35.0.0
*/
public function deleteShare(ShareAccessContext $accessContext, string $id): void;
public function deleteShare(ShareAccessContext $accessContext, Share $share): void;
/**
* Get a share.

@ -155,4 +155,19 @@ interface ISharingRegistry {
* @experimental 35.0.0
*/
public function getPermissionPresetCompatiblePermissionTypeClasses(): array;
/**
* @param class-string<IShareSourceType>[] $shareSourceTypeClasses
* @return list<class-string<ISharePermissionType>>
* @experimental 35.0.0
*/
public function getCompatiblePermissionTypeClasses(array $shareSourceTypeClasses): array;
/**
* @param class-string<IShareSourceType>[] $shareSourceTypeClasses
* @param class-string<IShareRecipientType>[] $shareRecipientTypeClasses
* @return list<class-string<ISharePropertyType>>
* @experimental 35.0.0
*/
public function getCompatiblePropertyTypeClasses(array $shareSourceTypeClasses, array $shareRecipientTypeClasses): array;
}

@ -114,4 +114,11 @@ final readonly class ShareRecipient {
return array_map(static fn (ShareRecipient $recipient): array => $recipient->format($registry, $l10nFactory, $urlGenerator, $userManager, $recipientDisplayNames[$recipientTypes[$recipient->class]?->getRecipientDisplayName($recipient->value) ?? $recipient->value] === 1), $recipients);
}
/**
* @experimental 35.0.0
*/
public function equals(ShareRecipient $other): bool {
return $this->class === $other->class && $this->value === $other->value && $this->instance === $other->instance;
}
}

@ -83,4 +83,11 @@ final class ShareSource {
return array_map(static fn (ShareSource $source): array => $source->format($registry, $l10nFactory, $sourceDisplayNames[$source->getMetadata($registry)->getDisplayName()] === 1), $sources);
}
/**
* @experimental 35.0.0
*/
public function equals(ShareSource $other): bool {
return $this->class === $other->class && $this->value === $other->value;
}
}

@ -137,8 +137,8 @@ final class GroupShareRecipientTypeTest extends TestCase {
$accessContext = new ShareAccessContext(currentUser: $this->user1);
$this->dbConnection->beginTransaction();
$id = $this->manager->createShare($accessContext);
$this->manager->addShareRecipient($accessContext, $id, new ShareRecipient($this->recipientType::class, $this->group1->getGID(), null));
$share = $this->manager->createShare($accessContext);
$this->manager->addShareRecipient($accessContext, $share, new ShareRecipient($this->recipientType::class, $this->group1->getGID(), null));
$this->dbConnection->commit();
$before = $this->manager->getTime();
@ -146,12 +146,12 @@ final class GroupShareRecipientTypeTest extends TestCase {
$after = $this->manager->getTime();
$this->dbConnection->beginTransaction();
$share = $this->manager->getShare($accessContext, $id);
$share = $this->manager->getShare($accessContext, $share->id);
$this->assertGreaterThanOrEqual(SharingManager::timeToMs($before), SharingManager::timeToMs($share->lastUpdated));
$this->assertLessThanOrEqual(SharingManager::timeToMs($after), SharingManager::timeToMs($share->lastUpdated));
$this->assertEquals([], $share->recipients);
$this->manager->deleteShare($accessContext, $id);
$this->manager->deleteShare($accessContext, $share);
$this->dbConnection->commit();
$registry->clear();
}

@ -164,8 +164,8 @@ final class TeamShareRecipientTypeTest extends TestCase {
$accessContext = new ShareAccessContext(currentUser: $this->user1);
$this->dbConnection->beginTransaction();
$id = $this->manager->createShare($accessContext);
$this->manager->addShareRecipient($accessContext, $id, new ShareRecipient($this->recipientType::class, $this->team1->getId(), null));
$share = $this->manager->createShare($accessContext);
$this->manager->addShareRecipient($accessContext, $share, new ShareRecipient($this->recipientType::class, $this->team1->getId(), null));
$this->dbConnection->commit();
$circlesManager = Server::get(CirclesManager::class);
@ -176,12 +176,12 @@ final class TeamShareRecipientTypeTest extends TestCase {
$after = $this->manager->getTime();
$this->dbConnection->beginTransaction();
$share = $this->manager->getShare($accessContext, $id);
$share = $this->manager->getShare($accessContext, $share->id);
$this->assertGreaterThanOrEqual($before, $share->lastUpdated->getTimestamp());
$this->assertLessThanOrEqual($after, $share->lastUpdated->getTimestamp());
$this->assertEquals([], $share->recipients);
$this->manager->deleteShare($accessContext, $id);
$this->manager->deleteShare($accessContext, $share);
$this->dbConnection->commit();
$registry->clear();
}

@ -141,8 +141,8 @@ final class UserShareRecipientTypeTest extends TestCase {
$accessContext = new ShareAccessContext(currentUser: $this->user1);
$this->dbConnection->beginTransaction();
$id = $this->manager->createShare($accessContext);
$this->manager->addShareRecipient($accessContext, $id, new ShareRecipient($this->recipientType::class, $this->user2->getUID(), null));
$share = $this->manager->createShare($accessContext);
$this->manager->addShareRecipient($accessContext, $share, new ShareRecipient($this->recipientType::class, $this->user2->getUID(), null));
$this->dbConnection->commit();
$before = $this->manager->getTime();
@ -150,12 +150,12 @@ final class UserShareRecipientTypeTest extends TestCase {
$after = $this->manager->getTime();
$this->dbConnection->beginTransaction();
$share = $this->manager->getShare($accessContext, $id);
$share = $this->manager->getShare($accessContext, $share->id);
$this->assertGreaterThanOrEqual(SharingManager::timeToMs($before), SharingManager::timeToMs($share->lastUpdated));
$this->assertLessThanOrEqual(SharingManager::timeToMs($after), SharingManager::timeToMs($share->lastUpdated));
$this->assertEquals([], $share->recipients);
$this->manager->deleteShare($accessContext, $id);
$this->manager->deleteShare($accessContext, $share);
$this->dbConnection->commit();
$registry->clear();
}

File diff suppressed because it is too large Load Diff

@ -17,6 +17,7 @@ use NCU\Sharing\Share;
use NCU\Sharing\ShareAccessContext;
use NCU\Sharing\ShareState;
use NCU\Sharing\Source\ShareSource;
use OC\Sharing\SharingManager;
use OCP\IURLGenerator;
use OCP\IUserManager;
use OCP\L10N\IFactory;
@ -25,15 +26,47 @@ use PHPUnit\Framework\Attributes\Group;
#[Group(name: 'DB')]
final class SharingManagerTest extends AbstractSharingManagerTests {
private function assertShareSyncedWithDb(ShareAccessContext $accessContext, Share $share): void {
$retrieved = $this->manager->getShare($accessContext, $share->id);
// don't compare time at sub-ms accuracy
$this->assertEquals(SharingManager::timeToMs($retrieved->lastUpdated), SharingManager::timeToMs($share->lastUpdated));
// now we compared the lastUpdated, make them the same to not fail the full comparison when there is a sub-ms lastUpdate difference
$retrieved = new Share(
$retrieved->id,
$retrieved->owner,
$share->lastUpdated,
$retrieved->state,
$retrieved->sources,
$retrieved->recipients,
$retrieved->properties,
$retrieved->permissions
);
// cache the enabled permissions for both
$retrieved->getEnabledPermissions();
$share->getEnabledPermissions();
// ensure source metadata is loaded
foreach ($share->sources as $source) {
$source->format($this->registry, $this->l10nFactory, false);
}
$this->assertEquals($retrieved, $share, 'share object not in sync with database');
}
#[\Override]
protected function searchRecipients(ShareAccessContext $accessContext, ?array $filterRecipientTypeClasses, string $query, int $limit, int $offset, ?string $id = null): array {
protected function searchRecipients(ShareAccessContext $accessContext, ?array $filterRecipientTypeClasses, string $query, int $limit, int $offset, ?Share $forShare = null): array {
try {
$this->dbConnection->beginTransaction();
if ($forShare instanceof Share) {
$this->assertShareSyncedWithDb($accessContext, $forShare);
}
/** @psalm-suppress ArgumentTypeCoercion */
$shares = ShareRecipient::formatMultiple($this->registry, Server::get(IFactory::class), Server::get(IURLGenerator::class), Server::get(IUserManager::class), $this->manager->searchRecipients($accessContext, $filterRecipientTypeClasses, $query, $limit, $offset, $id));
$recipients = ShareRecipient::formatMultiple($this->registry, Server::get(IFactory::class), Server::get(IURLGenerator::class), Server::get(IUserManager::class), $this->manager->searchRecipients($accessContext, $filterRecipientTypeClasses, $query, $limit, $offset, $forShare));
$this->dbConnection->commit();
return $shares;
return $recipients;
} catch (Exception $exception) {
$this->dbConnection->rollBack();
throw $exception;
@ -44,8 +77,9 @@ final class SharingManagerTest extends AbstractSharingManagerTests {
protected function createShare(ShareAccessContext $accessContext): array {
try {
$this->dbConnection->beginTransaction();
$id = $this->manager->createShare($accessContext);
$share = $this->manager->getShare($accessContext, $id)->format($this->registry, Server::get(IFactory::class), Server::get(IURLGenerator::class), Server::get(IUserManager::class));
$share = $this->manager->createShare($accessContext);
$this->assertShareSyncedWithDb($accessContext, $share);
$share = $share->format($this->registry, Server::get(IFactory::class), Server::get(IURLGenerator::class), Server::get(IUserManager::class));
$this->dbConnection->commit();
return $share;
} catch (Exception $exception) {
@ -56,11 +90,12 @@ final class SharingManagerTest extends AbstractSharingManagerTests {
#[
\Override]
protected function updateShareState(ShareAccessContext $accessContext, string $id, ShareState $state): array {
protected function updateShareState(ShareAccessContext $accessContext, Share $share, ShareState $state): array {
try {
$this->dbConnection->beginTransaction();
$this->manager->updateShareState($accessContext, $id, $state);
$share = $this->manager->getShare($accessContext, $id)->format($this->registry, Server::get(IFactory::class), Server::get(IURLGenerator::class), Server::get(IUserManager::class));
$share = $this->manager->updateShareState($accessContext, $share, $state);
$this->assertShareSyncedWithDb($accessContext, $share);
$share = $share->format($this->registry, Server::get(IFactory::class), Server::get(IURLGenerator::class), Server::get(IUserManager::class));
$this->dbConnection->commit();
return $share;
} catch (Exception $exception) {
@ -70,11 +105,12 @@ final class SharingManagerTest extends AbstractSharingManagerTests {
}
#[\Override]
protected function addShareSource(ShareAccessContext $accessContext, string $id, ShareSource $source): array {
protected function addShareSource(ShareAccessContext $accessContext, Share $share, ShareSource $source): array {
try {
$this->dbConnection->beginTransaction();
$this->manager->addShareSource($accessContext, $id, $source);
$share = $this->manager->getShare($accessContext, $id)->format($this->registry, Server::get(IFactory::class), Server::get(IURLGenerator::class), Server::get(IUserManager::class));
$share = $this->manager->addShareSource($accessContext, $share, $source);
$this->assertShareSyncedWithDb($accessContext, $share);
$share = $share->format($this->registry, Server::get(IFactory::class), Server::get(IURLGenerator::class), Server::get(IUserManager::class));
$this->dbConnection->commit();
return $share;
} catch (Exception $exception) {
@ -84,11 +120,13 @@ final class SharingManagerTest extends AbstractSharingManagerTests {
}
#[\Override]
protected function removeShareSource(ShareAccessContext $accessContext, string $id, ShareSource $source): array {
protected function removeShareSource(ShareAccessContext $accessContext, Share $share, ShareSource $source): array {
try {
$this->dbConnection->beginTransaction();
$this->manager->removeShareSource($accessContext, $id, $source);
$share = $this->manager->getShare($accessContext, $id)->format($this->registry, Server::get(IFactory::class), Server::get(IURLGenerator::class), Server::get(IUserManager::class));
$this->assertShareSyncedWithDb($accessContext, $share);
$share = $this->manager->removeShareSource($accessContext, $share, $source);
$this->assertShareSyncedWithDb($accessContext, $share);
$share = $share->format($this->registry, Server::get(IFactory::class), Server::get(IURLGenerator::class), Server::get(IUserManager::class));
$this->dbConnection->commit();
return $share;
} catch (Exception $exception) {
@ -98,11 +136,12 @@ final class SharingManagerTest extends AbstractSharingManagerTests {
}
#[\Override]
protected function addShareRecipient(ShareAccessContext $accessContext, string $id, ShareRecipient $recipient): array {
protected function addShareRecipient(ShareAccessContext $accessContext, Share $share, ShareRecipient $recipient): array {
try {
$this->dbConnection->beginTransaction();
$this->manager->addShareRecipient($accessContext, $id, $recipient);
$share = $this->manager->getShare($accessContext, $id)->format($this->registry, Server::get(IFactory::class), Server::get(IURLGenerator::class), Server::get(IUserManager::class));
$share = $this->manager->addShareRecipient($accessContext, $share, $recipient);
$this->assertShareSyncedWithDb($accessContext, $share);
$share = $share->format($this->registry, Server::get(IFactory::class), Server::get(IURLGenerator::class), Server::get(IUserManager::class));
$this->dbConnection->commit();
return $share;
} catch (Exception $exception) {
@ -112,11 +151,12 @@ final class SharingManagerTest extends AbstractSharingManagerTests {
}
#[\Override]
protected function removeShareRecipient(ShareAccessContext $accessContext, string $id, ShareRecipient $recipient): array {
protected function removeShareRecipient(ShareAccessContext $accessContext, Share $share, ShareRecipient $recipient): array {
try {
$this->dbConnection->beginTransaction();
$this->manager->removeShareRecipient($accessContext, $id, $recipient);
$share = $this->manager->getShare($accessContext, $id)->format($this->registry, Server::get(IFactory::class), Server::get(IURLGenerator::class), Server::get(IUserManager::class));
$share = $this->manager->removeShareRecipient($accessContext, $share, $recipient);
$this->assertShareSyncedWithDb($accessContext, $share);
$share = $share->format($this->registry, Server::get(IFactory::class), Server::get(IURLGenerator::class), Server::get(IUserManager::class));
$this->dbConnection->commit();
return $share;
} catch (Exception $exception) {
@ -126,11 +166,12 @@ final class SharingManagerTest extends AbstractSharingManagerTests {
}
#[\Override]
protected function updateShareRecipientSecret(ShareAccessContext $accessContext, string $id, ShareRecipient $recipient, string $secret): array {
protected function updateShareRecipientSecret(ShareAccessContext $accessContext, Share $share, ShareRecipient $recipient, string $secret): array {
try {
$this->dbConnection->beginTransaction();
$this->manager->updateShareRecipientSecret($accessContext, $id, $recipient, $secret);
$share = $this->manager->getShare($accessContext, $id)->format($this->registry, Server::get(IFactory::class), Server::get(IURLGenerator::class), Server::get(IUserManager::class));
$share = $this->manager->updateShareRecipientSecret($accessContext, $share, $recipient, $secret);
$this->assertShareSyncedWithDb($accessContext, $share);
$share = $share->format($this->registry, Server::get(IFactory::class), Server::get(IURLGenerator::class), Server::get(IUserManager::class));
$this->dbConnection->commit();
return $share;
} catch (Exception $exception) {
@ -140,11 +181,12 @@ final class SharingManagerTest extends AbstractSharingManagerTests {
}
#[\Override]
protected function updateShareProperty(ShareAccessContext $accessContext, string $id, ShareProperty $property): array {
protected function updateShareProperty(ShareAccessContext $accessContext, Share $share, ShareProperty $property): array {
try {
$this->dbConnection->beginTransaction();
$this->manager->updateShareProperty($accessContext, $id, $property);
$share = $this->manager->getShare($accessContext, $id)->format($this->registry, Server::get(IFactory::class), Server::get(IURLGenerator::class), Server::get(IUserManager::class));
$share = $this->manager->updateShareProperty($accessContext, $share, $property);
$this->assertShareSyncedWithDb($accessContext, $share);
$share = $share->format($this->registry, Server::get(IFactory::class), Server::get(IURLGenerator::class), Server::get(IUserManager::class));
$this->dbConnection->commit();
return $share;
} catch (Exception $exception) {
@ -154,11 +196,12 @@ final class SharingManagerTest extends AbstractSharingManagerTests {
}
#[\Override]
protected function updateSharePermission(ShareAccessContext $accessContext, string $id, SharePermission $permission): array {
protected function updateSharePermission(ShareAccessContext $accessContext, Share $share, SharePermission $permission): array {
try {
$this->dbConnection->beginTransaction();
$this->manager->updateSharePermission($accessContext, $id, $permission);
$share = $this->manager->getShare($accessContext, $id)->format($this->registry, Server::get(IFactory::class), Server::get(IURLGenerator::class), Server::get(IUserManager::class));
$share = $this->manager->updateSharePermission($accessContext, $share, $permission);
$this->assertShareSyncedWithDb($accessContext, $share);
$share = $share->format($this->registry, Server::get(IFactory::class), Server::get(IURLGenerator::class), Server::get(IUserManager::class));
$this->dbConnection->commit();
return $share;
} catch (Exception $exception) {
@ -168,12 +211,13 @@ final class SharingManagerTest extends AbstractSharingManagerTests {
}
#[\Override]
protected function selectSharePermissionPreset(ShareAccessContext $accessContext, string $id, string $permissionPresetClass): array {
protected function selectSharePermissionPreset(ShareAccessContext $accessContext, Share $share, string $permissionPresetClass): array {
try {
$this->dbConnection->beginTransaction();
/** @psalm-suppress ArgumentTypeCoercion */
$this->manager->selectSharePermissionPreset($accessContext, $id, $permissionPresetClass);
$share = $this->manager->getShare($accessContext, $id)->format($this->registry, Server::get(IFactory::class), Server::get(IURLGenerator::class), Server::get(IUserManager::class));
$share = $this->manager->selectSharePermissionPreset($accessContext, $share, $permissionPresetClass);
$this->assertShareSyncedWithDb($accessContext, $share);
$share = $share->format($this->registry, Server::get(IFactory::class), Server::get(IURLGenerator::class), Server::get(IUserManager::class));
$this->dbConnection->commit();
return $share;
} catch (Exception $exception) {
@ -183,10 +227,11 @@ final class SharingManagerTest extends AbstractSharingManagerTests {
}
#[\Override]
protected function deleteShare(ShareAccessContext $accessContext, string $id): void {
protected function deleteShare(ShareAccessContext $accessContext, Share $share): void {
try {
$this->dbConnection->beginTransaction();
$this->manager->deleteShare($accessContext, $id);
$this->assertShareSyncedWithDb($accessContext, $share);
$this->manager->deleteShare($accessContext, $share);
$this->dbConnection->commit();
} catch (Exception $exception) {
$this->dbConnection->rollBack();

Loading…
Cancel
Save