Add whole-app password protection

This commit is contained in:
2026-08-03 00:06:07 +03:30
parent 4eb9d7fb37
commit f5f54bac66
7 changed files with 178 additions and 6 deletions
+67 -1
View File
@@ -1,16 +1,37 @@
from __future__ import annotations
import base64
import time
from pathlib import Path
from typing import Any
from fastapi.testclient import TestClient
import pytest
from fastapi.testclient import TestClient as RawTestClient
from resume_agent.agent import save_json
from resume_agent.models import CareerProfile, TailoringPackage
from resume_agent.render import render_ohmycv_resume
from resume_agent.webapp import create_app
TEST_USERNAME = "resume-user"
TEST_PASSWORD = "test-password"
TEST_AUTHORIZATION = "Basic " + base64.b64encode(
f"{TEST_USERNAME}:{TEST_PASSWORD}".encode()
).decode()
TEST_AUTH_HEADERS = {"Authorization": TEST_AUTHORIZATION}
class TestClient(RawTestClient):
def __init__(self, app: Any, **kwargs: Any) -> None:
headers = {**TEST_AUTH_HEADERS, **kwargs.pop("headers", {})}
super().__init__(app, headers=headers, **kwargs)
@pytest.fixture(autouse=True)
def configure_authentication(monkeypatch: Any) -> None:
monkeypatch.setenv("RESUME_AGENT_USERNAME", TEST_USERNAME)
monkeypatch.setenv("RESUME_AGENT_PASSWORD", TEST_PASSWORD)
def sample_profile() -> CareerProfile:
return CareerProfile.model_validate(
@@ -98,6 +119,51 @@ def wait_for_task(client: TestClient, task_id: str) -> dict[str, Any]:
raise AssertionError("Background task did not finish in time.")
def test_authentication_protects_ui_api_static_and_editor(tmp_path: Path) -> None:
cv_dist = tmp_path / "cv"
cv_dist.mkdir()
(cv_dist / "index.html").write_text("editor", encoding="utf-8")
(cv_dist / "200.html").write_text("editor", encoding="utf-8")
app = create_app(
profile_path=tmp_path / "profile.json",
output_dir=tmp_path / "output",
cv_dist=cv_dist,
llm_factory=lambda: FakeLLM([]), # type: ignore[arg-type]
)
with RawTestClient(app) as client:
assert client.get("/healthz").status_code == 200
for path in ("/", "/api/status", "/favicon.ico", "/cv/"):
response = client.get(path)
assert response.status_code == 401
assert response.headers["www-authenticate"].startswith("Basic ")
wrong = client.get(
"/api/status",
headers={"Authorization": "Basic " + base64.b64encode(b"wrong:wrong").decode()},
)
assert wrong.status_code == 401
assert client.get("/", headers=TEST_AUTH_HEADERS).status_code == 200
assert client.get("/api/status", headers=TEST_AUTH_HEADERS).status_code == 200
assert client.get("/favicon.ico", headers=TEST_AUTH_HEADERS).status_code == 200
assert client.get("/cv/", headers=TEST_AUTH_HEADERS).status_code == 200
def test_missing_authentication_configuration_fails_closed(
tmp_path: Path, monkeypatch: Any
) -> None:
monkeypatch.delenv("RESUME_AGENT_USERNAME")
monkeypatch.delenv("RESUME_AGENT_PASSWORD")
app = create_app(
profile_path=tmp_path / "profile.json",
output_dir=tmp_path / "output",
llm_factory=lambda: FakeLLM([]), # type: ignore[arg-type]
)
with RawTestClient(app) as client:
assert client.get("/").status_code == 503
assert client.get("/healthz").status_code == 503
def test_root_and_status(tmp_path: Path, monkeypatch: Any) -> None:
monkeypatch.delenv("RESUME_AGENT_API_KEY", raising=False)
monkeypatch.delenv("OPENAI_API_KEY", raising=False)