PHP/Symfony: Cannot trigger the event for FOSUserBundle - php

I have a problem with subscribing events for FOSUserBundle (I am using this tutorial).
Basically, I am trying to just redirect a user to specified route after login. I made a class, just like in the tutorial:
//src:src/AppBundle/EventsListener/LoginListener.php
namespace AppBundle\EventsListener;
use FOS\UserBundle\FOSUserEvents;
use FOS\UserBundle\Event\FilterUserResponseEvent;
use FOS\UserBundle\Event\FormEvent;
use Symfony\Component\EventDispatcher\EventSubscriberInterface;
use Symfony\Component\HttpFoundation\RedirectResponse;
use Symfony\Component\Routing\Generator\UrlGeneratorInterface;
class LoginListener implements EventSubscriberInterface
{
private $router;
public function __construct(UrlGeneratorInterface $router)
{
$this->router = $router;
}
/**
* {#inheritDoc}
*/
public static function getSubscribedEvents()
{
return array(
FOSUserEvents::SECURITY_IMPLICIT_LOGIN => 'onSecurityImplicitLogin',
);
}
public function onSecurityImplicitLogin(FormEvent $event)
{
return $this->redirectToRoute('profile');
}
}
And my services.yml looks like this:
services:
login_listener:
class: AppBundle\EventsListener\LoginListener
arguments: ['#router']
tags:
- { name: 'kernel.event_subscriber' }
But it still redirects me to the "\" route. Can somebody help me find what's wrong? Besides, is this really the only way to define routes in this bundle?
And, by the way, are there any resources/manuals besides this one, "official docs"? I've found this manual very unclear and rough.
Thank you for concern!

For that simple purpose you have just to add in security.yml:
firewalls:
main:
form_login:
always_use_default_target_path: true
default_target_path: profile

Related

Why isn't InteractiveLoginEvent not fired with Symfony's 5.1 new security system?

With the new Symfony 5.1 security system, I'm not able to fire the InteractiveLoginEvent.
I followed the configuration in the official documentation (here and here) and the system was working perfectly in the former security system.
Below is my security.yaml :
security:
enable_authenticator_manager: true
encoders:
App\Entity\User:
algorithm: auto
app_user_provider:
entity:
class: App\Entity\User
property: email
firewalls:
dev:
pattern: ^/(_(profiler|wdt)|css|images|js)/
security: false
main:
lazy: true
form_login:
login_path: login
check_path: login
entry_point: form_login
guard:
authenticators:
- App\Security\LoginFormAuthenticator
logout:
path: logout
And the UserLocalSuscriber :
namespace App\EventSubscriber;
use Symfony\Component\EventDispatcher\EventSubscriberInterface;
use Symfony\Component\HttpFoundation\Session\SessionInterface;
use Symfony\Component\Security\Http\Event\InteractiveLoginEvent;
use Symfony\Component\Security\Http\SecurityEvents;
class UserLocaleSubscriber implements EventSubscriberInterface
{
private $session;
public function __construct(SessionInterface $session)
{
$this->session = $session;
}
public function onInteractiveLogin(InteractiveLoginEvent $event)
{
$user = $event->getAuthenticationToken()->getUser();
if ($user->getLocale() !== null) {
$this->session->set('_locale', $user->getLocale());
}
}
public static function getSubscribedEvents()
{
return [
SecurityEvents::INTERACTIVE_LOGIN => 'onInteractiveLogin',
];
}
}
How to configure it correctly? Without it, the user locale is not set properly once the user has been logged in.
For the Symfony's new security system, the SecurityEvents::INTERACTIVE_LOGIN has been replaced with Symfony\Component\Security\Http\Event\LoginSuccessEvent.
Change your subscriber to listen for this one:
use Symfony\Component\Security\Http\Event\LoginSuccessEvent;
use Symfony\Component\EventDispatcher\EventSubscriberInterface;
class UserLocaleSubscriber implements EventSubscriberInterface
{
public static function getSubscribedEvents(): array
{
return [
LoginSuccessEvent::class => 'onLoginSuccess',
];
}
public function onLoginSuccess(LoginSuccessEvent $event): void
{
//...
}
}
These new events are mentioned briefly in the blog with the announcement for the new authenticator system.
The rest of the documentation has not been updated yet, the new auth system will probably become the default on a future Symfony realese, but right now it still is an experimental feature.
Try using this method onSecurityInteractivelogin()

