Skip to content

Commit d87d382

Browse files
author
Jules Pietri
committed
Improved documentation about access controls
1 parent 36b3ff4 commit d87d382

File tree

8 files changed

+97
-57
lines changed

8 files changed

+97
-57
lines changed

configuration/external_parameters.rst

Lines changed: 6 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -285,7 +285,8 @@ Symfony provides the following env var processors:
285285
286286
# config/packages/security.yaml
287287
parameters:
288-
env(HEALTH_CHECK_METHOD): 'Symfony\Component\HttpFoundation\Request::METHOD_HEAD'
288+
env(HEALTH_CHECK_METHOD): !php/const Symfony\Component\HttpFoundation\Request::METHOD_HEAD
289+
289290
security:
290291
access_control:
291292
- { path: '^/health-check$', methods: '%env(const:HEALTH_CHECK_METHOD)%' }
@@ -301,7 +302,7 @@ Symfony provides the following env var processors:
301302
http://symfony.com/schema/dic/services/services-1.0.xsd">
302303
303304
<parameters>
304-
<parameter key="env(HEALTH_CHECK_METHOD)">Symfony\Component\HttpFoundation\Request::METHOD_HEAD</parameter>
305+
<parameter key="env(HEALTH_CHECK_METHOD)" type="constant">Symfony\Component\HttpFoundation\Request::METHOD_HEAD</parameter>
305306
</parameters>
306307
307308
<security:config>
@@ -312,7 +313,9 @@ Symfony provides the following env var processors:
312313
.. code-block:: php
313314
314315
// config/packages/security.php
315-
$container->setParameter('env(HEALTH_CHECK_METHOD)', 'Symfony\Component\HttpFoundation\Request::METHOD_HEAD');
316+
use Symfony\Component\HttpFoundation\Request;
317+
318+
$container->setParameter('env(HEALTH_CHECK_METHOD)', Request::METHOD_HEAD);
316319
$container->loadFromExtension('security', [
317320
'access_control' => [
318321
[

security.rst

Lines changed: 7 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -226,7 +226,7 @@ user to be logged in to access this URL:
226226
227227
access_control:
228228
# require ROLE_ADMIN for /admin*
229-
- { path: ^/admin, roles: ROLE_ADMIN }
229+
- { path: '^/admin', roles: ROLE_ADMIN }
230230
231231
.. code-block:: xml
232232
@@ -713,7 +713,7 @@ URL pattern. You saw this earlier, where anything matching the regular expressio
713713
714714
access_control:
715715
# require ROLE_ADMIN for /admin*
716-
- { path: ^/admin, roles: ROLE_ADMIN }
716+
- { path: '^/admin', roles: ROLE_ADMIN }
717717
718718
.. code-block:: xml
719719
@@ -751,7 +751,7 @@ URL pattern. You saw this earlier, where anything matching the regular expressio
751751
],
752752
'access_control' => [
753753
// require ROLE_ADMIN for /admin*
754-
['path' => '^/admin', 'role' => 'ROLE_ADMIN'],
754+
['path' => '^/admin', 'roles' => 'ROLE_ADMIN'],
755755
],
756756
]);
757757
@@ -773,8 +773,8 @@ matches the URL.
773773
# ...
774774
775775
access_control:
776-
- { path: ^/admin/users, roles: ROLE_SUPER_ADMIN }
777-
- { path: ^/admin, roles: ROLE_ADMIN }
776+
- { path: '^/admin/users', roles: ROLE_SUPER_ADMIN }
777+
- { path: '^/admin', roles: ROLE_ADMIN }
778778
779779
.. code-block:: xml
780780
@@ -801,8 +801,8 @@ matches the URL.
801801
// ...
802802
803803
'access_control' => [
804-
['path' => '^/admin/users', 'role' => 'ROLE_SUPER_ADMIN'],
805-
['path' => '^/admin', 'role' => 'ROLE_ADMIN'],
804+
['path' => '^/admin/users', 'roles' => 'ROLE_SUPER_ADMIN'],
805+
['path' => '^/admin', 'roles' => 'ROLE_ADMIN'],
806806
],
807807
]);
808808

