From 273097d96d894b6341d6f567c30a1d57c5e9c9ab Mon Sep 17 00:00:00 2001 From: sbwalker Date: Wed, 22 Oct 2025 14:06:15 -0400 Subject: [PATCH] refactor TenantDBContext to accomodate AspNetUserPasskeys --- .../OqtaneServiceCollectionExtensions.cs | 11 +-- .../Infrastructure/DatabaseManager.cs | 2 +- Oqtane.Server/Managers/UserManager.cs | 9 ++ .../Repository/Context/DBContextBase.cs | 5 +- .../Repository/Context/TenantDBContext.cs | 87 ++++++++++++++++++- 5 files changed, 102 insertions(+), 12 deletions(-) diff --git a/Oqtane.Server/Extensions/OqtaneServiceCollectionExtensions.cs b/Oqtane.Server/Extensions/OqtaneServiceCollectionExtensions.cs index 20f3bef9..b1573ac6 100644 --- a/Oqtane.Server/Extensions/OqtaneServiceCollectionExtensions.cs +++ b/Oqtane.Server/Extensions/OqtaneServiceCollectionExtensions.cs @@ -84,7 +84,11 @@ namespace Microsoft.Extensions.DependencyInjection options.Cookie.HttpOnly = true; }); - services.AddIdentityCore(options => { }) + services.AddIdentityCore(options => + { + // must be set prior to AddEntityFrameworkStores + options.Stores.SchemaVersion = IdentitySchemaVersions.Version3; + }) .AddEntityFrameworkStores() .AddSignInManager() .AddDefaultTokenProviders() @@ -167,7 +171,7 @@ namespace Microsoft.Extensions.DependencyInjection public static IServiceCollection AddOqtaneDbContext(this IServiceCollection services) { services.AddDbContext(options => { }, ServiceLifetime.Transient); - services.AddDbContext(options => { }, ServiceLifetime.Transient); + services.AddDbContext(options => { }, ServiceLifetime.Scoped); services.AddDbContextFactory(opt => { }, ServiceLifetime.Transient); return services; } @@ -366,9 +370,6 @@ namespace Microsoft.Extensions.DependencyInjection // User settings options.User.RequireUniqueEmail = false; options.User.AllowedUserNameCharacters = "abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789-._@+"; - - // Stores settings - options.Stores.SchemaVersion = IdentitySchemaVersions.Version3; }); // overrides defined in appsettings diff --git a/Oqtane.Server/Infrastructure/DatabaseManager.cs b/Oqtane.Server/Infrastructure/DatabaseManager.cs index 1cc78a5a..1d4debe7 100644 --- a/Oqtane.Server/Infrastructure/DatabaseManager.cs +++ b/Oqtane.Server/Infrastructure/DatabaseManager.cs @@ -395,7 +395,7 @@ namespace Oqtane.Infrastructure var connectionString = _configManager.GetSetting($"{SettingKeys.ConnectionStringsSection}:{tenant.DBConnectionString}", ""); if (!string.IsNullOrEmpty(connectionString)) { - using (var tenantDbContext = new TenantDBContext(DBContextDependencies)) + using (var tenantDbContext = new TenantDBContext(new DbContextOptions(), DBContextDependencies)) { AddEFMigrationsHistory(sql, connectionString, tenant.DBType, tenant.Version, false); // push latest model into database diff --git a/Oqtane.Server/Managers/UserManager.cs b/Oqtane.Server/Managers/UserManager.cs index d6d4afa0..5b5514ee 100644 --- a/Oqtane.Server/Managers/UserManager.cs +++ b/Oqtane.Server/Managers/UserManager.cs @@ -369,6 +369,15 @@ namespace Oqtane.Managers IdentityUser identityuser = await _identityUserManager.FindByNameAsync(user.Username); if (identityuser != null) { + try + { + var passKeysFunctional = await _identityUserManager.GetPasskeysAsync(identityuser); + } + catch (Exception ex) + { + var error = ex.ToString(); + } + var result = await _identitySignInManager.CheckPasswordSignInAsync(identityuser, user.Password, true); if (result.Succeeded) { diff --git a/Oqtane.Server/Repository/Context/DBContextBase.cs b/Oqtane.Server/Repository/Context/DBContextBase.cs index fc02291d..aedbf412 100644 --- a/Oqtane.Server/Repository/Context/DBContextBase.cs +++ b/Oqtane.Server/Repository/Context/DBContextBase.cs @@ -4,7 +4,6 @@ using System.Threading; using System.Threading.Tasks; using Microsoft.AspNetCore.Http; using Microsoft.AspNetCore.Identity; -using Microsoft.AspNetCore.Identity.EntityFrameworkCore; using Microsoft.EntityFrameworkCore; using Microsoft.EntityFrameworkCore.Migrations; using Microsoft.Extensions.Configuration; @@ -19,7 +18,7 @@ using Oqtane.Shared; namespace Oqtane.Repository { - public class DBContextBase : IdentityUserContext + public class DBContextBase : DbContext { private readonly ITenantManager _tenantManager; private readonly IHttpContextAccessor _accessor; @@ -75,8 +74,6 @@ namespace Oqtane.Repository protected override void OnModelCreating(ModelBuilder builder) { base.OnModelCreating(builder); - - ActiveDatabase.UpdateIdentityStoreTableNames(builder); } public override int SaveChanges() diff --git a/Oqtane.Server/Repository/Context/TenantDBContext.cs b/Oqtane.Server/Repository/Context/TenantDBContext.cs index bb0e1c72..a764e85e 100644 --- a/Oqtane.Server/Repository/Context/TenantDBContext.cs +++ b/Oqtane.Server/Repository/Context/TenantDBContext.cs @@ -1,6 +1,19 @@ +using System; +using System.Threading; +using System.Threading.Tasks; +using Microsoft.AspNetCore.Http; +using Microsoft.AspNetCore.Identity; +using Microsoft.AspNetCore.Identity.EntityFrameworkCore; using Microsoft.EntityFrameworkCore; +using Microsoft.EntityFrameworkCore.Migrations; +using Microsoft.Extensions.Configuration; +using Oqtane.Databases.Interfaces; +using Oqtane.Extensions; +using Oqtane.Infrastructure; +using Oqtane.Migrations.Framework; using Oqtane.Models; using Oqtane.Repository.Databases.Interfaces; +using Oqtane.Shared; // ReSharper disable CheckNamespace // ReSharper disable MemberCanBePrivate.Global @@ -8,9 +21,79 @@ using Oqtane.Repository.Databases.Interfaces; namespace Oqtane.Repository { - public class TenantDBContext : DBContextBase, IMultiDatabase + public class TenantDBContext : IdentityUserContext, IMultiDatabase { - public TenantDBContext(IDBContextDependencies DBContextDependencies) : base(DBContextDependencies) { } + private readonly ITenantManager _tenantManager; + private readonly IHttpContextAccessor _accessor; + private readonly IConfigurationRoot _config; + private string _connectionString = ""; + private string _databaseType = ""; + + public TenantDBContext(DbContextOptions options, IDBContextDependencies DBContextDependencies) : base(options) + { + _tenantManager = DBContextDependencies.TenantManager; + _accessor = DBContextDependencies.Accessor; + _config = DBContextDependencies.Config; + } + + public IDatabase ActiveDatabase { get; set; } + + protected override void OnConfiguring(DbContextOptionsBuilder optionsBuilder) + { + optionsBuilder.ReplaceService(); + + if (string.IsNullOrEmpty(_connectionString)) + { + Tenant tenant = _tenantManager.GetTenant(); + if (tenant != null) + { + _connectionString = _config.GetConnectionString(tenant.DBConnectionString); + if (_connectionString != null) + { + _connectionString = _connectionString.Replace($"|{Constants.DataDirectory}|", AppDomain.CurrentDomain.GetData(Constants.DataDirectory)?.ToString()); + _databaseType = tenant.DBType; + } + else + { + // tenant connection string does not exist in appsettings.json + } + } + } + + if (!string.IsNullOrEmpty(_databaseType)) + { + var type = Type.GetType(_databaseType); + ActiveDatabase = Activator.CreateInstance(type) as IDatabase; + } + + if (!string.IsNullOrEmpty(_connectionString) && ActiveDatabase != null) + { + optionsBuilder.UseOqtaneDatabase(ActiveDatabase, _connectionString); + } + + base.OnConfiguring(optionsBuilder); + } + + protected override void OnModelCreating(ModelBuilder builder) + { + base.OnModelCreating(builder); + + ActiveDatabase.UpdateIdentityStoreTableNames(builder); + } + + public override int SaveChanges() + { + DbContextUtils.SaveChanges(this, _accessor); + + return base.SaveChanges(); + } + + public override Task SaveChangesAsync(CancellationToken cancellationToken = default) + { + DbContextUtils.SaveChanges(this, _accessor); + + return base.SaveChangesAsync(cancellationToken); + } public virtual DbSet Site { get; set; } public virtual DbSet Page { get; set; }