My question is about how to inject the entity manager in the SwitchUserListener that already has 9 parameters.
I have a custom switch user flow where I need to set the ExternalClient passed along with the _switch_user parameter (?_switch_user=user1&external_client_id=1) in the session. I first have to fetch the ExternalClient from the database before I can set it.
In parameters.yml I've added
security.authentication.switchuser_listener.class: App\Bundle\Listener\SwitchUserListener
And for the content of App\Bundle\Listener\SwitchUserListener
I used the Symfony SwitchUserListener Symfony\Component\Security\Http\Firewall\SwitchUserListener.php
Everything works and when I fetch the external_client_id
parameter from the request variable in the listener it is populated. But I can't seem to get access to the entity manager.
Things I've tried:
Add decorator in services.yml
app.decorating_switch_user:
class: App\Bundle\Listener\SwitchUserListener
decorates: security.authentication.switchuser_listener
arguments: ['@app.decorating_switch_user.inner', '@doctrine.orm.entity_manager']
public: false
Overriding parent dependencies in services.yml
security.authentication.switchuser_listener:
abstract: true
test:
class: "%security.authentication.switchuser_listener.class%"
parent: security.authentication.switchuser_listener
public: false
# appends the '@doctrine.orm.entity_manager' argument to the parent
# argument list
arguments: ['@doctrine.orm.entity_manager']
Listen to SwitchUserEvent instead
app.switch_user_listener:
class: App\Bundle\Listener\SwitchUserListener
tags:
- { name: kernel.event_listener, event: security.switch_user, method: onSwitchUser }
Here I've replace the contents of 'App\Bundle\Listener\SwitchUserListener' with:
class SwitchUserListener
{
public function onSwitchUser(SwitchUserEvent $event)
{
$request = $event->getRequest();
echo "<pre>";
dump($externalClientId = $request->get('external_client_id'));
echo "</pre>";
exit;
}
}
I'm getting the external_client_id as well with this attempt but I have no idea how to inject the entity manager. And even If I did, I'd have no way of getting the original user that initiated the _switch_user request. SwitchUserEvent only has access to the getTargetUser()
method.
Conclusion:
If anybody has experience with this topic and is willing to share it that would be great. Ideally I would add the entity manager service to the previous 9 arguments of the __construct function. I'm expanding that class just like Matt is doing here: Symfony2: Making impersonating a user more friendly
You can override the service as follows. You may need to look up/change the concrete order of service arguments as it changed between symfony versions. Some arguments like $providerKey
can be left empty as they will be changed/injected automatically by symfony.
In order to save some time coding you won't need to override the constructor if you use setter injection.
A look at Symfony's default SwitchUserListener (switch to the tag/version used in your application) will help when implementing your new handle
method.
# app/config/services.yml
services:
# [..]
security.authentication.switchuser_listener:
class: 'Your\Namespace\SwitchUserListener'
public: false
abstract: true
arguments:
- '@security.context'
- ~
- '@security.user_checker'
- ~
- '@security.access.decision_manager'
- '@?logger'
- '_switch_user'
- ~
- '@?event_dispatcher'
- ~
calls:
- [ 'setEntityManager', [ '@doctrine.orm.entity_manager' ]]
tags:
- { name: monolog.logger, channel: security }
Now your SwitchUserListener might look like this:
namespace Your\Namespace;
use Symfony\Component\Security\Http\Firewall\SwitchUserListener as DefaultListener;
use Doctrine\ORM\EntityManagerInterface;
use Symfony\Component\HttpKernel\Event\GetResponseEvent;
class SwitchUserListener extends DefaultListener
{
/** @var EntityManagerInterface */
protected $em;
public function setEntityManager(EntityManagerInterface $em)
{
$this->em = $em;
}
/**
* Handles the switch to another user.
*
* @throws \LogicException if switching to a user failed
*/
public function handle(GetResponseEvent $event)
{
// Do your custom switching logic here
}
}
Don't forget to clear your cache!