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> 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.SonicContainer AS container ON container.SonicConnectionId = connection.SonicConnectionId INNER JOIN dbo.EsbService AS service ON service.EsbServiceId = container.EsbServiceId INNER JOIN dbo.Environment AS environment ON environment.EnvironmentId = service.EnvironmentId WHERE connection.IsActive = 1 AND container.IsActive = 1 AND service.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 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)) { if (reader.IsDBNull(managementPortOrdinal)) { continue; } 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 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, UpdatedAtUtc = SYSUTCDATETIME() 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, CreatedAtUtc, UpdatedAtUtc ) OUTPUT INSERTED.SonicCredentialId VALUES ( @SonicConnectionId, @CredentialName, @CredentialUserName, @CredentialSecret, @IsDefault, 1, SYSUTCDATETIME(), 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> 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 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; } }