From 75e208e419dc8120535432ad99a3c59713b7a7ec Mon Sep 17 00:00:00 2001 From: Robin Chalas Date: Sat, 30 Jul 2016 11:03:31 +0200 Subject: [PATCH] Integrate current firewall in profiler --- .../DataCollector/SecurityDataCollector.php | 39 +++- .../Resources/config/collectors.xml | 1 + .../views/Collector/security.html.twig | 166 +++++++++++++----- .../SecurityDataCollectorTest.php | 69 ++++++++ 4 files changed, 229 insertions(+), 46 deletions(-) diff --git a/src/Symfony/Bundle/SecurityBundle/DataCollector/SecurityDataCollector.php b/src/Symfony/Bundle/SecurityBundle/DataCollector/SecurityDataCollector.php index 2c66e86c34..7234c15030 100644 --- a/src/Symfony/Bundle/SecurityBundle/DataCollector/SecurityDataCollector.php +++ b/src/Symfony/Bundle/SecurityBundle/DataCollector/SecurityDataCollector.php @@ -21,6 +21,8 @@ use Symfony\Component\Security\Http\Logout\LogoutUrlGenerator; use Symfony\Component\Security\Core\Authorization\AccessDecisionManagerInterface; use Symfony\Component\Security\Core\Authorization\DebugAccessDecisionManager; use Symfony\Component\VarDumper\Cloner\Data; +use Symfony\Component\Security\Http\FirewallMapInterface; +use Symfony\Bundle\SecurityBundle\Security\FirewallMAp; /** * SecurityDataCollector. @@ -33,6 +35,7 @@ class SecurityDataCollector extends DataCollector private $roleHierarchy; private $logoutUrlGenerator; private $accessDecisionManager; + private $firewallMap; /** * Constructor. @@ -41,13 +44,15 @@ class SecurityDataCollector extends DataCollector * @param RoleHierarchyInterface|null $roleHierarchy * @param LogoutUrlGenerator|null $logoutUrlGenerator * @param AccessDecisionManagerInterface|null $accessDecisionManager + * @param FirewallMapInterface|null $firewallMap */ - public function __construct(TokenStorageInterface $tokenStorage = null, RoleHierarchyInterface $roleHierarchy = null, LogoutUrlGenerator $logoutUrlGenerator = null, AccessDecisionManagerInterface $accessDecisionManager = null) + public function __construct(TokenStorageInterface $tokenStorage = null, RoleHierarchyInterface $roleHierarchy = null, LogoutUrlGenerator $logoutUrlGenerator = null, AccessDecisionManagerInterface $accessDecisionManager = null, FirewallMapInterface $firewallMap = null) { $this->tokenStorage = $tokenStorage; $this->roleHierarchy = $roleHierarchy; $this->logoutUrlGenerator = $logoutUrlGenerator; $this->accessDecisionManager = $accessDecisionManager; + $this->firewallMap = $firewallMap; } /** @@ -132,6 +137,28 @@ class SecurityDataCollector extends DataCollector $this->data['voter_strategy'] = 'unknown'; $this->data['voters'] = array(); } + + // collect firewall context information + $this->data['firewall'] = null; + if ($this->firewallMap instanceof FirewallMap) { + $firewallConfig = $this->firewallMap->getFirewallConfig($request); + if (null !== $firewallConfig) { + $this->data['firewall'] = array( + 'name' => $firewallConfig->getName(), + 'allows_anonymous' => $firewallConfig->allowsAnonymous(), + 'request_matcher' => $firewallConfig->getRequestMatcher(), + 'security_enabled' => $firewallConfig->isSecurityEnabled(), + 'stateless' => $firewallConfig->isStateless(), + 'provider' => $firewallConfig->getProvider(), + 'context' => $firewallConfig->getContext(), + 'entry_point' => $firewallConfig->getEntryPoint(), + 'access_denied_handler' => $firewallConfig->getAccessDeniedHandler(), + 'access_denied_url' => $firewallConfig->getAccessDeniedUrl(), + 'user_checker' => $firewallConfig->getUserChecker(), + 'listeners' => $this->cloneVar($firewallConfig->getListeners()), + ); + } + } } /** @@ -255,6 +282,16 @@ class SecurityDataCollector extends DataCollector return $this->data['access_decision_log']; } + /** + * Returns the configuration of the current firewall context. + * + * @return array + */ + public function getFirewall() + { + return $this->data['firewall']; + } + /** * {@inheritdoc} */ diff --git a/src/Symfony/Bundle/SecurityBundle/Resources/config/collectors.xml b/src/Symfony/Bundle/SecurityBundle/Resources/config/collectors.xml index de157d5182..f812a9d790 100644 --- a/src/Symfony/Bundle/SecurityBundle/Resources/config/collectors.xml +++ b/src/Symfony/Bundle/SecurityBundle/Resources/config/collectors.xml @@ -11,6 +11,7 @@ + diff --git a/src/Symfony/Bundle/SecurityBundle/Resources/views/Collector/security.html.twig b/src/Symfony/Bundle/SecurityBundle/Resources/views/Collector/security.html.twig index e857f579e3..31ffe3c9d2 100644 --- a/src/Symfony/Bundle/SecurityBundle/Resources/views/Collector/security.html.twig +++ b/src/Symfony/Bundle/SecurityBundle/Resources/views/Collector/security.html.twig @@ -33,6 +33,12 @@ {{ collector.tokenClass|abbr_class }} {% endif %} + {% if collector.firewall %} +
+ Firewall name + {{ collector.firewall.name }} +
+ {% endif %} {% if collector.logoutUrl %}
Actions @@ -63,57 +69,127 @@ {% block panel %}