security/access_control.rst

Lines changed: 58 additions & 28 deletions
Original file line numberDiff line numberDiff line change
@@ -22,10 +22,10 @@ for each ``access_control`` entry, which determines whether or not a given
2222
access control should be used on this request. The following ``access_control``
2323
options are used for matching:
2424

25-
* ``path``
25+
* ``path`` # a regular expression (without delimiters)
2626
* ``ip`` or ``ips`` (netmasks are also supported)
27-
* ``host``
28-
* ``methods``
27+
* ``host`` # a regular expression
28+
* ``methods`` # one or many methods
2929

3030
Take the following ``access_control`` entries as an example:
3131

@@ -37,10 +37,10 @@ Take the following ``access_control`` entries as an example:
3737
security:
3838
# ...
3939
access_control:
40-
- { path: ^/admin, roles: ROLE_USER_IP, ip: 127.0.0.1 }
41-
- { path: ^/admin, roles: ROLE_USER_HOST, host: symfony\.com$ }
42-
- { path: ^/admin, roles: ROLE_USER_METHOD, methods: [POST, PUT] }
43-
- { path: ^/admin, roles: ROLE_USER }
40+
- { path: '^/admin', roles: ROLE_USER_IP, ip: 127.0.0.1 }
41+
- { path: '^/admin', roles: ROLE_USER_HOST, host: symfony\.com$ }
42+
- { path: '^/admin', roles: ROLE_USER_METHOD, methods: [POST, PUT] }
43+
- { path: '^/admin', roles: [ROLE_MANAGER, ROLE_ADMIN] }
4444
4545
.. code-block:: xml
4646
@@ -57,7 +57,12 @@ Take the following ``access_control`` entries as an example:
5757
<rule path="^/admin" role="ROLE_USER_IP" ip="127.0.0.1" />
5858
<rule path="^/admin" role="ROLE_USER_HOST" host="symfony\.com$" />
5959
<rule path="^/admin" role="ROLE_USER_METHOD" methods="POST, PUT" />
60-
<rule path="^/admin" role="ROLE_USER" />
60+
<rule path="^/admin" roles="ROLE_ADMIN, ROLE_MANAGER" />
61+
<!-- same as: -->
62+
<rule path="^/admin">
63+
<role>ROLE_MANAGER</role>
64+
<role>ROLE_ADMIN</role>
65+
</rule>
6166
</config>
6267
</srv:container>
6368
@@ -69,31 +74,33 @@ Take the following ``access_control`` entries as an example:
6974
'access_control' => [
7075
[
7176
'path' => '^/admin',
72-
'role' => 'ROLE_USER_IP',
73-
'ip' => '127.0.0.1',
77+
'roles' => 'ROLE_USER_IP',
78+
'ips' => '127.0.0.1',
7479
],
7580
[
7681
'path' => '^/admin',
77-
'role' => 'ROLE_USER_HOST',
82+
'roles' => 'ROLE_USER_HOST',
7883
'host' => 'symfony\.com$',
7984
],
8085
[
8186
'path' => '^/admin',
82-
'role' => 'ROLE_USER_METHOD',
87+
'roles' => 'ROLE_USER_METHOD',
8388
'methods' => 'POST, PUT',
8489
],
8590
[
8691
'path' => '^/admin',
87-
'role' => 'ROLE_USER',
92+
'roles' => 'ROLE_MANAGER, ROLE_ADMIN',
93+
// same as
94+
// 'roles' => ['ROLE_MANAGER', 'ROLE_ADMIN'],
8895
],
8996
],
9097
]);
9198
9299
For each incoming request, Symfony will decide which ``access_control``
93100
to use based on the URI, the client's IP address, the incoming host name,
94101
and the request method. Remember, the first rule that matches is used, and
95-
if ``ip``, ``host`` or ``method`` are not specified for an entry, that ``access_control``
96-
will match any ``ip``, ``host`` or ``method``:
102+
if ``ips``, ``host`` or ``methods`` are not specified for an entry, that
103+
``access_control`` will match any ``ips``, ``host`` or ``methods``:
97104

