Skip to content
Akash Kava edited this page Apr 13, 2022 · 11 revisions

Entity Access Control for Entity Framework Core

Entity Access Control is feature rich powerful alternative to GraphQL and OData. Entity Framework Core's features provide various ways to query database and create easy to read projections. We wanted clients such as browser or mobile phone apps to directly invoke these features without compromising security.

So Entity Access Control introduces set of Events and ACL Rules which allow you to control information sent to client. Just like how firewalls allow you to control your network traffic.

Features

  1. Conditional Lambda Expressions for Global Filters
  2. Global Filters with Navigation Properties
  3. Global Filters for Update and Delete
  4. Asynchronous Events
  5. Auditable, implement your own IAuditContext
  6. REST API Controller for accessing Entities with Security

Setup Secure Repository

[DIRegister(ServiceLifetime.Singleton)]
public class AppDbContextEvents: DbContextEvents<AppDbContext> {

    public AppDbContextEvents() {
       Register<ProjectEvents>();
       Register<EmailEvents>();
    }

}

/// AppDbContext is your class derived from DbContext

public class ProjectEvents: DbEntityEvents<Project> {

    // You can use constructor injection
    public ProjectEvents(
        AppDbContext db,
        IUserInfo user)
    {
        this.user = user;
        this.db = db;
    }

     public override IQueryContext<Project> Filter(IQueryContext<Project> q) {
            if (user.IsAuthenticated) {
                if(user.IsAdmin) {
                    // admin can access everything
                    return q;
                }
                if(user.IsManager) {
                    return q.Where(x => x.AccountID == user.AccountID || x.ManagerID == user.AccountID);
                }
                return q.Where(x => x.AccountID == user.AccountID);
            }
            throw new EntityAccessException("Access to Project denied");
        }

        public override Task Inserting(Project entity) {
            // while inserting
            // we want to associate AccountID to currently logged in user id if it is not admin..
            if(user.IsAdmin) {
                if (entity.AccountID == 0)
                    entity.AccountID = user.AccountID;
            } else {
                entity.AccountID = user.AccountID;
            }
            return Task.CompletedTask;
         }

    }

    public class EmailEvents: DbEntityEvents<Email> {
         private readonly AppDbContext db;
         private readonly IEmailService emailService
         public EmailEvents(AppDbContext db, IEmailService emailService) {
             this.db = db;
             this.emailService = emailService;
         }

         public override IQueryContext<Email> Filter(IQueryContext<Email> q) {
            if (user.IsAuthenticated) {
                if(user.IsAdmin) {
                    // admin can access everything
                    return q;
                }
                if(user.IsManager) {
                    return q.Where(x => x.Project.AccountID == user.AccountID
                       || x.Project.ManagerID == user.AccountID);
                }
                return q.Where(x => x.Project.AccountID == user.AccountID);
            }
            throw new EntityAccessException("Access to Email denied");
        }

         public async Task InsertedAsync(Email email) {
             var msg = new MimeMessage();
             // .. set properties ..
             try {
                await emailService.SendAsync(msg);
             } catch (Exception ex) {
                email.Error = ex.ToString();
             }
             email.DateSend = DateTime.UtcNow;
             await db.SaveChangesWithoutEvents();
         }
    }

}

Setup Controller

   [Route("api/entity")]
   public class EntityController: BaseEntityController<AppDbContext> {

       public EntityController(AppDbContext db): base(db) {
       }
  
   }

This will expose the endpoint to access all entities. To enforce authentication, you can create rules in SecurityRules class. You can throw an exception if user is not logged in or does not have sufficient permissions.

Clone this wiki locally