Thanks to visit codestin.com
Credit goes to github.com

Skip to content

Commit bc78096

Browse files
committed
Merge branch '2.1'
* 2.1: Furthering @fabpot's changes to the access_control explanations in the security chapter fixed explanations about how access control rules work Fix small typo in book/validation.rst Increase year to 2013
2 parents 6bd16c2 + 331001c commit bc78096

File tree

3 files changed

+154
-21
lines changed

3 files changed

+154
-21
lines changed

book/security.rst

Lines changed: 152 additions & 19 deletions
Original file line numberDiff line numberDiff line change
@@ -696,6 +696,12 @@ URL pattern. You've seen this already in the first example of this chapter,
696696
where anything matching the regular expression pattern ``^/admin`` requires
697697
the ``ROLE_ADMIN`` role.
698698

699+
.. caution::
700+
701+
Understanding exactly how ``access_control`` works is **very** important
702+
to make sure you application is properly secured. See :ref:`security-book-access-control-explanation`
703+
below for detailed information.
704+
699705
You can define as many URL patterns as you need - each is a regular expression.
700706

701707
.. configuration-block::
@@ -736,32 +742,136 @@ You can define as many URL patterns as you need - each is a regular expression.
736742
the ``^``) would correctly match ``/admin/foo`` but would also match URLs
737743
like ``/foo/admin``.
738744

739-
For each incoming request, Symfony2 tries to find a matching access control
740-
rule (the first one wins). If the user isn't authenticated yet, the authentication
741-
process is initiated (i.e. the user is given a chance to login). However,
742-
if the user *is* authenticated but doesn't have the required role, an
743-
:class:`Symfony\\Component\\Security\\Core\\Exception\\AccessDeniedException`
744-
exception is thrown, which you can handle and turn into a nice "access denied"
745-
error page for the user. See :doc:`/cookbook/controller/error_pages` for
746-
more information.
745+
.. _security-book-access-control-explanation:
746+
747+
Understanding how ``access_control`` works
748+
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
749+
750+
For each incoming request, Symfony2 checks each ``access_control`` entry
751+
to find *one* that matches the current request. As soon as it finds a matching
752+
``access_control`` entry, it stops - only the **first** matching ``access_control``
753+
is used to enforce access.
754+
755+
Each ``access_control`` has several options that configure two different
756+
things: (a) :ref:`should the incoming request match this access control entry<security-book-access-control-matching-options>`
757+
and (b) :ref:`once it matches, should some sort of access restriction be enforced<security-book-access-control-enforcement-options>`:
758+
759+
.. _security-book-access-control-matching-options:
760+
761+
**(a) Matching Options**
762+
763+
Symfony2 creates an instance of :class:`Symfony\\Component\\HttpFoundation\\RequestMatcher`
764+
for each ``access_control`` entry, which determines whether or not a given
765+
access control should be used on this request. The following ``access_control``
766+
options are used for matching:
767+
768+
* ``path``
769+
* ``ip``
770+
* ``host``
771+
* ``methods``
772+
773+
Take the following ``access_control`` entries as an example:
747774