Security Token

- {% if collector.token %} -
-
- {{ collector.user == 'anon.' ? 'Anonymous' : collector.user }} - Username + {% if collector.enabled %} + {% if collector.token %} +
+
+ {{ collector.user == 'anon.' ? 'Anonymous' : collector.user }} + Username +
+ +
+ {{ include('@WebProfiler/Icon/' ~ (collector.authenticated ? 'yes' : 'no') ~ '.svg') }} + Authenticated +
-
- {{ include('@WebProfiler/Icon/' ~ (collector.authenticated ? 'yes' : 'no') ~ '.svg') }} - Authenticated + + + + + + + + + + + + + + {% if collector.supportsRoleHierarchy %} + + + + + {% endif %} + + {% if collector.token %} + + + + + {% endif %} + +
PropertyValue
Roles + {{ collector.roles is empty ? 'none' : profiler_dump(collector.roles, maxDepth=1) }} + + {% if not collector.authenticated and collector.roles is empty %} +

User is not authenticated probably because they have no roles.

+ {% endif %} +
Inherited Roles{{ collector.inheritedRoles is empty ? 'none' : profiler_dump(collector.inheritedRoles, maxDepth=1) }}
Token{{ profiler_dump(collector.token) }}
+ {% elseif collector.enabled %} +
+

There is no security token.

-
+ {% endif %} - - - - - - - - - - - - +

Security Firewall

- {% if collector.supportsRoleHierarchy %} - - - - - {% endif %} - - {% if collector.token %} - - - - - {% endif %} - -
PropertyValue
Roles - {{ collector.roles is empty ? 'none' : profiler_dump(collector.roles, maxDepth=1) }} - {% if not collector.authenticated and collector.roles is empty %} -

User is not authenticated probably because they have no roles.

- {% endif %} -
Inherited Roles{{ collector.inheritedRoles is empty ? 'none' : profiler_dump(collector.inheritedRoles, maxDepth=1) }}
Token{{ profiler_dump(collector.token) }}
- {% elseif collector.enabled %} -
-

There is no security token.

-
+ {% if collector.firewall %} +
+
+ {{ collector.firewall.name }} + Name +
+
+ {{ include('@WebProfiler/Icon/' ~ (collector.firewall.security_enabled ? 'yes' : 'no') ~ '.svg') }} + Security enabled +
+
+ {{ include('@WebProfiler/Icon/' ~ (collector.firewall.stateless ? 'yes' : 'no') ~ '.svg') }} + Stateless +
+
+ {{ include('@WebProfiler/Icon/' ~ (collector.firewall.allows_anonymous ? 'yes' : 'no') ~ '.svg') }} + Allows anonymous +
+
+ {% if collector.firewall.security_enabled %} + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
KeyValue
provider{{ collector.firewall.provider }}
context{{ collector.firewall.context }}
entry_point{{ collector.firewall.entry_point }}
user_checker{{ collector.firewall.user_checker }}
access_denied_handler{{ collector.firewall.access_denied_handler }}
access_denied_url{{ collector.firewall.access_denied_url }}
listeners{{ collector.firewall.listeners is empty ? 'none' : profiler_dump(collector.firewall.listeners, maxDepth=1) }}
+ {% endif %} + {% elseif collector.enabled %} +
+

There is no firewall.

+
+ {% endif %} {% else %}

The security component is disabled.

