Skip to content

Commit 904e90b

Browse files
committed
feature #19398 [DX][SecurityBundle] Introduce a FirewallConfig class accessible from FirewallContext (chalasr)
This PR was merged into the 3.2-dev branch. Discussion ---------- [DX][SecurityBundle] Introduce a FirewallConfig class accessible from FirewallContext | Q | A | | --- | --- | | Branch? | master | | Bug fix? | no | | New feature? | yes | | BC breaks? | no | | Deprecations? | yes but it should not have any impact in userland | | Tests pass? | yes | | Fixed tickets | #15294 | | License | MIT | | Doc PR | todo | With this, the `FirewallContext` class now has a `getConfig()` method returning a `FirewallConfig` object representing the firewall configuration. Also this adds a `getContext()` method to the `FirewallMap` class of the `SecurityBundle`, to be able to retrieve the current context. In a next time, this could be useful to display some firewall related informations to the Profiler, as pointed out in #15294. Also, it can be useful to be able to access the current firewall configuration from an AuthenticationListener, especially for third party bundles (I can develop on demand). Commits ------- 52d25ed Introduce a FirewallConfig class
2 parents 34e5613 + 52d25ed commit 904e90b

File tree

8 files changed

+394
-8
lines changed

8 files changed

+394
-8
lines changed

src/Symfony/Bundle/SecurityBundle/DependencyInjection/SecurityExtension.php

+47-2
Original file line numberDiff line numberDiff line change
@@ -110,6 +110,7 @@ public function load(array $configs, ContainerBuilder $container)
110110
'Symfony\Component\Security\Core\Authorization\AccessDecisionManager',
111111
'Symfony\Component\Security\Core\Authorization\AuthorizationChecker',
112112
'Symfony\Component\Security\Core\Authorization\Voter\VoterInterface',
113+
'Symfony\Bundle\SecurityBundle\Security\FirewallConfig',
113114
'Symfony\Bundle\SecurityBundle\Security\FirewallMap',
114115
'Symfony\Bundle\SecurityBundle\Security\FirewallContext',
115116
'Symfony\Component\HttpFoundation\RequestMatcher',
@@ -236,14 +237,18 @@ private function createFirewalls($config, ContainerBuilder $container)
236237
$mapDef = $container->getDefinition('security.firewall.map');
237238
$map = $authenticationProviders = array();
238239
foreach ($firewalls as $name => $firewall) {
239-
list($matcher, $listeners, $exceptionListener) = $this->createFirewall($container, $name, $firewall, $authenticationProviders, $providerIds);
240+
$configId = 'security.firewall.map.config.'.$name;
241+
242+
list($matcher, $listeners, $exceptionListener) = $this->createFirewall($container, $name, $firewall, $authenticationProviders, $providerIds, $configId);
240243

241244
$contextId = 'security.firewall.map.context.'.$name;
242245
$context = $container->setDefinition($contextId, new DefinitionDecorator('security.firewall.context'));
243246
$context
244247
->replaceArgument(0, $listeners)
245248
->replaceArgument(1, $exceptionListener)
249+
->replaceArgument(2, new Reference($configId))
246250
;
251+
247252
$map[$contextId] = $matcher;
248253
}
249254
$mapDef->replaceArgument(1, $map);
@@ -258,8 +263,11 @@ private function createFirewalls($config, ContainerBuilder $container)
258263
;
259264
}
260265

