8000 add security.firewalls.not_full_fledged_handler option · symfony/symfony@a7ace88 · GitHub
[go: up one dir, main page]

Skip to content

Commit a7ace88

Browse files
committed
add security.firewalls.not_full_fledged_handler option
if not authenticated at all
1 parent 8f71c8d commit a7ace88

File tree

13 files changed

+255
-14
lines changed

13 files changed

+255
-14
lines changed

src/Symfony/Bundle/SecurityBundle/CHANGELOG.md

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -5,6 +5,7 @@ CHANGELOG
55
---
66

77
* Allow configuring the secret used to sign login links
8+
* Add `security.firewalls.not_full_fledged_handler` option to configure behavior where user is not full fledged
89

910
7.1
1011
---

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

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -175,6 +175,7 @@ public function collect(Request $request, Response $response, ?\Throwable $excep
175175
'access_denied_url' => $firewallConfig->getAccessDeniedUrl(),
176176
'user_checker' => $firewallConfig->getUserChecker(),
177177
'authenticators' => $firewallConfig->getAuthenticators(),
178+
'not_full_fledged_handler' => $firewallConfig->getNotFullFledgedHandler(),
178179
];
179180

180181
// generate exit impersonation path from current request
Original file line numberDiff line numberDiff line change
@@ -19,6 +19,7 @@
1919
use Symfony\Component\Config\Definition\Exception\InvalidConfigurationException;
2020
use Symfony\Component\Security\Http\EntryPoint\AuthenticationEntryPointInterface;
2121
use Symfony\Component\Security\Http\Session\SessionAuthenticationStrategy;
22+
use Symfony\Component\Security\Http\Authorization\SameAsNotFullFledgedHandle;
2223

