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

Skip to content

Commit 35cc02f

Browse files
committed
add security.firewalls.not_full_fledged_handler option
if not authenticated at all use callback instead boolean
1 parent da90fe7 commit 35cc02f

File tree

13 files changed

+216
-12
lines changed

13 files changed

+216
-12
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
@@ -176,6 +176,7 @@ public function collect(Request $request, Response $response, ?\Throwable $excep
176176
'access_denied_url' => $firewallConfig->getAccessDeniedUrl(),
177177
'user_checker' => $firewallConfig->getUserChecker(),
178178
'authenticators' => $firewallConfig->getAuthenticators(),
179+
'not_full_fledged_handler' => $firewall 6D4E Config->getNotFullFledgedHandler(),
179180
];
180181

181182
// generate exit impersonation path from current request

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

Lines changed: 9 additions & 0 deletions
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
->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
@@ -582,6 +582,7 @@ private function createFirewall(ContainerBuilder $container, string $id, array $
582582

583583
$config->replaceArgument(10, $listenerKeys);
584584
$config->replaceArgument(11, $firewall['switch_user'] ?? null);
585+
$config->replaceArgument(13, $firewall['not_full_fledged_handler'] ?? null);
585586

586587
return [$matcher, $listeners, $exceptionListener, null !== $logoutListenerId ? new Reference($logoutListenerId) : null, $firewallAuthenticationProviders];
587588
}
@@ -888,6 +889,11 @@ private function createExceptionListener(ContainerBuilder $container, array $con
888889
$listener->replaceArgument(5, $config['access_denied_url']);
889890
}
890891

892+
// not full fledged handler setup
893+
if (isset($config['not_full_fledged_handler'])) {
894+
$listener->replaceArgument(9, new Reference($config['not_full_fledged_handler']));
895+
}
896+
891897
return $exceptionListenerId;
892898
}
893899

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

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -43,6 +43,7 @@
4343
use Symfony\Component\Security\Core\User\MissingUserProvider;
4444
use Symfony\Component\Security\Core\Validator\Constraints\UserPasswordValidator;
4545
use Symfony\Component\Security\Http\Authentication\AuthenticationUtils;
46+
use Symfony\Component\Security\Http\Authorization\SameAsNotFullFledgedHandle;
4647
use Symfony\Component\Security\Http\Controller\SecurityTokenValueResolver;
4748
use Symfony\Component\Security\Http\Controller\UserValueResolver;
4849
use Symfony\Component\Security\Http\EventListener\IsGrantedAttributeListener;
@@ -221,6 +222,7 @@
221222
[], // listeners
222223
null, // switch_user
223224
null, // logout
225+
null, //not_full_fledged_handler
224226
])
225227

