Files
Data-Coupler/CredentialManager
Alessio e70abcdcb1 [Feature] Supporto Visual FoxPro / dBase come sorgente dati (managed, sola lettura)
Aggiunge un connettore completamente managed per database Visual FoxPro e dBase,
utilizzabile come sorgente dati in tutti i flussi dell'applicazione (discovery manuale,
esecuzione schedulata, hash/change-detection, backup/restore).

## Motivazione

Il provider OLE DB ufficiale (VFPOLEDB.1) è solo a 32-bit, deprecato e richiede
installazione separata. Il connettore managed usa DbfDataReader 1.0.0 (MIT), legge
direttamente i file .dbf/.fpt/.dbc a 64-bit senza dipendenze esterne e con streaming
efficiente (tabelle da centinaia di MB con uso di memoria contenuto).

## Nuovi file

- DataConnection/DB/FoxPro/FoxProConnectionInfo.cs
  Classe sealed con le informazioni di connessione risolte (cartella, percorso .dbc,
  encoding, flag IncludeDeleted). Proprietà di convenienza IsContainer e DisplayName.

- DataConnection/DB/FoxPro/FoxProReader.cs
  Helper statico che registra CodePagesEncodingProvider, risolve la connection string
  (path diretto, stile OLE DB con Provider=vfpoledb, chiave=valore), verifica l'accesso
  al filesystem, legge il catalogo .dbc (il file .dbc è esso stesso un DBF; filtro su
  OBJECTTYPE='Table' + cross-check esistenza fisica .dbf), enumera le tabelle free,
  mappa i tipi VFP (Character, Memo, Numeric, Float, Double, Integer, Currency, Date,
  DateTime, Logical, General) in descrizioni leggibili, esegue streaming dei record via
  DbfDataReader, auto-rileva l'encoding dall'header DBF (language driver byte) con
  fallback a code page 1252, e analizza query SELECT [TOP n] * FROM tabella.

- DataConnection/DB/FoxProDatabaseManager.cs
  Implementa IDatabaseManager. Lettura: TestConnectionAsync, GetTableNamesAsync,
  GetTableSchemaAsync, GetDatabaseSchemaAsync, GetAllRecordsAsync, ExecuteRawQueryAsync,
  GetPrimaryKeyFieldAsync (ritorna null, selezione manuale chiave), GetAvailableDatabasesAsync,
  ChangeDatabaseAsync (no-op per sorgenti file-based).
  Scrittura: tutti i metodi di modifica (Insert/Update/Delete/Upsert/BulkInsert/
  ExecuteCommand/ExecuteNonQuery) lanciano NotSupportedException con messaggio esplicito.

- DataConnection/DB/EF/SchemaProviders/FoxProSchemaProvider.cs
  Implementa IDatabaseSchemaProvider delegando a FoxProReader. Gestione errori per tabella
  in GetDatabaseSchemaAsync (una tabella non apribile non blocca la discovery).

- FOXPRO_CONNECTION.md
  Guida utente: passo-passo per creare la credenziale, formato connection string,
  tabella tipi VFP supportati, note su sola lettura e limitazioni query, tabella
  risoluzione problemi (caratteri accentati, tabelle mancanti, ecc.).

## File modificati

- DataConnection/DataConnection.csproj
  Aggiunto DbfDataReader 1.0.0 (porta transitivamente System.Text.Encoding.CodePages
  >= 10.0.3; rimossa dipendenza esplicita alla versione 9.0.3 che causava NU1605).

- DataConnection/DB/Enums/DatabaseType.cs
  Aggiunto valore Foxpro in fondo all'enum (preserva valori già persistiti).

- CredentialManager/Models/CredentialModels.cs
  Aggiunto DatabaseType.Foxpro all'enum e metodo BuildFoxproConnectionString che genera
  "Data Source=percorso[;CodePage=n][;IncludeDeleted=true]".

- DataConnection/CredentialManagement/Models/CredentialExtensions.cs
  Mappatura bidirezionale Foxpro tra enum CredentialManager e DataConnection.

- DataConnection/CredentialManagement/Services/DataConnectionCredentialService.cs
  Aggiunto TestFoxproConnection: verifica percorso accessibile, legge nomi tabelle,
  restituisce messaggio con conteggio tabelle, encoding rilevato e nota sola lettura.

