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

tki*_*dle 9 mongoose mongodb node.js express

我正在使用Node,Express和Mongoose创建一个相当简单的站点.该站点需要具有用户角色和权限.我的想法是,我将根据用户与数据库的交互来验证权限.

在mongoose中有没有办法确定当前可能由用户执行的CRUD操作的类型?

tki*_*dle 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']
        },

    ]
};
Run Code Online (Sandbox Code Playgroud)

中间件功能

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;
Run Code Online (Sandbox Code Playgroud)

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

与middlware路由

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


OMG*_*POP 6

这是我的实现。该代码可用于客户端和服务器。我将其用于我的快速/角度网站

  1. 减少代码重复,提高客户端/服务器之间的一致性
  2. 额外好处:在客户端的适配器上,我们只需返回true即可授予最大访问权限以测试服务器的健壮性(因为黑客能够轻松克服客户端的限制)

在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

}
Run Code Online (Sandbox Code Playgroud)

然后在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())
        }
    }
}
Run Code Online (Sandbox Code Playgroud)

在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)
}
Run Code Online (Sandbox Code Playgroud)


Mic*_*ael 0

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

app.use(function(req,res,next){
     console.log(req.method);
});
Run Code Online (Sandbox Code Playgroud)

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

编辑:

误读了你的问题。分配用户权限并根据权限允许访问数据库可能会更好。我不明白你所说的通过与数据库交互进行验证是什么意思。如果您已经允许他们与数据库交互,但他们没有适当的权限这样做,这不是一个安全问题吗?