diff options
-rw-r--r-- | jstests/auth/builtin_roles.js | 120 | ||||
-rw-r--r-- | src/mongo/db/auth/authz_manager_external_state_local.cpp | 23 |
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) { |