summaryrefslogtreecommitdiff
path: root/src
diff options
context:
space:
mode:
authorSpencer T Brody <spencer@mongodb.com>2014-07-17 17:44:23 -0400
committerSpencer T Brody <spencer@mongodb.com>2014-07-18 14:54:20 -0400
commit7dbc49a56d010ceaf6d9b57f373863763fe10280 (patch)
tree7b73dd77eef3e3d3649b5c0a04605387c5fc112e /src
parent4b12168d5c14f0177586eeda8b22fba433d6df1f (diff)
downloadmongo-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.err1
-rw-r--r--src/mongo/db/auth/user_management_commands_parser.cpp12
-rw-r--r--src/mongo/db/auth/user_management_commands_parser.h7
-rw-r--r--src/mongo/db/commands/user_management_commands.cpp33
-rw-r--r--src/mongo/tools/restore.cpp60
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(),