From ce4f614e90ae32953591c9dd113d909082c9006d Mon Sep 17 00:00:00 2001 From: Marius Klocke Date: Tue, 19 Dec 2023 13:47:15 +0100 Subject: [PATCH] Move request parsing from middleware into service --- src/Infrastructure/API/ActionInterface.php | 8 ++++- src/Infrastructure/API/Application.php | 1 - .../API/GraphQL/QueryAction.php | 14 +++++---- .../API/JsonParserMiddleware.php | 28 ------------------ src/Infrastructure/API/RequestParser.php | 29 +++++++++++++++++++ .../WebAuthn/Action/GetLoginOptionsAction.php | 19 ++++-------- .../WebAuthn/Action/PerformLoginAction.php | 8 +++-- .../Action/RegisterCredentialAction.php | 17 ++++------- 8 files changed, 62 insertions(+), 62 deletions(-) delete mode 100644 src/Infrastructure/API/JsonParserMiddleware.php create mode 100644 src/Infrastructure/API/RequestParser.php diff --git a/src/Infrastructure/API/ActionInterface.php b/src/Infrastructure/API/ActionInterface.php index f9c24b6d..a8507d4e 100644 --- a/src/Infrastructure/API/ActionInterface.php +++ b/src/Infrastructure/API/ActionInterface.php @@ -7,5 +7,11 @@ interface ActionInterface { + /** + * @param ServerRequestInterface $request + * @param ResponseInterface $response + * @param array $args + * @return ResponseInterface + */ public function __invoke(ServerRequestInterface $request, ResponseInterface $response, array $args): ResponseInterface; -} \ No newline at end of file +} diff --git a/src/Infrastructure/API/Application.php b/src/Infrastructure/API/Application.php index 621cc64e..e195247f 100644 --- a/src/Infrastructure/API/Application.php +++ b/src/Infrastructure/API/Application.php @@ -52,7 +52,6 @@ public function __construct() $this->add(new LoggingMiddleware($container)); $this->add(new TrailingSlash()); - $this->add(new JsonParserMiddleware()); $this->add(new AuthenticationMiddleware($container)); $this->add(new MaintenanceModeMiddleware()); diff --git a/src/Infrastructure/API/GraphQL/QueryAction.php b/src/Infrastructure/API/GraphQL/QueryAction.php index 626239ee..82169fd3 100644 --- a/src/Infrastructure/API/GraphQL/QueryAction.php +++ b/src/Infrastructure/API/GraphQL/QueryAction.php @@ -7,6 +7,7 @@ use HexagonalPlayground\Application\TypeAssert; use HexagonalPlayground\Infrastructure\API\ActionInterface; use HexagonalPlayground\Infrastructure\API\JsonResponseWriter; +use HexagonalPlayground\Infrastructure\API\RequestParser; use Psr\Container\ContainerInterface; use Psr\Http\Message\ResponseInterface; use Psr\Http\Message\ServerRequestInterface; @@ -14,20 +15,20 @@ class QueryAction implements ActionInterface { - /** @var ContainerInterface */ private ContainerInterface $container; - - /** @var JsonResponseWriter */ private JsonResponseWriter $responseWriter; + private RequestParser $requestParser; /** * @param ContainerInterface $container * @param JsonResponseWriter $responseWriter + * @param RequestParser $requestParser */ - public function __construct(ContainerInterface $container, JsonResponseWriter $responseWriter) + public function __construct(ContainerInterface $container, JsonResponseWriter $responseWriter, RequestParser $requestParser) { $this->container = $container; $this->responseWriter = $responseWriter; + $this->requestParser = $requestParser; } /** @@ -35,13 +36,14 @@ public function __construct(ContainerInterface $container, JsonResponseWriter $r */ public function __invoke(ServerRequestInterface $request, ResponseInterface $response, array $args): ResponseInterface { - $parsedBody = $request->getParsedBody(); + $parsedBody = $this->requestParser->parseJson($request); $query = $parsedBody['query'] ?? null; $variables = $parsedBody['variables'] ?? []; TypeAssert::assertString($query, 'query'); TypeAssert::assertArray($variables, 'variables'); + $request = $request->withParsedBody($parsedBody); $context = new AppContext($request, $this->container); $errorHandler = new ErrorHandler($this->container->get(LoggerInterface::class), $request); $schema = $this->container->get(Schema::class); @@ -53,4 +55,4 @@ public function __invoke(ServerRequestInterface $request, ResponseInterface $res return $this->responseWriter->write($response, $result->toArray()); } -} \ No newline at end of file +} diff --git a/src/Infrastructure/API/JsonParserMiddleware.php b/src/Infrastructure/API/JsonParserMiddleware.php deleted file mode 100644 index 7529a2cd..00000000 --- a/src/Infrastructure/API/JsonParserMiddleware.php +++ /dev/null @@ -1,28 +0,0 @@ -getHeader('Content-Type'))) { - $json = $request->getBody()->__toString(); - $parsed = json_decode($json, true); - $request = $request->withParsedBody($parsed); - } - return $handler->handle($request); - } -} \ No newline at end of file diff --git a/src/Infrastructure/API/RequestParser.php b/src/Infrastructure/API/RequestParser.php new file mode 100644 index 00000000..6e2c5c1b --- /dev/null +++ b/src/Infrastructure/API/RequestParser.php @@ -0,0 +1,29 @@ +getHeader('Content-Type'))) { + throw new InvalidInputException('Missing expected Content-Type header "application/json"'); + } + + try { + return json_decode((string)$request->getBody(), true, 64, JSON_THROW_ON_ERROR); + } catch (Throwable $throwable) { + throw new InvalidInputException('Failed to decode JSON from request body', 0, $throwable); + } + } +} diff --git a/src/Infrastructure/API/Security/WebAuthn/Action/GetLoginOptionsAction.php b/src/Infrastructure/API/Security/WebAuthn/Action/GetLoginOptionsAction.php index 6f726667..bbc186e5 100644 --- a/src/Infrastructure/API/Security/WebAuthn/Action/GetLoginOptionsAction.php +++ b/src/Infrastructure/API/Security/WebAuthn/Action/GetLoginOptionsAction.php @@ -7,6 +7,7 @@ use HexagonalPlayground\Application\TypeAssert; use HexagonalPlayground\Infrastructure\API\ActionInterface; use HexagonalPlayground\Infrastructure\API\JsonResponseWriter; +use HexagonalPlayground\Infrastructure\API\RequestParser; use HexagonalPlayground\Infrastructure\API\Security\WebAuthn\FakeCredentialDescriptorFactory; use HexagonalPlayground\Infrastructure\API\Security\WebAuthn\OptionsStoreInterface; use HexagonalPlayground\Infrastructure\API\Security\WebAuthn\RequestOptionsFactory; @@ -18,23 +19,13 @@ class GetLoginOptionsAction implements ActionInterface { - /** @var PublicKeyCredentialSourceRepository */ private PublicKeyCredentialSourceRepository $credentialRepository; - - /** @var RequestOptionsFactory */ private RequestOptionsFactory $requestOptionsFactory; - - /** @var OptionsStoreInterface */ private OptionsStoreInterface $optionsStore; - - /** @var FakeCredentialDescriptorFactory */ private FakeCredentialDescriptorFactory $fakeCredentialDescriptorFactory; - - /** @var UserRepositoryInterface */ private UserRepositoryInterface $userRepository; - - /** @var JsonResponseWriter */ private JsonResponseWriter $responseWriter; + private RequestParser $requestParser; /** * @param PublicKeyCredentialSourceRepository $credentialRepository @@ -43,8 +34,9 @@ class GetLoginOptionsAction implements ActionInterface * @param FakeCredentialDescriptorFactory $fakeCredentialDescriptorFactory * @param UserRepositoryInterface $userRepository * @param JsonResponseWriter $responseWriter + * @param RequestParser $requestParser */ - public function __construct(PublicKeyCredentialSourceRepository $credentialRepository, RequestOptionsFactory $requestOptionsFactory, OptionsStoreInterface $optionsStore, FakeCredentialDescriptorFactory $fakeCredentialDescriptorFactory, UserRepositoryInterface $userRepository, JsonResponseWriter $responseWriter) + public function __construct(PublicKeyCredentialSourceRepository $credentialRepository, RequestOptionsFactory $requestOptionsFactory, OptionsStoreInterface $optionsStore, FakeCredentialDescriptorFactory $fakeCredentialDescriptorFactory, UserRepositoryInterface $userRepository, JsonResponseWriter $responseWriter, RequestParser $requestParser) { $this->credentialRepository = $credentialRepository; $this->requestOptionsFactory = $requestOptionsFactory; @@ -52,6 +44,7 @@ public function __construct(PublicKeyCredentialSourceRepository $credentialRepos $this->fakeCredentialDescriptorFactory = $fakeCredentialDescriptorFactory; $this->userRepository = $userRepository; $this->responseWriter = $responseWriter; + $this->requestParser = $requestParser; } /** @@ -59,7 +52,7 @@ public function __construct(PublicKeyCredentialSourceRepository $credentialRepos */ public function __invoke(ServerRequestInterface $request, ResponseInterface $response, array $args): ResponseInterface { - $parsedBody = $request->getParsedBody(); + $parsedBody = $this->requestParser->parseJson($request); $email = $parsedBody['email'] ?? null; /** @var string $email */ diff --git a/src/Infrastructure/API/Security/WebAuthn/Action/PerformLoginAction.php b/src/Infrastructure/API/Security/WebAuthn/Action/PerformLoginAction.php index a0fa6be2..11184580 100644 --- a/src/Infrastructure/API/Security/WebAuthn/Action/PerformLoginAction.php +++ b/src/Infrastructure/API/Security/WebAuthn/Action/PerformLoginAction.php @@ -12,6 +12,7 @@ use HexagonalPlayground\Application\TypeAssert; use HexagonalPlayground\Infrastructure\API\ActionInterface; use HexagonalPlayground\Infrastructure\API\JsonResponseWriter; +use HexagonalPlayground\Infrastructure\API\RequestParser; use HexagonalPlayground\Infrastructure\API\Security\WebAuthn\OptionsStoreInterface; use Psr\Http\Message\ResponseInterface; use Psr\Http\Message\ServerRequestInterface; @@ -28,6 +29,7 @@ class PerformLoginAction implements ActionInterface private UserRepositoryInterface $userRepository; private TokenServiceInterface $tokenService; private JsonResponseWriter $responseWriter; + private RequestParser $requestParser; /** * @param OptionsStoreInterface $optionsStore @@ -36,8 +38,9 @@ class PerformLoginAction implements ActionInterface * @param UserRepositoryInterface $userRepository * @param TokenServiceInterface $tokenService * @param JsonResponseWriter $responseWriter + * @param RequestParser $requestParser */ - public function __construct(OptionsStoreInterface $optionsStore, PublicKeyCredentialLoader $credentialLoader, AuthenticatorAssertionResponseValidator $authenticatorAssertionResponseValidator, UserRepositoryInterface $userRepository, TokenServiceInterface $tokenService, JsonResponseWriter $responseWriter) + public function __construct(OptionsStoreInterface $optionsStore, PublicKeyCredentialLoader $credentialLoader, AuthenticatorAssertionResponseValidator $authenticatorAssertionResponseValidator, UserRepositoryInterface $userRepository, TokenServiceInterface $tokenService, JsonResponseWriter $responseWriter, RequestParser $requestParser) { $this->optionsStore = $optionsStore; $this->credentialLoader = $credentialLoader; @@ -45,6 +48,7 @@ public function __construct(OptionsStoreInterface $optionsStore, PublicKeyCreden $this->userRepository = $userRepository; $this->tokenService = $tokenService; $this->responseWriter = $responseWriter; + $this->requestParser = $requestParser; } /** @@ -52,7 +56,7 @@ public function __construct(OptionsStoreInterface $optionsStore, PublicKeyCreden */ public function __invoke(ServerRequestInterface $request, ResponseInterface $response, array $args): ResponseInterface { - $parsedBody = $request->getParsedBody(); + $parsedBody = $this->requestParser->parseJson($request); $email = $parsedBody['email'] ?? null; /** @var string $email */ diff --git a/src/Infrastructure/API/Security/WebAuthn/Action/RegisterCredentialAction.php b/src/Infrastructure/API/Security/WebAuthn/Action/RegisterCredentialAction.php index 637bca55..1cb60d4a 100644 --- a/src/Infrastructure/API/Security/WebAuthn/Action/RegisterCredentialAction.php +++ b/src/Infrastructure/API/Security/WebAuthn/Action/RegisterCredentialAction.php @@ -6,6 +6,7 @@ use HexagonalPlayground\Domain\Exception\InvalidInputException; use HexagonalPlayground\Application\TypeAssert; use HexagonalPlayground\Infrastructure\API\ActionInterface; +use HexagonalPlayground\Infrastructure\API\RequestParser; use HexagonalPlayground\Infrastructure\API\Security\AuthReader; use HexagonalPlayground\Infrastructure\API\Security\WebAuthn\OptionsStoreInterface; use HexagonalPlayground\Infrastructure\API\Security\WebAuthn\PublicKeyCredential; @@ -19,20 +20,12 @@ class RegisterCredentialAction implements ActionInterface { - /** @var PublicKeyCredentialSourceRepository */ private PublicKeyCredentialSourceRepository $credentialRepository; - - /** @var PublicKeyCredentialLoader */ private PublicKeyCredentialLoader $credentialLoader; - - /** @var AuthenticatorAttestationResponseValidator */ private AuthenticatorAttestationResponseValidator $authenticatorAttestationResponseValidator; - - /** @var OptionsStoreInterface */ private OptionsStoreInterface $creationOptionsStore; - - /** @var AuthReader */ private AuthReader $authReader; + private RequestParser $requestParser; /** * @param PublicKeyCredentialSourceRepository $credentialRepository @@ -40,14 +33,16 @@ class RegisterCredentialAction implements ActionInterface * @param AuthenticatorAttestationResponseValidator $authenticatorAttestationResponseValidator * @param OptionsStoreInterface $creationOptionsStore * @param AuthReader $authReader + * @param RequestParser $requestParser */ - public function __construct(PublicKeyCredentialSourceRepository $credentialRepository, PublicKeyCredentialLoader $credentialLoader, AuthenticatorAttestationResponseValidator $authenticatorAttestationResponseValidator, OptionsStoreInterface $creationOptionsStore, AuthReader $authReader) + public function __construct(PublicKeyCredentialSourceRepository $credentialRepository, PublicKeyCredentialLoader $credentialLoader, AuthenticatorAttestationResponseValidator $authenticatorAttestationResponseValidator, OptionsStoreInterface $creationOptionsStore, AuthReader $authReader, RequestParser $requestParser) { $this->credentialRepository = $credentialRepository; $this->credentialLoader = $credentialLoader; $this->authenticatorAttestationResponseValidator = $authenticatorAttestationResponseValidator; $this->creationOptionsStore = $creationOptionsStore; $this->authReader = $authReader; + $this->requestParser = $requestParser; } /** @@ -55,7 +50,7 @@ public function __construct(PublicKeyCredentialSourceRepository $credentialRepos */ public function __invoke(ServerRequestInterface $request, ResponseInterface $response, array $args): ResponseInterface { - $parsedBody = $request->getParsedBody(); + $parsedBody = $this->requestParser->parseJson($request); $name = $parsedBody['name'] ?? null; /** @var string $name */