← back

Roles &
Privileges

FILE  36_roles_privileges
TOPIC  Custom Roles · Role Inheritance · Privilege Actions · User Management · Scope
LEVEL  Intermediate/Advanced
01
RBAC Model
Role → Privileges → Resources + Actions
model

MongoDB's authorization model is built on Role-Based Access Control (RBAC). The hierarchy is: User → Roles → Privileges → (Resource + Actions). A user has no direct privileges — only the sum of privileges from all assigned roles.

  • Resource: a specific database, collection, cluster, or any combination
  • Action: a specific operation allowed on a resource (e.g., find, insert, dropCollection)
  • Privilege: one resource + one or more actions
  • Role: a named collection of privileges (optionally inheriting from other roles)
// Conceptual structure of a role:
{
  role: "orderManager",
  db: "shopDb",
  privileges: [
    {
      resource: { db: "shopDb", collection: "orders" },
      actions: ["find", "insert", "update"]
    },
    {
      resource: { db: "shopDb", collection: "customers" },
      actions: ["find"]
    }
  ],
  roles: ["read"]    // inherits all privileges from built-in "read" role
}

// Principle of least privilege: grant ONLY what the user needs
// An application user should NEVER have clusterAdmin, root, or userAdminAnyDatabase
TIP
Separate concerns with separate users: one admin user (userAdminAnyDatabase, never used by the app), one app user (readWrite on its database only), one monitoring user (clusterMonitor), one backup user (backup role). Never combine admin and application concerns in one account.
02
Privilege Actions
Complete reference — query, write, admin, and cluster actions
actions

Query & Write Actions (Collection Scope)

ActionPermits
finddb.collection.find(), findOne(), count(), distinct(), aggregate read stages
insertinsertOne(), insertMany(), bulkWrite() inserts
updateupdateOne(), updateMany(), findOneAndUpdate(), replaceOne()
removedeleteOne(), deleteMany(), findOneAndDelete()
bypassDocumentValidationWrite documents that violate schema validation rules
useUUIDUse a specific UUID for the collection's internal ID

Collection Admin Actions

ActionPermits
createIndexdb.collection.createIndex()
dropIndexdb.collection.dropIndex()
listIndexesdb.collection.getIndexes()
createCollectiondb.createCollection()
dropCollectiondb.collection.drop()
renameCollectionSameDBRename a collection within same database
compactRun compact command on a collection
validatedb.collection.validate()
collStatsdb.collection.stats()

Database Admin Actions

ActionPermits
dbStatsdb.stats()
listCollectionsdb.listCollections(), show collections
dropDatabasedb.dropDatabase()
enableShardingsh.enableSharding() on a database
createUserdb.createUser()
dropUserdb.dropUser()
grantRoleAssign roles to users
revokeRoleRemove roles from users
viewRoledb.getRole(), db.getRoles()
viewUserdb.getUser(), db.getUsers()

Cluster Admin Actions (admin db scope)

ActionPermits
serverStatusdb.serverStatus()
topdb.adminCommand({ top: 1 })
currentOpdb.currentOp(), $currentOp aggregation
killOpdb.killOp(opid)
replSetGetStatusrs.status()
addShardsh.addShard()
shardCollectionsh.shardCollection()
fsyncdb.adminCommand({ fsync: 1 })
03
Custom Role Creation
Fine-grained access beyond built-in roles
custom

Custom roles let you define the exact privilege set for a specific use case. Roles are stored in the admin database if you want them usable across all databases, or in a specific database to limit their scope.

// Create a role that can read orders but only write to a staging collection
use shopDb
db.createRole({
  role: "orderReader",
  privileges: [
    {
      resource: { db: "shopDb", collection: "orders" },
      actions: ["find", "listIndexes"]
    },
    {
      resource: { db: "shopDb", collection: "orderStats" },
      actions: ["find", "insert", "update", "remove"]
    }
  ],
  roles: []   // no inherited roles
})

