FOSUserBundle: Registration redirect if logged in - symfony

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

Related

Symfony 4 : Request object change

i'm working on Symfony 4.
I have a service:
App\EventListener\EbayExceptionListener:
tags:
- { name: kernel.event_listener, event: kernel.controller }
arguments: [ "#doctrine.orm.entity_manager" , "#router" , "#session" ]
And here is my listener :
<?php
namespace App\EventListener;
use App\Entity\Ebay;
use Doctrine\ORM\EntityManager;
use Symfony\Component\HttpFoundation\RedirectResponse;
use Symfony\Component\HttpFoundation\Session\Session;
use Symfony\Component\HttpKernel\Event\FilterControllerEvent;
use Symfony\Component\Routing\Router;
class EbayExceptionListener
{
private $repository;
private $router;
private $session;
private $redirectName = 'home';
public function __construct(EntityManager $entityManager, Router $router, Session $session)
{
$this->repository = $entityManager->getRepository(Ebay::class);
$this->router = $router;
$this->session = $session;
}
//Check if an eBay entity exist in the database, if not redirect to the form to create the eBay entity
public function onKernelController(FilterControllerEvent $event)
{
$ebay = $this->repository->findOneBy(['name' => 'Ebay']);
$request = $event->getRequest();
$routeName = $request->get('_route');
dump($routeName);
if ($routeName != "home" AND $ebay == null) {
$this->session->getFlashBag()->add('error', 'Please provide information for Ebay form');
return new RedirectResponse($this->router->generate($this->redirectName));
}
}
}
My Controller:
<?php
namespace App\Controller;
use Symfony\Component\Routing\Annotation\Route;
use Symfony\Bundle\FrameworkBundle\Controller\Controller;
class HomeController extends Controller
{
/**
* #Route("/", name="home")
*/
public function index()
{
return $this->render('home/index.html.twig', [
'controller_name' => 'HomeController',
]);
}
}
I have a problem with the var $routeName inside my Listener, in fact the value of this variable should be the name of the current route. But when I use the dump($routeName)
I have 2 results : "_wdt" and "home"
I don't know why the $request parameters are not the same when my listener is executed and after the page has been generated.
Because I have no page call "_wdt" it supposed to be "home".
Did I do something wrong ?
Thank you for your help.

How to Redirect out of an Event?

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?

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 }

FOSUserBundle: Success target after password reset

