143 lines
5.9 KiB
PowerShell
143 lines
5.9 KiB
PowerShell
# ============================================================
|
|
# Omika Diagnostic Center - Durcissement PostgreSQL (moindre privilege)
|
|
# Applique UNIQUEMENT 003_diagnostics_roles.sql (roles + ownership + grants).
|
|
# ============================================================
|
|
# A executer SUR LE SERVEUR (Windows Server 2025), PowerShell administrateur,
|
|
# avec un compte PostgreSQL ADMINISTRATIF (superutilisateur, ex. postgres).
|
|
#
|
|
# Ce script est VOLONTAIREMENT SEPARE de Install-DatabaseSchema.ps1 :
|
|
# 003 transfere l'ownership et gere les roles -> contexte administrateur.
|
|
# 001/002 (schema) sont appliques par Install-DatabaseSchema.ps1.
|
|
#
|
|
# NOTE (V2) : Install-DatabaseSchema.ps1 applique desormais TOUTES les
|
|
# migrations 001 -> 016 dans l'ordre (schema + roles + grants). Ce script reste
|
|
# disponible pour n'appliquer QUE 003 (transition/back-compat) ; pour une
|
|
# installation complete, preferer Install-DatabaseSchema.ps1.
|
|
#
|
|
# Garanties :
|
|
# - AUCUN mot de passe en dur, AUCUN secret (les mots de passe des roles
|
|
# LOGIN sont definis MANUELLEMENT ensuite via psql : \password <role>).
|
|
# - AUCUN DROP, AUCUNE suppression de role/objet/donnee (003 est idempotent).
|
|
# - Ne touche PAS au role historique omika_diagnostics_app (transition).
|
|
#
|
|
# Usage :
|
|
# $env:PGPASSWORD = '...' # facultatif - sinon saisie masquee demandee
|
|
# .\Harden-DatabaseRoles.ps1
|
|
# .\Harden-DatabaseRoles.ps1 -DatabaseDir "C:\Omika\Deploy\phase5b2\Database"
|
|
# .\Harden-DatabaseRoles.ps1 -PsqlPath "C:\Program Files\PostgreSQL\18\bin\psql.exe"
|
|
# .\Harden-DatabaseRoles.ps1 -Username postgres
|
|
# ============================================================
|
|
|
|
[CmdletBinding()]
|
|
param(
|
|
[string]$DatabaseDir,
|
|
[string]$PsqlPath,
|
|
[string]$PgHost = '127.0.0.1',
|
|
[int]$PgPort = 5432,
|
|
[string]$Database = 'omika_diagnostics',
|
|
[string]$Username = 'postgres'
|
|
)
|
|
|
|
$ErrorActionPreference = 'Stop'
|
|
|
|
# --- Resolution de DatabaseDir APRES le bloc param (bug $PSScriptRoot).
|
|
# Valeur par defaut : dossier frere "Database" du dossier du script.
|
|
if (-not $DatabaseDir) {
|
|
if ($PSScriptRoot) {
|
|
$DatabaseDir = Join-Path (Split-Path -Parent $PSScriptRoot) 'Database'
|
|
}
|
|
}
|
|
if (-not $DatabaseDir -or -not (Test-Path -LiteralPath $DatabaseDir)) {
|
|
Write-Error "Dossier des schemas introuvable : $DatabaseDir. Fournissez -DatabaseDir '<chemin>\Database'."
|
|
exit 1
|
|
}
|
|
|
|
# --- Detection de psql.exe (ordre : -PsqlPath, PATH, C:\Program Files\PostgreSQL\*).
|
|
function Resolve-PsqlPath {
|
|
param([string]$ExplicitPath)
|
|
|
|
if ($ExplicitPath) {
|
|
if (-not (Test-Path -LiteralPath $ExplicitPath)) {
|
|
Write-Error "PsqlPath fourni introuvable : $ExplicitPath"
|
|
exit 1
|
|
}
|
|
return (Resolve-Path -LiteralPath $ExplicitPath).Path
|
|
}
|
|
|
|
$inPath = Get-Command psql.exe -ErrorAction SilentlyContinue
|
|
if ($inPath) {
|
|
return $inPath.Source
|
|
}
|
|
|
|
$candidates = @(Get-ChildItem -Path 'C:\Program Files\PostgreSQL\*\bin\psql.exe' -ErrorAction SilentlyContinue)
|
|
if ($candidates.Count -gt 0) {
|
|
$best = $candidates | ForEach-Object {
|
|
$version = 0
|
|
$verDir = $_.Directory.Parent.Name
|
|
if ($verDir -match '^\d+$') { $version = [int]$verDir }
|
|
[PSCustomObject]@{ Path = $_.FullName; Version = $version }
|
|
} | Sort-Object -Property Version -Descending | Select-Object -First 1
|
|
return $best.Path
|
|
}
|
|
|
|
return $null
|
|
}
|
|
|
|
$psqlExe = Resolve-PsqlPath -ExplicitPath $PsqlPath
|
|
if (-not $psqlExe) {
|
|
Write-Error "Client 'psql.exe' introuvable. Fournissez -PsqlPath ou installez le client PostgreSQL."
|
|
exit 1
|
|
}
|
|
Write-Host "psql utilise : $psqlExe"
|
|
|
|
# --- Mot de passe : priorite a PGPASSWORD deja present ; sinon saisie masquee.
|
|
$useExistingPgPassword = -not [string]::IsNullOrEmpty($env:PGPASSWORD)
|
|
if (-not $useExistingPgPassword) {
|
|
$secure = Read-Host "Mot de passe PostgreSQL (role '$Username', base '$Database')" -AsSecureString
|
|
$plain = [Runtime.InteropServices.Marshal]::PtrToStringAuto(
|
|
[Runtime.InteropServices.Marshal]::SecureStringToBSTR($secure))
|
|
$env:PGPASSWORD = $plain
|
|
$plain = $null
|
|
}
|
|
|
|
$sqlPath = Join-Path $DatabaseDir '003_diagnostics_roles.sql'
|
|
if (-not (Test-Path -LiteralPath $sqlPath)) {
|
|
throw "Migration de securite introuvable : $sqlPath"
|
|
}
|
|
|
|
try {
|
|
Write-Host "Application du durcissement : 003_diagnostics_roles.sql (${PgHost}:${PgPort}/${Database}, role $Username) ..."
|
|
& $psqlExe `
|
|
-h $PgHost `
|
|
-p $PgPort `
|
|
-U $Username `
|
|
-d $Database `
|
|
-v ON_ERROR_STOP=1 `
|
|
-f $sqlPath
|
|
|
|
if ($LASTEXITCODE -ne 0) {
|
|
throw "psql a echoue (exit $LASTEXITCODE) sur 003_diagnostics_roles.sql."
|
|
}
|
|
|
|
Write-Host ''
|
|
Write-Host "Durcissement applique (idempotent)." -ForegroundColor Green
|
|
Write-Host "Roles : omika_diagnostics_owner (NOLOGIN), omika_diagnostics_worker, omika_diagnostics_web."
|
|
Write-Host "Worker : INSERT diagnostic_event + USAGE sequence. Web : SELECT diagnostic_event."
|
|
Write-Host ''
|
|
Write-Host "ETAPES MANUELLES SUIVANTES (jamais automatisees ici) :" -ForegroundColor Yellow
|
|
Write-Host " 1. Definir les mots de passe (psql connecte comme administrateur PostgreSQL) :"
|
|
Write-Host " \password omika_diagnostics_worker"
|
|
Write-Host " \password omika_diagnostics_web"
|
|
Write-Host " (jamais dans un script, un log, une commande en clair, ni PowerShell history)."
|
|
Write-Host " 2. Pointer Worker : Username=omika_diagnostics_worker ; Web : Username=omika_diagnostics_web"
|
|
Write-Host " (ConnectionStrings__DiagnosticsDatabase, secret injecte SUR LE SERVEUR uniquement)."
|
|
Write-Host " 3. Valider Worker (INSERT) et Web (SELECT)."
|
|
Write-Host " 4. SEULEMENT APRES validation : desactiver l'ancien role (action administrateur explicite) :"
|
|
Write-Host " ALTER ROLE omika_diagnostics_app NOLOGIN;"
|
|
}
|
|
finally {
|
|
if (-not $useExistingPgPassword) {
|
|
Remove-Item Env:PGPASSWORD -ErrorAction SilentlyContinue
|
|
}
|
|
}
|