VOOZH about

URL: https://www.nuget.org/packages/PrimusSaaS.Rbac.Dapper/

⇱ NuGet Gallery | PrimusSaaS.Rbac.Dapper 1.0.1




PrimusSaaS.Rbac.Dapper 1.0.1

dotnet add package PrimusSaaS.Rbac.Dapper --version 1.0.1
 
 
NuGet\Install-Package PrimusSaaS.Rbac.Dapper -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="PrimusSaaS.Rbac.Dapper" Version="1.0.1" />
 
 
For projects that support PackageReference, copy this XML node into the project file to reference the package.
<PackageVersion Include="PrimusSaaS.Rbac.Dapper" Version="1.0.1" />
 
Directory.Packages.props
<PackageReference Include="PrimusSaaS.Rbac.Dapper" />
 
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 PrimusSaaS.Rbac.Dapper --version 1.0.1
 
 
The NuGet Team does not provide support for this client. Please contact its maintainers for support.
#r "nuget: PrimusSaaS.Rbac.Dapper, 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.
#:package PrimusSaaS.Rbac.Dapper@1.0.1
 
 
#: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=PrimusSaaS.Rbac.Dapper&version=1.0.1
 
Install as a Cake Addin
#tool nuget:?package=PrimusSaaS.Rbac.Dapper&version=1.0.1
 
Install as a Cake Tool
The NuGet Team does not provide support for this client. Please contact its maintainers for support.

PrimusSaaS.Rbac.Dapper

Dapper storage adapter for PrimusSaaS.Rbac. A lightweight, provider-agnostic alternative to the EF Core adapter that gives you full control over SQL. No DbContext, no migrations, no EF dependency.

Start here

  • First runnable Dapper + SQLite path: /docs/modules/rbac/verified-dapper-quickstart
  • Existing host integration path: /docs/modules/rbac/integration-guide
  • Core RBAC concepts and package map: /docs/modules/rbac
  • Exact repository implementation reference: examples/RbacDapperQuickstart

Installation

# Core RBAC + Dapper adapter
dotnet add package PrimusSaaS.Rbac
dotnet add package PrimusSaaS.Rbac.Dapper

# Pick your ADO.NET driver (the adapter has no opinion on provider)
dotnet add package Microsoft.Data.SqlClient # SQL Server
dotnet add package Npgsql # PostgreSQL
dotnet add package Microsoft.Data.Sqlite # SQLite

Quickstart

Follow steps 1-5 in order for the minimum runnable setup. Step 6 is optional programmatic seeding. Steps 1 and 2 (schema + config) must be done before the app starts because the adapter checks for seed data on startup.

First-time integration order

If you are using this package for the first time, follow this order exactly:

  1. choose your database driver
  2. configure ConnectionStrings:Rbac
  3. create the RBAC schema
  4. implement IRbacDbConnectionFactory
  5. register AddPrimusRbacDapper(...)
  6. seed initial RBAC data
  7. test one allow case and one deny case

The main difference from the EF Core adapter is step 3: the schema must already exist before the store is first resolved.

1 — Add the connection string

// appsettings.json
{
 "ConnectionStrings": {
 "Rbac": "Server=localhost;Database=MyApp;Trusted_Connection=True;"
 },
 "Rbac": {
 "SeedMode": "none"
 }
}

SeedMode options: none (production default), minimal (one catch-all permission), sample (demo roles and groups for local dev).

2 — Create the schema

Run the DDL once against your database before first launch. See the full DDL section below for the complete script.

3 — Implement IRbacDbConnectionFactory

This is the one class you write — it tells the adapter how to get a connection:

using System.Data;
using Microsoft.Data.SqlClient;
using PrimusSaaS.Rbac.Dapper;

public class RbacConnectionFactory : IRbacDbConnectionFactory
{
 private readonly string _connectionString;

 public RbacConnectionFactory(IConfiguration config)
 => _connectionString = config.GetConnectionString("Rbac")!;

 public IDbConnection CreateConnection() => new SqlConnection(_connectionString);
}

