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

Lines changed: 47 additions & 2 deletions
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

Lines changed: 15 additions & 1 deletion
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" />
Lines changed: 126 additions & 0 deletions
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

Lines changed: 12 additions & 1 deletion
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

Lines changed: 29 additions & 3 deletions
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)
pFad - Phonifier reborn

Pfad - The Proxy pFad of © 2024 Garber Painting. All rights reserved.

Note: This service is not intended for secure transactions such as banking, social media, email, or purchasing. Use at your own risk. We assume no liability whatsoever for broken pages.


Alternative Proxies:

Alternative Proxy

pFad Proxy

pFad v3 Proxy

pFad v4 Proxy