Privileged 1.2.0

There is a newer version of this package available.
See the version list below for details.
dotnet add package Privileged --version 1.2.0
                    
NuGet\Install-Package Privileged -Version 1.2.0
                    
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="Privileged" Version="1.2.0" />
                    
For projects that support PackageReference, copy this XML node into the project file to reference the package.
<PackageVersion Include="Privileged" Version="1.2.0" />
                    
Directory.Packages.props
<PackageReference Include="Privileged" />
                    
Project file
For projects that support Central Package Management (CPM), copy this XML node into the solution Directory.Packages.props file to version the package.
paket add Privileged --version 1.2.0
                    
#r "nuget: Privileged, 1.2.0"
                    
#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.
#:package Privileged@1.2.0
                    
#:package directive can be used in C# file-based apps starting in .NET 10 preview 4. Copy this into a .cs file before any lines of code to reference the package.
#addin nuget:?package=Privileged&version=1.2.0
                    
Install as a Cake Addin
#tool nuget:?package=Privileged&version=1.2.0
                    
Install as a Cake Tool

Privileged

Privileged is an authorization library for restricting resources by action, subject and qualifiers. It's designed to be incrementally adoptable and can easily scale between a simple claim based and fully featured subject and action based authorization. It makes it easy to manage and share permissions across UI components, API services, and database queries.

Build Project License Coverage Status

Package Version Description
Privileged NuGet Core authorization library for rule-based permissions
Privileged.Components NuGet Blazor components for privilege-aware UI elements

Installation

Install the core package via NuGet:

dotnet add package Privileged

For Blazor applications, also install the components package:

dotnet add package Privileged.Components

Features

  • Versatile An incrementally adoptable and can easily scale between a simple claim based and fully featured subject and attribute based authorization.
  • Isomorphic Can be used on front-end and back-end and complementary packages make integration with Frontend and Backend effortless
  • Declarative Thanks to declarative rules, you can serialize and share permissions between UI and API or microservices
  • Rule-based Support for both allow and forbid rules with rule precedence
  • Aliases Create reusable aliases for actions, subjects, and qualifiers
  • Qualifiers Fine-grained control with field-level permissions
  • Blazor Integration Ready-to-use components for conditional rendering and privilege-aware form inputs
  • Performance Optimized Efficient rule evaluation and matching algorithms

General

Privileged operates on rules for what a user can actually do in the application. A rule itself depends on the 3 parameters:

  1. Action Describes what user can actually do in the app. User action is a word (usually a verb) which depends on the business logic (e.g., update, read). Very often it will be a list of words from CRUD - create, read, update and delete.
  2. Subject The subject which you want to check user action on. Usually this is a business (or domain) entity name (e.g., Subscription, Post, User).
  3. Qualifiers Can be used to restrict user action only to matched subject's qualifiers (e.g., to allow moderator to update published field of Post but not update description or title)

Basic Usage

Simple Rules

Using builder to create basic allow and forbid rules:

var context = new PrivilegeBuilder()
    .Allow("read", "Post")
    .Allow("write", "User")
    .Forbid("delete", "User")
    .Build();

// Check permissions
bool canReadPost = context.Allowed("read", "Post");      // true
bool canWriteUser = context.Allowed("write", "User");    // true
bool canDeleteUser = context.Allowed("delete", "User");  // false
bool canReadUser = context.Allowed("read", "User");      // false (not explicitly allowed)

Wildcard Rules

Use wildcards to allow all actions on a subject or an action on all subjects:

var context = new PrivilegeBuilder()
    .Allow("test", PrivilegeSubjects.All)    // Allow 'test' action on any subject
    .Allow(PrivilegeActions.All, "Post")     // Allow any action on 'Post'
    .Forbid("publish", "Post")               // Forbid overrides allow
    .Build();

context.Allowed("read", "Post").Should().BeTrue();
context.Allowed("update", "Post").Should().BeTrue();
context.Allowed("archive", "Post").Should().BeTrue();
context.Allowed("read", "User").Should().BeFalse();
context.Allowed("delete", "Post").Should().BeTrue();
context.Allowed("publish", "Post").Should().BeFalse();  // Forbid takes precedence
context.Allowed("test", "User").Should().BeTrue();
context.Allowed("test", "Post").Should().BeTrue();

Using Qualifiers

Qualifiers provide field-level or fine-grained permissions:

var context = new PrivilegeBuilder()
    .Allow("read", "Post", ["title", "id"])   // Only allow reading specific fields
    .Allow("read", "User")                     // Allow reading all User fields
    .Build();

// Post permissions with qualifiers
context.Allowed("read", "Post").Should().BeTrue();           // General permission
context.Allowed("read", "Post", "id").Should().BeTrue();     // Specific field allowed
context.Allowed("read", "Post", "title").Should().BeTrue();  // Specific field allowed
context.Allowed("read", "Post", "content").Should().BeFalse(); // Field not allowed

// User permissions without qualifiers
context.Allowed("read", "User").Should().BeTrue();           // All fields allowed
context.Allowed("read", "User", "id").Should().BeTrue();     // Any field allowed

Advanced Features

Multiple Actions and Subjects

Use extension methods for bulk rule creation:

var context = new PrivilegeBuilder()
    .Allow(["read", "update"], "Post")                    // Multiple actions, single subject
    .Allow("read", ["Post", "User"])                      // Single action, multiple subjects
    .Allow(["create", "read"], ["Post", "Comment"])       // Multiple actions and subjects
    .Build();

context.Allowed("read", "Post").Should().BeTrue();
context.Allowed("update", "Post").Should().BeTrue();
context.Allowed("read", "User").Should().BeTrue();
context.Allowed("create", "Comment").Should().BeTrue();

Aliases

Create reusable aliases for common groupings:

var context = new PrivilegeBuilder()
    .Alias("Manage", ["Create", "Update", "Delete"], PrivilegeMatch.Action)
    .Allow("Manage", "Project")                         // Allows all actions defined in the "Manage" alias
    .Allow("Read", "User")                              // Allows reading User
    .Allow("Update", "User", ["Profile", "Settings"])   // Allows updating User's Profile and Settings
    .Forbid("Delete", "User")                           // Forbids deleting User
    .Build();

bool canCreateProject = context.Allowed("Create", "Project");           // true
bool canReadUser = context.Allowed("Read", "User");                     // true
bool canUpdateProfile = context.Allowed("Update", "User", "Profile");   // true
bool canUpdatePassword = context.Allowed("Update", "User", "Password"); // false
bool canDeleteUser = context.Allowed("Delete", "User");                 // false

Qualifier Aliases

Aliases can also be used for qualifiers:

var context = new PrivilegeBuilder()
    .Alias("PublicFields", ["title", "summary", "author"], PrivilegeMatch.Qualifier)
    .Allow("read", "Post", ["PublicFields"])
    .Build();

context.Allowed("read", "Post", "title").Should().BeTrue();
context.Allowed("read", "Post", "summary").Should().BeTrue();
context.Allowed("read", "Post", "content").Should().BeFalse();

Rule Evaluation

Rule Precedence

Rules are evaluated in the order they are defined, with more specific rules taking precedence:

  1. Forbid rules always take precedence over allow rules when both match
  2. Rules are matched based on exact string comparison (case-insensitive by default)
  3. Wildcard rules (PrivilegeActions.All, PrivilegeSubjects.All) match any value
  4. Alias expansion happens during rule matching

String Comparison

By default, rule matching uses StringComparer.InvariantCultureIgnoreCase. You can customize this:

var context = new PrivilegeContext(rules, aliases, StringComparer.Ordinal);

API Reference

PrivilegeBuilder

  • Allow(string action, string subject, IEnumerable<string>? qualifiers = null) - Add an allow rule
  • Forbid(string action, string subject, IEnumerable<string>? qualifiers = null) - Add a forbid rule
  • Alias(string alias, IEnumerable<string> values, PrivilegeMatch type) - Create an alias
  • Build() - Create the PrivilegeContext

PrivilegeContext

  • Allowed(string? action, string? subject, string? qualifier = null) - Check if action is allowed
  • Forbidden(string? action, string? subject, string? qualifier = null) - Check if action is forbidden
  • MatchRules(string? action, string? subject, string? qualifier = null) - Get matching rules

Extension Methods

  • Allow(IEnumerable<string> actions, string subject, ...) - Allow multiple actions on single subject
  • Allow(string action, IEnumerable<string> subjects, ...) - Allow single action on multiple subjects
  • Allow(IEnumerable<string> actions, IEnumerable<string> subjects, ...) - Allow multiple actions on multiple subjects
  • Similar Forbid overloads for forbid rules

Blazor Integration

The Privileged.Components package provides components for conditional rendering based on permissions.

Setup

First, add the privilege context as a cascading value in your app:

// Program.cs or similar
       
// Create privilege rules
var privilegeContext = new PrivilegeBuilder()
    .Allow("read", "Post")
    .Allow("edit", "Post", ["title", "content"])
    .Allow("delete", "Post")
    .Build();

// Make available as cascading parameter
builder.Services.AddCascadingValue(_ => privilegeContext);

PrivilegeContextView Component

For scenarios where you need to load the privilege context asynchronously, use the PrivilegeContextView component:

