简体   繁体   中英

MongoDB + Node JS + Role Based Access Control (RBAC)

Am currently learning MEAN stack, developing a simple TODO's app and want to implement Role Based Access Control (RBAC) for that. How do i set up roles & permission on MongoDB.

I want 3 roles (roles may look funny but this is purely to learn) :

  • GOD
  • SUPER HERO
  • MAN

GOD - similar to super admin, can do anything in the application. C,R,U,D permissions for TODO's and for other users too. Can Create a TODO & assign it to any SUPER HERO or MAN directly. Update or Delete either a TODO or a User at any point in time.

SUPER HERO - similar to admin, has super power to do anything on his personal Data - C,R,U,D for TODO's. Can't create any users. Can only Read & add comments for TODO's created by GOD & assigned to him/her.

MAN - Can only Read and add comments to TODO's assigned to him/her.

To sum it up :

GOD - C,R,U,D [Global Level] SUPER HERO - C,R,U,D [Private] + R,U [Assigned to him] MAN - R,U [Assigned to him]

I understand that i need to have USERS & ROLES collections. Where ROLES inturn should have PERMISSIONS etc. How do i wire them all ?

I like names given to roles - GOD, SUPER HERO & MAN , easy to understand.

As you are using MEAN stack and much of routes validation happens on node , i would prefer keeping roles table simple.

Roles :

{
_id : 1,
name : GOD,
golbalPerms : true
},
{
_id : 2,
name : SUPER HERO,
privatePerms : true
},
{
_id : 3,
name : MAN
}

Users :

{
_id : 111,
name : Jesus,
roleId : 1
},
{
_id : 222,
name : BatMan,
roleId : 2
},
{
_id : 333,
name : Jack,
roleId : 3
}

When user logs in and sending user object back to client, make sure to replace roleId with corresponding role object from DB.

Coming to code on Node JS :

By completely understanding your usecase we can divide them into following methods -

  • CreateUser

  • CreateTodo

  • DeleteTodo

  • ReadTodo

  • UpdateTodo
  • CommentTodo

  • AssignTodo

Lets go step by step, CreateUser .

Routes code snippet :

app.all('/users', users.requiresLogin);

// Users Routes
app.route('/users')
    .post(users.hasPerms('globalPerms'), users.create);

In your Controller you can validate based on the input globalPerms , if validated allow to create user by calling next() else return with corresponding error message.

Now CreateTodo && DeleteTodo :

Both of them pretty much work on same logic with a small trick.

Routes code snippet :

app.all('/todos', users.requiresLogin);

// Users Routes
app.route('/todos')
    .post(users.hasPerms('globalPerms','privatePerms'), todos.create);
    .delete(users.hasPerms('globalPerms','privatePerms'), todos.delete);

For creating a Todo, globalPerms are with GOD & privatePerms are with SUPER HERO , both of them can be allowed.

Trick here will be in todos.delete method, just ensure user.id === todos.createById else SUPER HERO may go on to delete Todos created by GOD.

ReadTodo :

When a TODO is created it should have a createById stored likewise when a TODO is assigned to someone then assignedTo and assignedBy should be recorded too.

This makes lot of other operations easy to handle.

user.role.globalPerms - give GOD all TODO's data.

user.role.privatePerms - give TODO's either createdBy him/her or assigned to him/her.

user.role.globalPerms === undefined && user.role.privatePerms === undefined - its MAN and give TODO's which are only assignedTo him.

UpdateTodo & CommentTodo :

This is exact replica of what ReadTODO does so DIY

Last one, AssignTodo :

Simple one, loggedInUser.id === todos.createdById then he can assign it to anyone.

Two things to keep in mind here :

  1. As assigning part mostly happens on your UI (Angular) front, i have given that approach of checking loggedInUser.id === todos.createdById . Logged in user any ways will see all TODO's by read operation and can assign it to anyone he/she likes.

  2. Make sure a SUPER HERO can only assign a TODO to himself or other SUPER HERO or to a MAN but not to GOD. How you show Assign to options on UI front is out of scope of this question. This is just a heads up.

Hope this was clear.

NOTE : There was no necessity to give permissions to MAN in Roles collection & we managed all possible operations with out that.

This is a very broad question which can be solved in many ways.

You have added that you are using MEAN stack therefore I'll restrict my question to that.

One thing that you haven't included in the whole question is what kind of authentication architecture are you using. Let's say you are using token based authentication, generally people these days use it.

We have 3 types of users. You have different options available to differentiate between type of tokens as well.

  1. Different Collection (mongoDB) or Redis sets where they will be stored
  2. The encrypted token will have type of the user as well etc.. (This will come in handy if you don't need to store tokens on the backend, you can just decrypt and check)

    • It will completely depend on use case.

Now, before allowing any user's entry to user specific route make sure that you are checking the token first.

Example

app.post('/godlevelroute', godtokencheck, callrouteandfunction);
app.post('/superherolevelroute', superheroroute, callrouteandfunction);

You must send token in header from angular and then you can take the data out from the header and then you can check if that specific user has permission to go through that route or not.

Let's say a god level user is logged in then he'll have the godleveltoken with him and we'll check that first before allowing him to access that route or else you can just show error message.

This can be your sample token checking function on server end

function checkToken(req, res, next) {
var token = req.headers['accesstoken']; //access token from header
//now depending upon which system you are following you can run a check
};

Node Module Suggestion : https://www.npmjs.com/package/jsonwebtoken

Now coming to frontend part. You are using angular based on what you have written, you can intercept the token before showing any page.

You can go through this blog to get a pictorial representation of what I have tried to explain. Click Here

Possible approach-> have role embedded in user collection/schema: users document shall have the following:

{
    _id : "email@mail.com",
    name: "lorem ipsum",
    role: "MAN"
}

As far as your post describes, only god can make and assign TODOs. Roles Collection may hold the following:

{
    _id : "MAN",
    globalPerm: [],
    privatePerm: [],
    assignedPerm: ["r","u"],
},
{
    _id : "SUPER_HERO",
    globalPerm: [],
    privatePerm: ["c","r","u","d"],
    assignedPerm: ["c","r","u","d"],
},
{
    _id : "GOD",
    globalPerm: ["c","r","u","d"],
    privatePerm: ["c","r","u","d"],
    assignedPerm: ["c","r","u","d"],
}

Node JS Middlewares After getting correct permission values for a user, you might want to use middlewares. Sample express HTTP request route:

app.post('/updateTodo', permissions.check('privatePerm', 'c'), function (req, res) {
 // do stuff

};

permissions.check is called before actually executing function body to update TODO.

Hence if a user tries to update a todo, it will first verify the corresponding permissions.

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM