Gathering detailed insights and metrics for @permitio/permit-prisma
Gathering detailed insights and metrics for @permitio/permit-prisma
Gathering detailed insights and metrics for @permitio/permit-prisma
Gathering detailed insights and metrics for @permitio/permit-prisma
Fine-Grained Authorization Client Extension for Prisma ORM
npm install @permitio/permit-prisma
Typescript
Module System
Node Version
NPM Version
TypeScript (100%)
Total Downloads
0
Last Day
0
Last Week
0
Last Month
0
Last Year
0
Apache-2.0 License
3 Stars
50 Commits
1 Forks
4 Watchers
2 Branches
3 Contributors
Updated on May 22, 2025
Latest Version
0.0.2
Package Id
@permitio/permit-prisma@0.0.2
Unpacked Size
74.51 kB
Size
18.85 kB
File Count
23
NPM Version
10.9.2
Node Version
23.10.0
Published on
May 18, 2025
Cumulative downloads
Total Downloads
Last Day
0%
NaN
Compared to previous day
Last Week
0%
NaN
Compared to previous week
Last Month
0%
NaN
Compared to previous month
Last Year
0%
NaN
Compared to previous year
1
7
Permit Prisma (@permitio/permit-prisma
) is a Prisma Client extension that integrates Permit.io's fine-grained access control into your database queries. It enables role-based, attribute-based, and relationship-based access checks (RBAC, ABAC, ReBAC) directly through the Prisma Client, securing your application at the data layer. By using this extension, you ensure that every database operation is authorized according to central policies defined in Permit.io.
The extension implements three key capabilities:
Prisma is a powerful ORM but does not provide built-in fine-grained authorization controls. As your application scales, you need stricter control over who can create, read, update, or delete specific data. This extension addresses that gap by enforcing permission checks and filtering data as part of Prisma queries, while maintaining a clean separation between your data access and authorization logic.
Install the package (alongside the prisma client package) from npm:
1npm install @permitio/permit-prisma @prisma/client
Make sure you have:
https://cloudpdp.api.permit.io
http://localhost:7766
, recommended for ABAC/ReBAC). Read more on running a Local Policy Check using the PDP hereTo use the extension, import it and extend your Prisma Client instance:
1import { PrismaClient } from "@prisma/client"; 2import { createPermitClientExtension } from "@permitio/permit-prisma"; 3 4const prisma = new PrismaClient().$extends( 5 createPermitClientExtension({ 6 permitConfig: { 7 token: "<YOUR_PERMIT_API_KEY>", // Permit.io API Key (required) 8 pdp: "http://localhost:7766", // PDP address (required) 9 } 10 accessControlModel: "RBAC", // Access control model: 'RBAC' | 'ABAC' | 'ReBAC' 11 enableAutoSync: true, // Enable auto-sync of resource instances 12 enableDataFiltering: false, // Enable automatic query filtering by permissions 13 enableAutomaticChecks: true, // Enable automatic checks 14 }) 15);
permitConfig
is the object containing all Permit.io SDK configuration parameters:
permitConfig.token
is your secret API key for Permit (required). This connects the extension to Permit's policy engine.permitConfig.pdp
is the URL of the Permit Policy Decision Point service. For local development, it defaults to http://localhost:7766
. For production with Permit's cloud PDP, this will be automatically configured when using your API token.permitConfig.debug
(optional): Set to true
to enable detailed logging from the Permit SDK.enableAutomaticChecks
(default: false
): Critical setting that enables permission enforcement. If set to true
, the extension will automatically check permissions for all Prisma operations and block unauthorized actions. If false
, you'll need to handle permission checks manually using methods like prisma.$permit.check()
or prisma.$permit.enforceCheck()
. This must be enabled for enableAutoSync
and enableDataFiltering
to work.
accessControlModel
(required for optimal behavior): Specifies which permission model you're using: Role-Based ("rbac"
), Attribute-Based ("abac"
), or Relationship-Based ("rebac"
). This setting is crucial as it determines:
While the extension will default to basic RBAC behavior if not specified, you should always set this explicitly to match your Permit.io policy configuration for correct operation.
Examples:
"rbac"
if using simple role-based permissions"abac"
if using attribute-based conditions in your policies"rebac"
if using relationship-based or instance-level permissionsenableAutoSync
if true
, the extension will perform data ingestion by automatically syncing created/updated/deleted Prisma records to Permit.io as resource instances (including their attributes). This ensures Permit's data is up-to-date for permission checks. This is especially important for ReBAC scenarios where relationships between resources are used for permission decisions. Requires enableAutomaticChecks: true
.
enableDataFiltering
if true
, enables pre-fetch filtering where read queries (find, findMany) are automatically modified to include only records the active user is permitted to access. The extension adds an id IN (...)
clause to your queries before they reach the database. If false
, you can still manually filter using the provided filterQueryResults
method. Requires enableAutomaticChecks: true
.
Recommended configurations:
{ accessControlModel: "rbac", enableAutomaticChecks: true }
{ accessControlModel: "abac", enableAutomaticChecks: true, enableAutoSync: true }
{ accessControlModel: "rebac", enableAutomaticChecks: true, enableAutoSync: true, enableDataFiltering: true }
defaultTenant
(default: "default"
): The tenant key to associate with synced resources. Useful in multi-tenant applications where resources need to be isolated by tenant.
resourceTypeMapping
(optional): A custom map from Prisma model names to Permit resource types. Useful if your model names don't match your Permit resource keys.
Example: { User: "customer", BlogPost: "article" }
excludedModels
(optional): An array of model names to skip from permission checks or syncing.
Example: ["Log", "Audit", "SystemConfig"]
excludedOperations
(optional): An array of Prisma operations to skip from checking.
Example: ["findFirst", "count", "aggregate"]
These options can be combined with the core settings to customize the extension's behavior for your specific application needs.
Below is a full summary of the configuration options you can pass to createPermitClientExtension()
:
Option | Required | Default | Description |
---|---|---|---|
permitConfig | Yes | None | The Permit.io SDK config object. Must include token , pdp , and other core settings. See IPermitConfig . |
accessControlModel | No | 'rbac' | Defines the access control strategy. Can be 'rbac' , 'abac' , or 'rebac' . Determines how permissions are enforced. |
enableAutoSync | No | false | If true , resource instances (e.g., rows) are automatically synced with Permit.io on create/update/delete. Required for ReBAC and useful for ABAC. |
enableDataFiltering | No | false | If true , adds row-level filtering on findMany() queries to return only records the user has access to. Most useful with ReBAC. |
enableAutomaticChecks | No | false | If true , automatically checks permissions on all Prisma queries and mutations, rejecting unauthorized actions. |
defaultTenant | No | 'default' | The default tenant ID to use when syncing resource instances to Permit. |
resourceTypeMapping | No | {} (empty object) | A map of Prisma model names to Permit resource types. Useful when your model names differ from Permit's resource types. |
excludedModels | No | [] | List of model names to skip from automatic permission checks. |
excludedOperations | No | [] | List of operations to skip from automatic permission checks (e.g., ['createMany'] ). |
permitConfig.token
and permitConfig.pdp
are required to use the extension.enableAutomaticChecks
must be set to true
for enableAutoSync
and enableDataFiltering
to work.permitConfig.token
and permitConfig.pdp
is required.accessControlModel
to 'rebac'
if you're using relationship-based permissions and want instance filtering + syncing.enableAutoSync
to ensure resource attributes are kept in sync with Permit.io.permitConfig
.Example configurations for different scenarios:
1// Basic RBAC setup 2createPermitClientExtension({ 3 permitConfig: { token: "YOUR_API_KEY", pdp: "http://localhost:7766" }, 4 accessControlModel: "rbac", 5 enableAutomaticChecks: true 6}) 7 8// ABAC setup with attribute syncing 9createPermitClientExtension({ 10 permitConfig: { token: "YOUR_API_KEY", pdp: "http://localhost:7766" }, 11 accessControlModel: "abac", 12 enableAutomaticChecks: true, 13 enableAutoSync: true 14}) 15 16// ReBAC setup with full features 17createPermitClientExtension({ 18 permitConfig: { token: "YOUR_API_KEY", pdp: "http://localhost:7766" }, 19 accessControlModel: "rebac", 20 enableAutomaticChecks: true, 21 enableAutoSync: true, 22 enableDataFiltering: true 23})
This extension supports three access control models from Permit.io:
What it is: Users are assigned roles (Admin, Editor, Viewer) with predefined permissions to perform actions on resource types.
Example: An "Editor" role can update any document in the system.
Best for: Simple permission structures where access is determined by job function or user level.
What it is: Access decisions based on attributes of users, resources, or environment.
Examples:
user.department == document.department
document.status == "DRAFT"
How it works with the extension: When enableAutoSync
is on, resource attributes are automatically synced to Permit.io for policy evaluation.
Best for: Dynamic rules that depend on context or data properties.
What it is: Permissions based on relationships between users and specific resource instances.
Example: A user is an "Owner" of document-123 but just a "Viewer" of document-456.
How it works with the extension:
enableAutoSync: true
)Best for: Collaborative applications where users need different permissions on different instances of the same resource type.
Before performing any operations with the extension, you need to identify which user is making the request. This is a critical step as all permission checks and data filtering will be based on this user context.
In a typical web application, you'd set the user context at the beginning of a request handler after authenticating the user:
1// Express.js example 2app.get('/documents', async (req, res) => { 3 // Set the user context from your authentication system 4 prisma.$permit.setUser(req.user.id); 5 6 // All subsequent Prisma operations will respect this user's permissions 7 const documents = await prisma.document.findMany(); 8 9 // documents will only contain what this user is allowed to access 10 res.json(documents); 11});
You can set the user in different ways depending on your access control model:
1// Simple user identifier (for RBAC) 2prisma.$permit.setUser("user@example.com"); 3 4// User with attributes (for ABAC) 5prisma.$permit.setUser({ 6 key: "user@example.com", 7 attributes: { department: "engineering", clearance: "high" } 8}); 9 10// For ReBAC, you typically just need the user ID 11// The relationships are established via resource instances in Permit 12prisma.$permit.setUser("user@example.com"); 13// Then when you create/update resources, include relationship fields: 14await prisma.document.create({ 15 data: { 16 title: "Document Title", 17 content: "Content", 18 ownerId: "user@example.com" // This establishes the relationship 19 } 20});
Important Notes:
setUser
once per request to ensure proper permission enforcementIn this example, we implement role-based access to documents with different permission levels:
First, configure your resources, roles, and permissions in Permit.io:
document
resource with create
, read
, update
, delete
actionsadmin
role with all permissionscustomer
role with only read
permissionNext, create the users and assign roles:
1import { PrismaClient } from "@prisma/client"; 2import { createPermitClientExtension, AccessControlModel } from "@permitio/prisma-permit"; 3 4// Configure the extension with RBAC 5const prisma = new PrismaClient().$extends( 6 createPermitClientExtension({ 7 permitConfig: { 8 token: process.env.PERMIT_API_KEY!, 9 pdp: "http://localhost:7766" 10 }, 11 accessControlModel: AccessControlModel.RBAC, 12 enableAutomaticChecks: true // Automatically enforce permissions 13 }) 14); 15 16// Admin user operations 17async function adminOperations() { 18 // Set user context for the admin 19 prisma.$permit.setUser("admin@example.com"); 20 21 // Admin can create documents (allowed) 22 const doc = await prisma.document.create({ 23 data: { 24 title: "New Document", 25 content: "Document content", 26 ownerId: "admin@example.com" 27 } 28 }); 29 30 // Admin can read documents (allowed) 31 const docs = await prisma.document.findMany(); 32 33 // Admin can update documents (allowed) 34 await prisma.document.update({ 35 where: { id: doc.id }, 36 data: { title: "Updated Title" } 37 }); 38 39 // Admin can delete documents (allowed) 40 await prisma.document.delete({ where: { id: doc.id } }); 41} 42 43// Customer user operations 44async function customerOperations() { 45 // Set user context for the customer 46 prisma.$permit.setUser("customer@example.com"); 47 48 // Customer can read documents (allowed) 49 const docs = await prisma.document.findMany(); 50 51 try { 52 // Customer cannot create documents (denied) 53 await prisma.document.create({ 54 data: { 55 title: "Unauthorized Creation", 56 content: "This will fail", 57 ownerId: "customer@example.com" 58 } 59 }); 60 } catch (error) { 61 // Permission denied error 62 } 63 64 // All other operations will similarly be denied 65}
The extension automatically:
Intercepts all Prisma operations Checks if the current user has permission for the operation Allows or denies the operation based on the user's role
For complete implementation, see examples/rbac/document.ts
.
In this example, we implement attribute-based access to medical records where permissions are determined by matching the user's department with the record's department:
First, configure resources and attributes in Permit.io:
medical_record
resource with a department
attributedepartment
attribute1import { PrismaClient } from "@prisma/client"; 2import { createPermitClientExtension, AccessControlModel } from "@permitio/prisma-permit"; 3 4// Configure the extension with ABAC 5const prisma = new PrismaClient().$extends( 6 createPermitClientExtension({ 7 permitConfig: { 8 token: process.env.PERMIT_API_KEY!, 9 pdp: "http://localhost:7766" 10 }, 11 accessControlModel: AccessControlModel.ABAC, 12 enableAutomaticChecks: true, 13 }) 14); 15 16// Example: Cardiologist operations 17async function cardiologistOperations() { 18 // Set user with department attribute 19 prisma.$permit.setUser({ 20 key: "doctor_cardio@example.com", 21 attributes: { 22 department: "cardiology" 23 } 24 }); 25 26 // Can access cardiology records (ALLOWED) 27 const cardioRecords = await prisma.medicalRecord.findMany({ 28 where: { department: "cardiology" } 29 }); 30 31 // Can update cardiology records (ALLOWED) 32 if (cardioRecords.length > 0) { 33 await prisma.medicalRecord.update({ 34 where: { id: cardioRecords[0].id }, 35 data: { 36 content: "Updated by cardiologist", 37 department: "cardiology" // Must include department 38 } 39 }); 40 } 41 42 // Cannot access oncology records (DENIED) 43 try { 44 await prisma.medicalRecord.findMany({ 45 where: { department: "oncology" } 46 }); 47 } catch (error) { 48 // Permission denied 49 } 50} 51 52// Example: Oncologist operations 53async function oncologistOperations() { 54 // Set user with department attribute 55 prisma.$permit.setUser({ 56 key: "doctor_onco@example.com", 57 attributes: { 58 department: "oncology" 59 } 60 }); 61 62 // Can access oncology records (ALLOWED) 63 const oncoRecords = await prisma.medicalRecord.findMany({ 64 where: { department: "oncology" } 65 }); 66 67 // Cannot access cardiology records (DENIED) 68 try { 69 await prisma.medicalRecord.findMany({ 70 where: { department: "cardiology" } 71 }); 72 } catch (error) { 73 // Permission denied 74 } 75}
setUser
with an attributes
object).examples/abac/medical-records.ts
.In this example, we implement relationship-based access control for a file system where:
First, configure your resources, relationships, and instance roles in Permit.io:
Create Resources:
folder
resource with create
, read
, update
, delete
actionsfile
resource with create
, read
, update
, delete
actionsDefine Instance Roles:
folder#owner
role with full permissions (create, read, update, delete)folder#viewer
role with read-only permissionfile#owner
role with full permissionsfile#viewer
role with read-only permissionSet Role Derivations:
folder#owner
to derive file#owner
when a folder is parent of a filefolder#viewer
to derive file#viewer
when a folder is parent of a fileCreate Users and Assign Roles:
1import { PrismaClient } from "@prisma/client"; 2import { createPermitClientExtension, AccessControlModel } from "@permitio/permit-prisma"; 3 4// Configure the extension with ReBAC 5const prisma = new PrismaClient().$extends( 6 createPermitClientExtension({ 7 permitConfig: { 8 token: process.env.PERMIT_API_KEY!, 9 pdp: "http://localhost:7766" 10 }, 11 accessControlModel: AccessControlModel.ReBAC, 12 enableAutomaticChecks: true, 13 }) 14); 15 16// Owner user operations 17async function ownerOperations() { 18 // Set user context for the owner 19 prisma.$permit.setUser("owner_user"); 20 21 // Create a new folder (allowed) 22 const folder = await prisma.folder.create({ 23 data: { 24 name: "Owner's New Folder", 25 ownerId: "owner_user" 26 } 27 }); 28 29 // Create a file in the folder (allowed) 30 const file = await prisma.file.create({ 31 data: { 32 name: "Owner's File", 33 content: "Content created by owner", 34 folderId: folder.id // This establishes the relationship 35 } 36 }); 37 38 // Update the file (allowed) 39 await prisma.file.update({ 40 where: { id: file.id }, 41 data: { content: "Modified content" } 42 }); 43 44 // Read all files (returns all files the owner can access) 45 const files = await prisma.file.findMany(); 46} 47 48// Viewer user operations 49async function viewerOperations() { 50 // Set user context for the viewer 51 prisma.$permit.setUser("viewer_user"); 52 53 // Try to create a folder (denied) 54 try { 55 await prisma.folder.create({ 56 data: { 57 name: "Viewer's Folder", 58 ownerId: "viewer_user" 59 } 60 }); 61 } catch (error) { 62 // Permission denied error 63 } 64 65 // Read a folder (allowed) 66 const folder = await prisma.folder.findUnique({ 67 where: { id: "folder1" } 68 }); 69 70 // Read files (allowed, returns only files the viewer can access) 71 const files = await prisma.file.findMany(); 72 73 // Try to update a file (denied) 74 try { 75 await prisma.file.update({ 76 where: { id: "file1" }, 77 data: { content: "Attempt to modify" } 78 }); 79 } catch (error) { 80 // Permission denied error 81 } 82}
enableAutoSync
is enabled, relationships established in your data model are automatically synced to Permit.ioenableDataFiltering
ensures queries like findMany()
only return resources the user has permission to accessThe key advantage of ReBAC is that it maps naturally to real-world ownership and access patterns. Users can have different roles on different instances of the same resource type, and permissions flow naturally through relationships.
For a complete implementation with detailed tests, refer to examples/rebac/rebac-folder-file.ts
.
No vulnerabilities found.
No security vulnerabilities found.