From 431169eb8c0788b929cfc28e32da32267bcbbd26 Mon Sep 17 00:00:00 2001 From: Alejandro Celaya Date: Mon, 4 Jul 2016 17:54:24 +0200 Subject: [PATCH] Created middleware that checks authentication --- .../autoload/middleware-pipeline.global.php | 1 + config/autoload/services.global.php | 1 + .../CheckAuthenticationMiddleware.php | 100 ++++++++++++++++++ src/Service/RestTokenService.php | 11 ++ src/Service/RestTokenServiceInterface.php | 7 ++ src/Util/RestUtils.php | 5 +- 6 files changed, 123 insertions(+), 2 deletions(-) create mode 100644 src/Middleware/CheckAuthenticationMiddleware.php diff --git a/config/autoload/middleware-pipeline.global.php b/config/autoload/middleware-pipeline.global.php index ab903ac9..fc6f85f0 100644 --- a/config/autoload/middleware-pipeline.global.php +++ b/config/autoload/middleware-pipeline.global.php @@ -23,6 +23,7 @@ return [ 'rest' => [ 'path' => '/rest', 'middleware' => [ + Middleware\CheckAuthenticationMiddleware::class, Middleware\CrossDomainMiddleware::class, ], 'priority' => 5, diff --git a/config/autoload/services.global.php b/config/autoload/services.global.php index d5de3d4a..b08229e7 100644 --- a/config/autoload/services.global.php +++ b/config/autoload/services.global.php @@ -52,6 +52,7 @@ return [ Middleware\Rest\GetVisitsMiddleware::class => AnnotatedFactory::class, Middleware\Rest\ListShortcodesMiddleware::class => AnnotatedFactory::class, Middleware\CrossDomainMiddleware::class => InvokableFactory::class, + Middleware\CheckAuthenticationMiddleware::class => AnnotatedFactory::class, ], 'aliases' => [ 'em' => EntityManager::class, diff --git a/src/Middleware/CheckAuthenticationMiddleware.php b/src/Middleware/CheckAuthenticationMiddleware.php new file mode 100644 index 00000000..92081ad8 --- /dev/null +++ b/src/Middleware/CheckAuthenticationMiddleware.php @@ -0,0 +1,100 @@ +restTokenService = $restTokenService; + } + + /** + * Process an incoming request and/or response. + * + * Accepts a server-side request and a response instance, and does + * something with them. + * + * If the response is not complete and/or further processing would not + * interfere with the work done in the middleware, or if the middleware + * wants to delegate to another process, it can use the `$out` callable + * if present. + * + * If the middleware does not return a value, execution of the current + * request is considered complete, and the response instance provided will + * be considered the response to return. + * + * Alternately, the middleware may return a response instance. + * + * Often, middleware will `return $out();`, with the assumption that a + * later middleware will return a response. + * + * @param Request $request + * @param Response $response + * @param null|callable $out + * @return null|Response + */ + public function __invoke(Request $request, Response $response, callable $out = null) + { + // If current route is the authenticate route, continue to the next middleware + /** @var RouteResult $routeResult */ + $routeResult = $request->getAttribute(RouteResult::class); + if (isset($routeResult) && $routeResult->getMatchedRouteName() === 'rest-authenticate') { + return $out($request, $response); + } + + // Check that the auth header was provided, and that it belongs to a non-expired token + if (! $request->hasHeader(self::AUTH_TOKEN_HEADER)) { + return $this->createTokenErrorResponse(); + } + + $authToken = $request->getHeaderLine(self::AUTH_TOKEN_HEADER); + try { + $restToken = $this->restTokenService->getByToken($authToken); + if ($restToken->isExpired()) { + return $this->createTokenErrorResponse(); + } + + // Update the token expiration and continue to next middleware + $this->restTokenService->updateExpiration($restToken); + return $out($request, $response); + } catch (InvalidArgumentException $e) { + return $this->createTokenErrorResponse(); + } + } + + protected function createTokenErrorResponse() + { + return new JsonResponse([ + 'error' => RestUtils::INVALID_AUTH_TOKEN_ERROR, + 'message' => sprintf( + 'Missing or invalid auth token provided. Perform a new authentication request and send provided token ' + . 'on every new request on the "%s" header', + self::AUTH_TOKEN_HEADER + ), + ], 401); + } +} diff --git a/src/Service/RestTokenService.php b/src/Service/RestTokenService.php index aa9ea0b8..26d7f34c 100644 --- a/src/Service/RestTokenService.php +++ b/src/Service/RestTokenService.php @@ -84,4 +84,15 @@ class RestTokenService implements RestTokenServiceInterface // If credentials are not correct, throw exception throw AuthenticationException::fromCredentials($providedUsername, $providedPassword); } + + /** + * Updates the expiration of provided token, extending its life + * + * @param RestToken $token + */ + public function updateExpiration(RestToken $token) + { + $token->updateExpiration(); + $this->em->flush(); + } } diff --git a/src/Service/RestTokenServiceInterface.php b/src/Service/RestTokenServiceInterface.php index fb45483d..0cdec822 100644 --- a/src/Service/RestTokenServiceInterface.php +++ b/src/Service/RestTokenServiceInterface.php @@ -22,4 +22,11 @@ interface RestTokenServiceInterface * @throws AuthenticationException */ public function createToken($username, $password); + + /** + * Updates the expiration of provided token, extending its life + * + * @param RestToken $token + */ + public function updateExpiration(RestToken $token); } diff --git a/src/Util/RestUtils.php b/src/Util/RestUtils.php index 94ab47ec..f0c37a00 100644 --- a/src/Util/RestUtils.php +++ b/src/Util/RestUtils.php @@ -8,7 +8,8 @@ class RestUtils const INVALID_SHORTCODE_ERROR = 'INVALID_SHORTCODE'; const INVALID_URL_ERROR = 'INVALID_URL'; const INVALID_ARGUMENT_ERROR = 'INVALID_ARGUMENT'; - const INVALID_CREDENTIALS = 'INVALID_CREDENTIALS'; + const INVALID_CREDENTIALS_ERROR = 'INVALID_CREDENTIALS'; + const INVALID_AUTH_TOKEN_ERROR = 'INVALID_AUTH_TOKEN_ERROR'; const UNKNOWN_ERROR = 'UNKNOWN_ERROR'; public static function getRestErrorCodeFromException(Exception\ExceptionInterface $e) @@ -21,7 +22,7 @@ class RestUtils case $e instanceof Exception\InvalidArgumentException: return self::INVALID_ARGUMENT_ERROR; case $e instanceof Exception\AuthenticationException: - return self::INVALID_CREDENTIALS; + return self::INVALID_CREDENTIALS_ERROR; default: return self::UNKNOWN_ERROR; }