- DataConnection/DB/EF/DatabaseSchemaProviderFactory.cs
  Caso Foxpro => new FoxProSchemaProvider().

- Data_Coupler/Services/DataConnectionFactory.cs
  Branch Foxpro => new FoxProDatabaseManager(connectionString).

- Data_Coupler/Extensions/DataCoupler/DatabaseMethod.cs
  Aggiunto IsFoxproConnection() e caso Foxpro in CreateLimitedQuery
  (SELECT TOP n * FROM (query) AS subquery).

- CredentialManager/Integration/DataConnectionHelper.cs
  ValidateDatabaseCredential: introdotto flag isFileBased (Sqlite || Foxpro) per
  esonerare host/utente/password/porta; messaggio di errore specifico per il campo
  percorso FoxPro.

- Data_Coupler/Pages/CredentialManagement.razor
  Aggiunta opzione "Visual FoxPro (.dbc / .dbf)" nel selettore tipo database.
  Sezione di configurazione dedicata: banner sola lettura, campo percorso con esempi
  (.dbc e cartella), dropdown code page (Auto/1252/1250/1251/850/437/65001), checkbox
  record cancellati, pannello tipi supportati, anteprima connection string.
  Fix validazione form test-connessione: branch Foxpro che verifica solo DatabaseName
  (non Host/Username/Password), eliminando il falso errore "Il campo Host è obbligatorio".

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-06-06 19:34:21 +02:00
..
2026-05-28 11:29:10 +02:00

CredentialManager

Un sistema sicuro per la gestione delle credenziali di database e API REST utilizzando Entity Framework Core e SQLite, specificamente progettato per integrarsi con il progetto DataConnection.

Caratteristiche

  • Memorizzazione sicura delle credenziali con crittografia cross-platform
  • Supporto completo per tutti i database del progetto DataConnection:
    • SQL Server, MySQL, PostgreSQL, Oracle, SQLite, DB2, SAP HANA
  • Supporto per credenziali API REST (API Key, Basic Auth, Bearer Token)
  • Generazione automatica di connection string per tutti i tipi di database
  • Database SQLite con Entity Framework Core
  • Creazione automatica del database se non esiste
  • Logging integrato
  • Cross-platform (Windows, Linux, macOS)
  • Dependency Injection pronto
  • Validazione delle credenziali
  • Metodi di utilità per integrazione con DataConnection

Tipi di Database Supportati

Il CredentialManager supporta tutti i tipi di database gestiti dal progetto DataConnection:

  • SQL Server (porta predefinita: 1433)
  • MySQL (porta predefinita: 3306)
  • PostgreSQL (porta predefinita: 5432)
  • Oracle (porta predefinita: 1521)
  • SQLite (database locale)
  • DB2 (porta predefinita: 50000)
  • SAP HANA (porta predefinita: 30015)

Installazione

  1. Aggiungi il progetto CredentialManager alla tua soluzione
  2. Referenzia il progetto nel tuo progetto principale

Utilizzo Base

Utilizzo Standalone

using CredentialManager;
using CredentialManager.Models;
using CredentialManager.Integration;

// Crea un'istanza del servizio
var credentialService = await CredentialManagerFactory.CreateAsync();

// Salva una credenziale database con validazione
var dbCredential = new DatabaseCredential
{
    Name = "Database Produzione",
    DatabaseType = DatabaseType.SqlServer,
    Host = "sql-server.company.com",
    Port = 1433, // Si può omettere, verrà usata la porta predefinita
    DatabaseName = "ProductionDB",
    Username = "dbuser",
    Password = "secretpassword",
    CommandTimeout = 30,
    IgnoreSslErrors = false,
    AdditionalParameters = new Dictionary<string, string>
    {
        ["Encrypt"] = "True",
        ["TrustServerCertificate"] = "True"
    }
};

// Salva con validazione automatica
var id = await credentialService.SaveValidatedDatabaseCredentialAsync(dbCredential);

// Genera automaticamente la connection string
var connectionString = dbCredential.ToConnectionString();
Console.WriteLine($"Connection String: {connectionString}");

Integrazione con DataConnection

using CredentialManager.Integration;

// Ottieni credenziale validata
var credential = await credentialService.GetValidatedDatabaseCredentialAsync("Database Produzione");

