Files
MBN_STOCK_WEBVIEW/scripts/Initialize-DevelopmentLiveConfig.ps1

239 lines
7.8 KiB
PowerShell

[CmdletBinding()]
param(
[Parameter(Mandatory)]
[ValidateSet('127.0.0.1', '::1')]
[string] $PlayoutHost,
[Parameter(Mandatory)]
[ValidateRange(1, 65535)]
[int] $PlayoutPort,
[Parameter(Mandatory)]
[ValidatePattern('^[0-9A-Fa-f]{64}$')]
[string] $NativeSha256,
[Parameter(Mandatory)]
[ValidatePattern('^[0-9A-Fa-f]{64}$')]
[string] $InteropSha256,
[ValidateRange(0, 2147483647)]
[Nullable[int]] $OutputChannel = $null,
[switch] $Force,
[string] $ConfigurationDirectory = (
Join-Path $env:LOCALAPPDATA 'MBN_STOCK_WEBVIEW\Config')
)
Set-StrictMode -Version Latest
$ErrorActionPreference = 'Stop'
$requiredAuthorization = 'I_AUTHORIZE_LIVE_PROGRAM_OUTPUT_FOR_THIS_LAUNCH'
$sceneAllowlist = @(
'5001', '5006', '5011', '5016', '50160', '5023', '5024', '5025',
'5026', '5029', '5032', '5037', '5068', '5070', '5072', '5074',
'5076', '5077', '5078', '5079', '5080', '5081', '5082', '5083',
'5084', '5085', '5086', '50860', '5087', '5088', '6001', '6067',
'8001', '8002', '8003', '8018', '8032', '8035', '8040', '8046',
'8051', '8056', '8061', '8067', 'N5001'
)
function Assert-LocalAppDataPath {
param([Parameter(Mandatory)][string] $Path)
$localRoot = [IO.Path]::GetFullPath(
[Environment]::GetFolderPath(
[Environment+SpecialFolder]::LocalApplicationData))
$fullPath = [IO.Path]::GetFullPath($Path)
$rootPrefix = $localRoot.TrimEnd(
[IO.Path]::DirectorySeparatorChar,
[IO.Path]::AltDirectorySeparatorChar) + [IO.Path]::DirectorySeparatorChar
if (-not $fullPath.StartsWith(
$rootPrefix,
[StringComparison]::OrdinalIgnoreCase)) {
throw 'Development Live configuration must stay under LocalAppData.'
}
return $fullPath
}
function Assert-NoReparsePointChain {
param(
[Parameter(Mandatory)][string] $Directory,
[Parameter(Mandatory)][string] $StopDirectory
)
$current = [IO.Path]::GetFullPath($Directory)
$stop = [IO.Path]::GetFullPath($StopDirectory)
while (-not [string]::Equals(
$current,
$stop,
[StringComparison]::OrdinalIgnoreCase)) {
$item = Get-Item -LiteralPath $current -Force
if (($item.Attributes -band [IO.FileAttributes]::ReparsePoint) -ne 0) {
throw 'Development Live configuration directories cannot be reparse points.'
}
$parent = [IO.Path]::GetDirectoryName($current)
if ([string]::IsNullOrWhiteSpace($parent) -or
[string]::Equals(
$parent,
$current,
[StringComparison]::OrdinalIgnoreCase)) {
throw 'The LocalAppData configuration directory chain is invalid.'
}
$current = $parent
}
}
function Assert-WritableTarget {
param([Parameter(Mandatory)][string] $Path)
if (-not (Test-Path -LiteralPath $Path)) {
return
}
$item = Get-Item -LiteralPath $Path -Force
if ($item.PSIsContainer -or
($item.Attributes -band [IO.FileAttributes]::ReparsePoint) -ne 0) {
throw 'Development Live configuration targets must be ordinary files.'
}
if (-not $Force) {
throw "Configuration already exists. Review it and rerun with -Force: $Path"
}
}
function Set-CurrentUserOnlyAcl {
param([Parameter(Mandatory)][string] $Path)
$identity = [Security.Principal.WindowsIdentity]::GetCurrent().Name
$acl = [Security.AccessControl.FileSecurity]::new()
$acl.SetAccessRuleProtection($true, $false)
$rule = [Security.AccessControl.FileSystemAccessRule]::new(
$identity,
[Security.AccessControl.FileSystemRights]::FullControl,
[Security.AccessControl.AccessControlType]::Allow)
$acl.AddAccessRule($rule)
[IO.File]::SetAccessControl($Path, $acl)
}
function Invalidate-ExistingAuthorization {
param([Parameter(Mandatory)][string] $Path)
if (-not [IO.File]::Exists($Path)) {
return
}
$invalidatedPath = (
$Path + '.invalidated.' + [Guid]::NewGuid().ToString('N'))
[IO.File]::Move($Path, $invalidatedPath)
try {
Set-CurrentUserOnlyAcl $invalidatedPath
[IO.File]::Delete($invalidatedPath)
}
catch {
throw (
'The previous Development Live authorization was invalidated but ' +
"could not be removed. Remove this protected file before retrying: $invalidatedPath")
}
if ([IO.File]::Exists($Path)) {
throw 'The previous Development Live authorization could not be invalidated.'
}
}
function Write-ProtectedJson {
param(
[Parameter(Mandatory)][string] $Path,
[Parameter(Mandatory)][string] $Json
)
$temporaryPath = "$Path.tmp.$([Guid]::NewGuid().ToString('N'))"
try {
[IO.File]::WriteAllText(
$temporaryPath,
$Json,
[Text.UTF8Encoding]::new($false))
Set-CurrentUserOnlyAcl $temporaryPath
Move-Item -LiteralPath $temporaryPath -Destination $Path -Force
}
finally {
if ([IO.File]::Exists($temporaryPath)) {
Remove-Item -LiteralPath $temporaryPath -Force
}
}
}
$configurationRoot = Assert-LocalAppDataPath $ConfigurationDirectory
[IO.Directory]::CreateDirectory($configurationRoot) | Out-Null
$localApplicationData = [IO.Path]::GetFullPath(
[Environment]::GetFolderPath(
[Environment+SpecialFolder]::LocalApplicationData))
Assert-NoReparsePointChain $configurationRoot $localApplicationData
$playoutPath = Join-Path $configurationRoot 'playout.local.json'
$authorizationPath = Join-Path $configurationRoot 'playout.development-live.local.json'
Assert-WritableTarget $playoutPath
Assert-WritableTarget $authorizationPath
$playout = [ordered]@{
mode = 'DryRun'
host = $PlayoutHost
port = $PlayoutPort
tcpMode = 1
clientPort = 0
sceneDirectory = $null
outputChannel = $OutputChannel
layoutIndex = 10
legacySceneFadeDuration = 6
legacySceneBackgroundKind = 'None'
legacySceneBackgroundAssetPath = $null
legacySceneBackgroundVideoLoopCount = 2004
legacySceneBackgroundVideoLoopInfinite = $true
legacyBackgroundDirectory = $null
testProcessWindowTitlePattern = $null
testSceneAllowlist = $sceneAllowlist
trustedLiveOutputEnabled = $true
queueCapacity = 64
connectTimeoutMilliseconds = 5000
operationTimeoutMilliseconds = 5000
disconnectTimeoutMilliseconds = 3000
processPollIntervalMilliseconds = 1000
reconnectDelayMilliseconds = 1000
maximumReconnectAttempts = 0
reconnectEnabled = $false
maximumAutomaticRefreshesPerTakeIn = 0
}
$authorization = [ordered]@{
schemaVersion = 1
mode = 'Live'
authorization = $requiredAuthorization
nativeSha256 = $NativeSha256.ToUpperInvariant()
interopSha256 = $InteropSha256.ToUpperInvariant()
}
$playoutJson = $playout | ConvertTo-Json -Depth 6
$authorizationJson = $authorization | ConvertTo-Json -Depth 3
# In Force mode, invalidate the old authorization before changing the DryRun
# base. Any later failure therefore leaves no valid authorization at the exact
# path consumed by the Debug-only bootstrap.
Invalidate-ExistingAuthorization -Path $authorizationPath
# Write the safe DryRun base first and the new one-launch authorization last.
# Merely creating these files does not connect: Debug, the exact
# --development-live argument, vendor hash verification and runtime gates remain
# mandatory in the application.
Write-ProtectedJson $playoutPath $playoutJson
Write-ProtectedJson $authorizationPath $authorizationJson
Write-Host "Development Live base configuration created at: $playoutPath"
Write-Host "Development Live launch authorization created at: $authorizationPath"
Write-Warning (
'The base file remains DryRun. Use only Debug|x64 with the exact ' +
'Development Live (Package) profile after confirming the local development PGM target.')