Skip to content

Commit c14e9db

Browse files
committed
Add security post voter
1 parent ae9381d commit c14e9db

File tree

4 files changed

+79
-12
lines changed

4 files changed

+79
-12
lines changed

app/Resources/views/blog/post_show.html.twig

+1-1
Original file line numberDiff line numberDiff line change
@@ -55,7 +55,7 @@
5555
{% endblock %}
5656

5757
{% block sidebar %}
58-
{% if post.isAuthor(app.user) %}
58+
{% if is_granted('edit', post) %}
5959
<div class="section">
6060
<a class="btn btn-lg btn-block btn-success" href="{{ path('admin_post_edit', { id: post.id }) }}">
6161
<i class="fa fa-edit" aria-hidden="true"></i> {{ 'action.edit_post'|trans }}

app/config/services.yml

+8
Original file line numberDiff line numberDiff line change
@@ -48,6 +48,14 @@ services:
4848
tags:
4949
- { name: kernel.event_subscriber }
5050

51+
# To inject the voter into the security layer, you must declare it as a service and tag it with security.voter.
52+
# See http://symfony.com/doc/current/security/voters.html#configuring-the-voter
53+
app.post_voter:
54+
class: AppBundle\Security\PostVoter
55+
public: false
56+
tags:
57+
- { name: security.voter }
58+
5159
# Uncomment the following lines to define a service for the Post Doctrine repository.
5260
# It's not mandatory to create these services, but if you use repositories a lot,
5361
# these services simplify your code:

src/AppBundle/Controller/Admin/BlogController.php

+5-11
Original file line numberDiff line numberDiff line change
@@ -119,12 +119,9 @@ public function newAction(Request $request)
119119
*/
120120
public function showAction(Post $post)
121121
{
122-
// This security check can also be performed:
123-
// 1. Using an annotation: @Security("post.isAuthor(user)")
124-
// 2. Using a "voter" (see http://symfony.com/doc/current/cookbook/security/voters_data_permission.html)
125-
if (!$post->isAuthor($this->getUser())) {
126-
throw $this->createAccessDeniedException('Posts can only be shown to their authors.');
127-
}
122+
// This security check can also be performed
123+
// using an annotation: @Security("is_granted('show', post)")
124+
$this->denyAccessUnlessGranted('show', $post, 'Posts can only be shown to their authors.');
128125

129126
return $this->render('admin/blog/show.html.twig', [
130127
'post' => $post,
@@ -139,9 +136,7 @@ public function showAction(Post $post)
139136
*/
140137
public function editAction(Post $post, Request $request)
141138
{
142-
if (!$post->isAuthor($this->getUser())) {
143-
throw $this->createAccessDeniedException('Posts can only be edited by their authors.');
144-
}
139+
$this->denyAccessUnlessGranted('edit', $post, 'Posts can only be edited by their authors.');
145140

146141
$entityManager = $this->getDoctrine()->getManager();
147142

@@ -169,11 +164,10 @@ public function editAction(Post $post, Request $request)
169164
*
170165
* @Route("/{id}/delete", name="admin_post_delete")
171166
* @Method("POST")
172-
* @Security("post.isAuthor(user)")
167+
* @Security("is_granted('delete', post)")
173168
*
174169
* The Security annotation value is an expression (if it evaluates to false,
175170
* the authorization mechanism will prevent the user accessing this resource).
176-
* The isAuthor() method is defined in the AppBundle\Entity\Post entity.
177171
*/
178172
public function deleteAction(Request $request, Post $post)
179173
{

src/AppBundle/Security/PostVoter.php

+65
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,65 @@
1+
<?php
2+
3+
/*
4+
* This file is part of the Symfony package.
5+
*
6+
* (c) Fabien Potencier <[email protected]>
7+
*
8+
* For the full copyright and license information, please view the LICENSE
9+
* file that was distributed with this source code.
10+
*/
11+
12+
namespace AppBundle\Security;
13+
14+
use AppBundle\Entity\Post;
15+
use AppBundle\Entity\User;
16+
use Symfony\Component\Security\Core\Authentication\Token\TokenInterface;
17+
use Symfony\Component\Security\Core\Authorization\Voter\Voter;
18+
19+
/**
20+
* Decide whether the current user can show, edit or delete a Post object.
21+
*
22+
* See http://symfony.com/doc/current/security/voters.html
23+
*
24+
* @author Yonel Ceruto <[email protected]>
25+
*/
26+
class PostVoter extends Voter
27+
{
28+
const SHOW = 'show';
29+
const EDIT = 'edit';
30+
const DELETE = 'delete';
31+
32+
/**
33+
* {@inheritdoc}
34+
*/
35+
protected function supports($attribute, $subject)
36+
{
37+
// if the attribute isn't one we support, return false
38+
if (!in_array($attribute, [self::SHOW, self::EDIT, self::DELETE])) {
39+
return false;
40+
}
41+
42+
// only vote on Post objects inside this voter
43+
if (!$subject instanceof Post) {
44+
return false;
45+
}
46+
47+
return true;
48+
}
49+
50+
/**
51+
* {@inheritdoc}
52+
*/
53+
protected function voteOnAttribute($attribute, $post, TokenInterface $token)
54+
{
55+
$user = $token->getUser();
56+
57+
if (!$user instanceof User) {
58+
// the user must be logged in; if not, deny access
59+
return false;
60+
}
61+
62+
// you know $post is a Post object, thanks to supports
63+
return $user === $post->getAuthor();
64+
}
65+
}

0 commit comments

Comments
 (0)