summaryrefslogtreecommitdiff
path: root/jstests/libs
diff options
context:
space:
mode:
authorSophia Tan <sophia_tll@hotmail.com>2023-04-06 17:37:48 +0000
committerEvergreen Agent <no-reply@evergreen.mongodb.com>2023-04-06 21:19:24 +0000
commit84d1e35168bd516da60ce7a81b5871fef0181688 (patch)
tree2becc8fb8858f63511c190b8adc366a7d147fc0d /jstests/libs
parent88bd2c22da230a916028b6db6b7c5e36b753a110 (diff)
downloadmongo-84d1e35168bd516da60ce7a81b5871fef0181688.tar.gz
SERVER-74487 Always include tenant in db name attributes in error messages
Diffstat (limited to 'jstests/libs')
-rw-r--r--jstests/libs/override_methods/inject_dollar_tenant.js9
-rw-r--r--jstests/libs/override_methods/inject_security_token.js53
-rw-r--r--jstests/libs/override_methods/tenant_aware_response_checker.js119
3 files changed, 136 insertions, 45 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..9c9e9a0ef77
--- /dev/null
+++ b/jstests/libs/override_methods/tenant_aware_response_checker.js
@@ -0,0 +1,119 @@
+
+function getDbName(nss) {
+ if (nss.length === 0 || !nss.includes(".")) {
+ return nss;
+ }
+ return nss.split(".")[0];
+}
+
+function wordInString(str, word) {
+ let escapeRegExp = word.replace(/[.*+?^${}()|[\]\\]/g, "\\$&");
+ let regexp = new RegExp('\\b' + escapeRegExp + '\\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;
+ }
+
+ // Do not check change stream NoMatchingDocument error which does not contain prefixed db name.
+ if (errMsg.includes("Change stream was configured to require a post-image") ||
+ errMsg.includes("Change stream was configured to require a pre-image")) {
+ 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);
+ }
+ }
+}