diff options
author | Israel Hsu <israel.hsu@mongodb.com> | 2023-04-04 17:52:23 +0000 |
---|---|---|
committer | Evergreen Agent <no-reply@evergreen.mongodb.com> | 2023-04-04 19:19:18 +0000 |
commit | b584db7ad2e3256b37aa0191bb2b1215a7aff709 (patch) | |
tree | e5e02718c6af4b9f2bba1a4927c2f32c0913f05d | |
parent | e0a1eb3ce7cb84669666e94c9f37d1d3bffe53ec (diff) | |
download | mongo-b584db7ad2e3256b37aa0191bb2b1215a7aff709.tar.gz |
SERVER-69653 Add auth privilege requirements to the analyzeShardKey and configureQueryAnalyzer commands
17 files changed, 430 insertions, 12 deletions
diff --git a/buildscripts/resmokeconfig/suites/sharding_auth.yml b/buildscripts/resmokeconfig/suites/sharding_auth.yml index ad2859e3889..2b2862e5a8e 100644 --- a/buildscripts/resmokeconfig/suites/sharding_auth.yml +++ b/buildscripts/resmokeconfig/suites/sharding_auth.yml @@ -12,6 +12,7 @@ selector: - jstests/sharding/**/libs/**/*.js # Skip any tests that run with auth explicitly. - jstests/sharding/*[aA]uth*.js + - jstests/sharding/analyze_shard_key/*[aA]uth*.js - jstests/sharding/query/*[aA]uth*.js - jstests/sharding/change_streams/*[aA]uth*.js diff --git a/buildscripts/resmokeconfig/suites/sharding_auth_catalog_shard.yml b/buildscripts/resmokeconfig/suites/sharding_auth_catalog_shard.yml index a0525aad136..459b2debd5a 100644 --- a/buildscripts/resmokeconfig/suites/sharding_auth_catalog_shard.yml +++ b/buildscripts/resmokeconfig/suites/sharding_auth_catalog_shard.yml @@ -12,6 +12,7 @@ selector: - jstests/sharding/**/libs/**/*.js # Skip any tests that run with auth explicitly. - jstests/sharding/*[aA]uth*.js + - jstests/sharding/analyze_shard_key/*[aA]uth*.js - jstests/sharding/query/*[aA]uth*.js - jstests/sharding/change_streams/*[aA]uth*.js diff --git a/buildscripts/resmokeconfig/suites/sharding_continuous_config_stepdown.yml b/buildscripts/resmokeconfig/suites/sharding_continuous_config_stepdown.yml index 496c95c90fc..340e2e4a0cc 100644 --- a/buildscripts/resmokeconfig/suites/sharding_continuous_config_stepdown.yml +++ b/buildscripts/resmokeconfig/suites/sharding_continuous_config_stepdown.yml @@ -8,6 +8,7 @@ selector: # Skip any tests that run with auth explicitly. # Auth tests require authentication on the stepdown thread's connection - jstests/sharding/*[aA]uth*.js + - jstests/sharding/analyze_shard_key/*[aA]uth*.js - jstests/sharding/query/*[aA]uth*.js - jstests/sharding/change_streams/*[aA]uth*.js - jstests/sharding/internal_txns/internal_client_restrictions.js diff --git a/jstests/sharding/analyze_shard_key/analyze_shard_key_agg_stage_auth.js b/jstests/sharding/analyze_shard_key/analyze_shard_key_agg_stage_auth.js new file mode 100644 index 00000000000..78fc4f1d1e8 --- /dev/null +++ b/jstests/sharding/analyze_shard_key/analyze_shard_key_agg_stage_auth.js @@ -0,0 +1,134 @@ +/** + * Test to validate the privileges required by the analyzeShardKey aggregation stage, + * $_analyzeShardKeyReadWriteDistribution. + * + * @tags: [requires_fcv_70, featureFlagAnalyzeShardKey] + */ + +(function() { + +'use strict'; + +function runTest(primary) { + const dbName = "testDb"; + const collName0 = "testColl0"; + const collName1 = "testColl1"; + const ns0 = dbName + "." + collName0; + const ns1 = dbName + "." + collName1; + + const adminDb = primary.getDB("admin"); + assert.commandWorked( + adminDb.runCommand({createUser: "super", pwd: "super", roles: ["__system"]})); + assert(adminDb.auth("super", "super")); + const testDb = adminDb.getSiblingDB(dbName); + const docs = []; + const numDocs = 1000; + for (let i = 0; i < numDocs; i++) { + docs.push({x: i}); + } + assert.commandWorked(testDb.getCollection(collName0).insert(docs)); + assert.commandWorked(testDb.getCollection(collName1).insert(docs)); + assert(adminDb.logout()); + + // $_analyzeShardKeyReadWriteDistribution spec + const stageSpec = { + key: {x: 1}, + splitPointsNss: "config.analyzeShardKey.splitPoints.test", + splitPointsAfterClusterTime: new Timestamp(100, 1), + // The use of "dummyShard" for splitPointsShardId will cause the aggregation to fail on + // a sharded cluster with error code ShardNotFound. + splitPointsShardId: "dummyShard" + }; + const aggregateCmd0 = { + aggregate: collName0, + pipeline: [{$_analyzeShardKeyReadWriteDistribution: stageSpec}], + cursor: {} + }; + const aggregateCmd1 = { + aggregate: collName1, + pipeline: [{$_analyzeShardKeyReadWriteDistribution: stageSpec}], + cursor: {} + }; + + // Set up a user without any role or privilege. + assert(adminDb.auth("super", "super")); + assert.commandWorked(testDb.runCommand({createUser: "user_no_priv", pwd: "pwd", roles: []})); + assert(adminDb.logout()); + // Verify that the user is not authorized to run the analyzeShardKey stage against ns0 or ns1. + assert(testDb.auth("user_no_priv", "pwd")); + assert.commandFailedWithCode(testDb.runCommand(aggregateCmd0), ErrorCodes.Unauthorized); + assert.commandFailedWithCode(testDb.runCommand(aggregateCmd0), ErrorCodes.Unauthorized); + assert(testDb.logout()); + + // Set up a user with the 'analyzeShardKey' privilege against ns0. + assert(adminDb.auth("super", "super")); + assert.commandWorked(testDb.runCommand({ + createRole: "role_ns0_priv", + roles: [], + privileges: [{resource: {db: dbName, collection: collName0}, actions: ["analyzeShardKey"]}] + })); + assert.commandWorked(testDb.runCommand({ + createUser: "user_with_explicit_ns0_priv", + pwd: "pwd", + roles: [{role: "role_ns0_priv", db: dbName}] + })); + assert(adminDb.logout()); + // Verify that the user is authorized to run the aggregation stage against ns0 but not ns1. + assert(testDb.auth("user_with_explicit_ns0_priv", "pwd")); + assert.commandWorkedOrFailedWithCode(testDb.runCommand(aggregateCmd0), + ErrorCodes.ShardNotFound); + assert.commandFailedWithCode(testDb.runCommand(aggregateCmd1), ErrorCodes.Unauthorized); + assert(testDb.logout()); + + // Set up a user with the 'clusterManager' role. + assert(adminDb.auth("super", "super")); + assert.commandWorked(adminDb.runCommand({ + createUser: "user_cluster_mgr", + pwd: "pwd", + roles: [{role: "clusterManager", db: "admin"}] + })); + assert(adminDb.logout()); + // Verify that the user is authorized to run the aggregation stage against both ns0 and ns1. + assert(adminDb.auth("user_cluster_mgr", "pwd")); + assert.commandWorkedOrFailedWithCode(testDb.runCommand(aggregateCmd0), + ErrorCodes.ShardNotFound); + assert.commandWorkedOrFailedWithCode(testDb.runCommand(aggregateCmd1), + ErrorCodes.ShardNotFound); + assert(adminDb.logout()); + + // Set up a user with the 'enableSharding' role. + assert(adminDb.auth("super", "super")); + assert.commandWorked(adminDb.runCommand({ + createUser: "user_enable_sharding", + pwd: "pwd", + roles: [{role: "enableSharding", db: "testDb"}] + })); + assert(adminDb.logout()); + // Verify that the user is authorized to run the aggregation command against both ns0 and ns1. + assert(adminDb.auth("user_enable_sharding", "pwd")); + assert.commandWorkedOrFailedWithCode(testDb.runCommand(aggregateCmd0), + ErrorCodes.ShardNotFound); + assert.commandWorkedOrFailedWithCode(testDb.runCommand(aggregateCmd1), + ErrorCodes.ShardNotFound); + assert(adminDb.logout()); +} + +{ + const st = new ShardingTest({shards: 1, keyFile: "jstests/libs/key1"}); + + runTest(st.rs0.getPrimary()); + + st.stop(); +} + +{ + const rst = new ReplSetTest({nodes: 1, keyFile: "jstests/libs/key1"}); + rst.startSet(); + rst.initiate(); + const primary = rst.getPrimary(); + + runTest(primary); + + rst.stopSet(); +} +})(); diff --git a/jstests/sharding/analyze_shard_key/analyze_shard_key_auth.js b/jstests/sharding/analyze_shard_key/analyze_shard_key_auth.js new file mode 100644 index 00000000000..61060cf70fb --- /dev/null +++ b/jstests/sharding/analyze_shard_key/analyze_shard_key_auth.js @@ -0,0 +1,116 @@ +/** + * Test to validate the privileges required by the analyzeShardKey and configureQueryAnalyzer + * commands and _refreshQueryAnalyzerConfiguration internal command. + * + * @tags: [requires_fcv_70, featureFlagAnalyzeShardKey] + */ + +(function() { + +'use strict'; + +function runTest(conn) { + const dbName = "testDb"; + const collName0 = "testColl0"; + const collName1 = "testColl1"; + const ns0 = dbName + "." + collName0; + const ns1 = dbName + "." + collName1; + + const adminDb = conn.getDB("admin"); + assert.commandWorked( + adminDb.runCommand({createUser: "super", pwd: "super", roles: ["__system"]})); + assert(adminDb.auth("super", "super")); + const testDb = adminDb.getSiblingDB(dbName); + const docs = []; + const numDocs = 1000; + for (let i = 0; i < numDocs; i++) { + docs.push({x: i}); + } + assert.commandWorked(testDb.getCollection(collName0).insert(docs)); + assert.commandWorked(testDb.getCollection(collName1).insert(docs)); + assert(adminDb.logout()); + + // Set up a user without any role or privilege. + assert(adminDb.auth("super", "super")); + assert.commandWorked(adminDb.runCommand({createUser: "user_no_priv", pwd: "pwd", roles: []})); + assert(adminDb.logout()); + // Verify that the user is not authorized to run the analyzeShardKey command against ns0 or + // ns1. + assert(adminDb.auth("user_no_priv", "pwd")); + assert.commandFailedWithCode(adminDb.runCommand({"analyzeShardKey": ns0, key: {_id: 1}}), + ErrorCodes.Unauthorized); + assert.commandFailedWithCode(adminDb.runCommand({"analyzeShardKey": ns1, key: {_id: 1}}), + ErrorCodes.Unauthorized); + assert(adminDb.logout()); + + // Set up a user with the 'analyzeShardKey' privilege against ns0. + assert(adminDb.auth("super", "super")); + assert.commandWorked(adminDb.runCommand({ + createRole: "role_ns0_priv", + roles: [], + privileges: [{resource: {db: dbName, collection: collName0}, actions: ["analyzeShardKey"]}] + })); + assert.commandWorked(adminDb.runCommand({ + createUser: "user_with_explicit_ns0_priv", + pwd: "pwd", + roles: [{role: "role_ns0_priv", db: "admin"}] + })); + assert(adminDb.logout()); + // Verify that the user is authorized to run the analyzeShardKey command against ns0 but not + // ns1. + assert(adminDb.auth("user_with_explicit_ns0_priv", "pwd")); + assert.commandWorked(adminDb.runCommand({"analyzeShardKey": ns0, key: {_id: 1}})); + assert.commandFailedWithCode(adminDb.runCommand({"analyzeShardKey": ns1, key: {_id: 1}}), + ErrorCodes.Unauthorized); + assert(adminDb.logout()); + + // Set up a user with the 'clusterManager' role. + assert(adminDb.auth("super", "super")); + assert.commandWorked(adminDb.runCommand({ + createUser: "user_cluster_mgr", + pwd: "pwd", + roles: [{role: "clusterManager", db: "admin"}] + })); + assert(adminDb.logout()); + // Verify that the user is authorized to run the analyzeShardKey command against both ns0 + // and ns1. + assert(adminDb.auth("user_cluster_mgr", "pwd")); + assert.commandWorked(adminDb.runCommand({"analyzeShardKey": ns0, key: {_id: 1}})); + assert.commandWorked(adminDb.runCommand({"analyzeShardKey": ns1, key: {_id: 1}})); + assert(adminDb.logout()); + + // Set up a user with the 'enableSharding' role. + assert(adminDb.auth("super", "super")); + assert.commandWorked(adminDb.runCommand({ + createUser: "user_enable_sharding", + pwd: "pwd", + roles: [{role: "enableSharding", db: "admin"}] + })); + assert(adminDb.logout()); + // Verify that the user is authorized to run the analyzeShardKey command against both ns0 + // and ns1. + assert(adminDb.auth("user_enable_sharding", "pwd")); + assert.commandWorked(adminDb.runCommand({"analyzeShardKey": ns0, key: {_id: 1}})); + assert.commandWorked(adminDb.runCommand({"analyzeShardKey": ns1, key: {_id: 1}})); + assert(adminDb.logout()); +} + +{ + const st = new ShardingTest({shards: 1, keyFile: "jstests/libs/key1"}); + + runTest(st.s); + + st.stop(); +} + +{ + const rst = new ReplSetTest({nodes: 1, keyFile: "jstests/libs/key1"}); + rst.startSet(); + rst.initiate(); + const primary = rst.getPrimary(); + + runTest(primary); + + rst.stopSet(); +} +})(); diff --git a/jstests/sharding/analyze_shard_key/analyze_shard_key_basic.js b/jstests/sharding/analyze_shard_key/analyze_shard_key_basic.js index f24690d531b..e375a6d0478 100644 --- a/jstests/sharding/analyze_shard_key/analyze_shard_key_basic.js +++ b/jstests/sharding/analyze_shard_key/analyze_shard_key_basic.js @@ -282,8 +282,8 @@ if (!TestData.auth) { adminDb.runCommand({createUser: "admin", pwd: "pwd", roles: ["__system"]})); assert(adminDb.auth("admin", "pwd")); - // The analyzeShardKey command is supported on any mongod. - const testCases = [{conn: adminDb, isSupported: true}]; + // The analyzeShardKey command is not supported in multitenancy. + const testCases = [{conn: adminDb, isSupported: false}]; testNonExistingCollection(testCases, tenantId); rst.stopSet(); } diff --git a/jstests/sharding/analyze_shard_key/configure_query_analyzer_auth.js b/jstests/sharding/analyze_shard_key/configure_query_analyzer_auth.js new file mode 100644 index 00000000000..4e70099a2af --- /dev/null +++ b/jstests/sharding/analyze_shard_key/configure_query_analyzer_auth.js @@ -0,0 +1,140 @@ +/** + * Test to validate the privileges required by configureQueryAnalyzer command. Also tests the + * internal command _refreshQueryAnalyzerConfigurations. + * + * @tags: [requires_fcv_70, featureFlagAnalyzeShardKey] + */ + +(function() { + +'use strict'; + +function testConfigureQueryAnalyzer(conn) { + const dbName = "testDb"; + const collName0 = "testColl0"; + const collName1 = "testColl1"; + const ns0 = dbName + "." + collName0; + const ns1 = dbName + "." + collName1; + const otherDbName = "otherTestDb"; + + const adminDb = conn.getDB("admin"); + assert.commandWorked( + adminDb.runCommand({createUser: "super", pwd: "super", roles: ["__system"]})); + assert(adminDb.auth("super", "super")); + const testDb = adminDb.getSiblingDB(dbName); + assert.commandWorked(testDb.createCollection(collName0)); + assert.commandWorked(testDb.createCollection(collName1)); + assert(adminDb.logout()); + + const mode = "full"; + const sampleRate = 100; + + // Set up a user without any role or privilege. + assert(adminDb.auth("super", "super")); + assert.commandWorked(adminDb.runCommand({createUser: "user_no_priv", pwd: "pwd", roles: []})); + assert(adminDb.logout()); + // Verify that the user is not authorized to run the configureQueryAnalyzer command. + assert(adminDb.auth("user_no_priv", "pwd")); + assert.commandFailedWithCode( + adminDb.runCommand({"configureQueryAnalyzer": ns0, mode, sampleRate}), + ErrorCodes.Unauthorized); + assert.commandFailedWithCode( + adminDb.runCommand({"configureQueryAnalyzer": ns1, mode, sampleRate}), + ErrorCodes.Unauthorized); + assert(adminDb.logout()); + + // Set up a user with the 'configureQueryAnalyzer' privilege against ns0. + assert(adminDb.auth("super", "super")); + assert.commandWorked(adminDb.runCommand({ + createRole: "role_ns0_priv", + roles: [], + privileges: + [{resource: {db: dbName, collection: collName0}, actions: ["configureQueryAnalyzer"]}] + })); + assert.commandWorked(adminDb.runCommand({ + createUser: "user_with_explicit_ns0_priv", + pwd: "pwd", + roles: [{role: "role_ns0_priv", db: "admin"}] + })); + assert(adminDb.logout()); + // Verify that the user is authorized to run the configureQueryAnalyzer command against ns0 + // but not ns1. + assert(adminDb.auth("user_with_explicit_ns0_priv", "pwd")); + assert.commandWorked(adminDb.runCommand({"configureQueryAnalyzer": ns0, mode, sampleRate})); + assert.commandFailedWithCode( + adminDb.runCommand({"configureQueryAnalyzer": ns1, mode, sampleRate}), + ErrorCodes.Unauthorized); + assert(adminDb.logout()); + + // Set up a user with the 'clusterManager' role. + assert(adminDb.auth("super", "super")); + assert.commandWorked(adminDb.runCommand({ + createUser: "user_cluster_mgr", + pwd: "pwd", + roles: [{role: "clusterManager", db: "admin"}] + })); + assert(adminDb.logout()); + // Verify that the user is authorized to run the configureQueryAnalyzer command against both + // ns0 and ns1. + assert(adminDb.auth("user_cluster_mgr", "pwd")); + assert.commandWorked(adminDb.runCommand({"configureQueryAnalyzer": ns0, mode, sampleRate})); + assert.commandWorked(adminDb.runCommand({"configureQueryAnalyzer": ns1, mode, sampleRate})); + assert(adminDb.logout()); + + // Set up a user with the 'dbAdmin' role. + assert(adminDb.auth("super", "super")); + assert.commandWorked(adminDb.runCommand( + {createUser: "user_db_admin", pwd: "pwd", roles: [{role: "dbAdmin", db: dbName}]})); + assert(adminDb.logout()); + // Verify that the user is authorized to run the configureQueryAnalyzer command against both + // ns0 and ns1 but not against a ns in some other database. + assert(adminDb.auth("user_db_admin", "pwd")); + assert.commandWorked(adminDb.runCommand({"configureQueryAnalyzer": ns0, mode, sampleRate})); + assert.commandWorked(adminDb.runCommand({"configureQueryAnalyzer": ns1, mode, sampleRate})); + assert.commandFailedWithCode( + adminDb.runCommand({"configureQueryAnalyzer": otherDbName + collName0, mode, sampleRate}), + ErrorCodes.Unauthorized); + assert(adminDb.logout()); +} + +function testRefreshQueryAnalyzerConfiguration(conn) { + // This function uses the users that were setup in testConfigureQueryAnalyzer(). + const adminDb = conn.getDB("admin"); + + // Verify that a user with the internal role is authorized to run the + // _refreshQueryAnalyzerConfiguration command. + assert(adminDb.auth("super", "super")); + assert.commandWorked(adminDb.runCommand( + {_refreshQueryAnalyzerConfiguration: 1, name: conn.host, numQueriesExecutedPerSecond: 1})); + assert(adminDb.logout()); + + assert(adminDb.auth("user_no_priv", "pwd")); + assert.commandFailedWithCode(adminDb.runCommand({ + _refreshQueryAnalyzerConfiguration: 1, + name: conn.host, + numQueriesExecutedPerSecond: 1 + }), + ErrorCodes.Unauthorized); + assert(adminDb.logout()); +} + +{ + const st = new ShardingTest({shards: 1, keyFile: "jstests/libs/key1"}); + + testConfigureQueryAnalyzer(st.s); + testRefreshQueryAnalyzerConfiguration(st.configRS.getPrimary()); + st.stop(); +} + +{ + const rst = new ReplSetTest({nodes: 1, keyFile: "jstests/libs/key1"}); + rst.startSet(); + rst.initiate(); + const primary = rst.getPrimary(); + + testConfigureQueryAnalyzer(primary); + testRefreshQueryAnalyzerConfiguration(primary); + + rst.stopSet(); +} +})(); diff --git a/src/mongo/db/auth/action_type.idl b/src/mongo/db/auth/action_type.idl index 5ae0b8466fa..26d993be4c1 100644 --- a/src/mongo/db/auth/action_type.idl +++ b/src/mongo/db/auth/action_type.idl @@ -47,6 +47,7 @@ enums: advanceClusterTime : "advanceClusterTime" allCollectionStats: "allCollectionStats" analyze : "analyze" + analyzeShardKey : "analyzeShardKey" anyAction : "anyAction" # Special ActionType that represents *all* actions appendOplogNote : "appendOplogNote" applicationMessage : "applicationMessage" @@ -71,6 +72,7 @@ enums: collStats : "collStats" compact : "compact" compactStructuredEncryptionData: "compactStructuredEncryptionData" + configureQueryAnalyzer : "configureQueryAnalyzer" connPoolStats : "connPoolStats" connPoolSync : "connPoolSync" convertToCapped : "convertToCapped" @@ -131,6 +133,7 @@ enums: listCursors : "listCursors" listDatabases : "listDatabases" listIndexes : "listIndexes" + listSampledQueries : "listSampledQueries" listSearchIndexes : "listSearchIndexes" listSessions : "listSessions" listShards : "listShards" @@ -291,6 +294,7 @@ enums: - collStats - compact - compactStructuredEncryptionData + - configureQueryAnalyzer # TODO (SERVER-75656): remove from serverlessActionTypes. - convertToCapped - createCollection - createIndex @@ -363,6 +367,7 @@ enums: - collStats - compact - compactStructuredEncryptionData + - configureQueryAnalyzer # TODO (SERVER-75656): remove from serverlessActionTypes. - convertToCapped - createCollection - createIndex diff --git a/src/mongo/db/auth/authorization_session_test.cpp b/src/mongo/db/auth/authorization_session_test.cpp index c064533b263..cd81c6f1ce3 100644 --- a/src/mongo/db/auth/authorization_session_test.cpp +++ b/src/mongo/db/auth/authorization_session_test.cpp @@ -1395,6 +1395,13 @@ TEST_F(AuthorizationSessionTest, ExpiredSessionWithReauth) { ActionType::insert); } +/** + * TODO (SERVER-75289): This test was disabled by SERVER-69653, which added a privilege action + * called 'configureQueryAnalyzer' to the 'dbAdmin' role but not to the list for Serverless since + * the action is not meant to be supported in multitenant configurations. This has caused this unit + * test to fail. + */ +/**** TEST_F(AuthorizationSessionTest, ExpirationWithSecurityTokenNOK) { // Tests authorization flow from unauthenticated to active (via token) to unauthenticated to // active (via stateful connection) to unauthenticated. @@ -1412,9 +1419,8 @@ TEST_F(AuthorizationSessionTest, ExpirationWithSecurityTokenNOK) { ASSERT_OK(createUser(user, {{"readWrite", "test"}, {"dbAdmin", "test"}})); ASSERT_OK(createUser(adminUser, {{"readWriteAnyDatabase", "admin"}})); - VTS validatedTenancyScope = VTS(BSON(authUserFieldName << user.toBSON(true /* encodeTenant */)), - VTS::TokenForTestingTag{}); - VTS::set(_opCtx.get(), validatedTenancyScope); + VTS validatedTenancyScope = VTS(BSON(authUserFieldName << user.toBSON(true / * encodeTenant * +/)), VTS::TokenForTestingTag{}); VTS::set(_opCtx.get(), validatedTenancyScope); // Make sure that security token users can't be authorized with an expiration date. Date_t expirationTime = clockSource()->now() + Hours(1); @@ -1446,6 +1452,7 @@ TEST_F(AuthorizationSessionTest, ExpirationWithSecurityTokenNOK) { NamespaceString::createNamespaceString_forTest("anydb.somecollection")), ActionType::insert); } +****/ class SystemBucketsTest : public AuthorizationSessionTest { protected: diff --git a/src/mongo/db/auth/builtin_roles.yml b/src/mongo/db/auth/builtin_roles.yml index 7b9745e69b8..1babdd93486 100644 --- a/src/mongo/db/auth/builtin_roles.yml +++ b/src/mongo/db/auth/builtin_roles.yml @@ -92,6 +92,7 @@ roles: - collMod - collStats # clusterMonitor gets this also - compact + - configureQueryAnalyzer # clusterManager gets this also - convertToCapped # readWrite gets this also - createCollection # readWrite gets this also - dbStats # clusterMonitor gets this also @@ -129,6 +130,7 @@ roles: privileges: - matchType: any_normal actions: + - analyzeShardKey - enableSharding - refineCollectionShardKey - reshardCollection @@ -242,6 +244,7 @@ roles: - getShardMap - hostInfo - listDatabases + - listSampledQueries - listSessions # clusterManager gets this also - listShards # clusterManager gets this also - netstat @@ -356,6 +359,8 @@ roles: - matchType: any_normal actions: &clusterManagerRoleDatabaseActions + - analyzeShardKey # enableSharding gets this also + - configureQueryAnalyzer # dbAdmin gets this also - clearJumboFlag - splitChunk - moveChunk diff --git a/src/mongo/db/auth/builtin_roles_test.cpp b/src/mongo/db/auth/builtin_roles_test.cpp index 26d962db72b..22c8d5af515 100644 --- a/src/mongo/db/auth/builtin_roles_test.cpp +++ b/src/mongo/db/auth/builtin_roles_test.cpp @@ -137,6 +137,8 @@ TEST(BuiltinRoles, addSystemBucketsPrivilegesForBuiltinRoleClusterManager) { ActionType::splitVector, ActionType::refineCollectionShardKey, ActionType::reshardCollection, + ActionType::analyzeShardKey, + ActionType::configureQueryAnalyzer, }); for (const auto& priv : privs) { diff --git a/src/mongo/db/pipeline/aggregate_command.idl b/src/mongo/db/pipeline/aggregate_command.idl index a4b890b033c..abdd3d5ea47 100644 --- a/src/mongo/db/pipeline/aggregate_command.idl +++ b/src/mongo/db/pipeline/aggregate_command.idl @@ -170,6 +170,10 @@ commands: agg_stage: shardedDataDistribution resource_pattern: cluster action_type: shardedDataDistribution + - privilege: # $_analyzeShardKeyReadWriteDistribution + agg_stage: _analyzeShardKeyReadWriteDistribution + resource_pattern: exact_namespace + action_type: analyzeShardKey # Note that the 'CursorInitialReply' is not the only response that an aggregate command # could return. With 'explain' or 'exchange', the response would not include the fields in # 'CursorInitialReply'. But using 'explain' or 'exchange' is unstable, but otherwise the diff --git a/src/mongo/db/s/analyze_shard_key_cmd.cpp b/src/mongo/db/s/analyze_shard_key_cmd.cpp index 52ea94b9f3b..bd3a14f80a6 100644 --- a/src/mongo/db/s/analyze_shard_key_cmd.cpp +++ b/src/mongo/db/s/analyze_shard_key_cmd.cpp @@ -71,6 +71,9 @@ public: "analyzeShardKey command is not supported on a standalone mongod", repl::ReplicationCoordinator::get(opCtx)->isReplEnabled()); uassert(ErrorCodes::IllegalOperation, + "configQueryAnalyzer command is not supported on a multitenant replica set", + !gMultitenancySupport); + uassert(ErrorCodes::IllegalOperation, "analyzeShardKey command is not supported on a configsvr mongod", !serverGlobalParams.clusterRole.exclusivelyHasConfigRole()); @@ -123,7 +126,7 @@ public: "Unauthorized", AuthorizationSession::get(opCtx->getClient()) ->isAuthorizedForActionsOnResource(ResourcePattern::forExactNamespace(ns()), - ActionType::shardCollection)); + ActionType::analyzeShardKey)); } }; diff --git a/src/mongo/db/s/configure_query_analyzer_cmd.cpp b/src/mongo/db/s/configure_query_analyzer_cmd.cpp index 77538b5f1de..d8a0fef1e55 100644 --- a/src/mongo/db/s/configure_query_analyzer_cmd.cpp +++ b/src/mongo/db/s/configure_query_analyzer_cmd.cpp @@ -213,7 +213,7 @@ public: "Unauthorized", AuthorizationSession::get(opCtx->getClient()) ->isAuthorizedForActionsOnResource(ResourcePattern::forExactNamespace(ns()), - ActionType::createCollection)); + ActionType::configureQueryAnalyzer)); } }; diff --git a/src/mongo/db/s/document_source_analyze_shard_key_read_write_distribution.h b/src/mongo/db/s/document_source_analyze_shard_key_read_write_distribution.h index 4a70b8dd341..21a3b59f356 100644 --- a/src/mongo/db/s/document_source_analyze_shard_key_read_write_distribution.h +++ b/src/mongo/db/s/document_source_analyze_shard_key_read_write_distribution.h @@ -72,9 +72,8 @@ public: PrivilegeVector requiredPrivileges(bool isMongos, bool bypassDocumentValidation) const override { - // TODO (SERVER-69653): Add auth privilege requirements to the analyzeShardKey and - // configureQueryAnalyzer commands. - return {Privilege(ResourcePattern::forClusterResource(), ActionType::telemetryRead)}; + return { + Privilege(ResourcePattern::forExactNamespace(_nss), ActionType::analyzeShardKey)}; } stdx::unordered_set<NamespaceString> getInvolvedNamespaces() const override { diff --git a/src/mongo/s/commands/cluster_analyze_shard_key_cmd.cpp b/src/mongo/s/commands/cluster_analyze_shard_key_cmd.cpp index f1ecfa898c8..c04ad8cd926 100644 --- a/src/mongo/s/commands/cluster_analyze_shard_key_cmd.cpp +++ b/src/mongo/s/commands/cluster_analyze_shard_key_cmd.cpp @@ -175,7 +175,7 @@ public: "Unauthorized", AuthorizationSession::get(opCtx->getClient()) ->isAuthorizedForActionsOnResource(ResourcePattern::forExactNamespace(ns()), - ActionType::shardCollection)); + ActionType::analyzeShardKey)); } }; diff --git a/src/mongo/s/commands/cluster_configure_query_analyzer_cmd.cpp b/src/mongo/s/commands/cluster_configure_query_analyzer_cmd.cpp index 468aa04afc5..1a511206f04 100644 --- a/src/mongo/s/commands/cluster_configure_query_analyzer_cmd.cpp +++ b/src/mongo/s/commands/cluster_configure_query_analyzer_cmd.cpp @@ -87,7 +87,7 @@ public: "Unauthorized", AuthorizationSession::get(opCtx->getClient()) ->isAuthorizedForActionsOnResource(ResourcePattern::forExactNamespace(ns()), - ActionType::createCollection)); + ActionType::configureQueryAnalyzer)); } }; |