Pbac.AspNetCore 1.0.1

dotnet add package Pbac.AspNetCore --version 1.0.1
NuGet\Install-Package Pbac.AspNetCore -Version 1.0.1
This command is intended to be used within the Package Manager Console in Visual Studio, as it uses the NuGet module's version of Install-Package.
<PackageReference Include="Pbac.AspNetCore" Version="1.0.1" />
For projects that support PackageReference, copy this XML node into the project file to reference the package.
paket add Pbac.AspNetCore --version 1.0.1
#r "nuget: Pbac.AspNetCore, 1.0.1"
#r directive can be used in F# Interactive and Polyglot Notebooks. Copy this into the interactive tool or source code of the script to reference the package.
// Install Pbac.AspNetCore as a Cake Addin
#addin nuget:?package=Pbac.AspNetCore&version=1.0.1

// Install Pbac.AspNetCore as a Cake Tool
#tool nuget:?package=Pbac.AspNetCore&version=1.0.1

Pbac.AspNetCore

NuGet

Description

A helper library for implementing the permission/attribute based access control (ABAC) with JSON web tokens using the ASP.NET Core's policy-based access control (PBAC).

Usage

  1. Create the permissions enum:

    public enum Permissions
    {
        Create = 0,
        Read = 1,
        Update = 2,
        Delete = 3,
    }
    

    Note that having the exact underlying value for each permission is important - messing it up will mess the permissions stored!

    Also, only enums with the int as an underlying type (the default) are currently supported.

  2. Set the user permission values inside your identity provider using the PermissionSet:

    user.Claims[ClaimNames.Permissions] = new PermissionSet<Permissions>(new[]
        {
            Permissions.Create,
            Permissions.Read,
        })
        .ToCompactString();
    
  3. Add the permission policy requirement handler with the same claim name as above:

    services.AddPermissionBasedAuthorization<Permissions>(options =>
        options.PermissionsClaimName = ClaimNames.Permissions);
    

    Note that if you re-define the authorization DefaultPolicy in your app then the code above should be inserted after your logic to make the permission policies inherit the new default policy.

  4. (Optionally) Inherit the AuthorizePermission<T> with the specific permissions enum type:

    public class AuthorizePermissionAttribute : AuthorizePermissionAttribute<Permissions>
    {
        public AuthorizePermissionAttribute(Permissions permission)
            : base(permission)
        { }
    }
    

    Or create an extension method if using the Minimal APIs:

    public static TBuilder RequirePermission<TBuilder>(this TBuilder builder, Permissions permission)
        where TBuilder : IEndpointConventionBuilder
    {
        if (builder == null)
            throw new ArgumentNullException(nameof(builder));
    
        return builder.RequireAuthorization(new AuthorizePermissionAttribute<Permissions>(permission));
    }
    
  5. Decorate the controllers/endpoints with the AuthorizePermission<T> (or the one created above) attribute:

    [HttpGet]
    [Authorize(Permissions.Read)]
    public IActionResult Get()
        => Ok();
    

    Or if using the minimal APIs:

    app.MapGet("/", () => Results.Ok())
    .RequirePermission(Permissions.Read);
    

Implementation

The permission set is being serialized into a string as a hex number where N-th bit represents the presence of a permission with the underlying value of N.

For example, consider the following permission enum:

public enum Permissions
{
    Create = 0,
    Read = 1,
    Update = 2,
    Delete = 3,
    Manage = 4,
}

Then, the binary representation of a set containing all permissions above would look like this:

   1    F - permission string
┌┬┬┤ ┌┬┬┤
0001 1111 - permission bit values
││││ ││││
7654 3210 - bit positions
└┬┘│ ││││
 │ │ │││└ Create
 │ │ ││└ Read
 │ │ │└ Update
 │ │ └ Delete
 │ └ Manage
 └ Not used

For deserialization, the above is done in the reversed order.

Also, because based on the enum value we can calculate the specific bit position we need to check, we don't need to deserialize the entire string when we verify a single permission.

Benchmarks

  • Checking a single permission value from the compact string:

    Method Mean Error StdDev Allocated
    PermissionSet_HasPermission 74.17 ns 1.382 ns 1.225 ns -
  • Validating a user has a required permission in their claim list:

    Method Mean Error StdDev Allocated
    PermissionAuthorizationHandlerBenchmark_HandleRequirementAsync 359.4 ns 6.79 ns 13.25 ns 144 B
Product Compatible and additional computed target framework versions.
.NET net7.0 is compatible.  net7.0-android was computed.  net7.0-ios was computed.  net7.0-maccatalyst was computed.  net7.0-macos was computed.  net7.0-tvos was computed.  net7.0-windows was computed.  net8.0 was computed.  net8.0-android was computed.  net8.0-browser was computed.  net8.0-ios was computed.  net8.0-maccatalyst was computed.  net8.0-macos was computed.  net8.0-tvos was computed.  net8.0-windows was computed. 
Compatible target framework(s)
Included target framework(s) (in package)
Learn more about Target Frameworks and .NET Standard.

NuGet packages

This package is not used by any NuGet packages.

GitHub repositories

This package is not used by any popular GitHub repositories.

Version Downloads Last updated
1.0.1 147 7/16/2023
1.0.0 152 7/16/2023