using Microsoft.EntityFrameworkCore; using Microsoft.Extensions.Logging; using CredentialManager.Data; using CredentialManager.Models; using System.Text.Json; namespace CredentialManager.Services; /// /// Interfaccia per il servizio di gestione credenziali /// public interface ICredentialService { // Database Credentials Task SaveDatabaseCredentialAsync(DatabaseCredential credential); Task GetDatabaseCredentialAsync(string name); Task GetDatabaseCredentialAsync(int id); Task> GetAllDatabaseCredentialsAsync(); // REST API Credentials Task SaveRestApiCredentialAsync(RestApiCredential credential); Task GetRestApiCredentialAsync(string name); Task GetRestApiCredentialAsync(int id); Task> GetAllRestApiCredentialsAsync(); // SAP B1 Service Layer Credentials Task SaveSapB1CredentialAsync(SapB1ServiceLayerCredential credential); Task GetSapB1CredentialAsync(string name); Task GetSapB1CredentialAsync(int id); Task> GetAllSapB1CredentialsAsync(); // Salesforce Credentials Task SaveSalesforceCredentialAsync(SalesforceCredential credential); Task GetSalesforceCredentialAsync(string name); Task GetSalesforceCredentialAsync(int id); Task> GetAllSalesforceCredentialsAsync(); // Generic operations Task DeleteCredentialAsync(int id); Task DeleteCredentialAsync(string name); Task> GetCredentialNamesAsync(CredentialType? type = null); // Cascade delete operations Task DeleteCredentialCascadeAsync(int id); Task DeleteCredentialCascadeAsync(string name); // Helper methods to get credential ID by name Task GetCredentialIdByNameAsync(string name, CredentialType type); } /// /// Servizio per la gestione delle credenziali /// public class CredentialService : ICredentialService { private readonly CredentialDbContext _context; private readonly IEncryptionService _encryptionService; private readonly ILogger _logger; public CredentialService( CredentialDbContext context, IEncryptionService encryptionService, ILogger logger) { _context = context; _encryptionService = encryptionService; _logger = logger; } #region Database Credentials public async Task SaveDatabaseCredentialAsync(DatabaseCredential credential) { try { var entity = new CredentialEntity { Name = credential.Name, Type = CredentialType.Database.ToString(), DatabaseType = credential.DatabaseType.ToString(), Host = credential.Host, Port = credential.Port, DatabaseName = credential.DatabaseName, Username = credential.Username, EncryptedPassword = _encryptionService.Encrypt(credential.Password), ConnectionString = credential.ConnectionString, CommandTimeout = credential.CommandTimeout, IgnoreSslErrors = credential.IgnoreSslErrors, AdditionalParameters = credential.AdditionalParameters != null ? JsonSerializer.Serialize(credential.AdditionalParameters) : null, OdbcDsnName = credential.OdbcDsnName, OdbcMode = credential.OdbcMode.ToString(), CreatedAt = DateTime.UtcNow, CreatedBy = Environment.UserName }; // Verifica se esiste già una credenziale con lo stesso nome var existing = await _context.Credentials .FirstOrDefaultAsync(c => c.Name == credential.Name); if (existing != null) { // Aggiorna esistente existing.DatabaseType = entity.DatabaseType; existing.Host = entity.Host; existing.Port = entity.Port; existing.DatabaseName = entity.DatabaseName; existing.Username = entity.Username; existing.EncryptedPassword = entity.EncryptedPassword; existing.ConnectionString = entity.ConnectionString; existing.CommandTimeout = entity.CommandTimeout; existing.IgnoreSslErrors = entity.IgnoreSslErrors; existing.AdditionalParameters = entity.AdditionalParameters; existing.OdbcDsnName = entity.OdbcDsnName; existing.OdbcMode = entity.OdbcMode; existing.UpdatedAt = DateTime.UtcNow; _context.Credentials.Update(existing); await _context.SaveChangesAsync(); _logger.LogInformation("Credenziale database aggiornata: {Name}", credential.Name); return existing.Id; } else { // Crea nuovo _context.Credentials.Add(entity); await _context.SaveChangesAsync(); _logger.LogInformation("Nuova credenziale database salvata: {Name}", credential.Name); return entity.Id; } } catch (Exception ex) { _logger.LogError(ex, "Errore nel salvare la credenziale database: {Name}", credential.Name); throw; } } public async Task GetDatabaseCredentialAsync(string name) { try { var entity = await _context.Credentials .FirstOrDefaultAsync(c => c.Name == name && c.Type == CredentialType.Database.ToString() && c.IsActive); return entity != null ? MapToDatabaseCredential(entity) : null; } catch (Exception ex) { _logger.LogError(ex, "Errore nel recuperare la credenziale database: {Name}", name); throw; } } public async Task GetDatabaseCredentialAsync(int id) { try { var entity = await _context.Credentials .FirstOrDefaultAsync(c => c.Id == id && c.Type == CredentialType.Database.ToString() && c.IsActive); return entity != null ? MapToDatabaseCredential(entity) : null; } catch (Exception ex) { _logger.LogError(ex, "Errore nel recuperare la credenziale database: {Id}", id); throw; } } public async Task> GetAllDatabaseCredentialsAsync() { try { var entities = await _context.Credentials .Where(c => c.Type == CredentialType.Database.ToString() && c.IsActive) .ToListAsync(); return entities.Select(MapToDatabaseCredential).ToList(); } catch (Exception ex) { _logger.LogError(ex, "Errore nel recuperare tutte le credenziali database"); throw; } } #endregion #region REST API Credentials public async Task SaveRestApiCredentialAsync(RestApiCredential credential) { try { // Prepara i parametri aggiuntivi per includere i campi specifici del servizio var additionalParams = new Dictionary(); // Copia i parametri aggiuntivi esistenti if (credential.AdditionalParameters != null) { foreach (var param in credential.AdditionalParameters) { additionalParams[param.Key] = param.Value; } } // Aggiungi campi specifici per SAP B1 if (credential.ServiceType == RestServiceType.SapB1ServiceLayer) { if (!string.IsNullOrEmpty(credential.CompanyDatabase)) additionalParams["CompanyDatabase"] = credential.CompanyDatabase; if (!string.IsNullOrEmpty(credential.Language)) additionalParams["Language"] = credential.Language; if (!string.IsNullOrEmpty(credential.Version)) additionalParams["Version"] = credential.Version; additionalParams["UseTrustedConnection"] = credential.UseTrustedConnection.ToString(); } // Aggiungi campi specifici per Salesforce if (credential.ServiceType == RestServiceType.Salesforce) { if (!string.IsNullOrEmpty(credential.SecurityToken)) additionalParams["SecurityToken"] = credential.SecurityToken; if (!string.IsNullOrEmpty(credential.ClientId)) additionalParams["ClientId"] = credential.ClientId; if (!string.IsNullOrEmpty(credential.ClientSecret)) additionalParams["ClientSecret"] = credential.ClientSecret; if (!string.IsNullOrEmpty(credential.ApiVersion)) additionalParams["ApiVersion"] = credential.ApiVersion; additionalParams["IsSandbox"] = credential.IsSandbox.ToString(); additionalParams["UseSoapApi"] = credential.UseSoapApi.ToString(); if (!string.IsNullOrEmpty(credential.RefreshToken)) additionalParams["RefreshToken"] = credential.RefreshToken; if (!string.IsNullOrEmpty(credential.AccessToken)) additionalParams["AccessToken"] = credential.AccessToken; if (credential.TokenExpiry.HasValue) additionalParams["TokenExpiry"] = credential.TokenExpiry.Value.ToString("O"); } var entity = new CredentialEntity { Name = credential.Name, Type = CredentialType.RestApi.ToString(), RestServiceType = credential.ServiceType.ToString(), Host = credential.BaseUrl, Username = credential.Username, EncryptedPassword = !string.IsNullOrEmpty(credential.Password) ? _encryptionService.Encrypt(credential.Password) : null, EncryptedApiKey = !string.IsNullOrEmpty(credential.ApiKey) ? _encryptionService.Encrypt(credential.ApiKey) : null, EncryptedAuthToken = !string.IsNullOrEmpty(credential.AuthToken) ? _encryptionService.Encrypt(credential.AuthToken) : null, TimeoutSeconds = credential.TimeoutSeconds, IgnoreSslErrors = credential.IgnoreSslErrors, Headers = credential.Headers != null ? JsonSerializer.Serialize(credential.Headers) : null, AdditionalParameters = additionalParams.Count > 0 ? JsonSerializer.Serialize(additionalParams) : null, CreatedAt = DateTime.UtcNow, CreatedBy = Environment.UserName }; // Verifica se esiste già una credenziale con lo stesso nome var existing = await _context.Credentials .FirstOrDefaultAsync(c => c.Name == credential.Name); if (existing != null) { // Aggiorna esistente existing.RestServiceType = entity.RestServiceType; existing.Host = entity.Host; existing.Username = entity.Username; existing.EncryptedPassword = entity.EncryptedPassword; existing.EncryptedApiKey = entity.EncryptedApiKey; existing.EncryptedAuthToken = entity.EncryptedAuthToken; existing.TimeoutSeconds = entity.TimeoutSeconds; existing.IgnoreSslErrors = entity.IgnoreSslErrors; existing.Headers = entity.Headers; existing.AdditionalParameters = entity.AdditionalParameters; existing.UpdatedAt = DateTime.UtcNow; _context.Credentials.Update(existing); await _context.SaveChangesAsync(); _logger.LogInformation("Credenziale REST API aggiornata: {Name}", credential.Name); return existing.Id; } else { // Crea nuovo _context.Credentials.Add(entity); await _context.SaveChangesAsync(); _logger.LogInformation("Nuova credenziale REST API salvata: {Name}", credential.Name); return entity.Id; } } catch (Exception ex) { _logger.LogError(ex, "Errore nel salvare la credenziale REST API: {Name}", credential.Name); throw; } } public async Task GetRestApiCredentialAsync(string name) { try { var entity = await _context.Credentials .FirstOrDefaultAsync(c => c.Name == name && c.Type == CredentialType.RestApi.ToString() && c.IsActive); return entity != null ? MapToRestApiCredential(entity) : null; } catch (Exception ex) { _logger.LogError(ex, "Errore nel recuperare la credenziale REST API: {Name}", name); throw; } } public async Task GetRestApiCredentialAsync(int id) { try { var entity = await _context.Credentials .FirstOrDefaultAsync(c => c.Id == id && c.Type == CredentialType.RestApi.ToString() && c.IsActive); return entity != null ? MapToRestApiCredential(entity) : null; } catch (Exception ex) { _logger.LogError(ex, "Errore nel recuperare la credenziale REST API: {Id}", id); throw; } } public async Task> GetAllRestApiCredentialsAsync() { try { var entities = await _context.Credentials .Where(c => c.Type == CredentialType.RestApi.ToString() && c.IsActive) .ToListAsync(); return entities.Select(MapToRestApiCredential).ToList(); } catch (Exception ex) { _logger.LogError(ex, "Errore nel recuperare tutte le credenziali REST API"); throw; } } #endregion #region SAP B1 Service Layer Credentials public async Task SaveSapB1CredentialAsync(SapB1ServiceLayerCredential credential) { try { // Controlla se esiste già una credenziale con lo stesso nome var existingEntity = await _context.Credentials .FirstOrDefaultAsync(c => c.Name == credential.Name && c.Type == CredentialType.RestApi.ToString()); CredentialEntity entity; if (existingEntity != null) { entity = existingEntity; _logger.LogInformation("Aggiornamento credenziale SAP B1 esistente: {Name}", credential.Name); } else { entity = new CredentialEntity(); _context.Credentials.Add(entity); _logger.LogInformation("Creazione nuova credenziale SAP B1: {Name}", credential.Name); } // Popola i campi dell'entità entity.Name = credential.Name; entity.Type = CredentialType.RestApi.ToString(); entity.RestServiceType = RestServiceType.SapB1ServiceLayer.ToString(); entity.Host = credential.ServerUrl; entity.Username = credential.Username; entity.EncryptedPassword = _encryptionService.Encrypt(credential.Password); entity.TimeoutSeconds = credential.TimeoutSeconds; entity.IgnoreSslErrors = credential.IgnoreSslErrors; entity.Headers = credential.AdditionalHeaders != null ? JsonSerializer.Serialize(credential.AdditionalHeaders) : null; // Salva parametri aggiuntivi specifici per SAP B1 var additionalParams = new Dictionary { ["CompanyDatabase"] = credential.CompanyDatabase, ["Language"] = credential.Language ?? "en-US", ["Version"] = credential.Version ?? "v1", ["UseTrustedConnection"] = credential.UseTrustedConnection.ToString() }; entity.AdditionalParameters = JsonSerializer.Serialize(additionalParams); entity.CreatedAt = existingEntity?.CreatedAt ?? DateTime.UtcNow; entity.CreatedBy = existingEntity?.CreatedBy ?? Environment.UserName; entity.UpdatedAt = DateTime.UtcNow; await _context.SaveChangesAsync(); _logger.LogInformation("Credenziale SAP B1 salvata con ID: {Id}", entity.Id); return entity.Id; } catch (Exception ex) { _logger.LogError(ex, "Errore nel salvare la credenziale SAP B1: {Name}", credential.Name); throw; } } public async Task GetSapB1CredentialAsync(string name) { try { var entity = await _context.Credentials .FirstOrDefaultAsync(c => c.Name == name && c.Type == CredentialType.RestApi.ToString() && c.RestServiceType == RestServiceType.SapB1ServiceLayer.ToString() && c.IsActive); return entity != null ? MapToSapB1Credential(entity) : null; } catch (Exception ex) { _logger.LogError(ex, "Errore nel recuperare la credenziale SAP B1: {Name}", name); throw; } } public async Task GetSapB1CredentialAsync(int id) { try { var entity = await _context.Credentials.FindAsync(id); if (entity == null || entity.Type != CredentialType.RestApi.ToString() || entity.RestServiceType != RestServiceType.SapB1ServiceLayer.ToString() || !entity.IsActive) return null; return MapToSapB1Credential(entity); } catch (Exception ex) { _logger.LogError(ex, "Errore nel recuperare la credenziale SAP B1 con ID: {Id}", id); throw; } } public async Task> GetAllSapB1CredentialsAsync() { try { var entities = await _context.Credentials .Where(c => c.Type == CredentialType.RestApi.ToString() && c.RestServiceType == RestServiceType.SapB1ServiceLayer.ToString() && c.IsActive) .ToListAsync(); return entities.Select(MapToSapB1Credential).ToList(); } catch (Exception ex) { _logger.LogError(ex, "Errore nel recuperare tutte le credenziali SAP B1"); throw; } } #endregion #region Salesforce Credentials public async Task SaveSalesforceCredentialAsync(SalesforceCredential credential) { try { // Controlla se esiste già una credenziale con lo stesso nome var existingEntity = await _context.Credentials .FirstOrDefaultAsync(c => c.Name == credential.Name && c.Type == CredentialType.RestApi.ToString()); CredentialEntity entity; if (existingEntity != null) { entity = existingEntity; _logger.LogInformation("Aggiornamento credenziale Salesforce esistente: {Name}", credential.Name); } else { entity = new CredentialEntity(); _context.Credentials.Add(entity); _logger.LogInformation("Creazione nuova credenziale Salesforce: {Name}", credential.Name); } // Popola i campi dell'entità entity.Name = credential.Name; entity.Type = CredentialType.RestApi.ToString(); entity.RestServiceType = RestServiceType.Salesforce.ToString(); entity.Host = credential.LoginUrl; entity.Username = credential.Username; entity.EncryptedPassword = _encryptionService.Encrypt(credential.Password); entity.TimeoutSeconds = credential.TimeoutSeconds; // Salva parametri aggiuntivi specifici per Salesforce var additionalParams = new Dictionary { ["SecurityToken"] = credential.SecurityToken, ["ApiVersion"] = credential.ApiVersion, ["IsSandbox"] = credential.IsSandbox.ToString(), ["UseSoapApi"] = credential.UseSoapApi.ToString() }; // Aggiungi ClientId e ClientSecret se forniti if (!string.IsNullOrEmpty(credential.ClientId)) additionalParams["ClientId"] = credential.ClientId; if (!string.IsNullOrEmpty(credential.ClientSecret)) additionalParams["ClientSecret"] = credential.ClientSecret; if (!string.IsNullOrEmpty(credential.RefreshToken)) additionalParams["RefreshToken"] = credential.RefreshToken; if (!string.IsNullOrEmpty(credential.AccessToken)) additionalParams["AccessToken"] = credential.AccessToken; if (credential.TokenExpiry.HasValue) additionalParams["TokenExpiry"] = credential.TokenExpiry.Value.ToString("O"); entity.AdditionalParameters = JsonSerializer.Serialize(additionalParams); entity.CreatedAt = existingEntity?.CreatedAt ?? DateTime.UtcNow; entity.CreatedBy = existingEntity?.CreatedBy ?? Environment.UserName; entity.UpdatedAt = DateTime.UtcNow; await _context.SaveChangesAsync(); _logger.LogInformation("Credenziale Salesforce salvata con ID: {Id}", entity.Id); return entity.Id; } catch (Exception ex) { _logger.LogError(ex, "Errore nel salvare la credenziale Salesforce: {Name}", credential.Name); throw; } } public async Task GetSalesforceCredentialAsync(string name) { try { var entity = await _context.Credentials .FirstOrDefaultAsync(c => c.Name == name && c.Type == CredentialType.RestApi.ToString() && c.RestServiceType == RestServiceType.Salesforce.ToString() && c.IsActive); return entity != null ? MapToSalesforceCredential(entity) : null; } catch (Exception ex) { _logger.LogError(ex, "Errore nel recuperare la credenziale Salesforce: {Name}", name); throw; } } public async Task GetSalesforceCredentialAsync(int id) { try { var entity = await _context.Credentials.FindAsync(id); if (entity == null || entity.Type != CredentialType.RestApi.ToString() || entity.RestServiceType != RestServiceType.Salesforce.ToString() || !entity.IsActive) return null; return MapToSalesforceCredential(entity); } catch (Exception ex) { _logger.LogError(ex, "Errore nel recuperare la credenziale Salesforce con ID: {Id}", id); throw; } } public async Task> GetAllSalesforceCredentialsAsync() { try { var entities = await _context.Credentials .Where(c => c.Type == CredentialType.RestApi.ToString() && c.RestServiceType == RestServiceType.Salesforce.ToString() && c.IsActive) .ToListAsync(); return entities.Select(MapToSalesforceCredential).ToList(); } catch (Exception ex) { _logger.LogError(ex, "Errore nel recuperare tutte le credenziali Salesforce"); throw; } } #endregion #region Generic Operations public async Task DeleteCredentialAsync(int id) { try { var entity = await _context.Credentials.FindAsync(id); if (entity == null) return false; entity.IsActive = false; entity.UpdatedAt = DateTime.UtcNow; await _context.SaveChangesAsync(); _logger.LogInformation("Credenziale eliminata (soft delete): {Name} (ID: {Id})", entity.Name, entity.Id); return true; } catch (Exception ex) { _logger.LogError(ex, "Errore nell'eliminare la credenziale con ID: {Id}", id); throw; } } public async Task DeleteCredentialAsync(string name) { try { var entity = await _context.Credentials .FirstOrDefaultAsync(c => c.Name == name && c.IsActive); if (entity == null) return false; entity.IsActive = false; entity.UpdatedAt = DateTime.UtcNow; await _context.SaveChangesAsync(); _logger.LogInformation("Credenziale eliminata (soft delete): {Name} (ID: {Id})", entity.Name, entity.Id); return true; } catch (Exception ex) { _logger.LogError(ex, "Errore nell'eliminare la credenziale: {Name}", name); throw; } } public async Task> GetCredentialNamesAsync(CredentialType? type = null) { try { var query = _context.Credentials.Where(c => c.IsActive); if (type.HasValue) query = query.Where(c => c.Type == type.ToString()); return await query.Select(c => c.Name).ToListAsync(); } catch (Exception ex) { _logger.LogError(ex, "Errore nel recuperare i nomi delle credenziali"); throw; } } #endregion #region Private Mapping Methods private DatabaseCredential MapToDatabaseCredential(CredentialEntity entity) { var credential = new DatabaseCredential { Name = entity.Name, DatabaseType = Enum.Parse(entity.DatabaseType!), Host = entity.Host ?? string.Empty, Port = entity.Port ?? 0, DatabaseName = entity.DatabaseName ?? string.Empty, Username = entity.Username ?? string.Empty, Password = DecryptSafely(entity.EncryptedPassword, entity.Name, "password"), ConnectionString = entity.ConnectionString, CommandTimeout = entity.CommandTimeout, IgnoreSslErrors = entity.IgnoreSslErrors, OdbcDsnName = entity.OdbcDsnName, OdbcMode = !string.IsNullOrEmpty(entity.OdbcMode) && Enum.TryParse(entity.OdbcMode, out var odbcMode) ? odbcMode : OdbcConnectionMode.Dsn }; if (!string.IsNullOrEmpty(entity.AdditionalParameters)) { try { credential.AdditionalParameters = JsonSerializer.Deserialize>(entity.AdditionalParameters); } catch (Exception ex) { _logger.LogWarning(ex, "Errore nel deserializzare i parametri aggiuntivi per {Name}", entity.Name); } } return credential; } private RestApiCredential MapToRestApiCredential(CredentialEntity entity) { var credential = new RestApiCredential { Name = entity.Name, ServiceType = Enum.TryParse(entity.RestServiceType, out var serviceType) ? serviceType : RestServiceType.Generic, BaseUrl = entity.Host ?? string.Empty, Username = entity.Username, Password = !string.IsNullOrEmpty(entity.EncryptedPassword) ? DecryptSafely(entity.EncryptedPassword, entity.Name, "password") : null, ApiKey = !string.IsNullOrEmpty(entity.EncryptedApiKey) ? DecryptSafely(entity.EncryptedApiKey, entity.Name, "API key") : null, AuthToken = !string.IsNullOrEmpty(entity.EncryptedAuthToken) ? DecryptSafely(entity.EncryptedAuthToken, entity.Name, "auth token") : null, TimeoutSeconds = entity.TimeoutSeconds, IgnoreSslErrors = entity.IgnoreSslErrors }; if (!string.IsNullOrEmpty(entity.Headers)) { try { credential.Headers = JsonSerializer.Deserialize>(entity.Headers); } catch (Exception ex) { _logger.LogWarning(ex, "Errore nel deserializzare gli headers per {Name}", entity.Name); } } if (!string.IsNullOrEmpty(entity.AdditionalParameters)) { try { var additionalParams = JsonSerializer.Deserialize>(entity.AdditionalParameters); if (additionalParams != null) { // Imposta i parametri aggiuntivi generici credential.AdditionalParameters = new Dictionary(); // Mappa i campi specifici per SAP B1 if (credential.ServiceType == RestServiceType.SapB1ServiceLayer) { if (additionalParams.TryGetValue("CompanyDatabase", out var companyDb)) credential.CompanyDatabase = companyDb; if (additionalParams.TryGetValue("Language", out var language)) credential.Language = language; if (additionalParams.TryGetValue("Version", out var version)) credential.Version = version; if (additionalParams.TryGetValue("UseTrustedConnection", out var useTrusted) && bool.TryParse(useTrusted, out var trusted)) credential.UseTrustedConnection = trusted; } // Mappa i campi specifici per Salesforce else if (credential.ServiceType == RestServiceType.Salesforce) { if (additionalParams.TryGetValue("SecurityToken", out var securityToken)) credential.SecurityToken = securityToken; if (additionalParams.TryGetValue("ClientId", out var clientId)) credential.ClientId = clientId; if (additionalParams.TryGetValue("ClientSecret", out var clientSecret)) credential.ClientSecret = clientSecret; if (additionalParams.TryGetValue("ApiVersion", out var apiVersion)) credential.ApiVersion = apiVersion; if (additionalParams.TryGetValue("IsSandbox", out var isSandbox) && bool.TryParse(isSandbox, out var sandbox)) credential.IsSandbox = sandbox; if (additionalParams.TryGetValue("UseSoapApi", out var useSoap) && bool.TryParse(useSoap, out var soap)) credential.UseSoapApi = soap; if (additionalParams.TryGetValue("RefreshToken", out var refreshToken)) credential.RefreshToken = refreshToken; if (additionalParams.TryGetValue("AccessToken", out var accessToken)) credential.AccessToken = accessToken; if (additionalParams.TryGetValue("TokenExpiry", out var tokenExpiry) && DateTime.TryParse(tokenExpiry, out var expiry)) credential.TokenExpiry = expiry; } // Copia tutti i parametri che non sono specifici del servizio var serviceSpecificKeys = new HashSet { "CompanyDatabase", "Language", "Version", "UseTrustedConnection", "SecurityToken", "ClientId", "ClientSecret", "ApiVersion", "IsSandbox", "UseSoapApi", "RefreshToken", "AccessToken", "TokenExpiry" }; foreach (var param in additionalParams) { if (!serviceSpecificKeys.Contains(param.Key)) { credential.AdditionalParameters[param.Key] = param.Value; } } // Se non ci sono parametri aggiuntivi generici, imposta a null if (credential.AdditionalParameters.Count == 0) credential.AdditionalParameters = null; } } catch (Exception ex) { _logger.LogWarning(ex, "Errore nel deserializzare i parametri aggiuntivi per {Name}", entity.Name); } } return credential; } private SapB1ServiceLayerCredential MapToSapB1Credential(CredentialEntity entity) { var credential = new SapB1ServiceLayerCredential { Name = entity.Name, ServerUrl = entity.Host ?? string.Empty, Username = entity.Username ?? string.Empty, Password = !string.IsNullOrEmpty(entity.EncryptedPassword) ? _encryptionService.Decrypt(entity.EncryptedPassword) : string.Empty, TimeoutSeconds = entity.TimeoutSeconds, IgnoreSslErrors = entity.IgnoreSslErrors }; if (!string.IsNullOrEmpty(entity.Headers)) { try { credential.AdditionalHeaders = JsonSerializer.Deserialize>(entity.Headers); } catch (Exception ex) { _logger.LogWarning(ex, "Errore nel deserializzare gli headers per SAP B1 {Name}", entity.Name); } } if (!string.IsNullOrEmpty(entity.AdditionalParameters)) { try { var additionalParams = JsonSerializer.Deserialize>(entity.AdditionalParameters); if (additionalParams != null) { if (additionalParams.TryGetValue("CompanyDatabase", out var companyDb)) credential.CompanyDatabase = companyDb; if (additionalParams.TryGetValue("Language", out var language)) credential.Language = language; if (additionalParams.TryGetValue("Version", out var version)) credential.Version = version; if (additionalParams.TryGetValue("UseTrustedConnection", out var useTrusted) && bool.TryParse(useTrusted, out var trusted)) credential.UseTrustedConnection = trusted; } } catch (Exception ex) { _logger.LogWarning(ex, "Errore nel deserializzare i parametri aggiuntivi per SAP B1 {Name}", entity.Name); } } return credential; } private SalesforceCredential MapToSalesforceCredential(CredentialEntity entity) { var credential = new SalesforceCredential { Name = entity.Name, LoginUrl = entity.Host ?? "https://login.salesforce.com", Username = entity.Username ?? string.Empty, Password = !string.IsNullOrEmpty(entity.EncryptedPassword) ? _encryptionService.Decrypt(entity.EncryptedPassword) : string.Empty, TimeoutSeconds = entity.TimeoutSeconds }; if (!string.IsNullOrEmpty(entity.AdditionalParameters)) { try { var additionalParams = JsonSerializer.Deserialize>(entity.AdditionalParameters); if (additionalParams != null) { if (additionalParams.TryGetValue("SecurityToken", out var securityToken)) credential.SecurityToken = securityToken; if (additionalParams.TryGetValue("ClientId", out var clientId)) credential.ClientId = clientId; if (additionalParams.TryGetValue("ClientSecret", out var clientSecret)) credential.ClientSecret = clientSecret; if (additionalParams.TryGetValue("ApiVersion", out var apiVersion)) credential.ApiVersion = apiVersion; if (additionalParams.TryGetValue("IsSandbox", out var isSandbox) && bool.TryParse(isSandbox, out var sandbox)) credential.IsSandbox = sandbox; if (additionalParams.TryGetValue("UseSoapApi", out var useSoap) && bool.TryParse(useSoap, out var soap)) credential.UseSoapApi = soap; if (additionalParams.TryGetValue("RefreshToken", out var refreshToken)) credential.RefreshToken = refreshToken; if (additionalParams.TryGetValue("AccessToken", out var accessToken)) credential.AccessToken = accessToken; if (additionalParams.TryGetValue("TokenExpiry", out var tokenExpiry) && DateTime.TryParse(tokenExpiry, out var expiry)) credential.TokenExpiry = expiry; } } catch (Exception ex) { _logger.LogWarning(ex, "Errore nel deserializzare i parametri aggiuntivi per Salesforce {Name}", entity.Name); } } return credential; } #endregion #region Private Utility Methods /// /// Decrittografa in modo sicuro gestendo i fallimenti dovuti a migrazione tra macchine /// /// Valore crittografato /// Nome della credenziale per logging /// Nome del campo per logging /// Valore decrittografato o stringa speciale per indicare che serve re-inserimento private string DecryptSafely(string? encryptedValue, string credentialName, string fieldName) { if (string.IsNullOrEmpty(encryptedValue)) return string.Empty; try { return _encryptionService.Decrypt(encryptedValue); } catch (InvalidOperationException ex) when (ex.Message.Contains("Impossibile decrittografare")) { _logger.LogWarning("Impossibile decrittografare {FieldName} per la credenziale {CredentialName}. Probabile migrazione tra macchine diverse.", fieldName, credentialName); return "*** CREDENZIALI NON DISPONIBILI - REINSERIRE ***"; } catch (Exception ex) { _logger.LogWarning("Problema nella decrittografia di {FieldName} per la credenziale {CredentialName}: {Message}", fieldName, credentialName, ex.Message); return "*** ERRORE DECRITTOGRAFIA ***"; } } /// /// Verifica se una credenziale ha bisogno di essere re-inserita /// /// Valore della credenziale /// True se la credenziale deve essere re-inserita private bool NeedsReentry(string credentialValue) { return credentialValue.Contains("*** CREDENZIALI NON DISPONIBILI") || credentialValue.Contains("*** ERRORE DECRITTOGRAFIA ***"); } /// /// Ottiene l'ID di una credenziale per nome e tipo /// /// Nome della credenziale /// Tipo della credenziale /// ID della credenziale se trovata, null altrimenti public async Task GetCredentialIdByNameAsync(string name, CredentialType type) { try { var entity = await _context.Credentials .FirstOrDefaultAsync(c => c.Name == name && c.Type == type.ToString() && c.IsActive); return entity?.Id; } catch (Exception ex) { _logger.LogError(ex, "Errore nel recuperare l'ID della credenziale: {Name}, Tipo: {Type}", name, type); return null; } } /// /// Elimina fisicamente una credenziale e tutti i dati associati (profili e schedulazioni) in modo cascata /// /// ID della credenziale da eliminare /// True se l'eliminazione è riuscita, False altrimenti public async Task DeleteCredentialCascadeAsync(int id) { using var transaction = await _context.Database.BeginTransactionAsync(); try { var credential = await _context.Credentials.FindAsync(id); if (credential == null) { _logger.LogWarning("Tentativo di eliminare credenziale inesistente con ID: {Id}", id); return false; } _logger.LogInformation("Inizio eliminazione cascata per credenziale: {Name} (ID: {Id})", credential.Name, credential.Id); // 1. Trova tutti i profili che usano questa credenziale (come sorgente o destinazione) var profilesToDelete = await _context.DataCouplerProfiles .Where(p => p.SourceCredentialId == id || p.DestinationCredentialId == id) .ToListAsync(); _logger.LogInformation("Trovati {Count} profili associati alla credenziale {Name}", profilesToDelete.Count, credential.Name); // 2. Per ogni profilo, elimina le schedulazioni associate foreach (var profile in profilesToDelete) { var schedulesToDelete = await _context.ProfileSchedules .Where(s => s.ProfileId == profile.Id) .ToListAsync(); if (schedulesToDelete.Any()) { _logger.LogInformation("Eliminazione di {Count} schedulazioni per il profilo {ProfileName}", schedulesToDelete.Count, profile.Name); // Elimina le execution histories delle schedulazioni foreach (var schedule in schedulesToDelete) { var histories = await _context.ScheduleExecutionHistories .Where(h => h.ScheduleId == schedule.Id) .ToListAsync(); if (histories.Any()) { _context.ScheduleExecutionHistories.RemoveRange(histories); _logger.LogInformation("Eliminate {Count} execution histories per la schedulazione {ScheduleName}", histories.Count, schedule.Name); } } _context.ProfileSchedules.RemoveRange(schedulesToDelete); } } // 3. Elimina i profili if (profilesToDelete.Any()) { _context.DataCouplerProfiles.RemoveRange(profilesToDelete); _logger.LogInformation("Eliminati {Count} profili associati alla credenziale {Name}", profilesToDelete.Count, credential.Name); } // 4. Elimina le key associations associate (se presenti) var keyAssociations = await _context.KeyAssociations .Where(ka => ka.RestCredentialName == credential.Name) .ToListAsync(); if (keyAssociations.Any()) { _context.KeyAssociations.RemoveRange(keyAssociations); _logger.LogInformation("Eliminate {Count} key associations per la credenziale {Name}", keyAssociations.Count, credential.Name); } // 5. Infine, elimina la credenziale _context.Credentials.Remove(credential); // Salva tutte le modifiche await _context.SaveChangesAsync(); await transaction.CommitAsync(); _logger.LogInformation( "Credenziale {Name} (ID: {Id}) eliminata con successo insieme a {ProfileCount} profili, " + "{ScheduleCount} schedulazioni e {KeyAssociationCount} key associations", credential.Name, credential.Id, profilesToDelete.Count, profilesToDelete.Sum(p => _context.ProfileSchedules.Count(s => s.ProfileId == p.Id)), keyAssociations.Count); return true; } catch (Exception ex) { await transaction.RollbackAsync(); _logger.LogError(ex, "Errore durante l'eliminazione cascata della credenziale con ID: {Id}", id); throw; } } /// /// Elimina fisicamente una credenziale e tutti i dati associati (profili e schedulazioni) in modo cascata /// /// Nome della credenziale da eliminare /// True se l'eliminazione è riuscita, False altrimenti public async Task DeleteCredentialCascadeAsync(string name) { try { var credential = await _context.Credentials .FirstOrDefaultAsync(c => c.Name == name); if (credential == null) { _logger.LogWarning("Tentativo di eliminare credenziale inesistente: {Name}", name); return false; } return await DeleteCredentialCascadeAsync(credential.Id); } catch (Exception ex) { _logger.LogError(ex, "Errore durante l'eliminazione cascata della credenziale: {Name}", name); throw; } } #endregion }