New in Symfony 2.8: Simpler Security Voters
December 22, 2015 • Published by Javier Eguiluz
Warning: This post is about an unsupported Symfony version. Some of this information may be out of date. Read the most recent Symfony Docs.
Security Voters are the recommended way to check for user permissions in
Symfony applications. In Symfony 2.6 we introduced an AbstractVoter
class
to reduce the boilerplate code needed to define a voter. Instead of implementing
the VoterInterface
, you could just extend from AbstractVoter
.
In Symfony 2.8 we have simplified voters a bit more thanks to the new Voter class.
This article shows the main changes needed to upgrade a voter to use the new class.
The first step is to make your voters extend the new Voter
class instead of
the previous AbstractVoter
class:
1 2 3 4 5 6 7 8 9 10
namespace AppBundle\Security\Voter;
use Symfony\Component\Security\Core\Authorization\Voter\AbstractVoter;
use Symfony\Component\Security\Core\Authorization\Voter\Voter;
// Before Symfony 2.8
class CustomVoter extends AbstractVoter { ... }
// In Symfony 2.8
class CustomVoter extends Voter { ... }
The second important change is that supportsAttribute()
and supportsClass()
from VoterInterface
are deprecated in Symfony 2.8 and they are removed in 3.0.
These methods (and the getSupportedAttributes()
and getSupportedClasses()
methods of the AbstractVoter
class) have been replaced by the new
supports($attribute, $subject)
method, which checks if the given attribute
and "subject" are supported by the voter.
The "subject" can be anything you want to secure. Although it's usually an object
(e.g. BlogPost
, Invoice
, Product
) you can also pass simple variables
like a string (e.g. the path to a resource to be downloaded).
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32
// Before Symfony 2.8
class CustomVoter extends AbstractVoter
{
const VIEW = 'view';
const EDIT = 'edit';
protected function getSupportedAttributes()
{
return array(self::VIEW, self::EDIT);
}
protected function getSupportedClasses()
{
return array('AppBundle\Entity\BlogPost');
}
}
// In Symfony 2.8
use AppBundle\Entity\BlogPost;
class CustomVoter extends Voter
{
const VIEW = 'view';
const EDIT = 'edit';
public function supports($attribute, $subject)
{
return $subject instanceof BlogPost && in_array($attribute, array(
self::VIEW, self::EDIT
));
}
}
The last change is the introduction of a new voteOnAttribute()
method which
performs a single access check operation on a given attribute, "subject" and the
security token (this is almost identical to AbstractVoter
class):
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50
// ...
use Symfony\Component\Security\Core\User\UserInterface;
use AppBundle\Entity\User;
// Before Symfony 2.8
class CustomVoter extends AbstractVoter
{
// ...
protected function isGranted($attribute, $post, $user = null)
{
if ($attribute === self::VIEW && !$post->isPrivate()) {
return true;
}
if (!$user instanceof UserInterface) {
return false;
}
if ($attribute === self::EDIT && $user->getId() === $post->getOwner()->getId()) {
return true;
}
return false;
}
}
// In Symfony 2.8
class CustomVoter extends Voter
{
// ...
protected function voteOnAttribute($attribute, $post, TokenInterface $token)
{
if ($attribute === self::VIEW && !$post->isPrivate()) {
return true;
}
$user = $token->getUser();
if (!$user instanceof User) {
return false;
}
if ($attribute === self::EDIT && $user->getId() === $post->getOwner()->getId()) {
return true;
}
return false;
}
}
In summary, the new Voter
class requires you to implement two methods:
1 2 3 4 5 6 7
use Symfony\Component\Security\Core\Authentication\Token\TokenInterface;
abstract class Voter implements VoterInterface
{
abstract protected function supports($attribute, $subject);
abstract protected function voteOnAttribute($attribute, $subject, TokenInterface $token);
}
The existing AbstractVoter
class has been deprecated since version 2.8 and it's been removed in 3.0. If you plan to upgrade to Symfony 3 soon, start updating
your voters to use the Voter
class instead.
Help the Symfony project!
As with any Open-Source project, contributing code or documentation is the most common way to help, but we also have a wide range of sponsoring opportunities.
Comments are closed.
To ensure that comments stay relevant, they are closed for old posts.
if (!$user instanceof User) {
return false;
}
use Symfony\Component\Security\Core\User\UserInterface; ?