diff --git a/src/Bundle/ChillMainBundle/Security/Authorization/SavedExportVoter.php b/src/Bundle/ChillMainBundle/Security/Authorization/SavedExportVoter.php index a29a38662..43e788c90 100644 --- a/src/Bundle/ChillMainBundle/Security/Authorization/SavedExportVoter.php +++ b/src/Bundle/ChillMainBundle/Security/Authorization/SavedExportVoter.php @@ -12,8 +12,11 @@ declare(strict_types=1); namespace Chill\MainBundle\Security\Authorization; use Chill\MainBundle\Entity\SavedExport; +use Chill\MainBundle\Entity\User; +use Chill\MainBundle\Export\ExportManager; use Symfony\Component\Security\Core\Authentication\Token\TokenInterface; use Symfony\Component\Security\Core\Authorization\Voter\Voter; +use Symfony\Component\Security\Core\Security; class SavedExportVoter extends Voter { @@ -23,12 +26,17 @@ class SavedExportVoter extends Voter final public const GENERATE = 'CHLL_MAIN_EXPORT_SAVED_GENERATE'; + final public const SHARE = 'CHLL_MAIN_EXPORT_SAVED_SHARE'; + private const ALL = [ self::DELETE, self::EDIT, self::GENERATE, + self::SHARE, ]; + public function __construct(private ExportManager $exportManager, private Security $security) {} + protected function supports($attribute, $subject): bool { return $subject instanceof SavedExport && \in_array($attribute, self::ALL, true); @@ -49,4 +57,10 @@ class SavedExportVoter extends Voter default => throw new \UnexpectedValueException('attribute not supported: '.$attribute), }; } + + private function canUserGenerate(User $user, SavedExport $savedExport): bool + { + return ($savedExport->getUser() === $user || $savedExport->isSharedWithUser($user)) + && $this->security->isGranted(ChillExportVoter::EXPORT, $this->exportManager->getExport($savedExport->getExportAlias())); + } } diff --git a/src/Bundle/ChillMainBundle/Tests/Entity/Workflow/SavedExportTest.php b/src/Bundle/ChillMainBundle/Tests/Entity/Workflow/SavedExportTest.php new file mode 100644 index 000000000..999d127ed --- /dev/null +++ b/src/Bundle/ChillMainBundle/Tests/Entity/Workflow/SavedExportTest.php @@ -0,0 +1,39 @@ +addUser($user2); + + // Create a SavedExport entity + $savedExport = new SavedExport(); + + // Share the saved export with user1 + $savedExport->addShare($user1); + + // Share the saved export with the group + $savedExport->addShare($group); + + // Assertions + $this->assertTrue($savedExport->isSharedWithUser($user1), 'User1 should have access to the saved export.'); + $this->assertTrue($savedExport->isSharedWithUser($user2), 'User2 (via group) should have access to the saved export.'); + $this->assertFalse($savedExport->isSharedWithUser($user3), 'User3 should not have access to the saved export.'); + + } +} diff --git a/src/Bundle/ChillMainBundle/Tests/Security/Authorization/SavedExportVoterTest.php b/src/Bundle/ChillMainBundle/Tests/Security/Authorization/SavedExportVoterTest.php new file mode 100644 index 000000000..86c04be88 --- /dev/null +++ b/src/Bundle/ChillMainBundle/Tests/Security/Authorization/SavedExportVoterTest.php @@ -0,0 +1,114 @@ +prophesize(Security::class); + if (null !== $isGranted) { + $security->isGranted(Argument::any(), Argument::any())->willReturn($isGranted); + } + + $export = $this->prophesize(ExportInterface::class); + $exportManager = $this->prophesize(ExportManager::class); + $exportManager->getExport($savedExport->getExportAlias())->willReturn($export->reveal()); + + $voter = new SavedExportVoter($exportManager->reveal(), $security->reveal()); + $token = new UsernamePasswordToken($user, 'default', ['ROLE_USER']); + + self::assertEquals($expectedResult, $voter->vote($token, $savedExport, [$attribute])); + } + + public static function voteProvider(): iterable + { + $alls = [SavedExportVoter::GENERATE, SavedExportVoter::GENERATE, SavedExportVoter::EDIT, SavedExportVoter::DELETE]; + $userA = new User(); + $userB = new User(); + $userC = new User(); + $group = new UserGroup(); + $group->addUser($userC); + + $savedExport = new SavedExport(); + $savedExport->setExportAlias('dummy_export'); + $savedExport->setUser($userA); + + foreach ($alls as $attribute) { + yield [ + $attribute, + $savedExport, + $userA, + VoterInterface::ACCESS_GRANTED, + true, + ]; + } + + yield [ + SavedExportVoter::GENERATE, + $savedExport, + $userA, + VoterInterface::ACCESS_DENIED, + false, + ]; + + foreach ($alls as $attribute) { + yield [ + $attribute, + $savedExport, + $userB, + VoterInterface::ACCESS_DENIED, + true, + ]; + } + + $savedExport = new SavedExport(); + $savedExport->setExportAlias('dummy_export'); + $savedExport->setUser($userA); + $savedExport->addShare($userB); + + yield [ + SavedExportVoter::GENERATE, + $savedExport, + $userB, + VoterInterface::ACCESS_DENIED, + false, + ]; + + yield [ + SavedExportVoter::GENERATE, + $savedExport, + $userB, + VoterInterface::ACCESS_GRANTED, + true, + ]; + + foreach ([SavedExportVoter::EDIT, SavedExportVoter::DELETE] as $attribute) { + yield [ + $attribute, + $savedExport, + $userB, + VoterInterface::ACCESS_DENIED, + true, + ]; + } + } +}