Spaces:
Build error
Build error
File size: 4,339 Bytes
0939458 | 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 | // RBAC Middleware for Express/Fastify API
export enum Role {
CLIENT = 'CLIENT',
VENDOR = 'VENDOR',
ADMIN = 'ADMIN',
}
export enum Permission {
// Client permissions
VIEW_OWN_BOOKINGS = 'view_own_bookings',
CREATE_BOOKING = 'create_booking',
SIGN_CONTRACT = 'sign_contract',
VIEW_OWN_CONTRACTS = 'view_own_contracts',
REQUEST_AMENDMENT = 'request_amendment',
// Vendor permissions
MANAGE_PROFILE = 'manage_profile',
MANAGE_PACKAGES = 'manage_packages',
VIEW_LEADS = 'view_leads',
CREATE_CONTRACT = 'create_contract',
SEND_CONTRACT = 'send_contract',
MANAGE_DELIVERABLES = 'manage_deliverables',
// Admin permissions
MODERATE_VENDORS = 'moderate_vendors',
MODERATE_CONTENT = 'moderate_content',
OVERSEE_CONTRACTS = 'oversee_contracts',
MEDIATE_DISPUTES = 'mediate_disputes',
EXPORT_AUDIT_TRAIL = 'export_audit_trail',
MANAGE_CATEGORIES = 'manage_categories',
IMPERSONATE_USERS = 'impersonate_users',
MANAGE_USERS = 'manage_users',
}
const rolePermissions: Record<Role, Permission[]> = {
[Role.CLIENT]: [
Permission.VIEW_OWN_BOOKINGS,
Permission.CREATE_BOOKING,
Permission.SIGN_CONTRACT,
Permission.VIEW_OWN_CONTRACTS,
Permission.REQUEST_AMENDMENT,
],
[Role.VENDOR]: [
Permission.MANAGE_PROFILE,
Permission.MANAGE_PACKAGES,
Permission.VIEW_LEADS,
Permission.CREATE_CONTRACT,
Permission.SEND_CONTRACT,
Permission.MANAGE_DELIVERABLES,
],
[Role.ADMIN]: Object.values(Permission),
}
export function hasPermission(role: Role, permission: Permission): boolean {
return rolePermissions[role]?.includes(permission) ?? false
}
export function requirePermission(permission: Permission) {
return function (req: any, res: any, next: Function) {
const role = req.user?.role as Role
if (!role || !hasPermission(role, permission)) {
return res.status(403).json({
error: 'Forbidden',
message: `Missing required permission: ${permission}`,
})
}
next()
}
}
export function requireRole(...roles: Role[]) {
return function (req: any, res: any, next: Function) {
const role = req.user?.role as Role
if (!role || !roles.includes(role)) {
return res.status(403).json({
error: 'Forbidden',
message: `Access restricted to: ${roles.join(', ')}`,
})
}
next()
}
}
// Contract-specific RBAC: only the vendor who created it or the client who received it can access
export function requireContractAccess(contractIdParam: string = 'contractId') {
return async function (req: any, res: any, next: Function) {
const contractId = req.params[contractIdParam]
const userId = req.user?.id
const role = req.user?.role as Role
if (role === Role.ADMIN && hasPermission(role, Permission.OVERSEE_CONTRACTS)) {
return next()
}
// Fetch contract and verify ownership
try {
const contract = await fetchContract(contractId)
if (!contract) return res.status(404).json({ error: 'Contract not found' })
const hasAccess =
(role === Role.VENDOR && contract.vendorId === userId) ||
(role === Role.CLIENT && contract.clientId === userId)
if (!hasAccess) return res.status(403).json({ error: 'Access denied' })
next()
} catch (err) {
return res.status(500).json({ error: 'Internal error' })
}
}
}
async function fetchContract(id: string) {
// Replace with actual DB call
return { id, vendorId: '', clientId: '' }
}
// ββ Impersonation (admin-only) ββ
// Admins can impersonate with TTL-bound sessions and audit reason field
export async function impersonateUser(adminId: string, targetUserId: string, reason: string, ttlMinutes: number = 30) {
// 1. Verify admin has IMPERSONATE_USERS permission
// 2. Create session with role=targetUser.role, token expires in ttlMinutes
// 3. Log to AdminAuditLog:
// { adminId, action: 'impersonate_start', target: targetUserId, detail: reason, ttl: ttlMinutes }
// 4. Return impersonation session token
}
// ββ Rate Limiting ββ
// Keyed on IP + method + pathname, backed by Redis
// Implementation: use rate-limiter-flexible with Redis store
// export const rateLimiter = new RateLimiterRedis({...})
|