// Converti per l'uso con DbManagerOptions
var dbOptions = credential.ToDbManagerOptions();

// Oppure ottieni direttamente la connection string
var connectionString = credential.ToConnectionString();

Utilizzo con Dependency Injection

using CredentialManager;
using Microsoft.Extensions.DependencyInjection;

var services = new ServiceCollection();

// Registra i servizi
services.AddLogging(builder => builder.AddConsole());
services.AddCredentialManager("path/to/credentials.db"); // Percorso opzionale

var serviceProvider = services.BuildServiceProvider();

// Inizializza il database
await serviceProvider.InitializeCredentialManagerAsync();

// Usa il servizio
var credentialService = serviceProvider.GetRequiredService<ICredentialService>();

Tipi di Credenziali Supportate

Credenziali Database

var dbCredential = new DatabaseCredential
{
    Name = "Nome Univoco",
    DatabaseType = DatabaseType.SqlServer, // Tutti i tipi supportati da DataConnection
    Host = "server.company.com",
    Port = 1433, // Opzionale, usa porta predefinita se omesso
    DatabaseName = "MyDatabase",
    Username = "dbuser",
    Password = "password", // Verrà crittata automaticamente
    ConnectionString = "Server=...", // Opzionale, stringa di connessione completa
    CommandTimeout = 30, // Timeout comandi in secondi
    IgnoreSslErrors = false, // Per connessioni SSL non verificate
    AdditionalParameters = new Dictionary<string, string>
    {
        ["Encrypt"] = "True",
        ["TrustServerCertificate"] = "True"
    }
};

await credentialService.SaveValidatedDatabaseCredentialAsync(dbCredential);

Credenziali REST API

var apiCredential = new RestApiCredential
{
    Name = "API Esterna",
    BaseUrl = "https://api.external-service.com",
    ApiKey = "your-api-key", // Verrà crittata automaticamente
    Username = "apiuser", // Per Basic Auth
    Password = "apipass", // Per Basic Auth, verrà crittata
    AuthToken = "bearer-token", // Per Bearer Auth, verrà crittata
    TimeoutSeconds = 60, // Timeout richieste
    IgnoreSslErrors = false, // Per API con certificati non verificati
    Headers = new Dictionary<string, string>
    {
        ["Content-Type"] = "application/json",
        ["Accept"] = "application/json"
    },
    AdditionalParameters = new Dictionary<string, string>
    {
        ["CustomParam"] = "value"
    }
};

await credentialService.SaveValidatedRestApiCredentialAsync(apiCredential);

Generazione Connection String

Il CredentialManager può generare automaticamente connection string per tutti i tipi di database:

// Genera connection string automaticamente
var connectionString = credential.ToConnectionString();

// Connection string specifiche per tipo di database:
// SQL Server: "Server=host,port;Database=dbname;User Id=user;Password=pass;Connection Timeout=30"
// MySQL: "Server=host;Port=port;Database=dbname;Uid=user;Pwd=pass;Connection Timeout=30"
// PostgreSQL: "Host=host;Port=port;Database=dbname;Username=user;Password=pass;Timeout=30"
// Oracle: "Data Source=host:port/dbname;User Id=user;Password=pass;Connection Timeout=30"
// SQLite: "Data Source=dbname"
// DB2: "Server=host:port;Database=dbname;UID=user;PWD=pass;Connection Timeout=30"
// SAP HANA: "Server=host:port;DatabaseName=dbname;UserID=user;Password=pass;Connection Timeout=30"

Validazione delle Credenziali

using CredentialManager.Integration;

// Validazione manuale
var errors = DataConnectionHelper.ValidateDatabaseCredential(credential);
if (errors.Any())
{
    Console.WriteLine($"Errori: {string.Join(", ", errors)}");
}

// Validazione automatica durante il salvataggio
try
{
    await credentialService.SaveValidatedDatabaseCredentialAsync(credential);
}
catch (ArgumentException ex)
{
    Console.WriteLine($"Credenziale non valida: {ex.Message}");
}

Operazioni Principali

Salvare Credenziali

// Con validazione automatica (raccomandato)
var dbId = await credentialService.SaveValidatedDatabaseCredentialAsync(databaseCredential);
var apiId = await credentialService.SaveValidatedRestApiCredentialAsync(apiCredential);

