960166be9f
Aggiunta funzionalità completa per l'eliminazione sicura delle credenziali con rimozione automatica di tutti i dati associati. Modifiche principali: Backend: - Aggiunta interfaccia ICredentialService.DeleteCredentialCascadeAsync() - Implementato CredentialService.DeleteCredentialCascadeAsync() con gestione transazionale - Aggiornata IDataConnectionCredentialService con metodi cascade delete - Implementati wrapper in DataConnectionCredentialService Eliminazione cascata gestisce: - Execution histories delle schedulazioni - Profile schedules associate ai profili - Data Coupler profiles che usano le credenziali - Key associations per credenziali REST - Credenziale stessa Frontend (CredentialManagement.razor): - Aggiunto modale Bootstrap di conferma eliminazione con design danger - Messaggio di attenzione chiaro che elenca cosa verrà eliminato - Refactoring metodo DeleteCredential() per usare modale invece di confirm JS - Aggiunti metodi CloseDeleteConfirmModal() e ConfirmDeleteCredential() Sicurezza: - Eliminazione fisica (hard delete) con transazione database - Rollback automatico in caso di errore - Logging dettagliato di ogni operazione - Conferma esplicita dell'utente richiesta
1122 lines
46 KiB
C#
1122 lines
46 KiB
C#
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);
|
|
|
|
// Cascade delete operations
|
|
Task<bool> DeleteCredentialCascadeAsync(int id);
|
|
Task<bool> DeleteCredentialCascadeAsync(string name);
|
|
|
|
// Helper methods to get credential ID by name
|
|
Task<int?> GetCredentialIdByNameAsync(string name, CredentialType type);
|
|
}
|
|
|
|
/// <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 = DecryptSafely(entity.EncryptedPassword, entity.Name, "password"),
|
|
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)
|
|
? DecryptSafely(entity.EncryptedPassword, entity.Name, "password")
|
|
: null,
|
|
ApiKey = !string.IsNullOrEmpty(entity.EncryptedApiKey)
|
|
? DecryptSafely(entity.EncryptedApiKey, entity.Name, "API key")
|
|
: null,
|
|
AuthToken = !string.IsNullOrEmpty(entity.EncryptedAuthToken)
|
|
? DecryptSafely(entity.EncryptedAuthToken, entity.Name, "auth token")
|
|
: 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
|
|
|
|
#region Private Utility Methods /// <summary>
|
|
/// Decrittografa in modo sicuro gestendo i fallimenti dovuti a migrazione tra macchine
|
|
/// </summary>
|
|
/// <param name="encryptedValue">Valore crittografato</param>
|
|
/// <param name="credentialName">Nome della credenziale per logging</param>
|
|
/// <param name="fieldName">Nome del campo per logging</param>
|
|
/// <returns>Valore decrittografato o stringa speciale per indicare che serve re-inserimento</returns>
|
|
private string DecryptSafely(string? encryptedValue, string credentialName, string fieldName)
|
|
{
|
|
if (string.IsNullOrEmpty(encryptedValue))
|
|
return string.Empty;
|
|
|
|
try
|
|
{
|
|
return _encryptionService.Decrypt(encryptedValue);
|
|
}
|
|
catch (InvalidOperationException ex) when (ex.Message.Contains("Impossibile decrittografare"))
|
|
{
|
|
_logger.LogWarning("Impossibile decrittografare {FieldName} per la credenziale {CredentialName}. Probabile migrazione tra macchine diverse.",
|
|
fieldName, credentialName);
|
|
return "*** CREDENZIALI NON DISPONIBILI - REINSERIRE ***";
|
|
}
|
|
catch (Exception ex)
|
|
{
|
|
_logger.LogWarning("Problema nella decrittografia di {FieldName} per la credenziale {CredentialName}: {Message}",
|
|
fieldName, credentialName, ex.Message);
|
|
return "*** ERRORE DECRITTOGRAFIA ***";
|
|
}
|
|
}
|
|
|
|
/// <summary>
|
|
/// Verifica se una credenziale ha bisogno di essere re-inserita
|
|
/// </summary>
|
|
/// <param name="credentialValue">Valore della credenziale</param>
|
|
/// <returns>True se la credenziale deve essere re-inserita</returns>
|
|
private bool NeedsReentry(string credentialValue)
|
|
{
|
|
return credentialValue.Contains("*** CREDENZIALI NON DISPONIBILI") ||
|
|
credentialValue.Contains("*** ERRORE DECRITTOGRAFIA ***");
|
|
}
|
|
|
|
/// <summary>
|
|
/// Ottiene l'ID di una credenziale per nome e tipo
|
|
/// </summary>
|
|
/// <param name="name">Nome della credenziale</param>
|
|
/// <param name="type">Tipo della credenziale</param>
|
|
/// <returns>ID della credenziale se trovata, null altrimenti</returns>
|
|
public async Task<int?> GetCredentialIdByNameAsync(string name, CredentialType type)
|
|
{
|
|
try
|
|
{
|
|
var entity = await _context.Credentials
|
|
.FirstOrDefaultAsync(c => c.Name == name && c.Type == type.ToString() && c.IsActive);
|
|
|
|
return entity?.Id;
|
|
}
|
|
catch (Exception ex)
|
|
{
|
|
_logger.LogError(ex, "Errore nel recuperare l'ID della credenziale: {Name}, Tipo: {Type}", name, type);
|
|
return null;
|
|
}
|
|
}
|
|
|
|
/// <summary>
|
|
/// Elimina fisicamente una credenziale e tutti i dati associati (profili e schedulazioni) in modo cascata
|
|
/// </summary>
|
|
/// <param name="id">ID della credenziale da eliminare</param>
|
|
/// <returns>True se l'eliminazione è riuscita, False altrimenti</returns>
|
|
public async Task<bool> DeleteCredentialCascadeAsync(int id)
|
|
{
|
|
using var transaction = await _context.Database.BeginTransactionAsync();
|
|
try
|
|
{
|
|
var credential = await _context.Credentials.FindAsync(id);
|
|
if (credential == null)
|
|
{
|
|
_logger.LogWarning("Tentativo di eliminare credenziale inesistente con ID: {Id}", id);
|
|
return false;
|
|
}
|
|
|
|
_logger.LogInformation("Inizio eliminazione cascata per credenziale: {Name} (ID: {Id})", credential.Name, credential.Id);
|
|
|
|
// 1. Trova tutti i profili che usano questa credenziale (come sorgente o destinazione)
|
|
var profilesToDelete = await _context.DataCouplerProfiles
|
|
.Where(p => p.SourceCredentialId == id || p.DestinationCredentialId == id)
|
|
.ToListAsync();
|
|
|
|
_logger.LogInformation("Trovati {Count} profili associati alla credenziale {Name}", profilesToDelete.Count, credential.Name);
|
|
|
|
// 2. Per ogni profilo, elimina le schedulazioni associate
|
|
foreach (var profile in profilesToDelete)
|
|
{
|
|
var schedulesToDelete = await _context.ProfileSchedules
|
|
.Where(s => s.ProfileId == profile.Id)
|
|
.ToListAsync();
|
|
|
|
if (schedulesToDelete.Any())
|
|
{
|
|
_logger.LogInformation("Eliminazione di {Count} schedulazioni per il profilo {ProfileName}",
|
|
schedulesToDelete.Count, profile.Name);
|
|
|
|
// Elimina le execution histories delle schedulazioni
|
|
foreach (var schedule in schedulesToDelete)
|
|
{
|
|
var histories = await _context.ScheduleExecutionHistories
|
|
.Where(h => h.ScheduleId == schedule.Id)
|
|
.ToListAsync();
|
|
|
|
if (histories.Any())
|
|
{
|
|
_context.ScheduleExecutionHistories.RemoveRange(histories);
|
|
_logger.LogInformation("Eliminate {Count} execution histories per la schedulazione {ScheduleName}",
|
|
histories.Count, schedule.Name);
|
|
}
|
|
}
|
|
|
|
_context.ProfileSchedules.RemoveRange(schedulesToDelete);
|
|
}
|
|
}
|
|
|
|
// 3. Elimina i profili
|
|
if (profilesToDelete.Any())
|
|
{
|
|
_context.DataCouplerProfiles.RemoveRange(profilesToDelete);
|
|
_logger.LogInformation("Eliminati {Count} profili associati alla credenziale {Name}",
|
|
profilesToDelete.Count, credential.Name);
|
|
}
|
|
|
|
// 4. Elimina le key associations associate (se presenti)
|
|
var keyAssociations = await _context.KeyAssociations
|
|
.Where(ka => ka.RestCredentialName == credential.Name)
|
|
.ToListAsync();
|
|
|
|
if (keyAssociations.Any())
|
|
{
|
|
_context.KeyAssociations.RemoveRange(keyAssociations);
|
|
_logger.LogInformation("Eliminate {Count} key associations per la credenziale {Name}",
|
|
keyAssociations.Count, credential.Name);
|
|
}
|
|
|
|
// 5. Infine, elimina la credenziale
|
|
_context.Credentials.Remove(credential);
|
|
|
|
// Salva tutte le modifiche
|
|
await _context.SaveChangesAsync();
|
|
await transaction.CommitAsync();
|
|
|
|
_logger.LogInformation(
|
|
"Credenziale {Name} (ID: {Id}) eliminata con successo insieme a {ProfileCount} profili, " +
|
|
"{ScheduleCount} schedulazioni e {KeyAssociationCount} key associations",
|
|
credential.Name, credential.Id, profilesToDelete.Count,
|
|
profilesToDelete.Sum(p => _context.ProfileSchedules.Count(s => s.ProfileId == p.Id)),
|
|
keyAssociations.Count);
|
|
|
|
return true;
|
|
}
|
|
catch (Exception ex)
|
|
{
|
|
await transaction.RollbackAsync();
|
|
_logger.LogError(ex, "Errore durante l'eliminazione cascata della credenziale con ID: {Id}", id);
|
|
throw;
|
|
}
|
|
}
|
|
|
|
/// <summary>
|
|
/// Elimina fisicamente una credenziale e tutti i dati associati (profili e schedulazioni) in modo cascata
|
|
/// </summary>
|
|
/// <param name="name">Nome della credenziale da eliminare</param>
|
|
/// <returns>True se l'eliminazione è riuscita, False altrimenti</returns>
|
|
public async Task<bool> DeleteCredentialCascadeAsync(string name)
|
|
{
|
|
try
|
|
{
|
|
var credential = await _context.Credentials
|
|
.FirstOrDefaultAsync(c => c.Name == name);
|
|
|
|
if (credential == null)
|
|
{
|
|
_logger.LogWarning("Tentativo di eliminare credenziale inesistente: {Name}", name);
|
|
return false;
|
|
}
|
|
|
|
return await DeleteCredentialCascadeAsync(credential.Id);
|
|
}
|
|
catch (Exception ex)
|
|
{
|
|
_logger.LogError(ex, "Errore durante l'eliminazione cascata della credenziale: {Name}", name);
|
|
throw;
|
|
}
|
|
}
|
|
|
|
#endregion
|
|
}
|