Skip to content
Akash Kava edited this page Jan 15, 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. Lambda Expressions for Global Filters
  2. Global Filters with Navigation Properties
  3. Global Filters for Insert/Update and Delete
  4. Auditable, implement your own IAuditContext
  5. REST API Controller for accessing Entities with Security

Setup Secure Repository

[DIRegister(ServiceLifetime.Singleton)]
public class SecurityRules: BaseSecurityRules<IUserInfo> {

    public SecurityRules() {

        // User can access their own projects
        // Manager can access project created created by them or if they are set as Manager for
        // that project
        SetAllFilter<Project>((q, user) => {
            if(user.IsManager) {
                return q.Where(p => p.AccountID == user.AcccountID || p.ManagerID == user.AccountID);
            }
            return q.Where(p => p.AccountID == user.AccountID);
        });
    }

}

/// AppDbContext is your class derived from DbContext
/// IUserInfo is the current user logged in, you can supply your own interface

[DIRegister(ServiceLifetime.Scoped)]
public class SecureRepository: BaseSecureRepository<AppDbContext, IUserInfo> {

    public SecureRepository(
        AppDbContext db,
        IUserInfo user,
        SecurityRules rules)
        : base(db, user, rules)
    {

    }

    public bool SecurityDisabled => user.IsAdmin;

}

Setup Entity Events

public class AppDbContext: BaseDbContext<AppDbContext> {

    public AppDbContext(
        DbContextOptions<AppDbContext> options,
        AppEvents events,
        IServiceProvider services
    )
        : base(options, events, services)
    {
    }
}

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

    public AppEvents() {

        Register<ProjectEvents>();
        Register<EmailEvents>();

    }

    public class ProjectEvents: DbEntityEvents<Project> {

         private readonly IUserInfo user;
         public ProjectEvents(IUserInfo user) {
             this.user = user;
         }
 
         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;
            }
         }

    }

    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 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<SecureRepository> {

       public EntityController(SecureRepository 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