Files
123123/ZA.CoreService.ESBCertificateManager/Services/SonicCredentialTester.cs
T

303 lines
8.2 KiB
C#

using System.Diagnostics;
using System.Text;
using ZA.CoreService.ESBCertificateManager.Models;
namespace ZA.CoreService.ESBCertificateManager.Services;
public sealed class SonicCredentialTester
{
private readonly RuntimeSettings _runtimeSettings;
public SonicCredentialTester(
RuntimeSettings runtimeSettings)
{
_runtimeSettings =
runtimeSettings
?? throw new ArgumentNullException(
nameof(runtimeSettings));
}
public async Task<CredentialTestResult> TestAsync(
SonicSystemOption system,
string userName,
string secret,
string containerName,
CancellationToken cancellationToken = default)
{
ArgumentNullException.ThrowIfNull(system);
if (string.IsNullOrWhiteSpace(userName))
{
return CredentialTestResult.Failed(
"Der Sonic-Benutzername fehlt.");
}
if (string.IsNullOrEmpty(secret))
{
return CredentialTestResult.Failed(
"Das Sonic-Kennwort fehlt.");
}
if (string.IsNullOrWhiteSpace(containerName))
{
return CredentialTestResult.Failed(
"Für den Verbindungstest fehlt ein Container.");
}
string? javaPath =
JavaRuntimeLocator.Resolve(
_runtimeSettings.JavaExecutablePath);
string libraryPath =
PathResolver.ResolvePath(
_runtimeSettings.SonicClientLibraryPath);
string? toolsPath =
ResolveToolsDirectory();
if (javaPath is null)
{
return CredentialTestResult.Failed(
"Java 8 wurde nicht gefunden. "
+ "Runtime:JavaExecutablePath prüfen "
+ @"(z. B. C:\Program Files (x86)\Java\jre1.8.0_481\bin\java.exe).");
}
if (!Directory.Exists(libraryPath))
{
return CredentialTestResult.Failed(
$"Der Sonic-JAR-Ordner wurde nicht gefunden: {libraryPath}");
}
if (toolsPath is null)
{
return CredentialTestResult.Failed(
"SonicMfContainerTool.class wurde nicht gefunden.");
}
string[] jars =
Directory.GetFiles(
libraryPath,
"*.jar",
SearchOption.TopDirectoryOnly);
if (jars.Length == 0)
{
return CredentialTestResult.Failed(
"Im Sonic-Client-Ordner wurden keine JAR-Dateien gefunden.");
}
string classpath =
toolsPath
+ Path.PathSeparator
+ string.Join(
Path.PathSeparator,
jars.OrderBy(
Path.GetFileName,
StringComparer.OrdinalIgnoreCase));
ProcessStartInfo startInfo = new()
{
FileName = javaPath,
WorkingDirectory = toolsPath,
UseShellExecute = false,
RedirectStandardOutput = true,
RedirectStandardError = true,
StandardOutputEncoding = Encoding.UTF8,
StandardErrorEncoding = Encoding.UTF8,
CreateNoWindow = true
};
startInfo.ArgumentList.Add("-cp");
startInfo.ArgumentList.Add(classpath);
startInfo.ArgumentList.Add(
"SonicMfContainerTool");
startInfo.ArgumentList.Add("validate");
startInfo.ArgumentList.Add("--domain");
startInfo.ArgumentList.Add(
system.DomainName);
startInfo.ArgumentList.Add("--url");
startInfo.ArgumentList.Add(
system.ConnectionUrl);
startInfo.ArgumentList.Add("--user");
startInfo.ArgumentList.Add(
userName.Trim());
startInfo.ArgumentList.Add("--container");
startInfo.ArgumentList.Add(
containerName);
startInfo.ArgumentList.Add("--timeout");
startInfo.ArgumentList.Add("30");
startInfo.Environment[
"ESB_SONIC_PASSWORD"] = secret;
using Process process = new()
{
StartInfo = startInfo
};
try
{
if (!process.Start())
{
return CredentialTestResult.Failed(
"Der Java-Prozess konnte nicht gestartet werden.");
}
}
catch (Exception ex)
{
return CredentialTestResult.Failed(
$"Java konnte nicht gestartet werden: {ex.Message}");
}
Task<string> standardOutputTask =
process.StandardOutput.ReadToEndAsync();
Task<string> standardErrorTask =
process.StandardError.ReadToEndAsync();
using CancellationTokenSource timeoutSource =
CancellationTokenSource.CreateLinkedTokenSource(
cancellationToken);
timeoutSource.CancelAfter(
TimeSpan.FromSeconds(35));
try
{
await process.WaitForExitAsync(
timeoutSource.Token);
}
catch (OperationCanceledException)
when (!cancellationToken.IsCancellationRequested)
{
TryKill(process);
return CredentialTestResult.Failed(
"Der Sonic-Verbindungstest hat das Zeitlimit überschritten.");
}
catch (OperationCanceledException)
{
TryKill(process);
throw;
}
string standardOutput =
(await standardOutputTask).Trim();
string standardError =
(await standardErrorTask).Trim();
string combinedOutput =
string.Join(
Environment.NewLine,
new[]
{
standardOutput,
standardError
}
.Where(value =>
!string.IsNullOrWhiteSpace(value)));
bool success =
process.ExitCode == 0
&& combinedOutput.Contains(
"OK:ConnectionValidated",
StringComparison.OrdinalIgnoreCase)
&& !combinedOutput.Contains(
"ERROR:",
StringComparison.OrdinalIgnoreCase);
if (success)
{
return CredentialTestResult.Successful();
}
string error =
ExtractError(combinedOutput)
?? $"Sonic-Verbindungstest fehlgeschlagen, " +
$"ExitCode={process.ExitCode}.";
return CredentialTestResult.Failed(error);
}
private static string? ResolveToolsDirectory()
{
string[] candidates =
[
Path.Combine(
AppContext.BaseDirectory,
"Tools"),
Path.Combine(
Directory.GetCurrentDirectory(),
"Tools")
];
return candidates.FirstOrDefault(
directory =>
File.Exists(
Path.Combine(
directory,
"SonicMfContainerTool.class")));
}
private static string? ExtractError(
string output)
{
return output
.Split(
['\r', '\n'],
StringSplitOptions.RemoveEmptyEntries)
.FirstOrDefault(
line =>
line.StartsWith(
"ERROR:",
StringComparison.OrdinalIgnoreCase))
?["ERROR:".Length..]
.Trim();
}
private static void TryKill(
Process process)
{
try
{
if (!process.HasExited)
{
process.Kill(
entireProcessTree: true);
}
}
catch
{
// Der ursprüngliche Testfehler bleibt erhalten.
}
}
}
public sealed record CredentialTestResult(
bool Success,
string Message)
{
public static CredentialTestResult Successful()
{
return new CredentialTestResult(
true,
"Sonic-Anmeldung und lesender Containerzugriff waren erfolgreich.");
}
public static CredentialTestResult Failed(
string message)
{
return new CredentialTestResult(
false,
message);
}
}