Skip to content

Navigation Menu

Sign in
Appearance settings

Search code, repositories, users, issues, pull requests...

Provide feedback

We read every piece of feedback, and take your input very seriously.

Saved searches

Use saved searches to filter your results more quickly

Appearance settings

Commit 2317443

Browse filesBrowse files
committed
[Security] fixed issue where authentication listeners clear unrelated tokens
This commit fixes an issue where authentication listeners clear all security tokens in case of authentication failure. This behavior makes it impossible to combine certain authentication mechanisms, notably x509 with form-based login.
1 parent 5fde0b0 commit 2317443
Copy full SHA for 2317443
Expand file treeCollapse file tree

7 files changed

+466
-6
lines changed

‎src/Symfony/Component/Security/Http/Firewall/AbstractAuthenticationListener.php

Copy file name to clipboardExpand all lines: src/Symfony/Component/Security/Http/Firewall/AbstractAuthenticationListener.php
+4-1Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -186,7 +186,10 @@ private function onFailure(GetResponseEvent $event, Request $request, Authentica
186186
$this->logger->info(sprintf('Authentication request failed: %s', $failed->getMessage()));
187187
}
188188

189-
$this->securityContext->setToken(null);
189+
$token = $this->securityContext->getToken();
190+
if ($token instanceof UsernamePasswordToken && $this->providerKey === $token->getProviderKey()) {
191+
$this->securityContext->setToken(null);
192+
}
190193

191194
$response = $this->failureHandler->onAuthenticationFailure($request, $failed);
192195

‎src/Symfony/Component/Security/Http/Firewall/AbstractPreAuthenticatedListener.php

Copy file name to clipboardExpand all lines: src/Symfony/Component/Security/Http/Firewall/AbstractPreAuthenticatedListener.php
+18-1Lines changed: 18 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -21,6 +21,7 @@
2121
use Psr\Log\LoggerInterface;
2222
use Symfony\Component\HttpFoundation\Request;
2323
use Symfony\Component\EventDispatcher\EventDispatcherInterface;
24+
use Symfony\Component\Security\Core\Exception\BadCredentialsException;
2425

