0
0
mirror of https://github.com/mongodb/mongo.git synced 2024-12-01 01:21:03 +01:00
mongodb/jstests/auth/commands_user_defined_roles.js
2019-07-27 11:02:23 -04:00

223 lines
7.9 KiB
JavaScript

/*
Exhaustive test for authorization of commands with user-defined roles.
The test logic implemented here operates on the test cases defined
in jstests/auth/commands.js.
@tags: [requires_sharding]
*/
// TODO SERVER-35447: This test involves killing all sessions, which will not work as expected if
// the kill command is sent with an implicit session.
TestData.disableImplicitSessions = true;
// constants
var testUser = "userDefinedRolesTestUser";
var testRole = "userDefinedRolesTestRole";
load("jstests/auth/lib/commands_lib.js");
/**
* Run the command specified in 't' with the privileges specified in 'privileges'.
*/
function testProperAuthorization(conn, t, testcase, privileges) {
var out = "";
var runOnDb = conn.getDB(testcase.runOnDb);
var firstDb = conn.getDB(firstDbName);
var adminDb = conn.getDB(adminDbName);
var state = authCommandsLib.setup(conn, t, runOnDb);
adminDb.auth("admin", "password");
assert.commandWorked(adminDb.runCommand({updateRole: testRole, privileges: privileges}));
adminDb.logout();
assert(adminDb.auth(testUser, "password"));
authCommandsLib.authenticatedSetup(t, runOnDb);
var command = t.command;
if (typeof (command) === "function") {
command = t.command(state, testcase.commandArgs);
}
var res = runOnDb.runCommand(command);
if (!testcase.expectFail && res.ok != 1 && res.code != commandNotSupportedCode) {
// don't error if the test failed with code commandNotSupported since
// some storage engines (e.g wiredTiger) don't support some commands (e.g. touch)
out = "command failed with " + tojson(res) + " on db " + testcase.runOnDb +
" with privileges " + tojson(privileges);
} else if (testcase.expectFail && res.code == authErrCode) {
out = "expected authorization success" +
" but received " + tojson(res) + " on db " + testcase.runOnDb + " with privileges " +
tojson(privileges);
}
firstDb.logout();
authCommandsLib.teardown(conn, t, runOnDb, res);
return out;
}
function testInsufficientPrivileges(conn, t, testcase, privileges) {
var out = "";
var runOnDb = conn.getDB(testcase.runOnDb);
var firstDb = conn.getDB(firstDbName);
var adminDb = conn.getDB(adminDbName);
var state = authCommandsLib.setup(conn, t, runOnDb);
adminDb.auth("admin", "password");
assert.commandWorked(adminDb.runCommand({updateRole: testRole, privileges: privileges}));
adminDb.logout();
assert(adminDb.auth(testUser, "password"));
authCommandsLib.authenticatedSetup(t, runOnDb);
var command = t.command;
if (typeof (command) === "function") {
command = t.command(state, testcase.commandArgs);
}
var res = runOnDb.runCommand(command);
if (res.ok == 1 || res.code != authErrCode) {
out = "expected authorization failure " +
" but received " + tojson(res) + " with privileges " + tojson(privileges);
}
firstDb.logout();
authCommandsLib.teardown(conn, t, runOnDb);
return out;
}
function runOneTest(conn, t) {
var failures = [];
var msg;
for (var i = 0; i < t.testcases.length; i++) {
var testcase = t.testcases[i];
if (!("privileges" in testcase)) {
continue;
}
if (testcase.expectAuthzFailure) {
msg = testInsufficientPrivileges(conn, t, testcase, testcase.privileges);
if (msg) {
failures.push(t.testname + ": " + msg);
}
continue;
}
if ((testcase.privileges.length == 1 && testcase.privileges[0].actions.length > 1) ||
testcase.privileges.length > 1) {
for (var j = 0; j < testcase.privileges.length; j++) {
var p = testcase.privileges[j];
var resource = p.resource;
var actions = p.actions;
// A particular privilege can explicitly specify that it should not be removed when
// testing for authorization failure. This accommodates special-case behavior for
// views in conjunction with the create and collMod commands.
if (p.removeWhenTestingAuthzFailure === false) {
continue;
}
for (var k = 0; k < actions.length; k++) {
var privDoc = {resource: resource, actions: [actions[k]]};
msg = testInsufficientPrivileges(conn, t, testcase, [privDoc]);
if (msg) {
failures.push(t.testname + ": " + msg);
}
}
}
}
// Test for proper authorization with the privileges specified in the test case.
msg = testProperAuthorization(conn, t, testcase, testcase.privileges);
if (msg) {
failures.push(t.testname + ": " + msg);
}
var specialResource = function(resource) {
if (!resource)
return true;
// Tests which use {db: "local", collection: "oplog.rs"} will not work with
// {db: "", collection: "oplog.rs"}. oplog.rs is special, and does not match with
// forDatabaseName or anyNormalResource ResourcePatterns. The same is true of
// oplog.$main, but oplog.$main is also an illegal collection name on any database
// other than local. The other collections checked for here in the local database have
// the same property as oplog.rs.
return !resource.db || !resource.collection ||
resource.collection.startsWith("system.") || resource.db == "local";
};
// Test for proper authorization with the test case's privileges where non-system
// collections are modified to be the empty string.
msg = testProperAuthorization(conn, t, testcase, testcase.privileges.map(function(priv) {
// Make a copy of the privilege so as not to modify the original array.
var modifiedPrivilege = Object.extend({}, priv, true);
if (modifiedPrivilege.resource.collection && !specialResource(priv.resource)) {
modifiedPrivilege.resource.collection = "";
}
return modifiedPrivilege;
}));
if (msg) {
failures.push(t.testname + ": " + msg);
}
// Test for proper authorization with the test case's privileges where the database is the
// empty string.
msg = testProperAuthorization(conn, t, testcase, testcase.privileges.map(function(priv) {
// Make a copy of the privilege so as not to modify the original array.
var modifiedPrivilege = Object.extend({}, priv, true);
if (!specialResource(priv.resource)) {
modifiedPrivilege.resource.db = "";
}
return modifiedPrivilege;
}));
if (msg) {
failures.push(t.testname + ": " + msg);
}
}
return failures;
}
function createUsers(conn) {
var adminDb = conn.getDB(adminDbName);
var firstDb = conn.getDB(firstDbName);
adminDb.createUser({user: "admin", pwd: "password", roles: ["__system"]});
assert(adminDb.auth("admin", "password"));
assert.commandWorked(adminDb.runCommand({createRole: testRole, privileges: [], roles: []}));
assert.commandWorked(adminDb.runCommand(
{createUser: testUser, pwd: "password", roles: [{role: testRole, db: adminDbName}]}));
adminDb.logout();
}
var opts = {auth: "", enableExperimentalStorageDetailsCmd: ""};
var impls = {createUsers: createUsers, runOneTest: runOneTest};
// run all tests standalone
var conn = MongoRunner.runMongod(opts);
authCommandsLib.runTests(conn, impls);
MongoRunner.stopMongod(conn);
// run all tests sharded
// TODO: Remove 'shardAsReplicaSet: false' when SERVER-32672 is fixed.
conn = new ShardingTest({
shards: 2,
mongos: 1,
keyFile: "jstests/libs/key1",
other: {shardOptions: opts, shardAsReplicaSet: false}
});
authCommandsLib.runTests(conn, impls);
conn.stop();