2324
/**
2425
* SecurityExtension configuration structure.
@@ -214,6 +215,14 @@ private function addFirewallsSection(ArrayNodeDefinition $rootNode, array $facto
214215
-> B41A booleanNode('stateless')->defaultFalse()->end()
215216
->booleanNode('lazy')->defaultFalse()->end()
216217
->scalarNode('context')->cannotBeEmpty()->end()
218+
->scalarNode('not_full_fledged_handler')
219+
->beforeNormalization()
220+
->ifTrue(fn ($v): bool => $v == 'original')
221+
->then(fn ($v) => null)
222+
->ifTrue(fn ($v): bool => $v == 'same')
223+
->then(fn ($v) => SameAsNotFullFledgedHandle::class)
224+
->end()
225+
->end()
217226
->arrayNode('logout')
218227
->treatTrueLike([])
219228
->canBeUnset()

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

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -578,6 +578,7 @@ private function createFirewall(ContainerBuilder $container, string $id, array $
578578

579579
$config->replaceArgument(10, $listenerKeys);
580580
$config->replaceArgument(11, $firewall['switch_user'] ?? null);
581+
$config->replaceArgument(13, $firewall['not_full_fledged_handler'] ?? null);
581582

582583
return [$matcher, $listeners, $exceptionListener, null !== $logoutListenerId ? new Reference($logoutListenerId) : null, $firewallAuthenticationProviders];
583584
}
@@ -875,6 +876,11 @@ private function createExceptionListener(ContainerBuilder $container, array $con
875876
$listener->replaceArgument(5, $config['access_denied_url']);
876877
}
877878

879+
// not full fledged handler setup
880+
if (isset($config['not_full_fledged_handler'])) {
881+
$listener->replaceArgument(9, new Reference($config['not_full_fledged_handler']));
882+
}
883+
878884
return $exceptionListenerId;
879885
}
880886

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

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -42,6 +42,7 @@
4242
use Symfony\Component\Security\Core\User\MissingUserProvider;
4343
use Symfony\Component\Security\Core\Validator\Constraints\UserPasswordValidator;
4444
use Symfony\Component\Security\Http\Authentication\AuthenticationUtils;
45+
use Symfony\Component\Security\Http\Authorization\SameAsNotFullFledgedHandle;
4546
use Symfony\Component\Security\Http\Controller\SecurityTokenValueResolver;
4647
use Symfony\Component\Security\Http\Controller\UserValueResolver;
4748
use Symfony\Component\Security\Http\EventListener\IsGrantedAttributeListener;
@@ -218,6 +219,7 @@
218219
[], // listeners
219220
null, // switch_user
220221
null, // logout
222+
null, //not_full_fledged_handler
221223
])
222224

223225
->set('security.logout_url_generator', LogoutUrlGenerator::class)
@@ -310,5 +312,7 @@
310312
->set('cache.security_is_csrf_token_valid_attribute_expression_language')
311313
->parent('cache.system')
312314
->tag('cache.pool')
315+
316+
->set('security.same_as_not_full_fledged_handle', SameAsNotFullFledgedHandle::class)
313317
;
314318
};

src/Symfony/Bundle/SecurityBundle/Resources/config/security_listeners.php

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -139,6 +139,7 @@
139139
service('security.access.denied_handler')->nullOnInvalid(),
140140
service('logger')->nullOnInvalid(),
141141
false, // Stateless
142+
service('security.not.full.fledged_handler')->nullOnInvalid(),
142143
])
143144
->tag('monolog.logger', ['channel' => 'security'])
144145

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

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -282,6 +282,10 @@
282282
<th>authenticators</th>
283283
<td>{{ collector.firewall.authenticators is empty ? '(none)' : profiler_dump(collector.firewall.authenticators, maxDepth=1) }}</td>
284284
</tr>
285+
<tr>
286+
<th>not_full_fledged_handler</th>
287+
<td>{{ collector.firewall.not_full_fledged_handler ?: '(none)' }}</td>
288+
</tr>
285289
</tbody>
286290
</table>
287291
{% endif %}

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

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -30,6 +30,7 @@ public function __construct(
3030
private readonly array $authenticators = [],
3131
private readonly ?array $switchUser = null,
3232
private readonly ?array $logout = null,
33+
private readonly ?string $notFullFledgedHandler = null,
3334
) {
3435
}
3536

@@ -104,4 +105,9 @@ public function getLogout(): ?array
104105
{
105106
return $this->logout;
106107
}
108+
109+
public function getNotFullFledgedHandler(): ?string
110+
{
111+
return $this->notFullFledgedHandler;
112+
}
107113
}

src/Symfony/Bundle/SecurityBundle/Tests/DependencyInjection/CompleteConfigurationTestCase.php

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -149,6 +149,7 @@ public function testFirewalls()
149149
[],
150150
null,
151151
null,
152+
null,
152153
],
153154
[
154155
'secure',
@@ -184,6 +185,7 @@ public function testFirewalls()
184185
'enable_csrf' => null,
185186
'clear_site_data' => [],
186187
],
188+
null,
187189
],
188190
[
189191
'host',
@@ -201,6 +203,7 @@ public function testFirewalls()
201203
],
202204
null,
203205
null,
206+
null,
204207
],
205208
[
206209
'with_user_checker',
@@ -218,6 +221,7 @@ public function testFirewalls()
218221
],
219222
null,
220223
null,
224+
null,
221225
],
222226
], $configs);
223227

Lines changed: 34 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,34 @@
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\Component\Security\Http\Authorization;
13+
14+
use Symfony\Component\HttpFoundation\Request;
15+
use Symfony\Component\HttpFoundation\Response;
16+
use Symfony\Component\Security\Core\Exception\AccessDeniedException;
17+
18+
/**
19+
* This is used by the ExceptionListener to translate an AccessDeniedException
20+
* to a Response object.
2 10000 1+
*
22+
* @author Roman JOLY <eltharin18@outlook.fr>
23+
*/
24+
interface NotFullFledgedHandlerInterface
25+
{
26+
/**
27+
* Handles a not full fledged case for acces denied failure.
28+
* @return bool|Response : bool|Response
29+
* true : user have to be fully authenticated, continu to startAuthentication
30+
* false : user have'nt to be fully authenticated, throw original AcessDeniedException
31+
* Response: you can return your own response, AccesDeniedException wil be ignored
32+
*/
33+
public function handle(Request $request, AccessDeniedException $accessDeniedException): bool|Response;
34+
}
Lines changed: 37 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,37 @@
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\Component\Security\Http\Authorization;
13+
14+
use Symfony\Component\HttpFoundation\Request;
15+
use Symfony\Component\HttpFoundation\Response;
16+
use Symfony\Component\Security\Core\Authorization\Voter\AuthenticatedVoter;
17+
use Symfony\Component\Security\Core\Exception\AccessDeniedException;
18+
19+
/**
20+
* This is a basic NotFullFledgedHandle
21+
* If IS_AUTHENTICATED_FULLY is in access denied Exception Attrribute, behavior will be as before,
22+
* Otherwise The original AccessDeniedException is throw
23+
*
24+
* @author Roman JOLY <eltharin18@outlook.fr>
25+
*/
26+
class SameAsNotFullFledgedHandle implements NotFullFledgedHandlerInterface
27+
{
28+
public function handle(Request $request, AccessDeniedException $accessDeniedException): bool|Response
29+
{
30+
foreach($accessDeniedException->getAttributes() as $attribute) {
31+
if(in_array($attribute, [AuthenticatedVoter::IS_AUTHENTICATED_FULLY])) {
32+
return true;
33+
}
34+
}
35+
return false;
36+
}
37+
}

