AWS Database Blog

Introducing Amazon DocumentDB (with MongoDB compatibility) user-defined roles for access control

Amazon DocumentDB (with MongoDB compatibility) is a database service that is purpose-built for JSON data management at scale, fully managed and integrated with AWS, and enterprise-ready with high durability.

Amazon DocumentDB added support for role-based access control (RBAC) with user-defined roles. Support for user-defined roles builds upon the existing Role-Based Access Control (RBAC) functionality, allowing for more granular access control as well as creating custom roles to simplify user authorization within your Amazon DocumentDB clusters. Two common use cases for user-defined roles are applying least privilege access for the application user and supporting collection-level access control within an Amazon DocumentDB database.

This post introduces the new RBAC capabilities in Amazon DocumentDB: creation of custom roles, granular control of permitted operations, and granular control of permitted collections. For more information regarding RBAC capabilities, see Restricting Database Access Using Role-Based Access Control.

Use case: Least privilege access

A security best practice is to apply least privilege access within your databases by creating multiple users that each have least privilege access.

For example, creating a single application user with create, read, update, and delete (CRUD) access to all collections that the application requires and creating one or more support users with read-only access to a subset of collections.

Without support for user-defined roles in RBAC, you could only implement this requirement by placing collections requiring access from both the application user and support users in one database and using a second database for collections limited to access strictly by the application user, because RBAC was previously enforced at the database level. Now you can deploy your schema in a more manageable way by keeping all collections for a given application in a single database.

The following examples show the application user appuser with the built-in readWrite role in the app1 database and the support user supportuser with only the find privilege on the supportInfo collection in the app1 database:

db.createRole(
  {
    role: "appUserRole",
    privileges: [],
    roles: [
      {role: "readWrite", db: "app1"}
    ]
  }
)

db.createUser({user:"appuser",pwd:"secretPWD1",roles:[]})

db.grantRolesToUser("appuser","appUserRole")

db.createRole(
  {
    role: "supportUserRole",
    privileges: [
      {resource: {db: "app1", collection: "supportInfo"}, actions: ["find"]}
    ],
    roles: []
  }
)

db.createUser({user:" supportuser ",pwd:"secretPWD2",roles:[]})

db.grantRolesToUser("supportuser","supportUserRole")

Granular control of permitted operations

Building on your existing ability to control access based on built-in roles, you can now grant access based on actions. Granting a user the built-in read role permits the following actions: changeStreams, collStats, dbStats, find, killCursors, listIndexes, and listCollections. If user appuser1 only requires access to the commands covered by the find action in the app1 database, you can accomplish this with the following code. Note that the roles object is empty because we’re not defining any specific roles for this custom role.

db.createRole(
  {
    role: "findRole1",
    privileges: [
      {resource: {db: "app1", collection: ""}, actions: ["find"]}
    ],
    roles: []
  }
)

db.createUser({user:"appuser1",pwd:"secretPWD3",roles:[]})

db.grantRolesToUser("appuser1",["findRole1"])

Granular control of permitted collections

In addition to database level access control, you can now grant access at the collection level in addition to the database level. In the prior example, we granted the appuser1 user permission to issue commands covered by the find action in the app1 database. We can further limit the particular user’s access to one or more collections within a database. The following example limits the user’s access to collection coll1:

db.createRole(
  {
    role: "findRole2",
    privileges: [
      {resource: {db: "app1", collection: "coll1"}, actions: ["find"]}
    ],
    roles: []
  }
)

db.createUser({user:"appuser1",pwd:"secretPWD3",roles:[]})

db.grantRolesToUser("appuser1",["findRole2"])

Additional new commands

Additionally, you now have the ability to fully support user-defined roles through the following commands:

db.dropRole() – Drop a user-defined role. The following example drops the user-defined role findRole2:

db.dropRole("findRole2")

db.grantPrivilegesToRole() – Grant additional privileges (actions within a database and optionally a collection) to an existing user-defined role. The following example permits the find action to the coll4 collection in database app1 on the user-defined role findRole2:

db.grantPrivilegesToRole(
  "findRole2",
  [
    {resource: {db: "app1", collection: "coll4"}, actions: ["find"]}
  ]
)

db.revokePrivilegesFromRole() – Remove additional privileges from an existing user-defined role. The following example removes permission for the find action to the coll4 collection in database app1 on the user-defined role findRole2:

db.revokePrivilegesFromRole(
  "findRole2",
  [
    {resource: {db: "app1", collection: "coll4"}, actions: ["find"]}
  ]
)

db.grantRolesToRole() – Add additional built-in roles from an existing user-defined role. The following example permits all actions of the built-in readWrite role in database app2 on the user-defined role findRole2:

db.grantRolesToRole(
  "findRole2",
  [
    {role: "readWrite", db: "app2"}
  ]
)

db.revokeRolesFromRole() – Remove additional built-in roles from an existing user-defined role. The following example removes permission for all actions of the built-in readWrite role in database app2 on the user-defined role findRole2:

db.revokeRolesFromRole(
  "findRole2",
  [
    {role: "readWrite", db: "app2"}
  ]
)

db.updateRole() – Update an existing user-defined role with the provided document. The following example replaces the existing user-defined role findRole2:

db.updateRole(
  "findRole2",
  {
    privileges: [
      {resource: {db: "app1", collection: "coll1"}, actions: ["find"]},
      {resource: {db: "app1", collection: "coll2"}, actions: ["find"]},
      {resource: {db: "app1", collection: "coll3"}, actions: ["find"]}
    ],
    roles: []
  }
)

Summary

This post introduced user-defined roles for role-based access control in Amazon DocumentDB and walked through a common use case. For more information about RBAC in Amazon DocumentDB, see Restricting Database Access Using Role-Based Access Control (Built-In Roles). For more information about the first release of RBAC please visit this post.

If you have any questions or comments about this post, please use the comments section. If you have any features requests for Amazon DocumentDB, email us at documentdb-feature-request@amazon.com.


About the Author

Tim Callaghan is a Principal DocumentDB Specialist Solutions Architect at AWS. He enjoys working with customers looking to modernize existing data-driven applications and build new ones. Prior to joining AWS he has been both a consumer of Relational and NoSQL databases for over 30 years.