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

Skip to content

Commit 493097e

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

File tree

11 files changed

+313
-21
lines changed

11 files changed

+313
-21
lines changed

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

Lines changed: 15 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -19,12 +19,12 @@
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;
2526
use Symfony\Component\VarDumper\Caster\ClassStub;
2627
use Symfony\Component\VarDumper\Cloner\Data;
27-
use Symfony\Component\Security\Http\FirewallMapInterface;
2828
use Symfony\Bundle\SecurityBundle\Security\FirewallMap;
2929

3030
/**
@@ -38,7 +38,7 @@ class SecurityDataCollector extends DataCollector implements LateDataCollectorIn
3838
private $roleHierarchy;
3939
private $logoutUrlGenerator;
4040
private $accessDecisionManager;
41-
private $firewallMap;
41+
private $firewall;
4242
private $hasVarDumper;
4343

4444
/**
@@ -48,15 +48,15 @@ class SecurityDataCollector extends DataCollector implements LateDataCollectorIn
4848
* @param RoleHierarchyInterface|null $roleHierarchy
4949
* @param LogoutUrlGenerator|null $logoutUrlGenerator
5050
* @param AccessDecisionManagerInterface|null $accessDecisionManager
51-
* @param FirewallMapInterface|null $firewallMap
51+
* @param TraceableFirewallListener|null $firewall
5252
*/
53-
public function __construct(TokenStorageInterface $tokenStorage = null, RoleHierarchyInterface $roleHierarchy = null, LogoutUrlGenerator $logoutUrlGenerator = null, AccessDecisionManagerInterface $accessDecisionManager = null, FirewallMapInterface $firewallMap = null)
53+
public function __construct(TokenStorageInterface $tokenStorage = null, RoleHierarchyInterface $roleHierarchy = null, LogoutUrlGenerator $logoutUrlGenerator = null, AccessDecisionManagerInterface $accessDecisionManager = null, TraceableFirewallListener $firewall = null)
5454
{
5555
$this->tokenStorage = $tokenStorage;
5656
$this->roleHierarchy = $roleHierarchy;
5757
$this->logoutUrlGenerator = $logoutUrlGenerator;
5858
$this->accessDecisionManager = $accessDecisionManager;
59-
$this->firewallMap = $firewallMap;
59+
$this->firewall = $firewall;
6060
$this->hasVarDumper = class_exists(ClassStub::class);
6161
}
6262

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

149149
// collect firewall context information
150150
$this->data['firewall'] = null;
151-
if ($this->firewallMap instanceof FirewallMap) {
152-
$firewallConfig = $this->firewallMap->getFirewallConfig($request);
153-
if (null !== $firewallConfig) {
151+
$this->data['listeners'] = array();
152+
if ($this->firewall) {
153+
$map = $this->firewall->getMap();
154+
$this->data['listeners'] = $this->firewall->getWrappedListeners();
155+
if ($map instanceof FirewallMap && $firewallConfig = $map->getFirewallConfig($request)) {
154156
$this->data['firewall'] = array(
155157
'name' => $firewallConfig->getName(),
156158
'allows_anonymous' => $firewallConfig->allowsAnonymous(),
@@ -305,6 +307,11 @@ public function getFirewall()
305307
return $this->data['firewall'];
306308
}
307309

310+
public function getListeners()
311+
{
312+
return $this->data['listeners'];
313+
}
314+
308315
/**
309316
* {@inheritdoc}
310317
*/
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: 73 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,73 @@
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+
18+
/**
19+
* Wraps a security listener for calls record.
20+
*
21+
* @author Robin Chalas <[email protected]>
22+
*/
23+
class WrappedListener implements ListenerInterface
24+
{
25+
public $response;
26+
private $listener;
27+
private $time;
28+
private $stub;
29+
30+
public function __construct(ListenerInterface $listener)
31+
{
32+
$this->listener = $listener;
33+
}
34+
35+
/**
36+
* {@inheritdoc}
37+
*/
38+
public function handle(GetResponseEvent $event)
39+
{
40+
$startTime = microtime(true);
41+
$this->listener->handle($event);
42+
$this->time = microtime(true) - $startTime;
43+
}
44+
45+
/**
46+
* Proxies all method calls to the original listener.
47+
*/
48+
public function __call($method, $arguments)
49+
{
50+
return call_user_func_array(array($this->listener, $method), $arguments);
51+
}
52+
53+
public function getWrappedListener()
54+
{
55+
return $this->listener;
56+
}
57+
58+
public function getInfo()
59+
{
60+
$pretty = get_class($this->listener);
61+
62+
if (null === $this->stub) {
63+
$this->stub = new ClassStub($pretty);
64+
}
65+
66+
return array(
67+
'response' => $this->response,
68+
'time' => $this->time,
69+
'pretty' => $pretty,
70+
'stub' => $this->stub,
71+
);
72+
}
73+
}

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

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

5757
parent::onKernelFinishRequest($event);
5858
}
59+
60+
public function getMap()
61+
{
62+
return $this->map;
63+
}
5964
}

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" public="true">
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)