226228
->set('security.logout_url_generator', LogoutUrlGenerator::class)
@@ -313,5 +315,7 @@
313315
->set('cache.security_is_csrf_token_valid_attribute_expression_language')
314316
->parent('cache.system')
315317
->tag('cache.pool')
318+
319+
->set('security.same_as_not_full_fledged_handle', SameAsNotFullFledgedHandle::class)
316320
;
317321
};

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
@@ -334,6 +334,10 @@
334334
<th>authenticators</th>
335335
<td>{{ collector.firewall.authenticators is empty ? '(none)' : profiler_dump(collector.firewall.authenticators, maxDepth=1) }}</td>
336336
</tr>
337+
<tr>
338+
<th>not_full_fledged_handler</th>
339+
<td>{{ collector.firewall.not_full_fledged_handler ?: '(none)' }}</td>
340+
</tr>
337341
</tbody>
338342
</table>
339343
{% 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: 35 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,35 @@
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\Authentication\AuthenticationTrustResolverInterface;
17+
use Symfony\Component\Security\Core\Authentication\Token\TokenInterface;
18+
use Symfony\Component\Security\Core\Exception\AccessDeniedException;
19+
20+
/**
21+
* This is used by the ExceptionListener to translate an AccessDeniedException
22+
* to a Response object.
23+
*
24+
* @author Roman JOLY <eltharin18@outlook.fr>
25+
*/
26+
interface NotFullFledgedHandlerInterface
27+
{
28+
/**
29+
* Handles a not full fledged case for acces denied failure.
30+
* @return null|Response
31+
* null: throw original AcessDeniedException
32+
* Response: you can return your own response, AccesDeniedException wil be ignored
33+
*/
34+
public function handle(Request $request, AccessDeniedException $accessDeniedException, AuthenticationTrustResolverInterface $trustResolver, ?TokenInterface $token, callable $reauthenticateResponse): ?Response;
35+
}
Lines changed: 43 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,43 @@
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\Authentication\AuthenticationTrustResolverInterface;
17+
use Symfony\Component\Security\Core\Authentication\Token\TokenInterface;
18+
use Symfony\Component\Security\Core\Authorization\Voter\AuthenticatedVoter;
19+
use Symfony\Component\Security\Core\Exception\AccessDeniedException;
20+
21+
/**
22+
* This is a basic NotFullFledgedHandle
23+
* If IS_AUTHENTICATED_FULLY is in access denied Exception Attrribute, behavior will be as before,
24+
* Otherwise The original AccessDeniedException is throw
25+
*
26+
* @author Roman JOLY <eltharin18@outlook.fr>
27+
*/
28+
class SameAsNotFullFledgedHandle implements NotFullFledgedHandlerInterface
29+
{
30+
public function handle(Request $request, AccessDeniedException $accessDeniedException, AuthenticationTrustResolverInterface $trustResolver, ?TokenInterface $token, callable $reauthenticateResponse): ?Response
31+
{
32+
if( !$trustResolver->isAuthenticated($token)) {
33+
$reauthenticateResponse();
34+
}
35+
36+
foreach($accessDeniedException->getAttributes() as $attribute) {
37+
if(in_array($attribute, [AuthenticatedVoter::IS_AUTHENTICATED_FULLY])) {
38+
$reauthenticateResponse();
39+
}
40+
}
41+
return null;
42+
}
43+
}

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

Lines changed: 24 additions & 9 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,33 @@ 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]);
131132

132-
try {
133-
$insufficientAuthenticationException = new InsufficientAuthenticationException('Full authentication is required to access this resource.', 0, $exception);
134-
if (null !== $token) {
135-
$insufficientAuthenticationException->setToken($token);
133+
$starAuthenticationResponse = function () use($exception, $event, $token) {
134+
$this->logger?->debug('Access denied, the user is not fully authenticated; redirecting to authentication entry point.', ['exception' => $exception]);
135+
136+
try {
137+
$insufficientAuthenticationException = new InsufficientAuthenticationException('Full authentication is required to access this resource.', 0, $exception);
138+
if (null !== $token) {
139+
$insufficientAuthenticationException->setToken($token);
140+
}
141+
142+
$event->setResponse($this->startAuthentication($event->getRequest(), $insufficientAuthenticationException));
143+
} catch (\Exception $e) {
144+
$event->setThrowable($e);
136145
}
146+
};
137147

138-
$event->setResponse($this->startAuthentication($event->getRequest(), $insufficientAuthenticationException));
139-
} catch (\Exception $e) {
140-
$event->setThrowable($e);
148+
if(null === $this->notFullFledgedHandler) {
149+
$starAuthenticationResponse();
150+
return;
141151
}
142152

143-
return;
153+
$response = $this->notFullFledgedHandler->handle($event->getRequest(), $exception,$this->authenticationTrustResolver, $token, $starAuthenticationResponse);
154+
155+
if ($response instanceof Response) {
156+
$event->setResponse($response);
157+
return;
158+
}
144159
}
145160

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

0 commit comments

Comments
 (0)
0