238 lines
7.6 KiB
PowerShell
238 lines
7.6 KiB
PowerShell
<#
|
||
AZA – Step 12: Authorized Smoke-Test (PowerShell)
|
||
|
||
What it does:
|
||
- Loads MEDWORK_API_TOKENS (preferred) or MEDWORK_API_TOKEN (legacy) from deploy/.env
|
||
- Picks the first token (NEW) from a comma-separated list
|
||
- Calls GET /license/status with header: X-API-Token: <token>
|
||
- Optionally sends X-Device-Id (stable per run unless you override)
|
||
|
||
Run:
|
||
powershell -ExecutionPolicy Bypass -File .\deploy\authorized_test.ps1
|
||
Optional:
|
||
powershell -ExecutionPolicy Bypass -File .\deploy\authorized_test.ps1 -BaseUrl http://127.0.0.1:8000
|
||
powershell -ExecutionPolicy Bypass -File .\deploy\authorized_test.ps1 -DeviceId "test-device-123"
|
||
|
||
Expected:
|
||
200 + JSON: {"valid": true/false, "valid_until": 1774553596 or null}
|
||
#>
|
||
|
||
[CmdletBinding()]
|
||
param(
|
||
[string]$BaseUrl = "http://127.0.0.1:8000",
|
||
# default: .env next to this script (deploy\.env)
|
||
[string]$EnvFile = "",
|
||
[string]$DeviceId = ""
|
||
)
|
||
|
||
function Get-ScriptDir {
|
||
# Prefer PSScriptRoot when available, otherwise fall back to MyInvocation (works in Windows PowerShell).
|
||
if ($PSScriptRoot -and $PSScriptRoot.Trim().Length -gt 0) { return $PSScriptRoot }
|
||
$p = $MyInvocation.MyCommand.Path
|
||
if (-not $p) { throw "Cannot determine script directory (PSScriptRoot/MyInvocation empty)." }
|
||
return (Split-Path -Parent $p)
|
||
}
|
||
|
||
function Load-DotEnv([string]$Path) {
|
||
if (-not (Test-Path -LiteralPath $Path)) {
|
||
throw "Missing .env file at: $Path"
|
||
}
|
||
$map = @{}
|
||
Get-Content -LiteralPath $Path | ForEach-Object {
|
||
$line = $_.Trim()
|
||
if ($line.Length -eq 0) { return }
|
||
if ($line.StartsWith("#")) { return }
|
||
$idx = $line.IndexOf("=")
|
||
if ($idx -lt 1) { return }
|
||
$k = $line.Substring(0, $idx).Trim()
|
||
$v = $line.Substring($idx + 1).Trim()
|
||
# strip optional wrapping quotes
|
||
if (($v.StartsWith('"') -and $v.EndsWith('"')) -or ($v.StartsWith("'") -and $v.EndsWith("'"))) {
|
||
$v = $v.Substring(1, $v.Length - 2)
|
||
}
|
||
$map[$k] = $v
|
||
}
|
||
return $map
|
||
}
|
||
|
||
function First-TokenFromValue([string]$value) {
|
||
if (-not $value) { return "" }
|
||
# supports "NEW,OLD" and multi-line/space separated variants
|
||
$value = $value.Trim()
|
||
$parts = $value -split "[,\r\n]+" | ForEach-Object { $_.Trim() } | Where-Object { $_ -ne "" }
|
||
if ($parts.Count -ge 1) { return $parts[0] }
|
||
return ""
|
||
}
|
||
|
||
function All-TokensFromValue([string]$value) {
|
||
if (-not $value) { return @() }
|
||
$value = $value.Trim()
|
||
return ($value -split "[,\r\n]+" | ForEach-Object { $_.Trim() } | Where-Object { $_ -ne "" })
|
||
}
|
||
|
||
try {
|
||
if (-not $EnvFile -or $EnvFile.Trim().Length -eq 0) {
|
||
$scriptDir = Get-ScriptDir
|
||
$EnvFile = Join-Path $scriptDir ".env"
|
||
}
|
||
$envMap = Load-DotEnv -Path $EnvFile
|
||
} catch {
|
||
Write-Host "❌ $($_.Exception.Message)"
|
||
exit 1
|
||
}
|
||
|
||
$tokenValue = ""
|
||
$tokenSource = ""
|
||
if ($envMap.ContainsKey("MEDWORK_API_TOKENS")) { $tokenValue = $envMap["MEDWORK_API_TOKENS"] }
|
||
elseif ($envMap.ContainsKey("MEDWORK_API_TOKEN")) { $tokenValue = $envMap["MEDWORK_API_TOKEN"] }
|
||
|
||
$tokens = @()
|
||
if ($envMap.ContainsKey("MEDWORK_API_TOKENS")) {
|
||
$tokenSource = "MEDWORK_API_TOKENS"
|
||
$tokens = All-TokensFromValue -value $envMap["MEDWORK_API_TOKENS"]
|
||
} elseif ($envMap.ContainsKey("MEDWORK_API_TOKEN")) {
|
||
$tokenSource = "MEDWORK_API_TOKEN"
|
||
$tokens = All-TokensFromValue -value $envMap["MEDWORK_API_TOKEN"]
|
||
}
|
||
|
||
if (-not $tokens -or $tokens.Count -lt 1) {
|
||
Write-Host "❌ No token found in $EnvFile (expected MEDWORK_API_TOKENS or MEDWORK_API_TOKEN)."
|
||
exit 1
|
||
}
|
||
|
||
if (-not $DeviceId) {
|
||
# deterministic-ish per machine/user; override via -DeviceId when needed
|
||
$DeviceId = "ps-" + $env:COMPUTERNAME + "-" + $env:USERNAME
|
||
}
|
||
|
||
$base = $BaseUrl.TrimEnd("/")
|
||
$url = "$base/license/status"
|
||
|
||
Write-Host "[AZA] Authorized Smoke-Test"
|
||
Write-Host " BaseUrl: $base"
|
||
Write-Host " Endpoint: /license/status"
|
||
Write-Host " TokenSrc: $tokenSource"
|
||
Write-Host " Tokens: $($tokens.Count) (will try in order)"
|
||
Write-Host " TokenLen: $($tokens[0].Length) (first token length only)"
|
||
Write-Host " DeviceId: $DeviceId"
|
||
Write-Host ""
|
||
|
||
function Invoke-GetJson([string]$u, [hashtable]$h) {
|
||
try {
|
||
$resp = Invoke-RestMethod -Method GET -Uri $u -Headers $h -TimeoutSec 15
|
||
return @{ ok=$true; status=200; body=$resp }
|
||
} catch {
|
||
$e = $_.Exception
|
||
$status = $null
|
||
$body = ""
|
||
if ($e.Response -and $e.Response.StatusCode) {
|
||
$status = [int]$e.Response.StatusCode
|
||
try {
|
||
$stream = $e.Response.GetResponseStream()
|
||
if ($stream) {
|
||
$reader = New-Object System.IO.StreamReader($stream)
|
||
$body = $reader.ReadToEnd()
|
||
}
|
||
} catch { }
|
||
}
|
||
return @{ ok=$false; status=$status; body=$body; message=$e.Message }
|
||
}
|
||
}
|
||
|
||
$base = $BaseUrl.TrimEnd("/")
|
||
|
||
function Invoke-GetJsonNoAuth([string]$u) {
|
||
try {
|
||
$resp = Invoke-RestMethod -Method GET -Uri $u -TimeoutSec 15
|
||
return @{ ok=$true; status=200; body=$resp }
|
||
} catch {
|
||
$e = $_.Exception
|
||
$status = $null
|
||
if ($e.Response -and $e.Response.StatusCode) { $status = [int]$e.Response.StatusCode }
|
||
return @{ ok=$false; status=$status; message=$e.Message }
|
||
}
|
||
}
|
||
|
||
function Discover-LicenseStatusPath([string]$baseUrl) {
|
||
$openapiUrl = "$baseUrl/openapi.json"
|
||
$r = Invoke-GetJsonNoAuth $openapiUrl
|
||
if (-not $r.ok) { return @{ ok=$false; reason="openapi_unavailable"; path=$null } }
|
||
try {
|
||
$paths = $r.body.paths.PSObject.Properties.Name
|
||
$hits = @()
|
||
foreach ($p in $paths) {
|
||
$pl = $p.ToLowerInvariant()
|
||
if ($pl -like "*license*" -and $pl -like "*status*") { $hits += $p }
|
||
}
|
||
if ($hits.Count -ge 1) {
|
||
return @{ ok=$true; reason="openapi_match"; path=$hits[0]; all=$hits }
|
||
}
|
||
return @{ ok=$false; reason="openapi_no_match"; path=$null }
|
||
} catch {
|
||
return @{ ok=$false; reason="openapi_parse_failed"; path=$null }
|
||
}
|
||
}
|
||
|
||
$lastStatus = $null
|
||
# First: sanity check health so we know we're talking to the correct server
|
||
$health = Invoke-GetJsonNoAuth "$base/health"
|
||
if ($health.ok) {
|
||
Write-Host "Health: OK (GET /health)"
|
||
} else {
|
||
Write-Host "Health: not OK (GET /health) status=$($health.status)"
|
||
}
|
||
Write-Host ""
|
||
|
||
# Discover correct license/status path via OpenAPI if available
|
||
$disc = Discover-LicenseStatusPath $base
|
||
$pathsToTry = @()
|
||
if ($disc.ok -and $disc.path) {
|
||
$pathsToTry += $disc.path
|
||
if ($disc.all -and $disc.all.Count -gt 1) {
|
||
foreach ($p in $disc.all) { if ($p -ne $disc.path) { $pathsToTry += $p } }
|
||
}
|
||
Write-Host "Discovered path(s) via /openapi.json:"
|
||
$pathsToTry | ForEach-Object { Write-Host " - $_" }
|
||
} else {
|
||
# Fallback candidates (do not change server behavior, just try likely paths)
|
||
$pathsToTry = @("/license/status", "/api/license/status", "/v1/license/status", "/license/status/")
|
||
Write-Host "OpenAPI discovery not usable ($($disc.reason)). Trying common candidates:"
|
||
$pathsToTry | ForEach-Object { Write-Host " - $_" }
|
||
}
|
||
Write-Host ""
|
||
|
||
foreach ($path in $pathsToTry) {
|
||
$testUrl = "$base$path"
|
||
foreach ($t in $tokens) {
|
||
$headers = @{
|
||
"X-API-Token" = $t
|
||
"X-Device-Id" = $DeviceId
|
||
"Accept" = "application/json"
|
||
}
|
||
$res = Invoke-GetJson -u $testUrl -h $headers
|
||
if ($res.ok) {
|
||
Write-Host "OK HTTP 200 ($path)"
|
||
$json = $res.body | ConvertTo-Json -Depth 10 -Compress
|
||
Write-Host $json
|
||
exit 0
|
||
}
|
||
$lastStatus = $res.status
|
||
if ($res.status -eq 404) {
|
||
# try next path
|
||
break
|
||
}
|
||
if ($res.status -eq 401 -or $res.status -eq 403) {
|
||
# try next token
|
||
continue
|
||
}
|
||
Write-Host "FAIL HTTP $($res.status) ($path)"
|
||
if ($res.body) { Write-Host $res.body } else { Write-Host $res.message }
|
||
exit 1
|
||
}
|
||
}
|
||
|
||
Write-Host "FAIL (no matching license/status endpoint found, or all tokens unauthorized)"
|
||
Write-Host "LastStatus: $lastStatus"
|
||
exit 1
|
||
|