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>
143 lines
3.4 KiB
Python
143 lines
3.4 KiB
Python
"""Tests for database initialization and driver storage."""
|
|
import hashlib
|
|
from pathlib import Path
|
|
|
|
import pytest
|
|
|
|
|
|
@pytest.fixture
|
|
def db_env(tmp_path, monkeypatch):
|
|
"""Set up temp DATA_DIR and configure db to use temp paths."""
|
|
data_dir = tmp_path / "data"
|
|
data_dir.mkdir()
|
|
drivers_dir = data_dir / "drivers"
|
|
drivers_dir.mkdir()
|
|
db_path = str(data_dir / "imptune.db")
|
|
|
|
monkeypatch.setenv("DATA_DIR", str(data_dir))
|
|
|
|
import imptune.config as cfg
|
|
cfg.DATA_DIR = str(data_dir)
|
|
cfg.DB_PATH = db_path
|
|
cfg.DRIVERS_DIR = str(drivers_dir)
|
|
|
|
# Close and re-init the db with the temp path
|
|
from imptune.db.database import db
|
|
if not db.is_closed():
|
|
db.close()
|
|
|
|
return {
|
|
"data_dir": data_dir,
|
|
"drivers_dir": drivers_dir,
|
|
"db_path": db_path,
|
|
}
|
|
|
|
|
|
def test_create_tables(db_env):
|
|
"""init_db() creates all 4 tables in a fresh SQLite file."""
|
|
from imptune.db.database import db, init_db
|
|
|
|
if not db.is_closed():
|
|
db.close()
|
|
|
|
init_db()
|
|
|
|
tables = db.get_tables()
|
|
assert "owner" in tables
|
|
assert "client" in tables
|
|
assert "driver" in tables
|
|
assert "printer" in tables
|
|
assert "icon" in tables
|
|
|
|
db.close()
|
|
|
|
|
|
def test_wal_mode(db_env):
|
|
"""After init_db(), PRAGMA journal_mode returns 'wal'."""
|
|
from imptune.db.database import db, init_db
|
|
|
|
if not db.is_closed():
|
|
db.close()
|
|
|
|
init_db()
|
|
|
|
cursor = db.execute_sql("PRAGMA journal_mode;")
|
|
mode = cursor.fetchone()[0]
|
|
assert mode == "wal"
|
|
|
|
db.close()
|
|
|
|
|
|
def test_foreign_keys(db_env):
|
|
"""After init_db(), PRAGMA foreign_keys returns 1."""
|
|
from imptune.db.database import db, init_db
|
|
|
|
if not db.is_closed():
|
|
db.close()
|
|
|
|
init_db()
|
|
|
|
cursor = db.execute_sql("PRAGMA foreign_keys;")
|
|
value = cursor.fetchone()[0]
|
|
assert value == 1
|
|
|
|
db.close()
|
|
|
|
|
|
def test_idempotent(db_env):
|
|
"""Calling init_db() twice does not raise an error."""
|
|
from imptune.db.database import db, init_db
|
|
|
|
if not db.is_closed():
|
|
db.close()
|
|
|
|
init_db()
|
|
db.close()
|
|
init_db() # second call — must not raise
|
|
|
|
db.close()
|
|
|
|
|
|
def test_driver_store_save(db_env, tmp_path):
|
|
"""Saving bytes returns their SHA256 hex digest and creates the file."""
|
|
from imptune.storage.driver_store import DriverStore
|
|
|
|
drivers_dir = db_env["drivers_dir"]
|
|
store = DriverStore(str(drivers_dir))
|
|
|
|
data = b"test driver package content"
|
|
expected_sha256 = hashlib.sha256(data).hexdigest()
|
|
|
|
result = store.save(data)
|
|
|
|
assert result == expected_sha256
|
|
assert (drivers_dir / f"{expected_sha256}.zip").exists()
|
|
|
|
|
|
def test_driver_store_dedup(db_env):
|
|
"""Saving the same bytes twice results in one file on disk."""
|
|
from imptune.storage.driver_store import DriverStore
|
|
|
|
drivers_dir = db_env["drivers_dir"]
|
|
store = DriverStore(str(drivers_dir))
|
|
|
|
data = b"duplicate driver data"
|
|
store.save(data)
|
|
store.save(data)
|
|
|
|
files = list(drivers_dir.iterdir())
|
|
assert len(files) == 1
|
|
|
|
|
|
def test_driver_store_get_path(db_env):
|
|
"""get_path(sha256) returns the correct file path."""
|
|
from imptune.storage.driver_store import DriverStore
|
|
|
|
drivers_dir = db_env["drivers_dir"]
|
|
store = DriverStore(str(drivers_dir))
|
|
|
|
sha256 = "abcdef1234567890" * 4 # 64 hex chars
|
|
path = store.get_path(sha256)
|
|
|
|
assert path == Path(str(drivers_dir)) / f"{sha256}.zip"
|