diff --git a/src/Symfony/Component/Security/Http/EventListener/IsGrantedAttributeListener.php b/src/Symfony/Component/Security/Http/EventListener/IsGrantedAttributeListener.php index a0400a032d46a..bbd73a95b650e 100644 --- a/src/Symfony/Component/Security/Http/EventListener/IsGrantedAttributeListener.php +++ b/src/Symfony/Component/Security/Http/EventListener/IsGrantedAttributeListener.php @@ -14,6 +14,7 @@ use Symfony\Component\EventDispatcher\EventSubscriberInterface; use Symfony\Component\ExpressionLanguage\Expression; use Symfony\Component\ExpressionLanguage\ExpressionLanguage; +use Symfony\Component\HttpFoundation\Request; use Symfony\Component\HttpKernel\Event\ControllerArgumentsEvent; use Symfony\Component\HttpKernel\Exception\HttpException; use Symfony\Component\HttpKernel\KernelEvents; @@ -42,6 +43,7 @@ public function onKernelControllerArguments(ControllerArgumentsEvent $event) return; } + $request = $event->getRequest(); $arguments = $event->getNamedArguments(); foreach ($attributes as $attribute) { @@ -50,10 +52,10 @@ public function onKernelControllerArguments(ControllerArgumentsEvent $event) if ($subjectRef = $attribute->subject) { if (\is_array($subjectRef)) { foreach ($subjectRef as $refKey => $ref) { - $subject[\is_string($refKey) ? $refKey : (string) $ref] = $this->getIsGrantedSubject($ref, $arguments); + $subject[\is_string($refKey) ? $refKey : (string) $ref] = $this->getIsGrantedSubject($ref, $request, $arguments); } } else { - $subject = $this->getIsGrantedSubject($subjectRef, $arguments); + $subject = $this->getIsGrantedSubject($subjectRef, $request, $arguments); } } @@ -78,17 +80,21 @@ public static function getSubscribedEvents(): array return [KernelEvents::CONTROLLER_ARGUMENTS => ['onKernelControllerArguments', 10]]; } - private function getIsGrantedSubject(string|Expression $subjectRef, array $arguments): mixed + private function getIsGrantedSubject(string|Expression $subjectRef, Request $request, array $arguments): mixed { if ($subjectRef instanceof Expression) { $this->expressionLanguage ??= new ExpressionLanguage(); return $this->expressionLanguage->evaluate($subjectRef, [ + 'request' => $request, 'args' => $arguments, ]); } if (!\array_key_exists($subjectRef, $arguments)) { + if ('request' === $subjectRef) { + return $request; + } throw new RuntimeException(sprintf('Could not find the subject "%s" for the #[IsGranted] attribute. Try adding a "$%s" argument to your controller method.', $subjectRef, $subjectRef)); } diff --git a/src/Symfony/Component/Security/Http/Tests/EventListener/IsGrantedAttributeListenerTest.php b/src/Symfony/Component/Security/Http/Tests/EventListener/IsGrantedAttributeListenerTest.php index 8e165019a8ec4..3cc4f9e27d986 100644 --- a/src/Symfony/Component/Security/Http/Tests/EventListener/IsGrantedAttributeListenerTest.php +++ b/src/Symfony/Component/Security/Http/Tests/EventListener/IsGrantedAttributeListenerTest.php @@ -281,7 +281,7 @@ public function testNotFoundHttpException() $listener->onKernelControllerArguments($event); } - public function testIsGrantedwithExpressionInAttribute() + public function testIsGrantedWithExpressionInAttribute() { $authChecker = $this->createMock(AuthorizationCheckerInterface::class); $authChecker->expects($this->once()) @@ -301,8 +301,10 @@ public function testIsGrantedwithExpressionInAttribute() $listener->onKernelControllerArguments($event); } - public function testIsGrantedwithExpressionInSubject() + public function testIsGrantedWithExpressionInSubject() { + $request = new Request(); + $authChecker = $this->createMock(AuthorizationCheckerInterface::class); $authChecker->expects($this->once()) ->method('isGranted') @@ -314,6 +316,7 @@ public function testIsGrantedwithExpressionInSubject() ->method('evaluate') ->with(new Expression('args["post"].getAuthor()'), [ 'args' => ['post' => 'postVal'], + 'request' => $request, ]) ->willReturn('author'); @@ -321,7 +324,7 @@ public function testIsGrantedwithExpressionInSubject() $this->createMock(HttpKernelInterface::class), [new IsGrantedAttributeMethodsController(), 'withExpressionInSubject'], ['postVal'], - new Request(), + $request, null ); @@ -329,8 +332,10 @@ public function testIsGrantedwithExpressionInSubject() $listener->onKernelControllerArguments($event); } - public function testIsGrantedwithNestedExpressionInSubject() + public function testIsGrantedWithNestedExpressionInSubject() { + $request = new Request(); + $authChecker = $this->createMock(AuthorizationCheckerInterface::class); $authChecker->expects($this->once()) ->method('isGranted') @@ -342,6 +347,7 @@ public function testIsGrantedwithNestedExpressionInSubject() ->method('evaluate') ->with(new Expression('args["post"].getAuthor()'), [ 'args' => ['post' => 'postVal', 'arg2Name' => 'arg2Val'], + 'request' => $request, ]) ->willReturn('author'); @@ -349,11 +355,53 @@ public function testIsGrantedwithNestedExpressionInSubject() $this->createMock(HttpKernelInterface::class), [new IsGrantedAttributeMethodsController(), 'withNestedExpressionInSubject'], ['postVal', 'arg2Val'], - new Request(), + $request, null ); $listener = new IsGrantedAttributeListener($authChecker, $expressionLanguage); $listener->onKernelControllerArguments($event); } + + public function testIsGrantedWithRequestAsSubjectAndNoArgument() + { + $request = new Request(); + + $authChecker = $this->createMock(AuthorizationCheckerInterface::class); + $authChecker->expects($this->once()) + ->method('isGranted') + ->with('SOME_VOTER', $request) + ->willReturn(true); + + $event = new ControllerArgumentsEvent( + $this->createMock(HttpKernelInterface::class), + [new IsGrantedAttributeMethodsController(), 'withRequestAsSubjectAndNoArgument'], + [], + $request, + null + ); + + $listener = new IsGrantedAttributeListener($authChecker); + $listener->onKernelControllerArguments($event); + } + + public function testIsGrantedWithRequestAsSubjectAndArgument() + { + $authChecker = $this->createMock(AuthorizationCheckerInterface::class); + $authChecker->expects($this->once()) + ->method('isGranted') + ->with('SOME_VOTER', 'foobar') + ->willReturn(true); + + $event = new ControllerArgumentsEvent( + $this->createMock(HttpKernelInterface::class), + [new IsGrantedAttributeMethodsController(), 'withRequestAsSubjectAndArgument'], + ['foobar'], + new Request(), + null + ); + + $listener = new IsGrantedAttributeListener($authChecker); + $listener->onKernelControllerArguments($event); + } } diff --git a/src/Symfony/Component/Security/Http/Tests/Fixtures/IsGrantedAttributeMethodsController.php b/src/Symfony/Component/Security/Http/Tests/Fixtures/IsGrantedAttributeMethodsController.php index 63e73a7b23b93..cbce0107d45bc 100644 --- a/src/Symfony/Component/Security/Http/Tests/Fixtures/IsGrantedAttributeMethodsController.php +++ b/src/Symfony/Component/Security/Http/Tests/Fixtures/IsGrantedAttributeMethodsController.php @@ -62,4 +62,14 @@ public function withExpressionInSubject($post) public function withNestedExpressionInSubject($post, $arg2Name) { } + + #[IsGranted(attribute: 'SOME_VOTER', subject: 'request')] + public function withRequestAsSubjectAndNoArgument() + { + } + + #[IsGranted(attribute: 'SOME_VOTER', subject: 'request')] + public function withRequestAsSubjectAndArgument($request) + { + } }