Permit-Prisma Extension
The Permit-Prisma extension integrates Permit.io's fine-grained authorization directly into your Prisma ORM queries, ensuring every database operation respects your centralized permission policies.
This integration works at the data layer, automatically enforcing access controls for create, read, update, and delete operations across your application.
Key Capabilities
The Permit-Prisma extension provides three core capabilities:
-
Direct Permission Checks: Automatically verifies if users are authorized to perform specific actions on resources before executing database operations.
-
Permission Filtering: Restricts database queries so users see only data they're authorized to access, implementing true row-level security.
-
Resource Synchronization: Keeps your Permit.io policy engine in sync with your database by automatically ingesting resource instances and their relationships.
- Seamlessly integrate Permit.io authorization into your Prisma database operations
- Support for all authorization models: RBAC, ABAC, and ReBAC
- Automatic permission checks on database operations
- Data filtering based on user permissions
- Resource instance synchronization with Permit.io
Installation
Install the package (alongside the Prisma client package) from npm:
npm install @permitio/permit-prisma @prisma/client
Prerequisites
Before using the extension, ensure you have:
- A Permit.io account
- Permit's environment API key from your project settings
- A Policy Decision Point (PDP) - either Permit's hosted cloud PDP or a local PDP container (Use locally deployed PDP for ABAC and ReBAC policies)
- Resources, actions, roles, and policies defined in Permit.io's dashboard or via the API
Basic Setup
import { PrismaClient } from "@prisma/client";
import { createPermitClientExtension } from "@permitio/permit-prisma";
const prisma = new PrismaClient().$extends(
createPermitClientExtension({
permitConfig: {
token: "<YOUR_PERMIT_API_KEY>", // Permit API Key (required)
pdp: "http://localhost:7766", // PDP address (required)
},
enableAutomaticChecks: true, // Enable automatic permission checks
enableResourceSync: true, // Sync resource instances with Permit.io
enableAttributeSync: true, // Sync resource attributes with Permit.io
enableDataFiltering: true, // Enable automatic query filtering by permissions
})
);
Access Control Models
This extension supports three access control models from Permit.io:
Role-Based Access Control (RBAC)
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.
Attribute-Based Access Control (ABAC)
What it is: Access decisions based on attributes of users, resources, or environment.
Examples:
- Allow access if
user.department == document.department
- Allow updates if
document.status == "DRAFT"
How it works with the extension: When enableAttributeSync
is on, resource attributes are automatically synced to Permit.io for policy evaluation.
Best for: Dynamic rules that depend on context or data properties.
Relationship-Based Access Control (ReBAC)
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:
- Resource instances are synced to Permit.io (with
enableResourceSync: true
) - Permission checks include the specific resource instance ID
Best for: Collaborative applications where users need different permissions on different instances of the same resource type.
Choosing the Right Model
- RBAC: When you need simple, role-based access control
- ABAC: When decisions depend on data properties or contextual information
- ReBAC: When users need different permissions on different instances
Permission Enforcement
The Permit-Prisma extension implements fine-grained authorization checks on all Prisma operations. Here's how it works:
Permission Check Flow
When enableAutomaticChecks
is enabled, the extension:
- Intercepts all Prisma operations (create, read, update, delete)
- Maps the operation to a corresponding Permit.io action
- Checks if the current user has permission for that action on the resource
- Allows the operation to proceed if authorized, or throws a PermitError if denied
// Set the active user for permission checks
prisma.$permit.setUser("john@example.com");
// This will be checked against Permit.io policies automatically
try {
const document = await prisma.document.create({
data: {
title: "New Document",
content: "Document content",
}
});
console.log("Document created successfully");
} catch (error) {
if (error instanceof PermitError) {
console.error("Permission denied");
}
}
Configuration Examples for Different Models
Here are example configurations for each access control model:
RBAC Example
// Configure for RBAC
const prisma = new PrismaClient().$extends(
createPermitClientExtension({
permitConfig: { token: "YOUR_API_KEY", pdp: "http://localhost:7766" },
enableAutomaticChecks: true
})
);
// Simple role-based check
prisma.$permit.setUser("admin@example.com");
const documents = await prisma.document.findMany(); // Will succeed if admin role has read permission
ABAC Example
// Configure for ABAC
const prisma = new PrismaClient().$extends(
createPermitClientExtension({
permitConfig: { token: "YOUR_API_KEY", pdp: "http://localhost:7766" },
enableAutomaticChecks: true,
enableAttributeSync: true
})
);
// Set user with attributes
prisma.$permit.setUser({
key: "doctor@hospital.com",
attributes: { department: "cardiology" }
});
// Will succeed only if user department matches record department based on policy
const records = await prisma.medicalRecord.findMany({
where: { department: "cardiology" }
});
ReBAC Example
// Configure for ReBAC
const prisma = new PrismaClient().$extends(
createPermitClientExtension({
permitConfig: { token: "YOUR_API_KEY", pdp: "http://localhost:7766" },
enableAutomaticChecks: true,
enableResourceSync: true,
enableDataFiltering: true
})
);
// Set user for instance-level permissions
prisma.$permit.setUser("owner@example.com");
// Will only succeed if the user has permission on this specific file instance
const file = await prisma.file.findUnique({
where: { id: "file-123" }
});
Combined Model Example
// Configure for combined models (RBAC + ABAC + ReBAC)
const prisma = new PrismaClient().$extends(
createPermitClientExtension({
permitConfig: { token: "YOUR_API_KEY", pdp: "http://localhost:7766" },
enableAutomaticChecks: true,
enableResourceSync: true,
enableAttributeSync: true,
enableDataFiltering: true
})
);
Manual Permission Checks
For more control, you can also perform manual permission checks:
// Check permission manually
const canUpdateDocument = await prisma.$permit.check(
"john@example.com", // user
"update", // action
"document" // resource
);
if (canUpdateDocument) {
await prisma.document.update({
where: { id: "doc-123" },
data: { title: "Updated Title" }
});
}
// Or enforce check (throws if denied)
await prisma.$permit.enforceCheck(
"john@example.com",
"delete",
{ type: "document", key: "doc-123" }
);
await prisma.document.delete({ where: { id: "doc-123" } });