easy-permission-engine
v1.0.0
Published
A flexible and powerful permission management engine for TypeScript with RBAC, conditional permissions, role inheritance, and policy-based access control
Maintainers
Readme
Permission Engine
A flexible and powerful permission management system for TypeScript applications. This engine provides comprehensive role-based access control (RBAC) with support for conditional permissions, role inheritance, and policy-based access control.
Features
- 🔐 Role-Based Access Control (RBAC): Define roles with specific permissions
- 🎯 Conditional Permissions: Add conditions to permissions for fine-grained control
- 🔗 Role Inheritance: Create role hierarchies with inherited permissions
- 📜 Policy-Based Access Control: Define complex policies with custom logic
- ⚡ Performance: Efficient permission checking with minimal overhead
- 🛡️ Type-Safe: Full TypeScript support with comprehensive type definitions
- 🧪 Well-Tested: Designed with testability in mind
Installation
npm installArchitecture
This package provides two versions to fit different needs:
V1: PermissionEngine (In-Memory)
Simple, fast, in-memory storage. Great for getting started or small applications.
V2: PermissionEngineV2 (Repository-Based)
Scalable, stateless, database-backed. Recommended for production.
See ARCHITECTURE.md for detailed comparison.
Quick Start (V1 - Simple)
import { PermissionEngine, Role, User } from "./src";
// Create the engine
const engine = new PermissionEngine();
// Define a role
const editorRole: Role = {
id: "editor",
name: "Editor",
permissions: [
{ resource: "post", action: "create" },
{ resource: "post", action: "read" },
{ resource: "post", action: "update" },
],
};
// Add role to engine
engine.roles.addRole(editorRole);
// Create a user with the role
const user: User = {
id: "user-1",
roles: ["editor"],
};
// Add user to engine
engine.addUser(user);
// Check permissions
const canUpdate = engine.checkPermission("user-1", "post", "update");
console.log(canUpdate.granted); // true
const canDelete = engine.checkPermission("user-1", "post", "delete");
console.log(canDelete.granted); // falseQuick Start (V2 - Scalable/Production)
import {
PermissionEngineV2,
InMemoryUserRepository, // Replace with your DB repository
InMemoryRoleRepository,
InMemoryPolicyRepository,
User,
Role,
} from "./src";
// Create repositories (implement these with your database)
const userRepo = new InMemoryUserRepository();
const roleRepo = new InMemoryRoleRepository();
const policyRepo = new InMemoryPolicyRepository();
// Create stateless engine
const engine = new PermissionEngineV2(userRepo, roleRepo, policyRepo);
// Set up a role in your database
const editorRole: Role = {
id: "editor",
name: "Editor",
permissions: [
{ resource: "post", action: "create" },
{ resource: "post", action: "update" },
],
};
await roleRepo.saveRole(editorRole);
// User stored in your database
const user: User = {
id: "user-1",
roles: ["editor"],
};
await userRepo.saveUser(user);
// Check permissions (engine fetches from DB)
const result = await engine.checkPermission("user-1", "post", "update");
console.log(result.granted); // true
// 🎯 Engine is stateless - scales horizontally!
// 💾 All data in your database, not in memory
// ⚡ Add caching layer at repository level for performanceCore Concepts
Permissions
A permission defines access to a specific action on a resource:
{
resource: 'post', // The resource being accessed
action: 'update', // The action being performed
conditions: [] // Optional conditions
}Roles
Roles group permissions together and can be assigned to users:
const adminRole: Role = {
id: "admin",
name: "Administrator",
description: "Full system access",
permissions: [
{ resource: "*", action: "*" }, // Wildcard for all resources and actions
],
};Users
Users are assigned roles and can have direct permissions:
const user: User = {
id: "user-1",
roles: ["editor", "reviewer"],
permissions: [
// Optional direct permissions
{ resource: "settings", action: "read" },
],
attributes: {
// Optional attributes for condition evaluation
department: "engineering",
},
};Policies
Policies provide fine-grained access control with custom logic:
const policy: Policy = {
id: "business-hours-only",
name: "Business Hours Policy",
effect: PolicyEffect.DENY,
resources: ["sensitive-data"],
actions: ["read", "update"],
conditions: [
{
evaluate: (context) => {
const hour = new Date().getHours();
return hour < 9 || hour > 17; // Outside business hours
},
description: "Deny access outside business hours",
},
],
};
engine.policies.addPolicy(policy);Advanced Features
Conditional Permissions
Add conditions to permissions for context-aware access control:
const authorRole: Role = {
id: "author",
name: "Author",
permissions: [
{
resource: "post",
action: "update",
conditions: [
{
field: "postAuthorId",
operator: ConditionOperator.EQUALS,
value: "userId",
},
],
},
],
};
// Check with context
const result = engine.checkPermission("author-1", "post", "update", {
context: {
postAuthorId: "userId",
userId: "author-1",
},
});Supported Condition Operators
EQUALS: Exact matchNOT_EQUALS: Not equalIN: Value in arrayNOT_IN: Value not in arrayGREATER_THAN: Numeric comparisonLESS_THAN: Numeric comparisonGREATER_THAN_OR_EQUAL: Numeric comparisonLESS_THAN_OR_EQUAL: Numeric comparisonCONTAINS: String contains substringNOT_CONTAINS: String doesn't contain substringSTARTS_WITH: String starts withENDS_WITH: String ends withMATCHES: Regular expression match
Role Inheritance
Create role hierarchies where child roles inherit parent permissions:
const userRole: Role = {
id: "user",
name: "User",
permissions: [
{ resource: "profile", action: "read" },
{ resource: "profile", action: "update" },
],
};
const moderatorRole: Role = {
id: "moderator",
name: "Moderator",
permissions: [{ resource: "comment", action: "delete" }],
inherits: ["user"], // Inherits all user permissions
};
engine.roles.addRole(userRole);
engine.roles.addRole(moderatorRole);Multiple Permission Checks
Check multiple permissions at once:
// Check if user has ALL permissions
const hasAll = engine.checkAllPermissions("user-1", [
{ resource: "post", action: "create" },
{ resource: "post", action: "publish" },
]);
// Check if user has ANY permission
const hasAny = engine.checkAnyPermission("user-1", [
{ resource: "post", action: "delete" },
{ resource: "post", action: "publish" },
]);Direct Permission Management
Grant or revoke permissions directly to users:
// Grant permission
engine.grantPermission("user-1", {
resource: "admin-panel",
action: "access",
});
// Revoke permission
engine.revokePermission("user-1", "admin-panel", "access");API Reference
PermissionEngine
Main class for managing users, roles, and permissions.
Methods
addUser(user: User): void- Add a usergetUser(userId: string): User | undefined- Get a userremoveUser(userId: string): boolean- Remove a userassignRole(userId: string, roleId: string): boolean- Assign role to userremoveRole(userId: string, roleId: string): boolean- Remove role from usercheckPermission(userId: string, resource: string, action: string, options?: CheckOptions): PermissionCheckResult- Check permissioncheckAllPermissions(userId: string, checks: Array<{resource: string, action: string}>, options?: CheckOptions): PermissionCheckResult- Check multiple permissions (AND)checkAnyPermission(userId: string, checks: Array<{resource: string, action: string}>, options?: CheckOptions): PermissionCheckResult- Check multiple permissions (OR)grantPermission(userId: string, permission: Permission): boolean- Grant direct permissionrevokePermission(userId: string, resource: string, action: string): boolean- Revoke direct permission
RoleManager
Accessed via engine.roles
Methods
addRole(role: Role): void- Add a rolegetRole(roleId: string): Role | undefined- Get a rolegetAllRoles(): Role[]- Get all rolesremoveRole(roleId: string): boolean- Remove a roleupdateRole(roleId: string, updates: Partial<Role>): boolean- Update a rolegetRolePermissions(roleId: string): Permission[]- Get all permissions (including inherited)addPermissionToRole(roleId: string, permission: Permission): boolean- Add permission to roleremovePermissionFromRole(roleId: string, resource: string, action: string): boolean- Remove permission from role
PolicyManager
Accessed via engine.policies
Methods
addPolicy(policy: Policy): void- Add a policygetPolicy(policyId: string): Policy | undefined- Get a policygetAllPolicies(): Policy[]- Get all policiesremovePolicy(policyId: string): boolean- Remove a policy
Examples
See the examples directory for comprehensive examples:
npm run exampleBuilding
npm run buildThe compiled JavaScript will be in the dist/ directory.
Which Version Should I Use?
Use V1 (PermissionEngine) if:
- ✅ Getting started or prototyping
- ✅ Small application (< 1,000 users)
- ✅ Single server deployment
- ✅ All permission data fits in memory
- ✅ You want the simplest setup
Use V2 (PermissionEngineV2) if:
- ✅ Production application (recommended)
- ✅ Large user base (1,000s - millions)
- ✅ Multi-server / microservices deployment
- ✅ Need data persistence
- ✅ Want horizontal scalability
- ✅ Need caching strategies
See ARCHITECTURE.md and docs/DATABASE_INTEGRATION.md for details.
Use Cases
- Web Applications: Control access to routes, API endpoints, and resources
- Content Management Systems: Manage user roles and content permissions
- Multi-Tenant Applications: Isolate permissions between tenants
- API Services: Protect API endpoints with fine-grained access control
- Admin Panels: Control access to administrative features
- Microservices: Centralized permission service with database backend
Best Practices
- Use Roles for Common Permissions: Group common permissions into roles
- Use Policies for Complex Logic: Implement business rules with policies
- Use Conditions for Context-Aware Access: Add conditions when access depends on context
- Keep Permissions Granular: Define specific resources and actions
- Test Permission Logic: Thoroughly test your permission rules
- Document Roles and Policies: Maintain clear documentation of your access control model
License
MIT
Contributing
Contributions are welcome! Please feel free to submit issues or pull requests.
