Files
ImpTune/CLAUDE.md
T
kawaandClaude Haiku 4.5 ed41f7f520 feat(session): per-owner printer/config storage via cookie-scoped bearer key
Printers and groups (Client) are now scoped to an Owner identified by an opaque
bearer key (secrets.token_urlsafe(32)) stored in an httponly cookie, defaulting
to temporary. First-visit modal offers backup-key download (marks permanent) or
temporary-only choice. /session/restore re-attaches a fresh browser to a saved
key. Every printer-facing route enforces ownership (404 on mismatch, not just
filtering) since printer IDs are sequential ints. Drivers stay global/shared.

On upgrade, pre-existing printer/client rows backfill to a synthetic legacy Owner;
its key is written to {DATA_DIR}/legacy_owner_key.txt for manual restore.

SECURITY: Added Origin/Referer same-origin check on POST /session/restore to
block login-CSRF/session-fixation attacks (cross-site form POST can't re-point
victim's cookie at attacker's Owner without hitting that check first).

Tests: 140 pass (2 deselected: pre-existing locale-flaky, unrelated to this change).
Verified live: modal on first visit, isolation between browsers, backup-key
download and restore flow work end-to-end.

Co-Authored-By: Claude Haiku 4.5 <noreply@anthropic.com>
2026-08-04 11:29:43 +02:00

4.3 KiB
Raw Blame History

CLAUDE.md

Guide for Claude Code (claude.ai/code) work in repo.

Commands

Run tests:

pytest tests/
pytest tests/unit/test_inf_parser.py  # single test file
pytest tests/ -k "test_name"          # single test by name

Run dev server:

export DATA_DIR=/tmp/imptune_data
export COOKIE_SECURE=false  # plain HTTP — omit if serving behind TLS
uvicorn imptune.main:app --reload --port 8000

Docker:

docker-compose up
docker build -t imptune .

Install deps:

pip install -r requirements.txt -r requirements-dev.txt

Architecture

ImpTune make printer deploy packages (.intunewin for Intune, .zip for NinjaRMM) from Windows driver ZIPs + web UI. No external services — single FastAPI + SQLite + Docker volume.

Request flow:

  1. Driver upload → api/drivers.pyservices/inf_parser.py parse INF → storage/driver_store.py store by SHA256 → Peewee Driver record (shared/global — visible to every Owner)
  2. Printer config → api/printers.pydb/models.py Printer record (links Driver FK, scoped to request.state.owner)
  3. Icon upload → api/icons.py → Pillow validate PNG 256×256 → SHA256 storage → Icon record
  4. Package export → api/packages.pygenerators/script_generator.py render Jinja2 PS1 templates → generators/intunewin_builder.py encrypt ZIP (AES-256-CBC + HMAC-SHA256)

Key modules:

  • imptune/config.pyDATA_DIR, DB_PATH, DRIVERS_DIR, ICONS_DIR, COOKIE_SECURE from env
  • imptune/db/database.py — SQLite WAL mode + foreign_keys=1; all models inherit BaseModel; init_db() also backfills owner_id on pre-per-owner-scoping DBs into a synthetic legacy Owner (key written to {DATA_DIR}/legacy_owner_key.txt)
  • imptune/services/session.pyOwnerSessionMiddleware resolves request.state.owner from the imptune_owner_key cookie, creating one on first visit (skips /health)
  • imptune/services/inf_parser.py — auto-detect encoding (UTF-16/UTF-8/cp1252), resolve %TOKEN% from [Strings], handle multi-model INFs
  • imptune/generators/intunewin_builder.py — Python-native .intunewin (ZIP-in-ZIP); IV 16 bytes (not 32); match reference tool 1.8.6.0 output
  • imptune/templates/scripts/ — Jinja2 templates for install.ps1, uninstall.ps1, detect.ps1

Per-owner storage: Printer/Client (groups) are scoped to an Owner identified by an opaque bearer key in a cookie — no accounts. Driver stays global/shared. Every route taking a printer_id/client_id must filter/check .owner == request.state.owner (404, not 403, on mismatch) — printer IDs are small sequential ints, so a list-only filter isn't enough. Onboarding modal (templates/base.html, gated on request.state.is_new_owner) offers "download backup key" (GET /session/key/download, marks Owner.is_permanent) vs. temporary; /session/restore re-attaches a browser to a previously downloaded key. In tests, use the owner fixture (tests/conftest.py) when creating Printer/Client rows directly via the ORM so the client fixture's cookie-scoped requests can see them.

UI stack: Pico CSS + HTMX 2 + Alpine.js 3 + Jinja2 server-side templates.

HTMX pattern: Forms hx-post, swap #driver-list / #printer-list / #client-list targets. Errors return inline HTML fragments (HTTP 400/409) via _error_response(). Success return partials from templates/partials/.

PowerShell install script notes:

  • WOW64 64-bit relaunch guard (Intune run 32-bit, pnputil need 64-bit)
  • UAC self-elevation for user context (SYSTEM context skip)
  • Two-step: pnputil /add-driver then Add-PrinterDriver + Add-PrinterPort + Add-Printer
  • All idempotent (-ErrorAction SilentlyContinue)

Test Setup

conftest.py monkeypatch config.DATA_DIR + config.DB_PATH to temp dir per test. client fixture yield TestClient(app) with isolated SQLite. E2E in tests/e2e/ use Playwright.

Environment Variables

Var Default Purpose
DATA_DIR /data Storage root (DB + drivers + icons)
PORT 8000 Server port
COOKIE_SECURE true Owner-session cookie Secure flag. Set false for local plain-HTTP dev (uvicorn --reload) or the browser drops the cookie and a new Owner is created on every request.