<PrivilegeContextView>
    <Loading>
        <div class="spinner-border" role="status">
            <span class="visually-hidden">Loading permissions...</span>
        </div>
    </Loading>
    <Loaded>
        <PrivilegedView Action="read" Subject="Post">
            <p>Content loaded with permissions!</p>
        </PrivilegedView>
    </Loaded>
</PrivilegeContextView>

PrivilegeContextView component requires an IPrivilegeContextProvider service to be registered:

// In Program.cs
builder.Services.AddScoped<IPrivilegeContextProvider, YourPrivilegeContextProvider>();
Using PrivilegeContextView in a Layout

The most common pattern is to wrap your entire layout with PrivilegeContextView to ensure permissions are loaded before any page content is rendered:

@* MainLayout.razor *@
@inherits LayoutComponentBase

<div class="page">
    <div class="sidebar">
        <NavMenu />
    </div>

    <main>
        <PrivilegeContextView>
            @Body
        </PrivilegeContextView>
    </main>
</div>

With this approach, all pages will automatically have access to the privilege context, and users will see a loading state until permissions are loaded. Your navigation menu can also use privilege checking:

IPrivilegeContextProvider Implementation

The IPrivilegeContextProvider interface allows you to load privilege contexts asynchronously, which is useful for scenarios where permissions are loaded from external sources like APIs, databases, or authentication systems.

Here's how to implement a custom provider:

public class RemotePrivilegeContextProvider : IPrivilegeContextProvider
{
    private readonly HttpClient _httpClient;
    private readonly ILogger<RemotePrivilegeContextProvider> _logger;

    public RemotePrivilegeContextProvider(
        HttpClient httpClient, 
        ILogger<RemotePrivilegeContextProvider> logger)
    {
        _httpClient = httpClient;
        _logger = logger;
    }

    public async ValueTask<PrivilegeContext> GetContextAsync()
    {
        try
        {
            // Load privilege model from API
            var privilegeModel = await _httpClient.GetFromJsonAsync<PrivilegeModel>("/api/user/privileges");
            return new PrivilegeContext(privilegeModel)
        }
        catch (Exception ex)
        {
            _logger.LogError(ex, "Failed to load user privileges");
            
            // Return minimal context with basic read permissions as fallback
            return new PrivilegeBuilder()
                .Allow("read", "Public")
                .Build();
        }
    }
}

// Register the provider in Program.cs
builder.Services.AddScoped<IPrivilegeContextProvider, RemotePrivilegeContextProvider>();

For simpler scenarios, you can create a static provider:

public class StaticPrivilegeContextProvider : IPrivilegeContextProvider
{
    public ValueTask<PrivilegeContext> GetContextAsync()
    {
        var context = new PrivilegeBuilder()
            .Allow("read", "Post")
            .Allow("write", "Post", ["title", "content"])
            .Allow("delete", "Post")
            .Forbid("publish", "Post") // Override specific action
            .Build();
        
        return ValueTask.FromResult(context);
    }
}

PrivilegedView Component

Use the PrivilegedView component to conditionally render content:

@* Basic usage with ChildContent *@
<PrivilegedView Action="read" Subject="Post">
    <p>You can read posts!</p>
</PrivilegedView>

@* With both allowed and forbidden content *@
<PrivilegedView Action="delete" Subject="Post"
               Allowed="@allowedContent"
               Forbidden="@forbiddenContent" />

@* With field-level permissions *@
<PrivilegedView Action="edit" Subject="Post" Field="title">
    <input type="text" placeholder="Edit title" />
</PrivilegedView>

@code {
    private RenderFragment<PrivilegeContext> allowedContent = (context) =>
        @<button class="btn btn-danger">Delete Post</button>;

    private RenderFragment<PrivilegeContext> forbiddenContent = (context) =>
        @<span class="text-muted">Delete not allowed</span>;
}

The PrivilegeLink component extends the standard NavLink component to provide privilege-aware navigation. It only renders the link when the user has the required permissions, making it perfect for building navigation menus and UI elements that should only be visible to authorized users.

The link components require a PrivilegeContext cascading parameter.

@* Basic navigation link that only shows if user can read posts *@
<PrivilegeLink Subject="Post" Action="read" href="/posts">
    View Posts
</PrivilegeLink>

@* Link with custom action *@
<PrivilegeLink Subject="Post" Action="edit" href="/posts/edit">
    Edit Posts
</PrivilegeLink>

@* Link with field-level permissions *@
<PrivilegeLink Subject="Post" Action="update" Qualifier="title" href="/posts/edit-title">
    Edit Post Titles
</PrivilegeLink>

