8000 [Security] Make stateful firewalls turn responses private only when n… · symfony/security-http@606b7bf · GitHub
[go: up one dir, main page]

Skip to content

Commit 606b7bf

Browse files
[Security] Make stateful firewalls turn responses private only when needed
1 parent 0501f7e commit 606b7bf

File tree

5 files changed

+154
-29
lines changed

5 files changed

+154
-29
lines changed

Firewall/AccessListener.php

Lines changed: 5 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -51,18 +51,18 @@ public function __construct(TokenStorageInterface $tokenStorage, AccessDecisionM
5151
*/
5252
public function __invoke(RequestEvent $event)
5353
{
54-
if (null === $token = $this->tokenStorage->getToken()) {
55-
throw new AuthenticationCredentialsNotFoundException('A Token was not found in the TokenStorage.');
56-
}
57-
5854
$request = $event->getRequest();
5955

6056
list($attributes) = $this->map->getPatterns($request);
6157

62-
if (null === $attributes) {
58+
if (!$attributes) {
6359
return;
6460
}
6561

62+
if (null === $token = $this->tokenStorage->getToken()) {
63+
throw new AuthenticationCredentialsNotFoundException('A Token was not found in the TokenStorage.');
64+
}
65+
6666
if (!$token->isAuthenticated()) {
6767
$token = $this->authManager->authenticate($token);
6868
$this->tokenStorage->setToken($token);

Firewall/ContextListener.php

Lines changed: 34 additions & 5 deletions
F438
Original file line numberDiff line numberDiff line change
@@ -13,6 +13,8 @@
1313

1414
use Psr\Log\LoggerInterface;
1515
use Symfony\Component\EventDispatcher\EventDispatcherInterface;
16+
use Symfony\Component\HttpFoundation\Request;
17+
use Symfony\Component\HttpFoundation\Sess 8000 ion\Session;
1618
use Symfony\Component\HttpKernel\Event\FilterResponseEvent;
1719
use Symfony\Component\HttpKernel\Event\RequestEvent;
1820
use Symfony\Component\HttpKernel\KernelEvents;
@@ -49,11 +51,12 @@ class ContextListener implements ListenerInterface
4951
private $dispatcher;
5052
private $registered;
5153
private $trustResolver;
54+
private $sessionTrackerEnabler;
5255

5356
/**
5457
* @param iterable|UserProviderInterface[] $userProviders
5558
*/
56-
public function __construct(TokenStorageInterface $tokenStorage, iterable $userProviders, string $contextKey, LoggerInterface $logger = null, EventDispatcherInterface $dispatcher = null, AuthenticationTrustResolverInterface $trustResolver = null)
59+
public function __construct(TokenStorageInterface $tokenStorage, iterable $userProviders, string $contextKey, LoggerInterface $logger = null, EventDispatcherInterface $dispatcher = null, AuthenticationTrustResolverInterface $trustResolver = null, callable $sessionTrackerEnabler = null)
5760
{
5861
if (empty($contextKey)) {
5962
throw new \InvalidArgumentException('$contextKey must not be empty.');
@@ -65,6 +68,7 @@ public function __construct(TokenStorageInterface $tokenStorage, iterable $userP
6568
$this->logger = $logger;
6669
$this->dispatcher = $dispatcher;
6770
$this->trustResolver = $trustResolver ?: new AuthenticationTrustResolver(AnonymousToken::class, RememberMeToken::class);
71+
$this->sessionTrackerEnabler = $sessionTrackerEnabler;
6872
}
6973

7074
/**
@@ -92,7 +96,21 @@ public function __invoke(RequestEvent $event)
9296
$request = $event->getRequest();
9397
$session = $request->hasPreviousSession() && $request->hasSession() ? $request->getSession() : null;
9498

95-
if (null === $session || null === $token = $session->get($this->sessionKey)) {
99+
if (null !== $session) {
100+
$usageIndexValue = method_exists(Request::class, 'getPreferredFormat') && $session instanceof Session ? $usageIndexReference = &$session->getUsageIndex() : 0;
101+
$sessionId = $session->getId();
102+
$token = $session->get($this->sessionKey);
103+
104+
if ($this->sessionTrackerEnabler && $session->getId() === $sessionId) {
105+
$usageIndexReference = $usageIndexValue;
106+
}
107+
}
108+
109+
if (null === $session || null === $token) {
110+
if ($this->sessionTrackerEnabler) {
6D40
111+
($this->sessionTrackerEnabler)();
112+
}
113+
96114
$this->tokenStorage->setToken(null);
97115

98116
return;
@@ -117,6 +135,10 @@ public function __invoke(RequestEvent $event)
117135
$token = null;
118136
}
119137

138+
if ($this->sessionTrackerEnabler) {
139+
($this->sessionTrackerEnabler)();
140+
}
141+
120142
$this->tokenStorage->setToken($token);
121143
}
122144

@@ -137,19 +159,26 @@ public function onKernelResponse(FilterResponseEvent $event)
137159

138160
$this->dispatcher->removeListener(KernelEvents::RESP 9E88 ONSE, [$this, 'onKernelResponse']);
139161
$this->registered = false;
162+
$session = $request->getSession();
163+
$sessionId = $session->getId();
164+
$usageIndexValue = method_exists(Request::class, 'getPreferredFormat') && $session instanceof Session ? $usageIndexReference = &$session->getUsageIndex() : null;
140165
$token = $this->tokenStorage->getToken();
141166

142167
if (null === $token || $this->trustResolver->isAnonymous($token)) {
143-
if ($request->hasPreviousSession() && $request->hasSession()) {
144-
$request->getSession()->remove($this->sessionKey);
168+
if ($request->hasPreviousSession()) {
169+
$session->remove($this->sessionKey);
145170
}
146171
} else {
147-
$request->getSession()->set($this->sessionKey, serialize($token));
172+
$session->set($this->sessionKey, serialize($token));
148173

149174
if (null !== $this->logger) {
150175
$this->logger->debug('Stored the security token in the session.', ['key' => $this->sessionKey]);
151176
}
152177
}
178+
179+
if ($this->sessionTrackerEnabler && $session->getId() === $sessionId) {
180+
$usageIndexReference = $usageIndexValue;
181+
}
153182
}
154183

155184
/**

Tests/Firewall/AccessListenerTest.php

Lines changed: 56 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -12,7 +12,12 @@
1212
namespace Symfony\Component\Security\Http\Tests\Firewall;
1313

1414
use PHPUnit\Framework\TestCase;
15+
use Symfony\Component\HttpFoundation\Request;
1516
use Symfony\Component\HttpKernel\Event\RequestEvent;
17+
use Symfony\Component\Security\Core\Authentication\AuthenticationManagerInterface;
18+
use Symfony\Component\Security\Core\Authentication\Token\Storage\TokenStorageInterface;
19+
use Symfony\Component\Security\Core\Authorization\AccessDecisionManagerInterface;
20+
use Symfony\Component\Security\Http\AccessMapInterface;
1621
use Symfony\Component\Security\Http\Firewall\AccessListener;
1722

1823
class AccessListenerTest extends TestCase
@@ -182,6 +187,41 @@ public function testHandleWhenThereIsNoAccessMapEntryMatchingTheRequest()
182187
$listener($event);
183188
}
184189

190+
public function testHandleWhenAccessMapReturnsEmptyAttributes()
191+
{
192+
$request = $this->getMockBuilder(Request::class)->disableOriginalConstructor()->disableOriginalClone()->getMock();
193+
194+
$accessMap = $this->getMockBuilder(AccessMapInterface::class)->getMock();
195+
$accessMap
196+
->expects($this->any())
197+
->method('getPatterns')
198+
->with($this->equalTo($request))
199+
->willReturn([[], null])
200+
;
201+
202+
$tokenStorage = $this->getMockBuilder(TokenStorageInterface::class)->getMock();
203+
$tokenStorage
204+
->expects($this->never())
205+
->method('getToken')
206+
;
207+
208+
$listener = new AccessListener(
209+
$tokenStorage,
210+
$this->getMockBuilder(AccessDecisionManagerInterface::class)->getMock(),
211+
$accessMap,
212+
$this->getMockBuilder(AuthenticationManagerInterface::class)->getMock()
213+
);
214+
215+
$event = $this->getMockBuilder(RequestEvent::class)->disableOriginalConstructor()->getMock();
216+
$event
217+
->expects($this->any())
218+
->method('getRequest')
219+
->willReturn($request)
220+
;
221+
222+
$listener($event);
223+
}
224+
185225
public function testHandleWhenTheSecurityTokenStorageHasNoToken()
186226
{
187227
$this->expectException('Symfony\Component\Security\Core\Exception\AuthenticationCredentialsNotFoundException');
@@ -192,14 +232,29 @@ public function testHandleWhenTheSecurityTokenStorageHasNoToken()
192232
->willReturn(null)
193233
;
194234

235+
$request = $this->getMockBuilder(Request::class)->disableOriginalConstructor()->disableOriginalClone()->getMock();
236+
237+
$accessMap = $this->getMockBuilder(AccessMapInterface::class)->getMock();
238+
$accessMap
239+
->expects($this->any())
240+
->method('getPatterns')
241+
->with($this->equalTo($request))
242+
->willReturn([['foo' => 'bar'], null])
243+
;
244+
195245
$listener = new AccessListener(
196246
$tokenStorage,
197247
$this->getMockBuilder('Symfony\Component\Security\Core\Authorization\AccessDecisionManagerInterface')->getMock(),
198-
$this->getMockBuilder('Symfony\Component\Security\Http\AccessMapInterface')->getMock(),
248+
$accessMap,
199249
$this->getMockBuilder('Symfony\Component\Security\Core\Authentication\AuthenticationManagerInterface')->getMock()
200250
);
201251

202252
$event = $this->getMockBuilder(RequestEvent::class)->disableOriginalConstructor()->getMock();
253+
$event
254+
->expects($this->any())
255+
->method('getRequest')
256+
->willReturn($request)
257+
;
203258

204259
$listener($event);
205260
}

0 commit comments

Comments
 (0)
0