notifications: let Recognition set start/end dates; geenforce B2 client payload fetch
Recognition edit hid the time fields (grouped with Recertification), so start/end could not be adjusted even though the backend honors them. Show the time fields for every type except Recertification (due-date driven); Recognition end still auto-fills to the next 8 AM reset when blank. Also GE-Enforce B2 client (HTTPS payload consume): ShopdbEnforceClient.psm1 gains Get-ShopdbPayload (fetch by sha256, verify, cache) + Resolve-ShopdbPayloads (rewrite http/inline entries to local staged files so the engine installs from local, no SMB); Invoke-ShopdbEnforce resolves payloads before running the engine; importer parses PayloadSource/PayloadSha256/PayloadRef. VM-verified: a SYSTEM Windows client fetched a payload over HTTP by hash, hash matched.
This commit is contained in:
@@ -160,5 +160,87 @@ function New-ShopdbReport {
|
||||
}
|
||||
}
|
||||
|
||||
function Get-ShopdbPayload {
|
||||
<#
|
||||
Fetch a payload blob by content hash over HTTPS, verify the sha256, and
|
||||
cache it locally (content-addressed, last-known-good). This is how a
|
||||
share-less PC pulls an installer the manifest references. Returns the local
|
||||
path, or $null on failure / hash mismatch.
|
||||
#>
|
||||
param(
|
||||
[Parameter(Mandatory)][string]$Sha256,
|
||||
[Parameter(Mandatory)][hashtable]$Config,
|
||||
[string]$Filename,
|
||||
[string]$CacheDir = 'C:\ProgramData\ShopDB\geenforce'
|
||||
)
|
||||
$sha = $Sha256.Trim().ToLower()
|
||||
$payloadDir = Join-Path $CacheDir 'payloads'
|
||||
if (-not (Test-Path $payloadDir)) { New-Item -ItemType Directory -Path $payloadDir -Force | Out-Null }
|
||||
$ext = if ($Filename) { [System.IO.Path]::GetExtension($Filename) } else { '' }
|
||||
$dest = Join-Path $payloadDir "$sha$ext"
|
||||
|
||||
# Cache hit only counts if the cached bytes still hash correctly.
|
||||
if (Test-Path $dest) {
|
||||
if ((Get-FileHash -LiteralPath $dest -Algorithm SHA256).Hash.ToLower() -eq $sha) { return $dest }
|
||||
Remove-Item -LiteralPath $dest -Force -ErrorAction SilentlyContinue
|
||||
}
|
||||
|
||||
$uri = "$($Config.BaseUrl)/api/geenforce/payload/$sha"
|
||||
$tmp = "$dest.tmp"
|
||||
try {
|
||||
Invoke-WebRequest -Uri $uri -Headers @{ 'X-API-Key' = $Config.ApiToken } `
|
||||
-UseBasicParsing -TimeoutSec 120 -OutFile $tmp -ErrorAction Stop
|
||||
} catch {
|
||||
if (Test-Path $tmp) { Remove-Item -LiteralPath $tmp -Force -ErrorAction SilentlyContinue }
|
||||
return $null
|
||||
}
|
||||
$got = (Get-FileHash -LiteralPath $tmp -Algorithm SHA256).Hash.ToLower()
|
||||
if ($got -ne $sha) {
|
||||
Remove-Item -LiteralPath $tmp -Force -ErrorAction SilentlyContinue
|
||||
return $null
|
||||
}
|
||||
Move-Item -LiteralPath $tmp -Destination $dest -Force
|
||||
return $dest
|
||||
}
|
||||
|
||||
function Resolve-ShopdbPayloads {
|
||||
<#
|
||||
Rewrite a manifest so http/inline payload entries install from a locally
|
||||
fetched file instead of a share path - keeping the engine (and its SMB
|
||||
handling) untouched. For each entry with PayloadSha256 (PayloadSource
|
||||
http/inline), fetches + verifies the payload and points the entry's
|
||||
installer path at the local copy (Installer for MSI/EXE/CMD/BAT/INF, Script
|
||||
for PS1, Source for File). Returns a rewritten sibling manifest path, or the
|
||||
original path when there is nothing to resolve. Throws if a referenced
|
||||
payload cannot be fetched/verified (caller decides fail-safe behavior).
|
||||
#>
|
||||
param(
|
||||
[Parameter(Mandatory)][string]$ManifestPath,
|
||||
[Parameter(Mandatory)][hashtable]$Config,
|
||||
[string]$CacheDir = 'C:\ProgramData\ShopDB\geenforce'
|
||||
)
|
||||
$json = Get-Content -LiteralPath $ManifestPath -Raw | ConvertFrom-Json
|
||||
$pathField = @{ MSI='Installer'; EXE='Installer'; CMD='Installer'; BAT='Installer';
|
||||
INF='Installer'; PS1='Script'; File='Source' }
|
||||
$changed = $false
|
||||
foreach ($entry in @($json.Applications)) {
|
||||
$src = [string]$entry.PayloadSource
|
||||
$sha = [string]$entry.PayloadSha256
|
||||
if (-not $sha -or ($src -ne 'http' -and $src -ne 'inline')) { continue }
|
||||
$field = $pathField[[string]$entry.Type]
|
||||
if (-not $field) { continue }
|
||||
$local = Get-ShopdbPayload -Sha256 $sha -Config $Config -Filename $entry.PayloadRef -CacheDir $CacheDir
|
||||
if (-not $local) { throw "payload $sha for '$($entry.Name)' could not be fetched/verified" }
|
||||
if ($entry.PSObject.Properties.Name -contains $field) { $entry.$field = $local }
|
||||
else { $entry | Add-Member -NotePropertyName $field -NotePropertyValue $local }
|
||||
$changed = $true
|
||||
}
|
||||
if (-not $changed) { return $ManifestPath }
|
||||
$out = [System.IO.Path]::ChangeExtension($ManifestPath, '.resolved.json')
|
||||
($json | ConvertTo-Json -Depth 20) | Set-Content -LiteralPath $out -Encoding UTF8
|
||||
return $out
|
||||
}
|
||||
|
||||
Export-ModuleMember -Function Get-ShopdbConfig, Sync-ShopdbManifest, `
|
||||
Compare-ShopdbShadow, Send-ShopdbReport, New-ShopdbReport, Read-CachedVersion
|
||||
Compare-ShopdbShadow, Send-ShopdbReport, New-ShopdbReport, Read-CachedVersion, `
|
||||
Get-ShopdbPayload, Resolve-ShopdbPayloads
|
||||
|
||||
Reference in New Issue
Block a user