Files
starr-philproperties/apps/api/middleware/rbac.middleware.js
T

77 lines
3.2 KiB
JavaScript

/***********************************************************************************************************************************************************************
* File Name: rbac.middleware.js
* Type of Program: Middleware
* Description: Role-Based Access Control (RBAC) guards.
* Hierarchy: admin > staff > user (client)
*
* Exported guards:
* - requireClient() → acc_type in ['user', 'staff', 'admin']
* - requireStaff() → acc_type in ['staff', 'admin']
* - requireAdmin() → acc_type === 'admin' only
* - requireOwnerOrStaff() → owns the resource OR is staff/admin
* - requireOwnerOrAdmin() → owns the resource OR is admin
*
* Author: rgrgogu
* Date Created: Oct. 6, 2025
***********************************************************************************************************************************************************************
* HOW TO USE:
* const { requireAdmin, requireStaff } = require('../middleware/rbac.middleware');
* // Must be used AFTER authenticate middleware
* router.get('/users', authenticate, requireStaff(), handler);
* router.delete('/users/:id', authenticate, requireAdmin(), handler);
***********************************************************************************************************************************************************************/
const R = require('../utils/response.util');
const ROLES = { user: 1, staff: 2, admin: 3 };
/**
* Generic role guard factory.
* @param {string[]} allowed - list of acc_type values that can pass
*/
const requireRole = (allowed) => (req, res, next) => {
if (!req.user)
return R.error(res, 'Authentication required.', 401);
if (!allowed.includes(req.user.acc_type))
return R.error(res, 'You do not have permission to access this resource.', 403);
next();
};
/** Any logged-in user (client, staff, admin) */
const requireClient = () => requireRole(['user', 'staff', 'admin']);
/** Staff or Admin */
const requireStaff = () => requireRole(['staff', 'admin']);
/** Admin only */
const requireAdmin = () => requireRole(['admin']);
/**
* Allows resource owner OR staff/admin.
* Reads the owner's user_id from req.params.user_id or req.params.id.
*/
const requireOwnerOrStaff = () => (req, res, next) => {
if (!req.user) return R.error(res, 'Authentication required.', 401);
const targetId = Number(req.params.user_id || req.params.id);
const isOwner = req.user.user_id === targetId;
const elevated = ['staff', 'admin'].includes(req.user.acc_type);
if (!isOwner && !elevated)
return R.error(res, 'You do not have permission.', 403);
next();
};
/**
* Allows resource owner OR admin only.
*/
const requireOwnerOrAdmin = () => (req, res, next) => {
if (!req.user) return R.error(res, 'Authentication required.', 401);
const targetId = Number(req.params.user_id || req.params.id);
const isOwner = req.user.user_id === targetId;
const isAdmin = req.user.acc_type === 'admin';
if (!isOwner && !isAdmin)
return R.error(res, 'You do not have permission.', 403);
next();
};
module.exports = { requireClient, requireStaff, requireAdmin, requireOwnerOrStaff, requireOwnerOrAdmin };