diff --git a/DataConnection/REST/Implementations/SalesforceServiceClient.cs b/DataConnection/REST/Implementations/SalesforceServiceClient.cs index dd3308e..c20586d 100644 --- a/DataConnection/REST/Implementations/SalesforceServiceClient.cs +++ b/DataConnection/REST/Implementations/SalesforceServiceClient.cs @@ -132,13 +132,13 @@ namespace DataConnection.REST.Implementations if (string.IsNullOrEmpty(_options.Username) || string.IsNullOrEmpty(_options.Password)) { - Console.WriteLine("Salesforce authentication requires username and password in options"); + Console.WriteLine($"Salesforce authentication requires username and password in options. Username: '{_options.Username}', Password: '{(!string.IsNullOrEmpty(_options.Password) ? "***SET***" : "***NULL***")}'"); return false; } if (string.IsNullOrEmpty(_options.ApiKey) || string.IsNullOrEmpty(_options.AuthToken)) { - Console.WriteLine("Salesforce authentication requires ApiKey (ClientId) and AuthToken (ClientSecret) in options"); + Console.WriteLine($"Salesforce authentication requires ApiKey (ClientId) and AuthToken (ClientSecret) in options. ApiKey: '{_options.ApiKey}', AuthToken: '{(!string.IsNullOrEmpty(_options.AuthToken) ? "***SET***" : "***NULL***")}'"); return false; } diff --git a/Data_Coupler/Services/DataConnectionFactory.cs b/Data_Coupler/Services/DataConnectionFactory.cs index 819669c..1717aa2 100644 --- a/Data_Coupler/Services/DataConnectionFactory.cs +++ b/Data_Coupler/Services/DataConnectionFactory.cs @@ -7,9 +7,21 @@ using DataConnection.REST.Configuration; using DataConnection.CredentialManagement.Interfaces; using Microsoft.Extensions.DependencyInjection; using Microsoft.Extensions.Logging; +using System.Security.Cryptography; +using System.Text; namespace Data_Coupler.Services { + /// + /// Classe per tracciare i client REST cached con le loro credenziali + /// + internal class CachedRestClient + { + public IRestServiceClient Client { get; set; } = null!; + public string CredentialsHash { get; set; } = string.Empty; + public DateTime CachedAt { get; set; } + } + /// /// Factory service per creare istanze di DatabaseManager e RestClient /// @@ -46,8 +58,8 @@ namespace Data_Coupler.Services private readonly ILogger _logger; private readonly IDataConnectionCredentialService _credentialService; - // Cache per mantenere le istanze dei client REST autenticati - private readonly Dictionary _restClientCache = new(); + // Cache intelligente per client REST che mantiene lo stato di autenticazione ma verifica le credenziali + private readonly Dictionary _restClientCache = new(); public DataConnectionFactory(IServiceProvider serviceProvider, ILogger logger, IDataConnectionCredentialService credentialService) { @@ -76,17 +88,29 @@ namespace Data_Coupler.Services { try { - // Controlla se abbiamo già un client cached per questa credenziale - if (_restClientCache.TryGetValue(credentialName, out var cachedClient)) - { - _logger.LogInformation("Utilizzando client REST cached per {CredentialName}", credentialName); - return cachedClient; - } - + // SEMPRE recupera le credenziali fresh dal database per assicurare che siano aggiornate var credential = await _credentialService.GetRestApiCredentialAsync(credentialName); if (credential == null) { throw new ArgumentException($"Credenziale REST API '{credentialName}' non trovata"); + } + + // Calcola l'hash delle credenziali correnti + var currentCredentialsHash = CalculateCredentialsHash(credential); + + // Controlla se abbiamo un client cached con le stesse credenziali + if (_restClientCache.TryGetValue(credentialName, out var cachedClient)) + { + if (cachedClient.CredentialsHash == currentCredentialsHash) + { + _logger.LogInformation("Utilizzando client REST cached con credenziali verificate per {CredentialName}", credentialName); + return cachedClient.Client; + } + else + { + _logger.LogInformation("Credenziali cambiate, rimuovendo client cached per {CredentialName}", credentialName); + _restClientCache.Remove(credentialName); + } } var options = new RestServiceOptions { BaseUrl = credential.BaseUrl, @@ -101,8 +125,11 @@ namespace Data_Coupler.Services // Per Salesforce usiamo i campi specifici ClientId e ClientSecret options.ApiKey = credential.ClientId; // ClientId -> ApiKey options.AuthToken = credential.ClientSecret; // ClientSecret -> AuthToken - _logger.LogInformation("Salesforce mapping - ClientId: {ClientId}, ClientSecret: {HasSecret}, Username: {Username}", - credential.ClientId, !string.IsNullOrEmpty(credential.ClientSecret), credential.Username); + _logger.LogInformation("Salesforce mapping - ClientId: '{ClientId}', ClientSecret: {HasSecret}, Username: '{Username}', Password: {HasPassword}", + credential.ClientId, + !string.IsNullOrEmpty(credential.ClientSecret), + credential.Username, + !string.IsNullOrEmpty(credential.Password)); break; case RestServiceType.SapB1ServiceLayer: // Per SAP B1 usiamo il CompanyDatabase come ApiKey @@ -128,9 +155,15 @@ namespace Data_Coupler.Services _ => throw new NotSupportedException($"Tipo di servizio REST non supportato: {credential.ServiceType}") }; - // Salva il client nella cache - _restClientCache[credentialName] = client; - _logger.LogInformation("Client REST creato e cached per {CredentialName}", credentialName); + // Salva il client nella cache con l'hash delle credenziali + _restClientCache[credentialName] = new CachedRestClient + { + Client = client, + CredentialsHash = currentCredentialsHash, + CachedAt = DateTime.UtcNow + }; + + _logger.LogInformation("Client REST creato e cached con credenziali aggiornate per {CredentialName}", credentialName); return client; } @@ -143,13 +176,13 @@ namespace Data_Coupler.Services { try { - // Utilizza lo stesso client REST cached (che è già autenticato) + // Crea un nuovo client REST con credenziali aggiornate var restClient = await CreateRestServiceClientAsync(credentialName); // I service client già implementano IRestMetadataDiscovery if (restClient is IRestMetadataDiscovery metadataDiscovery) { - _logger.LogInformation("Utilizzando lo stesso client REST per metadata discovery per {CredentialName}", credentialName); + _logger.LogInformation("Utilizzando client REST per metadata discovery con credenziali aggiornate per {CredentialName}", credentialName); return metadataDiscovery; } @@ -178,7 +211,17 @@ namespace Data_Coupler.Services } /// - /// Pulisce la cache del client REST per una credenziale specifica + /// Calcola un hash delle credenziali per rilevare cambiamenti + /// + private string CalculateCredentialsHash(RestApiCredential credential) + { + var credentialString = $"{credential.BaseUrl}|{credential.Username}|{credential.Password}|{credential.ApiKey}|{credential.AuthToken}|{credential.ClientId}|{credential.ClientSecret}|{credential.CompanyDatabase}"; + var bytes = SHA256.HashData(Encoding.UTF8.GetBytes(credentialString)); + return Convert.ToBase64String(bytes); + } + + /// + /// Pulisce la cache del client REST per una credenziale specifica. /// public void ClearRestClientCache(string credentialName) { @@ -189,7 +232,7 @@ namespace Data_Coupler.Services } /// - /// Pulisce tutta la cache dei client REST + /// Pulisce tutta la cache dei client REST. /// public void ClearAllRestClientCache() {