# Data-Coupler: Sistema di Integrazione Dati Multi-Platform ## 📋 Project Overview **Data-Coupler** è una soluzione enterprise di integrazione dati sviluppata in .NET 9.0 con architettura Blazor Server. Il sistema facilita il trasferimento e la sincronizzazione di dati tra diverse tipologie di sorgenti: database relazionali, REST API, file Excel/CSV. ### 🎯 Obiettivi del Progetto - **Unificazione Data Sources**: Connessione trasparente a database eterogenei e API REST - **Trasferimenti Sicuri**: Gestione crittografata delle credenziali cross-platform - **Automazione**: Sistema di scheduling per operazioni periodiche - **Mappatura Intelligente**: Sistema avanzato di mapping campi tra sorgenti diverse - **Gestione Associazioni**: Tracking delle chiavi per evitare duplicati e gestire relazioni - **Backup e Ripristino**: Sistema completo di backup/restore per configurazioni e dati - **Amministrazione Avanzata**: Interfaccia unificata per gestione sistema e sicurezza ## 🚀 **NUOVE FUNZIONALITÀ - Salesforce OAuth2 Client Credentials Flow (2026)** ### Supporto `grant_type=client_credentials` per autenticazione server-to-server **Data Aggiornamento**: 2026 #### **Panoramica** Aggiunto supporto per il flusso OAuth2 `client_credentials` come alternativa al flusso `password` già esistente. Completamente retrocompatibile: il default rimane `Password`. #### **Enum `SalesforceGrantType`** (in `CredentialManager/Models/CredentialModels.cs`) ```csharp public enum SalesforceGrantType { Password, // grant_type=password — richiede Username, Password, SecurityToken (+ClientId/ClientSecret) ClientCredentials // grant_type=client_credentials — server-to-server, nessun utente } ``` #### **Differenze tra i flussi** | Aspetto | `password` | `client_credentials` | |---|---|---| | Richiede Username/Password | ✅ Sì | ❌ No | | Richiede SecurityToken | ✅ Sì (se non Connected App) | ❌ No | | ClientId / ClientSecret | Opzionale | ✅ Obbligatorio | | Base URL | login/test.salesforce.com | **My Domain URL** (es. `https://myorg.my.salesforce.com`) | | Utente Salesforce | Necessario | Integration User (assegnato nella Connected App) | #### **File modificati** - `CredentialManager/Models/CredentialModels.cs` — enum `SalesforceGrantType`, proprietà `GrantType` su `RestApiCredential` e `SalesforceCredential` - `DataConnection/REST/Configuration/RestServiceOptions.cs` — proprietà `SalesforceGrantType` - `DataConnection/REST/Implementations/SalesforceServiceClient.cs` — `AuthenticateWithPasswordAsync`, `AuthenticateWithClientCredentialsAsync`, `SendTokenRequestAsync`; `ILogger` iniettato; `NormalizeNumericValues` reso non-static - `Data_Coupler/Services/DataConnectionFactory.cs` — mapping `GrantType`, logger passato al client - `CredentialManager/Services/CredentialService.cs` — `GrantType` serializzato/deserializzato in `AdditionalParameters` JSON - `DataConnection/CredentialManagement/Services/DataConnectionCredentialService.cs` — `TestSalesforceOAuthLogin` instrada per `GrantType` - `Data_Coupler/Pages/CredentialManagement.razor` — dropdown "Tipo di Autenticazione OAuth2"; Username/Password/SecurityToken nascosti per `ClientCredentials`; warning My Domain URL --- ## 🚀 **NUOVE FUNZIONALITÀ - Salesforce Optimizations (Febbraio 2026)** ### Salesforce Batch Describe via Composite API **Data Aggiornamento**: Febbraio 2026 La discovery dei metadati Salesforce è stata ottimizzata tramite la Composite Batch API: #### **`BatchDescribeSObjectsAsync`** (nuovo metodo privato in `SalesforceServiceClient`) - Raggruppa i nomi degli SObject in chunk da 25 - Ogni chunk viene inviato come singola `POST /services/data/vXX.0/composite/batch` - I risultati vengono processati in parallelo via `Task.WhenAll` - **Risparmio concreto**: per 200 SObject, da 201 chiamate API a sole 9 #### **Discovery Parallela in `RESTMethod.cs`** - `DiscoverEntitySummariesAsync` (rapida, 1 chiamata) e `DiscoverEntitiesAsync` (batch) partono in parallelo - La lista entità diventa interattiva dopo ~0.3 s; i dettagli completano in background - `StateHasChanged()` chiamato dopo le summaries per aggiornare subito la UI #### **Fix Scheduler: External ID Relationships e Default Values** - **Bug 1** (`DataCoupler.razor.cs`): in entrambi i blocchi di update profilo esistente (riattivazione profilo inattivo + sovrascrittura profilo attivo), i campi `ExternalIdRelationshipsJson` e `DefaultValuesJson` venivano omessi nella copia → cancellati silenziosamente ad ogni re-salvataggio - **Bug 2** (`ScheduledProfileExecutionService.cs`): `TransformRecordForRest` non escludeva i campi sorgente usati nelle External ID Relationships dal loop di mapping normale, causando dati duplicati nell'entità destinazione (stessa logica già presente nella UI manuale, ora allineata allo scheduler) --- ## 🚀 **NUOVE FUNZIONALITÀ - Salesforce Batch Extraction** ### Miglioramenti Significativi alle Performance REST **Data Aggiornamento**: Settembre 2024 Il sistema `SalesforceServiceClient` è stato completamente potenziato con funzionalità avanzate per l'estrazione batch di oggetti REST: #### **Nuovi Metodi Implementati:** 1. **`BatchExecuteQueriesAsync`** - Esecuzione parallela di multiple query SOQL - Utilizza Salesforce Composite API (max 25 query per batch) - Processing parallelo automatico dei batch - **Performance**: 10-25x più veloce per grandi dataset 2. **`BatchFindEntitiesByKeysAsync`** - Ricerca batch di entità multiple - Ricerca simultanea con diverse combinazioni di chiavi - Riduzione drastica delle chiamate API (60-90% in meno) 3. **`BatchGetEntitiesByIdsAsync`** - Recupero batch tramite ID - Query ottimizzate con clausole IN (max 200 ID per query) - Gestione automatica di migliaia di ID 4. **`ExtractAllEntitiesAsync`** - Estrazione completa con paginazione - Paginazione automatica usando `nextRecordsUrl` - Auto-discovery campi disponibili - Gestione intelligente della memoria 5. **`ExtractEntitiesParallelAsync`** - Estrazione parallela avanzata - Split automatico per criteri (date, tipo, ecc.) - Deduplicazione automatica basata su ID - Processing parallelo ottimizzato 6. **`ExtractLargeDatasetAsync`** - Estrattore intelligente - Auto-detect dimensione dataset (>10K = parallelo) - Strategia adattiva (sequenziale vs parallelo) - Chunking automatico basato su date 7. **`ExtractRecentlyModifiedAsync`** - Sincronizzazione incrementale - Estrazione ottimizzata per record modificati di recente - Configurabile (ore/giorni indietro) #### **Utilità e Helper Methods:** - **`CreateDateBasedWhereClauses`**: Genera automaticamente chunk temporali - **Error Handling Avanzato**: Isolamento errori batch, fallback graceful - **Memory Management**: Streaming processing per evitare OutOfMemory #### **Risultati Performance:** - **🚀 Velocità**: 10-25x miglioramento per grandi dataset - **📉 API Calls**: Riduzione 60-90% chiamate API - **💾 Memoria**: Gestione ottimizzata con chunking - **🔄 Affidabilità**: Retry automatico e gestione errori robusta #### **Esempi di Utilizzo:** ```csharp // Estrazione completa con paginazione automatica var allAccounts = await salesforceClient.ExtractAllEntitiesAsync("Account"); // Estrazione parallela per grandi dataset var largeData = await salesforceClient.ExtractLargeDatasetAsync("Case", maxRecords: 100000); // Sincronizzazione incrementale (ultime 24 ore) var recent = await salesforceClient.ExtractRecentlyModifiedAsync("Contact", hoursBack: 24); // Ricerca batch multiple email var searchResults = await salesforceClient.BatchFindEntitiesByKeysAsync("Contact", emailList); ``` **Documentazione Completa**: `SALESFORCE_BATCH_EXTRACTION_IMPROVEMENTS.md` **Esempi Pratici**: `DataConnection\REST\Examples\SalesforceBatchExtractionExamples.cs` ### 🏗️ Architettura del Sistema #### Struttura Modulare ``` Data-Coupler/ ├── Data_Coupler/ # 🎯 Applicazione principale Blazor Server ├── DataConnection/ # 🔌 Libreria core per connessioni dati ├── CredentialManager/ # 🔐 Gestione sicura credenziali ├── Components/ # 🧩 Componenti UI riutilizzabili └── DatabaseUpdater/ # 🔧 Utility per manutenzione database ``` #### Stack Tecnologico - **.NET 9.0**: Framework principale - **Blazor Server**: Framework UI reattivo - **Entity Framework Core**: ORM per accesso dati - **SQLite**: Database embedded per configurazioni - **Bootstrap 5**: Framework CSS responsive #### Database Supportati - SQL Server, MySQL, PostgreSQL, Oracle, SQLite, IBM DB2, SAP HANA #### REST API Supportate - Generic REST APIs, Salesforce, SAP Business One Service Layer ## 🔨 Build and Test Commands ### Prerequisiti - .NET 9.0 SDK - Git per clone del repository - Editor: Visual Studio 2022, VS Code, o Rider ### Setup Ambiente di Sviluppo ```bash # Clone del repository git clone https://github.com/AlessioDalsi/Data-Coupler.git cd Data-Coupler # Restore dei pacchetti NuGet dotnet restore Data_Coupler.sln ``` ### Build Commands #### Build Development ```bash # Build completo della soluzione dotnet build Data_Coupler.sln # Build singolo progetto dotnet build Data_Coupler/Data_Coupler.csproj dotnet build DataConnection/DataConnection.csproj dotnet build CredentialManager/CredentialManager.csproj ``` #### Build Production ```bash # Build release dotnet build Data_Coupler.sln --configuration Release # Publish per deployment dotnet publish Data_Coupler/Data_Coupler.csproj \ --configuration Release \ --output ./publish \ --self-contained true \ --runtime win-x64 ``` #### Build per Diverse Piattaforme ```bash # Windows x64 dotnet publish --runtime win-x64 --configuration Release # Linux x64 dotnet publish --runtime linux-x64 --configuration Release # macOS x64 dotnet publish --runtime osx-x64 --configuration Release # macOS ARM64 (Apple Silicon) dotnet publish --runtime osx-arm64 --configuration Release ``` ### Run Commands #### Sviluppo Locale ```bash # Avvio applicazione principale dotnet run --project Data_Coupler/Data_Coupler.csproj # Avvio con hot reload dotnet watch run --project Data_Coupler/Data_Coupler.csproj # Avvio su porta specifica dotnet run --project Data_Coupler/Data_Coupler.csproj --urls "http://localhost:8080" ``` #### Utility e Manutenzione ```bash # Aggiornamento database dotnet run --project DatabaseUpdater/DatabaseUpdater.csproj # Creazione migrazione Entity Framework cd CredentialManager dotnet ef migrations add [MigrationName] dotnet ef database update ``` ### Test Commands #### Esecuzione Test Unitari ```bash # Esecuzione tutti i test dotnet test Data_Coupler.sln # Test con verbosità dettagliata dotnet test Data_Coupler.sln --verbosity detailed # Test con coverage dotnet test Data_Coupler.sln --collect:"XPlat Code Coverage" # Test specifici per progetto dotnet test DataConnection.Tests/DataConnection.Tests.csproj dotnet test CredentialManager.Tests/CredentialManager.Tests.csproj ``` #### Test di Integrazione ```bash # Test di integrazione database dotnet test --filter "Category=Integration" # Test connessioni REST dotnet test --filter "Category=RestIntegration" # Test end-to-end dotnet test --filter "Category=E2E" ``` #### Performance Testing ```bash # Benchmark performance dotnet run --project PerformanceTests/PerformanceTests.csproj --configuration Release # Memory profiling dotnet run --project Data_Coupler/Data_Coupler.csproj --configuration Release # Utilizzare strumenti come dotMemory o PerfView ``` ## 📝 Code Style Guidelines ### Convenzioni di Naming #### C# Naming Conventions ```csharp // Classes e Interfaces: PascalCase public class DataConnectionFactory { } public interface IDatabaseManager { } // Methods: PascalCase public async Task> GetDataAsync() { } // Properties: PascalCase public string ConnectionString { get; set; } // Private fields: camelCase con underscore private readonly ILogger _logger; // Local variables e parameters: camelCase public void ProcessData(string connectionName, int batchSize) { } // Constants: PascalCase public const string DefaultTimeout = "30"; ``` #### File e Directory Naming ``` // Cartelle: PascalCase Services/ Models/ Extensions/ // File C#: PascalCase con classe principale DatabaseConnectionService.cs CredentialEntity.cs // File Razor: PascalCase DataCoupler.razor ProfileManagement.razor ``` ### Organizzazione Codice #### Structure dei File ```csharp using System; // System namespaces using System.Collections.Generic; using Microsoft.Extensions; // Microsoft namespaces using DataConnection.Interfaces; // Project namespaces using ThirdParty.Library; // Third-party namespaces namespace Data_Coupler.Services { /// /// Descrizione chiara della classe /// public class ExampleService : IExampleService { #region Private Fields private readonly ILogger _logger; #endregion #region Constructor public ExampleService(ILogger logger) { _logger = logger ?? throw new ArgumentNullException(nameof(logger)); } #endregion #region Public Methods public async Task ProcessAsync(Request request) { // Implementation } #endregion #region Private Methods private void ValidateRequest(Request request) { // Validation logic } #endregion } } ``` #### Async/Await Pattern ```csharp // ✅ Corretto public async Task> GetDataAsync(CancellationToken cancellationToken = default) { var data = await _repository.GetDataAsync(cancellationToken); return data.ToList(); } // ❌ Evitare public async Task> GetData() { var data = await _repository.GetDataAsync().Result; // Blocking call return data.ToList(); } ``` #### Error Handling ```csharp // ✅ Gestione errori strutturata public async Task> ProcessDataAsync(string input) { try { if (string.IsNullOrWhiteSpace(input)) return Result.Failure("Input cannot be empty"); var data = await _service.ProcessAsync(input); return Result.Success(data); } catch (ValidationException ex) { _logger.LogWarning(ex, "Validation failed for input: {Input}", input); return Result.Failure(ex.Message); } catch (Exception ex) { _logger.LogError(ex, "Unexpected error processing input: {Input}", input); return Result.Failure("An unexpected error occurred"); } } ``` ### Dependency Injection Guidelines #### Service Registration ```csharp // Program.cs - Registrazione servizi // Singleton per servizi stateless builder.Services.AddSingleton(); // Scoped per servizi con stato per request builder.Services.AddScoped(); // Transient per servizi leggeri builder.Services.AddTransient, ModelValidator>(); ``` #### Constructor Injection ```csharp public class ProfileService { private readonly IDbContext _context; private readonly ILogger _logger; private readonly IOptions _options; public ProfileService( IDbContext context, ILogger logger, IOptions options) { _context = context ?? throw new ArgumentNullException(nameof(context)); _logger = logger ?? throw new ArgumentNullException(nameof(logger)); _options = options ?? throw new ArgumentNullException(nameof(options)); } } ``` ### Commenti e Documentazione #### XML Documentation ```csharp /// /// Trasferisce dati da una sorgente a una destinazione utilizzando il mapping specificato /// /// Connessione alla sorgente dati /// Connessione alla destinazione /// Configurazione mapping campi /// Token per cancellazione operazione /// Risultato del trasferimento con statistiche /// Lanciata quando un parametro richiesto è null /// Lanciata quando le connessioni non sono valide public async Task TransferDataAsync( IDataConnection sourceConnection, IDataConnection destinationConnection, FieldMapping mapping, CancellationToken cancellationToken = default) { // Implementation } ``` ## 🧪 Testing Instructions ### Struttura Test #### Test Project Organization ``` Tests/ ├── Unit/ # Test unitari │ ├── Data_Coupler.Tests/ │ ├── DataConnection.Tests/ │ └── CredentialManager.Tests/ ├── Integration/ # Test di integrazione │ ├── Database.Integration.Tests/ │ └── API.Integration.Tests/ └── E2E/ # Test end-to-end └── UI.Tests/ ``` ### Test Unitari #### Setup Test Class ```csharp [TestClass] public class CredentialServiceTests { private Mock _mockContext; private Mock _mockEncryption; private Mock> _mockLogger; private CredentialService _service; [TestInitialize] public void Setup() { _mockContext = new Mock(); _mockEncryption = new Mock(); _mockLogger = new Mock>(); _service = new CredentialService( _mockContext.Object, _mockEncryption.Object, _mockLogger.Object); } [TestCleanup] public void Cleanup() { _service?.Dispose(); } } ``` #### Test Methods Pattern ```csharp [TestMethod] public async Task GetCredentialAsync_WithValidName_ReturnsCredential() { // Arrange var credentialName = "TestCredential"; var expectedCredential = new CredentialEntity { Name = credentialName }; _mockContext.Setup(x => x.Credentials.FirstOrDefaultAsync( It.IsAny>>(), It.IsAny())) .ReturnsAsync(expectedCredential); // Act var result = await _service.GetCredentialAsync(credentialName); // Assert Assert.IsNotNull(result); Assert.AreEqual(credentialName, result.Name); _mockContext.Verify(x => x.Credentials.FirstOrDefaultAsync( It.IsAny>>(), It.IsAny()), Times.Once); } [TestMethod] public async Task GetCredentialAsync_WithNullName_ThrowsArgumentNullException() { // Act & Assert await Assert.ThrowsExceptionAsync( () => _service.GetCredentialAsync(null)); } ``` ### Test di Integrazione #### Database Integration Tests ```csharp [TestClass] public class DatabaseIntegrationTests { private TestDatabase _testDb; private IDatabaseManager _databaseManager; [TestInitialize] public async Task Setup() { _testDb = await TestDatabase.CreateAsync(); _databaseManager = new EFCoreDatabaseManager(_testDb.ConnectionString); } [TestCleanup] public async Task Cleanup() { await _testDb.DisposeAsync(); } [TestMethod] public async Task ExecuteQueryAsync_WithValidQuery_ReturnsData() { // Arrange await _testDb.SeedDataAsync(); var query = "SELECT * FROM TestTable WHERE Id = 1"; // Act var result = await _databaseManager.ExecuteQueryAsync(query); // Assert Assert.IsNotNull(result); Assert.IsTrue(result.Any()); } } ``` #### REST API Integration Tests ```csharp [TestClass] public class RestApiIntegrationTests { private TestServer _testServer; private HttpClient _client; [TestInitialize] public void Setup() { var builder = WebApplication.CreateBuilder(); // Configure test server _testServer = new TestServer(builder); _client = _testServer.CreateClient(); } [TestMethod] public async Task GetData_WithValidCredentials_ReturnsSuccess() { // Arrange var apiClient = new GenericRestServiceClient(_client); // Act var result = await apiClient.GetAsync("/api/data"); // Assert Assert.IsNotNull(result); Assert.IsTrue(result.Success); } } ``` ### Test End-to-End #### Playwright UI Tests ```csharp [TestClass] public class UITests { private IPlaywright _playwright; private IBrowser _browser; private IPage _page; [TestInitialize] public async Task Setup() { _playwright = await Playwright.CreateAsync(); _browser = await _playwright.Chromium.LaunchAsync(); _page = await _browser.NewPageAsync(); } [TestMethod] public async Task DataTransfer_CompleteWorkflow_Success() { // Navigate to application await _page.GotoAsync("http://localhost:7550"); // Configure source connection await _page.ClickAsync("#source-database-tab"); await _page.FillAsync("#connection-name", "TestConnection"); // Configure mapping await _page.ClickAsync("#next-step"); // Execute transfer await _page.ClickAsync("#execute-transfer"); // Verify results var successMessage = await _page.WaitForSelectorAsync(".success-message"); Assert.IsNotNull(successMessage); } } ``` ### Test Configuration #### appsettings.Test.json ```json { "ConnectionStrings": { "DefaultConnection": "Data Source=:memory:", "TestDatabase": "Server=(localdb)\\mssqllocaldb;Database=DataCouplerTest;Trusted_Connection=true;" }, "Logging": { "LogLevel": { "Default": "Debug", "Microsoft": "Warning" } }, "TestSettings": { "UseInMemoryDatabase": true, "MockExternalServices": true } } ``` ### Test Execution Scripts #### PowerShell Test Runner ```powershell # run-tests.ps1 param( [string]$Configuration = "Debug", [string]$Filter = "", [switch]$Coverage ) Write-Host "Running tests..." -ForegroundColor Green if ($Coverage) { dotnet test Data_Coupler.sln ` --configuration $Configuration ` --collect:"XPlat Code Coverage" ` --results-directory TestResults ` --filter $Filter # Generate coverage report reportgenerator ` -reports:"TestResults/*/coverage.cobertura.xml" ` -targetdir:"TestResults/CoverageReport" ` -reporttypes:Html } else { dotnet test Data_Coupler.sln ` --configuration $Configuration ` --filter $Filter } ``` ## 🔒 Security Considerations ### Gestione Credenziali #### Crittografia dei Dati Sensibili ```csharp public class EncryptionService : IEncryptionService { private readonly IDataProtector _protector; public EncryptionService(IDataProtectionProvider provider) { _protector = provider.CreateProtector("DataCoupler.Credentials.v1"); } public string Encrypt(string plaintext) { if (string.IsNullOrEmpty(plaintext)) return plaintext; return _protector.Protect(plaintext); } public string Decrypt(string ciphertext) { if (string.IsNullOrEmpty(ciphertext)) return ciphertext; try { return _protector.Unprotect(ciphertext); } catch (CryptographicException) { // Handle decryption failure throw new SecurityException("Failed to decrypt data"); } } } ``` #### Configurazione Data Protection ```csharp // Program.cs builder.Services.AddDataProtection() .PersistKeysToFileSystem(new DirectoryInfo(keyPath)) .SetApplicationName("DataCoupler") .SetDefaultKeyLifetime(TimeSpan.FromDays(90)); ``` ### Validazione Input #### SQL Injection Prevention ```csharp // ✅ Parametrized Queries public async Task> GetDataAsync(string tableName, int id) { // Validate table name against whitelist if (!IsValidTableName(tableName)) throw new ArgumentException("Invalid table name"); var query = $"SELECT * FROM [{tableName}] WHERE Id = @id"; var parameters = new { id }; return await _connection.QueryAsync(query, parameters); } // ❌ Mai utilizzare string concatenation public async Task> GetDataUnsafe(string tableName, int id) { var query = $"SELECT * FROM {tableName} WHERE Id = {id}"; // VULNERABLE return await _connection.QueryAsync(query); } ``` #### Input Sanitization ```csharp public class InputValidator { private static readonly Regex AllowedTableName = new(@"^[a-zA-Z][a-zA-Z0-9_]*$"); private static readonly Regex AllowedFieldName = new(@"^[a-zA-Z][a-zA-Z0-9_]*$"); public static bool IsValidTableName(string tableName) { return !string.IsNullOrWhiteSpace(tableName) && tableName.Length <= 128 && AllowedTableName.IsMatch(tableName); } public static bool IsValidFieldName(string fieldName) { return !string.IsNullOrWhiteSpace(fieldName) && fieldName.Length <= 128 && AllowedFieldName.IsMatch(fieldName); } public static string SanitizeStringInput(string input, int maxLength = 255) { if (string.IsNullOrEmpty(input)) return string.Empty; // Remove potential XSS characters input = input.Replace("<", "<") .Replace(">", ">") .Replace("\"", """) .Replace("'", "'") .Replace("/", "/"); return input.Length > maxLength ? input.Substring(0, maxLength) : input; } } ``` ### Autenticazione e Autorizzazione #### Connection Security ```csharp public class SecureConnectionFactory : IDataConnectionFactory { public async Task CreateDatabaseManagerAsync(string credentialName) { var credential = await _credentialService.GetCredentialAsync(credentialName); if (credential == null) throw new UnauthorizedAccessException("Credential not found"); // Validate credential permissions if (!await ValidateCredentialPermissions(credential)) throw new UnauthorizedAccessException("Insufficient permissions"); // Create connection with timeout var connectionString = BuildSecureConnectionString(credential); return new DatabaseManager(connectionString); } private string BuildSecureConnectionString(CredentialEntity credential) { var builder = new SqlConnectionStringBuilder { DataSource = credential.Host, InitialCatalog = credential.DatabaseName, UserID = credential.Username, Password = _encryptionService.Decrypt(credential.EncryptedPassword), // Security settings Encrypt = true, TrustServerCertificate = false, ConnectTimeout = 30, CommandTimeout = 300 }; return builder.ConnectionString; } } ``` ### HTTPS e Comunicazioni Sicure #### HTTPS Configuration ```csharp // Program.cs if (!app.Environment.IsDevelopment()) { app.UseHsts(); app.UseHttpsRedirection(); } // Force HTTPS in production app.Use(async (context, next) => { if (!context.Request.IsHttps && !app.Environment.IsDevelopment()) { var httpsUrl = $"https://{context.Request.Host}{context.Request.Path}{context.Request.QueryString}"; context.Response.Redirect(httpsUrl, permanent: true); return; } await next(); }); ``` #### HTTP Client Security ```csharp public class SecureHttpClientFactory { public HttpClient CreateSecureClient(RestCredentialSettings settings) { var handler = new HttpClientHandler(); if (!settings.IgnoreSslErrors) { handler.ServerCertificateCustomValidationCallback = (sender, cert, chain, sslPolicyErrors) => { // Validate certificate chain return sslPolicyErrors == SslPolicyErrors.None; }; } var client = new HttpClient(handler) { Timeout = TimeSpan.FromSeconds(settings.TimeoutSeconds) }; // Add security headers client.DefaultRequestHeaders.Add("User-Agent", "DataCoupler/1.0"); client.DefaultRequestHeaders.Add("X-Requested-With", "XMLHttpRequest"); return client; } } ``` ### Logging e Audit #### Secure Logging ```csharp public class SecurityAuditLogger { private readonly ILogger _logger; public void LogCredentialAccess(string credentialName, string operation, string userId) { _logger.LogInformation( "Credential Access: {Operation} on {CredentialName} by {UserId} at {Timestamp}", operation, credentialName, userId, DateTime.UtcNow); } public void LogDataTransfer(string sourceType, string destinationType, int recordCount, string userId) { _logger.LogInformation( "Data Transfer: {RecordCount} records from {SourceType} to {DestinationType} by {UserId} at {Timestamp}", recordCount, sourceType, destinationType, userId, DateTime.UtcNow); } public void LogSecurityEvent(string eventType, string details, string userId) { _logger.LogWarning( "Security Event: {EventType} - {Details} by {UserId} at {Timestamp}", eventType, details, userId, DateTime.UtcNow); } } ``` ### Error Handling Sicuro #### Information Disclosure Prevention ```csharp public class SecureErrorHandler { private readonly ILogger _logger; public ErrorResponse HandleError(Exception ex, bool isDevelopment) { // Log full error details _logger.LogError(ex, "Error occurred: {Message}", ex.Message); // Return sanitized error to client if (isDevelopment) { return new ErrorResponse { Message = ex.Message, StackTrace = ex.StackTrace, Type = ex.GetType().Name }; } // Production: return generic message return new ErrorResponse { Message = "An error occurred while processing your request", ErrorId = Guid.NewGuid().ToString() // For correlation }; } } ``` ### Database Security #### Connection Security ```csharp public class DatabaseSecuritySettings { public static SqlConnectionStringBuilder CreateSecureConnectionString( string server, string database, string username, string password) { return new SqlConnectionStringBuilder { DataSource = server, InitialCatalog = database, UserID = username, Password = password, // Security settings Encrypt = true, TrustServerCertificate = false, IntegratedSecurity = false, MultipleActiveResultSets = false, // Timeout settings ConnectTimeout = 30, CommandTimeout = 300, // Connection pooling Pooling = true, MinPoolSize = 0, MaxPoolSize = 100 }; } } ``` ## Sistema di Backup e Impostazioni ### Architettura del Sistema di Backup Il sistema implementa un approccio modulare per il backup e ripristino dei dati critici: #### Componenti Principali **BackupService (`Data_Coupler.Services.BackupService`)** - **Interfaccia**: `IBackupService` con metodi asincroni per export/import - **Serializzazione**: Utilizza `System.Text.Json` per formato JSON leggibile - **Sicurezza**: Esclude automaticamente password e chiavi API dai backup - **Transazioni**: Operazioni di import wrapped in transazioni per integrità dati **Modelli di Backup (`Data_Coupler.Models.BackupModels`)** ```csharp // Struttura principale del backup public class SystemBackupData { public BackupMetadata Metadata { get; set; } public List Profiles { get; set; } public List Credentials { get; set; } public List KeyAssociations { get; set; } public List Schedules { get; set; } } // Metadati per versioning e validazione public class BackupMetadata { public string Version { get; set; } = "1.0"; public DateTime CreatedAt { get; set; } public string CreatedBy { get; set; } public List IncludedComponents { get; set; } public string Description { get; set; } } ``` #### Funzionalità di Export **Configurazione Flessibile** ```csharp var options = new BackupOptions { IncludeProfiles = true, IncludeCredentials = true, IncludeKeyAssociations = true, IncludeSchedules = true, ActiveRecordsOnly = false, Description = "Backup completo sistema" }; var result = await backupService.ExportBackupAsync(options); ``` **Componenti Supportati**: - **Profili Data Coupler**: Configurazioni complete di trasferimento dati - **Credenziali**: Informazioni di connessione (senza dati sensibili) - **Associazioni Chiavi**: Mapping tra chiavi sorgente e destinazione - **Schedulazioni**: Configurazioni di esecuzione automatica #### Funzionalità di Import **Validazione Rigorosa** - Controllo formato e versione del file backup - Validazione integrità dati JSON - Verifica compatibilità componenti **Modalità di Ripristino** - **Overwrite**: Sostituisce dati esistenti - **Merge**: Integra con dati esistenti - **Preview**: Mostra cosa verrà importato senza eseguire ### Interfaccia Settings **Struttura a Tab Organizzata** **Tab Backup** - Export selettivo per componente - Import con validazione file - Storico backup con timestamp - Anteprima contenuto backup **Tab Sistema** - Statistiche database in tempo reale - Configurazioni performance (batch size, timeout) - Informazioni sistema (versione, framework, OS) - Monitoraggio utilizzo memoria **Tab Sicurezza** - Gestione crittografia credenziali - Configurazioni audit logging - Impostazioni connessioni sicure (HTTPS, SSL) - Backup automatici crittografati **Tab Manutenzione** - Ottimizzazione database automatica - Pulizia file temporanei e log - Monitoraggio performance (CPU, memoria, connessioni) - Schedulazione manutenzione automatica - Storico operazioni manutenzione #### Implementazione Sicurezza **Esclusione Dati Sensibili** ```csharp public class CredentialBackup { public int Id { get; set; } public string Name { get; set; } public string ConnectionType { get; set; } public string Server { get; set; } public int Port { get; set; } public string Database { get; set; } public string Username { get; set; } // Password e ApiKey intenzionalmente esclusi public bool IsActive { get; set; } public DateTime CreatedAt { get; set; } public DateTime UpdatedAt { get; set; } } ``` **Logging Operazioni** ```csharp _logger.LogInformation("Backup export started by {User} with options: {Options}", Environment.UserName, JsonSerializer.Serialize(options)); _logger.LogInformation("Backup import completed: {ProfilesCount} profiles, {CredentialsCount} credentials restored", profilesRestored, credentialsRestored); ``` #### Registrazione Servizi **Dependency Injection Setup** ```csharp // Program.cs builder.Services.AddScoped(); ``` **Routing e Navigazione** ```csharp // NavMenu.razor Impostazioni ``` #### Best Practices Implementate **Error Handling Robusto** - Try-catch con logging specifico per ogni operazione - Rollback automatico in caso di errore durante import - Messaggi di errore user-friendly con dettagli tecnici nei log **UX/UI Responsiva** - Indicatori di progresso per operazioni lunghe - Toast notifications per feedback immediato - Validazione client-side per upload file - Design responsive con Bootstrap 5 **Performance Ottimizzata** - Operazioni asincrone per non bloccare UI - Batch processing per grandi dataset - Lazy loading per componenti tab non attivi --- **Versione**: 1.1 **Ultimo Aggiornamento**: 20 Febbraio 2026 **Framework**: .NET 9.0 **Sviluppatore**: Alessio Dalsanto