big changes
This commit is contained in:
@@ -0,0 +1,185 @@
|
||||
namespace ZA.CoreService.ESBCertificateManager.Services;
|
||||
|
||||
public sealed class LocalCertificateDeployer
|
||||
{
|
||||
private static readonly HashSet<string> CertificateExtensions =
|
||||
new(StringComparer.OrdinalIgnoreCase)
|
||||
{
|
||||
".cer",
|
||||
".crt",
|
||||
".pem",
|
||||
".pfx"
|
||||
};
|
||||
|
||||
public async Task<string> DeployAsync(
|
||||
string sourceCertificatePath,
|
||||
string configuredTargetDirectory,
|
||||
string targetFileName,
|
||||
bool backupEnabled,
|
||||
string backupDirectoryName,
|
||||
CancellationToken cancellationToken)
|
||||
{
|
||||
if (string.IsNullOrWhiteSpace(sourceCertificatePath))
|
||||
{
|
||||
throw new ArgumentException(
|
||||
"Es wurde keine Zertifikatsquelle angegeben.",
|
||||
nameof(sourceCertificatePath));
|
||||
}
|
||||
|
||||
if (!File.Exists(sourceCertificatePath))
|
||||
{
|
||||
throw new FileNotFoundException(
|
||||
"Die ausgewählte Zertifikatsdatei wurde nicht gefunden.",
|
||||
sourceCertificatePath);
|
||||
}
|
||||
|
||||
if (string.IsNullOrWhiteSpace(configuredTargetDirectory))
|
||||
{
|
||||
throw new ArgumentException(
|
||||
"Der Zertifikats-Zielordner ist nicht konfiguriert.",
|
||||
nameof(configuredTargetDirectory));
|
||||
}
|
||||
|
||||
if (string.IsNullOrWhiteSpace(targetFileName))
|
||||
{
|
||||
throw new ArgumentException(
|
||||
"Der Zertifikats-Zieldateiname ist nicht konfiguriert.",
|
||||
nameof(targetFileName));
|
||||
}
|
||||
|
||||
if (Path.GetFileName(targetFileName) != targetFileName)
|
||||
{
|
||||
throw new InvalidOperationException(
|
||||
"TargetFileName darf keinen Verzeichnispfad enthalten.");
|
||||
}
|
||||
|
||||
string targetDirectory = Path.GetFullPath(
|
||||
Path.IsPathRooted(configuredTargetDirectory)
|
||||
? configuredTargetDirectory
|
||||
: Path.Combine(
|
||||
AppContext.BaseDirectory,
|
||||
configuredTargetDirectory));
|
||||
|
||||
cancellationToken.ThrowIfCancellationRequested();
|
||||
|
||||
Directory.CreateDirectory(targetDirectory);
|
||||
|
||||
string destinationPath = Path.Combine(
|
||||
targetDirectory,
|
||||
targetFileName);
|
||||
|
||||
string temporaryPath = destinationPath + ".tmp";
|
||||
|
||||
try
|
||||
{
|
||||
if (backupEnabled)
|
||||
{
|
||||
BackupExistingCertificates(
|
||||
targetDirectory,
|
||||
backupDirectoryName,
|
||||
cancellationToken);
|
||||
}
|
||||
|
||||
await using (FileStream source = new(
|
||||
sourceCertificatePath,
|
||||
FileMode.Open,
|
||||
FileAccess.Read,
|
||||
FileShare.Read))
|
||||
await using (FileStream temporaryTarget = new(
|
||||
temporaryPath,
|
||||
FileMode.Create,
|
||||
FileAccess.Write,
|
||||
FileShare.None))
|
||||
{
|
||||
await source.CopyToAsync(
|
||||
temporaryTarget,
|
||||
cancellationToken);
|
||||
}
|
||||
|
||||
cancellationToken.ThrowIfCancellationRequested();
|
||||
|
||||
File.Move(
|
||||
temporaryPath,
|
||||
destinationPath,
|
||||
overwrite: true);
|
||||
|
||||
return destinationPath;
|
||||
}
|
||||
catch
|
||||
{
|
||||
TryDeleteTemporaryFile(temporaryPath);
|
||||
throw;
|
||||
}
|
||||
}
|
||||
|
||||
private static void BackupExistingCertificates(
|
||||
string targetDirectory,
|
||||
string configuredBackupDirectoryName,
|
||||
CancellationToken cancellationToken)
|
||||
{
|
||||
List<string> filesToBackup = Directory
|
||||
.EnumerateFiles(
|
||||
targetDirectory,
|
||||
"*",
|
||||
SearchOption.TopDirectoryOnly)
|
||||
.Where(path =>
|
||||
CertificateExtensions.Contains(
|
||||
Path.GetExtension(path)))
|
||||
.ToList();
|
||||
|
||||
if (filesToBackup.Count == 0)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
string backupDirectoryName =
|
||||
string.IsNullOrWhiteSpace(configuredBackupDirectoryName)
|
||||
? "Backup"
|
||||
: configuredBackupDirectoryName.Trim();
|
||||
|
||||
if (Path.GetFileName(backupDirectoryName)
|
||||
!= backupDirectoryName)
|
||||
{
|
||||
throw new InvalidOperationException(
|
||||
"BackupDirectoryName darf keinen Verzeichnispfad enthalten.");
|
||||
}
|
||||
|
||||
string backupDirectory = Path.Combine(
|
||||
targetDirectory,
|
||||
backupDirectoryName,
|
||||
DateTime.Now.ToString("yyyyMMdd_HHmmss_fff"));
|
||||
|
||||
Directory.CreateDirectory(backupDirectory);
|
||||
|
||||
foreach (string certificateFile in filesToBackup)
|
||||
{
|
||||
cancellationToken.ThrowIfCancellationRequested();
|
||||
|
||||
string backupPath = Path.Combine(
|
||||
backupDirectory,
|
||||
Path.GetFileName(certificateFile));
|
||||
|
||||
File.Copy(
|
||||
certificateFile,
|
||||
backupPath,
|
||||
overwrite: false);
|
||||
}
|
||||
}
|
||||
|
||||
private static void TryDeleteTemporaryFile(
|
||||
string temporaryPath)
|
||||
{
|
||||
try
|
||||
{
|
||||
if (File.Exists(temporaryPath))
|
||||
{
|
||||
File.Delete(temporaryPath);
|
||||
}
|
||||
}
|
||||
catch
|
||||
{
|
||||
// Der ursprüngliche Fehler darf nicht verdeckt werden.
|
||||
// TODO: Aufräumfehler später separat protokollieren.
|
||||
}
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user