Skip to content

Instantly share code, notes, and snippets.

@77web
Created February 25, 2013 07:23
Show Gist options
  • Star 20 You must be signed in to star a gist
  • Fork 8 You must be signed in to fork a gist
  • Save 77web/5028312 to your computer and use it in GitHub Desktop.
Save 77web/5028312 to your computer and use it in GitHub Desktop.
How to switch user accounts between two(or more) different firewalls when using Symfony\Bundle\SecurityBundle
//you may have some more configs here...
providers:
user:
entity: { class: MyAppBundle:User, property: loginEmail }
admin:
entity: { class: MyAppBundle:Admin, property: username }
firewalls:
dev:
pattern: ^/(_(profiler|wdt)|css|images|js)/
security: false
login:
pattern: /login$
security: false
frontend:
provider: user
anonymous: ~
pattern: ^/user
form_login:
login_path: /user/login
check_path: /user/login_check
logout:
path: /user/logout
target: /user/
backend:
provider: admin
pattern: ^/admin
form_login:
login_path: /admin/login
check_path: /admin/login_check
logout:
path: /admin/logout
target: /admin/
#anonymous: ~
access_control:
- { path: ^/user/switch$, roles: IS_AUTHENTICATED_ANONYMOUSLY }
- { path: ^/user, roles: ROLE_USER }
- { path: ^/admin, roles: ROLE_ADMIN }
//you may have some more routings here...
admin_switch_to_user:
pattern: /admin/user/switch/{userId}
defaults: { _controller: MyAppBundle:Admin\User:switch }
requirements: { userId: integer }
user_switch:
pattern: /user/switch
defaults: { _controller: MyAppBundle:Account:switch }
user_switch_exit:
pattern: /user/exit_switch
defaults: { _controller: MyAppBundle:Account:exitSwitch }
use Symfony\Component\Security\Core\Authentication\UsernamePasswordToken;
use Symfony\Component\Security\Http\Event\InteractiveLoginEvent;
//you may have some more action methods here...
public function switchAction()
{
if ($this->get('session')->get('userid_to_switch'))
{
$user = $this->getDoctrine()->getEntityManager()->find('MyAppBundle:User', $this->get('session')->get('userid_to_switch'));
if ($user)
{
//create token instance
//2nd argument is password, but empty string is accepted
//3rd argument is "firewall" name(be careful, not a "provider" name!!! though UsernamePasswordToken.php names it as "providerKey")
$token = new UsernamePasswordToken($user, '', 'frontend', $user->getRoles());
//set token instance to security context
$this->get('security.context')->setToken($token);
//fire a login event
$event = new InteractiveLoginEvent($this->getRequest(), $token);
$this->get('event_dispatcher')->dispatch('security.interactive_login', $event);
//redirect to some user scoped actions
return $this->redirect($this->generateUrl('user_homepage'));
}
}
//fallback
return $this->redirect($this->generateUrl('admin_homepage'));
}
public function exitSwitchAction()
{
//unset "userid_to_switch" in session to avoid conflicts
$this->get('session')->set('userid_to_switch', null);
//logout as user
$this->get('security.context')->setToken(null);
//going back to admin context...
return $this->redirect($this->generateUrl('admin_homepage'));
}
//you may have some more action methods here...
public function switchAction($userId)
{
//never use the session key "userid_to_switch" in other actions
$this->get('session')->set('userid_to_switch', $userId);
return $this->redirect($this->generateUrl('user_switch'));
}
//how to render a link to "exit_switch" in switched user context
{% if app.user %}
{% if app.session.get('userid_to_switch') %}
<a href="{{ path('user_switch_exit') }}">{{ 'Re-switch as admin'|trans }}</a>
{% else %}
<a href="{{ path('user_logout') }}">{{ 'Logout'|trans }}</a>
{% endif %}
{% endif %}
@Htbaa
Copy link

Htbaa commented Sep 28, 2013

Thanks for this! This was exactly what I needed. Had some issues with it at first but that was because I didn't have my firewalls and access_control setup properly.

My problem involved 3 providers. One was a username/password combination but the other two were email/password so I couldn't use the built in chaining. This works like a charm!

@bnlab
Copy link

bnlab commented Apr 14, 2014

This is very handy piece of code. Thanks.

@gnat42
Copy link

gnat42 commented Nov 17, 2020

Not sure why but this has stopped working for me in SF5. Investigating the issue...

@gnat42
Copy link

gnat42 commented Nov 17, 2020

Figured it out - sometime in SF4/SF5 time frame there was a change that checks if the user in the session/token matches the user retrieved from the DB. Because additional roles are added (ROLE_PREVIOUS_ADMIN or IS_IMPERSONATOR) that check fails. I fixed it by implementing the EquatableInterface on the User object and doing my own check. Returning true from there allows it to work as designed above.

@77web
Copy link
Author

77web commented Nov 17, 2020

@gnat42
Thank you!!
I've totally given up for this in SF4, but your advice enables it again!

@gnat42
Copy link

gnat42 commented Nov 18, 2020

No problem!

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment