Three Elements of Authorization

Subject, resource, and operation. For example: a user (subject) reading and writing (operations) a file (resource). The relationship among subject, resource, and operation is called an authorization relation. Based on this relationship, we can abstract it into a triple (subject, resource, operation), and this triple is the basic unit of authorization.

Role-Based Access Control

Role-Based Access Control (RBAC) is an access control model that manages users’ access to resources through roles. A role is a collection of permissions, and users acquire the corresponding permissions by being assigned roles. The role-based access control model is simple and easy to use. There are other access control models as well, such as Attribute-Based Access Control (ABAC).

role-based-access-control

Permission Access Control List

An Access Control List (ACL) is a permission control model used to manage users’ access to resources. An ACL controls users’ operation permissions on a resource by defining an access control list for each resource.

ACL Storage Design

public class PermissionGranted
{
    public int Id { get; set; }

    public int RoleId { get; set; }

    public required string PermissionName { get; set; }

    public string? ResourceType { get; set; }

    public string? ResourceId { get; set; }
}

Configuring the ACL Entity in the DbContext

public void Configure(EntityTypeBuilder<PermissionGranted> builder)
{
    builder.ToTable("PermissionGranted");

    builder.Property(x => x.Id);
    builder.Property(x => x.PermissionName).HasMaxLength(64);
    builder.Property(x => x.ResourceType).HasMaxLength(16);
    builder.Property(x => x.ResourceId).HasMaxLength(32);

    builder.HasOne<Role>().WithMany().HasForeignKey(x => x.RoleId).IsRequired();

    builder.HasIndex(x => new { x.RoleId, x.PermissionName, x.ResourceType, x.ResourceId }).IsUnique();
}

Permission ACL Storage Design

public class PermissionGranted
{
    public int Id { get; set; }

    public int RoleId { get; set; }

    public required string PermissionName { get; set; }

    public string? ResourceType { get; set; }

    public string? ResourceId { get; set; }
}

Configuring the ACL Entity in the DbContext

public void Configure(EntityTypeBuilder<PermissionGranted> builder)
{
    builder.ToTable("PermissionGranted");

    builder.Property(x => x.Id);
    builder.Property(x => x.PermissionName).HasMaxLength(64);
    builder.Property(x => x.ResourceType).HasMaxLength(16);
    builder.Property(x => x.ResourceId).HasMaxLength(32);

    builder.HasOne<Role>().WithMany().HasForeignKey(x => x.RoleId).IsRequired();

    builder.HasIndex(x => new { x.RoleId, x.PermissionName, x.ResourceType, x.ResourceId }).IsUnique();
}

Designing a Permission Checker

public interface IPermissionChecker
{
    Task<bool> IsGrantedAsync(string name, string? resourceType = null, string? resourceId = null);

    Task<bool> IsGrantedAsync(ClaimsPrincipal claimsPrincipal, string name, string? resourceType = null, string? resourceId = null);
}

Implementing the Abstract Permission Checker

Iterate over the permissions of each role; if any role has the permission, return true to indicate that authorization is granted.


public abstract class PermissionChecker : IPermissionChecker
{
    public abstract Task<bool> IsGrantedAsync(string name, string? resourceType = null, string? resourceId = null);

    public abstract Task<bool> IsGrantedAsync(ClaimsPrincipal claimsPrincipal, string name, string? resourceType = null, string? resourceId = null);
}

Implementing Permission Checkers

The ACL list is stored in the database. You can implement a local permission checker via the DbContext, or a remote permission checker via HttpClient.

permission-checker

public class RemotePermissionChecker: PermissionChecker

public class LocalPermissionChecker: PermissionChecker