FOSUserBundle - Registration controller override error - symfony

I am trying to override the registration controller of FOSUserBundle.
Here's my configuration :
services:
project.registration.controller:
class: Project\UserBundle\Controller\RegistrationController
arguments: [ #event_dispatcher,#fos_user.registration.form.factory,#fos_user.user_manager,#security.token_storage]
And the controller so far:
<?php
namespace Project\UserBundle\Controller;
use Symfony\Component\DependencyInjection\ContainerInterface;
use Symfony\Component\HttpFoundation\RedirectResponse;
use FOS\UserBundle\Form\Factory\FactoryInterface;
use FOS\UserBundle\Model\UserManagerInterface;
use Symfony\Component\EventDispatcher\EventDispatcherInterface;
use Symfony\Component\Security\Core\Authentication\Token\Storage\TokenStorageInterface;
use FOS\UserBundle\Controller\RegistrationController as BaseController;
class RegistrationController extends BaseController
{
public function __construct(EventDispatcherInterface $eventDispatcher, FactoryInterface $formFactory, UserManagerInterface $userManager, TokenStorageInterface $tokenStorage)
{
parent::__construct($eventDispatcher, $formFactory, $userManager, $tokenStorage);
}
However everytime i try to go to the registration page I meet this error :
Catchable Fatal Error: Argument 1 passed to Project\UserBundle\Controller\RegistrationController::__construct() must implement interface Symfony\Component\EventDispatcher\EventDispatcherInterface, none given, called in C:\wamp\www\cmagic\vendor\symfony\symfony\src\Symfony\Component\HttpKernel\Controller\ControllerResolver.php on line 187 and defined
I think I followed for controllers used as services or did I miss a step ?
EDIT : This is for symfony 2.8

It worked for me in Symfony 3, but getParent(){} for bundles is already deprecated in 3.4 version and disabled in 4.
namespace AppBundle;
use Symfony\Component\HttpKernel\Bundle\Bundle;
class AppBundle extends Bundle
{
public function getParent()
{
return 'FOSUserBundle';
}
}
namespace AppBundle\Controller;
use FOS\UserBundle\Controller\RegistrationController as BaseController;
use FOS\UserBundle\Event\FormEvent;
use FOS\UserBundle\FOSUserEvents;
use Symfony\Component\EventDispatcher\EventDispatcherInterface;
use Symfony\Component\HttpFoundation\RedirectResponse;
use Symfony\Component\HttpFoundation\Request;
use Symfony\Component\HttpFoundation\Response;
class RegistrationController extends BaseController
{
/**
* #param Request $request
*
* #return Response
*/
public function registerAction(Request $request)
{
/** #var $formFactory FactoryInterface */
$formFactory = $this->get('fos_user.registration.form.factory');
/** #var $userManager UserManagerInterface */
$userManager = $this->get('fos_user.user_manager');
/** #var $dispatcher EventDispatcherInterface */
$dispatcher = $this->get('event_dispatcher');
$user = $userManager->createUser();
$user->setEnabled(true);
$event = new GetResponseUserEvent($user, $request);
$dispatcher->dispatch(FOSUserEvents::REGISTRATION_INITIALIZE, $event);
if (null !== $event->getResponse()) {
return $event->getResponse();
}
$form = $formFactory->createForm();
$form->setData($user);
$form->handleRequest($request);
if ($form->isSubmitted()) {
if ($form->isValid()) {
$event = new FormEvent($form, $request);
$dispatcher->dispatch(FOSUserEvents::REGISTRATION_SUCCESS, $event);
$userManager->updateUser($user);
if (null === $response = $event->getResponse()) {
$url = $this->generateUrl('fos_user_registration_confirmed');
$response = new RedirectResponse($url);
}
$dispatcher->dispatch(FOSUserEvents::REGISTRATION_COMPLETED, new FilterUserResponseEvent($user, $request, $response));
return $response;
}
$event = new FormEvent($form, $request);
$dispatcher->dispatch(FOSUserEvents::REGISTRATION_FAILURE, $event);
if (null !== $response = $event->getResponse()) {
return $response;
}
}
return $this->render('#FOSUser/Registration/register.html.twig', array(
'form' => $form->createView(),
));
}
}

Related

Symfony 3: Global call for the Controller after user login

Because I have many controllers, after the user is logged in, I want to transfer some user settings to Twig. But I don't want to make it in each controller:
Eg:
use Symfony\Bundle\FrameworkBundle\Controller\Controller;
class DefaultController extends Controller
{
public function indexAction()
{
$user = $this->getUser();
$settings = $user->getSettings();
// ...
}
}
Is there a possibility to make it at a higher level from the call of the DefaultController?
Solution 1
Use app.user variable in Twig which is globally accessible:
{{ app.user.username }}
More info: https://symfony.com/doc/current/templates.html#the-app-global-variable
Solution 2
Write custom Twig function:
// src/Twig/UserExtension.php
namespace App\Twig;
use Twig\Extension\AbstractExtension;
use Twig\TwigFunction;
use Symfony\Component\Security\Core\Security;
class UserExtension extends AbstractExtension
{
Security $security
public function __construct(Security $security)
{
// Avoid calling getUser() in the constructor: auth may not
// be complete yet. Instead, store the entire Security object.
$this->security = $security;
}
public function getFunctions()
{
return [
new TwigFunction('get_user_settings', [$this, 'getUserSettings']),
];
}
public function getUserSettings()
{
$user = $this->security->getUser();
return $user->getSettings();
}
}
Usage in Twig:
{{ get_user_settings().setting1 }}
More info: https://symfony.com/doc/current/templating/twig_extension.html
How to inject current logged user: https://symfony.com/doc/current/security.html#b-fetching-the-user-from-a-service
Ok, maybe I don't describe it clearly. This is the solution I have searched for:
<?php
namespace AppBundle\Subscriber;
use Doctrine\ORM\EntityManagerInterface;
use Symfony\Component\HttpKernel\Event\FilterControllerEvent;
use Symfony\Component\HttpKernel\KernelEvents;
use Symfony\Component\EventDispatcher\EventSubscriberInterface;
use Symfony\Component\Security\Core\Authentication\Token\Storage\TokenStorageInterface;
use Symfony\Component\Security\Core\User\UserInterface;
class ControllerSubscriber implements EventSubscriberInterface
{
/**
* #var TokenStorageInterface
*/
private $tokenStorage;
/**
* #var EntityManagerInterface
*/
private $em;
/**
* ControllerSubscriber constructor.
*
* #param TokenStorageInterface $tokenStorage
* #param EntityManagerInterface $em
*/
public function __construct(TokenStorageInterface $tokenStorage, EntityManagerInterface $em)
{
$this->tokenStorage = $tokenStorage;
$this->em = $em;
}
/**
* #param FilterControllerEvent $event
*/
public function onKernelController(FilterControllerEvent $event)
{
if (!$event->isMasterRequest()) {
return;
}
if (!$token = $this->tokenStorage->getToken()) {
return;
}
if (!$user = $token->getUser()) {
return;
}
// if no user
if (!$user instanceof UserInterface) {
return;
} else {
// .... do something
}
}
/**
* #return array
*/
public static function getSubscribedEvents()
{
return array(
// must be registered before (i.e. with a higher priority than) the default Locale listener
KernelEvents::CONTROLLER => array(array('onKernelController', 1)),
);
}
}
services.yml
app.controller.subscriber:
class: AppBundle\Subscriber\ControllerSubscriber
arguments: ["#security.token_storage","#doctrine.orm.default_entity_manager"]
tags:
- { name: kernel.event_listener, event: kernel.controller, method: onKernelController }

Null returned for HttpFoundation/response

I'm getting the error in Symfony 4:
Return value of
App\Controller\RegistrationCreatorController::register() must be an
instance of Symfony\Component\HttpFoundation\Response, null returned
Within the RegistrationCreatorController I have this:
namespace App\Controller;
use App\Entity\User;
use App\Entity\Creator;
use App\Entity\CreatorApplication;
use App\Form\CreatorRegistrationForm;
use App\Security\UserAuthenticationAuthenticator;
use Symfony\Bundle\FrameworkBundle\Controller\AbstractController;
use Symfony\Component\HttpFoundation\Request;
use Symfony\Component\HttpFoundation\Response;
use Symfony\Component\Security\Core\Encoder\UserPasswordEncoderInterface;
use Symfony\Component\Security\Guard\GuardAuthenticatorHandler;
class RegistrationCreatorController extends AbstractController
{
public function register(
Request $request,
UserPasswordEncoderInterface $passwordEncoder,
GuardAuthenticatorHandler $guardHandler,
UserAuthenticationAuthenticator $authenticator
): Response
{
$user = new User();
$creator = new Creator();
$application = new CreatorApplication();
$form = $this
->createForm(
CreatorRegistrationForm::class,
[$user, $creator, $application]
);
$form->handleRequest($request);
if ($form->isSubmitted() && $form->isValid()) {
$user
->setEmail(
$form->get('email')->getData()
)
->setPassword(
$passwordEncoder->encodePassword(
$user,
$form->get('password')->getData()
)
)
->setFirstName(
$form->get('firstname')->getData()
)
->setLastName(
$form->get('lastname')->getData()
)
->setBirthday(
$form->get('birthday')->getData()
);
$application->createApplication(
$form->get('question-1')->getData(),
$form->get('question-2')->getData(),
$form->get('question-3')->getData(),
$form->get('question-4')->getData()
);
$creator
->associateUser($user)
->associateApplication($application);
$entityManager = $this->getDoctrine()->getManager();
$entityManager->persist($user);
$entityManager->persist($creator);
$entityManager->persist($application);
$entityManager->flush(); //this is what inserts to db!
// do anything else you need here, like send an
return $guardHandler->authenticateUserAndHandleSuccess(
$user,
$request,
$authenticator,
'main' // This is the issue here //
);
}
return $this->render('registration/creator-register.html.twig', [
'creatorForm' => $form->createView(),
]);
}
}
The error specifically points to this:
return $guardHandler->authenticateUserAndHandleSuccess(
$user,
$request,
$authenticator,
'main' // This is the even more specific issue //
);
I'm not entirely sure what the problem is? When I look into the security.yaml file it points to the userAuthenticationAuthenticator which was auto generated from Symfony 4 when I called the make:registration-form.
UserAuthenticationAuthenticator:
<?php
namespace App\Security;
use App\Entity\User;
use Doctrine\ORM\EntityManagerInterface;
use Symfony\Component\HttpFoundation\RedirectResponse;
use Symfony\Component\HttpFoundation\Request;
use Symfony\Component\Routing\Generator\UrlGeneratorInterface;
use Symfony\Component\Security\Core\Authentication\Token\TokenInterface;
use Symfony\Component\Security\Core\Exception\CustomUserMessageAuthenticationException;
use Symfony\Component\Security\Core\Encoder\UserPasswordEncoderInterface;
use Symfony\Component\Security\Core\Exception\InvalidCsrfTokenException;
use Symfony\Component\Security\Core\Security;
use Symfony\Component\Security\Core\User\UserInterface;
use Symfony\Component\Security\Core\User\UserProviderInterface;
use Symfony\Component\Security\Csrf\CsrfToken;
use Symfony\Component\Security\Csrf\CsrfTokenManagerInterface;
use Symfony\Component\Security\Guard\Authenticator\AbstractFormLoginAuthenticator;
use Symfony\Component\Security\Http\Util\TargetPathTrait;
class UserAuthenticationAuthenticator extends AbstractFormLoginAuthenticator
{
use TargetPathTrait;
private $entityManager;
private $urlGenerator;
private $csrfTokenManager;
private $passwordEncoder;
public function __construct(EntityManagerInterface $entityManager, UrlGeneratorInterface $urlGenerator, CsrfTokenManagerInterface $csrfTokenManager, UserPasswordEncoderInterface $passwordEncoder)
{
$this->entityManager = $entityManager;
$this->urlGenerator = $urlGenerator;
$this->csrfTokenManager = $csrfTokenManager;
$this->passwordEncoder = $passwordEncoder;
}
public function supports(Request $request)
{
return 'app_login' === $request->attributes->get('_route')
&& $request->isMethod('POST');
}
public function getCredentials(Request $request)
{
$credentials = [
'email' => $request->request->get('email'),
'password' => $request->request->get('password'),
'csrf_token' => $request->request->get('_csrf_token'),
];
$request->getSession()->set(
Security::LAST_USERNAME,
$credentials['email']
);
return $credentials;
}
public function getUser($credentials, UserProviderInterface $userProvider)
{
$token = new CsrfToken('authenticate', $credentials['csrf_token']);
if (!$this->csrfTokenManager->isTokenValid($token)) {
throw new InvalidCsrfTokenException();
}
$user = $this->entityManager->getRepository(User::class)->findOneBy(['email' => $credentials['email']]);
if (!$user) {
// fail authentication with a custom error
throw new CustomUserMessageAuthenticationException('Email could not be found.');
}
return $user;
}
public function checkCredentials($credentials, UserInterface $user)
{
return $this->passwordEncoder->isPasswordValid($user, $credentials['password']);
}
public function onAuthenticationSuccess(Request $request, TokenInterface $token, $providerKey)
{
if ($targetPath = $this->getTargetPath($request->getSession(), $providerKey)) {
return new RedirectResponse($targetPath);
}
// For example : return new RedirectResponse($this->urlGenerator->generate('some_route'));
//throw new \Exception('TODO: provide a valid redirect inside '.__FILE__);
}
protected function getLoginUrl()
{
return $this->urlGenerator->generate('app_login');
}
}
?>
I thought the guard handler would return a response, but I guess not?
the doc comment tells, that it returns a response if any
/**
* Convenience method for authenticating the user and returning the
* Response *if any* for success.
*/
https://github.com/symfony/symfony/blob/d97f9ab131ae1fbc3c4371f2a38b8c1e41eef499/src/Symfony/Component/Security/Guard/GuardAuthenticatorHandler.php#L84
and it might even throw an Exception...
So no, a Response is not necessarily the result. Especially if you read the AuthenticatorInterface
a pragmatic solution would be to just redirect to the index page (/) in case no Response object is returned:
return $guardhandler->authenticateUserAndHandleSuccess(...)
?: new RedirectResponse('/'); // fallback

Symfony login success redirect to change password screen

Admin generate user then user first time login i want to redirect user to change password.
I have google but there are no any post found that make this fix.
Below i have attached service.yml and loginsuccessListener file code.
service.yml
services:
fos_user.security.interactive_login_listener:
class: OnlineCare\MarketingWebBundle\EventListener\LastLoginListener
tags:
- { name: kernel.event_subscriber, event: kernel.request, method: onKernelRequest }
arguments: ['#fos_user.user_manager','#router','#service_container']
LastLoginListiner.php
namespace OnlineCare\MarketingWebBundle\EventListener;
use FOS\UserBundle\FOSUserEvents;
use FOS\UserBundle\Event\UserEvent;
use FOS\UserBundle\Model\UserManagerInterface;
use FOS\UserBundle\Model\UserInterface;
use Symfony\Component\EventDispatcher\EventSubscriberInterface;
use Symfony\Component\Security\Http\Event\InteractiveLoginEvent;
use Symfony\Component\Security\Http\SecurityEvents;
use Symfony\Component\HttpFoundation\RedirectResponse;
use Symfony\Component\Routing\Generator\UrlGeneratorInterface;
use Symfony\Component\DependencyInjection\ContainerInterface;
class LastLoginListener implements EventSubscriberInterface
{
protected $userManager;
private $router;
private $container;
public function __construct(UserManagerInterface $userManager, UrlGeneratorInterface $router, ContainerInterface $container)
{
$this->userManager = $userManager;
$this->router = $router;
$this->container = $container;
}
public static function getSubscribedEvents()
{
return array(
FOSUserEvents::SECURITY_IMPLICIT_LOGIN => 'onImplicitLogin',
SecurityEvents::INTERACTIVE_LOGIN => 'onSecurityInteractiveLogin',
);
}
public function onImplicitLogin(UserEvent $event)
{
$user = $event->getUser();
$user->setLastLogin(new \DateTime());
$this->userManager->updateUser($user);
}
public function onSecurityInteractiveLogin(InteractiveLoginEvent $event)
{
$user = $event->getAuthenticationToken()->getUser();
if(empty($user->getLastLogin())){
$url = $this->router->generate('fos_user_change_password');
**return $event->setResponse(new RedirectResponse($url));**
} elseif ($user instanceof UserInterface) {
$user->setLastLogin(new \DateTime());
$this->userManager->updateUser($user);
}
}
}

Submitting symfony register controller fosuserbundle does not work

I tried to follow this tutorial to override default controller of fos user bundle [Overriding Default FOSUserBundle Controllers][1]
And i can change controller but when I try to submit button it doesn't work:
<?php
// src/AppBundle/Controller/RegistrationController.php
namespace AppBundle\Controller;
use Symfony\Component\HttpFoundation\RedirectResponse;
use FOS\UserBundle\Controller\RegistrationController as BaseController;
use Symfony\Component\HttpFoundation\Request;
class RegistrationController extends BaseController
{
public function registerAction(Request $request)
{
/** #var $formFactory FactoryInterface */
$formFactory = $this->get('fos_user.registration.form.factory');
/** #var $userManager UserManagerInterface */
$userManager = $this->get('fos_user.user_manager');
/** #var $dispatcher EventDispatcherInterface */
$dispatcher = $this->get('event_dispatcher');
$user = $userManager->createUser();
$user->setEnabled(true);
$event = new GetResponseUserEvent($user, $request);
$dispatcher->dispatch(FOSUserEvents::REGISTRATION_INITIALIZE, $event);
var_dump('prova');
if (null !== $event->getResponse()) {
return $event->getResponse();
}
$form = $this->createForm(RegistrationType::class, $user, [
'method' => 'POST',
]);
//$form = $formFactory->createForm();
//$form->setData($user);
$form->handleRequest($request);
if ($form->isSubmitted()) {
var_dump($form);
if ($form->isValid()) {
var_dump($form);
}
}
return $this->render('#FOSUser/Registration/register.html.twig', array(
'form' => $form->createView(),
));
}
}
The var_dump before the submit work but the var_dump inside
if ($form->isSubmitted()) {
if ($form->isValid()) {
doesn't work . I believe that submit logic is in another code but i don't understand how I can change it.
I don't understand how it's possible. Please help me ?
You don't set a formType to your form, so he have nothing to handle.
Here is a simple example :
$form = $this->createForm(YourFormType::class, $user, [
'method' => 'POST',
]);
Good luck ! :)

FOSUserBundle override action of a controller

I'm trying to override the editAction in ProfileController.php but it's doesn't work. I can override template and form with success but not the actions of controllers.
app/config.yml
# FOS UserBundle Configuration
fos_user:
db_driver: orm
firewall_name: main
user_class: Intranet\UserBundle\Entity\User
registration:
form:
type: intranet_user_registration
profile:
form:
type: intranet_user_profile
old src/Intranet/UserBundle/Controller/ProfileController.php
<?php
namespace Intranet\UserBundle\Controller;
use FOS\UserBundle\Controller\ProfileController as BaseController;
class ProfileController extends BaseController
{
/**
* Edit the user
*/
public function editAction(Request $request)
{
var_dump($request) die(); // just for the test
}
}
new src/Intranet/UserBundle/Controller/ProfileController.php
<?php
namespace Intranet\UserBundle\Controller;
use FOS\UserBundle\Controller\ProfileController as BaseController;
use Symfony\Component\HttpFoundation\Request;
use Symfony\Component\Security\Core\Exception\AccessDeniedException;
class ProfileController extends BaseController
{
public function editAction(Request $request)
{
$user = $this->container->get('security.context')->getToken()->getUser();
if (!is_object($user) || !$user instanceof UserInterface) {
throw new AccessDeniedException('This user does not have access to this section.');
}
/** #var $dispatcher \Symfony\Component\EventDispatcher\EventDispatcherInterface */
$dispatcher = $this->container->get('event_dispatcher');
$event = new GetResponseUserEvent($user, $request);
$dispatcher->dispatch(FOSUserEvents::PROFILE_EDIT_INITIALIZE, $event);
if (null !== $event->getResponse()) {
return $event->getResponse();
}
/** #var $formFactory \FOS\UserBundle\Form\Factory\FactoryInterface */
$formFactory = $this->container->get('fos_user.profile.form.factory');
$form = $formFactory->createForm();
$form->setData($user);
if ('POST' === $request->getMethod()) {
$form->bind($request);
if ($form->isValid()) {
/** #var $userManager \FOS\UserBundle\Model\UserManagerInterface */
$userManager = $this->container->get('fos_user.user_manager');
$event = new FormEvent($form, $request);
$dispatcher->dispatch(FOSUserEvents::PROFILE_EDIT_SUCCESS, $event);
$userManager->updateUser($user);
if (null === $response = $event->getResponse()) {
$url = $this->container->get('router')->generate('fos_user_profile_show');
$response = new RedirectResponse($url);
}
$dispatcher->dispatch(FOSUserEvents::PROFILE_EDIT_COMPLETED, new FilterUserResponseEvent($user, $request, $response));
return $response;
}
}
return $this->container->get('templating')->renderResponse(
'FOSUserBundle:Profile:edit.html.'.$this->container->getParameter('fos_user.template.engine'),
array('form' => $form->createView())
);
}
}
src/Intranet/UserBundle/IntranetUserBundle.php
<?php
namespace Intranet\UserBundle;
use Symfony\Component\HttpKernel\Bundle\Bundle;
class IntranetUserBundle extends Bundle
{
public function getParent()
{
return 'FOSUserBundle';
}
}
No error message, SF2 ignore my override :/
Ok I found, the controller need the same use of his parent
Last version of ProfileController :
namespace Intranet\UserBundle\Controller;
/* ALL USE IS REQUIRED !!!! */
use FOS\UserBundle\Controller\ProfileController as BaseController;
use Symfony\Component\HttpFoundation\Request;
use Symfony\Component\HttpFoundation\RedirectResponse;
use FOS\UserBundle\Model\UserInterface;
use FOS\UserBundle\Event\GetResponseUserEvent;
use FOS\UserBundle\FOSUserEvents;
use FOS\UserBundle\Event\FormEvent;
use FOS\UserBundle\Event\FilterUserResponseEvent;
use Symfony\Component\Security\Core\Exception\AccessDeniedException;
class ProfileController extends BaseController
{
public function editAction(Request $request)
{
$user = $this->container->get('security.context')->getToken()->getUser();
if (!is_object($user) || !$user instanceof UserInterface) {
throw new AccessDeniedException('This user does not have access to this section.');
}
/** #var $dispatcher \Symfony\Component\EventDispatcher\EventDispatcherInterface */
$dispatcher = $this->container->get('event_dispatcher');
$event = new GetResponseUserEvent($user, $request);
$dispatcher->dispatch(FOSUserEvents::PROFILE_EDIT_INITIALIZE, $event);
if (null !== $event->getResponse()) {
return $event->getResponse();
}
/** #var $formFactory \FOS\UserBundle\Form\Factory\FactoryInterface */
$formFactory = $this->container->get('fos_user.profile.form.factory');
$form = $formFactory->createForm();
$form->setData($user);
if ('POST' === $request->getMethod()) {
$form->bind($request);
if ($form->isValid()) {
/** #var $userManager \FOS\UserBundle\Model\UserManagerInterface */
$userManager = $this->container->get('fos_user.user_manager');
$event = new FormEvent($form, $request);
$dispatcher->dispatch(FOSUserEvents::PROFILE_EDIT_SUCCESS, $event);
$userManager->updateUser($user);
if (null === $response = $event->getResponse()) {
$url = $this->container->get('router')->generate('fos_user_profile_show');
$response = new RedirectResponse($url);
}
$dispatcher->dispatch(FOSUserEvents::PROFILE_EDIT_COMPLETED, new FilterUserResponseEvent($user, $request, $response));
return $response;
}
}
return $this->container->get('templating')->renderResponse(
'FOSUserBundle:Profile:edit.html.'.$this->container->getParameter('fos_user.template.engine'),
array('form' => $form->createView())
);
}
}

Resources