748-
Since Symfony uses the first access control rule it matches, a URL like ``/admin/users/new``
749-
will match the first rule and require only the ``ROLE_SUPER_ADMIN`` role.
750-
Any URL like ``/admin/blog`` will match the second rule and require ``ROLE_ADMIN``.
775+
.. configuration-block::
776+
777+
.. code-block:: yaml
778+
779+
# app/config/security.yml
780+
security:
781+
# ...
782+
access_control:
783+
- { path: ^/user, roles: ROLE_USER_IP, ip: 127.0.0.1 }
784+
- { path: ^/user, roles: ROLE_USER_HOST, host: symfony.com }
785+
- { path: ^/user, roles: ROLE_USER_METHOD, methods: [POST, PUT] }
786+
- { path: ^/user, roles: ROLE_USER }
787+
788+
.. code-block:: xml
789+
790+
<access-control>
791+
<rule path="^/user" role="ROLE_USER_IP" ip="127.0.0.1" />
792+
<rule path="^/user" role="ROLE_USER_HOST" host="symfony.com" />
793+
<rule path="^/user" role="ROLE_USER_METHOD" method="POST, PUT" />
794+
<rule path="^/user" role="ROLE_USER" />
795+
</access-control>
796+
797+
.. code-block:: php
798+
799+
'access_control' => array(
800+
array('path' => '^/user', 'role' => 'ROLE_USER_IP', 'ip' => '127.0.0.1'),
801+
array('path' => '^/user', 'role' => 'ROLE_USER_HOST', 'host' => 'symfony.com'),
802+
array('path' => '^/user', 'role' => 'ROLE_USER_METHOD', 'method' => 'POST, PUT'),
803+
array('path' => '^/user', 'role' => 'ROLE_USER'),
804+
),
805+
806+
For each incoming request, Symfony will decided which ``access_control``
807+
to use based on the URI, the client's IP address, the incoming host name,
808+
and the request method. Remember, the first rule that matches is used, and
809+
if ``ip``, ``host`` or ``method`` are not specified for an entry, that ``access_control``
810+
will match any ``ip``, ``host`` or ``method``:
811+
812+
+-----------------+-------------+-------------+------------+--------------------------------+-------------------------------------------------------------+
813+
| **URI** | **IP** | **HOST** | **METHOD** | ``access_control`` | Why? |
814+
+-----------------+-------------+-------------+------------+--------------------------------+-------------------------------------------------------------+
815+
| ``/admin/user`` | 127.0.0.1 | example.com | GET | rule #1 (``ROLE_USER_IP``) | The URI matches ``path`` and the IP matches ``ip``. |
816+
+-----------------+-------------+-------------+------------+--------------------------------+-------------------------------------------------------------+
817+
| ``/admin/user`` | 127.0.0.1 | symfony.com | GET | rule #1 (``ROLE_USER_IP``) | The ``path`` and ``ip`` still match. This would also match |
818+
| | | | | | the ``ROLE_USER_HOST`` entry, but *only* the **first** |
819+
| | | | | | ``access_control`` match is used. |
820+
+-----------------+-------------+-------------+------------+--------------------------------+-------------------------------------------------------------+
821+
| ``/admin/user`` | 168.0.0.1 | symfony.com | GET | rule #2 (``ROLE_USER_HOST``) | The ``ip`` doesn't match the first rule, so the second |
822+
| | | | | | rule (which matches) is used. |
823+
+-----------------+-------------+-------------+------------+--------------------------------+-------------------------------------------------------------+
824+
| ``/admin/user`` | 168.0.0.1 | symfony.com | POST | rule #2 (``ROLE_USER_HOST``) | The second rule still matches. This would also match the |
825+
| | | | | | third rule (``ROLE_USER_METHOD``), but only the **first** |
826+
| | | | | | matched ``access_control`` is used. |
827+
+-----------------+-------------+-------------+------------+--------------------------------+-------------------------------------------------------------+
828+
| ``/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, |
829+
| | | | | | but the third - ``ROLE_USER_METHOD`` - matches and is used. |
830+
+-----------------+-------------+-------------+------------+--------------------------------+-------------------------------------------------------------+
831+
| ``/admin/user`` | 168.0.0.1 | example.com | GET | rule #4 (``ROLE_USER``) | The ``ip``, ``host`` and ``method`` prevent the first |
832+
| | | | | | three entries from matching. But since the URI matches the |
833+
| | | | | | ``path`` pattern of the ``ROLE_USER`` entry, it is used. |
834+
+-----------------+-------------+-------------+------------+--------------------------------+-------------------------------------------------------------+
835+
| ``/foo`` | 127.0.0.1 | symfony.com | POST | matches no entries | This doesn't match any ``access_control`` rules, since its |
836+
| | | | | | URI doesn't match any of the ``path`` values. |
837+
+-----------------+-------------+-------------+------------+--------------------------------+-------------------------------------------------------------+
838+
839+
.. _security-book-access-control-enforcement-options:
840+
841+
**(b) Access Enforcement**
842+
843+
Once Symfony2 has decided which ``access_control`` entry matches (if any),
844+
it then *enforces* access restrictions based on the ``roles`` and ``requires_channel``
845+
options:
846+
847+
* ``role`` If the user does not have the given role(s), then access is denied
848+
(internally, an :class:`Symfony\\Component\\Security\\Core\\Exception\\AccessDeniedException`
849+
is thrown);
850+
851+
* ``requires_channel`` If the incoming request's channel (e.g. ``http``)
852+
does not match this value (e.g. ``https``), the user will be redirected
853+
(e.g. redirected from ``http`` to ``https``, or vice versa).
854+
855+
.. tip::
856+
857+
If access is denied, the system will try to authenticate the user if not
858+
already (e.g. redirect the user to the login page). If the user is already
859+
logged in, the 403 "access denied" error page will be shown. See
860+
:doc:`/cookbook/controller/error_pages` for more information.
751861

