Shoe Diamente
Shoe Diamente

Reputation: 794

Is it possible to implement an authorised DbContext?

Given a DbContext and a ClientContext (custom session data about the user) is it possible create a DbContext that is "authorised": where only a subset of the rows on each "table" is available?

With an authorised DbContext I'm trying to have a central row-level authorisation implementation.

I've researched it a bit and the only way to filter out a DbSet would be to use something like Queryable.Where but that returns an IQueryable<T> and there doesn't seem to be a way to return a filtered DbSet<T> (except maybe for global queries that you can setup in Startup but they don't have access to injected dependencies like ClientContext).

Is it possible to define DbSet<T> authorisation filters via an injected scoped dependency like ClientContext?

Upvotes: 0

Views: 90

Answers (2)

Dejan Janjušević
Dejan Janjušević

Reputation: 3230

There are model-level query filters: https://learn.microsoft.com/en-us/ef/core/what-is-new/ef-core-2.0#model-level-query-filters

From the link:

This feature allows LINQ query predicates (a boolean expression typically passed to the LINQ Where query operator) to be defined directly on Entity Types in the metadata model (usually in OnModelCreating). Such filters are automatically applied to any LINQ queries involving those Entity Types, including Entity Types referenced indirectly, such as through the use of Include or direct navigation property references.

Example from the link:

public class BloggingContext : DbContext
{
    public DbSet<Blog> Blogs { get; set; }
    public DbSet<Post> Posts { get; set; }

    public int TenantId { get; set; }

    protected override void OnModelCreating(ModelBuilder modelBuilder)
    {
        modelBuilder.Entity<Post>().HasQueryFilter(
            p => !p.IsDeleted
            && p.TenantId == this.TenantId);
    }
}

You can use this for simple scenarios. You define an instance property in your DbContext and in OnModelCreating you specify HasQueryFilter on any entity you want to filter. The property is an instance property, so if you have a scoped DbContext, the correct property value from that request would be used, which is handy if you want to filter by something from your UserContext. I have personally never tried this so I don't know how complex it allows your implementation to be, but you can play with it.

Upvotes: 2

sommmen
sommmen

Reputation: 7618

I'm not sure about EF and EF core, but we abstract the DbContext away into functional specific 'logic' blocks.

e.g:

class DbContext()
{
   public DbSet<PeopleEntity> peoples;
}
class PeopleLogic()
{
   DbContext _context;

   PeopleLogic(DbContext context)
   {
       _context = context;
   }

   IEnumerable GetAllPeoples()
   {
      // create context,
      // apply filters
      // return result
   }
}

We ofcourse have a base for simple CRUD operations;

public void AddOrUpdate(){
            lock (SyncDatabaseWriteObject)
            {
                try
                {
                    using (var context = CreateContext())
                    {
                        //insert the entity and add it to the db context
                        context.Set<TEntity>().AddOrUpdate((TEntity)entity);
                        context.SaveChanges();
                    }

                    return entity;
                }
                catch (Exception ex)
                {
                    throw new DatabaseAccessException("Error occured while getting saving.", ex);
                }
            }
}

And instead of passing the dbcontext around, we pass around logics.

e.g. we seperate the logic for the database and the access to the database into 2 seperate projects, the business layer then only uses the dbAccess layer.

Upvotes: 0

Related Questions