Hassle-Free Role Management in Symfony

Written by jcfrane

March 9, 2020

Authorization is such an important part of any good system. A secured and robust system must have an efficient set of rules on how a particular user should access a given resource. That’s where Symfony’s Security Component really stands out. It makes developer’s life easy by making Authorization configurable and flexible as much as possible.

Role Management accompanied by Symfony’s denyAccessUnlessGranted() is one of my favorite combinations when implementing Authorization inside my application. While the power is given to us, it is our task to make our code easily maintainable.

In this article. I’ll share with you how I made my Authorization Layer as flexible as possible in a way that changes in business logic doesn’t require me to change my Authorization Layer as well.


Supposed you have a user who is a Merchandiser and that he should have all access to products. How we gonna do that?

Often than not, developers seem to think straight when facing this kind of task. Somebody might do it like so:

security.yml

ROLE_MERCHANDISER: ROLE_PRODUCT

ProductController.php

class ProductController {
  public function view() {
   $this->denyAccessUnlessGranted('ROLE_PRODUCT');
  }

  public function create() {
    $this->denyAccessUnlessGranted('ROLE_PRODUCT');
  }
}

While this approach will do, there are several issues with this in terms of scalability and adaptability with change.

If suddenly, the Merchandiser does not have access to create any more? We definitely need to remove the ROLE_PRODUCT role in his account. Unfortunately, doing so will also result in him losing access to view. This might look trivial now, but imagine you have hundreds of functions and suddenly the access rules changed? That was hell of a refactor!


1 Controller’s Function : 1 Role

The idea is that each function in the Controller class should correspond with 1 role. E.g view() should correspond to ROLE_PRODUCT_VIEW

Now we can do that as follows:

security.yml

ROLE_PRODUCT_WRITE:
  - ROLE_PRODUCT_CREATE
  - ROLE_PRODUCT_UPDATE
  - ROLE_PRODUCT_DELETE
  - ROLE_PRODUCT_READ

ProductController.yml

class ProductController {
  public function view() {
   $this->denyAccessUnlessGranted('ROLE_PRODUCT_READ');
  }

  public function create() {
    $this->denyAccessUnlessGranted('ROLE_PRODUCT_CREATE');
  }

  // So on
}

Supposedly, at the beginning the Merchandiser has write access to Product like so:

ROLE_MERCHANDISER: ROLE_PRODUCT_WRITE

Then suddenly, the rules changed and the Merchandiser can only access the viewing function. You can just change the security.yml like so:

ROLE_MERCHANDISER: ROLE_PRODUCT_READ

With this approach, you don’t have to ruin your codebase anymore ?


Conclusion

I have used this method for quite a long time now. It proved to be robust and unbreakable. Introduction with new User types / roles is very easy to do.

PS: You can create additional files to have a logical grouping across your roles. E.g have a file called product_roles.yml then import it on your security.yml.

An additional tip, you can actually rename the lower roles as permissions such as ROLE_PRODUCT_CREATE can be ROLE_PERMISSION_CREATE. The reason is that each access is really like a permission/action and not a role. ?

Hope you learned something new with this blogpost ?

You May Also Like…

Github Code Navigation Made Easy!

Github Code Navigation Made Easy!

Did you know GitHub's unlimited private repositories are now free? Yeah, you read that right!As such, I started to use...

0 Comments

Submit a Comment

Your email address will not be published. Required fields are marked *