Skip to content

Adding a new interface that give voters access to AccessDecisionManager #14550

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 2 commits into from

Conversation

weaverryan
Copy link
Member

Q A
Bug fix? no
New feature? yes
BC breaks? no
Deprecations? no
Tests pass? yes
Fixed tickets partially #12360
License MIT
Doc PR not yet (but I'm good for it)

Hi guys!

Problem: From inside a voter, if you want to check an attribute from another voter, you can't do that easily. You need the security.access.decision_manager service, but injecting it (or security.authorization_checker) causes a circular reference exception. So, you need to inject the entire container.

Solution: With this, if your voter implements the new AccessDecisionManagerAwareInterface, then setAccessDecisionManager is called on it before vote().

This allows code like:

public function vote(TokenInterface $token, $object, array $attributes)
{
    if ($this->accessDecisionManager->decide($token, array('ROLE_ADMIN')) {
        return self::ACCESS_GRANTED;
    }

    // ... your normal logic
}

Thanks!

weaverryan added 2 commits May 4, 2015 20:59
I just don't like doing any work in the __construct
@stof
Copy link
Member

stof commented May 5, 2015

There is a drawback with this implementation: any such voter creates a circular object graph, which hurts garbage collection.
This will mainly affect tests where the kernel is shutdown in the process and then booted again, as the kernel is only booted once during a normal request handling.

@ogizanagi
Copy link
Contributor

👍

@nicolas-grekas
Copy link
Member

Does it make sense to add a DI tag instead of this configuration-through-implements?

@weaverryan
Copy link
Member Author

@nicolas-grekas Do you mean give your voters an extra tag, and then a compiler pass will modify those service Definitions to have a setAccessDecisionManager "call" on them? Conceptually, I'm not against it - but I think we'll have a "circular reference", as I believe the AccessDecisionManager requires all the voters as a constructor arg, so the voters can't depend back on the AccessDecisionManager. Or were you thinking of something different?

@nicolas-grekas
Copy link
Member

That's true. An other idea trying to remove the circular dep: what about always injecting the AccessDecisionManager in a voting attribute? That would allow removing the new interface, and rely on the existing supportsAttribute instead? I don't know well the voter code so please forgive me if that's an obvious bad idea... :)

@weaverryan
Copy link
Member Author

@nicolas-grekas That's really clever :). And actually, I think we can make it work technically. But, the "attributes" are what the user passes to isGranted() originally (i.e. the "permission" you're checking for). This would kind of change the meaning/purpose of the attributes, so I don't think it's a good idea unfortunately :/.

I see a few options:

a) Be ok with the circular reference. Afterall, you're opting into it
b) Pass the AccessDecisionManager into vote() as the 4th arg. Actually, we'd need to deprecate the old interface, create a new interface method (e.g. voteOnAccess) to do this, but very possible and there's a clear path to make this happen
c) Add a __destruct() method in AccessDecisionManager that unsets $this->voters to avoid the reference. I'm not 100% sure __destruct() will be called when you shutdown the kernel, but I could check. And there's no real precedence for this in core.

Would any of these 3 options be acceptable?

@nicolas-grekas
Copy link
Member

I see two valid options:

  1. one is allowing the access decision manager to be injected in the voters: the patch is easy (see [Security] Add setVoters() on AccessDecisionManager #14733). It has the drawback of creating a circular object graph, and has the benefit of not requiring any deprecation.
  2. your b) option: passing it as 4th argument to the vote() method. This prevents the circular object graph but requires a change in the interface, and makes every voter aware of the access decision manager, which is usually not required.

Since these are all services, my preference goes for 1.
If one wants to cut the circular graph, calling ->setVoters with an empty array would break it.

@nicolas-grekas
Copy link
Member

See #14733
@weaverryan I'd happily let your do the doc PR...

@weaverryan
Copy link
Member Author

@nicolas-grekas Fast and great reply. I'm closing this - your approach is superior and simpler.

Thanks!

@weaverryan weaverryan closed this May 23, 2015
@weaverryan weaverryan deleted the access_decision_manager branch May 23, 2015 19:22
fabpot added a commit that referenced this pull request Jun 1, 2015
…icolas-grekas)

This PR was merged into the 2.8 branch.

Discussion
----------

[Security] Add setVoters() on AccessDecisionManager

| Q             | A
| ------------- | ---
| Bug fix?      | no
| New feature?  | yes
| BC breaks?    | no
| Deprecations? | no
| Tests pass?   | yes
| Fixed tickets | partially #12360
| License       | MIT
| Doc PR        | -

Alternative for #14550

Commits
-------

3fd7cea [Security] Add setVoters() on AccessDecisionManager
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
Projects
None yet
Development

Successfully merging this pull request may close these issues.

5 participants