Skip to content

Commit 0fb1215

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 bdf8cd6 commit 0fb1215

File tree

5 files changed

+91
-134
lines changed

5 files changed

+91
-134
lines changed

Authenticator/AnonymousAuthenticator.php

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

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
}

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) {

Tests/Authenticator/AnonymousAuthenticatorTest.php

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

Tests/Firewall/AccessListenerTest.php

Lines changed: 50 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -19,6 +19,7 @@
1919
use Symfony\Component\Security\Core\Authentication\Token\Storage\TokenStorage;
2020
use Symfony\Component\Security\Core\Authentication\Token\Storage\TokenStorageInterface;
2121
use Symfony\Component\Security\Core\Authorization\AccessDecisionManagerInterface;
22+
use Symfony\Component\Security\Core\Exception\AccessDeniedException;
2223
use Symfony\Component\Security\Http\AccessMapInterface;
2324
use Symfony\Component\Security\Http\Event\LazyResponseEvent;
2425
use Symfony\Component\Security\Http\Firewall\AccessListener;
@@ -229,6 +230,55 @@ public function testHandleWhenTheSecurityTokenStorageHasNoToken()
229230
$listener(new RequestEvent($this->createMock(HttpKernelInterface::class), $request, HttpKernelInterface::MASTER_REQUEST));
230231
}
231232

233+
public function testHandleWhenTheSecurityTokenStorageHasNoTokenAndExceptionOnTokenIsFalse()
234+
{
235+
$this->expectException(AccessDeniedException::class);
236+
$tokenStorage = new TokenStorage();
237+
$request = new Request();
238+
239+
$accessMap = $this->createMock(AccessMapInterface::class);
240+
$accessMap->expects($this->any())
241+
->method('getPatterns')
242+
->with($this->equalTo($request))
243+
->willReturn([['foo' => 'bar'], null])
244+
;
245+
246+
$listener = new AccessListener(
247+
$tokenStorage,
248+
$this->getMockBuilder('Symfony\Component\Security\Core\Authorization\AccessDecisionManagerInterface')->getMock(),
249+
$accessMap,
250+
$this->getMockBuilder('Symfony\Component\Security\Core\Authentication\AuthenticationManagerInterface')->getMock(),
251+
false
252+
);
253+
254+
$listener(new RequestEvent($this->createMock(HttpKernelInterface::class), $request, HttpKernelInterface::MASTER_REQUEST));
255+
}
256+
257+
public function testHandleWhenPublicAccessIsAllowedAndExceptionOnTokenIsFalse()
258+
{
259+
$tokenStorage = new TokenStorage();
260+
$request = new Request();
261+
262+
$accessMap = $this->createMock(AccessMapInterface::class);
263+
$accessMap->expects($this->any())
264+
->method('getPatterns')
265+
->with($this->equalTo($request))
266+
->willReturn([[AccessListener::PUBLIC_ACCESS], null])
267+
;
268+
269+
$listener = new AccessListener(
270+
$tokenStorage,
271+
$this->getMockBuilder('Symfony\Component\Security\Core\Authorization\AccessDecisionManagerInterface')->getMock(),
272+
$accessMap,
273+
$this->getMockBuilder('Symfony\Component\Security\Core\Authentication\AuthenticationManagerInterface')->getMock(),
274+
false
275+
);
276+
277+
$listener(new RequestEvent($this->createMock(HttpKernelInterface::class), $request, HttpKernelInterface::MASTER_REQUEST));
278+
279+
$this->expectNotToPerformAssertions();
280+
}
281+
232282
public function testHandleMWithultipleAttributesShouldBeHandledAsAnd()
233283
{
234284
$request = new Request();

0 commit comments

Comments
 (0)