Files
mongo/jstests/sharding/authCommands2.js

322 lines
13 KiB
JavaScript

/**
* This tests using DB commands with authentication enabled when sharded.
*/
var rsOpts = { oplogSize: 10, verbose : 2, useHostname : false };
var st = new ShardingTest({ keyFile : 'jstests/libs/key1', shards : 2, chunksize : 1, config : 3,
rs : rsOpts, other : { nopreallocj : 1, verbose : 2, useHostname : false }});
var mongos = st.s;
var adminDB = mongos.getDB( 'admin' );
var configDB = mongos.getDB( 'config' );
var testDB = mongos.getDB( 'test' );
// Secondaries should be up here, since we awaitReplication in the ShardingTest, but we *don't*
// wait for the mongos to explicitly detect them.
ReplSetTest.awaitRSClientHosts( mongos, st.rs0.getSecondaries(), { ok : true, secondary : true });
ReplSetTest.awaitRSClientHosts( mongos, st.rs1.getSecondaries(), { ok : true, secondary : true });
st.printShardingStatus();
jsTestLog('Setting up initial users');
var rwUser = 'rwUser';
var roUser = 'roUser';
var password = 'password';
try {
adminDB.addUser( rwUser, password, false, st.rs0.numNodes );
} catch (e) {} // expected b/c of SERVER-6101. TODO: remove try/catch once SERVER-6101 is fixed.
assert( adminDB.auth( rwUser, password ) );
adminDB.addUser( roUser, password, true, st.rs0.numNodes );
testDB.addUser( rwUser, password, false, st.rs0.numNodes );
testDB.addUser( roUser, password, true, st.rs0.numNodes );
authenticatedConn = new Mongo( mongos.host );
authenticatedConn.getDB( 'admin' ).auth( rwUser, password );
// Add user to shards to prevent localhost connections from having automatic full access
st.rs0.getPrimary().getDB( 'admin' ).addUser( 'user', 'password', false, 3 );
st.rs1.getPrimary().getDB( 'admin' ).addUser( 'user', 'password', false, 3 );
jsTestLog('Creating initial data');
st.adminCommand( { enablesharding : "test" } );
st.adminCommand( { shardcollection : "test.foo" , key : { i : 1, j : 1 } } );
// Stop the balancer, so no moveChunks will interfere with the splits we're testing
st.stopBalancer()
var str = 'a';
while ( str.length < 8000 ) {
str += str;
}
for ( var i = 0; i < 100; i++ ) {
for ( var j = 0; j < 10; j++ ) {
testDB.foo.save({i:i, j:j, str:str});
testDB.bar.save({i:i, j:j, str:str}); // non-sharded collection with same data
}
}
testDB.getLastError( 'majority' );
// Wait for the balancer to start back up
st.startBalancer()
// Make sure we've done at least some splitting, so the balancer will work
assert.gt( configDB.chunks.find({ ns : 'test.foo' }).count(), 2 )
// Make sure we eventually balance all the chunks we've created
assert.soon( function() {
var x = st.chunkDiff( "foo", "test" );
print( "chunk diff: " + x );
return x < 2 && configDB.locks.findOne({ _id : 'test.foo' }).state == 0;
}, "no balance happened", 60000 );
var map = function() { emit (this.i, this.j) };
var reduce = function( key, values ) {
var jCount = 0;
values.forEach( function(j) { jCount += j; } );
return jCount;
};
var checkCommandSucceeded = function( db, cmdObj ) {
print( "Running command that should succeed: " );
printjson( cmdObj );
resultObj = db.runCommand( cmdObj );
printjson( resultObj )
assert ( resultObj.ok );
return resultObj;
}
var checkCommandFailed = function( db, cmdObj ) {
print( "Running command that should fail: " );
printjson( cmdObj );
resultObj = db.runCommand( cmdObj );
printjson( resultObj )
assert ( !resultObj.ok );
return resultObj;
}
var checkReadOps = function( hasReadAuth ) {
if ( hasReadAuth ) {
print( "Checking read operations, should work" );
assert.eq( 1000, testDB.foo.find().itcount() );
assert.eq( 1000, testDB.foo.count() );
assert.eq( null, testDB.runCommand({getlasterror : 1}).err );
checkCommandSucceeded( testDB, {dbstats : 1} );
checkCommandSucceeded( testDB, {collstats : 'foo'} );
// inline map-reduce works read-only on non-sharded collections
var res = checkCommandSucceeded( testDB, {mapreduce : 'bar', map : map, reduce : reduce,
out : {inline : 1}});
assert.eq( 100, res.results.length );
assert.eq( 45, res.results[0].value );
res = checkCommandSucceeded( testDB,
{aggregate:'foo',
pipeline: [ {$project : {j : 1}},
{$group : {_id : 'j', sum : {$sum : '$j'}}}]} );
assert.eq( 4500, res.result[0].sum );
res = checkCommandSucceeded( testDB, { $eval : 'return db.bar.findOne();'} );
assert.eq(str, res.retval.str);
} else {
print( "Checking read operations, should fail" );
assert.throws( function() { testDB.foo.find().itcount(); } );
assert.eq(0, testDB.runCommand({getlasterror : 1}).ok);
checkCommandFailed( testDB, {dbstats : 1} );
checkCommandFailed( testDB, {collstats : 'foo'} );
checkCommandFailed( testDB, {mapreduce : 'foo', map : map, reduce : reduce,
out : { inline : 1 }} );
checkCommandFailed( testDB, {aggregate:'foo',
pipeline: [ {$project : {j : 1}},
{$group : {_id : 'j', sum : {$sum : '$j'}}}]} );
checkCommandFailed( testDB, { $eval : 'return db.bar.findOne();'} );
}
}
var checkWriteOps = function( hasWriteAuth ) {
if ( hasWriteAuth ) {
print( "Checking write operations, should work" );
testDB.foo.insert({a : 1, i : 1, j : 1});
res = checkCommandSucceeded( testDB, { findAndModify: "foo", query: {a:1, i:1, j:1},
update: {$set: {b:1}}});
assert.eq(1, res.value.a);
assert.eq(null, res.value.b);
assert.eq(1, testDB.foo.findOne({a:1}).b);
testDB.foo.remove({a : 1});
assert.eq( null, testDB.runCommand({getlasterror : 1}).err );
checkCommandSucceeded( testDB, {reIndex:'foo'} );
checkCommandSucceeded( testDB, {repairDatabase : 1} );
// Test both inline and regular map-reduce
var res = checkCommandSucceeded( testDB, {mapreduce : 'foo', map : map, reduce : reduce,
out : {inline : 1}});
assert.eq( 100, res.results.length );
assert.eq( 45, res.results[0].value );
checkCommandSucceeded( testDB, {mapreduce : 'foo', map : map, reduce : reduce,
out : 'mrOutput'} );
assert.eq( 100, testDB.mrOutput.count() );
assert.eq( 45, testDB.mrOutput.findOne().value );
checkCommandSucceeded( testDB, {drop : 'foo'} );
assert.eq( 0, testDB.foo.count() );
testDB.foo.insert({a:1});
assert.eq( 1, testDB.foo.count() );
checkCommandSucceeded( testDB, {dropDatabase : 1} );
assert.eq( 0, testDB.foo.count() );
checkCommandSucceeded( testDB, {create : 'baz'} );
checkCommandSucceeded( testDB, { $eval : 'db.baz.insert({a:1});'} );
assert.eq(1, testDB.baz.findOne().a);
} else {
print( "Checking write operations, should fail" );
testDB.foo.insert({a : 1, i : 1, j : 1});
assert.eq(0, authenticatedConn.getDB('test').foo.count({a : 1, i : 1, j : 1}));
checkCommandFailed( testDB, { findAndModify: "foo", query: {a:1, i:1, j:1},
update: {$set: {b:1}}} );
checkCommandFailed( testDB, {reIndex:'foo'} );
checkCommandFailed( testDB, {repairDatabase : 1} );
// Test both inline and regular map-reduce. Inline MR on sharded collections requires write access.
checkCommandFailed( testDB, {mapreduce : 'foo', map : map, reduce : reduce,
out : {inline : 1}} );
checkCommandFailed( testDB, {mapreduce : 'foo', map : map, reduce : reduce,
out : 'mrOutput'} );
checkCommandFailed( testDB, {drop : 'foo'} );
checkCommandFailed( testDB, {dropDatabase : 1} );
passed = true;
try {
// For some reason when create fails it throws an exception instead of just returning ok:0
res = testDB.runCommand( {create : 'baz'} );
if ( !res.ok ) {
passed = false;
}
} catch (e) {
// expected
printjson(e);
passed = false;
}
assert( !passed );
var res = testDB.runCommand( { $eval : 'db.baz.insert({a:1});'} );
printjson( res );
// If you have read-only auth and try to do a $eval that writes, the $eval command succeeds,
// but the write fails
assert( !res.ok || testDB.baz.count() == 0 );
}
}
var checkAdminReadOps = function( hasReadAuth ) {
if ( hasReadAuth ) {
checkCommandSucceeded( adminDB, {getShardVersion : 'test.foo'} );
checkCommandSucceeded( adminDB, {getCmdLineOpts : 1} );
checkCommandSucceeded( adminDB, {serverStatus : 1} );
checkCommandSucceeded( adminDB, {listShards : 1} );
checkCommandSucceeded( adminDB, {whatsmyuri : 1} );
checkCommandSucceeded( adminDB, {isdbgrid : 1} );
checkCommandSucceeded( adminDB, {ismaster : 1} );
} else {
checkCommandFailed( adminDB, {getShardVersion : 'test.foo'} );
checkCommandFailed( adminDB, {getCmdLineOpts : 1} );
checkCommandFailed( adminDB, {serverStatus : 1} );
checkCommandFailed( adminDB, {listShards : 1} );
checkCommandFailed( adminDB, {whatsmyuri : 1} );
// isdbgrid and ismaster don't require any auth
checkCommandSucceeded( adminDB, {isdbgrid : 1} );
checkCommandSucceeded( adminDB, {ismaster : 1} );
}
}
var checkAdminWriteOps = function( hasWriteAuth ) {
if ( hasWriteAuth ) {
checkCommandSucceeded( adminDB, {split : 'test.foo', find : {i : 1, j : 1}} );
chunk = configDB.chunks.findOne({ shard : st.rs0.name });
checkCommandSucceeded( adminDB, {moveChunk : 'test.foo', find : chunk.min,
to : st.rs1.name} );
} else {
checkCommandFailed( adminDB, {split : 'test.foo', find : {i : 1, j : 1}} );
chunkKey = { i : { $minKey : 1 }, j : { $minKey : 1 } };
checkCommandFailed( adminDB, {moveChunk : 'test.foo', find : chunkKey,
to : st.rs1.name} );
}
}
var checkRemoveShard = function( hasWriteAuth ) {
if ( hasWriteAuth ) {
// start draining
checkCommandSucceeded( adminDB, { removeshard : st.rs1.name } );
// Wait for shard to be completely removed
checkRemoveShard = function() {
res = checkCommandSucceeded( adminDB, { removeshard : st.rs1.name } );
return res.msg == 'removeshard completed successfully';
}
assert.soon( checkRemoveShard , "failed to remove shard" );
} else {
checkCommandFailed( adminDB, { removeshard : st.rs1.name } );
}
}
var checkAddShard = function( hasWriteAuth ) {
if ( hasWriteAuth ) {
checkCommandSucceeded( adminDB, { addshard : st.rs1.getURL() } );
} else {
checkCommandFailed( adminDB, { addshard : st.rs1.getURL() } );
}
}
st.stopBalancer();
jsTestLog("Checking admin commands with read-write auth credentials");
checkAdminWriteOps( true );
assert( adminDB.logout().ok );
jsTestLog("Checking admin commands with no auth credentials");
checkAdminReadOps( false );
checkAdminWriteOps( false );
jsTestLog("Checking admin commands with read-only auth credentials");
assert( adminDB.auth( roUser, password ) );
checkAdminReadOps( true );
checkAdminWriteOps( false );
assert( adminDB.logout().ok );
jsTestLog("Checking commands with no auth credentials");
checkReadOps( false );
checkWriteOps( false );
// Authenticate as read-only user
jsTestLog("Checking commands with read-only auth credentials");
assert( testDB.auth( roUser, password ) );
checkReadOps( true );
checkWriteOps( false );
// Authenticate as read-write user
jsTestLog("Checking commands with read-write auth credentials");
assert( testDB.auth( rwUser, password ) );
checkReadOps( true );
checkWriteOps( true );
jsTestLog("Check drainging/removing a shard");
assert( testDB.logout().ok );
checkRemoveShard( false );
assert( adminDB.auth( roUser, password ) );
checkRemoveShard( false );
assert( adminDB.auth( rwUser, password ) );
assert( testDB.dropDatabase().ok );
checkRemoveShard( true );
adminDB.printShardingStatus();
jsTestLog("Check adding a shard")
assert( adminDB.logout().ok );
checkAddShard( false );
assert( adminDB.auth( roUser, password ) );
checkAddShard( false );
assert( adminDB.auth( rwUser, password ) );
checkAddShard( true );
adminDB.printShardingStatus();
st.stop();