security.rst 19 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443
  1. Security
  2. ========
  3. Users management
  4. ----------------
  5. By default, the SonataAdminBundle does not come with any user management, however it is most likely the application
  6. requires such feature. The Sonata Project includes a ``SonataUserBundle`` which integrates the ``FOSUserBundle``.
  7. The ``FOSUserBundle`` adds support for a database-backed user system in Symfony2. It provides a flexible framework
  8. for user management that aims to handle common tasks such as user login, registration and password retrieval.
  9. The ``SonataUserBundle`` is just a thin wrapper to include the ``FOSUserBundle`` into the ``AdminBundle``. The
  10. ``SonataUserBundle`` includes :
  11. * A default login area
  12. * A default ``user_block`` template which is used to display the current user and the logout link
  13. * 2 Admin classes : User and Group
  14. * A default class for User and Group.
  15. There is a little magic in the ``SonataAdminBundle`` if the bundle detects the ``SonataUserBundle`` class, then
  16. the default ``user_block`` template will be changed to use the one provided by the ``SonataUserBundle``.
  17. The install process is available on the dedicated `SonataUserBundle's documentation area <http://sonata-project.org/bundles/user/master/doc/reference/installation.html>`_
  18. Security handlers
  19. -----------------
  20. The security part is managed by a ``SecurityHandler``, the bundle comes with 3 handlers
  21. - ``sonata.admin.security.handler.role`` : ROLES to handle permissions
  22. - ``sonata.admin.security.handler.acl`` : ACL and ROLES to handle permissions
  23. - ``sonata.admin.security.handler.noop`` : always returns true, can be used with the Symfony2 firewall
  24. The default value is ``sonata.admin.security.handler.noop``, if you want to change the default value
  25. you can set the ``security_handler`` to ``sonata.admin.security.handler.acl`` or ``sonata.admin.security.handler.role``.
  26. To quickly secure an admin the role security can be used. It allows to specify the actions a user can with the admin. The ACL
  27. security system is more advanced and allows to secure the objects. For people using the previous ACL implementation,
  28. you can switch the security_handler to the role security handler.
  29. Configuration
  30. ~~~~~~~~~~~~~
  31. Only the security handler is required to determine which type of security to use. The other parameters are set as default,
  32. change them if needed.
  33. Using roles:
  34. .. code-block:: yaml
  35. sonata_admin:
  36. security:
  37. handler: sonata.admin.security.handler.role
  38. # role security information
  39. information:
  40. EDIT: EDIT
  41. LIST: LIST
  42. CREATE: CREATE
  43. VIEW: VIEW
  44. DELETE: DELETE
  45. EXPORT: EXPORT
  46. OPERATOR: OPERATOR
  47. MASTER: MASTER
  48. Using ACL:
  49. .. code-block:: yaml
  50. # app/config/config.yml
  51. sonata_admin:
  52. security:
  53. handler: sonata.admin.security.handler.acl
  54. # acl security information
  55. information:
  56. GUEST: [VIEW, LIST]
  57. STAFF: [EDIT, LIST, CREATE]
  58. EDITOR: [OPERATOR, EXPORT]
  59. ADMIN: [MASTER]
  60. # permissions not related to an object instance and also to be available when objects do not exist
  61. # the DELETE admin permission means the user is allowed to batch delete objects
  62. admin_permissions: [CREATE, LIST, DELETE, UNDELETE, EXPORT, OPERATOR, MASTER]
  63. # permission related to the objects
  64. object_permissions: [VIEW, EDIT, DELETE, UNDELETE, OPERATOR, MASTER, OWNER]
  65. The following section explains how to set up ACL with the ``FriendsOfSymfony/UserBundle``.
  66. ACL and FriendsOfSymfony/UserBundle
  67. -----------------------------------
  68. If you want an easy way to handle users, please use :
  69. - https://github.com/FriendsOfSymfony/FOSUserBundle : handle users and groups stored in RDMS or MongoDB
  70. - https://github.com/sonata-project/SonataUserBundle : integrates the ``FriendsOfSymfony/UserBundle`` with
  71. the ``AdminBundle``
  72. The security integration is a work in progress and has some known issues :
  73. - ACL permissions are immutables
  74. - A listener must be implemented that creates the object Access Control List with the required rules if objects are
  75. created outside the Admin
  76. Configuration
  77. ~~~~~~~~~~~~~
  78. Before you can use ``FriendsOfSymfony/FOSUserBundle`` you need to set it up as described in the documentation
  79. of the bundle. In step 4 you need to create a User class (in a custom UserBundle). Do it as follows:
  80. .. code-block:: php
  81. <?php
  82. namespace Acme\UserBundle\Entity;
  83. use Sonata\UserBundle\Entity\BaseUser as BaseUser;
  84. use Doctrine\ORM\Mapping as ORM;
  85. /**
  86. * @ORM\Entity
  87. * @ORM\Table(name="fos_user")
  88. \*/
  89. class User extends BaseUser
  90. {
  91. /**
  92. * @ORM\Id
  93. * @ORM\Column(type="integer")
  94. * @ORM\GeneratedValue(strategy="AUTO")
  95. \*/
  96. protected $id;
  97. public function __construct()
  98. {
  99. parent::__construct();
  100. // your own logic
  101. }
  102. }
  103. In your ``app/config/config.yml`` you then need to put the following:
  104. .. code-block:: yaml
  105. fos_user:
  106. db_driver: orm
  107. firewall_name: main
  108. user_class: Acme\UserBundle\Entity\User
  109. The following configuration for the SonataUserBundle defines:
  110. - the ``FriendsOfSymfony/FOSUserBundle`` as a security provider
  111. - the login form for authentification
  112. - the access control : resources with related required roles, the important part is the admin configuration
  113. - the ``acl`` option to enable the ACL.
  114. - the ``AdminPermissionMap`` defines the permissions of the Admin class
  115. .. code-block:: yaml
  116. # app/config/security.yml
  117. parameters:
  118. # ... other parameters
  119. security.acl.permission.map.class: Sonata\AdminBundle\Security\Acl\Permission\AdminPermissionMap
  120. # optionally use a custom MaskBuilder
  121. #sonata.admin.security.mask.builder.class: Sonata\AdminBundle\Security\Acl\Permission\MaskBuilder
  122. In ``app/config/security.yml``:
  123. .. code-block:: yaml
  124. security:
  125. providers:
  126. fos_userbundle:
  127. id: fos_user.user_manager
  128. firewalls:
  129. main:
  130. pattern: .*
  131. form-login:
  132. provider: fos_userbundle
  133. login_path: /login
  134. use_forward: false
  135. check_path: /login_check
  136. failure_path: null
  137. logout: true
  138. anonymous: true
  139. access_control:
  140. # The WDT has to be allowed to anonymous users to avoid requiring the login with the AJAX request
  141. - { path: ^/wdt/, role: IS_AUTHENTICATED_ANONYMOUSLY }
  142. - { path: ^/profiler/, role: IS_AUTHENTICATED_ANONYMOUSLY }
  143. # AsseticBundle paths used when using the controller for assets
  144. - { path: ^/js/, role: IS_AUTHENTICATED_ANONYMOUSLY }
  145. - { path: ^/css/, role: IS_AUTHENTICATED_ANONYMOUSLY }
  146. # URL of FOSUserBundle which need to be available to anonymous users
  147. - { path: ^/login$, role: IS_AUTHENTICATED_ANONYMOUSLY }
  148. - { path: ^/login_check$, role: IS_AUTHENTICATED_ANONYMOUSLY } # for the case of a failed login
  149. - { path: ^/user/new$, role: IS_AUTHENTICATED_ANONYMOUSLY }
  150. - { path: ^/user/check-confirmation-email$, role: IS_AUTHENTICATED_ANONYMOUSLY }
  151. - { path: ^/user/confirm/, role: IS_AUTHENTICATED_ANONYMOUSLY }
  152. - { path: ^/user/confirmed$, role: IS_AUTHENTICATED_ANONYMOUSLY }
  153. - { path: ^/user/request-reset-password$, role: IS_AUTHENTICATED_ANONYMOUSLY }
  154. - { path: ^/user/send-resetting-email$, role: IS_AUTHENTICATED_ANONYMOUSLY }
  155. - { path: ^/user/check-resetting-email$, role: IS_AUTHENTICATED_ANONYMOUSLY }
  156. - { path: ^/user/reset-password/, role: IS_AUTHENTICATED_ANONYMOUSLY }
  157. # Secured part of the site
  158. # This config requires being logged for the whole site and having the admin role for the admin part.
  159. # Change these rules to adapt them to your needs
  160. - { path: ^/admin/, role: ROLE_ADMIN }
  161. - { path: ^/.*, role: IS_AUTHENTICATED_ANONYMOUSLY }
  162. role_hierarchy:
  163. ROLE_ADMIN: [ROLE_USER, ROLE_SONATA_ADMIN]
  164. ROLE_SUPER_ADMIN: [ROLE_ADMIN, ROLE_ALLOWED_TO_SWITCH]
  165. acl:
  166. connection: default
  167. - Install the ACL tables ``php app/console init:acl``
  168. - Create a new root user :
  169. .. code-block:: sh
  170. # php app/console fos:user:create --super-admin
  171. Please choose a username:root
  172. Please choose an email:root@domain.com
  173. Please choose a password:root
  174. Created user root
  175. If you have Admin classes, you can install or update the related CRUD ACL rules :
  176. .. code-block:: sh
  177. # php app/console sonata:admin:setup-acl
  178. Starting ACL AdminBundle configuration
  179. > install ACL for sonata.media.admin.media
  180. - add role: ROLE_SONATA_MEDIA_ADMIN_MEDIA_GUEST, permissions: ["VIEW","LIST"]
  181. - add role: ROLE_SONATA_MEDIA_ADMIN_MEDIA_STAFF, permissions: ["EDIT","LIST","CREATE"]
  182. - add role: ROLE_SONATA_MEDIA_ADMIN_MEDIA_EDITOR, permissions: ["OPERATOR","EXPORT"]
  183. - add role: ROLE_SONATA_MEDIA_ADMIN_MEDIA_ADMIN, permissions: ["MASTER"]
  184. ... skipped ...
  185. If you already have objects, you can generate the object ACL rules for each object of an admin:
  186. .. code-block:: sh
  187. $ php app/console sonata:admin:generate-object-acl
  188. Optionally, you can specify an object owner, and step through each admin. See the help of the command for more information.
  189. If you try to access to the admin class you should see the login form, just logon with the ``root`` user.
  190. An Admin is displayed in the dashboard (and menu) when the user has the role ``LIST``. To change this override the ``showIn``
  191. method in the Admin class.
  192. Roles and Access control lists
  193. ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
  194. A user can have several roles when working with an application. Each Admin class has several roles, and each role specifies the permissions
  195. 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.
  196. By default each Admin class contains the following roles, override the property ``$securityInformation`` to change this:
  197. - ROLE_SONATA_..._GUEST: a guest that is allowed to view an object and a list of objects;
  198. - ROLE_SONATA_..._STAFF: probably the biggest part of the users, a staff user has the same permissions as guests and is additionally
  199. allowed to EDIT and CREATE new objects;
  200. - ROLE_SONATA_..._EDITOR: an editor is granted all access and, compared to the staff users, is allowed to DELETE and EXPORT;
  201. - ROLE_SONATA_..._ADMIN: an administrative user is granted all access and on top of that, the user is allowed to grant other users access.
  202. Owner:
  203. - 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;
  204. - this means the user owning the object is always allowed to DELETE the object, even when it only has the staff role.
  205. Vocabulary used for Access Control Lists:
  206. - Role: a user role;
  207. - ACL: a list of access rules, the Admin uses 2 types:
  208. - Admin ACL: created from the Security information of the Admin class for each admin and shares the Access Control Entries that specify
  209. what the user can do (permissions) with the admin
  210. - Object ACL: also created from the security information of the Admin class however created for each object, it uses 2 scopes:
  211. - Class-Scope: the class scope contains the rules that are valid for all object of a certain class;
  212. - Object-Scope: specifies the owner;
  213. - Sid: Security identity, an ACL role for the Class-Scope ACL and the user for the Object-Scope ACL;
  214. - Oid: Object identity, identifies the ACL, for the admin ACL this is the admin code, for the object ACL this is the object id;
  215. - ACE: a role (or sid) and its permissions;
  216. - Permission: this tells what the user is allowed to do with the Object identity;
  217. - Bitmask: a permission can have several bitmasks, each bitmask represents a permission. When permission VIEW is requested and
  218. it contains the VIEW and EDIT bitmask and the user only has the EDIT permission, then the permission VIEW is granted.
  219. - PermissionMap: configures the bitmasks for each permission, to change the default mapping create a voter for the domain class of the Admin.
  220. There can be many voters that may have different permission maps. However, prevent that multiple voters vote on the same class with
  221. overlapping bitmasks.
  222. See the cookbook article "Advanced ACL concepts" for the meaning of the different permissions:
  223. http://symfony.com/doc/current/cookbook/security/acl_advanced.html#pre-authorization-decisions.
  224. How is access granted?
  225. ~~~~~~~~~~~~~~~~~~~~~~
  226. In the application the security context is asked if access is granted for a role or a permission (admin.isGranted):
  227. - Token: a token identifies a user between requests;
  228. - Voter: sort of judge that returns if access is granted of denied, if the voter should not vote for a case, it returns abstrain;
  229. - AccessDecisionManager: decides if access is granted or denied according a specific strategy. It grants access if at least one (affirmative
  230. strategy), all (unanimous strategy) or more then half (consensus strategy) of the counted votes granted access;
  231. - RoleVoter: votes for all attributes stating with "ROLE_" and grants access if the user has this role;
  232. - RoleHierarchieVoter: when the role ROLE_SONATA_ADMIN is voted for, it also votes "granted" if the user has the role ROLE_SUPER_ADMIN;
  233. - AclVoter: grants access for the permissions of the Admin class if the user has the permission, the user has a permission that is
  234. included in the bitmasks of the permission requested to vote for or the user owns the object.
  235. Create a custom voter or a custom permission map
  236. ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
  237. 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:
  238. - create a custom voter class that extends the AclVoter
  239. .. code-block:: php
  240. namespace Acme\DemoBundle\Security\Authorization\Voter;
  241. use FOS\UserBundle\Model\UserInterface;
  242. use Symfony\Component\Security\Core\Authentication\Token\TokenInterface;
  243. use Symfony\Component\Security\Acl\Voter\AclVoter;
  244. class UserAclVoter extends AclVoter
  245. {
  246. /**
  247. * {@InheritDoc}
  248. */
  249. public function supportsClass($class)
  250. {
  251. // support the Class-Scope ACL for votes with the custom permission map
  252. // return $class === 'Sonata\UserBundle\Admin\Entity\UserAdmin' || $is_subclass_of($class, 'FOS\UserBundle\Model\UserInterface');
  253. // if you use php >=5.3.7 you can check the inheritance with is_a($class, 'Sonata\UserBundle\Admin\Entity\UserAdmin');
  254. // support the Object-Scope ACL
  255. return is_subclass_of($class, 'FOS\UserBundle\Model\UserInterface');
  256. }
  257. public function supportsAttribute($attribute)
  258. {
  259. return $attribute === 'EDIT' || $attribute === 'DELETE';
  260. }
  261. public function vote(TokenInterface $token, $object, array $attributes)
  262. {
  263. if (!$this->supportsClass(get_class($object))) {
  264. return self::ACCESS_ABSTAIN;
  265. }
  266. foreach ($attributes as $attribute) {
  267. if ($this->supportsAttribute($attribute) && $object instanceof UserInterface) {
  268. if ($object->isSuperAdmin() && !$token->getUser()->isSuperAdmin()) {
  269. // deny a non super admin user to edit a super admin user
  270. return self::ACCESS_DENIED;
  271. }
  272. }
  273. }
  274. // use the parent vote with the custom permission map:
  275. // return parent::vote($token, $object, $attributes);
  276. // otherwise leave the permission voting to the AclVoter that is using the default permission map
  277. return self::ACCESS_ABSTAIN;
  278. }
  279. }
  280. - optionally create a custom permission map, copy to start the Sonata\AdminBundle\Security\Acl\Permission\AdminPermissionMap.php to your bundle
  281. - declare the voter and permission map as a service
  282. .. code-block:: xml
  283. <!-- src/Acme/DemoBundle/Resources/config/services.xml -->
  284. <parameters>
  285. <parameter key="security.acl.user_voter.class">Acme\DemoBundle\Security\Authorization\Voter\UserAclVoter</parameter>
  286. <!-- <parameter key="security.acl.user_permission.map.class">Acme\DemoBundle\Security\Acl\Permission\UserAdminPermissionMap</parameter> -->
  287. </parameters>
  288. <services>
  289. <!-- <service id="security.acl.user_permission.map" class="%security.acl.permission.map.class%" public="false"></service> -->
  290. <service id="security.acl.voter.user_permissions" class="%security.acl.user_voter.class%" public="false">
  291. <tag name="monolog.logger" channel="security" />
  292. <argument type="service" id="security.acl.provider" />
  293. <argument type="service" id="security.acl.object_identity_retrieval_strategy" />
  294. <argument type="service" id="security.acl.security_identity_retrieval_strategy" />
  295. <argument type="service" id="security.acl.permission.map" />
  296. <argument type="service" id="logger" on-invalid="null" />
  297. <tag name="security.voter" priority="255" />
  298. </service>
  299. </services>
  300. - change the access decission strategy to ``unanimous``
  301. .. code-block:: yaml
  302. # app/config/security.yml
  303. security:
  304. access_decision_manager:
  305. # Strategy can be: affirmative, unanimous or consensus
  306. strategy: unanimous
  307. - to make this work the permission needs to be checked using the Object ACL
  308. - modify the template (or code) where applicable:
  309. .. code-block:: html
  310. {% if admin.isGranted('EDIT', user_object) %} {# ... #} {% endif %}
  311. - because the object ACL permission is checked, the ACL for the object must have been created, otherwise the AclVoter
  312. 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
  313. created using the Admin. If objects are also created outside the Admin, have a look at the ``createSecurityObject`` method in the
  314. AclSecurityHandler.
  315. Usage
  316. ~~~~~
  317. Everytime you create a new ``Admin`` class, you should create start the command ``php app/console sonata:admin:setup-acl``
  318. so the ACL database will be updated with the latest roles and permissions.
  319. In the templates, or in your code, you can use the Admin method ``isGranted``:
  320. - check for an admin that the user is allowed to EDIT:
  321. .. code-block:: html
  322. {# use the admin security method #}
  323. {% if admin.isGranted('EDIT') %} {# ... #} {% endif %}
  324. {# or use the default is_granted symfony helper, the following will give the same result #}
  325. {% if is_granted('ROLE_SUPER_ADMIN') or is_granted('EDIT', admin) %} {# ... #} {% endif %}
  326. - 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:
  327. .. code-block:: html
  328. {# use the admin security method #}
  329. {% if admin.isGranted('DELETE', object) %} {# ... #} {% endif %}
  330. {# or use the default is_granted symfony helper, the following will give the same result #}
  331. {% if is_granted('ROLE_SUPER_ADMIN') or is_granted('DELETE', object) %} {# ... #} {% endif %}