summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorIsrael Hsu <israel.hsu@mongodb.com>2023-04-04 17:52:23 +0000
committerEvergreen Agent <no-reply@evergreen.mongodb.com>2023-04-04 19:19:18 +0000
commitb584db7ad2e3256b37aa0191bb2b1215a7aff709 (patch)
treee5e02718c6af4b9f2bba1a4927c2f32c0913f05d
parente0a1eb3ce7cb84669666e94c9f37d1d3bffe53ec (diff)
downloadmongo-b584db7ad2e3256b37aa0191bb2b1215a7aff709.tar.gz
SERVER-69653 Add auth privilege requirements to the analyzeShardKey and configureQueryAnalyzer commands
-rw-r--r--buildscripts/resmokeconfig/suites/sharding_auth.yml1
-rw-r--r--buildscripts/resmokeconfig/suites/sharding_auth_catalog_shard.yml1
-rw-r--r--buildscripts/resmokeconfig/suites/sharding_continuous_config_stepdown.yml1
-rw-r--r--jstests/sharding/analyze_shard_key/analyze_shard_key_agg_stage_auth.js134
-rw-r--r--jstests/sharding/analyze_shard_key/analyze_shard_key_auth.js116
-rw-r--r--jstests/sharding/analyze_shard_key/analyze_shard_key_basic.js4
-rw-r--r--jstests/sharding/analyze_shard_key/configure_query_analyzer_auth.js140
-rw-r--r--src/mongo/db/auth/action_type.idl5
-rw-r--r--src/mongo/db/auth/authorization_session_test.cpp13
-rw-r--r--src/mongo/db/auth/builtin_roles.yml5
-rw-r--r--src/mongo/db/auth/builtin_roles_test.cpp2
-rw-r--r--src/mongo/db/pipeline/aggregate_command.idl4
-rw-r--r--src/mongo/db/s/analyze_shard_key_cmd.cpp5
-rw-r--r--src/mongo/db/s/configure_query_analyzer_cmd.cpp2
-rw-r--r--src/mongo/db/s/document_source_analyze_shard_key_read_write_distribution.h5
-rw-r--r--src/mongo/s/commands/cluster_analyze_shard_key_cmd.cpp2
-rw-r--r--src/mongo/s/commands/cluster_configure_query_analyzer_cmd.cpp2
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));
}
};