Yes, it's called Voter now! And we made it *even* a little bit easier to use. For people using Symfony 3, that's it's definitely important to know what the class is called now.
If you use the Download button on this page and "Download Course Code", you'll get the starting and ending version of the code. If you're familiar with Symfony, you can get either codebase working quickly by opening a terminal, moving into the directory (e.g. start), then running:
It's very cool when you *focus* on new features in latest version like `Simpler Security Voters`, `security.authorization_checker`, `$this->isGranted()`, etc. in your screencasts. Thanks for this!
Hmm, yea, I see what you're asking - I have had this urge before too :). So, *maybe*. The question is: will the user (by taking normal actions - e.g. clicking a link) be taken to a page where the cookie will not be ready to be eaten? If so, then if you include this logic in a voter, then if the user hits this page while navigating, they will see the 403 Access Denied page... which isn't a great user experience. So if it would be normal for a user to access this page and you want to display a nice message like "Hey, this cookie is still baking!", then I would not use voters. But if it is quite abnormal for the user to access this page (maybe they are getting clever and manually changing the URL to a cookie that is not ready yet), then yea - user a voter!
If you wanted an admin to be able to dynamically grant user with a role how would you go about it? I meant there would be several problems: - creating a roles entity that would store all available roles in the database, but now there is no single source for roles - after some time you would end up with some roles that exist in the code base but do not exist in the database and the other way around. - should user roles and other strings that are to have the same functionality - ROLE_USER, ALLOW_EDIT be stored in the same column - roles of the user? Thanks for your time guys, you are awesome! :)
Phew, good quest - this can be a confusing issue, because *a lot* of what you should do depends on your exact situation. I'll make some points :)
1) The easiest way to assign roles to users if via a User.roles field. I don't think creating a Role entity makes sense - because it doesn't make sense for an admin to be able to create a new role (e.g. if they created ROLE_MANAGE_BLOG, that wouldn't be used in the code anywhere).
2) Normally, there is in fact no single source for roles - you just kind of use them in your controller with isGranted() or in access_control. If you want a single source (not a bad idea), I like creating a class - e.g. SecurityRoles - that has nothing on it but some constants for each role: const ROLE_MANAGE_BLOG = 'ROLE_MANAGE_BLOG'. Then at least you can reference these within your code and you have one spot to look at for all the roles. You *are* right that eventually, you could have roles stored in a user that aren't used in your codebase. But I don't think that's a problem - it's just a little bit of extra data stored in the user. And if the user's roles were ever updated by the admin, it would correctly save *without* the old roles.
3) Assuming you have a User.roles field, this would *only* store things like ROLE_USER, ROLE_MANAGE_BLOG, ROLE_*, etc. It would NOT store things like ALLOW_EDIT. Why? Because when you say isGranted('ALLOW_EDIT', $blogPost), this does not check to see if the user has the ALLOW_EDIT role. Instead, it calls your custom voter that handles ALLOW_EDIT and then you perform whatever logic you need. In technical terms, "roles" are only those things that start with ROLE_ (and are handled by the RoleVoter, which checks to see if the user has this role). Anything else you pass to isGranted is called an "attribute" - and your custom handler takes care of those.
Thanks for taking the time to come out with this great tutorial. I've run into an issue where the var_dump(...);die; from the CookieVoter/isGranted function is not appearing, instead I get the 403 error in it's place.
- Using the following in the CookieController/NomAction: if (!$this->get('security.context')->isGranted('NOM', $cookie)) as I'm not running Symfony 2.6 (running 2.5.2).
- I've registered the services.yml as shown in the video in the config.yml but it doesn't appear to be calling the voter?
So... this is a Symfony 2 lesson included in a Symfony 3 course... While the principles of Voters may remain the same, all of them implementation and architecture has drastically changed in Symfony 3.
## extends Voter instead of extends AbstractVoter This completely changes the way the a Voter is implemented, now with two VERY different methods to implement:
supports( $attribute, $subject ) { return (bool); }```
voteOnAttribute( $attribute, $subject, $token ) { return (bool); }`
optionally override `vote( $token, $subject, array $attributes )```
Yes, I can go to the documentation and figure all this out myself... but then I wouldn't need to bother with this course.
You're 100% right :). I've just removed this course from the Symfony 3 track, and moved the New Awesome of Symfony 3.0 course from extra credit into this old spot (because it has an updated chapter with the new way to do voters). The simplification we added to the voters is definitely a big change (in terms of the code you actually write).
Yes! But... also no :). Your solution works just fine, unless you're taking advantage of the "role hierarchy" in security.yml. But if you aren't, then you can keep it simple and do this. In Symfony 2.8, we made a change that allows injecting the "security.access.decision_manager" service directly, which has a decide() method you can call on it (https://github.com/symfony/... - similar to isGranted(), but you pass it the token and not the user. We still need one small change, however, to make this useable with the AbstractVoter (i.e. the problem is that you're not given the token).
Long way to say again, YES! But no... but in 2.8, you should be able to do it the "right" way without injecting the container.
26 Comments
it might be worth mentioning that the class AbstractVoter doesn't exits in Symfony 3 :(
Yes, it's called Voter now! And we made it *even* a little bit easier to use. For people using Symfony 3, that's it's definitely important to know what the class is called now.
Cheers!
I love documentation http://symfony.com/doc/curr...
Do you provide a download for the script to create the sf2_voters database?
Hey Joe!
If you use the Download button on this page and "Download Course Code", you'll get the starting and ending version of the code. If you're familiar with Symfony, you can get either codebase working quickly by opening a terminal, moving into the directory (e.g. start), then running:
But the last command loads up the database by using the fixture files you see here: https://github.com/knpuniversity/symfony-voters/tree/master/src/AppBundle/DataFixtures/ORM
Let me know if that helps!
Great! Thanks for your help and thanks for the lesson.
Thank you very much for ths tutorial! This tutorial is incredibly simple and informative, I appreciate it!
It's very cool when you *focus* on new features in latest version like `Simpler Security Voters`, `security.authorization_checker`, `$this->isGranted()`, etc. in your screencasts. Thanks for this!
Could/would you include non-security logic in a voter?
Hmm, yea, I see what you're asking - I have had this urge before too :). So, *maybe*. The question is: will the user (by taking normal actions - e.g. clicking a link) be taken to a page where the cookie will not be ready to be eaten? If so, then if you include this logic in a voter, then if the user hits this page while navigating, they will see the 403 Access Denied page... which isn't a great user experience. So if it would be normal for a user to access this page and you want to display a nice message like "Hey, this cookie is still baking!", then I would not use voters. But if it is quite abnormal for the user to access this page (maybe they are getting clever and manually changing the URL to a cookie that is not ready yet), then yea - user a voter!
Cheers!
Thanks, Ryan. That makes a lot of sense.
If you wanted an admin to be able to dynamically grant user with a role how would you go about it?
I meant there would be several problems:
- creating a roles entity that would store all available roles in the database, but now there is no single source for roles - after some time you would end up with some roles that exist in the code base but do not exist in the database and the other way around.
- should user roles and other strings that are to have the same functionality - ROLE_USER, ALLOW_EDIT be stored in the same column - roles of the user?
Thanks for your time guys, you are awesome! :)
Hi there!
Phew, good quest - this can be a confusing issue, because *a lot* of what you should do depends on your exact situation. I'll make some points :)
1) The easiest way to assign roles to users if via a User.roles field. I don't think creating a Role entity makes sense - because it doesn't make sense for an admin to be able to create a new role (e.g. if they created ROLE_MANAGE_BLOG, that wouldn't be used in the code anywhere).
2) Normally, there is in fact no single source for roles - you just kind of use them in your controller with isGranted() or in access_control. If you want a single source (not a bad idea), I like creating a class - e.g. SecurityRoles - that has nothing on it but some constants for each role: const ROLE_MANAGE_BLOG = 'ROLE_MANAGE_BLOG'. Then at least you can reference these within your code and you have one spot to look at for all the roles. You *are* right that eventually, you could have roles stored in a user that aren't used in your codebase. But I don't think that's a problem - it's just a little bit of extra data stored in the user. And if the user's roles were ever updated by the admin, it would correctly save *without* the old roles.
3) Assuming you have a User.roles field, this would *only* store things like ROLE_USER, ROLE_MANAGE_BLOG, ROLE_*, etc. It would NOT store things like ALLOW_EDIT. Why? Because when you say isGranted('ALLOW_EDIT', $blogPost), this does not check to see if the user has the ALLOW_EDIT role. Instead, it calls your custom voter that handles ALLOW_EDIT and then you perform whatever logic you need. In technical terms, "roles" are only those things that start with ROLE_ (and are handled by the RoleVoter, which checks to see if the user has this role). Anything else you pass to isGranted is called an "attribute" - and your custom handler takes care of those.
Does that help? :)
Thank you guys, nice video, again =)
Great one, much appreciated !
Thanks Ryan. Great Post.
This is awesome. I am going to update my project to reflect these changes, much cleaner and efficient. Thanks Ryan!
Woot! Do it up - yay voters!
Thanks for taking the time to come out with this great tutorial. I've run into an issue where the var_dump(...);die; from the CookieVoter/isGranted function is not appearing, instead I get the 403 error in it's place.
- Using the following in the CookieController/NomAction: if (!$this->get('security.context')->isGranted('NOM', $cookie)) as I'm not running Symfony 2.6 (running 2.5.2).
- I've registered the services.yml as shown in the video in the config.yml but it doesn't appear to be calling the voter?
Cheers!
So... this is a Symfony 2 lesson included in a Symfony 3 course...
While the principles of Voters may remain the same, all of them implementation and architecture has drastically changed in Symfony 3.
##
extends Voterinstead ofextends AbstractVoterThis completely changes the way the a Voter is implemented, now with two VERY different methods to implement:
Yo Tony!
You're 100% right :). I've just removed this course from the Symfony 3 track, and moved the New Awesome of Symfony 3.0 course from extra credit into this old spot (because it has an updated chapter with the new way to do voters). The simplification we added to the voters is definitely a big change (in terms of the code you actually write).
Thanks!
As always, you're awesomely on top of things! Thank you Ryan!
Haha, I'd say *you're* on top of things - keeping us honest around here :).
Hi Ryan,
Thanks for this tutorial. I just have a suggestion about the way to check the current user's role in the voter, I tested another way to do it :
if (in_array('ROLE_ADMIN', $user->getRoles())) {
return true;
}
So we don't need to inject the container right ?
Hey Adrien!
Yes! But... also no :). Your solution works just fine, unless you're taking advantage of the "role hierarchy" in security.yml. But if you aren't, then you can keep it simple and do this. In Symfony 2.8, we made a change that allows injecting the "security.access.decision_manager" service directly, which has a decide() method you can call on it (https://github.com/symfony/... - similar to isGranted(), but you pass it the token and not the user. We still need one small change, however, to make this useable with the AbstractVoter (i.e. the problem is that you're not given the token).
Long way to say again, YES! But no... but in 2.8, you should be able to do it the "right" way without injecting the container.
Cheers!
Thanks for your clarification. I get it :) This new service sounds great, I'm looking forward to use this one !
"Houston: no signs of life"
Start the conversation!