Auto-migrate SonicContainer schema on startup, rename legacy typo columns, and use fallback SQL when the column is not yet present. Co-authored-by: Cursor <cursoragent@cursor.com>
2252 lines
66 KiB
C#
2252 lines
66 KiB
C#
using System.Data;
|
|
using Microsoft.Data.SqlClient;
|
|
using ZA.CoreService.ESBCertificateManager.Models;
|
|
|
|
namespace ZA.CoreService.ESBCertificateManager.Services;
|
|
|
|
public sealed class SonicSetupRepository
|
|
{
|
|
private readonly string _connectionString;
|
|
|
|
public SonicSetupRepository(string connectionString)
|
|
{
|
|
if (string.IsNullOrWhiteSpace(connectionString))
|
|
{
|
|
throw new ArgumentException(
|
|
"Der SQL-Connection-String fehlt.",
|
|
nameof(connectionString));
|
|
}
|
|
|
|
_connectionString = connectionString;
|
|
}
|
|
|
|
public async Task<IReadOnlyList<SonicSystemOption>> GetSystemsAsync(
|
|
string environmentCode,
|
|
CancellationToken cancellationToken = default)
|
|
{
|
|
if (string.IsNullOrWhiteSpace(environmentCode))
|
|
{
|
|
throw new ArgumentException(
|
|
"Der EnvironmentCode fehlt.",
|
|
nameof(environmentCode));
|
|
}
|
|
|
|
const string sql = """
|
|
SELECT
|
|
connection.[SonicConnectionId],
|
|
connection.[ConnectionName],
|
|
environment.[EnvironmentCode],
|
|
connection.[DomainName],
|
|
connection.[ManagementHost],
|
|
connection.[ManagementPort],
|
|
connection.[ConnectionProtocol],
|
|
MIN(container.[ContainerName])
|
|
AS [ValidationContainerName]
|
|
FROM [dbo].[SonicConnection] AS connection
|
|
INNER JOIN [dbo].[Environment] AS environment
|
|
ON environment.[EnvironmentId] =
|
|
connection.[EnvironmentId]
|
|
LEFT JOIN [dbo].[SonicContainer] AS container
|
|
ON container.[SonicConnectionId] =
|
|
connection.[SonicConnectionId]
|
|
AND container.[IsActive] = 1
|
|
WHERE connection.[IsActive] = 1
|
|
AND environment.[IsActive] = 1
|
|
AND environment.[EnvironmentCode] =
|
|
@EnvironmentCode
|
|
GROUP BY
|
|
connection.[SonicConnectionId],
|
|
connection.[ConnectionName],
|
|
environment.[EnvironmentCode],
|
|
connection.[DomainName],
|
|
connection.[ManagementHost],
|
|
connection.[ManagementPort],
|
|
connection.[ConnectionProtocol]
|
|
ORDER BY connection.[ConnectionName];
|
|
""";
|
|
|
|
List<SonicSystemOption> systems = [];
|
|
|
|
await using SqlConnection connection =
|
|
new(_connectionString);
|
|
|
|
await connection.OpenAsync(cancellationToken);
|
|
|
|
await using SqlCommand command =
|
|
new(sql, connection);
|
|
|
|
command.Parameters.Add(
|
|
new SqlParameter(
|
|
"@EnvironmentCode",
|
|
SqlDbType.NVarChar,
|
|
30)
|
|
{
|
|
Value = environmentCode
|
|
.Trim()
|
|
.ToUpperInvariant()
|
|
});
|
|
|
|
await using SqlDataReader reader =
|
|
await command.ExecuteReaderAsync(cancellationToken);
|
|
|
|
int sonicConnectionIdOrdinal =
|
|
reader.GetOrdinal("SonicConnectionId");
|
|
|
|
int connectionNameOrdinal =
|
|
reader.GetOrdinal("ConnectionName");
|
|
|
|
int environmentCodeOrdinal =
|
|
reader.GetOrdinal("EnvironmentCode");
|
|
|
|
int domainNameOrdinal =
|
|
reader.GetOrdinal("DomainName");
|
|
|
|
int managementHostOrdinal =
|
|
reader.GetOrdinal("ManagementHost");
|
|
|
|
int managementPortOrdinal =
|
|
reader.GetOrdinal("ManagementPort");
|
|
|
|
int connectionProtocolOrdinal =
|
|
reader.GetOrdinal("ConnectionProtocol");
|
|
|
|
int validationContainerNameOrdinal =
|
|
reader.GetOrdinal("ValidationContainerName");
|
|
|
|
while (await reader.ReadAsync(cancellationToken))
|
|
{
|
|
systems.Add(
|
|
new SonicSystemOption
|
|
{
|
|
SonicConnectionId =
|
|
reader.GetInt32(
|
|
sonicConnectionIdOrdinal),
|
|
|
|
ConnectionName =
|
|
reader.GetString(
|
|
connectionNameOrdinal),
|
|
|
|
EnvironmentCode =
|
|
reader.GetString(
|
|
environmentCodeOrdinal),
|
|
|
|
DomainName =
|
|
reader.IsDBNull(domainNameOrdinal)
|
|
? string.Empty
|
|
: reader.GetString(
|
|
domainNameOrdinal),
|
|
|
|
ManagementHost =
|
|
reader.GetString(
|
|
managementHostOrdinal),
|
|
|
|
ManagementPort =
|
|
reader.GetInt32(
|
|
managementPortOrdinal),
|
|
|
|
ConnectionProtocol =
|
|
reader.IsDBNull(connectionProtocolOrdinal)
|
|
? "tcp"
|
|
: reader.GetString(
|
|
connectionProtocolOrdinal),
|
|
|
|
ValidationContainerName =
|
|
reader.IsDBNull(
|
|
validationContainerNameOrdinal)
|
|
? string.Empty
|
|
: reader.GetString(
|
|
validationContainerNameOrdinal)
|
|
});
|
|
}
|
|
|
|
return systems;
|
|
}
|
|
|
|
public async Task<int> AddCredentialAsync(
|
|
int sonicConnectionId,
|
|
string credentialName,
|
|
string userName,
|
|
string secret,
|
|
bool isDefault,
|
|
CancellationToken cancellationToken = default)
|
|
{
|
|
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;
|
|
""";
|
|
|
|
await using SqlCommand clearDefaultCommand =
|
|
new(
|
|
clearDefaultSql,
|
|
connection,
|
|
transaction);
|
|
|
|
clearDefaultCommand.Parameters.Add(
|
|
new SqlParameter(
|
|
"@SonicConnectionId",
|
|
SqlDbType.Int)
|
|
{
|
|
Value = sonicConnectionId
|
|
});
|
|
|
|
await clearDefaultCommand.ExecuteNonQueryAsync(
|
|
cancellationToken);
|
|
}
|
|
|
|
const string insertSql = """
|
|
INSERT INTO [dbo].[SonicCredential]
|
|
(
|
|
[SonicConnectionId],
|
|
[CredentialName],
|
|
[CredentialUserName],
|
|
[CredentialSecret],
|
|
[IsDefault],
|
|
[IsActive],
|
|
[CreationDateTime],
|
|
[CreatedBy],
|
|
[ModifiedDateTime],
|
|
[ModifiedBy]
|
|
)
|
|
OUTPUT INSERTED.[SonicCredentialId]
|
|
VALUES
|
|
(
|
|
@SonicConnectionId,
|
|
@CredentialName,
|
|
@CredentialUserName,
|
|
@CredentialSecret,
|
|
@IsDefault,
|
|
1,
|
|
SYSUTCDATETIME(),
|
|
SUSER_SNAME(),
|
|
NULL,
|
|
NULL
|
|
);
|
|
""";
|
|
|
|
await using SqlCommand insertCommand =
|
|
new(
|
|
insertSql,
|
|
connection,
|
|
transaction);
|
|
|
|
insertCommand.Parameters.Add(
|
|
new SqlParameter(
|
|
"@SonicConnectionId",
|
|
SqlDbType.Int)
|
|
{
|
|
Value = sonicConnectionId
|
|
});
|
|
|
|
insertCommand.Parameters.Add(
|
|
new SqlParameter(
|
|
"@CredentialName",
|
|
SqlDbType.NVarChar,
|
|
150)
|
|
{
|
|
Value = normalizedCredentialName
|
|
});
|
|
|
|
insertCommand.Parameters.Add(
|
|
new SqlParameter(
|
|
"@CredentialUserName",
|
|
SqlDbType.NVarChar,
|
|
256)
|
|
{
|
|
Value = normalizedUserName
|
|
});
|
|
|
|
insertCommand.Parameters.Add(
|
|
new SqlParameter(
|
|
"@CredentialSecret",
|
|
SqlDbType.NVarChar,
|
|
512)
|
|
{
|
|
Value = secret
|
|
});
|
|
|
|
insertCommand.Parameters.Add(
|
|
new SqlParameter(
|
|
"@IsDefault",
|
|
SqlDbType.Bit)
|
|
{
|
|
Value = isDefault
|
|
});
|
|
|
|
object? result =
|
|
await insertCommand.ExecuteScalarAsync(
|
|
cancellationToken);
|
|
|
|
if (result is null || result is DBNull)
|
|
{
|
|
throw new InvalidOperationException(
|
|
"Das Benutzerprofil konnte nicht angelegt werden.");
|
|
}
|
|
|
|
int credentialId =
|
|
Convert.ToInt32(result);
|
|
|
|
await transaction.CommitAsync(
|
|
cancellationToken);
|
|
|
|
return credentialId;
|
|
}
|
|
catch
|
|
{
|
|
try
|
|
{
|
|
await transaction.RollbackAsync(
|
|
CancellationToken.None);
|
|
}
|
|
catch
|
|
{
|
|
// Die ursprüngliche Exception soll erhalten bleiben.
|
|
}
|
|
|
|
throw;
|
|
}
|
|
}
|
|
|
|
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,
|
|
CancellationToken cancellationToken = default)
|
|
{
|
|
if (sonicConnectionId <= 0)
|
|
{
|
|
throw new ArgumentOutOfRangeException(
|
|
nameof(sonicConnectionId));
|
|
}
|
|
|
|
const string sql = """
|
|
SELECT
|
|
[SonicCredentialId],
|
|
[SonicConnectionId],
|
|
[CredentialName],
|
|
[CredentialUserName],
|
|
[CredentialSecret],
|
|
[IsDefault],
|
|
[IsActive]
|
|
FROM [dbo].[SonicCredential]
|
|
WHERE [SonicConnectionId] = @SonicConnectionId
|
|
AND [IsActive] = 1
|
|
ORDER BY
|
|
[IsDefault] DESC,
|
|
[CredentialName];
|
|
""";
|
|
|
|
List<SonicCredentialProfile> profiles = [];
|
|
|
|
await using SqlConnection connection =
|
|
new(_connectionString);
|
|
|
|
await connection.OpenAsync(cancellationToken);
|
|
|
|
await using SqlCommand command =
|
|
new(sql, connection);
|
|
|
|
command.Parameters.Add(
|
|
new SqlParameter(
|
|
"@SonicConnectionId",
|
|
SqlDbType.Int)
|
|
{
|
|
Value = sonicConnectionId
|
|
});
|
|
|
|
await using SqlDataReader reader =
|
|
await command.ExecuteReaderAsync(cancellationToken);
|
|
|
|
int credentialIdOrdinal =
|
|
reader.GetOrdinal("SonicCredentialId");
|
|
|
|
int connectionIdOrdinal =
|
|
reader.GetOrdinal("SonicConnectionId");
|
|
|
|
int credentialNameOrdinal =
|
|
reader.GetOrdinal("CredentialName");
|
|
|
|
int userNameOrdinal =
|
|
reader.GetOrdinal("CredentialUserName");
|
|
|
|
int secretOrdinal =
|
|
reader.GetOrdinal("CredentialSecret");
|
|
|
|
int isDefaultOrdinal =
|
|
reader.GetOrdinal("IsDefault");
|
|
|
|
int isActiveOrdinal =
|
|
reader.GetOrdinal("IsActive");
|
|
|
|
while (await reader.ReadAsync(cancellationToken))
|
|
{
|
|
string? userName =
|
|
reader.IsDBNull(userNameOrdinal)
|
|
? null
|
|
: reader.GetString(userNameOrdinal);
|
|
|
|
string? secret =
|
|
reader.IsDBNull(secretOrdinal)
|
|
? null
|
|
: reader.GetString(secretOrdinal);
|
|
|
|
if (string.IsNullOrWhiteSpace(userName)
|
|
|| string.IsNullOrEmpty(secret))
|
|
{
|
|
continue;
|
|
}
|
|
|
|
profiles.Add(
|
|
new SonicCredentialProfile
|
|
{
|
|
SonicCredentialId =
|
|
reader.GetInt32(
|
|
credentialIdOrdinal),
|
|
|
|
SonicConnectionId =
|
|
reader.GetInt32(
|
|
connectionIdOrdinal),
|
|
|
|
CredentialName =
|
|
reader.GetString(
|
|
credentialNameOrdinal),
|
|
|
|
UserName = userName,
|
|
Secret = secret,
|
|
|
|
IsDefault =
|
|
reader.GetBoolean(
|
|
isDefaultOrdinal),
|
|
|
|
IsActive =
|
|
reader.GetBoolean(
|
|
isActiveOrdinal)
|
|
});
|
|
}
|
|
|
|
return profiles;
|
|
}
|
|
|
|
public async Task<SonicCredentialProfile?> GetCredentialByIdAsync(
|
|
int sonicCredentialId,
|
|
CancellationToken cancellationToken = default)
|
|
{
|
|
if (sonicCredentialId <= 0)
|
|
{
|
|
throw new ArgumentOutOfRangeException(
|
|
nameof(sonicCredentialId));
|
|
}
|
|
|
|
const string sql = """
|
|
SELECT
|
|
[SonicCredentialId],
|
|
[SonicConnectionId],
|
|
[CredentialName],
|
|
[CredentialUserName],
|
|
[CredentialSecret],
|
|
[IsDefault],
|
|
[IsActive]
|
|
FROM [dbo].[SonicCredential]
|
|
WHERE [SonicCredentialId] = @SonicCredentialId
|
|
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(
|
|
"@SonicCredentialId",
|
|
SqlDbType.Int)
|
|
{
|
|
Value = sonicCredentialId
|
|
});
|
|
|
|
await using SqlDataReader reader =
|
|
await command.ExecuteReaderAsync(cancellationToken);
|
|
|
|
if (!await reader.ReadAsync(cancellationToken))
|
|
{
|
|
return null;
|
|
}
|
|
|
|
int userNameOrdinal =
|
|
reader.GetOrdinal("CredentialUserName");
|
|
|
|
int secretOrdinal =
|
|
reader.GetOrdinal("CredentialSecret");
|
|
|
|
string? userName =
|
|
reader.IsDBNull(userNameOrdinal)
|
|
? null
|
|
: reader.GetString(userNameOrdinal);
|
|
|
|
string? secret =
|
|
reader.IsDBNull(secretOrdinal)
|
|
? null
|
|
: reader.GetString(secretOrdinal);
|
|
|
|
if (string.IsNullOrWhiteSpace(userName)
|
|
|| string.IsNullOrEmpty(secret))
|
|
{
|
|
return null;
|
|
}
|
|
|
|
return new SonicCredentialProfile
|
|
{
|
|
SonicCredentialId =
|
|
reader.GetInt32(
|
|
reader.GetOrdinal("SonicCredentialId")),
|
|
|
|
SonicConnectionId =
|
|
reader.GetInt32(
|
|
reader.GetOrdinal("SonicConnectionId")),
|
|
|
|
CredentialName =
|
|
reader.GetString(
|
|
reader.GetOrdinal("CredentialName")),
|
|
|
|
UserName = userName,
|
|
Secret = secret,
|
|
|
|
IsDefault =
|
|
reader.GetBoolean(
|
|
reader.GetOrdinal("IsDefault")),
|
|
|
|
IsActive =
|
|
reader.GetBoolean(
|
|
reader.GetOrdinal("IsActive"))
|
|
};
|
|
}
|
|
|
|
public async Task<IReadOnlyList<CompanyOption>> GetCompaniesAsync(
|
|
CancellationToken cancellationToken = default)
|
|
{
|
|
const string sql = """
|
|
SELECT
|
|
[CompanyId],
|
|
[CompanyCode],
|
|
[CompanyName]
|
|
FROM [dbo].[Company]
|
|
WHERE [IsActive] = 1
|
|
ORDER BY [CompanyCode];
|
|
""";
|
|
|
|
List<CompanyOption> companies = [];
|
|
|
|
await using SqlConnection connection =
|
|
new(_connectionString);
|
|
|
|
await connection.OpenAsync(cancellationToken);
|
|
|
|
await using SqlCommand command =
|
|
new(sql, connection);
|
|
|
|
await using SqlDataReader reader =
|
|
await command.ExecuteReaderAsync(cancellationToken);
|
|
|
|
while (await reader.ReadAsync(cancellationToken))
|
|
{
|
|
companies.Add(
|
|
new CompanyOption
|
|
{
|
|
CompanyId =
|
|
reader.GetInt32(
|
|
reader.GetOrdinal("CompanyId")),
|
|
|
|
CompanyCode =
|
|
reader.GetString(
|
|
reader.GetOrdinal("CompanyCode")),
|
|
|
|
CompanyName =
|
|
reader.GetString(
|
|
reader.GetOrdinal("CompanyName"))
|
|
});
|
|
}
|
|
|
|
return companies;
|
|
}
|
|
|
|
public async Task<IReadOnlyList<ContainerOption>> GetContainersAsync(
|
|
string environmentCode,
|
|
int? sonicConnectionId = null,
|
|
CancellationToken cancellationToken = default)
|
|
{
|
|
if (string.IsNullOrWhiteSpace(environmentCode))
|
|
{
|
|
throw new ArgumentException(
|
|
"Der EnvironmentCode fehlt.",
|
|
nameof(environmentCode));
|
|
}
|
|
|
|
const string sqlWithCheckUrl = """
|
|
SELECT
|
|
container.[SonicContainerId],
|
|
container.[SonicConnectionId],
|
|
container.[CompanyId],
|
|
container.[ContainerName],
|
|
container.[ContainerDisplayName],
|
|
container.[CertificateCheckUrl],
|
|
container.[RestartTimeoutSeconds],
|
|
company.[CompanyCode],
|
|
connection.[ConnectionName]
|
|
FROM [dbo].[SonicContainer] AS container
|
|
INNER JOIN [dbo].[Company] AS company
|
|
ON company.[CompanyId] = container.[CompanyId]
|
|
INNER JOIN [dbo].[SonicConnection] AS connection
|
|
ON connection.[SonicConnectionId] =
|
|
container.[SonicConnectionId]
|
|
INNER JOIN [dbo].[Environment] AS environment
|
|
ON environment.[EnvironmentId] =
|
|
connection.[EnvironmentId]
|
|
WHERE container.[IsActive] = 1
|
|
AND company.[IsActive] = 1
|
|
AND connection.[IsActive] = 1
|
|
AND environment.[IsActive] = 1
|
|
AND environment.[EnvironmentCode] = @EnvironmentCode
|
|
AND
|
|
(
|
|
@SonicConnectionId IS NULL
|
|
OR container.[SonicConnectionId] = @SonicConnectionId
|
|
)
|
|
ORDER BY
|
|
company.[CompanyCode],
|
|
container.[ContainerName];
|
|
""";
|
|
|
|
const string sqlWithoutCheckUrl = """
|
|
SELECT
|
|
container.[SonicContainerId],
|
|
container.[SonicConnectionId],
|
|
container.[CompanyId],
|
|
container.[ContainerName],
|
|
container.[ContainerDisplayName],
|
|
container.[RestartTimeoutSeconds],
|
|
company.[CompanyCode],
|
|
connection.[ConnectionName]
|
|
FROM [dbo].[SonicContainer] AS container
|
|
INNER JOIN [dbo].[Company] AS company
|
|
ON company.[CompanyId] = container.[CompanyId]
|
|
INNER JOIN [dbo].[SonicConnection] AS connection
|
|
ON connection.[SonicConnectionId] =
|
|
container.[SonicConnectionId]
|
|
INNER JOIN [dbo].[Environment] AS environment
|
|
ON environment.[EnvironmentId] =
|
|
connection.[EnvironmentId]
|
|
WHERE container.[IsActive] = 1
|
|
AND company.[IsActive] = 1
|
|
AND connection.[IsActive] = 1
|
|
AND environment.[IsActive] = 1
|
|
AND environment.[EnvironmentCode] = @EnvironmentCode
|
|
AND
|
|
(
|
|
@SonicConnectionId IS NULL
|
|
OR container.[SonicConnectionId] = @SonicConnectionId
|
|
)
|
|
ORDER BY
|
|
company.[CompanyCode],
|
|
container.[ContainerName];
|
|
""";
|
|
|
|
List<ContainerOption> containers = [];
|
|
|
|
await using SqlConnection connection =
|
|
new(_connectionString);
|
|
|
|
await connection.OpenAsync(cancellationToken);
|
|
|
|
bool hasCertificateCheckUrl =
|
|
await DatabaseSchemaPatcher.HasCertificateCheckUrlColumnAsync(
|
|
_connectionString,
|
|
cancellationToken);
|
|
|
|
string sql = hasCertificateCheckUrl
|
|
? sqlWithCheckUrl
|
|
: sqlWithoutCheckUrl;
|
|
|
|
await using SqlCommand command =
|
|
new(sql, connection);
|
|
|
|
command.Parameters.Add(
|
|
new SqlParameter(
|
|
"@EnvironmentCode",
|
|
SqlDbType.NVarChar,
|
|
30)
|
|
{
|
|
Value = environmentCode
|
|
.Trim()
|
|
.ToUpperInvariant()
|
|
});
|
|
|
|
command.Parameters.Add(
|
|
new SqlParameter(
|
|
"@SonicConnectionId",
|
|
SqlDbType.Int)
|
|
{
|
|
Value = sonicConnectionId is null or <= 0
|
|
? DBNull.Value
|
|
: sonicConnectionId.Value
|
|
});
|
|
|
|
await using SqlDataReader reader =
|
|
await command.ExecuteReaderAsync(cancellationToken);
|
|
|
|
while (await reader.ReadAsync(cancellationToken))
|
|
{
|
|
int displayOrdinal =
|
|
reader.GetOrdinal("ContainerDisplayName");
|
|
|
|
int? checkUrlOrdinal = hasCertificateCheckUrl
|
|
? reader.GetOrdinal("CertificateCheckUrl")
|
|
: null;
|
|
|
|
containers.Add(
|
|
new ContainerOption
|
|
{
|
|
SonicContainerId =
|
|
reader.GetInt32(
|
|
reader.GetOrdinal("SonicContainerId")),
|
|
|
|
SonicConnectionId =
|
|
reader.GetInt32(
|
|
reader.GetOrdinal("SonicConnectionId")),
|
|
|
|
CompanyId =
|
|
reader.GetInt32(
|
|
reader.GetOrdinal("CompanyId")),
|
|
|
|
ContainerName =
|
|
reader.GetString(
|
|
reader.GetOrdinal("ContainerName")),
|
|
|
|
ContainerDisplayName =
|
|
reader.IsDBNull(displayOrdinal)
|
|
? null
|
|
: reader.GetString(displayOrdinal),
|
|
|
|
CertificateCheckUrl =
|
|
checkUrlOrdinal is int urlOrdinal
|
|
&& !reader.IsDBNull(urlOrdinal)
|
|
? reader.GetString(urlOrdinal)
|
|
: null,
|
|
|
|
RestartTimeoutSeconds =
|
|
reader.GetInt32(
|
|
reader.GetOrdinal("RestartTimeoutSeconds")),
|
|
|
|
CompanyCode =
|
|
reader.GetString(
|
|
reader.GetOrdinal("CompanyCode")),
|
|
|
|
ConnectionName =
|
|
reader.GetString(
|
|
reader.GetOrdinal("ConnectionName"))
|
|
});
|
|
}
|
|
|
|
return containers;
|
|
}
|
|
|
|
public async Task<IReadOnlyList<CertificateTargetOption>>
|
|
GetCertificateTargetsAsync(
|
|
string environmentCode,
|
|
int? sonicContainerId = null,
|
|
CancellationToken cancellationToken = default)
|
|
{
|
|
if (string.IsNullOrWhiteSpace(environmentCode))
|
|
{
|
|
throw new ArgumentException(
|
|
"Der EnvironmentCode fehlt.",
|
|
nameof(environmentCode));
|
|
}
|
|
|
|
const string sql = """
|
|
SELECT
|
|
target.[CertificateTargetId],
|
|
target.[SonicContainerId],
|
|
target.[TargetDirectory],
|
|
target.[TargetFileName],
|
|
target.[BackupEnabled],
|
|
target.[BackupDirectoryName],
|
|
container.[ContainerName],
|
|
connection.[ConnectionName],
|
|
company.[CompanyCode]
|
|
FROM [dbo].[CertificateTarget] AS target
|
|
INNER JOIN [dbo].[SonicContainer] AS container
|
|
ON container.[SonicContainerId] =
|
|
target.[SonicContainerId]
|
|
INNER JOIN [dbo].[SonicConnection] AS connection
|
|
ON connection.[SonicConnectionId] =
|
|
container.[SonicConnectionId]
|
|
INNER JOIN [dbo].[Company] AS company
|
|
ON company.[CompanyId] = container.[CompanyId]
|
|
INNER JOIN [dbo].[Environment] AS environment
|
|
ON environment.[EnvironmentId] =
|
|
connection.[EnvironmentId]
|
|
WHERE target.[IsActive] = 1
|
|
AND container.[IsActive] = 1
|
|
AND connection.[IsActive] = 1
|
|
AND company.[IsActive] = 1
|
|
AND environment.[IsActive] = 1
|
|
AND environment.[EnvironmentCode] = @EnvironmentCode
|
|
AND
|
|
(
|
|
@SonicContainerId IS NULL
|
|
OR target.[SonicContainerId] = @SonicContainerId
|
|
)
|
|
ORDER BY
|
|
company.[CompanyCode],
|
|
container.[ContainerName],
|
|
target.[TargetDirectory],
|
|
target.[TargetFileName];
|
|
""";
|
|
|
|
List<CertificateTargetOption> targets = [];
|
|
|
|
await using SqlConnection connection =
|
|
new(_connectionString);
|
|
|
|
await connection.OpenAsync(cancellationToken);
|
|
|
|
await using SqlCommand command =
|
|
new(sql, connection);
|
|
|
|
command.Parameters.Add(
|
|
new SqlParameter(
|
|
"@EnvironmentCode",
|
|
SqlDbType.NVarChar,
|
|
30)
|
|
{
|
|
Value = environmentCode
|
|
.Trim()
|
|
.ToUpperInvariant()
|
|
});
|
|
|
|
command.Parameters.Add(
|
|
new SqlParameter(
|
|
"@SonicContainerId",
|
|
SqlDbType.Int)
|
|
{
|
|
Value = sonicContainerId is null or <= 0
|
|
? DBNull.Value
|
|
: sonicContainerId.Value
|
|
});
|
|
|
|
await using SqlDataReader reader =
|
|
await command.ExecuteReaderAsync(cancellationToken);
|
|
|
|
while (await reader.ReadAsync(cancellationToken))
|
|
{
|
|
targets.Add(
|
|
new CertificateTargetOption
|
|
{
|
|
CertificateTargetId =
|
|
reader.GetInt32(
|
|
reader.GetOrdinal("CertificateTargetId")),
|
|
|
|
SonicContainerId =
|
|
reader.GetInt32(
|
|
reader.GetOrdinal("SonicContainerId")),
|
|
|
|
TargetDirectory =
|
|
reader.GetString(
|
|
reader.GetOrdinal("TargetDirectory")),
|
|
|
|
TargetFileName =
|
|
reader.GetString(
|
|
reader.GetOrdinal("TargetFileName")),
|
|
|
|
BackupEnabled =
|
|
reader.GetBoolean(
|
|
reader.GetOrdinal("BackupEnabled")),
|
|
|
|
BackupDirectoryName =
|
|
reader.GetString(
|
|
reader.GetOrdinal("BackupDirectoryName")),
|
|
|
|
ContainerName =
|
|
reader.GetString(
|
|
reader.GetOrdinal("ContainerName")),
|
|
|
|
ConnectionName =
|
|
reader.GetString(
|
|
reader.GetOrdinal("ConnectionName")),
|
|
|
|
CompanyCode =
|
|
reader.GetString(
|
|
reader.GetOrdinal("CompanyCode"))
|
|
});
|
|
}
|
|
|
|
return targets;
|
|
}
|
|
|
|
public async Task<int> AddSonicConnectionAsync(
|
|
string environmentCode,
|
|
string connectionName,
|
|
string managementHost,
|
|
int managementPort,
|
|
string domainName,
|
|
string connectionProtocol,
|
|
CancellationToken cancellationToken = default)
|
|
{
|
|
if (string.IsNullOrWhiteSpace(environmentCode))
|
|
{
|
|
throw new ArgumentException(
|
|
"Der EnvironmentCode fehlt.",
|
|
nameof(environmentCode));
|
|
}
|
|
|
|
string name = RequireTrimmed(
|
|
connectionName,
|
|
150,
|
|
"ConnectionName");
|
|
|
|
string host = RequireTrimmed(
|
|
managementHost,
|
|
255,
|
|
"ManagementHost");
|
|
|
|
string domain = RequireTrimmed(
|
|
domainName,
|
|
150,
|
|
"DomainName");
|
|
|
|
string protocol = string.IsNullOrWhiteSpace(connectionProtocol)
|
|
? "tcp"
|
|
: connectionProtocol.Trim().TrimEnd(':', '/');
|
|
|
|
if (managementPort is < 1 or > 65535)
|
|
{
|
|
throw new ArgumentOutOfRangeException(
|
|
nameof(managementPort),
|
|
"Der ManagementPort muss zwischen 1 und 65535 liegen.");
|
|
}
|
|
|
|
const string sql = """
|
|
INSERT INTO [dbo].[SonicConnection]
|
|
(
|
|
[EnvironmentId],
|
|
[ConnectionName],
|
|
[ManagementHost],
|
|
[ManagementPort],
|
|
[DomainName],
|
|
[ConnectionProtocol],
|
|
[IsActive],
|
|
[CreationDateTime],
|
|
[CreatedBy]
|
|
)
|
|
OUTPUT INSERTED.[SonicConnectionId]
|
|
SELECT
|
|
environment.[EnvironmentId],
|
|
@ConnectionName,
|
|
@ManagementHost,
|
|
@ManagementPort,
|
|
@DomainName,
|
|
@ConnectionProtocol,
|
|
1,
|
|
SYSUTCDATETIME(),
|
|
SUSER_SNAME()
|
|
FROM [dbo].[Environment] AS environment
|
|
WHERE environment.[EnvironmentCode] = @EnvironmentCode
|
|
AND environment.[IsActive] = 1;
|
|
""";
|
|
|
|
await using SqlConnection connection =
|
|
new(_connectionString);
|
|
|
|
await connection.OpenAsync(cancellationToken);
|
|
|
|
await using SqlCommand command =
|
|
new(sql, connection);
|
|
|
|
command.Parameters.Add(
|
|
new SqlParameter(
|
|
"@EnvironmentCode",
|
|
SqlDbType.NVarChar,
|
|
30)
|
|
{
|
|
Value = environmentCode
|
|
.Trim()
|
|
.ToUpperInvariant()
|
|
});
|
|
|
|
command.Parameters.Add(
|
|
new SqlParameter(
|
|
"@ConnectionName",
|
|
SqlDbType.NVarChar,
|
|
150)
|
|
{
|
|
Value = name
|
|
});
|
|
|
|
command.Parameters.Add(
|
|
new SqlParameter(
|
|
"@ManagementHost",
|
|
SqlDbType.NVarChar,
|
|
255)
|
|
{
|
|
Value = host
|
|
});
|
|
|
|
command.Parameters.Add(
|
|
new SqlParameter(
|
|
"@ManagementPort",
|
|
SqlDbType.Int)
|
|
{
|
|
Value = managementPort
|
|
});
|
|
|
|
command.Parameters.Add(
|
|
new SqlParameter(
|
|
"@DomainName",
|
|
SqlDbType.NVarChar,
|
|
150)
|
|
{
|
|
Value = domain
|
|
});
|
|
|
|
command.Parameters.Add(
|
|
new SqlParameter(
|
|
"@ConnectionProtocol",
|
|
SqlDbType.NVarChar,
|
|
20)
|
|
{
|
|
Value = protocol
|
|
});
|
|
|
|
object? result =
|
|
await command.ExecuteScalarAsync(cancellationToken);
|
|
|
|
if (result is null || result is DBNull)
|
|
{
|
|
throw new InvalidOperationException(
|
|
$"Die Umgebung '{environmentCode}' wurde nicht gefunden " +
|
|
"oder die Sonic-Verbindung konnte nicht angelegt werden.");
|
|
}
|
|
|
|
return Convert.ToInt32(result);
|
|
}
|
|
|
|
public async Task<int> AddSonicContainerAsync(
|
|
int companyId,
|
|
int sonicConnectionId,
|
|
string containerName,
|
|
string? containerDisplayName,
|
|
int restartTimeoutSeconds,
|
|
string? certificateCheckUrl = null,
|
|
CancellationToken cancellationToken = default)
|
|
{
|
|
if (companyId <= 0)
|
|
{
|
|
throw new ArgumentOutOfRangeException(nameof(companyId));
|
|
}
|
|
|
|
if (sonicConnectionId <= 0)
|
|
{
|
|
throw new ArgumentOutOfRangeException(nameof(sonicConnectionId));
|
|
}
|
|
|
|
string name = RequireTrimmed(
|
|
containerName,
|
|
250,
|
|
"ContainerName");
|
|
|
|
string? display =
|
|
string.IsNullOrWhiteSpace(containerDisplayName)
|
|
? null
|
|
: containerDisplayName.Trim();
|
|
|
|
if (display is { Length: > 250 })
|
|
{
|
|
throw new ArgumentException(
|
|
"Der Anzeigename darf maximal 250 Zeichen enthalten.",
|
|
nameof(containerDisplayName));
|
|
}
|
|
|
|
if (restartTimeoutSeconds is < 10 or > 3600)
|
|
{
|
|
throw new ArgumentOutOfRangeException(
|
|
nameof(restartTimeoutSeconds),
|
|
"Timeout muss zwischen 10 und 3600 Sekunden liegen.");
|
|
}
|
|
|
|
string? checkUrl =
|
|
TlsEndpointProbeService.NormalizeCheckUrl(certificateCheckUrl);
|
|
|
|
bool hasCertificateCheckUrl =
|
|
await DatabaseSchemaPatcher.HasCertificateCheckUrlColumnAsync(
|
|
_connectionString,
|
|
cancellationToken);
|
|
|
|
string sql = hasCertificateCheckUrl
|
|
? """
|
|
INSERT INTO [dbo].[SonicContainer]
|
|
(
|
|
[CompanyId],
|
|
[SonicConnectionId],
|
|
[ContainerName],
|
|
[ContainerDisplayName],
|
|
[CertificateCheckUrl],
|
|
[RestartTimeoutSeconds],
|
|
[IsActive],
|
|
[CreationDateTime],
|
|
[CreatedBy]
|
|
)
|
|
OUTPUT INSERTED.[SonicContainerId]
|
|
VALUES
|
|
(
|
|
@CompanyId,
|
|
@SonicConnectionId,
|
|
@ContainerName,
|
|
@ContainerDisplayName,
|
|
@CertificateCheckUrl,
|
|
@RestartTimeoutSeconds,
|
|
1,
|
|
SYSUTCDATETIME(),
|
|
SUSER_SNAME()
|
|
);
|
|
"""
|
|
: """
|
|
INSERT INTO [dbo].[SonicContainer]
|
|
(
|
|
[CompanyId],
|
|
[SonicConnectionId],
|
|
[ContainerName],
|
|
[ContainerDisplayName],
|
|
[RestartTimeoutSeconds],
|
|
[IsActive],
|
|
[CreationDateTime],
|
|
[CreatedBy]
|
|
)
|
|
OUTPUT INSERTED.[SonicContainerId]
|
|
VALUES
|
|
(
|
|
@CompanyId,
|
|
@SonicConnectionId,
|
|
@ContainerName,
|
|
@ContainerDisplayName,
|
|
@RestartTimeoutSeconds,
|
|
1,
|
|
SYSUTCDATETIME(),
|
|
SUSER_SNAME()
|
|
);
|
|
""";
|
|
|
|
await using SqlConnection connection =
|
|
new(_connectionString);
|
|
|
|
await connection.OpenAsync(cancellationToken);
|
|
|
|
await using SqlCommand command =
|
|
new(sql, connection);
|
|
|
|
command.Parameters.Add(
|
|
new SqlParameter("@CompanyId", SqlDbType.Int)
|
|
{
|
|
Value = companyId
|
|
});
|
|
|
|
command.Parameters.Add(
|
|
new SqlParameter("@SonicConnectionId", SqlDbType.Int)
|
|
{
|
|
Value = sonicConnectionId
|
|
});
|
|
|
|
command.Parameters.Add(
|
|
new SqlParameter(
|
|
"@ContainerName",
|
|
SqlDbType.NVarChar,
|
|
250)
|
|
{
|
|
Value = name
|
|
});
|
|
|
|
command.Parameters.Add(
|
|
new SqlParameter(
|
|
"@ContainerDisplayName",
|
|
SqlDbType.NVarChar,
|
|
250)
|
|
{
|
|
Value = display is null
|
|
? DBNull.Value
|
|
: display
|
|
});
|
|
|
|
if (hasCertificateCheckUrl)
|
|
{
|
|
command.Parameters.Add(
|
|
new SqlParameter(
|
|
"@CertificateCheckUrl",
|
|
SqlDbType.NVarChar,
|
|
500)
|
|
{
|
|
Value = checkUrl is null
|
|
? DBNull.Value
|
|
: checkUrl
|
|
});
|
|
}
|
|
|
|
command.Parameters.Add(
|
|
new SqlParameter(
|
|
"@RestartTimeoutSeconds",
|
|
SqlDbType.Int)
|
|
{
|
|
Value = restartTimeoutSeconds
|
|
});
|
|
|
|
object? result =
|
|
await command.ExecuteScalarAsync(cancellationToken);
|
|
|
|
if (result is null || result is DBNull)
|
|
{
|
|
throw new InvalidOperationException(
|
|
"Der Container konnte nicht angelegt werden.");
|
|
}
|
|
|
|
return Convert.ToInt32(result);
|
|
}
|
|
|
|
public async Task<int> AddCertificateTargetAsync(
|
|
int sonicContainerId,
|
|
string targetDirectory,
|
|
string targetFileName,
|
|
bool backupEnabled,
|
|
string backupDirectoryName,
|
|
CancellationToken cancellationToken = default)
|
|
{
|
|
if (sonicContainerId <= 0)
|
|
{
|
|
throw new ArgumentOutOfRangeException(
|
|
nameof(sonicContainerId));
|
|
}
|
|
|
|
string directory = RequireTrimmed(
|
|
targetDirectory,
|
|
500,
|
|
"TargetDirectory");
|
|
|
|
string fileName = RequireTrimmed(
|
|
targetFileName,
|
|
260,
|
|
"TargetFileName");
|
|
|
|
string backupName = string.IsNullOrWhiteSpace(backupDirectoryName)
|
|
? "Backup"
|
|
: backupDirectoryName.Trim();
|
|
|
|
if (backupName.Length > 100)
|
|
{
|
|
throw new ArgumentException(
|
|
"Der Backup-Ordnername darf maximal 100 Zeichen enthalten.",
|
|
nameof(backupDirectoryName));
|
|
}
|
|
|
|
const string sql = """
|
|
INSERT INTO [dbo].[CertificateTarget]
|
|
(
|
|
[SonicContainerId],
|
|
[TargetDirectory],
|
|
[TargetFileName],
|
|
[BackupEnabled],
|
|
[BackupDirectoryName],
|
|
[IsActive],
|
|
[CreationDateTime],
|
|
[CreatedBy]
|
|
)
|
|
OUTPUT INSERTED.[CertificateTargetId]
|
|
VALUES
|
|
(
|
|
@SonicContainerId,
|
|
@TargetDirectory,
|
|
@TargetFileName,
|
|
@BackupEnabled,
|
|
@BackupDirectoryName,
|
|
1,
|
|
SYSUTCDATETIME(),
|
|
SUSER_SNAME()
|
|
);
|
|
""";
|
|
|
|
await using SqlConnection connection =
|
|
new(_connectionString);
|
|
|
|
await connection.OpenAsync(cancellationToken);
|
|
|
|
await using SqlCommand command =
|
|
new(sql, connection);
|
|
|
|
command.Parameters.Add(
|
|
new SqlParameter("@SonicContainerId", SqlDbType.Int)
|
|
{
|
|
Value = sonicContainerId
|
|
});
|
|
|
|
command.Parameters.Add(
|
|
new SqlParameter(
|
|
"@TargetDirectory",
|
|
SqlDbType.NVarChar,
|
|
500)
|
|
{
|
|
Value = directory
|
|
});
|
|
|
|
command.Parameters.Add(
|
|
new SqlParameter(
|
|
"@TargetFileName",
|
|
SqlDbType.NVarChar,
|
|
260)
|
|
{
|
|
Value = fileName
|
|
});
|
|
|
|
command.Parameters.Add(
|
|
new SqlParameter("@BackupEnabled", SqlDbType.Bit)
|
|
{
|
|
Value = backupEnabled
|
|
});
|
|
|
|
command.Parameters.Add(
|
|
new SqlParameter(
|
|
"@BackupDirectoryName",
|
|
SqlDbType.NVarChar,
|
|
100)
|
|
{
|
|
Value = backupName
|
|
});
|
|
|
|
object? result =
|
|
await command.ExecuteScalarAsync(cancellationToken);
|
|
|
|
if (result is null || result is DBNull)
|
|
{
|
|
throw new InvalidOperationException(
|
|
"Das Zertifikat-Ziel konnte nicht angelegt werden.");
|
|
}
|
|
|
|
return Convert.ToInt32(result);
|
|
}
|
|
|
|
public async Task UpdateSonicConnectionAsync(
|
|
int sonicConnectionId,
|
|
string connectionName,
|
|
string managementHost,
|
|
int managementPort,
|
|
string domainName,
|
|
string connectionProtocol,
|
|
CancellationToken cancellationToken = default)
|
|
{
|
|
if (sonicConnectionId <= 0)
|
|
{
|
|
throw new ArgumentOutOfRangeException(
|
|
nameof(sonicConnectionId));
|
|
}
|
|
|
|
string name = RequireTrimmed(
|
|
connectionName,
|
|
150,
|
|
"ConnectionName");
|
|
|
|
string host = RequireTrimmed(
|
|
managementHost,
|
|
255,
|
|
"ManagementHost");
|
|
|
|
string domain = RequireTrimmed(
|
|
domainName,
|
|
150,
|
|
"DomainName");
|
|
|
|
string protocol = string.IsNullOrWhiteSpace(connectionProtocol)
|
|
? "tcp"
|
|
: connectionProtocol.Trim().TrimEnd(':', '/');
|
|
|
|
if (managementPort is < 1 or > 65535)
|
|
{
|
|
throw new ArgumentOutOfRangeException(
|
|
nameof(managementPort),
|
|
"Der ManagementPort muss zwischen 1 und 65535 liegen.");
|
|
}
|
|
|
|
const string sql = """
|
|
UPDATE [dbo].[SonicConnection]
|
|
SET
|
|
[ConnectionName] = @ConnectionName,
|
|
[ManagementHost] = @ManagementHost,
|
|
[ManagementPort] = @ManagementPort,
|
|
[DomainName] = @DomainName,
|
|
[ConnectionProtocol] = @ConnectionProtocol,
|
|
[ModifiedDateTime] = SYSUTCDATETIME(),
|
|
[ModifiedBy] = SUSER_SNAME()
|
|
WHERE [SonicConnectionId] = @SonicConnectionId
|
|
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("@SonicConnectionId", SqlDbType.Int)
|
|
{
|
|
Value = sonicConnectionId
|
|
});
|
|
|
|
command.Parameters.Add(
|
|
new SqlParameter(
|
|
"@ConnectionName",
|
|
SqlDbType.NVarChar,
|
|
150)
|
|
{
|
|
Value = name
|
|
});
|
|
|
|
command.Parameters.Add(
|
|
new SqlParameter(
|
|
"@ManagementHost",
|
|
SqlDbType.NVarChar,
|
|
255)
|
|
{
|
|
Value = host
|
|
});
|
|
|
|
command.Parameters.Add(
|
|
new SqlParameter("@ManagementPort", SqlDbType.Int)
|
|
{
|
|
Value = managementPort
|
|
});
|
|
|
|
command.Parameters.Add(
|
|
new SqlParameter(
|
|
"@DomainName",
|
|
SqlDbType.NVarChar,
|
|
150)
|
|
{
|
|
Value = domain
|
|
});
|
|
|
|
command.Parameters.Add(
|
|
new SqlParameter(
|
|
"@ConnectionProtocol",
|
|
SqlDbType.NVarChar,
|
|
20)
|
|
{
|
|
Value = protocol
|
|
});
|
|
|
|
int affected =
|
|
await command.ExecuteNonQueryAsync(cancellationToken);
|
|
|
|
if (affected == 0)
|
|
{
|
|
throw new InvalidOperationException(
|
|
$"Sonic-System Id {sonicConnectionId} "
|
|
+ "wurde nicht gefunden oder ist bereits inaktiv.");
|
|
}
|
|
}
|
|
|
|
public async Task DeactivateSonicConnectionAsync(
|
|
int sonicConnectionId,
|
|
CancellationToken cancellationToken = default)
|
|
{
|
|
if (sonicConnectionId <= 0)
|
|
{
|
|
throw new ArgumentOutOfRangeException(
|
|
nameof(sonicConnectionId));
|
|
}
|
|
|
|
const string sql = """
|
|
UPDATE [dbo].[SonicConnection]
|
|
SET
|
|
[IsActive] = 0,
|
|
[ModifiedDateTime] = SYSUTCDATETIME(),
|
|
[ModifiedBy] = SUSER_SNAME()
|
|
WHERE [SonicConnectionId] = @SonicConnectionId
|
|
AND [IsActive] = 1;
|
|
""";
|
|
|
|
await ExecuteSoftDeleteAsync(
|
|
sql,
|
|
"@SonicConnectionId",
|
|
sonicConnectionId,
|
|
$"Sonic-System Id {sonicConnectionId} "
|
|
+ "wurde nicht gefunden oder ist bereits inaktiv.",
|
|
cancellationToken);
|
|
}
|
|
|
|
public async Task UpdateSonicContainerAsync(
|
|
int sonicContainerId,
|
|
int companyId,
|
|
int sonicConnectionId,
|
|
string containerName,
|
|
string? containerDisplayName,
|
|
int restartTimeoutSeconds,
|
|
string? certificateCheckUrl = null,
|
|
CancellationToken cancellationToken = default)
|
|
{
|
|
if (sonicContainerId <= 0)
|
|
{
|
|
throw new ArgumentOutOfRangeException(
|
|
nameof(sonicContainerId));
|
|
}
|
|
|
|
if (companyId <= 0)
|
|
{
|
|
throw new ArgumentOutOfRangeException(nameof(companyId));
|
|
}
|
|
|
|
if (sonicConnectionId <= 0)
|
|
{
|
|
throw new ArgumentOutOfRangeException(nameof(sonicConnectionId));
|
|
}
|
|
|
|
string name = RequireTrimmed(
|
|
containerName,
|
|
250,
|
|
"ContainerName");
|
|
|
|
string? display =
|
|
string.IsNullOrWhiteSpace(containerDisplayName)
|
|
? null
|
|
: containerDisplayName.Trim();
|
|
|
|
if (display is { Length: > 250 })
|
|
{
|
|
throw new ArgumentException(
|
|
"Der Anzeigename darf maximal 250 Zeichen enthalten.",
|
|
nameof(containerDisplayName));
|
|
}
|
|
|
|
if (restartTimeoutSeconds is < 10 or > 3600)
|
|
{
|
|
throw new ArgumentOutOfRangeException(
|
|
nameof(restartTimeoutSeconds),
|
|
"Timeout muss zwischen 10 und 3600 Sekunden liegen.");
|
|
}
|
|
|
|
string? checkUrl =
|
|
TlsEndpointProbeService.NormalizeCheckUrl(certificateCheckUrl);
|
|
|
|
bool hasCertificateCheckUrl =
|
|
await DatabaseSchemaPatcher.HasCertificateCheckUrlColumnAsync(
|
|
_connectionString,
|
|
cancellationToken);
|
|
|
|
string sql = hasCertificateCheckUrl
|
|
? """
|
|
UPDATE [dbo].[SonicContainer]
|
|
SET
|
|
[CompanyId] = @CompanyId,
|
|
[SonicConnectionId] = @SonicConnectionId,
|
|
[ContainerName] = @ContainerName,
|
|
[ContainerDisplayName] = @ContainerDisplayName,
|
|
[CertificateCheckUrl] = @CertificateCheckUrl,
|
|
[RestartTimeoutSeconds] = @RestartTimeoutSeconds,
|
|
[ModifiedDateTime] = SYSUTCDATETIME(),
|
|
[ModifiedBy] = SUSER_SNAME()
|
|
WHERE [SonicContainerId] = @SonicContainerId
|
|
AND [IsActive] = 1;
|
|
"""
|
|
: """
|
|
UPDATE [dbo].[SonicContainer]
|
|
SET
|
|
[CompanyId] = @CompanyId,
|
|
[SonicConnectionId] = @SonicConnectionId,
|
|
[ContainerName] = @ContainerName,
|
|
[ContainerDisplayName] = @ContainerDisplayName,
|
|
[RestartTimeoutSeconds] = @RestartTimeoutSeconds,
|
|
[ModifiedDateTime] = SYSUTCDATETIME(),
|
|
[ModifiedBy] = SUSER_SNAME()
|
|
WHERE [SonicContainerId] = @SonicContainerId
|
|
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("@SonicContainerId", SqlDbType.Int)
|
|
{
|
|
Value = sonicContainerId
|
|
});
|
|
|
|
command.Parameters.Add(
|
|
new SqlParameter("@CompanyId", SqlDbType.Int)
|
|
{
|
|
Value = companyId
|
|
});
|
|
|
|
command.Parameters.Add(
|
|
new SqlParameter("@SonicConnectionId", SqlDbType.Int)
|
|
{
|
|
Value = sonicConnectionId
|
|
});
|
|
|
|
command.Parameters.Add(
|
|
new SqlParameter(
|
|
"@ContainerName",
|
|
SqlDbType.NVarChar,
|
|
250)
|
|
{
|
|
Value = name
|
|
});
|
|
|
|
command.Parameters.Add(
|
|
new SqlParameter(
|
|
"@ContainerDisplayName",
|
|
SqlDbType.NVarChar,
|
|
250)
|
|
{
|
|
Value = display is null
|
|
? DBNull.Value
|
|
: display
|
|
});
|
|
|
|
if (hasCertificateCheckUrl)
|
|
{
|
|
command.Parameters.Add(
|
|
new SqlParameter(
|
|
"@CertificateCheckUrl",
|
|
SqlDbType.NVarChar,
|
|
500)
|
|
{
|
|
Value = checkUrl is null
|
|
? DBNull.Value
|
|
: checkUrl
|
|
});
|
|
}
|
|
|
|
command.Parameters.Add(
|
|
new SqlParameter(
|
|
"@RestartTimeoutSeconds",
|
|
SqlDbType.Int)
|
|
{
|
|
Value = restartTimeoutSeconds
|
|
});
|
|
|
|
int affected =
|
|
await command.ExecuteNonQueryAsync(cancellationToken);
|
|
|
|
if (affected == 0)
|
|
{
|
|
throw new InvalidOperationException(
|
|
$"Container Id {sonicContainerId} "
|
|
+ "wurde nicht gefunden oder ist bereits inaktiv.");
|
|
}
|
|
}
|
|
|
|
public async Task DeactivateSonicContainerAsync(
|
|
int sonicContainerId,
|
|
CancellationToken cancellationToken = default)
|
|
{
|
|
if (sonicContainerId <= 0)
|
|
{
|
|
throw new ArgumentOutOfRangeException(
|
|
nameof(sonicContainerId));
|
|
}
|
|
|
|
const string sql = """
|
|
UPDATE [dbo].[SonicContainer]
|
|
SET
|
|
[IsActive] = 0,
|
|
[ModifiedDateTime] = SYSUTCDATETIME(),
|
|
[ModifiedBy] = SUSER_SNAME()
|
|
WHERE [SonicContainerId] = @SonicContainerId
|
|
AND [IsActive] = 1;
|
|
""";
|
|
|
|
await ExecuteSoftDeleteAsync(
|
|
sql,
|
|
"@SonicContainerId",
|
|
sonicContainerId,
|
|
$"Container Id {sonicContainerId} "
|
|
+ "wurde nicht gefunden oder ist bereits inaktiv.",
|
|
cancellationToken);
|
|
}
|
|
|
|
public async Task UpdateCertificateTargetAsync(
|
|
int certificateTargetId,
|
|
int sonicContainerId,
|
|
string targetDirectory,
|
|
string targetFileName,
|
|
bool backupEnabled,
|
|
string backupDirectoryName,
|
|
CancellationToken cancellationToken = default)
|
|
{
|
|
if (certificateTargetId <= 0)
|
|
{
|
|
throw new ArgumentOutOfRangeException(
|
|
nameof(certificateTargetId));
|
|
}
|
|
|
|
if (sonicContainerId <= 0)
|
|
{
|
|
throw new ArgumentOutOfRangeException(
|
|
nameof(sonicContainerId));
|
|
}
|
|
|
|
string directory = RequireTrimmed(
|
|
targetDirectory,
|
|
500,
|
|
"TargetDirectory");
|
|
|
|
string fileName = RequireTrimmed(
|
|
targetFileName,
|
|
260,
|
|
"TargetFileName");
|
|
|
|
string backupName = string.IsNullOrWhiteSpace(backupDirectoryName)
|
|
? "Backup"
|
|
: backupDirectoryName.Trim();
|
|
|
|
if (backupName.Length > 100)
|
|
{
|
|
throw new ArgumentException(
|
|
"Der Backup-Ordnername darf maximal 100 Zeichen enthalten.",
|
|
nameof(backupDirectoryName));
|
|
}
|
|
|
|
const string sql = """
|
|
UPDATE [dbo].[CertificateTarget]
|
|
SET
|
|
[SonicContainerId] = @SonicContainerId,
|
|
[TargetDirectory] = @TargetDirectory,
|
|
[TargetFileName] = @TargetFileName,
|
|
[BackupEnabled] = @BackupEnabled,
|
|
[BackupDirectoryName] = @BackupDirectoryName,
|
|
[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
|
|
});
|
|
|
|
command.Parameters.Add(
|
|
new SqlParameter("@SonicContainerId", SqlDbType.Int)
|
|
{
|
|
Value = sonicContainerId
|
|
});
|
|
|
|
command.Parameters.Add(
|
|
new SqlParameter(
|
|
"@TargetDirectory",
|
|
SqlDbType.NVarChar,
|
|
500)
|
|
{
|
|
Value = directory
|
|
});
|
|
|
|
command.Parameters.Add(
|
|
new SqlParameter(
|
|
"@TargetFileName",
|
|
SqlDbType.NVarChar,
|
|
260)
|
|
{
|
|
Value = fileName
|
|
});
|
|
|
|
command.Parameters.Add(
|
|
new SqlParameter("@BackupEnabled", SqlDbType.Bit)
|
|
{
|
|
Value = backupEnabled
|
|
});
|
|
|
|
command.Parameters.Add(
|
|
new SqlParameter(
|
|
"@BackupDirectoryName",
|
|
SqlDbType.NVarChar,
|
|
100)
|
|
{
|
|
Value = backupName
|
|
});
|
|
|
|
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 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 ExecuteSoftDeleteAsync(
|
|
sql,
|
|
"@CertificateTargetId",
|
|
certificateTargetId,
|
|
$"Zertifikat-Pfad Id {certificateTargetId} "
|
|
+ "wurde nicht gefunden oder ist bereits inaktiv.",
|
|
cancellationToken);
|
|
}
|
|
|
|
private async Task ExecuteSoftDeleteAsync(
|
|
string sql,
|
|
string idParameterName,
|
|
int id,
|
|
string notFoundMessage,
|
|
CancellationToken cancellationToken)
|
|
{
|
|
await using SqlConnection connection =
|
|
new(_connectionString);
|
|
|
|
await connection.OpenAsync(cancellationToken);
|
|
|
|
await using SqlCommand command =
|
|
new(sql, connection);
|
|
|
|
command.Parameters.Add(
|
|
new SqlParameter(idParameterName, SqlDbType.Int)
|
|
{
|
|
Value = id
|
|
});
|
|
|
|
int affected =
|
|
await command.ExecuteNonQueryAsync(cancellationToken);
|
|
|
|
if (affected == 0)
|
|
{
|
|
throw new InvalidOperationException(notFoundMessage);
|
|
}
|
|
}
|
|
|
|
public async Task<bool> ExistsSonicConnectionAsync(
|
|
int sonicConnectionId,
|
|
CancellationToken cancellationToken = default)
|
|
{
|
|
return await ExistsAsync(
|
|
"""
|
|
SELECT 1
|
|
FROM [dbo].[SonicConnection]
|
|
WHERE [SonicConnectionId] = @Id
|
|
AND [IsActive] = 1;
|
|
""",
|
|
sonicConnectionId,
|
|
cancellationToken);
|
|
}
|
|
|
|
public async Task<bool> ExistsSonicContainerAsync(
|
|
int sonicContainerId,
|
|
CancellationToken cancellationToken = default)
|
|
{
|
|
return await ExistsAsync(
|
|
"""
|
|
SELECT 1
|
|
FROM [dbo].[SonicContainer]
|
|
WHERE [SonicContainerId] = @Id
|
|
AND [IsActive] = 1;
|
|
""",
|
|
sonicContainerId,
|
|
cancellationToken);
|
|
}
|
|
|
|
public async Task<bool> ExistsCertificateTargetAsync(
|
|
int certificateTargetId,
|
|
CancellationToken cancellationToken = default)
|
|
{
|
|
return await ExistsAsync(
|
|
"""
|
|
SELECT 1
|
|
FROM [dbo].[CertificateTarget]
|
|
WHERE [CertificateTargetId] = @Id
|
|
AND [IsActive] = 1;
|
|
""",
|
|
certificateTargetId,
|
|
cancellationToken);
|
|
}
|
|
|
|
private async Task<bool> ExistsAsync(
|
|
string sql,
|
|
int id,
|
|
CancellationToken cancellationToken)
|
|
{
|
|
if (id <= 0)
|
|
{
|
|
return false;
|
|
}
|
|
|
|
await using SqlConnection connection =
|
|
new(_connectionString);
|
|
|
|
await connection.OpenAsync(cancellationToken);
|
|
|
|
await using SqlCommand command =
|
|
new(sql, connection);
|
|
|
|
command.Parameters.Add(
|
|
new SqlParameter("@Id", SqlDbType.Int)
|
|
{
|
|
Value = id
|
|
});
|
|
|
|
object? result =
|
|
await command.ExecuteScalarAsync(cancellationToken);
|
|
|
|
return result is not null && result is not DBNull;
|
|
}
|
|
|
|
private static string RequireTrimmed(
|
|
string? value,
|
|
int maxLength,
|
|
string fieldName)
|
|
{
|
|
if (string.IsNullOrWhiteSpace(value))
|
|
{
|
|
throw new ArgumentException(
|
|
$"'{fieldName}' fehlt.");
|
|
}
|
|
|
|
string trimmed = value.Trim();
|
|
|
|
if (trimmed.Length > maxLength)
|
|
{
|
|
throw new ArgumentException(
|
|
$"'{fieldName}' darf maximal {maxLength} Zeichen enthalten.");
|
|
}
|
|
|
|
return trimmed;
|
|
}
|
|
}
|
|
|