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
Related
I have the following code that checks whether the API-key is the correct one before sending data to the front end.
file1Controller.php
<?php
namespace App\Controller;
use Symfony\Bundle\FrameworkBundle\Controller\AbstractController;
use Symfony\Component\HttpFoundation\JsonResponse;
use Symfony\Component\HttpFoundation\Request;
use Symfony\Component\Routing\Annotation\Route;
class file1Controller extends AbstractController
{
/**
* #Route("/Some/URI", methods={"GET"}) // "/Some/URI" here
* #param Request $request
* #return JsonResponse
*/
public function list(Request $request)
{
if (empty($request->headers->get('api-key'))) {
return new JsonResponse(['error' => 'Please provide an API_key'], 401);
}
if ($request->headers->get('api-key') !== $_ENV['API_KEY']) {
return new JsonResponse(['error' => 'Invalid API key'], 401);
}
return new JsonResponse($this->getDoctrine()->getRepository('App:Something')->findAll()); //Something here
}
}
Which works excatly as intended (tested it with Postman) for my simple learning example. I would like to generalize it so that I can use it in other places. Almost everything should stay the same except the parts where there are comments. I have tried the following:
General.php
<?php
namespace App;
use Symfony\Bundle\FrameworkBundle\Controller\AbstractController;
use Symfony\Component\HttpFoundation\JsonResponse;
use Symfony\Component\HttpFoundation\Request;
use Symfony\Component\Routing\Annotation\Route;
class General extends AbstractController
{
private $request;
private $route;
private $entity;
/**
* ApiKeyAuthenticator constructor.
* #param Request $request
* #param String $route
* #param String $entity
*/
function __construct(Request $request, String $route, String $entity)
{
$this->request = $request;
$this->route = $route;
$this->entity = $entity;
}
/**
* #Route({$route}, methods={"GET"}) //notice here
* #return JsonResponse
*/
public function list()
{
if (empty($this->request->headers->get('api-key'))) {
return new JsonResponse(['error' => 'Please provide an API_key'], 401);
}
if ($this->request->headers->get('api-key') !== $_ENV['API_KEY']) {
return new JsonResponse(['error' => 'Invalid API key'], 401);
}
return new JsonResponse($this->getDoctrine()->getRepository('App:{$this->entity}')->findAll()); //notice here
}
}
Then I change the code of file1Controller.php to:
<?php
namespace App\Controller;
require(__DIR__.'/../General.php'); //note that there's no error accessing the file here
use Symfony\Component\HttpFoundation\Request;
class file1Controller
{
/**
* #param Request $request
*/
public function AuthenticateAPI(Request $request)
{
$AuthenticatorObject = new ApiKeyAuthenticator($request, "/Some/URI", 'Something'); //getting undefiend class
return $AuthenticatorObject;
}
}
This is unfortunately not working when testing it with Postman and I'm getting an undefiend class error on this line $AuthenticatorObject = new ApiKeyAuthenticator($request, "/Some/URI", 'Something'); in file1Controller.php
What did I do wrong and how could I fix it?
You shouldn't call your controllers like this in Symfony:
require(__DIR__.'/../General.php'); //note that there's no error accessing the file here
Please check out defining and accessing controllers as service in Symfony documentation:
How to Define Controllers as Services
How to Forward Requests to another Controller
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(),
));
}
}
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);
}
}
}
I am working on a Symfony 3.3.8 project with FOSUserBundle. I have created two registration and two profile pages for student and provider respectively. Now I am trying to override the FOSUserBundle's loginAction() method, where I am checking the logged-in user for it's role. If the role is ROLE_STUDENT I am trying to redirect the user to student profile page after successful login and if the role is ROLE_PROVIDER I want the user to be redirected to the provider profile page after successful login. Here is my overridden SecurityController with loginAction():
<?php
namespace Epita\HousingBundle\Controller;
use FOS\UserBundle\Controller\SecurityController as BaseController;
use Symfony\Component\HttpFoundation\Request;
use Symfony\Component\Security\Core\Security;
use Symfony\Component\Security\Core\SecurityContextInterface;
use Sensio\Bundle\FrameworkExtraBundle\Configuration\Route;
use Symfony\Component\Security\Core\Exception\AuthenticationException;
class SecurityController extends BaseController {
/**
*
*/
public function loginAction(Request $request) {
/** #var $session \Symfony\Component\HttpFoundation\Session\Session */
$session = $request->getSession();
$key = '_security.main.target_path';
$securityContext = $this->container->get('security.authorization_checker');
if ($securityContext->isGranted('IS_AUTHENTICATED_REMEMBERED')) {
$user = $this->getUser();
}
if ($this->container->get('session')->has($key))
{
if ($this->get('security.authorization_checker')->isGranted('ROLE_STUDENT')) {
return $this->redirectToRoute('student_profile');
} else if ($this->get('security.authorization_checker')->isGranted('ROLE_PROVIDER')) {
return $this->redirectToRoute('provider_profile');
}
}
else{
return $this->redirectToRoute('student_profile');
}
if (class_exists('\Symfony\Component\Security\Core\Security')) {
$authErrorKey = Security::AUTHENTICATION_ERROR;
$lastUsernameKey = Security::LAST_USERNAME;
} else {
// BC for SF < 2.6
$authErrorKey = SecurityContextInterface::AUTHENTICATION_ERROR;
$lastUsernameKey = SecurityContextInterface::LAST_USERNAME;
}
// get the error if any (works with forward and redirect -- see below)
if ($request->attributes->has($authErrorKey)) {
$error = $request->attributes->get($authErrorKey);
} elseif (null !== $session && $session->has($authErrorKey)) {
$error = $session->get($authErrorKey);
$session->remove($authErrorKey);
} else {
$error = null;
}
if (!$error instanceof AuthenticationException) {
$error = null; // The value does not come from the security component.
}
// last username entered by the user
$lastUsername = (null === $session) ? '' : $session->get($lastUsernameKey);
if ($this->has('security.csrf.token_manager')) {
$csrfToken = $this->get('security.csrf.token_manager')->getToken('authenticate')->getValue();
} else {
// BC for SF < 2.4
$csrfToken = $this->has('form.csrf_provider')
? $this->get('form.csrf_provider')->generateCsrfToken('authenticate')
: null;
}
return $this->renderLogin(array(
'last_username' => $lastUsername,
'error' => $error,
'csrf_token' => $csrfToken,
));
}
/**
*
* #param array $data
*
* #return \Symfony\Component\HttpFoundation\Response
*/
protected function renderLogin(array $data)
{
$template = sprintf('EpitaHousingBundle:Security:login.html.twig');
return $this->container->get('templating')->renderResponse($template, $data);
}
public function checkAction()
{
throw new \RuntimeException('You must configure the check path to be handled by the firewall using form_login in your security firewall configuration.');
}
public function logoutAction()
{
throw new \RuntimeException('You must activate the logout in your security firewall configuration.');
}
}
But this solution does not work for me. My guess is I have to play around with the session. Since I am new to this can anyone help me?
Thanks in advance. Let me know if you need more details.
Here is some code that might help you.
1 - This is the Event/LoginSuccessHandler, your logic is in this class
<?php
namespace AppBundle\Event;
use Symfony\Component\HttpFoundation\Request;
use Symfony\Component\HttpFoundation\RedirectResponse;
use Symfony\Component\Routing\Router;
use Symfony\Component\Security\Core\Authentication\Token\TokenInterface;
use Symfony\Component\Security\Core\Authorization\AuthorizationChecker;
use Symfony\Component\Security\Http\Authentication\AuthenticationSuccessHandlerInterface;
class LoginSuccessHandler implements AuthenticationSuccessHandlerInterface
{
protected $router;
protected $security;
public function __construct(Router $router, AuthorizationChecker $security)
{
$this->router = $router;
$this->security = $security;
}
public function onAuthenticationSuccess(Request $request, TokenInterface $token)
{
$url = 'homepage';
if ($this->security->isGranted('ROLE_STUDENT')) {
$url = 'student_route';
}
if ($this->security->isGranted('ROLE_PROVIDER')) {
$url = 'provider_route';
}
$response = new RedirectResponse($this->router->generate($url));
return $response;
}
}
2 - We set up the config that listens the login event in your services.yml
login_success_handler:
class: AppBundle\Event\LoginSuccessHandler
arguments:
- "#router"
- "#security.authorization_checker"
tags:
- { name: 'monolog.logger', channel: 'security' }
3 - That should be it. Try and tell me if something is wrong
I need to override the regitration contrller by 1 in my bundle.
I made the file inside userbundle, but it gives that error that I saw a lot at stackoverflow but no reply.
Error is
ContextErrorException: Runtime Notice: Declaration of TA\UserBundle\Controller\RegistrationController::registerAction() should be compatible with FOS\UserBundle\Controller\RegistrationController::registerAction(Symfony\Component\HttpFoundation\Request $request) in E:\php\xampp\htdocs\platform\src\TA\UserBundle\Controller\RegistrationController.php line 9
Controller
<?php
//src\TA\UserBundle\Controller\RegistrationController.php
namespace TA\UserBundle\Controller;
use Symfony\Component\HttpFoundation\RedirectResponse;
use FOS\UserBundle\Controller\RegistrationController as BaseController;
class RegistrationController extends BaseController
{
public function registerAction()
{
$form = $this->container->get('fos_user.registration.form');
$formHandler = $this->container->get('fos_user.registration.form.handler');
$confirmationEnabled = $this->container->getParameter('fos_user.registration.confirmation.enabled');
$process = $formHandler->process($confirmationEnabled);
if ($process) {
$user = $form->getData();
/*****************************************************
* Add new functionality (e.g. log the registration) *
*****************************************************/
$this->container->get('logger')->info(
sprintf('New user registration: %s', $user)
);
if ($confirmationEnabled) {
$this->container->get('session')->set('fos_user_send_confirmation_email/email', $user->getEmail());
$route = 'fos_user_registration_check_email';
} else {
$this->authenticateUser($user);
$route = 'fos_user_registration_confirmed';
}
$this->setFlash('fos_user_success', 'registration.flash.user_created');
$url = $this->container->get('router')->generate($route);
return new RedirectResponse($url);
}
return $this->container->get('templating')->renderResponse('FOSUserBundle:Registration:register.html.'.$this->getEngine(), array(
'form' => $form->createView(),
));
}
}
even when I changed
public function registerAction()
to
public function registerAction(Request $request)
error became
ContextErrorException: Runtime Notice: Declaration of TA\UserBundle\Controller\RegistrationController::registerAction() should be compatible with FOS\UserBundle\Controller\RegistrationController::registerAction(Symfony\Component\HttpFoundation\Request $request) in E:\php\xampp\htdocs\platform\src\TA\UserBundle\Controller\RegistrationController.php line 9
This one worked for me. My Example:
<?php
namespace FRMKS\FoodR\FoodApp\Controller;
use FOS\UserBundle\Controller\RegistrationController as BaseController;
use Symfony\Component\HttpFoundation\Request;
class RegistrationController extends BaseController
{
public function registerAction(Request $request)
{
$response = parent::registerAction($request);
// ... do custom stuff
return $response;
}
}
Basically you need to add use of request, parameter 'Request $request' to 'registerAction' function and pass 'request' param to parent 'registerAction' function
Did you try to put the full 'Symfony\Component\HttpFoundation\Request $request' into your method definition?
public function registerAction(Symfony\Component\HttpFoundation\Request $request) {}