Admin Bundle - Security implementation update

The SonataAdminBundle has updated the security implementation. It started by a few questions on the sonata mailing list on how to hide groups and admins from the menu and the dashboard. And how to delegate granting of permissions to other users. This was not possible yet and had to be developed. While developing it was found that the current ACL implementation was not matching the meanings of the permissions mentioned in the cookbook article on the Symfony website. While coding several other subjects have also been included.

Changes

  • changed ACL Security handler to match with the Symfony cookbook article;
  • added RoleSecurity handler to be backwards compatible with previous Acl implementation, this also provides a quick security option for admins;
  • added object ACLs;
  • updated default roles and permissions, they are also moved to the configuration and can be customized;
  • pass object to isGranted check in the controllers and templates;
  • for existing objects, generate the object ACLs using sonata:admin:generate-object-acl command (implemented for the Sonata);
  • the logic of the sonata:admin:setup-acl is moved to a service and is updated to detect updates and deletes of Admin ACLs;
  • SonataUserBundle:

    • split user form in editable roles and readonly roles
    • implement custom voter to deny access for normal users to super admin users

This blog post will explain the changes using an example. Given we have a blog application with a frontend and a backend. And we want to secure the backend application. The first step is to require admin users to login. Next step is to restrict the actions an admin user can do. The Sonata Admin has 2 options to help with this task.

The security configuration for both options have the following in common for the configuration in security.yml:

# app/config/security.yml
security:
    providers:
        fos_userbundle:
            id: fos_user.user_manager

    firewalls:
        main:
            pattern:      .*
            form-login:
                provider:       fos_userbundle
                login_path:     /login
                use_forward:    false
                check_path:     /login_check
                failure_path:   null
            logout:       true
            anonymous:    true

    access_control:
        # The WDT has to be allowed to anonymous users to avoid requiring the login with the AJAX request
        - { path: ^/wdt/, role: IS_AUTHENTICATED_ANONYMOUSLY }
        - { path: ^/profiler/, role: IS_AUTHENTICATED_ANONYMOUSLY }

        # AsseticBundle paths used when using the controller for assets
        - { path: ^/js/, role: IS_AUTHENTICATED_ANONYMOUSLY }
        - { path: ^/css/, role: IS_AUTHENTICATED_ANONYMOUSLY }

        # URL of FOSUserBundle which need to be available to anonymous users
        - { path: ^/login$, role: IS_AUTHENTICATED_ANONYMOUSLY }
        - { path: ^/login_check$, role: IS_AUTHENTICATED_ANONYMOUSLY } # for the case of a failed login
        - { path: ^/user/new$, role: IS_AUTHENTICATED_ANONYMOUSLY }
        - { path: ^/user/check-confirmation-email$, role: IS_AUTHENTICATED_ANONYMOUSLY }
        - { path: ^/user/confirm/, role: IS_AUTHENTICATED_ANONYMOUSLY }
        - { path: ^/user/confirmed$, role: IS_AUTHENTICATED_ANONYMOUSLY }
        - { path: ^/user/request-reset-password$, role: IS_AUTHENTICATED_ANONYMOUSLY }
        - { path: ^/user/send-resetting-email$, role: IS_AUTHENTICATED_ANONYMOUSLY }
        - { path: ^/user/check-resetting-email$, role: IS_AUTHENTICATED_ANONYMOUSLY }
        - { path: ^/user/reset-password/, role: IS_AUTHENTICATED_ANONYMOUSLY }

        # Secured part of the site
        # This config requires being logged for the whole site and having the admin role for the admin part.
        # Change these rules to adapt them to your needs
        - { path: ^/admin/, role: ROLE_ADMIN }
        - { path: ^/.*, role: IS_AUTHENTICATED_ANONYMOUSLY }

    role_hierarchy:
        ROLE_ADMIN:       [ROLE_USER, ROLE_SONATA_ADMIN]
        ROLE_SUPER_ADMIN: [ROLE_ADMIN, ROLE_ALLOWED_TO_SWITCH]

Role Security

