Express.js / Mongoose用户angular色和权限

我正在用Node,Express和Mongoose创build一个相当简单的站点。 该网站需要有用户angular色和权限。 我的想法是,我将根据用户与数据库的交互validation权限。

在mongoose有没有一种方法来确定目前正在执行的CRUD操作的types可能是由用户?

我find了一个解决scheme。 听到人们对此的意见是很好的。

我有一个权限configuration对象,它定义每个angular色及其权限。

权限configuration对象

 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'] }, ] }; 

中间件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 = getRoles(role); return roles; }, check : function (action, resource, loginRequired) { return check(action, resource, loginRequired); } } })(); module.exports = permissions; 

然后我创build了一个中间件函数,当check方法被调用时,它从req对象(req.user.role)获取用户angular色。 然后查看传递给中间件的参数,并将它们与权限configuration对象中的参数交叉引用。

与中间件路线

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

这是我的实现。 该代码可以重用于客户端和服务器 。 我用它来expression/angular度的网站

  1. 减less代码重复,客户机/服务器之间更好的一致性
  2. 额外的好处:在客户端的适配器上,我们可以简单地返回true授予最大访问权限来testing服务器的健壮性(因为黑客和轻易克服客户端限制)

在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 } 

然后在app / server / helper.js(充当适配器)

 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()) } } } 

在app / client / helper.js上,也充当适配器。

 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) } 

我个人从鬼魂中获得灵感。 在我的configuration中有perms,而permissions.js导出一个canThis函数,取当前logging的用户。 这是整个项目

我的configuration文件的一部分

 "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. eg "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" 

用法示例:

 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 

也许我所做的并不好,但是就我所能看到的而言,它运作良好。

是的,您可以通过request参数来访问它。

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

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

编辑:

错读你的问题。 根据权限分配用户权限并允许访问数据库可能会更好。 我不明白你的意思是通过与数据库的交互来validation。 如果您已经允许他们与数据库进行交互,并且他们没有相应的权限,那么这不是一个安全问题吗?

检查节点模块的权限 。 这是非常简单的概念,我希望他们也会允许所有的CRUD方法。