Creating Role-Based Access Control in MongoDB

Creating Role-Based Access Control in MongoDB

MongoDB provides user access through role-based controls, including many built-in roles that can be assigned to users. The two most well-known controls are the read and read/write roles, however, sometimes, they’re not as granular as we’d like them to be.

I recently had a chance to explore the user-defined roles in introduced in MongoDB version 2.6. In this article, we’ll go over the MongoDB user-defined roles and define some custom roles that you might find useful.

  1. Create Roles in MongoDB

    Creating a user-defined role in MongoDB is quite simple. You can use createRole command to create a new role, and 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 you want to create the role in as they’ll only be defined in the database in which they were created.  If you want to create a role that grants access to more than one database, then it will need to be created on the admin database.

    Let’s go over the major components of the create role syntax.

  2. Role Privileges

    Create permissions for a user-defined role by adding a privilege and defining your actions and resources:

    Privilege Actions

    Actions are a set of operations that are grouped together, such as the insert action that 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, and a privilege constitutes the actions along with the resource it applies to. Use the following to add the actions find, insert and update on database “mydb”.

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

    Privilege Resources

    The resource document specifies the scope of which your privilege actions will apply, and can be set at various granularities as follows:

    a.  Collection

    The resource can be set to resource: {db: "<db-name>", collection: "<collection name>" } to grant the specified actions to only 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>" } to 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 the admin database.

    e.  Cluster-Wide Resource

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

    f.  All Resources

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

  3. 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.

    Here’s an example of the roles parameter:

    roles: [{ role: "read", db: "&amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;lt;db name&amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;gt;"}]

    In this example, the custom role would inherit all the permissions of role “read” over the defined database. If 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, and can be used to control write acknowledgments from the database. Note that a write concern is not required when creating a role.  Write concern can include fields w, j and wtimeout:

    W  –  Write Concern

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

    J  –  Write Concern

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

    Wtimeout  –  Write Concern

    This 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.

  4. Assigning Roles

    Custom roles are db specific, and 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" : "&amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;lt;user&amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;gt;",pwd: "&amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;lt;password&amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;gt;","roles" : [{"role" : "myrole", "db" : "db1"}]})

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

  5. Custom User Roles

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

    Single DB – Read, Insert & Update Permissions

    The inbuilt roles read and readWrite may sometimes feel like too many permissions or too few. 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 also need permissions to create and update documents, and these can be included 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, let’s assume I have a db named “db1” on which I set the above permissions. The create command would look something like this:

    Use db1.
    createRole: "&amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;lt;role-name&amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;gt;",
    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 that’s been 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.

    All DB’s – Read, Insert & Update Permissions

    We can create a role on the admin database that’s 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.
    createRole: "&amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;lt;role-name&amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;gt;",
    privileges: [
    { resource: { db: "", collection: "" },
    actions: [ "insert","update","createIndex", "createCollection" ] }
    roles: [{ role: "readAnyDatabase", db: "admin"}]

    Writer Roles with Write Concern

    If you have a scenario where write concern needs to be enforced, here’s how it can be added to a role. Adding write concern to a role would enforce it on all of the users granted under this role on the DB. Let’s define a role with a write concern that enforces majority writes:

    Use admin.
    createRole: "&amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;lt;role-name&amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;gt;",
    privileges: [ ],
    roles: [{ role: "readWriteAnyDatabase", db: "admin"}],
    writeConcern: { w: “majority”, j: false, wtimeout: 300 }