1024programmer Java Express.js/Mongooseuserrolesandpermissions-Express.js/Mongooseuserrolesandpermissions

Express.js/Mongooseuserrolesandpermissions-Express.js/Mongooseuserrolesandpermissions

I am creating a fairly simple site with Node, Express and Mongoose. The site needs to have user roles and permissions. My thoughts are that i’ll validate permissions based on user interaction with the data base.

I’m creating a fairly simple site using Node, Express and Mongoose. The site requires user roles and permissions. The idea is that I will validate permissions based on the user’s interaction with the database.

In mongoose is there a way to determine the type of CRUD operation currently being carried out possibly by a user?

Is there a way in mongoose to determine the type of CRUD operation that may currently be performed by the user?

5 solutions

#1


8

I’ve found a solution. It would be great to hear peoples opinions on this.

I found the solution. It would be great to hear people’s thoughts on this.

I have a permissions config object which defines each role and their permissions.

I have a permission configuration object that defines each role and its permissions.

Permissions config object

Permission configuration object

roles.admin = {
     id: "admin",
     name: "Admin",
     description: "",
     resource : [
         {
             id: 'blog',
             permissions: ['create', 'read', 'update', 'delete']
         },
         {
             id: 'user',
             permissions: ['create', 'read', 'update', 'delete']
         },
         {
             id: 'journal',
             permissions: ['create', 'read', 'update', 'delete']
         },

     ]
 };

 roles.editor = {
     id: "editor",
     name: "Editor",
     description: "",
     resource : [
         {
             id: 'blog',
             permissions: ['create', 'read', 'update', 'delete']
         },
         {
             id: 'user',
             permissions: ['read']
         },
         {
             id: 'journal',
             permissions: ['create', 'read', 'update']
         },

     ]
 };
 

Middleware function

var roles = require('./config');


 var permissiOns= (function () {

   var getRoles = function (role) {

     var rolesArr = [];

     if (typeof role === 'object' && Array.isArray(role)) {

         // Returns selected roles
         for (var i = 0, len = role.length; i <len; i++) {
             rolesArr.push(roles[role[i]]);
         };
         return rolesArr;

     } else if (typeof role === 'string' || !role) {

         // Returns all roles
         if (!role) {
             for (var role in roles) {
                 rolesArr.push(roles[role]);
             };
         }

         // Returns single role
         rolesArr.push(roles[role]);
         return rolesArr;

     }

 },
 check = function (action, resource, loginRequired) {

     return function(req, res, next) {

         var isAuth = req.isAuthenticated();

         // If user is required to be logged in & isn't
         if (loginRequired && !isAuth) {
             return next(new Error("You must be logged in to view this area"));
         }

         if (isAuth || !loginRequired) {

             var authRole = isAuth ? req.user.role : 'user',
                 role = get(authRole),
                 hasPermission = false;

             (function () {
                 for (var i = 0, len = role[0].resource.length; i <len; i++){
                     if (role[0].resource[i].id === resource && role[0].resource[i].permissions.indexOf(action) !== -1) {
                         hasPermission = true;
                         return;
                     }
                 };
             })();

             if (hasPermission) {
                 next();
             } else {
                 return next(new Error("You are trying to " + action + " a " + resource + " and do not have the correct permissions."));
             }

         }
     }
 }

 return {
     get : function (role) {

         var roles = getRole(role);

         return roles;
     },
     check : function (action, resource, loginRequired) {
         return check(action, resource, loginRequired);
     }
 }

 })();

 module.exports = permissions;
 

Then i created a middleware function, when the check method gets called it gets the users role from the req object (req.user.role). It then looks at the params passed to the middleware and cross references them with those in the permissions config object.

Then I created a middleware function that gets the user role from the req object (req.user.role) when the check method is called.It then looks at the parameters passed to the middleware and cross-references them with the parameters in the permissions configuration object.

Route with middlware

with middlware routing

