-
-
Notifications
You must be signed in to change notification settings - Fork 9.6k
[Security][SecurityBundle] Add is_granted feature for impersonator on AuthorizationChecker #28794
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
Adding such a method which is not part of the interface is a bad idea, as it would force to code against the implementation rather than the interface (which is not a supported usage of Symfony, as we allow ourselves to decorate services with a different implementation, and we do it regularly to implement the profiler features for instance). I think this should be implemented as a separate class (most of the logic is handled by the access decision manager anyway) |
Agree, where do you think I should put this code ? Do you have any idea about a classname ? |
edited. ping @stof |
use Symfony\Component\Security\Core\Role\SwitchUserRole; | ||
|
||
/** | ||
* SwitchUserAuthorizationChecker. |
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.
this line is useless, as it does not provide additional information
|
||
/** | ||
* @param AccessDecisionManagerInterface $accessDecisionManager | ||
* @param TokenStorageInterface $tokenStorage |
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.
imo this PHPDoc is not needed, as the arguments are typehinted, not sure about the member vars PHPDoc
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.
removed
$token = $this->getOriginalToken($this->tokenStorage->getToken()); | ||
|
||
if (false !== $token) { | ||
return $this->accessDecisionManager->decide($token, $attributes, $subject); |
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.
maybe checking for === false
first and return false
? Not sure if its worth to switch
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.
IMO actual is better than switching which would give something like this
$token = $this->getOriginalToken($this->tokenStorage->getToken());
if (false === $token) {
return false;
}
return $this->accessDecisionManager->decide($token, $attributes, $subject);
namespace Symfony\Component\Security\Core\Authorization; | ||
|
||
/** | ||
* The AuthorizationCheckerInterface. |
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.
needed?
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.
removed
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.
im not sure separate handling of the two tokens makes sense (thus two twig functions etc.)
alternatively what about a default fallback mechanism? If the current token doesnt grant access, test the source token, if any. Optionally the security bundle could add configuration to white- and/or blacklist specific attributes. Does that make sense?
/** | ||
* @author Fabien Papet <[email protected]> | ||
*/ | ||
interface SwitchUserAuthorizationCheckerInterface |
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.
this duplicates AuthorizationCheckerInterface
basically, which looks odd
I'm not sure about using fallbacks. I think using a single is_granted can be confusing and can lead to errors (from a DX point of view). Maybe other Symfony contributors have an idea ? |
I am not sure I understand the use case here. Can you elaborate a bit? |
The use case is pretty simple. The user admin can switch into a user. and sometimes on several screens, there are some actions that only the user admin can do. And I have to check the impersonator roles to acheive that |
So if I understand this correctly, we are looking for a shortcut for |
No, because we can have different levels for user impersonation. And I want to check the roles of impersonator |
What do you mean with different levels in this context? I am sorry to bother you with these questions but I would like to better understand the use case to see whether or not this fits in the Symfony core. |
That totally fine 👍 |
I think this is not the goal of the impersonating. You should be the impersonated user with the exact roles, no matter from which role you did the impersonation... |
It is :) |
* | ||
* @return TokenInterface|false The original TokenInterface instance, false if the current TokenInterface is not switched | ||
*/ | ||
private function getOriginalToken(TokenInterface $token) |
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.
As of 4.3 you can simply check that the token is an instance of SwitchUserToken
and remove this method :). See #22048
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.
Code is from october2018 it didn't exist where I did this. I'll edit this evening
What about a voter that checks for a We could then have a Twig function |
Maybe an IMPERSONATOR_ROLE_X to be consistent with RoleVoter ? |
To be flexible this should be working with any attributes to trigger custom voters not just using roles or roles hierarchy. // ...
class SwitchUserVoter extends Voter
{
private $prefix = 'IMPERSONATOR_';
private $accessDecisionManager;
// ...
public function supports(...)
{
return 0 === strpos($attribute, $this->prefix);
}
public function voteOnAttribute(...)
{
if (!$token instanceof SwitchUserToken) {
return false;
}
return $this->accessDecisionManager->decide($token->getOriginalToken(), [substr($attribute, strlen($this->prefix))], $subject);
}
} That would allow both: {% is_granted('EDIT_POST', post) or impersonator_is_granted('MANAGE_POST', post) %}
{# or with a custom prefix X_ #}
{% is_granted(['EDIT_POST', 'X_MANAGE_POST'], post) %} Plus the second example is valid for any call of |
The prefix would be easier to decorate also in |
class SwitchRoleVoter implements VoterInterface | ||
{ | ||
/** | ||
* @var AccessDecisionManager |
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.
Can be removed since this is inferred from the controller.
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.
from the constructor* right ?
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.
Yes this auto correction is amazing some time :D
continue; | ||
} | ||
|
||
return $this->accessDecisionManager->decide($token->getOriginalToken(), [substr($attribute, \strlen($this->prefix))], $subject); |
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.
if (VoterInterface::ACCESS_GRANTED === $result = $this->accessDecisionManager->decide($token->getOriginalToken(), [substr($attribute, \strlen($this->prefix))], $subject)) {
return $result;
}
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.
Ah no this should just decorate the call forget that comment.
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.
Hmm I think this is more complex, the current logic prevents passing two prefixed attributes, you should extend Voter to simplify the decoration or handle this case too IMHO.
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.
Without extending, you could go with:
$attributes = array_map(function ($attribute) {
return substr($attribute, \strlen($this->prefix));
}, array_filter($attributes, function ($attribute) {
return 0 === strpos($attribute, $this->prefix) }));
}));
if (!$attributes) {
return $result;
}
return $this->accessDecisionManager->decide($token->getOriginalToken(), $attributes, $subject);
WDYT?
Closing as I don't think this feature (as explained by @OskarStark) should be in core. |
This feature adds a new is*Granted function for the user impersonating another user. I may need some help for tests and also for the function naming. A twig extension may be added if you want to (or in another PR).
Todo