summaryrefslogtreecommitdiff
path: root/jstests/concurrency/fsm_workloads/auth_privilege_consistency.js
blob: 054f0c5ca15bfd945d48096dfb1ee9acdfe3222f (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
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
'use strict';

/**
 * auth_privilege_consistency.js
 *
 * Validate user cache invalidation upon subordinate role removal.
 */
load('jstests/concurrency/fsm_workload_helpers/drop_utils.js');  // for dropRoles

var $config = (function() {
    const kTestNamePrefix = 'auth_privilege_consistency';
    const kTestUserName = kTestNamePrefix + '_user';
    const kTestUserPassword = 'secret';
    const kTestRoleNamePrefix = kTestNamePrefix + '_role_';

    const states = (function() {
        let roleName = kTestRoleNamePrefix;
        let roleWithDB = {};
        let privilege = {actions: ['insert', 'update', 'remove', 'find']};
        let RSnodes = [];

        function getTestUser(node, dbName) {
            const users = assert
                              .commandWorked(node.getDB(dbName).runCommand(
                                  {usersInfo: kTestUserName, showPrivileges: 1}))
                              .users;
            assert.eq(users.length, 1, tojson(users));
            return users[0];
        }

        return {
            init: function(db, collName) {},

            mutateInit: function(db, collName) {
                privilege.resource = {db: db.getName(), collection: ''};
                roleName += this.tid;
                roleWithDB = {role: roleName, db: db.getName()};
                db.createRole({role: roleName, privileges: [privilege], roles: []});
            },

            mutate: function(db, collName) {
                // Revoke privs from intermediate role,
                // then give that, now empty, role to the user.
                db.revokePrivilegesFromRole(roleName, [privilege]);
                db.grantRolesToUser(kTestUserName, [roleWithDB]);

                // Take the role away from the user, and give it privs.
                db.revokeRolesFromUser(kTestUserName, [roleWithDB]);
                db.grantPrivilegesToRole(roleName, [privilege]);
            },

            observeInit: function(db, collName) {
                // Drop privileges to normal user.
                // The workload runner disallows `db.logout()` for reasons we're okay with.
                assert.commandWorked(db.runCommand({logout: 1}));
                assert(db.auth(kTestUserName, kTestUserPassword));

                // Setup a connection to every member host if this is a replica set
                // so that we can confirm secondary state during observe().
                const isMaster = assert.commandWorked(db.runCommand({isMaster: 1}));
                jsTest.log('isMaster: ' + tojson(isMaster));
                if (isMaster.hosts) {
                    const allHosts = isMaster.hosts.concat(isMaster.passives);
                    allHosts.forEach(function(node) {
                        if (node === isMaster.me) {
                            // Reuse existing connection for db's connection.
                            const conn = db.getMongo();
                            RSnodes.push(conn);
                            return;
                        }

                        // Create a new connection to any node which isn't "me".
                        const conn = new Mongo(node);
                        assert(conn);
                        conn.setSlaveOk();
                        RSnodes.push(conn);
                    });

                    // Wait for user to replicate to all nodes.
                    RSnodes.forEach(function(node) {
                        assert.soon(function() {
                            try {
                                getTestUser(node, db.getName());
                                return true;
                            } catch (e) {
                                return false;
                            }
                        });
                    });
                }
            },

            observe: function(db, collName) {
                // Make sure we never appear to have any privileges,
                // but that we remain authenticated.
                const info =
                    assert.commandWorked(db.runCommand({connectionStatus: 1, showPrivileges: true}))
                        .authInfo;
                assert.eq(info.authenticatedUsers.length, 1, tojson(info));
                assert.eq(info.authenticatedUsers[0].user, kTestUserName, tojson(info));
                assert.eq(info.authenticatedUserPrivileges.length, 0, tojson(info));

                // If this is a ReplSet, iterate nodes and check usersInfo.
                RSnodes.forEach(function(node) {
                    const user = getTestUser(node, db.getName());
                    jsTest.log(node + ' userInfo: ' + tojson(user));
                    assert.eq(user.user, kTestUserName, tojson(user));
                    assert.eq(user.inheritedPrivileges.length, 0, tojson(user));
                });
            },
        };
    })();

    const transitions = {
        init: {mutateInit: 0.8, observeInit: 0.2},
        mutateInit: {mutate: 1},
        mutate: {mutate: 1},
        observeInit: {observe: 1},
        observe: {observe: 1},
    };

    function setup(db, collName, cluster) {
        db.createUser({user: kTestUserName, pwd: kTestUserPassword, roles: []});
    }

    function teardown(db, collName, cluster) {
        const pattern = new RegExp('^' + kTestRoleNamePrefix + '\\d+$');
        dropRoles(db, pattern);
        db.dropUser(kTestUserName);
    }

    return {
        threadCount: 50,
        iterations: 50,
        states: states,
        transitions: transitions,
        setup: setup,
        teardown: teardown,
    };
})();