Creating Role-Based Access Control in MongoDB

MongoDB provides user access on role-based controls. It provides many built-in roles to be assigned to users: most notably the read and readwrite roles. Sometimes, however, they are not as granular as we would like.

I recently had a chance to explore user-defined roles in MongoDB which were introduced in version 2.6. In this blog post, I will go over the MongoDB user-defined roles and define some custom roles that you might find useful.

 

1. Create Roles:

 

Creating a user-defined role in MongoDB is simple. You can use createRole command to create a new role.  The generic create role syntax is as follows.


{
   createRole: "<role name>",
   privileges: [{
      resource: { <resource> },
      actions: [ "<action>",] },
   ],
   roles: [
      { role: "<role>", db: "<database>" } | "<role>",],
   writeConcern: <write concern document>
}

Before running the createRole command, make sure you switch to the database that you want to create the role in. Note that the roles will be defined only in the database where the role has been created.  If you want to create a role that grants access to more than one database, then that needs to be created on the admin database.

Let us go over the major components of the create role syntax.

1.1  Privileges:

Permissions can be added to a user-defined role by adding a privilege. A privilege contains action and resource fields which are discussed below.

Actions:

  • Actions are a set of operations that are grouped together. For example, the insert action can perform both insert and create. Actions are as granular as MongoDB’s role-based access control gets. The privileges parameter can be used to add roles to mongo actions. A privilege constitutes the actions along with the resource on which the action applies. The following adds actions find, insert and update on database “mydb”.

privileges:
[
   {resource: {db: "mydb", collection: "" },
   actions: [ “find”,”insert”,”update” ] }
]

Resource:

The resource document would specify the scope at which the actions would apply. The scope can be set at various granularities as follows.

     a. Collection:

The resource can be set to  resource: {db: "<db-name>", collection: "<collection name>" } which would grant the specified actions to just that particular collection.

     b. Database:

The resource can be set to a particular database by leaving the collection parameter empty. Resource string  resource: {db: "<db-name>", collection: "<collection name>" } sets the scope to the entire database.

     c. Single Collection Across Databases:

The resource can be set to a particular collection using resource: {db: ", collection: "<collection name>" } this would grant permissions to the collection on all databases. This permission can only be added onto a role created on the admin database.

     d. Multiple Collections Across Databases:

The resource can be set to all collections except the system collections across all databases by leaving both the db and collection parameters empty. resource: {db: "", collection: "" }. This resource like the one above can only be granted on a role created on to the admin database.

     e. Cluster-wide Resource:

A cluster-wide resource can be specified by using resource: { cluster : true }. This cluster-wide resource can be used to specify the state of the system such as shutdown, replSetReconfig rather than to grant permissions on any particular document.

     f. All Resources:

It’s not recommended to use this scope for other than extraordinary circumstances. {anyResource: true }  can be used to set scope set to all resource’s.

 

1.2 Roles:

Inbuilt roles can be added to a custom role as well. When an inbuilt role is added by using the roles: [] parameter it adds the permissions of the inbuilt role to the custom role.

 

An example of the roles parameter would be as follows.


roles: [{ role: "read", db: "<db name>"}]

Here, the custom role would inherit all the permissions of role read over the database defined. Let’s say a role is inherited onto a database db1. The custom role can either be created on the database db1 or on the admin database.

Write Concern:

Write concern defines the level of acknowledgment requested from MongoDB.It can be used to control write acknowledgments from the database. A write concern is not required when creating a role.  Write concern can include fields w, j and wtimeout

W: Field W can be used to state the number of instances the write has been propagated to.

J: can be set to determine whether the write is written to the journal.

Wtimeout: Is used to set the time by which the write has to achieve write concern. The write concern might still be achieved after the error is thrown.  If a Wtimeout has not been set and the write concern cannot be achieved, the write will be blocked indefinitely.

2. Assigning Roles:

Custom roles are db specific. A role can only be assigned to a user in the same database.

Let’s say we created a role “myrole” on database “db1”. We can create a user on the database using the following commands.


Use db1

db.createUser({"user" : "<user>",pwd: "<password>","roles" : [{"role" : "myrole", "db" : "db1"}]})

For further information on user management refer to this post by Dharshan on  user management in MongoDB.

 

3. Custom Roles:

Let’s go over some custom roles that might be useful.

User with Read, Insert and Update Permissions on a Single DB:

The inbuilt roles read and readWrite might sometimes feel like too many permissions or too little permissions. Let’s see how we can create a custom role granting just read, insert and write permissions.

We already know we need all read permissions so we can add the inbuilt role “read” to our custom role. We would also need permissions to create and update documents. These can be added by adding privilege actions insert and update. If we wanted to give the user the ability to create index and create collection, we can add the privilege action createIndex and createCollection.

For the scope, lets assume I have a db named “db1” on which I the above permissions. The create command would look something like this.


Use db1.

db.createRole(

{

createRole: "<role-name>",

privileges: [

{ resource: { db: "db1", collection: "" },

actions: [ "insert","update","createIndex", "createCollection" ] }

],

roles: [{ role: "read", db: "db1"}]

})

The above command would create a role with <role-name> in database db1.  A user granted permission by the above role will not have the “remove” privilege action. Also, note that the methods db.collection.findAndModify(),  db.collection.mapReduce() and  db.collection.aggregate() cannot be run in full as they require the remove privilege.

User with only Read, Insert and Update Permissions on All DB’s:

We can create a role on the admin database similar to the one above to grant Read, Create and Update privileges on All DB’s. This role should be created on the admin DB and the subsequent user should also be created on the admin DB.

For this role instead of using the standard read role, we can inherit permissions from readAnyDatabase role. The role create would look something like this.


Use admin.

db.createRole(

{

createRole: "<role-name>",

privileges: [

{ resource: { db: "", collection: "" },

actions: [ "insert","update","createIndex", "createCollection" ] }

],

roles: [{ role: "readAnyDatabase", db: "admin"}]

})

Writer Roles with Write Concern.

You could have a use case where write concern needs to be enforced. For this purpose, Write concern can be added to a role. Adding write concern to a role would enforce it on all the users granted the role on the DB. Let us define a role with a write concern that enforces majority writes.


Use admin.

db.createRole(

{

createRole: "<role-name>",

privileges: [ ],

roles: [{ role: "readWriteAnyDatabase", db: "admin"}],

writeConcern: { w: “majority”, j: false, wtimeout: 300 }

})


Liked this post?

Join the ScaleGrid newsletter and never miss out!

Neeraj is a member of technical staff at Scalegrid Inc


10 Shares
+13
Tweet
Share
Share7
Pin