app.get('/journal', `**permissions.check('read', 'journal')**`, function (req, res) {
      // do stuff
 };
 

#2


3

This is my implementation. The code is reusable for client and server. I use it for my express/angular website

This is my implementation. Code can be reused on both client and server. I use it for my express/angular website

  1. Reduce code duplicate, better consistency between client/server
  2. Reduce code duplication and better maintain consistency between client/server

  3. Bonus benefit: on client’s adapter, we can simply return true to grant max access to test the robustness of server (since hackers and easily overcome client side restrict)
  4. Bonus: On the client’s adapter we can simply return true to grant maximum access to test the server’s robustness (because hackers and easily overcome Client restrictions)

in app/both/both.js

var accessList = {
     //note: same name as controller's function name
     assignEditor: 'assignEditor'

     ,adminPage: 'adminPage'
     ,editorPage: 'editorPage'
     ,profilePage: 'profilePage'

     ,createArticle: 'createArticle'
     ,updateArticle: 'updateArticle'
     ,deleteArticle: 'deleteArticle'
     ,undeleteArticle: 'undeleteArticle'
     ,banArticle: 'banArticle'
     ,unbanArticle: 'unbanArticle'

     ,createComment: 'createComment'
     ,updateComment: 'updateComment'
     ,deleteComment: 'deleteComment'
     ,undeleteComment: 'undeleteComment'
     ,banComment: 'banComment'
     ,unbanComment: 'unbanComment'

     ,updateProfile: 'updateProfile'

 }
 exports.accessList = accessList

 var resourceList = {
     //Note: same name as req.resource name
     profile: 'profile'
     ,article: 'article'
     ,comment: 'comment'
 }
 exports.resourceList = resourceList

 var roleList = {
     admin: 'admin'
     ,editor: 'editor'
     ,entityCreator: 'entityCreator'
     ,profileOwner: 'profileOwner' //creator or profile owner
     ,normal: 'normal' //normal user, signed in
     ,visitor: 'visitor' //not signed in, not used, open pages are uncontrolled
 }

 var permissiOnList= {}

 permissionList[accessList.assignEditor] = [roleList.admin]

 permissionList[accessList.adminPage] = [roleList.admin]
 permissionList[accessList.editorPage] = [roleList.admin, roleList.editor]
 permissionList[accessList.profilePage] = [roleList.admin, roleList.editor, roleList.normal]

 permissionList[accessList.createArticle] = [roleList.admin, roleList.editor, roleList.normal]
 permissionList[accessList.updateArticle] = [roleList.admin, roleList.editor, roleList.entityCreator]
 permissionList[accessList.deleteArticle] = [roleList.admin, roleList.editor, roleList.entityCreator]
 permissionList[accessList.undeleteArticle] = [roleList.admin, roleList.editor, roleList.entityCreator]
 permissionList[accessList.banArticle] = [roleList.admin, roleList.editor]
 permissionList[accessList.unbanArticle] = [roleList.admin, roleList.editor]

 permissionList[accessList.createComment] = [roleList.admin, roleList.editor, roleList.normal]
 permissionList[accessList.updateComment] = [roleList.admin, roleList.editor, roleList.entityCreator]
 permissionList[accessList.deleteComment] = [roleList.admin, roleList.editor, roleList.entityCreator]
 permissionList[accessList.undeleteComment] = [roleList.admin, roleList.editor, roleList.entityCreator]
 permissionList[accessList.banComment] = [roleList.admin, roleList.editor]
 permissionList[accessList.unbanComment] = [roleList.admin, roleList.editor]

 permissionList[accessList.updateProfile] = [roleList.admin, roleList.profileOwner]



 var getRoles = function(access, resource, isAuthenticated, entity, user) {
     var roles = [roleList.visitor]
     if (isAuthenticated) {
         roles = [roleList.normal]
         if (user.username === 'admin')
             roles = [roleList.admin]
         else if (user.type === 'editor')
             roles = [roleList.editor]


         if (resource) {
             if (resource === resourceList.profile) {
                 //Note: on server _id is a object, client _id is string, which does not have equals method
                 if (entity && entity._id.toString() === user._id.toString())
                     roles.push(roleList.profileOwner)
             }
             else if (resource === resourceList.article) {
                 if (entity && entity.statusMeta.createdBy._id.toString() === user._id.toString())
                     roles.push(roleList.entityCreator)
             }
             else if (resource === resourceList.comment) {
                 if (entity && entity.statusMeta.createdBy._id.toString() === user._id.toString())
                     roles.push(roleList.entityCreator)
             }
         }
     }
     return roles
 }


 exports.havePermission = function(access, resource, isAuthenticated, entity, user) {
     var roles = getRoles(access, resource, isAuthenticated, entity, user)


     //Note: we can implement black list here as well, like IP Ban

     if (!permissionList[access])
         return true

     for (var i = 0; i <roles.length; i++) {
         var role = roles[i]
         if (permissionList[access].indexOf(role) !== -1)
             return true
     }
     return false

 }
 

Then on app/server/helper.js (act as adapter)

Then on app/server/helper.js (acts as adapter)

var both = require(dir.both + '/both.js')
 exports.accessList = both.accessList
 exports.resourceList = both.resourceList
 exports.havePermission = function(access, resource, req) {
     return both.havePermission(access, resource, req.isAuthenticated(), req[resource], req.user)
 }


 //todo: use this function in other places
 exports.getPermissiOnError= function(message) {
     var err = new Error(message || 'you do not have the permission')
     err.status = 403
     return err
 }

 exports.getAuthenticatiOnError= function(message) {
     var err = new Error(message || 'please sign in')
     err.status = 401
     return err
 }

 exports.requiresPermission = function(access, resource) {
     return function(req, res, next) {
         if (exports.havePermission(access, resource, req))
             return next()
         else {
             if (!req.isAuthenticated())
                 return next(exports.getAuthenticationError())
             else
                 return next(exports.getPermissionError())
         }
     }
 }
 

on app/client/helper.js, also act as adapter.

On app/client/helper.js, also acts as an adapter.

exports.accessList = both.accessList
 exports.resourceList = both.resourceList
 exports.havePermission = function(access, resource, userService, entity) {
     //Note: In debugging, we can grant client helper all access, and test robustness of server
     return both.havePermission(access, resource, userService.isAuthenticated(), entity, userService.user)
 }
 

#3


1

I personnally took inspiration from ghost. In my config there is the perms, and permissions.jsexport a canThisfunction that take the current logged user. Here is the whole project

I personally draw inspiration from ghosts. In my configuration I have perms and permissions.jsexport is a canThisfunction which accepts the currently logged in user. This is the entire project

Part of my config file

Part of the configuration file

"user_groups": {
     "admin": {
       "full_name": "Administrators",
       "description": "Adminsitators.",
       "allowedActions": "all"
     },
     "modo": {
       "full_name": "Moderators",
       "description": "Moderators.",
       "allowedActions": ["mod:*", "comment:*", "user:delete browse add banish edit"]
     },
     "user": {
       "full_name": "User",
       "description": "User.",
       "allowedActions": ["mod:browse add star", "comment:browse add", "user:browse"]
     },
     "guest": {
       "full_name": "Guest",
       "description": "Guest.",
       "allowedActions": ["mod:browse", "comment:browse", "user:browse add"]
     }
   },

 mOngoose= require("mongoose")
 ###
 This utility function determine whether an user can do this or this
 using the permissions. e. g. "mod" "delete"

 @param userId the id of the user
 @param object the current object name ("mod", "user"...)
 @param action to be executed on the object (delete, edit, browse...)
 @param owner the optional owner id of the object to be "actionned"
 ###

 # **Important this is a promise but to make a lighter code I removed it**
 exports.canThis = (userId, object, action, ownerId, callback) ->
   User = mongoose.model("User")
   if typeof ownerId is "function"
     callback = ownerId
     ownerId = undefined
   if userId is ""
     return process(undefined, object, action, ownerId, callback)
   User.findById(userId, (err, user) ->
     if err then return callback err
     process(user, object, action, ownerId, callback)
   )


 process = (user, object, action, ownerId, callback) ->
   if user then role = user.role or "user"
   group = config.user_groups[role or "guest"]
   if not group then return callback(new Error "No suitable group")

   #Parses the perms
   actiOns= group.allowedActions
   for objAction inroles.push(roleList.entityCreator)
             }
             else if (resource === resourceList.comment) {
                 if (entity && entity.statusMeta.createdBy._id.toString() === user._id.toString())
                     roles.push(roleList.entityCreator)
             }
         }
     }
     return roles
 }


 exports.havePermission = function(access, resource, isAuthenticated, entity, user) {
     var roles = getRoles(access, resource, isAuthenticated, entity, user)


     //Note: we can implement black list here as well, like IP Ban

     if (!permissionList[access])
         return true

     for (var i = 0; i <roles.length; i++) {
         var role = roles[i]
         if (permissionList[access].indexOf(role) !== -1)
             return true
     }
     return false

 }
 

