using System.Data; using Microsoft.Data.SqlClient; using ZA.CoreService.ESBCertificateManager.Models; namespace ZA.CoreService.ESBCertificateManager.Services; public sealed class SonicConnectionRepository { private readonly string _connectionString; public SonicConnectionRepository( string connectionString) { if (string.IsNullOrWhiteSpace(connectionString)) { throw new ArgumentException( "Der SQL-Connection-String ist nicht konfiguriert.", nameof(connectionString)); } _connectionString = connectionString; } public async Task> GetActiveAsync( CancellationToken cancellationToken = default) { const string sql = """ SELECT SonicConnectionId, ConnectionName, ManagementHost, ManagementPort, ConnectionProtocol, DomainName, CredentialUserName, CredentialReference, CredentialSecret, JavaHomePath, SonicHomePath, Notes, IsActive FROM dbo.SonicConnection WHERE IsActive = 1 ORDER BY ConnectionName; """; List connections = []; 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); int idOrdinal = reader.GetOrdinal("SonicConnectionId"); int nameOrdinal = reader.GetOrdinal("ConnectionName"); int hostOrdinal = reader.GetOrdinal("ManagementHost"); int portOrdinal = reader.GetOrdinal("ManagementPort"); int protocolOrdinal = reader.GetOrdinal("ConnectionProtocol"); int domainOrdinal = reader.GetOrdinal("DomainName"); int userNameOrdinal = reader.GetOrdinal("CredentialUserName"); int credentialReferenceOrdinal = reader.GetOrdinal("CredentialReference"); int credentialSecretOrdinal = reader.GetOrdinal("CredentialSecret"); int javaHomeOrdinal = reader.GetOrdinal("JavaHomePath"); int sonicHomeOrdinal = reader.GetOrdinal("SonicHomePath"); int notesOrdinal = reader.GetOrdinal("Notes"); int isActiveOrdinal = reader.GetOrdinal("IsActive"); while (await reader.ReadAsync(cancellationToken)) { connections.Add( new DatabaseSonicConnection { Id = reader.GetInt32(idOrdinal), Name = reader.GetString(nameOrdinal), ManagementHost = reader.GetString(hostOrdinal), ManagementPort = ReadNullableInt32( reader, portOrdinal), ConnectionProtocol = ReadNullableString( reader, protocolOrdinal), DomainName = ReadNullableString( reader, domainOrdinal), CredentialUserName = ReadNullableString( reader, userNameOrdinal), CredentialReference = ReadNullableString( reader, credentialReferenceOrdinal), CredentialSecret = ReadNullableString( reader, credentialSecretOrdinal), JavaHomePath = ReadNullableString( reader, javaHomeOrdinal), SonicHomePath = ReadNullableString( reader, sonicHomeOrdinal), Notes = ReadNullableString( reader, notesOrdinal), IsActive = reader.GetBoolean( isActiveOrdinal) }); } return connections; } public async Task SaveCredentialsAsync( int sonicConnectionId, string credentialUserName, string credentialSecret, CancellationToken cancellationToken = default) { if (sonicConnectionId <= 0) { throw new ArgumentOutOfRangeException( nameof(sonicConnectionId), "Die SonicConnectionId ist ungültig."); } if (string.IsNullOrWhiteSpace( credentialUserName)) { throw new ArgumentException( "Der Sonic-Benutzername fehlt.", nameof(credentialUserName)); } if (string.IsNullOrEmpty( credentialSecret)) { throw new ArgumentException( "Das Sonic-Kennwort fehlt.", nameof(credentialSecret)); } string normalizedUserName = credentialUserName.Trim(); if (normalizedUserName.Length > 256) { throw new ArgumentException( "Der Sonic-Benutzername darf maximal 256 Zeichen enthalten.", nameof(credentialUserName)); } if (credentialSecret.Length > 512) { throw new ArgumentException( "Das Sonic-Kennwort darf maximal 512 Zeichen enthalten.", nameof(credentialSecret)); } const string sql = """ UPDATE dbo.SonicConnection SET CredentialUserName = @CredentialUserName, CredentialSecret = @CredentialSecret, UpdatedAtUtc = SYSUTCDATETIME() 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( "@CredentialUserName", SqlDbType.NVarChar, 256) { Value = normalizedUserName }); command.Parameters.Add( new SqlParameter( "@CredentialSecret", SqlDbType.NVarChar, 512) { Value = credentialSecret }); command.Parameters.Add( new SqlParameter( "@SonicConnectionId", SqlDbType.Int) { Value = sonicConnectionId }); int affectedRows = await command.ExecuteNonQueryAsync( cancellationToken); if (affectedRows != 1) { throw new InvalidOperationException( "Die aktive Sonic-Verbindung wurde nicht eindeutig gefunden."); } } public async Task ClearCredentialsAsync( int sonicConnectionId, CancellationToken cancellationToken = default) { if (sonicConnectionId <= 0) { throw new ArgumentOutOfRangeException( nameof(sonicConnectionId), "Die SonicConnectionId ist ungültig."); } const string sql = """ UPDATE dbo.SonicConnection SET CredentialUserName = NULL, CredentialSecret = NULL, UpdatedAtUtc = SYSUTCDATETIME() WHERE SonicConnectionId = @SonicConnectionId; """; 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 }); int affectedRows = await command.ExecuteNonQueryAsync( cancellationToken); if (affectedRows != 1) { throw new InvalidOperationException( "Die Sonic-Verbindung wurde nicht eindeutig gefunden."); } } private static string? ReadNullableString( SqlDataReader reader, int ordinal) { return reader.IsDBNull(ordinal) ? null : reader.GetString(ordinal); } private static int? ReadNullableInt32( SqlDataReader reader, int ordinal) { return reader.IsDBNull(ordinal) ? null : reader.GetInt32(ordinal); } }