symfony EventSubscriber ignored in Symfony 3.4

I'm trying to use an event subscriber to redirect person registering to a different route, than the standard FOSUser bundle directs them to. Going by some tutorials for Symfony 3.3, but wondered as I have version 3.4 if anything needs changing to make it work, as it currently just goes to the standard page? Thanks
EventSubscriber
namespace eventsBundle\EventListener;
use Symfony\Component\EventDispatcher\EventSubscriberInterface;
use Symfony\Component\Security\Http\Util\TargetPathTrait;
use Symfony\Component\Routing\RouterInterface;
use FOS\UserBundle\Event\FormEvent;
use Symfony\Component\HttpFoundation\RedirectResponse;
use FOS\UserBundle\FOSUserEvents;
class RedirectAfterRegistrationSubscriber implements
EventSubscriberInterface
{
use TargetPathTrait;
private $router;
public function __construct(RouterInterface $router)
{
$this->router = $router;
}
public function onRegistrationSuccess(FormEvent $event)
{
die();
// main is your firewall's name
$url = $this->getTargetPath($event->getRequest()->getSession(),
'main');
if (!$url) {
$url = $this->router->generate('homepage');
}
$response = new RedirectResponse($url);
$event->setResponse($response);
}
public static function getSubscribedEvents()
{
return [
FOSUserEvents::REGISTRATION_SUCCESS =>
['onRegistrationSuccess',-5]
];
}
}
services.yml
services:
_defaults:
autowire: true
autoconfigure: true
eventsBundle\:
resource: '../../src/eventsBundle/*'
exclude: '../../src/eventsBundle/{Entity,Repository,Tests}'
eventsBundle\EventListener\RedirectAfterRegistrationSubscriber:
autowire: true
I added die(); just to make sure it was going to this but, has not effect
The services.yml file to change is the one in my bundle not the main services.yml under app/config, this now picks up the EventSubscriber
Looks like you need to add tag for your subscriber.
eventsBundle\EventListener\RedirectAfterRegistrationSubscriber:
autowire: true
tags:
- { name: kernel.event_subscriber }

Catch-all route in Symfony 3

