Flag of Ukraine
SymfonyCasts stands united with the people of Ukraine
This tutorial has a new version, check it out!

Security: Creating Roles and Role Hierarchies

Keep on Learning!

If you liked what you've learned so far, dive in!
Subscribe to get access to this tutorial plus
video, code and script downloads.

Start your All-Access Pass
Buy just this tutorial for $12.00

Security: Creating Roles and Role Hierarchies

Let’s change gears and mention a few more things about security. Earlier, we saw how you could enforce security in two different ways. The access_control method is the easiest, but we can always enforce things manually in the controller. In both cases, we’re checking whether or not a user has a specific role. If they do, they get access. If they don’t, they’ll see the login page or the access denied screen.

In our example, we showed a pretty basic system with just ROLE_USER and ROLE_ADMIN. If you need another role, just start using it. For example, if only some users are able to create events, we can protect event creation with a new role.

To show this, let’s make the role that’s passed to enforceUserSecurity configurable and then only let a user create an event if they have some ROLE_EVENT_CREATE role:

// src/Yoda/EventBundle/Controller/EventController.php
// ...

public function createAction(Request $request)

// also change ROLE_USER to ROLE_EVENT_CREATE in newAction

// ...
private function enforceUserSecurity($role = 'ROLE_USER')
    $securityContext = $this->container->get('security.context');
    if (!$securityContext->isGranted($role)) {
        // in Symfony 2.5
        // throw $this->createAccessDeniedException('message!');
        throw new AccessDeniedException('Need '.$role);

The only rule when creating a role is that it must start with ROLE_. If it doesn’t, you won’t get an error, but security won’t be enforced.

Try it out by logging in as admin. But first, reload the fixtures, since our users were deleted earlier when running our functional test.

Now, try to create an event. No access! Our admin user has ROLE_USER and ROLE_ADMIN, but not ROLE_EVENT_CREATE. If we want to give all administrators the ability to create events, we can take advantage of role hierarchy, which we can see in security.yml. Add ROLE_EVENT_CREATE to ROLE_ADMIN and refresh again:

# app/config/security.yml
    # ...

We are in! Now let’s schedule that wookiee wine down!

Strategies for Controller Access

Keep these two tips in mind when using roles:

  1. Protect the actual parts of your application using feature-specific roles, not user-specific roles. This means your roles should describe the features they give you access to, like ROLE_EVENT_CREATE and not the type of user that should have access, like ROLE_ADMIN.
  1. Use the role hierarchy section to manage which types of users have which roles. For example, you might decide that ROLE_USER should have ROLE_BLOG_CREATE and ROLE_EVENT_CREATE, which you setup here. Assign your actual users these user-specific roles, like ROLE_USER or ROLE_MARKETING.

By following these tips, you’ll be able to easily control the exact areas of your site that different users have access to.

Leave a comment!

Login or Register to join the conversation
Krzysztof-K Avatar
Krzysztof-K Avatar Krzysztof-K | posted 3 years ago | edited

What about Roles Hierarchy inside Voter?

if (in_array('ROLE_EVENT_CREATE', $user->getRoles())) {
                    return true;
Above code will fail about that when user has ROLE_ADMIN - it will check only for ROLE_EVENT_CREATE in roles, but ignore whole hierarchy.
How to deal with that?

For anyone else finding this, check out the answer over here: https://symfonycasts.com/sc... :)

Peter-K Avatar
Peter-K Avatar Peter-K | posted 5 years ago

I cant get my head around this.

My scenario: I have a site where you have multiple section but for this example lets say we have a LIST section only.
This LIST section have 2 states:
1st state: see the list
2nd state: see the list and edit/add/delete - CRUD

Is this how you would set up this scenario:


Issue here is that both of these users will access the same template but only role_crud_user will see extra buttons.

Lets say the route is /list => template list.html.twig

But surely in controller I need to validate if role_readonly OR role_crud then access granted.

Next issue is that in my template I must have then if role_crud_user then show button.

Soo simple but before I was always using if this role or that role or that role then button is visible and I found it not really scalable and now I am going into the same problem so where I am making mistake?

The issue with my old approch is that there might be a new role ROLE_LIST_ONLY_DELETE then I would have to go to code and add another OR role_delete then show delete button.

So the question is how would you set up this scenario initially and also if there is another ROLE_DELETE that can see the list but only delete (cannot add or edit)


Hey Peter K.

If you want to enable/disable buttons per action, I don't see how you can get rid off those if's (4 in total)
But, what you can do is setup a good ROLE hierarchy, something like:

// Whoever have this ROLE, will be able to do everything

And in your template you check directly for each role

{% if is_granted('ROLE_UPDATE_USER') %}
    // show update button
{% else if ... %}

Another thing you can give it a try is to use Voters, we have a tutorial about them, is a bit old, but I believe voters haven't changed too much since then.

I hope this help you :) Cheers!

1 Reply
Cat in space

"Houston: no signs of life"
Start the conversation!

What PHP libraries does this tutorial use?

// composer.json
    "require": {
        "php": ">=5.3.3",
        "symfony/symfony": "~2.4", // v2.4.2
        "doctrine/orm": "~2.2,>=2.2.3", // v2.4.2
        "doctrine/doctrine-bundle": "~1.2", // v1.2.0
        "twig/extensions": "~1.0", // v1.0.1
        "symfony/assetic-bundle": "~2.3", // v2.3.0
        "symfony/swiftmailer-bundle": "~2.3", // v2.3.5
        "symfony/monolog-bundle": "~2.4", // v2.5.0
        "sensio/distribution-bundle": "~2.3", // v2.3.4
        "sensio/framework-extra-bundle": "~3.0", // v3.0.0
        "sensio/generator-bundle": "~2.3", // v2.3.4
        "incenteev/composer-parameter-handler": "~2.0", // v2.1.0
        "doctrine/doctrine-fixtures-bundle": "~2.2.0", // v2.2.0
        "ircmaxell/password-compat": "~1.0.3", // 1.0.3
        "phpunit/phpunit": "~4.1" // 4.1.0