fix(dashboard): address persistent auth review
This commit is contained in:
@@ -176,6 +176,35 @@ def test_logout_clears_cookie_with_matching_scope(client: TestClient) -> None:
|
||||
assert "Path=/" in header
|
||||
|
||||
|
||||
def test_cleanup_route_clears_cookie_and_redirects(client: TestClient) -> None:
|
||||
response = client.get(
|
||||
"/api/auth/cleanup",
|
||||
headers={"referer": "https://testserver/"},
|
||||
follow_redirects=False,
|
||||
)
|
||||
|
||||
assert response.status_code == 303
|
||||
assert response.headers["location"] == "/"
|
||||
header = response.headers["set-cookie"]
|
||||
assert f"{authentication.AUTH_COOKIE_NAME}=" in header
|
||||
assert "Max-Age=0" in header
|
||||
assert "HttpOnly" in header
|
||||
assert "Secure" in header
|
||||
assert "SameSite=lax" in header
|
||||
assert "Path=/" in header
|
||||
|
||||
|
||||
def test_cleanup_route_rejects_cross_origin_navigation(client: TestClient) -> None:
|
||||
response = client.get(
|
||||
"/api/auth/cleanup",
|
||||
headers={"referer": "https://attacker.example/"},
|
||||
follow_redirects=False,
|
||||
)
|
||||
|
||||
assert response.status_code == 403
|
||||
assert "set-cookie" not in response.headers
|
||||
|
||||
|
||||
def test_cookie_token_round_trip(client: TestClient) -> None:
|
||||
response = client.post(
|
||||
"/api/auth/login",
|
||||
|
||||
@@ -108,6 +108,13 @@ def test_invalid_cookie_blocks_database_and_page_dispatch(monkeypatch):
|
||||
assert not at.exception
|
||||
assert "관리자 로그인" in _body_text(at)
|
||||
assert "이번 달 수입/지출 현황" not in _body_text(at)
|
||||
assert "/api/auth/cleanup" in _body_text(at)
|
||||
|
||||
|
||||
def test_no_cookie_does_not_render_cookie_cleanup_navigation(monkeypatch):
|
||||
at = _app_with_cookie(monkeypatch, None)
|
||||
|
||||
assert "/api/auth/cleanup" not in _body_text(at)
|
||||
|
||||
|
||||
def test_valid_cookie_allows_dashboard_and_is_not_stored_in_session_state(
|
||||
@@ -153,3 +160,24 @@ def test_password_rotation_invalidates_existing_cookie(monkeypatch, tmp_path):
|
||||
|
||||
assert "관리자 로그인" in _body_text(at)
|
||||
assert "이번 달 수입/지출 현황" not in _body_text(at)
|
||||
|
||||
|
||||
def test_signing_secret_rotation_invalidates_existing_cookie(monkeypatch, tmp_path):
|
||||
from scripts.ci_fixture_db import create_ci_fixture_db
|
||||
|
||||
fixture = tmp_path / "fixture.sqlite3"
|
||||
create_ci_fixture_db(fixture)
|
||||
monkeypatch.setenv("JARCA_LEDGER_DB_PATH", str(fixture))
|
||||
settings = _configure(monkeypatch)
|
||||
token = authentication.issue_persistent_token(settings, remember_me=True).value
|
||||
monkeypatch.setattr(authentication, "_request_cookie", lambda: token)
|
||||
|
||||
at = AppTest.from_file(str(APP_PATH), default_timeout=30).run()
|
||||
assert "이번 달 수입/지출 현황" in _body_text(at)
|
||||
|
||||
monkeypatch.setenv(authentication.SIGNING_SECRET_ENV, "r" * 64)
|
||||
at = at.run()
|
||||
|
||||
assert "관리자 로그인" in _body_text(at)
|
||||
assert "/api/auth/cleanup" in _body_text(at)
|
||||
assert "이번 달 수입/지출 현황" not in _body_text(at)
|
||||
|
||||
@@ -150,14 +150,30 @@ def test_oversized_and_malformed_tokens_are_rejected() -> None:
|
||||
|
||||
|
||||
def test_payload_does_not_encode_secrets() -> None:
|
||||
settings = replace(_settings(), admin_id="configured-admin-identity")
|
||||
issued = authentication.issue_persistent_token(
|
||||
_settings(),
|
||||
settings,
|
||||
remember_me=False,
|
||||
now=1_700_000_000,
|
||||
token_id="fixed-token-id",
|
||||
)
|
||||
payload = authentication._serializer_for(_settings()).loads(issued.value)
|
||||
payload = authentication._serializer_for(settings).loads(issued.value)
|
||||
|
||||
assert set(payload) == {"v", "sub", "iat", "exp", "jti"}
|
||||
assert "password" not in json.dumps(payload).lower()
|
||||
assert "secret" not in json.dumps(payload).lower()
|
||||
assert settings.admin_id not in json.dumps(payload)
|
||||
|
||||
|
||||
def test_remember_me_token_expires_at_exact_30_day_boundary() -> None:
|
||||
settings = _settings()
|
||||
issued = authentication.issue_persistent_token(
|
||||
settings, remember_me=True, now=1_700_000_000, token_id="boundary-token-id"
|
||||
)
|
||||
|
||||
assert authentication.verify_persistent_token(
|
||||
settings, issued.value, now=issued.expires_at - 1
|
||||
) is not None
|
||||
assert authentication.verify_persistent_token(
|
||||
settings, issued.value, now=issued.expires_at
|
||||
) is None
|
||||
|
||||
Reference in New Issue
Block a user