Thanks to visit codestin.com
Credit goes to github.com

Skip to content

[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

Closed
wants to merge 1 commit into from
Closed

[Security][SecurityBundle] Add is_granted feature for impersonator on AuthorizationChecker #28794

wants to merge 1 commit into from

Conversation

FabienPapet
Copy link

@FabienPapet FabienPapet commented Oct 9, 2018

Q A
Branch? master
Bug fix? no
New feature? yes
BC breaks? no
Deprecations? no
Tests pass? no test yet
License MIT
Doc PR Not yet

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

  • Add doc
  • Add tests

@FabienPapet FabienPapet changed the title Add is_granted feature for impersonator on AuthorizationChecker [Security Bundle]Add is_granted feature for impersonator on AuthorizationChecker Oct 9, 2018
@stof
Copy link
Member

stof commented Oct 10, 2018

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)

@FabienPapet
Copy link
Author

FabienPapet commented Oct 10, 2018

Agree, where do you think I should put this code ? Do you have any idea about a classname ?
ImpersonatorAccessDecisionManager SwithUserDecisionManager ?

@FabienPapet
Copy link
Author

edited. ping @stof

use Symfony\Component\Security\Core\Role\SwitchUserRole;

/**
* SwitchUserAuthorizationChecker.
Copy link
Contributor

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
Copy link
Contributor

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

Copy link
Author

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);
Copy link
Contributor

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

Copy link
Author

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.
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

needed?

Copy link
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

removed

@FabienPapet FabienPapet changed the title [Security Bundle]Add is_granted feature for impersonator on AuthorizationChecker [Security][SecurityBundle] Add is_granted feature for impersonator on AuthorizationChecker Oct 11, 2018
Copy link
Contributor

@ro0NL ro0NL left a 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
Copy link
Contributor

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

@FabienPapet
Copy link
Author

FabienPapet commented Oct 12, 2018

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 ?

@nicolas-grekas nicolas-grekas added this to the next milestone Oct 14, 2018
@xabbuh
Copy link
Member

xabbuh commented Mar 8, 2019

I am not sure I understand the use case here. Can you elaborate a bit?

@FabienPapet
Copy link
Author

The use case is pretty simple.
I have an app where I have a User, a user admin and an administrator.

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

@xabbuh
Copy link
Member

xabbuh commented Mar 8, 2019

So if I understand this correctly, we are looking for a shortcut for is_granted('ROLE_ALLOWED_TO_SWITCH')?

@FabienPapet
Copy link
Author

No, because we can have different levels for user impersonation. And I want to check the roles of impersonator

@xabbuh
Copy link
Member

xabbuh commented Mar 8, 2019

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.

@FabienPapet
Copy link
Author

That totally fine 👍
I mean the impersonator can be for example a manager, or a member of the application support.
Both manager and support can impersonate a user, but the manager can do some actions that the support team can not. I want to check this on the user list. Not sure if it's fully clear now

@OskarStark
Copy link
Contributor

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...
But if I understand you correct, this is the goal of the PR, right?

@FabienPapet
Copy link
Author

It is :)

*
* @return TokenInterface|false The original TokenInterface instance, false if the current TokenInterface is not switched
*/
private function getOriginalToken(TokenInterface $token)
Copy link
Contributor

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

Copy link
Author

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

@HeahDude
Copy link
Contributor

HeahDude commented Mar 11, 2019

What about a voter that checks for a IMPERSONATOR_ATTRIBUTE_X with IMPERSONATOR_ prefix configurable, it would early return false if the token is not an instance of SwitchUserToken then remove the prefix before calling its decorated auth checker (we are no tied by circular dependency anymore, but the voter should be in the SecurityBundle though)?

We could then have a Twig function impersonator_is_granted('ATTRIBUTE_X') that adds the prefix before calling is_granted in the bridge security bundle too?

@FabienPapet
Copy link
Author

Maybe an IMPERSONATOR_ROLE_X to be consistent with RoleVoter ?

@HeahDude
Copy link
Contributor

HeahDude commented Mar 11, 2019

To be flexible this should be working with any attributes to trigger custom voters not just using roles or roles hierarchy.
Using a SwitchUserVoter would get rid of that new interface, and would be simple to implement:

// ...
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 isGranted() anywhere (that's what I mean by more flexible), while we can decorate the method easily in twig, in raw PHP it requires a new contract.

@HeahDude
Copy link
Contributor

The prefix would be easier to decorate also in Security and ControllerTrait helpers.

class SwitchRoleVoter implements VoterInterface
{
/**
* @var AccessDecisionManager
Copy link
Contributor

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.

Copy link
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

from the constructor* right ?

Copy link
Contributor

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);
Copy link
Contributor

@HeahDude HeahDude Mar 11, 2019

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;
}

Copy link
Contributor

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.

Copy link
Contributor

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.

Copy link
Contributor

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?

@fabpot
Copy link
Member

fabpot commented Aug 11, 2020

Closing as I don't think this feature (as explained by @OskarStark) should be in core.

@fabpot fabpot closed this Aug 11, 2020
@FabienPapet FabienPapet deleted the add-isgranted-impersonator branch August 11, 2020 11:57
@nicolas-grekas nicolas-grekas modified the milestones: next, 5.2 Oct 5, 2020
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Projects
None yet
Development

Successfully merging this pull request may close these issues.

9 participants