feature #19490 [SecurityBundle] Integrate current firewall in Profiler (chalasr)
This PR was merged into the 3.2-dev branch.
Discussion
----------
[SecurityBundle] Integrate current firewall in Profiler
| Q | A |
| --- | --- |
| Branch? | master |
| Bug fix? | no |
| New feature? | yes |
| BC breaks? | no |
| Deprecations? | no |
| Tests pass? | yes |
| Fixed tickets | n/a |
| License | MIT |
Based on #19398.
This integrates current firewall information into the Profiler.
**Toolbar**
![Profiler toolbar](http://image.prntscr.com/image/bedec39cea4945e994c8531b80241cf6.png)
**Panel**
![Profiler panel](http://image.prntscr.com/image/3b656c1346844c6194a0db42cb8f9fdc.png)
Examples:
<details>
<summary>
Show config</summary>
``` yaml
main:
pattern: ^/
anonymous: false
stateless: true
provider: in_memory
access_denied_url: /access_denied
http_basic: ~
```
</details>
![Panel](http://image.prntscr.com/image/057062a1da744f3c8e00c3c77ded46a8.png)
<details>
<summary>
Show config</summary>
``` yaml
main:
pattern: ^/
anonymous: true
stateless: false
provider: in_memory
context: dummy
access_denied_url: /access_denied
http_basic: ~
```
</details>
![Panel](http://image.prntscr.com/image/a44e54cf018d4bc98c3e0ecf92c37416.png)
<details>
<summary>
Show config</summary>
``` yaml
api:
pattern: ^/
security: false
```
</details>
![Panel](http://image.prntscr.com/image/c4ea3d7c792447b2ae2b18cd4e08d0dd.png)
Commits
-------
75e208e
Integrate current firewall in profiler
This commit is contained in:
commit
f747fffeb4
@ -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}
|
||||
*/
|
||||
|
@ -11,6 +11,7 @@
|
||||
<argument type="service" id="security.role_hierarchy" />
|
||||
<argument type="service" id="security.logout_url_generator" />
|
||||
<argument type="service" id="security.access.decision_manager" />
|
||||
<argument type="service" id="security.firewall.map" />
|
||||
</service>
|
||||
</services>
|
||||
</container>
|
||||
|
@ -33,6 +33,12 @@
|
||||
<span>{{ collector.tokenClass|abbr_class }}</span>
|
||||
</div>
|
||||
{% endif %}
|
||||
{% if collector.firewall %}
|
||||
<div class="sf-toolbar-info-piece">
|
||||
<b>Firewall name</b>
|
||||
<span>{{ collector.firewall.name }}</span>
|
||||
</div>
|
||||
{% endif %}
|
||||
{% if collector.logoutUrl %}
|
||||
<div class="sf-toolbar-info-piece">
|
||||
<b>Actions</b>
|
||||
@ -63,6 +69,7 @@
|
||||
{% block panel %}
|
||||
<h2>Security Token</h2>
|
||||
|
||||
{% if collector.enabled %}
|
||||
{% if collector.token %}
|
||||
<div class="metrics">
|
||||
<div class="metric">
|
||||
@ -114,6 +121,75 @@
|
||||
<div class="empty">
|
||||
<p>There is no security token.</p>
|
||||
</div>
|
||||
{% endif %}
|
||||
|
||||
|
||||
<h2>Security Firewall</h2>
|
||||
|
||||
{% if collector.firewall %}
|
||||
<div class="metrics">
|
||||
<div class="metric">
|
||||
<span class="value">{{ collector.firewall.name }}</span>
|
||||
<span class="label">Name</span>
|
||||
</div>
|
||||
<div class="metric">
|
||||
<span class="value">{{ include('@WebProfiler/Icon/' ~ (collector.firewall.security_enabled ? 'yes' : 'no') ~ '.svg') }}</span>
|
||||
<span class="label">Security enabled</span>
|
||||
</div>
|
||||
<div class="metric">
|
||||
<span class="value">{{ include('@WebProfiler/Icon/' ~ (collector.firewall.stateless ? 'yes' : 'no') ~ '.svg') }}</span>
|
||||
<span class="label">Stateless</span>
|
||||
</div>
|
||||
<div class="metric">
|
||||
<span class="value">{{ include('@WebProfiler/Icon/' ~ (collector.firewall.allows_anonymous ? 'yes' : 'no') ~ '.svg') }}</span>
|
||||
<span class="label">Allows anonymous</span>
|
||||
</div>
|
||||
</div>
|
||||
{% if collector.firewall.security_enabled %}
|
||||
<table>
|
||||
<thead>
|
||||
<tr>
|
||||
<th scope="col" class="key">Key</th>
|
||||
<th scope="col">Value</th>
|
||||
</tr>
|
||||
</thead>
|
||||
<tbody>
|
||||
<tr>
|
||||
<th>provider</th>
|
||||
<td>{{ collector.firewall.provider }}</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<th>context</th>
|
||||
<td>{{ collector.firewall.context }}</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<th>entry_point</th>
|
||||
<td>{{ collector.firewall.entry_point }}</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<th>user_checker</th>
|
||||
<td>{{ collector.firewall.user_checker }}</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<th>access_denied_handler</th>
|
||||
<td>{{ collector.firewall.access_denied_handler }}</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<th>access_denied_url</th>
|
||||
<td>{{ collector.firewall.access_denied_url }}</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<th>listeners</th>
|
||||
<td>{{ collector.firewall.listeners is empty ? 'none' : profiler_dump(collector.firewall.listeners, maxDepth=1) }}</td>
|
||||
</tr>
|
||||
</tbody>
|
||||
</table>
|
||||
{% endif %}
|
||||
{% elseif collector.enabled %}
|
||||
<div class="empty">
|
||||
<p>There is no firewall.</p>
|
||||
</div>
|
||||
{% endif %}
|
||||
{% else %}
|
||||
<div class="empty">
|
||||
<p>The security component is disabled.</p>
|
||||
|
@ -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(
|
||||
|
Reference in New Issue
Block a user