![]() Server : Apache/2 System : Linux server-15-235-50-60 5.15.0-164-generic #174-Ubuntu SMP Fri Nov 14 20:25:16 UTC 2025 x86_64 User : gositeme ( 1004) PHP Version : 8.2.29 Disable Function : exec,system,passthru,shell_exec,proc_close,proc_open,dl,popen,show_source,posix_kill,posix_mkfifo,posix_getpwuid,posix_setpgid,posix_setsid,posix_setuid,posix_setgid,posix_seteuid,posix_setegid,posix_uname Directory : /home/gositeme/domains/lavocat.ca/public_html/src/lib/ |
import { prisma } from './prisma';
export interface PermissionCheck {
role: string;
resource: string;
action: string;
conditions?: any;
userId?: string;
targetId?: string;
}
export interface User {
id: string;
role: string;
email: string;
name?: string;
}
// Role hierarchy for permission inheritance
const ROLE_HIERARCHY = {
'SUPERADMIN': 999,
'ADMIN': 100,
'LAWYER': 90,
'SECRETARY': 50,
'CLERK': 40,
'ASSISTANT': 30,
'USER': 10
};
// Check if user has permission for a specific action
export async function hasPermission(
user: User,
resource: string,
action: string,
options?: {
targetId?: string;
conditions?: any;
}
): Promise<boolean> {
try {
// SUPERADMIN has all permissions
if (user.role === 'SUPERADMIN' || user.role === 'SUPERADMIN') {
return true;
}
// Find specific permission
const permission = await prisma.permission.findUnique({
where: {
role_resource_action: {
role: user.role,
resource,
action
}
}
});
if (!permission) {
return false;
}
// Check additional conditions if they exist
if (permission.conditions) {
const conditions = JSON.parse(permission.conditions);
return checkConditions(user, conditions, options);
}
return true;
} catch (error) {
console.error('Error checking permission:', error);
return false;
}
}
// Check conditional permissions
function checkConditions(
user: User,
conditions: any,
options?: { targetId?: string; conditions?: any }
): boolean {
// Scope-based conditions
if (conditions.scope) {
switch (conditions.scope) {
case 'own':
return options?.targetId === user.id;
case 'assigned':
// TODO: Check if user is assigned to the case
return true; // Placeholder - implement case assignment check
case 'basic_info':
return true; // Allow reading basic info
default:
return true;
}
}
// Field-based restrictions
if (conditions.fields) {
return true; // Field-level permissions handled at application level
}
// Type-based restrictions
if (conditions.types) {
return true; // Type-level permissions handled at application level
}
return true;
}
// Check multiple permissions at once
export async function hasAnyPermission(
user: User,
checks: Array<{ resource: string; action: string; conditions?: any }>
): Promise<boolean> {
for (const check of checks) {
if (await hasPermission(user, check.resource, check.action, check.conditions)) {
return true;
}
}
return false;
}
// Check if user can access a specific case
export async function canAccessCase(
user: User,
caseId: string,
action: 'read' | 'write' | 'delete' = 'read'
): Promise<boolean> {
// SUPERADMIN and LAWYER roles can access all cases
if (user.role === 'SUPERADMIN' || user.role === 'SUPERADMIN' || user.role === 'LAWYER' || user.role === 'ADMIN') {
return hasPermission(user, 'cases', action);
}
// Check if user is assigned to the case
const assignment = await prisma.caseAssignment.findFirst({
where: {
registrationId: caseId,
userId: user.id,
isActive: true
}
});
if (assignment) {
return hasPermission(user, 'cases', action, { targetId: caseId });
}
// Check if it's the user's own case (for clients)
if (user.role === 'USER') {
const registration = await prisma.registration.findFirst({
where: {
id: caseId,
userId: user.id
}
});
return !!registration;
}
return false;
}
// Check document access permissions
export async function canAccessDocument(
user: User,
documentId: string,
action: 'read' | 'write' | 'download' | 'delete' = 'read'
): Promise<boolean> {
// Get document details
const document = await prisma.document.findUnique({
where: { id: documentId },
include: { registration: true }
});
if (!document) {
return false;
}
// Check case access first
const canAccessRelatedCase = await canAccessCase(user, document.registrationId, 'read');
if (!canAccessRelatedCase) {
return false;
}
// Check specific document permission
return hasPermission(user, 'documents', action, { targetId: documentId });
}
// Get user's role level for comparisons
export function getRoleLevel(role: string): number {
return ROLE_HIERARCHY[role as keyof typeof ROLE_HIERARCHY] || 0;
}
// Check if user can manage another user
export function canManageUser(managerRole: string, targetRole: string): boolean {
return getRoleLevel(managerRole) > getRoleLevel(targetRole);
}
// Permission middleware for API routes
export function requirePermission(resource: string, action: string) {
return async (user: User, options?: any) => {
const allowed = await hasPermission(user, resource, action, options);
if (!allowed) {
throw new Error(`Insufficient permissions: ${resource}:${action}`);
}
return true;
};
}
// Get all permissions for a role
export async function getRolePermissions(role: string) {
return prisma.permission.findMany({
where: { role },
select: {
resource: true,
action: true,
conditions: true
}
});
}
// Assign user to case
export async function assignUserToCase(
caseId: string,
userId: string,
role: string,
assignedBy: string
): Promise<boolean> {
try {
await prisma.caseAssignment.create({
data: {
registrationId: caseId,
userId,
role,
assignedBy
}
});
return true;
} catch (error) {
console.error('Error assigning user to case:', error);
return false;
}
}
// Remove user from case
export async function removeUserFromCase(
caseId: string,
userId: string
): Promise<boolean> {
try {
await prisma.caseAssignment.updateMany({
where: {
registrationId: caseId,
userId
},
data: {
isActive: false
}
});
return true;
} catch (error) {
console.error('Error removing user from case:', error);
return false;
}
}
// Get users assigned to a case
export async function getCaseAssignments(caseId: string) {
return prisma.caseAssignment.findMany({
where: {
registrationId: caseId,
isActive: true
},
include: {
user: {
select: {
id: true,
name: true,
email: true,
role: true,
title: true
}
}
}
});
}