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>
54 lines
1.7 KiB
Python
54 lines
1.7 KiB
Python
"""End-to-end guard: upload a driver through the HTTP endpoint, then export
|
|
a NinjaRMM package from the resulting Driver record. This prevents regressions
|
|
where DriverStore save path and packages.py driver lookup path diverge."""
|
|
import io
|
|
import json
|
|
import zipfile
|
|
|
|
import pytest
|
|
|
|
|
|
@pytest.fixture
|
|
def driver_zip_bytes():
|
|
buf = io.BytesIO()
|
|
with zipfile.ZipFile(buf, "w", compression=zipfile.ZIP_DEFLATED) as zf:
|
|
zf.writestr("printer.inf", "[Version]\nSignature=$WINDOWS NT$\n")
|
|
zf.writestr("printer.cat", "FAKE_CAT")
|
|
return buf.getvalue()
|
|
|
|
|
|
def test_upload_then_ninja_export_finds_driver_on_disk(
|
|
client, tmp_data_dir, driver_zip_bytes, owner
|
|
):
|
|
from imptune.db.models import Client, Driver, Printer
|
|
|
|
upload_resp = client.post(
|
|
"/drivers/upload",
|
|
files={"file": ("printer_driver.zip", driver_zip_bytes, "application/zip")},
|
|
)
|
|
assert upload_resp.status_code == 200
|
|
|
|
driver = Driver.select().order_by(Driver.id.desc()).first()
|
|
assert driver is not None
|
|
driver.driver_desc = json.dumps(["HP LaserJet Pro"])
|
|
driver.save()
|
|
|
|
tenant = Client.create(name="Acme Corp", owner=owner)
|
|
printer = Printer.create(
|
|
name="Round Trip Printer",
|
|
ip_address="192.168.1.50",
|
|
port_name="IP_192.168.1.50",
|
|
client=tenant,
|
|
driver=driver,
|
|
owner=owner,
|
|
)
|
|
|
|
resp = client.get(f"/printers/{printer.id}/packages/ninja")
|
|
assert resp.status_code == 200, resp.text
|
|
assert resp.headers["content-type"] == "application/zip"
|
|
|
|
out = zipfile.ZipFile(io.BytesIO(resp.content))
|
|
names = out.namelist()
|
|
assert any(n.endswith("install.ps1") for n in names)
|
|
assert any("drivers/printer.inf" in n for n in names)
|