@@ -123,6 +123,91 @@ Using ``@Security``, this looks like:
123123 // ...
124124 }
125125
126+ Using Expressions for Complex Security Restrictions
127+ ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
128+
129+ If your security logic is a little bit more complex, you can use an `expression `_
130+ inside ``@Security ``. In the following example, a user can only access the
131+ controller if their email matches the value returned by the ``getAuthorEmail ``
132+ method on the ``Post `` object:
133+
134+ ..code-block ::php
135+
136+ use AppBundle\Entity\Post;
137+ use Sensio\Bundle\FrameworkExtraBundle\Configuration\Route;
138+ use Sensio\Bundle\FrameworkExtraBundle\Configuration\Security;
139+
140+ /**
141+ * @Route("/{id}/edit", name="admin_post_edit")
142+ * @Security("user.getEmail() == post.getAuthorEmail()")
143+ */
144+ public function editAction(Post $post)
145+ {
146+ // ...
147+ }
148+
149+ Notice that this requires the use of the `ParamConverter `_, which automatically
150+ queries for the ``Post `` object and puts it on the ``$post `` argument. This
151+ is what makes it possible to use the ``post `` variable in the expression.
152+
153+ This has one major drawback: an expression in an annotation cannot easily
154+ be reused in other parts of the application. Imagine that you want to add
155+ a link in a template that will only be seen by authors. Right now you'll
156+ need to repeat the expression code using Twig syntax:
157+
158+ ..code-block ::html+jinja
159+
160+ {% if app.user and app.user.email == post.authorEmail %}
161+ <a href=""> ... </a>
162+ {% endif %}
163+
164+ The easiest solution - if your logic is simple enough - is to add a new method
165+ to the ``Post `` entity that checks if a given user is its author:
166+
167+ ..code-block ::php
168+
169+ // src/AppBundle/Entity/Post.php
170+ // ...
171+
172+ class Post
173+ {
174+ // ...
175+
176+ /**
177+ * Is the given User the author of this Post?
178+ *
179+ * @return bool
180+ */
181+ public function isAuthor(User $user = null)
182+ {
183+ return $user && $user->getEmail() == $this->getAuthorEmail();
184+ }
185+ }
186+
187+ Now you can reuse this method both in the template and in the security expression:
188+
189+ ..code-block ::php
190+
191+ use AppBundle\Entity\Post;
192+ use Sensio\Bundle\FrameworkExtraBundle\Configuration\Security;
193+
194+ /**
195+ * @Route("/{id}/edit", name="admin_post_edit")
196+ * @Security("post.isAuthor(user)")
197+ */
198+ public function editAction(Post $post)
199+ {
200+ // ...
201+ }
202+
203+ ..code-block ::html+jinja
204+
205+ {% if post.isAuthor(app.user) %}
206+ <a href=""> ... </a>
207+ {% endif %}
208+
209+ .. _best-practices-directly-isGranted :
210+
126211Checking Permissions without @Security
127212--------------------------------------
128213
@@ -267,7 +352,9 @@ develop :doc:`your own user provider </cookbook/security/custom_provider>` and
267352
268353.. _`Security Cookbook Section` :http://symfony.com/doc/current/cookbook/security/index.html
269354.. _`security.yml` :http://symfony.com/doc/current/reference/configuration/security.html
355+ .. _`ParamConverter` :http://symfony.com/doc/current/bundles/SensioFrameworkExtraBundle/annotations/converters.html
270356.. _`@Security annotation` :http://symfony.com/doc/current/bundles/SensioFrameworkExtraBundle/annotations/security.html
271357.. _`security voter` :http://symfony.com/doc/current/cookbook/security/voters_data_permission.html
272358.. _`ACL's` :http://symfony.com/doc/current/cookbook/security/acl.html
359+ .. _`expression` :http://symfony.com/doc/current/components/expression_language/introduction.html
273360.. _`FOSUserBundle` :https://github.com/FriendsOfSymfony/FOSUserBundle