src/Symfony/Component/Security/Http/Firewall/ExceptionListener.php

Lines changed: 19 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -29,6 +29,7 @@
2929
use Symfony\Component\Security\Core\Exception\LazyResponseException;
3030
use Symfony\Component\Security\Core\Exception\LogoutException;
3131
use Symfony\Component\Security\Http\Authorization\AccessDeniedHandlerInterface;
32+
use Symfony\Component\Security\Http\Authorization\NotFullFledgedHandlerInterface;
3233
use Symfony\Component\Security\Http\EntryPoint\AuthenticationEntryPointInterface;
3334
use Symfony\Component\Security\Http\EntryPoint\Exception\NotAnEntryPointException;
3435
use Symfony\Component\Security\Http\HttpUtils;
@@ -57,6 +58,7 @@ public function __construct(
5758
private ?AccessDeniedHandlerInterface $accessDeniedHandler = null,
5859
private ?LoggerInterface $logger = null,
5960
private bool $stateless = false,
61+
private ?NotFullFledgedHandlerInterface $notFullFledgedHandler = null,
6062
) {
6163
}
6264

@@ -127,20 +129,26 @@ private function handleAccessDeniedException(ExceptionEvent $event, AccessDenied
127129

128130
$token = $this->tokenStorage->getToken();
129131
if (!$this->authenticationTrustResolver->isFullFledged($token)) {
130-
$this->logger?->debug('Access denied, the user is not fully authenticated; redirecting to authentication entry point.', ['exception' => $exception]);
131-
132-
try {
133-
$insufficientAuthenticationException = new InsufficientAuthenticationException('Full authentication is required to access this resource.', 0, $exception);
134-
if (null !== $token) {
135-
$insufficientAuthenticationException->setToken($token);
132+
$response = ((!$this->authenticationTrustResolver->isAuthenticated($token)) || (null === $this->notFullFledgedHandler) ? true : $this->notFullFledgedHandler->handle($event->getRequest(), $exception));
133+
134+
if ($response instanceof Response) {
135+
$event->setResponse($response);
136+
} elseif ($response === true) {
137+
$this->logger?->debug('Access denied, the user is not fully authenticated; redirecting to authentication entry point.', ['exception' => $exception]);
138+
139+
try {
140+
$insufficientAuthenticationException = new InsufficientAuthenticationException('Full authentication is required to access this resource.', 0, $exception);
141+
if (null !== $token) {
142+
$insufficientAuthenticationException->setToken($token);
143+
}
144+
145+
$event->setResponse($this->startAuthentication($event->getRequest(), $insufficientAuthenticationException));
146+
} catch (\Exception $e) {
147+
$event->setThrowable($e);
136148
}
137149

138-
$event->setResponse($this->startAuthentication($event->getRequest(), $insufficientAuthenticationException));
139-
} catch (\Exception $e) {
140-
$event->setThrowable($e);
150+
return;
141151
}
142-
143-
return;
144152
}
145153

146154
$this->logger?->debug('Access denied, the user is neither anonymous, nor remember-me.', ['exception' => $exception]);

0 commit comments

Comments
 (0)
0