As Dong has pointed out, implementing an Access Control List, is the answer — role based access control, extrapolating from the question.
How simple you would like to keep the design of your RBAC system, depends on the complexity of your app, and the corresponding requirements.
Here is a very simple instance of how it could be done. Given, that a role attribute is defined for all of the users, we could define a roles object atop of our controller file:
const roles = {
'normal': { can: [] },
'admin': { can: ['read'] },
'superadmin': { can: ['read', 'write'] },
}
...
exports.getAdminPage = (req, res) => {
const operation = 'read';
// req.user is set post authentication
if (
!roles[req.user.role] ||
roles[req.user.role].can.indexOf(operation) === -1
) {
// early return if the access control check fails
return res.status(404).end(); // or an "access denied" page
}
// rest of the code...
...
}
...
You can extend the roles object to accommodate conditional access control. Imagine your post on a social network (say, Hashnode :); as the author only you have the ability to edit it, so in this situation, we can implement the ACL in the corresponding Post controller file, like so:
const roles = {
'normal': {
can: [
'read',
'write',
{
name: 'edit',
when: (params) => params.reqUserID === params.authorID,
}
]
},
...
}
...
exports.sendDraft = (req, res) => {
const operation = 'edit'
let userCanObject = '';
roles[req.user.role] && roles[req.user.role].can.forEach(
can => {
if (
(typeof can === 'string' && can === operation) ||
(typeof can === 'object' && can.name === operation)
) {
userCanObject = op;
}
}
);
Post
.findById(req.postID)
.select('author')
.exec((err, post) => {
...
const userCanDoOp = (
typeof userCanObject === 'string' &&
userCanObject === operation
) || (
typeof userCanObject === 'object' &&
userCanObject.name === operation &&
userCanObject.when({
reqUserID: req.user.id,
authorID: post.author
})
);
// early return
if (!userCanDoOp) {
return res.status(404).end();
}
...
}
...
}
Of course, as your app scales, the recommended way is to move the RBAC code into its own module. I highly recommend the following article for a complete overview on implementing access control, the correct way™. It is an awesome read!
Hope this helps! :)