diff options
Diffstat (limited to 'jstests/auth/getMore.js')
-rw-r--r-- | jstests/auth/getMore.js | 682 |
1 files changed, 336 insertions, 346 deletions
diff --git a/jstests/auth/getMore.js b/jstests/auth/getMore.js index d58c52a205c..4495d61200b 100644 --- a/jstests/auth/getMore.js +++ b/jstests/auth/getMore.js @@ -1,351 +1,341 @@ // Tests that a user can only run a getMore on a cursor that they created. // @tags: [requires_sharding] (function() { - "use strict"; - - // TODO SERVER-35447: Multiple users cannot be authenticated on one connection within a session. - TestData.disableImplicitSessions = true; - - function runTest(conn) { - let adminDB = conn.getDB("admin"); - let isMaster = adminDB.runCommand("ismaster"); - assert.commandWorked(isMaster); - const isMongos = (isMaster.msg === "isdbgrid"); - - // Create the admin user. - assert.commandWorked( - adminDB.runCommand({createUser: "admin", pwd: "admin", roles: ["root"]})); - assert.eq(1, adminDB.auth("admin", "admin")); - - // Set up the test database. - const testDBName = "auth_getMore"; - let testDB = adminDB.getSiblingDB(testDBName); - testDB.dropDatabase(); - assert.writeOK(testDB.foo.insert({_id: 0})); - assert.writeOK(testDB.foo.insert({_id: 1})); - assert.writeOK(testDB.foo.insert({_id: 2})); - - // - // Test that a user can only run a getMore on a cursor that they created. - // - - // Create two users, "Alice" and "Mallory". - assert.commandWorked( - testDB.runCommand({createUser: "Alice", pwd: "pwd", roles: ["readWrite"]})); - assert.commandWorked( - testDB.runCommand({createUser: "Mallory", pwd: "pwd", roles: ["readWrite"]})); - adminDB.logout(); - - // Test that "Mallory" cannot use a find cursor created by "Alice". - assert.eq(1, testDB.auth("Alice", "pwd")); - let res = assert.commandWorked(testDB.runCommand({find: "foo", batchSize: 0})); - let cursorId = res.cursor.id; - assert.neq(0, cursorId); - testDB.logout(); - assert.eq(1, testDB.auth("Mallory", "pwd")); - assert.commandFailedWithCode(testDB.runCommand({getMore: cursorId, collection: "foo"}), - ErrorCodes.Unauthorized, - "read from another user's find cursor"); - testDB.logout(); - - // Test that "Mallory" cannot use a legacy find cursor created by "Alice". - testDB.getMongo().forceReadMode("legacy"); - assert.eq(1, testDB.auth("Alice", "pwd")); - let cursor = testDB.foo.find().batchSize(2); +"use strict"; + +// TODO SERVER-35447: Multiple users cannot be authenticated on one connection within a session. +TestData.disableImplicitSessions = true; + +function runTest(conn) { + let adminDB = conn.getDB("admin"); + let isMaster = adminDB.runCommand("ismaster"); + assert.commandWorked(isMaster); + const isMongos = (isMaster.msg === "isdbgrid"); + + // Create the admin user. + assert.commandWorked(adminDB.runCommand({createUser: "admin", pwd: "admin", roles: ["root"]})); + assert.eq(1, adminDB.auth("admin", "admin")); + + // Set up the test database. + const testDBName = "auth_getMore"; + let testDB = adminDB.getSiblingDB(testDBName); + testDB.dropDatabase(); + assert.writeOK(testDB.foo.insert({_id: 0})); + assert.writeOK(testDB.foo.insert({_id: 1})); + assert.writeOK(testDB.foo.insert({_id: 2})); + + // + // Test that a user can only run a getMore on a cursor that they created. + // + + // Create two users, "Alice" and "Mallory". + assert.commandWorked( + testDB.runCommand({createUser: "Alice", pwd: "pwd", roles: ["readWrite"]})); + assert.commandWorked( + testDB.runCommand({createUser: "Mallory", pwd: "pwd", roles: ["readWrite"]})); + adminDB.logout(); + + // Test that "Mallory" cannot use a find cursor created by "Alice". + assert.eq(1, testDB.auth("Alice", "pwd")); + let res = assert.commandWorked(testDB.runCommand({find: "foo", batchSize: 0})); + let cursorId = res.cursor.id; + assert.neq(0, cursorId); + testDB.logout(); + assert.eq(1, testDB.auth("Mallory", "pwd")); + assert.commandFailedWithCode(testDB.runCommand({getMore: cursorId, collection: "foo"}), + ErrorCodes.Unauthorized, + "read from another user's find cursor"); + testDB.logout(); + + // Test that "Mallory" cannot use a legacy find cursor created by "Alice". + testDB.getMongo().forceReadMode("legacy"); + assert.eq(1, testDB.auth("Alice", "pwd")); + let cursor = testDB.foo.find().batchSize(2); + cursor.next(); + cursor.next(); + testDB.logout(); + assert.eq(1, testDB.auth("Mallory", "pwd")); + assert.throws(function() { cursor.next(); - cursor.next(); - testDB.logout(); - assert.eq(1, testDB.auth("Mallory", "pwd")); - assert.throws(function() { - cursor.next(); - }, [], "read from another user's legacy find cursor"); - testDB.logout(); - testDB.getMongo().forceReadMode("commands"); - - // Test that "Mallory" cannot use an aggregation cursor created by "Alice". - assert.eq(1, testDB.auth("Alice", "pwd")); - res = assert.commandWorked( - testDB.runCommand({aggregate: "foo", pipeline: [], cursor: {batchSize: 0}})); - cursorId = res.cursor.id; - assert.neq(0, cursorId); - testDB.logout(); - assert.eq(1, testDB.auth("Mallory", "pwd")); - assert.commandFailedWithCode(testDB.runCommand({getMore: cursorId, collection: "foo"}), - ErrorCodes.Unauthorized, - "read from another user's aggregate cursor"); - testDB.logout(); - - // Test that "Mallory" cannot use a listCollections cursor created by "Alice". - assert.eq(1, testDB.auth("Alice", "pwd")); - res = assert.commandWorked(testDB.runCommand({listCollections: 1, cursor: {batchSize: 0}})); - cursorId = res.cursor.id; - assert.neq(0, cursorId); - testDB.logout(); - assert.eq(1, testDB.auth("Mallory", "pwd")); - assert.commandFailedWithCode( - testDB.runCommand({getMore: cursorId, collection: "$cmd.listCollections"}), - ErrorCodes.Unauthorized, - "read from another user's listCollections cursor"); - testDB.logout(); - - // Test that "Mallory" cannot use a listIndexes cursor created by "Alice". - assert.eq(1, testDB.auth("Alice", "pwd")); - res = assert.commandWorked(testDB.runCommand({listIndexes: "foo", cursor: {batchSize: 0}})); - cursorId = res.cursor.id; - assert.neq(0, cursorId); - testDB.logout(); - assert.eq(1, testDB.auth("Mallory", "pwd")); - assert.commandFailedWithCode(testDB.runCommand({getMore: cursorId, collection: "foo"}), - ErrorCodes.Unauthorized, - "read from another user's listIndexes cursor"); - testDB.logout(); - - // - // Test that a user can call getMore on an indexStats cursor they created, unless the - // indexStats privilege has been revoked in the meantime. - // - - assert.eq(1, adminDB.auth("admin", "admin")); - assert.commandWorked(testDB.runCommand({ - createRole: "indexStatsOnly", - privileges: [{resource: {db: testDBName, collection: "foo"}, actions: ["indexStats"]}], - roles: [] - })); - assert.commandWorked( - testDB.runCommand({createUser: "Bob", pwd: "pwd", roles: ["indexStatsOnly"]})); - adminDB.logout(); - - assert.eq(1, testDB.auth("Bob", "pwd")); - res = assert.commandWorked(testDB.runCommand( - {aggregate: "foo", pipeline: [{$indexStats: {}}], cursor: {batchSize: 0}})); - cursorId = res.cursor.id; - assert.neq(0, cursorId); - assert.commandWorked(testDB.runCommand({getMore: cursorId, collection: "foo"})); - - res = assert.commandWorked(testDB.runCommand( - {aggregate: "foo", pipeline: [{$indexStats: {}}], cursor: {batchSize: 0}})); - cursorId = res.cursor.id; - assert.neq(0, cursorId); - testDB.logout(); - - assert.eq(1, adminDB.auth("admin", "admin")); - assert.commandWorked( - testDB.runCommand({revokeRolesFromUser: "Bob", roles: ["indexStatsOnly"]})); - adminDB.logout(); - - assert.eq(1, testDB.auth("Bob", "pwd")); - assert.commandFailedWithCode(testDB.runCommand({getMore: cursorId, collection: "foo"}), - ErrorCodes.Unauthorized, - "read from a cursor without required privileges"); - testDB.logout(); - - // - // Test that a user can call getMore on a listCollections cursor they created, unless the - // readWrite privilege has been revoked in the meantime. - // - - assert.eq(1, adminDB.auth("admin", "admin")); - - assert.commandWorked( - testDB.runCommand({createUser: "Tom", pwd: "pwd", roles: ["readWrite"]})); - adminDB.logout(); - - assert.eq(1, testDB.auth("Tom", "pwd")); - res = assert.commandWorked(testDB.runCommand({listCollections: 1, cursor: {batchSize: 0}})); - cursorId = res.cursor.id; - assert.neq(0, cursorId); - assert.commandWorked( - testDB.runCommand({getMore: cursorId, collection: "$cmd.listCollections"})); - - res = assert.commandWorked(testDB.runCommand({listCollections: 1, cursor: {batchSize: 0}})); - cursorId = res.cursor.id; - assert.neq(0, cursorId); - testDB.logout(); - - assert.eq(1, adminDB.auth("admin", "admin")); - assert.commandWorked(testDB.runCommand({revokeRolesFromUser: "Tom", roles: ["readWrite"]})); - adminDB.logout(); - - assert.eq(1, testDB.auth("Tom", "pwd")); - assert.commandFailedWithCode( - testDB.runCommand({getMore: cursorId, collection: "$cmd.listCollections"}), - ErrorCodes.Unauthorized, - "read from a cursor without required privileges"); - testDB.logout(); - // - // Test that a user can call getMore on a listIndexes cursor they created, unless the - // readWrite privilege has been revoked in the meantime. - // - - assert.eq(1, adminDB.auth("admin", "admin")); - - assert.commandWorked( - testDB.runCommand({createUser: "Bill", pwd: "pwd", roles: ["readWrite"]})); - adminDB.logout(); - - assert.eq(1, testDB.auth("Bill", "pwd")); - res = assert.commandWorked(testDB.runCommand({listIndexes: "foo", cursor: {batchSize: 0}})); - cursorId = res.cursor.id; - assert.neq(0, cursorId); - assert.commandWorked(testDB.runCommand({getMore: cursorId, collection: "foo"})); - - res = assert.commandWorked(testDB.runCommand({listIndexes: "foo", cursor: {batchSize: 0}})); - cursorId = res.cursor.id; - assert.neq(0, cursorId); - testDB.logout(); - - assert.eq(1, adminDB.auth("admin", "admin")); - assert.commandWorked( - testDB.runCommand({revokeRolesFromUser: "Bill", roles: ["readWrite"]})); - adminDB.logout(); - - assert.eq(1, testDB.auth("Bill", "pwd")); - assert.commandFailedWithCode(testDB.runCommand({getMore: cursorId, collection: "foo"}), - ErrorCodes.Unauthorized, - "read from a cursor without required privileges"); - testDB.logout(); - - // - // Test that a user can run a getMore on an aggregate cursor they created, unless some - // privileges required for the pipeline have been revoked in the meantime. - // - - assert.eq(1, testDB.auth("Alice", "pwd")); - res = assert.commandWorked(testDB.runCommand({ - aggregate: "foo", - pipeline: [{$match: {_id: 0}}, {$out: "out"}], - cursor: {batchSize: 0} - })); - cursorId = res.cursor.id; - assert.neq(0, cursorId); - assert.commandWorked(testDB.runCommand({getMore: cursorId, collection: "foo"})); - - res = assert.commandWorked(testDB.runCommand({ - aggregate: "foo", - pipeline: [{$match: {_id: 0}}, {$out: "out"}], - cursor: {batchSize: 0} - })); - cursorId = res.cursor.id; - assert.neq(0, cursorId); - testDB.logout(); - - assert.eq(1, adminDB.auth("admin", "admin")); - testDB.revokeRolesFromUser("Alice", ["readWrite"]); - testDB.grantRolesToUser("Alice", ["read"]); - adminDB.logout(); - - assert.eq(1, testDB.auth("Alice", "pwd")); - assert.commandFailedWithCode( - testDB.runCommand( - {aggregate: "foo", pipeline: [{$match: {_id: 0}}, {$out: "out"}], cursor: {}}), - ErrorCodes.Unauthorized, - "user should no longer have write privileges"); - assert.commandFailedWithCode(testDB.runCommand({getMore: cursorId, collection: "foo"}), - ErrorCodes.Unauthorized, - "wrote from a cursor without required privileges"); - testDB.logout(); - - // - // Test that if there were multiple users authenticated when the cursor was created, then at - // least one of them must be authenticated in order to run getMore on the cursor. - // - - assert.eq(1, adminDB.auth("admin", "admin")); - assert.writeOK(testDB.bar.insert({_id: 0})); - - // Create a user "fooUser" on the test database that can read the "foo" collection. - assert.commandWorked(testDB.runCommand({ - createRole: "readFoo", - privileges: [{resource: {db: testDBName, collection: "foo"}, actions: ["find"]}], - roles: [] - })); - assert.commandWorked( - testDB.runCommand({createUser: "fooUser", pwd: "pwd", roles: ["readFoo"]})); - - // Create a user "fooBarUser" on the admin database that can read the "foo" and "bar" - // collections. - assert.commandWorked(adminDB.runCommand({ - createRole: "readFooBar", - privileges: [ - {resource: {db: testDBName, collection: "foo"}, actions: ["find"]}, - {resource: {db: testDBName, collection: "bar"}, actions: ["find"]} - ], - roles: [] - })); - assert.commandWorked( - adminDB.runCommand({createUser: "fooBarUser", pwd: "pwd", roles: ["readFooBar"]})); - - adminDB.logout(); - - // Test that a cursor created by "fooUser" and "fooBarUser" can be used by "fooUser". - assert.eq(1, testDB.auth("fooUser", "pwd")); - assert.eq(1, adminDB.auth("fooBarUser", "pwd")); - res = assert.commandWorked(testDB.runCommand({find: "foo", batchSize: 0})); - cursorId = res.cursor.id; - assert.neq(0, cursorId); - adminDB.logout(); - assert.commandWorked(testDB.runCommand({getMore: cursorId, collection: "foo"})); - testDB.logout(); - - // Test that a cursor created by "fooUser" and "fooBarUser" cannot be used by "fooUser" if - // "fooUser" does not have the privilege to read the collection. - assert.eq(1, testDB.auth("fooUser", "pwd")); - assert.eq(1, adminDB.auth("fooBarUser", "pwd")); - res = assert.commandWorked(testDB.runCommand({find: "bar", batchSize: 0})); - cursorId = res.cursor.id; - assert.neq(0, cursorId); - adminDB.logout(); - assert.commandFailedWithCode(testDB.runCommand({getMore: cursorId, collection: "bar"}), - ErrorCodes.Unauthorized, - "read from a cursor without required privileges"); - testDB.logout(); - - // Test that an aggregate cursor created by "fooUser" and "fooBarUser" cannot be used by - // "fooUser" if "fooUser" does not have all privileges required by the pipeline. - assert.eq(1, testDB.auth("fooUser", "pwd")); - assert.eq(1, adminDB.auth("fooBarUser", "pwd")); - res = assert.commandWorked(testDB.runCommand({ - aggregate: "foo", - pipeline: [ - {$match: {_id: 0}}, - {$lookup: {from: "bar", localField: "_id", foreignField: "_id", as: "bar"}} - ], - cursor: {batchSize: 0} - })); - cursorId = res.cursor.id; - assert.neq(0, cursorId); - assert.commandWorked(testDB.runCommand({getMore: cursorId, collection: "foo"})); - - res = assert.commandWorked(testDB.runCommand({ - aggregate: "foo", - pipeline: [ - {$match: {_id: 0}}, - {$lookup: {from: "bar", localField: "_id", foreignField: "_id", as: "bar"}} - ], - cursor: {batchSize: 0} - })); - cursorId = res.cursor.id; - assert.neq(0, cursorId); - adminDB.logout(); - assert.commandFailedWithCode(testDB.runCommand({getMore: cursorId, collection: "foo"}), - ErrorCodes.Unauthorized, - "read from a cursor without required privileges"); - testDB.logout(); - } - - // Run the test on a standalone. - let conn = MongoRunner.runMongod({auth: "", bind_ip: "127.0.0.1"}); - runTest(conn); - MongoRunner.stopMongod(conn); - - // Run the test on a sharded cluster. - // TODO: Remove 'shardAsReplicaSet: false' when SERVER-32672 is fixed. - let cluster = new ShardingTest({ - shards: 1, - mongos: 1, - keyFile: "jstests/libs/key1", - other: {shardOptions: {auth: ""}, shardAsReplicaSet: false} - }); - runTest(cluster); - cluster.stop(); + }, [], "read from another user's legacy find cursor"); + testDB.logout(); + testDB.getMongo().forceReadMode("commands"); + + // Test that "Mallory" cannot use an aggregation cursor created by "Alice". + assert.eq(1, testDB.auth("Alice", "pwd")); + res = assert.commandWorked( + testDB.runCommand({aggregate: "foo", pipeline: [], cursor: {batchSize: 0}})); + cursorId = res.cursor.id; + assert.neq(0, cursorId); + testDB.logout(); + assert.eq(1, testDB.auth("Mallory", "pwd")); + assert.commandFailedWithCode(testDB.runCommand({getMore: cursorId, collection: "foo"}), + ErrorCodes.Unauthorized, + "read from another user's aggregate cursor"); + testDB.logout(); + + // Test that "Mallory" cannot use a listCollections cursor created by "Alice". + assert.eq(1, testDB.auth("Alice", "pwd")); + res = assert.commandWorked(testDB.runCommand({listCollections: 1, cursor: {batchSize: 0}})); + cursorId = res.cursor.id; + assert.neq(0, cursorId); + testDB.logout(); + assert.eq(1, testDB.auth("Mallory", "pwd")); + assert.commandFailedWithCode( + testDB.runCommand({getMore: cursorId, collection: "$cmd.listCollections"}), + ErrorCodes.Unauthorized, + "read from another user's listCollections cursor"); + testDB.logout(); + + // Test that "Mallory" cannot use a listIndexes cursor created by "Alice". + assert.eq(1, testDB.auth("Alice", "pwd")); + res = assert.commandWorked(testDB.runCommand({listIndexes: "foo", cursor: {batchSize: 0}})); + cursorId = res.cursor.id; + assert.neq(0, cursorId); + testDB.logout(); + assert.eq(1, testDB.auth("Mallory", "pwd")); + assert.commandFailedWithCode(testDB.runCommand({getMore: cursorId, collection: "foo"}), + ErrorCodes.Unauthorized, + "read from another user's listIndexes cursor"); + testDB.logout(); + + // + // Test that a user can call getMore on an indexStats cursor they created, unless the + // indexStats privilege has been revoked in the meantime. + // + + assert.eq(1, adminDB.auth("admin", "admin")); + assert.commandWorked(testDB.runCommand({ + createRole: "indexStatsOnly", + privileges: [{resource: {db: testDBName, collection: "foo"}, actions: ["indexStats"]}], + roles: [] + })); + assert.commandWorked( + testDB.runCommand({createUser: "Bob", pwd: "pwd", roles: ["indexStatsOnly"]})); + adminDB.logout(); + + assert.eq(1, testDB.auth("Bob", "pwd")); + res = assert.commandWorked(testDB.runCommand( + {aggregate: "foo", pipeline: [{$indexStats: {}}], cursor: {batchSize: 0}})); + cursorId = res.cursor.id; + assert.neq(0, cursorId); + assert.commandWorked(testDB.runCommand({getMore: cursorId, collection: "foo"})); + + res = assert.commandWorked(testDB.runCommand( + {aggregate: "foo", pipeline: [{$indexStats: {}}], cursor: {batchSize: 0}})); + cursorId = res.cursor.id; + assert.neq(0, cursorId); + testDB.logout(); + + assert.eq(1, adminDB.auth("admin", "admin")); + assert.commandWorked( + testDB.runCommand({revokeRolesFromUser: "Bob", roles: ["indexStatsOnly"]})); + adminDB.logout(); + + assert.eq(1, testDB.auth("Bob", "pwd")); + assert.commandFailedWithCode(testDB.runCommand({getMore: cursorId, collection: "foo"}), + ErrorCodes.Unauthorized, + "read from a cursor without required privileges"); + testDB.logout(); + + // + // Test that a user can call getMore on a listCollections cursor they created, unless the + // readWrite privilege has been revoked in the meantime. + // + + assert.eq(1, adminDB.auth("admin", "admin")); + + assert.commandWorked(testDB.runCommand({createUser: "Tom", pwd: "pwd", roles: ["readWrite"]})); + adminDB.logout(); + + assert.eq(1, testDB.auth("Tom", "pwd")); + res = assert.commandWorked(testDB.runCommand({listCollections: 1, cursor: {batchSize: 0}})); + cursorId = res.cursor.id; + assert.neq(0, cursorId); + assert.commandWorked( + testDB.runCommand({getMore: cursorId, collection: "$cmd.listCollections"})); + + res = assert.commandWorked(testDB.runCommand({listCollections: 1, cursor: {batchSize: 0}})); + cursorId = res.cursor.id; + assert.neq(0, cursorId); + testDB.logout(); + + assert.eq(1, adminDB.auth("admin", "admin")); + assert.commandWorked(testDB.runCommand({revokeRolesFromUser: "Tom", roles: ["readWrite"]})); + adminDB.logout(); + + assert.eq(1, testDB.auth("Tom", "pwd")); + assert.commandFailedWithCode( + testDB.runCommand({getMore: cursorId, collection: "$cmd.listCollections"}), + ErrorCodes.Unauthorized, + "read from a cursor without required privileges"); + testDB.logout(); + // + // Test that a user can call getMore on a listIndexes cursor they created, unless the + // readWrite privilege has been revoked in the meantime. + // + + assert.eq(1, adminDB.auth("admin", "admin")); + + assert.commandWorked(testDB.runCommand({createUser: "Bill", pwd: "pwd", roles: ["readWrite"]})); + adminDB.logout(); + + assert.eq(1, testDB.auth("Bill", "pwd")); + res = assert.commandWorked(testDB.runCommand({listIndexes: "foo", cursor: {batchSize: 0}})); + cursorId = res.cursor.id; + assert.neq(0, cursorId); + assert.commandWorked(testDB.runCommand({getMore: cursorId, collection: "foo"})); + + res = assert.commandWorked(testDB.runCommand({listIndexes: "foo", cursor: {batchSize: 0}})); + cursorId = res.cursor.id; + assert.neq(0, cursorId); + testDB.logout(); + + assert.eq(1, adminDB.auth("admin", "admin")); + assert.commandWorked(testDB.runCommand({revokeRolesFromUser: "Bill", roles: ["readWrite"]})); + adminDB.logout(); + + assert.eq(1, testDB.auth("Bill", "pwd")); + assert.commandFailedWithCode(testDB.runCommand({getMore: cursorId, collection: "foo"}), + ErrorCodes.Unauthorized, + "read from a cursor without required privileges"); + testDB.logout(); + + // + // Test that a user can run a getMore on an aggregate cursor they created, unless some + // privileges required for the pipeline have been revoked in the meantime. + // + + assert.eq(1, testDB.auth("Alice", "pwd")); + res = assert.commandWorked(testDB.runCommand( + {aggregate: "foo", pipeline: [{$match: {_id: 0}}, {$out: "out"}], cursor: {batchSize: 0}})); + cursorId = res.cursor.id; + assert.neq(0, cursorId); + assert.commandWorked(testDB.runCommand({getMore: cursorId, collection: "foo"})); + + res = assert.commandWorked(testDB.runCommand( + {aggregate: "foo", pipeline: [{$match: {_id: 0}}, {$out: "out"}], cursor: {batchSize: 0}})); + cursorId = res.cursor.id; + assert.neq(0, cursorId); + testDB.logout(); + + assert.eq(1, adminDB.auth("admin", "admin")); + testDB.revokeRolesFromUser("Alice", ["readWrite"]); + testDB.grantRolesToUser("Alice", ["read"]); + adminDB.logout(); + + assert.eq(1, testDB.auth("Alice", "pwd")); + assert.commandFailedWithCode( + testDB.runCommand( + {aggregate: "foo", pipeline: [{$match: {_id: 0}}, {$out: "out"}], cursor: {}}), + ErrorCodes.Unauthorized, + "user should no longer have write privileges"); + assert.commandFailedWithCode(testDB.runCommand({getMore: cursorId, collection: "foo"}), + ErrorCodes.Unauthorized, + "wrote from a cursor without required privileges"); + testDB.logout(); + + // + // Test that if there were multiple users authenticated when the cursor was created, then at + // least one of them must be authenticated in order to run getMore on the cursor. + // + + assert.eq(1, adminDB.auth("admin", "admin")); + assert.writeOK(testDB.bar.insert({_id: 0})); + + // Create a user "fooUser" on the test database that can read the "foo" collection. + assert.commandWorked(testDB.runCommand({ + createRole: "readFoo", + privileges: [{resource: {db: testDBName, collection: "foo"}, actions: ["find"]}], + roles: [] + })); + assert.commandWorked( + testDB.runCommand({createUser: "fooUser", pwd: "pwd", roles: ["readFoo"]})); + + // Create a user "fooBarUser" on the admin database that can read the "foo" and "bar" + // collections. + assert.commandWorked(adminDB.runCommand({ + createRole: "readFooBar", + privileges: [ + {resource: {db: testDBName, collection: "foo"}, actions: ["find"]}, + {resource: {db: testDBName, collection: "bar"}, actions: ["find"]} + ], + roles: [] + })); + assert.commandWorked( + adminDB.runCommand({createUser: "fooBarUser", pwd: "pwd", roles: ["readFooBar"]})); + + adminDB.logout(); + + // Test that a cursor created by "fooUser" and "fooBarUser" can be used by "fooUser". + assert.eq(1, testDB.auth("fooUser", "pwd")); + assert.eq(1, adminDB.auth("fooBarUser", "pwd")); + res = assert.commandWorked(testDB.runCommand({find: "foo", batchSize: 0})); + cursorId = res.cursor.id; + assert.neq(0, cursorId); + adminDB.logout(); + assert.commandWorked(testDB.runCommand({getMore: cursorId, collection: "foo"})); + testDB.logout(); + + // Test that a cursor created by "fooUser" and "fooBarUser" cannot be used by "fooUser" if + // "fooUser" does not have the privilege to read the collection. + assert.eq(1, testDB.auth("fooUser", "pwd")); + assert.eq(1, adminDB.auth("fooBarUser", "pwd")); + res = assert.commandWorked(testDB.runCommand({find: "bar", batchSize: 0})); + cursorId = res.cursor.id; + assert.neq(0, cursorId); + adminDB.logout(); + assert.commandFailedWithCode(testDB.runCommand({getMore: cursorId, collection: "bar"}), + ErrorCodes.Unauthorized, + "read from a cursor without required privileges"); + testDB.logout(); + + // Test that an aggregate cursor created by "fooUser" and "fooBarUser" cannot be used by + // "fooUser" if "fooUser" does not have all privileges required by the pipeline. + assert.eq(1, testDB.auth("fooUser", "pwd")); + assert.eq(1, adminDB.auth("fooBarUser", "pwd")); + res = assert.commandWorked(testDB.runCommand({ + aggregate: "foo", + pipeline: [ + {$match: {_id: 0}}, + {$lookup: {from: "bar", localField: "_id", foreignField: "_id", as: "bar"}} + ], + cursor: {batchSize: 0} + })); + cursorId = res.cursor.id; + assert.neq(0, cursorId); + assert.commandWorked(testDB.runCommand({getMore: cursorId, collection: "foo"})); + + res = assert.commandWorked(testDB.runCommand({ + aggregate: "foo", + pipeline: [ + {$match: {_id: 0}}, + {$lookup: {from: "bar", localField: "_id", foreignField: "_id", as: "bar"}} + ], + cursor: {batchSize: 0} + })); + cursorId = res.cursor.id; + assert.neq(0, cursorId); + adminDB.logout(); + assert.commandFailedWithCode(testDB.runCommand({getMore: cursorId, collection: "foo"}), + ErrorCodes.Unauthorized, + "read from a cursor without required privileges"); + testDB.logout(); +} + +// Run the test on a standalone. +let conn = MongoRunner.runMongod({auth: "", bind_ip: "127.0.0.1"}); +runTest(conn); +MongoRunner.stopMongod(conn); + +// Run the test on a sharded cluster. +// TODO: Remove 'shardAsReplicaSet: false' when SERVER-32672 is fixed. +let cluster = new ShardingTest({ + shards: 1, + mongos: 1, + keyFile: "jstests/libs/key1", + other: {shardOptions: {auth: ""}, shardAsReplicaSet: false} +}); +runTest(cluster); +cluster.stop(); }()); |