Symfony: Roles and Permissions in configuration

There are different approaches to managing roles and permissions on a project, ranging from very simple hardcoded checks to fully adjustable database based solutions.

I wanted to build something in between - a configuration based system, where the roles and permissions would be stored in config files, while users would have roles stored against their accounts in database.

motivation

The reason config based roles worked on this particular project was that it offered the right amount of complexity, flexibility and control.

Database based roles and permissions systems are great, they offer a lot of flexibility and freedom, but are a bit harder to control, especially to control and revert changes.

Fully coded alternative gives the opposite - 100% control, but very little operational flexibility - every change requires a code change, new release. Defining complex rules can also easily become mess, as it tends to scatter the logic to different places.

Config based is somewhere in the middle - roles and permissions definitions are controlled in code, at a single place, and it also offers all the complexity to support all different scenarios - all while some operational flexibility is still preserved.

how

I am going to explain the basic idea here, showing all the code here on the blog would be a bit impractical, but you can definitely check it in a gist I published.

1) create an extension that loads your config files

In my case this was part of Symfony 3 project, so it’s a standard Symfony bundle and it simply loads given config files.

2) create roles and permissions config files with format that matches your needs

I had some specific project requirements, I needed to control CRUD access to entities, control access to various routes, support role hierarchy/inheritance and also group permissions into logical chunks in an API application.
An example of what I came up with:

roles.yml:

1
2
3
4
5
6
7
8
9
10
auth:
    roles:
        ROLE_USER:
            - login
            - dashboard_everyone
            - statistics

        ROLE_FRONT_DESK:
            - ROLE_USER                                           
            - manage_users                                                    

permissions.yml:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
auth:
    permissions:
        login:
            # A route name
            users_get_current:
                - read
            # An entity, this gives read-only access
            Record:
                - read

        dashboard_everyone:
            # Route names again
            reporting.device:
                - read
            reporting.system.status:
                - read

        statistics:
            # Route name again
            statistics.daily:
                - read

        manage_users:
            # Full access to an entity
            User:
                - read
                - create
                - update
                - delete                                                       
3) parse the roles and permissions config files into a structure that is useful for you

I created a simple parser that takes the arrays coming from the yaml files into flat arrays of all roles and permissions for given user.

4) use security voters to give/deny access to various operations in your application

Here we can use the very useful Symfony Security Voter mechanism to vote on whether we want to give/reject access to a particular user/operation combination.

5) profit!!!

Examples of use, this is taken from a REST API controller:

1
2
3
$this->denyAccessUnlessGranted('update', $entity);
$this->denyAccessUnlessGranted('read', $this->routeName);
// ... you can add more types of voters and mechanisms and use them similarly

Again, here is the whole source code roughly taken from a Symfony 3 project.

I wish you happy coding and no security breaches!