I have a catch-all fallback route in Symfony2 that I couldn't get to work in Symfony3. I tried this exact syntax (a verbatim copy of my Symfony2 route) and that didn't work.
fallback:
path: /{req}
defaults: { _controller: MyBundle:Default:catchAll }
requirements:
req: ".+"
How can I get this working in Symfony3? (It's literally the only thing holding me back from using Symfony3 and keeping me at v2.8)
This should help you:
route1:
path: /{req}
defaults: { _controller: 'AppBundle:Default:index' }
requirements:
req: ".+"
Where, my controller is called "DefaultController", and I have a function called "indexAction()".
Here is my code for the DefaultController:
class DefaultController extends Controller
{
/**
* #Route("/", name="homepage")
*/
public function indexAction(Request $request)
...
I actually did try what you said in my environment, and it didn't work until I had the right controller settings specified.
EDIT:
For this to work, it was necessary to add the parameter Request $request (with the type hint) to the action's method signature.
I found the current accepted answer almost useful for Symfony 4, so I'm going to add my solution:
This is what I did to get it working in Symfony 4:
Open /src/Controller/DefaultController.php, make sure there is a function called index(){}
It's not required to add the Request $request as first param as some comment suggest.
This is the method that will handle all urls caught by the routes.yaml
Open /config/routes.yaml, add this:
yourRouteNameHere:
path: /{req}
defaults: { _controller: 'App\Controller\DefaultController::index' }
requirements: # the controller --^ the method --^
req: ".*"` # not ".+"
You can also override Exception controller.
# app/config/config.yml
twig:
exception_controller: app.exception_controller:showAction
# app/config/services.yml
services:
app.exception_controller:
class: AppBundle\Controller\ExceptionController
arguments: ['#twig', '%kernel.debug%']
namespace AppBundle\Controller;
use Symfony\Component\Debug\Exception\FlattenException;
use Symfony\Component\HttpKernel\Log\DebugLoggerInterface;
use Symfony\Component\HttpFoundation\Request;
use Symfony\Component\HttpFoundation\Response;
class ExceptionController
{
protected $twig;
protected $debug;
public function __construct(\Twig_Environment $twig, $debug)
{
$this->twig = $twig;
$this->debug = $debug;
}
public function showAction(Request $request, FlattenException $exception, DebugLoggerInterface $logger = null)
{
// some action
return new Response($this->twig->render('error/template.html.twig', [
'status_code' => $exception->getStatusCode()
]
));
}
}

Redirect Symfony2 LogoutSuccessHandler to original logout target

I need to modify my user object on logout. To do this, I have a security.yml that contains the following (amongst other things) -
#...
logout:
success_handler: my.logout_success_handler
target: /
#...
...this defines a logout success handler, which is defined in services.yml like this -
my.security.logout_success_handler:
class: My\Security\LogoutSuccessHandler
arguments: ["#security.context", "#doctrine.orm.default_entity_manager"]
...finally, the business-end of my handler is like this -
// ...
public function onLogoutSuccess(Request $request)
{
$user = $this->securityContext->getToken()->getUser();
// ... do stuff with the user object....
$this->em->flush();
// now what?
}
// ...
So, where it says "now what?" I understand that I need to return a Response object. Ideally I want that response object to redirect the user to whatever is defined in logout.target in the security.yml.
Is there an easy way I can query that? Or, even better, is there another way of doing this kind of thing that doesn't require me to get involved with the request/response objects at all?
Thanks
You could define your target as a parameter in your parameters.yml or config.yml:
parameters:
logout.target: /
And then reference this value in your security.yml:
logout:
success_handler: my.logout_success_handler
target: %logout.target%
And/or inject it into your logout handler:
my.security.logout_success_handler:
class: My\Security\LogoutSuccessHandler
arguments: ["#security.context", "#doctrine.orm.default_entity_manager", %logout.target%]
And return a RedirectResponse with this value:
// Assign the 3. constructor parameter to the instance variable $logoutTarget
public function onLogoutSuccess(Request $request)
{
// ...
return new RedirectResponse($this->logoutTarget);
}
So, I think I've figured out the right answer -
Rather than implementing LogoutSuccessHandlerInterface and configuring a logout.success_handler, my security.yml now looks like this -
# ...
logout:
handlers: [my.bundle.security.logout_handler]
# ...
...and I'm implementing Symfony\Component\Security\Http\Logout\LogoutHandlerInterface. Confusing naming, but this seems to be the preferred way of doing post-logout operations without having to get involved with the response object. My implementation looks like this -
namespace My\Bundle\Security;
use Symfony\Component\Security\Http\Logout\LogoutHandlerInterface;
use Symfony\Component\Security\Core\Authentication\Token\TokenInterface;
use Symfony\Component\HttpFoundation\Response;
use Symfony\Component\HttpFoundation\Request;
use Doctrine\ORM\EntityManager;
/**
* Do post logout stuff
*/
class LogoutHandler implements LogoutHandlerInterface
{
/**
* #var EntityManager
*/
protected $em;
/**
* Constructor
* #param EntityManager $em
*/
public function __construct(EntityManager $em)
{
$this->em = $em;
}
/**
* Do post logout stuff
*/
public function logout(Request $request, Response $response, TokenInterface $authToken)
{
$user = $authToken->getUser();
// do stuff with the user object...
$this->em->flush();
return $response;
}
}
...as you can see, the LogoutHandlerInterface provides a pre-made $response object that I can just return when I'm finished.
You could use composition and inject the default LogoutSuccessHandler into your object and call the onLogoutSucces method on it.
The following pseudu code shows the idea of doing it.
class MyLogoutSuccessHandler implements \LogoutSuccessHandler
{
protected $original;
public function __construct(OriginalLogoutSuccesHandler $original)
{
$this->original = $original;
}
public function onLogoutSuccess(Request $request)
{
// do stuf your want and delegate to the original
return $this->original->onLogoutSuccess($request);
}
}
This is also the way HttpKernelInterface works in StackPHP and when you use HttpCache in your application.
Hopefully this helps, happy coding :)

Change language on login

Symfony 2.1.3-dev
SonataUserBundle
SonataAdminBundle
JMSI18nRoutingBundle
By default the language is french, but I enabled "en"
I installed this bundles and most things work fine.
But I would like to do the following :
A user XXX (SonataUserBundle) has in the field "locale" the value "en"
When this user logs in I want to show up the pages in english.
The user has not to switch manually.
I think this should be done on the autentification process.
The problem is that SonataUserBundle (based on FOSUser) does not do the authentification (seen HERE)
So I tried to do THIS, but there must be some configuration issues.
When applying the wsse_secured to the whole site :
wsse_secured:
pattern: ^/
wsse: true
I get the following error : A Token was not found in the SecurityContext.
When adding anonymous to my config.yml :
firewalls:
....
main:
pattern: ^/
wsse: true
anonymous: true
I can access the home page, but when trying to login I get this error :
You must configure the check path to be handled by the firewall using form_login in your security firewall configuration.
When adding the checkpath for FOS it works but the systems does not work with my wsse-provider (I added code in WsseProvider.php to make me know)
So my question : How can I get work this WSSE authentification. I followed strictly the indications in the doc.
EDIT :
I perhaps made an error by implementing the wsse security files in my own bundle.
Now I moved it to sonata user bundle and I get the following error :
ErrorException: Catchable Fatal Error: Argument 1 passed to Application\Sonata\UserBundle\Security\Authentication\Provider\WsseProvider::__construct() must implement interface Symfony\Component\Security\Core\User\UserProviderInterface, string given, called in ..\app\cache\dev\appDevDebugProjectContainer.php on line 4413 and defined in ..\src\Application\Sonata\UserBundle\Security\Authentication\Provider\WsseProvider.php line 17
What's wrong with my UserProviderInterface in WsseProvider.php :
<?php
namespace Application\Sonata\UserBundle\Security\Authentication\Provider;
use Symfony\Component\Security\Core\Authentication\Provider\AuthenticationProviderInterface;
use Symfony\Component\Security\Core\User\UserProviderInterface;
use Symfony\Component\Security\Core\Exception\AuthenticationException;
use Symfony\Component\Security\Core\Exception\NonceExpiredException;
use Symfony\Component\Security\Core\Authentication\Token\TokenInterface;
use Application\Sonata\UserBundle\Security\Authentication\Token\WsseUserToken;
class WsseProvider implements AuthenticationProviderInterface
{
private $userProvider;
private $cacheDir;
public function __construct(UserProviderInterface $userProvider, $cacheDir)
{
$this->userProvider = $userProvider;
$this->cacheDir = $cacheDir;
}
...
I've solve this problem with a simple KernelRequestListener:
<?php
namespace ACME\DemoBundle\Listener;
use Symfony\Component\HttpKernel\Event\GetResponseEvent;
use Symfony\Component\DependencyInjection\ContainerInterface;
class RequestListener
{
protected $container;
public function __construct(ContainerInterface $container)
{
$this->container = $container;
}
public function onKernelRequest(GetResponseEvent $event)
{
$userlocale = null;
$request = $event->getRequest();
$user = $this->container->get('security.context')->getToken()->getUser();
if (!is_object($user)) {
return;
}
$userlocale = $user->getLocale();
if($userlocale !== NULL AND $userlocale !== '')
{
$request->setLocale($userlocale);
}
}
}
Register service in Acme/Demo/Resources/service.yml:
ACME.demo.listener.request:
class: ACME\DemoBundle\Listener\RequestListener
arguments: [ #service_container ]
tags:
- { name: kernel.event_listener, event: kernel.request, method: onKernelRequest }
Other solution I've found there: Here

Categories