geenforce: display-readiness batch (server hardening, PS client wiring, display scope)

Get GE-Enforce closer to running on credential-less Intune/Entra display PCs
that pull manifest + payloads over HTTPS instead of SMB.

Server (plugins/geenforce/api/routes.py):
- Rate-limit + 512MB served-size ceiling on GET /payload/<sha256> (reuses the
  login limiter's cache pattern, config-overridable via GEENFORCE_PAYLOAD_*).
- New tests: payload hardening, manifestblobs model-vs-migration parity, and a
  report-contract test locking the lowercase per-entry report keys.

PS client (plugins/geenforce/client/):
- Fix New-ShopdbReport per-entry key casing to lowercase (name/action/selfhealed/
  exitcode/message) to match what the server reads; the engine emits PascalCase.
- Enforce TLS 1.2 in the network functions.
- Fetch + merge the fleet-wide common scope alongside the pctype scope
  (pctype wins on conflict; -NoCommon opt-out).
- Normalize whatever the engine returns into a well-formed summary.
- Make the empty-cache fail-safe observable: event-log entry + report ping
  instead of a silent exit 0.

Manifest (plugins/geenforce/seed_display_scope.py + docs/GE-ENFORCE-DISPLAY.md):
- Seed a gea-shopfloor-display scope: 4 Edge kiosk drift-heal registry entries
  + 1 data-driven dispatcher (Dashboard/Lobby/3DPrintRoom via display-type.txt).
  Kiosk EXEs stay image-baked; the manifest heals policy/config drift only.
- Documents the common SMB-payload audit (entries needing http/inline before a
  share-less display can inherit common).

Migration registry (shopdb/plugins/alembic_template.py + test):
- Register the pre-existing manifestblobs and the new printersupplyalerts tables
  in PLUGIN_TABLE_OWNERS; update EXPECTED_HEAD_REVISION for geenforce (0002blobs),
  printers (0002supplyalerts), and printedparts (0004txnrev) which had drifted.
This commit is contained in:
cproudlock
2026-07-23 08:16:38 -04:00
parent b211e817d5
commit 9d65ef103d
12 changed files with 1261 additions and 25 deletions

View File

@@ -95,6 +95,50 @@ at the end of its main loop) and knows each entry's action; shape them into the
`results` list at the call site (`New-ShopdbReport` in the kit takes a summary
with `Installed/Skipped/Failed/Filtered` + a `Results` list).
The engine emits per-entry outcomes in PascalCase (`Name/Action/SelfHealed/
ExitCode/Message`); `New-ShopdbReport` maps every per-entry key down to the
lowercase names above (`name/action/selfhealed/exitcode/message`) before POST,
so the entire wire contract shopdb reads is lowercase. `ConvertTo-ShopdbSummary`
first normalizes whatever the engine returns (a well-formed summary, a bare
return code, `$null`, or several emitted objects) into the count/results shape
`New-ShopdbReport` expects, so a not-yet-compliant engine still produces a valid
report.
## Common-scope inheritance
Every PC inherits the fleet-wide `common` scope on top of its own pctype scope,
mirroring the real GE-Enforce.ps1 (which applies `common\manifest.json` first,
then the pctype's). `Invoke-ShopdbEnforce.ps1` fetches the `common` scope in
addition to `-Scope` and merges it in via `Merge-ShopdbManifests`:
- entries are keyed by `Name` (case-insensitive);
- common's unique entries come first, then all pctype entries (common enforces
ahead of the pctype, as on the share);
- on a `Name` conflict the pctype entry wins (its override replaces common's).
Common is fetched over the same fail-safe path (ETag + last-known-good cache).
Pass `-NoCommon` to enforce the pctype alone, or `-CommonScope <name>` to inherit
a different fleet scope. A run whose `-Scope` already is the common scope does
not merge itself. This is how the three display subtypes (Dashboard, Lobby, 3D
Print Room), selected by `C:\Enrollment\display-type.txt`, pick up shared policy
without duplicating it per subtype.
## Fail-safe is observable, not silent
Any error still exits 0 - a bad web app never blocks or breaks a PC. But a fresh
display with an EMPTY cache (first boot, shopdb unreachable or the token
rejected with 401 / a TLS-trust failure) would otherwise enforce nothing
*silently*. When no manifest and no cache are available, the kit:
- writes a Windows Application event-log entry (source `ShopdbEnforce`, event id
1001, type Error) naming the scope and the reason (HTTP status or transport
error), and
- fires a best-effort report ping (counts `failed: 1`, a single
`(manifest-fetch)` result carrying the reason) so the miss surfaces under
GE-Enforce > Enforcement Reports.
The cycle still exits 0; the signal just makes the no-enforcement state visible.
## Cutover (safe, staged)
1. **Configure** the registry values on a canary PC; mint the token.