For PostgreSQL, swap SqlConnection for NpgsqlConnection. For SQLite, use SqliteConnection. The adapter works with any IDbConnection.

4 — Register in Program.cs

using PrimusSaaS.Rbac;
using PrimusSaaS.Rbac.Dapper;

// Register the connection factory (singleton — connection pooling is handled by the driver)
builder.Services.AddSingleton<IRbacDbConnectionFactory, RbacConnectionFactory>();

// Register RBAC with Dapper store
builder.Services.AddPrimusRbacDapper(opts =>
 builder.Configuration.GetSection("Rbac").Bind(opts));

// Optional: enable PostgreSQL session variable initializer for DB RLS policies
builder.Services.AddPrimusRbacPostgresRls();

What gets registered in DI:

Service Lifetime Purpose
IRbacService Scoped Main entry point — evaluate access, manage roles/permissions
IRbacStoreRbacDapperStore Scoped Dapper-backed store
IRbacAuditSink Singleton Logs RBAC decisions (override to send elsewhere)
IRbacDecisionCache Singleton No-op by default (plug in Redis etc.)
IRbacDbConnectionFactory Singleton Your implementation

Note: RbacDapperStore also implements IRbacStoreAsync, which RbacService.EvaluateAsync() discovers at runtime via cast — no separate DI registration is needed.

When using PostgreSQL Row-Level Security policies that reference current_setting('app.rbac_actor'), app.rbac_roles, or app.rbac_tenant, call AddPrimusRbacPostgresRls() so IRbacRlsInitializer resolves to PostgresRbacRlsInitializer instead of the default no-op implementation.

5 — Protect your endpoints

Option A — attribute-based (AspNetCore package required):

dotnet add package PrimusSaaS.Rbac.AspNetCore
// On your endpoint or controller
[PrimusRbacAuthorize("orders", "read")]
public IActionResult GetOrders() { ... }

No extra AddPrimusRbacAspNetCore() registration method exists. Referencing the package is enough; the integration surface is the attribute and the .RequirePrimusRbac(...) endpoint filter.

app.MapGet("/orders", () => Results.Ok())
 .RequireAuthorization()
 .RequirePrimusRbac("orders", "read");

Option B — programmatic check via IRbacService:

public class OrdersController : ControllerBase
{
 private readonly IRbacService _rbac;
 public OrdersController(IRbacService rbac) => _rbac = rbac;

 public async Task<IActionResult> GetOrders()
 {
 var decision = await _rbac.EvaluateAsync(new RbacAccessRequest
 {
 PrincipalId = User.FindFirstValue(ClaimTypes.NameIdentifier)!,
 Resource = "orders",
 Action = "read",
 TenantId = User.FindFirstValue("tid")
 });

 if (!decision.Allowed)
 return Forbid();

 return Ok(await _orderService.GetAllAsync());
 }
}

6 — Seed initial roles and permissions (optional)

Use SeedPrimusRbac after app.Build() to create your initial data programmatically:

var app = builder.Build();

app.Services.SeedPrimusRbac(rbac =>
{
 rbac.UpsertPermission(new RbacPermission(
 Id: "perm:orders:read",
 Action: "read",
 Resource: "orders",
 Effect: RbacEffect.Allow,
 Scope: new RbacScope(ApplicationId: "my-app")));

 rbac.UpsertRole(new RbacRole(
 Id: "role:viewer",
 Name: "Viewer",
 Scope: new RbacScope(ApplicationId: "my-app"),
 PermissionIds: new[] { "perm:orders:read" }));
});

Schema DDL

Column names match the EF Core adapter so you can swap providers without migrating data. Run the script once before first launch.

SQL Server

CREATE TABLE RbacRoles (
 Id NVARCHAR(120) NOT NULL PRIMARY KEY,
 Name NVARCHAR(120) NOT NULL,
 ApplicationId NVARCHAR(100) NULL,
 TenantId NVARCHAR(100) NULL,
 Environment NVARCHAR(50) NULL, -- maps to RbacScope.Qualifier
 PermissionIdsJson NVARCHAR(MAX) NOT NULL DEFAULT '[]',
 InheritsJson NVARCHAR(MAX) NULL,
 Description NVARCHAR(MAX) NULL,
 MetadataJson NVARCHAR(MAX) NULL
);
CREATE INDEX IX_RbacRoles_Name ON RbacRoles (Name);
CREATE INDEX IX_RbacRoles_Scope ON RbacRoles (ApplicationId, TenantId, Environment);

