Potentially buggy construction of the `Cas` service
Created by: J-Ben87
Scenario
- a buggy request is "redirected" to the
Symfony\Component\HttpKernel\Controller\ErrorController
- the kernel handles the subrequest and ends up in the
Symfony\Component\HttpKernel\HttpKernel::finishRequest()
method where theRequest
is popped from theRequestStack
- then the kernel ends up in the
Symfony\Component\HttpKernel\HttpKernel::terminate()
method where theKernelEvents::TERMINATE
event is dispatched - during the process, the
Symfony\Component\HttpKernel\EventListener\ProfilerListener
collects (among others) the "not called listeners" - this triggers constructing all listeners which have not been called during the request lifecycle
If for some reason you have registered an EventListener
or an EventSubscriber
which depends on the EcPhp\CasLib\Cas
service, this will trigger an exception because it depends on the Psr\Http\Message\ServerRequestInterface
service (1st argument) which is created using a service factory here. But it turns out that this service is injected the symfony.request
which is the current request of the RequestStack
(see here). And as described in the scenario, in this case the Request
has been popped from the RequestStack
which means that RequestStack::getCurrentRequest()
will return null
. Injecting null
instead of a Request
in the Symfony\Bridge\PsrHttpMessage\HttpMessageFactoryInterface::createRequest()
factory method which requires a non nullable Request
object as 1st parameter will then trigger an exception.
I believe injecting a Request
object directly in a service is discouraged by Symfony because of this kind of scenario. They recommend to inject the RequestStack
instead, and get the (current) request at runtime.
Anyway, because the Symfony\Bridge\PsrHttpMessage\HttpMessageFactoryInterface::createRequest()
requires a non nullable Request
object and the RequestStack::getCurrentRequest()
returns a Request
or null
, it should definitely not be used to provide the request to the mentionned method.
I have to admit that I don't know how to get around that issue, but maybe you will because you definitely know your project better than I do. However I am ready to help if you need.
Steps to reproduce
- create an event listener
<?php
namespace App\EventListener;
use EcPhp\CasLib\CasInterface;
final class LogoutListener
{
public function __construct(private CasInterface $cas)
{
}
public function __invoke(): void
{
$this->cas->logout();
}
}
- register it
services:
App\EventListener\LogoutListener:
tags: [{ name: 'kernel.event_listener', event: 'Symfony\Component\Security\Http\Event\LogoutEvent', priority: 128 }]
- check it
$ bin/console debug:event-dispatcher Symfony\\Component\\Security\\Http\\Event\\LogoutEvent
In PsrHttpFactory.php line 49:
Symfony\Bridge\PsrHttpMessage\Factory\PsrHttpFactory::createRequest(): Argument #1 ($symfonyRequest) must be of type Symfony\Component\HttpFoundation\Request, null given, called in /app/var/cache/dev/ContainerTUsuQ6W/getCasService.ph
p on line 34