Developer Resources
Role-Based Permissions System Setup
This document summarizes the role-based permissions system that has been implemented.
Role-Based Permissions System Setup
This document summarizes the role-based permissions system that has been implemented.
✅ What Has Been Completed
1. Role Enum
Added 5 role types to the system:
superadmin- Full system accessadmin- Administrator accessstaff- Staff member accessaccountant- Accountant accesscustomer- Customer access
2. Database Schema
Updated schema with:
- Role enum type (
role_type) - Permission table with module-based organization
- Role-Permission mapping (many-to-many)
- User-Role relationship (one-to-one)
- Complete two-way relations using Drizzle ORM
3. Seed Script
Created comprehensive seed script that:
- Creates 32 permissions across 8 modules (System, Users, Roles, Permissions, Finance, Inventory, Reports, Settings)
- Creates superadmin role with all permissions
- Creates a superadmin user with configurable credentials
- Is idempotent (safe to run multiple times)
4. Scripts Added
Added to package.json:
pnpm db:seed # Run from project root📊 System Architecture
┌─────────┐ ┌──────┐ ┌─────────────┐
│ User │────1────│ Role │────M────│ Permission │
└─────────┘ └──────┘ └─────────────┘
1 user 1 role Many permissions
= 1 role = Many permissionsPermission Structure
Format: {Module}_{Action}
Examples:
Finance_CREATE- Create permission for Finance moduleFinance_READ- Read permission for Finance moduleFinance_UPDATE- Update permission for Finance moduleFinance_DELETE- Delete permission for Finance module
Modules Included
- System - Core system operations
- Users - User management
- Roles - Role management
- Permissions - Permission management
- Finance - Financial operations
- Inventory - Inventory management
- Reports - Reporting functionality
- Settings - System settings
Each module has 4 CRUD permissions (CREATE, READ, UPDATE, DELETE) = 32 total permissions
🚀 Next Steps to Complete Setup
1. Install Dependencies
cd packages/db
pnpm install2. Regenerate Migrations
Since we added the role enum, regenerate migrations:
pnpm db:generate3. Apply Migrations
pnpm db:push
# or
pnpm db:migrate4. Configure Environment
Edit apps/api/.env and add:
env
# Database (should already exist)
DATABASE_URL=postgresql://user:password@localhost:5432/dbname
# Optional: Customize superadmin credentials
SUPERADMIN_EMAIL=admin@yourdomain.com
SUPERADMIN_PASSWORD=YourSecurePassword123!
SUPERADMIN_NAME=System Administrator5. Run Seed Script
pnpm db:seedExpected output:
🌱 Starting database seed...
📝 Creating permissions...
✅ Created 32 permissions
👑 Creating superadmin role...
✅ Created superadmin role
🔐 Assigning permissions to superadmin role...
✅ Assigned 32 permissions to superadmin role
👤 Creating superadmin user...
✅ Created superadmin user
📋 Superadmin Credentials:
Email: admin@yourdomain.com
Password: YourSecurePassword123!
⚠️ Please change the password after first login!
🎉 Database seeding completed successfully!📝 Implementation Guide
Querying User with Permissions
// Get user with role and all permissions
const userWithPermissions = await db.query.user.findFirst({
where: eq(user.id, userId),
with: {
role: {
with: {
rolePermissions: {
with: {
permission: true
}
}
}
}
}
});
// Extract permission codes
const permissions = userWithPermissions?.role?.rolePermissions.map(
rp => rp.permission.code
) || [];
// Check if user has permission
const hasFinanceAccess = permissions.includes('Finance_READ');Creating Permission Guards
Example NestJS guard:
import { Injectable, CanActivate, ExecutionContext } from '@nestjs/common';
import { Reflector } from '@nestjs/core';
@Injectable()
export class PermissionsGuard implements CanActivate {
constructor(private reflector: Reflector) {}
async canActivate(context: ExecutionContext): Promise<boolean> {
const requiredPermissions = this.reflector.get<string[]>(
'permissions',
context.getHandler()
);
if (!requiredPermissions) {
return true;
}
const request = context.switchToHttp().getRequest();
const user = request.user;
// Fetch user permissions from database or session
const userPermissions = await this.getUserPermissions(user.id);
return requiredPermissions.every(permission =>
userPermissions.includes(permission)
);
}
}Using Permission Decorator
import { SetMetadata } from '@nestjs/common';
export const Permissions = (...permissions: string[]) =>
SetMetadata('permissions', permissions);
// Usage in controller
@Get('transactions')
@Permissions('Finance_READ')
async getTransactions() {
// Only users with Finance_READ permission can access
}
@Post('transactions')
@Permissions('Finance_CREATE')
async createTransaction(@Body() dto: CreateTransactionDto) {
// Only users with Finance_CREATE permission can access
}🔐 Security Best Practices
- Always validate permissions on the backend - never trust client-side checks
- Use specific permissions - prefer
Finance_READover broadadminchecks - Audit permission changes - log when roles/permissions are modified
- Principle of least privilege - give users only the permissions they need
- Regular reviews - periodically review user roles and permissions
📚 Additional Resources
🎯 Summary of Changes
Files Created:
packages/db/src/seed.ts- Seed scriptpackages/db/SEED_README.md- Seed documentation
Files Modified:
packages/db/src/schema/roles.ts- Added role enumpackages/db/src/schema/auth.ts- Added roleId to user tablepackages/db/src/index.ts- Exported roles schemapackages/db/package.json- Added seed script and dependenciespackage.json- Added root-level seed script
Database Changes:
- Added
role_typeenum with 5 values - Modified
role.nameto use enum type - Added
user.role_idforeign key - Removed
user_rolejunction table (one-to-one relationship)