Initial commit: ESB Certificate Manager.

EOF

Co-authored-by: Cursor <cursoragent@cursor.com>
This commit is contained in:
2026-07-24 08:10:13 +02:00
co-authored by Cursor
commit ed8e2822d1
36 changed files with 5111 additions and 0 deletions
@@ -0,0 +1,17 @@
namespace ZA.CoreService.ESBCertificateManager.Models;
public sealed class AppSettings
{
public string ConnectionString { get; set; } = string.Empty;
public bool UseOfflineSampleData { get; set; } = true;
public string SampleTargetsPath { get; set; } = "Data/targets.sample.json";
public string LogDirectory { get; set; } = "Logs";
public int TlsTimeoutSeconds { get; set; } = 8;
public int TlsRetryCount { get; set; } = 2;
/// <summary>
/// Verbindungskonfigurationen für Progress Sonic ESB Management Instanzen.
/// Jeder Eintrag entspricht einer Sonic-Domain (z.B. einer Umgebung oder Tochtergesellschaft).
/// </summary>
public List<SonicConnection> SonicConnections { get; set; } = [];
}
@@ -0,0 +1,12 @@
namespace ZA.CoreService.ESBCertificateManager.Models
{
public sealed class CertificateInfo
{
public required string Subject { get; init; }
public required string Issuer { get; init; }
public required string FingerprintSha256 { get; init; }
public DateTimeOffset ValidFrom { get; init; }
public DateTimeOffset ValidUntil { get; init; }
public bool IsCurrentlyValid { get; init; }
}
}
@@ -0,0 +1,40 @@
namespace ZA.CoreService.ESBCertificateManager.Models;
public sealed class DeploymentRunResult
{
public Guid RunId { get; init; } = Guid.NewGuid();
public required IReadOnlyList<TargetStepResult> TargetResults { get; init; }
public bool OverallSuccess => TargetResults.All(r => r.Success);
public DateTimeOffset StartedAt { get; init; }
public DateTimeOffset FinishedAt { get; init; }
public string CertificateFilePath { get; init; } = string.Empty;
public string CertificateFingerprint { get; init; } = string.Empty;
}
public sealed class TargetStepResult
{
public required int TargetId { get; init; }
public required string TargetName { get; init; }
public bool Success { get; init; }
public required string StatusText { get; init; }
public string? Detail { get; init; }
public IReadOnlyList<string> Steps { get; init; } = [];
public DateTimeOffset StartedAt { get; init; }
public DateTimeOffset FinishedAt { get; init; }
public bool CopySucceeded { get; init; }
public bool RestartSucceeded { get; init; }
public bool TlsSucceeded { get; init; }
public string? ObservedFingerprint { get; init; }
}
public sealed class ValidationIssue
{
public required string Message { get; init; }
public int? TargetId { get; init; }
}
public sealed class PreflightValidationResult
{
public bool IsValid => Issues.Count == 0;
public List<ValidationIssue> Issues { get; } = [];
}
@@ -0,0 +1,67 @@
namespace ZA.CoreService.ESBCertificateManager.Models;
public sealed class DeploymentTarget
{
public int Id { get; init; }
public required string Name { get; init; }
public required string Environment { get; init; }
public bool IsActive { get; init; } = true;
public required string TargetDirectory { get; init; }
public required string CertificateFileName { get; init; }
/// <summary>Name des Sonic-ESB-Containers (z.B. "sonic-container-a").</summary>
public string ContainerName { get; init; } = string.Empty;
public RestartType RestartType { get; init; } = RestartType.None;
// --- Command-basierter Neustart ---
public string RestartCommand { get; init; } = string.Empty;
public string RestartArguments { get; init; } = string.Empty;
public int RestartTimeoutSeconds { get; init; } = 60;
// --- Sonic-ESB-Management-Neustart ---
/// <summary>
/// Referenz auf den Namen einer <see cref="SonicConnection"/> in AppSettings.
/// Pflichtfeld bei RestartType = SonicContainer oder SonicContainerWithXapi.
/// </summary>
public string SonicConnectionName { get; init; } = string.Empty;
/// <summary>
/// Pfad zur XApi-Ressourcendatei (.xml/.zip), die vor dem Neustart importiert wird.
/// Pflichtfeld bei RestartType = SonicContainerWithXapi.
/// </summary>
public string XapiSourcePath { get; init; } = string.Empty;
// --- TLS-Probe nach Deployment ---
public string TlsHost { get; init; } = string.Empty;
public int? TlsPort { get; init; }
/// <summary>
/// Hostname, der im TLS-Handshake als ServerName (SNI) verwendet wird.
/// Wichtig wenn TlsHost eine IP-Adresse ist, das Zertifikat aber einen DNS-Namen trägt.
/// Ist leer, wird TlsHost als ServerName verwendet.
/// </summary>
public string TlsServerName { get; init; } = string.Empty;
/// <summary>
/// Erwarteter SHA-256-Fingerprint des Zertifikats nach dem Deployment (ohne Trennzeichen).
/// Wenn gesetzt, schlägt der TLS-Probe fehl wenn der Fingerprint abweicht.
/// </summary>
public string? ExpectedFingerprint { get; init; }
public int SortOrder { get; init; }
}
public enum RestartType
{
None = 0,
/// <summary>Neustart über einen lokalen Betriebssystem-Prozess (RestartCommand).</summary>
Command = 1,
/// <summary>Container-Neustart über die Sonic ESB Management Console REST-API.</summary>
SonicContainer = 2,
/// <summary>XApi-Ressourcen importieren und danach Container neu starten (Sonic ESB).</summary>
SonicContainerWithXapi = 3
}
@@ -0,0 +1,107 @@
namespace ZA.CoreService.ESBCertificateManager.Models;
/// <summary>
/// Verbindungskonfiguration für eine Progress Sonic ESB Management Instanz.
/// </summary>
public sealed class SonicConnection
{
/// <summary>
/// Eindeutiger Bezeichner; wird in <see cref="DeploymentTarget.SonicConnectionName"/> referenziert.
/// </summary>
public required string Name { get; init; }
/// <summary>Sonic-Domain-Name, z.B. "proalpha-test".</summary>
public required string DomainName { get; init; }
/// <summary>
/// Sonic-Broker-/Management-URL im Sonic-Format, z.B. "tcp://dekun-painwbdet:13070".
/// Der Hostname wird daraus extrahiert (für HTTP-Modus: Basis-URL, für WinRM-Modus: Zielrechner).
/// </summary>
public required string ConnectionUrl { get; init; }
/// <summary>Benutzername für die Management-Konsole.</summary>
public required string Username { get; init; }
/// <summary>Passwort für die Management-Konsole.</summary>
public required string Password { get; init; }
/// <summary>
/// Management-Modus: HttpApi (REST) oder WinRm (PowerShell Remoting).
/// Standard: WinRm da Sonic 10.x kein HTTP REST API bereitstellt.
/// </summary>
public SonicManagementMode ManagementMode { get; init; } = SonicManagementMode.WinRm;
// ---------------------------------------------------------------
// HTTP REST API (ManagementMode = HttpApi)
// ---------------------------------------------------------------
/// <summary>HTTP-Port der Sonic Management Console REST-API (Standard: 8080).</summary>
public int ManagementHttpPort { get; init; } = 8080;
/// <summary>Präfix für alle REST-API-Pfade (Standard: "/api/v1").</summary>
public string ApiBasePath { get; init; } = "/api/v1";
/// <summary>Konfigurierter Pfad zum Auflisten aller Container. Leer = automatische Erkennung.</summary>
public string ContainerListPath { get; init; } = string.Empty;
/// <summary>Konfigurierter Pfad zum Neustarten. Platzhalter: {domain}, {container}.</summary>
public string ContainerRestartPath { get; init; } = string.Empty;
/// <summary>Konfigurierter Pfad zum Stoppen. Platzhalter: {domain}, {container}.</summary>
public string ContainerStopPath { get; init; } = string.Empty;
/// <summary>Konfigurierter Pfad zum Starten. Platzhalter: {domain}, {container}.</summary>
public string ContainerStartPath { get; init; } = string.Empty;
// ---------------------------------------------------------------
// WinRM / PowerShell Remoting (ManagementMode = WinRm)
// ---------------------------------------------------------------
/// <summary>
/// WinRM-Port auf dem Zielrechner (Standard: 5985 = HTTP, 5986 = HTTPS).
/// </summary>
public int WinRmPort { get; init; } = 5985;
/// <summary>
/// PowerShell-Scriptblock zum Neustarten eines Containers.
/// Platzhalter: {container} = Container-Name (nicht enkodiert), {domain} = Domain-Name.
/// Beispiel für Windows-Service: "Restart-Service -Name 'CT-ZADBService' -Force"
/// Beispiel für Sonic-Skript: "& 'C:\\Sonic\\bin\\stopContainer.bat' '{container}'; Start-Sleep 5; & 'C:\\Sonic\\bin\\startContainer.bat' '{container}'"
/// </summary>
public string WinRmRestartScript { get; init; } = string.Empty;
/// <summary>
/// PowerShell-Scriptblock zum Auflisten aller Container der Domain.
/// Ausgabe: eine Zeile pro Container-Name.
/// Beispiel: "Get-Service -DisplayName 'Sonic*' | Select-Object -ExpandProperty Name"
/// </summary>
public string WinRmContainerListScript { get; init; } = string.Empty;
/// <summary>
/// PowerShell-Scriptblock für XApi-Import.
/// Platzhalter: {container}, {xapiPath}.
/// </summary>
public string WinRmXapiImportScript { get; init; } = string.Empty;
// ---------------------------------------------------------------
// Gemeinsame Einstellungen
// ---------------------------------------------------------------
/// <summary>Timeout in Sekunden für einzelne API-/Script-Aufrufe (Standard: 60).</summary>
public int TimeoutSeconds { get; init; } = 60;
/// <summary>Wartezeit in Sekunden nach einem Container-Neustart (Standard: 15).</summary>
public int PostRestartDelaySeconds { get; init; } = 15;
}
public enum SonicManagementMode
{
/// <summary>HTTP REST API (wenn vom Sonic-Server bereitgestellt).</summary>
HttpApi,
/// <summary>
/// PowerShell Remoting (WinRM) Standard für Sonic 10.x auf Windows.
/// Führt konfigurierte Scriptblöcke via Invoke-Command auf dem Sonic-Server aus.
/// </summary>
WinRm
}