Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

How to add parameter/s to every query in DbContext in EF Core to secure tenants data?

I am building sort of multi tenant application with shared tables using .NET Core 2.0 and EF Core.

I am also using generic repository together with Unit of Work if it matters.

I want to make it properly secured and also avoid repeating the logic, so I think if it's possible to somehow modify the DbContext which I am using to for every find operation add something like: entity => entity.tenantId == userContext.tenantId.

I also have to ensure that while creating the correct tenantId is applied and do not authorize update of other tenant property, but so far this logic is included in Service Layer - correct me if I am wrong with this approach?

The IUserContext is defined in Domain abstractions and the application layer implements it differently (API or Web App), but I am not sure if it is not code smell/anti pattern when data layer is doing this kind of logic? ( I am afraid it is).

Should this logic go to the Services (it will then have to be repeated many times which is not good idea I think), DbContext or should I adjust the repository in some way?

like image 609
Marek Urbanowicz Avatar asked Jan 01 '26 08:01

Marek Urbanowicz


1 Answers

So what you want is that if someone would write some Linq statement like this

var result = myDbcontext.myDbSet.SomeLinq(...)

It would internally be like

var result = myDbContext.myDbSet
    .Where(entity => entity.tenantId == userContext.tenantId)
    .SomeLinq(...)

So what you should do, is that when users think that they access myDbContext.myDbSet, they actually get the subset where tenantId == userContext.tenantId

I think the neat solution would be to create a class that exposes an IQueryable for every DbSet in your DbContext and hides the actual DbContext.

Something like this:

class MyOriginalDbContext : DbContext
{
    public DbSet<Student> Students {get; set;}
    public DbSet<Teacher> Teachers {get; set;}
    public DbSet<ClassRoom> ClassRooms {get; set;}
    ...
}

public MyLimitedContext : IDisposable
{
    // to be filled in constructor
    private readonly MyOriginalDbcontext dbContext = ...
    private readonly int tenantId = ...

    IQueryable<Student> Students
    {
        get
        {
            return this.dbContext.Students
                .Where(student => student.tenantId == tenantId);
        }
    }

    IQueryable<Student> Teachers
    {
        get
        {
            return this.dbContext.Teachers
                .Where(teacher => teacher.tenantId == tenantId);
        }
    }
    ...

Users won't notice the difference:

using (var dbContext = new MyLimitedContext(...))
{
     var TeachersWithTheirStudents = dbContext.Teachers
         .Join(dbContext.Student)
         .GroupBy(teacher => teacher.Id,
            ...
}
like image 125
Harald Coppoolse Avatar answered Jan 02 '26 21:01

Harald Coppoolse



Donate For Us

If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!