using CredentialManager.Models; using CredentialManager.Services; using CredentialManager.Integration; using DataConnection.CredentialManagement.Interfaces; using DataConnection.CredentialManagement.Models; using Microsoft.Extensions.Logging; using Microsoft.Data.SqlClient; using Microsoft.Data.Sqlite; namespace DataConnection.CredentialManagement.Services; /// /// Servizio per l'integrazione delle credenziali con DataConnection /// public class DataConnectionCredentialService : IDataConnectionCredentialService { private readonly ICredentialService _credentialService; private readonly IKeyAssociationService _keyAssociationService; private readonly ILogger _logger; public DataConnectionCredentialService( ICredentialService credentialService, IKeyAssociationService keyAssociationService, ILogger logger) { _credentialService = credentialService; _keyAssociationService = keyAssociationService; _logger = logger; } #region Database Credentials public async Task GetDatabaseCredentialAsync(string name) { return await _credentialService.GetDatabaseCredentialAsync(name); } public async Task GetDatabaseCredentialAsync(int id) { return await _credentialService.GetDatabaseCredentialAsync(id); } public async Task> GetAllDatabaseCredentialsAsync() { return await _credentialService.GetAllDatabaseCredentialsAsync(); } public async Task SaveDatabaseCredentialAsync(DatabaseCredential credential) { _logger.LogInformation("Saving database credential: {Name}", credential.Name); return await _credentialService.SaveDatabaseCredentialAsync(credential); } public async Task DeleteDatabaseCredentialAsync(int id) { _logger.LogInformation("Deleting database credential with ID: {Id}", id); return await _credentialService.DeleteCredentialAsync(id); } public async Task DeleteDatabaseCredentialAsync(string name) { _logger.LogInformation("Deleting database credential: {Name}", name); return await _credentialService.DeleteCredentialAsync(name); } #endregion #region REST API Credentials public async Task GetRestApiCredentialAsync(string name) { return await _credentialService.GetRestApiCredentialAsync(name); } public async Task GetRestApiCredentialAsync(int id) { return await _credentialService.GetRestApiCredentialAsync(id); } public async Task> GetAllRestApiCredentialsAsync() { return await _credentialService.GetAllRestApiCredentialsAsync(); } public async Task SaveRestApiCredentialAsync(RestApiCredential credential) { _logger.LogInformation("Saving REST API credential: {Name}", credential.Name); return await _credentialService.SaveRestApiCredentialAsync(credential); } public async Task DeleteRestApiCredentialAsync(int id) { _logger.LogInformation("Deleting REST API credential with ID: {Id}", id); return await _credentialService.DeleteCredentialAsync(id); } public async Task DeleteRestApiCredentialAsync(string name) { _logger.LogInformation("Deleting REST API credential: {Name}", name); return await _credentialService.DeleteCredentialAsync(name); } #endregion #region DataConnection Specific Operations public async Task GetConnectionStringAsync(string credentialName) { var credential = await GetDatabaseCredentialAsync(credentialName); if (credential == null) throw new InvalidOperationException($"Database credential '{credentialName}' not found"); return credential.ToConnectionString(); } public async Task GetConnectionStringAsync(int credentialId) { var credential = await GetDatabaseCredentialAsync(credentialId); if (credential == null) throw new InvalidOperationException($"Database credential with ID '{credentialId}' not found"); return credential.ToConnectionString(); } public async Task GetDbManagerOptionsAsync(string credentialName) { var credential = await GetDatabaseCredentialAsync(credentialName); if (credential == null) throw new InvalidOperationException($"Database credential '{credentialName}' not found"); var options = new DataConnection.EF.DbManagerOptions { ServerConnectionString = credential.ToConnectionString(), DatabaseName = credential.DatabaseName!, DatabaseType = credential.DatabaseType.ToDataConnectionDatabaseType(), CommandTimeout = credential.CommandTimeout }; // Configura automaticamente il servizio di scoperta database options.ConfigureDatabaseDiscovery(options.DatabaseType); _logger.LogDebug("Created DbManagerOptions for credential: {Name} ({DatabaseType})", credentialName, credential.DatabaseType); return options; } public async Task GetDbManagerOptionsAsync(int credentialId) { var credential = await GetDatabaseCredentialAsync(credentialId); if (credential == null) throw new InvalidOperationException($"Database credential with ID '{credentialId}' not found"); var options = new DataConnection.EF.DbManagerOptions { ServerConnectionString = credential.ToConnectionString(), DatabaseName = credential.DatabaseName!, DatabaseType = credential.DatabaseType.ToDataConnectionDatabaseType(), CommandTimeout = credential.CommandTimeout }; // Configura automaticamente il servizio di scoperta database options.ConfigureDatabaseDiscovery(options.DatabaseType); _logger.LogDebug("Created DbManagerOptions for credential ID: {Id} ({DatabaseType})", credentialId, credential.DatabaseType); return options; } public async Task GetRestServiceOptionsAsync(string credentialName) { var credential = await GetRestApiCredentialAsync(credentialName); if (credential == null) throw new InvalidOperationException($"REST API credential '{credentialName}' not found"); var options = new DataConnection.REST.Configuration.RestServiceOptions { BaseUrl = credential.BaseUrl, ApiKey = credential.ApiKey, Username = credential.Username, Password = credential.Password, AuthToken = credential.AuthToken, TimeoutSeconds = credential.TimeoutSeconds, IgnoreSslErrors = credential.IgnoreSslErrors }; _logger.LogDebug("Created RestServiceOptions for credential: {Name} ({BaseUrl})", credentialName, credential.BaseUrl); return options; } public async Task GetRestServiceOptionsAsync(int credentialId) { var credential = await GetRestApiCredentialAsync(credentialId); if (credential == null) throw new InvalidOperationException($"REST API credential with ID '{credentialId}' not found"); var options = new DataConnection.REST.Configuration.RestServiceOptions { BaseUrl = credential.BaseUrl, ApiKey = credential.ApiKey, Username = credential.Username, Password = credential.Password, AuthToken = credential.AuthToken, TimeoutSeconds = credential.TimeoutSeconds, IgnoreSslErrors = credential.IgnoreSslErrors }; _logger.LogDebug("Created RestServiceOptions for credential ID: {Id} ({BaseUrl})", credentialId, credential.BaseUrl); return options; } #endregion #region Connection Testing public async Task<(bool Success, string Message)> TestDatabaseConnectionAsync(string credentialName) { try { var credential = await GetDatabaseCredentialAsync(credentialName); if (credential == null) return (false, $"Credenziale '{credentialName}' non trovata"); return await TestDatabaseConnectionAsync(credential); } catch (Exception ex) { _logger.LogError(ex, "Errore nel test della connessione per credenziale: {Name}", credentialName); return (false, $"Errore nel test: {ex.Message}"); } } public async Task<(bool Success, string Message)> TestDatabaseConnectionAsync(DatabaseCredential credential) { try { var connectionString = ConnectionStringBuilder.BuildConnectionString(credential); // Test base della sintassi if (string.IsNullOrEmpty(connectionString)) return (false, "Connection string vuota o non valida"); _logger.LogInformation("Testing database connection for {Name} ({DatabaseType})", credential.Name, credential.DatabaseType); // Test di connessione diretto basato sul tipo di database try { return credential.DatabaseType switch { CredentialManager.Models.DatabaseType.SqlServer => await TestSqlServerConnection(connectionString, credential), CredentialManager.Models.DatabaseType.MySql => await TestMySqlConnection(connectionString, credential), CredentialManager.Models.DatabaseType.PostgreSql => await TestPostgreSqlConnection(connectionString, credential), CredentialManager.Models.DatabaseType.Oracle => await TestOracleConnection(connectionString, credential), CredentialManager.Models.DatabaseType.Sqlite => await TestSqliteConnection(connectionString, credential), _ => (false, $"Test di connessione non implementato per {credential.DatabaseType}") }; } catch (Exception ex) { _logger.LogError(ex, "Database connection test failed for {Name}", credential.Name); return (false, $"Errore di connessione: {ex.Message}"); } } catch (Exception ex) { _logger.LogError(ex, "Errore nel test della connessione database"); return (false, $"Errore: {ex.Message}"); } } private async Task<(bool Success, string Message)> TestSqlServerConnection(string connectionString, DatabaseCredential credential) { try { using var connection = new Microsoft.Data.SqlClient.SqlConnection(connectionString); connection.Open(); var command = connection.CreateCommand(); command.CommandText = "SELECT @@VERSION"; command.CommandTimeout = credential.CommandTimeout; var version = await command.ExecuteScalarAsync(); return (true, $"Connessione SQL Server riuscita!\n\nDettagli:\n- Host: {credential.Host}:{credential.Port}\n- Database: {credential.DatabaseName ?? "Default"}\n- Versione: {version?.ToString()?.Split('\n')[0]}\n- Timeout: {credential.CommandTimeout}s"); } catch (Exception ex) { return (false, $"Errore SQL Server: {ex.Message}"); } } private Task<(bool Success, string Message)> TestMySqlConnection(string connectionString, DatabaseCredential credential) { try { // Per MySQL, dovremmo usare MySql.Data.MySqlClient o Pomelo.EntityFrameworkCore.MySql // Per ora returnamo un messaggio che indica che il test non è implementato return Task.FromResult<(bool Success, string Message)>((false, "Test MySQL non implementato - installare MySql.Data.MySqlClient")); } catch (Exception ex) { return Task.FromResult<(bool Success, string Message)>((false, $"Errore MySQL: {ex.Message}")); } } private Task<(bool Success, string Message)> TestPostgreSqlConnection(string connectionString, DatabaseCredential credential) { try { // Per PostgreSQL, dovremmo usare Npgsql return Task.FromResult<(bool Success, string Message)>((false, "Test PostgreSQL non implementato - installare Npgsql")); } catch (Exception ex) { return Task.FromResult<(bool Success, string Message)>((false, $"Errore PostgreSQL: {ex.Message}")); } } private Task<(bool Success, string Message)> TestOracleConnection(string connectionString, DatabaseCredential credential) { try { // Per Oracle, dovremmo usare Oracle.ManagedDataAccess return Task.FromResult<(bool Success, string Message)>((false, "Test Oracle non implementato - installare Oracle.ManagedDataAccess")); } catch (Exception ex) { return Task.FromResult<(bool Success, string Message)>((false, $"Errore Oracle: {ex.Message}")); } } private async Task<(bool Success, string Message)> TestSqliteConnection(string connectionString, DatabaseCredential credential) { try { using var connection = new Microsoft.Data.Sqlite.SqliteConnection(connectionString); connection.Open(); var command = connection.CreateCommand(); command.CommandText = "SELECT sqlite_version()"; command.CommandTimeout = credential.CommandTimeout; var version = await command.ExecuteScalarAsync(); return (true, $"Connessione SQLite riuscita!\n\nDettagli:\n- File: {credential.Host ?? connectionString}\n- Versione SQLite: {version}\n- Timeout: {credential.CommandTimeout}s"); } catch (Exception ex) { return (false, $"Errore SQLite: {ex.Message}"); } } public async Task<(bool Success, string Message)> TestRestApiConnectionAsync(string credentialName) { try { _logger.LogInformation("Attempting to test REST API connection for credential: {Name}", credentialName); var credential = await GetRestApiCredentialAsync(credentialName); if (credential == null) { _logger.LogWarning("REST API credential '{Name}' not found", credentialName); return (false, $"Credenziale REST API '{credentialName}' non trovata"); } _logger.LogInformation("Found credential: {Name}, ServiceType: {ServiceType}, BaseUrl: {BaseUrl}", credential.Name, credential.ServiceType, credential.BaseUrl); return await TestRestApiConnectionAsync(credential); } catch (Exception ex) { _logger.LogError(ex, "Errore nel test della connessione REST API per credenziale: {Name}", credentialName); return (false, $"Errore nel test: {ex.Message}"); } } public async Task<(bool Success, string Message)> TestRestApiConnectionAsync(RestApiCredential credential) { try { if (string.IsNullOrEmpty(credential.BaseUrl)) return (false, "Base URL non specificata"); // Test base dell'URL if (!Uri.TryCreate(credential.BaseUrl, UriKind.Absolute, out var uri)) return (false, "Base URL non valida"); // Per i servizi specifici, eseguiamo test di autenticazione reali switch (credential.ServiceType) { case RestServiceType.SapB1ServiceLayer: return await TestSapB1ServiceLayerAuthentication(credential); case RestServiceType.Salesforce: return await TestSalesforceAuthentication(credential); default: // Per REST API generiche, facciamo un test di connettività base return await TestGenericRestApiConnection(credential); } } catch (Exception ex) { _logger.LogError(ex, "Errore nel test della connessione REST API"); return (false, $"Errore: {ex.Message}"); } } private async Task<(bool Success, string Message)> TestSapB1ServiceLayerAuthentication(RestApiCredential credential) { try { _logger.LogInformation("Testing SAP B1 Service Layer authentication for {Name} ({BaseUrl})", credential.Name, credential.BaseUrl); using var httpClient = new HttpClient(); httpClient.Timeout = TimeSpan.FromSeconds(credential.TimeoutSeconds); // Test di login al Service Layer var loginUrl = credential.BaseUrl.TrimEnd('/') + "/Login"; var loginData = new { CompanyDB = credential.CompanyDatabase, UserName = credential.Username, Password = credential.Password, Language = credential.Language ?? "en-US" }; var loginJson = System.Text.Json.JsonSerializer.Serialize(loginData); var loginContent = new StringContent(loginJson, System.Text.Encoding.UTF8, "application/json"); var response = await httpClient.PostAsync(loginUrl, loginContent); if (response.IsSuccessStatusCode) { return (true, $"Autenticazione SAP B1 Service Layer riuscita!\n\nDettagli:\n- Server: {credential.BaseUrl}\n- Company DB: {credential.CompanyDatabase}\n- Versione: {credential.Version}\n- Lingua: {credential.Language}\n- Timeout: {credential.TimeoutSeconds}s"); } else { var errorContent = await response.Content.ReadAsStringAsync(); return (false, $"Autenticazione SAP B1 fallita. Status: {response.StatusCode}\nDettagli: {errorContent}"); } } catch (HttpRequestException ex) { return (false, $"Errore di rete SAP B1: {ex.Message}"); } catch (TaskCanceledException ex) when (ex.InnerException is TimeoutException || ex.Message.Contains("timeout")) { return (false, $"Timeout della connessione SAP B1 ({credential.TimeoutSeconds}s)"); } } private async Task<(bool Success, string Message)> TestSalesforceAuthentication(RestApiCredential credential) { try { _logger.LogInformation("Testing Salesforce authentication for {Name} ({BaseUrl})", credential.Name, credential.BaseUrl); _logger.LogDebug("Salesforce credential details: Username={Username}, HasPassword={HasPassword}, HasSecurityToken={HasSecurityToken}, HasClientId={HasClientId}, HasClientSecret={HasClientSecret}", credential.Username, !string.IsNullOrEmpty(credential.Password), !string.IsNullOrEmpty(credential.SecurityToken), !string.IsNullOrEmpty(credential.ClientId), !string.IsNullOrEmpty(credential.ClientSecret)); using var httpClient = new HttpClient(); httpClient.Timeout = TimeSpan.FromSeconds(credential.TimeoutSeconds); // Test di autenticazione OAuth2 var tokenUrl = credential.BaseUrl.TrimEnd('/') + "/services/oauth2/token"; var tokenData = new List> { new("grant_type", "password"), new("username", credential.Username ?? "") }; // Aggiungiamo password + security token se disponibile var password = credential.Password ?? ""; if (!string.IsNullOrEmpty(credential.SecurityToken)) { password += credential.SecurityToken; } tokenData.Add(new("password", password)); // Aggiungiamo client credentials se disponibili if (!string.IsNullOrEmpty(credential.ClientId)) { tokenData.Add(new("client_id", credential.ClientId)); } if (!string.IsNullOrEmpty(credential.ClientSecret)) { tokenData.Add(new("client_secret", credential.ClientSecret)); } _logger.LogDebug("Posting to Salesforce token URL: {TokenUrl}", tokenUrl); var tokenContent = new FormUrlEncodedContent(tokenData); var response = await httpClient.PostAsync(tokenUrl, tokenContent); if (response.IsSuccessStatusCode) { var responseContent = await response.Content.ReadAsStringAsync(); _logger.LogInformation("Salesforce authentication successful for {Name}", credential.Name); return (true, $"Autenticazione Salesforce riuscita!\n\nDettagli:\n- Login URL: {credential.BaseUrl}\n- API Version: {credential.ApiVersion}\n- Sandbox: {credential.IsSandbox}\n- Tipo Auth: OAuth2\n- Timeout: {credential.TimeoutSeconds}s"); } else { var errorContent = await response.Content.ReadAsStringAsync(); _logger.LogWarning("Salesforce authentication failed for {Name}. Status: {StatusCode}, Response: {Response}", credential.Name, response.StatusCode, errorContent); return (false, $"Autenticazione Salesforce fallita. Status: {response.StatusCode}\nDettagli: {errorContent}"); } } catch (HttpRequestException ex) { _logger.LogError(ex, "Network error during Salesforce authentication for {Name}", credential.Name); return (false, $"Errore di rete Salesforce: {ex.Message}"); } catch (TaskCanceledException ex) when (ex.InnerException is TimeoutException || ex.Message.Contains("timeout")) { _logger.LogWarning("Timeout during Salesforce authentication for {Name} ({TimeoutSeconds}s)", credential.Name, credential.TimeoutSeconds); return (false, $"Timeout della connessione Salesforce ({credential.TimeoutSeconds}s)"); } catch (Exception ex) { _logger.LogError(ex, "Unexpected error during Salesforce authentication for {Name}", credential.Name); return (false, $"Errore imprevisto: {ex.Message}"); } } private async Task<(bool Success, string Message)> TestGenericRestApiConnection(RestApiCredential credential) { try { // Test di connessione reale per REST API generiche using var httpClient = new HttpClient(); // Configurazione del timeout httpClient.Timeout = TimeSpan.FromSeconds(credential.TimeoutSeconds); // Configurazione autenticazione if (!string.IsNullOrEmpty(credential.ApiKey)) { httpClient.DefaultRequestHeaders.Add("X-API-Key", credential.ApiKey); } else if (!string.IsNullOrEmpty(credential.AuthToken)) { httpClient.DefaultRequestHeaders.Add("Authorization", $"Bearer {credential.AuthToken}"); } else if (!string.IsNullOrEmpty(credential.Username) && !string.IsNullOrEmpty(credential.Password)) { var authValue = Convert.ToBase64String(System.Text.Encoding.UTF8.GetBytes($"{credential.Username}:{credential.Password}")); httpClient.DefaultRequestHeaders.Add("Authorization", $"Basic {authValue}"); } _logger.LogInformation("Testing generic REST API connection for {Name} ({BaseUrl})", credential.Name, credential.BaseUrl); // Facciamo una richiesta HEAD o GET semplice per testare la connettività var testEndpoint = credential.BaseUrl.TrimEnd('/') + "/"; var response = await httpClient.SendAsync(new HttpRequestMessage(HttpMethod.Head, testEndpoint)); // Anche un 404 o 401 indica che il server è raggiungibile var isReachable = response.StatusCode != System.Net.HttpStatusCode.RequestTimeout && !IsNetworkError(response.StatusCode); if (isReachable) { return (true, $"Connessione REST API riuscita!\n\nDettagli:\n- URL: {credential.BaseUrl}\n- Status: {response.StatusCode}\n- Timeout: {credential.TimeoutSeconds}s\n- Auth: {GetAuthType(credential)}"); } else { return (false, $"Connessione fallita. Status: {response.StatusCode}\nVerifica URL e configurazione di rete."); } } catch (HttpRequestException ex) { _logger.LogError(ex, "Generic REST API connection test failed for {Name}", credential.Name); return (false, $"Errore di rete: {ex.Message}"); } catch (TaskCanceledException ex) when (ex.InnerException is TimeoutException || ex.Message.Contains("timeout")) { return (false, $"Timeout della connessione ({credential.TimeoutSeconds}s). Il server potrebbe essere irraggiungibile."); } } private static bool IsNetworkError(System.Net.HttpStatusCode statusCode) { return statusCode == System.Net.HttpStatusCode.RequestTimeout || statusCode == System.Net.HttpStatusCode.BadGateway || statusCode == System.Net.HttpStatusCode.ServiceUnavailable || statusCode == System.Net.HttpStatusCode.GatewayTimeout; } private static string GetAuthType(RestApiCredential credential) { if (!string.IsNullOrEmpty(credential.ApiKey)) return "API Key"; if (!string.IsNullOrEmpty(credential.AuthToken)) return "Auth Token"; if (!string.IsNullOrEmpty(credential.Username)) return "Basic Auth"; return "Nessuna"; } #endregion #region SAP B1 Service Layer Credentials public async Task GetSapB1CredentialAsync(string name) { return await _credentialService.GetSapB1CredentialAsync(name); } public async Task GetSapB1CredentialAsync(int id) { return await _credentialService.GetSapB1CredentialAsync(id); } public async Task> GetAllSapB1CredentialsAsync() { return await _credentialService.GetAllSapB1CredentialsAsync(); } public async Task SaveSapB1CredentialAsync(SapB1ServiceLayerCredential credential) { _logger.LogInformation("Saving SAP B1 Service Layer credential: {Name}", credential.Name); return await _credentialService.SaveSapB1CredentialAsync(credential); } public async Task DeleteSapB1CredentialAsync(int id) { _logger.LogInformation("Deleting SAP B1 Service Layer credential with ID: {Id}", id); return await _credentialService.DeleteCredentialAsync(id); } public async Task DeleteSapB1CredentialAsync(string name) { _logger.LogInformation("Deleting SAP B1 Service Layer credential: {Name}", name); return await _credentialService.DeleteCredentialAsync(name); } public async Task<(bool Success, string Message)> TestSapB1ConnectionAsync(string credentialName) { try { var credential = await GetSapB1CredentialAsync(credentialName); if (credential == null) return (false, $"Credenziale SAP B1 '{credentialName}' non trovata"); return await TestSapB1ConnectionAsync(credential); } catch (Exception ex) { _logger.LogError(ex, "Errore nel test della connessione SAP B1 per credenziale: {Name}", credentialName); return (false, $"Errore nel test: {ex.Message}"); } } public async Task<(bool Success, string Message)> TestSapB1ConnectionAsync(SapB1ServiceLayerCredential credential) { try { if (string.IsNullOrEmpty(credential.ServerUrl)) return (false, "Server URL non specificato"); if (!Uri.TryCreate(credential.ServerUrl, UriKind.Absolute, out var uri)) return (false, "Server URL non valido"); _logger.LogInformation("Testing SAP B1 Service Layer connection for {Name} ({ServerUrl})", credential.Name, credential.ServerUrl); using var httpClient = new HttpClient(); httpClient.Timeout = TimeSpan.FromSeconds(credential.TimeoutSeconds); // Test di accesso al Service Layer var loginUrl = credential.ServerUrl.TrimEnd('/') + "/Login"; var loginData = new { CompanyDB = credential.CompanyDatabase, UserName = credential.Username, Password = credential.Password, Language = credential.Language ?? "en-US" }; var loginJson = System.Text.Json.JsonSerializer.Serialize(loginData); var loginContent = new StringContent(loginJson, System.Text.Encoding.UTF8, "application/json"); try { var response = await httpClient.PostAsync(loginUrl, loginContent); if (response.IsSuccessStatusCode) { return (true, $"Connessione SAP B1 Service Layer riuscita!\n\nDettagli:\n- Server: {credential.ServerUrl}\n- Company DB: {credential.CompanyDatabase}\n- Versione: {credential.Version}\n- Lingua: {credential.Language}\n- Timeout: {credential.TimeoutSeconds}s"); } else { var errorContent = await response.Content.ReadAsStringAsync(); return (false, $"Login fallito. Status: {response.StatusCode}\nDettagli: {errorContent}"); } } catch (HttpRequestException ex) { return (false, $"Errore di rete SAP B1: {ex.Message}"); } catch (TaskCanceledException ex) when (ex.InnerException is TimeoutException || ex.Message.Contains("timeout")) { return (false, $"Timeout della connessione SAP B1 ({credential.TimeoutSeconds}s)."); } } catch (Exception ex) { _logger.LogError(ex, "Errore nel test della connessione SAP B1"); return (false, $"Errore: {ex.Message}"); } } #endregion #region Salesforce Credentials public async Task GetSalesforceCredentialAsync(string name) { return await _credentialService.GetSalesforceCredentialAsync(name); } public async Task GetSalesforceCredentialAsync(int id) { return await _credentialService.GetSalesforceCredentialAsync(id); } public async Task> GetAllSalesforceCredentialsAsync() { return await _credentialService.GetAllSalesforceCredentialsAsync(); } public async Task SaveSalesforceCredentialAsync(SalesforceCredential credential) { _logger.LogInformation("Saving Salesforce credential: {Name}", credential.Name); return await _credentialService.SaveSalesforceCredentialAsync(credential); } public async Task DeleteSalesforceCredentialAsync(int id) { _logger.LogInformation("Deleting Salesforce credential with ID: {Id}", id); return await _credentialService.DeleteCredentialAsync(id); } public async Task DeleteSalesforceCredentialAsync(string name) { _logger.LogInformation("Deleting Salesforce credential: {Name}", name); return await _credentialService.DeleteCredentialAsync(name); } public async Task<(bool Success, string Message)> TestSalesforceConnectionAsync(string credentialName) { try { var credential = await GetSalesforceCredentialAsync(credentialName); if (credential == null) return (false, $"Credenziale Salesforce '{credentialName}' non trovata"); return await TestSalesforceConnectionAsync(credential); } catch (Exception ex) { _logger.LogError(ex, "Errore nel test della connessione Salesforce per credenziale: {Name}", credentialName); return (false, $"Errore nel test: {ex.Message}"); } } public async Task<(bool Success, string Message)> TestSalesforceConnectionAsync(SalesforceCredential credential) { try { if (string.IsNullOrEmpty(credential.LoginUrl)) return (false, "Login URL non specificato"); if (!Uri.TryCreate(credential.LoginUrl, UriKind.Absolute, out var uri)) return (false, "Login URL non valido"); _logger.LogInformation("Testing Salesforce connection for {Name} ({LoginUrl})", credential.Name, credential.LoginUrl); using var httpClient = new HttpClient(); httpClient.Timeout = TimeSpan.FromSeconds(credential.TimeoutSeconds); // Test di login SOAP/REST if (credential.UseSoapApi) { return await TestSalesforceSoapLogin(httpClient, credential); } else { return await TestSalesforceOAuthLogin(httpClient, credential); } } catch (Exception ex) { _logger.LogError(ex, "Errore nel test della connessione Salesforce"); return (false, $"Errore: {ex.Message}"); } } private async Task<(bool Success, string Message)> TestSalesforceOAuthLogin(HttpClient httpClient, SalesforceCredential credential) { try { var tokenUrl = credential.LoginUrl.TrimEnd('/') + "/services/oauth2/token"; var tokenData = new List> { new("grant_type", "password"), new("username", credential.Username), new("password", credential.Password + credential.SecurityToken), new("client_id", credential.ClientId ?? ""), new("client_secret", credential.ClientSecret ?? "") }; var tokenContent = new FormUrlEncodedContent(tokenData); var response = await httpClient.PostAsync(tokenUrl, tokenContent); if (response.IsSuccessStatusCode) { var responseContent = await response.Content.ReadAsStringAsync(); return (true, $"Connessione Salesforce riuscita!\n\nDettagli:\n- Login URL: {credential.LoginUrl}\n- API Version: {credential.ApiVersion}\n- Sandbox: {credential.IsSandbox}\n- Tipo Auth: OAuth2\n- Timeout: {credential.TimeoutSeconds}s"); } else { var errorContent = await response.Content.ReadAsStringAsync(); return (false, $"Login OAuth Salesforce fallito. Status: {response.StatusCode}\nDettagli: {errorContent}"); } } catch (Exception ex) { return (false, $"Errore OAuth Salesforce: {ex.Message}"); } } private async Task<(bool Success, string Message)> TestSalesforceSoapLogin(HttpClient httpClient, SalesforceCredential credential) { try { // Test semplificato per SOAP - verifica solo la raggiungibilità del servizio var soapUrl = credential.LoginUrl.TrimEnd('/') + "/services/Soap/u/" + credential.ApiVersion; var response = await httpClient.SendAsync(new HttpRequestMessage(HttpMethod.Head, soapUrl)); if (response.IsSuccessStatusCode || response.StatusCode == System.Net.HttpStatusCode.MethodNotAllowed) { return (true, $"Connessione Salesforce SOAP riuscita!\n\nDettagli:\n- SOAP URL: {soapUrl}\n- API Version: {credential.ApiVersion}\n- Sandbox: {credential.IsSandbox}\n- Tipo Auth: SOAP\n- Timeout: {credential.TimeoutSeconds}s"); } else { return (false, $"Servizio SOAP Salesforce non raggiungibile. Status: {response.StatusCode}"); } } catch (Exception ex) { return (false, $"Errore SOAP Salesforce: {ex.Message}"); } } #endregion #region Key Associations public async Task SaveKeyAssociationAsync(KeyAssociation association) { return await _keyAssociationService.SaveAssociationAsync(association); } public async Task FindKeyAssociationByValueAsync(string keyValue, string destinationEntity, string restCredentialName) { return await _keyAssociationService.FindAssociationByKeyValueAsync(keyValue, destinationEntity, restCredentialName); } public async Task FindKeyAssociationByValueAsync(string keyValue) { return await _keyAssociationService.FindAssociationByKeyValueAsync(keyValue); } public async Task> GetKeyAssociationsByDestinationAsync(string destinationEntity, string restCredentialName) { return await _keyAssociationService.GetAssociationsByDestinationAsync(destinationEntity, restCredentialName); } public async Task> GetAllActiveKeyAssociationsAsync() { return await _keyAssociationService.GetAllActiveAssociationsAsync(); } public async Task> GetAllKeyAssociationsAsync() { return await _keyAssociationService.GetAllAssociationsAsync(); } public async Task UpdateKeyAssociationAsync(KeyAssociation association) { return await _keyAssociationService.UpdateAssociationAsync(association); } public async Task DeactivateKeyAssociationAsync(int id) { return await _keyAssociationService.DeactivateAssociationAsync(id); } public async Task DeleteKeyAssociationAsync(int id) { return await _keyAssociationService.DeleteAssociationAsync(id); } public async Task ClearKeyAssociationsAsync(string destinationEntity, string restCredentialName) { return await _keyAssociationService.ClearAssociationsAsync(destinationEntity, restCredentialName); } public async Task ClearAllKeyAssociationsAsync() { return await _keyAssociationService.ClearAllAssociationsAsync(); } public async Task> GetInvalidKeyAssociationsAsync(string destinationEntity, string restCredentialName) { return await _keyAssociationService.GetInvalidAssociationsAsync(destinationEntity, restCredentialName); } public async Task CleanupInvalidKeyAssociationsAsync(string destinationEntity, string restCredentialName) { return await _keyAssociationService.CleanupInvalidAssociationsAsync(destinationEntity, restCredentialName); } public async Task UpdateKeyAssociationLastVerifiedAsync(int id) { return await _keyAssociationService.UpdateLastVerifiedAsync(id); } public async Task GetKeyAssociationStatisticsAsync() { return await _keyAssociationService.GetStatisticsAsync(); } #region Helper Methods public async Task GetCredentialIdByNameAsync(string name, CredentialManager.Models.CredentialType type) { return await _credentialService.GetCredentialIdByNameAsync(name, type); } #endregion #endregion }