Kanav Kanav - 4 months ago 113
PHP Question

Manage Roles and Assign Permissions to Roles - Symfony

I'm building an Admin panel in Symfony 3 based on Roles & Permissions. Each admin will be assigned a role(or multiple roles) and then he will be able to do things based on the permissions assigned to that role.




To give you an idea, here's an example:


  • Admin panel has the functionality to add users, edit users and delete users.

  • I created a role:
    USER_MANAGEMENT_WITHOUT_DELETE
    which has permission to
    user_create
    and
    user_edit
    .

  • I created
    USER_MANAGEMENT_WITH_DELETE
    role which has permission to
    user_create
    ,
    user_edit
    and
    user_delete

  • So now, admin with role
    USER_MANAGEMENT_WITH_DELETE
    can
    add
    ,
    edit
    and
    delete
    the users where as admin with role
    USER_MANAGEMENT_WITHOUT_DELETE
    can only
    add
    and
    edit
    users but cannot delete them.






I searched and found out about FOSUserBundle and ACL. Some recommended ACL while others say it's better to use FOSUserBunder

I also read the documentation of FOSUserBunder and how it store Roles in
roles
column, something like
a:1:{i:0;s:10:"ROLE_ADMIN";}
, but there is nothing mentioned about permissions. So here are my queries:


  1. I'm confused between the two. Which one should i use?

  2. If i use
    FOSUserBunder
    , how to manage permissions?


Answer

Roles are not specific tu FOSUserBundle. They are in Symfony.

ACLs are more complex than using roles. So I would suggest to use roles.

From the Symfony documentation : Alternatives to ACLs

Using ACL's isn't trivial, and for simpler use cases, it may be overkill. If your permission logic could be described by just writing some code (e.g. to check if a Blog is owned by the current User), then consider using voters. A voter is passed the object being voted on, which you can use to make complex decisions and effectively implement your own ACL. Enforcing authorization (e.g. the isGranted part) will look similar to what you see in this entry, but your voter class will handle the logic behind the scenes, instead of the ACL system.

To deal with 'permissions', I would sugget to use Voters :

First of all create a voter like this :

Configuration :

# app/config/services.yml
services:
    app.user_permissions:
        class: AppBundle\Voters\UserPermissionsVoter
        arguments: ['@security.access.decision_manager']
        tags:
            - { name: security.voter }
        public: false

And the class :

namespace AppBundle\Voters;

use Symfony\Component\Security\Core\Authorization\Voter\Voter;
use Symfony\Component\Security\Core\Authentication\Token\TokenInterface;
use Symfony\Component\Security\Core\User\UserInterface;

class UserPermissionsVoter extends Voter
{
    const USER_CREATE = 'user_create';
    const USER_EDIT = 'user_edit';
    const USER_DELETE = 'user_delete';

    private $decisionManager;

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

    protected function supports($attribute, $object)
    {    
        if (!in_array($attribute, array(self::USER_CREATE,self::USER_EDIT,self::USER_DELETE))) {
            return false;
        }

        return true;
    }

    protected function voteOnAttribute($attribute, $object, TokenInterface $token)
    {
        $user = $token->getUser();

        if (!$user instanceof UserInterface) {
            return false;
        }

        switch($attribute) {
            case self::USER_CREATE:
                if ($this->decisionManager->decide($token, array('ROLE_USER_MANAGEMENT_WITH_DELETE'))
                    || $this->decisionManager->decide($token, array('USER_MANAGEMENT_WITHOUT_DELETE'))
                ){
                    return true;
                }
            break;
            case self::USER_EDIT:
                // ...
            break;
            case self::USER_DELETE:
                // ...
            break;
        }

        return false;
    }
}

Then you can check for permission in your controller :

userCreateAction()
{
    if(!$this->isGranted('user_create')){throw $this->createAccessDeniedException('You are not allowed to create an user.');}

    // next steps ...
}