Skip to content
Original file line number Diff line number Diff line change
Expand Up @@ -19,7 +19,7 @@
use Symfony\Component\Security\Core\Role\RoleInterface;
use Symfony\Component\Security\Http\Logout\LogoutUrlGenerator;
use Symfony\Component\Security\Core\Authorization\AccessDecisionManagerInterface;
use Symfony\Component\Security\Core\Authorization\DebugAccessDecisionManager;
use Symfony\Component\Security\Core\Authorization\TraceableAccessDecisionManager;
use Symfony\Component\VarDumper\Cloner\Data;
use Symfony\Component\Security\Http\FirewallMapInterface;
use Symfony\Bundle\SecurityBundle\Security\FirewallMap;
Expand Down Expand Up @@ -120,7 +120,7 @@ public function collect(Request $request, Response $response, \Exception $except
}

// collect voters and access decision manager information
if ($this->accessDecisionManager instanceof DebugAccessDecisionManager) {
if ($this->accessDecisionManager instanceof TraceableAccessDecisionManager) {
$this->data['access_decision_log'] = array_map(function ($decision) {
$decision['object'] = $this->cloneVar($decision['object']);

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,7 @@
xsi:schemaLocation="http://symfony.com/schema/dic/services http://symfony.com/schema/dic/services/services-1.0.xsd">

<services>
<service id="debug.security.access.decision_manager" class="Symfony\Component\Security\Core\Authorization\DebugAccessDecisionManager" decorates="security.access.decision_manager" public="false">
<service id="debug.security.access.decision_manager" class="Symfony\Component\Security\Core\Authorization\TraceableAccessDecisionManager" decorates="security.access.decision_manager" public="false">
<argument type="service" id="debug.security.access.decision_manager.inner" />
</service>
</services>
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -11,88 +11,11 @@

namespace Symfony\Component\Security\Core\Authorization;

use Symfony\Component\Security\Core\Authentication\Token\TokenInterface;

/**
* Decorates the original AccessDecisionManager class to log information
* about the security voters and the decisions made by them.
*
* @author Javier Eguiluz <javier.eguiluz@gmail.com>
*
* @internal
* @deprecated The DebugAccessDecisionManager class has been renamed and is deprecated since version 3.3 and will be removed in 4.0. Use the TraceableAccessDecisionManager class instead.
*
* This is a placeholder for the old class, that got renamed; this is not a BC break since the class is internal, this
* placeholder is here just to help backward compatibility with older SecurityBundle versions.
*/
class DebugAccessDecisionManager implements AccessDecisionManagerInterface
{
private $manager;
private $strategy;
private $voters = array();
private $decisionLog = array();

public function __construct(AccessDecisionManagerInterface $manager)
{
$this->manager = $manager;

if ($this->manager instanceof AccessDecisionManager) {
// The strategy is stored in a private property of the decorated service
$reflection = new \ReflectionProperty(AccessDecisionManager::class, 'strategy');
$reflection->setAccessible(true);
$this->strategy = $reflection->getValue($manager);
}
}

/**
* {@inheritdoc}
*/
public function decide(TokenInterface $token, array $attributes, $object = null)
{
$result = $this->manager->decide($token, $attributes, $object);

$this->decisionLog[] = array(
'attributes' => $attributes,
'object' => $object,
'result' => $result,
);

return $result;
}

/**
* {@inheritdoc}
*/
public function setVoters(array $voters)
{
if (!method_exists($this->manager, 'setVoters')) {
return;
}

$this->voters = $voters;
$this->manager->setVoters($voters);
}

/**
* @return string
*/
public function getStrategy()
{
// The $strategy property is misleading because it stores the name of its
// method (e.g. 'decideAffirmative') instead of the original strategy name
// (e.g. 'affirmative')
return null === $this->strategy ? '-' : strtolower(substr($this->strategy, 6));
}

/**
* @return array
*/
public function getVoters()
{
return $this->voters;
}

/**
* @return array
*/
public function getDecisionLog()
{
return $this->decisionLog;
}
}
class_exists(TraceableAccessDecisionManager::class);
Original file line number Diff line number Diff line change
@@ -0,0 +1,101 @@
<?php

/*
* This file is part of the Symfony package.
*
* (c) Fabien Potencier <fabien@symfony.com>
*
* For the full copyright and license information, please view the LICENSE
* file that was distributed with this source code.
*/

namespace Symfony\Component\Security\Core\Authorization;

use Symfony\Component\Security\Core\Authentication\Token\TokenInterface;
use Symfony\Component\Security\Core\Authorization\DebugAccessDecisionManager;

/**
* Decorates the original AccessDecisionManager class to log information
* about the security voters and the decisions made by them.
*
* @author Javier Eguiluz <javier.eguiluz@gmail.com>
*
* @internal
*/
class TraceableAccessDecisionManager implements AccessDecisionManagerInterface
{
private $manager;
private $strategy;
private $voters = array();
private $decisionLog = array();

public function __construct(AccessDecisionManagerInterface $manager)
{
$this->manager = $manager;

if ($this->manager instanceof AccessDecisionManager) {
// The strategy is stored in a private property of the decorated service
$reflection = new \ReflectionProperty(AccessDecisionManager::class, 'strategy');
$reflection->setAccessible(true);
$this->strategy = $reflection->getValue($manager);
}
}

/**
* {@inheritdoc}
*/
public function decide(TokenInterface $token, array $attributes, $object = null)
{
$result = $this->manager->decide($token, $attributes, $object);

$this->decisionLog[] = array(
'attributes' => $attributes,
'object' => $object,
'result' => $result,
);

return $result;
}

/**
* {@inheritdoc}
*/
public function setVoters(array $voters)
{
if (!method_exists($this->manager, 'setVoters')) {
return;
}

$this->voters = $voters;
$this->manager->setVoters($voters);
}

/**
* @return string
*/
public function getStrategy()
{
// The $strategy property is misleading because it stores the name of its
// method (e.g. 'decideAffirmative') instead of the original strategy name
// (e.g. 'affirmative')
return null === $this->strategy ? '-' : strtolower(substr($this->strategy, 6));
}

/**
* @return array
*/
public function getVoters()
{
return $this->voters;
}

/**
* @return array
*/
public function getDecisionLog()
{
return $this->decisionLog;
}
}

class_alias(TraceableAccessDecisionManager::class, DebugAccessDecisionManager::class);
Original file line number Diff line number Diff line change
Expand Up @@ -13,16 +13,17 @@

use Symfony\Component\Security\Core\Authorization\AccessDecisionManager;
use Symfony\Component\Security\Core\Authorization\DebugAccessDecisionManager;
use Symfony\Component\Security\Core\Authorization\TraceableAccessDecisionManager;
use Symfony\Component\Security\Core\Authentication\Token\TokenInterface;

class DebugAccessDecisionManagerTest extends \PHPUnit_Framework_TestCase
class TraceableAccessDecisionManagerTest extends \PHPUnit_Framework_TestCase
{
/**
* @dataProvider provideObjectsAndLogs
*/
public function testDecideLog($expectedLog, $object)
{
$adm = new DebugAccessDecisionManager(new AccessDecisionManager());
$adm = new TraceableAccessDecisionManager(new AccessDecisionManager());
$adm->decide($this->getMockBuilder(TokenInterface::class)->getMock(), array('ATTRIBUTE_1'), $object);

$this->assertSame($expectedLog, $adm->getDecisionLog());
Expand All @@ -40,4 +41,13 @@ public function provideObjectsAndLogs()
yield array(array(array('attributes' => array('ATTRIBUTE_1'), 'object' => $x = array(), 'result' => false)), $x);
yield array(array(array('attributes' => array('ATTRIBUTE_1'), 'object' => $object, 'result' => false)), $object);
}

public function testDebugAccessDecisionManagerAliasExistsForBC()
{
$adm = new TraceableAccessDecisionManager(new AccessDecisionManager());

if (!$adm instanceof DebugAccessDecisionManager) {
$this->fail('For BC, TraceableAccessDecisionManager must be an instance of DebugAccessDecisionManager');
}
}
}