// Senza validazione
var dbId = await credentialService.SaveDatabaseCredentialAsync(databaseCredential);
var apiId = await credentialService.SaveRestApiCredentialAsync(apiCredential);

Recuperare Credenziali

// Con validazione automatica (raccomandato)
var dbCred = await credentialService.GetValidatedDatabaseCredentialAsync("Database Produzione");
var apiCred = await credentialService.GetValidatedRestApiCredentialAsync("API Esterna");

// Senza validazione
var dbCred = await credentialService.GetDatabaseCredentialAsync("Database Produzione");
var apiCred = await credentialService.GetRestApiCredentialAsync("API Esterna");

// Per ID
var dbCred = await credentialService.GetDatabaseCredentialAsync(1);
var apiCred = await credentialService.GetRestApiCredentialAsync(2);

// Tutte le credenziali di un tipo
var allDbCreds = await credentialService.GetAllDatabaseCredentialsAsync();
var allApiCreds = await credentialService.GetAllRestApiCredentialsAsync();

Eliminare Credenziali

// Per nome
await credentialService.DeleteCredentialAsync("Nome Credenziale");

// Per ID
await credentialService.DeleteCredentialAsync(1);

Ottenere Lista Nomi

// Tutti i nomi
var allNames = await credentialService.GetCredentialNamesAsync();

// Solo nomi di un tipo specifico
var dbNames = await credentialService.GetCredentialNamesAsync(CredentialType.Database);
var apiNames = await credentialService.GetCredentialNamesAsync(CredentialType.RestApi);

Configurazione

Percorso Database Personalizzato

// Specificare un percorso personalizzato per il database
services.AddCredentialManager("/custom/path/credentials.db");

// O per utilizzo standalone
var service = await CredentialManagerFactory.CreateAsync("/custom/path/credentials.db");

Logging Personalizzato

var loggerFactory = LoggerFactory.Create(builder =>
    builder.AddConsole().SetMinimumLevel(LogLevel.Debug));

var service = await CredentialManagerFactory.CreateAsync(
    databasePath: "credentials.db",
    loggerFactory: loggerFactory);

Sicurezza

  • Le password, API key e token sono crittati utilizzando:
    • Windows: ProtectedData con entropia personalizzata
    • Altri OS: AES-256 con chiave derivata
  • I dati sono memorizzati localmente in un database SQLite
  • Le credenziali eliminate sono contrassegnate come inattive (soft delete)
  • Validazione automatica dei dati di input

Struttura Database

Il database SQLite viene creato automaticamente con la seguente struttura:

CREATE TABLE Credentials (
    Id INTEGER PRIMARY KEY AUTOINCREMENT,
    Name NVARCHAR(100) NOT NULL UNIQUE,
    Type NVARCHAR(50) NOT NULL,
    DatabaseType NVARCHAR(50),
    Host NVARCHAR(200),
    Port INTEGER,
    DatabaseName NVARCHAR(100),
    Username NVARCHAR(100),
    EncryptedPassword TEXT,
    ConnectionString NVARCHAR(500),
    EncryptedApiKey NVARCHAR(500),
    EncryptedAuthToken NVARCHAR(500),
    CommandTimeout INTEGER DEFAULT 30,
    TimeoutSeconds INTEGER DEFAULT 100,
    IgnoreSslErrors INTEGER DEFAULT 0,
    Headers NVARCHAR(2000),
    AdditionalParameters NVARCHAR(2000),
    CreatedAt DATETIME NOT NULL,
    UpdatedAt DATETIME,
    CreatedBy NVARCHAR(100),
    IsActive INTEGER NOT NULL DEFAULT 1
);

Porte Predefinite

Il sistema assegna automaticamente le porte predefinite se non specificate:

  • SQL Server: 1433
  • MySQL: 3306
  • PostgreSQL: 5432
  • Oracle: 1521
  • DB2: 50000
  • SAP HANA: 30015
  • SQLite: N/A (database locale)

Requisiti

  • .NET 9.0
  • Entity Framework Core 9.0
  • SQLite

Note

  • Il database viene creato nella cartella %APPDATA%/CredentialManager/ se non specificato un percorso personalizzato
  • Tutte le operazioni sono asincrone
  • Il servizio è thread-safe
  • Le migrazioni del database vengono applicate automaticamente all'avvio
  • Piena compatibilità con il progetto DataConnection