Thanks to visit codestin.com
Credit goes to github.com

Skip to content

Commit ac84a6c

Browse files
wouterjfabpot
authored andcommitted
Removed AnonymousToken from the authenticator system
* Anonymous users are actual to unauthenticated users, both are now represented by no token * Added a PUBLIC_ACCESS Security attribute to be used in access_control * Deprecated "anonymous: lazy" in favor of "lazy: true"
1 parent 28bb74c commit ac84a6c

File tree

14 files changed

+142
-158
lines changed

14 files changed

+142
-158
lines changed

UPGRADE-5.1.md

Lines changed: 18 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -112,6 +112,24 @@ Routing
112112
SecurityBundle
113113
--------------
114114

115+
* Deprecated `anonymous: lazy` in favor of `lazy: true`
116+
117+
*Before*
118+
```yaml
119+
security:
120+
firewalls:
121+
main:
122+
anonymous: lazy
123+
```
124+
125+
*After*
126+
```yaml
127+
security:
128+
firewalls:
129+
main:
130+
anonymous: true
131+
lazy: true
132+
```
115133
* Marked the `AnonymousFactory`, `FormLoginFactory`, `FormLoginLdapFactory`, `GuardAuthenticationFactory`,
116134
`HttpBasicFactory`, `HttpBasicLdapFactory`, `JsonLoginFactory`, `JsonLoginLdapFactory`, `RememberMeFactory`, `RemoteUserFactory`
117135
and `X509Factory` as `@internal`. Instead of extending these classes, create your own implementation based on

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

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -197,6 +197,7 @@ private function addFirewallsSection(ArrayNodeDefinition $rootNode, array $facto
197197
->scalarNode('entry_point')->end()
198198
->scalarNode('provider')->end()
199199
->booleanNode('stateless')->defaultFalse()->end()
200+
->booleanNode('lazy')->defaultFalse()->end()
200201
->scalarNode('context')->cannotBeEmpty()->end()
201202
->arrayNode('logout')
202203
->treatTrueLike([])

src/Symfony/Bundle/SecurityBundle/DependencyInjection/Security/Factory/AnonymousFactory.php

Lines changed: 3 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -12,6 +12,7 @@
1212
namespace Symfony\Bundle\SecurityBundle\DependencyInjection\Security\Factory;
1313

1414
use Symfony\Component\Config\Definition\Builder\NodeDefinition;
15+
use Symfony\Component\Config\Definition\Exception\InvalidConfigurationException;
1516
use Symfony\Component\DependencyInjection\ChildDefinition;
1617
use Symfony\Component\DependencyInjection\ContainerBuilder;
1718
use Symfony\Component\DependencyInjection\Parameter;
@@ -46,16 +47,7 @@ public function create(ContainerBuilder $container, $id, $config, $userProvider,
4647

4748
public function createAuthenticator(ContainerBuilder $container, string $firewallName, array $config, string $userProviderId): string
4849
{
49-
if (null === $config['secret']) {
50-
$config['secret'] = new Parameter('container.build_hash');
51-
}
52-
53-
$authenticatorId = 'security.authenticator.anonymous.'.$firewallName;
54-
$container
55-
->setDefinition($authenticatorId, new ChildDefinition('security.authenticator.anonymous'))
56-
->replaceArgument(0, $config['secret']);
57-
58-
return $authenticatorId;
50+
throw new InvalidConfigurationException(sprintf('The authenticator manager no longer has "anonymous" security. Please remove this option under the "%s" firewall'.($config['lazy'] ? ' and add "lazy: true"' : '').'.', $firewallName));
5951
}
6052

6153
public function getPosition()
@@ -76,7 +68,7 @@ public function addConfiguration(NodeDefinition $builder)
7668
->then(function ($v) { return ['lazy' => true]; })
7769
->end()
7870
->children()
79-
->booleanNode('lazy')->defaultFalse()->end()
71+
->booleanNode('lazy')->defaultFalse()->setDeprecated('symfony/security-bundle', '5.1', 'Using "anonymous: lazy" to make the firewall lazy is deprecated, use "lazy: true" instead.')->end()
8072
->scalarNode('secret')->defaultNull()->end()
8173
->end()
8274
;

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

Lines changed: 9 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -112,6 +112,13 @@ public function load(array $configs, ContainerBuilder $container)
112112

113113
if ($this->authenticatorManagerEnabled = $config['enable_authenticator_manager']) {
114114
$loader->load('security_authenticator.xml');
115+
116+
// The authenticator system no longer has anonymous tokens. This makes sure AccessListener
117+
// and AuthorizationChecker do not throw AuthenticationCredentialsNotFoundException when no
118+
// token is available in the token storage.
119+
$container->getDefinition('security.access_listener')->setArgument(4, false);
120+
$container->getDefinition('security.authorization_checker')->setArgument(4, false);
121+
$container->getDefinition('security.authorization_checker')->setArgument(5, false);
115122
} else {
116123
$loader->load('security_legacy.xml');
117124
}
@@ -269,7 +276,8 @@ private function createFirewalls(array $config, ContainerBuilder $container)
269276
list($matcher, $listeners, $exceptionListener, $logoutListener) = $this->createFirewall($container, $name, $firewall, $authenticationProviders, $providerIds, $configId);
270277

271278
$contextId = 'security.firewall.map.context.'.$name;
272-
$context = new ChildDefinition($firewall['stateless'] || empty($firewall['anonymous']['lazy']) ? 'security.firewall.context' : 'security.firewall.lazy_context');
279+
$isLazy = !$firewall['stateless'] && (!empty($firewall['anonymous']['lazy']) || $firewall['lazy']);
280+
$context = new ChildDefinition($isLazy ? 'security.firewall.lazy_context' : 'security.firewall.context');
273281
$context = $container->setDefinition($contextId, $context);
274282
$context
275283
->replaceArgument(0, new IteratorArgument($listeners))

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

Lines changed: 0 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -111,13 +111,6 @@
111111
<argument type="service" id="property_accessor" on-invalid="null" />
112112
</service>
113113

114-
<service id="security.authenticator.anonymous"
115-
class="Symfony\Component\Security\Http\Authenticator\AnonymousAuthenticator"
116-
abstract="true">
117-
<argument type="abstract">secret</argument>
118-
<argument type="service" id="security.untracked_token_storage" />
119-
</service>
120-
121114
<service id="security.authenticator.remember_me"
122115
class="Symfony\Component\Security\Http\Authenticator\RememberMeAuthenticator"
123116
abstract="true">

src/Symfony/Bundle/SecurityBundle/Tests/Functional/app/Guarded/config.yml

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -26,7 +26,8 @@ security:
2626
firewalls:
2727
secure:
2828
pattern: ^/
29-
anonymous: lazy
29+
anonymous: ~
30+
lazy: true
3031
stateless: false
3132
guard:
3233
authenticators:

src/Symfony/Bundle/SecurityBundle/Tests/Functional/app/StandardFormLogin/config.yml

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -27,7 +27,8 @@ security:
2727
check_path: /login_check
2828
default_target_path: /profile
2929
logout: ~
30-
anonymous: lazy
30+
anonymous: ~
31+
lazy: true
3132

3233
# This firewall is here just to check its the logout functionality
3334
second_area:

src/Symfony/Component/Security/Core/Authorization/AuthorizationChecker.php

Lines changed: 9 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -29,24 +29,30 @@ class AuthorizationChecker implements AuthorizationCheckerInterface
2929
private $accessDecisionManager;
3030
private $authenticationManager;
3131
private $alwaysAuthenticate;
32+
private $exceptionOnNoToken;
3233

33-
public function __construct(TokenStorageInterface $tokenStorage, AuthenticationManagerInterface $authenticationManager, AccessDecisionManagerInterface $accessDecisionManager, bool $alwaysAuthenticate = false)
34+
public function __construct(TokenStorageInterface $tokenStorage, AuthenticationManagerInterface $authenticationManager, AccessDecisionManagerInterface $accessDecisionManager, bool $alwaysAuthenticate = false, bool $exceptionOnNoToken = true)
3435
{
3536
$this->tokenStorage = $tokenStorage;
3637
$this->authenticationManager = $authenticationManager;
3738
$this->accessDecisionManager = $accessDecisionManager;
3839
$this->alwaysAuthenticate = $alwaysAuthenticate;
40+
$this->exceptionOnNoToken = $exceptionOnNoToken;
3941
}
4042

4143
/**
4244
* {@inheritdoc}
4345
*
44-
* @throws AuthenticationCredentialsNotFoundException when the token storage has no authentication token
46+
* @throws AuthenticationCredentialsNotFoundException when the token storage has no authentication token and $exceptionOnNoToken is set to true
4547
*/
4648
final public function isGranted($attribute, $subject = null): bool
4749
{
4850
if (null === ($token = $this->tokenStorage->getToken())) {
49-
throw new AuthenticationCredentialsNotFoundException('The token storage contains no authentication token. One possible reason may be that there is no firewall configured for this URL.');
51+
if ($this->exceptionOnNoToken) {
52+
throw new AuthenticationCredentialsNotFoundException('The token storage contains no authentication token. One possible reason may be that there is no firewall configured for this URL.');
53+
}
54+
55+
return false;
5056
}
5157

5258
if ($this->alwaysAuthenticate || !$token->isAuthenticated()) {

src/Symfony/Component/Security/Core/Tests/Authorization/AuthorizationCheckerTest.php

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -73,6 +73,13 @@ public function testVoteWithoutAuthenticationToken()
7373
$this->authorizationChecker->isGranted('ROLE_FOO');
7474
}
7575

76+
public function testVoteWithoutAuthenticationTokenAndExceptionOnNoTokenIsFalse()
77+
{
78+
$authorizationChecker = new AuthorizationChecker($this->tokenStorage, $this->authenticationManager, $this->accessDecisionManager, false, false);
79+
80+
$this->assertFalse($authorizationChecker->isGranted('ROLE_FOO'));
81+
}
82+
7683
/**
7784
* @dataProvider isGrantedProvider
7885
*/

src/Symfony/Component/Security/Http/Authenticator/AnonymousAuthenticator.php

Lines changed: 0 additions & 67 deletions
This file was deleted.

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

Lines changed: 38 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -31,17 +31,21 @@
3131
*/
3232
class AccessListener extends AbstractListener
3333
{
34+
const PUBLIC_ACCESS = 'PUBLIC_ACCESS';
35+
3436
private $tokenStorage;
3537
private $accessDecisionManager;
3638
private $map;
3739
private $authManager;
40+
private $exceptionOnNoToken;
3841

39-
public function __construct(TokenStorageInterface $tokenStorage, AccessDecisionManagerInterface $accessDecisionManager, AccessMapInterface $map, AuthenticationManagerInterface $authManager)
42+
public function __construct(TokenStorageInterface $tokenStorage, AccessDecisionManagerInterface $accessDecisionManager, AccessMapInterface $map, AuthenticationManagerInterface $authManager, bool $exceptionOnNoToken = true)
4043
{
4144
$this->tokenStorage = $tokenStorage;
4245
$this->accessDecisionManager = $accessDecisionManager;
4346
$this->map = $map;
4447
$this->authManager = $authManager;
48+
$this->exceptionOnNoToken = $exceptionOnNoToken;
4549
}
4650

4751
/**
@@ -52,18 +56,18 @@ public function supports(Request $request): ?bool
5256
[$attributes] = $this->map->getPatterns($request);
5357
$request->attributes->set('_access_control_attributes', $attributes);
5458

55-
return $attributes && [AuthenticatedVoter::IS_AUTHENTICATED_ANONYMOUSLY] !== $attributes ? true : null;
59+
return $attributes && ([AuthenticatedVoter::IS_AUTHENTICATED_ANONYMOUSLY] !== $attributes && [self::PUBLIC_ACCESS] !== $attributes) ? true : null;
5660
}
5761

5862
/**
5963
* Handles access authorization.
6064
*
6165
* @throws AccessDeniedException
62-
* @throws AuthenticationCredentialsNotFoundException
66+
* @throws AuthenticationCredentialsNotFoundException when the token storage has no authentication token and $exceptionOnNoToken is set to true
6367
*/
6468
public function authenticate(RequestEvent $event)
6569
{
66-
if (!$event instanceof LazyResponseEvent && null === $token = $this->tokenStorage->getToken()) {
70+
if (!$event instanceof LazyResponseEvent && null === ($token = $this->tokenStorage->getToken()) && $this->exceptionOnNoToken) {
6771
throw new AuthenticationCredentialsNotFoundException('A Token was not found in the TokenStorage.');
6872
}
6973

@@ -76,8 +80,26 @@ public function authenticate(RequestEvent $event)
7680
return;
7781
}
7882

79-
if ($event instanceof LazyResponseEvent && null === $token = $this->tokenStorage->getToken()) {
80-
throw new AuthenticationCredentialsNotFoundException('A Token was not found in the TokenStorage.');
83+
if ($event instanceof LazyResponseEvent) {
84+
$token = $this->tokenStorage->getToken();
85+
}
86+
87+
if (null === $token) {
88+
if ($this->exceptionOnNoToken) {
89+
throw new AuthenticationCredentialsNotFoundException('A Token was not found in the TokenStorage.');
90+
}
91+
92+
if ([AuthenticatedVoter::IS_AUTHENTICATED_ANONYMOUSLY] === $attributes) {
93+
trigger_deprecation('symfony/security-http', '5.1', 'Using "IS_AUTHENTICATED_ANONYMOUSLY" in your access_control rules when using the authenticator Security system is deprecated, use "PUBLIC_ACCESS" instead.');
94+
95+
return;
96+
}
97+
98+
if ([self::PUBLIC_ACCESS] === $attributes) {
99+
return;
100+
}
101+
102+
throw $this->createAccessDeniedException($request, $attributes);
81103
}
82104

83105
if (!$token->isAuthenticated()) {
@@ -86,11 +108,16 @@ public function authenticate(RequestEvent $event)
86108
}
87109

88110
if (!$this->accessDecisionManager->decide($token, $attributes, $request, true)) {
89-
$exception = new AccessDeniedException();
90-
$exception->setAttributes($attributes);
91-
$exception->setSubject($request);
92-
93-
throw $exception;
111+
throw $this->createAccessDeniedException($request, $attributes);
94112
}
95113
}
114+
115+
private function createAccessDeniedException(Request $request, array $attributes)
116+
{
117+
$exception = new AccessDeniedException();
118+
$exception->setAttributes($attributes);
119+
$exception->setSubject($request);
120+
121+
return $exception;
122+
}
96123
}

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

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -144,7 +144,9 @@ private function handleAccessDeniedException(ExceptionEvent $event, AccessDenied
144144

145145
try {
146146
$insufficientAuthenticationException = new InsufficientAuthenticationException('Full authentication is required to access this resource.', 0, $exception);
147-
$insufficientAuthenticationException->setToken($token);
147+
if (null !== $token) {
148+
$insufficientAuthenticationException->setToken($token);
149+
}
148150

149151
$event->setResponse($this->startAuthentication($event->getRequest(), $insufficientAuthenticationException));
150152
} catch (\Exception $e) {

0 commit comments

Comments
 (0)