mirror of
https://github.com/shlinkio/shlink.git
synced 2025-02-25 18:45:27 -06:00
109 lines
3.8 KiB
PHP
109 lines
3.8 KiB
PHP
<?php
|
|
declare(strict_types=1);
|
|
|
|
namespace Shlinkio\Shlink\Rest\Middleware;
|
|
|
|
use Fig\Http\Message\RequestMethodInterface;
|
|
use Fig\Http\Message\StatusCodeInterface;
|
|
use Psr\Container\ContainerExceptionInterface;
|
|
use Psr\Http\Message\ResponseInterface as Response;
|
|
use Psr\Http\Message\ServerRequestInterface as Request;
|
|
use Psr\Http\Server\MiddlewareInterface;
|
|
use Psr\Http\Server\RequestHandlerInterface;
|
|
use Psr\Log\LoggerInterface;
|
|
use Psr\Log\NullLogger;
|
|
use Shlinkio\Shlink\Rest\Authentication\RequestToHttpAuthPlugin;
|
|
use Shlinkio\Shlink\Rest\Authentication\RequestToHttpAuthPluginInterface;
|
|
use Shlinkio\Shlink\Rest\Exception\NoAuthenticationException;
|
|
use Shlinkio\Shlink\Rest\Exception\VerifyAuthenticationException;
|
|
use Shlinkio\Shlink\Rest\Util\RestUtils;
|
|
use Zend\Diactoros\Response\JsonResponse;
|
|
use Zend\Expressive\Router\RouteResult;
|
|
use Zend\I18n\Translator\TranslatorInterface;
|
|
use function implode;
|
|
use function in_array;
|
|
use function sprintf;
|
|
|
|
class AuthenticationMiddleware implements MiddlewareInterface, StatusCodeInterface, RequestMethodInterface
|
|
{
|
|
/**
|
|
* @var TranslatorInterface
|
|
*/
|
|
private $translator;
|
|
/**
|
|
* @var LoggerInterface
|
|
*/
|
|
private $logger;
|
|
/**
|
|
* @var array
|
|
*/
|
|
private $routesWhitelist;
|
|
/**
|
|
* @var RequestToHttpAuthPluginInterface
|
|
*/
|
|
private $requestToAuthPlugin;
|
|
|
|
public function __construct(
|
|
RequestToHttpAuthPluginInterface $requestToAuthPlugin,
|
|
TranslatorInterface $translator,
|
|
array $routesWhitelist,
|
|
LoggerInterface $logger = null
|
|
) {
|
|
$this->translator = $translator;
|
|
$this->routesWhitelist = $routesWhitelist;
|
|
$this->logger = $logger ?: new NullLogger();
|
|
$this->requestToAuthPlugin = $requestToAuthPlugin;
|
|
}
|
|
|
|
/**
|
|
* Process an incoming server request and return a response, optionally delegating
|
|
* to the next middleware component to create the response.
|
|
*
|
|
* @param Request $request
|
|
* @param RequestHandlerInterface $handler
|
|
*
|
|
* @return Response
|
|
* @throws \InvalidArgumentException
|
|
*/
|
|
public function process(Request $request, RequestHandlerInterface $handler): Response
|
|
{
|
|
/** @var RouteResult|null $routeResult */
|
|
$routeResult = $request->getAttribute(RouteResult::class);
|
|
if ($routeResult === null
|
|
|| $routeResult->isFailure()
|
|
|| $request->getMethod() === self::METHOD_OPTIONS
|
|
|| in_array($routeResult->getMatchedRouteName(), $this->routesWhitelist, true)
|
|
) {
|
|
return $handler->handle($request);
|
|
}
|
|
|
|
try {
|
|
$plugin = $this->requestToAuthPlugin->fromRequest($request);
|
|
} 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"]'
|
|
), implode('", "', RequestToHttpAuthPlugin::SUPPORTED_AUTH_HEADERS)));
|
|
}
|
|
|
|
try {
|
|
$plugin->verify($request);
|
|
$response = $handler->handle($request);
|
|
return $plugin->update($request, $response);
|
|
} catch (VerifyAuthenticationException $e) {
|
|
$this->logger->warning('Authentication verification failed.' . PHP_EOL . $e);
|
|
return $this->createErrorResponse($e->getPublicMessage(), $e->getErrorCode());
|
|
}
|
|
}
|
|
|
|
private function createErrorResponse(
|
|
string $message,
|
|
string $errorCode = RestUtils::INVALID_AUTHORIZATION_ERROR
|
|
): JsonResponse {
|
|
return new JsonResponse([
|
|
'error' => $errorCode,
|
|
'message' => $message,
|
|
], self::STATUS_UNAUTHORIZED);
|
|
}
|
|
}
|