summaryrefslogtreecommitdiff
path: root/jstests/libs/override_methods/tenant_aware_response_checker.js
blob: 9c9e9a0ef774513c1f6714b6224c3e78eba0d3e4 (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
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);
        }
    }
}