Skip to content

Commit 99f2f55

Browse files
authored
Merge pull request #42 from gabito1451/role-based-authorization-middleware
Implement the role-based authorization middleware and set up the infrastructure for admin-exclusive routes
2 parents d87d7ee + 62341b2 commit 99f2f55

5 files changed

Lines changed: 135 additions & 5 deletions

File tree

package-lock.json

Lines changed: 0 additions & 5 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

src/__tests__/authorize.test.js

Lines changed: 61 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,61 @@
1+
const authorize = require('../middlewares/authorize');
2+
3+
describe('Authorize Middleware', () => {
4+
let req;
5+
let res;
6+
let next;
7+
8+
beforeEach(() => {
9+
req = {
10+
user: null,
11+
};
12+
res = {
13+
status: jest.fn().mockReturnThis(),
14+
json: jest.fn().mockReturnThis(),
15+
};
16+
next = jest.fn();
17+
});
18+
19+
it('should call next if user has an allowed role', () => {
20+
req.user = { role: 'admin' };
21+
const middleware = authorize('admin', 'superadmin');
22+
23+
middleware(req, res, next);
24+
25+
expect(next).toHaveBeenCalledWith();
26+
expect(next).not.toHaveBeenCalledWith(expect.any(Error));
27+
});
28+
29+
it('should return 401 if user is not authenticated', () => {
30+
req.user = null;
31+
const middleware = authorize('admin');
32+
33+
middleware(req, res, next);
34+
35+
expect(next).toHaveBeenCalledWith(expect.objectContaining({
36+
statusCode: 401,
37+
message: 'Authentication required'
38+
}));
39+
});
40+
41+
it('should return 403 if user role is not allowed', () => {
42+
req.user = { role: 'user' };
43+
const middleware = authorize('admin');
44+
45+
middleware(req, res, next);
46+
47+
expect(next).toHaveBeenCalledWith(expect.objectContaining({
48+
statusCode: 403,
49+
message: 'Access forbidden: insufficient permissions'
50+
}));
51+
});
52+
53+
it('should work with multiple allowed roles', () => {
54+
req.user = { role: 'editor' };
55+
const middleware = authorize('admin', 'editor');
56+
57+
middleware(req, res, next);
58+
59+
expect(next).toHaveBeenCalledWith();
60+
});
61+
});

src/app.js

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -7,6 +7,7 @@ const { getLoggerStream } = require('./utils/logger');
77
const { globalLimiter, authLimiter } = require('./middlewares/rateLimiter');
88
const authRoutes = require('./routes/auth.routes');
99
const protectedRoutes = require('./routes/protected.routes');
10+
const adminRoutes = require('./routes/admin.routes');
1011

1112
const app = express();
1213

@@ -40,6 +41,9 @@ app.use('/api/auth', authLimiter, authRoutes);
4041
// Protected routes
4142
app.use('/api/protected', protectedRoutes);
4243

44+
// Admin routes
45+
app.use('/api/admin', adminRoutes);
46+
4347
// Global error handling middleware - must be registered last
4448
const errorHandler = require('./middlewares/errorHandler');
4549
app.use(errorHandler);

src/middlewares/authorize.js

Lines changed: 29 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,29 @@
1+
/**
2+
* Middleware to restrict access by user role
3+
* @param {...string} allowedRoles - Roles allowed to access the route
4+
* @returns {Function} Middleware function
5+
*/
6+
const authorize = (...allowedRoles) => {
7+
return (req, res, next) => {
8+
// Check if user is authenticated (req.user must be populated by auth middleware)
9+
if (!req.user) {
10+
const error = new Error('Authentication required');
11+
error.statusCode = 401;
12+
error.isOperational = true;
13+
return next(error);
14+
}
15+
16+
// Check if user's role is in the list of allowed roles
17+
if (!allowedRoles.includes(req.user.role)) {
18+
const error = new Error('Access forbidden: insufficient permissions');
19+
error.statusCode = 403;
20+
error.isOperational = true;
21+
return next(error);
22+
}
23+
24+
// User is authorized
25+
next();
26+
};
27+
};
28+
29+
module.exports = authorize;

src/routes/admin.routes.js

Lines changed: 41 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,41 @@
1+
const express = require('express');
2+
const authenticate = require('../middlewares/auth');
3+
const authorize = require('../middlewares/authorize');
4+
const { sendSuccess } = require('../utils/response');
5+
6+
const router = express.Router();
7+
8+
/**
9+
* Admin Routes
10+
* All routes in this router require authentication and 'admin' role
11+
*/
12+
13+
// Global middleware for this router
14+
router.use(authenticate);
15+
router.use(authorize('admin'));
16+
17+
// GET /api/admin/dashboard - Admin dashboard data
18+
router.get('/dashboard', (req, res) => {
19+
sendSuccess(res, {
20+
admin: {
21+
id: req.user._id,
22+
fullName: req.user.fullName,
23+
role: req.user.role
24+
},
25+
stats: {
26+
totalUsers: 0,
27+
activeCampaigns: 0,
28+
pendingVerifications: 0
29+
}
30+
}, 200, 'Admin dashboard statistics retrieved');
31+
});
32+
33+
// GET /api/admin/users - List all users
34+
router.get('/users', (req, res) => {
35+
sendSuccess(res, {
36+
users: [],
37+
message: 'User management system placeholder'
38+
}, 200, 'Users retrieved successfully');
39+
});
40+
41+
module.exports = router;

0 commit comments

Comments
 (0)