diff --git a/geoportal/c2cgeoportal_geoportal/__init__.py b/geoportal/c2cgeoportal_geoportal/__init__.py index 46a44c6194..fa3d824f07 100644 --- a/geoportal/c2cgeoportal_geoportal/__init__.py +++ b/geoportal/c2cgeoportal_geoportal/__init__.py @@ -738,6 +738,7 @@ def add_static_route(name: str, attr: str, path: str, renderer: str) -> None: c2cgeoportal_geoportal.views.add_redirect(config, "apihelp_redirect", "/apihelp.html", "apihelp.html") config.add_route("themes", "/themes", request_method="GET", pregenerator=C2CPregenerator(role=True)) + add_cors_route(config, "/themes", "themes") config.add_route("invalidate", "/invalidate", request_method="GET") diff --git a/geoportal/c2cgeoportal_geoportal/lib/common_headers.py b/geoportal/c2cgeoportal_geoportal/lib/common_headers.py index 5fabba2937..c80a59c271 100644 --- a/geoportal/c2cgeoportal_geoportal/lib/common_headers.py +++ b/geoportal/c2cgeoportal_geoportal/lib/common_headers.py @@ -63,7 +63,12 @@ def _set_cors_headers( credentials: bool, ) -> None: """Handle CORS requests, as specified in https://www.w3.org/TR/cors/.""" - response.vary = (response.vary or ()) + ("Origin",) + response.vary = ( + *(response.vary or ()), + "Origin", + "Access-Control-Request-Headers", + *(("Access-Control-Request-Method",) if request.method == "OPTIONS" else ()), + ) if "Origin" not in request.headers: return # Not a CORS request if this header is missing diff --git a/geoportal/tests/functional/test_login.py b/geoportal/tests/functional/test_login.py index 516ee12fab..597cfbd7ae 100644 --- a/geoportal/tests/functional/test_login.py +++ b/geoportal/tests/functional/test_login.py @@ -273,8 +273,6 @@ def test_change_password_good_is_password_changed(self): assert user._password == crypt.crypt("1234", user._password) def test_login_0(self): - from tests import DummyRequest - from c2cgeoportal_geoportal.views.login import Login request = self._create_request_obj() @@ -360,7 +358,7 @@ class F: "functionalities": {"func": ["value"]}, } assert login.loginuser() == expected - assert request.response.headers["Vary"] == "Origin, Cookie" + assert request.response.headers["Vary"] == "Origin, Access-Control-Request-Headers, Cookie" def test_intranet(self): from tests import DummyRequest diff --git a/geoportal/tests/functional/test_oauth2.py b/geoportal/tests/functional/test_oauth2.py index 48d0a61bdf..145de44097 100644 --- a/geoportal/tests/functional/test_oauth2.py +++ b/geoportal/tests/functional/test_oauth2.py @@ -152,7 +152,7 @@ def test_oauth2_protocol_test_login_get_token_is_login(self) -> None: response = Login(request).oauth2token() assert response.headers["Content-Type"] == "application/json" assert response.headers["Pragma"] == "no-cache" - assert response.headers["Vary"] == "Origin, Cookie" + assert response.headers["Vary"] == "Origin, Access-Control-Request-Headers, Cookie" assert response.headers["Cache-Control"] == "max-age=10, no-store, public" data = json.loads(response.body) assert set(data.keys()) == {"access_token", "expires_in", "token_type", "refresh_token"} @@ -271,7 +271,7 @@ def test_oauth2_protocol_test_login_get_token_refresh_token_is_login(self) -> No response = Login(request).oauth2token() assert response.headers["Content-Type"] == "application/json" assert response.headers["Pragma"] == "no-cache" - assert response.headers["Vary"] == "Origin, Cookie" + assert response.headers["Vary"] == "Origin, Access-Control-Request-Headers, Cookie" assert response.headers["Cache-Control"] == "max-age=10, no-store, public" data = json.loads(response.body) assert set(data.keys()) == {"access_token", "expires_in", "token_type", "refresh_token"} @@ -355,7 +355,7 @@ def test_state_oauth2_protocol_test_login_get_token_refresh_token_is_login(self) response = Login(request).oauth2token() assert response.headers["Content-Type"] == "application/json" assert response.headers["Pragma"] == "no-cache" - assert response.headers["Vary"] == "Origin, Cookie" + assert response.headers["Vary"] == "Origin, Access-Control-Request-Headers, Cookie" assert response.headers["Cache-Control"] == "max-age=10, no-store, public" data = json.loads(response.body) assert set(data.keys()) == {"access_token", "expires_in", "token_type", "refresh_token"} @@ -478,7 +478,7 @@ def test_oauth2_protocol_test_login_get_token_refresh_token_wrong_code(self) -> response = Login(request).oauth2token() assert response.headers["Content-Type"] == "application/json" assert response.headers["Pragma"] == "no-cache" - assert response.headers["Vary"] == "Origin, Cookie" + assert response.headers["Vary"] == "Origin, Access-Control-Request-Headers, Cookie" assert response.headers["Cache-Control"] == "max-age=10, no-store, public" data = json.loads(response.body) assert set(data.keys()) == {"access_token", "expires_in", "token_type", "refresh_token"} @@ -611,7 +611,7 @@ def test_pkce_oauth2_protocol_test_login_get_token_refresh_token_is_login(self) response = Login(request).oauth2token() assert response.headers["Content-Type"] == "application/json" assert response.headers["Pragma"] == "no-cache" - assert response.headers["Vary"] == "Origin, Cookie" + assert response.headers["Vary"] == "Origin, Access-Control-Request-Headers, Cookie" assert response.headers["Cache-Control"] == "max-age=10, no-store, public" data = json.loads(response.body) assert set(data.keys()) == {"access_token", "expires_in", "token_type", "refresh_token"} @@ -708,7 +708,7 @@ def test_pkce_state_oauth2_protocol_test_login_get_token_refresh_token_is_login( response = Login(request).oauth2token() assert response.headers["Content-Type"] == "application/json" assert response.headers["Pragma"] == "no-cache" - assert response.headers["Vary"] == "Origin, Cookie" + assert response.headers["Vary"] == "Origin, Access-Control-Request-Headers, Cookie" assert response.headers["Cache-Control"] == "max-age=10, no-store, public" data = json.loads(response.body) assert set(data.keys()) == {"access_token", "expires_in", "token_type", "refresh_token"} diff --git a/geoportal/tests/test_caching.py b/geoportal/tests/test_caching.py index 8293f44c1f..2e5b5a5ae7 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 @@ -64,7 +64,7 @@ def test_simple(self): "Cache-Control": "max-age=10, public", "Content-Length": "0", "Content-Type": "text/html; charset=UTF-8", - "Vary": "Origin, Cookie", + "Vary": "Origin, Access-Control-Request-Headers, Cookie", } # 2. If the value of the Origin header is not a case-sensitive match for @@ -74,7 +74,7 @@ def test_simple(self): "Cache-Control": "max-age=10, public", "Content-Length": "0", "Content-Type": "text/html; charset=UTF-8", - "Vary": "Origin, Cookie", + "Vary": "Origin, Access-Control-Request-Headers, Cookie", } # 3. If the resource supports credentials add a single @@ -85,7 +85,7 @@ def test_simple(self): "Cache-Control": "max-age=10, public", "Content-Length": "0", "Content-Type": "text/html; charset=UTF-8", - "Vary": "Origin, Cookie", + "Vary": "Origin, Access-Control-Request-Headers, Cookie", "Access-Control-Max-Age": self.MAX_AGE, "Access-Control-Allow-Origin": self.ORIGIN2, "Access-Control-Allow-Methods": CORS_METHODS, @@ -110,7 +110,7 @@ def test_preflight(self): assert self._do("OPTIONS", {"Access-Control-Request-Method": "GET"}) == { "Content-Length": "0", "Content-Type": "text/html; charset=UTF-8", - "Vary": "Origin", + "Vary": "Origin, Access-Control-Request-Headers, Access-Control-Request-Method", } # 2. If the value of the Origin header is not a case-sensitive match for @@ -119,7 +119,7 @@ def test_preflight(self): assert self._do("OPTIONS", {"Origin": "http://foe.com", "Access-Control-Request-Method": "GET"}) == { "Content-Length": "0", "Content-Type": "text/html; charset=UTF-8", - "Vary": "Origin", + "Vary": "Origin, Access-Control-Request-Headers, Access-Control-Request-Method", } # 3. If there is no Access-Control-Request-Method header or if parsing @@ -128,7 +128,7 @@ def test_preflight(self): assert self._do("OPTIONS", {"Origin": self.ORIGIN1}) == { "Content-Length": "0", "Content-Type": "text/html; charset=UTF-8", - "Vary": "Origin", + "Vary": "Origin, Access-Control-Request-Headers, Access-Control-Request-Method", } # 4. If there are no Access-Control-Request-Headers headers let header @@ -136,7 +136,7 @@ def test_preflight(self): assert self._do("OPTIONS", {"Origin": self.ORIGIN1, "Access-Control-Request-Method": "GET"}) == { "Content-Length": "0", "Content-Type": "text/html; charset=UTF-8", - "Vary": "Origin", + "Vary": "Origin, Access-Control-Request-Headers, Access-Control-Request-Method", "Access-Control-Allow-Origin": self.ORIGIN1, "Access-Control-Max-Age": self.MAX_AGE, "Access-Control-Allow-Methods": CORS_METHODS, @@ -162,7 +162,7 @@ def test_preflight(self): ) == { "Content-Length": "0", "Content-Type": "text/html; charset=UTF-8", - "Vary": "Origin", + "Vary": "Origin, Access-Control-Request-Headers, Access-Control-Request-Method", "Access-Control-Allow-Origin": self.ORIGIN1, "Access-Control-Allow-Credentials": "true", "Access-Control-Max-Age": self.MAX_AGE, @@ -191,7 +191,7 @@ def test_preflight(self): ) == { "Content-Length": "0", "Content-Type": "text/html; charset=UTF-8", - "Vary": "Origin", + "Vary": "Origin, Access-Control-Request-Headers, Access-Control-Request-Method", "Access-Control-Allow-Origin": self.ORIGIN1, "Access-Control-Max-Age": self.MAX_AGE, "Access-Control-Allow-Methods": CORS_METHODS, @@ -205,7 +205,7 @@ def test_not_configured(self): "Cache-Control": "max-age=10, public", "Content-Length": "0", "Content-Type": "text/html; charset=UTF-8", - "Vary": "Origin, Cookie", + "Vary": "Origin, Access-Control-Request-Headers, Cookie", } def test_match_all(self): @@ -220,7 +220,7 @@ def test_match_all(self): "Cache-Control": "max-age=10, public", "Content-Length": "0", "Content-Type": "text/html; charset=UTF-8", - "Vary": "Origin, Cookie", + "Vary": "Origin, Access-Control-Request-Headers, Cookie", "Access-Control-Max-Age": self.MAX_AGE, "Access-Control-Allow-Origin": self.ORIGIN1, "Access-Control-Allow-Methods": CORS_METHODS, @@ -233,7 +233,7 @@ def test_match_all(self): "Cache-Control": "max-age=10, public", "Content-Length": "0", "Content-Type": "text/html; charset=UTF-8", - "Vary": "Origin, Cookie", + "Vary": "Origin, Access-Control-Request-Headers, Cookie", "Access-Control-Max-Age": self.MAX_AGE, "Access-Control-Allow-Origin": "*", "Access-Control-Allow-Methods": CORS_METHODS, @@ -252,7 +252,7 @@ def test_match_all(self): ) == { "Content-Length": "0", "Content-Type": "text/html; charset=UTF-8", - "Vary": "Origin", + "Vary": "Origin, Access-Control-Request-Headers, Access-Control-Request-Method", "Access-Control-Allow-Origin": "*", "Access-Control-Max-Age": self.MAX_AGE, "Access-Control-Allow-Methods": CORS_METHODS,