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 172fd63

Browse filesBrowse files
committed
[Security] made code easier to understand, added some missing unit tests
1 parent 616b6c5 commit 172fd63
Copy full SHA for 172fd63

File tree

Expand file treeCollapse file tree

2 files changed

+248
-75
lines changed
Filter options
Expand file treeCollapse file tree

2 files changed

+248
-75
lines changed

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

Copy file name to clipboardExpand all lines: src/Symfony/Component/Security/Http/Firewall/ExceptionListener.php
+58-75Lines changed: 58 additions & 75 deletions
Original file line numberDiff line numberDiff line change
@@ -81,100 +81,83 @@ public function onKernelException(GetResponseForExceptionEvent $event)
8181
$event->getDispatcher()->removeListener(KernelEvents::EXCEPTION, array($this, 'onKernelException'));
8282

8383
$exception = $event->getException();
84-
$request = $event->getRequest();
85-
86-
while (null !== $exception) {
84+
do {
8785
if ($exception instanceof AuthenticationException) {
88-
if (null !== $this->logger) {
89-
$this->logger->info(sprintf('Authentication exception occurred; redirecting to authentication entry point (%s)', $exception->getMessage()));
90-
}
91-
92-
try {
93-
$response = $this->startAuthentication($request, $exception);
94-
95-
break;
96-
} catch (\Exception $e) {
97-
$event->setException($e);
98-
99-
return;
100-
}
86+
return $this->handleAuthenticationException($event, $exception);
87+
} elseif ($exception instanceof AccessDeniedException) {
88+
return $this->handleAccessDeniedException($event, $exception);
89+
} elseif ($exception instanceof LogoutException) {
90+
return $this->handleLogoutException($event, $exception);
10191
}
92+
} while (null !== $exception = $exception->getPrevious());
93+
}
10294

