在 ASP.NET Core 8 中忽略管理员的全局 HasQueryFilter()

问题描述 投票:0回答:1

我需要允许“超级用户”跨多个租户全面访问数据和视图。

我为应用程序使用单个数据库和单个代码库。为了隔离每个租户的数据,已在

ApplicationDbContext
的模型构建器中创建了一个过滤器。

但是,超级用户应该能够绕过此过滤器并有权访问所有数据(不用担心,无论这是好事还是坏事)。我知道我可以通过添加

IgnoreQueryFilters()
来实现此目的,但随后我需要将其添加到每个过滤器中。

有没有办法在全球范围内实现这一目标?

非常感谢您的指导。

编辑

我添加了完整且修改后的ApplicationDbContext和Program.cs。为什么代码块周围的 if 语句

HasQueryFilter()
不起作用?

正如 Panagiotis Kanavos 所说,如果用户是管理员,代码不应调用

HasQueryFilter
。但如何实现这一点呢?我尝试了下面的代码,但没有成功。

应用程序DbContext

using Whatever.Models;
using Microsoft.AspNetCore.Identity;
using Microsoft.AspNetCore.Identity.EntityFrameworkCore;
using Microsoft.EntityFrameworkCore;

namespace Whatever.Data
{
    public partial class ApplicationDbContext : 
IdentityDbContext<ApplicationUser, ApplicationRole, string,
    IdentityUserClaim<string>, ApplicationUserRole, 
IdentityUserLogin<string>, IdentityRoleClaim<string>, 
IdentityUserToken<string>>
{
    private readonly IHttpContextAccessor _contextAccessor;
    public 
ApplicationDbContext(DbContextOptions<ApplicationDbContext> 
options, IHttpContextAccessor httpContextAccessor)
        : base(options)
    {
        _contextAccessor = httpContextAccessor;
    }
   
    public virtual DbSet<ApplicationUser> ApplicationUser { get; set; }
    public virtual DbSet<ApplicationRole> ApplicationRole { get; set; }
    public virtual DbSet<ApplicationUserRole> ApplicationUserRole { get; set; }
    public virtual DbSet<Tenant> Tenant { get; set; }
    public virtual DbSet<TenantPerson> TenantPerson { get; set; }
    public virtual DbSet<Customer> Customer { get; set; }
    public virtual DbSet<CustomerMember> CustomerMember { get; set; }              

    protected override void OnModelCreating(ModelBuilder modelBuilder)
    {
        base.OnModelCreating(modelBuilder);           

        modelBuilder.Entity<ApplicationUserRole>(userRole =>
        {
            userRole.HasKey(ur => new { ur.UserId, ur.RoleId });

            userRole.HasOne(ur => ur.Role)
            .WithMany(r => r.UserRoles)
            .HasForeignKey(ur => ur.RoleId)
            .IsRequired();

            userRole.HasOne(ur => ur.User)
            .WithMany(r => r.UserRoles)
            .HasForeignKey(ur => ur.UserId)
            .IsRequired();
        });      
          
        if(!_contextAccessor.HttpContext.User.IsInRole("SuperUser"))  // Does not work properly
        {       
            modelBuilder.Entity<Tenant>().HasQueryFilter(e => e.Id == CurrentTenantId);
            modelBuilder.Entity<TenantPerson>().HasQueryFilter(e => e.TenantId == CurrentTenantId);
            modelBuilder.Entity<Customer>().HasQueryFilter(e => e.TenantId == CurrentTenantId);
            modelBuilder.Entity<CustomerMember>().HasQueryFilter(e => e.TenantId == CurrentTenantId);
        }          

        modelBuilder.Entity<Project>()
            .HasOne(e => e.Profile)
        .WithOne(e => e.Project)
            .HasForeignKey<Profile>(e => e.ProjectId)
            .IsRequired(false);
    }

    public virtual Guid CurrentTenantId => GetCurrentTenantId();

  private Guid GetCurrentTenantId()
  {
     string cUsername = _contextAccessor?.HttpContext?.User.Identity?.Name ?? string.Empty;
     ApplicationUser? storedUser = Users.FirstOrDefault(u => u.UserName == cUsername);
     return storedUser?.TenantId ?? Guid.Empty;
  }
  }
  }
  }
  }

