summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorSara Golemon <sara.golemon@mongodb.com>2020-11-02 23:15:33 +0000
committerEvergreen Agent <no-reply@evergreen.mongodb.com>2020-11-10 22:24:06 +0000
commit458b245815739fa15b9f18543e343444b6129c74 (patch)
tree74b20ef84bf68ed051811e5db7a3ee41fcfd886f
parent23ae68b0fecde9f0484dc276f376697d91fcc344 (diff)
downloadmongo-458b245815739fa15b9f18543e343444b6129c74.tar.gz
SERVER-51859 Fix builtin privileges reporting via rolesInfo
-rw-r--r--jstests/auth/builtin_roles.js120
-rw-r--r--src/mongo/db/auth/authz_manager_external_state_local.cpp23
2 files changed, 136 insertions, 7 deletions
diff --git a/jstests/auth/builtin_roles.js b/jstests/auth/builtin_roles.js
new file mode 100644
index 00000000000..f99fca2061b
--- /dev/null
+++ b/jstests/auth/builtin_roles.js
@@ -0,0 +1,120 @@
+// Check that builtin roles contain valid permissions.
+
+(function() {
+'use strict';
+
+function assertIsBuiltinRole(def, name, expectPrivs = false, expectAuthRest = false) {
+ jsTest.log(tojson(def));
+ assert.eq(def.db, name.db);
+ assert.eq(def.role, name.role);
+ assert.eq(def.isBuiltin, true);
+ assert.eq(def.roles.length, 0);
+ assert.eq(def.inheritedRoles.length, 0);
+ if (expectPrivs === false) {
+ assert(def.privileges === undefined);
+ assert(def.inheritedPrivileges === undefined);
+ } else if (expectPrivs === true) {
+ assert.gt(def.privileges.length, 0);
+ assert.eq(bsonWoCompare(def.privileges, def.inheritedPrivileges), 0);
+ } else {
+ assert.eq(bsonWoCompare(def.privileges, expectPrivs), 0);
+ assert.eq(bsonWoCompare(def.privileges, def.inheritedPrivileges), 0);
+ }
+ if (expectAuthRest === false) {
+ assert(def.authenticationRestrictions === undefined);
+ assert(def.inheritedAuthenticationRestrictions === undefined);
+ } else if (expectAuthRest === true) {
+ assert.eq(def.authenticationRestrictions.length, 0);
+ assert.eq(def.inheritedAuthenticationRestrictions.length, 0);
+ } else {
+ // Builtin roles never have authentication restrictions.
+ assert(false);
+ }
+}
+
+function runTest(mongo) {
+ const admin = mongo.getDB('admin');
+ const test = mongo.getDB('test');
+
+ function rolesInfo(db, role, basic = false) {
+ const cmd = {
+ rolesInfo: role,
+ showPrivileges: !basic,
+ showAuthenticationRestrictions: !basic,
+ showBuiltinRoles: !basic,
+ };
+ return assert.commandWorked(db.runCommand(cmd))
+ .roles.sort((a, b) => (a.role + '.' + a.db).localeCompare(b.role + '.' + b.db, 'en'));
+ }
+
+ const kTestReadRole = {role: 'read', db: 'test'};
+ const kAdminReadRole = {role: 'read', db: 'admin'};
+ const kReadRoleActions = [
+ 'changeStream',
+ 'collStats',
+ 'dbHash',
+ 'dbStats',
+ 'find',
+ 'killCursors',
+ 'listCollections',
+ 'listIndexes',
+ 'planCacheRead'
+ ];
+ const kAdminReadPrivs = [
+ {resource: {db: 'admin', collection: ''}, actions: kReadRoleActions},
+ {resource: {db: 'admin', collection: 'system.js'}, actions: kReadRoleActions},
+ ];
+ const kTestReadPrivs = [
+ {resource: {db: 'test', collection: ''}, actions: kReadRoleActions},
+ {resource: {db: 'test', collection: 'system.js'}, actions: kReadRoleActions},
+ ];
+
+ const adminReadBasic = rolesInfo(admin, 'read', true);
+ assert.eq(adminReadBasic.length, 1);
+ assertIsBuiltinRole(adminReadBasic[0], kAdminReadRole, false, false);
+
+ const adminRead = rolesInfo(admin, 'read');
+ assert.eq(adminRead.length, 1);
+ assertIsBuiltinRole(adminRead[0], kAdminReadRole, true, true);
+ assertIsBuiltinRole(adminRead[0], kAdminReadRole, kAdminReadPrivs, true);
+
+ const testRead = rolesInfo(admin, kTestReadRole);
+ assert.eq(testRead.length, 1);
+ assertIsBuiltinRole(testRead[0], kTestReadRole, true, true);
+ assertIsBuiltinRole(testRead[0], kTestReadRole, kTestReadPrivs, true);
+
+ const testMulti = rolesInfo(admin, ['read', kTestReadRole]);
+ assert.eq(testMulti.length, 2);
+ assertIsBuiltinRole(testMulti[0], kAdminReadRole, kAdminReadPrivs, true);
+ assertIsBuiltinRole(testMulti[1], kTestReadRole, kTestReadPrivs, true);
+
+ const testRole1Privs = [{resource: {db: 'test', collection: ''}, actions: ['insert']}];
+ assert.commandWorked(
+ test.runCommand({createRole: 'role1', roles: ['read'], privileges: testRole1Privs}));
+
+ const testRoles = rolesInfo(test, 1);
+ const testUserRoles = testRoles.filter((r) => !r.isBuiltin);
+ assert.eq(testUserRoles.length, 1);
+ const testUserRole1 = testUserRoles[0];
+ jsTest.log('testUserRole1: ' + tojson(testUserRole1));
+ assert.eq(testUserRole1.db, 'test');
+ assert.eq(testUserRole1.role, 'role1');
+ assert.eq(testUserRole1.roles, [kTestReadRole]);
+ assert.eq(testUserRole1.inheritedRoles, [kTestReadRole]);
+ assert.eq(testUserRole1.privileges, testRole1Privs);
+ assert.eq(testUserRole1.inheritedPrivileges, testRole1Privs.concat(kTestReadPrivs));
+
+ const testRolesReadRole = testRoles.filter((r) => r.role === 'read');
+ assert.eq(testRolesReadRole.length, 1);
+ assertIsBuiltinRole(testRolesReadRole[0], kTestReadRole, kTestReadPrivs, true);
+}
+
+// Standalone
+const mongod = MongoRunner.runMongod();
+runTest(mongod);
+MongoRunner.stopMongod(mongod);
+
+const st = new ShardingTest({shards: 1, mongos: 1, config: 1, other: {shardAsReplicaSet: false}});
+runTest(st.s0);
+st.stop();
+})();
diff --git a/src/mongo/db/auth/authz_manager_external_state_local.cpp b/src/mongo/db/auth/authz_manager_external_state_local.cpp
index 852cb44e57f..966a8271517 100644
--- a/src/mongo/db/auth/authz_manager_external_state_local.cpp
+++ b/src/mongo/db/auth/authz_manager_external_state_local.cpp
@@ -102,6 +102,14 @@ void serializeResolvedRoles(BSONObjBuilder* user,
if (data.privileges) {
BSONArrayBuilder privsBuilder(user->subarrayStart("inheritedPrivileges"));
+ if (roleDoc) {
+ auto privs = roleDoc.get()["privileges"];
+ if (privs) {
+ for (const auto& privilege : privs.Obj()) {
+ privsBuilder.append(privilege);
+ }
+ }
+ }
for (const auto& privilege : data.privileges.get()) {
privsBuilder.append(privilege.toBSON());
}
@@ -338,7 +346,7 @@ Status AuthzManagerExternalStateLocal::getUserDescription(OperationContext* opCt
directRoles = filterAndMapRole(&resultBuilder, userDoc, ResolveRoleOption::kAll, false);
} else {
// We are able to artifically construct the external user from the request
- resultBuilder.append("_id", userName.getUser());
+ resultBuilder.append("_id", str::stream() << userName.getDB() << '.' << userName.getUser());
resultBuilder.append("user", userName.getUser());
resultBuilder.append("db", userName.getDB());
resultBuilder.append("credentials", BSON("external" << true));
@@ -542,16 +550,16 @@ Status AuthzManagerExternalStateLocal::getRolesDescription(
auth::addPrivilegesForBuiltinRole(role, &privs));
BSONObjBuilder builtinBuilder;
- builtinBuilder.append("_id",
- str::stream() << role.getDB() << '.' << role.getRole());
builtinBuilder.append("db", role.getDB());
builtinBuilder.append("role", role.getRole());
builtinBuilder.append("roles", BSONArray());
- BSONArrayBuilder builtinPrivs(builtinBuilder.subarrayStart("privileges"));
- for (const auto& priv : privs) {
- builtinPrivs.append(priv.toBSON());
+ if (showPrivileges == PrivilegeFormat::kShowSeparate) {
+ BSONArrayBuilder builtinPrivs(builtinBuilder.subarrayStart("privileges"));
+ for (const auto& priv : privs) {
+ builtinPrivs.append(priv.toBSON());
+ }
+ builtinPrivs.doneFast();
}
- builtinPrivs.doneFast();
roleDoc = builtinBuilder.obj();
} else {
@@ -568,6 +576,7 @@ Status AuthzManagerExternalStateLocal::getRolesDescription(
auto data = uassertStatusOK(resolveRoles(opCtx, subRoles, option));
data.roles->insert(subRoles.cbegin(), subRoles.cend());
serializeResolvedRoles(&roleBuilder, data, roleDoc);
+ roleBuilder.append("isBuiltin", auth::isBuiltinRole(role));
result->push_back(roleBuilder.obj());
} catch (const AssertionException& ex) {