Role Hierarchy
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.
With a Subscription, click any sentence in the script to jump to that part of the video!
Login SubscribeSo far, our site has two types of users. First, for some pages, like the account page, we only care that you are logged in - a "normal" user. And second, there are a few admin pages. Open up ArticleAdminController
and CommentAdminController
. Both of these are protected by ROLE_ADMIN
:
// ... lines 1 - 11 | |
/** | |
* @IsGranted("ROLE_ADMIN") | |
*/ | |
class ArticleAdminController extends AbstractController | |
{ | |
// ... lines 17 - 29 | |
} |
// ... lines 1 - 11 | |
/** | |
* @IsGranted("ROLE_ADMIN") | |
*/ | |
class CommentAdminController extends Controller | |
{ | |
// ... lines 17 - 35 | |
} |
A lot of sites are just this simple: you have normal users and admin users, who have access to all of the admin sections. But, if you have a more complex setup - like a bigger company where different groups of people need access to different things, this isn't good enough. The question is: what's the best way to organize that with roles?
Role Naming
Well, there are only two possibilities. First, you could use roles that are named by the type of user that will have them - like ROLE_EDITOR
, ROLE_HUMAN_RESOURCES
or ROLE_THE_PERSON_THAT_OWNS_THE_COMPANY
... or something like that. But, I don't love this option. It's just not super clear what having ROLE_EDITOR
will give me access to.
Instead, I like to use role names that specifically describe what you're protecting - like ROLE_ADMIN_ARTICLE
for ArticleAdminController
:
// ... lines 1 - 11 | |
/** | |
* @IsGranted("ROLE_ADMIN_ARTICLE") | |
*/ | |
class ArticleAdminController extends AbstractController | |
{ | |
// ... lines 17 - 29 | |
} |
And, for CommentAdminController
: ROLE_ADMIN_COMMENT
:
// ... lines 1 - 11 | |
/** | |
* @IsGranted("ROLE_ADMIN_COMMENT") | |
*/ | |
class CommentAdminController extends Controller | |
{ | |
// ... lines 17 - 35 | |
} |
Oh, and also open base.html.twig
. There's one other spot here where we use ROLE_ADMIN
. There it is: to hide or show the "Create Post" link. Now that should be ROLE_ADMIN_ARTICLE
:
// ... line 1 | |
<html lang="en"> | |
// ... lines 3 - 15 | |
<body> | |
<nav class="navbar navbar-expand-lg navbar-dark navbar-bg mb-5"> | |
// ... lines 18 - 21 | |
<div class="collapse navbar-collapse" id="navbarNavDropdown"> | |
// ... lines 23 - 34 | |
<ul class="navbar-nav ml-auto"> | |
{% if is_granted('ROLE_USER') %} | |
<li class="nav-item dropdown" style="margin-right: 75px;"> | |
// ... lines 38 - 40 | |
<div class="dropdown-menu" aria-labelledby="navbarDropdownMenuLink"> | |
// ... line 42 | |
{% if is_granted('ROLE_ADMIN_ARTICLE') %} | |
<a class="dropdown-item" href="{{ path('admin_article_new') }}">Create Post</a> | |
{% endif %} | |
// ... line 46 | |
</div> | |
</li> | |
// ... lines 49 - 52 | |
{% endif %} | |
</ul> | |
</div> | |
</nav> | |
// ... lines 57 - 74 | |
</body> | |
</html> |
role_hierarchy
I love it! Except... for one problem. Go to /admin/comment
. Access denied! Well, I'm not even logged in as an admin user. But even if I were, I would still not have access! Admin users do not have these two new roles!
And, yea, we could go back to UserFixture
, add ROLE_ADMIN_COMMENT
and ROLE_ADMIN_ARTICLE
and then reload the fixtures. But, this highlights an annoying problem. Each time we add a new admin section to the site and introduce a new role, we will need to go into the database, find all the users who need access to that new section, and give them that new role. That's a bummer!
But... don't worry! Symfony has our backs with a sweet feature called role_hierarchy
. Open config/packages/security.yaml
. Anywhere inside, I'll do it above firewalls, add role_hierarchy
. Below, put ROLE_ADMIN
set to an array with ROLE_ADMIN_COMMENT
and ROLE_ADMIN_ARTICLE
:
security: | |
// ... lines 2 - 13 | |
role_hierarchy: | |
ROLE_ADMIN: [ROLE_ADMIN_COMMENT, ROLE_ADMIN_ARTICLE] | |
// ... lines 16 - 55 |
It's just that simple. Now, anybody that has ROLE_ADMIN
also has these two roles, automatically. To prove it, go log out so that we can log in as one of our admin users: admin2@thespacebar.com
, password engage
.
Go back to /admin/comment
and... access granted!
This is even cooler than you might think! It allows us to organize our roles into different groups of people in our company. For example, ROLE_EDITOR
could be given access to all the sections that "editors" need. Then, the only role that you need to assign to an editor user is this one role: ROLE_EDITOR
. And if all editors need access to a new section in the future, just add that new role to role_hierarchy
.
We can use this new super-power to try out a really cool feature that allows you to impersonate users... and become the international spy you always knew you would.
Sorry about comment in Symfony 2 Track.
What about Roles Hierarchy inside Voter?
ROLE_ADMIN: [ROLE_USER, ROLE_EVENT_CREATE]
``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?
Is
`
foreach ($user->getRoles() as $role) {
`
propper solution?