I am creating a fairly simple site with Node, Express and Mongoose. The site needs to have have user roles and permissions. My thoughts are that i\'ll validate permissions based
I personnally took inspiration from ghost. In my config there is the perms, and permissions.js
export a canThis
function that take the current logged user. Here is the whole project
Part of my config 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.