Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Managing users/roles/groups in FOSUserBundle

I am developing a simple CRUD to manage users/roles/groups of the application in which I am working. To manage users I'm using FOSUserBundle. What I want to do can be accomplished in several ways:

  • Assigning roles to groups and then assign users to these groups
  • Assigning roles to users directly

But I have no idea how. I knew that FOSUser BaseUser class already has a column roles and in the documentation of FOSUser explains how to establish a ManyToMany relationship between users and groups but do not talk anything about roles. The only idea that comes to mind is to create an entity to manage the roles as well as a form for the same purpose, something like what you see below:

Role Entity

use Symfony\Component\Security\Core\Role\RoleInterface;
use Doctrine\Common\Collections\ArrayCollection;
use Doctrine\ORM\Mapping as ORM;

/**
 * @ORM\Table(name="fos_role")
 * @ORM\Entity(repositoryClass="UserBundle\Entity\Repository\RoleRepository")
 * 
 * @see User
 * @see \UserBundle\Role\RoleHierarchy
 * 
 */
class Role implements RoleInterface
{
    /**
     * @ORM\Column(name="id", type="integer")
     * @ORM\Id()
     * @ORM\GeneratedValue(strategy="AUTO")
     */
    private $id;

    /**
     * @ORM\Column(name="name", type="string", length=80, unique=true)
     */
    private $name;

    /**
     * @ORM\ManyToOne(targetEntity="Role", inversedBy="children")
     * @ORM\JoinColumn(name="parent_id", referencedColumnName="id", nullable=true)
     * @var Role[]
     */
    private $parent;

    /**
     * @ORM\OneToMany(targetEntity="Role", mappedBy="parent")
     * @var ArrayCollection|Role[]
     */
    private $children;

    /**
     * @ORM\ManyToMany(targetEntity="User", mappedBy="roles")
     */
    private $users;

    public function __construct($role = "")
    {
        if (0 !== strlen($role)) {
            $this->name = strtoupper($role);
        }

        $this->users = new ArrayCollection();
        $this->children = new ArrayCollection();
    }

    /**
     * @see RoleInterface
     */
    public function getRole()
    {
        return $this->name;
    }

    public function getId()
    {
        return $this->id;
    }

    public function setId($id)
    {
        $this->id = $id;
    }

    public function getName()
    {
        return $this->name;
    }

    public function setName($name)
    {
        $this->name = $name;
    }

    public function getUsers()
    {
        return $this->users;
    }

    public function addUser($user, $addRoleToUser = true)
    {
        $this->users->add($user);
        $addRoleToUser && $user->addRole($this, false);
    }

    public function removeUser($user)
    {
        $this->users->removeElement($user);
    }

    public function getChildren()
    {
        return $this->children;
    }

    public function addChildren(Role $child, $setParentToChild = true)
    {
        $this->children->add($child);
        $setParentToChild && $child->setParent($this, false);
    }

    public function getDescendant(& $descendants = array())
    {
        foreach ($this->children as $role) {
            $descendants[spl_object_hash($role)] = $role;
            $role->getDescendant($descendants);
        }
        return $descendants;
    }

    public function removeChildren(Role $children)
    {
        $this->children->removeElement($children);
    }

    public function getParent()
    {
        return $this->parent;
    }

    public function setParent(Role $parent, $addChildToParent = true)
    {
        $addChildToParent && $parent->addChildren($this, false);
        $this->parent = $parent;
    }

    public function __toString()
    {
        if ($this->children->count()) {
            $childNameList = array();
            foreach ($this->children as $child) {
                $childNameList[] = $child->getName();
            }
            return sprintf('%s [%s]', $this->name, implode(', ', $childNameList));
        }
        return sprintf('%s', $this->name);
    }
}

Role Form Type

use Symfony\Component\Form\AbstractType;
use Symfony\Component\Form\FormBuilderInterface;
use Symfony\Component\OptionsResolver\OptionsResolverInterface;

class RoleType extends AbstractType {

    /**
     * @param FormBuilderInterface $builder
     * @param array $options
     */
    public function buildForm(FormBuilderInterface $builder, array $options)
    {
        $builder
                ->add('name')
                ->add('parent');
    }

    /**
     * @param OptionsResolverInterface $resolver
     */
    public function setDefaultOptions(OptionsResolverInterface $resolver)
    {
        $resolver->setDefaults(array(
            'data_class' => 'Tanane\UserBundle\Entity\Role'
        ));
    }