CREATE TABLE RbacPermissions (
 Id NVARCHAR(120) NOT NULL PRIMARY KEY,
 Action NVARCHAR(80) NOT NULL,
 Resource NVARCHAR(200) NOT NULL,
 Effect INT NOT NULL,
 ApplicationId NVARCHAR(100) NULL,
 TenantId NVARCHAR(100) NULL,
 Environment NVARCHAR(50) NULL,
 Description NVARCHAR(MAX) NULL,
 ConditionsJson NVARCHAR(MAX) NULL,
 ConditionGroupsJson NVARCHAR(MAX) NULL,
 MetadataJson NVARCHAR(MAX) NULL
);
CREATE INDEX IX_RbacPermissions_ActionResource ON RbacPermissions (Action, Resource);
CREATE INDEX IX_RbacPermissions_Scope ON RbacPermissions (ApplicationId, TenantId, Environment, Action, Resource);

CREATE TABLE RbacAssignments (
 Id NVARCHAR(140) NOT NULL PRIMARY KEY,
 PrincipalId NVARCHAR(120) NOT NULL,
 PrincipalType NVARCHAR(40) NOT NULL,
 RoleId NVARCHAR(120) NOT NULL,
 ApplicationId NVARCHAR(100) NULL,
 TenantId NVARCHAR(100) NULL,
 Environment NVARCHAR(50) NULL,
 ExpiresAt DATETIME2 NULL,
 Enabled BIT NOT NULL DEFAULT 1,
 AttributesJson NVARCHAR(MAX) NULL
);
CREATE INDEX IX_RbacAssignments_PrincipalId ON RbacAssignments (PrincipalId);
CREATE INDEX IX_RbacAssignments_RoleId ON RbacAssignments (RoleId);
CREATE INDEX IX_RbacAssignments_PrincipalScope ON RbacAssignments (PrincipalId, ApplicationId, TenantId, Environment);

CREATE TABLE RbacGroups (
 Id NVARCHAR(140) NOT NULL PRIMARY KEY,
 Name NVARCHAR(120) NOT NULL,
 Type NVARCHAR(80) NOT NULL,
 ApplicationId NVARCHAR(100) NULL,
 TenantId NVARCHAR(100) NULL,
 Environment NVARCHAR(50) NULL,
 ParentId NVARCHAR(140) NULL,
 Description NVARCHAR(MAX) NULL,
 MetadataJson NVARCHAR(MAX) NULL
);
CREATE INDEX IX_RbacGroups_ParentId ON RbacGroups (ParentId);
CREATE INDEX IX_RbacGroups_Type ON RbacGroups (Type);
CREATE INDEX IX_RbacGroups_Scope ON RbacGroups (ApplicationId, TenantId, Environment);

CREATE TABLE RbacMemberships (
 Id NVARCHAR(160) NOT NULL PRIMARY KEY,
 GroupId NVARCHAR(140) NOT NULL,
 PrincipalId NVARCHAR(120) NOT NULL,
 PrincipalType NVARCHAR(40) NOT NULL,
 ApplicationId NVARCHAR(100) NULL,
 TenantId NVARCHAR(100) NULL,
 Environment NVARCHAR(50) NULL,
 ExpiresAt DATETIME2 NULL,
 Enabled BIT NOT NULL DEFAULT 1,
 AttributesJson NVARCHAR(MAX) NULL
);
CREATE INDEX IX_RbacMemberships_GroupId ON RbacMemberships (GroupId);
CREATE INDEX IX_RbacMemberships_PrincipalId ON RbacMemberships (PrincipalId);
CREATE INDEX IX_RbacMemberships_GroupPrincipal ON RbacMemberships (GroupId, PrincipalId);
CREATE INDEX IX_RbacMemberships_Scope ON RbacMemberships (PrincipalId, ApplicationId, TenantId, Environment);