// Create a role that can manage indexes but NOT read/write data
db.createRole({
  role: "indexAdmin",
  privileges: [
    {
      resource: { db: "shopDb", collection: "" },  // all collections in shopDb
      actions: ["createIndex", "dropIndex", "listIndexes", "collStats"]
    },
    {
      resource: { db: "shopDb", collection: "" },
      actions: ["listCollections", "dbStats"]
    }
  ],
  roles: []
})

// Create a monitoring role — read-only serverStatus and currentOp
use admin
db.createRole({
  role: "appMonitor",
  privileges: [
    {
      resource: { cluster: true },
      actions: ["serverStatus", "currentOp", "top"]
    },
    {
      resource: { db: "", collection: "" },   // all resources
      actions: ["dbStats", "collStats", "listCollections", "listIndexes"]
    }
  ],
  roles: []
})

// Update an existing role (replace all privileges)
db.updateRole("orderReader", {
  privileges: [
    {
      resource: { db: "shopDb", collection: "orders" },
      actions: ["find", "listIndexes", "collStats"]   // added collStats
    }
  ],
  roles: []
})

// Drop a custom role
db.dropRole("orderReader")

Resource Specifier Reference

ResourceScope
{ db: "mydb", collection: "orders" }Specific collection in specific database
{ db: "mydb", collection: "" }All collections in a specific database
{ db: "", collection: "orders" }Named collection across all databases
{ db: "", collection: "" }All collections in all databases
{ cluster: true }Cluster-level operations (replication, sharding)
04
Role Inheritance
Roles can inherit from other roles — built-in and custom
inheritance

A role can include other roles in its roles array, inheriting all of their privileges. This allows building layered roles without duplicating privilege definitions.

// Built-in role inheritance chain example:
// dbOwner = readWrite + dbAdmin + userAdmin
// clusterAdmin = clusterManager + clusterMonitor + hostManager

// Custom role inheriting from built-in roles
use shopDb
db.createRole({
  role: "shopAppUser",
  privileges: [
    {
      resource: { db: "shopDb", collection: "coupons" },
      actions: ["find"]                // read-only on coupons (not in readWrite)
    }
  ],
  roles: [
    { role: "readWrite", db: "shopDb" },     // full read/write on shopDb
    { role: "read",      db: "analyticsDb" } // read-only on analytics
  ]
})

// Role inheriting from a custom role
db.createRole({
  role: "shopSuperUser",
  privileges: [],
  roles: [
    { role: "shopAppUser",   db: "shopDb" },  // all shopAppUser privileges
    { role: "dbAdmin",       db: "shopDb" }   // + schema/index admin
  ]
})

// Grant and revoke roles on an existing role
db.grantRolesToRole("shopAppUser", [{ role: "read", db: "logsDb" }])
db.revokeRolesFromRole("shopAppUser", [{ role: "read", db: "analyticsDb" }])
WARN
Role inheritance is additive — you can only add privileges through inheritance, never restrict them. If a parent role grants find on all collections, the child role also has find on all collections with no way to remove it. Design the parent roles narrowly and build up, rather than starting from broad roles and trying to restrict.
05
User Management
Create · Update · Grant · Revoke · Drop
users
// Create user with multiple roles (principal database: shopDb)
use shopDb
db.createUser({
  user: "appService",
  pwd:  passwordPrompt(),   // interactive — never hardcode in scripts
  roles: [
    { role: "readWrite",    db: "shopDb" },
    { role: "read",         db: "catalogDb" }
  ],
  customData: { team: "backend", env: "production" }  // metadata (optional)
})

// Create x.509 user (in $external database)
use $external
db.createUser({
  user: "CN=appService,OU=Engineering,O=MyCompany,C=US",
  roles: [{ role: "readWrite", db: "shopDb" }]
})

// Update user password
use shopDb
db.updateUser("appService", { pwd: "newSecurePass!@#" })

