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:
@@ -24,6 +24,16 @@
|
||||
.PARAMETER ShadowMode
|
||||
Fetch + compare + report, but install from the share (no behavior change).
|
||||
|
||||
.PARAMETER CommonScope
|
||||
The fleet-wide scope every PC inherits (default 'common'). Its manifest is
|
||||
fetched in addition to -Scope and merged in, so a display enforces its own
|
||||
scope entries PLUS common's. On a Name conflict the -Scope (pctype) entry
|
||||
wins. Set -NoCommon to disable, or point at a different common scope name.
|
||||
|
||||
.PARAMETER NoCommon
|
||||
Do not fetch or merge the common scope; enforce -Scope alone (the original
|
||||
single-scope behavior).
|
||||
|
||||
.NOTES
|
||||
Fail-safe: any error exits 0 so a bad web app never blocks or breaks a PC.
|
||||
Config comes from HKLM:\SOFTWARE\GE\ShopDB (BaseUrl, ApiToken) - see the psm1.
|
||||
@@ -34,6 +44,8 @@ param(
|
||||
[Parameter(Mandatory)] [string]$EnginePath,
|
||||
[string]$ShareManifestPath,
|
||||
[switch]$ShadowMode,
|
||||
[string]$CommonScope = 'common',
|
||||
[switch]$NoCommon,
|
||||
[string]$BaseUrl,
|
||||
[string]$ApiToken,
|
||||
[string]$LogFile = "C:\Logs\Shopfloor\shopdb-enforce-$(Get-Date -Format yyyyMMdd).log"
|
||||
@@ -46,6 +58,27 @@ function Write-Log {
|
||||
Write-Host $line
|
||||
}
|
||||
|
||||
function Write-ShopdbEventLog {
|
||||
<#
|
||||
Write a Windows Application event-log entry under source 'ShopdbEnforce'.
|
||||
Used to make an otherwise silent fail-safe (no manifest and an empty cache)
|
||||
observable to whoever watches the display. Best-effort: registering the
|
||||
source needs admin, which the SYSTEM scheduled task has; any failure is
|
||||
swallowed so it can never break the fail-safe.
|
||||
#>
|
||||
param([string]$Message,
|
||||
[string]$EntryType = 'Warning',
|
||||
[int]$EventId = 1001)
|
||||
$source = 'ShopdbEnforce'
|
||||
try {
|
||||
if (-not [System.Diagnostics.EventLog]::SourceExists($source)) {
|
||||
New-EventLog -LogName Application -Source $source -ErrorAction Stop
|
||||
}
|
||||
Write-EventLog -LogName Application -Source $source -EntryType $EntryType `
|
||||
-EventId $EventId -Message $Message -ErrorAction Stop
|
||||
} catch {}
|
||||
}
|
||||
|
||||
try {
|
||||
Import-Module (Join-Path $PSScriptRoot 'ShopdbEnforceClient.psm1') -Force
|
||||
|
||||
@@ -57,7 +90,21 @@ try {
|
||||
|
||||
$sync = Sync-ShopdbManifest -Scope $Scope -Config $config
|
||||
if (-not $sync.Path) {
|
||||
Write-Log "No manifest available for $Scope (shopdb unreachable, no cache)." 'WARN'
|
||||
# Fail-safe stays (exit 0), but a fresh display with an empty cache would
|
||||
# otherwise enforce nothing SILENTLY. Surface it: a Windows event-log
|
||||
# entry plus a best-effort report ping so it shows in Enforcement Reports.
|
||||
$reason = if ($sync.Error) { $sync.Error } else { 'shopdb unreachable and no cached manifest' }
|
||||
Write-Log "No manifest available for $Scope ($reason)." 'WARN'
|
||||
Write-ShopdbEventLog -Message ("GE-Enforce could not fetch a manifest for scope '$Scope' and has no cached copy; nothing was enforced this cycle. Reason: $reason") -EntryType 'Error' -EventId 1001
|
||||
try {
|
||||
$failReport = New-ShopdbReport -Scope $Scope -AppliedVersion 0 -Summary @{
|
||||
Installed = 0; Skipped = 0; Failed = 1; Filtered = 0; EnforcerVersion = '2.6'
|
||||
Results = @(@{ Name = '(manifest-fetch)'; Action = 'failed'; Message = $reason })
|
||||
}
|
||||
if (Send-ShopdbReport -Config $config -Report $failReport) {
|
||||
Write-Log 'Reported empty-cache fetch failure to shopdb.'
|
||||
}
|
||||
} catch {}
|
||||
exit 0
|
||||
}
|
||||
Write-Log "Manifest for $Scope from $($sync.Source) (v$($sync.Version))."
|
||||
@@ -75,29 +122,49 @@ try {
|
||||
|
||||
# Which manifest the engine actually runs against.
|
||||
if ($ShadowMode -and $ShareManifestPath) {
|
||||
# Shadow: install from the share exactly as today (no payload resolve).
|
||||
# Shadow: install from the share exactly as today (no payload resolve,
|
||||
# no common merge - the share already carries its own common scope).
|
||||
$manifestToRun = $ShareManifestPath
|
||||
} else {
|
||||
# Common-scope inheritance: a display enforces its own scope PLUS the
|
||||
# fleet-wide common scope. Fetch common too (best-effort, same fail-safe
|
||||
# cache) and merge it in with the pctype winning on conflict. Skipped
|
||||
# when -NoCommon, or when this run IS the common scope.
|
||||
$manifestToMerge = $sync.Path
|
||||
if (-not $NoCommon -and $CommonScope -and ($CommonScope -ine $Scope)) {
|
||||
$commonSync = Sync-ShopdbManifest -Scope $CommonScope -Config $config
|
||||
if ($commonSync.Path) {
|
||||
$manifestToMerge = Merge-ShopdbManifests -PrimaryManifestPath $sync.Path -CommonManifestPath $commonSync.Path
|
||||
if ($manifestToMerge -ne $sync.Path) {
|
||||
Write-Log "Merged common scope '$CommonScope' (from $($commonSync.Source), v$($commonSync.Version)) into $Scope."
|
||||
}
|
||||
} else {
|
||||
Write-Log "Common scope '$CommonScope' unavailable (no fetch, no cache) - enforcing $Scope alone." 'WARN'
|
||||
}
|
||||
}
|
||||
|
||||
# Cutover: stage any http/inline payloads to local files and rewrite the
|
||||
# manifest to point at them, so the UNCHANGED engine installs from local
|
||||
# (no SMB needed for share-less PCs).
|
||||
$manifestToRun = Resolve-ShopdbPayloads -ManifestPath $sync.Path -Config $config
|
||||
if ($manifestToRun -ne $sync.Path) {
|
||||
$manifestToRun = Resolve-ShopdbPayloads -ManifestPath $manifestToMerge -Config $config
|
||||
if ($manifestToRun -ne $manifestToMerge) {
|
||||
Write-Log "Resolved http/inline payloads to local files: $manifestToRun"
|
||||
}
|
||||
}
|
||||
|
||||
# --- INTEGRATION POINT ---------------------------------------------------
|
||||
# Run the engine. Install-FromManifest.ps1 is expected to return (or you
|
||||
# adapt it to return) a summary carrying Installed/Skipped/Failed/Filtered
|
||||
# and a Results list of @{Name;Action;SelfHealed;ExitCode;Message}. Wire this
|
||||
# to your engine's actual return/parse; the shape below is the contract.
|
||||
# Run the engine. EXPECTED ENGINE CONTRACT: Install-FromManifest.ps1 returns
|
||||
# a summary object (hashtable or PSCustomObject) carrying integer counts
|
||||
# Installed / Skipped / Failed / Filtered
|
||||
# a string EnforcerVersion, and a Results list of per-entry outcomes
|
||||
# @{ Name; Action; SelfHealed; ExitCode; Message }.
|
||||
# The engine may not honor that yet: it might return $null, a bare return
|
||||
# code, or emit several objects. ConvertTo-ShopdbSummary adapts whatever it
|
||||
# returns into a well-formed summary hashtable so the report stage always
|
||||
# gets clean input (we do NOT assume the engine was fixed).
|
||||
Write-Log "Running engine against $manifestToRun"
|
||||
$summary = & $EnginePath -ManifestPath $manifestToRun -PCType $Scope
|
||||
if (-not $summary) {
|
||||
$summary = @{ Installed = 0; Skipped = 0; Failed = 0; Filtered = 0; Results = @() }
|
||||
}
|
||||
if (-not $summary.EnforcerVersion) { $summary.EnforcerVersion = '2.6' }
|
||||
$engineResult = & $EnginePath -ManifestPath $manifestToRun -PCType $Scope
|
||||
$summary = ConvertTo-ShopdbSummary -EngineResult $engineResult
|
||||
|
||||
# Report the result (best-effort).
|
||||
$appliedVersion = 0
|
||||
|
||||
Reference in New Issue
Block a user