mirror of https://github.com/mongodb/mongo
527 lines
17 KiB
JavaScript
527 lines
17 KiB
JavaScript
/**
|
|
* This file tests basic authorization for different roles in stand alone and sharded
|
|
* environment. This file covers all types of operations except commands.
|
|
*/
|
|
|
|
/**
|
|
* Data structure that contains all the users that are going to be used in the tests.
|
|
* The structure is as follows:
|
|
* 1st level field name: database names.
|
|
* 2nd level field name: user names.
|
|
* 3rd level is an object that has the format:
|
|
* { pwd: <password>, roles: [<list of roles>] }
|
|
* @tags: [requires_sharding]
|
|
*/
|
|
var AUTH_INFO = {
|
|
admin: {
|
|
root: {pwd: 'root', roles: ['root']},
|
|
cluster: {pwd: 'cluster', roles: ['clusterAdmin']},
|
|
anone: {pwd: 'none', roles: []},
|
|
aro: {pwd: 'ro', roles: ['read']},
|
|
arw: {pwd: 'rw', roles: ['readWrite']},
|
|
aadmin: {pwd: 'admin', roles: ['dbAdmin']},
|
|
auadmin: {pwd: 'uadmin', roles: ['userAdmin']},
|
|
any_ro: {pwd: 'ro', roles: ['readAnyDatabase']},
|
|
any_rw: {pwd: 'rw', roles: ['readWriteAnyDatabase']},
|
|
any_admin: {pwd: 'admin', roles: ['dbAdminAnyDatabase']},
|
|
any_uadmin: {pwd: 'uadmin', roles: ['userAdminAnyDatabase']}
|
|
},
|
|
test: {
|
|
none: {pwd: 'none', roles: []},
|
|
ro: {pwd: 'ro', roles: ['read']},
|
|
rw: {pwd: 'rw', roles: ['readWrite']},
|
|
roadmin: {pwd: 'roadmin', roles: ['read', 'dbAdmin']},
|
|
admin: {pwd: 'admin', roles: ['dbAdmin']},
|
|
uadmin: {pwd: 'uadmin', roles: ['userAdmin']}
|
|
}
|
|
};
|
|
|
|
// Constants that lists the privileges of a given role.
|
|
var READ_PERM = {query: 1, index_r: 1, killCursor: 1};
|
|
var READ_WRITE_PERM =
|
|
{insert: 1, update: 1, remove: 1, query: 1, index_r: 1, index_w: 1, killCursor: 1};
|
|
var ADMIN_PERM = {index_r: 1, index_w: 1, profile_r: 1};
|
|
var UADMIN_PERM = {user_r: 1, user_w: 1};
|
|
var CLUSTER_PERM =
|
|
{killOp: 1, currentOp: 1, fsync_unlock: 1, killCursor: 1, killAnyCursor: 1, profile_r: 1};
|
|
|
|
/**
|
|
* Checks whether an error occurs after running an operation.
|
|
*
|
|
* @param shouldPass {Boolean} true means that the operation should succeed.
|
|
* @param opFunc {function()} a function object which contains the operation to perform.
|
|
*/
|
|
var checkErr = function(shouldPass, opFunc) {
|
|
var success = true;
|
|
|
|
var exception = null;
|
|
try {
|
|
opFunc();
|
|
} catch (x) {
|
|
exception = x;
|
|
success = false;
|
|
}
|
|
|
|
assert(success == shouldPass,
|
|
'expected shouldPass: ' + shouldPass + ', got: ' + success + ', op: ' + tojson(opFunc) +
|
|
', exception: ' + tojson(exception));
|
|
};
|
|
|
|
/**
|
|
* Runs a series of operations against the db provided.
|
|
*
|
|
* @param db {DB} the database object to use.
|
|
* @param allowedActions {Object} the lists of operations that are allowed for the
|
|
* current user. The data structure is represented as a map with the presence of
|
|
* a field name means that the operation is allowed and not allowed if it is
|
|
* not present. The list of field names are: insert, update, remove, query, killOp,
|
|
* currentOp, index_r, index_w, profile_r, profile_w, user_r, user_w, killCursor,
|
|
* fsync_unlock.
|
|
*/
|
|
var testOps = function(db, allowedActions) {
|
|
checkErr(allowedActions.hasOwnProperty('insert'), function() {
|
|
var res = db.user.insert({y: 1});
|
|
if (res.hasWriteError())
|
|
throw Error("insert failed: " + tojson(res.getRawResponse()));
|
|
});
|
|
|
|
checkErr(allowedActions.hasOwnProperty('update'), function() {
|
|
var res = db.user.update({y: 1}, {z: 3});
|
|
if (res.hasWriteError())
|
|
throw Error("update failed: " + tojson(res.getRawResponse()));
|
|
});
|
|
|
|
checkErr(allowedActions.hasOwnProperty('remove'), function() {
|
|
var res = db.user.remove({y: 1});
|
|
if (res.hasWriteError())
|
|
throw Error("remove failed: " + tojson(res.getRawResponse()));
|
|
});
|
|
|
|
checkErr(allowedActions.hasOwnProperty('query'), function() {
|
|
db.user.findOne({y: 1});
|
|
});
|
|
|
|
checkErr(allowedActions.hasOwnProperty('killOp'), function() {
|
|
var errorCodeUnauthorized = 13;
|
|
var res = db.killOp(1);
|
|
|
|
if (res.code == errorCodeUnauthorized) {
|
|
throw Error("unauthorized killOp");
|
|
}
|
|
});
|
|
|
|
checkErr(allowedActions.hasOwnProperty('currentOp'), function() {
|
|
var errorCodeUnauthorized = 13;
|
|
var res = db.currentOp();
|
|
|
|
if (res.code == errorCodeUnauthorized) {
|
|
throw Error("unauthorized currentOp");
|
|
}
|
|
});
|
|
|
|
checkErr(allowedActions.hasOwnProperty('index_r'), function() {
|
|
var errorCodeUnauthorized = 13;
|
|
var res = db.runCommand({"listIndexes": "user"});
|
|
|
|
if (res.code == errorCodeUnauthorized) {
|
|
throw Error("unauthorized listIndexes");
|
|
}
|
|
});
|
|
|
|
checkErr(allowedActions.hasOwnProperty('index_w'), function() {
|
|
var res = db.user.createIndex({x: 1});
|
|
if (res.code == 13) { // Unauthorized
|
|
throw Error("unauthorized currentOp");
|
|
}
|
|
});
|
|
|
|
checkErr(allowedActions.hasOwnProperty('profile_r'), function() {
|
|
db.system.profile.findOne();
|
|
});
|
|
|
|
checkErr(allowedActions.hasOwnProperty('profile_w'), function() {
|
|
var res = db.system.profile.insert({x: 1});
|
|
if (res.hasWriteError()) {
|
|
throw Error("profile insert failed: " + tojson(res.getRawResponse()));
|
|
}
|
|
});
|
|
|
|
checkErr(allowedActions.hasOwnProperty('user_r'), function() {
|
|
var result = db.runCommand({usersInfo: 1});
|
|
if (!result.ok) {
|
|
throw new Error(tojson(result));
|
|
}
|
|
});
|
|
|
|
checkErr(allowedActions.hasOwnProperty('user_w'), function() {
|
|
db.createUser({user: 'a', pwd: 'a', roles: jsTest.basicUserRoles});
|
|
assert(db.dropUser('a'));
|
|
});
|
|
|
|
// Test for kill cursor
|
|
(function() {
|
|
var newConn = new Mongo(db.getMongo().host);
|
|
var dbName = db.getName();
|
|
var db2 = newConn.getDB(dbName);
|
|
|
|
if (db2.getName() == 'admin') {
|
|
assert.eq(1, db2.auth('aro', AUTH_INFO.admin.aro.pwd));
|
|
} else {
|
|
assert.eq(1, db2.auth('ro', AUTH_INFO.test.ro.pwd));
|
|
}
|
|
|
|
// Create cursor from db2.
|
|
var cmdRes = db2.runCommand({find: db2.kill_cursor.getName(), batchSize: 2});
|
|
assert.commandWorked(cmdRes);
|
|
var cursorId = cmdRes.cursor.id;
|
|
assert(!bsonBinaryEqual({cursorId: cursorId}, {cursorId: NumberLong(0)}),
|
|
"find command didn't return a cursor: " + tojson(cmdRes));
|
|
|
|
const shouldSucceed = (function() {
|
|
// admin users can do anything they want.
|
|
if (allowedActions.hasOwnProperty('killAnyCursor')) {
|
|
return true;
|
|
}
|
|
|
|
// users can kill their own cursors
|
|
const users = assert.commandWorked(db.runCommand({connectionStatus: 1}))
|
|
.authInfo.authenticatedUsers;
|
|
const users2 = assert.commandWorked(db2.runCommand({connectionStatus: 1}))
|
|
.authInfo.authenticatedUsers;
|
|
if (!users.length && !users2.length) {
|
|
// Special case, no-auth
|
|
return true;
|
|
}
|
|
return users.some(function(u) {
|
|
return users2.some(function(u2) {
|
|
return ((u.db === u2.db) && (u.user === u2.user));
|
|
});
|
|
});
|
|
})();
|
|
|
|
checkErr(shouldSucceed, function() {
|
|
// Issue killCursor command from db.
|
|
cmdRes = db.runCommand({killCursors: db2.kill_cursor.getName(), cursors: [cursorId]});
|
|
assert.commandWorked(cmdRes);
|
|
assert(bsonBinaryEqual({cursorId: cmdRes.cursorsKilled}, {cursorId: [cursorId]}),
|
|
"unauthorized to kill cursor: " + tojson(cmdRes));
|
|
});
|
|
})();
|
|
|
|
var isMongos = db.runCommand({isdbgrid: 1}).isdbgrid;
|
|
// Note: fsyncUnlock is not supported in mongos.
|
|
if (!isMongos) {
|
|
checkErr(allowedActions.hasOwnProperty('fsync_unlock'), function() {
|
|
var res = db.fsyncUnlock();
|
|
var errorCodeUnauthorized = 13;
|
|
|
|
if (res.code == errorCodeUnauthorized) {
|
|
throw Error("unauthorized fsyncUnlock");
|
|
}
|
|
});
|
|
}
|
|
};
|
|
|
|
// List of tests to run. Has the format:
|
|
//
|
|
// {
|
|
// name: {String} description of the test
|
|
// test: {function(Mongo)} the test function to run which accepts a Mongo connection
|
|
// object.
|
|
// }
|
|
var TESTS = [
|
|
{
|
|
name: 'Test multiple user login separate connection',
|
|
test: function(conn) {
|
|
var testDB = conn.getDB('test');
|
|
assert.eq(1, testDB.auth('ro', AUTH_INFO.test.ro.pwd));
|
|
|
|
var conn2 = new Mongo(conn.host);
|
|
var testDB2 = conn2.getDB('test');
|
|
assert.eq(1, testDB2.auth('uadmin', AUTH_INFO.test.uadmin.pwd));
|
|
|
|
testOps(testDB, READ_PERM);
|
|
testOps(testDB2, UADMIN_PERM);
|
|
}
|
|
},
|
|
{
|
|
name: 'Test user with no role',
|
|
test: function(conn) {
|
|
var testDB = conn.getDB('test');
|
|
assert.eq(1, testDB.auth('none', AUTH_INFO.test.none.pwd));
|
|
|
|
testOps(testDB, {});
|
|
}
|
|
},
|
|
{
|
|
name: 'Test read only user',
|
|
test: function(conn) {
|
|
var testDB = conn.getDB('test');
|
|
assert.eq(1, testDB.auth('ro', AUTH_INFO.test.ro.pwd));
|
|
|
|
testOps(testDB, READ_PERM);
|
|
}
|
|
},
|
|
{
|
|
name: 'Test read/write user',
|
|
test: function(conn) {
|
|
var testDB = conn.getDB('test');
|
|
assert.eq(1, testDB.auth('rw', AUTH_INFO.test.rw.pwd));
|
|
|
|
testOps(testDB, READ_WRITE_PERM);
|
|
}
|
|
},
|
|
{
|
|
name: 'Test read + dbAdmin user',
|
|
test: function(conn) {
|
|
var testDB = conn.getDB('test');
|
|
assert.eq(1, testDB.auth('roadmin', AUTH_INFO.test.roadmin.pwd));
|
|
|
|
var combinedPerm = Object.extend({}, READ_PERM);
|
|
combinedPerm = Object.extend(combinedPerm, ADMIN_PERM);
|
|
testOps(testDB, combinedPerm);
|
|
}
|
|
},
|
|
{
|
|
name: 'Test dbAdmin user',
|
|
test: function(conn) {
|
|
var testDB = conn.getDB('test');
|
|
assert.eq(1, testDB.auth('admin', AUTH_INFO.test.admin.pwd));
|
|
|
|
testOps(testDB, ADMIN_PERM);
|
|
}
|
|
},
|
|
{
|
|
name: 'Test userAdmin user',
|
|
test: function(conn) {
|
|
var testDB = conn.getDB('test');
|
|
assert.eq(1, testDB.auth('uadmin', AUTH_INFO.test.uadmin.pwd));
|
|
|
|
testOps(testDB, UADMIN_PERM);
|
|
}
|
|
},
|
|
{
|
|
name: 'Test cluster user',
|
|
test: function(conn) {
|
|
var adminDB = conn.getDB('admin');
|
|
assert.eq(1, adminDB.auth('cluster', AUTH_INFO.admin.cluster.pwd));
|
|
|
|
testOps(conn.getDB('test'), CLUSTER_PERM);
|
|
}
|
|
},
|
|
{
|
|
name: 'Test admin user with no role',
|
|
test: function(conn) {
|
|
var adminDB = conn.getDB('admin');
|
|
assert.eq(1, adminDB.auth('anone', AUTH_INFO.admin.anone.pwd));
|
|
|
|
testOps(adminDB, {});
|
|
testOps(conn.getDB('test'), {});
|
|
}
|
|
},
|
|
{
|
|
name: 'Test read only admin user',
|
|
test: function(conn) {
|
|
var adminDB = conn.getDB('admin');
|
|
assert.eq(1, adminDB.auth('aro', AUTH_INFO.admin.aro.pwd));
|
|
|
|
testOps(adminDB, READ_PERM);
|
|
testOps(conn.getDB('test'), {});
|
|
}
|
|
},
|
|
{
|
|
name: 'Test read/write admin user',
|
|
test: function(conn) {
|
|
var adminDB = conn.getDB('admin');
|
|
assert.eq(1, adminDB.auth('arw', AUTH_INFO.admin.arw.pwd));
|
|
|
|
testOps(adminDB, READ_WRITE_PERM);
|
|
testOps(conn.getDB('test'), {});
|
|
}
|
|
},
|
|
{
|
|
name: 'Test dbAdmin admin user',
|
|
test: function(conn) {
|
|
var adminDB = conn.getDB('admin');
|
|
assert.eq(1, adminDB.auth('aadmin', AUTH_INFO.admin.aadmin.pwd));
|
|
|
|
testOps(adminDB, ADMIN_PERM);
|
|
testOps(conn.getDB('test'), {});
|
|
}
|
|
},
|
|
{
|
|
name: 'Test userAdmin admin user',
|
|
test: function(conn) {
|
|
var adminDB = conn.getDB('admin');
|
|
assert.eq(1, adminDB.auth('auadmin', AUTH_INFO.admin.auadmin.pwd));
|
|
|
|
testOps(adminDB, UADMIN_PERM);
|
|
testOps(conn.getDB('test'), {});
|
|
}
|
|
},
|
|
{
|
|
name: 'Test read only any db user',
|
|
test: function(conn) {
|
|
var adminDB = conn.getDB('admin');
|
|
assert.eq(1, adminDB.auth('any_ro', AUTH_INFO.admin.any_ro.pwd));
|
|
|
|
testOps(adminDB, READ_PERM);
|
|
testOps(conn.getDB('test'), READ_PERM);
|
|
}
|
|
},
|
|
{
|
|
name: 'Test read/write any db user',
|
|
test: function(conn) {
|
|
var adminDB = conn.getDB('admin');
|
|
assert.eq(1, adminDB.auth('any_rw', AUTH_INFO.admin.any_rw.pwd));
|
|
|
|
testOps(adminDB, READ_WRITE_PERM);
|
|
testOps(conn.getDB('test'), READ_WRITE_PERM);
|
|
}
|
|
},
|
|
{
|
|
name: 'Test dbAdmin any db user',
|
|
test: function(conn) {
|
|
var adminDB = conn.getDB('admin');
|
|
assert.eq(1, adminDB.auth('any_admin', AUTH_INFO.admin.any_admin.pwd));
|
|
|
|
testOps(adminDB, ADMIN_PERM);
|
|
testOps(conn.getDB('test'), ADMIN_PERM);
|
|
}
|
|
},
|
|
{
|
|
name: 'Test userAdmin any db user',
|
|
test: function(conn) {
|
|
var adminDB = conn.getDB('admin');
|
|
assert.eq(1, adminDB.auth('any_uadmin', AUTH_INFO.admin.any_uadmin.pwd));
|
|
|
|
testOps(adminDB, UADMIN_PERM);
|
|
testOps(conn.getDB('test'), UADMIN_PERM);
|
|
}
|
|
},
|
|
|
|
{
|
|
name: 'Test change role',
|
|
test: function(conn) {
|
|
var testDB = conn.getDB('test');
|
|
assert.eq(1, testDB.auth('rw', AUTH_INFO.test.rw.pwd));
|
|
|
|
var newConn = new Mongo(conn.host);
|
|
assert.eq(1, newConn.getDB('admin').auth('any_uadmin', AUTH_INFO.admin.any_uadmin.pwd));
|
|
newConn.getDB('test').updateUser('rw', {roles: ['read']});
|
|
var origSpec = newConn.getDB("test").getUser("rw");
|
|
|
|
// role change should affect users already authenticated.
|
|
testOps(testDB, READ_PERM);
|
|
|
|
// role change should affect active connections.
|
|
testDB.runCommand({logout: 1});
|
|
assert.eq(1, testDB.auth('rw', AUTH_INFO.test.rw.pwd));
|
|
testOps(testDB, READ_PERM);
|
|
|
|
// role change should also affect new connections.
|
|
var newConn3 = new Mongo(conn.host);
|
|
var testDB3 = newConn3.getDB('test');
|
|
assert.eq(1, testDB3.auth('rw', AUTH_INFO.test.rw.pwd));
|
|
testOps(testDB3, READ_PERM);
|
|
|
|
newConn.getDB('test').updateUser('rw', {roles: origSpec.roles});
|
|
}
|
|
},
|
|
|
|
{
|
|
name: 'Test override user',
|
|
test: function(conn) {
|
|
var testDB = conn.getDB('test');
|
|
assert.eq(1, testDB.auth('rw', AUTH_INFO.test.rw.pwd));
|
|
testDB.logout();
|
|
assert.eq(1, testDB.auth('ro', AUTH_INFO.test.ro.pwd));
|
|
testOps(testDB, READ_PERM);
|
|
|
|
testDB.runCommand({logout: 1});
|
|
testOps(testDB, {});
|
|
}
|
|
}
|
|
];
|
|
|
|
/**
|
|
* Driver method for setting up the test environment, running them, cleanup
|
|
* after every test and keeping track of test failures.
|
|
*
|
|
* @param conn {Mongo} a connection to a mongod or mongos to test.
|
|
*/
|
|
var runTests = function(conn) {
|
|
var setup = function() {
|
|
var testDB = conn.getDB('test');
|
|
var adminDB = conn.getDB('admin');
|
|
|
|
adminDB.createUser(
|
|
{user: 'root', pwd: AUTH_INFO.admin.root.pwd, roles: AUTH_INFO.admin.root.roles});
|
|
adminDB.auth('root', AUTH_INFO.admin.root.pwd);
|
|
|
|
for (var x = 0; x < 10; x++) {
|
|
testDB.kill_cursor.insert({x: x});
|
|
adminDB.kill_cursor.insert({x: x});
|
|
}
|
|
|
|
for (var dbName in AUTH_INFO) {
|
|
var dbObj = AUTH_INFO[dbName];
|
|
|
|
for (var userName in dbObj) {
|
|
if (dbName == 'admin' && userName == 'root') {
|
|
// We already registered this user.
|
|
continue;
|
|
}
|
|
|
|
var info = dbObj[userName];
|
|
conn.getDB(dbName).createUser({user: userName, pwd: info.pwd, roles: info.roles});
|
|
}
|
|
}
|
|
|
|
adminDB.runCommand({logout: 1});
|
|
};
|
|
|
|
var teardown = function() {
|
|
var adminDB = conn.getDB('admin');
|
|
adminDB.auth('root', AUTH_INFO.admin.root.pwd);
|
|
conn.getDB('test').dropDatabase();
|
|
adminDB.dropDatabase();
|
|
};
|
|
|
|
var failures = [];
|
|
setup();
|
|
TESTS.forEach(function(testFunc) {
|
|
try {
|
|
jsTest.log(testFunc.name);
|
|
|
|
var newConn = new Mongo(conn.host);
|
|
newConn.host = conn.host;
|
|
testFunc.test(newConn);
|
|
} catch (x) {
|
|
failures.push(testFunc.name);
|
|
jsTestLog(tojson(x));
|
|
}
|
|
});
|
|
|
|
teardown();
|
|
|
|
if (failures.length > 0) {
|
|
var list = '';
|
|
failures.forEach(function(test) {
|
|
list += (test + '\n');
|
|
});
|
|
throw Error('Tests failed:\n' + list);
|
|
}
|
|
};
|
|
|
|
var conn = MongoRunner.runMongod({auth: ''});
|
|
runTests(conn);
|
|
MongoRunner.stopMongod(conn);
|
|
|
|
jsTest.log('Test sharding');
|
|
var st = new ShardingTest({shards: 1, keyFile: 'jstests/libs/key1'});
|
|
runTests(st.s);
|
|
st.stop();
|
|
|
|
print('SUCCESS! Completed basic_role_auth.js');
|