48 lines
1.2 KiB
TypeScript
48 lines
1.2 KiB
TypeScript
import { Request, Response, NextFunction } from 'express'
|
|
import { EmployeeRole } from '@prisma/client'
|
|
|
|
// Role hierarchy: OWNER > MANAGER > AGENT
|
|
const ROLE_RANK: Record<EmployeeRole, number> = {
|
|
OWNER: 3,
|
|
MANAGER: 2,
|
|
AGENT: 1,
|
|
}
|
|
|
|
/**
|
|
* requireRole(minimumRole)
|
|
* Middleware that blocks requests from employees whose role rank
|
|
* is below the required minimum.
|
|
*
|
|
* Usage:
|
|
* router.post('/invite', requireRole('OWNER'), handler)
|
|
* router.patch('/something', requireRole('MANAGER'), handler)
|
|
*/
|
|
export function requireRole(minimumRole: EmployeeRole) {
|
|
return (req: Request, res: Response, next: NextFunction) => {
|
|
const employee = req.employee
|
|
|
|
if (!employee) {
|
|
return res.status(401).json({
|
|
error: 'unauthenticated',
|
|
message: 'Authentication required',
|
|
statusCode: 401,
|
|
})
|
|
}
|
|
|
|
const employeeRank = ROLE_RANK[employee.role as EmployeeRole] ?? 0
|
|
const requiredRank = ROLE_RANK[minimumRole] ?? 99
|
|
|
|
if (employeeRank < requiredRank) {
|
|
return res.status(403).json({
|
|
error: 'forbidden',
|
|
message: `This action requires the ${minimumRole} role or higher`,
|
|
statusCode: 403,
|
|
requiredRole: minimumRole,
|
|
yourRole: employee.role,
|
|
})
|
|
}
|
|
|
|
next()
|
|
}
|
|
}
|