PostgreSQL

CREATE TABLE "RbacRoles" (
 "Id" VARCHAR(120) NOT NULL PRIMARY KEY,
 "Name" VARCHAR(120) NOT NULL,
 "ApplicationId" VARCHAR(100) NULL,
 "TenantId" VARCHAR(100) NULL,
 "Environment" VARCHAR(50) NULL,
 "PermissionIdsJson" TEXT NOT NULL DEFAULT '[]',
 "InheritsJson" TEXT NULL,
 "Description" TEXT NULL,
 "MetadataJson" TEXT NULL
);
CREATE INDEX "IX_RbacRoles_Name" ON "RbacRoles" ("Name");
CREATE INDEX "IX_RbacRoles_Scope" ON "RbacRoles" ("ApplicationId", "TenantId", "Environment");

CREATE TABLE "RbacPermissions" (
 "Id" VARCHAR(120) NOT NULL PRIMARY KEY,
 "Action" VARCHAR(80) NOT NULL,
 "Resource" VARCHAR(200) NOT NULL,
 "Effect" INT NOT NULL,
 "ApplicationId" VARCHAR(100) NULL,
 "TenantId" VARCHAR(100) NULL,
 "Environment" VARCHAR(50) NULL,
 "Description" TEXT NULL,
 "ConditionsJson" TEXT NULL,
 "ConditionGroupsJson" TEXT NULL,
 "MetadataJson" TEXT NULL
);
CREATE INDEX "IX_RbacPermissions_ActionResource" ON "RbacPermissions" ("Action", "Resource");
CREATE INDEX "IX_RbacPermissions_Scope" ON "RbacPermissions" ("ApplicationId", "TenantId", "Environment", "Action", "Resource");

CREATE TABLE "RbacAssignments" (
 "Id" VARCHAR(140) NOT NULL PRIMARY KEY,
 "PrincipalId" VARCHAR(120) NOT NULL,
 "PrincipalType" VARCHAR(40) NOT NULL,
 "RoleId" VARCHAR(120) NOT NULL,
 "ApplicationId" VARCHAR(100) NULL,
 "TenantId" VARCHAR(100) NULL,
 "Environment" VARCHAR(50) NULL,
 "ExpiresAt" TIMESTAMPTZ NULL,
 "Enabled" BOOLEAN NOT NULL DEFAULT TRUE,
 "AttributesJson" TEXT NULL
);
CREATE INDEX "IX_RbacAssignments_PrincipalId" ON "RbacAssignments" ("PrincipalId");
CREATE INDEX "IX_RbacAssignments_RoleId" ON "RbacAssignments" ("RoleId");
CREATE INDEX "IX_RbacAssignments_PrincipalScope" ON "RbacAssignments" ("PrincipalId", "ApplicationId", "TenantId", "Environment");

CREATE TABLE "RbacGroups" (
 "Id" VARCHAR(140) NOT NULL PRIMARY KEY,
 "Name" VARCHAR(120) NOT NULL,
 "Type" VARCHAR(80) NOT NULL,
 "ApplicationId" VARCHAR(100) NULL,
 "TenantId" VARCHAR(100) NULL,
 "Environment" VARCHAR(50) NULL,
 "ParentId" VARCHAR(140) NULL,
 "Description" TEXT NULL,
 "MetadataJson" TEXT NULL
);
CREATE INDEX "IX_RbacGroups_ParentId" ON "RbacGroups" ("ParentId");
CREATE INDEX "IX_RbacGroups_Type" ON "RbacGroups" ("Type");
CREATE INDEX "IX_RbacGroups_Scope" ON "RbacGroups" ("ApplicationId", "TenantId", "Environment");

