Cover of the book Symfony 5: The Fast Track

Symfony 5: The Fast Track is the best book to learn modern Symfony development, from zero to production. +300 pages showcasing Symfony with Docker, APIs, queues & async tasks, Webpack, SPAs, etc.

Security

3.x version
Maintained

Security

WARNING: You are browsing the documentation for EasyAdmin 3.x, which has just been released. Switch to EasyAdmin 2.x docs if your application has not been upgraded to EasyAdmin 3 yet.

EasyAdmin relies on Symfony Security for everything related to security. That’s why before restricting access to some parts of the backend, you need to properly setup security in your Symfony application:

  1. Create users in your application and assign them proper permissions (e.g. ROLE_ADMIN);
  2. Define a firewall that covers the URL of the backend.

Logged in User Information

When accessing a protected backend, EasyAdmin displays the details of the user who is logged in the application and a menu with some options like “logout”. Read the user menu reference for more details.

Restrict Access to the Entire Backend

Using the access_control option, you can tell Symfony to require certain permissions to browse the URL associated to the backend. This is simple to do because each dashboard only uses a single URL:

1
2
3
4
5
6
7
8
# config/packages/security.yaml
security:
    # ...

    access_control:
        # change '/admin' by the URL used by your Dashboard
        - { path: ^/admin, roles: ROLE_ADMIN }
        # ...

Another option is to add security annotations to the dashboard controller:

// app/Controller/Admin/DashboardController.php
use EasyCorp\Bundle\EasyAdminBundle\Config\Dashboard;
use EasyCorp\Bundle\EasyAdminBundle\Controller\AbstractDashboardController;
use Sensio\Bundle\FrameworkExtraBundle\Configuration\IsGranted;

/**
 * @IsGranted("ROLE_ADMIN")
 */
class DashboardController extends AbstractDashboardController
{
    // ...
}

Restrict Access to Menu Items

Use the setPermission() method to define the security permission that the user must have in order to see the menu item:

public function configureMenuItems(): iterable
{
    return [
        // ...

        MenuItem::linkToCrud('Blog Posts', null, BlogPost::class)
            ->setPermission('ROLE_EDITOR'),
    ];
}

If your needs are more advanced, remember that the dashboard class is a regular Symfony controller, so you can use any service related to security to evaluate complex expressions. In those cases, it’s more convenient to use the alternative menu item definition to not have to deal with array merges:

public function configureMenuItems(): iterable
{
    yield MenuItem::linkToDashboard('Dashboard', 'fa fa-home');

    if ($this->isGranted('ROLE_EDITOR') && '...') {
        yield MenuItem::linkToCrud('Blog Posts', null, BlogPost::class);
    }

    // ...
}

Restrict Access to Actions

Use the setPermission() method to define the security permission required to see the action link/button:

use EasyCorp\Bundle\EasyAdminBundle\Config\Action;
use EasyCorp\Bundle\EasyAdminBundle\Config\Actions;
use EasyCorp\Bundle\EasyAdminBundle\Config\Crud;

public function configureActions(Actions $actions): Actions
{
    $viewInvoice = Action::new('invoice', 'View invoice', 'fa fa-file-invoice')
        ->linkToCrudAction('renderInvoice');

    return $actions
        // ...
        ->add(Crud::PAGE_DETAIL, $viewInvoice)
        // use the 'setPermission()' method to set the permission of actions
        // (the same permission is granted to the action on all pages)
        ->setPermission('invoice', 'ROLE_FINANCE')

        // you can set permissions for built-in actions in the same way
        ->setPermission(Action::NEW, 'ROLE_ADMIN')
    ;
}

Restrict Access to Fields

There are several options to restrict the information displayed in the page depending on the logged in user. First, you can show/hide the entire field with the setPermission() method:

public function getFields(string $action): iterable
{
    return [
        IdField::new('id'),
        TextField::new('price'),
        IntegerField::new('stock'),
        // users must have this permission/role to see this field
        IntegerField::new('sales')->setPermission('ROLE_ADMIN'),
        FloatField::new('commission')->setPermission('ROLE_FINANCE'),
        // ...
    ];
}

You can also restrict which items users can see in the index and detail pages thanks to the setEntityPermission() method. This value is passed as the first argument of the call to is_granted($permissions, $item) function to decide if the current user can see the given item:

namespace App\Controller\Admin;

use EasyCorp\Bundle\EasyAdminBundle\Config\Crud;
use EasyCorp\Bundle\EasyAdminBundle\Controller\AbstractCrudController;

class ProductCrudController extends AbstractCrudController
{
    // ...

    public function configureCrud(Crud $crud): Crud
    {
        return $crud
            ->setEntityPermission('ROLE_ADMIN')
            // ...
        ;
    }
}

In the detail page, if the user doesn’t have permission they will see an appropriate error message (and you’ll see a detailed error message in the application logs).

In the index page, to avoid confusion and pagination errors, if the user doesn’t have permission to see some items, an empty row will be displayed at the bottom of the list with a message explaining that they don’t have enough permissions to see some items:

Index page with some results hidden because user does not have enough permissions

Custom Security Voters

EasyAdmin implements a Symfony security voter to check the permissions defined for actions, entities, menu items, etc. The actual security permissions are defined as constants in the EasyCorp\Bundle\EasyAdminBundle\Security\Permission class (e.g. Permission::EA_EXECUTE_ACTION, Permission::EA_VIEW_MENU_ITEM, etc.)

If you define a custom security voter for the backend, consider changing the access decision strategy used by your application. The default strategy, called affirmative, grants access as soon as one voter grants access (if EasyAdmin voter grants access, your custom voter won’t be able to deny it).

That’s why you should change the default strategy to unanimous, which grants access only if there are no voters denying access:

1
2
3
4
# config/packages/security.yaml
security:
    access_decision_manager:
        strategy: unanimous

This work, including the code samples, is licensed under a Creative Commons BY-SA 3.0 license.