    /**
     * @return string
     */
    public function getName()
    {
        return 'role';
    }    
}

If so what would add to my user form would look something like this

public function buildForm(FormBuilderInterface $builder, array $options)
{
    $builder
            ->add('username', 'text')
            ->add('email', 'email')
            ->add('enabled', null, array(
                'label' => 'Habilitado',
                'required' => false
            ))
            ->add('rolesCollection', 'entity', array(
                'class' => 'UserBundle:Role',
                'multiple' => true,
                'expanded' => true,
                'attr' => array('class' => 'single-line-checks')
            ))
            ->add('groups', 'entity', array(
                'class' => 'UserBundle:Group',
                'multiple' => true,
                'expanded' => true,
    ));
}

But I do not know if it is the right way to handle the roles since in this case would be creating a new table in my DB called fos_roles where were the relationships between users/roles is handled but relationships between groups/roles stay out of it, then that's where I'm a little lost and need help from the more experienced in that tell me and alert if I'm on track and that would make them to achieve what I explain in the first two points. Any advice or help? How do you deal with this?

like image 823
ReynierPM Avatar asked Sep 15 '14 21:09

ReynierPM


People also ask

Does Fos userbundle use cookies?

Our website, platform and/or any sub domains use cookies to understand how you use our services, and to improve both your experience and our marketing relevance. FOSUserBundle is a preferred choice to create user management (login, register, profile, logout) system in Symfony.

How do I assign a role to a user?

To manage users, groups, or roles, you must be assigned a role that includes the relevant actions or combination of actions. For example, to assign roles to users, your role assignments must include UME actions that enable you to change both principals, roles and users, such as Manage_Roles and Manage_Users .

What is the use of User_Manager?

This manager, which is accessed through the fos_user.user_manager service, makes the bundle "agnostic" to where the users are stored and it's a good practice to use it. Before using this manager, read How to Define Custom Actions if you haven't done it already so you can modify the behavior of the new action.


2 Answers

Symfony Roles

The way FOSUserBundle deals with Roles is to store them in the roles column that you've seen, in a serialised format like this: a:1:{i:0;s:10:"ROLE_ADMIN";}. So there's no need for any other tables or entities^.

^ This is in contrast to Groups, which need to be explicitly configured, are represented by a separate Table/Entity, and do involve relating Users to Groups in the DB. Groups let you define arbitrary collections of Roles which can then be given to each User as a discrete bundle.

A User can be a member of any number of Roles. They're identified by strings starting with "ROLE_", you can just start using a new Role.

What the Roles mean for your application is completely up to you, but they're quite a high-level tool - a User is either in a particular Role or they aren't.

You put people in Roles either via the Symfony console:

php app/console fos:user:promote testuser ROLE_ADMIN

Or in PHP:

$user = $this->getUser();
$userManager = $container->get('fos_user.user_manager');
$user->addRole('ROLE_ADMIN');
$userManager->updateUser($user);

And you can test membership in PHP:

$user = $this->getUser();
if ($user->hasRole('ROLE_ADMIN'))
{
    //do something
}

Or using Annotations:

/**
 * @Security("has_role('ROLE_ADMIN')")
 */
 public function adminAction()
 {
     //...

or

/**
 * @Security("has_role('ROLE_ADMIN')")
 */
class AdminController
{
    //...
like image 62
frumious Avatar answered Oct 16 '22 01:10

frumious


I added the functionality to add default group to the user during registration by overriding the confirmAction in Registration Controller

What I did is I overrided the Registration Controller in my project Bundle by defining the parent to FosUserBUndle .

Then created a function confirmedAction and in the body of the function added this code

$repository = $em->getRepository('AdminAdminBundle:Group');
$group = $repository->findOneByName('staff');

$em = $this->getDoctrine()->getEntityManager();
$user = $this->getUser();
$user->addGroup($group);

$userManager = $this->get('fos_user.user_manager');

$userManager->updateUser($user);

if (!is_object($user) || !$user instanceof FOS\UserBundle\Model\UserInterface) {
    throw new AccessDeniedException('This user does not have access to this section.');
}

return $this->render('FOSUserBundle:Registration:confirmed.html.twig',
                      ['user' => $user]);

And it perfectly saved in db with group assignment.

Hope this will help some one in need as there is little information about the implementation in official fosuserbundle doc.

like image 23
Neeraj Avatar answered Oct 15 '22 23:10

Neeraj