Skip to content

Commit dd1ca33

Browse files
committed
bug symfony#6495 Revert "minor symfony#6478 Replace reference to the "request" service (gerryvdm)" (WouterJ)
This PR was merged into the 2.7 branch. Discussion ---------- Revert "minor symfony#6478 Replace reference to the "request" service (gerryvdm)" This reverts commit 139e2e7, reversing changes made to 351f796. Similair to symfony#6490 but then for 2.7 Commits ------- 8da5a30 Revert "minor symfony#6478 Replace reference to the "request" service (gerryvdm)"
2 parents f5bcba6 + 8da5a30 commit dd1ca33

File tree

135 files changed

+9915
-1811
lines changed

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

135 files changed

+9915
-1811
lines changed

best_practices/forms.rst

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -21,7 +21,7 @@ form in its own PHP class::
2121

2222
use Symfony\Component\Form\AbstractType;
2323
use Symfony\Component\Form\FormBuilderInterface;
24-
use Symfony\Component\OptionsResolver\OptionsResolverInterface;
24+
use Symfony\Component\OptionsResolver\OptionsResolver;
2525

2626
class PostType extends AbstractType
2727
{
@@ -36,7 +36,7 @@ form in its own PHP class::
3636
;
3737
}
3838

39-
public function setDefaultOptions(OptionsResolverInterface $resolver)
39+
public function configureOptions(OptionsResolver $resolver)
4040
{
4141
$resolver->setDefaults(array(
4242
'data_class' => 'AppBundle\Entity\Post'

best_practices/security.rst

Lines changed: 159 additions & 17 deletions
Original file line numberDiff line numberDiff line change
@@ -74,14 +74,16 @@ Authorization (i.e. Denying Access)
7474
-----------------------------------
7575

7676
Symfony gives you several ways to enforce authorization, including the ``access_control``
77-
configuration in :doc:`security.yml </reference/configuration/security>` and
78-
using :ref:`isGranted <best-practices-directly-isGranted>` on the ``security.context``
77+
configuration in :doc:`security.yml </reference/configuration/security>`, the
78+
:ref:`@Security annotation <best-practices-security-annotation>` and using
79+
:ref:`isGranted <best-practices-directly-isGranted>` on the ``security.authorization_checker``
7980
service directly.
8081

8182
.. best-practice::
8283

8384
* For protecting broad URL patterns, use ``access_control``;
84-
* Check security directly on the ``security.context`` service whenever
85+
* Whenever possible, use the ``@Security`` annotation;
86+
* Check security directly on the ``security.authorization_checker`` service whenever
8587
you have a more complex situation.
8688

8789
There are also different ways to centralize your authorization logic, like
@@ -93,21 +95,132 @@ with a custom security voter or with ACL.
9395
* For restricting access to *any* object by *any* user via an admin
9496
interface, use the Symfony ACL.
9597

96-
.. _best-practices-directly-isGranted:
97-
.. _checking-permissions-without-security:
98+
.. _best-practices-security-annotation:
99+
100+
The @Security Annotation
101+
------------------------
98102

99-
Manually Checking Permissions
100-
-----------------------------
103+
For controlling access on a controller-by-controller basis, use the ``@Security``
104+
annotation whenever possible. It's easy to read and is placed consistently
105+
above each action.
101106

102-
If you cannot control the access based on URL patterns, you can always do
103-
the security checks in PHP:
107+
In our application, you need the ``ROLE_ADMIN`` in order to create a new post.
108+
Using ``@Security``, this looks like:
104109

105110
.. code-block:: php
106111
107-
use Symfony\Component\Security\Core\Exception\AccessDeniedException;
112+
use Sensio\Bundle\FrameworkExtraBundle\Configuration\Route;
113+
use Sensio\Bundle\FrameworkExtraBundle\Configuration\Security;
114+
// ...
115+
116+
/**
117+
* Displays a form to create a new Post entity.
118+
*
119+
* @Route("/new", name="admin_post_new")
120+
* @Security("has_role('ROLE_ADMIN')")
121+
*/
122+
public function newAction()
123+
{
124+
// ...
125+
}
126+
127+
Using Expressions for Complex Security Restrictions
128+
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
129+
130+
If your security logic is a little bit more complex, you can use an `expression`_
131+
inside ``@Security``. In the following example, a user can only access the
132+
controller if their email matches the value returned by the ``getAuthorEmail``
133+
method on the ``Post`` object:
134+
135+
.. code-block:: php
136+
137+
use AppBundle\Entity\Post;
138+
use Sensio\Bundle\FrameworkExtraBundle\Configuration\Route;
139+
use Sensio\Bundle\FrameworkExtraBundle\Configuration\Security;
140+
141+
/**
142+
* @Route("/{id}/edit", name="admin_post_edit")
143+
* @Security("user.getEmail() == post.getAuthorEmail()")
144+
*/
145+
public function editAction(Post $post)
146+
{
147+
// ...
148+
}
149+
150+
Notice that this requires the use of the `ParamConverter`_, which automatically
151+
queries for the ``Post`` object and puts it on the ``$post`` argument. This
152+
is what makes it possible to use the ``post`` variable in the expression.
153+
154+
This has one major drawback: an expression in an annotation cannot easily
155+
be reused in other parts of the application. Imagine that you want to add
156+
a link in a template that will only be seen by authors. Right now you'll
157+
need to repeat the expression code using Twig syntax:
158+
159+
.. code-block:: html+jinja
160+
161+
{% if app.user and app.user.email == post.authorEmail %}
162+
<a href=""> ... </a>
163+
{% endif %}
108164

165+
The easiest solution - if your logic is simple enough - is to add a new method
166+
to the ``Post`` entity that checks if a given user is its author:
167+
168+
.. code-block:: php
169+
170+
// src/AppBundle/Entity/Post.php
109171
// ...
110172
173+
class Post
174+
{
175+
// ...
176+
177+
/**
178+
* Is the given User the author of this Post?
179+
*
180+
* @return bool
181+
*/
182+
public function isAuthor(User $user = null)
183+
{
184+
return $user && $user->getEmail() == $this->getAuthorEmail();
185+
}
186+
}
187+
188+
Now you can reuse this method both in the template and in the security expression:
189+
190+
.. code-block:: php
191+
192+
use AppBundle\Entity\Post;
193+
use Sensio\Bundle\FrameworkExtraBundle\Configuration\Security;
194+
195+
/**
196+
* @Route("/{id}/edit", name="admin_post_edit")
197+
* @Security("post.isAuthor(user)")
198+
*/
199+
public function editAction(Post $post)
200+
{
201+
// ...
202+
}
203+
204+
.. code-block:: html+jinja
205+
206+
{% if post.isAuthor(app.user) %}
207+
<a href=""> ... </a>
208+
{% endif %}
209+
210+
.. _best-practices-directly-isGranted:
211+
.. _checking-permissions-without-security:
212+
.. _manually-checking-permissions:
213+
214+
Checking Permissions without @Security
215+
--------------------------------------
216+
217+
The above example with ``@Security`` only works because we're using the
218+
:ref:`ParamConverter <best-practices-paramconverter>`, which gives the expression
219+
access to the a ``post`` variable. If you don't use this, or have some other
220+
more advanced use-case, you can always do the same security check in PHP:
221+
222+
.. code-block:: php
223+
111224
/**
112225
* @Route("/{id}/edit", name="admin_post_edit")
113226
*/
@@ -121,7 +234,16 @@ the security checks in PHP:
121234
}
122235
123236
if (!$post->isAuthor($this->getUser())) {
124-
throw new AccessDeniedException();
237+
$this->denyAccessUnlessGranted('edit', $post);
238+
239+
// or without the shortcut:
240+
//
241+
// use Symfony\Component\Security\Core\Exception\AccessDeniedException;
242+
// ...
243+
//
244+
// if (!$this->get('security.authorization_checker')->isGranted('edit', $post)) {
245+
// throw $this->createAccessDeniedException();
246+
// }
125247
}
126248
127249
// ...
@@ -192,13 +314,23 @@ To enable the security voter in the application, define a new service:
192314
tags:
193315
- { name: security.voter }
194316
195-
Now, you can use the voter with the ``security.context`` service:
317+
Now, you can use the voter with the ``@Security`` annotation:
196318

197319
.. code-block:: php
198320
199-
use Symfony\Component\Security\Core\Exception\AccessDeniedException;
321+
/**
322+
* @Route("/{id}/edit", name="admin_post_edit")
323+
* @Security("is_granted('edit', post)")
324+
*/
325+
public function editAction(Post $post)
326+
{
327+
// ...
328+
}
200329
201-
// ...
330+
You can also use this directly with the ``security.authorization_checker`` service or
331+
via the even easier shortcut in a controller:
332+
333+
.. code-block:: php
202334
203335
/**
204336
* @Route("/{id}/edit", name="admin_post_edit")
@@ -207,9 +339,16 @@ Now, you can use the voter with the ``security.context`` service:
207339
{
208340
$post = // query for the post ...
209341
210-
if (!$this->get('security.context')->isGranted('edit', $post)) {
211-
throw new AccessDeniedException();
212-
}
342+
$this->denyAccessUnlessGranted('edit', $post);
343+
344+
// or without the shortcut:
345+
//
346+
// use Symfony\Component\Security\Core\Exception\AccessDeniedException;
347+
// ...
348+
//
349+
// if (!$this->get('security.authorization_checker')->isGranted('edit', $post)) {
350+
// throw $this->createAccessDeniedException();
351+
// }
213352
}
214353
215354
Learn More
@@ -230,4 +369,7 @@ If your company uses a user login method not supported by Symfony, you can
230369
develop :doc:`your own user provider </cookbook/security/custom_provider>` and
231370
:doc:`your own authentication provider </cookbook/security/custom_authentication_provider>`.
232371

372+
.. _`ParamConverter`: http://symfony.com/doc/current/bundles/SensioFrameworkExtraBundle/annotations/converters.html
373+
.. _`@Security annotation`: http://symfony.com/doc/current/bundles/SensioFrameworkExtraBundle/annotations/security.html
374+
.. _`expression`: http://symfony.com/doc/current/components/expression_language/introduction.html
233375
.. _`FOSUserBundle`: https://github.com/FriendsOfSymfony/FOSUserBundle

book/controller.rst

Lines changed: 47 additions & 28 deletions
Original file line numberDiff line numberDiff line change
@@ -421,22 +421,26 @@ method is just a helper method that generates the URL for a given route.
421421
Redirecting
422422
~~~~~~~~~~~
423423

424-
To redirect the user's browser to another page of your app, use the ``generateUrl()``
425-
method in combination with another helper method called
426-
:method:`Symfony\\Bundle\\FrameworkBundle\\Controller\\Controller::redirect`
427-
which takes a URL as an argument::
424+
If you want to redirect the user to another page, use the ``redirectToRoute()`` method::
428425

429426
public function indexAction()
430427
{
431-
return $this->redirect($this->generateUrl('homepage'));
428+
return $this->redirectToRoute('homepage');
429+
430+
// redirectToRoute is equivalent to using redirect() and generateUrl() together:
431+
// return $this->redirect($this->generateUrl('homepage'));
432432
}
433433

434-
By default, the ``redirect()`` method performs a 302 (temporary) redirect. To
435-
perform a 301 (permanent) redirect, modify the second argument::
434+
.. versionadded:: 2.6
435+
The ``redirectToRoute()`` method was introduced in Symfony 2.6. Previously (and still now), you
436+
could use ``redirect()`` and ``generateUrl()`` together for this (see the example above).
437+
438+
By default, the ``redirectToRoute()`` method performs a 302 (temporary) redirect. To
439+
perform a 301 (permanent) redirect, modify the third argument::
436440

437441
public function indexAction()
438442
{
439-
return $this->redirect($this->generateUrl('homepage'), 301);
443+
return $this->redirectToRoute('homepage', array(), 301);
440444
}
441445

442446
To redirect to an *external* site, use ``redirect()`` and pass it the external URL::
@@ -450,12 +454,16 @@ For more information, see the :doc:`Routing chapter </book/routing>`.
450454

451455
.. tip::
452456

453-
The ``redirect()`` method is simply a shortcut that creates a ``Response``
454-
object that specializes in redirecting the user. It's equivalent to::
457+
The ``redirectToRoute()`` method is simply a shortcut that creates a
458+
``Response`` object that specializes in redirecting the user. It's
459+
equivalent to::
455460

456461
use Symfony\Component\HttpFoundation\RedirectResponse;
457462

458-
return new RedirectResponse($this->generateUrl('homepage'));
463+
public function indexAction()
464+
{
465+
return new RedirectResponse($this->generateUrl('homepage'));
466+
}
459467

460468
.. index::
461469
single: Controller; Rendering templates
@@ -520,12 +528,15 @@ need::
520528

521529
$mailer = $this->get('mailer');
522530

523-
What other services exist? To list all services, use the ``container:debug``
531+
What other services exist? To list all services, use the ``debug:container``
524532
console command:
525533

526534
.. code-block:: bash
527535
528-
$ php app/console container:debug
536+
$ php app/console debug:container
537+
538+
.. versionadded:: 2.6
539+
Prior to Symfony 2.6, this command was called ``container:debug``.
529540

530541
For more information, see the :doc:`/book/service_container` chapter.
531542

@@ -649,12 +660,14 @@ For example, imagine you're processing a form submission::
649660
if ($form->isValid()) {
650661
// do some sort of processing
651662

652-
$request->getSession()->getFlashBag()->add(
663+
$this->addFlash(
653664
'notice',
654665
'Your changes were saved!'
655666
);
656667

657-
return $this->redirect($this->generateUrl(...));
668+
// $this->addFlash is equivalent to $this->get('session')->getFlashBag()->add
669+
670+
return $this->redirectToRoute(...);
658671
}
659672

660673
return $this->render(...);
@@ -750,7 +763,7 @@ headers and content that's sent back to the client::
750763
use Symfony\Component\HttpFoundation\Response;
751764

752765
// create a simple Response with a 200 status code (the default)
753-
$response = new Response('Hello '.$name, 200);
766+
$response = new Response('Hello '.$name, Response::HTTP_OK);
754767

755768
// create a JSON-response with a 200 status code
756769
$response = new Response(json_encode(array('name' => $name)));
@@ -816,24 +829,30 @@ The target controller method might look something like this::
816829
Just like when creating a controller for a route, the order of the arguments of
817830
``fancyAction()`` doesn't matter: the matching is done by name.
818831

819-
Checking the Validity of a CSRF Token inside Controller
820-
-------------------------------------------------------
832+
.. _checking-the-validity-of-a-csrf-token:
821833

822-
You may sometimes want to use :ref:`CSRF protection <forms-csrf>` in a controller where
823-
you don't have a Symfony form.
834+
Validating a CSRF Token
835+
-----------------------
824836

825-
If, for example, you're doing a DELETE action, you can use the
826-
:method:`Symfony\\Component\\Form\\Extension\\Csrf\\CsrfProvider\\CsrfProviderInterface::isCsrfTokenValid`
837+
Sometimes, you want to use CSRF protection in an action where you don't want to
838+
use the Symfony Form component. If, for example, you're doing a DELETE action,
839+
you can use the :method:`Symfony\\Bundle\\FrameworkBundle\\Controller\\Controller::isCsrfTokenValid`
827840
method to check the CSRF token::
828841

829-
$csrf = $this->container->get('form.csrf_provider');
842+
if ($this->isCsrfTokenValid('token_id', $submittedToken)) {
843+
// ... do something, like deleting an object
844+
}
830845

831-
$intention = 'authenticate';
832-
$token = $csrf->generateCsrfToken($intention);
846+
.. versionadded:: 2.6
847+
The ``isCsrfTokenValid()`` shortcut method was introduced in Symfony 2.6.
848+
It is equivalent to executing the following code:
833849

834-
if (!$csrf->isCsrfTokenValid($intention, $token)) {
835-
// CSRF token invalid! Do something, like redirect with an error.
836-
}
850+
.. code-block:: php
851+
852+
use Symfony\Component\Security\Csrf\CsrfToken;
853+
854+
$this->get('security.csrf.token_manager')
855+
->isTokenValid(new CsrfToken('token_id', 'TOKEN'));
837856
838857
Final Thoughts
839858
--------------

0 commit comments

Comments
 (0)