Then on app/server/helper.js (act as adapter)

Then on app/server/helper.js (acts as adapter)

var both = require(dir.both + '/both.js')
 exports.accessList = both.accessList
 exports.resourceList = both.resourceList
 exports.havePermission = function(access, resource, req) {
     return both.havePermission(access, resource, req.isAuthenticated(), req[resource], req.user)
 }


 //todo: use this function in other places
 exports.getPermissiOnError= function(message) {
     var err = new Error(message || 'you do not have the permission')
     err.status = 403
     return err
 }

 exports.getAuthenticatiOnError= function(message) {
     var err = new Error(message || 'please sign in')
     err.status = 401
     return err
 }

 exports.requiresPermission = function(access, resource) {
     return function(req, res, next) {
         if (exports.havePermission(access, resource, req))
             return next()
         else {
             if (!req.isAuthenticated())
                 return next(exports.getAuthenticationError())
             else
                 return next(exports.getPermissionError())
         }
     }
 }
 

on app/client/helper.js, also act as adapter.

On app/client/helper.js, also acts as an adapter.

exports.accessList = both.accessList
 exports.resourceList = both.resourceList
 exports.havePermission = function(access, resource, userService, entity) {
     //Note: In debugging, we can grant client helper all access, and test robustness of server
     return both.havePermission(access, resource, userService.isAuthenticated(), entity, userService.user)
 }
 