261-
private function createFirewall(ContainerBuilder $container, $id, $firewall, &$authenticationProviders, $providerIds)
266+
private function createFirewall(ContainerBuilder $container, $id, $firewall, &$authenticationProviders, $providerIds, $configId)
262267
{
268+
$config = $container->setDefinition($configId, new DefinitionDecorator('security.firewall.config'));
269+
$config->replaceArgument(0, $id);
270+
263271
// Matcher
264272
$matcher = null;
265273
if (isset($firewall['request_matcher'])) {
@@ -271,20 +279,28 @@ private function createFirewall(ContainerBuilder $container, $id, $firewall, &$a
271279
$matcher = $this->createRequestMatcher($container, $pattern, $host, $methods);
272280
}
273281

282+
$config->replaceArgument(1, (string) $matcher);
283+
$config->replaceArgument(2, $firewall['security']);
284+
274285
// Security disabled?
275286
if (false === $firewall['security']) {
276287
return array($matcher, array(), null);
277288
}
278289

290+
$config->replaceArgument(3, $firewall['stateless']);
291+
279292
// Provider id (take the first registered provider if none defined)
280293
if (isset($firewall['provider'])) {
281294
$defaultProvider = $this->getUserProviderId($firewall['provider']);
282295
} else {
283296
$defaultProvider = reset($providerIds);
284297
}
285298

299+
$config->replaceArgument(4, $defaultProvider);
300+
286301
// Register listeners
287302
$listeners = array();
303+
$listenerKeys = array();
288304

289305
// Channel listener
290306
$listeners[] = new Reference('security.channel_listener');
@@ -296,11 +312,14 @@ private function createFirewall(ContainerBuilder $container, $id, $firewall, &$a
296312
$contextKey = $firewall['context'];
297313
}
298314

315+
$config->replaceArgument(5, $contextKey);
316+
299317
$listeners[] = new Reference($this->createContextListener($container, $contextKey));
300318
}
301319

302320
// Logout listener
303321
if (isset($firewall['logout'])) {
322+
$listenerKeys[] = 'logout';
304323
$listenerId = 'security.logout_listener.'.$id;
305324
$listener = $container->setDefinition($listenerId, new DefinitionDecorator('security.logout_listener'));
306325
$listener->replaceArgument(3, array(
@@ -363,10 +382,13 @@ private function createFirewall(ContainerBuilder $container, $id, $firewall, &$a
363382
// Authentication listeners
364383
list($authListeners, $defaultEntryPoint) = $this->createAuthenticationListeners($container, $id, $firewall, $authenticationProviders, $defaultProvider, $configuredEntryPoint);
365384

385+
$config->replaceArgument(6, $configuredEntryPoint ?: $defaultEntryPoint);
386+
366387
$listeners = array_merge($listeners, $authListeners);
367388

368389
// Switch user listener
369390
if (isset($firewall['switch_user'])) {
391+
$listenerKeys[] = 'switch_user';
370392
$listeners[] = new Reference($this->createSwitchUserListener($container, $id, $firewall['switch_user'], $defaultProvider));
371393
}
372394

@@ -376,7 +398,30 @@ private function createFirewall(ContainerBuilder $container, $id, $firewall, &$a
376398
// Exception listener
377399
$exceptionListener = new Reference($this->createExceptionListener($container, $firewall, $id, $configuredEntryPoint ?: $defaultEntryPoint, $firewall['stateless']));
378400

401+
if (isset($firewall['access_denied_handler'])) {
402+
$config->replaceArgument(7, $firewall['access_denied_handler']);
403+
}
404+
if (isset($firewall['access_denied_url'])) {
405+
$config->replaceArgument(8, $firewall['access_denied_url']);
406+
}
407+
379408
$container->setAlias(new Alias('security.user_checker.'.$id, false), $firewall['user_checker']);
409+
$config->replaceArgument(9, $firewall['user_checker']);
410+
411+
foreach ($this->factories as $position) {
412+
foreach ($position as $factory) {
413+
$key = str_replace('-', '_', $factory->getKey());
414+
if (array_key_exists($key, $firewall)) {
415+
$listenerKeys[] = $key;
416+
}
417+
}
418+
}
419+
420+
if (isset($firewall['anonymous'])) {
421+
$listenerKeys[] = 'anonymous';
422+
}
423+
424+
$config->replaceArgument(10, $listenerKeys);
380425

381426
return array($matcher, $listeners, $exceptionListener);
382427
}

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

+15-1
Original file line numberDiff line numberDiff line change
@@ -111,6 +111,21 @@
111111
<service id="security.firewall.context" class="Symfony\Bundle\SecurityBundle\Security\FirewallContext" abstract="true">
112112
<argument type="collection" />
113113
<argument type="service" id="security.exception_listener" />
114+
<argument /> <!-- FirewallConfig -->
115+
</service>
116+
117+
<service id="security.firewall.config" class="Symfony\Bundle\SecurityBundle\Security\FirewallConfig" abstract="true" public="false">
118+
<argument /> <!-- name -->
119+
<argument /> <!-- request_matcher -->
120+
<argument /> <!-- security enabled -->
121+
<argument /> <!-- stateless -->
122+
<argument /> <!-- provider -->
123+
<argument /> <!-- context -->
124+
<argument /> <!-- entry_point -->
125+
<argument /> <!-- user_checker -->
126+
<argument /> <!-- access_denied_handler -->
127+
<argument /> <!-- access_denied_url -->
128+
<argument type="collection" /> <!-- listeners -->
114129
</service>
115130

116131
<service id="security.logout_url_generator" class="Symfony\Component\Security\Http\Logout\LogoutUrlGenerator" public="false">
@@ -119,7 +134,6 @@
119134
<argument type="service" id="security.token_storage" on-invalid="null" />
120135
</service>
121136

122-
123137
<!-- Provisioning -->
124138
<service id="security.user.provider.in_memory" class="Symfony\Component\Security\Core\User\InMemoryUserProvider" abstract="true" public="false" />
125139
<service id="security.user.provider.in_memory.user" class="Symfony\Component\Security\Core\User\User" abstract="true" public="false" />
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,126 @@
1+
<?php
2+
3+
/*
4+
* This file is part of the Symfony package.
5+
*
6+
* (c) Fabien Potencier <fabien@symfony.com>
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\Security;
13+
14+
/**
15+
* @author Robin Chalas <robin.chalas@gmail.com>
16+
*/
17+
class FirewallConfig
18+
{
19+
private $name;
20+
private $requestMatcher;
21+
private $securityEnabled;
22+
private $stateless;
23+
private $provider;
24+
private $context;
25+
private $entryPoint;
26+
private $accessDeniedHandler;
27+
private $accessDeniedUrl;
28+
private $userChecker;
29+
private $listeners;
30+
31+
public function __construct($name, $requestMatcher, $securityEnabled = true, $stateless = false, $provider = null, $context = null, $entryPoint = null, $accessDeniedHandler = null, $accessDeniedUrl = null, $userChecker = null, $listeners = array())
32+
{
33+
$this->name = $name;
34+
$this->requestMatcher = $requestMatcher;
35+
$this->securityEnabled = $securityEnabled;
36+
$this->stateless = $stateless;
37+
$this->provider = $provider;
38+
$this->context = $context;
39+
$this->entryPoint = $entryPoint;
40+
$this->accessDeniedHandler = $accessDeniedHandler;
41+
$this->accessDeniedUrl = $accessDeniedUrl;
42+
$this->userChecker = $userChecker;
43+
$this->listeners = $listeners;
44+
}
45+
46+
public function getName()
47+
{
48+
return $this->name;
49+
}
50+
51+
/**
52+
* @return string The request matcher service id
53+
*/
54+
public function getRequestMatcher()
55+
{
56+
return $this->requestMatcher;
57+
}
58+
59+
public function isSecurityEnabled()
60+
{
61+
return $this->securityEnabled;
62+
}
63+
64+
public function allowsAnonymous()
65+
{
66+
return in_array('anonymous', $this->listeners, true);
67+
}
68+
69+
public function isStateless()
70+
{
71+
return $this->stateless;
72+
}
73+
74+
/**
75+
* @return string The provider service id
76+
*/
77+
public function getProvider()
78+
{
79+
return $this->provider;
80+
}
81+
82+
/**
83+
* @return string The context key
84+
*/
85+
public function getContext()
86+
{
87+
return $this->context;
88+
}
89+
90+
/**
91+
* @return string The entry_point service id
92+
*/
93+
public function getEntryPoint()
94+
{
95+
return $this->entryPoint;
96+
}
97+
98+
/**
99+
* @return string The user_checker service id
100+
*/
101+
public function getUserChecker()
102+
{
103+
return $this->userChecker;
104+
}
105+
106+
/**
107+
* @return string The access_denied_handler service id
108+
*/
109+
public function getAccessDeniedHandler()
110+
{
111+
return $this->accessDeniedHandler;
112+
}
113+
114+
public function getAccessDeniedUrl()
115+
{
116+
return $this->accessDeniedUrl;
117+
}
118+
119+
/**
120+
* @return array An array of listener keys
121+
*/
122+
public function getListeners()
123+
{
124+
return $this->listeners;
125+
}
126+
}

src/Symfony/Bundle/SecurityBundle/Security/FirewallContext.php

+12-1
Original file line numberDiff line numberDiff line change
@@ -23,11 +23,22 @@ class FirewallContext
2323
{
2424
private $listeners;
2525
private $exceptionListener;
26+
private $config;
2627

27-
public function __construct(array $listeners, ExceptionListener $exceptionListener = null)
28+
public function __construct(array $listeners, ExceptionListener $exceptionListener = null, FirewallConfig $config = null)
2829
{
30+
if (null === $config) {
31+
@trigger_error(sprintf('"%s()" expects an instance of "%s" as third argument since version 3.2 and will trigger an error in 4.0 if not provided.', __METHOD__, FirewallConfig::class), E_USER_DEPRECATED);
32+
}
33+
2934
$this->listeners = $listeners;
3035
$this->exceptionListener = $exceptionListener;
36+
$this->config = $config;
37+
}
38+
39+
public function getConfig()
40+
{
41+
return $this->config;
3142
}
3243

3344
public function getContext()

src/Symfony/Bundle/SecurityBundle/Security/FirewallMap.php

+29-3
Original file line numberDiff line numberDiff line change
@@ -35,18 +35,44 @@ public function __construct(ContainerInterface $container, array $map)
3535
$this->contexts = new \SplObjectStorage();
3636
}
3737

38+
/**
39+
* {@inheritdoc}
40+
*/
3841
public function getListeners(Request $request)
42+
{
43+
$context = $this->getFirewallContext($request);
44+
45+
if (null === $context) {
46+
return array(array(), null);
47+
}
48+
49+
return $context->getContext();
50+
}
51+
52+
/**
53+
* @return FirewallConfig|null
54+
*/
55+
public function getFirewallConfig(Request $request)
56+
{
57+
$context = $this->getFirewallContext($request);
58+
59+
if (null === $context) {
60+
return;
61+
}
62+
63+
return $context->getConfig();
64+
}
65+
66+
private function getFirewallContext(Request $request)
3967
{
4068
if ($this->contexts->contains($request)) {
4169
return $this->contexts[$request];
4270
}
4371

4472
foreach ($this->map as $contextId => $requestMatcher) {
4573
if (null === $requestMatcher || $requestMatcher->matches($request)) {
46-
return $this->contexts[$request] = $this->container->get($contextId)->getContext();
74+
return $this->contexts[$request] = $this->container->get($contextId);
4775
}
4876
}
49-
50-
return array(array(), null);
5177
}
5278
}

0 commit comments

Comments
 (0)