summaryrefslogtreecommitdiff
path: root/jstests/auth/user_roles_find_agg.js
blob: 860ac3f8cc50c4ea9ce31122e72e21100a2d3327 (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
// Tests that $$USER_ROLES works as expected in a find command and an aggregate command (on both a
// standalone mongod and a sharded cluster).
// @tags: [requires_fcv_70]

(function() {
"use strict";
load("jstests/concurrency/fsm_workload_helpers/server_types.js");  // For isMongos.

const dbName = "test";
const findCollName = "find_coll";
const aggCollName = "team_budget";

function runFind(db) {
    // We need to create a collection for the following findOne() to run upon and we need to insert
    // a document in that collection so that the findOne() actually has a document to project the
    // $$USER_ROLES onto.
    let coll = db.getCollection(findCollName);
    assert.commandWorked(coll.insert({a: 1}));

    let result = coll.findOne({}, {myRoles: "$$USER_ROLES"});
    assert.eq(
        [
            {_id: "admin.readWriteAnyDatabase", role: "readWriteAnyDatabase", db: "admin"},
            {_id: "test.read", role: "read", db: dbName}
        ],
        result.myRoles);
}

function runAgg(db) {
    let engDoc = {
        _id: 0,
        allowedRoles: ["eng-app-prod", "eng-app-stg", "read"],
        comment: "only for engineering team",
        teamMembers: ["John", "Ashley", "Gina"],
        yearlyEduBudget: 15000,
        yearlyTnEBudget: 2000
    };

    let salesDoc = {
        _id: 1,
        allowedRoles: ["sales-person"],
        comment: "only for sales team",
        salesWins: 1000,
    };

    let coll = db.getCollection(aggCollName);
    assert.commandWorked(coll.insertMany([engDoc, salesDoc]));

    // This will match the documents where the intersection between the allowedRoles field and the
    // user"s roles is not empty, i.e. the user"s role allows them to see the document in the
    // results. In this case, only the engDoc has the the "read" role that was assigned to the user,
    // so only the engDoc will appear in the results.
    let pipeline = [{
        $match:
            {$expr: {$not: {$eq: [{$setIntersection: ["$allowedRoles", "$$USER_ROLES.role"]}, []]}}}
    }];
    let res = coll.aggregate(pipeline).toArray();
    assert.eq([engDoc], res);
}

function runTest(conn, shardingTest = null) {
    // Create a user on the admin database with the root role so that we can create users with other
    // roles to other databases.
    let admin = conn.getDB("admin");
    assert.commandWorked(admin.runCommand({createUser: "admin", pwd: "admin", roles: ["root"]}));
    admin.auth("admin", "admin");

    if (shardingTest) {
        // Shard the two collections that will be used in the find and aggregate commands.
        assert.commandWorked(st.getDB("admin").runCommand({enableSharding: dbName}));
        st.shardColl(st.getDB(dbName).getCollection(findCollName), {_id: 1});
        st.shardColl(st.getDB(dbName).getCollection(aggCollName), {_id: 1});
    }

    const db = conn.getDB(dbName);

    // Create a user that has roles on more than one database. The readWriteAnyDatabase is necessary
    // for the inserts that follow to work.
    assert.commandWorked(db.runCommand({
        createUser: "user",
        pwd: "pwd",
        roles: [{role: "readWriteAnyDatabase", db: "admin"}, {role: "read", db: dbName}]
    }));

    // Logout of the admin user so that we can log into the other user so we can access those roles
    // with $$USER_ROLES below.
    admin.logout();
    db.auth("user", "pwd");

    runFind(db);

    runAgg(db);

    db.logout();
}

jsTest.log("Test standalone");
const mongod = MongoRunner.runMongod({auth: ""});
runTest(mongod);
MongoRunner.stopMongod(mongod);

jsTest.log("Test sharded cluster");
const st = new ShardingTest({
    mongos: 1,
    config: 1,
    shards: 2,
    keyFile: 'jstests/libs/key1',
});

runTest(st.s, st);
st.stop();
}());