I already resolve this problem. I find Express.js role-based permissions middleware and use it. It's great and I will re-write my code!
I'm want to check multi permistion but it not working for me.
I create 3 middlwares to check Permistion: requiredAuth, checkAdmin and checkCompanyManager.
Model: User: {name, permistion, isBlocked, company: {id, userPermistion}}
requiredAuth funtion will check and find signedUser, and set it tores.locals.user
const checkAdmin = (req, res, next) => {
let user = res.locals.user
if (user.permission === 2) next()
else res.json({errors: "Only admin can do this action"})
}
const checkCompanyManager = (req, res, next) => {
let user = res.locals.user
let companyId = req.body.companyId ? req.body.companyId : req.query.companyId
if (user.company.id && user.company.id.equals(companyId)
&& user.company.userPermistion === 1) next()
else res.json({errors: "Only company member can do this action"})
}
And last, I use all in router to check action block user (Only admin or company manager can block user)
router.post('/admin/block-by-ids',
requiredAuth,
checkAdmin || checkCompanyManager,
userController.blockByIds
)
But it's not working, because if checkAdmin wrong, it's break and return json, not run checkCompanyManager I can solve this problem as follows:
router.post('/admin/block-by-ids',
requiredAuth,
(req, res, next) => {
let user = res.locals.user
let companyId = req.body.companyId
if ((user.permission === 2) ||
(user.company.id && user.company.id.equals(companyId) &&
user.company.userPermistion === 1)) {
next()
} else next("Only admin or company manager can do this action")
},
userController.blockByIds
)
But it's not fun! I want only use middleware to check and do not want to write code again. How can I do this? I want an idea from you!
The || operator does not do what you think it does. It returns the first truthy value:
var a = 1 || 2; // a is 1
What you need is an OR middleware. Something like:
function or (middleware1, middleware2) {
return function (req, res, next) {
var alreadyCalledNext = false;
function resolve () {
if (!alreadyCalledNext) {
alreadyCalledNext = true;
next();
}
}
middleware1(req,res,resolve);
middleware2(req,res,resolve);
}
}
router.post('/admin/block-by-ids',
requiredAuth,
or(checkAdmin, checkCompanyManager),
userController.blockByIds
)
But the above implementation runs into another problem. Once you've sent res.json you cannot send another response. So if either checkAdmin or checkCompanyManager fails you need to stop them from sending res.json unless both fails. So you need to stub res and pass a fake res (just like what we did with next above):
function or (middleware1, middleware2) {
return function (req, res, next) {
var alreadyCalledNext = false;
function resolve () {
if (!alreadyCalledNext) {
alreadyCalledNext = true;
next();
}
}
var jsonCount = 0;
var fakeRes = {
locals: res.locals,
json: function (data) {
jsonCount ++;
if (jsonCount >= 2) { // both must fail for OR to fail
res.json(data);
}
}
}
middleware1(req,fakeRes,resolve);
middleware2(req,fakeRes,resolve);
}
}
This should work.
IMHO the solution above feels over-engineered. I would personally make checkAdmin and checkCompanyManager regular functions returning boolean then wrap them in a checkPermissions middleware:
const isAdmin = (req,res) => {
let user = res.locals.user
return user.permission === 2
}
const isCompanyManager = (req,res) => {
let user = res.locals.user
let companyId = req.body.companyId ? req.body.companyId : req.query.companyId
return user.company.id && user.company.id.equals(companyId) && user.company.userPermistion === 1
}
const checkPermissions = function (checks) {
return (req, res, next) => {
// Call next if any check passes:
for (let i=0; i<checks.length; i++) {
if (checks[i](req,res)) return next();
}
res.json({errors: "You don't have authorization to do this action"})
}
}
router.post('/admin/block-by-ids',
requiredAuth,
checkPermissions([isAdmin, isCompanyManager]),
userController.blockByIds
)
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With