Symfony: How to Refresh the Authenticated User from the Database

Symfony: How do I refresh the authenticated user from the database?

Try this:

$em = $this->getDoctrine()->getManager();
$loggedInUser = $this->get('security.context')->getToken()->getUser();
$loggedInUser->addRole('ROLE_XYZ');

$em->persist($loggedInUser);
$em->flush();

$token = new \Symfony\Component\Security\Core\Authentication\Token\UsernamePasswordToken(
$loggedInUser,
null,
'main',
$loggedInUser->getRoles()
);

$this->container->get('security.context')->setToken($token);

When are user roles refreshed and how to force it?

So after a couple of days trying to find a viable solution and contributing to the Symfony2 user mailing list, I finally found it. The following has been derived from the discussion at https://groups.google.com/d/topic/symfony2/NDBb4JN3mNc/discussion

It turns out that there's an interface Symfony\Component\Security\Core\User\EquatableInterface that is not intended for comparing object identity but precisely to

test if two objects are equal in security and re-authentication context

Implement that interface in your user class (the one already implementing UserInterface). Implement the only required method isEqualTo(UserInterface $user) so that it returns false if the current user's roles differ from those of the passed user.

Note: The User object is serialized in the session. Because of the way serialization works, make sure to store the roles in a field of your user object, and do not retrieve them directly in the getRoles() Method, otherwise all of that won't work!

Here's an example of how the specific methods might look like:

protected $roles = null;

public function getRoles() {

if ($this->roles == null) {
$this->roles = ...; // Retrieve the fresh list of roles
// from wherever they are stored here
}

return $this->roles;
}

public function isEqualTo(UserInterface $user) {

if ($user instanceof YourUserClass) {
// Check that the roles are the same, in any order
$isEqual = count($this->getRoles()) == count($user->getRoles());
if ($isEqual) {
foreach($this->getRoles() as $role) {
$isEqual = $isEqual && in_array($role, $user->getRoles());
}
}
return $isEqual;
}

return false;
}

Also, note that when the roles actually change and you reload the page, the profiler toolbar might tell you that your user is not authenticated. Plus, looking into the profiler, you might find that the roles didn't actually get refreshed.

I found out that the role refreshing actually does work. It's just that if no authorization constraints are hit (no @Secure annotations, no required roles in the firewall etc.), the refreshing is not actually done and the user is kept in the "unauthenticated" state.

As soon as you hit a page that performs any kind of authorization check, the user roles are being refreshed and the profiler toolbar displays the user with a green dot and "Authenticated: yes" again.

That's an acceptable behavior for me - hope it was helpful :)

Update user data after login in Symfony 3

You are updating the entity class(object). To save it in the database you should use the doctrine manager service.

in a controller you do something like this: doc

$em = $this->getDoctrine()->getManager();

// tells Doctrine you want to (eventually) save the Product (no queries yet)
$em->persist($product);

// actually executes the queries (i.e. the INSERT query)
$em->flush();

but you are not in a controller so you can inject the doctrine service in the constructor and persist your entity on the event.

<?php
namespace AppBundle\EventListener;

use Symfony\Component\Security\Core\Event\AuthenticationEvent;
use Symfony\Component\Security\Core\User\AdvancedUserInterface;

class LoginListener {
protected $doctrine;

public function __construct($doctrine){
$this->doctrine = $doctrine;
}

public function onSuccessfulLogin(AuthenticationEvent $event)
{
$user = $event->getAuthenticationToken()->getUser();
if($user instanceof AdvancedUserInterface){
$user->updateLastLogin();
$this->doctrine->getManager()->persist($user);
$this->doctrine->getManager()->flush();
}
}

public function onLoginError(AuthenticationEvent $event)
{
// Login error
}
}

and in your service definition:

services:
app.login_service:
class: AppBundle\EventListener\LoginListener
arguments: [doctrine]

Symfony 3, populating token and refreshing user

This is a tricky one, thanks to the repository it was easier to isolate the problem. You are binding the user object form the authentication token to the createForm() method. After the

$form->handleRequest($request)

call the email off the token user object is updated.

I first thought to solve this by implementing the EquatableInterface.html in the User entity but this did not work, as the compared object already had the wrong email address set.

It may also be useful to implement the EquatableInterface interface, which defines a method to check if the user is equal to the current user. This interface requires an isEqualTo() method.)

Than I thought about forcing a reload of the user from the db and resetting the security token, but in the it came to my mind, that it might be sufficient to just refresh the current user object from the database in case the form fails:

$this->get('doctrine')->getManager()->refresh($this->getUser());`

In your controller, this would solve your issue.

/**
* @Route("/edit_me", name="edit")
* @Security("has_role('ROLE_USER')")
*/
public function editMyselfAction(Request $request) {
$form = $this->createForm(User::class, $this->getUser());

if ($request->isMethod(Request::METHOD_POST)) {
$form->handleRequest($request);
if ($form->isSubmitted() && $form->isValid()) {
$user = $form->getData();
$em = $this->getDoctrine()->getManager();
$em->persist($user);
$em->flush();
} else {
$this->get('doctrine')->getManager()->refresh($this->getUser());
}
}

return $this->render(':security:edit.html.twig',['form' => $form->createView()]);
}

Alternative solution

The issue at the Symfony repository resulted in some valuable input about Avoiding Entities in Forms and
Decoupling Your Security User which provides a more complex approach for a solution to your problem.



Related Topics



Leave a reply



Submit