98105
+-----------------+-------------+-------------+------------+--------------------------------+-------------------------------------------------------------+
99106
| URI | IP | HOST | METHOD | ``access_control`` | Why? |
@@ -114,9 +121,9 @@ will match any ``ip``, ``host`` or ``method``:
114121
| ``/admin/user`` | 168.0.0.1 | example.com | POST | rule #3 (``ROLE_USER_METHOD``) | The ``ip`` and ``host`` don't match the first two entries, |
115122
| | | | | | but the third - ``ROLE_USER_METHOD`` - matches and is used. |
116123
+-----------------+-------------+-------------+------------+--------------------------------+-------------------------------------------------------------+
117-
| ``/admin/user`` | 168.0.0.1 | example.com | GET | rule #4 (``ROLE_USER``) | The ``ip``, ``host`` and ``method`` prevent the first |
124+
| ``/admin/user`` | 168.0.0.1 | example.com | GET | rule #4 (``ROLE_MANAGER``) | The ``ip``, ``host`` and ``method`` prevent the first |
118125
| | | | | | three entries from matching. But since the URI matches the |
119-
| | | | | | ``path`` pattern of the ``ROLE_USER`` entry, it is used. |
126+
| | | | | | ``path`` pattern, then the ``ROLE_USER`` entry is used. |
120127
+-----------------+-------------+-------------+------------+--------------------------------+-------------------------------------------------------------+
121128
| ``/foo`` | 127.0.0.1 | symfony.com | POST | matches no entries | This doesn't match any ``access_control`` rules, since its |
122129
| | | | | | URI doesn't match any of the ``path`` values. |
@@ -144,6 +151,14 @@ options:
144151
does not match this value (e.g. ``https``), the user will be redirected
145152
(e.g. redirected from ``http`` to ``https``, or vice versa).
146153

154+
.. tip::
155+
156+
Behind the scene, the array value of ``roles`` is passed as ``$attributes``
157+
argument to each voter in the application with the
158+
:class:`Symfony\\Component\\HttpFoundation\\Request` as ``$subject``. You
159+
can learn how to use your custom attributes by reading
160+
:ref:`security/custom-voter`.
161+
147162
.. tip::
148163

