Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Symfony 2 - Loading roles from database

My roles are stored in the database and I am trying to load them dynamically upon login. What I'm doing is querying for the roles and setting them on the user object in my user provider as seen here:

public function loadUserByUsername($username) {
    $q = $this
        ->createQueryBuilder('u')
        ->where('u.username = :username')
        ->setParameter('username', $username)
        ->getQuery()
    ;

    try {
        // The Query::getSingleResult() method throws an exception
        // if there is no record matching the criteria.
        $user = $q->getSingleResult();

        // Permissions
        $permissions = $this->_em->getRepository('E:ModulePermission')->getPermissionsForUser($user);

        if ($permissions !== null) {
            foreach ($permissions as $permission) {
                $name = strtoupper(str_replace(" ", "_", $permission['name']));

                $role = "ROLE_%s_%s";

                if ($permission['view']) {
                    $user->addRole(sprintf($role, $name, 'VIEW'));
                }

                if ($permission['add']) {
                    $user->addRole(sprintf($role, $name, 'ADD'));
                }

                if ($permission['edit']) {
                    $user->addRole(sprintf($role, $name, 'EDIT'));
                }

                if ($permission['delete']) {
                    $user->addRole(sprintf($role, $name, 'DELETE'));
                }
            }
        }

    } catch (NoResultException $e) {
        throw new UsernameNotFoundException(sprintf('Unable to find an active admin Entity:User object identified by "%s".', $username), null, 0, $e);
    }

    return $user;
}

And the user entity:

class User implements AdvancedUserInterface, \Serializable {
    ....

    protected $roles;

    ....

    public function __construct() {
        $this->salt = base_convert(sha1(uniqid(mt_rand(), true)), 16, 36);
        $this->roles = array();
    }


    ....


    public function getRoles() {
        $roles = $this->roles;

        // Ensure we having something
        $roles[] = static::ROLE_DEFAULT;

        return array_unique($roles);
    }

    public function addRole($role) {
        $role = strtoupper($role);
        $roles = $this->getRoles();
        if ($role === static::ROLE_DEFAULT) {
            return $this;
        }

        if (!in_array($role, $roles, true)) {
            $this->roles[] = $role;
        }

        return $this;
    }

    public function hasRole($role) {
        $role = strtoupper($role);
        $roles = $this->getRoles();
        return in_array($role, $roles, true);
    }
}

This works fine and dandy and I see the correct roles when I do:

$this->get('security.context')->getUser()->getRoles()

The problem (I think), is that the token does not know about these roles. Because calling getRoles() on the token is showing only ROLE_USER, which is the default role.

It seems to me that the token is being created before the user is loaded by the UserProvider. I've looked through a lot of the security component but I can't for the life of me find the right part of the process to hook into to set these roles correctly so that the token knows about them.

Update Following the Load roles from database doc works fine, but this does not match my use case as shown here. My schema differs as each role has additional permissions (view/add/edit/delete) and this is why I am attempting the approach here. I don't want to have to alter my schema just to work with Symfony's security. I'd rather understand why these roles are not properly bound (not sure the correct doctrine word here) on my user object at this point.

like image 341
ftdysa Avatar asked Jan 08 '14 20:01

ftdysa


2 Answers

It looks like you may not be aware of the built in role management that Symfony offers. Read the docs - Managing roles in the database It is actually quite simple to do what you want, all you need to do is implement an interface and define your necessary function. The docs I linked to provide great examples. Take a look.

UPDATE

It looks like the docs don't give you the use statement for the AdvancedUserInterface. Here it is:

// in your user entity
use Symfony\Component\Security\Core\User\AdvancedUserInterface;

then in your role entity:

use Symfony\Component\Security\Core\Role\RoleInterface;

The docs show you how to do the rest.

UPDATE

Take a look at this blog post, which shows how to create roles dynamically:

Dynamically create roles

like image 112
Sehael Avatar answered Sep 28 '22 21:09

Sehael


The problem here stemmed from the fact that I thought I was implementing

Symfony\Component\Security\Core\User\EquatableInterface;

but wasn't (as you can see in the original question, I forgot to add it to my class definition). I'm leaving this here for people if they come across it. All you need is to implement this interface, and add the following method to your user entity.

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;
}
like image 38
ftdysa Avatar answered Sep 28 '22 23:09

ftdysa