程序.cs

using Whatever.Data;
using Microsoft.AspNetCore.Authorization;
using Microsoft.AspNetCore.Identity;
using Microsoft.EntityFrameworkCore;

var builder = WebApplication.CreateBuilder(args);

// Add services to the container.
var connectionString = 
builder.Configuration.GetConnectionString("DefaultConnection") ?? throw new InvalidOperationException("Connection string 'DefaultConnection' not found.");
builder.Services.AddDbContext<ApplicationDbContext>(options =>
    options.UseSqlServer(connectionString));

builder.Services.AddDatabaseDeveloperPageExceptionFilter();

builder.Services.AddIdentity<ApplicationUser, ApplicationRole> 
   (options => options.SignIn.RequireConfirmedAccount = true)
    .AddEntityFrameworkStores<ApplicationDbContext>()
    .AddDefaultUI()
    .AddDefaultTokenProviders();

builder.Services.AddControllersWithViews();
builder.Services.AddRazorPages();
builder.Services.AddAuthorization(options =>
{
    options.FallbackPolicy = new AuthorizationPolicyBuilder()
        .RequireAuthenticatedUser()
        .Build();
});

var app = builder.Build();

// Configure the HTTP request pipeline.
if (app.Environment.IsDevelopment())
{
    app.UseMigrationsEndPoint();
}
else
{
    app.UseExceptionHandler("/Home/Error");        
    app.UseHsts();
}
app.UseHttpsRedirection();
app.UseStaticFiles();
app.UseRouting();
app.UseAuthorization();
app.MapControllerRoute(
    name: "default",
    pattern: "{controller=Customer}/{action=Index}/{id?}");
app.MapRazorPages();

app.Run();
c# asp.net-core asp.net-authorization
1个回答
1
投票

在我的测试中,您检索 currentName 的逻辑在

modelCreating
中未正确完成。
HasQueryFilter
方法需要引用在执行查询时评估的属性,而不是在构建模型时评估的属性。这是一个代码示例。

ApplicationDbContext.cs

public class ApplicationDbContext : IdentityDbContext
{

    private readonly IHttpContextAccessor _contextAccessor;

    public ApplicationDbContext(DbContextOptions<ApplicationDbContext> options, IHttpContextAccessor contextAccessor)
    : base(options)
    {
        _contextAccessor = contextAccessor;
    }

    public DbSet<TenantPerson> TenantPersons { get; set; }
    public DbSet<Customer> Customers { get; set; }
    public DbSet<ApplicationUser> Users { get; set; }

    public virtual Guid CurrentTenantId => GetCurrentTenantId();

    public bool ShouldApplyTenantFilter()
    {
        // Replace the logic below with your actual superuser check
        var isSuperUser = _contextAccessor.HttpContext.User.IsInRole("Super");
        return !isSuperUser;
    }

    private Guid GetCurrentTenantId()
    {
        string cUserName = _contextAccessor.HttpContext.User.Identity?.Name ?? string.Empty;
        ApplicationUser sUser = Users.FirstOrDefault(u => u.UserName == cUserName);
        return sUser?.TenantId ?? Guid.Empty;
    }

    protected override void OnModelCreating(ModelBuilder modelBuilder)
    {
        base.OnModelCreating(modelBuilder);
        //Logic of verifying if run into the filter
        modelBuilder.Entity<TenantPerson>().HasQueryFilter(e => ShouldApplyTenantFilter() ? e.TenantId == CurrentTenantId : true);
        modelBuilder.Entity<Customer>().HasQueryFilter(e => ShouldApplyTenantFilter() ? e.TenantId == CurrentTenantId : true);
    }
}

当超级用户登录时,它会绕过过滤器,获取所有记录。

当普通用户登录时,过滤器将起作用。

© www.soinside.com 2019 - 2024. All rights reserved.