Merge pull request #5741 from sbwalker/dev

refactor TenantDBContext to accomodate AspNetUserPasskeys
This commit is contained in:
Shaun Walker
2025-10-22 14:06:30 -04:00
committed by GitHub
5 changed files with 102 additions and 12 deletions

View File

@@ -84,7 +84,11 @@ namespace Microsoft.Extensions.DependencyInjection
options.Cookie.HttpOnly = true; options.Cookie.HttpOnly = true;
}); });
services.AddIdentityCore<IdentityUser>(options => { }) services.AddIdentityCore<IdentityUser>(options =>
{
// must be set prior to AddEntityFrameworkStores
options.Stores.SchemaVersion = IdentitySchemaVersions.Version3;
})
.AddEntityFrameworkStores<TenantDBContext>() .AddEntityFrameworkStores<TenantDBContext>()
.AddSignInManager() .AddSignInManager()
.AddDefaultTokenProviders() .AddDefaultTokenProviders()
@@ -167,7 +171,7 @@ namespace Microsoft.Extensions.DependencyInjection
public static IServiceCollection AddOqtaneDbContext(this IServiceCollection services) public static IServiceCollection AddOqtaneDbContext(this IServiceCollection services)
{ {
services.AddDbContext<MasterDBContext>(options => { }, ServiceLifetime.Transient); services.AddDbContext<MasterDBContext>(options => { }, ServiceLifetime.Transient);
services.AddDbContext<TenantDBContext>(options => { }, ServiceLifetime.Transient); services.AddDbContext<TenantDBContext>(options => { }, ServiceLifetime.Scoped);
services.AddDbContextFactory<TenantDBContext>(opt => { }, ServiceLifetime.Transient); services.AddDbContextFactory<TenantDBContext>(opt => { }, ServiceLifetime.Transient);
return services; return services;
} }
@@ -366,9 +370,6 @@ namespace Microsoft.Extensions.DependencyInjection
// User settings // User settings
options.User.RequireUniqueEmail = false; options.User.RequireUniqueEmail = false;
options.User.AllowedUserNameCharacters = "abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789-._@+"; options.User.AllowedUserNameCharacters = "abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789-._@+";
// Stores settings
options.Stores.SchemaVersion = IdentitySchemaVersions.Version3;
}); });
// overrides defined in appsettings // overrides defined in appsettings

View File

@@ -395,7 +395,7 @@ namespace Oqtane.Infrastructure
var connectionString = _configManager.GetSetting($"{SettingKeys.ConnectionStringsSection}:{tenant.DBConnectionString}", ""); var connectionString = _configManager.GetSetting($"{SettingKeys.ConnectionStringsSection}:{tenant.DBConnectionString}", "");
if (!string.IsNullOrEmpty(connectionString)) if (!string.IsNullOrEmpty(connectionString))
{ {
using (var tenantDbContext = new TenantDBContext(DBContextDependencies)) using (var tenantDbContext = new TenantDBContext(new DbContextOptions<TenantDBContext>(), DBContextDependencies))
{ {
AddEFMigrationsHistory(sql, connectionString, tenant.DBType, tenant.Version, false); AddEFMigrationsHistory(sql, connectionString, tenant.DBType, tenant.Version, false);
// push latest model into database // push latest model into database

View File

@@ -369,6 +369,15 @@ namespace Oqtane.Managers
IdentityUser identityuser = await _identityUserManager.FindByNameAsync(user.Username); IdentityUser identityuser = await _identityUserManager.FindByNameAsync(user.Username);
if (identityuser != null) 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); var result = await _identitySignInManager.CheckPasswordSignInAsync(identityuser, user.Password, true);
if (result.Succeeded) if (result.Succeeded)
{ {

View File

@@ -4,7 +4,6 @@ using System.Threading;
using System.Threading.Tasks; using System.Threading.Tasks;
using Microsoft.AspNetCore.Http; using Microsoft.AspNetCore.Http;
using Microsoft.AspNetCore.Identity; using Microsoft.AspNetCore.Identity;
using Microsoft.AspNetCore.Identity.EntityFrameworkCore;
using Microsoft.EntityFrameworkCore; using Microsoft.EntityFrameworkCore;
using Microsoft.EntityFrameworkCore.Migrations; using Microsoft.EntityFrameworkCore.Migrations;
using Microsoft.Extensions.Configuration; using Microsoft.Extensions.Configuration;
@@ -19,7 +18,7 @@ using Oqtane.Shared;
namespace Oqtane.Repository namespace Oqtane.Repository
{ {
public class DBContextBase : IdentityUserContext<IdentityUser> public class DBContextBase : DbContext
{ {
private readonly ITenantManager _tenantManager; private readonly ITenantManager _tenantManager;
private readonly IHttpContextAccessor _accessor; private readonly IHttpContextAccessor _accessor;
@@ -75,8 +74,6 @@ namespace Oqtane.Repository
protected override void OnModelCreating(ModelBuilder builder) protected override void OnModelCreating(ModelBuilder builder)
{ {
base.OnModelCreating(builder); base.OnModelCreating(builder);
ActiveDatabase.UpdateIdentityStoreTableNames(builder);
} }
public override int SaveChanges() public override int SaveChanges()

View File

@@ -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;
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.Models;
using Oqtane.Repository.Databases.Interfaces; using Oqtane.Repository.Databases.Interfaces;
using Oqtane.Shared;
// ReSharper disable CheckNamespace // ReSharper disable CheckNamespace
// ReSharper disable MemberCanBePrivate.Global // ReSharper disable MemberCanBePrivate.Global
@@ -8,9 +21,79 @@ using Oqtane.Repository.Databases.Interfaces;
namespace Oqtane.Repository namespace Oqtane.Repository
{ {
public class TenantDBContext : DBContextBase, IMultiDatabase public class TenantDBContext : IdentityUserContext<IdentityUser>, 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<TenantDBContext> 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<IMigrationsAssembly, MultiDatabaseMigrationsAssembly>();
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<int> SaveChangesAsync(CancellationToken cancellationToken = default)
{
DbContextUtils.SaveChanges(this, _accessor);
return base.SaveChangesAsync(cancellationToken);
}
public virtual DbSet<Site> Site { get; set; } public virtual DbSet<Site> Site { get; set; }
public virtual DbSet<Page> Page { get; set; } public virtual DbSet<Page> Page { get; set; }