diff --git a/geoportal/c2cgeoportal_geoportal/lib/common_headers.py b/geoportal/c2cgeoportal_geoportal/lib/common_headers.py index d72e52fc10..58b8fd785c 100644 --- a/geoportal/c2cgeoportal_geoportal/lib/common_headers.py +++ b/geoportal/c2cgeoportal_geoportal/lib/common_headers.py @@ -1,4 +1,4 @@ -# Copyright (c) 2012-2023, Camptocamp SA +# Copyright (c) 2012-2024, Camptocamp SA # All rights reserved. # Redistribution and use in source and binary forms, with or without @@ -33,8 +33,6 @@ import pyramid.request import pyramid.response -from c2cgeoportal_geoportal.lib import is_intranet - _LOG = logging.getLogger(__name__) @@ -119,6 +117,8 @@ def _set_common_headers( ) -> pyramid.response.Response: """Set the common headers.""" + del request # Unused + response.headers.update(service_headers_settings.get("headers", {})) if cache in (Cache.PRIVATE, Cache.PRIVATE_NO): @@ -136,10 +136,7 @@ def _set_common_headers( elif cache in (Cache.PUBLIC, Cache.PUBLIC_NO): response.cache_control.public = True elif cache in (Cache.PRIVATE, Cache.PRIVATE_NO): - if hasattr(request, "user") and request.user is not None or is_intranet(request): - response.cache_control.private = True - else: - response.cache_control.public = True + response.cache_control.private = True else: raise Exception("Invalid cache type") # pylint: disable=broad-exception-raised diff --git a/geoportal/c2cgeoportal_geoportal/scaffolds/update/{{cookiecutter.project}}/CONST_CHANGELOG.txt b/geoportal/c2cgeoportal_geoportal/scaffolds/update/{{cookiecutter.project}}/CONST_CHANGELOG.txt index 4927070275..710841154c 100644 --- a/geoportal/c2cgeoportal_geoportal/scaffolds/update/{{cookiecutter.project}}/CONST_CHANGELOG.txt +++ b/geoportal/c2cgeoportal_geoportal/scaffolds/update/{{cookiecutter.project}}/CONST_CHANGELOG.txt @@ -22,6 +22,12 @@ Version 2.8.1 End of support by GitHub: https://github.blog/changelog/2024-04-10-github-hosted-runner-images-deprecation-notice-docker-compose-v1/. +4. Previously the `Cache-Control` header was set to `public` when we are not authenticated to an endpoint + that use the authentication. But this didn't works in every cases, for example when we use the intranet + role and when we have a reverse proxy with cache. Now it's always set to private for all the endpoints + that use the authentication. + + ============= Version 2.8.0 ============= diff --git a/geoportal/tests/functional/test_mapserverproxy.py b/geoportal/tests/functional/test_mapserverproxy.py index 4b3e536f5a..062ef3f75d 100644 --- a/geoportal/tests/functional/test_mapserverproxy.py +++ b/geoportal/tests/functional/test_mapserverproxy.py @@ -1,4 +1,4 @@ -# Copyright (c) 2013-2023, Camptocamp SA +# Copyright (c) 2013-2024, Camptocamp SA # All rights reserved. # Redistribution and use in source and binary forms, with or without @@ -281,7 +281,7 @@ def test_get_legend_graphic(self): ) ) response = MapservProxy(request).proxy() - self.assertTrue(response.cache_control.public) + self.assertFalse(response.cache_control.public) assert response.cache_control.max_age == 3600 def test_getlegendgraphic_custom_nocache(self): @@ -362,10 +362,10 @@ def test_get_feature_info(self): re.sub(pattern, "", l) for l in response.body.decode("utf-8").splitlines() ).encode("utf-8") assert response_body.decode("utf-8") == expected_response - self.assertTrue(response.cache_control.public) + self.assertFalse(response.cache_control.public) assert response.cache_control.max_age == 10 self.assertEqual( - str(response.cache_control), "max-age=10, must-revalidate, no-cache, no-store, public" + str(response.cache_control), "max-age=10, must-revalidate, no-cache, no-store, private" ) def test_get_map_unprotected_layer_anonymous(self): @@ -389,7 +389,7 @@ def test_get_map_unprotected_layer_anonymous(self): self.assertTrue(response.status_int, 200) self.assertEqual( - str(response.cache_control), "max-age=10, must-revalidate, no-cache, no-store, public" + str(response.cache_control), "max-age=10, must-revalidate, no-cache, no-store, private" ) # 4 points md5sum = hashlib.md5(response.body).hexdigest() @@ -470,7 +470,7 @@ def test_get_map_protected_layer_anonymous(self): self.assertTrue(response.status_int, 200) self.assertEqual( - str(response.cache_control), "max-age=10, must-revalidate, no-cache, no-store, public" + str(response.cache_control), "max-age=10, must-revalidate, no-cache, no-store, private" ) # empty md5sum = hashlib.md5(response.body).hexdigest() @@ -900,7 +900,7 @@ def test_get_feature_wfs_url(self): self.assertTrue(response.body != "") self.assertEqual( - str(response.cache_control), "max-age=10, must-revalidate, no-cache, no-store, public" + str(response.cache_control), "max-age=10, must-revalidate, no-cache, no-store, private" ) def test_substitution(self): diff --git a/geoportal/tests/functional/test_oauth2.py b/geoportal/tests/functional/test_oauth2.py index 02d7ba8368..bc16d26ca6 100644 --- a/geoportal/tests/functional/test_oauth2.py +++ b/geoportal/tests/functional/test_oauth2.py @@ -1,4 +1,4 @@ -# Copyright (c) 2021-2023, Camptocamp SA +# Copyright (c) 2021-2024, Camptocamp SA # All rights reserved. # Redistribution and use in source and binary forms, with or without @@ -151,7 +151,7 @@ def test_oauth2_protocol_test_login_get_token_is_login(self) -> None: assert response.headers["Content-Type"] == "application/json" assert response.headers["Pragma"] == "no-cache" assert response.headers["Vary"] == "Origin, Cookie" - assert response.headers["Cache-Control"] == "max-age=10, no-store, public" + assert response.headers["Cache-Control"] == "max-age=10, no-store, private" data = json.loads(response.body) assert set(data.keys()) == {"access_token", "expires_in", "token_type", "refresh_token"} assert data["expires_in"] == 3600 @@ -264,7 +264,7 @@ def test_oauth2_protocol_test_login_get_token_refresh_token_is_login(self) -> No assert response.headers["Content-Type"] == "application/json" assert response.headers["Pragma"] == "no-cache" assert response.headers["Vary"] == "Origin, Cookie" - assert response.headers["Cache-Control"] == "max-age=10, no-store, public" + assert response.headers["Cache-Control"] == "max-age=10, no-store, private" data = json.loads(response.body) assert set(data.keys()) == {"access_token", "expires_in", "token_type", "refresh_token"} assert data["expires_in"] == 3600 @@ -345,7 +345,7 @@ def test_state_oauth2_protocol_test_login_get_token_refresh_token_is_login(self) assert response.headers["Content-Type"] == "application/json" assert response.headers["Pragma"] == "no-cache" assert response.headers["Vary"] == "Origin, Cookie" - assert response.headers["Cache-Control"] == "max-age=10, no-store, public" + assert response.headers["Cache-Control"] == "max-age=10, no-store, private" data = json.loads(response.body) assert set(data.keys()) == {"access_token", "expires_in", "token_type", "refresh_token"} assert data["expires_in"] == 3600 @@ -463,7 +463,7 @@ def test_oauth2_protocol_test_login_get_token_refresh_token_wrong_code(self) -> assert response.headers["Content-Type"] == "application/json" assert response.headers["Pragma"] == "no-cache" assert response.headers["Vary"] == "Origin, Cookie" - assert response.headers["Cache-Control"] == "max-age=10, no-store, public" + assert response.headers["Cache-Control"] == "max-age=10, no-store, private" data = json.loads(response.body) assert set(data.keys()) == {"access_token", "expires_in", "token_type", "refresh_token"} assert data["expires_in"] == 3600 @@ -592,7 +592,7 @@ def test_pkce_oauth2_protocol_test_login_get_token_refresh_token_is_login(self) assert response.headers["Content-Type"] == "application/json" assert response.headers["Pragma"] == "no-cache" assert response.headers["Vary"] == "Origin, Cookie" - assert response.headers["Cache-Control"] == "max-age=10, no-store, public" + assert response.headers["Cache-Control"] == "max-age=10, no-store, private" data = json.loads(response.body) assert set(data.keys()) == {"access_token", "expires_in", "token_type", "refresh_token"} assert data["expires_in"] == 3600 @@ -686,7 +686,7 @@ def test_pkce_state_oauth2_protocol_test_login_get_token_refresh_token_is_login( assert response.headers["Content-Type"] == "application/json" assert response.headers["Pragma"] == "no-cache" assert response.headers["Vary"] == "Origin, Cookie" - assert response.headers["Cache-Control"] == "max-age=10, no-store, public" + assert response.headers["Cache-Control"] == "max-age=10, no-store, private" data = json.loads(response.body) assert set(data.keys()) == {"access_token", "expires_in", "token_type", "refresh_token"} assert data["expires_in"] == 3600 diff --git a/geoportal/tests/test_caching.py b/geoportal/tests/test_caching.py index 8293f44c1f..d9d82779e6 100644 --- a/geoportal/tests/test_caching.py +++ b/geoportal/tests/test_caching.py @@ -1,4 +1,4 @@ -# Copyright (c) 2015-2023, Camptocamp SA +# Copyright (c) 2015-2024, Camptocamp SA # All rights reserved. # Redistribution and use in source and binary forms, with or without @@ -61,7 +61,7 @@ def test_simple(self): # 1. If the Origin header is not present terminate this set of steps. # The request is outside the scope of this specification. assert self._do("POST", {}) == { - "Cache-Control": "max-age=10, public", + "Cache-Control": "max-age=10, private", "Content-Length": "0", "Content-Type": "text/html; charset=UTF-8", "Vary": "Origin, Cookie", @@ -71,7 +71,7 @@ def test_simple(self): # any of the values in list of origins, do not set any additional # headers and terminate this set of steps. assert self._do("POST", {"Origin": "http://foe.com"}) == { - "Cache-Control": "max-age=10, public", + "Cache-Control": "max-age=10, private", "Content-Length": "0", "Content-Type": "text/html; charset=UTF-8", "Vary": "Origin, Cookie", @@ -82,7 +82,7 @@ def test_simple(self): # header as value, and add a single Access-Control-Allow-Credentials # header with the case-sensitive string "true" as value. assert self._do("POST", {"Origin": self.ORIGIN2}, credentials=True) == { - "Cache-Control": "max-age=10, public", + "Cache-Control": "max-age=10, private", "Content-Length": "0", "Content-Type": "text/html; charset=UTF-8", "Vary": "Origin, Cookie", @@ -202,7 +202,7 @@ def test_preflight(self): def test_not_configured(self): # If the service is not configured, then no CORS head. assert self._do("GET", {"Origin": self.ORIGIN1}, settings=None) == { - "Cache-Control": "max-age=10, public", + "Cache-Control": "max-age=10, private", "Content-Length": "0", "Content-Type": "text/html; charset=UTF-8", "Vary": "Origin, Cookie", @@ -217,7 +217,7 @@ def test_match_all(self): # An origin included in the access_control_allow_origin list is OK with # credentials assert self._do("POST", {"Origin": self.ORIGIN1}, credentials=True, settings=settings) == { - "Cache-Control": "max-age=10, public", + "Cache-Control": "max-age=10, private", "Content-Length": "0", "Content-Type": "text/html; charset=UTF-8", "Vary": "Origin, Cookie", @@ -230,7 +230,7 @@ def test_match_all(self): # 3. Otherwise, add a single Access-Control-Allow-Origin header, with # either the value of the Origin header or the string "*" as value. assert self._do("POST", {"Origin": "http://www.guest.com"}, settings=settings) == { - "Cache-Control": "max-age=10, public", + "Cache-Control": "max-age=10, private", "Content-Length": "0", "Content-Type": "text/html; charset=UTF-8", "Vary": "Origin, Cookie",