-
-
Notifications
You must be signed in to change notification settings - Fork 5.2k
Improved documentation about access controls #11118
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Changes from all commits
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -22,10 +22,10 @@ for each ``access_control`` entry, which determines whether or not a given | |
access control should be used on this request. The following ``access_control`` | ||
options are used for matching: | ||
|
||
* ``path`` | ||
* ``ip`` or ``ips`` (netmasks are also supported) | ||
* ``host`` | ||
* ``methods`` | ||
* ``path``: a regular expression (without delimiters) | ||
* ``ip`` or ``ips``: netmasks are also supported | ||
* ``host``: a regular expression | ||
* ``methods``: one or many methods | ||
|
||
Take the following ``access_control`` entries as an example: | ||
|
||
|
@@ -37,10 +37,11 @@ Take the following ``access_control`` entries as an example: | |
security: | ||
# ... | ||
access_control: | ||
- { path: ^/admin, roles: ROLE_USER_IP, ip: 127.0.0.1 } | ||
- { path: ^/admin, roles: ROLE_USER_HOST, host: symfony\.com$ } | ||
- { path: ^/admin, roles: ROLE_USER_METHOD, methods: [POST, PUT] } | ||
- { path: ^/admin, roles: ROLE_USER } | ||
- { path: '^/admin', roles: ROLE_USER_IP, ip: 127.0.0.1 } | ||
- { path: '^/admin', roles: ROLE_USER_HOST, host: symfony\.com$ } | ||
- { path: '^/admin', roles: ROLE_USER_METHOD, methods: [POST, PUT] } | ||
# Defining many roles means we need one of them (like an OR condition) | ||
- { path: '^/admin', roles: [ROLE_MANAGER, ROLE_ADMIN] } | ||
|
||
.. code-block:: xml | ||
|
||
|
@@ -57,7 +58,8 @@ Take the following ``access_control`` entries as an example: | |
<rule path="^/admin" role="ROLE_USER_IP" ip="127.0.0.1" /> | ||
<rule path="^/admin" role="ROLE_USER_HOST" host="symfony\.com$" /> | ||
<rule path="^/admin" role="ROLE_USER_METHOD" methods="POST, PUT" /> | ||
<rule path="^/admin" role="ROLE_USER" /> | ||
<!-- Defining many roles, mean we need one of them (like an OR condition) --> | ||
<rule path="^/admin" roles="ROLE_ADMIN, ROLE_MANAGER" /> | ||
</config> | ||
</srv:container> | ||
|
||
|
@@ -69,31 +71,32 @@ Take the following ``access_control`` entries as an example: | |
'access_control' => [ | ||
[ | ||
'path' => '^/admin', | ||
'role' => 'ROLE_USER_IP', | ||
'ip' => '127.0.0.1', | ||
'roles' => 'ROLE_USER_IP', | ||
'ips' => '127.0.0.1', | ||
], | ||
[ | ||
'path' => '^/admin', | ||
'role' => 'ROLE_USER_HOST', | ||
'roles' => 'ROLE_USER_HOST', | ||
'host' => 'symfony\.com$', | ||
], | ||
[ | ||
'path' => '^/admin', | ||
'role' => 'ROLE_USER_METHOD', | ||
'roles' => 'ROLE_USER_METHOD', | ||
'methods' => 'POST, PUT', | ||
], | ||
[ | ||
'path' => '^/admin', | ||
'role' => 'ROLE_USER', | ||
// Defining many roles, mean we need one of them (like an OR condition) | ||
'roles' => ['ROLE_MANAGER', 'ROLE_ADMIN'], | ||
], | ||
], | ||
]); | ||
|
||
For each incoming request, Symfony will decide which ``access_control`` | ||
to use based on the URI, the client's IP address, the incoming host name, | ||
and the request method. Remember, the first rule that matches is used, and | ||
if ``ip``, ``host`` or ``method`` are not specified for an entry, that ``access_control`` | ||
will match any ``ip``, ``host`` or ``method``: | ||
if ``ips``, ``host`` or ``methods`` are not specified for an entry, that | ||
``access_control`` will match any ``ips``, ``host`` or ``methods``: | ||
|
||
+-----------------+-------------+-------------+------------+--------------------------------+-------------------------------------------------------------+ | ||
| URI | IP | HOST | METHOD | ``access_control`` | Why? | | ||
|
@@ -114,9 +117,10 @@ will match any ``ip``, ``host`` or ``method``: | |
| ``/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, | | ||
| | | | | | but the third - ``ROLE_USER_METHOD`` - matches and is used. | | ||
+-----------------+-------------+-------------+------------+--------------------------------+-------------------------------------------------------------+ | ||
| ``/admin/user`` | 168.0.0.1 | example.com | GET | rule #4 (``ROLE_USER``) | The ``ip``, ``host`` and ``method`` prevent the first | | ||
| ``/admin/user`` | 168.0.0.1 | example.com | GET | rule #4 (``ROLE_MANAGER``) | The ``ip``, ``host`` and ``method`` prevent the first | | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more.
right? There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Given the amount of confusion that are provided by the array roles, should we even document them? I mean, the role hierarchy is complex enough to manage everything with single roles instead of array roles. Sometimes it's better to hide confusing things 😃 There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. This question will especially, if |
||
| | | | | | three entries from matching. But since the URI matches the | | ||
| | | | | | ``path`` pattern of the ``ROLE_USER`` entry, it is used. | | ||
| | | | | | ``path`` pattern, then the ``ROLE_MANAGER`` (or the | | ||
| | | | | | ``ROLE_ADMIN``) is used. | | ||
+-----------------+-------------+-------------+------------+--------------------------------+-------------------------------------------------------------+ | ||
| ``/foo`` | 127.0.0.1 | symfony.com | POST | matches no entries | This doesn't match any ``access_control`` rules, since its | | ||
| | | | | | URI doesn't match any of the ``path`` values. | | ||
|
@@ -144,6 +148,14 @@ options: | |
does not match this value (e.g. ``https``), the user will be redirected | ||
(e.g. redirected from ``http`` to ``https``, or vice versa). | ||
|
||
.. tip:: | ||
|
||
Behind the scenes, the array value of ``roles`` is passed as the | ||
``$attributes`` argument to each voter in the application with the | ||
:class:`Symfony\\Component\\HttpFoundation\\Request` as ``$subject``. You | ||
can learn how to use your custom attributes by reading | ||
:ref:`security/custom-voter`. | ||
|
||
.. tip:: | ||
|
||
If access is denied, the system will try to authenticate the user if not | ||
|
@@ -180,8 +192,8 @@ pattern so that it is only accessible by requests from the local server itself: | |
access_control: | ||
# | ||
# the 'ips' option supports IP addresses and subnet masks | ||
- { path: ^/internal, roles: IS_AUTHENTICATED_ANONYMOUSLY, ips: [127.0.0.1, ::1, 192.168.0.1/24] } | ||
- { path: ^/internal, roles: ROLE_NO_ACCESS } | ||
- { path: '^/internal', roles: IS_AUTHENTICATED_ANONYMOUSLY, ips: [127.0.0.1, ::1, 192.168.0.1/24] } | ||
- { path: '^/internal', roles: ROLE_NO_ACCESS } | ||
|
||
.. code-block:: xml | ||
|
||
|
@@ -214,13 +226,13 @@ pattern so that it is only accessible by requests from the local server itself: | |
'access_control' => [ | ||
[ | ||
'path' => '^/internal', | ||
'role' => 'IS_AUTHENTICATED_ANONYMOUSLY', | ||
'roles' => 'IS_AUTHENTICATED_ANONYMOUSLY', | ||
// the 'ips' option supports IP addresses and subnet masks | ||
'ips' => ['127.0.0.1', '::1'], | ||
], | ||
[ | ||
'path' => '^/internal', | ||
'role' => 'ROLE_NO_ACCESS', | ||
'roles' => 'ROLE_NO_ACCESS', | ||
], | ||
], | ||
]); | ||
|
@@ -265,7 +277,9 @@ key: | |
access_control: | ||
- | ||
path: ^/_internal/secure | ||
allow_if: "'127.0.0.1' == request.getClientIp() or has_role('ROLE_ADMIN')" | ||
roles: 'ROLE_ADMIN' | ||
// with this expression, we need it to be true OR a ROLE_ADMIN | ||
allow_if: "'127.0.0.1' == request.getClientIp() or request.header.has('X-Secure-Access')" | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. you should document with a comment here that the logic is or. You explain it more below, but a comment is all most people will read ;) There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Done, thank you! |
||
|
||
.. code-block:: xml | ||
|
||
|
@@ -279,7 +293,9 @@ key: | |
|
||
<config> | ||
<rule path="^/_internal/secure" | ||
allow-if="'127.0.0.1' == request.getClientIp() or has_role('ROLE_ADMIN')" /> | ||
role="ROLE_ADMIN" | ||
<!-- with this expression, we need it to be true OR a ROLE_ADMIN --> | ||
allow-if="'127.0.0.1' == request.getClientIp() or request.header.has('X-Secure-Access')" /> | ||
</config> | ||
</srv:container> | ||
|
||
|
@@ -288,13 +304,27 @@ key: | |
'access_control' => [ | ||
[ | ||
'path' => '^/_internal/secure', | ||
'allow_if' => '"127.0.0.1" == request.getClientIp() or has_role("ROLE_ADMIN")', | ||
'roles' => 'ROLE_ADMIN', | ||
// with this expression, we need it to be true OR a ROLE_ADMIN | ||
'allow_if' => '"127.0.0.1" == request.getClientIp() or request.header.has('X-Secure-Access')', | ||
], | ||
], | ||
|
||
In this case, when the user tries to access any URL starting with ``/_internal/secure``, | ||
they will only be granted access if the IP address is ``127.0.0.1`` or if | ||
the user has the ``ROLE_ADMIN`` role. | ||
In this case, when the user tries to access any URL starting with | ||
``/_internal/secure``, they will only be granted access if the IP address is | ||
``127.0.0.1`` or a secure header, or if the user has the ``ROLE_ADMIN`` role. | ||
|
||
.. note:: | ||
|
||
Internally ``allow_if`` triggers the built-in | ||
:class:`Symfony\\Component\\Security\\Core\\Authorization\\Voter\\ExpressionVoter` | ||
as like it was part of the attributes defined in the ``roles`` option. | ||
javiereguiluz marked this conversation as resolved.
Show resolved
Hide resolved
|
||
By default, access is granted if one of the attribute is granted. In other | ||
words, it means ``allow_if`` can grant access even if the | ||
:class:`Symfony\\Component\\Security\\Core\\Authorization\\Voter\\RoleVoter` or | ||
any other voter denied it. | ||
To change this behavior, you may need to learn more about | ||
:ref:`access decision strategy <security-voters-change-strategy>`. | ||
javiereguiluz marked this conversation as resolved.
Show resolved
Hide resolved
|
||
|
||
Inside the expression, you have access to a number of different variables | ||
and functions including ``request``, which is the Symfony | ||
|
@@ -345,7 +375,7 @@ the user will be redirected to ``https``: | |
'access_control' => [ | ||
[ | ||
'path' => '^/cart/checkout', | ||
'role' => 'IS_AUTHENTICATED_ANONYMOUSLY', | ||
'roles' => 'IS_AUTHENTICATED_ANONYMOUSLY', | ||
'requires_channel' => 'https', | ||
], | ||
], | ||
|
Uh oh!
There was an error while loading. Please reload this page.