diff options
author | Sophia Tan <sophia_tll@hotmail.com> | 2023-04-06 17:37:48 +0000 |
---|---|---|
committer | Evergreen Agent <no-reply@evergreen.mongodb.com> | 2023-04-06 21:19:24 +0000 |
commit | 84d1e35168bd516da60ce7a81b5871fef0181688 (patch) | |
tree | 2becc8fb8858f63511c190b8adc366a7d147fc0d /jstests/libs | |
parent | 88bd2c22da230a916028b6db6b7c5e36b753a110 (diff) | |
download | mongo-84d1e35168bd516da60ce7a81b5871fef0181688.tar.gz |
SERVER-74487 Always include tenant in db name attributes in error messages
Diffstat (limited to 'jstests/libs')
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); + } + } +} |