feat: implement admin-only middleware and enhance permission checks with logging
This commit is contained in:
parent
e86986d40c
commit
2a69b83d84
@ -1,6 +1,7 @@
|
||||
const UnitOfWork = require('../../database/UnitOfWork');
|
||||
const PermissionService = require('../../services/permissions/PermissionService');
|
||||
const PermissionRepository = require('../../repositories/permissions/PermissionRepository');
|
||||
const { logger } = require('../../middleware/logger');
|
||||
|
||||
class PermissionController {
|
||||
static async list(req, res) {
|
||||
@ -37,10 +38,17 @@ class PermissionController {
|
||||
static async getUserPermissions(req, res) {
|
||||
// Access control: only self or admin/super_admin can view
|
||||
const requestedUserId = Number(req.params.id);
|
||||
const requesterUserId = req.user.userId;
|
||||
const requesterUserId = Number(req.user.userId ?? req.user.id ?? req.user.sub);
|
||||
const requesterRole = req.user.role;
|
||||
|
||||
if (requestedUserId !== requesterUserId && requesterRole !== 'admin' && requesterRole !== 'super_admin') {
|
||||
const requesterIdLog = Number.isNaN(requesterUserId) ? (req.user.userId ?? req.user.id ?? req.user.sub) : requesterUserId;
|
||||
logger.warn('PermissionController.getUserPermissions:forbidden', {
|
||||
requestedUserId,
|
||||
requesterUserId: requesterIdLog,
|
||||
requesterRole,
|
||||
route: req.originalUrl
|
||||
});
|
||||
return res.status(403).json({ success: false, message: 'Forbidden' });
|
||||
}
|
||||
|
||||
|
||||
16
middleware/adminOnly.js
Normal file
16
middleware/adminOnly.js
Normal file
@ -0,0 +1,16 @@
|
||||
const { logger } = require('./logger');
|
||||
|
||||
function adminOnly(req, res, next) {
|
||||
const role = req.user?.role;
|
||||
if (!role || !['admin', 'super_admin'].includes(role)) {
|
||||
logger.warn('adminOnly:forbidden', {
|
||||
role,
|
||||
route: req.originalUrl,
|
||||
method: req.method
|
||||
});
|
||||
return res.status(403).json({ success: false, message: 'Admin role required' });
|
||||
}
|
||||
next();
|
||||
}
|
||||
|
||||
module.exports = adminOnly;
|
||||
@ -4,6 +4,10 @@ const { logger } = require('./logger');
|
||||
function authMiddleware(req, res, next) {
|
||||
const authHeader = req.headers.authorization;
|
||||
if (!authHeader || !authHeader.startsWith('Bearer ')) {
|
||||
logger.warn('authMiddleware:missingToken', {
|
||||
method: req.method,
|
||||
route: req.originalUrl
|
||||
});
|
||||
return res.status(401).json({ success: false, message: 'No access token provided' });
|
||||
}
|
||||
|
||||
@ -33,9 +37,15 @@ function authMiddleware(req, res, next) {
|
||||
// console fallback for local dev
|
||||
console.log('[authMiddleware] verified', authDebug);
|
||||
|
||||
// Normalize common user fields for downstream checks
|
||||
const normalizedUserId = payload.userId ?? payload.id ?? payload.sub;
|
||||
const normalizedRole = payload.role ?? payload.userRole ?? payload.user_role ?? payload.type;
|
||||
|
||||
// Attach user info to request (with normalized userType for downstream checks)
|
||||
req.user = {
|
||||
...payload,
|
||||
userId: normalizedUserId,
|
||||
role: normalizedRole ?? payload.role,
|
||||
userType: payload.userType ?? payload.user_type,
|
||||
user_type: payload.user_type ?? payload.userType
|
||||
};
|
||||
|
||||
@ -2,6 +2,7 @@ const express = require('express');
|
||||
const router = express.Router();
|
||||
|
||||
const authMiddleware = require('../middleware/authMiddleware');
|
||||
const adminOnly = require('../middleware/adminOnly');
|
||||
const AdminUserController = require('../controller/admin/AdminUserController');
|
||||
const DocumentTemplateController = require('../controller/documentTemplate/DocumentTemplateController');
|
||||
const CompanyStampController = require('../controller/companyStamp/CompanyStampController');
|
||||
@ -10,12 +11,6 @@ const AffiliateController = require('../controller/affiliate/AffiliateController
|
||||
const NewsController = require('../controller/news/NewsController');
|
||||
|
||||
// Helper middlewares for company-stamp
|
||||
function adminOnly(req, res, next) {
|
||||
if (!req.user || !['admin','super_admin'].includes(req.user.role)) {
|
||||
return res.status(403).json({ error: 'Admin role required' });
|
||||
}
|
||||
next();
|
||||
}
|
||||
function forceCompanyForAdmin(req, res, next) {
|
||||
if (req.user && ['admin','super_admin'].includes(req.user.role) && req.user.user_type !== 'company') {
|
||||
req.user.user_type = 'company';
|
||||
|
||||
@ -3,6 +3,7 @@ const path = require('path');
|
||||
const router = express.Router();
|
||||
|
||||
const authMiddleware = require('../middleware/authMiddleware');
|
||||
const adminOnly = require('../middleware/adminOnly');
|
||||
const UserSettingsController = require('../controller/auth/UserSettingsController');
|
||||
const ReferralTokenController = require('../controller/referral/ReferralTokenController');
|
||||
const ReferralRegistrationController = require('../controller/referral/ReferralRegistrationController');
|
||||
@ -24,18 +25,6 @@ const NewsController = require('../controller/news/NewsController');
|
||||
const InvoiceController = require('../controller/invoice/InvoiceController'); // NEW
|
||||
|
||||
// small helpers copied from original files
|
||||
function adminOnly(req, res, next) {
|
||||
if (!req.user || !['admin', 'super_admin'].includes(req.user.role)) {
|
||||
return res.status(403).json({ error: 'Forbidden: Admins only' });
|
||||
}
|
||||
next();
|
||||
}
|
||||
function requireAdmin(req, res, next) {
|
||||
if (!req.user || req.user.role !== 'admin') {
|
||||
return res.status(403).json({ success: false, message: 'Forbidden: Admins only.' });
|
||||
}
|
||||
next();
|
||||
}
|
||||
|
||||
// NEW helper used by company-stamp routes
|
||||
function forceCompanyForAdmin(req, res, next) {
|
||||
@ -55,19 +44,19 @@ router.get('/users/:id/full', authMiddleware, UserController.getFullUserData);
|
||||
router.get('/user/settings', authMiddleware, UserSettingsController.getSettings);
|
||||
router.get('/users/:id/permissions', authMiddleware, PermissionController.getUserPermissions);
|
||||
router.get('/admin/users/:id/full', authMiddleware, adminOnly, AdminUserController.getFullUserAccountDetails);
|
||||
router.get('/admin/users/:id/detailed', authMiddleware, requireAdmin, AdminUserController.getDetailedUserInfo);
|
||||
router.get('/admin/users/:id/detailed', authMiddleware, adminOnly, AdminUserController.getDetailedUserInfo);
|
||||
router.get('/users/:id/documents', authMiddleware, UserController.getUserDocumentsAndContracts);
|
||||
router.get('/verify-password-reset', (req, res) => { /* Note: was moved from PasswordResetController.verifyPasswordResetToken */ res.status(204).end(); }); // keep placeholder if controller already registered via other verb
|
||||
|
||||
// admin.js GETs
|
||||
router.get('/admin/user-stats', authMiddleware, requireAdmin, AdminUserController.getUserStats);
|
||||
router.get('/admin/user-list', authMiddleware, requireAdmin, AdminUserController.getUserList);
|
||||
router.get('/admin/verification-pending-users', authMiddleware, requireAdmin, AdminUserController.getVerificationPendingUsers);
|
||||
router.get('/admin/unverified-users', authMiddleware, requireAdmin, AdminUserController.getUnverifiedUsers);
|
||||
router.get('/admin/user/:id/documents', authMiddleware, requireAdmin, UserDocumentController.getAllDocumentsForUser);
|
||||
router.get('/admin/server-status', authMiddleware, requireAdmin, ServerStatusController.getStatus);
|
||||
router.get('/admin/user-stats', authMiddleware, adminOnly, AdminUserController.getUserStats);
|
||||
router.get('/admin/user-list', authMiddleware, adminOnly, AdminUserController.getUserList);
|
||||
router.get('/admin/verification-pending-users', authMiddleware, adminOnly, AdminUserController.getVerificationPendingUsers);
|
||||
router.get('/admin/unverified-users', authMiddleware, adminOnly, AdminUserController.getUnverifiedUsers);
|
||||
router.get('/admin/user/:id/documents', authMiddleware, adminOnly, UserDocumentController.getAllDocumentsForUser);
|
||||
router.get('/admin/server-status', authMiddleware, adminOnly, ServerStatusController.getStatus);
|
||||
// Contract preview for admin: latest active by user type
|
||||
router.get('/admin/contracts/:id/preview', authMiddleware, requireAdmin, DocumentTemplateController.previewLatestForUser);
|
||||
router.get('/admin/contracts/:id/preview', authMiddleware, adminOnly, DocumentTemplateController.previewLatestForUser);
|
||||
|
||||
// permissions.js GETs
|
||||
router.get('/permissions', authMiddleware, PermissionController.list);
|
||||
|
||||
@ -2,6 +2,7 @@ const express = require('express');
|
||||
const router = express.Router();
|
||||
|
||||
const authMiddleware = require('../middleware/authMiddleware');
|
||||
const adminOnly = require('../middleware/adminOnly');
|
||||
const DocumentTemplateController = require('../controller/documentTemplate/DocumentTemplateController');
|
||||
const CompanyStampController = require('../controller/companyStamp/CompanyStampController'); // <-- added
|
||||
const CoffeeController = require('../controller/admin/CoffeeController');
|
||||
@ -16,12 +17,6 @@ const multer = require('multer');
|
||||
const upload = multer({ storage: multer.memoryStorage() });
|
||||
|
||||
// Helper middlewares for company-stamp
|
||||
function adminOnly(req, res, next) {
|
||||
if (!req.user || !['admin','super_admin'].includes(req.user.role)) {
|
||||
return res.status(403).json({ error: 'Admin role required' });
|
||||
}
|
||||
next();
|
||||
}
|
||||
function forceCompanyForAdmin(req, res, next) {
|
||||
if (req.user && ['admin','super_admin'].includes(req.user.role) && req.user.user_type !== 'company') {
|
||||
req.user.user_type = 'company';
|
||||
|
||||
@ -2,6 +2,7 @@ const express = require('express');
|
||||
const router = express.Router();
|
||||
|
||||
const authMiddleware = require('../middleware/authMiddleware');
|
||||
const adminOnly = require('../middleware/adminOnly');
|
||||
|
||||
// Controllers used by POST routes
|
||||
const LoginController = require('../controller/login/LoginController');
|
||||
@ -107,15 +108,6 @@ router.post('/admin/send-password-reset/:userId', authMiddleware, adminOnly, asy
|
||||
}
|
||||
});
|
||||
|
||||
// Helper middleware for company-stamp routes
|
||||
function adminOnly(req, res, next) {
|
||||
if (!req.user || !['admin','super_admin'].includes(req.user.role)) {
|
||||
return res.status(403).json({ error: 'Admin role required' });
|
||||
}
|
||||
next();
|
||||
}
|
||||
|
||||
|
||||
// NEW: ensure service sees a "company" user_type for admin users
|
||||
function forceCompanyForAdmin(req, res, next) {
|
||||
if (req.user && ['admin','super_admin'].includes(req.user.role) && req.user.user_type !== 'company') {
|
||||
|
||||
@ -2,20 +2,13 @@ const express = require('express');
|
||||
const router = express.Router();
|
||||
|
||||
const authMiddleware = require('../middleware/authMiddleware');
|
||||
const adminOnly = require('../middleware/adminOnly');
|
||||
const AdminUserController = require('../controller/admin/AdminUserController');
|
||||
const DocumentTemplateController = require('../controller/documentTemplate/DocumentTemplateController');
|
||||
const CoffeeController = require('../controller/admin/CoffeeController');
|
||||
const multer = require('multer');
|
||||
const upload = multer({ storage: multer.memoryStorage() });
|
||||
|
||||
// Helper middleware for admin-only routes (keeps consistency with other route files)
|
||||
function adminOnly(req, res, next) {
|
||||
if (!req.user || !['admin','super_admin'].includes(req.user.role)) {
|
||||
return res.status(403).json({ error: 'Admin role required' });
|
||||
}
|
||||
next();
|
||||
}
|
||||
|
||||
// PUT /admin/users/:id/permissions (moved from routes/admin.js)
|
||||
router.put('/admin/users/:id/permissions', authMiddleware, adminOnly, AdminUserController.updateUserPermissions);
|
||||
|
||||
|
||||
Loading…
Reference in New Issue
Block a user