From 606d7c8721b8c9b9c9ec0e3d774b8865976bcff4 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Petr=20Gajd=C5=AF=C5=A1ek?= Date: Thu, 17 Oct 2024 15:36:02 +0200 Subject: [PATCH] Fix CORS header caching in GraphQL responses Ensure that that dynamic Access-Control-Allow-Origin header is not cached. - Remove Access-Control-Allow-Origin header before saving to cache - Add Access-Control-Allow-Origin dynamically when serving cached responses based on the incoming request's Origin This resolves issues with CORS headers being cached alongside GraphQL responses, which could cause incorrect Access-Control-Allow-Origin values for clients with different origins. --- doc/10_GraphQL/10_Events.md | 2 ++ src/Controller/WebserviceController.php | 10 ------- src/Service/OutputCacheService.php | 37 +++++++++++++++++++++++-- 3 files changed, 37 insertions(+), 12 deletions(-) diff --git a/doc/10_GraphQL/10_Events.md b/doc/10_GraphQL/10_Events.md index efa1f72f..60503345 100644 --- a/doc/10_GraphQL/10_Events.md +++ b/doc/10_GraphQL/10_Events.md @@ -230,6 +230,8 @@ class GraphQlSubscriber implements EventSubscriberInterface - `OutputCacheEvents::PRE_LOAD`: is triggered before trying to load an entry from cache, if cache is enabled. You can disable the cache for this request by setting `$event->setUseCache(false)`. If you disable the cache, the entry won't be loaded nor saved - `OutputCacheEvents::PRE_SAVE`: if cache is enabled, it's triggered before saving an entry into the cache. You can use it to modify the response before it gets saved. +Uncacheable headers, such as CORS Access-Control-Allow-Origin, are removed from the response before the PRE_SAVE event and re-added after the cached response is loaded. + ```php headers->set('Access-Control-Allow-Origin', $origin); - $response->headers->set('Access-Control-Allow-Credentials', 'true'); - $response->headers->set('Access-Control-Allow-Methods', 'GET, POST, OPTIONS'); - $response->headers->set('Access-Control-Allow-Headers', 'Origin, Content-Type, X-Auth-Token'); - $this->cacheService->save($request, $response); return $response; diff --git a/src/Service/OutputCacheService.php b/src/Service/OutputCacheService.php index ac3c95a1..41e0d220 100644 --- a/src/Service/OutputCacheService.php +++ b/src/Service/OutputCacheService.php @@ -71,7 +71,12 @@ public function load(Request $request) $cacheKey = $this->computeKey($request); - return $this->loadFromCache($cacheKey); + $response = $this->loadFromCache($cacheKey); + if ($response) { + $this->addCorsHeaders($response); + } + + return $response; } /** @@ -85,11 +90,39 @@ public function save(Request $request, JsonResponse $response, $extraTags = []): $clientname = $request->attributes->getString('clientname'); $extraTags = array_merge(['output', 'datahub', $clientname], $extraTags); + $this->removeCorsHeaders($response); + $event = new OutputCachePreSaveEvent($request, $response); $this->eventDispatcher->dispatch($event, OutputCacheEvents::PRE_SAVE); - $this->saveToCache($cacheKey, $event->getResponse(), $extraTags); + $this->saveToCache($cacheKey, $response, $extraTags); + + $this->addCorsHeaders($response); + } + } + + /** + * Removes CORS headers including Access-Control-Allow-Origin that should not be cached. + */ + protected function removeCorsHeaders(JsonResponse $response): void + { + $response->headers->remove('Access-Control-Allow-Origin'); + $response->headers->remove('Access-Control-Allow-Credentials'); + $response->headers->remove('Access-Control-Allow-Methods'); + $response->headers->remove('Access-Control-Allow-Headers'); + } + + protected function addCorsHeaders(JsonResponse $response): void + { + $origin = '*'; + if (!empty($_SERVER['HTTP_ORIGIN'])) { + $origin = $_SERVER['HTTP_ORIGIN']; } + + $response->headers->set('Access-Control-Allow-Origin', $origin); + $response->headers->set('Access-Control-Allow-Credentials', 'true'); + $response->headers->set('Access-Control-Allow-Methods', 'GET, POST, OPTIONS'); + $response->headers->set('Access-Control-Allow-Headers', 'Origin, Content-Type, X-Auth-Token'); } /**