PrintToVeraeTimeStamp/platform/windows/scripts/user-token.ps1

95 lines
5.1 KiB
PowerShell

# Verae signing key handling for the installer (runs elevated, acts for the LOGGED-IN user).
# -Mode status : detect target user + existing key → INI [token]
# -Mode validate : check key in -TokenFile against Verae (free) → INI [validate]
# -Mode save : validate-format + write <profile>\.verae\xverae-token with user-only ACL → INI [save]
# The key is never printed; only a masked form (xVerae-...abcd) is logged.
param(
[ValidateSet("status","validate","save")][string]$Mode = "status",
[string]$TokenFile,
[Parameter(Mandatory=$true)][string]$IniOut
)
$ErrorActionPreference = "Continue"
. (Join-Path $PSScriptRoot "common.ps1")
[Net.ServicePointManager]::SecurityProtocol = [Net.SecurityProtocolType]::Tls12
$ValidateUrl = "https://zap.verae.ai/v1/status" # documented as free; 401 = bad key
# Prefer the Go validator (verae-print.exe validate-key): PowerShell 5.1's
# Invoke-WebRequest was seen timing out on nimo (proxy auto-detect) while Go and
# curl answered in <1 s. The key is read from a file and never on a command line.
function Find-VeraeExe {
foreach ($c in @((Join-Path $PSScriptRoot "verae-print.exe"), (Join-Path (Split-Path -Parent $PSScriptRoot) "bin\verae-print.exe"), (Join-Path (Split-Path -Parent $PSScriptRoot) "verae-print.exe"))) {
if (Test-Path $c) { return $c }
}
return $null
}
function Test-VeraeKey([string]$tok, [string]$file) {
if ($tok -notmatch '^(xVerae-|key-)\S{8,}$') { return @{ result = "badformat"; detail = "must start with xVerae- (or legacy key-)" } }
$exe = Find-VeraeExe
if ($exe -and $file -and (Test-Path $file)) {
$ini = Join-Path $env:TEMP ("verae-val-" + [guid]::NewGuid().ToString("N") + ".ini")
try {
& $exe validate-key --token-file $file --out $ini 2>&1 | Out-Null
$kv = @{}
if (Test-Path $ini) { Get-Content $ini | ForEach-Object { if ($_ -match '^([^=\[]+)=(.*)$') { $kv[$Matches[1]] = $Matches[2] } } }
if ($kv.result) { return @{ result = $kv.result; detail = $kv.detail } }
} finally { Remove-Item $ini -ErrorAction SilentlyContinue }
}
try {
$r = Invoke-WebRequest -UseBasicParsing -TimeoutSec 10 -Uri $ValidateUrl -Headers @{ "x-api-key" = $tok }
return @{ result = "valid"; detail = "HTTP $($r.StatusCode)" }
} catch {
$code = $null
if ($_.Exception.Response) { $code = [int]$_.Exception.Response.StatusCode }
if ($code -eq 401 -or $code -eq 403) { return @{ result = "invalid"; detail = "HTTP $code from Verae" } }
if ($code) { return @{ result = "unknown"; detail = "HTTP $code from Verae" } }
return @{ result = "unreachable"; detail = $_.Exception.Message }
}
}
$u = Get-VeraeTargetUser
$tokPath = Join-Path $u.Profile ".verae\xverae-token"
Write-Host "user-token.ps1 mode=$Mode target=$($u.Account) sid=$($u.Sid) profile=$($u.Profile) via=$($u.Source) runningAs=$([Security.Principal.WindowsIdentity]::GetCurrent().Name)"
switch ($Mode) {
"status" {
$existing = $null
if (Test-Path $tokPath) { $existing = (Get-Content $tokPath -Raw -ErrorAction SilentlyContinue) }
if ($existing) { $existing = $existing.Trim() }
$v = @{ result = "none"; detail = "" }
if ($existing) { $v = Test-VeraeKey $existing $tokPath }
Write-Host "existing key: $([bool]$existing) $(Get-MaskedToken $existing) check=$($v.result) $($v.detail)"
Write-VeraeIni $IniOut "token" ([ordered]@{
account = $u.Account; sid = $u.Sid; profile = $u.Profile; source = $u.Source; path = $tokPath
present = $(if ($existing) { 1 } else { 0 }); masked = (Get-MaskedToken $existing)
check = $v.result; detail = $v.detail })
}
"validate" {
$tok = (Get-Content $TokenFile -Raw).Trim()
$v = Test-VeraeKey $tok $TokenFile
Write-Host "validate $(Get-MaskedToken $tok): $($v.result) $($v.detail)"
Write-VeraeIni $IniOut "validate" ([ordered]@{ result = $v.result; detail = $v.detail; masked = (Get-MaskedToken $tok) })
}
"save" {
$tok = (Get-Content $TokenFile -Raw).Trim()
$ok = $false; $err = ""
try {
if ($tok -notmatch '^(xVerae-|key-)\S{8,}$') { throw "bad token format" }
$dir = Split-Path $tokPath -Parent
New-Item -ItemType Directory -Force -Path $dir | Out-Null
# same format as internal/auth.SaveXVeraeToken: token + "\n", no BOM
[IO.File]::WriteAllText($tokPath, $tok + "`n", (New-Object Text.UTF8Encoding($false)))
# ACL: the logged-in user owns it; only user, SYSTEM, Administrators have access
icacls $dir /grant "*$($u.Sid):(OI)(CI)F" /C | Out-Host
icacls $dir /setowner "*$($u.Sid)" /C | Out-Host
icacls $tokPath /inheritance:r /grant:r "*$($u.Sid):F" "*S-1-5-18:F" "*S-1-5-32-544:F" /C | Out-Host
icacls $tokPath /setowner "*$($u.Sid)" /C | Out-Host
$back = (Get-Content $tokPath -Raw).Trim()
$ok = ($back -eq $tok)
if (-not $ok) { $err = "read-back mismatch" }
icacls $tokPath | Out-Host
} catch { $err = $_.Exception.Message }
Write-Host "save $(Get-MaskedToken $tok) -> $tokPath : $(if ($ok) {'OK'} else {"FAIL $err"})"
Write-VeraeIni $IniOut "save" ([ordered]@{ result = $(if ($ok) { "OK" } else { "FAIL" }); detail = $err; path = $tokPath; masked = (Get-MaskedToken $tok); account = $u.Account })
}
}
exit 0