How to Redirect out of an Event? - symfony

I am listening to security.authentication.success event. Everytime a customer logs in i need to check if he still got main information. If not, then i want to redirect him to the form for creation. It is running good but in which way can i redirect out of an event? Simply returning an Redirect Object does not work.
Service:
applypie_userbundle.newuser_listener:
class: Applypie\Bundle\UserBundle\EventListener\NewUserListener
arguments: [#router]
tags:
- { name: kernel.event_listener, event: security.authentication.success, method: onLogin }
Listener:
namespace Applypie\Bundle\UserBundle\EventListener;
use Symfony\Component\Security\Core\Event\AuthenticationEvent;
use Symfony\Component\Security\Core\SecurityContext;
use Symfony\Component\HttpFoundation\RedirectResponse;
use Symfony\Component\Routing\Router;
class NewUserListener
{
protected $context;
protected $router;
public function __construct(Router $router)
{
$this->router = $router;
}
/**
* #param AuthenticationEvent $event
*
* if current user neither have an applicant row or an company row, redirect him to create applicant row
*/
public function onLogin(AuthenticationEvent $event)
{
$user = $event->getAuthenticationToken()->getUser();
if(!$user->getApplicant() && !count($user->getCompanies()))
{
return new RedirectResponse($this->router->generate('applypie_user_applicant_create'));
}
}
}

To cut the long story short, yes, you are listening to the correct event but... is someone waiting your method return?... NOPE!
I can't test this right now, but this is the idea:
security.yml
firewalls:
main:
pattern: ^/
form_login:
success_handler: my.security.login_handler
services.yml
services:
my.security.login_handler:
class: Applypie\Bundle\UserBundle\EventListener\NewUserListener
arguments: [#router]
NewUserListener
namespace Applypie\Bundle\UserBundle\EventListener;
use Symfony\Component\Security\Http\Authentication\AuthenticationSuccessHandlerInterface;
use Symfony\Component\HttpFoundation\RedirectResponse;
use Symfony\Component\Routing\RouterInterface;
use Symfony\Component\Security\Core\Authentication\Token\TokenInterface;
use Symfony\Component\HttpFoundation\Request;
class NewUserListener implements AuthenticationSuccessHandlerInterface
{
protected $router;
public function __construct(RouterInterface $router)
{
$this->router = $router;
}
protected function getResponse($name)
{
$url = $this->router->generate($name);
$response = new RedirectResponse($url);
return $response;
}
public function onAuthenticationSuccess(Request $request, TokenInterface $token)
{
$user = $token->getUser();
$route = 'user_home';
if(!$user->getApplicant() && empty($user->getCompanies())) {
$route = 'applypie_user_applicant_create';
}
return $this->getResponse($route);
}
}
Sources:
http://api.symfony.com/2.3/Symfony/Component/Security/Http/Authentication/AuthenticationSuccessHandlerInterface.html
Do something just after symfony2 login success and before redirect?

Related

Symfony 3.4: Access doctrine within an EventListener

I am trying to access to doctrine within an EventListener which tests a database table, if it is invalid then the user will be redirected to a config page where he will fix the issues!
The test will be executed before every call to a controller, so I will use the Event kernel.controller:
[EDITED: SOLVED, TESTING AND REDIRECTING VERY FINE]
<?php
namespace AdminBundle\EventListener;
use Symfony\Bundle\FrameworkBundle\Routing\Router;
use Doctrine\ORM\EntityManager;
use Symfony\Component\HttpFoundation\RedirectResponse;
use Symfony\Component\HttpFoundation\RequestStack;
use Symfony\Component\HttpFoundation\Session\Session;
class BuildReferencesEventListener
{
/**
* #var EntityManager
*/
protected $em;
/**
* #var Router
*/
protected $router;
protected $references = [
1 => 'oxygen',
2 => 'pH',
3 => 'pCO2'
];
protected $redirect_route = 'references';
protected $requestStack;
protected $session;
public function __construct(EntityManager $entityManager, Router $router, RequestStack $requestStack, Session $session)
{
$this->em = $entityManager;
$this->router = $router;
$this->requestStack = $requestStack;
$this->session = $session;
}
public function onKernelController()
{
$em = $this->em;
$savedReferences = $em->getRepository('AdminBundle:ParamReference')->findAll();
$references = $this->references;
if (count($savedReferences) <= 0){
$this->redirect();
}
for ($i =0; $i<count($savedReferences); $i++){
if ($savedReferences[$i] !== $references[$i]) {
$this->redirect();
}
}
}
public function redirect()
{
$request = $this->requestStack->getCurrentRequest();
$route = $request->get('_route');
$this->session->getFlashBag()->add('warning', 'You need to setup the references for the parameters before starting the work');
if ($route != 'references'){
$url = $this->router->generate('references');
$redirect = new RedirectResponse($url);
$redirect->send();
}
}
}
Here is services.yml
build.references:
class: AdminBundle\EventListener\BuildReferencesEventListener
arguments: ['#doctrine.orm.entity_manager', '#router', '#request_stack', '#session']
tags:
- { name: kernel.event_listener, event: kernel.controller, method: onKernelController }
Add argument inside your service.yml event listner service section
Your.service:
Class: AdminBundle\EventListener\BuildReferencesEventListener
arguments: ['#doctrine.orm.entity_manager']
tags:
- { name: kernel.event_listener, event: kernel.controller }
And add this __cunstruct code inside your listner:
namespace AdminBundle\EventListener;
use Symfony\Component\HttpKernel\Event\FilterControllerEvent;
use Doctrine\ORM\EntityManager;
class BuildReferencesEventListener
{
protected $em;
function __construct(EntityManager $em)
{
$this->em = $em;
}
public function onKernelController(FilterControllerEvent $event)
{
//TO DO call doctrine
// $em = Enity Manager...
// $em->getRepository...
// I hope its clear enough what im trying to do
}
In Symfony 3.4 and above, autowiring is enabled by default, so you just have to add a type-hinted argument to your service's constructor method and a private property to assign the argument's value to.
use Doctrine\ORM\EntityManagerInterface;
use Symfony\Component\HttpKernel\Event\FilterControllerEvent;
class BuildReferencesEventListener
{
private $em;
public __construct(EntityManagerInterface $em)
{
$this->em = $em;
}
public function onKernelController(FilterControllerEvent $event)
{
$em = this->em;
// [...]
}
}
If you need to
declare arguments explicitly, you could do this in your service configuration:
# config/services.yaml
services:
# ... same code as before
# explicitly configure the service
AdminBundle\EventListener\BuildReferencesEventListener:
arguments:
$em: '#doctrine.orm.entity_manager_interface'
This could be useful to pass parameters that the service container doesn't know.
To learn more about the service container, https://symfony.com/doc/3.4/service_container.html#injecting-services-config-into-a-service.

how to make the onAuthenticationSuccess function being called

I would like to have some action after a success authentification.
I created a class extended DefaultAuthenticationSuccessHandler, and redefined the onAuthenticationSuccess function.
Though the authentication is a success, the programm never goes to the function : why ?
namespace my_name_space;
use Symfony\Component\Security\Http\Authentication\DefaultAuthenticationSuccessHandler;
use Symfony\Component\HttpFoundation\Request;
use Symfony\Component\Security\Core\Authentication\Token\TokenInterface;
use Doctrine\ORM\EntityManager;
use Symfony\Component\Security\Http\HttpUtils;
use Symfony\Bridge\Monolog\Logger;
class AuthenticationSuccessHandler extends DefaultAuthenticationSuccessHandler {
private $em;
private $logger;
function __construct(Logger $logger, HttpUtils $httpUtils, EntityManager $em) {
parent::__construct($httpUtils);
$this->em = $em;
$this->logger = $logger;
}
public function onAuthenticationSuccess(Request $request, TokenInterface $token) {
$this->logger->info("onAuthenticationSuccess");
$response = parent::onAuthenticationSuccess($request, $token);
$token->eraseCredentials();
$token->getUser()->addRole('ROLE_USER');
return $response;
}
}
services:
security.authentication.success.handler:
class: MY\NAMESPACE\AuthenticationSuccessHandler
arguments: ["#monolog.logger.php","#security.http_utils", "#doctrine.orm.default_entity_manager"]
security.yml
form_login:
check_path: _security_check
use_referer: true
use_forward: true
login_path: my_login
success_handler: security.authentication.success.handler
You can listen for the event that is triggered after a successful login attempt by registering a service to the related security tag:
app.login_listener:
class: AppBundle\EventListener\LoginListener
arguments:
- #security.authorization_checker
- #security.token_storage
tags:
- { name: kernel.event_listener, event: security.interactive_login, method: yourMethod }
Where yourMethod can contain the code you want to execute.
There are a lot of same issues on SO, github, google groups, most of them are unsolved.
Also, you should define a custom AuthenticationSuccessHandler rather than extending the default.
Example:
services.yml
login_success_handler:
class: YourBundle\EventListener\CustomAuthenticationSuccessHandler
arguments: ["#doctrine", "#router"]
tags:
- { name: kernel.event_listener, event: security.interactive_login, method: onSecurityInteractiveLogin }
CustomAuthenticationSuccessHandler.php
use Symfony\Component\HttpFoundation\Request;
use Symfony\Component\Security\Core\Authentication\Token\TokenInterface;
use Symfony\Component\HttpFoundation\Routing\RouterInterface;
use Symfony\Component\Security\Http\Event\InteractiveLoginEvent;
use Doctrine\ORM\EntityManager;
class CustomAuthenticationSuccessHandler {
private $em;
private $logger;
public function __construct(EntityManager $em, RouterInterface $router)
{
$this->em = $em;
$this->router = $router;
}
public function onSecurityInteractiveLogin(InteractiveLoginEvent $event)
{
$this->onAuthenticationSuccess($event->getRequest(), $event->getAuthenticationToken());
}
public function onAuthenticationSuccess(Request $request, TokenInterface $token)
{
$user = $token->getUser();
$user->eraseCredentials();
$user->addRole('ROLE_USER');
$this->em->flush() // If you don't do this, your changes will not be saved.
$response = new RedirectResponse($this->router->generate('your_success_route'));
return $response;
}
}
Don't need of manually set the success_handler in your form, because the SuccessHandler is an EventListener and will be used on the corresponding event.
I don't know what the interactive login event is proposed here therefore I write a separate answer. #chalasr's answer worked for me but with security.authentication.success event:
login_success_handler:
class: YourBundle\EventListener\CustomAuthenticationSuccessHandler
arguments: ["#doctrine", "#router"]
tags:
- { name: kernel.event_listener, event: security.authentication.success, method: onAuthenticationSuccess }

FOSUserBundle: Registration redirect if logged in

I was wondering what would be the best method to redirect a user who is ALREADY LOGGED IN if they try and access the registration page.
I would prefer a method that does not require me overriding the registration controller simply to implement a login check & redirect.
I looked at the registration initialization event but I don't know how to initiate the redirect since there doesn't seem to be a way to set the event response via the UserEvent class.
Thanks
I use something like the following..
namespace Acme\UserBundle\EventSubscriber;
use FOS\UserBundle\Event\GetResponseUserEvent;
use FOS\UserBundle\FOSUserEvents;
use Symfony\Component\EventDispatcher\EventSubscriberInterface;
use Symfony\Component\HttpFoundation\RedirectResponse;
use Symfony\Component\Routing\Generator\UrlGeneratorInterface;
use Symfony\Component\Security\Core\SecurityContextInterface;
class FOSUserSubscriber implements EventSubscriberInterface
{
protected $router;
protected $securityContext;
public function __construct(
UrlGeneratorInterface $router,
SecurityContextInterface $securityContext
)
{
$this->router = $router;
$this->securityContext = $securityContext;
}
public static function getSubscribedEvents()
{
return array(
FOSUserEvents::REGISTRATION_INITIALIZE => 'forwardToRouteIfUser',
);
}
public function forwardToRouteIfUser(GetResponseUserEvent $event)
{
if (!$this->securityContext->isGranted('IS_AUTHENTICATED_REMEMBERED')) {
return;
}
$url = $this->router->generate('acme_the_route_to_redirect_to');
$response = new RedirectResponse($url);
$event->setResponse($response);
}
}
With
parameters:
acme_user.subscriber.fos_user.class:
Acme\UserBundle\EventSubscriber\FOSUserSubscriber
services:
acme_user.subscriber.fos_user:
class: %acme_user.subscriber.fos_user.class%
arguments:
- #router
- #security.context
tags:
- { name: kernel.event_subscriber

how to redirect after login success by using EventListener

I have a problem in the code of event listener because i need to make a EventListener when i login as type 1 ==>redirect to:
return $this->redirect($this->generateUrl('demands_patient'));
else redirect to another path .And this is the code of loginlistener
use Doctrine\ORM\Event\LifecycleEventArgs;
use Register\UserBundle\Entity\User;
use FOS\UserBundle\FOSUserEvents;
use FOS\UserBundle\Event\FormEvent;
use FOS\UserBundle\Event\GetResponseUserEvent;
use Symfony\Component\EventDispatcher\EventSubscriberInterface;
use Symfony\Component\HttpFoundation\RedirectResponse;
use Symfony\Component\Routing\Generator\UrlGeneratorInterface;
use Symfony\Component\Security\Http\Event\InteractiveLoginEvent;
use Symfony\Component\Security\Core\AuthenticationEvents;
use Symfony\Component\EventDispatcher\EventSubscriberInterface;
use Symfony\Component\Security\Http\Event\InteractiveLoginEvent;
class LoginListener implements EventSubscriberInterface
{
private $router;
public function __construct(UrlGeneratorInterface $router)
{
$this->router = $router;
}
public static function onSecurityInteractiveLogin()
{
return array(
FOSUserEvents::AUTHENTICATION_SUCCESS => 'redirectlogin',
);
}
public function redirectlogin(InteractiveLoginEvent $event)
{
$user = $event->getUser();
if($user->getType()=="1")
{
return $this->redirect($this->generateUrl('demands_patient'));
}
else
{
return $this->redirect($this->generateUrl('demands'));
}
}
}
and i add configuration in services.yml:
register_user.eventlistener.loginlistener:
class: Register\UserBundle\EventListener\LoginListener
arguments: [#router]
tags:
- { name: kernel.onSecurityInteractiveLogin }
Just today I faced a similar problem.
My solution was to use a Handler instead of a Listener, as explained in http://www.reecefowell.com/2011/10/26/redirecting-on-loginlogout-in-symfony2-using-loginhandlers/
What I did is:
In my bundle's `services.yml':
parameters:
cambra_comunitats.handler.user_login_redirect_handler.class: Cambra\ComunitatsBundle\Handler\UserLoginRedirectHandler
services:
cambra_comunitats.handler.user_login_redirect_handler:
class: "%cambra_comunitats.handler.user_login_redirect_handler.class%"
arguments: [#router, #security.context]
tags:
- { name: 'monolog.logger', channel: 'security' }
Then, I created the Cambra\ComunitatsBundle\Handler\UserLoginRedirectHandler class:
class UserLoginRedirectHandler implements \Symfony\Component\Security\Http\Authentication\AuthenticationSuccessHandlerInterface
{
protected $router;
protected $security;
public function __construct(\Symfony\Component\Routing\Router $router, \Symfony\Component\Security\Core\SecurityContext $security)
{
$this->router = $router;
$this->security = $security;
}
public function onAuthenticationSuccess(\Symfony\Component\HttpFoundation\Request $request, \Symfony\Component\Security\Core\Authentication\Token\TokenInterface $token)
{
if ($this->security->isGranted('ROLE_ADMIN') && $this->security->getToken()->getUser()->isMustRedirect())
{
$response = new \Symfony\Component\HttpFoundation\RedirectResponse($this->router->generate('new_destination'));
} else
{
$referer_url = $this->router->generate('index_page');
$response = new \Symfony\Component\HttpFoundation\RedirectResponse($referer_url);
}
return $response;
}
}
And, finally, I enabled it on security.yml, inside of firewalls.main.form_login:
//.....
firewalls:
//...
main:
//...
form_login:
//...
success_handler: cambra_comunitats.handler.user_login_redirect_handler

Symfony2: check-email after registration

First time I'm posting a message in this forum, which I use regularly. I use FOSUserbundle in my Symfony2 application to manage users. I activated the sending of the email confirmation when users create an account by the following thing:
fos_user:
registration:
confirmation:
enabled: true
It works very well: the email is sent successfully. I am redirected to the page /check-email that says that this email has been sent. However, I would like to change the redirection : I would like to be redirected to my index page and not to /check-email. So I did my research and I knew he had to go through the FOSUserBundle events (list here).
What I did :
class RegistrationListener implements EventSubscriberInterface {
private $router;
public function __construct(UrlGeneratorInterface $router) {
$this->router = $router;
}
public static function getSubscribedEvents() {
return array(
FOSUserEvents::REGISTRATION_CONFIRM => 'onRegistrationConfirm');
}
public function onRegistrationConfirm(FormEvent $event) {
$url = $this->router->generate('listeArticlesAccueil');
$event->setResponse(new RedirectResponse($url));
}
}
and services.yml
services:
listener_user.registration_listener:
class: My\Soft\UserBundle\EventListener\RegistrationListener
arguments: [#router]
tags:
- { name: kernel.event_subscriber }
The problem is that every time I am redirected to the page /check-email. I therefore told me that it was probably the wrong event. So I also tried REGISTRATION_SUCCESS. But nothing changes. So either I have not used the right event, or I'm doing something wrong.
In either case, I hope you can help me !
Thanks a lot and sorry for my bad english ;)
I know that this question has been posted a long time ago, but i found a solution and I would like to share it to others that have the same issue.
In this case, the event that you should use is REGISTRATION_SUCCESS.
You have to prioritize the listener REGISTRATION_SUCCESS to be called before FOS\UserBundle\EventListener\EmailConfirmationListener::onRegistrationSuccess like this:
public static function getSubscribedEvents()
{
return [
FOSUserEvents::REGISTRATION_SUCCESS => [
['onRegistrationSuccess', -10],
],
];
}
If you don't do it, EmailConfirmationListener will be called earlier and you will be redirected to fos_user_registration_check_email route.
Here is what I did:
class RedirectAfterRegistrationSubscriber implements EventSubscriberInterface
{
private $router;
public function __construct(RouterInterface $router)
{
$this->router = $router;
}
public function onRegistrationSuccess(FormEvent $event)
{
$event->stopPropagation();
$url = $this->router->generate('homepage');
$response = new RedirectResponse($url);
$event->setResponse($response);
}
public static function getSubscribedEvents()
{
return [
FOSUserEvents::REGISTRATION_SUCCESS => ['onRegistrationSuccess',-10],
];
}
}
And in app/services.yml:
app.redirect_after_registration_subscriber:
class: AppBundle\EventListener\RedirectAfterRegistrationSubscriber
arguments: ['#router']
tags:
- { name: kernel.event_subscriber }
I hope this helps.
I would bet on REGISTRATION_SUCCESS.
The doc (https://github.com/FriendsOfSymfony/FOSUserBundle/blob/master/FOSUserEvents.php) says
that this event allows to set the response.
REGISTRATION_CONFIRM only allows to access the user
Instead of that use REGISTRATION_CONFIRM.
Here is the complete code for the event listener:
/**
* Description of RegisterationConfirmListener
*
* #author imran
*/
use FOS\UserBundle\FOSUserEvents;
use FOS\UserBundle\Event\GetResponseUserEvent;
use Symfony\Component\EventDispatcher\EventSubscriberInterface;
use Symfony\Component\HttpFoundation\RedirectResponse;
use Symfony\Component\Routing\Generator\UrlGeneratorInterface;
/**
* Description of RegisterationConfirmListener
*
* #author imran
*/
use FOS\UserBundle\FOSUserEvents;
use FOS\UserBundle\Event\GetResponseUserEvent;
use Symfony\Component\EventDispatcher\EventSubscriberInterface;
use Symfony\Component\HttpFoundation\RedirectResponse;
use Symfony\Component\Routing\Generator\UrlGeneratorInterface;
/**
* Listener responsible to change the redirection at the end of the password resetting
*/
class RegisterationConfirmListener implements EventSubscriberInterface {
private $router;
public function __construct(UrlGeneratorInterface $router) {
$this->router = $router;
}
public static function getSubscribedEvents() {
return [
FOSUserEvents::REGISTRATION_CONFIRM => 'onRegisterationConfirm',
];
}
public function onRegisterationConfirm(GetResponseUserEvent $event) {
$url = $this->router->generate('home');
$event->setResponse(new RedirectResponse($url));
}
}
For Services.yml:
app_user.registeration_confirmed:
class: Wishlocker\AppBundle\EventListener\RegisterationConfirmListener
arguments: [ #router ]
tags:
- { name: kernel.event_subscriber }

Resources