149164
If access is denied, the system will try to authenticate the user if not
@@ -180,8 +195,8 @@ pattern so that it is only accessible by requests from the local server itself:
180195
access_control:
181196
#
182197
# the 'ips' option supports IP addresses and subnet masks
183-
- { path: ^/internal, roles: IS_AUTHENTICATED_ANONYMOUSLY, ips: [127.0.0.1, ::1, 192.168.0.1/24] }
184-
- { path: ^/internal, roles: ROLE_NO_ACCESS }
198+
- { path: '^/internal', roles: IS_AUTHENTICATED_ANONYMOUSLY, ips: [127.0.0.1, ::1, 192.168.0.1/24] }
199+
- { path: '^/internal', roles: ROLE_NO_ACCESS }
185200
186201
.. code-block:: xml
187202
@@ -214,13 +229,13 @@ pattern so that it is only accessible by requests from the local server itself:
214229
'access_control' => [
215230
[
216231
'path' => '^/internal',
217-
'role' => 'IS_AUTHENTICATED_ANONYMOUSLY',
232+
'roles' => 'IS_AUTHENTICATED_ANONYMOUSLY',
218233
// the 'ips' option supports IP addresses and subnet masks
219234
'ips' => ['127.0.0.1', '::1'],
220235
],
221236
[
222237
'path' => '^/internal',
223-
'role' => 'ROLE_NO_ACCESS',
238+
'roles' => 'ROLE_NO_ACCESS',
224239
],
225240
],
226241
]);
@@ -265,7 +280,8 @@ key:
265280
access_control:
266281
-
267282
path: ^/_internal/secure
268-
allow_if: "'127.0.0.1' == request.getClientIp() or has_role('ROLE_ADMIN')"
283+
roles: 'ROLE_ADMIN'
284+
allow_if: "'127.0.0.1' == request.getClientIp() or request.header.has('X-Secure-Access')"
269285
270286
.. code-block:: xml
271287
@@ -279,7 +295,8 @@ key:
279295
280296
<config>
281297
<rule path="^/_internal/secure"
282-
allow-if="'127.0.0.1' == request.getClientIp() or has_role('ROLE_ADMIN')" />
298+
role="ROLE_ADMIN"
299+
allow-if="'127.0.0.1' == request.getClientIp() or request.header.has('X-Secure-Access')" />
283300
</config>
284301
</srv:container>
285302
@@ -288,13 +305,26 @@ key:
288305
'access_control' => [
289306
[
290307
'path' => '^/_internal/secure',
291-
'allow_if' => '"127.0.0.1" == request.getClientIp() or has_role("ROLE_ADMIN")',
308+
'roles' => 'ROLE_ADMIN',
309+
'allow_if' => '"127.0.0.1" == request.getClientIp() or request.header.has('X-Secure-Access')',
292310
],
293311
],
294312
295-
In this case, when the user tries to access any URL starting with ``/_internal/secure``,
296-
they will only be granted access if the IP address is ``127.0.0.1`` or if
297-
the user has the ``ROLE_ADMIN`` role.
313+
In this case, when the user tries to access any URL starting with
314+
``/_internal/secure``, they will only be granted access if the IP address is
315+
``127.0.0.1`` or a secure header, or if the user has the ``ROLE_ADMIN`` role.
316+
317+
.. note::
318+
319+
Internally ``allow_if`` enforce calling the built-in
320+
:class:`Symfony\\Component\\Security\\Core\\Authorization\\Voter\\ExpressionVoter`
321+
as like it was part of the attributes defined in the ``roles`` option.
322+
By default, access is granted if one of the attribute is granted. In other
323+
words, it means ``allow_if`` can grant access even if the
324+
:class:`Symfony\\Component\\Security\\Core\\Authorization\\Voter\\RoleVoter` or
325+
any other voter denied it.
326+
To change this behavior, you may need to learn more about
327+
:ref:`access decision strategy <security-voters-change-strategy>`.
298328

299329
Inside the expression, you have access to a number of different variables
300330
and functions including ``request``, which is the Symfony
@@ -345,7 +375,7 @@ the user will be redirected to ``https``:
345375
'access_control' => [
346376
[
347377
'path' => '^/cart/checkout',
348-
'role' => 'IS_AUTHENTICATED_ANONYMOUSLY',
378+
'roles' => 'IS_AUTHENTICATED_ANONYMOUSLY',
349379
'requires_channel' => 'https',
350380
],
351381
],

security/api_key_authentication.rst

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -369,7 +369,7 @@ If you have defined ``access_control``, make sure to add a new entry:
369369
# ...
370370
371371
access_control:
372-
- { path: ^/api, roles: ROLE_API }
372+
- { path: '^/api', roles: ROLE_API }
373373
374374
.. code-block:: xml
375375
@@ -392,7 +392,7 @@ If you have defined ``access_control``, make sure to add a new entry:
392392
'access_control' => [
393393
[
394394
'path' => '^/api',
395-
'role' => 'ROLE_API',
395+
'roles' => 'ROLE_API',
396396
],
397397
],
398398
]);

security/force_https.rst

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -18,7 +18,7 @@ to use HTTPS then you could use the following configuration:
1818
# ...
1919
2020
access_control:
21-
- { path: ^/secure, roles: ROLE_ADMIN, requires_channel: https }
21+
- { path: '^/secure', roles: ROLE_ADMIN, requires_channel: https }
2222
2323
.. code-block:: xml
2424
@@ -46,7 +46,7 @@ to use HTTPS then you could use the following configuration:
4646
'access_control' => [
4747
[
4848
'path' => '^/secure',
49-
'role' => 'ROLE_ADMIN',
49+
'roles' => 'ROLE_ADMIN',
5050
'requires_channel' => 'https',
5151
],
5252
],
@@ -66,7 +66,7 @@ role:
6666
# ...
6767
6868
access_control:
69-
- { path: ^/login, roles: IS_AUTHENTICATED_ANONYMOUSLY, requires_channel: https }
69+
- { path: '^/login', roles: IS_AUTHENTICATED_ANONYMOUSLY, requires_channel: https }
7070
7171
.. code-block:: xml
7272
@@ -97,7 +97,7 @@ role:
9797
'access_control' => [
9898
[
9999
'path' => '^/login',
100-
'role' => 'IS_AUTHENTICATED_ANONYMOUSLY',
100+
'roles' => 'IS_AUTHENTICATED_ANONYMOUSLY',
101101
'requires_channel' => 'https',
102102
],
103103
],

