diff options
author | Spencer T Brody <spencer@mongodb.com> | 2014-07-17 17:44:23 -0400 |
---|---|---|
committer | Spencer T Brody <spencer@mongodb.com> | 2014-07-18 14:54:20 -0400 |
commit | 7dbc49a56d010ceaf6d9b57f373863763fe10280 (patch) | |
tree | 7b73dd77eef3e3d3649b5c0a04605387c5fc112e /src | |
parent | 4b12168d5c14f0177586eeda8b22fba433d6df1f (diff) | |
download | mongo-7dbc49a56d010ceaf6d9b57f373863763fe10280.tar.gz |
SERVER-14212 When restoring users and roles for a single db with --drop, don't drop users/roles from other databases
Diffstat (limited to 'src')
-rw-r--r-- | src/mongo/base/error_codes.err | 1 | ||||
-rw-r--r-- | src/mongo/db/auth/user_management_commands_parser.cpp | 12 | ||||
-rw-r--r-- | src/mongo/db/auth/user_management_commands_parser.h | 7 | ||||
-rw-r--r-- | src/mongo/db/commands/user_management_commands.cpp | 33 | ||||
-rw-r--r-- | src/mongo/tools/restore.cpp | 60 |
5 files changed, 92 insertions, 21 deletions
diff --git a/src/mongo/base/error_codes.err b/src/mongo/base/error_codes.err index 95076f74414..49cd774b730 100644 --- a/src/mongo/base/error_codes.err +++ b/src/mongo/base/error_codes.err @@ -100,6 +100,7 @@ error_code("NoProjectionFound", 97) error_code("DBPathInUse", 98) error_code("WriteConcernNotDefined", 99) error_code("CannotSatisfyWriteConcern", 100) +error_code("OutdatedClient", 101) # Non-sequential error codes (for compatibility only) error_code("NotMaster", 10107) #this comes from assert_util.h diff --git a/src/mongo/db/auth/user_management_commands_parser.cpp b/src/mongo/db/auth/user_management_commands_parser.cpp index 027f2e4c62a..bd6ec6d025d 100644 --- a/src/mongo/db/auth/user_management_commands_parser.cpp +++ b/src/mongo/db/auth/user_management_commands_parser.cpp @@ -658,6 +658,7 @@ namespace auth { validFieldNames.insert("_mergeAuthzCollections"); validFieldNames.insert("tempUsersCollection"); validFieldNames.insert("tempRolesCollection"); + validFieldNames.insert("db"); validFieldNames.insert("drop"); validFieldNames.insert("writeConcern"); @@ -687,6 +688,17 @@ namespace auth { return status; } + status = bsonExtractStringField(cmdObj, "db", &parsedArgs->db); + if (!status.isOK()) { + if (status == ErrorCodes::NoSuchKey) { + return Status(ErrorCodes::OutdatedClient, + "Missing \"db\" field for _mergeAuthzCollections command. This is " + "most likely due to running an outdated (pre-2.6.4) version of " + "mongorestore."); + } + return status; + } + status = bsonExtractBooleanFieldWithDefault(cmdObj, "drop", false, diff --git a/src/mongo/db/auth/user_management_commands_parser.h b/src/mongo/db/auth/user_management_commands_parser.h index 503d0d21b9b..91cb92200ee 100644 --- a/src/mongo/db/auth/user_management_commands_parser.h +++ b/src/mongo/db/auth/user_management_commands_parser.h @@ -222,6 +222,7 @@ namespace auth { struct MergeAuthzCollectionsArgs { std::string usersCollName; std::string rolesCollName; + std::string db; bool drop; BSONObj writeConcern; MergeAuthzCollectionsArgs() : drop(false) {} @@ -230,7 +231,11 @@ namespace auth { /** * Takes a command object describing an invocation of the "_mergeAuthzCollections" command and * parses out the name of the temporary collections to use for user and role data, whether or - * not to drop the existing users/roles, and the writeConcern. + * not to drop the existing users/roles, the database if this is a for a db-specific restore, + * and the writeConcern. + * Returns ErrorCodes::OutdatedClient if the "db" field is missing, as that likely indicates + * the command was sent by an outdated (pre 2.6.4) version of mongorestore. + * Returns other codes indicating missing or incorrectly typed fields. */ Status parseMergeAuthzCollectionsCommand(const BSONObj& cmdObj, MergeAuthzCollectionsArgs* parsedArgs); diff --git a/src/mongo/db/commands/user_management_commands.cpp b/src/mongo/db/commands/user_management_commands.cpp index 84c771f97c2..b35abc89b1c 100644 --- a/src/mongo/db/commands/user_management_commands.cpp +++ b/src/mongo/db/commands/user_management_commands.cpp @@ -2629,15 +2629,20 @@ namespace mongo { /** * Designed to be used with stdx::bind to be called on every user object in the result * set of a query over the tempUsersCollection provided to the command. For each user - * in the temp collection, adds that user to the actual admin.system.users collection. + * in the temp collection that is defined on the given db, adds that user to the actual + * admin.system.users collection. * Also removes any users it encounters from the usersToDrop set. */ static void addUser(AuthorizationManager* authzManager, + const StringData& db, bool update, const BSONObj& writeConcern, unordered_set<UserName>* usersToDrop, const BSONObj& userObj) { UserName userName = extractUserNameFromBSON(userObj); + if (!db.empty() && userName.getDB() != db) { + return; + } if (update && usersToDrop->count(userName)) { auditCreateOrUpdateUser(userObj, false); @@ -2666,15 +2671,20 @@ namespace mongo { /** * Designed to be used with stdx::bind to be called on every role object in the result * set of a query over the tempRolesCollection provided to the command. For each role - * in the temp collection, adds that role to the actual admin.system.roles collection. + * in the temp collection that is defined on the given db, adds that role to the actual + * admin.system.roles collection. * Also removes any roles it encounters from the rolesToDrop set. */ static void addRole(AuthorizationManager* authzManager, + const StringData& db, bool update, const BSONObj& writeConcern, unordered_set<RoleName>* rolesToDrop, const BSONObj roleObj) { RoleName roleName = extractRoleNameFromBSON(roleObj); + if (!db.empty() && roleName.getDB() != db) { + return; + } if (update && rolesToDrop->count(roleName)) { auditCreateOrUpdateRole(roleObj, false); @@ -2704,6 +2714,7 @@ namespace mongo { */ Status processUsers(AuthorizationManager* authzManager, const StringData& usersCollName, + const StringData& db, bool drop, const BSONObj& writeConcern) { // When the "drop" argument has been provided, we use this set to store the users @@ -2719,12 +2730,14 @@ namespace mongo { if (drop) { // Create map of the users currently in the DB + BSONObj query = db.empty() ? + BSONObj() : BSON(AuthorizationManager::USER_DB_FIELD_NAME << db); BSONObj fields = BSON(AuthorizationManager::USER_NAME_FIELD_NAME << 1 << AuthorizationManager::USER_DB_FIELD_NAME << 1); Status status = authzManager->queryAuthzDocument( AuthorizationManager::usersCollectionNamespace, - BSONObj(), + query, fields, stdx::bind(&CmdMergeAuthzCollections::extractAndInsertUserName, &usersToDrop, @@ -2736,10 +2749,11 @@ namespace mongo { Status status = authzManager->queryAuthzDocument( NamespaceString(usersCollName), - BSONObj(), + db.empty() ? BSONObj() : BSON(AuthorizationManager::USER_DB_FIELD_NAME << db), BSONObj(), stdx::bind(&CmdMergeAuthzCollections::addUser, authzManager, + db, drop, writeConcern, &usersToDrop, @@ -2778,6 +2792,7 @@ namespace mongo { */ Status processRoles(AuthorizationManager* authzManager, const StringData& rolesCollName, + const StringData& db, bool drop, const BSONObj& writeConcern) { // When the "drop" argument has been provided, we use this set to store the roles @@ -2792,12 +2807,14 @@ namespace mongo { if (drop) { // Create map of the roles currently in the DB + BSONObj query = db.empty() ? + BSONObj() : BSON(AuthorizationManager::ROLE_SOURCE_FIELD_NAME << db); BSONObj fields = BSON(AuthorizationManager::ROLE_NAME_FIELD_NAME << 1 << AuthorizationManager::ROLE_SOURCE_FIELD_NAME << 1); Status status = authzManager->queryAuthzDocument( AuthorizationManager::rolesCollectionNamespace, - BSONObj(), + query, fields, stdx::bind(&CmdMergeAuthzCollections::extractAndInsertRoleName, &rolesToDrop, @@ -2809,10 +2826,12 @@ namespace mongo { Status status = authzManager->queryAuthzDocument( NamespaceString(rolesCollName), - BSONObj(), + db.empty() ? + BSONObj() : BSON(AuthorizationManager::ROLE_SOURCE_FIELD_NAME << db), BSONObj(), stdx::bind(&CmdMergeAuthzCollections::addRole, authzManager, + db, drop, writeConcern, &rolesToDrop, @@ -2881,6 +2900,7 @@ namespace mongo { if (!args.usersCollName.empty()) { Status status = processUsers(authzManager, args.usersCollName, + args.db, args.drop, args.writeConcern); if (!status.isOK()) { @@ -2891,6 +2911,7 @@ namespace mongo { if (!args.rolesCollName.empty()) { Status status = processRoles(authzManager, args.rolesCollName, + args.db, args.drop, args.writeConcern); if (!status.isOK()) { diff --git a/src/mongo/tools/restore.cpp b/src/mongo/tools/restore.cpp index d4e2f8339af..9de3fd85eb0 100644 --- a/src/mongo/tools/restore.cpp +++ b/src/mongo/tools/restore.cpp @@ -436,6 +436,13 @@ public: processFileAndMetadata(root, ns); } + static std::string getMessageAboutBrokenDBUserRestore(const StringData& serverBinVersion) { + return str::stream() << "Running mongorestore with --drop, --db, and " + "--restoreDbUsersAndRoles flags has erroneous behavior when the target server " + "version is between 2.6.0 and 2.6.3 (see SERVER-14212). Detected server version " << + serverBinVersion << ". Aborting."; + } + /** * 1) Drop collection if --drop was specified. For system.users or system.roles collections, * however, you don't want to remove all the users/roles up front as some of them may be needed @@ -554,14 +561,27 @@ public: } else { // Use _mergeAuthzCollections command to move into admin.system.users the user // docs that were restored into the temp user collection + BSONObjBuilder cmdBuilder; + cmdBuilder.append("_mergeAuthzCollections", 1); + cmdBuilder.append("tempUsersCollection", mongoRestoreGlobalParams.tempUsersColl); + cmdBuilder.append("drop", mongoRestoreGlobalParams.drop); + cmdBuilder.append("writeConcern", BSON("w" << mongoRestoreGlobalParams.w)); + if (versionCmp(_serverBinVersion, "2.6.4") < 0) { + uassert(18528, + getMessageAboutBrokenDBUserRestore(_serverBinVersion), + !mongoRestoreGlobalParams.drop || + toolGlobalParams.db.empty() || + toolGlobalParams.db == "admin"); + } else { + // If we're doing a db-specific restore of the "admin" db, we want user data for + // *all* databases restored, not just the admin db, so we pass "" as the "db" + // param to _mergeAuthzCollections + cmdBuilder.append("db", + toolGlobalParams.db == "admin" ? "" : toolGlobalParams.db); + } + BSONObj res; - conn().runCommand("admin", - BSON("_mergeAuthzCollections" << 1 << - "tempUsersCollection" << - mongoRestoreGlobalParams.tempUsersColl << - "drop" << mongoRestoreGlobalParams.drop << - "writeConcern" << BSON("w" << mongoRestoreGlobalParams.w)), - res); + conn().runCommand("admin", cmdBuilder.done(), res); uassert(17412, mongoutils::str::stream() << "Cannot restore users because the " "_mergeAuthzCollections command failed: " << res.toString(), @@ -573,14 +593,26 @@ public: if (_curns == "admin.system.roles") { // Use _mergeAuthzCollections command to move into admin.system.roles the role // docs that were restored into the temp roles collection + BSONObjBuilder cmdBuilder; + cmdBuilder.append("_mergeAuthzCollections", 1); + cmdBuilder.append("tempRolesCollection", mongoRestoreGlobalParams.tempRolesColl); + cmdBuilder.append("drop", mongoRestoreGlobalParams.drop); + cmdBuilder.append("writeConcern", BSON("w" << mongoRestoreGlobalParams.w)); + if (versionCmp(_serverBinVersion, "2.6.4") < 0) { + uassert(18529, + getMessageAboutBrokenDBUserRestore(_serverBinVersion), + !mongoRestoreGlobalParams.drop || + toolGlobalParams.db.empty() || + toolGlobalParams.db == "admin"); + } else { + // If we're doing a db-specific restore of the "admin" db, we want role data for + // *all* databases restored, not just the admin db, so we pass "" as the "db" + // param to _mergeAuthzCollections + cmdBuilder.append("db", toolGlobalParams.db == "admin" ? "" : toolGlobalParams.db); + } + BSONObj res; - conn().runCommand("admin", - BSON("_mergeAuthzCollections" << 1 << - "tempRolesCollection" << - mongoRestoreGlobalParams.tempRolesColl << - "drop" << mongoRestoreGlobalParams.drop << - "writeConcern" << BSON("w" << mongoRestoreGlobalParams.w)), - res); + conn().runCommand("admin", cmdBuilder.done(), res); uassert(17413, mongoutils::str::stream() << "Cannot restore roles because the " "_mergeAuthzCollections command failed: " << res.toString(), |