mirror of
https://github.com/kennethreitz/responder.git
synced 2026-07-21 18:39:29 +00:00
ca1c259ff9
- secret_key default is now None, not the public "NOTASECRET" (which now
raises). sessions="auto" (default) signs with secret_key /
RESPONDER_SECRET_KEY, else mints a random per-process key with a loud
warning; sessions=True requires a real key; sessions=False disables sessions.
- Session cookies are Secure by default in production (session_https_only=None
-> Secure unless debug); SameSite=None without Secure is rejected. New
session_max_age knob.
- resolve_secret_key() + SessionConfigError in ext/sessions.py.
- req.session raises a guiding RuntimeError when sessions are disabled;
resp.session is now a delegating property (removed from __slots__ + the
eager bind), so resp.session = {...} writes through to the cookie.
- Migrated test fixtures (conftest + make_api + test_round7) to
session_https_only=False so the http TestClient round-trips cookies.
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
71 lines
2.0 KiB
Python
71 lines
2.0 KiB
Python
"""v5: secure-by-default session configuration."""
|
|
|
|
import pytest
|
|
from starlette.testclient import TestClient
|
|
|
|
import responder
|
|
|
|
KEY = "a-real-private-secret-key-32chars!"
|
|
|
|
|
|
def _client(api):
|
|
return TestClient(api, base_url="http://;")
|
|
|
|
|
|
def test_secure_flag_set_in_production():
|
|
# debug=False + session_https_only default None => Secure cookie.
|
|
api = responder.API(allowed_hosts=[";"], secret_key=KEY)
|
|
|
|
@api.route("/", methods=["POST"])
|
|
def view(req, resp):
|
|
req.session["k"] = "v"
|
|
resp.text = "ok"
|
|
|
|
r = _client(api).post("/")
|
|
assert "secure" in r.headers.get("set-cookie", "").lower()
|
|
|
|
|
|
def test_secure_flag_absent_when_disabled():
|
|
api = responder.API(allowed_hosts=[";"], secret_key=KEY, session_https_only=False)
|
|
|
|
@api.route("/", methods=["POST"])
|
|
def view(req, resp):
|
|
req.session["k"] = "v"
|
|
resp.text = "ok"
|
|
|
|
r = _client(api).post("/")
|
|
assert "secure" not in r.headers.get("set-cookie", "").lower()
|
|
|
|
|
|
def test_samesite_none_without_secure_raises():
|
|
with pytest.raises(ValueError, match="SameSite=None"):
|
|
responder.API(
|
|
allowed_hosts=[";"],
|
|
secret_key=KEY,
|
|
session_same_site="none",
|
|
session_https_only=False,
|
|
)
|
|
|
|
|
|
def test_sessions_true_without_key_raises(monkeypatch):
|
|
monkeypatch.delenv("RESPONDER_SECRET_KEY", raising=False)
|
|
from responder.ext.sessions import SessionConfigError
|
|
|
|
with pytest.raises((SessionConfigError, ValueError)):
|
|
responder.API(allowed_hosts=[";"], sessions=True)
|
|
|
|
|
|
def test_secret_key_from_env(monkeypatch):
|
|
monkeypatch.setenv("RESPONDER_SECRET_KEY", KEY)
|
|
api = responder.API(allowed_hosts=[";"], session_https_only=False)
|
|
assert api.secret_key == KEY
|
|
|
|
|
|
def test_session_backend_with_sessions_false_raises():
|
|
from responder.ext.sessions import MemorySessionBackend
|
|
|
|
with pytest.raises(ValueError, match="sessions=False"):
|
|
responder.API(
|
|
allowed_hosts=[";"], sessions=False, session_backend=MemorySessionBackend()
|
|
)
|