2526
/**
2627
* AbstractPreAuthenticatedListener is the base class for all listener that
@@ -59,7 +60,12 @@ final public function handle(GetResponseEvent $event)
5960
$this->logger->debug(sprintf('Checking secure context token: %s', $this->securityContext->getToken()));
6061
}
6162

62-
list($user, $credentials) = $this->getPreAuthenticatedData($request);
63+
try {
64+
list($user, $credentials) = $this->getPreAuthenticatedData($request);
65+
} catch (BadCredentialsException $exception) {
66+
$this->clearToken();
67+
return;
68+
}
6369

6470
if (null !== $token = $this->securityContext->getToken()) {
6571
if ($token instanceof PreAuthenticatedToken && $this->providerKey == $token->getProviderKey() && $token->isAuthenticated() && $token->getUsername() === $user) {
@@ -84,6 +90,17 @@ final public function handle(GetResponseEvent $event)
8490
$this->dispatcher->dispatch(SecurityEvents::INTERACTIVE_LOGIN, $loginEvent);
8591
}
8692
} catch (AuthenticationException $failed) {
93+
$this->clearToken();
94+
}
95+
}
96+
97+
/**
98+
* Clears a PreAuthenticatedToken for this provider (if present)
99+
*/
100+
protected function clearToken()
101+
{
102+
$token = $this->securityContext->getToken();
103+
if ($token instanceof PreAuthenticatedToken && $this->providerKey === $token->getProviderKey()) {
87104
$this->securityContext->setToken(null);
88105

89106
if (null !== $this->logger) {

‎src/Symfony/Component/Security/Http/Firewall/BasicAuthenticationListener.php

Copy file name to clipboardExpand all lines: src/Symfony/Component/Security/Http/Firewall/BasicAuthenticationListener.php
+4-1Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -74,7 +74,10 @@ public function handle(GetResponseEvent $event)
7474
$token = $this->authenticationManager->authenticate(new UsernamePasswordToken($username, $request->headers->get('PHP_AUTH_PW'), $this->providerKey));
7575
$this->securityContext->setToken($token);
7676
} catch (AuthenticationException $failed) {
77-
$this->securityContext->setToken(null);
77+
$token = $this->securityContext->getToken();
78+
if ($token instanceof UsernamePasswordToken && $this->providerKey === $token->getProviderKey()) {
79+
$this->securityContext->setToken(null);
80+
}
7881

7982
if (null !== $this->logger) {
8083
$this->logger->info(sprintf('Authentication request failed for user "%s": %s', $username, $failed->getMessage()));

‎src/Symfony/Component/Security/Http/Firewall/DigestAuthenticationListener.php

Copy file name to clipboardExpand all lines: src/Symfony/Component/Security/Http/Firewall/DigestAuthenticationListener.php
+4-1Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -124,7 +124,10 @@ public function handle(GetResponseEvent $event)
124124

125125
private function fail(GetResponseEvent $event, Request $request, AuthenticationException $authException)
126126
{
127-
$this->securityContext->setToken(null);
127+
$token = $this->securityContext->getToken();
128+
if ($token instanceof UsernamePasswordToken && $this->providerKey === $token->getProviderKey()) {
129+
$this->securityContext->setToken(null);
130+
}
128131

129132
if (null !== $this->logger) {
130133
$this->logger->info($authException);
+267Lines changed: 267 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,267 @@
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\Tests\Http\Firewall;
13+
14+
use Symfony\Component\HttpFoundation\Request;
15+
use Symfony\Component\Security\Core\Authentication\Token\PreAuthenticatedToken;
16+
use Symfony\Component\Security\Core\Authentication\Token\UsernamePasswordToken;
17+
use Symfony\Component\Security\Core\Exception\AuthenticationException;
18+
19+
class AbstractPreAuthenticatedListenerTest extends \PHPUnit_Framework_TestCase
20+
{
21+
protected function setUp()
22+
{
23+
if (!class_exists('Symfony\Component\EventDispatcher\EventDispatcher')) {
24+
$this->markTestSkipped('The "EventDispatcher" component is not available');
25+
}
26+
27+
if (!class_exists('Symfony\Component\HttpFoundation\Request')) {
28+
$this->markTestSkipped('The "HttpFoundation" component is not available');
29+
}
30+
31+
if (!class_exists('Symfony\Component\HttpKernel\HttpKernel')) {
32+
$this->markTestSkipped('The "HttpKernel" component is not available');
33+
}
34+
}
35+
36+
public function testHandleWithValidValues()
37+
{
38+
$userCredentials = array('TheUser', 'TheCredentials');
39+
40+
$request = new Request(array(), array(), array(), array(), array(), array());
41+
42+
$token = $this->getMock('Symfony\Component\Security\Core\Authentication\Token\TokenInterface');
43+
44+
$context = $this->getMock('Symfony\Component\Security\Core\SecurityContextInterface');
45+
$context
46+
->expects($this->any())
47+
->method('getToken')
48+
->will($this->returnValue(null))
49+
;
50+
$context
51+
->expects($this->once())
52+
->method('setToken')
53+
->with($this->equalTo($token))
54+
;
55+
56+
$authenticationManager = $this->getMock('Symfony\Component\Security\Core\Authentication\AuthenticationManagerInterface');
57+
$authenticationManager
58+
->expects($this->once())
59+
->method('authenticate')
60+
->with($this->isInstanceOf('Symfony\Component\Security\Core\Authentication\Token\PreAuthenticatedToken'))
61+
->will($this->returnValue($token))
62+
;
63+
64+
$listener = $this->getMockForAbstractClass('Symfony\Component\Security\Http\Firewall\AbstractPreAuthenticatedListener', array(
65+
$context,
66+
$authenticationManager,
67+
'TheProviderKey'
68+
));
69+
$listener
70+
->expects($this->once())
71+
->method('getPreAuthenticatedData')
72+
->will($this->returnValue($userCredentials));
73+
74+
$event = $this->getMock('Symfony\Component\HttpKernel\Event\GetResponseEvent', array(), array(), '', false);
75+
$event
76+
->expects($this->any())
77+
->method('getRequest')
78+
->will($this->returnValue($request))
79+
;
80+
81+
$listener->handle($event);
82+
}
83+
84+
public function testHandleWhenAuthenticationFails()
85+
{
86+
$userCredentials = array('TheUser', 'TheCredentials');
87+
88+
$request = new Request(array(), array(), array(), array(), array(), array());
89+
90+
$context = $this->getMock('Symfony\Component\Security\Core\SecurityContextInterface');
91+
$context
92+
->expects($this->any())
93+
->method('getToken')
94+
->will($this->returnValue(null))
95+
;
96+
$context
97+
->expects($this->never())
98+
->method('setToken')
99+
;
100+
101+
$exception = new AuthenticationException('Authentication failed.');
102+
$authenticationManager = $this->getMock('Symfony\Component\Security\Core\Authentication\AuthenticationManagerInterface');
103+
$authenticationManager
104+
->expects($this->once())
105+
->method('authenticate')
106+
->with($this->isInstanceOf('Symfony\Component\Security\Core\Authentication\Token\PreAuthenticatedToken'))
107+
->will($this->throwException($exception))
108+
;
109+
110+
$listener = $this->getMockForAbstractClass('Symfony\Component\Security\Http\Firewall\AbstractPreAuthenticatedListener', array(
111+
$context,
112+
$authenticationManager,
113+
'TheProviderKey'
114+
));
115+
$listener
116+
->expects($this->once())
117+
->method('getPreAuthenticatedData')
118+
->will($this->returnValue($userCredentials));
119+
120+
$event = $this->getMock('Symfony\Component\HttpKernel\Event\GetResponseEvent', array(), array(), '', false);
121+
$event
122+
->expects($this->any())
123+
->method('getRequest')
124+
->will($this->returnValue($request))
125+
;
126+
127+
$listener->handle($event);
128+
}
129+
130+
public function testHandleWhenAuthenticationFailsWithDifferentToken()
131+
{
132+
$userCredentials = array('TheUser', 'TheCredentials');
133+
134+
$token = new UsernamePasswordToken('TheUsername', 'ThePassword', 'TheProviderKey', array('ROLE_FOO'));
135+
136+
$request = new Request(array(), array(), array(), array(), array(), array());
137+
138+
$context = $this->getMock('Symfony\Component\Security\Core\SecurityContextInterface');
139+
$context
140+
->expects($this->any())
141+
->method('getToken')
142+
->will($this->returnValue($token))
143+
;
144+
$context
145+
->expects($this->never())
146+
->method('setToken')
147+
;
148+
149+
$exception = new AuthenticationException('Authentication failed.');
150+
$authenticationManager = $this->getMock('Symfony\Component\Security\Core\Authentication\AuthenticationManagerInterface');
151+
$authenticationManager
152+
->expects($this->once())
153+
->method('authenticate')
154+
->with($this->isInstanceOf('Symfony\Component\Security\Core\Authentication\Token\PreAuthenticatedToken'))
155+
->will($this->throwException($exception))
156+
;
157+
158+
$listener = $this->getMockForAbstractClass('Symfony\Component\Security\Http\Firewall\AbstractPreAuthenticatedListener', array(
159+
$context,
160+
$authenticationManager,
161+
'TheProviderKey'
162+
));
163+
$listener
164+
->expects($this->once())
165+
->method('getPreAuthenticatedData')
166+
->will($this->returnValue($userCredentials));
167+
168+
$event = $this->getMock('Symfony\Component\HttpKernel\Event\GetResponseEvent', array(), array(), '', false);
169+
$event
170+
->expects($this->any())
171+
->method('getRequest')
172+
->will($this->returnValue($request))
173+
;
174+
175+
$listener->handle($event);
176+
}
177+
178+
public function testHandleWithASimilarAuthenticatedToken()
179+
{
180+
$userCredentials = array('TheUser', 'TheCredentials');
181+
182+
$request = new Request(array(), array(), array(), array(), array(), array());
183+
184+
$token = new PreAuthenticatedToken('TheUser', 'TheCredentials', 'TheProviderKey', array('ROLE_FOO'));
185+
186+
$context = $this->getMock('Symfony\Component\Security\Core\SecurityContextInterface');
187+
$context
188+
->expects($this->any())
189+
->method('getToken')
190+
->will($this->returnValue($token))
191+
;
192+
193+
$authenticationManager = $this->getMock('Symfony\Component\Security\Core\Authentication\AuthenticationManagerInterface');
194+
$authenticationManager
195+
->expects($this->never())
196+
->method('authenticate')
197+
;
198+
199+
$listener = $this->getMockForAbstractClass('Symfony\Component\Security\Http\Firewall\AbstractPreAuthenticatedListener', array(
200+
$context,
201+
$authenticationManager,
202+
'TheProviderKey'
203+
));
204+
$listener
205+
->expects($this->once())
206+
->method('getPreAuthenticatedData')
207+
->will($this->returnValue($userCredentials));
208+
209+
$event = $this->getMock('Symfony\Component\HttpKernel\Event\GetResponseEvent', array(), array(), '', false);
210+
$event
211+
->expects($this->any())
212+
->method('getRequest')
213+
->will($this->returnValue($request))
214+
;
215+
216+
$listener->handle($event);
217+
}
218+
219+
public function testHandleWithAnInvalidSimilarToken()
220+
{
221+
$userCredentials = array('TheUser', 'TheCredentials');
222+
223+
$request = new Request(array(), array(), array(), array(), array(), array());
224+
225+
$token = new PreAuthenticatedToken('AnotherUser', 'TheCredentials', 'TheProviderKey', array('ROLE_FOO'));
226+
227+
$context = $this->getMock('Symfony\Component\Security\Core\SecurityContextInterface');
228+
$context
229+
->expects($this->any())
230+
->method('getToken')
231+
->will($this->returnValue($token))
232+
;
233+
$context
234+
->expects($this->once())
235+
->method('setToken')
236+
->with($this->equalTo(null))
237+
;
238+
239+
$exception = new AuthenticationException('Authentication failed.');
240+
$authenticationManager = $this->getMock('Symfony\Component\Security\Core\Authentication\AuthenticationManagerInterface');
241+
$authenticationManager
242+
->expects($this->once())
243+
->method('authenticate')
244+
->with($this->isInstanceOf('Symfony\Component\Security\Core\Authentication\Token\PreAuthenticatedToken'))
245+
->will($this->throwException($exception))
246+
;
247+
248+
$listener = $this->getMockForAbstractClass('Symfony\Component\Security\Http\Firewall\AbstractPreAuthenticatedListener', array(
249+
$context,
250+
$authenticationManager,
251+
'TheProviderKey'
252+
));
253+
$listener
254+
->expects($this->once())
255+
->method('getPreAuthenticatedData')
256+
->will($this->returnValue($userCredentials));
257+
258+
$event = $this->getMock('Symfony\Component\HttpKernel\Event\GetResponseEvent', array(), array(), '', false);
259+
$event
260+
->expects($this->any())
261+
->method('getRequest')
262+
->will($this->returnValue($request))
263+
;
264+
265+
$listener->handle($event);
266+
}
267+
}

0 commit comments

Comments
0 (0)
Morty Proxy This is a proxified and sanitized view of the page, visit original site.