I would like to override the ProfileController's edit action of FosUserBundle. I've created the controller in my own UserBundle, copied the edit action into it and made some changes. In this controller there is a check if the logged in user is an instanceOf UserInterFace. Apparently it's not because it throws an access denied exception when I go to /profile/edit
Why isn't the logged in user an instanceOf UserInterFace anymore?
Controller:
namespace Tennisconnect\UserBundle\Controller;
use Symfony\Component\HttpFoundation\RedirectResponse;
use Symfony\Component\Security\Core\Exception\AccessDeniedException;
use FOS\UserBundle\Controller\ProfileController as BaseController;
class ProfileController extends BaseController
{
/**
* Edit the user
*/
public function editAction()
{
$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.');
}
$form = $this->container->get('fos_user.profile.form');
$formHandler = $this->container->get('fos_user.profile.form.handler');
$process = $formHandler->process($user);
if ($process) {
$user->upload();
$this->setFlash('fos_user_success', 'profile.flash.updated');
return new RedirectResponse($this->container->get('router')->generate('fos_user_profile_show'));
}
return $this->container->get('templating')->renderResponse(
'FOSUserBundle:Profile:edit.html.'.$this->container->getParameter('fos_user.template.engine'),
array('form' => $form->createView(), 'theme' => $this->container->getParameter('fos_user.template.theme'))
);
}
}
Reading your code snippet, I would say it's simply because you don't match the full qualified namespace of UserInterface.
Either import the class with:
use Symfony\Component\Security\Core\User\UserInterface;
or modify your code like this:
if (!is_object($user) || !$user instanceof Symfony\Component\Security\Core\User\UserInterface) {
The cleaner solution is to create a new Bundle "MyFOSBundle", in the bundle class :
getParent()
{
return "FosBundle";
}
Then you write the file you want override on the same location.
http://symfony.com/doc/master/cookbook/bundles/inheritance.html
Related
My website is running Symfony 3.4 and I made my own user member system.
My User entity contains a Datetime field 'lastLogin' and I can't find a solution to update it every time a user logged in.
I created a custom UserChecker then I tried to update the field in it :
<?php
namespace CoreBundle\Security;
use CoreBundle\Entity\User as AppUser;
use Symfony\Component\Security\Core\Exception\AuthenticationException;
use Symfony\Component\Security\Core\User\UserCheckerInterface;
use Symfony\Component\Security\Core\User\UserInterface;
class UserChecker implements UserCheckerInterface
{
public function checkPreAuth(UserInterface $user)
{
if (!$user instanceof AppUser) {
return;
}
if ( $user->getDeleted() || !$user->getEnabled() )
{
throw new AuthenticationException();
}
else
{
// BELOW IS WHAT I TRY, BUT FAIL.
$entityManager = $this->get('doctrine')->getManager();
$user->setLastLogin(new \DateTime());
$entityManager->persist($user);
$entityManager->flush();
}
}
public function checkPostAuth(UserInterface $user)
{
if (!$user instanceof AppUser) {
return;
}
}
}
But it doesn't work. Maybe I can't use the doctrine entity manager in this file ?
If I use $this->get('doctrine')->getManager(); I get :
Fatal Error: Call to undefined method
CoreBundle\Security\UserChecker::get()
Dunno why #doncallisto removed his post. It was (IMHO) the right thing.
Take a look at http://symfony.com/doc/current/components/security/authentication.html#authentication-success-and-failure-events
So you have several options.
SecurityEvents::INTERACTIVE_LOGIN - triggers every time the user
full out the login form and submit credentials. Will work, but you
won't get last_login updates if you have remember_me cookie or similar
AuthenticationEvents::AUTHENTICATION_SUCCESS - triggers each time
(every request) when authentication was successful. It means your last_login will be updated each time on every request unless user logged out
so you'll need a EventSubscriber. Take a look at this article. https://thisdata.com/blog/subscribing-to-symfonys-security-events/
MAybe you'll need a simplified version.
public static function getSubscribedEvents()
{
return array(
// AuthenticationEvents::AUTHENTICATION_FAILURE => 'onAuthenticationFailure', // no need for this at that moment
SecurityEvents::INTERACTIVE_LOGIN => 'onSecurityInteractiveLogin', // this ist what you want
);
}
and then the onSecurityInteractiveLogin method itself.
public function onSecurityInteractiveLogin( InteractiveLoginEvent $event )
{
$user = $this->tokenStorage->getToken()->getUser();
if( $user instanceof User )
{
$user->setLastLogin( new \DateTime() );
$this->entityManager->flush();
}
}
P.S.
FosUserBundle uses interactive_login and a custom event to set last_login on entity
look at: https://github.com/FriendsOfSymfony/FOSUserBundle/blob/master/EventListener/LastLoginListener.php#L63
Friend you can use this to inject the entityManager by a constructor
use Doctrine\ORM\EntityManagerInterface;
public function __construct(EntityManagerInterface $userManager){
$this->userManager = $userManager;
}
And in the checkPreAuth you call it
public function checkPreAuth(UserInterface $user){
if (!$user instanceof AppUser) {
return;
}
if ( $user->getDeleted() || !$user->getEnabled() ){
throw new AuthenticationException();
}else{
// BELOW IS WHAT I TRY, BUT FAIL.
$user->setLastLogin(new \DateTime());
$this->userManager->persist($user);
$this->userManager->flush();
}
}
Im trying to override FOSUser RegistrationController. In SF2 I used to copy the controller in my AppBundle\Controller and add bundle inheritance
class AppBundle extends Bundle
{
public function getParent()
{
return 'FOSUserBundle';
}
}
In symfony 3 the bundle inheritance is not supported anymore, and when I do the same as above, I end up with errors saying that symfony cannot find the services used in this controller (cannot autowire..)
Does anyone has an idea how to override FOSUser in SF3 please ?
<?php
// src/Acme/UserBundle/Controller/RegistrationController.php
namespace Acme\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.twig', array(
'form' => $form->createView(),
));
}
}
from the official documentation
I'm not truly sure of my answer because i didnt override the FOS User Bundle but the Easy Admin Bundle in sf4. Here's what i did : create a new controller in src/Controller/MyCustomController. Next i extended MyCustomController with the Controller i wanted to overide :
<?php
namespace App\Controller;
use EasyCorp\Bundle\EasyAdminBundle\Controller\AdminController as BaseAdminController;
class MyCustomController extends BaseAdminController {}
?>
I guess you can do the same in sf3 with the FosUserBundle.
Hope i'm right !
I am working in symfony sonata admin. I am trying to extend ProfileFOSUser1Controller but unable to extend it. I have tried by clearing the cache also but didn't work? This is my controller code:
<?php
namespace Application\Sonata\UserBundle\Controller;
use Sonata\UserBundle\Controller\ProfileFOSUser1Controller as BaseController;
/**
* Overwrite methods from the ProfileFOSUser1Controller if you want to change the behavior
* for the current profile
*
*/
class ProfileUserController extends BaseController
{
/**
* #throws AccessDeniedException
*
* #return Response|RedirectResponse
*/
public function editAuthenticationAction()
{
echo "here"; die;
$user = $this->getUser();
if (!is_object($user) || !$user instanceof UserInterface) {
throw new AccessDeniedException('This user does not have access to this section.');
}
$form = $this->get('sonata.user.authentication.form');
$formHandler = $this->get('sonata.user.authentication.form_handler');
$process = $formHandler->process($user);
if ($process) {
$this->setFlash('sonata_user_success', 'profile.flash.updated');
return $this->redirect($this->generateUrl('sonata_user_profile_show'));
}
return $this->render('SonataUserBundle:Profile:edit_authentication.html.twig', [
'form' => $form->createView(),
]);
}
}
have a look here How to Use Bundle Inheritance to Override Parts of a Bundle
I think your controller name should be ProfileFOSUser1Controller
This issue has been resolved, I have changed the class name to the same name as base class have: changed "ProfileUserController" to "ProfileFOSUser1Controller". Now its working fine.
I'm trying to figure that problem for a long day.
I have Sonata User Bundle installed and I use it to manage users and profile edition and so on.
BUT, I a need to override Sonata Profile Controller.
If I understood correctly (I'm a perfect beginner in Symfony), I have to extend SonataUserBundle (which has been done using the easy extend bundle).
So, when I declare a new controller, nothing happens. Not even an error message.
Any ideas ?
Here are my files
[ BUNDLE EXTENSION FILE ]
// ApplicationSonataUserBundle.php
namespace Application\Sonata\UserBundle;
use Symfony\Component\HttpKernel\Bundle\Bundle;
class ApplicationSonataUserBundle extends Bundle
{
/**
* {#inheritdoc}
*/
public function getParent()
{
return 'SonataUserBundle';
}
}
[ CONTROLLER FILE ]
namespace Application\Sonata\UserBundle\Controller;
use Sonata\UserBundle\Controller\ProfileFOSUser1Controller as BaseController;
class ProfileFOSUser1Controller extends BaseController {
public function editProfileAction() {
die('toto');
$user = $this->getUser();
if (!is_object($user) || !$user instanceof UserInterface) {
throw new AccessDeniedException('This user does not have access to this section.');
}
$form = $this->get('sonata.user.profile.form');
$formHandler = $this->get('sonata.user.profile.form.handler');
$process = $formHandler->process($user);
if ($process) {
$this->setFlash('sonata_user_success', 'profile.flash.updated');
return $this->redirect($this->generateUrl('sonata_user_profile_edit'));
}
return $this->render('SonataUserBundle:Profile:edit_profile.html.twig', array(
'form' => $form->createView(),
'breadcrumb_context' => 'user_profile',
));
}
}
You have die('toto'); as first line in the controller method, isn't that going to terminate all code below?
Ok so I have found the answer by myself :)
I forgot to specify the classes I needed
use FOS\UserBundle\Model\UserInterface;
use Symfony\Bundle\FrameworkBundle\Controller\Controller;
use Symfony\Component\HttpFoundation\RedirectResponse;
use Symfony\Component\HttpFoundation\Response;
use Symfony\Component\Security\Core\Exception\AccessDeniedException;
use Sonata\UserBundle\Controller\ProfileFOSUser1Controller as BaseController;
I cleared the cache and everything was ok !
Thanks all !
I'm using FosUserBundle 1.3 and Symfo 2.6.
I need to add addresses (collection type) when I register a new User. Is it possible ?
Address entity has the owner side so when I want to register (create) the user, it returns me an error (contraint integrity userID cannot be null).
I see that I can do something like that into the Controller before the flush :
if($entity->getAddresses() !== null){
foreach($entity->getAddresses() as $address){
$address->setUser($entity);
}
}
Is it best practice ?
Now, where can I do that with Fos ? I think that here seems to be the right place but I'm not sure and can I override it ?
RegistrationFormHandler
...
public function process($confirmation = false)
{
$user = $this->createUser();
$this->form->setData($user);
if ('POST' === $this->request->getMethod()) {
$this->form->bind($this->request);
if ($this->form->isValid()) {
// I WOULD LIKE TO COPY MY CODE HERE
//
$this->onSuccess($user, $confirmation);
return true;
}
}
return false;
}
...
This will work since 2.0 version.
I think you should register you own EventListener that will listen to FOSUserEvents::REGISTRATION_SUCCESS or FOSUserEvents::REGISTRATION_COMPLETED and then you can add there your custom logic without controller overriding. Something like:
RegistrationListener class:
namespace AppBundle\EventListener;
use FOS\UserBundle\Event\FilterUserResponseEvent;
use FOS\UserBundle\FOSUserEvents;
use Symfony\Component\EventDispatcher\EventSubscriberInterface;
class RegistrationListener implements EventSubscriberInterface
{
public static function getSubscribedEvents()
{
return array(
FOSUserEvents::REGISTRATION_COMPLETED => 'completed'
);
}
public function completed(FilterUserResponseEvent $event)
{
$user = $event->getUser();
// add your custom logic here
}
}
and services.yml:
app_listener.registration:
class: AppBundle\EventListener\RegistrationListener
tags:
- { name: kernel.event_subscriber }
Remember to add proper dependencies to your listener class if needed.
For 1.3 version
You need to create a child bundle whose parent is FOSUserBundle and ovveride: RegistraionController::registerAction in your case:
<?php
namespace Acme\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();
// you custom logic
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(),
));
}
}