Add live TLS certificate probing and improve restart error handling.
Configure CertificateCheckUrl per container for curl-like TLS checks, classify Sonic permission errors, and extend setup wizard for container management. Co-authored-by: Cursor <cursoragent@cursor.com>
This commit is contained in:
@@ -380,6 +380,228 @@ public sealed class SonicSetupRepository
|
||||
}
|
||||
}
|
||||
|
||||
public async Task UpdateCredentialAsync(
|
||||
int sonicCredentialId,
|
||||
int sonicConnectionId,
|
||||
string credentialName,
|
||||
string userName,
|
||||
string secret,
|
||||
bool isDefault,
|
||||
CancellationToken cancellationToken = default)
|
||||
{
|
||||
if (sonicCredentialId <= 0)
|
||||
{
|
||||
throw new ArgumentOutOfRangeException(
|
||||
nameof(sonicCredentialId));
|
||||
}
|
||||
|
||||
if (sonicConnectionId <= 0)
|
||||
{
|
||||
throw new ArgumentOutOfRangeException(
|
||||
nameof(sonicConnectionId));
|
||||
}
|
||||
|
||||
if (string.IsNullOrWhiteSpace(credentialName))
|
||||
{
|
||||
throw new ArgumentException(
|
||||
"Der Profilname fehlt.",
|
||||
nameof(credentialName));
|
||||
}
|
||||
|
||||
if (string.IsNullOrWhiteSpace(userName))
|
||||
{
|
||||
throw new ArgumentException(
|
||||
"Der Sonic-Benutzername fehlt.",
|
||||
nameof(userName));
|
||||
}
|
||||
|
||||
if (string.IsNullOrEmpty(secret))
|
||||
{
|
||||
throw new ArgumentException(
|
||||
"Das Sonic-Kennwort fehlt.",
|
||||
nameof(secret));
|
||||
}
|
||||
|
||||
string normalizedCredentialName =
|
||||
credentialName.Trim();
|
||||
|
||||
string normalizedUserName =
|
||||
userName.Trim();
|
||||
|
||||
if (normalizedCredentialName.Length > 150)
|
||||
{
|
||||
throw new ArgumentException(
|
||||
"Der Profilname darf maximal 150 Zeichen enthalten.",
|
||||
nameof(credentialName));
|
||||
}
|
||||
|
||||
if (normalizedUserName.Length > 256)
|
||||
{
|
||||
throw new ArgumentException(
|
||||
"Der Benutzername darf maximal 256 Zeichen enthalten.",
|
||||
nameof(userName));
|
||||
}
|
||||
|
||||
if (secret.Length > 512)
|
||||
{
|
||||
throw new ArgumentException(
|
||||
"Das Kennwort darf maximal 512 Zeichen enthalten.",
|
||||
nameof(secret));
|
||||
}
|
||||
|
||||
await using SqlConnection connection =
|
||||
new(_connectionString);
|
||||
|
||||
await connection.OpenAsync(cancellationToken);
|
||||
|
||||
await using SqlTransaction transaction =
|
||||
(SqlTransaction)
|
||||
await connection.BeginTransactionAsync(
|
||||
cancellationToken);
|
||||
|
||||
try
|
||||
{
|
||||
if (isDefault)
|
||||
{
|
||||
const string clearDefaultSql = """
|
||||
UPDATE [dbo].[SonicCredential]
|
||||
SET
|
||||
[IsDefault] = 0,
|
||||
[ModifiedDateTime] = SYSUTCDATETIME(),
|
||||
[ModifiedBy] = SUSER_SNAME()
|
||||
WHERE [SonicConnectionId] =
|
||||
@SonicConnectionId
|
||||
AND [IsDefault] = 1
|
||||
AND [SonicCredentialId] <>
|
||||
@SonicCredentialId;
|
||||
""";
|
||||
|
||||
await using SqlCommand clearDefaultCommand =
|
||||
new(
|
||||
clearDefaultSql,
|
||||
connection,
|
||||
transaction);
|
||||
|
||||
clearDefaultCommand.Parameters.Add(
|
||||
new SqlParameter(
|
||||
"@SonicConnectionId",
|
||||
SqlDbType.Int)
|
||||
{
|
||||
Value = sonicConnectionId
|
||||
});
|
||||
|
||||
clearDefaultCommand.Parameters.Add(
|
||||
new SqlParameter(
|
||||
"@SonicCredentialId",
|
||||
SqlDbType.Int)
|
||||
{
|
||||
Value = sonicCredentialId
|
||||
});
|
||||
|
||||
await clearDefaultCommand.ExecuteNonQueryAsync(
|
||||
cancellationToken);
|
||||
}
|
||||
|
||||
const string updateSql = """
|
||||
UPDATE [dbo].[SonicCredential]
|
||||
SET
|
||||
[CredentialName] = @CredentialName,
|
||||
[CredentialUserName] = @CredentialUserName,
|
||||
[CredentialSecret] = @CredentialSecret,
|
||||
[IsDefault] = @IsDefault,
|
||||
[ModifiedDateTime] = SYSUTCDATETIME(),
|
||||
[ModifiedBy] = SUSER_SNAME()
|
||||
WHERE [SonicCredentialId] = @SonicCredentialId
|
||||
AND [SonicConnectionId] = @SonicConnectionId
|
||||
AND [IsActive] = 1;
|
||||
""";
|
||||
|
||||
await using SqlCommand updateCommand =
|
||||
new(
|
||||
updateSql,
|
||||
connection,
|
||||
transaction);
|
||||
|
||||
updateCommand.Parameters.Add(
|
||||
new SqlParameter(
|
||||
"@SonicCredentialId",
|
||||
SqlDbType.Int)
|
||||
{
|
||||
Value = sonicCredentialId
|
||||
});
|
||||
|
||||
updateCommand.Parameters.Add(
|
||||
new SqlParameter(
|
||||
"@SonicConnectionId",
|
||||
SqlDbType.Int)
|
||||
{
|
||||
Value = sonicConnectionId
|
||||
});
|
||||
|
||||
updateCommand.Parameters.Add(
|
||||
new SqlParameter(
|
||||
"@CredentialName",
|
||||
SqlDbType.NVarChar,
|
||||
150)
|
||||
{
|
||||
Value = normalizedCredentialName
|
||||
});
|
||||
|
||||
updateCommand.Parameters.Add(
|
||||
new SqlParameter(
|
||||
"@CredentialUserName",
|
||||
SqlDbType.NVarChar,
|
||||
256)
|
||||
{
|
||||
Value = normalizedUserName
|
||||
});
|
||||
|
||||
updateCommand.Parameters.Add(
|
||||
new SqlParameter(
|
||||
"@CredentialSecret",
|
||||
SqlDbType.NVarChar,
|
||||
512)
|
||||
{
|
||||
Value = secret
|
||||
});
|
||||
|
||||
updateCommand.Parameters.Add(
|
||||
new SqlParameter(
|
||||
"@IsDefault",
|
||||
SqlDbType.Bit)
|
||||
{
|
||||
Value = isDefault
|
||||
});
|
||||
|
||||
int affected =
|
||||
await updateCommand.ExecuteNonQueryAsync(
|
||||
cancellationToken);
|
||||
|
||||
if (affected == 0)
|
||||
{
|
||||
throw new InvalidOperationException(
|
||||
$"Benutzerprofil Id {sonicCredentialId} "
|
||||
+ "wurde nicht gefunden oder ist inaktiv.");
|
||||
}
|
||||
|
||||
await transaction.CommitAsync(cancellationToken);
|
||||
}
|
||||
catch
|
||||
{
|
||||
try
|
||||
{
|
||||
await transaction.RollbackAsync(
|
||||
CancellationToken.None);
|
||||
}
|
||||
catch
|
||||
{
|
||||
// Die ursprüngliche Exception soll erhalten bleiben.
|
||||
}
|
||||
|
||||
throw;
|
||||
}
|
||||
}
|
||||
|
||||
public async Task<IReadOnlyList<SonicCredentialProfile>>
|
||||
GetCredentialsAsync(
|
||||
int sonicConnectionId,
|
||||
@@ -663,6 +885,7 @@ public sealed class SonicSetupRepository
|
||||
container.[CompanyId],
|
||||
container.[ContainerName],
|
||||
container.[ContainerDisplayName],
|
||||
container.[CertificateCheckUrl],
|
||||
container.[RestartTimeoutSeconds],
|
||||
company.[CompanyCode],
|
||||
connection.[ConnectionName]
|
||||
@@ -729,6 +952,9 @@ public sealed class SonicSetupRepository
|
||||
int displayOrdinal =
|
||||
reader.GetOrdinal("ContainerDisplayName");
|
||||
|
||||
int checkUrlOrdinal =
|
||||
reader.GetOrdinal("CertificateCheckUrl");
|
||||
|
||||
containers.Add(
|
||||
new ContainerOption
|
||||
{
|
||||
@@ -753,6 +979,11 @@ public sealed class SonicSetupRepository
|
||||
? null
|
||||
: reader.GetString(displayOrdinal),
|
||||
|
||||
CertificateCheckUrl =
|
||||
reader.IsDBNull(checkUrlOrdinal)
|
||||
? null
|
||||
: reader.GetString(checkUrlOrdinal),
|
||||
|
||||
RestartTimeoutSeconds =
|
||||
reader.GetInt32(
|
||||
reader.GetOrdinal("RestartTimeoutSeconds")),
|
||||
@@ -1057,6 +1288,7 @@ public sealed class SonicSetupRepository
|
||||
string containerName,
|
||||
string? containerDisplayName,
|
||||
int restartTimeoutSeconds,
|
||||
string? certificateCheckUrl = null,
|
||||
CancellationToken cancellationToken = default)
|
||||
{
|
||||
if (companyId <= 0)
|
||||
@@ -1093,6 +1325,9 @@ public sealed class SonicSetupRepository
|
||||
"Timeout muss zwischen 10 und 3600 Sekunden liegen.");
|
||||
}
|
||||
|
||||
string? checkUrl =
|
||||
TlsEndpointProbeService.NormalizeCheckUrl(certificateCheckUrl);
|
||||
|
||||
const string sql = """
|
||||
INSERT INTO [dbo].[SonicContainer]
|
||||
(
|
||||
@@ -1100,6 +1335,7 @@ public sealed class SonicSetupRepository
|
||||
[SonicConnectionId],
|
||||
[ContainerName],
|
||||
[ContainerDisplayName],
|
||||
[CertificateCheckUrl],
|
||||
[RestartTimeoutSeconds],
|
||||
[IsActive],
|
||||
[CreationDateTime],
|
||||
@@ -1112,6 +1348,7 @@ public sealed class SonicSetupRepository
|
||||
@SonicConnectionId,
|
||||
@ContainerName,
|
||||
@ContainerDisplayName,
|
||||
@CertificateCheckUrl,
|
||||
@RestartTimeoutSeconds,
|
||||
1,
|
||||
SYSUTCDATETIME(),
|
||||
@@ -1159,6 +1396,17 @@ public sealed class SonicSetupRepository
|
||||
: display
|
||||
});
|
||||
|
||||
command.Parameters.Add(
|
||||
new SqlParameter(
|
||||
"@CertificateCheckUrl",
|
||||
SqlDbType.NVarChar,
|
||||
500)
|
||||
{
|
||||
Value = checkUrl is null
|
||||
? DBNull.Value
|
||||
: checkUrl
|
||||
});
|
||||
|
||||
command.Parameters.Add(
|
||||
new SqlParameter(
|
||||
"@RestartTimeoutSeconds",
|
||||
@@ -1457,6 +1705,7 @@ public sealed class SonicSetupRepository
|
||||
string containerName,
|
||||
string? containerDisplayName,
|
||||
int restartTimeoutSeconds,
|
||||
string? certificateCheckUrl = null,
|
||||
CancellationToken cancellationToken = default)
|
||||
{
|
||||
if (sonicContainerId <= 0)
|
||||
@@ -1499,6 +1748,9 @@ public sealed class SonicSetupRepository
|
||||
"Timeout muss zwischen 10 und 3600 Sekunden liegen.");
|
||||
}
|
||||
|
||||
string? checkUrl =
|
||||
TlsEndpointProbeService.NormalizeCheckUrl(certificateCheckUrl);
|
||||
|
||||
const string sql = """
|
||||
UPDATE [dbo].[SonicContainer]
|
||||
SET
|
||||
@@ -1506,6 +1758,7 @@ public sealed class SonicSetupRepository
|
||||
[SonicConnectionId] = @SonicConnectionId,
|
||||
[ContainerName] = @ContainerName,
|
||||
[ContainerDisplayName] = @ContainerDisplayName,
|
||||
[CertificateCheckUrl] = @CertificateCheckUrl,
|
||||
[RestartTimeoutSeconds] = @RestartTimeoutSeconds,
|
||||
[ModifiedDateTime] = SYSUTCDATETIME(),
|
||||
[ModifiedBy] = SUSER_SNAME()
|
||||
@@ -1559,6 +1812,17 @@ public sealed class SonicSetupRepository
|
||||
: display
|
||||
});
|
||||
|
||||
command.Parameters.Add(
|
||||
new SqlParameter(
|
||||
"@CertificateCheckUrl",
|
||||
SqlDbType.NVarChar,
|
||||
500)
|
||||
{
|
||||
Value = checkUrl is null
|
||||
? DBNull.Value
|
||||
: checkUrl
|
||||
});
|
||||
|
||||
command.Parameters.Add(
|
||||
new SqlParameter(
|
||||
"@RestartTimeoutSeconds",
|
||||
|
||||
Reference in New Issue
Block a user