// This test ensures that internal commands only run with proper authorization and fail without // proper authorization. // @tags: [requires_profiling] import {testOnlyCommands} from "jstests/auth/test_only_commands_list.js"; import {AllCommandsTest} from "jstests/libs/all_commands_test.js"; import {ShardingTest} from "jstests/libs/shardingtest.js"; // Multiple users cannot be authenticated on one connection within a session. TestData.disableImplicitSessions = true; // Cannot run the filtering metadata check on tests that run refineCollectionShardKey. TestData.skipCheckShardFilteringMetadata = true; const adminDbName = "admin"; const shard0name = "shard0000"; const dbName = "admin"; const collName = dbName + ".coll"; const ns = dbName + ".ns"; const dbPath = MongoRunner.toRealDir("$dataDir/commands_built_in_roles_sharded/"); const migrationOperationId = UUID(); const testOnlyCommandsSet = new Set(testOnlyCommands); const sysUser = { user: "admin", pwd: "password", roles: ["__system"] }; const noroleUser = { user: "testuser", pwd: "password", roles: [] }; /** * internalCommandsMap contains tests for each internal command. For each command name there is * a test object. Each test object inside the map has the following fields. * * testname: The name of the command to run. * command: This includes the command details to run using runCommand. * precommand: This function is run before the sysUser runs the command (to be tested). This * creates preconditions so that the command is not blocked. * postcommand: This function is run after the sysUser runs the command (to be tested) * successfully. This ensures other commands will not be blocked. * skip: Some commands are skipped. */ const internalCommandsMap = { _clusterQueryWithoutShardKey: { testname: "_clusterQueryWithoutShardKey", command: { _clusterQueryWithoutShardKey: 1, writeCmd: { update: "foo", updates: [ {q: {x: 1}, u: {$set: {a: 90}, upsert: false}}, ] }, }, }, _addShard: { testname: "_addShard", command: { _addShard: 1, shardIdentity: { shardName: shard0name, clusterId: ObjectId('5b2031806195dffd744258ee'), configsvrConnectionString: "foobarbaz/host:20022,host:20023,host:20024" } }, }, _clusterWriteWithoutShardKey: { testname: "_clusterWriteWithoutShardKey", command: {_clusterWriteWithoutShardKey: 1, writeCmd: {}, shardId: "", targetDocId: {}}, }, _configsvrAbortReshardCollection: { testname: "_configsvrAbortReshardCollection", command: {_configsvrAbortReshardCollection: "test.x"}, }, _configsvrAddShard: { testname: "_configsvrAddShard", command: {_configsvrAddShard: "x"}, }, _configsvrAddShardToZone: { testname: "_configsvrAddShardToZone", command: {_configsvrAddShardToZone: shard0name, zone: 'z'}, }, _configsvrBalancerStart: { testname: "_configsvrBalancerStart", command: {_configsvrBalancerStart: 1}, }, _configsvrBalancerStatus: { testname: "_configsvrBalancerStatus", command: {_configsvrBalancerStatus: 1}, }, _configsvrBalancerStop: { testname: "_configsvrBalancerStop", command: {_configsvrBalancerStop: 1}, }, _configsvrCommitChunkMigration: { testname: "_configsvrCommitChunkMigration", command: { _configsvrCommitChunkMigration: "db.fooHashed", fromShard: "move_chunk_basic-rs0", toShard: "move_chunk_basic-rs1", migratedChunk: { lastmod: { e: new ObjectId('62b052ac7f5653479a67a54f'), t: new Timestamp(1655722668, 22), v: new Timestamp(1, 0) }, min: {_id: MinKey}, max: {_id: 611686018427387902} }, fromShardCollectionVersion: { e: new ObjectId('62b052ac7f5653479a67a54f'), t: new Timestamp(1655722668, 22), v: new Timestamp(1, 3) }, validAfter: new Timestamp(1655722670, 6) }, }, _configsvrBalancerCollectionStatus: { testname: "_configsvrBalancerCollectionStatus", command: { _configsvrBalancerCollectionStatus: "x.y", }, }, _configsvrCleanupReshardCollection: { testname: "_configsvrCleanupReshardCollection", command: {_configsvrCleanupReshardCollection: "test.x"}, }, _configsvrClearJumboFlag: { testname: "_configsvrClearJumboFlag", command: {_configsvrClearJumboFlag: "x.y", epoch: ObjectId(), minKey: {x: 0}, maxKey: {x: 10}}, }, _configsvrCommitChunksMerge: { testname: "_configsvrCommitChunksMerge", command: { _configsvrCommitChunksMerge: "x.y", shard: shard0name, collUUID: {uuid: UUID()}, chunkRange: {min: {a: 1}, max: {a: 10}} }, }, _configsvrCommitChunkSplit: { testname: "_configsvrCommitChunkSplit", command: {_configsvrCommitChunkSplit: "x.y"}, }, _configsvrCommitIndex: { testname: "_configsvrCommitIndex", command: { _configsvrCommitIndex: "x.y", keyPattern: {x: 1}, name: 'x_1', options: {}, collectionUUID: UUID(), collectionIndexUUID: UUID(), lastmod: Timestamp(1, 0), }, }, _configsvrCommitRefineCollectionShardKey: { testname: "_configsvrCommitRefineCollectionShardKey", command: { _configsvrCommitRefineCollectionShardKey: "test.x", key: {aKey: 1}, newEpoch: new ObjectId(), newTimestamp: Timestamp(), oldTimestamp: Timestamp() }, }, _configsvrCommitMergeAllChunksOnShard: { testname: "_configsvrCommitMergeAllChunksOnShard", command: {_configsvrCommitMergeAllChunksOnShard: "test.x", shard: shard0name}, }, _configsvrCommitReshardCollection: { testname: "_configsvrCommitReshardCollection", command: {_configsvrCommitReshardCollection: "test.x"}, }, _configsvrConfigureCollectionBalancing: { testname: "_configsvrConfigureCollectionBalancing", command: {_configsvrConfigureCollectionBalancing: "test.x"}, }, _configsvrCreateDatabase: { testname: "_configsvrCreateDatabase", command: {_configsvrCreateDatabase: "test.x", primaryShardId: ""}, }, _configsvrDropIndexCatalogEntry: { testname: "_configsvrDropIndexCatalogEntry", command: { _configsvrDropIndexCatalogEntry: "x.y", name: 'x_1', collectionUUID: UUID(), lastmod: Timestamp(1, 0), }, }, _configsvrCheckClusterMetadataConsistency: { testname: "_configsvrCheckClusterMetadataConsistency", command: { _configsvrCheckClusterMetadataConsistency: "x.y", cursor: {}, }, }, _configsvrCheckMetadataConsistency: { testname: "_configsvrCheckMetadataConsistency", command: { _configsvrCheckMetadataConsistency: "x.y", cursor: {}, }, }, _configsvrCollMod: { testname: "_configsvrCollMod", command: { _configsvrCollMod: "x.y", collModRequest: {}, }, }, _configsvrCommitMovePrimary: { testname: "_configsvrCommitMovePrimary", command: { _configsvrCommitMovePrimary: "test", expectedDatabaseVersion: { uuid: new UUID(), timestamp: new Timestamp(1691525961, 12), lastMod: NumberInt(5), }, to: shard0name }, }, _configsvrEnsureChunkVersionIsGreaterThan: { testname: "_configsvrEnsureChunkVersionIsGreaterThan", command: { _configsvrEnsureChunkVersionIsGreaterThan: "x.y", minKey: {}, maxKey: {}, version: {e: ObjectId("6657bdabfd296e9f62d2816c"), t: Timestamp(), v: Timestamp()}, collectionUUID: UUID(), nss: ns, }, }, _configsvrGetHistoricalPlacement: { testname: "_configsvrGetHistoricalPlacement", command: { _configsvrGetHistoricalPlacement: "x.y", at: new Timestamp(1691525961, 12), }, }, _configsvrMoveRange: { testname: "_configsvrMoveRange", command: { _configsvrMoveRange: "x.y", toShard: shard0name, }, }, _configsvrRemoveChunks: { testname: "_configsvrRemoveChunks", command: { _configsvrRemoveChunks: 1, collectionUUID: UUID(), }, }, _configsvrRemoveShard: { testname: "_configsvrRemoveShard", command: {_configsvrRemoveShard: 1, removeShard: shard0name}, }, _configsvrRemoveShardFromZone: { testname: "_configsvrRemoveShardFromZone", command: {_configsvrRemoveShardFromZone: 1, removeShard: shard0name, zone: 'z'}, }, _configsvrRemoveTags: { testname: "_configsvrRemoveTags", command: {_configsvrRemoveTags: "test"}, }, _configsvrRepairShardedCollectionChunksHistory: { testname: "_configsvrRepairShardedCollectionChunksHistory", command: {_configsvrRepairShardedCollectionChunksHistory: ns}, }, _configsvrResetPlacementHistory: { testname: "_configsvrResetPlacementHistory", command: {_configsvrResetPlacementHistory: ns}, }, _configsvrReshardCollection: { testname: "_configsvrReshardCollection", command: {_configsvrReshardCollection: ns, key: {_id: 1}}, }, _configsvrRunRestore: { testname: "_configsvrRunRestore", command: {_configsvrRunRestore: 1}, }, _configsvrSetAllowMigrations: { testname: "_configsvrSetAllowMigrations", command: { _configsvrSetAllowMigrations: ns, allowMigrations: false, writeConcern: {w: "majority"} }, }, _configsvrSetClusterParameter: { testname: "_configsvrSetClusterParameter", command: { _configsvrSetClusterParameter: {}, }, }, _configsvrSetUserWriteBlockMode: { testname: "_configsvrSetUserWriteBlockMode", command: {_configsvrSetUserWriteBlockMode: 1, global: true}, }, _configsvrTransitionFromDedicatedConfigServer: { testname: "_configsvrTransitionFromDedicatedConfigServer", command: {_configsvrTransitionFromDedicatedConfigServer: 1}, }, _configsvrTransitionToDedicatedConfigServer: { testname: "_configsvrTransitionToDedicatedConfigServer", command: {_configsvrTransitionToDedicatedConfigServer: 1}, }, _configsvrUpdateZoneKeyRange: { testname: "_configsvrUpdateZoneKeyRange", command: {_configsvrUpdateZoneKeyRange: 'test.foo', min: {x: 1}, max: {x: 5}, zone: 'z'}, }, _dropConnectionsToMongot: { testname: "_dropConnectionsToMongot", command: {_dropConnectionsToMongot: 1, hostAndPort: []}, }, _flushDatabaseCacheUpdates: { testname: "_flushDatabaseCacheUpdates", command: {_flushDatabaseCacheUpdates: 'test'}, }, _flushDatabaseCacheUpdatesWithWriteConcern: { testname: "_flushDatabaseCacheUpdatesWithWriteConcern", command: {_flushDatabaseCacheUpdatesWithWriteConcern: 'test', writeConcern: {w: 2}}, }, _flushReshardingStateChange: { testname: "_flushReshardingStateChange", command: { _flushReshardingStateChange: ns, reshardingUUID: UUID(), }, }, _flushRoutingTableCacheUpdates: { testname: "_flushRoutingTableCacheUpdates", command: { _flushRoutingTableCacheUpdates: ns, }, }, _flushRoutingTableCacheUpdatesWithWriteConcern: { testname: "_flushRoutingTableCacheUpdatesWithWriteConcern", command: {_flushRoutingTableCacheUpdatesWithWriteConcern: ns, writeConcern: {w: 2}}, }, _getNextSessionMods: { testname: "_getNextSessionMods", command: {_getNextSessionMods: "a-b"}, }, _getUserCacheGeneration: { testname: "_getUserCacheGeneration", command: {_getUserCacheGeneration: 1}, }, _hashBSONElement: { testname: "_hashBSONElement", command: {_hashBSONElement: 0, seed: 1}, }, _isSelf: { skip: true, // This command does not need '__system' role as it is currently used in atlas // tools. testname: "_isSelf", command: {_isSelf: 1}, }, _killOperations: { testname: "_killOperations", command: {_killOperations: 1, operationKeys: [UUID()]}, }, _mergeAuthzCollections: { testname: "_mergeAuthzCollections", command: { _mergeAuthzCollections: 1, tempUsersCollection: 'admin.tempusers', tempRolesCollection: 'admin.temproles', db: "", drop: false }, }, _migrateClone: { testname: "_migrateClone", command: {_migrateClone: "test"}, }, _mongotConnPoolStats: { testname: "_mongotConnPoolStats", command: {_mongotConnPoolStats: 1}, }, _recvChunkAbort: { testname: "_recvChunkAbort", command: {_recvChunkAbort: 1}, }, _recvChunkCommit: { testname: "_recvChunkCommit", command: {_recvChunkCommit: 1}, }, _recvChunkReleaseCritSec: { testname: "_recvChunkReleaseCritSec", command: {_recvChunkReleaseCritSec: 1}, }, _recvChunkStart: { testname: "_recvChunkStart", command: {_recvChunkStart: ns}, }, _recvChunkStatus: { testname: "_recvChunkStatus", command: {_recvChunkStatus: ns}, }, _refreshQueryAnalyzerConfiguration: { testname: "_refreshQueryAnalyzerConfiguration", command: {_refreshQueryAnalyzerConfiguration: 1, name: "test", numQueriesExecutedPerSecond: 1}, }, _shardsvrAbortReshardCollection: { testname: "_shardsvrAbortReshardCollection", command: {_shardsvrAbortReshardCollection: UUID(), userCanceled: true}, }, _shardsvrRunSearchIndexCommand: { // test only comand. Bc this command is included in test_only_commands_list.js, this will be // skipped. testname: "__shardsvrRunSearchIndexCommand", command: {__shardsvrRunSearchIndexCommand: 1, hostAndPort: []}, }, _shardsvrBeginMigrationBlockingOperation: { testname: "_shardsvrBeginMigrationBlockingOperation", command: {_shardsvrBeginMigrationBlockingOperation: ns, operationId: migrationOperationId}, postcommand: (db) => { assert.commandWorked(db.runCommand( {_shardsvrEndMigrationBlockingOperation: ns, operationId: migrationOperationId})); }, }, _shardsvrChangePrimary: { testname: "_shardsvrChangePrimary", command: { _shardsvrChangePrimary: "test", expectedDatabaseVersion: { uuid: new UUID(), timestamp: new Timestamp(1691525961, 12), lastMod: NumberInt(5), }, to: shard0name }, }, _shardsvrCleanupStructuredEncryptionData: { testname: "_shardsvrCleanupStructuredEncryptionData", command: {_shardsvrCleanupStructuredEncryptionData: "test", cleanupTokens: {}}, }, _shardsvrCleanupReshardCollection: { testname: "_shardsvrCleanupReshardCollection", command: {_shardsvrCleanupReshardCollection: "test.x", reshardingUUID: UUID()}, }, _shardsvrCloneCatalogData: { testname: "_shardsvrCloneCatalogData", command: {_shardsvrCloneCatalogData: 'test', from: [], writeConcern: {w: "majority"}}, }, _shardsvrCompactStructuredEncryptionData: { testname: "_shardsvrCompactStructuredEncryptionData", command: {_shardsvrCompactStructuredEncryptionData: 'test', compactionTokens: {}}, }, _shardsvrConvertToCapped: { testname: "_shardsvrConvertToCapped", command: {_shardsvrConvertToCapped: 'test', size: 0}, }, _shardsvrRegisterIndex: { testname: "_shardsvrRegisterIndex", command: { _shardsvrRegisterIndex: ns, keyPattern: {x: 1}, options: {}, name: 'x_1', collectionUUID: UUID(), indexCollectionUUID: UUID(), lastmod: Timestamp(0, 0), writeConcern: {w: 'majority'} }, }, _shardsvrCommitIndexParticipant: { testname: "_shardsvrCommitIndexParticipant", command: { _shardsvrCommitIndexParticipant: "x.y", name: 'x_1', keyPattern: {x: 1}, options: {}, collectionUUID: UUID(), lastmod: Timestamp(1, 0), }, }, _shardsvrCommitReshardCollection: { testname: "_shardsvrCommitReshardCollection", command: { _shardsvrCommitReshardCollection: "x.y", reshardingUUID: UUID(), }, }, _shardsvrDropCollection: { testname: "_shardsvrDropCollection", command: { _shardsvrDropCollection: "x.y", collectionUUID: UUID(), }, }, _shardsvrConvertToCappedParticipant: { testname: "_shardsvrConvertToCappedParticipant", command: { _shardsvrConvertToCappedParticipant: "x.y", size: 0, targetUUID: UUID(), }, }, _shardsvrJoinDDLCoordinators: { testname: "_shardsvrJoinDDLCoordinators", command: { _shardsvrJoinDDLCoordinators: "x.y", }, }, _shardsvrCreateCollection: { testname: "_shardsvrCreateCollection", command: { _shardsvrCreateCollection: "x.y", collectionUUID: UUID(), }, }, _shardsvrDropCollectionIfUUIDNotMatchingWithWriteConcern: { testname: "_shardsvrDropCollectionIfUUIDNotMatchingWithWriteConcern", command: { _shardsvrDropCollectionIfUUIDNotMatchingWithWriteConcern: "x.y", expectedCollectionUUID: UUID(), }, }, _shardsvrDropCollectionParticipant: { testname: "_shardsvrDropCollectionParticipant", command: { _shardsvrDropCollectionParticipant: "x.y", }, }, _shardsvrDropIndexCatalogEntryParticipant: { testname: "_shardsvrDropIndexCatalogEntryParticipant", command: { _shardsvrDropIndexCatalogEntryParticipant: "x.y", name: 'x_1', collectionUUID: UUID(), lastmod: Timestamp(1, 0), }, }, _shardsvrDropIndexes: { testname: "_shardsvrDropIndexes", command: { _shardsvrDropIndexes: "x.y", index: "*", collectionUUID: UUID(), }, }, _shardsvrCreateCollectionParticipant: { testname: "_shardsvrCreateCollectionParticipant", command: { _shardsvrCreateCollectionParticipant: "x.y", indexes: [], options: {}, idIndex: {v: 2, key: {_id: 1}, name: "_id_"}, }, }, _shardsvrCoordinateMultiUpdate: { testname: "_shardsvrCoordinateMultiUpdate", precommand: (precommanddb, user, pwd, coll) => { assert(precommanddb.auth(user, pwd)); assert.commandWorked(coll.insert({x: 1, y: 1})); assert.commandWorked(coll.insert({x: 1, y: 2})); precommanddb.logout(); }, command: { _shardsvrCoordinateMultiUpdate: collName, uuid: UUID(), command: {update: collName, updates: [{q: {x: 1}, u: {$set: {y: 2}}, multi: true}]} }, }, _shardsvrEndMigrationBlockingOperation: { testname: "_shardsvrEndMigrationBlockingOperation", command: { _shardsvrEndMigrationBlockingOperation: "ns", operationId: migrationOperationId, }, }, _shardsvrGetStatsForBalancing: { testname: "_shardsvrGetStatsForBalancing", command: {_shardsvrGetStatsForBalancing: "ns", collections: [], scaleFactor: 1}, }, _shardsvrJoinMigrations: { testname: "_shardsvrJoinMigrations", command: {_shardsvrJoinMigrations: 1}, }, _shardsvrMergeAllChunksOnShard: { testname: "_shardsvrMergeAllChunksOnShard", command: { _shardsvrMergeAllChunksOnShard: "x.y", shard: shard0name, maxNumberOfChunksToMerge: NumberInt(2) }, }, _shardsvrMovePrimary: { testname: "_shardsvrMovePrimary", command: { _shardsvrMovePrimary: "test", expectedDatabaseVersion: { uuid: new UUID(), timestamp: new Timestamp(1691525961, 12), lastMod: NumberInt(5), }, to: shard0name }, }, _shardsvrMovePrimaryEnterCriticalSection: { testname: "_shardsvrMovePrimaryEnterCriticalSection", command: {_shardsvrMovePrimaryEnterCriticalSection: "test", reason: {}}, }, _shardsvrMovePrimaryExitCriticalSection: { testname: "_shardsvrMovePrimaryExitCriticalSection", command: {_shardsvrMovePrimaryExitCriticalSection: "test", reason: {}}, }, _shardsvrMoveRange: { testname: "_shardsvrMoveRange", command: { _shardsvrMoveRange: "test.view", fromShard: shard0name, toShard: "shard0001", maxChunkSizeBytes: NumberInt(100) }, }, _shardsvrNotifyShardingEvent: { testname: "_shardsvrNotifyShardingEvent", command: {_shardsvrNotifyShardingEvent: "test", eventType: "databasesAdded", details: {}}, }, _shardsvrRenameCollection: { testname: "_shardsvrRenameCollection", command: {_shardsvrRenameCollection: "test.collection", to: "db.collection_renamed"}, }, _shardsvrRenameCollectionParticipant: { testname: "_shardsvrRenameCollectionParticipant", command: { _shardsvrRenameCollectionParticipant: "test.collection", to: "db.collection_renamed", sourceUUID: UUID() }, }, _shardsvrRenameCollectionParticipantUnblock: { testname: "_shardsvrRenameCollectionParticipantUnblock", command: { _shardsvrRenameCollectionParticipantUnblock: "test.collection", to: "db.collection_renamed", sourceUUID: UUID() }, }, _shardsvrRenameIndexMetadata: { testname: "_shardsvrRenameIndexMetadata", command: { _shardsvrRenameIndexMetadata: "test.collection", toNss: ns, indexVersion: {uuid: UUID(), version: Timestamp()}, }, }, _shardsvrDropDatabase: { testname: "_shardsvrDropDatabase", command: { _shardsvrDropDatabase: 1, }, }, _shardsvrDropDatabaseParticipant: { testname: "_shardsvrDropDatabaseParticipant", command: { _shardsvrDropDatabaseParticipant: "test.x", }, }, _shardsvrReshardCollection: { testname: "_shardsvrReshardCollection", command: { _shardsvrReshardCollection: "test.x", phase: "unset", key: {}, }, }, _shardsvrReshardingOperationTime: { testname: "_shardsvrReshardingOperationTime", command: { _shardsvrReshardingOperationTime: "test.x", }, }, _shardsvrRefineCollectionShardKey: { testname: "_shardsvrRefineCollectionShardKey", command: {_shardsvrRefineCollectionShardKey: "test.x", newShardKey: {}}, }, _shardsvrSetAllowMigrations: { testname: "_shardsvrSetAllowMigrations", command: {_shardsvrSetAllowMigrations: "db.collection", allowMigrations: true}, }, _shardsvrSetClusterParameter: { testname: "_shardsvrSetClusterParameter", command: {_shardsvrSetClusterParameter: {}, clusterParameterTime: Timestamp()}, }, _shardsvrSetUserWriteBlockMode: { testname: "_shardsvrSetUserWriteBlockMode", command: { _shardsvrSetUserWriteBlockMode: 1, global: true, phase: 'complete', }, }, _shardsvrValidateShardKeyCandidate: { testname: "_shardsvrValidateShardKeyCandidate", command: {_shardsvrValidateShardKeyCandidate: "x.y", key: {a: 1}}, }, _shardsvrCollModParticipant: { testname: "_shardsvrCollModParticipant", command: { _shardsvrCollModParticipant: "x.y", collModRequest: {}, }, }, _shardsvrCollMod: { testname: "_shardsvrCollMod", command: { _shardsvrCollMod: "x.y", }, }, _shardsvrParticipantBlock: { testname: "_shardsvrParticipantBlock", command: { _shardsvrParticipantBlock: "x.y", }, }, _shardsvrUnregisterIndex: { testname: "_shardsvrUnregisterIndex", command: { _shardsvrUnregisterIndex: "x.y", name: 'x_1', collectionUUID: UUID(), lastmod: Timestamp(1, 0), }, }, _shardsvrUntrackUnsplittableCollection: { testname: "_shardsvrUntrackUnsplittableCollection", command: {_shardsvrUntrackUnsplittableCollection: "x.y", writeConcern: {w: 'majority'}}, }, _shardsvrCheckMetadataConsistency: { testname: "_shardsvrCheckMetadataConsistency", command: { _shardsvrCheckMetadataConsistency: "x.y", }, }, _shardsvrCheckMetadataConsistencyParticipant: { testname: "_shardsvrCheckMetadataConsistencyParticipant", command: { _shardsvrCheckMetadataConsistencyParticipant: "x.y", primaryShardId: shard0name, }, }, _transferMods: { testname: "_transferMods", command: { _transferMods: "x.y", sessionId: {id: UUID()}, }, }, }; /** * Parameters: * db -- database object where the user is created. * userName -- the username. * roles -- roles for the user * * Returns: * void after creating the user for the test case. */ function createUser(db, userName, roles) { assert(db.auth("admin", "password")); db.createUser({user: userName, pwd: "password", roles: roles}); db.logout(); return userName; } /** * runOneCommandAuthorizationTest runs authorization failure and success tests for a single command. * If the user with __system role can run the command without getting Unauthorized error AND if the * user with no roles can run the command and get an 'Unauthorized` error, then we consider the test * as passed. Otherwise, the command fails the authorization check. * * Parameters: * testObject -- testObject contains the test information for a single command. * commandName -- command Name for the test. * db -- db on which the command is going to run. * secondDb -- the precommands are run on the second db. * coll -- collection for the second db command. * Returns: * result includes the pass/fail value and the command results for both system role * and no roles. */ function runOneCommandAuthorizationTest(testObject, commandName, db, secondDb, coll) { const cmdOK = {commandWorked: 1, commandFailed: 0}; let result = {pass: true}; if (testObject.precommand != undefined) { if (secondDb == undefined) { return result; } testObject.precommand(secondDb, sysUser.user, sysUser.pwd, coll); } assert(db.auth(noroleUser.user, noroleUser.pwd)); result.noRoleRes = db.runCommand(testObject.command); db.logout(); if (result.noRoleRes.ok === cmdOK.commandWorked || result.noRoleRes.code !== ErrorCodes.Unauthorized) { result.pass = false; return result; } assert(db.auth(sysUser.user, sysUser.pwd)); result.sysRes = db.runCommand(testObject.command); if (result.sysRes.ok === cmdOK.commandWorked && testObject.postcommand !== undefined) { testObject.postcommand(db); } db.logout(); const sysRolePass = (result.sysRes.ok === cmdOK.commandWorked || result.sysRes.code !== ErrorCodes.Unauthorized); assert(sysRolePass); return result; } /** * Some commands may be skipped if they are test only commands. isSelf command is also skipped. * @param testObjectForCommand test object for the command. * @param commandName command Name. * @returns true if the command is a test only command or if the command needs to be skipped( * currently only isSelf is skipped. It is used by atlas tools without __system role). */ function skipCommand(testObjectForCommand, commandName) { if (testOnlyCommandsSet.has(commandName) || testObjectForCommand.skip) { return true; } return false; } /** * Parameters: * conn -- connection, either to standalone mongod, * or to mongos in sharded cluster * firstDb -- all commands are tested on the first db. * secondDb -- the precommands are run on the second db. * coll -- collection for the second db command. * * Returns: * results of the test */ function runAuthorizationTestsOnAllInternalCommands(conn, firstDb, secondDb, coll) { let results = {}; let fails = []; const availableCommandsList = AllCommandsTest.checkCommandCoverage(conn, internalCommandsMap, function(cmdName) { return !cmdName.startsWith('_') || testOnlyCommandsSet.hasOwnProperty(cmdName); }); for (const commandName of availableCommandsList) { const test = internalCommandsMap[commandName]; assert(test, "Coverage failure: must explicitly define a test for " + commandName); if (skipCommand(test, commandName)) { continue; } results[commandName] = runOneCommandAuthorizationTest(test, commandName, firstDb, secondDb, coll); } return results; } /** * Sets up the standalone test setup and runs authorization tests on all internal commands. */ function runStandaloneTest() { jsTestLog("Starting standalone test"); // Setup standalone mongod, create a DB and create the admin const dbPath = MongoRunner.toRealDir("$dataDir/commands_built_in_roles_standalone/"); mkdir(dbPath); const opts = { auth: "", setParameter: { trafficRecordingDirectory: dbPath, mongotHost: "localhost:27017", // We have to set the mongotHost parameter for the // $search-relatead tests to pass configuration checks. syncdelay: 0 // Disable checkpoints as this can cause some commands to fail transiently. } }; const conn = MongoRunner.runMongod(opts); const adminDb = conn.getDB(adminDbName); adminDb.createUser({user: "admin", pwd: "password", roles: ["__system"]}); createUser(adminDb, noroleUser.user, noroleUser.roles); const results = runAuthorizationTestsOnAllInternalCommands(conn, adminDb); MongoRunner.stopMongod(conn); return results; } /** * Sets up the sharded cluster test setup. */ function setupShardedClusterTest() { mkdir(dbPath); const opts = { auth: "", setParameter: { trafficRecordingDirectory: dbPath, mongotHost: "localhost:27017", // We have to set the mongotHost parameter for the // $search-related tests to pass configuration checks. syncdelay: 0 // Disable checkpoints as this can cause some commands to fail transiently. } }; return opts; } /** * run mongos test. */ function runMongosTest(opts) { jsTestLog("Starting mongos test"); const shardingTest = new ShardingTest({ shards: 2, mongos: 1, config: 1, keyFile: "jstests/libs/key1", other: { rsOptions: opts, // We have to set the mongotHost parameter for the $search-related tests to pass // configuration checks. mongosOptions: {setParameter: {trafficRecordingDirectory: dbPath, mongotHost: "localhost:27017"}} } }); const mongos = shardingTest.s0; const mongosAdminDB = mongos.getDB("admin"); mongosAdminDB.createUser({user: "admin", pwd: "password", roles: ["__system"]}); createUser(mongosAdminDB, noroleUser.user, noroleUser.roles); const results = runAuthorizationTestsOnAllInternalCommands(mongos, mongosAdminDB); shardingTest.stop(); return results; } /** * run sharded server test. */ function runShardedServerTest(opts) { const shardingTest = new ShardingTest({ shards: 1, mongos: 1, config: 1, keyFile: "jstests/libs/key1", useHostname: false, other: { rsOptions: opts, // We have to set the mongotHost parameter for the $search-related tests to pass // configuration checks. mongosOptions: {setParameter: {trafficRecordingDirectory: dbPath, mongotHost: "localhost:27017"}} } }); const shardPrimary = shardingTest.rs0.getPrimary(); const shardAdminDB = shardPrimary.getDB("admin"); const mongosDB = shardingTest.s0.getDB("admin"); const coll = mongosDB.getCollection(collName); mongosDB.createUser({user: "admin", pwd: "password", roles: ["__system"]}); if (!TestData.configShard) { shardAdminDB.createUser({user: "admin", pwd: "password", roles: ["__system"]}); } createUser(shardAdminDB, noroleUser.user, noroleUser.roles); const results = runAuthorizationTestsOnAllInternalCommands(shardPrimary, shardAdminDB, mongosDB, coll); shardingTest.stop(); return results; } /** * run config server test. */ function runConfigServer(opts) { const shardingTest = new ShardingTest({ shards: 1, mongos: 1, config: 1, keyFile: "jstests/libs/key1", other: { rsOptions: opts, // We have to set the mongotHost parameter for the $search-related tests to pass // configuration checks. mongosOptions: {setParameter: {trafficRecordingDirectory: dbPath, mongotHost: "localhost:27017"}} } }); const configPrimary = shardingTest.configRS.getPrimary(); const configAdminDB = configPrimary.getDB("admin"); configAdminDB.createUser({user: "admin", pwd: "password", roles: ["__system"]}); createUser(configAdminDB, noroleUser.user, noroleUser.roles); const results = runAuthorizationTestsOnAllInternalCommands(configPrimary, configAdminDB); shardingTest.stop(); return results; } /** * singleCommandCheckResult gets the update from four setups(runStandaloneTest, sharded cluster, * sharded server, config server) and returns the combined result for the command. * * * Parameters: * commandName -- command name. * resultmap -- an object that has setups as the keys and results for the setup as the values. * * Returns: a combined result for different test setups for a single command. */ function singleCommandCheckResult(commandName, resultmap) { let singleCommandCombinedResult = {pass: true, absent: true}; for (const [setupName, resultsForSetup] of Object.entries(resultmap)) { if (!resultsForSetup.hasOwnProperty(commandName)) { continue; } if (singleCommandCombinedResult.pass) { singleCommandCombinedResult.pass = resultsForSetup[commandName].pass; } singleCommandCombinedResult.absent = false; if (!resultsForSetup[commandName].pass) { jsTestLog("Test Failure at setup:" + setupName + " command:" + commandName + tojson(resultsForSetup[commandName])); } } return singleCommandCombinedResult; } /** * checkResults summarize the results from all the results from all the setups. * * Parameters: * resultmap -- an object that has setups as the keys and results for the setup as the values. * * Returns: None. * * The results for all the test objects are accumulated and a combined result for the all the tests * is announced. */ function checkResults(resultmap) { let summaryResultsCount = {passCount: 0, failCount: 0, absentCount: 0}; for (const [commandName, testObject] of Object.entries(internalCommandsMap)) { if (skipCommand(testObject, commandName)) { continue; } const currentResult = singleCommandCheckResult(commandName, resultmap); if (currentResult.absent) { summaryResultsCount.absentCount++; jsTestLog( "Command ${commandName} did not get tested. This may be because a feature flag is not enabled, or this command does not exist in mongod and mongos."); } else if (currentResult.pass === true) { summaryResultsCount.passCount++; } else { jsTestLog("Test Result Failed command:" + tojson(currentResult)); summaryResultsCount.failCount++; } } jsTest.log("Result: tests passed " + summaryResultsCount.passCount + ". Failed Commands Count: " + summaryResultsCount.failCount + ". Absent Commands Count: " + summaryResultsCount.absentCount + "."); assert.eq(0, summaryResultsCount.failCount); } let resultmap = {}; resultmap.Standalone = runStandaloneTest(); const opts = setupShardedClusterTest(); resultmap.ShardedClusterMongos = runMongosTest(opts); resultmap.ShardedServer = runShardedServerTest(opts); resultmap.ConfigServer = runConfigServer(opts); checkResults(resultmap);