771 lines
28 KiB
ReStructuredText
771 lines
28 KiB
ReStructuredText
Security
|
|
========
|
|
|
|
User management
|
|
---------------
|
|
|
|
By default, the SonataAdminBundle does not come with any user management,
|
|
however it is most likely the application requires such a feature. The Sonata
|
|
Project includes a ``SonataUserBundle`` which integrates the ``FOSUserBundle``.
|
|
|
|
The ``FOSUserBundle`` adds support for a database-backed user system in Symfony.
|
|
It provides a flexible framework for user management that aims to handle common
|
|
tasks such as user login, registration and password retrieval.
|
|
|
|
The ``SonataUserBundle`` is just a thin wrapper to include the ``FOSUserBundle``
|
|
into the ``AdminBundle``. The ``SonataUserBundle`` includes:
|
|
|
|
* A default login area
|
|
* A default ``user_block`` template which is used to display the current user
|
|
and the logout link
|
|
* 2 Admin classes: User and Group
|
|
* A default class for User and Group.
|
|
|
|
There is a little magic in the ``SonataAdminBundle``: if the bundle detects the
|
|
``SonataUserBundle`` class, then the default ``user_block`` template will be
|
|
changed to use the one provided by the ``SonataUserBundle``.
|
|
|
|
The install process is available on the dedicated
|
|
`SonataUserBundle's documentation area`_.
|
|
|
|
Security handlers
|
|
-----------------
|
|
|
|
The security part is managed by a ``SecurityHandler``, the bundle comes with 3 handlers:
|
|
|
|
- ``sonata.admin.security.handler.role``: ROLES to handle permissions
|
|
- ``sonata.admin.security.handler.acl``: ACL and ROLES to handle permissions
|
|
- ``sonata.admin.security.handler.noop``: always returns true, can be used
|
|
with the Symfony firewall
|
|
|
|
The default value is ``sonata.admin.security.handler.noop``, if you want to
|
|
change the default value you can set the ``security_handler`` to
|
|
``sonata.admin.security.handler.acl`` or ``sonata.admin.security.handler.role``.
|
|
|
|
To quickly secure an admin the role security can be used. It allows to specify
|
|
the actions a user can do with the admin. The ACL security system is more advanced
|
|
and allows to secure the objects. For people using the previous ACL
|
|
implementation, you can switch the ``security_handler`` to the role security handler.
|
|
|
|
Configuration
|
|
~~~~~~~~~~~~~
|
|
|
|
Only the security handler is required to determine which type of security to use.
|
|
The other parameters are set as default, change them if needed.
|
|
|
|
Using roles:
|
|
|
|
.. configuration-block::
|
|
|
|
.. code-block:: yaml
|
|
|
|
# app/config/config.yml
|
|
|
|
sonata_admin:
|
|
security:
|
|
handler: sonata.admin.security.handler.role
|
|
|
|
Using ACL:
|
|
|
|
.. configuration-block::
|
|
|
|
.. code-block:: yaml
|
|
|
|
# app/config/config.yml
|
|
|
|
sonata_admin:
|
|
security:
|
|
handler: sonata.admin.security.handler.acl
|
|
|
|
# acl security information
|
|
information:
|
|
GUEST: [VIEW, LIST]
|
|
STAFF: [EDIT, LIST, CREATE]
|
|
EDITOR: [OPERATOR, EXPORT]
|
|
ADMIN: [MASTER]
|
|
|
|
# permissions not related to an object instance and also to be available when objects do not exist
|
|
# the DELETE admin permission means the user is allowed to batch delete objects
|
|
admin_permissions: [CREATE, LIST, DELETE, UNDELETE, EXPORT, OPERATOR, MASTER]
|
|
|
|
# permission related to the objects
|
|
object_permissions: [VIEW, EDIT, DELETE, UNDELETE, OPERATOR, MASTER, OWNER]
|
|
|
|
Later, we will explain how to set up ACL with the ``FriendsOfSymfony/UserBundle``.
|
|
|
|
Role handler
|
|
------------
|
|
|
|
The ``sonata.admin.security.handler.role`` allows you to operate finely on the
|
|
actions that can be done (depending on the entity class), without requiring to set up ACL.
|
|
|
|
Configuration
|
|
~~~~~~~~~~~~~
|
|
|
|
First, activate the role security handler as described above.
|
|
|
|
Each time a user tries to do an action in the admin, Sonata checks if he is
|
|
either a super admin (``ROLE_SUPER_ADMIN``) **or** has the permission.
|
|
|
|
The permissions are:
|
|
|
|
========== ==================================================
|
|
Permission Description
|
|
========== ==================================================
|
|
LIST view the list of objects
|
|
VIEW view the detail of one object
|
|
CREATE create a new object
|
|
EDIT update an existing object
|
|
DELETE delete an existing object
|
|
EXPORT (for the native Sonata export links)
|
|
ALL grants LIST, VIEW, CREATE, EDIT, DELETE and EXPORT
|
|
========== ==================================================
|
|
|
|
Each permission is relative to an admin: if you try to get a list in FooAdmin (declared as ``app.admin.foo``
|
|
service), Sonata will check if the user has the ``ROLE_APP_ADMIN_FOO_EDIT`` or ``ROLE_APP_ADMIN_FOO_ALL`` roles.
|
|
|
|
The role name will be based on the name of your admin service. For instance, ``acme.blog.post.admin`` will become ``ROLE_ACME_BLOG_POST_ADMIN_{ACTION}``.
|
|
|
|
.. note::
|
|
|
|
If your admin service is named like ``my.blog.admin.foo_bar`` (note the underscore ``_``) it will become: ``ROLE_MY_BLOG_ADMIN_FOO_BAR_{ACTION}``
|
|
|
|
So our ``security.yml`` file may look something like this:
|
|
|
|
.. configuration-block::
|
|
|
|
.. code-block:: yaml
|
|
|
|
# app/config/security.yml
|
|
|
|
security:
|
|
# ...
|
|
role_hierarchy:
|
|
|
|
# for convenience, I decided to gather Sonata roles here
|
|
ROLE_SONATA_FOO_READER:
|
|
- ROLE_SONATA_ADMIN_DEMO_FOO_LIST
|
|
- ROLE_SONATA_ADMIN_DEMO_FOO_VIEW
|
|
ROLE_SONATA_FOO_EDITOR:
|
|
- ROLE_SONATA_ADMIN_DEMO_FOO_CREATE
|
|
- ROLE_SONATA_ADMIN_DEMO_FOO_EDIT
|
|
ROLE_SONATA_FOO_ADMIN:
|
|
- ROLE_SONATA_ADMIN_DEMO_FOO_DELETE
|
|
- ROLE_SONATA_ADMIN_DEMO_FOO_EXPORT
|
|
|
|
# those are the roles I will use (less verbose)
|
|
ROLE_STAFF: [ROLE_USER, ROLE_SONATA_FOO_READER]
|
|
ROLE_ADMIN: [ROLE_STAFF, ROLE_SONATA_FOO_EDITOR, ROLE_SONATA_FOO_ADMIN]
|
|
ROLE_SUPER_ADMIN: [ROLE_ADMIN, ROLE_ALLOWED_TO_SWITCH]
|
|
|
|
# you could alternatively use for an admin who has all rights
|
|
ROLE_ALL_ADMIN: [ROLE_STAFF, ROLE_SONATA_FOO_ALL]
|
|
|
|
# set access_strategy to unanimous, else you may have unexpected behaviors
|
|
access_decision_manager:
|
|
strategy: unanimous
|
|
|
|
Note that we also set ``access_strategy`` to unanimous.
|
|
It means that if one voter (for example Sonata) refuses access, access will be denied.
|
|
For more information on this subject, please see `changing the access decision strategy`_
|
|
in the Symfony documentation.
|
|
|
|
Usage
|
|
~~~~~
|
|
|
|
You can now test if a user is authorized from an Admin class:
|
|
|
|
.. code-block:: php
|
|
|
|
if ($this->hasAccess('list')) {
|
|
// ...
|
|
}
|
|
|
|
From a controller extending ``Sonata\AdminBundle\Controller\CRUDController``:
|
|
|
|
.. code-block:: php
|
|
|
|
if ($this->admin->hasAccess('list')) {
|
|
// ...
|
|
}
|
|
|
|
Or from a Twig template:
|
|
|
|
.. code-block:: jinja
|
|
|
|
{% if is_granted('VIEW') %}
|
|
<p>Hello there!</p>
|
|
{% endif %}
|
|
|
|
Note that you do not have to re-specify the prefix.
|
|
|
|
Sonata checks those permissions for the action it handles internally.
|
|
Of course you will have to recheck them in your own code.
|
|
|
|
Yon can also create your own permissions, for example ``EMAIL``
|
|
(which will turn into role ``ROLE_APP_ADMIN_FOO_EMAIL``).
|
|
|
|
Going further
|
|
~~~~~~~~~~~~~
|
|
|
|
Because Sonata role handler supplements Symfony security, but does not override it, you are free to do more advanced operations.
|
|
For example, you can `create your own voter`_
|
|
|
|
Customizing the handler behavior
|
|
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
|
|
|
|
If you want to change the handler behavior (for example, to pass the current object to voters), extend
|
|
``Sonata\AdminBundle\Security\Handler\RoleSecurityHandler``, and override the ``isGranted`` method.
|
|
|
|
Then declare your handler as a service:
|
|
|
|
.. configuration-block::
|
|
|
|
.. code-block:: xml
|
|
|
|
<service id="app.security.handler.role" class="AppBundle\Security\Handler\RoleSecurityHandler" public="false">
|
|
<argument type="service" id="security.context" on-invalid="null" />
|
|
<argument type="collection">
|
|
<argument>ROLE_SUPER_ADMIN</argument>
|
|
</argument>
|
|
</service>
|
|
|
|
And specify it as Sonata security handler on your configuration:
|
|
|
|
.. configuration-block::
|
|
|
|
.. code-block:: yaml
|
|
|
|
# app/config/config.yml
|
|
|
|
sonata_admin:
|
|
security:
|
|
handler: app.security.handler.role
|
|
|
|
ACL and FriendsOfSymfony/UserBundle
|
|
-----------------------------------
|
|
|
|
If you want an easy way to handle users, please use:
|
|
|
|
- `FOSUserBundle <https://github.com/FriendsOfSymfony/FOSUserBundle>`_: handles
|
|
users and groups stored in RDBMS or MongoDB
|
|
- `SonataUserBundle <https://github.com/sonata-project/SonataUserBundle>`_: integrates the
|
|
``FriendsOfSymfony/UserBundle`` with the ``AdminBundle``
|
|
|
|
The security integration is a work in progress and has some known issues:
|
|
|
|
- ACL permissions are immutables
|
|
- A listener must be implemented that creates the object Access Control List
|
|
with the required rules if objects are created outside the Admin
|
|
|
|
Configuration
|
|
~~~~~~~~~~~~~
|
|
|
|
Before you can use ``FriendsOfSymfony/FOSUserBundle`` you need to set it up as
|
|
described in the documentation of the bundle. In step 4 you need to create a
|
|
User class (in a custom UserBundle). Do it as follows:
|
|
|
|
.. code-block:: php
|
|
|
|
<?php
|
|
// src/AppBundle/Entity/User.php
|
|
|
|
namespace AppBundle\Entity;
|
|
|
|
use Sonata\UserBundle\Entity\BaseUser as BaseUser;
|
|
use Doctrine\ORM\Mapping as ORM;
|
|
|
|
/**
|
|
* @ORM\Entity
|
|
* @ORM\Table(name="fos_user")
|
|
*/
|
|
class User extends BaseUser
|
|
{
|
|
/**
|
|
* @ORM\Id
|
|
* @ORM\Column(type="integer")
|
|
* @ORM\GeneratedValue(strategy="AUTO")
|
|
*/
|
|
protected $id;
|
|
|
|
public function __construct()
|
|
{
|
|
parent::__construct();
|
|
// your own logic
|
|
}
|
|
}
|
|
|
|
In your ``app/config/config.yml`` you then need to put the following:
|
|
|
|
.. configuration-block::
|
|
|
|
.. code-block:: yaml
|
|
|
|
# app/config/config.yml
|
|
|
|
fos_user:
|
|
db_driver: orm
|
|
firewall_name: main
|
|
user_class: AppBundle\Entity\User
|
|
|
|
The following configuration for the SonataUserBundle defines:
|
|
|
|
- the ``FriendsOfSymfony/FOSUserBundle`` as a security provider
|
|
- the login form for authentication
|
|
- the access control: resources with related required roles, the important
|
|
part is the admin configuration
|
|
- the ``acl`` option to enable the ACL
|
|
- the ``AdminPermissionMap`` defines the permissions of the Admin class
|
|
|
|
.. configuration-block::
|
|
|
|
.. code-block:: yaml
|
|
|
|
# src/AppBundle/Resources/config/services.yml
|
|
|
|
# Symfony >= 3
|
|
services:
|
|
security.acl.permission.map:
|
|
class: Sonata\AdminBundle\Security\Acl\Permission\AdminPermissionMap
|
|
|
|
# optionally use a custom MaskBuilder
|
|
#sonata.admin.security.mask.builder:
|
|
# class: Sonata\AdminBundle\Security\Acl\Permission\MaskBuilder
|
|
|
|
# Symfony < 3
|
|
parameters:
|
|
security.acl.permission.map.class: Sonata\AdminBundle\Security\Acl\Permission\AdminPermissionMap
|
|
|
|
# optionally use a custom MaskBuilder
|
|
#sonata.admin.security.mask.builder.class: Sonata\AdminBundle\Security\Acl\Permission\MaskBuilder
|
|
|
|
In ``app/config/security.yml``:
|
|
|
|
.. configuration-block::
|
|
|
|
.. code-block:: yaml
|
|
|
|
# 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]
|
|
|
|
acl:
|
|
connection: default
|
|
|
|
- Install the ACL tables ``php app/console init:acl``
|
|
|
|
- Create a new root user:
|
|
|
|
.. code-block:: bash
|
|
|
|
$ php app/console fos:user:create --super-admin
|
|
Please choose a username:root
|
|
Please choose an email:root@domain.com
|
|
Please choose a password:root
|
|
Created user root
|
|
|
|
If you have Admin classes, you can install or update the related CRUD ACL rules:
|
|
|
|
.. code-block:: bash
|
|
|
|
$ php app/console sonata:admin:setup-acl
|
|
Starting ACL AdminBundle configuration
|
|
> install ACL for sonata.media.admin.media
|
|
- add role: ROLE_SONATA_MEDIA_ADMIN_MEDIA_GUEST, permissions: ["VIEW","LIST"]
|
|
- add role: ROLE_SONATA_MEDIA_ADMIN_MEDIA_STAFF, permissions: ["EDIT","LIST","CREATE"]
|
|
- add role: ROLE_SONATA_MEDIA_ADMIN_MEDIA_EDITOR, permissions: ["OPERATOR","EXPORT"]
|
|
- add role: ROLE_SONATA_MEDIA_ADMIN_MEDIA_ADMIN, permissions: ["MASTER"]
|
|
... skipped ...
|
|
|
|
If you already have objects, you can generate the object ACL rules for each
|
|
object of an admin:
|
|
|
|
.. code-block:: bash
|
|
|
|
$ 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.
|
|
|
|
If you try to access to the admin class you should see the login form, just
|
|
log in with the ``root`` user.
|
|
|
|
An Admin is displayed in the dashboard (and menu) when the user has the role
|
|
``LIST``. To change this override the ``showIn`` method in the Admin class.
|
|
|
|
Roles and Access control lists
|
|
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
|
|
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 specifically, 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, override the
|
|
property ``$securityInformation`` to change this:
|
|
|
|
- ``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 for
|
|
that object and is granted all access for that object;
|
|
- this means the user owning the object is always allowed to ``DELETE`` the
|
|
object, even when they only have the staff role.
|
|
|
|
Vocabulary used for Access Control Lists:
|
|
|
|
- **Role:** a user role;
|
|
- **ACL:** a list of access rules, the Admin uses 2 types;
|
|
- **Admin ACL:** created from the Security information of the Admin class
|
|
for each admin and shares the Access Control Entries that specify what
|
|
the user can do (permissions) with the admin;
|
|
- **Object ACL:** also created from the security information of the ``Admin``
|
|
class however created for each object, it uses 2 scopes:
|
|
|
|
- **Class-Scope:** the class scope contains the rules that are valid
|
|
for all object of a certain class;
|
|
- **Object-Scope:** specifies the owner;
|
|
- **Sid:** Security identity, an ACL role for the Class-Scope ACL and the
|
|
user for the Object-Scope ACL;
|
|
- **Oid:** Object identity, identifies the ACL, for the admin ACL this is
|
|
the admin code, for the object ACL this is the object id;
|
|
- **ACE:** a role (or sid) and its permissions;
|
|
- **Permission:** this tells what the user is allowed to do with the Object
|
|
identity;
|
|
- **Bitmask:** a permission can have several bitmasks, each bitmask
|
|
represents a permission. When permission ``VIEW`` is requested and it
|
|
contains the ``VIEW`` and ``EDIT`` bitmask and the user only has the
|
|
``EDIT`` permission, then the permission ``VIEW`` is granted.
|
|
- **PermissionMap:** configures the bitmasks for each permission, to change
|
|
the default mapping create a voter for the domain class of the Admin.
|
|
|
|
There can be many voters that may have different permission maps. However,
|
|
prevent that multiple voters vote on the same class with overlapping bitmasks.
|
|
|
|
See the cookbook article "`Advanced ACL concepts
|
|
<http://symfony.com/doc/current/cookbook/security/acl_advanced.html#pre-authorization-decisions.>`_"
|
|
for the meaning of the different permissions.
|
|
|
|
How is access granted?
|
|
~~~~~~~~~~~~~~~~~~~~~~
|
|
|
|
In the application the security context is asked if access is granted for a role
|
|
or a permission (``admin.isGranted``):
|
|
|
|
- **Token:** a token identifies a user between requests;
|
|
- **Voter:** sort of judge that returns whether access is granted or denied, if the
|
|
voter should not vote for a case, it returns abstain;
|
|
- **AccessDecisionManager:** decides whether access is granted or denied according
|
|
a specific strategy. It grants access if at least one (affirmative strategy),
|
|
all (unanimous strategy) or more then half (consensus strategy) of the
|
|
counted votes granted access;
|
|
- **RoleVoter:** votes for all attributes stating with ``ROLE_`` and grants
|
|
access if the user has this role;
|
|
- **RoleHierarchyVoter:** when the role ``ROLE_SONATA_ADMIN`` is voted for,
|
|
it also votes "granted" if the user has the role ``ROLE_SUPER_ADMIN``;
|
|
- **AclVoter:** grants access for the permissions of the ``Admin`` class if
|
|
the user has the permission, the user has a permission that is included in
|
|
the bitmasks of the permission requested to vote for or the user owns the
|
|
object.
|
|
|
|
Create a custom voter or a custom permission map
|
|
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
|
|
|
|
In some occasions you need to create a custom voter or a custom permission map
|
|
because for example you want to restrict access using extra rules:
|
|
|
|
- create a custom voter class that extends the ``AclVoter``
|
|
|
|
.. code-block:: php
|
|
|
|
<?php
|
|
// src/AppBundle/Security/Authorization/Voter/UserAclVoter.php
|
|
|
|
namespace AppBundle\Security\Authorization\Voter;
|
|
|
|
use FOS\UserBundle\Model\UserInterface;
|
|
use Symfony\Component\Security\Core\Authentication\Token\TokenInterface;
|
|
use Symfony\Component\Security\Acl\Voter\AclVoter;
|
|
|
|
class UserAclVoter extends AclVoter
|
|
{
|
|
public function supportsClass($class)
|
|
{
|
|
// support the Class-Scope ACL for votes with the custom permission map
|
|
// return $class === 'Sonata\UserBundle\Admin\Entity\UserAdmin' || is_subclass_of($class, 'FOS\UserBundle\Model\UserInterface');
|
|
// if you use php >=5.3.7 you can check the inheritance with is_a($class, 'Sonata\UserBundle\Admin\Entity\UserAdmin');
|
|
// support the Object-Scope ACL
|
|
return is_subclass_of($class, 'FOS\UserBundle\Model\UserInterface');
|
|
}
|
|
|
|
public function supportsAttribute($attribute)
|
|
{
|
|
return $attribute === 'EDIT' || $attribute === 'DELETE';
|
|
}
|
|
|
|
public function vote(TokenInterface $token, $object, array $attributes)
|
|
{
|
|
if (!$this->supportsClass(get_class($object))) {
|
|
return self::ACCESS_ABSTAIN;
|
|
}
|
|
|
|
foreach ($attributes as $attribute) {
|
|
if ($this->supportsAttribute($attribute) && $object instanceof UserInterface) {
|
|
if ($object->isSuperAdmin() && !$token->getUser()->isSuperAdmin()) {
|
|
// deny a non super admin user to edit a super admin user
|
|
return self::ACCESS_DENIED;
|
|
}
|
|
}
|
|
}
|
|
|
|
// use the parent vote with the custom permission map:
|
|
// return parent::vote($token, $object, $attributes);
|
|
// otherwise leave the permission voting to the AclVoter that is using the default permission map
|
|
return self::ACCESS_ABSTAIN;
|
|
}
|
|
}
|
|
|
|
- optionally create a custom permission map, copy to start the
|
|
``Sonata\AdminBundle\Security\Acl\Permission\AdminPermissionMap.php`` to
|
|
your bundle
|
|
|
|
- declare the voter and permission map as a service
|
|
|
|
.. configuration-block::
|
|
|
|
.. code-block:: xml
|
|
|
|
<!-- src/AppBundle/Resources/config/services.xml -->
|
|
|
|
<!-- <service id="security.acl.user_permission.map" class="AppBundle\Security\Acl\Permission\UserAdminPermissionMap" public="false"></service> -->
|
|
|
|
<service id="security.acl.voter.user_permissions" class="AppBundle\Security\Authorization\Voter\UserAclVoter" public="false">
|
|
<tag name="monolog.logger" channel="security" />
|
|
<argument type="service" id="security.acl.provider" />
|
|
<argument type="service" id="security.acl.object_identity_retrieval_strategy" />
|
|
<argument type="service" id="security.acl.security_identity_retrieval_strategy" />
|
|
<argument type="service" id="security.acl.permission.map" />
|
|
<argument type="service" id="logger" on-invalid="null" />
|
|
<tag name="security.voter" priority="255" />
|
|
</service>
|
|
|
|
- change the access decision strategy to ``unanimous``
|
|
|
|
.. configuration-block::
|
|
|
|
.. code-block:: yaml
|
|
|
|
# app/config/security.yml
|
|
|
|
security:
|
|
access_decision_manager:
|
|
|
|
# strategy value can be: affirmative, unanimous or consensus
|
|
strategy: unanimous
|
|
|
|
- to make this work the permission needs to be checked using the Object ACL
|
|
|
|
- modify the template (or code) where applicable:
|
|
|
|
.. code-block:: html+jinja
|
|
|
|
{% if admin.hasAccess('edit', user_object) %}
|
|
{# ... #}
|
|
{% endif %}
|
|
|
|
- because the object ACL permission is checked, the ACL for the object must
|
|
have been created, otherwise the ``AclVoter`` will deny ``EDIT`` access
|
|
for a non super admin user trying to edit another non super admin user.
|
|
This is automatically done when the object is created using the Admin.
|
|
If objects are also created outside the Admin, have a look at the
|
|
``createSecurityObject`` method in the ``AclSecurityHandler``.
|
|
|
|
Usage
|
|
~~~~~
|
|
|
|
Every time you create a new ``Admin`` class, you should start with the command
|
|
``php app/console sonata:admin:setup-acl`` so the ACL database will be updated
|
|
with the latest roles and permissions.
|
|
|
|
In the templates, or in your code, you can use the Admin method ``hasAccess()``:
|
|
|
|
- check for an admin that the user is allowed to ``EDIT``:
|
|
|
|
.. code-block:: html+jinja
|
|
|
|
{# use the admin security method #}
|
|
{% if admin.hasAccess('edit') %}
|
|
{# ... #}
|
|
{% endif %}
|
|
|
|
{# or use the default is_granted Symfony helper, the following will give the same result #}
|
|
{% if is_granted('ROLE_SUPER_ADMIN') or is_granted('EDIT', admin) %}
|
|
{# ... #}
|
|
{% endif %}
|
|
|
|
- check for an admin that the user is allowed to ``DELETE``, the object is added
|
|
to also check if the object owner is allowed to ``DELETE``:
|
|
|
|
.. code-block:: html+jinja
|
|
|
|
{# use the admin security method #}
|
|
{% if admin.hasAccess('delete', object) %}
|
|
{# ... #}
|
|
{% endif %}
|
|
|
|
{# or use the default is_granted Symfony helper, the following will give the same result #}
|
|
{% if is_granted('ROLE_SUPER_ADMIN') or is_granted('DELETE', object) %}
|
|
{# ... #}
|
|
{% endif %}
|
|
|
|
List filtering
|
|
~~~~~~~~~~~~~~
|
|
|
|
List filtering using ACL is available as a third party bundle:
|
|
`CoopTilleulsAclSonataAdminExtensionBundle <https://github.com/coopTilleuls/CoopTilleulsAclSonataAdminExtensionBundle>`_.
|
|
When enabled, the logged in user will only see the objects for which it has the `VIEW` right (or superior).
|
|
|
|
ACL editor
|
|
----------
|
|
|
|
SonataAdminBundle provides a user-friendly ACL editor
|
|
interface.
|
|
It will be automatically available if the ``sonata.admin.security.handler.acl``
|
|
security handler is used and properly configured.
|
|
|
|
The ACL editor is only available for users with `OWNER` or `MASTER` permissions
|
|
on the object instance.
|
|
The `OWNER` and `MASTER` permissions can only be edited by an user with the
|
|
`OWNER` permission on the object instance.
|
|
|
|
.. figure:: ../images/acl_editor.png
|
|
:align: center
|
|
:alt: The ACL editor
|
|
:width: 700px
|
|
|
|
User list customization
|
|
~~~~~~~~~~~~~~~~~~~~~~~
|
|
|
|
By default, the ACL editor allows to set permissions for all users managed by
|
|
``FOSUserBundle``.
|
|
|
|
To customize displayed user override
|
|
``Sonata\AdminBundle\Controller\CRUDController::getAclUsers()``. This method must
|
|
return an iterable collection of users.
|
|
|
|
.. code-block:: php
|
|
|
|
protected function getAclUsers()
|
|
{
|
|
$userManager = $container->get('fos_user.user_manager');
|
|
|
|
// Display only kevin and anne
|
|
$users[] = $userManager->findUserByUsername('kevin');
|
|
$users[] = $userManager->findUserByUsername('anne');
|
|
|
|
return new \ArrayIterator($users);
|
|
}
|
|
|
|
Role list customization
|
|
~~~~~~~~~~~~~~~~~~~~~~~
|
|
|
|
By default, the ACL editor allows to set permissions for all roles.
|
|
|
|
To customize displayed role override
|
|
``Sonata\AdminBundle\Controller\CRUDController::getAclRoles()``. This method must
|
|
return an iterable collection of roles.
|
|
|
|
.. code-block:: php
|
|
|
|
protected function getAclRoles()
|
|
{
|
|
// Display only ROLE_BAPTISTE and ROLE_HELENE
|
|
$roles = array(
|
|
'ROLE_BAPTISTE',
|
|
'ROLE_HELENE'
|
|
);
|
|
|
|
return new \ArrayIterator($roles);
|
|
}
|
|
|
|
Custom user manager
|
|
~~~~~~~~~~~~~~~~~~~
|
|
|
|
If your project does not use `FOSUserBundle`, you can globally configure another
|
|
service to use when retrieving your users.
|
|
|
|
- Create a service with a method called ``findUsers()`` returning an iterable
|
|
collection of users
|
|
- Update your admin configuration to reference your service name
|
|
|
|
.. configuration-block::
|
|
|
|
.. code-block:: yaml
|
|
|
|
# app/config/config.yml
|
|
|
|
sonata_admin:
|
|
security:
|
|
|
|
# the name of your service
|
|
acl_user_manager: my_user_manager
|
|
|
|
.. _`SonataUserBundle's documentation area`: https://sonata-project.org/bundles/user/master/doc/reference/installation.html
|
|
.. _`changing the access decision strategy`: http://symfony.com/doc/2.2/cookbook/security/voters.html#changing-the-access-decision-strategy
|
|
.. _`create your own voter`: http://symfony.com/doc/2.2/cookbook/security/voters.html
|