Thanks to visit codestin.com
Credit goes to github.com

Skip to content

Commit f633d9e

Browse files
committed
Give info about called security listeners in profiler
1 parent 25f1368 commit f633d9e

File tree

10 files changed

+320
-19
lines changed

10 files changed

+320
-19
lines changed

src/Symfony/Bundle/SecurityBundle/DataCollector/SecurityDataCollector.php

Lines changed: 15 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -19,6 +19,7 @@
1919
use Symfony\Component\HttpKernel\DataCollector\DataCollector;
2020
use Symfony\Component\HttpKernel\DataCollector\LateDataCollectorInterface;
2121
use Symfony\Component\Security\Core\Role\RoleInterface;
22+
use Symfony\Bundle\SecurityBundle\Debug\TraceableFirewallListener;
2223
use Symfony\Component\Security\Http\Logout\LogoutUrlGenerator;
2324
use Symfony\Component\Security\Core\Authorization\AccessDecisionManagerInterface;
2425
use Symfony\Component\Security\Core\Authorization\TraceableAccessDecisionManager;
@@ -38,7 +39,7 @@ class SecurityDataCollector extends DataCollector implements LateDataCollectorIn
3839
private $roleHierarchy;
3940
private $logoutUrlGenerator;
4041
private $accessDecisionManager;
41-
private $firewallMap;
42+
private $firewall;
4243
private $hasVarDumper;
4344

4445
/**
@@ -48,15 +49,15 @@ class SecurityDataCollector extends DataCollector implements LateDataCollectorIn
4849
* @param RoleHierarchyInterface|null $roleHierarchy
4950
* @param LogoutUrlGenerator|null $logoutUrlGenerator
5051
* @param AccessDecisionManagerInterface|null $accessDecisionManager
51-
* @param FirewallMapInterface|null $firewallMap
52+
* @param TraceableFirewallListener|null $firewall
5253
*/
53-
public function __construct(TokenStorageInterface $tokenStorage = null, RoleHierarchyInterface $roleHierarchy = null, LogoutUrlGenerator $logoutUrlGenerator = null, AccessDecisionManagerInterface $accessDecisionManager = null, FirewallMapInterface $firewallMap = null)
54+
public function __construct(TokenStorageInterface $tokenStorage = null, RoleHierarchyInterface $roleHierarchy = null, LogoutUrlGenerator $logoutUrlGenerator = null, AccessDecisionManagerInterface $accessDecisionManager = null, TraceableFirewallListener $firewall = null)
5455
{
5556
$this->tokenStorage = $tokenStorage;
5657
$this->roleHierarchy = $roleHierarchy;
5758
$this->logoutUrlGenerator = $logoutUrlGenerator;
5859
$this->accessDecisionManager = $accessDecisionManager;
59-
$this->firewallMap = $firewallMap;
60+
$this->firewall = $firewall;
6061
$this->hasVarDumper = class_exists(ClassStub::class);
6162
}
6263

