Skip to content

[Security] Document custom access decision managers #16034

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

Merged
merged 1 commit into from
Oct 29, 2021
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
90 changes: 57 additions & 33 deletions components/security/authorization.rst
Original file line number Diff line number Diff line change
Expand Up @@ -37,26 +37,8 @@ Access Decision Manager
Since deciding whether or not a user is authorized to perform a certain
action can be a complicated process, the standard :class:`Symfony\\Component\\Security\\Core\\Authorization\\AccessDecisionManager`
itself depends on multiple voters, and makes a final verdict based on all
the votes (either positive, negative or neutral) it has received. It
recognizes several strategies:

``affirmative`` (default)
grant access as soon as there is one voter granting access;

``consensus``
grant access if there are more voters granting access than there are denying;

``unanimous``
only grant access if none of the voters has denied access. If all voters
abstained from voting, the decision is based on the ``allow_if_all_abstain``
config option (which defaults to ``false``).

``priority``
grants or denies access by the first voter that does not abstain;

.. versionadded:: 5.1

The ``priority`` version strategy was introduced in Symfony 5.1.
the votes (either positive, negative or neutral) it has received and the
given strategy.

Usage of the available options in detail::

Expand All @@ -65,27 +47,69 @@ Usage of the available options in detail::
// instances of Symfony\Component\Security\Core\Authorization\Voter\VoterInterface
$voters = [...];

// one of "affirmative", "consensus", "unanimous", "priority"
// instance of Symfony\Component\Security\Core\Authorization\Strategy\AccessDecisionStrategyInterface
$strategy = ...;

// whether or not to grant access when all voters abstain
$allowIfAllAbstainDecisions = ...;

// whether or not to grant access when there is no majority (applies only to the "consensus" strategy)
$allowIfEqualGrantedDeniedDecisions = ...;

$accessDecisionManager = new AccessDecisionManager(
$voters,
$strategy,
$allowIfAllAbstainDecisions,
$allowIfEqualGrantedDeniedDecisions
);
$accessDecisionManager = new AccessDecisionManager($voters, $strategy);

.. seealso::

You can change the default strategy in the
:ref:`configuration <security-voters-change-strategy>`.

Strategies
----------

.. versionadded:: 5.4

The strategy classes were introduced in Symfony 5.4. In earlier versions, the strategy was passed as a string.

The following strategies are bundled with the component:

``AffirmativeStrategy`` (default)
grant access as soon as there is one voter granting access;

``ConsensusStrategy``
grant access if there are more voters granting access than there are denying;
if there is a draw between votes, the decision is made based on the
``$allowIfEqualGrantedDeniedDecisions`` constructor parameter which defaults to ``true``.

``UnanimousStrategy``
only grant access if none of the voters has denied access.

``PriorityStrategy``
grants or denies access by the first voter that does not abstain;

.. versionadded:: 5.1

The "priority" version strategy was introduced in Symfony 5.1.

If all voters abstained from voting, the decision is based on the ``$allowIfAllAbstainDecisions``
constructor parameter which is supported by all of the built-in strategies and defaults to ``false``.

If none of the built-in strategies seem to fit, a custom strategy may be provided. The strategy will
receive a stream of votes and may return as soon as it has seen enough votes to come to a conclusion.

::

/**
* Always picks the third voter.
*/
class ThirdVoterStrategy implements AccessDecisionStrategyInterface
{
public function decide(\Traversable $results): bool
{
$votes = 0;
foreach ($results as $result) {
if (++$votes === 3) {
return $result === VoterInterface::ACCESS_GRANTED;
}
}

return false;
}
}

Voters
------

Expand Down
53 changes: 52 additions & 1 deletion security/voters.rst
Original file line number Diff line number Diff line change
Expand Up @@ -345,7 +345,58 @@ security configuration:
Custom Access Decision Strategy
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~

If none of the built-in strategies fits your use case, define the ``service``
.. versionadded:: 5.4

The ``strategy_service`` option was introduced in Symfony 5.4.

If none of the built-in strategies fits your use case, define the ``strategy_service``
option to use a custom service (your service must implement the
:class:`Symfony\\Component\\Security\\Core\Authorization\\Strategy\\AccessDecisionStrategyInterface`):

.. configuration-block::

.. code-block:: yaml

# config/packages/security.yaml
security:
access_decision_manager:
strategy_service: App\Security\MyCustomAccessDecisionStrategy
# ...

.. code-block:: xml

<!-- config/packages/security.xml -->
<?xml version="1.0" encoding="UTF-8" ?>
<srv:container xmlns="http://symfony.com/schema/dic/security"
xmlns:srv="http://symfony.com/schema/dic/services"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://symfony.com/schema/dic/services
https://symfony.com/schema/dic/services/services-1.0.xsd"
>

<config>
<access-decision-manager
strategy-service="App\Security\MyCustomAccessDecisionStrategy"/>
</config>
</srv:container>

.. code-block:: php

// config/packages/security.php
use App\Security\MyCustomAccessDecisionStrategy;
use Symfony\Config\SecurityConfig;

return static function (SecurityConfig $security) {
$security->accessDecisionManager()
->strategyService(MyCustomAccessDecisionStrategy::class)
// ...
;
};

Custom Access Decision Manager
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~

If you need to provide an entirely custom access decision manager, define the ``service``
option to use a custom service as the Access Decision Manager (your service
must implement the :class:`Symfony\\Component\\Security\\Core\\Authorization\\AccessDecisionManagerInterface`):

Expand Down