diff --git a/app/Resources/views/blog/post_show.html.twig b/app/Resources/views/blog/post_show.html.twig
index d1cbf36f0..37e9dc2d9 100644
--- a/app/Resources/views/blog/post_show.html.twig
+++ b/app/Resources/views/blog/post_show.html.twig
@@ -55,7 +55,7 @@
{% endblock %}
{% block sidebar %}
- {% if post.isAuthor(app.user) %}
+ {% if is_granted('edit', post) %}
{{ 'action.edit_post'|trans }}
diff --git a/app/config/services.yml b/app/config/services.yml
index a3c388a99..7fedcc5d3 100644
--- a/app/config/services.yml
+++ b/app/config/services.yml
@@ -48,6 +48,14 @@ services:
tags:
- { name: kernel.event_subscriber }
+ # To inject the voter into the security layer, you must declare it as a service and tag it with security.voter.
+ # See http://symfony.com/doc/current/security/voters.html#configuring-the-voter
+ app.post_voter:
+ class: AppBundle\Security\PostVoter
+ public: false
+ tags:
+ - { name: security.voter }
+
# Uncomment the following lines to define a service for the Post Doctrine repository.
# It's not mandatory to create these services, but if you use repositories a lot,
# these services simplify your code:
diff --git a/src/AppBundle/Controller/Admin/BlogController.php b/src/AppBundle/Controller/Admin/BlogController.php
index 98042098c..14f886b9f 100644
--- a/src/AppBundle/Controller/Admin/BlogController.php
+++ b/src/AppBundle/Controller/Admin/BlogController.php
@@ -119,12 +119,9 @@ public function newAction(Request $request)
*/
public function showAction(Post $post)
{
- // This security check can also be performed:
- // 1. Using an annotation: @Security("post.isAuthor(user)")
- // 2. Using a "voter" (see http://symfony.com/doc/current/cookbook/security/voters_data_permission.html)
- if (!$post->isAuthor($this->getUser())) {
- throw $this->createAccessDeniedException('Posts can only be shown to their authors.');
- }
+ // This security check can also be performed
+ // using an annotation: @Security("is_granted('show', post)")
+ $this->denyAccessUnlessGranted('show', $post, 'Posts can only be shown to their authors.');
return $this->render('admin/blog/show.html.twig', [
'post' => $post,
@@ -139,9 +136,7 @@ public function showAction(Post $post)
*/
public function editAction(Post $post, Request $request)
{
- if (!$post->isAuthor($this->getUser())) {
- throw $this->createAccessDeniedException('Posts can only be edited by their authors.');
- }
+ $this->denyAccessUnlessGranted('edit', $post, 'Posts can only be edited by their authors.');
$entityManager = $this->getDoctrine()->getManager();
@@ -169,11 +164,10 @@ public function editAction(Post $post, Request $request)
*
* @Route("/{id}/delete", name="admin_post_delete")
* @Method("POST")
- * @Security("post.isAuthor(user)")
+ * @Security("is_granted('delete', post)")
*
* The Security annotation value is an expression (if it evaluates to false,
* the authorization mechanism will prevent the user accessing this resource).
- * The isAuthor() method is defined in the AppBundle\Entity\Post entity.
*/
public function deleteAction(Request $request, Post $post)
{
diff --git a/src/AppBundle/Security/PostVoter.php b/src/AppBundle/Security/PostVoter.php
new file mode 100644
index 000000000..cd2269fe7
--- /dev/null
+++ b/src/AppBundle/Security/PostVoter.php
@@ -0,0 +1,61 @@
+
+ *
+ * For the full copyright and license information, please view the LICENSE
+ * file that was distributed with this source code.
+ */
+
+namespace AppBundle\Security;
+
+use AppBundle\Entity\Post;
+use AppBundle\Entity\User;
+use Symfony\Component\Security\Core\Authentication\Token\TokenInterface;
+use Symfony\Component\Security\Core\Authorization\Voter\Voter;
+
+/**
+ * It grants or denies permissions for actions related to blog posts (such as
+ * showing, editing and deleting posts).
+ *
+ * See http://symfony.com/doc/current/security/voters.html
+ *
+ * @author Yonel Ceruto
+ */
+class PostVoter extends Voter
+{
+ // Defining these constants is overkill for this simple application, but for real
+ // applications, it's a recommended practice to avoid relying on "magic strings"
+ const SHOW = 'show';
+ const EDIT = 'edit';
+ const DELETE = 'delete';
+
+ /**
+ * {@inheritdoc}
+ */
+ protected function supports($attribute, $subject)
+ {
+ // this voter is only executed for three specific permissions on Post objects
+ return $subject instanceof Post && in_array($attribute, [self::SHOW, self::EDIT, self::DELETE]);
+ }
+
+ /**
+ * {@inheritdoc}
+ */
+ protected function voteOnAttribute($attribute, $post, TokenInterface $token)
+ {
+ $user = $token->getUser();
+
+ // the user must be logged in; if not, deny permission
+ if (!$user instanceof User) {
+ return false;
+ }
+
+ // the logic of this voter is pretty simple: if the logged user is the
+ // author of the given blog post, grant permission; otherwise, deny it.
+ // (the supports() method guarantees that $post is a Post object)
+ return $user === $post->getAuthor();
+ }
+}