Customising your Access Decision Stategy
In this video you will learn how to customise the Access Decision Strategy, which in turn allows you to influence the outcome of Symfony's security voting process.
This is a more advanced topic, and not something you will likely need to change for most of your voting needs. However, it is nice to know this feature exists, and it's really not that hard to implement.
The typical situation when using voters is that you would have one voter for each resource / class you are protecting. If this is your circumstance then the following will be of no use to you.
However, if you have multiple voters for a given resource, then you may wish to customise the outcome of the ballot / vote.
This is achieved by changing the Access Decision Strategy.
There are three access decision strategies available in Symfony:
affirmative(default) - This grants access as soon as there is one voter granting access;
consensus- This grants access if there are more voters granting access than denying;
unanimous- This only grants access once all voters grant access.
Changing your access decision strategy is as easy as updating
# app/config/security.yml security: access_decision_manager: strategy: consensus
One thing to note is that this configures the access decision strategy for your entire application. As best I understand it currently, there is no way to specify individual strategies for certain voters.
An interesting way in which the
affirmative strategy works is that if, for example, you had two voters for a given resource, and one returns
ACCESS_DENIED, and the other returns
ACCESS_GRANTED, then you would be granted access.
priority of your event listeners makes no difference here. Each voter will be called but the overall outcome is that as long as at least one voter grants access, then access will be granted. More on listener priorities here (scroll down a bit).
Perhaps even more interesting though is the outcome of
consensus. If you have three voters and two return
ACCESS_GRANTED then the consensus is that you should be granted access. That seems fair enough.
But what if you only have two voters, and one returns
ACCESS_GRANTED, whilst the other returns
ACCESS_DENIED? My assumption was that in this instance, you would be denied. However, in reality, you will get
If you want to deny access, you need to update your
# app/config/security.yml security: access_decision_manager: strategy: consensus allow_if_equal_granted_denied: false # default = true
This is something that should be tested with an acceptance test.
Lastly, unaminous will deny access unless every single voter that is taking part in the vote returns
The only caveat to this is that any voters returning
ACCESS_ABSTAIN will be ignored.
So, if any single voter returns
ACCESS_DENIED, then access will be denied, even if you had 100 voters and 99 granted access and 1 denied, the vote result would be
Edge Case - All Abstain
What would happen if every voter in the vote returned
Well, by default, access would be denied in this case.
You can change this if you need to, by updating
# app/config/security.yml security: access_decision_manager: allow_if_all_abstain: true # default = false
That covers the three available options inside the
access_decision_manager section of the
If you would like to see all the available options, be sure to check out the Security configuration reference.
As ever, be sure to test your security logic with the appropriate level of unit, integration, and acceptance tests. It's fairly easy to make mistakes or get unexpected outcomes as these are parts of your system that likely / hopefully you rarely need to configure.