diff options
33 files changed, 253 insertions, 129 deletions
diff --git a/jstests/libs/override_methods/inject_dollar_tenant.js b/jstests/libs/override_methods/inject_dollar_tenant.js index 5e253cbd4bb..a336d8d2023 100644 --- a/jstests/libs/override_methods/inject_dollar_tenant.js +++ b/jstests/libs/override_methods/inject_dollar_tenant.js @@ -6,6 +6,11 @@ 'use strict'; load("jstests/libs/override_methods/override_helpers.js"); // For 'OverrideHelpers'. +load( + "jstests/libs/override_methods/tenant_aware_response_checker.js"); // For + // `assertExpectedDbNameInResponse` + // and + // `updateDbNamesInResponse`. const kTenantId = ObjectId(TestData.tenant); @@ -16,6 +21,10 @@ function runCommandWithDollarTenant( const cmdToRun = Object.assign({}, cmdObj, {"$tenant": kTenantId}); // Actually run the provided command. let res = originalRunCommand.apply(conn, makeRunCommandArgs(cmdToRun)); + + const prefixedDbName = kTenantId + "_" + dbName; + assertExpectedDbNameInResponse(res, dbName, prefixedDbName); + updateDbNamesInResponse(res, dbName, prefixedDbName); return res; } diff --git a/jstests/libs/override_methods/inject_security_token.js b/jstests/libs/override_methods/inject_security_token.js index b7ef386f330..813384be613 100644 --- a/jstests/libs/override_methods/inject_security_token.js +++ b/jstests/libs/override_methods/inject_security_token.js @@ -6,6 +6,11 @@ 'use strict'; load("jstests/libs/override_methods/override_helpers.js"); // For 'OverrideHelpers'. +load( + "jstests/libs/override_methods/tenant_aware_response_checker.js"); // For + // `assertExpectedDbNameInResponse` + // and + // `updateDbNamesInResponse`. const kUserName = "userTenant1"; const kTenantId = ObjectId(); @@ -52,46 +57,6 @@ function prepareSecurityToken(conn) { } } -function getDbName(nss) { - if (nss.length === 0 || !nss.includes(".")) { - return nss; - } - return nss.split(".")[0]; -} - -function checkDbNameInString(str, requestDbName, logError) { - if (requestDbName.length === 0) { - return; - } - if (requestDbName.includes("_")) { - return; - } - assert.eq(false, str.includes(kTenantId.str + "_" + requestDbName), logError); -} - -function checkReponse(res, requestDbName, logError) { - // We expect the response db name matches request. - for (let k of Object.keys(res)) { - let v = res[k]; - if (typeof v === "string") { - if (k === "dbName" || k == "db" || k == "dropped") { - checkDbNameInString(v, requestDbName, logError); - } else if (k === "namespace" || k === "ns") { - checkDbNameInString(getDbName(v), requestDbName, logError); - } else if (k === "errmsg" || k == "name") { - checkDbNameInString(v, requestDbName, logError); - } - } else if (Array.isArray(v)) { - v.forEach((item) => { - if (typeof item === "object" && item !== null) - checkReponse(item, requestDbName, logError); - }); - } else if (typeof v === "object" && v !== null && Object.keys(v).length > 0) { - checkReponse(v, requestDbName, logError); - } - } -} - const kCmdsAllowedWithSecurityToken = new Set([ `abortTransaction`, `aggregate`, @@ -170,12 +135,10 @@ function runCommandWithResponseCheck( // Actually run the provided command. let res = originalRunCommand.apply(conn, makeRunCommandArgs(cmdObj)); - const logUnmatchedDbName = (dbName, resObj) => { - return `Response db name does not match sent db name "${dbName}", response: ${ - tojsononeline(resObj)}`; - }; + const prefixedDbName = kTenantId + "_" + dbName; - checkReponse(res, dbName, logUnmatchedDbName(dbName, res)); + assertExpectedDbNameInResponse(res, dbName, prefixedDbName); + updateDbNamesInResponse(res, dbName, prefixedDbName); return res; } diff --git a/jstests/libs/override_methods/tenant_aware_response_checker.js b/jstests/libs/override_methods/tenant_aware_response_checker.js new file mode 100644 index 00000000000..2b864b29684 --- /dev/null +++ b/jstests/libs/override_methods/tenant_aware_response_checker.js @@ -0,0 +1,112 @@ + +function getDbName(nss) { + if (nss.length === 0 || !nss.includes(".")) { + return nss; + } + return nss.split(".")[0]; +} + +function wordInString(str, word) { + let regexp = new RegExp('\\b' + word + '\\b', 'i'); + return regexp.test(str); +} + +function checkExpectedDbNameInString(str, dbName, prefixedDbName) { + // System db names (admin, local and config) should never be tenant prefixed. + if (dbName == "admin" || dbName == "local" || dbName == "config") { + assert.eq(false, + wordInString(str, prefixedDbName), + `Response db name "${str}" does not match sent db name "${dbName}"`); + return; + } + // Currently, we do not expect prefixed db name in db name field as we only test with + // "featureFlagRequireTenantID: true". + // TODO SERVER-70740: expect prefixed db name if "expectPrefix" option in request is true. + assert.eq(false, + wordInString(str, prefixedDbName), + `Response db name "${str}" does not match sent db name "${dbName}"`); +} + +function checkExpectedDbInErrorMsg(errMsg, dbName, prefixedDbName) { + // The db name in error message should always include tenant prefixed db name regardless how the + // tenantId was received in the request. + + // If the dbName doesn't exist in the error message at all, there is no need to check that it's + // prefixed. + if (!wordInString(errMsg, dbName)) { + return; + } + + // TODO SERVER-74486: We will check collection ns string in future. + if (errMsg.includes(dbName + ".")) { + // Do not check ns until we change error mssage to include tenant in ns.; + return; + } + + // System db names (admin, local and config) should never be tenant prefixed. + if (dbName == "admin" || dbName == "local" || dbName == "config") { + assert.eq(false, + wordInString(errMsg, prefixedDbName), + `Response db name "${errMsg}" does not match sent db name "${dbName}"`); + return; + } + + assert.eq(true, + errMsg.includes(prefixedDbName), + `The db name in the errmsg does not contain expected tenant prefixed db name "${ + prefixedDbName}", error msg: ${errMsg}`); +} + +/** + * Check all the db names in the response are expected. + * @param {*} res response object. + * @param {*} requestDbName the original db name requested by jstest. + * @param {*} prefixedDbName the tenant prefixed db name expected by inject_dollar_tenant.js and + * inject_security_toiken.js. + */ +function assertExpectedDbNameInResponse(res, requestDbName, prefixedDbName) { + if (requestDbName.length === 0) { + return; + } + + for (let k of Object.keys(res)) { + let v = res[k]; + if (typeof v === "string") { + if (k === "dbName" || k == "db" || k == "dropped") { + checkExpectedDbNameInString(v, requestDbName, prefixedDbName); + } else if (k === "namespace" || k === "ns") { + checkExpectedDbNameInString(getDbName(v), requestDbName, prefixedDbName); + } else if (k == "name") { + checkExpectedDbNameInString(v, requestDbName, prefixedDbName); + } else if (k === "errmsg") { + checkExpectedDbInErrorMsg(v, requestDbName, prefixedDbName); + } + } else if (Array.isArray(v)) { + v.forEach((item) => { + if (typeof item === "object" && item !== null) + assertExpectedDbNameInResponse(item, requestDbName, prefixedDbName); + }); + } else if (typeof v === "object" && v !== null && Object.keys(v).length > 0) { + assertExpectedDbNameInResponse(v, requestDbName, prefixedDbName); + } + } +} + +function updateDbNamesInResponse(res, requestDbName, prefixedDbName) { + for (let k of Object.keys(res)) { + let v = res[k]; + if (typeof v === "string") { + // Replace prefixed db name with db name. + if (v.includes(prefixedDbName)) { + res[k] = v.replace(prefixedDbName, requestDbName); + } + } else if (Array.isArray(v)) { + v.forEach((item) => { + if (typeof item === "object" && item !== null) + updateDbNamesInResponse(item, requestDbName, prefixedDbName); + }); + } else if (typeof v === "object" && v !== null && Object.keys(v).length > 0) { + updateDbNamesInResponse(v, requestDbName, prefixedDbName); + } + } +} diff --git a/src/mongo/db/catalog/capped_utils.cpp b/src/mongo/db/catalog/capped_utils.cpp index b9a9c1cf92d..93dd61ed5a9 100644 --- a/src/mongo/db/catalog/capped_utils.cpp +++ b/src/mongo/db/catalog/capped_utils.cpp @@ -283,8 +283,9 @@ void convertToCapped(OperationContext* opCtx, const NamespaceString& ns, long lo !userInitiatedWritesAndNotPrimary); Database* const db = coll.getDb(); - uassert( - ErrorCodes::NamespaceNotFound, str::stream() << "database " << dbname << " not found", db); + uassert(ErrorCodes::NamespaceNotFound, + str::stream() << "database " << dbname.toStringForErrorMsg() << " not found", + db); if (coll) { IndexBuildsCoordinator::get(opCtx)->assertNoIndexBuildInProgForCollection(coll->uuid()); diff --git a/src/mongo/db/catalog/collection_catalog.cpp b/src/mongo/db/catalog/collection_catalog.cpp index 2a31e76ec09..e6495fd77d8 100644 --- a/src/mongo/db/catalog/collection_catalog.cpp +++ b/src/mongo/db/catalog/collection_catalog.cpp @@ -1748,8 +1748,8 @@ NamespaceString CollectionCatalog::resolveNamespaceStringOrUUID( resolvedNss && resolvedNss->isValid()); uassert(ErrorCodes::NamespaceNotFound, - str::stream() << "UUID: " << nsOrUUID.toString() - << " specified in provided db name: " << nsOrUUID.dbname() + str::stream() << "UUID: " << nsOrUUID.toString() << " specified in provided db name: " + << nsOrUUID.dbName()->toStringForErrorMsg() << " resolved to a collection in a different database, resolved nss: " << *resolvedNss, resolvedNss->dbName() == nsOrUUID.dbName()); diff --git a/src/mongo/db/catalog/collection_impl.cpp b/src/mongo/db/catalog/collection_impl.cpp index 05b10d228e1..998beb24519 100644 --- a/src/mongo/db/catalog/collection_impl.cpp +++ b/src/mongo/db/catalog/collection_impl.cpp @@ -136,7 +136,7 @@ Status validateIsNotInDbs(const NamespaceString& ns, if (std::find(disallowedDbs.begin(), disallowedDbs.end(), ns.dbName()) != disallowedDbs.end()) { return {ErrorCodes::InvalidOptions, str::stream() << optionName << " collection option is not supported on the " - << ns.db() << " database"}; + << ns.dbName().toStringForErrorMsg() << " database"}; } return Status::OK(); diff --git a/src/mongo/db/catalog/create_collection_test.cpp b/src/mongo/db/catalog/create_collection_test.cpp index d787d4f4259..1d443428ad8 100644 --- a/src/mongo/db/catalog/create_collection_test.cpp +++ b/src/mongo/db/catalog/create_collection_test.cpp @@ -116,7 +116,7 @@ void CreateCollectionTest::validateValidator(const std::string& validatorStr, AutoGetCollection autoColl(opCtx.get(), newNss, MODE_IX); auto db = autoColl.ensureDbExists(opCtx.get()); ASSERT_TRUE(db) << "Cannot create collection " << newNss << " because database " - << newNss.db() << " does not exist."; + << newNss.dbName().toStringForErrorMsg() << " does not exist."; WriteUnitOfWork wuow(opCtx.get()); const auto status = diff --git a/src/mongo/db/catalog/database_holder_impl.cpp b/src/mongo/db/catalog/database_holder_impl.cpp index 84cd79d584f..6a3f8a364a7 100644 --- a/src/mongo/db/catalog/database_holder_impl.cpp +++ b/src/mongo/db/catalog/database_holder_impl.cpp @@ -48,7 +48,7 @@ namespace mongo { Database* DatabaseHolderImpl::getDb(OperationContext* opCtx, const DatabaseName& dbName) const { uassert( 13280, - "invalid db name: " + dbName.db(), + "invalid db name: " + dbName.toStringForErrorMsg(), NamespaceString::validDBName(dbName.db(), NamespaceString::DollarInDbNameBehavior::Allow)); invariant(opCtx->lockState()->isDbLockedForMode(dbName, MODE_IS) || @@ -66,7 +66,7 @@ Database* DatabaseHolderImpl::getDb(OperationContext* opCtx, const DatabaseName& bool DatabaseHolderImpl::dbExists(OperationContext* opCtx, const DatabaseName& dbName) const { uassert( 6198702, - "invalid db name: " + dbName.db(), + "invalid db name: " + dbName.toStringForErrorMsg(), NamespaceString::validDBName(dbName.db(), NamespaceString::DollarInDbNameBehavior::Allow)); stdx::lock_guard<SimpleMutex> lk(_m); auto it = _dbs.find(dbName); @@ -105,7 +105,7 @@ Database* DatabaseHolderImpl::openDb(OperationContext* opCtx, bool* justCreated) { uassert( 6198701, - "invalid db name: " + dbName.db(), + "invalid db name: " + dbName.toStringForErrorMsg(), NamespaceString::validDBName(dbName.db(), NamespaceString::DollarInDbNameBehavior::Allow)); invariant(opCtx->lockState()->isDbLockedForMode(dbName, MODE_IX)); @@ -134,8 +134,8 @@ Database* DatabaseHolderImpl::openDb(OperationContext* opCtx, auto duplicates = _getNamesWithConflictingCasing_inlock(dbName); uassert(ErrorCodes::DatabaseDifferCase, str::stream() << "db already exists with different case already have: [" - << (*duplicates.cbegin()) << "] trying to create [" << dbName.toString() - << "]", + << (*duplicates.cbegin()) << "] trying to create [" + << dbName.toStringForErrorMsg() << "]", duplicates.empty()); // Do the catalog lookup and database creation outside of the scoped lock, because these may @@ -231,7 +231,7 @@ void DatabaseHolderImpl::dropDb(OperationContext* opCtx, Database* db) { void DatabaseHolderImpl::close(OperationContext* opCtx, const DatabaseName& dbName) { uassert( 6198700, - "invalid db name: " + dbName.db(), + "invalid db name: " + dbName.toStringForErrorMsg(), NamespaceString::validDBName(dbName.db(), NamespaceString::DollarInDbNameBehavior::Allow)); invariant(opCtx->lockState()->isDbLockedForMode(dbName, MODE_X)); diff --git a/src/mongo/db/catalog/drop_database.cpp b/src/mongo/db/catalog/drop_database.cpp index 8840ee75782..008e3e4db26 100644 --- a/src/mongo/db/catalog/drop_database.cpp +++ b/src/mongo/db/catalog/drop_database.cpp @@ -64,8 +64,8 @@ namespace { Status _checkNssAndReplState(OperationContext* opCtx, Database* db, const DatabaseName& dbName) { if (!db) { return Status(ErrorCodes::NamespaceNotFound, - str::stream() - << "Could not drop database " << dbName << " because it does not exist"); + str::stream() << "Could not drop database " << dbName.toStringForErrorMsg() + << " because it does not exist"); } auto replCoord = repl::ReplicationCoordinator::get(opCtx); @@ -74,7 +74,8 @@ Status _checkNssAndReplState(OperationContext* opCtx, Database* db, const Databa if (userInitiatedWritesAndNotPrimary) { return Status(ErrorCodes::NotWritablePrimary, - str::stream() << "Not primary while dropping database " << dbName); + str::stream() << "Not primary while dropping database " + << dbName.toStringForErrorMsg()); } return Status::OK(); @@ -139,7 +140,8 @@ Status _dropDatabase(OperationContext* opCtx, const DatabaseName& dbName, bool a // As of SERVER-32205, dropping the admin database is prohibited. uassert(ErrorCodes::IllegalOperation, - str::stream() << "Dropping the '" << dbName << "' database is prohibited.", + str::stream() << "Dropping the '" << dbName.toStringForErrorMsg() + << "' database is prohibited.", dbName.db() != DatabaseName::kAdmin.db()); { @@ -168,8 +170,8 @@ Status _dropDatabase(OperationContext* opCtx, const DatabaseName& dbName, bool a if (db->isDropPending(opCtx)) { return Status(ErrorCodes::DatabaseDropPending, - str::stream() - << "The database is currently being dropped. Database: " << dbName); + str::stream() << "The database is currently being dropped. Database: " + << dbName.toStringForErrorMsg()); } LOGV2(20337, @@ -395,8 +397,8 @@ Status _dropDatabase(OperationContext* opCtx, const DatabaseName& dbName, bool a if (!result.status.isOK()) { return result.status.withContext(str::stream() - << "dropDatabase " << dbName << " failed waiting for " - << numCollectionsToDrop + << "dropDatabase " << dbName.toStringForErrorMsg() + << " failed waiting for " << numCollectionsToDrop << " collection drop(s) (most recent drop optime: " << awaitOpTime.toString() << ") to replicate."); } @@ -422,7 +424,7 @@ Status _dropDatabase(OperationContext* opCtx, const DatabaseName& dbName, bool a auto db = autoDB.getDb(); if (!db) { return Status(ErrorCodes::NamespaceNotFound, - str::stream() << "Could not drop database " << dbName + str::stream() << "Could not drop database " << dbName.toStringForErrorMsg() << " because it does not exist after dropping " << numCollectionsToDrop << " collection(s)."); } @@ -433,7 +435,7 @@ Status _dropDatabase(OperationContext* opCtx, const DatabaseName& dbName, bool a if (userInitiatedWritesAndNotPrimary) { return Status(ErrorCodes::PrimarySteppedDown, str::stream() - << "Could not drop database " << dbName + << "Could not drop database " << dbName.toStringForErrorMsg() << " because we transitioned from PRIMARY to " << replCoord->getMemberState().toString() << " while waiting for " << numCollectionsToDrop << " pending collection drop(s)."); diff --git a/src/mongo/db/catalog/drop_indexes.cpp b/src/mongo/db/catalog/drop_indexes.cpp index 8da57458b44..e0da849cd11 100644 --- a/src/mongo/db/catalog/drop_indexes.cpp +++ b/src/mongo/db/catalog/drop_indexes.cpp @@ -83,7 +83,8 @@ Status checkReplState(OperationContext* opCtx, if (writesAreReplicatedAndNotPrimary) { return Status(ErrorCodes::NotWritablePrimary, str::stream() << "Not primary while dropping indexes on database " - << dbAndUUID.db() << " with collection " << dbAndUUID.uuid()); + << dbAndUUID.dbName()->toStringForErrorMsg() + << " with collection " << dbAndUUID.uuid()); } // Disallow index drops on drop-pending namespaces (system.drop.*) if we are primary. @@ -92,8 +93,8 @@ Status checkReplState(OperationContext* opCtx, if (isPrimary && nss.isDropPendingNamespace()) { return Status(ErrorCodes::NamespaceNotFound, str::stream() << "Cannot drop indexes on drop-pending namespace " << nss - << " in database " << dbAndUUID.db() << " with uuid " - << dbAndUUID.uuid()); + << " in database " << dbAndUUID.dbName()->toStringForErrorMsg() + << " with uuid " << dbAndUUID.uuid()); } return Status::OK(); @@ -473,7 +474,8 @@ DropIndexesReply dropIndexes(OperationContext* opCtx, if (!*collection) { uasserted(ErrorCodes::NamespaceNotFound, str::stream() << "Collection '" << nss << "' with UUID " << dbAndUUID.uuid() - << " in database " << dbAndUUID.db() << " does not exist."); + << " in database " << dbAndUUID.dbName()->toStringForErrorMsg() + << " does not exist."); } // The collection could have been renamed when we dropped locks. diff --git a/src/mongo/db/catalog/rename_collection.cpp b/src/mongo/db/catalog/rename_collection.cpp index c9a961ae126..cf50754a73b 100644 --- a/src/mongo/db/catalog/rename_collection.cpp +++ b/src/mongo/db/catalog/rename_collection.cpp @@ -104,8 +104,8 @@ Status checkSourceAndTargetNamespaces(OperationContext* opCtx, auto db = DatabaseHolder::get(opCtx)->getDb(opCtx, source.dbName()); if (!db || db->isDropPending(opCtx)) return Status(ErrorCodes::NamespaceNotFound, - str::stream() - << "Database " << source.db() << " does not exist or is drop pending"); + str::stream() << "Database " << source.dbName().toStringForErrorMsg() + << " does not exist or is drop pending"); auto catalog = CollectionCatalog::get(opCtx); const auto sourceColl = catalog->lookupCollectionByNamespace(opCtx, source); diff --git a/src/mongo/db/commands.cpp b/src/mongo/db/commands.cpp index c0ccb1336e4..3d15df11089 100644 --- a/src/mongo/db/commands.cpp +++ b/src/mongo/db/commands.cpp @@ -568,7 +568,7 @@ void CommandHelpers::canUseTransactions(const NamespaceString& nss, const auto dbName = nss.dbName(); uassert(ErrorCodes::OperationNotSupportedInTransaction, - str::stream() << "Cannot run command against the '" << dbName + str::stream() << "Cannot run command against the '" << dbName.toStringForErrorMsg() << "' database in a transaction.", dbName.db() != DatabaseName::kLocal.db()); @@ -876,10 +876,11 @@ void CommandInvocation::checkAuthorization(OperationContext* opCtx, namespace mmb = mutablebson; mmb::Document cmdToLog(request.body, mmb::Document::kInPlaceDisabled); c->snipForLogging(&cmdToLog); - auto dbname = request.getDatabase(); + auto dbName = DatabaseNameUtil::deserialize(request.getValidatedTenantId(), + request.getDatabase()); uasserted(ErrorCodes::Unauthorized, - str::stream() << "not authorized on " << dbname << " to execute command " - << redact(cmdToLog.getObject())); + str::stream() << "not authorized on " << dbName.toStringForErrorMsg() + << " to execute command " << redact(cmdToLog.getObject())); } } } catch (const DBException& e) { diff --git a/src/mongo/db/commands/collection_to_capped.cpp b/src/mongo/db/commands/collection_to_capped.cpp index 2796e297e61..a420f9c1f57 100644 --- a/src/mongo/db/commands/collection_to_capped.cpp +++ b/src/mongo/db/commands/collection_to_capped.cpp @@ -131,7 +131,7 @@ public: Database* const db = autoColl.getDb(); if (!db) { uasserted(ErrorCodes::NamespaceNotFound, - str::stream() << "database " << dbName.toString() << " not found"); + str::stream() << "database " << dbName.toStringForErrorMsg() << " not found"); } cloneCollectionAsCapped(opCtx, db, fromNs, toNs, size, temp); diff --git a/src/mongo/db/commands/dbcheck.cpp b/src/mongo/db/commands/dbcheck.cpp index c55f42df246..29a18c6c3f5 100644 --- a/src/mongo/db/commands/dbcheck.cpp +++ b/src/mongo/db/commands/dbcheck.cpp @@ -201,7 +201,9 @@ std::unique_ptr<DbCheckRun> fullDatabaseRun(OperationContext* opCtx, dbName.db() != "local"); AutoGetDb agd(opCtx, dbName, MODE_IS); - uassert(ErrorCodes::NamespaceNotFound, "Database " + dbName.db() + " not found", agd.getDb()); + uassert(ErrorCodes::NamespaceNotFound, + "Database " + dbName.toStringForErrorMsg() + " not found", + agd.getDb()); uassert(6769501, "dbCheck no longer supports snapshotRead:false", invocation.getSnapshotRead()); diff --git a/src/mongo/db/commands/dbcommands.cpp b/src/mongo/db/commands/dbcommands.cpp index a0269f2ccc4..fae5c8b9a84 100644 --- a/src/mongo/db/commands/dbcommands.cpp +++ b/src/mongo/db/commands/dbcommands.cpp @@ -151,7 +151,7 @@ public: repl::ReplicationCoordinator::modeNone) && (dbName == DatabaseName::kLocal)) { uasserted(ErrorCodes::IllegalOperation, - str::stream() << "Cannot drop '" << dbName + str::stream() << "Cannot drop '" << dbName.toStringForErrorMsg() << "' database while replication is active"); } @@ -628,7 +628,7 @@ public: ErrorCodes::BadValue, "Scale factor must be greater than zero", cmd.getScale() > 0); uassert(ErrorCodes::InvalidNamespace, - str::stream() << "Invalid db name: " << dbname, + str::stream() << "Invalid db name: " << dbname.toStringForErrorMsg(), NamespaceString::validDBName(dbname.db(), NamespaceString::DollarInDbNameBehavior::Allow)); diff --git a/src/mongo/db/commands/explain_cmd.cpp b/src/mongo/db/commands/explain_cmd.cpp index 137d418b389..c10e8250aed 100644 --- a/src/mongo/db/commands/explain_cmd.cpp +++ b/src/mongo/db/commands/explain_cmd.cpp @@ -167,7 +167,7 @@ std::unique_ptr<CommandInvocation> CmdExplain::parse(OperationContext* opCtx, IDLParserContext(ExplainCommandRequest::kCommandName, APIParameters::get(opCtx).getAPIStrict().value_or(false)), request); - std::string dbname = cmdObj.getDbName().toString(); + auto const dbName = cmdObj.getDbName(); ExplainOptions::Verbosity verbosity = cmdObj.getVerbosity(); auto explainedObj = cmdObj.getCommandParameter(); @@ -179,10 +179,13 @@ std::unique_ptr<CommandInvocation> CmdExplain::parse(OperationContext* opCtx, } if (auto innerDb = explainedObj["$db"]) { + auto innerDbName = + DatabaseNameUtil::deserialize(dbName.tenantId(), innerDb.checkAndGetStringData()); uassert(ErrorCodes::InvalidNamespace, - str::stream() << "Mismatched $db in explain command. Expected " << dbname - << " but got " << innerDb.checkAndGetStringData(), - innerDb.checkAndGetStringData() == dbname); + str::stream() << "Mismatched $db in explain command. Expected " + << dbName.toStringForErrorMsg() << " but got " + << innerDbName.toStringForErrorMsg(), + innerDb.checkAndGetStringData() == dbName.toString()); } auto explainedCommand = CommandHelpers::findCommand(explainedObj.firstElementFieldName()); uassert(ErrorCodes::CommandNotFound, @@ -190,8 +193,8 @@ std::unique_ptr<CommandInvocation> CmdExplain::parse(OperationContext* opCtx, << explainedObj.firstElementFieldName(), explainedCommand); auto innerRequest = - std::make_unique<OpMsgRequest>(OpMsgRequest::fromDBAndBody(dbname, explainedObj)); - innerRequest->validatedTenancyScope = request.validatedTenancyScope; + std::make_unique<OpMsgRequest>(OpMsgRequestBuilder::createWithValidatedTenancyScope( + dbName, request.validatedTenancyScope, explainedObj)); auto innerInvocation = explainedCommand->parseForExplain(opCtx, *innerRequest, verbosity); return std::make_unique<Invocation>( this, request, std::move(verbosity), std::move(innerRequest), std::move(innerInvocation)); diff --git a/src/mongo/db/commands/find_and_modify.cpp b/src/mongo/db/commands/find_and_modify.cpp index 664a65a6b39..5fdf5944c91 100644 --- a/src/mongo/db/commands/find_and_modify.cpp +++ b/src/mongo/db/commands/find_and_modify.cpp @@ -351,7 +351,7 @@ void CmdFindAndModify::Invocation::explain(OperationContext* opCtx, uassertStatusOK(userAllowedWriteNS(opCtx, nss)); auto const curOp = CurOp::get(opCtx); OpDebug* const opDebug = &curOp->debug(); - const std::string dbName = request.getDbName().toString(); + auto const dbName = request.getDbName(); if (request.getRemove().value_or(false)) { auto deleteRequest = DeleteRequest{}; @@ -366,7 +366,7 @@ void CmdFindAndModify::Invocation::explain(OperationContext* opCtx, // locks so that the timing information is more accurate. AutoGetCollection collection(opCtx, nss, MODE_IX); uassert(ErrorCodes::NamespaceNotFound, - str::stream() << "database " << dbName << " does not exist", + str::stream() << "database " << dbName.toStringForErrorMsg() << " does not exist", collection.getDb()); CollectionShardingState::assertCollectionLockedAndAcquire(opCtx, nss) @@ -391,7 +391,7 @@ void CmdFindAndModify::Invocation::explain(OperationContext* opCtx, // locks so that the timing information is more accurate. AutoGetCollection collection(opCtx, nss, MODE_IX); uassert(ErrorCodes::NamespaceNotFound, - str::stream() << "database " << dbName << " does not exist", + str::stream() << "database " << dbName.toStringForErrorMsg() << " does not exist", collection.getDb()); CollectionShardingState::assertCollectionLockedAndAcquire(opCtx, nss) diff --git a/src/mongo/db/commands/fle2_compact_cmd.cpp b/src/mongo/db/commands/fle2_compact_cmd.cpp index b5fabd10b27..9e5d86eefb5 100644 --- a/src/mongo/db/commands/fle2_compact_cmd.cpp +++ b/src/mongo/db/commands/fle2_compact_cmd.cpp @@ -80,7 +80,8 @@ CompactStats compactEncryptedCompactionCollection(OperationContext* opCtx, AutoGetDb autoDb(opCtx, edcNss.dbName(), MODE_IX); uassert(ErrorCodes::NamespaceNotFound, - str::stream() << "Database '" << edcNss.db() << "' does not exist", + str::stream() << "Database '" << edcNss.dbName().toStringForErrorMsg() + << "' does not exist", autoDb.getDb()); auto catalog = CollectionCatalog::get(opCtx); diff --git a/src/mongo/db/commands/user_management_commands.cpp b/src/mongo/db/commands/user_management_commands.cpp index 233c9772a46..868a7278540 100644 --- a/src/mongo/db/commands/user_management_commands.cpp +++ b/src/mongo/db/commands/user_management_commands.cpp @@ -1971,8 +1971,9 @@ DropAllRolesFromDatabaseReply CmdUMCTyped<DropAllRolesFromDatabaseCommand>::Invo txn.update(usersNSS(dbname.tenantId()), rolesMatch, BSON("$pull" << rolesMatch)); if (!swCount.isOK()) { return useDefaultCode(swCount.getStatus(), ErrorCodes::UserModificationFailed) - .withContext(str::stream() << "Failed to remove roles from \"" << dbname.db() - << "\" db from all users"); + .withContext(str::stream() + << "Failed to remove roles from \"" << dbname.toStringForErrorMsg() + << "\" db from all users"); } // Remove these roles from all other roles @@ -1981,15 +1982,16 @@ DropAllRolesFromDatabaseReply CmdUMCTyped<DropAllRolesFromDatabaseCommand>::Invo BSON("$pull" << rolesMatch)); if (!swCount.isOK()) { return useDefaultCode(swCount.getStatus(), ErrorCodes::RoleModificationFailed) - .withContext(str::stream() << "Failed to remove roles from \"" << dbname.db() - << "\" db from all roles"); + .withContext(str::stream() + << "Failed to remove roles from \"" << dbname.toStringForErrorMsg() + << "\" db from all roles"); } // Finally, remove the actual role documents swCount = txn.remove(rolesNSS(dbname.tenantId()), roleMatch); if (!swCount.isOK()) { return swCount.getStatus().withContext( - str::stream() << "Removed roles from \"" << dbname.db() + str::stream() << "Removed roles from \"" << dbname.toStringForErrorMsg() << "\" db " " from all users and roles but failed to actually delete" " those roles themselves"); diff --git a/src/mongo/db/commands/user_management_commands_common.cpp b/src/mongo/db/commands/user_management_commands_common.cpp index 1b7d1223e6b..aad109f76eb 100644 --- a/src/mongo/db/commands/user_management_commands_common.cpp +++ b/src/mongo/db/commands/user_management_commands_common.cpp @@ -187,7 +187,8 @@ void checkAuthForTypedCommand(OperationContext* opCtx, const CreateUserCommand& auto* as = AuthorizationSession::get(opCtx->getClient()); uassert(ErrorCodes::Unauthorized, - str::stream() << "Not authorized to create users on db: " << dbname, + str::stream() << "Not authorized to create users on db: " + << dbname.toStringForErrorMsg(), as->isAuthorizedForActionsOnResource( ResourcePattern::forDatabaseName(dbname.toStringWithTenantId()), ActionType::createUser)); @@ -249,7 +250,8 @@ void checkAuthForTypedCommand(OperationContext* opCtx, const CreateRoleCommand& RoleName roleName(request.getCommandParameter(), dbname); uassert(ErrorCodes::Unauthorized, - str::stream() << "Not authorized to create roles on db: " << dbname, + str::stream() << "Not authorized to create roles on db: " + << dbname.toStringForErrorMsg(), as->isAuthorizedToCreateRole(roleName)); uassertStatusOK(checkAuthorizedToGrantRoles(as, resolveRoleNames(request.getRoles(), dbname))); @@ -309,7 +311,8 @@ void checkAuthForTypedCommand(OperationContext* opCtx, const DropRoleCommand& re uassert( ErrorCodes::Unauthorized, - str::stream() << "Not authorized to drop roles from the " << dbname << " database", + str::stream() << "Not authorized to drop roles from the " << dbname.toStringForErrorMsg() + << " database", as->isAuthorizedForActionsOnResource( ResourcePattern::forDatabaseName(dbname.toStringWithTenantId()), ActionType::dropRole)); } @@ -320,7 +323,8 @@ void checkAuthForTypedCommand(OperationContext* opCtx, auto* as = AuthorizationSession::get(opCtx->getClient()); uassert( ErrorCodes::Unauthorized, - str::stream() << "Not authorized to drop users from the " << dbname << " database", + str::stream() << "Not authorized to drop users from the " << dbname.toStringForErrorMsg() + << " database", as->isAuthorizedForActionsOnResource( ResourcePattern::forDatabaseName(dbname.toStringWithTenantId()), ActionType::dropUser)); } @@ -344,7 +348,8 @@ void checkAuthForTypedCommand(OperationContext* opCtx, const UsersInfoCommand& r if (arg.isAllOnCurrentDB()) { uassert(ErrorCodes::Unauthorized, - str::stream() << "Not authorized to view users from the " << dbname << " database", + str::stream() << "Not authorized to view users from the " + << dbname.toStringForErrorMsg() << " database", as->isAuthorizedForActionsOnResource( ResourcePattern::forDatabaseName(dbname.toStringWithTenantId()), ActionType::viewUser)); @@ -371,8 +376,8 @@ void checkAuthForTypedCommand(OperationContext* opCtx, const UsersInfoCommand& r continue; } uassert(ErrorCodes::Unauthorized, - str::stream() << "Not authorized to view users from the " << dbname - << " database", + str::stream() << "Not authorized to view users from the " + << dbname.toStringForErrorMsg() << " database", as->isAuthorizedForActionsOnResource( ResourcePattern::forDatabaseName(userName.getDB()), ActionType::viewUser)); } @@ -391,7 +396,8 @@ void checkAuthForTypedCommand(OperationContext* opCtx, auto* as = AuthorizationSession::get(opCtx->getClient()); uassert( ErrorCodes::Unauthorized, - str::stream() << "Not authorized to drop roles from the " << dbname << " database", + str::stream() << "Not authorized to drop roles from the " << dbname.toStringForErrorMsg() + << " database", as->isAuthorizedForActionsOnResource( ResourcePattern::forDatabaseName(dbname.toStringWithTenantId()), ActionType::dropRole)); } @@ -404,7 +410,8 @@ void checkAuthForTypedCommand(OperationContext* opCtx, const RolesInfoCommand& r invariant(!arg.isAllForAllDBs()); if (arg.isAllOnCurrentDB()) { uassert(ErrorCodes::Unauthorized, - str::stream() << "Not authorized to view roles from the " << dbname << " database", + str::stream() << "Not authorized to view roles from the " + << dbname.toStringForErrorMsg() << " database", as->isAuthorizedForActionsOnResource( ResourcePattern::forDatabaseName(dbname.toStringWithTenantId()), ActionType::viewRole)); diff --git a/src/mongo/db/database_name.h b/src/mongo/db/database_name.h index bc1457f192e..716df3ed3c7 100644 --- a/src/mongo/db/database_name.h +++ b/src/mongo/db/database_name.h @@ -153,6 +153,16 @@ public: return _dbString; } + /** + * This function should only be used when logging a db name in an error message. + */ + std::string toStringForErrorMsg() const { + if (_tenantId) + return str::stream() << *_tenantId << '_' << _dbString; + + return _dbString; + } + bool equalCaseInsensitive(const DatabaseName& other) const { return (_tenantId == other._tenantId) && boost::iequals(toString(), other.toString()); } diff --git a/src/mongo/db/pipeline/document_source_graph_lookup.cpp b/src/mongo/db/pipeline/document_source_graph_lookup.cpp index f8a7244b03a..a79b28205b5 100644 --- a/src/mongo/db/pipeline/document_source_graph_lookup.cpp +++ b/src/mongo/db/pipeline/document_source_graph_lookup.cpp @@ -88,7 +88,7 @@ NamespaceString parseGraphLookupFromAndResolveNamespace(const BSONElement& elem, uassert(ErrorCodes::FailedToParse, str::stream() << "$graphLookup with syntax {from: {db:<>, coll:<>},..} is not supported for db: " - << nss.db() << " and coll: " << nss.coll(), + << nss.dbName().toStringForErrorMsg() << " and coll: " << nss.coll(), nss == NamespaceString::kTenantMigrationOplogView); return nss; } diff --git a/src/mongo/db/pipeline/document_source_lookup.cpp b/src/mongo/db/pipeline/document_source_lookup.cpp index 9dee46e0326..3ca1f8663d5 100644 --- a/src/mongo/db/pipeline/document_source_lookup.cpp +++ b/src/mongo/db/pipeline/document_source_lookup.cpp @@ -117,7 +117,7 @@ NamespaceString parseLookupFromAndResolveNamespace(const BSONElement& elem, uassert( ErrorCodes::FailedToParse, str::stream() << "$lookup with syntax {from: {db:<>, coll:<>},..} is not supported for db: " - << nss.db() << " and coll: " << nss.coll(), + << nss.dbName().toStringForErrorMsg() << " and coll: " << nss.coll(), nss.isConfigDotCacheDotChunks() || nss == NamespaceString::kRsOplogNamespace || nss == NamespaceString::kTenantMigrationOplogView || nss == NamespaceString::kConfigsvrCollectionsNamespace); diff --git a/src/mongo/db/repl/apply_ops.cpp b/src/mongo/db/repl/apply_ops.cpp index c5326d53be0..b707565572a 100644 --- a/src/mongo/db/repl/apply_ops.cpp +++ b/src/mongo/db/repl/apply_ops.cpp @@ -254,7 +254,8 @@ Status applyOps(OperationContext* opCtx, if (userInitiatedWritesAndNotPrimary) return Status(ErrorCodes::NotWritablePrimary, - str::stream() << "Not primary while applying ops to database " << dbName); + str::stream() << "Not primary while applying ops to database " + << dbName.toStringForErrorMsg()); LOGV2_DEBUG(5854600, 2, diff --git a/src/mongo/db/repl/oplog_applier_utils.cpp b/src/mongo/db/repl/oplog_applier_utils.cpp index 2861c7e877d..676bcfa86c7 100644 --- a/src/mongo/db/repl/oplog_applier_utils.cpp +++ b/src/mongo/db/repl/oplog_applier_utils.cpp @@ -379,7 +379,8 @@ Status OplogApplierUtils::applyOplogEntryOrGroupedInsertsCommon( } uassert(ErrorCodes::NamespaceNotFound, - str::stream() << "missing database (" << nss.db() << ")", + str::stream() << "missing database (" + << nss.dbName().toStringForErrorMsg() << ")", db); OldClientContext ctx(opCtx, autoColl->getNss(), db); diff --git a/src/mongo/db/repl/rollback_source_impl.cpp b/src/mongo/db/repl/rollback_source_impl.cpp index 29e7ffc0c93..de16558a010 100644 --- a/src/mongo/db/repl/rollback_source_impl.cpp +++ b/src/mongo/db/repl/rollback_source_impl.cpp @@ -108,10 +108,10 @@ StatusWith<BSONObj> RollbackSourceImpl::getCollectionInfoByUUID(const DatabaseNa std::list<BSONObj> info = _getConnection()->getCollectionInfos(dbName, BSON("info.uuid" << uuid)); if (info.empty()) { - return StatusWith<BSONObj>(ErrorCodes::NoSuchKey, - str::stream() - << "No collection info found for collection with uuid: " - << uuid.toString() << " in db: " << dbName.db()); + return StatusWith<BSONObj>( + ErrorCodes::NoSuchKey, + str::stream() << "No collection info found for collection with uuid: " + << uuid.toString() << " in db: " << dbName.toStringForErrorMsg()); } invariant(info.size() == 1U); return info.front(); diff --git a/src/mongo/db/repl/storage_interface_impl.cpp b/src/mongo/db/repl/storage_interface_impl.cpp index ae2e11a957d..10e91f5bd76 100644 --- a/src/mongo/db/repl/storage_interface_impl.cpp +++ b/src/mongo/db/repl/storage_interface_impl.cpp @@ -564,9 +564,9 @@ Status StorageInterfaceImpl::renameCollection(OperationContext* opCtx, AutoGetDb autoDB(opCtx, fromNS.dbName(), MODE_X); if (!autoDB.getDb()) { return Status(ErrorCodes::NamespaceNotFound, - str::stream() - << "Cannot rename collection from " << fromNS.ns() << " to " - << toNS.ns() << ". Database " << fromNS.db() << " not found."); + str::stream() << "Cannot rename collection from " << fromNS.ns() << " to " + << toNS.ns() << ". Database " + << fromNS.dbName().toStringForErrorMsg() << " not found."); } WriteUnitOfWork wunit(opCtx); const auto status = autoDB.getDb()->renameCollection(opCtx, fromNS, toNS, stayTemp); diff --git a/src/mongo/db/s/database_sharding_state.cpp b/src/mongo/db/s/database_sharding_state.cpp index 2f312869758..08b706d7ef3 100644 --- a/src/mongo/db/s/database_sharding_state.cpp +++ b/src/mongo/db/s/database_sharding_state.cpp @@ -178,18 +178,19 @@ void DatabaseShardingState::assertMatchingDbVersion(OperationContext* opCtx, : ShardingMigrationCriticalSection::kRead); uassert( StaleDbRoutingVersion(dbName.toString(), receivedVersion, boost::none, critSecSignal), - str::stream() << "The critical section for the database " << dbName + str::stream() << "The critical section for the database " + << dbName.toStringForErrorMsg() << " is acquired with reason: " << scopedDss->getCriticalSectionReason(), !critSecSignal); } const auto wantedVersion = scopedDss->getDbVersion(opCtx); uassert(StaleDbRoutingVersion(dbName.toString(), receivedVersion, boost::none), - str::stream() << "No cached info for the database " << dbName, + str::stream() << "No cached info for the database " << dbName.toStringForErrorMsg(), wantedVersion); uassert(StaleDbRoutingVersion(dbName.toString(), receivedVersion, *wantedVersion), - str::stream() << "Version mismatch for the database " << dbName, + str::stream() << "Version mismatch for the database " << dbName.toStringForErrorMsg(), receivedVersion == *wantedVersion); } @@ -204,7 +205,8 @@ void DatabaseShardingState::assertIsPrimaryShardForDb(OperationContext* opCtx, } uassert(ErrorCodes::IllegalOperation, - str::stream() << "Received request without the version for the database " << dbName, + str::stream() << "Received request without the version for the database " + << dbName.toStringForErrorMsg(), OperationShardingState::get(opCtx).hasDbVersion()); Lock::DBLock dbLock(opCtx, dbName, MODE_IS); @@ -214,8 +216,9 @@ void DatabaseShardingState::assertIsPrimaryShardForDb(OperationContext* opCtx, const auto primaryShardId = scopedDss->_dbInfo->getPrimary(); const auto thisShardId = ShardingState::get(opCtx)->shardId(); uassert(ErrorCodes::IllegalOperation, - str::stream() << "This is not the primary shard for the database " << dbName - << ". Expected: " << primaryShardId << " Actual: " << thisShardId, + str::stream() << "This is not the primary shard for the database " + << dbName.toStringForErrorMsg() << ". Expected: " << primaryShardId + << " Actual: " << thisShardId, primaryShardId == thisShardId); } diff --git a/src/mongo/db/s/operation_sharding_state.cpp b/src/mongo/db/s/operation_sharding_state.cpp index 81c30addfdb..bae4f064b07 100644 --- a/src/mongo/db/s/operation_sharding_state.cpp +++ b/src/mongo/db/s/operation_sharding_state.cpp @@ -78,8 +78,8 @@ void OperationShardingState::setShardRole(OperationContext* opCtx, if (!emplaceResult.second) { uassert(640571, str::stream() << "Illegal attempt to change the expected database version for " - << nss.db() << " from " << tracker.v << " to " - << *databaseVersion, + << nss.dbName().toStringForErrorMsg() << " from " << tracker.v + << " to " << *databaseVersion, tracker.v == *databaseVersion); } invariant(++tracker.recursion > 0); diff --git a/src/mongo/db/s/shard_filtering_metadata_refresh.cpp b/src/mongo/db/s/shard_filtering_metadata_refresh.cpp index 7f447e2c773..15c9bf76b83 100644 --- a/src/mongo/db/s/shard_filtering_metadata_refresh.cpp +++ b/src/mongo/db/s/shard_filtering_metadata_refresh.cpp @@ -192,7 +192,8 @@ SharedSemiFuture<void> recoverRefreshDbVersion(OperationContext* opCtx, }) .onCompletion([=](Status status) { uassert(ErrorCodes::DatabaseMetadataRefreshCanceled, - str::stream() << "Canceled metadata refresh for database " << dbName, + str::stream() << "Canceled metadata refresh for database " + << dbName.toStringForErrorMsg(), !cancellationToken.isCanceled()); if (status.isOK() || status == ErrorCodes::NamespaceNotFound) { diff --git a/src/mongo/db/s/shardsvr_move_primary_command.cpp b/src/mongo/db/s/shardsvr_move_primary_command.cpp index 55170519081..d707cdfccc0 100644 --- a/src/mongo/db/s/shardsvr_move_primary_command.cpp +++ b/src/mongo/db/s/shardsvr_move_primary_command.cpp @@ -101,11 +101,12 @@ public: uassert( ErrorCodes::InvalidNamespace, - str::stream() << "invalid db name specified: " << dbName.db(), + str::stream() << "invalid db name specified: " << dbName.toStringForErrorMsg(), NamespaceString::validDBName(dbName, NamespaceString::DollarInDbNameBehavior::Allow)); uassert(ErrorCodes::InvalidOptions, - str::stream() << "Can't move primary for " << dbName.db() << " database", + str::stream() << "Can't move primary for " << dbName.toStringForErrorMsg() + << " database", !dbNss.isOnInternalDb()); uassert(ErrorCodes::InvalidOptions, diff --git a/src/mongo/db/shard_role.cpp b/src/mongo/db/shard_role.cpp index 0334bad3d13..f6145574a39 100644 --- a/src/mongo/db/shard_role.cpp +++ b/src/mongo/db/shard_role.cpp @@ -470,7 +470,8 @@ std::vector<ScopedCollectionOrViewAcquisition> acquireCollectionsOrViews( tassert(7300400, str::stream() << "Cannot acquire locks for collections across different databases ('" - << dbName << "' vs '" << nss.dbName() << "'", + << dbName.toStringForErrorMsg() << "' vs '" + << nss.dbName().toStringForErrorMsg() << "'", dbName == nss.dbName()); ar.second.dbLock = dbLock; diff --git a/src/mongo/s/stale_shard_version_helpers.cpp b/src/mongo/s/stale_shard_version_helpers.cpp index f1a45a3d409..aad9bf091ad 100644 --- a/src/mongo/s/stale_shard_version_helpers.cpp +++ b/src/mongo/s/stale_shard_version_helpers.cpp @@ -66,7 +66,8 @@ void checkErrorStatusAndMaxRetries(const Status& status, error_details::ErrorExtraInfoForImpl<ErrorCodes::StaleDbVersion>::type>(); invariant(staleInfo->getDb() == nss.db(), str::stream() << "StaleDbVersion error on unexpected database. Expected " - << nss.db() << ", received " << staleInfo->getDb()); + << nss.dbName().toStringForErrorMsg() << ", received " + << staleInfo->getDb()); // If the database version is stale, refresh its entry in the catalog cache. catalogCache->onStaleDatabaseVersion(staleInfo->getDb(), staleInfo->getVersionWanted()); |