Quantcast
Channel: Symfony Blog
Viewing all articles
Browse latest Browse all 3057

New in Symfony 2.6: Simpler Security Voters

$
0
0
Roman MarintšenkoRyan Weaver

Contributed by
Roman Marintšenko andRyan Weaver in #11183.

Security Voters provide a mechanism to set up fine-grained restrictions in Symfony applications. The main advantage over ACLs is that they are an order of magnitude easier to set up, configure and use.

In previous Symfony versions, voters implemented the VoterInterface interface, which has the following signature:

1
2
3
4
5
6
interfaceVoterInterface{publicfunctionsupportsAttribute($attribute);publicfunctionsupportsClass($class);publicfunctionvote(TokenInterface$token,$object,array$attributes);}

Implementing this interface is pretty easy, but the resulting code was usually a bit bloated, as demonstrated by the following 83 lines of code needed to define a simple voter:

 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
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
// src/Acme/DemoBundle/Security/Authorization/Voter/PostVoter.phpnamespaceAcme\DemoBundle\Security\Authorization\Voter;useSymfony\Component\Security\Core\Authorization\Voter\VoterInterface;useSymfony\Component\Security\Core\Authentication\Token\TokenInterface;useSymfony\Component\Security\Core\User\UserInterface;classPostVoterimplementsVoterInterface{constVIEW='view';constEDIT='edit';publicfunctionsupportsAttribute($attribute){returnin_array($attribute,array(self::VIEW,self::EDIT,));}publicfunctionsupportsClass($class){$supportedClass='Acme\DemoBundle\Entity\Post';return$supportedClass===$class||is_subclass_of($class,$supportedClass);}/**     * @var \Acme\DemoBundle\Entity\Post $post     */publicfunctionvote(TokenInterface$token,$post,array$attributes){// check if class of this object is supported by this voterif(!$this->supportsClass(get_class($post))){returnVoterInterface::ACCESS_ABSTAIN;}// check if the voter is used correct, only allow one attribute// this isn't a requirement, it's just one easy way for you to// design your voterif(1!==count($attributes)){thrownew\InvalidArgumentException('Only one attribute is allowed for VIEW or EDIT');}// set the attribute to check against$attribute=$attributes[0];// check if the given attribute is covered by this voterif(!$this->supportsAttribute($attribute)){returnVoterInterface::ACCESS_ABSTAIN;}// get current logged in user$user=$token->getUser();// make sure there is a user object (i.e. that the user is logged in)if(!$userinstanceofUserInterface){returnVoterInterface::ACCESS_DENIED;}switch($attribute){caseself::VIEW:// the data object could have for example a method isPrivate()// which checks the Boolean attribute $privateif(!$post->isPrivate()){returnVoterInterface::ACCESS_GRANTED;}break;caseself::EDIT:// we assume that our data object has a method getOwner() to// get the current owner user entity for this data objectif($user->getId()===$post->getOwner()->getId()){returnVoterInterface::ACCESS_GRANTED;}break;}returnVoterInterface::ACCESS_DENIED;}}

As the result of the Symfony DX initiative, Symfony 2.6 will allow to define much simpler security voters. To do so, use the new AbstractVoter class which implements VoterInterface and defines the following methods:

1
2
3
4
5
6
7
8
9
abstractclassAbstractVoterimplementsVoterInterface{publicfunctionsupportsAttribute($attribute);publicfunctionsupportsClass($class);publicfunctionvote(TokenInterface$token,$object,array$attributes);abstractprotectedfunctiongetSupportedClasses();abstractprotectedfunctiongetSupportedAttributes();abstractprotectedfunctionisGranted($attribute,$object,$user=null);}

The three methods supportsAttribute(), supportsClass() and vote() help you reduce the boilerplate code of the voter and let you focus on the specific business logic of your application. As a result, the same voter shown above now takes only 41 lines of code:

 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
// src/Acme/DemoBundle/Security/Authorization/Voter/PostVoter.phpnamespaceAcme\DemoBundle\Security\Authorization\Voter;useSymfony\Component\Security\Core\Authorization\Voter\AbstractVoter;useSymfony\Component\Security\Core\User\UserInterface;classPostVoterextendsAbstractVoter{constVIEW='view';constEDIT='edit';protectedfunctiongetSupportedAttributes(){returnarray(self::VIEW,self::EDIT);}protectedfunctiongetSupportedClasses(){returnarray('Acme\DemoBundle\Entity\Post');}protectedfunctionisGranted($attribute,$post,$user=null){// make sure there is a user object (i.e. that the user is logged in)if(!$userinstanceofUserInterface){returnfalse;}// custom business logic to decide if the given user can view// and/or edit the given postif($attribute==self::VIEW&&!$post->isPrivate()){returntrue;}if($attribute==self::EDIT&&$user->getId()===$post->getOwner()->getId()){returntrue;}returnfalse;}}

Writing less code to get the same results as before boosts your productivity. That's our obsession since the introduction of the DX initiative and Symfony 2.6 will be the first version to embrace this new philosophy.


Be trained by Symfony experts - 2014-10-06 Cologne - 2014-10-06 Cologne - 2014-10-08 Cologne

Viewing all articles
Browse latest Browse all 3057

Trending Articles