Using a role security is the easiest option. By default the following roles are available for an Admin class:

  • ROLE_SONATA_..._VIEW: view an object;
  • ROLE_SONATA_..._EDIT: edit an object;
  • ROLE_SONATA_..._CREATE: create an object;
  • ROLE_SONATA_..._LIST: view a list of objects;
  • ROLE_SONATA_..._DELETE: delete an object;
  • ROLE_SONATA_..._OPERATOR: all access is granted;
  • ROLE_SONATA_..._MASTER: all access is granted and the user is allowed to assign the Admin class roles to other users.

Installation

  • Configure the role security handler:

Edit the sonata_admin.yml file :

sonata_admin:
    security:
        handler: sonata.admin.security.handler.role
  • Install the SonataUserBundle, this will add a list and form to manage users and groups:

    • a super admin can assign all roles to other users;
    • a user with the MASTER permission for an Admin class can assign the roles to other users.
  • An Admin is displayed in the dashboard (and menu) when the user has the role ROLE_SONATA_..._LIST.

ACL Security

A user can have several roles when working with an application. Each Admin class has several roles, and each role specifies the permissions of the user for the Admin class. Or more specific, what the user can do with the domain object(s) the Admin class is created for.

By default each Admin class contains the following roles:

  • ROLE_SONATA_..._GUEST: a guest that is allowed to view an object and a list of objects;
  • ROLE_SONATA_..._STAFF: probably the biggest part of the users, a staff user has the same permissions as guests and is additionally allowed to EDIT and CREATE new objects;
  • ROLE_SONATA_..._EDITOR: an editor is granted all access and, compared to the staff users, is allowed to DELETE;
  • ROLE_SONATA_..._ADMIN: an administrative user is granted all access and on top of that, the user is allowed to grant other users access.

Owner:

  • when an object is created, the currently logged in user is set as owner and is granted all access for that object;
  • this means the user owning the object is always allowed to DELETE the object, even when it only has the staff role.

Installation

  • configure the ACL security handler:

Edit the app/config/sonata_admin.yml file:

sonata_admin:
    security:
        handler: sonata.admin.security.handler.acl

Edit the app/config/security.yml file:

parameters:
    # ... other parameters
    security.acl.permission.map.class: Sonata\AdminBundle\Security\Acl\Permission\AdminPermissionMap

security:
    # ...
    acl:
        connection: default
  • Setup the Admin ACL rules, re-run this command when adding a new admin:

    $ php app/console sonata:admin:setup-acl

  • If you already have objects, you can generate the object ACL rules for each object of an admin:

    $ php app/console sonata:admin:generate-object-acl

Optionally, you can specify an object owner, and step through each admin. See the help of the command for more information.

  • Install the SonataUserBundle, this will add a list and form to manage users and groups:

    • a super admin can assign all roles to other users;
    • a user with the MASTER permission for an Admin class can assign the roles to other users;
    • a normal user is not able to view or edit a super admin user and will only see the super admin users in the list.
  • An Admin is displayed in the dashboard (and menu) when the user has the LIST permission, this is the case for a user with at least the role ROLE_SONATA_..._GUEST.

What's next

Although the updated implementation will give a nice basis, it can always be improved. When you have a case that is not supported, you are invited to create a pull request. The following subjects are known to be solved:

  1. Decouple the creation of object ACLs from the Admin class.

    The creation of object ACLs is done in the ACL security handler, and only works for objects created with the admin. This should be improved to also allow creating object ACLs for new objects created outside the Admin. An example is a new user created with the command fos:user:create, this user will not have an object ACL created and therefore access will be denied to edit the user.

  2. What would be the best place for custom ACL inheritance rules?

    We don't know the answer for this question. If you have a real use case and an idea, please create a pull request.

  3. Check the objects DELETE permission for the batch delete action.

    The Admin ACL includes the DELETE permission and is connected to users which have the EDITOR or ADMIN role. First step is that the controller checks the Admin ACL DELETE permission. The second step is to check the ACL for each object, this will generate a lot of ACL queries if the list is long. ACL records can be preloaded to solve this, however this second step is left for a new pull request. If you want to implement this, you can create a custom controller.

Comments

  • jaimesuez (Jan 24, 2012)
    This is really a great news!!! Specially "split user form in editable roles and readonly roles". I needed to make many hacks for implementing read_only access on symfony1 admin generator. Really thanks!
The comment form is closed for this current news.