126 lines
5.7 KiB
C#
126 lines
5.7 KiB
C#
using Microsoft.AspNetCore.DataProtection;
|
|
using Microsoft.AspNetCore.DataProtection.EntityFrameworkCore;
|
|
using Microsoft.EntityFrameworkCore;
|
|
using ScadaLink.Commons.Entities.Audit;
|
|
using ScadaLink.Commons.Entities.Deployment;
|
|
using ScadaLink.Commons.Entities.ExternalSystems;
|
|
using ScadaLink.Commons.Entities.InboundApi;
|
|
using ScadaLink.Commons.Entities.Instances;
|
|
using ScadaLink.Commons.Entities.Notifications;
|
|
using ScadaLink.Commons.Entities.Scripts;
|
|
using ScadaLink.Commons.Entities.Security;
|
|
using ScadaLink.Commons.Entities.Sites;
|
|
using ScadaLink.Commons.Entities.Templates;
|
|
|
|
namespace ScadaLink.ConfigurationDatabase;
|
|
|
|
public class ScadaLinkDbContext : DbContext, IDataProtectionKeyContext
|
|
{
|
|
private readonly IDataProtectionProvider? _dataProtectionProvider;
|
|
|
|
public ScadaLinkDbContext(DbContextOptions<ScadaLinkDbContext> options) : base(options)
|
|
{
|
|
}
|
|
|
|
/// <summary>
|
|
/// Creates a context with an explicit Data Protection provider used to encrypt
|
|
/// secret-bearing configuration columns at rest. The runtime resolves this overload
|
|
/// via DI; design-time tooling uses the single-argument overload.
|
|
/// </summary>
|
|
public ScadaLinkDbContext(DbContextOptions<ScadaLinkDbContext> options, IDataProtectionProvider dataProtectionProvider)
|
|
: base(options)
|
|
{
|
|
_dataProtectionProvider = dataProtectionProvider
|
|
?? throw new ArgumentNullException(nameof(dataProtectionProvider));
|
|
}
|
|
|
|
// Templates
|
|
public DbSet<Template> Templates => Set<Template>();
|
|
public DbSet<TemplateAttribute> TemplateAttributes => Set<TemplateAttribute>();
|
|
public DbSet<TemplateAlarm> TemplateAlarms => Set<TemplateAlarm>();
|
|
public DbSet<TemplateScript> TemplateScripts => Set<TemplateScript>();
|
|
public DbSet<TemplateComposition> TemplateCompositions => Set<TemplateComposition>();
|
|
public DbSet<TemplateFolder> TemplateFolders => Set<TemplateFolder>();
|
|
|
|
// Instances
|
|
public DbSet<Instance> Instances => Set<Instance>();
|
|
public DbSet<InstanceAttributeOverride> InstanceAttributeOverrides => Set<InstanceAttributeOverride>();
|
|
public DbSet<InstanceAlarmOverride> InstanceAlarmOverrides => Set<InstanceAlarmOverride>();
|
|
public DbSet<InstanceConnectionBinding> InstanceConnectionBindings => Set<InstanceConnectionBinding>();
|
|
public DbSet<Area> Areas => Set<Area>();
|
|
|
|
// Sites
|
|
public DbSet<Site> Sites => Set<Site>();
|
|
public DbSet<DataConnection> DataConnections => Set<DataConnection>();
|
|
|
|
// Deployment
|
|
public DbSet<DeploymentRecord> DeploymentRecords => Set<DeploymentRecord>();
|
|
public DbSet<SystemArtifactDeploymentRecord> SystemArtifactDeploymentRecords => Set<SystemArtifactDeploymentRecord>();
|
|
public DbSet<DeployedConfigSnapshot> DeployedConfigSnapshots => Set<DeployedConfigSnapshot>();
|
|
|
|
// External Systems
|
|
public DbSet<ExternalSystemDefinition> ExternalSystemDefinitions => Set<ExternalSystemDefinition>();
|
|
public DbSet<ExternalSystemMethod> ExternalSystemMethods => Set<ExternalSystemMethod>();
|
|
public DbSet<DatabaseConnectionDefinition> DatabaseConnectionDefinitions => Set<DatabaseConnectionDefinition>();
|
|
|
|
// Notifications
|
|
public DbSet<NotificationList> NotificationLists => Set<NotificationList>();
|
|
public DbSet<NotificationRecipient> NotificationRecipients => Set<NotificationRecipient>();
|
|
public DbSet<SmtpConfiguration> SmtpConfigurations => Set<SmtpConfiguration>();
|
|
|
|
// Scripts
|
|
public DbSet<SharedScript> SharedScripts => Set<SharedScript>();
|
|
|
|
// Security
|
|
public DbSet<LdapGroupMapping> LdapGroupMappings => Set<LdapGroupMapping>();
|
|
public DbSet<SiteScopeRule> SiteScopeRules => Set<SiteScopeRule>();
|
|
|
|
// Inbound API
|
|
public DbSet<ApiKey> ApiKeys => Set<ApiKey>();
|
|
public DbSet<ApiMethod> ApiMethods => Set<ApiMethod>();
|
|
|
|
// Audit
|
|
public DbSet<AuditLogEntry> AuditLogEntries => Set<AuditLogEntry>();
|
|
|
|
// Data Protection Keys (for shared ASP.NET Data Protection across nodes)
|
|
public DbSet<DataProtectionKey> DataProtectionKeys => Set<DataProtectionKey>();
|
|
|
|
protected override void OnModelCreating(ModelBuilder modelBuilder)
|
|
{
|
|
modelBuilder.ApplyConfigurationsFromAssembly(typeof(ScadaLinkDbContext).Assembly);
|
|
|
|
ApplySecretColumnEncryption(modelBuilder);
|
|
}
|
|
|
|
/// <summary>
|
|
/// Applies encryption-at-rest to columns that hold authentication secrets
|
|
/// (SMTP credentials, external-system auth config, database connection strings)
|
|
/// so they are never persisted as plaintext.
|
|
/// </summary>
|
|
/// <remarks>
|
|
/// When no Data Protection provider is supplied (design-time <c>dotnet ef</c> tooling,
|
|
/// which only emits schema and never reads or writes secret data), an ephemeral provider
|
|
/// is used. The encrypted-column type is <c>nvarchar</c> either way, so the generated
|
|
/// schema is identical regardless of which provider is in effect. The runtime path always
|
|
/// receives the DI-registered provider whose keys are persisted to this database.
|
|
/// </remarks>
|
|
private void ApplySecretColumnEncryption(ModelBuilder modelBuilder)
|
|
{
|
|
IDataProtectionProvider provider = _dataProtectionProvider ?? new EphemeralDataProtectionProvider();
|
|
var converter = new EncryptedStringConverter(
|
|
provider.CreateProtector(EncryptedStringConverter.ProtectorPurpose));
|
|
|
|
modelBuilder.Entity<SmtpConfiguration>()
|
|
.Property(s => s.Credentials)
|
|
.HasConversion(converter);
|
|
|
|
modelBuilder.Entity<ExternalSystemDefinition>()
|
|
.Property(e => e.AuthConfiguration)
|
|
.HasConversion(converter);
|
|
|
|
modelBuilder.Entity<DatabaseConnectionDefinition>()
|
|
.Property(d => d.ConnectionString)
|
|
.HasConversion((Microsoft.EntityFrameworkCore.Storage.ValueConversion.ValueConverter)converter);
|
|
}
|
|
}
|