CREATE TABLE "RbacMemberships" (
 "Id" VARCHAR(160) NOT NULL PRIMARY KEY,
 "GroupId" VARCHAR(140) NOT NULL,
 "PrincipalId" VARCHAR(120) NOT NULL,
 "PrincipalType" VARCHAR(40) NOT NULL,
 "ApplicationId" VARCHAR(100) NULL,
 "TenantId" VARCHAR(100) NULL,
 "Environment" VARCHAR(50) NULL,
 "ExpiresAt" TIMESTAMPTZ NULL,
 "Enabled" BOOLEAN NOT NULL DEFAULT TRUE,
 "AttributesJson" TEXT NULL
);
CREATE INDEX "IX_RbacMemberships_GroupId" ON "RbacMemberships" ("GroupId");
CREATE INDEX "IX_RbacMemberships_PrincipalId" ON "RbacMemberships" ("PrincipalId");
CREATE INDEX "IX_RbacMemberships_GroupPrincipal" ON "RbacMemberships" ("GroupId", "PrincipalId");
CREATE INDEX "IX_RbacMemberships_Scope" ON "RbacMemberships" ("PrincipalId", "ApplicationId", "TenantId", "Environment");

SQLite: Use TEXT for all string/JSON/datetime columns and INTEGER instead of BOOLEAN / BIT. SQLite has no VARCHAR length enforcement but specifying it is harmless.


Migrating from PrimusSaaS.Rbac.EFCore

The two adapters share the same table names, column names, and JSON encoding. Swap is straightforward:

  1. Remove AddPrimusRbacEfCore<TContext>() and the EF Core package reference.
  2. Add IRbacDbConnectionFactory registration + AddPrimusRbacDapper().
  3. You no longer need modelBuilder.AddPrimusRbac() in your DbContext.
  4. Schema stays the same — no data migration needed.

Behaviour notes

  • Per-operation connections — each store method opens a fresh connection from IRbacDbConnectionFactory.CreateConnection(). ADO.NET connection pooling handles the actual socket reuse.
  • Upsert strategy — SELECT EXISTS then INSERT or UPDATE (cross-database portable, no dialect-specific MERGE/ON CONFLICT).
  • Async — implements IRbacStoreAsync; uses DbConnection.OpenAsync when the provider exposes it.
  • Seeding — same SeedMode option as EFCore (none / minimal / sample). Seeds only when all tables are empty.
Product Versions Compatible and additional computed target framework versions.
.NET net8.0 net8.0 is compatible.  net8.0-android net8.0-android was computed.  net8.0-browser net8.0-browser was computed.  net8.0-ios net8.0-ios was computed.  net8.0-maccatalyst net8.0-maccatalyst was computed.  net8.0-macos net8.0-macos was computed.  net8.0-tvos net8.0-tvos was computed.  net8.0-windows net8.0-windows was computed.  net9.0 net9.0 is compatible.  net9.0-android net9.0-android was computed.  net9.0-browser net9.0-browser was computed.  net9.0-ios net9.0-ios was computed.  net9.0-maccatalyst net9.0-maccatalyst was computed.  net9.0-macos net9.0-macos was computed.  net9.0-tvos net9.0-tvos was computed.  net9.0-windows net9.0-windows was computed.  net10.0 net10.0 is compatible.  net10.0-android net10.0-android was computed.  net10.0-browser net10.0-browser was computed.  net10.0-ios net10.0-ios was computed.  net10.0-maccatalyst net10.0-maccatalyst was computed.  net10.0-macos net10.0-macos was computed.  net10.0-tvos net10.0-tvos was computed.  net10.0-windows net10.0-windows was computed. 
Compatible target framework(s)
Included target framework(s) (in package)
Learn more about Target Frameworks and .NET Standard.

NuGet packages (1)

Showing the top 1 NuGet packages that depend on PrimusSaaS.Rbac.Dapper:

Package Downloads
PrimusSaaS.Rbac.Dapper.MySql

MySQL connection factory for PrimusSaaS.Rbac.Dapper. Drop-in replacement for the SQL Server factory.

GitHub repositories

This package is not used by any popular GitHub repositories.

Version Downloads Last Updated
1.0.1 157 4/19/2026
1.0.0 188 4/1/2026