@* Navigation menu example *@
<nav class="navbar">
    <PrivilegeLink Subject="Post" href="/posts" class="nav-link">
        Posts
    </PrivilegeLink>
    <PrivilegeLink Subject="User" Action="manage" href="/users" class="nav-link">
        Users
    </PrivilegeLink>
    <PrivilegeLink Subject="Settings" Action="edit" href="/settings" class="nav-link">
        Settings
    </PrivilegeLink>
</nav>

@* Using with CSS classes and additional attributes *@
<PrivilegeLink Subject="Post" 
               Action="delete" 
               href="/posts/delete" 
               class="btn btn-danger"
               @onclick="ConfirmDelete">
    Delete Post
</PrivilegeLink>

Privilege-Aware Input Components

The Privileged.Components package also includes privilege-aware input components that automatically handle read/write permissions:

The input components require a PrivilegeContext cascading parameter.

@* Text input that becomes read-only based on permissions *@
<PrivilegeInputText @bind-Value="@model.Title"
                    Subject="Post"
                    Field="title"
                    ReadAction="read"
                    UpdateAction="update" />

@* Number input with privilege checking *@
<PrivilegeInputNumber @bind-Value="@model.Views"
                      Subject="Post"
                      Field="views" />

@* Select dropdown with privilege-based enabling/disabling *@
<PrivilegeInputSelect @bind-Value="@model.Status"
                      Subject="Post"
                      Field="status">
    <option value="draft">Draft</option>
    <option value="published">Published</option>
</PrivilegeInputSelect>

@* Checkbox with privilege checking *@
<PrivilegeInputCheckbox @bind-Value="@model.IsActive"
                        Subject="Post"
                        Field="isActive" />

@* Text area with privilege checking *@
<PrivilegeInputTextArea @bind-Value="@model.Content"
                        Subject="Post"
                        Field="content"
                        rows="5" />

These components automatically:

  • Enable/disable based on update permissions
  • Show/hide based on read permissions

License

This project is licensed under the MIT License.

References

Inspired by CASL

Product Compatible and additional computed target framework versions.
.NET net5.0 was computed.  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 is compatible.  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.  net9.0 is compatible.  net9.0-android was computed.  net9.0-browser was computed.  net9.0-ios was computed.  net9.0-maccatalyst was computed.  net9.0-macos was computed.  net9.0-tvos was computed.  net9.0-windows was computed.  net10.0 was computed.  net10.0-android was computed.  net10.0-browser was computed.  net10.0-ios was computed.  net10.0-maccatalyst was computed.  net10.0-macos was computed.  net10.0-tvos was computed.  net10.0-windows was computed. 
.NET Core netcoreapp2.0 was computed.  netcoreapp2.1 was computed.  netcoreapp2.2 was computed.  netcoreapp3.0 was computed.  netcoreapp3.1 was computed. 
.NET Standard netstandard2.0 is compatible.  netstandard2.1 was computed. 
.NET Framework net461 was computed.  net462 was computed.  net463 was computed.  net47 was computed.  net471 was computed.  net472 was computed.  net48 was computed.  net481 was computed. 
MonoAndroid monoandroid was computed. 
MonoMac monomac was computed. 
MonoTouch monotouch was computed. 
Tizen tizen40 was computed.  tizen60 was computed. 
Xamarin.iOS xamarinios was computed. 
Xamarin.Mac xamarinmac was computed. 
Xamarin.TVOS xamarintvos was computed. 
Xamarin.WatchOS xamarinwatchos was computed. 
Compatible target framework(s)
Included target framework(s) (in package)
Learn more about Target Frameworks and .NET Standard.
  • .NETStandard 2.0

    • No dependencies.
  • net8.0

    • No dependencies.
  • net9.0

    • No dependencies.

NuGet packages (2)

Showing the top 2 NuGet packages that depend on Privileged:

Package Downloads
Privileged.Components

An authorization library for restricting resources by action, subject and qualifiers with rule-based permissions

Privileged.Authorization

An authorization library for restricting resources by action, subject and qualifiers with rule-based permissions

GitHub repositories

This package is not used by any popular GitHub repositories.

Version Downloads Last Updated
2.4.1 159 8/18/2025
2.4.0 85 8/15/2025
2.3.2 136 8/15/2025
2.3.1 139 8/14/2025
2.3.0 139 8/14/2025
2.2.2 139 8/14/2025
2.2.1 135 8/14/2025
2.2.0 138 8/13/2025
2.1.0 134 8/13/2025
2.0.2 137 8/12/2025
2.0.0 131 8/10/2025
1.2.0 104 8/9/2025
1.1.0 116 8/9/2025
1.0.1 222 7/26/2024
1.0.0 185 1/10/2024
1.0.0-beta.2 86 1/9/2024