summaryrefslogtreecommitdiff
path: root/jstests/sharding/authCommands2.js
diff options
context:
space:
mode:
Diffstat (limited to 'jstests/sharding/authCommands2.js')
-rw-r--r--jstests/sharding/authCommands2.js321
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();