Skip to content

Commit

Permalink
feature #894 Forward exception code to the response status code (theo…
Browse files Browse the repository at this point in the history
…fidry)

This PR was squashed before being merged into the 2.x branch.

Discussion
----------

Forward exception code to the response status code

It is not rare for some exceptions to have their status code taken as an HTTP status code. This avoids to have to override/decorate the authentication handler to mutate the returned response

Commits
-------

1e1e5ca Forward exception code to the response status code
  • Loading branch information
chalasr committed Jul 14, 2021
2 parents ed81a4f + 1e1e5ca commit b94205c
Show file tree
Hide file tree
Showing 2 changed files with 76 additions and 1 deletion.
19 changes: 18 additions & 1 deletion Security/Http/Authentication/AuthenticationFailureHandler.php
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,7 @@
use Lexik\Bundle\JWTAuthenticationBundle\Events;
use Lexik\Bundle\JWTAuthenticationBundle\Response\JWTAuthenticationFailureResponse;
use Symfony\Component\HttpFoundation\Request;
use Symfony\Component\HttpFoundation\Response;
use Symfony\Component\Security\Core\Exception\AuthenticationException;
use Symfony\Component\Security\Http\Authentication\AuthenticationFailureHandlerInterface;
use Symfony\Contracts\EventDispatcher\EventDispatcherInterface;
Expand Down Expand Up @@ -33,13 +34,29 @@ public function __construct(EventDispatcherInterface $dispatcher)
public function onAuthenticationFailure(Request $request, AuthenticationException $exception)
{
$errorMessage = strtr($exception->getMessageKey(), $exception->getMessageData());
$statusCode = self::mapExceptionCodeToStatusCode($exception->getCode());

$event = new AuthenticationFailureEvent(
$exception,
new JWTAuthenticationFailureResponse($errorMessage)
new JWTAuthenticationFailureResponse($errorMessage, $statusCode)
);

$this->dispatcher->dispatch($event, Events::AUTHENTICATION_FAILURE);

return $event->getResponse();
}

/**
* @param string|int $exceptionCode
*/
private static function mapExceptionCodeToStatusCode($exceptionCode): int
{
$canMapToStatusCode = is_int($exceptionCode)
&& $exceptionCode >= 400
&& $exceptionCode < 500;

return $canMapToStatusCode
? $exceptionCode
: Response::HTTP_UNAUTHORIZED;
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@

namespace Lexik\Bundle\JWTAuthenticationBundle\Tests\Security\Http\Authentication;

use Exception;
use Lexik\Bundle\JWTAuthenticationBundle\Security\Http\Authentication\AuthenticationFailureHandler;
use PHPUnit\Framework\TestCase;
use Symfony\Component\Security\Core\Exception\AuthenticationException;
Expand Down Expand Up @@ -35,6 +36,63 @@ public function testOnAuthenticationFailure()
$this->assertEquals($authenticationException->getMessageKey(), $content['message']);
}

/**
* test onAuthenticationFailure method.
*/
public function testOnAuthenticationFailureWithANonDefaultHttpFailureStatusCode()
{
$dispatcher = $this
->getMockBuilder('Symfony\Component\EventDispatcher\EventDispatcherInterface')
->disableOriginalConstructor()
->getMock();

$authenticationException = new AuthenticationException('', 403);

$handler = new AuthenticationFailureHandler($dispatcher);
$response = $handler->onAuthenticationFailure($this->getRequest(), $authenticationException);
$content = json_decode($response->getContent(), true);

$this->assertInstanceOf('Symfony\Component\HttpFoundation\JsonResponse', $response);
$this->assertEquals(403, $response->getStatusCode());
$this->assertEquals(403, $content['code']);
$this->assertEquals($authenticationException->getMessageKey(), $content['message']);
}

/**
* test onAuthenticationFailure method.
*
* @dataProvider nonHttpStatusCodeProvider
*
* @param string|int $nonHttpStatusCode
*/
public function testOnAuthenticationFailureWithANonHttpStatusCode($nonHttpStatusCode)
{
$dispatcher = $this
->getMockBuilder('Symfony\Component\EventDispatcher\EventDispatcherInterface')
->disableOriginalConstructor()
->getMock();

$authenticationException = new AuthenticationException('', $nonHttpStatusCode);

$handler = new AuthenticationFailureHandler($dispatcher);
$response = $handler->onAuthenticationFailure($this->getRequest(), $authenticationException);
$content = json_decode($response->getContent(), true);

$this->assertInstanceOf('Symfony\Component\HttpFoundation\JsonResponse', $response);
$this->assertEquals(401, $response->getStatusCode());
$this->assertEquals(401, $content['code']);
$this->assertEquals($authenticationException->getMessageKey(), $content['message']);
}

public static function nonHttpStatusCodeProvider(): iterable
{
yield 'server error HTTP status code' => [500];
yield 'redirection HTTP status code' => [500];
yield 'success HTTP status code' => [500];
yield 'non HTTP status code' => [1302];
yield 'default status code' => [0];
}

/**
* @return \PHPUnit_Framework_MockObject_MockObject
*/
Expand Down

0 comments on commit b94205c

Please sign in to comment.