189 lines
8.4 KiB
Markdown
189 lines
8.4 KiB
Markdown
---
|
|
phase: "07.4"
|
|
plan: "01"
|
|
type: execute
|
|
wave: 0
|
|
depends_on: []
|
|
files_modified:
|
|
- backend/tests/test_auth_fgp.py
|
|
autonomous: true
|
|
requirements:
|
|
- FGP-CONCERN # tracked in .planning/codebase/CONCERNS.md §"No Token Fingerprint / Token Binding"
|
|
|
|
must_haves:
|
|
truths:
|
|
- "test_auth_fgp.py exists with exactly 4 test functions"
|
|
- "All 4 tests are decorated with @pytest.mark.xfail(strict=False, reason='not implemented yet')"
|
|
- "pytest -v reports all 4 as XFAIL — no failures, no errors"
|
|
- "No production source files are modified"
|
|
artifacts:
|
|
- path: "backend/tests/test_auth_fgp.py"
|
|
provides: "Wave 0 xfail stubs for FGP-01..FGP-04"
|
|
contains: "test_fgp_match_returns_200"
|
|
key_links:
|
|
- from: "backend/tests/test_auth_fgp.py"
|
|
to: "backend/tests/test_auth_deps.py"
|
|
via: "imports FakeRedis, copies make_test_app / auth_client / _create_user patterns"
|
|
pattern: "from tests.test_auth_api import FakeRedis"
|
|
---
|
|
|
|
<objective>
|
|
Create the Wave 0 test scaffold for Phase 7.4: a new test file `backend/tests/test_auth_fgp.py` containing 4 xfail stubs covering the four fingerprint behaviours (FGP-01..FGP-04). No production code is touched in this plan.
|
|
|
|
Purpose: Establishes the Nyquist test harness before any production code changes. Follows the xfail(strict=False) Wave 0 convention established in Phases 7.2 and 7.3.
|
|
Output: `backend/tests/test_auth_fgp.py` with 4 stubs; full test suite still passes with zero failures.
|
|
</objective>
|
|
|
|
<execution_context>
|
|
@$HOME/.claude/get-shit-done/workflows/execute-plan.md
|
|
@$HOME/.claude/get-shit-done/templates/summary.md
|
|
</execution_context>
|
|
|
|
<context>
|
|
@.planning/PROJECT.md
|
|
@.planning/ROADMAP.md
|
|
@.planning/STATE.md
|
|
@.planning/phases/07.4-security-token-fingerprinting-token-binding-inserted/07.4-CONTEXT.md
|
|
@.planning/phases/07.4-security-token-fingerprinting-token-binding-inserted/07.4-RESEARCH.md
|
|
@.planning/phases/07.4-security-token-fingerprinting-token-binding-inserted/07.4-PATTERNS.md
|
|
|
|
<interfaces>
|
|
<!-- Key patterns the executor needs. Extracted from codebase. -->
|
|
|
|
From backend/tests/test_auth_deps.py (full harness to copy):
|
|
|
|
Imports:
|
|
import uuid
|
|
import pytest
|
|
import pytest_asyncio
|
|
from httpx import ASGITransport, AsyncClient
|
|
from fastapi import FastAPI, Depends
|
|
from sqlalchemy.ext.asyncio import AsyncSession
|
|
from tests.test_auth_api import FakeRedis
|
|
|
|
make_test_app():
|
|
Creates minimal FastAPI app with /test/me route wired to get_current_user.
|
|
Sets app.state.redis = FakeRedis() so NBF check and fgp check can access request.app.state.redis.
|
|
|
|
auth_client fixture:
|
|
@pytest_asyncio.fixture
|
|
async def auth_client(db_session: AsyncSession):
|
|
app = make_test_app()
|
|
app.dependency_overrides[get_db] = lambda: db_session
|
|
async with AsyncClient(transport=ASGITransport(app=app), base_url="http://test") as c:
|
|
yield c
|
|
app.dependency_overrides.clear()
|
|
|
|
_create_user helper (no decorator — plain async function):
|
|
Inserts minimal User row; returns User ORM object.
|
|
Fields: id=uuid.uuid4(), handle, email, password_hash, role, is_active=True
|
|
|
|
Test function pattern:
|
|
@pytest.mark.asyncio
|
|
async def test_name(auth_client, db_session):
|
|
from services.auth import create_access_token
|
|
user = await _create_user(db_session)
|
|
token = create_access_token(str(user.id), "user")
|
|
resp = await auth_client.get("/test/me", headers={"Authorization": f"Bearer {token}"})
|
|
assert resp.status_code == 200
|
|
|
|
conftest _patch_es256_test_keys is autouse=True at session scope — no explicit reference needed in this file.
|
|
|
|
From backend/tests/test_auth_es256.py (xfail stub pattern for Phase 7.3):
|
|
@pytest.mark.xfail(strict=False, reason="not implemented yet")
|
|
@pytest.mark.asyncio
|
|
async def test_stub_name(...):
|
|
pytest.xfail("not implemented yet")
|
|
</interfaces>
|
|
</context>
|
|
|
|
<tasks>
|
|
|
|
<task type="auto" tdd="true">
|
|
<name>Task 1: Create test_auth_fgp.py with 4 xfail stubs (FGP-01..FGP-04)</name>
|
|
<files>backend/tests/test_auth_fgp.py</files>
|
|
<read_first>
|
|
- backend/tests/test_auth_deps.py — copy the full harness (make_test_app, auth_client fixture, _create_user helper, imports block). This is the structural template; copy verbatim then modify test functions only.
|
|
- backend/tests/test_auth_es256.py — xfail stub decoration pattern used in Phase 7.3. Stubs use @pytest.mark.xfail(strict=False, reason="not implemented yet") and body is only pytest.xfail("not implemented yet").
|
|
- .planning/phases/07.4-security-token-fingerprinting-token-binding-inserted/07.4-RESEARCH.md §"Test Case Structure" — exact test names and behaviour descriptions for all 4 tests.
|
|
</read_first>
|
|
<behavior>
|
|
- FGP-01 test_fgp_match_returns_200: issues token with user_agent="Mozilla/5.0" accept_lang="en", sends request with matching headers — expects 200
|
|
- FGP-02 test_fgp_mismatch_returns_401: issues token with user_agent="Mozilla/5.0" accept_lang="en", sends request with user_agent="different-agent" — expects 401 and detail="Token fingerprint mismatch"
|
|
- FGP-03 test_no_fgp_claim_allowed: crafts token manually without "fgp" key in payload — expects 200 (migration grace)
|
|
- FGP-04 test_missing_headers_empty_string_binding: issues token with no user_agent/accept_lang args (defaults to ""), sends request with no User-Agent/Accept-Language headers — expects 200
|
|
</behavior>
|
|
<action>
|
|
Create backend/tests/test_auth_fgp.py. Structure:
|
|
|
|
1. Module docstring explaining Phase 7.4 fgp test coverage (FGP-01..04).
|
|
|
|
2. Imports: copy the exact imports block from test_auth_deps.py. Add these additional imports needed for FGP-03 (manual token craft): `import base64`, `import time`, `import jwt as _jwt`, and `from config import settings`.
|
|
|
|
3. Copy make_test_app() verbatim from test_auth_deps.py (includes app.state.redis = FakeRedis()). Do NOT add a /test/admin route — not needed here.
|
|
|
|
4. Copy auth_client fixture verbatim from test_auth_deps.py.
|
|
|
|
5. Copy _create_user helper verbatim from test_auth_deps.py.
|
|
|
|
6. Write 4 test functions in this order:
|
|
- test_fgp_match_returns_200 (FGP-01)
|
|
- test_fgp_mismatch_returns_401 (FGP-02)
|
|
- test_no_fgp_claim_allowed (FGP-03)
|
|
- test_missing_headers_empty_string_binding (FGP-04)
|
|
|
|
Each test function:
|
|
- Decorated with @pytest.mark.xfail(strict=False, reason="not implemented yet") ABOVE @pytest.mark.asyncio
|
|
- Decorated with @pytest.mark.asyncio
|
|
- Accepts (auth_client, db_session) as parameters
|
|
- Body is a single line: pytest.xfail("not implemented yet")
|
|
|
|
Do NOT write any assertion logic, token construction, or request code inside the stubs. The stub body is ONLY the pytest.xfail() call. Implementation comes in Wave 1 (Plan 07.4-02).
|
|
|
|
7. No production files (services/auth.py, deps/auth.py, api/auth.py) may be modified in this task.
|
|
</action>
|
|
<verify>
|
|
<automated>cd /Users/nik/Documents/Progamming/document_scanner/backend && pytest tests/test_auth_fgp.py -v 2>&1 | tail -20</automated>
|
|
</verify>
|
|
<done>
|
|
- backend/tests/test_auth_fgp.py exists
|
|
- pytest tests/test_auth_fgp.py -v shows exactly 4 tests, all reported as XFAIL
|
|
- Zero FAILED, zero ERROR entries in the output
|
|
- pytest -v (full suite) still passes with zero failures
|
|
</done>
|
|
</task>
|
|
|
|
</tasks>
|
|
|
|
<threat_model>
|
|
## Trust Boundaries
|
|
|
|
| Boundary | Description |
|
|
|----------|-------------|
|
|
| test harness → test DB | xfail stubs create no DB state; no trust boundary crossed in Wave 0 |
|
|
|
|
## STRIDE Threat Register
|
|
|
|
| Threat ID | Category | Component | Disposition | Mitigation Plan |
|
|
|-----------|----------|-----------|-------------|-----------------|
|
|
| T-07.4-SC | Tampering | npm/pip/cargo installs | accept | No new packages installed in this plan; stdlib only |
|
|
</threat_model>
|
|
|
|
<verification>
|
|
After this plan completes:
|
|
- `pytest tests/test_auth_fgp.py -v` shows 4 XFAIL
|
|
- `pytest -v` (full suite) exits 0 with the same pass count as before this plan (411+) plus 4 new XFAIL
|
|
- No changes to backend/services/auth.py, backend/deps/auth.py, or backend/api/auth.py
|
|
</verification>
|
|
|
|
<success_criteria>
|
|
- `backend/tests/test_auth_fgp.py` exists with 4 test stubs (FGP-01..04)
|
|
- All 4 stubs use `@pytest.mark.xfail(strict=False, reason="not implemented yet")`
|
|
- Full test suite passes with zero failures; 4 new XFAIL added
|
|
- No production code modified
|
|
</success_criteria>
|
|
|
|
<output>
|
|
Create `.planning/phases/07.4-security-token-fingerprinting-token-binding-inserted/07.4-01-SUMMARY.md` when done
|
|
</output>
|