diff options
author | Maria van Keulen <maria@mongodb.com> | 2018-01-10 17:25:17 -0500 |
---|---|---|
committer | Maria van Keulen <maria@mongodb.com> | 2018-02-12 19:40:12 -0500 |
commit | 200871c6f995ff1e51ca6565deecb01778b15d47 (patch) | |
tree | d39e98fd68fbc8a8bfde39019385b1f3b386b349 | |
parent | e74bf273ef83cfbee332fe33079c0f640c71f7bb (diff) | |
download | mongo-200871c6f995ff1e51ca6565deecb01778b15d47.tar.gz |
SERVER-32205 Prohibit dropping the admin database at catalog level
(cherry picked from commit 72fc7cea722f952e3ad47c5470cb60ae2114097f)
-rw-r--r-- | jstests/multiVersion/set_feature_compatibility_version.js | 59 | ||||
-rw-r--r-- | jstests/noPassthrough/feature_compatibility_version.js | 4 | ||||
-rw-r--r-- | jstests/noPassthrough/system_indexes.js | 24 | ||||
-rw-r--r-- | jstests/replsets/drop_oplog.js | 2 | ||||
-rw-r--r-- | src/mongo/db/catalog/database_impl.cpp | 1 | ||||
-rw-r--r-- | src/mongo/db/catalog/drop_database.cpp | 6 | ||||
-rw-r--r-- | src/mongo/db/catalog/drop_database_test.cpp | 9 | ||||
-rw-r--r-- | src/mongo/db/commands/dbcommands.cpp | 2 | ||||
-rw-r--r-- | src/mongo/db/repl/apply_ops_test.cpp | 24 |
9 files changed, 59 insertions, 72 deletions
diff --git a/jstests/multiVersion/set_feature_compatibility_version.js b/jstests/multiVersion/set_feature_compatibility_version.js index abf6043546d..e831c1e2736 100644 --- a/jstests/multiVersion/set_feature_compatibility_version.js +++ b/jstests/multiVersion/set_feature_compatibility_version.js @@ -30,25 +30,6 @@ TestData.skipCheckingUUIDsConsistentAcrossCluster = true; }; let doStartupFailTests = function(withUUIDs, dbpath) { - // Fail to start up if no admin database is present but other non-local databases are - // present. - if (withUUIDs) { - setupMissingAdminDB(latest, dbpath); - } else { - setupMissingAdminDB(downgrade, dbpath); - } - conn = MongoRunner.runMongod({dbpath: dbpath, binVersion: latest, noCleanData: true}); - assert.eq( - null, - conn, - "expected mongod to fail when data files are present and no admin database is found."); - if (!withUUIDs) { - conn = - MongoRunner.runMongod({dbpath: dbpath, binVersion: downgrade, noCleanData: true}); - assert.neq(null, conn, "expected 3.4 to startup when the admin database is missing"); - MongoRunner.stopMongod(conn); - } - // Fail to start up if no featureCompatibilityVersion document is present and non-local // databases are present. if (withUUIDs) { @@ -93,20 +74,6 @@ TestData.skipCheckingUUIDsConsistentAcrossCluster = true; return conn; }; - let setupMissingAdminDB = function(version, dbpath) { - conn = MongoRunner.runMongod({dbpath: dbpath, binVersion: version}); - assert.neq(null, - conn, - "mongod was unable to start up with version=" + version + " and no data files"); - let testDB = conn.getDB("test"); - assert.commandWorked(testDB.createCollection("testcoll")); - adminDB = conn.getDB("admin"); - assert.commandWorked(adminDB.runCommand({dropDatabase: 1}), - "expected drop of admin database to be successful"); - MongoRunner.stopMongod(conn); - return conn; - }; - // // Standalone tests. // @@ -230,35 +197,11 @@ TestData.skipCheckingUUIDsConsistentAcrossCluster = true; // Fail to start up if no featureCompatibilityVersion is present and no collections have UUIDs. doStartupFailTests(/*withUUIDs*/ false, dbpath); - // --repair can be used to restore a missing admin database and featureCompatibilityVersion - // document if at least some collections have UUIDs. - conn = setupMissingAdminDB(latest, dbpath); - recoverMMapJournal(isMMAPv1, conn, dbpath); - let returnCode = runMongoProgram("mongod", "--port", conn.port, "--repair", "--dbpath", dbpath); - assert.eq( - returnCode, - 0, - "expected mongod --repair to execute successfully when restoring a missing admin database."); - conn = MongoRunner.runMongod({dbpath: dbpath, binVersion: latest, noCleanData: true}); - assert.neq(null, - conn, - "mongod was unable to start up with version=" + latest + " and existing data files"); - // featureCompatibilityVersion is 3.4 and targetVersion is 3.6 because the admin.system.version - // collection was restored without a UUID. - adminDB = conn.getDB("admin"); - assert.eq(adminDB.system.version.findOne({_id: "featureCompatibilityVersion"}).version, "3.4"); - assert.eq(adminDB.system.version.findOne({_id: "featureCompatibilityVersion"}).targetVersion, - "3.6"); - let adminInfos = adminDB.getCollectionInfos(); - assert(!adminInfos[0].info.uuid, - "Expected collection with infos " + tojson(adminInfos) + " to not have a UUID."); - MongoRunner.stopMongod(conn); - // --repair can be used to restore a missing featureCompatibilityVersion document to an // existing admin database if at least some collections have UUIDs. conn = setupMissingFCVDoc(latest, dbpath); recoverMMapJournal(isMMAPv1, conn, dbpath); - returnCode = runMongoProgram("mongod", "--port", conn.port, "--repair", "--dbpath", dbpath); + let returnCode = runMongoProgram("mongod", "--port", conn.port, "--repair", "--dbpath", dbpath); assert.eq( returnCode, 0, diff --git a/jstests/noPassthrough/feature_compatibility_version.js b/jstests/noPassthrough/feature_compatibility_version.js index 016c5d12e08..f3824a3a0b5 100644 --- a/jstests/noPassthrough/feature_compatibility_version.js +++ b/jstests/noPassthrough/feature_compatibility_version.js @@ -91,9 +91,5 @@ adminDB.system.version.insert({_id: "featureCompatibilityVersion", version: "3.6"})); checkFCV(adminDB, "3.6"); - // Dropping the admin database changes the featureCompatibilityVersion server parameter to 3.4. - adminDB.dropDatabase(); - checkFCVDocumentMissing(adminDB); - MongoRunner.stopMongod(conn); }()); diff --git a/jstests/noPassthrough/system_indexes.js b/jstests/noPassthrough/system_indexes.js index f95d7bf1a8d..3fa784e97da 100644 --- a/jstests/noPassthrough/system_indexes.js +++ b/jstests/noPassthrough/system_indexes.js @@ -47,10 +47,14 @@ // TEST: Destroying the admin.system.users index and restarting will recreate it, even if // admin.system.roles does not exist - db.dropDatabase(); - // Restore the featureCompatibilityVersion document since as of SERVER-29452, mongod fails to - // startup without such a document. - assert.commandWorked(db.runCommand({setFeatureCompatibilityVersion: "3.6"})); + // Use _mergeAuthzCollections to clear admin.system.users and admin.system.roles. + assert.commandWorked(db.adminCommand({ + _mergeAuthzCollections: 1, + tempUsersCollection: 'admin.tempusers', + tempRolesCollection: 'admin.temproles', + db: "", + drop: true + })); db.createUser({user: "user", pwd: "pwd", roles: []}); assert.commandWorked(db.system.users.dropIndexes()); MongoRunner.stopMongod(conn); @@ -60,10 +64,14 @@ // TEST: Destroying the admin.system.roles index and restarting will recreate it, even if // admin.system.users does not exist - db.dropDatabase(); - // Restore the featureCompatibilityVersion document since as of SERVER-29452, mongod fails to - // startup without such a document. - assert.commandWorked(db.runCommand({setFeatureCompatibilityVersion: "3.6"})); + // Use _mergeAuthzCollections to clear admin.system.users and admin.system.roles. + assert.commandWorked(db.adminCommand({ + _mergeAuthzCollections: 1, + tempUsersCollection: 'admin.tempusers', + tempRolesCollection: 'admin.temproles', + db: "", + drop: true + })); db.createRole({role: "role", privileges: [], roles: []}); assert.commandWorked(db.system.roles.dropIndexes()); MongoRunner.stopMongod(conn); diff --git a/jstests/replsets/drop_oplog.js b/jstests/replsets/drop_oplog.js index df89ec255ff..8e2a7c2e1d2 100644 --- a/jstests/replsets/drop_oplog.js +++ b/jstests/replsets/drop_oplog.js @@ -22,7 +22,7 @@ let adminDB = master.getDB('admin'); dropOutput = adminDB.dropDatabase(); assert.eq(dropOutput.ok, 0); - assert.eq(dropOutput.errmsg, "Cannot drop 'admin' database while replication is active"); + assert.eq(dropOutput.errmsg, "Dropping the 'admin' database is prohibited."); let renameOutput = localDB.oplog.rs.renameCollection("poison"); assert.eq(renameOutput.ok, 0); diff --git a/src/mongo/db/catalog/database_impl.cpp b/src/mongo/db/catalog/database_impl.cpp index dc044477718..d9d123fa167 100644 --- a/src/mongo/db/catalog/database_impl.cpp +++ b/src/mongo/db/catalog/database_impl.cpp @@ -856,6 +856,7 @@ void DatabaseImpl::dropDatabase(OperationContext* opCtx, Database* db) { // Store the name so we have if for after the db object is deleted const string name = db->name(); + LOG(1) << "dropDatabase " << name; invariant(opCtx->lockState()->isDbLockedForMode(name, MODE_X)); diff --git a/src/mongo/db/catalog/drop_database.cpp b/src/mongo/db/catalog/drop_database.cpp index cea9b55f66b..25a4d9f4294 100644 --- a/src/mongo/db/catalog/drop_database.cpp +++ b/src/mongo/db/catalog/drop_database.cpp @@ -82,6 +82,12 @@ Status dropDatabase(OperationContext* opCtx, const std::string& dbName) { uassert(ErrorCodes::IllegalOperation, "Cannot drop a database in read-only mode", !storageGlobalParams.readOnly); + + // As of SERVER-32205, dropping the admin database is prohibited. + uassert(ErrorCodes::IllegalOperation, + str::stream() << "Dropping the '" << dbName << "' database is prohibited.", + dbName != NamespaceString::kAdminDb); + // TODO (Kal): OldClientContext legacy, needs to be removed { CurOp::get(opCtx)->ensureStarted(); diff --git a/src/mongo/db/catalog/drop_database_test.cpp b/src/mongo/db/catalog/drop_database_test.cpp index f26a624d687..1d351be54d4 100644 --- a/src/mongo/db/catalog/drop_database_test.cpp +++ b/src/mongo/db/catalog/drop_database_test.cpp @@ -455,4 +455,13 @@ TEST_F(DropDatabaseTest, ASSERT_FALSE(db->isDropPending(_opCtx.get())); } +TEST_F(DropDatabaseTest, DropDatabaseFailsToDropAdmin) { + NamespaceString adminNSS(NamespaceString::kAdminDb, "foo"); + _createCollection(_opCtx.get(), adminNSS); + ASSERT_THROWS_CODE_AND_WHAT(dropDatabase(_opCtx.get(), adminNSS.db().toString()).ignore(), + AssertionException, + ErrorCodes::IllegalOperation, + "Dropping the 'admin' database is prohibited."); +} + } // namespace diff --git a/src/mongo/db/commands/dbcommands.cpp b/src/mongo/db/commands/dbcommands.cpp index 693bb58d5a0..9bd32266d0f 100644 --- a/src/mongo/db/commands/dbcommands.cpp +++ b/src/mongo/db/commands/dbcommands.cpp @@ -181,7 +181,7 @@ public: if ((repl::getGlobalReplicationCoordinator()->getReplicationMode() != repl::ReplicationCoordinator::modeNone) && - ((dbname == NamespaceString::kLocalDb) || (dbname == NamespaceString::kAdminDb))) { + (dbname == NamespaceString::kLocalDb)) { return appendCommandStatus( result, Status(ErrorCodes::IllegalOperation, diff --git a/src/mongo/db/repl/apply_ops_test.cpp b/src/mongo/db/repl/apply_ops_test.cpp index 2bb1a221f86..e43a3092978 100644 --- a/src/mongo/db/repl/apply_ops_test.cpp +++ b/src/mongo/db/repl/apply_ops_test.cpp @@ -306,6 +306,30 @@ TEST_F(ApplyOpsTest, ApplyOpsPropagatesOplogApplicationMode) { stopCapturingLogMessages(); } +TEST_F(ApplyOpsTest, ApplyOpsFailsToDropAdmin) { + auto opCtx = cc().makeOperationContext(); + auto mode = OplogApplication::Mode::kApplyOpsCmd; + + // Create a collection on the admin database. + NamespaceString nss("admin.foo"); + CollectionOptions options; + options.uuid = UUID::gen(); + ASSERT_OK(_storage->createCollection(opCtx.get(), nss, options)); + + auto dropDatabaseOp = BSON("op" + << "c" + << "ns" + << nss.getCommandNS().ns() + << "o" + << BSON("dropDatabase" << 1)); + + auto dropDatabaseCmdObj = BSON("applyOps" << BSON_ARRAY(dropDatabaseOp)); + BSONObjBuilder resultBuilder; + auto status = + applyOps(opCtx.get(), nss.db().toString(), dropDatabaseCmdObj, mode, &resultBuilder); + ASSERT_EQUALS(ErrorCodes::UnknownError, status); +} + } // namespace } // namespace repl } // namespace mongo |