2016-07-04 17:54:24 +02:00
|
|
|
<?php
|
2017-10-12 10:13:20 +02:00
|
|
|
declare(strict_types=1);
|
|
|
|
|
|
2016-07-19 17:07:59 +02:00
|
|
|
namespace Shlinkio\Shlink\Rest\Middleware;
|
2016-07-04 17:54:24 +02:00
|
|
|
|
2018-09-24 19:24:23 +02:00
|
|
|
use Fig\Http\Message\RequestMethodInterface;
|
2017-03-25 09:37:13 +01:00
|
|
|
use Fig\Http\Message\StatusCodeInterface;
|
2018-09-28 22:08:01 +02:00
|
|
|
use Psr\Container\ContainerExceptionInterface;
|
2016-07-04 17:54:24 +02:00
|
|
|
use Psr\Http\Message\ResponseInterface as Response;
|
|
|
|
|
use Psr\Http\Message\ServerRequestInterface as Request;
|
2018-03-26 18:49:28 +02:00
|
|
|
use Psr\Http\Server\MiddlewareInterface;
|
|
|
|
|
use Psr\Http\Server\RequestHandlerInterface;
|
2016-08-08 12:33:58 +02:00
|
|
|
use Psr\Log\LoggerInterface;
|
|
|
|
|
use Psr\Log\NullLogger;
|
2018-09-29 08:16:40 +02:00
|
|
|
use Shlinkio\Shlink\Rest\Authentication\RequestToHttpAuthPlugin;
|
|
|
|
|
use Shlinkio\Shlink\Rest\Authentication\RequestToHttpAuthPluginInterface;
|
2018-09-28 22:08:01 +02:00
|
|
|
use Shlinkio\Shlink\Rest\Exception\NoAuthenticationException;
|
|
|
|
|
use Shlinkio\Shlink\Rest\Exception\VerifyAuthenticationException;
|
2016-07-19 17:07:59 +02:00
|
|
|
use Shlinkio\Shlink\Rest\Util\RestUtils;
|
2016-07-04 17:54:24 +02:00
|
|
|
use Zend\Diactoros\Response\JsonResponse;
|
|
|
|
|
use Zend\Expressive\Router\RouteResult;
|
2016-07-21 16:41:16 +02:00
|
|
|
use Zend\I18n\Translator\TranslatorInterface;
|
2018-09-28 22:08:01 +02:00
|
|
|
use function implode;
|
|
|
|
|
use function in_array;
|
|
|
|
|
use function sprintf;
|
2016-07-04 17:54:24 +02:00
|
|
|
|
2018-09-24 19:24:23 +02:00
|
|
|
class AuthenticationMiddleware implements MiddlewareInterface, StatusCodeInterface, RequestMethodInterface
|
2016-07-04 17:54:24 +02:00
|
|
|
{
|
2016-07-21 16:41:16 +02:00
|
|
|
/**
|
|
|
|
|
* @var TranslatorInterface
|
|
|
|
|
*/
|
|
|
|
|
private $translator;
|
2016-08-08 12:33:58 +02:00
|
|
|
/**
|
|
|
|
|
* @var LoggerInterface
|
|
|
|
|
*/
|
|
|
|
|
private $logger;
|
2018-05-01 18:35:12 +02:00
|
|
|
/**
|
|
|
|
|
* @var array
|
|
|
|
|
*/
|
|
|
|
|
private $routesWhitelist;
|
2018-09-28 22:08:01 +02:00
|
|
|
/**
|
2018-09-29 08:16:40 +02:00
|
|
|
* @var RequestToHttpAuthPluginInterface
|
2018-09-28 22:08:01 +02:00
|
|
|
*/
|
2018-09-29 08:16:40 +02:00
|
|
|
private $requestToAuthPlugin;
|
2016-07-04 17:54:24 +02:00
|
|
|
|
2016-08-08 12:33:58 +02:00
|
|
|
public function __construct(
|
2018-09-29 08:16:40 +02:00
|
|
|
RequestToHttpAuthPluginInterface $requestToAuthPlugin,
|
2016-08-08 12:33:58 +02:00
|
|
|
TranslatorInterface $translator,
|
2018-05-01 18:35:12 +02:00
|
|
|
array $routesWhitelist,
|
2016-08-08 12:33:58 +02:00
|
|
|
LoggerInterface $logger = null
|
|
|
|
|
) {
|
2016-07-21 16:41:16 +02:00
|
|
|
$this->translator = $translator;
|
2018-05-01 18:35:12 +02:00
|
|
|
$this->routesWhitelist = $routesWhitelist;
|
2016-08-08 12:33:58 +02:00
|
|
|
$this->logger = $logger ?: new NullLogger();
|
2018-09-29 08:16:40 +02:00
|
|
|
$this->requestToAuthPlugin = $requestToAuthPlugin;
|
2016-07-04 17:54:24 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/**
|
2017-03-25 09:37:13 +01:00
|
|
|
* Process an incoming server request and return a response, optionally delegating
|
|
|
|
|
* to the next middleware component to create the response.
|
2016-07-04 17:54:24 +02:00
|
|
|
*
|
|
|
|
|
* @param Request $request
|
2018-03-26 18:49:28 +02:00
|
|
|
* @param RequestHandlerInterface $handler
|
2017-03-25 09:37:13 +01:00
|
|
|
*
|
|
|
|
|
* @return Response
|
2017-07-22 14:20:40 +02:00
|
|
|
* @throws \InvalidArgumentException
|
2016-07-04 17:54:24 +02:00
|
|
|
*/
|
2018-03-26 18:49:28 +02:00
|
|
|
public function process(Request $request, RequestHandlerInterface $handler): Response
|
2016-07-04 17:54:24 +02:00
|
|
|
{
|
2017-12-27 16:23:54 +01:00
|
|
|
/** @var RouteResult|null $routeResult */
|
2016-07-04 17:54:24 +02:00
|
|
|
$routeResult = $request->getAttribute(RouteResult::class);
|
2017-12-27 16:23:54 +01:00
|
|
|
if ($routeResult === null
|
2016-07-26 19:09:54 +02:00
|
|
|
|| $routeResult->isFailure()
|
2018-09-24 19:24:23 +02:00
|
|
|
|| $request->getMethod() === self::METHOD_OPTIONS
|
2018-09-28 22:08:01 +02:00
|
|
|
|| in_array($routeResult->getMatchedRouteName(), $this->routesWhitelist, true)
|
2016-07-05 19:19:23 +02:00
|
|
|
) {
|
2018-03-26 18:49:28 +02:00
|
|
|
return $handler->handle($request);
|
2016-07-04 17:54:24 +02:00
|
|
|
}
|
|
|
|
|
|
2018-09-28 22:08:01 +02:00
|
|
|
try {
|
2018-09-29 08:16:40 +02:00
|
|
|
$plugin = $this->requestToAuthPlugin->fromRequest($request);
|
2018-09-28 22:08:01 +02:00
|
|
|
} catch (ContainerExceptionInterface | NoAuthenticationException $e) {
|
|
|
|
|
$this->logger->warning('Invalid or no authentication provided.' . PHP_EOL . $e);
|
|
|
|
|
return $this->createErrorResponse(sprintf($this->translator->translate(
|
|
|
|
|
'Expected one of the following authentication headers, but none were provided, ["%s"]'
|
2018-09-29 08:16:40 +02:00
|
|
|
), implode('", "', RequestToHttpAuthPlugin::SUPPORTED_AUTH_HEADERS)));
|
2016-08-07 19:53:14 +02:00
|
|
|
}
|
|
|
|
|
|
2016-07-04 17:54:24 +02:00
|
|
|
try {
|
2018-09-28 22:08:01 +02:00
|
|
|
$plugin->verify($request);
|
2018-03-26 18:49:28 +02:00
|
|
|
$response = $handler->handle($request);
|
2018-09-28 22:08:01 +02:00
|
|
|
return $plugin->update($request, $response);
|
|
|
|
|
} catch (VerifyAuthenticationException $e) {
|
|
|
|
|
$this->logger->warning('Authentication verification failed.' . PHP_EOL . $e);
|
|
|
|
|
return $this->createErrorResponse($e->getPublicMessage(), $e->getErrorCode());
|
2016-07-04 17:54:24 +02:00
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
2018-09-28 22:08:01 +02:00
|
|
|
private function createErrorResponse(
|
|
|
|
|
string $message,
|
|
|
|
|
string $errorCode = RestUtils::INVALID_AUTHORIZATION_ERROR
|
|
|
|
|
): JsonResponse {
|
2016-07-04 17:54:24 +02:00
|
|
|
return new JsonResponse([
|
2018-09-28 22:08:01 +02:00
|
|
|
'error' => $errorCode,
|
|
|
|
|
'message' => $message,
|
2017-03-25 09:37:13 +01:00
|
|
|
], self::STATUS_UNAUTHORIZED);
|
2016-07-04 17:54:24 +02:00
|
|
|
}
|
|
|
|
|
}
|