feat(P01): auth + sessions + user scoping #17
Reference in New Issue
Block a user
Delete Branch "phase/01-auth-sessions-scoping"
Deleting a branch is permanent. Although the deleted branch may continue to exist for a short time before it actually gets removed, it CANNOT be undone in most cases. Continue?
REQ-69 user/pass auth (argon2id, server-side sessions, CSRF), REQ-70 admin-managed accounts (first-user-admin, register race P0 fixed), REQ-71 user scoping (runs/<user_id>/<run_id>/, owner-keyed registry, sweep+pruning). Security review: P0+P1 fixed in-phase (
90221fb). Verify: PASS all 4 layers (184 backend + 119 frontend tests). Ships as v0.3.1.Wave 1 (T-1.1/T-1.2/T-1.3): researchx.config.paths.data_root() (RESEARCHX_DATA_DIR -> backend/var/researchx dev, D-107); UserStore persisted atomically to <data_root>/users.json (argon2-cffi PasswordHasher() defaults, all hash/verify via asyncio.to_thread, password >= 12 enforced, unique email); SessionStore persisted to <data_root>/sessions.json (token_urlsafe(32), sha256 token_hash only, 14-day sliding expiry with >60s persist throttle, immediate revoke / revoke_all_for); pydantic auth request/response contracts (no password_hash ever emitted); RunState.owner_id: str | None = None (serialization-compatible, old state files deserialize unchanged); config/ids.py consolidates the 8-char slug generator + ^[a-z0-9]{8}$ validation in one place (P-14 groundwork). 12 new store tests. ---ci--- project: researchx phase: 1 milestone: v0.3 status: execute persona: backend-engineer task: T-1.1..T-1.3 requirements: covered: [REQ-69] ---/ci---Wave 3 (T-1.6/T-1.7): LocalStorageAdapter gains REQUIRED owner_id (validated ^[a-z0-9]{8}$ — D-105/D-106), run_dir -> base_dir/runs/<owner_id>/<run_id>/, list_run_ids() scanning the owner partition defensively, read_run_artifact error embeds the owner path; StorageAdapter protocol gains append_run_artifact (the actor already called it); run-id generators consolidated into config/ids.py — one alphabet, one validator (P-14; new_run_id now delegates); RunRegistry keyed (owner_id, run_id) — cross-owner access 404s exactly like a missing run (authorization half of P-8); routes construct owner-scoped storage per request from the guard dependency; owner_id stamped into every persisted state via make_run_config(owner_id=...) + route stamping (RunActor class untouched — engine contracts unchanged); cost counters owner-keyed; config_status storage display = the data root string (no more run_dir('00000000').parent private-registry read for the path); demo.py passes the owner (P-9); mechanical owner_id constructor args across test_local_storage (layout re-pinned to runs/<owner>/<run_id>/ — P-10, + partition isolation + owner-path error tests), test_engine, test_probe_wiring, test_p0_regressions, test_resume, test_integration_p1; test_auth gains the two-user isolation 404 suite + persisted owner_id + owner-keyed counters pins. ---ci--- project: researchx phase: 1 milestone: v0.3 status: execute persona: backend-engineer task: T-1.6, T-1.7 requirements: covered: [REQ-71] ---/ci---Refreshes the committed frontend/openapi.json snapshot from the FastAPI app (exported via create_app().openapi(), never a live server): auth routes (/api/auth/register|login|logout|me), admin surface (/api/admin/users, POST /api/admin/users, /api/admin/users/{user_id}/deactivate), and /healthz join the v0.2 route set; UserPublic/LoginBody/RegisterBody/AdminCreateUserBody schemas included. Operation-ids stay stable. frontend/src/lib/api/generated/schema.d.ts regenerated from the snapshot via openapi-typescript (pnpm generate:api). New gate script pnpm check:api-drift regenerates from the committed snapshot and diffs (--check — exit 1 on drift, G-005); verified failing on an intentional schema edit, green on the committed pair. ---ci--- phase: 1, milestone: v0.3, status: execute, persona: frontend-engineer, task: T-1.11 ---/ci---