asnannp commited on
Commit
75ce93d
·
1 Parent(s): fba198b

deploy: sync backend to Space root (learn-lesson HF cache fix)

Browse files
app/routes/auth.py CHANGED
@@ -269,14 +269,14 @@ def _auth_redirect_for_user(user: User, next_path: str) -> RedirectResponse:
269
  def _is_production_environment(settings: object | None = None) -> bool:
270
  """Resolve production even if an older Settings build lacks is_production."""
271
  active = settings or get_settings()
272
- try:
273
- flag = getattr(active, "is_production", None)
274
- except AttributeError:
275
- flag = None
276
  if isinstance(flag, bool):
277
  production = flag
278
  else:
279
- production = str(getattr(active, "environment", "") or "").strip().casefold() == "production"
 
 
 
280
  logger.debug("[FIX] media-auth cookie production=%s", production)
281
  return production
282
 
 
269
  def _is_production_environment(settings: object | None = None) -> bool:
270
  """Resolve production even if an older Settings build lacks is_production."""
271
  active = settings or get_settings()
272
+ flag = getattr(active, "is_production", None)
 
 
 
273
  if isinstance(flag, bool):
274
  production = flag
275
  else:
276
+ environment = str(getattr(active, "environment", "") or "").strip().casefold()
277
+ # Unknown environment fails closed so a missing flag cannot mint a
278
+ # non-Secure cookie that browsers would still send.
279
+ production = True if not environment else environment == "production"
280
  logger.debug("[FIX] media-auth cookie production=%s", production)
281
  return production
282
 
tests/test_media_auth_cookie.py CHANGED
@@ -11,6 +11,25 @@ from types import SimpleNamespace
11
  from fastapi import Response
12
 
13
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
14
  def test_settings_exposes_is_production_from_environment() -> None:
15
  from app.core.config import Settings
16
 
@@ -20,7 +39,7 @@ def test_settings_exposes_is_production_from_environment() -> None:
20
  assert Settings(environment=" staging ").is_production is False
21
 
22
 
23
- def test_signup_sets_media_cookie_without_crashing(auth_client) -> None:
24
  response = auth_client.post(
25
  "/auth/signup",
26
  json={
@@ -31,12 +50,35 @@ def test_signup_sets_media_cookie_without_crashing(auth_client) -> None:
31
  )
32
  assert response.status_code == 201, response.text
33
  assert response.json()["access_token"]
34
- cookie = response.headers.get("set-cookie", "")
35
- assert "docdoe_media_token=" in cookie
36
- assert "secure" not in cookie.lower()
 
 
 
37
 
38
 
39
- def test_media_cookie_is_secure_when_environment_is_production() -> None:
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
40
  from app.routes.auth import _set_media_auth_cookie
41
 
42
  response = Response()
@@ -46,7 +88,25 @@ def test_media_cookie_is_secure_when_environment_is_production() -> None:
46
  3600,
47
  settings=SimpleNamespace(environment="production"),
48
  )
49
- cookie = response.headers.get("set-cookie", "")
50
- assert "docdoe_media_token=token-value" in cookie
51
- assert "samesite=none" in cookie.lower()
52
- assert "secure" in cookie.lower()
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
11
  from fastapi import Response
12
 
13
 
14
+ def _media_cookie_flags(header: str) -> dict[str, object]:
15
+ """Parse Set-Cookie attributes so JWT contents cannot fake flags."""
16
+ parts = [part.strip() for part in header.split(";") if part.strip()]
17
+ assert parts, f"empty Set-Cookie header: {header!r}"
18
+ name, _, value = parts[0].partition("=")
19
+ flags: dict[str, str | bool] = {}
20
+ for part in parts[1:]:
21
+ key, separator, raw = part.partition("=")
22
+ flags[key.casefold()] = raw if separator else True
23
+ return {
24
+ "name": name,
25
+ "value": value,
26
+ "httponly": "httponly" in flags,
27
+ "secure": "secure" in flags,
28
+ "path": str(flags.get("path", "")),
29
+ "samesite": str(flags.get("samesite", "")).casefold(),
30
+ }
31
+
32
+
33
  def test_settings_exposes_is_production_from_environment() -> None:
34
  from app.core.config import Settings
35
 
 
39
  assert Settings(environment=" staging ").is_production is False
40
 
41
 
42
+ def test_signup_sets_dev_media_cookie_flags(auth_client) -> None:
43
  response = auth_client.post(
44
  "/auth/signup",
45
  json={
 
50
  )
51
  assert response.status_code == 201, response.text
52
  assert response.json()["access_token"]
53
+ cookie = _media_cookie_flags(response.headers.get("set-cookie", ""))
54
+ assert cookie["name"] == "docdoe_media_token"
55
+ assert cookie["httponly"] is True
56
+ assert cookie["secure"] is False
57
+ assert cookie["path"] == "/generated"
58
+ assert cookie["samesite"] == "lax"
59
 
60
 
61
+ def test_media_cookie_uses_settings_is_production() -> None:
62
+ from app.core.config import Settings
63
+ from app.routes.auth import _set_media_auth_cookie
64
+
65
+ response = Response()
66
+ _set_media_auth_cookie(
67
+ response,
68
+ "token-value",
69
+ 3600,
70
+ settings=Settings(environment="production"),
71
+ )
72
+ cookie = _media_cookie_flags(response.headers.get("set-cookie", ""))
73
+ assert cookie["name"] == "docdoe_media_token"
74
+ assert cookie["value"] == "token-value"
75
+ assert cookie["httponly"] is True
76
+ assert cookie["secure"] is True
77
+ assert cookie["path"] == "/generated"
78
+ assert cookie["samesite"] == "none"
79
+
80
+
81
+ def test_media_cookie_falls_back_to_environment_when_flag_missing() -> None:
82
  from app.routes.auth import _set_media_auth_cookie
83
 
84
  response = Response()
 
88
  3600,
89
  settings=SimpleNamespace(environment="production"),
90
  )
91
+ cookie = _media_cookie_flags(response.headers.get("set-cookie", ""))
92
+ assert cookie["httponly"] is True
93
+ assert cookie["secure"] is True
94
+ assert cookie["path"] == "/generated"
95
+ assert cookie["samesite"] == "none"
96
+
97
+
98
+ def test_unknown_environment_fails_closed_to_secure_cookie() -> None:
99
+ from app.routes.auth import _set_media_auth_cookie
100
+
101
+ response = Response()
102
+ _set_media_auth_cookie(
103
+ response,
104
+ "token-value",
105
+ 3600,
106
+ settings=SimpleNamespace(),
107
+ )
108
+ cookie = _media_cookie_flags(response.headers.get("set-cookie", ""))
109
+ assert cookie["secure"] is True
110
+ assert cookie["samesite"] == "none"
111
+ assert cookie["httponly"] is True
112
+ assert cookie["path"] == "/generated"