-
-
Notifications
You must be signed in to change notification settings - Fork 9.6k
[Security] Deprecate isGranted()/decide() on more than one attribute #33584
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Conversation
fa2a125
to
9a18319
Compare
Looks like the build failures are not a result of the changes in this PR |
For me, this pull request is not acceptable. I use myself in many projects just such a form in which a lot of ROLE_ is transmitted. For me it is just clear because I know who has access to the controller. I think calling multiple functions is definitely slower and using the I completely disagree, although it is not my decision - if it comes into 4.4 - instead of upgrading, I will have to stay with version 4.3 SF. |
I don't think deprecating this is possible, or even wanted for some cases. But I would given that the ExpressionLanguage is the most advanced, recommend this over using strings. The biggest issues is the ambiguity of the attributes, it's confusing documentation, and how voters receive the information, the subject (ahem, object, wait is it subject? *) can be null while the The PHPDoc states that Historically the Moving forward I think we first need clearly identify and clarify how voters should be used, is the subject/object a mixed type or object type? What are the attributes really about. Then we can think about deprecating some accepted value types. |
@michaljusiega You would really stay on an old version for such a minor "feature"? I'd rather see this feature removed as it makes the life of a developer more difficult. As mentioned in the PR, this is confusing as you can't predict the behavior.
I'd argue that you're not using the voter system correctly then. Voting on roles is an implementation detail that should be hidden in a voter, not done in the code directly. For more information, you can read this blog post: https://stovepipe.systems/post/symfony-security-roles-vs-voters |
I fully agree removing this behavior ... but I also understand people using/needing it today. Here's an alternative proposal: change |
@javiereguiluz: Then such a change would make sense. |
@michaljusiega yes, that would be the idea. However, we need to think carefully about this. For example, using role hierarchy you could define a single special role that includes two or more ... and then we could get rid of |
I don’t like the idea of adding more getters 😟 |
src/Symfony/Component/Security/Core/Authorization/AccessDecisionManager.php
Show resolved
Hide resolved
Thanks for the discussion! I think there are now 2 proposals on how to continue this PR:
I'm in favor of (1). Using (2) has, if any, exactly the same drawbacks as (1) and introduces syntax sugar for something that is just boolean PHP logic ( /cc @weaverryan @nicolas-grekas can you please share your opinion on a proposal? This way, I can finish this before the feature-freeze 😃 |
@wouterj Can we imagine a system "strategy" that would be passed in parameter ? |
@maxhelias if you mean the voting strategy, I don't think that's a good idea as it would make it impossible to use the existing methods for this. If you mean a separate setting, it would make authorization even more complex due to the amount of permutations you can have. |
I'm in favor of option 1 as well. |
src/Symfony/Component/Security/Core/Authorization/AuthorizationChecker.php
Outdated
Show resolved
Hide resolved
Hello, I will describe your problem again. I suggest the second option. Many of you probably have applications that claim permissions based on the @IsGranted annotation or the is_granted function in Twig. Many of these applications are probably not based on basic roles such as: ROLE_USER, ROLE_ADMIN ... In my case, I have several applications for which I check if the logged in user HAS ANY permission for which he has access to the module / controller / view, so in the case of an array it looks like this: For me, this way is the clearest, because the expression And what about 40 or more elements in the array for which the permissions are checked. In exchange for some silly expression, the code will be completely illegible ... Look also from the perspective of ordinary mortals who use Symfony. There are applications for which this pull-request may be a fiasco for further updates. I am asking for your understanding. I would prefer to stay with the option for which: Or leave it as it is and don't look for a hole in the whole. |
Hi @michaljusiega! Thanks for explaining and sharing your usage of the feature! I would argue in that case that creating a custom voter is the best possible solution, even when this PR is not merged in any version. The RoleVoter is just a basic one provided by Symfony to help people start using the authorization. It implements user roles. The definition of a role is "the function assumed or part played by a person or thing in a particular situation." You are instead wanting to grant access based on what sections a user can visit. That asks for a custom voter that implements such logic, for instance like this: if ($this->isGranted('SECTION_ACCESS', ['SECTION_A', 'SECTION_B'])) {
// ...
} With a voter that accepts the class SectionVoter extends Voter
{
protected function supports($attribute, $subject)
{
return 'SECTION_ACCESS' === $attribute;
}
protected function voteOnAttribute($attribute, $subject, TokenInterface $token)
{
$user = $token->getUser();
$sections = (array) $subject;
if (!$user instanceof User) {
return false;
}
foreach ($sections as $section) {
if ($user->hasAccessToSection($section)) {
return true;
}
}
return false;
}
} |
857da55
to
daf1ac6
Compare
Alright, I've rebased the PR and fixed some things mentioned by @jvasseur. That leaves only the BC problem with |
I understand that you need to develop ... but this way you will slow down people's work. I totally disagree with these changes ... having 300 controllers, I have a permission scheme for which I allow people to be granted permissions. As I mentioned, I don't have a simple application that does something ... connects over 30 databases, connects to 30 HTTP servers, has 200 users a day, the access system is also important ... If I were to adapt the application to these changes, it would take a lot of time because ... you decided something about yourself ... If something works, do not change it ... give an alternative for which the change will be pleasant, not through torment. |
This current behavior is just asking for trouble for big applications. I work for a company that has hundreds of tenants, high activity through API and webinterface, one mistake because of not knowing if it does
This is a change that should help reduce the amount of unknown and confusing behavior. I've never used this feature because I can't tell what it does by looking at it. This is not a selfish decision, this is cleaning up a confusing public API. The fact that you've used this feature through 300+ controllers, makes me wonder how many hidden security related issues you have because a developer implemented it wrongly, or a voter that caused undesired behavior.
It doesn't work (well), that's why there's a proposal to change it and reduce the complexity of the public API.
You can propose the annotation feature in the SensioFrameworkExtra bundle. In regards of the public API of the authorization checker, imo it should stay the same as it will only increase complexity. In regards of twig, new functions could be added, though I'm not sure that's the best idea. As compromise the security bundle could register two new functions with the expression language: I rather advice you to create a voter and handle permissions in those. You can read why in the blog post I linked you in: #33584 (comment) |
I am not talking about the security issue here, but about the readability for the developer. I do not intend to modify the code because someone does not understand how is_granted work ... and as I mentioned - changing into an expression as string is unreadable ... isn't ? |
For voting on multiple attributes isn't there access decision strategy. https://symfony.com/doc/current/security/voters.html#changing-the-access-decision-strategy I don't understand why would it confuse a developer whether multiple attributes are ORed or ANDed |
@faizanakram99 no, that's for multiple voters. You can have multiple voters voting on the same security attribute (the first argument of What a voter does to decide whether it allows multiple attributes is up to the logic in the voter. A voter just gets all attributes at the same time (e.g. This creates confusion, see for instance leaseweb/LswSecureControllerBundle#4 for a real example: The voter in that bundle only says "allowed!" if all attributes pass. This is exactly different from the voters in core (RoleVoter, RoleHierarchyVoter, AuthenticationVoter), which allow as soon as one attribute passes. The decision strategy has nothing to do with this. Can you see how this is confusing? 😉 |
I'm sorry, but you keep skipping the good alternative (which is far better than voting on multiple attributes in the first place). A blog post, documentation and an example has been provided to you in the past comments by @linaori and me. I don't think it's worth repeating my and your arguments again, they are clear enough. |
Nevermind. Keep these changes. |
daf1ac6
to
c64b0be
Compare
Thank you @wouterj. |
@@ -197,6 +197,24 @@ Security | |||
* The `LdapUserProvider` class has been deprecated, use `Symfony\Component\Ldap\Security\LdapUserProvider` instead. | |||
* Implementations of `PasswordEncoderInterface` and `UserPasswordEncoderInterface` should add a new `needsRehash()` method | |||
* Deprecated returning a non-boolean value when implementing `Guard\AuthenticatorInterface::checkCredentials()`. Please explicitly return `false` to indicate invalid credentials. | |||
* Deprecated passing more than one attribute to `AccessDecisionManager::decide()` and `AuthorizationChecker::isGranted()` (and indirectly the `is_granted()` Twig and ExpressionLanguage function) |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Missed entry for UPGRADE-5.0.md
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
see #33696
My documentation commit from July (sensiolabs/SensioFrameworkExtraBundle@97cc360) should be reverted now. Also, neither |
that's logical not to mention this annotation, as it is not part of the core. |
…rule (chalasr) This PR was merged into the 4.4 branch. Discussion ---------- [Security] Fix defining multiple roles per access_control rule | Q | A | ------------- | --- | Branch? | 4.4 | Bug fix? | yes | New feature? | no | Deprecations? | no | Tickets | - | License | MIT | Doc PR | symfony/symfony-docs#12371 needs to be reverted #33584 deprecated passing multiple attributes to `AccessDecisionManager::decide()`, but this change must not impact `access_control` as you cannot define multiple rules with the same criteria for request matching (the first match wins). Commits ------- 338b3df [Security] Fix defining multiple roles per access_control rule
If this code is deprecated, how about the alternative? Don't call it voter. The value is generated dynamically. |
// if you want AND
foreach ($child->getRoles() as $role) {
if (!$this->security->isGranted($role)) {
// ...
}
}
// if you want OR
$granted = false;
foreach ($child->getRoles() as $role) {
if ($this->security->isGranted($role)) {
$granted = true;
break;
}
}
if (!$granted) {
// ...
} |
What is the benefit of this feature? Apparently there's no plus. Without this feature, you can use ExpressionLanguage. Why was there such a need? Previously
But now I can't use it as an array. There is no benefit in this. No flexibility, just type forced. |
Exactly, that's why it's been deprecated. It caused a lot of confusion. When you need context for your votes, you can do something like this: $this->security->isGranted('CAN_VIEW_POST', $child); In your voter you can then check if the roles are are available for the current token. |
Isn't that complicated $this->security->isGranted('has_role("ROLE_ADMIN") or .....') |
Authorization logic belongs in your voters, not wherever you call the authorization layer. |
Discussing more will not bring any results. Solution for those who want to use legacy feature. @michaljusiega https://gist.github.com/appaydin/959f56ddc9a9fe3aee365c6ba3e7653f |
To be honest, the amount of negativity for such a small chance in the Security components makes me really demotivated to work on issues like #30765 and #30914 This change has some advantages:
And the change has some disadvantages:
Any change in the Security component will lead to some advantages and some disadvantages. We should try to carefully weigh all of these and decide which changes are needed. If we, as a community, are not open for any changes in the usage of the Security component, we end up with the same outdated component over 2 years. Given that it already is pretty much years behind its competitors, I'm afraid this will mark the end of Symfony Security. I would also argue that needing to pass an array into |
At Symfony project we love discussing about new features, ways to fix bugs and anything related to developing. Listening to others who disagree with you is great to challenge yourself and makes you a better developer. However, we can't keep discussing about the same things forever. At some point, we need to make decisions. If this discussion would've received tens of messages against it, we'd keep the discussion open and we'd even revert this (as it has happened in the past with other features). Nonetheless, few people are against this and most people seem to be in favor, including the entire Symfony Core Team. So, let's end this discussion for now (that's why we're locking it) and we'll reopen it in the future if people complain when they start using this feature in their real projects. Thank you all for the discussion! (and thanks Wouter for your patience and kindness ❤️) |
While I expect it not be used much, it is currently possible to call
isGranted()
on more than one attribute:Supporting this includes a couple of problems/questions:
OR
orAND
;I think we can do better. In the past, we've created all tooling for this to be self-explaining and easier:
This PR deprecates passing more than one attribute to
isGranted()
anddecide()
to remove this confusing bit in Security usage.Backwards compatiblity help
I need some help in how to approach changing the
VoterInterface::vote(TokenInterface $token, $subject, array $attributes)
method in a backwards compatible way. Removingarray
breaks all Voters, so does changing it tostring
and removed the parameter all together.