diff --git a/src/Symfony/Bundle/SecurityBundle/Tests/DataCollector/SecurityDataCollectorTest.php b/src/Symfony/Bundle/SecurityBundle/Tests/DataCollector/SecurityDataCollectorTest.php index a9b6818d64..e5a6a4b760 100644 --- a/src/Symfony/Bundle/SecurityBundle/Tests/DataCollector/SecurityDataCollectorTest.php +++ b/src/Symfony/Bundle/SecurityBundle/Tests/DataCollector/SecurityDataCollectorTest.php @@ -12,10 +12,13 @@ namespace Symfony\Bundle\SecurityBundle\Tests\DataCollector; use Symfony\Bundle\SecurityBundle\DataCollector\SecurityDataCollector; +use Symfony\Bundle\SecurityBundle\Security\FirewallConfig; +use Symfony\Bundle\SecurityBundle\Security\FirewallMap; use Symfony\Component\Security\Core\Authentication\Token\Storage\TokenStorage; use Symfony\Component\Security\Core\Authentication\Token\UsernamePasswordToken; use Symfony\Component\Security\Core\Role\Role; use Symfony\Component\Security\Core\Role\RoleHierarchy; +use Symfony\Component\Security\Http\FirewallMapInterface; class SecurityDataCollectorTest extends \PHPUnit_Framework_TestCase { @@ -32,6 +35,7 @@ class SecurityDataCollectorTest extends \PHPUnit_Framework_TestCase $this->assertCount(0, $collector->getRoles()); $this->assertCount(0, $collector->getInheritedRoles()); $this->assertEmpty($collector->getUser()); + $this->assertNull($collector->getFirewall()); } public function testCollectWhenAuthenticationTokenIsNull() @@ -47,6 +51,7 @@ class SecurityDataCollectorTest extends \PHPUnit_Framework_TestCase $this->assertCount(0, $collector->getRoles()); $this->assertCount(0, $collector->getInheritedRoles()); $this->assertEmpty($collector->getUser()); + $this->assertNull($collector->getFirewall()); } /** @dataProvider provideRoles */ @@ -71,6 +76,70 @@ class SecurityDataCollectorTest extends \PHPUnit_Framework_TestCase $this->assertSame('hhamon', $collector->getUser()); } + public function testGetFirewall() + { + $firewallConfig = new FirewallConfig('dummy', 'security.request_matcher.dummy'); + $request = $this->getRequest(); + + $firewallMap = $this + ->getMockBuilder(FirewallMap::class) + ->disableOriginalConstructor() + ->getMock(); + $firewallMap + ->expects($this->once()) + ->method('getFirewallConfig') + ->with($request) + ->willReturn($firewallConfig); + + $collector = new SecurityDataCollector(null, null, null, null, $firewallMap); + $collector->collect($request, $this->getResponse()); + $collected = $collector->getFirewall(); + + $this->assertSame($firewallConfig->getName(), $collected['name']); + $this->assertSame($firewallConfig->allowsAnonymous(), $collected['allows_anonymous']); + $this->assertSame($firewallConfig->getRequestMatcher(), $collected['request_matcher']); + $this->assertSame($firewallConfig->isSecurityEnabled(), $collected['security_enabled']); + $this->assertSame($firewallConfig->isStateless(), $collected['stateless']); + $this->assertSame($firewallConfig->getProvider(), $collected['provider']); + $this->assertSame($firewallConfig->getContext(), $collected['context']); + $this->assertSame($firewallConfig->getEntryPoint(), $collected['entry_point']); + $this->assertSame($firewallConfig->getAccessDeniedHandler(), $collected['access_denied_handler']); + $this->assertSame($firewallConfig->getAccessDeniedUrl(), $collected['access_denied_url']); + $this->assertSame($firewallConfig->getUserChecker(), $collected['user_checker']); + $this->assertSame($firewallConfig->getListeners(), $collected['listeners']->getRawData()[0][0]); + } + + public function testGetFirewallReturnsNull() + { + $request = $this->getRequest(); + $response = $this->getResponse(); + + // Don't inject any firewall map + $collector = new SecurityDataCollector(); + $collector->collect($request, $response); + $this->assertNull($collector->getFirewall()); + + // Inject an instance that is not context aware + $firewallMap = $this + ->getMockBuilder(FirewallMapInterface::class) + ->disableOriginalConstructor() + ->getMock(); + + $collector = new SecurityDataCollector(null, null, null, null, $firewallMap); + $collector->collect($request, $response); + $this->assertNull($collector->getFirewall()); + + // Null config + $firewallMap = $this + ->getMockBuilder(FirewallMap::class) + ->disableOriginalConstructor() + ->getMock(); + + $collector = new SecurityDataCollector(null, null, null, null, $firewallMap); + $collector->collect($request, $response); + $this->assertNull($collector->getFirewall()); + } + public function provideRoles() { return array(