#3


1

I personnally took inspiration from ghost. In my config there is the perms, and permissions.jsexport a canThisfunction that take the current logged user. Here is the whole project

I personally draw inspiration from ghosts. In my configuration I have perms and permissions.jsexport is a canThisfunction which accepts the currently logged in user. This is the entire project

Part of my config file

Part of the configuration file

"user_groups": {
     "admin": {
       "full_name": "Administrators",
       "description": "Adminsitators.",
       "allowedActions": "all"
     },
     "modo": {
       "full_name": "Moderators",
       "description": "Moderators.",
       "allowedActions": ["mod:*", "comment:*", "user:delete browse add banish edit"]
     },
     "user": {
       "full_name": "User",
       "description": "User.",
       "allowedActions": ["mod:browse add star", "comment:browse add", "user:browse"]
     },
     "guest": {
       "full_name": "Guest",
       "description": "Guest.",
       "allowedActions": ["mod:browse", "comment:browse", "user:browse add"]
     }
   },

 mOngoose= require("mongoose")
 ###
 This utility function determine whether an user can do this or this
 using the permissions. e. g. "mod" "delete"

 @param userId the id of the user
 @param object the current object name ("mod", "user"...)
 @param action to be executed on the object (delete, edit, browse...)
 @param owner the optional owner id of the object to be "actionned"
 ###

 # **Important this is a promise but to make a lighter code I removed it**
 exports.canThis = (userId, object, action, ownerId, callback) ->
   User = mongoose.model("User")
   if typeof ownerId is "function"
     callback = ownerId
     ownerId = undefined
   if userId is ""
     return process(undefined, object, action, ownerId, callback)
   User.findById(userId, (err, user) ->
     if err then return callback err
     process(user, object, action, ownerId, callback)
   )


 process = (user, object, action, ownerId, callback) ->
   if user then role = user.role or "user"
   group = config.user_groups[role or "guest"]
   if not group then return callback(new Error "No suitable group")

   #Parses the perms
   actiOns= group.allowedActions
   for objAction in actions when objAction.indexOf object is 0
     # We get all the allowed actions for the object and group
     act = objAction.split(":")[1]
     obj = objAction.split(":")[0]
     if act.split(" ").indexOf(action) isnt -1 and obj is object
       return callback true

   callback false

 cOnfig= require "../config"
 

Usage example:

exports.edit = (userid, name) ->
   # Q promise
   deferred = Q.defer()
   # default value
   can=false
   # We check wheteher it can or not
   canThis(userid, "user", "edit").then((can)->
     if not userid
       return deferred.reject(error.throwError "", "UNAUTHORIZED")
     User = mongoose.model "User"
     User.findOne({username: name}).select("username location website public_email company bio").exec()
   ).then((user) ->
     # Can the current user do that?
     if not user._id.equals(userid) and can is false
       return deferred.reject(error.throwError "", "UNAUTHORIZED")
     #Done!
     deferred.resolve user
   ).fail((err) ->
     deferred.reject err
   )
   deferred.promise
 

Perhaps what I’ve done isn’t good, but it works well as far as I can see.

Maybe what I’m doing isn’t great, but from what I’ve seen, it works well.

#4


0

Yes, you can access that through the request argument.

Yes, you can access it via request parameters.

app.use(function(req,res,next){
      console.log(req.method);
 });
 

http://nodejs.org/api/http.html#http_message_method

Edit:

Misread your question. It would probably just be better to assign user permissions and allow access to the database based upon the permissions. I don’t understand what you mean by validate by means of interaction with the database. If you are already allowing them to interact with the database and they don’t have the proper permissions to do so, isn’t that a security issue?

Misread your question. It might be better to assign user permissions and allow permission-based access to the database. Validating by interaction with the database, I don’t understand what you mean. If you’ve allowed them to interact with the database and they don’t have the appropriate permissions, isn’t that a security issue?

#5


0

Check the Node module permission for that matter. It’s pretty simple concept, I hope they’ll allow all CRUD methods too.

Check the permissions of the Node module. This is very simple concept, I wish they allowed all CRUD methods as well.

actions when objAction.indexOf object is 0
# We get all the allowed actions for the object and group
act = objAction.split(“:”)[1]
obj = objAction.split(“:”)[0]
if act.split(” “).indexOf(action) isnt -1 and obj is object
return callback true

callback false

cOnfig= require “../config”

Usage example:

exports.edit = (userid, name) ->
   # Q promise
   deferred = Q.defer()
   # default value
   can=false
   # We check wheteher it can or not
   canThis(userid, "user", "edit").then((can)->
     if not userid
       return deferred.reject(error.throwError "", "UNAUTHORIZED")
     User = mongoose.model "User"
     User.findOne({username: name}).select("username location website public_email company bio").exec()
   ).then((user) ->
     # Can the current user do that?
     if not user._id.equals(userid) and can is false
       return deferred.reject(error.throwError "", "UNAUTHORIZED")
     #Done!
     deferred.resolve user
   ).fail((err) ->
     deferred.reject err
   )
   deferred.promise
 

Perhaps what I’ve done isn’t good, but it works well as far as I can see.

Maybe what I’m doing isn’t great, but from what I’ve seen, it works well.

#4


0

Yes, you can access that through the request argument.

Yes, you can access it via request parameters.

app.use(function(req,res,next){
      console.log(req.method);
 });
 

http://nodejs.org/api/http.html#http_message_method

Edit:

Misread your question. It would probably just be better to assign user permissions and allow access to the database based upon the permissions. I don’t understand what you mean by validate by means of interaction with the database. If you are already allowing them to interact with the database and they don’t have the proper permissions to do so, isn’t that a security issue?

Misread your question. It might be better to assign user permissions and allow permission-based access to the database. Validating by interaction with the database, I don’t understand what you mean. If you’ve allowed them to interact with the database and they don’t have the appropriate permissions, isn’t that a security issue?

#5


0

Check the Node module permission for that matter. It’s pretty simple concept, I hope they’ll allow all CRUD methods too.

Check the permissions of the Node module. This is very simple concept, I wish they allowed all CRUD methods as well.

This article is from the internet and does not represent1024programmerPosition, please indicate the source when reprinting:https://www.1024programmer.com/746479

author: admin

Previous article
Next article

Leave a Reply

Your email address will not be published. Required fields are marked *

Contact Us

Contact us

181-3619-1160

Online consultation: QQ交谈

E-mail: [email protected]

Working hours: Monday to Friday, 9:00-17:30, holidays off

Follow wechat
Scan wechat and follow us

Scan wechat and follow us

Follow Weibo
Back to top
首页
微信
电话
搜索