3

I started working on a Voter System to secure my URLs and I have many questions.

First, I don't know if I have to work with "Roles" or with "Permissions".

By "Roles" I mean something like Role_User, Role_Manager, etc. And by "Permissions", I mean something like "Access_users", "Edit_User", "Add_User", etc.

Am I right with that ?

If yes, I'd like to check Permissions in my Voters to control the access to URL, event if I don't have an object to control.

Example

/**
 * @Route("/", name="list")
 * @Security("is_granted('ROLE_ADMIN') or is_granted('PERM_ACCESS_SERIES')")
 */
public function list()
{
    $series = $this->seriesService->findAll();

    return $this->render('backend/series/list.html.twig', [
        "series" => $series
    ]);
}

In this case, I want to check if the user have the role ROLE_ADMIN or the permission PERM_ACCESS_SERIES, but I don't know how to do it in my Voters.

For information, the 'ROLE_ADMIN' is set in my security.yaml config, but I'd like to make these roles dynamic, to be able to create roles (and assign permissions to role?).

In my Voter class, when I control on a Document, I check the owner, and other Users that can manage the Document, but when I don't pass a Document, I don't really know what to do.

Is just get the user's permissions and check if the permission asked is into them. something like that :

if (in_array($attribute, $user->getPermissions())) {
    return true;
}

Actually here is my voter

 protected function voteOnAttribute($attribute, $subject, TokenInterface $token)
{
    //ADMIN and SUPER_ADMIN can do anything they want !
    if ($this->decisionManager->decide($token, array(User::ROLE_ADMIN, User::ROLE_SUPER_ADMIN))) {
        return true;
    }

    $user = $token->getUser();
    if (!$user instanceof User) {
        // the user must be logged in. if not, deny access
        return false;
    }

    $series = $subject;
    switch ($attribute) {
        case self::PERM_ACCESS_SERIES:
            if (in_array($attribute, $user->getPermissions())) {
                return $this->canAccess($series, $user);
            }
            break;

        case self::PERM_ADD_SERIES:
            if (in_array($attribute, $user->getPermissions())) {
                return $this->canAdd($series, $user);
            }
            break;

        case self::PERM_EDIT_SERIES:
            if (in_array($attribute, $user->getPermissions())) {
                return $this->canEdit($series, $user);
            }
            break;

        case self::PERM_DELETE_SERIES:
            if (in_array($attribute, $user->getPermissions())) {
                return $this->canDelete($series, $user);
            }
            break;

        default:
            throw new \LogicException('This code should not be reached!');
            break;
    }

    return false;
}

On each case, I check if the attribute is in the user's permissions.

Well, as you can see, I lack a lot of sense about Roles and Permissions, so advices and help to organize all that are welcomed.

Also, if something is unclear don't hesitate to tell me, English is not my native language so maybe My sentences are not well constructed and completely incomprehensible.

Etshy
  • 870
  • 7
  • 25

1 Answers1

5

What you saying Roles and Permissions in your way are the same thing. No need to distinguish between them. You can think of your PERM, like ROLE_ACCESS_SERIES and so on, its just matter of naming / conventions but from Symfony point of view its the same.

Assigning permissions to roles, isn't nothing else than role hierarchy, more in documentation. Dont forget as well, that one user can have multiple roles.

From now I will talk only about roles.

For dynamic roles, your UserInterface object has method getRoles() which doesn't have to be static, but can for example load roles from database / any storage you want for each user.

For object to check in permission. For example ROLE_LIST_SERIES doesn't usually need any object to check permission on, so its ok without object, but ROLE_EDIT_SERIES for example should have always object which we are editing associated, means if no is provided we throw exception and it means we are missing somewhere place to pass it.

For me, if we take for example CRUD I will have roles like this:

ROLE_ENTITY_MASTER:
    - ROLE_ENTITY_LIST
    - ROLE_ENTITY_EDIT
    - ROLE_ENTITY_ADD
    # and you continue what you need.

Advantage of this is, that for example if I wanna give somebody full access to something I just need to assign one role and if I want somebody restrict I give him only roles I want. Then in my getRoles() (in reality I'm loading from database, so this is just pseudocode).

getRoles() {
    return [ROLE_ADMIN, ROLE_ENTITY_MASTER2, ROLE_ENTITY_MASTER];
}
M. Kebza
  • 1,488
  • 1
  • 10
  • 14
  • Thanks a lot, you first sentence helped me clarify my thoughts actually. I use FOSUser so I already have the getRoles and all others fields required. but can I do this "role hierarchy" (like you did with the ROLE_ENTITY_MASTER) dynamically. Like that I could give admin the ability to create custom _MASTER_ Roles that inherit the _permissions_ (or other _MASTER_) roles they want. Or I need to do that hierarchy (to create _MASTER_ roles for each of my Documents) in the config, (like in the doc, with `role_hierarchy` options) and it can't be dynamic? – Etshy Aug 17 '18 at 13:00
  • I haven't tried to do roles dynamically (because I have them associated with my resources, just got script which dumps them on config). But I dont see reason for your admin to create roles, because you can just assign more roles to your rules, instead of creating new ones. – M. Kebza Aug 18 '18 at 01:14
  • Well I wanted to add the possibility to create role that iunherit other roles to "simplify" the role set to others Users. Like Admin create a 'updater' Role with update and view Role, but not delete and the he can just assign 'updater' role to Users. But just assign all the roles could be good too. Thanks a lot for your help, btw. – Etshy Aug 18 '18 at 15:15
  • 1
    For that FOSUser has Groups, you assign roles to Group (Editor) and then assign this groups to user – M. Kebza Aug 19 '18 at 02:19
  • Oh thanks, didn't see that. I'll take a look to see how it could ork with Voters. – Etshy Aug 19 '18 at 08:54
  • It will work, its two things - Groups saying which ROLES user have, Voters checking if ROLE has access. – M. Kebza Aug 19 '18 at 13:01
  • But I'll need to check roles on all the "User's groups' roles" ? or it will work checking the user's roles ? That's what I have to search about. Thanks a lot about these advices. – Etshy Aug 19 '18 at 21:24
  • FOSUser collects roles from group in your `getRoles()`, from where you collect roles depends only on your `getRoles` function in your user. – M. Kebza Aug 20 '18 at 01:02