// Update user roles (replaces ALL roles — not additive)
db.updateUser("appService", {
  roles: [
    { role: "readWrite", db: "shopDb" },
    { role: "read",      db: "catalogDb" },
    { role: "read",      db: "inventoryDb" }  // new addition
  ]
})

// Grant additional roles without touching existing ones
db.grantRolesToUser("appService", [
  { role: "clusterMonitor", db: "admin" }
])

// Revoke specific roles
db.revokeRolesFromUser("appService", [
  { role: "read", db: "catalogDb" }
])

// Drop user
db.dropUser("appService")

// Drop ALL users from a database
db.dropAllUsers()

Authentication Database

Every MongoDB user belongs to an authentication database — the database where createUser() was called. When connecting, the driver must specify authSource matching that database.

// User created in shopDb must authenticate against shopDb:
// mongodb://appService:pass@host:27017/shopDb?authSource=shopDb

// Users created in admin db authenticate against admin:
// mongodb://adminUser:pass@host:27017/?authSource=admin

// x.509 users authenticate against $external:
// --authenticationDatabase '$external' --authenticationMechanism MONGODB-X509
06
Role Scope & Database
Where roles are defined affects where they can be granted
scope

Custom roles belong to the database where they were created. A role created in shopDb can only be granted to users whose authentication database is shopDb, or to the admin database users. Roles stored in admin can be granted to any user.

Role LocationCan Be Granted ToUse Case
Defined in adminAny user on any databaseCross-database roles; shared roles
Defined in shopDbUsers in shopDb onlyApplication-specific roles; isolation
Built-in rolesAny user (specify db: where applied)Standard access patterns
// Best practice: create cross-cutting roles in admin
use admin
db.createRole({
  role: "readAllDatabases",
  privileges: [
    { resource: { db: "", collection: "" }, actions: ["find", "listCollections", "listIndexes"] }
  ],
  roles: []
})

// Grant admin-db role to any user
use shopDb
db.grantRolesToUser("reportingUser", [
  { role: "readAllDatabases", db: "admin" }  // must specify db: "admin" since that's where it lives
])

// Create database-scoped role (only usable within shopDb context)
use shopDb
db.createRole({
  role: "shopCartOnly",
  privileges: [
    { resource: { db: "shopDb", collection: "carts" }, actions: ["find", "insert", "update"] }
  ],
  roles: []
})
// This role CANNOT be granted to users in orderDb or admin
07
Role & User Inspection
Audit who has what access
audit
// View all users in current database
db.getUsers()
db.getUsers({ showCredentials: false, showPrivileges: false })

// View a specific user with full privilege expansion
db.getUser("appService", {
  showPrivileges: true,   // expand inherited roles into privileges
  showAuthenticationRestrictions: true
})

// View all roles in current database
db.getRoles({ showBuiltinRoles: false })  // custom roles only
db.getRoles({ showBuiltinRoles: true  })  // all roles including built-ins

// View specific role with inherited privilege expansion
db.getRole("shopAppUser", {
  showPrivileges: true,   // resolve inherited role privileges
  showBuiltinRoles: true
})

// Find all users with a specific role (manual approach via aggregation)
db.system.users.aggregate([
  {
    $match: {
      "roles.role": "dbAdmin",
      "roles.db":   "shopDb"
    }
  },
  { $project: { user: 1, db: 1, roles: 1, _id: 0 } }
])

// usersInfo command (admin-level view across all databases)
use admin
db.adminCommand({ usersInfo: 1 })                // all users in admin
db.adminCommand({ usersInfo: { forAllDBs: true } })// all users everywhere
db.adminCommand({ rolesInfo: 1, showPrivileges: true })
TIP
Regularly audit users and roles as part of your security practice. Rotate application passwords periodically; remove stale users when team members leave or services are decommissioned. In Atlas, use database access reviews and set password expiry policies for human users.