diff options
Diffstat (limited to 'jstests/sharding/authCommands2.js')
| -rw-r--r-- | jstests/sharding/authCommands2.js | 321 |
1 files changed, 321 insertions, 0 deletions
diff --git a/jstests/sharding/authCommands2.js b/jstests/sharding/authCommands2.js new file mode 100644 index 00000000000..5f403d70313 --- /dev/null +++ b/jstests/sharding/authCommands2.js @@ -0,0 +1,321 @@ +/** + * 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(); |
