feat: Integrazione completa gestione credenziali per database e REST API con supporto SAP B1 e Salesforce
NUOVE FUNZIONALITÀ: - Aggiunto modulo CredentialManager per gestione centralizzata credenziali - Implementata UI Blazor per gestione credenziali (CredentialManagement.razor) - Supporto completo per credenziali database (SQL Server, MySQL, PostgreSQL, Oracle, SQLite, DB2, SAP HANA) - Gestione unificata REST API con supporto specifico per SAP B1 Service Layer e Salesforce - Test reali di connessione per database, SAP B1 e Salesforce OAuth2 - Selezione dinamica tipo servizio REST (Generico, SAP B1, Salesforce) con campi specifici - Persistenza sicura di credenziali con crittografia password e campi sensibili COMPONENTI AGGIUNTI: - CredentialManager/Models/: CredentialEntity, CredentialModels (DatabaseCredential, RestApiCredential, SapB1ServiceLayerCredential, SalesforceCredential) - CredentialManager/Services/: CredentialService, EncryptionService, DatabaseInitializer - CredentialManager/Data/: CredentialDbContext con Entity Framework - DataConnection/CredentialManagement/: Interfacce e servizi di integrazione - Data_Coupler/Pages/CredentialManagement.razor: UI completa per gestione credenziali MIGLIORAMENTI UI: - Form dinamica per REST API con campi specifici per tipo servizio - Validazione campi obbligatori per Salesforce (ClientId, ClientSecret, SecurityToken) - Test connessione in tempo reale dalla modale di inserimento/modifica - Rimozione sezioni separate per SAP B1 e Salesforce (ora unificate in REST API) - Gestione stato loading durante operazioni async PERSISTENZA AVANZATA: - Campo RestServiceType aggiunto a CredentialEntity con migrazione automatica - Serializzazione campi specifici Salesforce/SAP B1 in AdditionalParameters JSON - Mapping bidirezionale tra entità database e modelli business - Gestione nullability e conversioni tipo sicure SICUREZZA: - Crittografia AES-256 per password e token sensibili - Gestione sicura ConnectionString database - Validazione input e sanitizzazione dati TESTING E CONNETTIVITÀ: - Test autenticazione reale SAP B1 Service Layer - Test OAuth2 Salesforce con supporto Connected App - Test connettività database multi-provider - Logging dettagliato per debugging e monitoraggio CONFIGURAZIONE: - Dependency injection per tutti i servizi - Configurazione Entity Framework con SQLite - Tasks VS Code per build e run - Gestione connection string centralizzata CORREZIONI: - Risolti errori nullability in CredentialService - Aggiunto using Microsoft.JSInterop per IJSRuntime - Fix compilazione e warning Files modificati: 35+ file tra nuovi e aggiornati
This commit is contained in:
@@ -0,0 +1,922 @@
|
||||
using Microsoft.EntityFrameworkCore;
|
||||
using Microsoft.Extensions.Logging;
|
||||
using CredentialManager.Data;
|
||||
using CredentialManager.Models;
|
||||
using System.Text.Json;
|
||||
|
||||
namespace CredentialManager.Services;
|
||||
|
||||
/// <summary>
|
||||
/// Interfaccia per il servizio di gestione credenziali
|
||||
/// </summary>
|
||||
public interface ICredentialService
|
||||
{
|
||||
// Database Credentials
|
||||
Task<int> SaveDatabaseCredentialAsync(DatabaseCredential credential);
|
||||
Task<DatabaseCredential?> GetDatabaseCredentialAsync(string name);
|
||||
Task<DatabaseCredential?> GetDatabaseCredentialAsync(int id);
|
||||
Task<List<DatabaseCredential>> GetAllDatabaseCredentialsAsync();
|
||||
|
||||
// REST API Credentials
|
||||
Task<int> SaveRestApiCredentialAsync(RestApiCredential credential);
|
||||
Task<RestApiCredential?> GetRestApiCredentialAsync(string name);
|
||||
Task<RestApiCredential?> GetRestApiCredentialAsync(int id);
|
||||
Task<List<RestApiCredential>> GetAllRestApiCredentialsAsync();
|
||||
|
||||
// SAP B1 Service Layer Credentials
|
||||
Task<int> SaveSapB1CredentialAsync(SapB1ServiceLayerCredential credential);
|
||||
Task<SapB1ServiceLayerCredential?> GetSapB1CredentialAsync(string name);
|
||||
Task<SapB1ServiceLayerCredential?> GetSapB1CredentialAsync(int id);
|
||||
Task<List<SapB1ServiceLayerCredential>> GetAllSapB1CredentialsAsync();
|
||||
|
||||
// Salesforce Credentials
|
||||
Task<int> SaveSalesforceCredentialAsync(SalesforceCredential credential);
|
||||
Task<SalesforceCredential?> GetSalesforceCredentialAsync(string name);
|
||||
Task<SalesforceCredential?> GetSalesforceCredentialAsync(int id);
|
||||
Task<List<SalesforceCredential>> GetAllSalesforceCredentialsAsync();
|
||||
|
||||
// Generic operations
|
||||
Task<bool> DeleteCredentialAsync(int id);
|
||||
Task<bool> DeleteCredentialAsync(string name);
|
||||
Task<List<string>> GetCredentialNamesAsync(CredentialType? type = null);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Servizio per la gestione delle credenziali
|
||||
/// </summary>
|
||||
public class CredentialService : ICredentialService
|
||||
{
|
||||
private readonly CredentialDbContext _context;
|
||||
private readonly IEncryptionService _encryptionService;
|
||||
private readonly ILogger<CredentialService> _logger;
|
||||
|
||||
public CredentialService(
|
||||
CredentialDbContext context,
|
||||
IEncryptionService encryptionService,
|
||||
ILogger<CredentialService> logger)
|
||||
{
|
||||
_context = context;
|
||||
_encryptionService = encryptionService;
|
||||
_logger = logger;
|
||||
}
|
||||
|
||||
#region Database Credentials
|
||||
|
||||
public async Task<int> 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,
|
||||
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.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<DatabaseCredential?> 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<DatabaseCredential?> 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<List<DatabaseCredential>> 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<int> SaveRestApiCredentialAsync(RestApiCredential credential)
|
||||
{
|
||||
try
|
||||
{
|
||||
// Prepara i parametri aggiuntivi per includere i campi specifici del servizio
|
||||
var additionalParams = new Dictionary<string, string>();
|
||||
|
||||
// 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<RestApiCredential?> 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<RestApiCredential?> 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<List<RestApiCredential>> 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<int> 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<string, string>
|
||||
{
|
||||
["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<SapB1ServiceLayerCredential?> 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<SapB1ServiceLayerCredential?> 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<List<SapB1ServiceLayerCredential>> 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<int> 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<string, string>
|
||||
{
|
||||
["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<SalesforceCredential?> 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<SalesforceCredential?> 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<List<SalesforceCredential>> 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<bool> 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<bool> 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<List<string>> 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<DatabaseType>(entity.DatabaseType!),
|
||||
Host = entity.Host ?? string.Empty,
|
||||
Port = entity.Port ?? 0,
|
||||
DatabaseName = entity.DatabaseName ?? string.Empty,
|
||||
Username = entity.Username ?? string.Empty,
|
||||
Password = _encryptionService.Decrypt(entity.EncryptedPassword!),
|
||||
ConnectionString = entity.ConnectionString,
|
||||
CommandTimeout = entity.CommandTimeout,
|
||||
IgnoreSslErrors = entity.IgnoreSslErrors
|
||||
};
|
||||
|
||||
if (!string.IsNullOrEmpty(entity.AdditionalParameters))
|
||||
{
|
||||
try
|
||||
{
|
||||
credential.AdditionalParameters = JsonSerializer.Deserialize<Dictionary<string, string>>(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<RestServiceType>(entity.RestServiceType, out var serviceType)
|
||||
? serviceType
|
||||
: RestServiceType.Generic,
|
||||
BaseUrl = entity.Host ?? string.Empty,
|
||||
Username = entity.Username,
|
||||
Password = !string.IsNullOrEmpty(entity.EncryptedPassword)
|
||||
? _encryptionService.Decrypt(entity.EncryptedPassword)
|
||||
: null,
|
||||
ApiKey = !string.IsNullOrEmpty(entity.EncryptedApiKey)
|
||||
? _encryptionService.Decrypt(entity.EncryptedApiKey)
|
||||
: null,
|
||||
AuthToken = !string.IsNullOrEmpty(entity.EncryptedAuthToken)
|
||||
? _encryptionService.Decrypt(entity.EncryptedAuthToken)
|
||||
: null,
|
||||
TimeoutSeconds = entity.TimeoutSeconds,
|
||||
IgnoreSslErrors = entity.IgnoreSslErrors
|
||||
};
|
||||
|
||||
if (!string.IsNullOrEmpty(entity.Headers))
|
||||
{
|
||||
try
|
||||
{
|
||||
credential.Headers = JsonSerializer.Deserialize<Dictionary<string, string>>(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<Dictionary<string, string>>(entity.AdditionalParameters);
|
||||
if (additionalParams != null)
|
||||
{
|
||||
// Imposta i parametri aggiuntivi generici
|
||||
credential.AdditionalParameters = new Dictionary<string, string>();
|
||||
|
||||
// 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<string>
|
||||
{
|
||||
"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<Dictionary<string, string>>(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<Dictionary<string, string>>(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<Dictionary<string, string>>(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
|
||||
}
|
||||
@@ -0,0 +1,167 @@
|
||||
using Microsoft.EntityFrameworkCore;
|
||||
using Microsoft.Extensions.Logging;
|
||||
using CredentialManager.Data;
|
||||
|
||||
namespace CredentialManager.Services;
|
||||
|
||||
/// <summary>
|
||||
/// Interfaccia per l'inizializzazione del database
|
||||
/// </summary>
|
||||
public interface IDatabaseInitializer
|
||||
{
|
||||
Task InitializeAsync();
|
||||
Task<bool> DatabaseExistsAsync();
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Servizio per l'inizializzazione del database SQLite
|
||||
/// </summary>
|
||||
public class DatabaseInitializer : IDatabaseInitializer
|
||||
{
|
||||
private readonly CredentialDbContext _context;
|
||||
private readonly ILogger<DatabaseInitializer> _logger;
|
||||
|
||||
public DatabaseInitializer(CredentialDbContext context, ILogger<DatabaseInitializer> logger)
|
||||
{
|
||||
_context = context;
|
||||
_logger = logger;
|
||||
} public async Task InitializeAsync()
|
||||
{
|
||||
try
|
||||
{
|
||||
_logger.LogInformation("Inizializzazione database in corso...");
|
||||
|
||||
// Per SQLite con EnsureCreatedAsync è più semplice e sicuro
|
||||
// Crea il database e le tabelle se non esistono
|
||||
var created = await _context.Database.EnsureCreatedAsync();
|
||||
|
||||
if (created)
|
||||
{
|
||||
_logger.LogInformation("Database creato con successo");
|
||||
|
||||
// Aggiungi dati di esempio se necessario
|
||||
await SeedInitialDataAsync();
|
||||
}
|
||||
else
|
||||
{
|
||||
_logger.LogInformation("Database esistente trovato");
|
||||
|
||||
// Verifica che le tabelle esistano
|
||||
await VerifyTablesAsync();
|
||||
|
||||
// Applica migrazioni se necessario
|
||||
await ApplyMigrationsAsync();
|
||||
}
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
_logger.LogError(ex, "Errore durante l'inizializzazione del database");
|
||||
throw;
|
||||
}
|
||||
}
|
||||
|
||||
private async Task VerifyTablesAsync()
|
||||
{
|
||||
try
|
||||
{
|
||||
// Prova a fare una query semplice per verificare che la tabella esista
|
||||
await _context.Credentials.CountAsync();
|
||||
_logger.LogInformation("Verifica tabelle completata con successo");
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
_logger.LogWarning("Tabelle mancanti, ricreazione database...");
|
||||
|
||||
// Se le tabelle non esistono, le ricreiamo
|
||||
await _context.Database.EnsureDeletedAsync();
|
||||
await _context.Database.EnsureCreatedAsync();
|
||||
await SeedInitialDataAsync();
|
||||
|
||||
_logger.LogInformation("Database ricreato con successo");
|
||||
}
|
||||
}
|
||||
|
||||
public async Task<bool> DatabaseExistsAsync()
|
||||
{
|
||||
try
|
||||
{
|
||||
return await _context.Database.CanConnectAsync();
|
||||
}
|
||||
catch
|
||||
{
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
private async Task SeedInitialDataAsync()
|
||||
{
|
||||
try
|
||||
{
|
||||
// Verifica se ci sono già dati
|
||||
if (await _context.Credentials.AnyAsync())
|
||||
{
|
||||
_logger.LogInformation("Dati esistenti trovati. Seeding saltato.");
|
||||
return;
|
||||
}
|
||||
|
||||
_logger.LogInformation("Aggiunta dati di esempio...");
|
||||
|
||||
// Qui puoi aggiungere dati di esempio se necessario
|
||||
// Ad esempio:
|
||||
/*
|
||||
var sampleCredentials = new List<CredentialEntity>
|
||||
{
|
||||
new CredentialEntity
|
||||
{
|
||||
Name = "Esempio Database",
|
||||
Type = "Database",
|
||||
Host = "localhost",
|
||||
Port = 1433,
|
||||
DatabaseName = "TestDB",
|
||||
Username = "testuser",
|
||||
CreatedAt = DateTime.UtcNow,
|
||||
CreatedBy = "System",
|
||||
IsActive = true
|
||||
}
|
||||
};
|
||||
|
||||
_context.Credentials.AddRange(sampleCredentials);
|
||||
await _context.SaveChangesAsync();
|
||||
*/
|
||||
|
||||
_logger.LogInformation("Dati di esempio aggiunti con successo");
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
_logger.LogError(ex, "Errore durante il seeding dei dati iniziali");
|
||||
throw;
|
||||
}
|
||||
} private async Task ApplyMigrationsAsync()
|
||||
{
|
||||
try
|
||||
{
|
||||
_logger.LogInformation("Verifica e applicazione migrazioni...");
|
||||
|
||||
// Verifica se la colonna RestServiceType esiste usando una query diretta
|
||||
try
|
||||
{
|
||||
await _context.Database.ExecuteSqlRawAsync(
|
||||
"SELECT RestServiceType FROM Credentials LIMIT 1");
|
||||
_logger.LogInformation("Colonna RestServiceType già presente");
|
||||
}
|
||||
catch (Microsoft.Data.Sqlite.SqliteException)
|
||||
{
|
||||
// La colonna non esiste, la aggiungiamo
|
||||
_logger.LogInformation("Aggiunta colonna RestServiceType...");
|
||||
await _context.Database.ExecuteSqlRawAsync(
|
||||
"ALTER TABLE Credentials ADD COLUMN RestServiceType TEXT");
|
||||
_logger.LogInformation("Colonna RestServiceType aggiunta con successo");
|
||||
}
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
_logger.LogError(ex, "Errore nell'applicazione delle migrazioni");
|
||||
throw;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,139 @@
|
||||
using System.Security.Cryptography;
|
||||
using System.Text;
|
||||
using System.Runtime.InteropServices;
|
||||
|
||||
namespace CredentialManager.Services;
|
||||
|
||||
/// <summary>
|
||||
/// Interfaccia per il servizio di crittografia
|
||||
/// </summary>
|
||||
public interface IEncryptionService
|
||||
{
|
||||
string Encrypt(string plainText);
|
||||
string Decrypt(string encryptedText);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Servizio per la crittografia delle password cross-platform
|
||||
/// </summary>
|
||||
public class EncryptionService : IEncryptionService
|
||||
{
|
||||
private readonly byte[] _key;
|
||||
private readonly byte[] _iv;
|
||||
|
||||
public EncryptionService()
|
||||
{
|
||||
// Chiave e IV derivati da una stringa fissa (in produzione dovrebbero essere configurabili)
|
||||
var keySource = "CredentialManager2025KeyForEncryption!";
|
||||
var ivSource = "CredMgr2025IV!";
|
||||
|
||||
using var sha256 = SHA256.Create();
|
||||
_key = sha256.ComputeHash(Encoding.UTF8.GetBytes(keySource));
|
||||
_iv = new byte[16];
|
||||
Array.Copy(Encoding.UTF8.GetBytes(ivSource), _iv, Math.Min(16, ivSource.Length));
|
||||
}
|
||||
|
||||
public string Encrypt(string plainText)
|
||||
{
|
||||
if (string.IsNullOrEmpty(plainText))
|
||||
return string.Empty;
|
||||
|
||||
try
|
||||
{
|
||||
// Su Windows, usa ProtectedData se disponibile
|
||||
if (RuntimeInformation.IsOSPlatform(OSPlatform.Windows))
|
||||
{
|
||||
return EncryptWithProtectedData(plainText);
|
||||
}
|
||||
|
||||
// Su altre piattaforme, usa AES
|
||||
return EncryptWithAes(plainText);
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
throw new InvalidOperationException("Errore durante la crittografia", ex);
|
||||
}
|
||||
}
|
||||
|
||||
public string Decrypt(string encryptedText)
|
||||
{
|
||||
if (string.IsNullOrEmpty(encryptedText))
|
||||
return string.Empty;
|
||||
|
||||
try
|
||||
{
|
||||
// Su Windows, usa ProtectedData se disponibile
|
||||
if (RuntimeInformation.IsOSPlatform(OSPlatform.Windows))
|
||||
{
|
||||
return DecryptWithProtectedData(encryptedText);
|
||||
}
|
||||
|
||||
// Su altre piattaforme, usa AES
|
||||
return DecryptWithAes(encryptedText);
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
throw new InvalidOperationException("Errore durante la decrittografia", ex);
|
||||
}
|
||||
} private string EncryptWithProtectedData(string plainText)
|
||||
{
|
||||
byte[] plainTextBytes = Encoding.UTF8.GetBytes(plainText);
|
||||
byte[] entropy = Encoding.UTF8.GetBytes("CredentialManager2025");
|
||||
|
||||
if (OperatingSystem.IsWindows())
|
||||
{
|
||||
byte[] encryptedBytes = ProtectedData.Protect(plainTextBytes, entropy, DataProtectionScope.CurrentUser);
|
||||
return Convert.ToBase64String(encryptedBytes);
|
||||
}
|
||||
|
||||
// Fallback ad AES se non su Windows
|
||||
return EncryptWithAes(plainText);
|
||||
}
|
||||
|
||||
private string DecryptWithProtectedData(string encryptedText)
|
||||
{
|
||||
if (OperatingSystem.IsWindows())
|
||||
{
|
||||
byte[] encryptedBytes = Convert.FromBase64String(encryptedText);
|
||||
byte[] entropy = Encoding.UTF8.GetBytes("CredentialManager2025");
|
||||
byte[] decryptedBytes = ProtectedData.Unprotect(encryptedBytes, entropy, DataProtectionScope.CurrentUser);
|
||||
return Encoding.UTF8.GetString(decryptedBytes);
|
||||
}
|
||||
|
||||
// Fallback ad AES se non su Windows
|
||||
return DecryptWithAes(encryptedText);
|
||||
}
|
||||
|
||||
private string EncryptWithAes(string plainText)
|
||||
{
|
||||
using var aes = Aes.Create();
|
||||
aes.Key = _key;
|
||||
aes.IV = _iv;
|
||||
|
||||
using var encryptor = aes.CreateEncryptor();
|
||||
using var msEncrypt = new MemoryStream();
|
||||
using var csEncrypt = new CryptoStream(msEncrypt, encryptor, CryptoStreamMode.Write);
|
||||
using var swEncrypt = new StreamWriter(csEncrypt);
|
||||
|
||||
swEncrypt.Write(plainText);
|
||||
swEncrypt.Close();
|
||||
|
||||
return Convert.ToBase64String(msEncrypt.ToArray());
|
||||
}
|
||||
|
||||
private string DecryptWithAes(string encryptedText)
|
||||
{
|
||||
byte[] cipherBytes = Convert.FromBase64String(encryptedText);
|
||||
|
||||
using var aes = Aes.Create();
|
||||
aes.Key = _key;
|
||||
aes.IV = _iv;
|
||||
|
||||
using var decryptor = aes.CreateDecryptor();
|
||||
using var msDecrypt = new MemoryStream(cipherBytes);
|
||||
using var csDecrypt = new CryptoStream(msDecrypt, decryptor, CryptoStreamMode.Read);
|
||||
using var srDecrypt = new StreamReader(csDecrypt);
|
||||
|
||||
return srDecrypt.ReadToEnd();
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user