16

Say for example I grant a new role to the currently authenticated user in a controller, like so:

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

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

On the next page load, when I grab the authenticated user again:

$loggedInUser = $this->get('security.context')->getToken()->getUser();

They are not granted the role. I am guessing this is because the user is stored in the session and needs to be refreshed.

How do I do this?

I am using FOSUserBundle if that makes a difference.

EDIT: This question was originally asked in the context of Symfony version 2.3 but there are answers for more recent versions below as well.

Nada_Surf
  • 616
  • 1
  • 7
  • 19
  • Is the database updated? – cheesemacfly Oct 31 '13 at 18:15
  • Yes the database is updated. So if I log out and in again then the new role is reflected. – Nada_Surf Oct 31 '13 at 18:24
  • I was checking for the new role in a twig template using `if is_granted('ROLE_XYZ')`. I changed this to `if app.user.hasRole('ROLE_XYZ')` and now it is working. Strange... – Nada_Surf Oct 31 '13 at 19:55
  • 1
    @Nada_Surf - that's because hasRole checks for the exact roles. It ignores the role hierarchy.. Eg: is ROLE_SUPER_ADMIN has ROLE_USER, ROLE_ADMIN.. If you check hasRole('ROLE_ADMIN')--- false, hasGranted('ROLE_ADMIN')--- true – user1236048 Oct 31 '13 at 21:38
  • Thanks Alex, however, the call to `is_granted('ROLE_XYZ')` does return TRUE as soon as I have logged out and back in again. – Nada_Surf Oct 31 '13 at 21:58

5 Answers5

23

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);
dmnptr
  • 4,258
  • 1
  • 20
  • 19
15

There's no need for the token reset in the previous answer. Just, in your security config file (security.yml, etc...), add this:

security:
    always_authenticate_before_granting: true
Marc
  • 441
  • 1
  • 6
  • 11
  • 1
    If this is a point relating to another answer I think it would be better suited to being a comment on that answer, rather than an answer in it's own right – beresfordt Feb 28 '15 at 13:09
  • 3
    Apparently you need some reputation to do that. Smart moves all arround. – Marc Mar 02 '15 at 02:22
14

While an answer is accepted, Symfony actually has a native way to refresh the User object. Credit goes out to Joeri Timmermans for this article.

Steps for refreshing the User object:

  1. Make your User entity implement the interface

Symfony\Component\Security\Core\User\EquatableInterface

  1. Implement the abstract function isEqualTo:

public function isEqualTo(UserInterface $user)
{
    if ($user instanceof User) {
        // 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;
}

The code above refreshes the User object if any new roles are added. The same principle also holds true for other fields you compare.

Thomas Maurstad Larsson
  • 2,217
  • 2
  • 21
  • 19
  • Thanks for this!, until now I didn't understand EquatableInterface! – wonzbak Nov 06 '15 at 14:57
  • 3
    Just a FYI: If you implement `EquatableInterface`, you may also want to add the other comparisons that Symfony would normally make (until you implemented EquatableInterface). If your User also implements AdvancedUserInterface, then you may want to add comparisons for `isAccountNonExpired`, `isAccountNonLocked`, `isCredentialsNonExpired`, and `isEnabled`. Check out https://github.com/symfony/security/blob/v3.1.2/Core/Authentication/Token/AbstractToken.php#L250-L282 – simshaun Jul 15 '16 at 19:51
  • 1
    FYI: the `AdvancedUserInterface` is deprecated now in favor of a `UserChecker` – Mike Doe Aug 07 '19 at 21:47
4
$user = $this->getUser();
$userManager = $this->get('fos_user.user_manager');
$user->addRole('ROLE_TEACHER');
$userManager->updateUser($user);
$newtoken = new \Symfony\Component\Security\Core\Authentication\Token\UsernamePasswordToken($user,null,'main', $user->getRoles());
$token = $this->get('security.token_storage')->setToken($newtoken);
cretthie
  • 349
  • 1
  • 2
  • 11
  • `security.context` didn't work in Symfony 4 for me, but `security.token_storage` did the thing. Thanks! – Sentry Oct 16 '19 at 17:36
3

In Symfony 4

public function somename(ObjectManager $om, TokenStorageInterface $ts)
    {
        $user = $this->getUser();
        if ($user) {
            $user->setRoles(['ROLE_VIP']); //change/update role
            // persist if need
            $om->flush();
            $ts->setToken(
                new PostAuthenticationGuardToken($user, 'main', $user->getRoles())
            );
            //...
        } else {
            //...
        }
    }
Polidoor
  • 31
  • 3