Thanks to visit codestin.com
Credit goes to github.com

Skip to content

[Security] Make request always available to #[IsGranted] #48080

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
Expand Up @@ -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;
Expand Down Expand Up @@ -42,6 +43,7 @@ public function onKernelControllerArguments(ControllerArgumentsEvent $event)
return;
}

$request = $event->getRequest();
$arguments = $event->getNamedArguments();

foreach ($attributes as $attribute) {
Expand All @@ -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);
}
}

Expand All @@ -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) {
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I don't think we should add that special case. It can be surprising as strings mean argument names. Having special string values makes things harder to explain request means the current request only when you don't have an argument named $request in this code).
To me, voting on the Request object is something that case be solved by using the Expression.

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Thanks for the feedback, will create a followup PR to address this issue.

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));
}

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -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())
Expand All @@ -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')
Expand All @@ -314,23 +316,26 @@ public function testIsGrantedwithExpressionInSubject()
->method('evaluate')
->with(new Expression('args["post"].getAuthor()'), [
'args' => ['post' => 'postVal'],
'request' => $request,
])
->willReturn('author');

$event = new ControllerArgumentsEvent(
$this->createMock(HttpKernelInterface::class),
[new IsGrantedAttributeMethodsController(), 'withExpressionInSubject'],
['postVal'],
new Request(),
$request,
null
);

$listener = new IsGrantedAttributeListener($authChecker, $expressionLanguage);
$listener->onKernelControllerArguments($event);
}

public function testIsGrantedwithNestedExpressionInSubject()
public function testIsGrantedWithNestedExpressionInSubject()
{
$request = new Request();

$authChecker = $this->createMock(AuthorizationCheckerInterface::class);
$authChecker->expects($this->once())
->method('isGranted')
Expand All @@ -342,18 +347,61 @@ public function testIsGrantedwithNestedExpressionInSubject()
->method('evaluate')
->with(new Expression('args["post"].getAuthor()'), [
'args' => ['post' => 'postVal', 'arg2Name' => 'arg2Val'],
'request' => $request,
])
->willReturn('author');

$event = new ControllerArgumentsEvent(
$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);
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -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)
{
}
}