After the user did reset his password using the password reset of FOSUserBundle, by default he is redirected to the FOSUserProfile.
I want to redirect to a different route.
Is this possible and if yes, how?
It can be done by creating a resetting subscriber:
namespace Acme\UserBundle\EventListener;
use FOS\UserBundle\FOSUserEvents;
use FOS\UserBundle\Event\FormEvent;
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 PasswordResettingListener implements EventSubscriberInterface {
private $router;
public function __construct(UrlGeneratorInterface $router) {
$this->router = $router;
}
public static function getSubscribedEvents() {
return [
FOSUserEvents::RESETTING_RESET_SUCCESS => 'onPasswordResettingSuccess',
];
}
public function onPasswordResettingSuccess(FormEvent $event) {
$url = $this->router->generate('homepage');
$event->setResponse(new RedirectResponse($url));
}
}
And then registering it as a service with kernel.event_subscriber tag:
# src/Acme/UserBundle/Resources/config/services.yml
services:
acme_user.password_resetting:
class: Acme\UserBundle\EventListener\PasswordResettingListener
arguments: [ #router ]
tags:
- { name: kernel.event_subscriber }
In case you are not using the FOS user profile view, there is an ugly yet simple way:
Add in your app/config/routing.yml:
fos_user_profile_show:
path: /yourpath

allow only one connection on the same login with FOSUserBundle

I'm creating a website thanks to Symfony2 with FOSUserBundle.
I'm triyng to deny multiple connections on the same login (but from different computers for example).
I've 2 solutions :
Create an event listner on authentification but I didn't manage to make it. (even with the cookbook).
override the login_check method but my FOSUserBundle doesn't work if I do it.
Do you have any better options?
Or any solutions?
Got it finaly. There is just one last update to make to solve it all.
You need to add an other field to the User entity. sessionId (string).
Then update your LoginListener class like that :
// YourSite\UserBundle\Listener\YourSiteLoginListener.php
//...
public function onSecurityInteractiveLogin(InteractiveLoginEvent $event)
{
$request = $event->getRequest();
$session = $request->getSession();
$user = $event->getAuthenticationToken()->getUser();
$has_session = is_file ( '/path_to_your_php_session_file/'.'sess_'.$user->getSessionId() );
if($user->getLogged() && $has_session){
throw new AuthenticationException('this user is already logged');
}else{
$user->setLogged(true);
$user->setSessionId($session->getId());
$this->userManager->updateUser($user);
}
}
Maybe this will help people to solve this problem.
It's kind of a solution but there is still a problem :
If the user session is killed by php (after too mush time without action for example), you will have to go into your database to reset the "logged" value to 0.
So my solution is :
-add the field "logged" (boolean) to you User entity.
-in YourSite\UserBundle\Listener create a : YourSiteLoginListener.php with this code
namespace YourSite\UserBundle\Listener;
use FOS\UserBundle\Model\UserManagerInterface;
use FOS\UserBundle\Model\UserInterface;
use Symfony\Component\Security\Http\Event\InteractiveLoginEvent;
use Symfony\Component\Security\Core\Exception\AuthenticationException;
use Symfony\Component\Security\Core\SecurityContext;
class YourSiteLoginListener
{
private $userManager;
public function __construct(UserManagerInterface $userManager)
{
$this->userManager = $userManager;
}
public function onSecurityInteractiveLogin(InteractiveLoginEvent $event)
{
$user = $event->getAuthenticationToken()->getUser();
if($user->getLogged()){
throw new AuthenticationException('this user is already logged');
}else{
$user->setLogged(true);
$this->userManager->updateUser($user);
}
}
}
-then in the same directory, create a logout handler : YourSiteLogoutHandler.php
namespace YourSite\UserBundle\Listener;
use FOS\UserBundle\Model\UserManagerInterface;
use FOS\UserBundle\Model\UserInterface;
use Symfony\Component\Security\Core\Authentication\Token\TokenInterface;
use Symfony\Component\HttpFoundation\Response;
use Symfony\Component\HttpFoundation\Request;
use Symfony\Component\Security\Http\Logout\LogoutHandlerInterface;
class YourSiteLogoutHandler implements LogoutHandlerInterface
{
private $userManager;
public function __construct(UserManagerInterface $userManager)
{
$this->userManager = $userManager;
}
public function logout (Request $request, Response $response, TokenInterface $token){
$user = $token->getUser();
if($user->getLogged()){
$user->setLogged(false);
$this->userManager->updateUser($user);
}
}
}
-finaly declare those services in your app/config.yml for example:
services:
yoursite_login_listener:
class: YourSite\UserBundle\Listener\YourSiteLoginListener
arguments: [#fos_user.user_manager]
tags:
- { name: kernel.event_listener, event: security.interactive_login, method :onSecurityInteractiveLogin }
yoursite_logout_handler:
class: YourSite\UserBundle\Listener\YourSiteLogoutHandler
arguments: [#fos_user.user_manager]
In Symfony3, the logout handler was not trigged by the code above.
I rebuild the code so the system is updated when the user is logging out.
namespace YourSite\UserBundle\Listener;
use FOS\UserBundle\Model\UserManagerInterface;
use Symfony\Component\HttpFoundation\Request;
use Symfony\Component\HttpFoundation\RedirectResponse;
use Symfony\Component\Security\Http\Logout\LogoutSuccessHandlerInterface;
class LogoutSuccessHandler implements LogoutSuccessHandlerInterface
{
private $userManager;
public function __construct(UserManagerInterface $userManager)
{
$this->userManager = $userManager;
}
public function onLogoutSuccess(Request $request){
global $kernel;
$user = $kernel->getContainer()->get('security.token_storage')->getToken()->getUser();
if($user->getLogged()){
$user->setLogged(false);
$this->userManager->updateUser($user);
}
$referer = $request->headers->get('referer');
return new RedirectResponse($referer);
}
}

Resources