security/form_login_setup.rst

Lines changed: 6 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -283,7 +283,7 @@ all URLs (including the ``/login`` URL), will cause a redirect loop:
283283
284284
# ...
285285
access_control:
286-
- { path: ^/, roles: ROLE_ADMIN }
286+
- { path: '^/', roles: ROLE_ADMIN }
287287
288288
.. code-block:: xml
289289
@@ -307,7 +307,7 @@ all URLs (including the ``/login`` URL), will cause a redirect loop:
307307
308308
// ...
309309
'access_control' => [
310-
['path' => '^/', 'role' => 'ROLE_ADMIN'],
310+
['path' => '^/', 'roles' => 'ROLE_ADMIN'],
311311
],
312312
313313
Adding an access control that matches ``/login/*`` and requires *no* authentication
@@ -321,8 +321,8 @@ fixes the problem:
321321
322322
# ...
323323
access_control:
324-
- { path: ^/login, roles: IS_AUTHENTICATED_ANONYMOUSLY }
325-
- { path: ^/, roles: ROLE_ADMIN }
324+
- { path: '^/login', roles: IS_AUTHENTICATED_ANONYMOUSLY }
325+
- { path: '^/', roles: ROLE_ADMIN }
326326
327327
.. code-block:: xml
328328
@@ -347,8 +347,8 @@ fixes the problem:
347347
348348
// ...
349349
'access_control' => [
350-
['path' => '^/login', 'role' => 'IS_AUTHENTICATED_ANONYMOUSLY'],
351-
['path' => '^/', 'role' => 'ROLE_ADMIN'],
350+
['path' => '^/login', 'roles' => 'IS_AUTHENTICATED_ANONYMOUSLY'],
351+
['path' => '^/', 'roles' => 'ROLE_ADMIN'],
352352
],
353353
354354
3. Be Sure check_path Is Behind a Firewall

security/multiple_guard_authenticators.rst

Lines changed: 6 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -108,9 +108,9 @@ the solution is to split the configuration into two separate firewalls:
108108
authenticators:
109109
- AppBundle\Security\LoginFormAuthenticator
110110
access_control:
111-
- { path: ^/login, roles: IS_AUTHENTICATED_ANONYMOUSLY }
112-
- { path: ^/api, roles: ROLE_API_USER }
113-
- { path: ^/, roles: ROLE_USER }
111+
- { path: '^/login', roles: IS_AUTHENTICATED_ANONYMOUSLY }
112+
- { path: '^/api', roles: ROLE_API_USER }
113+
- { path: '^/', roles: ROLE_USER }
114114
115115
.. code-block:: xml
116116
@@ -168,8 +168,8 @@ the solution is to split the configuration into two separate firewalls:
168168
],
169169
],
170170
'access_control' => [
171-
['path' => '^/login', 'role' => 'IS_AUTHENTICATED_ANONYMOUSLY'],
172-
['path' => '^/api', 'role' => 'ROLE_API_USER'],
173-
['path' => '^/', 'role' => 'ROLE_USER'],
171+
['path' => '^/login', 'roles' => 'IS_AUTHENTICATED_ANONYMOUSLY'],
172+
['path' => '^/api', 'roles' => 'ROLE_API_USER'],
173+
['path' => '^/', 'roles' => 'ROLE_USER'],
174174
],
175175
]);

0 commit comments

Comments
 (0)