103-
if ($exception instanceof AccessDeniedException) {
104-
$event->setException(new AccessDeniedHttpException($exception->getMessage(), $exception));
105-
106-
$token = $this->context->getToken();
107-
if (!$this->authenticationTrustResolver->isFullFledged($token)) {
108-
if (null !== $this->logger) {
109-
$this->logger->debug(sprintf('Access is denied (user is not fully authenticated) by "%s" at line %s; redirecting to authentication entry point', $exception->getFile(), $exception->getLine()));
110-
}
111-
112-
try {
113-
$insufficientAuthenticationException = new InsufficientAuthenticationException('Full authentication is required to access this resource.', 0, $exception);
114-
$insufficientAuthenticationException->setToken($token);
115-
$response = $this->startAuthentication($request, $insufficientAuthenticationException);
116-
117-
break;
118-
} catch (\Exception $e) {
119-
$event->setException($e);
120-
121-
return;
122-
}
123-
} else {
124-
if (null !== $this->logger) {
125-
$this->logger->debug(sprintf('Access is denied (and user is neither anonymous, nor remember-me) by "%s" at line %s', $exception->getFile(), $exception->getLine()));
126-
}
127-
128-
try {
129-
if (null !== $this->accessDeniedHandler) {
130-
$response = $this->accessDeniedHandler->handle($request, $exception);
131-
132-
if (!$response instanceof Response) {
133-
return;
134-
}
95+
private function handleAuthenticationException(GetResponseForExceptionEvent $event, AuthenticationException $exception)
96+
{
97+
if (null !== $this->logger) {
98+
$this->logger->info(sprintf('Authentication exception occurred; redirecting to authentication entry point (%s)', $exception->getMessage()));
99+
}
135100

136-
break;
137-
}
101+
try {
102+
$event->setResponse($this->startAuthentication($event->getRequest(), $exception));
103+
} catch (\Exception $e) {
104+
$event->setException($e);
105+
}
106+
}
138107

139-
if (null !== $this->errorPage) {
140-
$subRequest = $this->httpUtils->createRequest($request, $this->errorPage);
141-
$subRequest->attributes->set(SecurityContextInterface::ACCESS_DENIED_ERROR, $exception);
108+
private function handleAccessDeniedException(GetResponseForExceptionEvent $event, AccessDeniedException $exception)
109+
{
110+
$event->setException(new AccessDeniedHttpException($exception->getMessage(), $exception));
142111

143-
$response = $event->getKernel()->handle($subRequest, HttpKernelInterface::SUB_REQUEST, true);
112+
$token = $this->context->getToken();
113+
if (!$this->authenticationTrustResolver->isFullFledged($token)) {
114+
if (null !== $this->logger) {
115+
$this->logger->debug(sprintf('Access is denied (user is not fully authenticated) by "%s" at line %s; redirecting to authentication entry point', $exception->getFile(), $exception->getLine()));
116+
}
144117

145-
break;
146-
}
118+
try {
119+
$insufficientAuthenticationException = new InsufficientAuthenticationException('Full authentication is required to access this resource.', 0, $exception);
120+
$insufficientAuthenticationException->setToken($token);
147121

148-
return;
122+
$event->setResponse($this->startAuthentication($event->getRequest(), $insufficientAuthenticationException));
123+
} catch (\Exception $e) {
124+
$event->setException($e);
125+
}
149126

150-
} catch (\Exception $e) {
151-
if (null !== $this->logger) {
152-
$this->logger->error(sprintf('Exception thrown when handling an exception (%s: %s)', get_class($e), $e->getMessage()));
153-
}
127+
return;
128+
}
154129

155-
$event->setException(new \RuntimeException('Exception thrown when handling an exception.', 0, $e));
130+
if (null !== $this->logger) {
131+
$this->logger->debug(sprintf('Access is denied (and user is neither anonymous, nor remember-me) by "%s" at line %s', $exception->getFile(), $exception->getLine()));
132+
}
156133

157-
return;
158-
}
159-
}
160-
}
134+
try {
135+
if (null !== $this->accessDeniedHandler) {
136+
$response = $this->accessDeniedHandler->handle($event->getRequest(), $exception);
161137

162-
if ($exception instanceof LogoutException) {
163-
if (null !== $this->logger) {
164-
$this->logger->info(sprintf('Logout exception occurred; wrapping with AccessDeniedHttpException (%s)', $exception->getMessage()));
138+
if ($response instanceof Response) {
139+
$event->setResponse($response);
165140
}
141+
} elseif (null !== $this->errorPage) {
142+
$subRequest = $this->httpUtils->createRequest($event->getRequest(), $this->errorPage);
143+
$subRequest->attributes->set(SecurityContextInterface::ACCESS_DENIED_ERROR, $exception);
166144

167-
return;
145+
$event->setResponse($event->getKernel()->handle($subRequest, HttpKernelInterface::SUB_REQUEST, true));
168146
}
169-
170-
if (null === $exception->getPrevious()) {
171-
return;
147+
} catch (\Exception $e) {
148+
if (null !== $this->logger) {
149+
$this->logger->error(sprintf('Exception thrown when handling an exception (%s: %s)', get_class($e), $e->getMessage()));
172150
}
173151

174-
$exception = $exception->getPrevious();
152+
$event->setException(new \RuntimeException('Exception thrown when handling an exception.', 0, $e));
175153
}
154+
}
176155

177-
$event->setResponse($response);
156+
private function handleLogoutException(GetResponseForExceptionEvent $event, LogoutException $exception)
157+
{
158+
if (null !== $this->logger) {
159+
$this->logger->info(sprintf('Logout exception occurred; wrapping with AccessDeniedHttpException (%s)', $exception->getMessage()));
160+
}
178161
}
179162

180163
/**
+190Lines changed: 190 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,190 @@
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\HttpFoundation\Response;
16+
use Symfony\Component\HttpKernel\Event\GetResponseForExceptionEvent;
17+
use Symfony\Component\HttpKernel\HttpKernelInterface;
18+
use Symfony\Component\Security\Core\Authentication\AuthenticationTrustResolverInterface;
19+
use Symfony\Component\Security\Core\Exception\AccessDeniedException;
20+
use Symfony\Component\Security\Core\Exception\AuthenticationException;
21+
use Symfony\Component\Security\Core\SecurityContextInterface;
22+
use Symfony\Component\Security\Http\Authorization\AccessDeniedHandlerInterface;
23+
use Symfony\Component\Security\Http\EntryPoint\AuthenticationEntryPointInterface;
24+
use Symfony\Component\Security\Http\Firewall\ExceptionListener;
25+
use Symfony\Component\Security\Http\HttpUtils;
26+
27+
class ExceptionListenerTest extends \PHPUnit_Framework_TestCase
28+
{
29+
/**
30+
* @dataProvider getAuthenticationExceptionProvider
31+
*/
32+
public function testAuthenticationExceptionWithoutEntryPoint(\Exception $exception, \Exception $eventException = null)
33+
{
34+
$event = $this->createEvent($exception);
35+
36+
$listener = $this->createExceptionListener();
37+
$listener->onKernelException($event);
38+
39+
$this->assertNull($event->getResponse());
40+
$this->assertSame(null === $eventException ? $exception : $eventException, $event->getException());
41+
}
42+
43+
/**
44+
* @dataProvider getAuthenticationExceptionProvider
45+
*/
46+
public function testAuthenticationExceptionWithEntryPoint(\Exception $exception, \Exception $eventException = null)
47+
{
48+
$event = $this->createEvent($exception = new AuthenticationException());
49+
50+
$listener = $this->createExceptionListener(null, null, null, $this->createEntryPoint());
51+
$listener->onKernelException($event);
52+
53+
$this->assertEquals('OK', $event->getResponse()->getContent());
54+
$this->assertSame($exception, $event->getException());
55+
}
56+
57+
public function getAuthenticationExceptionProvider()
58+
{
59+
return array(
60+
array(new AuthenticationException()),
61+
array(new \LogicException('random', 0, $e = new AuthenticationException()), $e),
62+
array(new \LogicException('random', 0, $e = new AuthenticationException('embed', 0, new AuthenticationException())), $e),
63+
array(new \LogicException('random', 0, $e = new AuthenticationException('embed', 0, new AccessDeniedException())), $e),
64+
array(new AuthenticationException('random', 0, new \LogicException())),
65+
);
66+
}
67+
68+
/**
69+
* @dataProvider getAccessDeniedExceptionProvider
70+
*/
71+
public function testAccessDeniedExceptionFullFledgedAndWithoutAccessDeniedHandlerAndWithoutErrorPage(\Exception $exception, \Exception $eventException = null)
72+
{
73+
$event = $this->createEvent($exception);
74+
75+
$listener = $this->createExceptionListener(null, $this->createTrustResolver(true));
76+
$listener->onKernelException($event);
77+
78+
$this->assertNull($event->getResponse());
79+
$this->assertSame(null === $eventException ? $exception : $eventException, $event->getException()->getPrevious());
80+
}
81+
82+
/**
83+
* @dataProvider getAccessDeniedExceptionProvider
84+
*/
85+
public function testAccessDeniedExceptionFullFledgedAndWithoutAccessDeniedHandlerAndWithErrorPage(\Exception $exception, \Exception $eventException = null)
86+
{
87+
$kernel = $this->getMock('Symfony\Component\HttpKernel\HttpKernelInterface');
88+
$kernel->expects($this->once())->method('handle')->will($this->returnValue(new Response('error')));
89+
90+
$event = $this->createEvent($exception, $kernel);
91+
92+
$httpUtils = $this->getMock('Symfony\Component\Security\Http\HttpUtils');
93+
$httpUtils->expects($this->once())->method('createRequest')->will($this->returnValue(Request::create('/error')));
94+
95+
$listener = $this->createExceptionListener(null, $this->createTrustResolver(true), $httpUtils, null, '/error');
96+
$listener->onKernelException($event);
97+
98+
$this->assertEquals('error', $event->getResponse()->getContent());
99+
$this->assertSame(null === $eventException ? $exception : $eventException, $event->getException()->getPrevious());
100+
}
101+
102+
/**
103+
* @dataProvider getAccessDeniedExceptionProvider
104+
*/
105+
public function testAccessDeniedExceptionFullFledgedAndWithAccessDeniedHandlerAndWithoutErrorPage(\Exception $exception, \Exception $eventException = null)
106+
{
107+
$event = $this->createEvent($exception);
108+
109+
$accessDeniedHandler = $this->getMock('Symfony\Component\Security\Http\Authorization\AccessDeniedHandlerInterface');
110+
$accessDeniedHandler->expects($this->once())->method('handle')->will($this->returnValue(new Response('error')));
111+
112+
$listener = $this->createExceptionListener(null, $this->createTrustResolver(true), null, null, null, $accessDeniedHandler);
113+
$listener->onKernelException($event);
114+
115+
$this->assertEquals('error', $event->getResponse()->getContent());
116+
$this->assertSame(null === $eventException ? $exception : $eventException, $event->getException()->getPrevious());
117+
}
118+
119+
/**
120+
* @dataProvider getAccessDeniedExceptionProvider
121+
*/
122+
public function testAccessDeniedExceptionNotFullFledged(\Exception $exception, \Exception $eventException = null)
123+
{
124+
$event = $this->createEvent($exception);
125+
126+
$context = $this->getMock('Symfony\Component\Security\Core\SecurityContextInterface');
127+
$context->expects($this->once())->method('getToken')->will($this->returnValue($this->getMock('Symfony\Component\Security\Core\Authentication\Token\TokenInterface')));
128+
129+
$listener = $this->createExceptionListener($context, $this->createTrustResolver(false), null, $this->createEntryPoint());
130+
$listener->onKernelException($event);
131+
132+
$this->assertEquals('OK', $event->getResponse()->getContent());
133+
$this->assertSame(null === $eventException ? $exception : $eventException, $event->getException()->getPrevious());
134+
}
135+
136+
public function getAccessDeniedExceptionProvider()
137+
{
138+
return array(
139+
array(new AccessDeniedException()),
140+
array(new \LogicException('random', 0, $e = new AccessDeniedException()), $e),
141+
array(new \LogicException('random', 0, $e = new AccessDeniedException('embed', new AccessDeniedException())), $e),
142+
array(new \LogicException('random', 0, $e = new AccessDeniedException('embed', new AuthenticationException())), $e),
143+
array(new AccessDeniedException('random', new \LogicException())),
144+
);
145+
}
146+
147+
private function createEntryPoint()
148+
{
149+
$entryPoint = $this->getMock('Symfony\Component\Security\Http\EntryPoint\AuthenticationEntryPointInterface');
150+
$entryPoint->expects($this->once())->method('start')->will($this->returnValue(new Response('OK')));
151+
152+
return $entryPoint;
153+
}
154+
155+
private function createTrustResolver($fullFledged)
156+
{
157+
$trustResolver = $this->getMock('Symfony\Component\Security\Core\Authentication\AuthenticationTrustResolverInterface');
158+
$trustResolver->expects($this->once())->method('isFullFledged')->will($this->returnValue($fullFledged));
159+
160+
return $trustResolver;
161+
}
162+
163+
private function createEvent(\Exception $exception, $kernel = null)
164+
{
165+
if (null === $kernel) {
166+
$kernel = $this->getMock('Symfony\Component\HttpKernel\HttpKernelInterface');
167+
}
168+
169+
$event = new GetResponseForExceptionEvent($kernel, Request::create('/'), HttpKernelInterface::MASTER_REQUEST, $exception);
170+
171+
// FIXME: to be removed in 2.4
172+
$dispatcher = $this->getMock('Symfony\Component\EventDispatcher\EventDispatcherInterface');
173+
$event->setDispatcher($dispatcher);
174+
175+
return $event;
176+
}
177+
178+
private function createExceptionListener(SecurityContextInterface $context = null, AuthenticationTrustResolverInterface $trustResolver = null, HttpUtils $httpUtils = null, AuthenticationEntryPointInterface $authenticationEntryPoint = null, $errorPage = null, AccessDeniedHandlerInterface $accessDeniedHandler = null)
179+
{
180+
return new ExceptionListener(
181+
$context ? $context : $this->getMock('Symfony\Component\Security\Core\SecurityContextInterface'),
182+
$trustResolver ? $trustResolver : $this->getMock('Symfony\Component\Security\Core\Authentication\AuthenticationTrustResolverInterface'),
183+
$httpUtils ? $httpUtils : $this->getMock('Symfony\Component\Security\Http\HttpUtils'),
184+
'key',
185+
$authenticationEntryPoint,
186+
$errorPage,
187+
$accessDeniedHandler
188+
);
189+
}
190+
}

0 commit comments

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