752862
.. _book-security-securing-ip:
753863

754864
Securing by IP
755865
~~~~~~~~~~~~~~
756866

757867
Certain situations may arise when you may need to restrict access to a given
758-
route based on IP. This is particularly relevant in the case of
868+
path based on IP. This is particularly relevant in the case of
759869
:ref:`Edge Side Includes<edge-side-includes>` (ESI), for example. When ESI is
760870
enabled, it's recommended to secure access to ESI URLs. Indeed, some ESI may
761-
contain some private contents like the current logged in user's information. To
762-
prevent any direct access to these resources from a web browser by guessing the
763-
URL pattern, the ESI route must be secured to be only visible from the trusted
764-
reverse proxy cache.
871+
contain some private content like the current logged in user's information. To
872+
prevent any direct access to these resources from a web browser (by guessing the
873+
ESI URL pattern), the ESI route **must** be secured to be only visible from
874+
the trusted reverse proxy cache.
765875

766876
Here is an example of how you might secure all ESI routes that start with a
767877
given prefix, ``/esi``, from outside access:
@@ -775,28 +885,51 @@ given prefix, ``/esi``, from outside access:
775885
# ...
776886
access_control:
777887
- { path: ^/esi, roles: IS_AUTHENTICATED_ANONYMOUSLY, ip: 127.0.0.1 }
888+
- { path: ^/esi, roles: ROLE_NO_ACCESS }
778889
779890
.. code-block:: xml
780891
781892
<access-control>
782893
<rule path="^/esi" role="IS_AUTHENTICATED_ANONYMOUSLY" ip="127.0.0.1" />
894+
<rule path="^/esi" role="ROLE_NO_ACCESS" />
783895
</access-control>
784896
785897
.. code-block:: php
786898
787899
'access_control' => array(
788900
array('path' => '^/esi', 'role' => 'IS_AUTHENTICATED_ANONYMOUSLY', 'ip' => '127.0.0.1'),
901+
array('path' => '^/esi', 'role' => 'ROLE_NO_ACCESS'),
789902
),
790903
791-
.. _book-security-securing-channel:
904+
Here is how it works when the path is ``/esi/something`` coming from the
905+
``10.0.0.1`` IP:
906+
907+
* The first access control rule does not match and is ignored as the ``path``
908+
matches but the ``ip`` does not;
909+
910+
* The second access control rule matches (the only restriction being the
911+
``path`` and it matches): as the user cannot have the ``ROLE_NO_ACCESS``
912+
role as it's not defined, access is denied (the ``ROLE_NO_ACCESS`` role can
913+
be anything that does not match an existing role, it just serves as a trick
914+
to always deny access).
915+
916+
Now, if the same request comes from ``127.0.0.1``:
917+
918+
* Now, the first access control rule does match as both the ``path`` and the
919+
``ip`` match: access is allowed as the user always has the
920+
``IS_AUTHENTICATED_ANONYMOUSLY`` role.
921+
922+
* The second access rule is not examined as the first rule matched.
792923

793924
.. include:: /book/_security-2012-6431.rst.inc
794925

926+
.. _book-security-securing-channel:
927+
795928
Securing by Channel
796929
~~~~~~~~~~~~~~~~~~~
797930

798-
Much like securing based on IP, requiring the use of SSL is as simple as
799-
adding a new access_control entry:
931+
You can also require a user to access a URL via SSL; just use the
932+
``requires_channel`` argument in any ``access_control`` entries:
800933

801934
.. configuration-block::
802935

book/validation.rst

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -371,7 +371,7 @@ constraint, have several configuration options available. Suppose that the
371371
372372
// ...
373373
use Symfony\Component\Validator\Mapping\ClassMetadata;
374-
use Symfony\Component\Validator\Constraints\NotBlank;
374+
use Symfony\Component\Validator\Constraints\Choice;
375375
376376
class Author
377377
{

contributing/code/license.rst

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -14,7 +14,7 @@ According to `Wikipedia`_:
1414
The License
1515
-----------
1616

17-
Copyright (c) 2004-2012 Fabien Potencier
17+
Copyright (c) 2004-2013 Fabien Potencier
1818

1919
Permission is hereby granted, free of charge, to any person obtaining a copy
2020
of this software and associated documentation files (the "Software"), to deal

0 commit comments

Comments
 (0)