我正在使用Node,Express和Mongoose创建一个相当简单的站点。该站点需要具有用户角色和权限。我的想法是,我将根据用户与数据库的交互来验证权限。
在mongoose中是否有办法确定当前可能由用户执行的CRUD操作类型?
答案 0 :(得分:10)
我找到了解决方案。听到人们对此的看法会很棒。
我有一个权限配置对象,用于定义每个角色及其权限。
权限配置对象
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']
},
]
};
中间件功能
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;
然后我创建了一个中间件函数,当检查方法被调用时,它从 req 对象(req.user.role)获取用户角色。然后查看传递给中间件的参数,并将它们与权限配置对象中的参数进行交叉引用。
使用middlware路由
app.get('/journal', `**permissions.check('read', 'journal')**`, function (req, res) {
// do stuff
};
答案 1 :(得分:4)
这是我的实施。 代码可以重用于客户端和服务器。我将它用于我的express / angular网站
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())
}
}
}
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)
}
答案 2 :(得分:1)
我个人从幽灵中获取灵感。在我的配置中有perms,permissions.js
导出一个canThis
函数,它接受当前登录的用户。 Here is the whole project
我的配置文件的一部分
"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"
用法示例:
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
也许我所做的并不好,但就我所见,它运作良好。
答案 3 :(得分:0)
是的,您可以通过request
参数访问它。
app.use(function(req,res,next){
console.log(req.method);
});
http://nodejs.org/api/http.html#http_message_method
修改强>
误读了你的问题。分配用户权限并允许基于权限访问数据库可能更好。通过与数据库的交互验证,我不明白你的意思。如果您已经允许它们与数据库进行交互,并且它们没有相应的权限,那么这不是一个安全问题吗?
答案 4 :(得分:0)
检查节点模块permission。这是一个非常简单的概念,我希望他们也允许所有的CRUD方法。