summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorSpencer Jackson <spencer.jackson@mongodb.com>2016-11-09 19:09:33 -0500
committerSpencer Jackson <spencer.jackson@mongodb.com>2016-11-14 17:31:05 -0500
commitb8e6d89476d421a43c706818d397c913fe3e8921 (patch)
tree44d5ec17347b0539cae956a58492dd5a57386ed7
parent4f43174cf45b1319917ecb8d4a4cce65c8029adc (diff)
downloadmongo-b8e6d89476d421a43c706818d397c913fe3e8921.tar.gz
SERVER-26839: Improve readWriteDatabase role coverage
-rw-r--r--jstests/auth/commands_builtin_roles.js10
-rw-r--r--jstests/auth/commands_user_defined_roles.js19
-rw-r--r--jstests/auth/lib/commands_lib.js362
-rw-r--r--src/mongo/db/auth/authorization_session.cpp7
-rw-r--r--src/mongo/db/auth/privilege_parser.cpp9
-rw-r--r--src/mongo/db/auth/role_graph_builtin_roles.cpp48
6 files changed, 418 insertions, 37 deletions
diff --git a/jstests/auth/commands_builtin_roles.js b/jstests/auth/commands_builtin_roles.js
index 2fb65de8663..9708c08e868 100644
--- a/jstests/auth/commands_builtin_roles.js
+++ b/jstests/auth/commands_builtin_roles.js
@@ -11,8 +11,10 @@ load("jstests/auth/lib/commands_lib.js");
var roles = [
{key: "read", role: "read", dbname: firstDbName},
+ {key: "readLocal", role: {role: "read", db: "local"}, dbname: adminDbName},
{key: "readAnyDatabase", role: "readAnyDatabase", dbname: adminDbName},
{key: "readWrite", role: "readWrite", dbname: firstDbName},
+ {key: "readWriteLocal", role: {role: "readWrite", db: "local"}, dbname: adminDbName},
{key: "readWriteAnyDatabase", role: "readWriteAnyDatabase", dbname: adminDbName},
{key: "userAdmin", role: "userAdmin", dbname: firstDbName},
{key: "userAdminAnyDatabase", role: "userAdminAnyDatabase", dbname: adminDbName},
@@ -47,10 +49,10 @@ function testProperAuthorization(conn, t, testcase, r) {
var runOnDb = conn.getDB(testcase.runOnDb);
authCommandsLib.setup(conn, t, runOnDb);
- assert(r.db.auth("user|" + r.role, "password"));
+ assert(r.db.auth("user|" + r.key, "password"));
var res = runOnDb.runCommand(t.command);
- if (testcase.roles[r.role]) {
+ if (testcase.roles[r.key]) {
if (res.ok == 0 && res.code == authErrCode) {
out = "expected authorization success" + " but received " + tojson(res) + " on db " +
testcase.runOnDb + " with role " + r.key;
@@ -104,7 +106,7 @@ function createUsers(conn) {
for (var i = 0; i < roles.length; i++) {
r = roles[i];
r.db = conn.getDB(r.dbname);
- r.db.createUser({user: "user|" + r.role, pwd: "password", roles: [r.role]});
+ r.db.createUser({user: "user|" + r.key, pwd: "password", roles: [r.role]});
}
adminDb.logout();
}
@@ -122,7 +124,7 @@ function checkForNonExistentRoles() {
for (role in testcase.roles) {
var roleExists = false;
for (var k = 0; k < roles.length; k++) {
- if (roles[k].role === role) {
+ if (roles[k].key === role) {
roleExists = true;
break;
}
diff --git a/jstests/auth/commands_user_defined_roles.js b/jstests/auth/commands_user_defined_roles.js
index dd6abc7d252..e54c9340e42 100644
--- a/jstests/auth/commands_user_defined_roles.js
+++ b/jstests/auth/commands_user_defined_roles.js
@@ -123,13 +123,26 @@ function runOneTest(conn, t) {
failures.push(t.testname + ": " + msg);
}
+ var specialResource = function(resource) {
+ if (!resource)
+ return true;
+
+ // Tests which use {db: "local", collection: "oplog.rs"} will not work with
+ // {db: "", collection: "oplog.rs"}. oplog.rs is special, and does not match with
+ // forDatabaseName or anyNormalResource ResourcePatterns. The same is true of
+ // oplog.$main, but oplog.$main is also an illegal collection name on any database
+ // other than local. The other collections checked for here in the local database have
+ // the same property as oplog.rs.
+ return !resource.db || !resource.collection ||
+ resource.collection.startsWith("system.") || resource.db == "local";
+ };
+
// Test for proper authorization with the test case's privileges where non-system
// collections are modified to be the empty string.
msg = testProperAuthorization(conn, t, testcase, testcase.privileges.map(function(priv) {
// Make a copy of the privilege so as not to modify the original array.
var modifiedPrivilege = Object.extend({}, priv, true);
- if (modifiedPrivilege.resource.collection &&
- !modifiedPrivilege.resource.collection.startsWith('system.')) {
+ if (modifiedPrivilege.resource.collection && !specialResource(priv.resource)) {
modifiedPrivilege.resource.collection = "";
}
return modifiedPrivilege;
@@ -143,7 +156,7 @@ function runOneTest(conn, t) {
msg = testProperAuthorization(conn, t, testcase, testcase.privileges.map(function(priv) {
// Make a copy of the privilege so as not to modify the original array.
var modifiedPrivilege = Object.extend({}, priv, true);
- if (modifiedPrivilege.resource.db) {
+ if (!specialResource(priv.resource)) {
modifiedPrivilege.resource.db = "";
}
return modifiedPrivilege;
diff --git a/jstests/auth/lib/commands_lib.js b/jstests/auth/lib/commands_lib.js
index daea8325abb..e58836ebf05 100644
--- a/jstests/auth/lib/commands_lib.js
+++ b/jstests/auth/lib/commands_lib.js
@@ -142,8 +142,10 @@ var roles_hostManager = {hostManager: 1, clusterAdmin: 1, root: 1, __system: 1};
var roles_clusterManager = {clusterManager: 1, clusterAdmin: 1, root: 1, __system: 1};
var roles_all = {
read: 1,
+ readLocal: 1,
readAnyDatabase: 1,
readWrite: 1,
+ readWriteLocal: 1,
readWriteAnyDatabase: 1,
userAdmin: 1,
userAdminAnyDatabase: 1,
@@ -195,7 +197,7 @@ var authCommandsLib = {
skipStandalone: true,
testcases: [{
runOnDb: "config",
- roles: Object.extend({readWriteAnyDatabase: 1}, roles_clusterManager)
+ roles: roles_clusterManager,
}]
},
@@ -2354,6 +2356,169 @@ var authCommandsLib = {
]
},
{
+ testname: "find_config_changelog",
+ command: {find: "changelog"},
+ testcases: [
+ {
+ runOnDb: "config",
+ roles: {
+ "clusterAdmin": 1,
+ "clusterManager": 1,
+ "clusterMonitor": 1,
+ "backup": 1,
+ "root": 1,
+ "__system": 1
+ },
+ privileges:
+ [{resource: {db: "config", collection: "changelog"}, actions: ["find"]}]
+ },
+ ]
+ },
+ {
+ testname: "find_local_me",
+ skipSharded: true,
+ command: {find: "me"},
+ testcases: [
+ {
+ runOnDb: "local",
+ roles: {
+ "clusterAdmin": 1,
+ "clusterMonitor": 1,
+ "readLocal": 1,
+ "readWriteLocal": 1,
+ "backup": 1,
+ "root": 1,
+ "__system": 1
+ },
+ privileges: [{resource: {db: "local", collection: "me"}, actions: ["find"]}]
+ },
+ ]
+ },
+ {
+ testname: "find_oplog_main",
+ skipSharded: true,
+ command: {find: "oplog.$main"},
+ testcases: [
+ {
+ runOnDb: "local",
+ roles: {
+ "clusterAdmin": 1,
+ "clusterMonitor": 1,
+ "readLocal": 1,
+ "readWriteLocal": 1,
+ "backup": 1,
+ "root": 1,
+ "__system": 1
+ },
+ privileges:
+ [{resource: {db: "local", collection: "oplog.$main"}, actions: ["find"]}]
+ },
+ ]
+ },
+ {
+ testname: "find_oplog_rs",
+ skipSharded: true,
+ command: {find: "oplog.rs"},
+ testcases: [
+ {
+ runOnDb: "local",
+ roles: {
+ "clusterAdmin": 1,
+ "clusterMonitor": 1,
+ "readLocal": 1,
+ "readWriteLocal": 1,
+ "backup": 1,
+ "root": 1,
+ "__system": 1
+ },
+ privileges: [{resource: {db: "local", collection: "oplog.rs"}, actions: ["find"]}]
+ },
+ ]
+ },
+ {
+ testname: "find_replset_election",
+ skipSharded: true,
+ command: {find: "replset.election"},
+ testcases: [
+ {
+ runOnDb: "local",
+ roles: {
+ "clusterAdmin": 1,
+ "clusterMonitor": 1,
+ "readLocal": 1,
+ "readWriteLocal": 1,
+ "backup": 1,
+ "root": 1,
+ "__system": 1
+ },
+ privileges:
+ [{resource: {db: "local", collection: "replset.election"}, actions: ["find"]}]
+ },
+ ]
+ },
+ {
+ testname: "find_replset_minvalid",
+ skipSharded: true,
+ command: {find: "replset.minvalid"},
+ testcases: [
+ {
+ runOnDb: "local",
+ roles: {
+ "clusterAdmin": 1,
+ "clusterMonitor": 1,
+ "readLocal": 1,
+ "readWriteLocal": 1,
+ "backup": 1,
+ "root": 1,
+ "__system": 1
+ },
+ privileges:
+ [{resource: {db: "local", collection: "replset.minvalid"}, actions: ["find"]}]
+ },
+ ]
+ },
+ {
+ testname: "find_sources",
+ skipSharded: true,
+ command: {find: "sources"},
+ testcases: [
+ {
+ runOnDb: "local",
+ roles: {
+ "clusterAdmin": 1,
+ "clusterMonitor": 1,
+ "readLocal": 1,
+ "readWriteLocal": 1,
+ "backup": 1,
+ "root": 1,
+ "__system": 1
+ },
+ privileges: [{resource: {db: "local", collection: "sources"}, actions: ["find"]}]
+ },
+ ]
+ },
+ {
+ testname: "find_startup_log",
+ command: {find: "startup_log"},
+ skipSharded: true,
+ testcases: [
+ {
+ runOnDb: "local",
+ roles: {
+ "clusterAdmin": 1,
+ "clusterMonitor": 1,
+ "readLocal": 1,
+ "readWriteLocal": 1,
+ "backup": 1,
+ "root": 1,
+ "__system": 1
+ },
+ privileges:
+ [{resource: {db: "local", collection: "startup_log"}, actions: ["find"]}]
+ },
+ ]
+ },
+ {
testname: "find_views",
setup: function(db) {
db.createView("view", "collection", [{$match: {}}]);
@@ -2713,6 +2878,189 @@ var authCommandsLib = {
]
},
{
+ testname: "insert",
+ command: {insert: "foo", documents: [{data: 5}]},
+ testcases: [
+ {
+ runOnDb: firstDbName,
+ roles: roles_write,
+ privileges: [{resource: {db: firstDbName, collection: "foo"}, actions: ["insert"]}],
+ },
+ {
+ runOnDb: secondDbName,
+ roles: {"readWriteAnyDatabase": 1, "root": 1, "__system": 1, "restore": 1},
+ privileges:
+ [{resource: {db: secondDbName, collection: "foo"}, actions: ["insert"]}],
+ }
+
+ ]
+ },
+ {
+ testname: "insert_config_changelog",
+ command: {insert: "changelog", documents: [{data: 5}]},
+ testcases: [{
+ runOnDb: "config",
+ roles:
+ {"clusterAdmin": 1, "clusterManager": 1, "root": 1, "__system": 1, "restore": 1},
+ privileges:
+ [{resource: {db: "config", collection: "changelog"}, actions: ["insert"]}],
+ }]
+ },
+ {
+ testname: "insert_me",
+ command: {insert: "me", documents: [{data: 5}]},
+ skipSharded: true,
+ testcases: [{
+ runOnDb: "local",
+ roles: {
+ "clusterAdmin": 1,
+ "clusterManager": 1,
+ "readWriteLocal": 1,
+ "root": 1,
+ "__system": 1,
+ "restore": 1
+ },
+ privileges: [{resource: {db: "local", collection: "me"}, actions: ["insert"]}],
+ }]
+ },
+ /* Untestable, because insert to oplog.$main will always fail
+ {
+ testname: "insert_oplog_main",
+ command: {insert: "oplog.$main", documents: [{ts: Timestamp()}]},
+ skipSharded: true,
+ setup: function(db) {
+ db.createCollection("oplog.$main", {capped: true, size: 10000});
+ },
+ teardown: function(db) {
+ db.oplog.$main.drop();
+ },
+ testcases: [
+ {
+ runOnDb: "local",
+ roles: {"clusterAdmin": 1, "clusterMonitor": 1, "readWriteLocal": 1, "restore": 1,
+ "root": 1, "__system": 1},
+ privileges:
+ [{resource: {db: "local", collection: "oplog.$main"}, actions: ["insert"]}],
+ },
+
+ ]
+ },*/
+ {
+ testname: "insert_oplog_rs",
+ command: {insert: "oplog.rs", documents: [{ts: Timestamp()}]},
+ skipSharded: true,
+ setup: function(db) {
+ db.createCollection("oplog.rs", {capped: true, size: 10000});
+ },
+ teardown: function(db) {
+ db.oplog.rs.drop();
+ },
+ testcases: [
+ {
+ runOnDb: "local",
+ roles: {
+ "clusterAdmin": 1,
+ "clusterManager": 1,
+ "readWriteLocal": 1,
+ "restore": 1,
+ "root": 1,
+ "__system": 1
+ },
+ privileges:
+ [{resource: {db: "local", collection: "oplog.rs"}, actions: ["insert"]}],
+ },
+
+ ]
+ },
+
+ {
+ testname: "insert_replset_election",
+ command: {insert: "replset.election", documents: [{data: 5}]},
+ skipSharded: true,
+ testcases: [{
+ runOnDb: "local",
+ roles: {
+ "clusterAdmin": 1,
+ "clusterManager": 1,
+ "readWriteLocal": 1,
+ "root": 1,
+ "__system": 1,
+ "restore": 1
+ },
+ privileges:
+ [{resource: {db: "local", collection: "replset.election"}, actions: ["insert"]}],
+ }
+
+ ]
+ },
+ {
+ testname: "insert_replset_minvalid",
+ command: {insert: "replset.minvalid", documents: [{data: 5}]},
+ skipSharded: true,
+ testcases: [{
+ runOnDb: "local",
+ roles: {
+ "clusterAdmin": 1,
+ "clusterManager": 1,
+ "readWriteLocal": 1,
+ "root": 1,
+ "__system": 1,
+ "restore": 1
+ },
+ privileges:
+ [{resource: {db: "local", collection: "replset.minvalid"}, actions: ["insert"]}],
+ }
+
+ ]
+ },
+ {
+ testname: "insert_system_users",
+ command: {insert: "system.users", documents: [{data: 5}]},
+ testcases: [
+ {
+ runOnDb: "admin",
+ roles: {"root": 1, "__system": 1, "restore": 1},
+ privileges:
+ [{resource: {db: "admin", collection: "system.users"}, actions: ["insert"]}],
+ },
+ ]
+ },
+ {
+ testname: "insert_sources",
+ command: {insert: "sources", documents: [{data: 5}]},
+ skipSharded: true,
+ testcases: [{
+ runOnDb: "local",
+ roles: {
+ "clusterAdmin": 1,
+ "clusterManager": 1,
+ "readWriteLocal": 1,
+ "root": 1,
+ "__system": 1,
+ "restore": 1
+ },
+ privileges: [{resource: {db: "local", collection: "sources"}, actions: ["insert"]}],
+ }]
+ },
+ {
+ testname: "insert_startup_log",
+ command: {insert: "startup_log", documents: [{data: 5}]},
+ skipSharded: true,
+ testcases: [{
+ runOnDb: "local",
+ roles: {
+ "clusterAdmin": 1,
+ "clusterManager": 1,
+ "readWriteLocal": 1,
+ "root": 1,
+ "__system": 1,
+ "restore": 1
+ },
+ privileges:
+ [{resource: {db: "local", collection: "startup_log"}, actions: ["insert"]}],
+ }]
+ },
+ {
testname: "isMaster",
command: {isMaster: 1},
testcases: [
@@ -3909,9 +4257,7 @@ var authCommandsLib = {
testcases: [
{
runOnDb: adminDbName,
- // addShardToZone only checks that you can write to config.shards,
- // that's why readWriteAnyDatabase passes.
- roles: Object.extend({readWriteAnyDatabase: 1}, roles_clusterManager),
+ roles: roles_clusterManager,
privileges: [{resource: {db: 'config', collection: 'shards'}, actions: ['update']}],
},
]
@@ -3931,9 +4277,7 @@ var authCommandsLib = {
testcases: [
{
runOnDb: adminDbName,
- // removeShardZone only checks that you can write to config.shards,
- // that's why readWriteAnyDatabase passes.
- roles: Object.extend({readWriteAnyDatabase: 1}, roles_clusterManager),
+ roles: roles_clusterManager,
privileges: [
{resource: {db: 'config', collection: 'shards'}, actions: ['update']},
{resource: {db: 'config', collection: 'tags'}, actions: ['find']}
@@ -3956,9 +4300,7 @@ var authCommandsLib = {
testcases: [
{
runOnDb: adminDbName,
- // updateZoneKeyRange only checks that you can write on config.tags,
- // that's why readWriteAnyDatabase passes.
- roles: Object.extend({readWriteAnyDatabase: 1}, roles_clusterManager),
+ roles: roles_clusterManager,
privileges: [
{resource: {db: 'config', collection: 'shards'}, actions: ['find']},
{
diff --git a/src/mongo/db/auth/authorization_session.cpp b/src/mongo/db/auth/authorization_session.cpp
index b140ca36dff..6acd07ac4e6 100644
--- a/src/mongo/db/auth/authorization_session.cpp
+++ b/src/mongo/db/auth/authorization_session.cpp
@@ -641,7 +641,12 @@ static int buildResourceSearchList(const ResourcePattern& target,
resourceSearchList[size++] = ResourcePattern::forAnyResource();
if (target.isExactNamespacePattern()) {
if (!target.ns().isSystem()) {
- resourceSearchList[size++] = ResourcePattern::forAnyNormalResource();
+ // Some databases should not be matchable with ResourcePattern::forAnyNormalResource.
+ // 'local' and 'config' are used to store special system collections, which user level
+ // administrators should not be able to manipulate.
+ if (target.ns().db() != "local" && target.ns().db() != "config") {
+ resourceSearchList[size++] = ResourcePattern::forAnyNormalResource();
+ }
resourceSearchList[size++] = ResourcePattern::forDatabaseName(target.ns().db());
}
resourceSearchList[size++] = ResourcePattern::forCollectionName(target.ns().coll());
diff --git a/src/mongo/db/auth/privilege_parser.cpp b/src/mongo/db/auth/privilege_parser.cpp
index e9781c818bc..42bae0fcb56 100644
--- a/src/mongo/db/auth/privilege_parser.cpp
+++ b/src/mongo/db/auth/privilege_parser.cpp
@@ -95,8 +95,13 @@ bool ParsedResource::isValid(std::string* errMsg) const {
}
if (isCollectionSet() &&
(!NamespaceString::validCollectionName(getCollection()) && !getCollection().empty())) {
- *errMsg = stream() << getCollection() << " is not a valid collection name";
- return false;
+ // local.oplog.$main is a real collection that the server will create. But, collection
+ // names with a '$' character are illegal. We must make an exception for this collection
+ // here so we can grant users access to it.
+ if (!(getDb() == "local" && getCollection() == "oplog.$main")) {
+ *errMsg = stream() << getCollection() << " is not a valid collection name";
+ return false;
+ }
}
return true;
}
diff --git a/src/mongo/db/auth/role_graph_builtin_roles.cpp b/src/mongo/db/auth/role_graph_builtin_roles.cpp
index a25dc14eed9..3e8dd382a50 100644
--- a/src/mongo/db/auth/role_graph_builtin_roles.cpp
+++ b/src/mongo/db/auth/role_graph_builtin_roles.cpp
@@ -414,14 +414,17 @@ void addClusterMonitorPrivileges(PrivilegeVector* privileges) {
Privilege::addPrivilegeToPrivilegeVector(
privileges,
Privilege(ResourcePattern::forAnyNormalResource(), clusterMonitorRoleDatabaseActions));
- addReadOnlyDbPrivileges(privileges, "config");
Privilege::addPrivilegeToPrivilegeVector(
privileges,
- Privilege(ResourcePattern::forExactNamespace(NamespaceString("local.system.replset")),
- ActionType::find));
+ Privilege(ResourcePattern::forDatabaseName("config"), clusterMonitorRoleDatabaseActions));
+ Privilege::addPrivilegeToPrivilegeVector(
+ privileges,
+ Privilege(ResourcePattern::forDatabaseName("local"), clusterMonitorRoleDatabaseActions));
+ addReadOnlyDbPrivileges(privileges, "local");
+ addReadOnlyDbPrivileges(privileges, "config");
Privilege::addPrivilegeToPrivilegeVector(
privileges,
- Privilege(ResourcePattern::forExactNamespace(NamespaceString("local.sources")),
+ Privilege(ResourcePattern::forExactNamespace(NamespaceString("local", "system.replset")),
ActionType::find));
Privilege::addPrivilegeToPrivilegeVector(
privileges,
@@ -444,27 +447,25 @@ void addClusterManagerPrivileges(PrivilegeVector* privileges) {
Privilege::addPrivilegeToPrivilegeVector(
privileges,
Privilege(ResourcePattern::forAnyNormalResource(), clusterManagerRoleDatabaseActions));
- addReadOnlyDbPrivileges(privileges, "config");
-
- ActionSet writeActions;
- writeActions << ActionType::insert << ActionType::update << ActionType::remove;
Privilege::addPrivilegeToPrivilegeVector(
privileges,
- Privilege(ResourcePattern::forExactNamespace(NamespaceString("config", "settings")),
- writeActions));
+ Privilege(ResourcePattern::forDatabaseName("config"), clusterManagerRoleDatabaseActions));
+ Privilege::addPrivilegeToPrivilegeVector(
+ privileges,
+ Privilege(ResourcePattern::forDatabaseName("local"), clusterManagerRoleDatabaseActions));
Privilege::addPrivilegeToPrivilegeVector(
privileges,
Privilege(ResourcePattern::forExactNamespace(NamespaceString("local", "system.replset")),
readRoleActions));
+ addReadOnlyDbPrivileges(privileges, "config");
+
+ ActionSet writeActions;
+ writeActions << ActionType::insert << ActionType::update << ActionType::remove;
Privilege::addPrivilegeToPrivilegeVector(
- privileges,
- Privilege(ResourcePattern::forExactNamespace(NamespaceString("config", "tags")),
- writeActions));
- // Primarily for zone commands
+ privileges, Privilege(ResourcePattern::forDatabaseName("config"), writeActions));
Privilege::addPrivilegeToPrivilegeVector(
- privileges,
- Privilege(ResourcePattern::forExactNamespace(NamespaceString("config", "shards")),
- writeActions));
+ privileges, Privilege(ResourcePattern::forDatabaseName("local"), writeActions));
+
// Fake collection used for setFeatureCompatibilityVersion permissions.
Privilege::addPrivilegeToPrivilegeVector(
privileges,
@@ -498,6 +499,12 @@ void addBackupPrivileges(PrivilegeVector* privileges) {
privileges, Privilege(ResourcePattern::forClusterResource(), clusterActions));
Privilege::addPrivilegeToPrivilegeVector(
+ privileges, Privilege(ResourcePattern::forDatabaseName("config"), ActionType::find));
+
+ Privilege::addPrivilegeToPrivilegeVector(
+ privileges, Privilege(ResourcePattern::forDatabaseName("local"), ActionType::find));
+
+ Privilege::addPrivilegeToPrivilegeVector(
privileges,
Privilege(ResourcePattern::forCollectionName("system.indexes"), ActionType::find));
@@ -567,6 +574,12 @@ void addRestorePrivileges(PrivilegeVector* privileges) {
Privilege::addPrivilegeToPrivilegeVector(
privileges, Privilege(ResourcePattern::forAnyResource(), ActionType::listCollections));
+ Privilege::addPrivilegeToPrivilegeVector(
+ privileges, Privilege(ResourcePattern::forDatabaseName("config"), actions));
+
+ Privilege::addPrivilegeToPrivilegeVector(
+ privileges, Privilege(ResourcePattern::forDatabaseName("local"), actions));
+
// Privileges for user/role management
Privilege::addPrivilegeToPrivilegeVector(
privileges, Privilege(ResourcePattern::forAnyNormalResource(), userAdminRoleActions));
@@ -624,6 +637,7 @@ void addRootRolePrivileges(PrivilegeVector* privileges) {
addUserAdminAnyDbPrivileges(privileges);
addDbAdminAnyDbPrivileges(privileges);
addReadWriteAnyDbPrivileges(privileges);
+ addBackupPrivileges(privileges);
addRestorePrivileges(privileges);
Privilege::addPrivilegeToPrivilegeVector(
privileges, Privilege(ResourcePattern::forAnyResource(), ActionType::validate));