From aac71ec2cd2734434106d4b23c6d900cf4cf9df5 Mon Sep 17 00:00:00 2001 From: Mike Date: Wed, 29 Jul 2026 09:55:29 +0200 Subject: [PATCH] Allow soft-deleting certificate paths from settings. Co-authored-by: Cursor --- .../Services/SonicSetupRepository.cs | 47 +++++++++++++ .../Setup/SetupCoordinator.cs | 21 ++++++ .../Setup/SetupWizardForm.cs | 68 ++++++++++++++++++- 3 files changed, 134 insertions(+), 2 deletions(-) diff --git a/ZA.CoreService.ESBCertificateManager/Services/SonicSetupRepository.cs b/ZA.CoreService.ESBCertificateManager/Services/SonicSetupRepository.cs index 1330caa..cbdf34d 100644 --- a/ZA.CoreService.ESBCertificateManager/Services/SonicSetupRepository.cs +++ b/ZA.CoreService.ESBCertificateManager/Services/SonicSetupRepository.cs @@ -1299,6 +1299,53 @@ public sealed class SonicSetupRepository return Convert.ToInt32(result); } + public async Task DeactivateCertificateTargetAsync( + int certificateTargetId, + CancellationToken cancellationToken = default) + { + if (certificateTargetId <= 0) + { + throw new ArgumentOutOfRangeException( + nameof(certificateTargetId)); + } + + const string sql = """ + UPDATE [dbo].[CertificateTarget] + SET + [IsActive] = 0, + [ModifiedDateTime] = SYSUTCDATETIME(), + [ModifiedBy] = SUSER_SNAME() + WHERE [CertificateTargetId] = @CertificateTargetId + AND [IsActive] = 1; + """; + + await using SqlConnection connection = + new(_connectionString); + + await connection.OpenAsync(cancellationToken); + + await using SqlCommand command = + new(sql, connection); + + command.Parameters.Add( + new SqlParameter( + "@CertificateTargetId", + SqlDbType.Int) + { + Value = certificateTargetId + }); + + int affected = + await command.ExecuteNonQueryAsync(cancellationToken); + + if (affected == 0) + { + throw new InvalidOperationException( + $"Zertifikat-Pfad Id {certificateTargetId} " + + "wurde nicht gefunden oder ist bereits inaktiv."); + } + } + public async Task ExistsSonicConnectionAsync( int sonicConnectionId, CancellationToken cancellationToken = default) diff --git a/ZA.CoreService.ESBCertificateManager/Setup/SetupCoordinator.cs b/ZA.CoreService.ESBCertificateManager/Setup/SetupCoordinator.cs index 886f895..e04c6c6 100644 --- a/ZA.CoreService.ESBCertificateManager/Setup/SetupCoordinator.cs +++ b/ZA.CoreService.ESBCertificateManager/Setup/SetupCoordinator.cs @@ -466,6 +466,27 @@ public sealed class SetupCoordinator return id; } + public async Task DeactivateCertificateTargetAsync( + int certificateTargetId, + CancellationToken cancellationToken = default) + { + await _setupRepository.DeactivateCertificateTargetAsync( + certificateTargetId, + cancellationToken); + + bool stillActive = + await _setupRepository.ExistsCertificateTargetAsync( + certificateTargetId, + cancellationToken); + + if (stillActive) + { + throw new InvalidOperationException( + $"Zertifikat-Pfad Id {certificateTargetId} " + + "konnte nicht deaktiviert werden."); + } + } + private static async Task EnsureExistsAsync( Func> existsCheck, string errorMessage) diff --git a/ZA.CoreService.ESBCertificateManager/Setup/SetupWizardForm.cs b/ZA.CoreService.ESBCertificateManager/Setup/SetupWizardForm.cs index 4fdff3a..f43e91c 100644 --- a/ZA.CoreService.ESBCertificateManager/Setup/SetupWizardForm.cs +++ b/ZA.CoreService.ESBCertificateManager/Setup/SetupWizardForm.cs @@ -414,7 +414,9 @@ public sealed class SetupWizardForm : Form "+ Pfad hinzufügen", async () => await AddPathAsync(), "Pfad prüfen", - async () => await ProbeSelectedPathAsync()); + async () => await ProbeSelectedPathAsync(), + "Pfad löschen", + async () => await DeleteSelectedPathAsync()); _lvPaths = SettingsUi.CreateListView(); _lvPaths.Dock = DockStyle.Fill; @@ -432,7 +434,9 @@ public sealed class SetupWizardForm : Form string addButtonText, Func onAdd, string? secondaryText = null, - Func? onSecondary = null) + Func? onSecondary = null, + string? tertiaryText = null, + Func? onTertiary = null) { Panel toolbar = new() { @@ -447,6 +451,16 @@ public sealed class SetupWizardForm : Form add.Width = 230; add.Click += async (_, _) => await onAdd(); + if (tertiaryText is not null && onTertiary is not null) + { + Button tertiary = SettingsUi.CreateGhostButton(tertiaryText); + tertiary.Dock = DockStyle.Left; + tertiary.Width = 140; + tertiary.Margin = new Padding(8, 0, 0, 0); + tertiary.Click += async (_, _) => await onTertiary(); + toolbar.Controls.Add(tertiary); + } + if (secondaryText is not null && onSecondary is not null) { Button secondary = SettingsUi.CreateGhostButton(secondaryText); @@ -927,6 +941,56 @@ public sealed class SetupWizardForm : Form MessageBoxIcon.Information); } + private async Task DeleteSelectedPathAsync() + { + if (_lvPaths.SelectedItems.Count == 0 + || _lvPaths.SelectedItems[0].Tag is not CertificateTargetOption target) + { + MessageBox.Show( + this, + "Bitte zuerst einen Zertifikat-Pfad in der Liste wählen.", + "Pfad löschen", + MessageBoxButtons.OK, + MessageBoxIcon.Information); + return; + } + + DialogResult confirm = MessageBox.Show( + this, + "Zertifikat-Pfad wirklich aus der Konfiguration entfernen?\n\n" + + $"{target.FullTargetPath}\n\n" + + "Die Datei auf dem Server bleibt unverändert. " + + "Der Eintrag wird in SQL nur deaktiviert (IsActive=0).", + "Pfad löschen", + MessageBoxButtons.YesNo, + MessageBoxIcon.Warning); + + if (confirm != DialogResult.Yes) + { + return; + } + + try + { + await _coordinator.DeactivateCertificateTargetAsync( + target.CertificateTargetId); + + await LoadPathsListAsync(); + await RefreshAllAsync(); + + MessageBox.Show( + this, + $"Pfad Id {target.CertificateTargetId} wurde deaktiviert.", + "Pfad gelöscht", + MessageBoxButtons.OK, + MessageBoxIcon.Information); + } + catch (Exception ex) + { + ShowError(ex.Message); + } + } + private static void SelectListItem( ListView listView, Func match)