//----------------------------------------------------------------------- // // Copyright (c) lanedirt. All rights reserved. // Licensed under the AGPLv3 license. See LICENSE.md file in the project root for full license information. // //----------------------------------------------------------------------- namespace AliasServerDb; using AliasVault.WorkerStatus.Database; using Microsoft.AspNetCore.DataProtection.EntityFrameworkCore; using Microsoft.AspNetCore.Identity; using Microsoft.EntityFrameworkCore; using Microsoft.EntityFrameworkCore.Storage.ValueConversion; using Microsoft.Extensions.Configuration; /// /// The AliasServerDbContext class. Note: we are using DbContext instead of IdentityDbContext because /// we have two separate user objects, one for the admin panel and one for the vault. We manually /// define the Identity tables in the OnModelCreating method. /// public class AliasServerDbContext : WorkerStatusDbContext, IDataProtectionKeyContext { /// /// Initializes a new instance of the class. /// public AliasServerDbContext() { } /// /// Initializes a new instance of the class. /// /// DbContextOptions. public AliasServerDbContext(DbContextOptions options) : base(options) { } /// /// Gets or sets the DataProtectionKeys DbSet. /// public DbSet DataProtectionKeys { get; set; } /// /// Gets or sets the AliasVaultUser DbSet. /// public DbSet AliasVaultUsers { get; set; } /// /// Gets or sets the AliasVaultRoles DbSet. /// public DbSet AliasVaultRoles { get; set; } /// /// Gets or sets the UserRoles DbSet. /// public DbSet> UserRoles { get; set; } /// /// Gets or sets the UserClaims DbSet. /// public DbSet> UserClaims { get; set; } /// /// Gets or sets the UserLogin DbSet. /// public DbSet> UserLogin { get; set; } /// /// Gets or sets the RoleClaims DbSet. /// public DbSet> RoleClaims { get; set; } /// /// Gets or sets the UserTokens DbSet. /// public DbSet> UserTokens { get; set; } /// /// Gets or sets the UserRefreshTokens DbSet. /// public DbSet AliasVaultUserRefreshTokens { get; set; } /// /// Gets or sets the AdminUser DbSet. /// public DbSet AdminUsers { get; set; } /// /// Gets or sets the AdminRoles DbSet. /// public DbSet AdminRoles { get; set; } /// /// Gets or sets the Vaults DbSet. /// public DbSet Vaults { get; set; } /// /// Gets or sets the Emails DbSet. /// public DbSet Emails { get; set; } /// /// Gets or sets the EmailAttachments DbSet. /// public DbSet EmailAttachments { get; set; } /// /// Gets or sets the UserEmailClaims DbSet. /// public DbSet UserEmailClaims { get; set; } /// /// Gets or sets the UserEncryptionKeys DbSet. /// public DbSet UserEncryptionKeys { get; set; } /// /// Gets or sets the Logs DbSet. /// public DbSet Logs { get; set; } /// /// Gets or sets the AuthLogs DbSet. /// public DbSet AuthLogs { get; set; } /// /// Gets or sets the ServerSettings DbSet. /// public DbSet ServerSettings { get; set; } = null!; /// /// Gets or sets the TaskRunnerJobs DbSet. /// public DbSet TaskRunnerJobs { get; set; } /// /// Sets up the connection string if it is not already configured. /// /// DbContextOptionsBuilder instance. protected override void OnConfiguring(DbContextOptionsBuilder optionsBuilder) { AppContext.SetSwitch("Npgsql.EnableLegacyTimestampBehavior", true); if (optionsBuilder.IsConfigured) { return; } var configuration = new ConfigurationBuilder() .SetBasePath(Directory.GetCurrentDirectory()) .AddJsonFile("appsettings.json") .Build(); // Add SQLite connection with enhanced settings var connectionString = configuration.GetConnectionString("AliasServerDbContext"); optionsBuilder .UseNpgsql(connectionString, options => options.CommandTimeout(60)) .UseLazyLoadingProxies(); } /// /// The OnModelCreating method. /// /// ModelBuilder instance. protected override void OnModelCreating(ModelBuilder modelBuilder) { base.OnModelCreating(modelBuilder); // Configure all DateTime properties to use timestamp with time zone in UTC foreach (var entityType in modelBuilder.Model.GetEntityTypes()) { foreach (var property in entityType.GetProperties()) { if (property.ClrType == typeof(DateTime) || property.ClrType == typeof(DateTime?)) { property.SetColumnType("timestamp with time zone"); // Add value converter for DateTime properties var converter = new ValueConverter( v => v.ToUniversalTime(), v => v.ToUniversalTime()); property.SetValueConverter(converter); } } } // Configure AspNetIdentity tables manually. modelBuilder.Entity>(entity => { entity.HasKey(r => new { r.UserId, r.RoleId }); entity.ToTable("UserRoles"); }); modelBuilder.Entity>(entity => { entity.HasKey(c => c.Id); entity.ToTable("UserClaims"); }); modelBuilder.Entity>(entity => { entity.HasKey(l => new { l.LoginProvider, l.ProviderKey }); entity.ToTable("UserLogins"); }); modelBuilder.Entity>(entity => { entity.HasKey(rc => rc.Id); entity.ToTable("RoleClaims"); }); modelBuilder.Entity>(entity => { entity.HasKey(t => new { t.UserId, t.LoginProvider, t.Name }); entity.ToTable("UserTokens"); }); // Configure Log entity modelBuilder.Entity(builder => { builder.ToTable("Logs"); builder.Property(e => e.Application).HasMaxLength(50).IsRequired(); builder.Property(e => e.Message); builder.Property(e => e.MessageTemplate); builder.Property(e => e.Level).HasMaxLength(128); builder.Property(e => e.TimeStamp); builder.Property(e => e.Exception); builder.Property(e => e.Properties); builder.Property(e => e.LogEvent); // Indexes for faster querying builder.HasIndex(e => e.TimeStamp); builder.HasIndex(e => e.Application); }); // Configure Vault - AliasVaultUser relationship modelBuilder.Entity() .HasOne(l => l.User) .WithMany(c => c.Vaults) .HasForeignKey(l => l.UserId) .OnDelete(DeleteBehavior.Cascade); // Configure UserEmailClaim - AliasVaultUser relationship // Note: when a user is deleted the email claims user FK's should be set to NULL // so the claims themselves are preserved to prevent re-use of the email address. modelBuilder.Entity() .HasOne(e => e.User) .WithMany(u => u.EmailClaims) .HasForeignKey(e => e.UserId) .OnDelete(DeleteBehavior.SetNull); // Configure Email - UserEncryptionKey relationship modelBuilder.Entity() .HasOne(l => l.EncryptionKey) .WithMany(c => c.Emails) .HasForeignKey(l => l.UserEncryptionKeyId) .OnDelete(DeleteBehavior.Cascade); // Configure UserEncryptionKey - AliasVaultUser relationship modelBuilder.Entity() .HasOne(l => l.User) .WithMany(c => c.EncryptionKeys) .HasForeignKey(l => l.UserId) .OnDelete(DeleteBehavior.Cascade); } }