@@ -148,9 +149,11 @@ public function collect(Request $request, Response $response, \Exception $except
148149

149150
// collect firewall context information
150151
$this->data['firewall'] = null;
151-
if ($this->firewallMap instanceof FirewallMap) {
152-
$firewallConfig = $this->firewallMap->getFirewallConfig($request);
153-
if (null !== $firewallConfig) {
152+
$this->data['listeners'] = array();
153+
if ($this->firewall) {
154+
$map = $this->firewall->getMap();
155+
$this->data['listeners'] = $this->firewall->getWrappedListeners();
156+
if ($map instanceof FirewallMap && $firewallConfig = $map->getFirewallConfig($request)) {
154157
$this->data['firewall'] = array(
155158
'name' => $firewallConfig->getName(),
156159
'allows_anonymous' => $firewallConfig->allowsAnonymous(),
@@ -305,6 +308,11 @@ public function getFirewall()
305308
return $this->data['firewall'];
306309
}
307310

311+
public function getListeners()
312+
{
313+
return $this->data['listeners'];
314+
}
315+
308316
/**
309317
* {@inheritdoc}
310318
*/
Lines changed: 46 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,46 @@
1+
<?php
2+
3+
/*
4+
* This file is part of the Symfony package.
5+
*
6+
* (c) Fabien Potencier <[email protected]>
7+
*
8+
* For the full copyright and license information, please view the LICENSE
9+
* file that was distributed with this source code.
10+
*/
11+
12+
namespace Symfony\Bundle\SecurityBundle\Debug;
13+
14+
use Symfony\Bundle\SecurityBundle\EventListener\FirewallListener;
15+
use Symfony\Component\HttpKernel\Event\GetResponseEvent;
16+
17+
/**
18+
* Firewall collecting called listeners.
19+
*
20+
* @author Robin Chalas <[email protected]>
21+
*
22+
* @final since version 3.3
23+
*/
24+
class TraceableFirewallListener extends FirewallListener
25+
{
26+
private $wrappedListeners;
27+
28+
public function getWrappedListeners()
29+
{
30+
return $this->wrappedListeners;
31+
}
32+
33+
protected function handleRequest(GetResponseEvent $event, $listeners)
34+
{
35+
foreach ($listeners as $listener) {
36+
$wrappedListener = new WrappedListener($listener);
37+
$wrappedListener->handle($event);
38+
$wrappedListener->response = $event->getResponse();
39+
$this->wrappedListeners[] = $wrappedListener->getInfo();
40+
41+
if ($event->hasResponse()) {
42+
break;
43+
}
44+
}
45+
}
46+
}
Lines changed: 74 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,74 @@
1+
<?php
2+
3+
/*
4+
* This file is part of the Symfony package.
5+
*
6+
* (c) Fabien Potencier <[email protected]>
7+
*
8+
* For the full copyright and license information, please view the LICENSE
9+
* file that was distributed with this source code.
10+
*/
11+
12+
namespace Symfony\Bundle\SecurityBundle\Debug;
13+
14+
use Symfony\Component\HttpKernel\Event\GetResponseEvent;
15+
use Symfony\Component\Security\Http\Firewall\ListenerInterface;
16+
use Symfony\Component\VarDumper\Caster\ClassStub;
17+
use Symfony\Component\VarDumper\Cloner\VarCloner;
18+
19+
/**
20+
* Wraps a security listener for calls record.
21+
*
22+
* @author Robin Chalas <[email protected]>
23+
*/
24+
class WrappedListener implements ListenerInterface
25+
{
26+
public $response;
27+
private $listener;
28+
private $time;
29+
private $stub;
30+
31+
public function __construct(ListenerInterface $listener)
32+
{
33+
$this->listener = $listener;
34+
}
35+
36+
/**
37+
* {@inheritdoc}
38+
*/
39+
public function handle(GetResponseEvent $event)
40+
{
41+
$startTime = microtime(true);
42+
$this->listener->handle($event);
43+
$this->time = microtime(true) - $startTime;
44+
}
45+
46+
/**
47+
* Proxies all method calls to the original listener.
48+
*/
49+
public function __call($method, $arguments)
50+
{
51+
return call_user_func_array(array($this->listener, $method), $arguments);
52+
}
53+
54+
public function getWrappedListener()
55+
{
56+
return $this->listener;
57+
}
58+
59+
public function getInfo()
60+
{
61+
$pretty = get_class($this->listener);
62+
63+
if (null === $this->stub) {
64+
$this->stub = new ClassStub($pretty);
65+
}
66+
67+
return array(
68+
'response' => $this->response,
69+
'time' => $this->time,
70+
'pretty' => $pretty,
71+
'stub' => $this->stub,
72+
);
73+
}
74+
}

src/Symfony/Bundle/SecurityBundle/EventListener/FirewallListener.php

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -56,4 +56,12 @@ public function onKernelFinishRequest(FinishRequestEvent $event)
5656

5757
parent::onKernelFinishRequest($event);
5858
}
59+
60+
/**
61+
* @internal
62+
*/
63+
public function getMap()
64+
{
65+
return $this->map;
66+
}
5967
}

src/Symfony/Bundle/SecurityBundle/Resources/config/collectors.xml

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -13,7 +13,7 @@
1313
<argument type="service" id="security.role_hierarchy" />
1414
<argument type="service" id="security.logout_url_generator" />
1515
<argument type="service" id="security.access.decision_manager" />
16-
<argument type="service" id="security.firewall.map" />
16+
<argument type="service" id="security.firewall" />
1717
</service>
1818
</services>
1919
</container>

src/Symfony/Bundle/SecurityBundle/Resources/config/security_debug.xml

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -10,5 +10,11 @@
1010
<service id="debug.security.access.decision_manager" class="Symfony\Component\Security\Core\Authorization\TraceableAccessDecisionManager" decorates="security.access.decision_manager">
1111
<argument type="service" id="debug.security.access.decision_manager.inner" />
1212
</service>
13+
14+
<service id="debug.security.firewall" class="Symfony\Bundle\SecurityBundle\Debug\TraceableFirewallListener" decorates="security.firewall">
15+
<argument type="service" id="security.firewall.map" />
16+
<argument type="service" id="event_dispatcher" />
17+
<argument type="service" id="security.logout_url_generator" />
18+
</service>
1319
</services>
1420
</container>

src/Symfony/Bundle/SecurityBundle/Resources/views/Collector/security.html.twig

Lines changed: 42 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -150,6 +150,8 @@
150150
</div>
151151

152152
{% if collector.firewall.security_enabled %}
153+
<h4>Configuration</h4>
154+
153155
<table>
154156
<thead>
155157
<tr>
@@ -188,6 +190,46 @@
188190
</tr>
189191
</tbody>
190192
</table>
193+
194+
<h4>Listeners</h4>
195+
196+
{% if collector.listeners|default([]) is empty %}
197+
<div class="empty">
198+
<p>No security listeners have been recorded. Check that debugging is enabled in the kernel.</p>
199+
</div>
200+
{% else %}
201+
<table>
202+
<thead>
203+
<tr>
204+
<th>Listener</th>
205+
<th>Duration</th>
206+
<th>Response</th>
207+
</tr>
208+
</thead>
209+
210+
{% set previous_event = (collector.listeners|first) %}
211+
{% for listener in collector.listeners %}
212+
{% if loop.first or listener != previous_event %}
213+
{% if not loop.first %}
214+
</tbody>
215+
{% endif %}
216+
217+
<tbody>
218+
{% set previous_event = listener %}
219+
{% endif %}
220+
221+
<tr>
222+
<td class="font-normal">{{ profiler_dump(listener.stub) }}</td>
223+
<td class="no-wrap">{{ '%0.2f'|format(listener.time * 1000) }} ms</td>
224+
<td class="font-normal">{{ listener.response ? profiler_dump(listener.response) : '(none)' }}</td>
225+
</tr>
226+
227+
{% if loop.last %}
228+
</tbody>
229+
{% endif %}
230+
{% endfor %}
231+
</table>
232+
{% endif %}
191233
{% endif %}
192234
{% elseif collector.enabled %}
193235
<div class="empty">

src/Symfony/Bundle/SecurityBundle/Tests/DataCollector/SecurityDataCollectorTest.php

Lines changed: 48 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -13,13 +13,19 @@
1313

1414
use PHPUnit\Framework\TestCase;
1515
use Symfony\Bundle\SecurityBundle\DataCollector\SecurityDataCollector;
16+
use Symfony\Bundle\SecurityBundle\Debug\TraceableFirewallListener;
1617
use Symfony\Bundle\SecurityBundle\Security\FirewallConfig;
1718
use Symfony\Bundle\SecurityBundle\Security\FirewallMap;
19+
use Symfony\Component\EventDispatcher\EventDispatcher;
20+
use Symfony\Component\HttpKernel\Event\GetResponseEvent;
21+
use Symfony\Component\HttpKernel\HttpKernelInterface;
1822
use Symfony\Component\Security\Core\Authentication\Token\Storage\TokenStorage;
1923
use Symfony\Component\Security\Core\Authentication\Token\UsernamePasswordToken;
2024
use Symfony\Component\Security\Core\Role\Role;
2125
use Symfony\Component\Security\Core\Role\RoleHierarchy;
26+
use Symfony\Component\Security\Http\Firewall\ListenerInterface;
2227
use Symfony\Component\Security\Http\FirewallMapInterface;
28+
use Symfony\Component\Security\Http\Logout\LogoutUrlGenerator;
2329

2430
class SecurityDataCollectorTest extends TestCase
2531
{
@@ -89,7 +95,7 @@ public function testGetFirewall()
8995
->with($request)
9096
->willReturn($firewallConfig);
9197

92-
$collector = new SecurityDataCollector(null, null, null, null, $firewallMap);
98+
$collector = new SecurityDataCollector(null, null, null, null, new TraceableFirewallListener($firewallMap, new EventDispatcher(), new LogoutUrlGenerator()));
9399
$collector->collect($request, $this->getResponse());
94100
$collector->lateCollect();
95101
$collected = $collector->getFirewall();
@@ -124,7 +130,7 @@ public function testGetFirewallReturnsNull()
124130
->disableOriginalConstructor()
125131
->getMock();
126132

127-
$collector = new SecurityDataCollector(null, null, null, null, $firewallMap);
133+
$collector = new SecurityDataCollector(null, null, null, null, new TraceableFirewallListener($firewallMap, new EventDispatcher(), new LogoutUrlGenerator()));
128134
$collector->collect($request, $response);
129135
$this->assertNull($collector->getFirewall());
130136

@@ -134,11 +140,50 @@ public function testGetFirewallReturnsNull()
134140
->disableOriginalConstructor()
135141
->getMock();
136142

137-
$collector = new SecurityDataCollector(null, null, null, null, $firewallMap);
143+
$collector = new SecurityDataCollector(null, null, null, null, new TraceableFirewallListener($firewallMap, new EventDispatcher(), new LogoutUrlGenerator()));
138144
$collector->collect($request, $response);
139145
$this->assertNull($collector->getFirewall());
140146
}
141147

148+
/**
149+
* @group time-sensitive
150+
*/
151+
public function testGetListeners()
152+
{
153+
$request = $this->getRequest();
154+
$event = new GetResponseEvent($this->getMockBuilder(HttpKernelInterface::class)->getMock(), $request, HttpKernelInterface::MASTER_REQUEST);
155+
$event->setResponse($response = $this->getResponse());
156+
$listener = $this->getMockBuilder(ListenerInterface::class)->getMock();
157+
$listener
158+
->expects($this->once())
159+
->method('handle')
160+
->with($event);
161+
$firewallMap = $this
162+
->getMockBuilder(FirewallMap::class)
163+
->disableOriginalConstructor()
164+
->getMock();
165+
$firewallMap
166+
->expects($this->any())
167+
->method('getFirewallConfig')
168+
->with($request)
169+
->willReturn(null);
170+
$firewallMap
171+
->expects($this->once())
172+
->method('getListeners')
173+
->with($request)
174+
->willReturn(array(array($listener), null));
175+
176+
$firewall = new TraceableFirewallListener($firewallMap, new EventDispatcher(), new LogoutUrlGenerator());
177+
$firewall->onKernelRequest($event);
178+
179+
$collector = new SecurityDataCollector(null, null, null, null, $firewall);
180+
$collector->collect($request, $response);
181+
182+
$this->assertNotEmpty($collected = $collector->getListeners()[0]);
183+
$collector->lateCollect();
184+
$this->addToAssertionCount(1);
185+
}
186+
142187
public function provideRoles()
143188
{
144189
return array(

0 commit comments

Comments
 (0)