feat: require the CMS API key on /api/hotel and /api/product

This commit is contained in:
fromme committed 2026-10-08 11:37:57 +02:00
1 parent 7839b97e63
commit 32b70f9b70
3 files changed
+51 -21

No files matched your search

+37 -7
View File
@@ -1,12 +1,42 @@
# Backchannel endpoints MyE&P reads hotel and product data from. Same environment files and key
# as content-feed.http: pick "local" and put cmsApiKey in http-client.private.env.json.
### GET product data for myep
GET https://ep-reisen.ddev.site/api/product
?product=SSTMR
&hotel=SSTGR
&key=abcabc123
GET {{host}}/api/product?product=3VIIN&hotel=VLTS
X-Api-Key: {{cmsApiKey}}
> {%
client.test("200 with key", function () {
client.assert(response.status === 200, "expected 200, got " + response.status);
});
%}
### GET hotel data for myep
GET https://ep-reisen.ddev.site/api/hotel
?hotel=DPWGRP
&key=abcabc123
GET {{host}}/api/hotel?hotel=DPWGRP
X-Api-Key: {{cmsApiKey}}
> {%
client.test("200 with key", function () {
client.assert(response.status === 200, "expected 200, got " + response.status);
});
%}
### Transition form: key as query parameter, as MyE&P sends it today (still accepted)
GET {{host}}/api/hotel?hotel=DPWGRP&key={{cmsApiKey}}
> {%
client.test("200 with query key", function () {
client.assert(response.status === 200, "expected 200, got " + response.status);
});
%}
### Any other value is refused now - this used to pass
GET {{host}}/api/hotel?hotel=DPWGRP&key=AbcAbc123
> {%
client.test("403 with the old dummy key", function () {
client.assert(response.status === 403, "expected 403, got " + response.status);
});
%}
###
@@ -3,6 +3,7 @@
namespace EP\EpProducts\Middleware;
use EP\EpProducts\ContentFeed\ContentFeedApi;
use EP\EpProducts\Http\ApiKeyAuthenticator;
use EP\EpProducts\Service\MyEpImageService;
use EP\EpProducts\Utility\HotelIconUtility;
use Psr\Http\Message\ResponseInterface;
@@ -47,13 +48,19 @@ class ApiMiddleware implements MiddlewareInterface
*/
private $contentFeedApi;
/**
* @var ApiKeyAuthenticator
*/
private $authenticator;
public function __construct(
MyEpImageService $hotelImageService,
MyEpImageService $productImageService,
MyEpImageService $regionImageService,
FileRepository $fileRepository,
ImageService $imageService,
ContentFeedApi $contentFeedApi
ContentFeedApi $contentFeedApi,
ApiKeyAuthenticator $authenticator
) {
$this->hotelImageService = $hotelImageService;
$this->productImageService = $productImageService;
@@ -61,6 +68,7 @@ class ApiMiddleware implements MiddlewareInterface
$this->fileRepository = $fileRepository;
$this->imageService = $imageService;
$this->contentFeedApi = $contentFeedApi;
$this->authenticator = $authenticator;
}
public function process(ServerRequestInterface $request, RequestHandlerInterface $handler): ResponseInterface
@@ -84,12 +92,8 @@ class ApiMiddleware implements MiddlewareInterface
private function handleHotelRequest(ServerRequestInterface $request): ResponseInterface
{
// This is currently fake, we only check for existence. Since we are communicating
// on the backchannel only it's not much of a problem.
$apiKey = $request->getQueryParams()['key'] ?? null;
if (null === $apiKey) {
return new JsonResponse(['message' => 'No api key provided'], 403);
if (!$this->authenticator->isAuthorized($request)) {
return new JsonResponse(['message' => 'Invalid or missing api key'], 403);
}
$hotelCode = $request->getQueryParams()['hotel'] ?? null;
@@ -109,12 +113,8 @@ class ApiMiddleware implements MiddlewareInterface
private function handleProductRequest(ServerRequestInterface $request): ResponseInterface
{
// This is currently fake, we only check for existence. Since we are communicating
// on the backchannel only it's not much of a problem.
$apiKey = $request->getQueryParams()['key'] ?? null;
if (null === $apiKey) {
return new JsonResponse(['message' => 'No api key provided'], 403);
if (!$this->authenticator->isAuthorized($request)) {
return new JsonResponse(['message' => 'Invalid or missing api key'], 403);
}
$productCode = $request->getQueryParams()['product'] ?? null;
@@ -76,7 +76,7 @@ productImportSftpHostKey =
# cat=SftpImport; type=int+; label=SFTP connection timeout in seconds
productImportSftpTimeout = 15
# cat=ContentFeed; type=string; label=API key for the CMS backchannel endpoints (chatbot content feed, CMS index); sent as X-Api-Key. Empty denies every request.
# cat=ContentFeed; type=string; label=API key for the CMS backchannel endpoints used by MyE&P (/api/hotel, /api/product, /api/content/*); sent as X-Api-Key or ?key=. Empty denies every request.
cmsApiKey =
# cat=ContentFeed; type=boolean; label=Serve the chatbot content feed (off: all feed endpoints answer 503)