How to set up dynamic permissions for your project

The basic approach

In our projects we usually use a Role based permission system. If we imagine a situation in which all users have some sort of private information in the database, to keep something secure we essentially use the simple security methods outlined in the Symfony documentation:

as an annotation:

/**
 * @Security("has_role('ROLE_ADMIN')")
 */
public function dashboardAction($name)
{
 // ...
}

in the method body:

if ($this->get('security.authorization_checker')->isGranted(‘IS_AUTHENTICATED_ANONYMOUSLY’)) {
 // ...
}

or in Twig:

{% if is_granted('IS_AUTHENTICATED_FULLY') %}
 <p>Username: {{ app.user.username }}</p>
{% endif %}

Flexibility needed

This is a powerful tool to ensure the information is secure, but it’s often not enough. It doesn’t allow us the flexibility to create multiple roles, either with the same or with different permissions. What should we do in a project where that flexibility is needed?

Example scenario

By way of example, suppose our system contains multiple “Shops”, each of which is related to some “Modules” (such as discounts, vouchers, rewards and etc). Supposing a module is activated, each shop needs to be able to give a specific selection of permissions, to their own “appUsers” only.

We need to add some new entities, for example “CustomRole” and “RolePermission”.

Structure

In the database our structure might look like this: blogpermissionsss

If structured this way, we have the opportunity to add new modules and permissions to our database and code during development. New Shops and CustomRoles can be created from the admin panel.

Custom security voter

In order to use our CustomRoles for Security Checking we need to create a custom security voter as described in the Symfony documentation.

When it’s ready our voter will be a powerful tool, it might look something like this. For better performance we added 5 minutes caching to database queries in the prod environment.

CustomRole and AppUser entities need to implement ShopInterface to use it as a second argument in our security checker voter.

Time to check

Now we can check our own permissions, like this:
as an annotation:

/**
 * @Security("is_granted('role_permission_address_edit', $customRole)")
 */
public function addressEditAction()
{
 // ...
}

in the method body:

if ($this->get('security.authorization_checker')->isGranted('role_permission_dashboard_view', $user)) {
 // ...
}

or in Twig:

{% if is_granted(‘shop_address’, shop) %}
 <p>Address: ...</p>
{% endif %}

Thanks for reading, hope this helps!