diff --git a/src/Bridge/Symfony/Bundle/Resources/config/graphql.xml b/src/Bridge/Symfony/Bundle/Resources/config/graphql.xml index 701a7ac9878..07617218bf5 100644 --- a/src/Bridge/Symfony/Bundle/Resources/config/graphql.xml +++ b/src/Bridge/Symfony/Bundle/Resources/config/graphql.xml @@ -53,12 +53,12 @@ - + - + diff --git a/src/GraphQl/Resolver/Stage/SecurityPostDenormalizeStage.php b/src/GraphQl/Resolver/Stage/SecurityPostDenormalizeStage.php index 18a1e2ad18a..d6ab052d085 100644 --- a/src/GraphQl/Resolver/Stage/SecurityPostDenormalizeStage.php +++ b/src/GraphQl/Resolver/Stage/SecurityPostDenormalizeStage.php @@ -30,7 +30,7 @@ final class SecurityPostDenormalizeStage implements SecurityPostDenormalizeStage private $resourceMetadataFactory; private $resourceAccessChecker; - public function __construct(ResourceMetadataFactoryInterface $resourceMetadataFactory, ResourceAccessCheckerInterface $resourceAccessChecker) + public function __construct(ResourceMetadataFactoryInterface $resourceMetadataFactory, ?ResourceAccessCheckerInterface $resourceAccessChecker) { $this->resourceMetadataFactory = $resourceMetadataFactory; $this->resourceAccessChecker = $resourceAccessChecker; @@ -44,6 +44,7 @@ public function __invoke(string $resourceClass, string $operationName, array $co $resourceMetadata = $this->resourceMetadataFactory->create($resourceClass); $isGranted = $resourceMetadata->getGraphqlAttribute($operationName, 'security_post_denormalize', null, true); + if (null === $isGranted) { // Backward compatibility $isGranted = $resourceMetadata->getGraphqlAttribute($operationName, 'access_control', null, true); @@ -52,6 +53,10 @@ public function __invoke(string $resourceClass, string $operationName, array $co } } + if (null !== $isGranted && null === $this->resourceAccessChecker) { + throw new \LogicException('Cannot check security expression when SecurityBundle is not installed. Try running "composer require symfony/security-bundle".'); + } + if (null === $isGranted || $this->resourceAccessChecker->isGranted($resourceClass, (string) $isGranted, $context['extra_variables'])) { return; } diff --git a/src/GraphQl/Resolver/Stage/SecurityStage.php b/src/GraphQl/Resolver/Stage/SecurityStage.php index 36fa2e37619..b3afa035618 100644 --- a/src/GraphQl/Resolver/Stage/SecurityStage.php +++ b/src/GraphQl/Resolver/Stage/SecurityStage.php @@ -30,7 +30,7 @@ final class SecurityStage implements SecurityStageInterface private $resourceMetadataFactory; private $resourceAccessChecker; - public function __construct(ResourceMetadataFactoryInterface $resourceMetadataFactory, ResourceAccessCheckerInterface $resourceAccessChecker) + public function __construct(ResourceMetadataFactoryInterface $resourceMetadataFactory, ?ResourceAccessCheckerInterface $resourceAccessChecker) { $this->resourceMetadataFactory = $resourceMetadataFactory; $this->resourceAccessChecker = $resourceAccessChecker; @@ -44,6 +44,11 @@ public function __invoke(string $resourceClass, string $operationName, array $co $resourceMetadata = $this->resourceMetadataFactory->create($resourceClass); $isGranted = $resourceMetadata->getGraphqlAttribute($operationName, 'security', null, true); + + if (null !== $isGranted && null === $this->resourceAccessChecker) { + throw new \LogicException('Cannot check security expression when SecurityBundle is not installed. Try running "composer require symfony/security-bundle".'); + } + if (null === $isGranted || $this->resourceAccessChecker->isGranted($resourceClass, (string) $isGranted, $context['extra_variables'])) { return; } diff --git a/tests/GraphQl/Resolver/Stage/SecurityPostDenormalizeStageTest.php b/tests/GraphQl/Resolver/Stage/SecurityPostDenormalizeStageTest.php index 21f11dda5ce..301963c836a 100644 --- a/tests/GraphQl/Resolver/Stage/SecurityPostDenormalizeStageTest.php +++ b/tests/GraphQl/Resolver/Stage/SecurityPostDenormalizeStageTest.php @@ -115,4 +115,34 @@ public function testNotGranted(): void 'extra_variables' => $extraVariables, ]); } + + public function testNoSecurityBundleInstalled(): void + { + $this->securityPostDenormalizeStage = new SecurityPostDenormalizeStage($this->resourceMetadataFactoryProphecy->reveal(), null); + + $operationName = 'item_query'; + $resourceClass = 'myResource'; + $isGranted = 'not_granted'; + $resourceMetadata = (new ResourceMetadata())->withGraphql([ + $operationName => ['security_post_denormalize' => $isGranted], + ]); + $this->resourceMetadataFactoryProphecy->create($resourceClass)->willReturn($resourceMetadata); + + $this->expectException(\LogicException::class); + + ($this->securityPostDenormalizeStage)($resourceClass, 'item_query', []); + } + + public function testNoSecurityBundleInstalledNoExpression(): void + { + $this->securityPostDenormalizeStage = new SecurityPostDenormalizeStage($this->resourceMetadataFactoryProphecy->reveal(), null); + + $resourceClass = 'myResource'; + $resourceMetadata = new ResourceMetadata(); + $this->resourceMetadataFactoryProphecy->create($resourceClass)->willReturn($resourceMetadata); + + $this->resourceAccessCheckerProphecy->isGranted(Argument::any())->shouldNotBeCalled(); + + ($this->securityPostDenormalizeStage)($resourceClass, 'item_query', []); + } } diff --git a/tests/GraphQl/Resolver/Stage/SecurityStageTest.php b/tests/GraphQl/Resolver/Stage/SecurityStageTest.php index 4b18a7345fd..a49207679a2 100644 --- a/tests/GraphQl/Resolver/Stage/SecurityStageTest.php +++ b/tests/GraphQl/Resolver/Stage/SecurityStageTest.php @@ -96,4 +96,34 @@ public function testNotGranted(): void 'extra_variables' => $extraVariables, ]); } + + public function testNoSecurityBundleInstalled(): void + { + $this->securityStage = new SecurityStage($this->resourceMetadataFactoryProphecy->reveal(), null); + + $operationName = 'item_query'; + $resourceClass = 'myResource'; + $isGranted = 'not_granted'; + $resourceMetadata = (new ResourceMetadata())->withGraphql([ + $operationName => ['security' => $isGranted], + ]); + $this->resourceMetadataFactoryProphecy->create($resourceClass)->willReturn($resourceMetadata); + + $this->expectException(\LogicException::class); + + ($this->securityStage)($resourceClass, 'item_query', []); + } + + public function testNoSecurityBundleInstalledNoExpression(): void + { + $this->securityStage = new SecurityStage($this->resourceMetadataFactoryProphecy->reveal(), null); + + $resourceClass = 'myResource'; + $resourceMetadata = new ResourceMetadata(); + $this->resourceMetadataFactoryProphecy->create($resourceClass)->willReturn($resourceMetadata); + + $this->resourceAccessCheckerProphecy->isGranted(Argument::any())->shouldNotBeCalled(); + + ($this->securityStage)($resourceClass, 'item_query', []); + } }