GranularPermissions.Mvc 1.0.5

dotnet add package GranularPermissions.Mvc --version 1.0.5                
NuGet\Install-Package GranularPermissions.Mvc -Version 1.0.5                
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="GranularPermissions.Mvc" Version="1.0.5" />                
For projects that support PackageReference, copy this XML node into the project file to reference the package.
paket add GranularPermissions.Mvc --version 1.0.5                
#r "nuget: GranularPermissions.Mvc, 1.0.5"                
#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 GranularPermissions.Mvc as a Cake Addin
#addin nuget:?package=GranularPermissions.Mvc&version=1.0.5

// Install GranularPermissions.Mvc as a Cake Tool
#tool nuget:?package=GranularPermissions.Mvc&version=1.0.5                

GranularPermissions

The world's most over-engineered permissions system.

Supports net5.0.

Features

  • Generic nodes (not tied to a resource)
  • Resource-bound nodes, evaluated with respect to a particular resource marked IPermissionManaged
  • Simple chains allow/deny grants with respect to an identifier (e.g. user ID)
    • Evaluated one after each other in order of provided Index to come up with a final pass/fail answer
  • Default disallow policy if no matching grants
  • Complex resource-bound grants using a DSL which is compiled into AST transformation at startup and then evaluated at runtime
  • AspNetMvc (Core) extensions for registering services, logging permissions for debugging etc

Terminology

  • Node: A permission entry. Something that can be done that needs its access controlled.
    • These have a key which by convention is represented as EntityName.[SubGroup].Action.
  • Grants: Allow/deny rules that cover a single permission node. Can be typed as Generic or ResourceBound.
  • ResourceBound grant: Evaluation of the grant must be performed in the context pf a resource.
    • For example, Product.Edit probably requires the product in question to be considered when checking to see if the user should have the permission or not.
  • Generic grant: Does not require a resource when evaluating. E.g. Product.Create.
  • Chain: grants are organised into chains and have identifiers within a chain. When nodes are checked, all grants that match the identifier in the specified chain are considered.
    • Example chains: Groups or Users
    • Example identifiers: Group ID or User ID
  • Condition: An additional requirement on top of a ResourceBound grant. Written in a DSL for this permissions system.

DSL examples

new Cat
{
    Breed = CatBreed.Bengal,
    Age = 10,
    Name = "Felix"
}

You could write some conditions for a resource bound grant on node Cat.Adopt:

resource.Name == "Felix" || resource.Age < 10

resource.Age != 5

Supported operators: <=, >=, <, >, &&, ||, ., ==, !=, !, ~= (regex)

Usage

From an ASP.NET MVC Core project, in ConfigureServices in Startup.cs:

services.AddScoped<IPermissionGrantProvider, SomePermissionGrantProvider>();
services.AddGranularPermissions(typeof(Permissions));

SomePermissionGrantProvider must implement IPermissionGrantProvider. Its role is to return all grants (which implement IPermissionGrantSerialized) persisted in the system. You may wish to retrieve them from a database, for instance.

The Permissions class must define all permission nodes you wish to exist in your project:

public static class Permissions
{
    public static class Product
    {
        public static readonly ResourceNode<ProductModel> View =
            new ResourceNode<ProductModel>("Product.View", "View an individual product");
        
        public static readonly GenericNode Create =
            new GenericNode("Product.Create", "Create a product");
        
        public static readonly ResourceNode<ProductModel> Buy =
            new ResourceNode<ProductModel>("Product.Purchase", "Purchase an individual product");
    }
    
    public static class Cat
    {
        public static readonly ResourceNode<ProductModel> Pet =
            new ResourceNode<ProductModel>("Cat.Pet", "Pet the cat without being bitten/scratched");
        
        public static readonly GenericNode Adopt =
            new GenericNode("Cat.Adopt", "Be adopted by a cat");
    }
}

To check a permission within a specified chain, call GetResultUsingChain on the IPermissionsService instance which will return a PermissionResult (Unset, Allow or Deny).

It is possible to reload the grants at runtime by calling ReplaceAllGrants with a new IEnumerable<IPermissionGrantSerialized>.

Example use-case

A web application has two groups which have different levels of access. Group 1 is for "Everyone" and includes a basic level of permission grants. Group 2, "Administrators" includes a higher level of access for a subset of application users who are in this group.

Within GranularPermissions, you'd have:

  • One chain, "Groups"
  • A set of IPermissionGrantSerialized for the "Groups" chain with Identifier=1. These will apply to all users in the Everyone group.
  • A set of IPermissionGrantSerialized for the "Groups" chain with Identifier=2. These will apply to all users in the Administrators group.
  • Some IPermissionGrantProvider which returns the aforementioned IPermissionGrantSerialized instances from DB

A simple permissions checker would call GetResultUsingChain("Groups", Permissions.Node.ToCheck, groupId) for each of the user's groups and then aggregate the results to come up with a final allowed/denied result. For example, the administrators group may be considered a higher priority than the everyone group.

For resource bound nodes, it's simply a case of passing an additional argument GetResultUsingChain("Groups", Permissions.Node.ToCheckWithResource, groupId, resourceToCheckAgainst).

TODO

  • Add ability to register DSL functions/identifiers
Product Compatible and additional computed target framework versions.
.NET net5.0 is compatible.  net5.0-windows was computed.  net6.0 was computed.  net6.0-android was computed.  net6.0-ios was computed.  net6.0-maccatalyst was computed.  net6.0-macos was computed.  net6.0-tvos was computed.  net6.0-windows was computed.  net7.0 was computed.  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.5 1,220 5/16/2021
1.0.1-beta 1,511 6/14/2019
1.0.0-beta 1,632 8/23/2018
0.0.4-alpha 989 3/21/2018
0.0.3-alpha 787 3/20/2018
0.0.2-alpha 850 3/20/2018