summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--jstests/auth/auth_schema_upgrade.js86
-rw-r--r--jstests/auth/lib/commands_lib.js19
-rw-r--r--jstests/multiVersion/libs/auth_helpers.js4
-rw-r--r--src/mongo/db/auth/action_types.txt1
-rw-r--r--src/mongo/db/auth/role_graph_builtin_roles.cpp3
-rw-r--r--src/mongo/db/auth/user_management_commands_parser.cpp42
-rw-r--r--src/mongo/db/auth/user_management_commands_parser.h14
-rw-r--r--src/mongo/db/commands/user_management_commands.cpp222
-rw-r--r--src/mongo/db/commands/user_management_commands.h1
-rw-r--r--src/mongo/db/commands/user_management_commands_common.cpp8
-rw-r--r--src/mongo/s/commands/cluster_user_management_commands.cpp95
11 files changed, 494 insertions, 1 deletions
diff --git a/jstests/auth/auth_schema_upgrade.js b/jstests/auth/auth_schema_upgrade.js
new file mode 100644
index 00000000000..001e5c04a1b
--- /dev/null
+++ b/jstests/auth/auth_schema_upgrade.js
@@ -0,0 +1,86 @@
+// Standalone test of authSchemaUpgrade
+load('./jstests/multiVersion/libs/auth_helpers.js');
+
+var setupCRUsers = function(conn){
+ jsTest.log("setting up legacy users");
+ var adminDB = conn.getDB('admin');
+
+ adminDB.system.version.update({_id:"authSchema"},{"currentVersion":3},{upsert:true})
+
+ adminDB.createUser({user: 'user1', pwd: 'pass',
+ roles: jsTest.adminUserRoles});
+ assert(adminDB.auth({mechanism: 'MONGODB-CR',
+ user: 'user1', pwd: 'pass'}));
+
+ adminDB.createUser({user: 'user2', pwd: 'pass',
+ roles: jsTest.adminUserRoles});
+ assert(adminDB.auth({mechanism: 'MONGODB-CR',
+ user: 'user2', pwd: 'pass'}));
+
+ // Add $external no-op user to verify that it does not affect
+ // authSchemaUpgrade SERVER-18475
+ adminDB.getSiblingDB('$external').createUser({user: "evil", roles: []});
+
+ jsTest.log("Verifying user documents before upgrading");
+
+ // We haven't run authSchemaUpgrade so there shouldn't be
+ // any stored SCRAM-SHA-1 credentials.
+ verifyUserDoc(adminDB, 'user1', true, false);
+ verifyUserDoc(adminDB, 'user2', true, false);
+ verifyUserDoc(adminDB.getSiblingDB('$external'), "evil", false, false, true);
+
+ adminDB.updateUser('user1', {pwd: 'newpass',
+ roles: jsTest.adminUserRoles});
+ verifyAuth(adminDB, 'user1', 'newpass', true, true);
+
+ verifyUserDoc(adminDB, 'user1', true, false);
+}
+
+var verifySchemaUpgrade = function(adminDB) {
+ // All users should only have SCRAM credentials.
+ verifyUserDoc(adminDB, 'user1', false, true);
+ verifyUserDoc(adminDB, 'user2', false, true);
+ verifyUserDoc(adminDB.getSiblingDB('$external'), "evil", false, false, true);
+
+ // After authSchemaUpgrade MONGODB-CR no longer works.
+ verifyAuth(adminDB, 'user1', 'newpass', false, true);
+ verifyAuth(adminDB, 'user2', 'pass', false, true);
+}
+
+var runAndVerifySchemaUpgrade = function(conn){
+ jsTest.log("run authSchemaUpgrade");
+ var adminDB = conn.getDB('admin');
+
+ assert.commandWorked(adminDB.runCommand('authSchemaUpgrade'));
+ verifySchemaUpgrade(adminDB);
+}
+
+var testAuthSchemaUpgrade = function(conn) {
+ setupCRUsers(conn);
+ runAndVerifySchemaUpgrade(conn);
+}
+
+// Test authSchemaUpgrade and upgrade shards
+var testUpgradeShards = function(mongos, shard) {
+ setupCRUsers(shard);
+
+ assert.commandWorked(mongos.adminCommand({"authSchemaUpgrade":1,"upgradeShards":1}));
+ verifySchemaUpgrade(shard.getDB('admin'));
+}
+
+jsTest.log('Test authSchemUpgrade standalone');
+var conn = MongoRunner.runMongod();
+testAuthSchemaUpgrade(conn);
+MongoRunner.stopMongod(conn);
+
+jsTest.log('Test authSchemUpgrade sharded');
+var dopts = { smallfiles: "", nopreallocj: ""}
+var st = new ShardingTest(
+ { shards: 1,
+ mongos: 1,
+ config: 1,
+ useHostname: false, // Needed when relying on the localhost exception
+ other: { shardOptions: dopts, configOptions: dopts, mongosOptions: { verbose: 1 } } } );
+testAuthSchemaUpgrade(st.s);
+testUpgradeShards(st.s, st.shard0);
+st.stop();
diff --git a/jstests/auth/lib/commands_lib.js b/jstests/auth/lib/commands_lib.js
index 2ce024ade88..f677f5a2ebc 100644
--- a/jstests/auth/lib/commands_lib.js
+++ b/jstests/auth/lib/commands_lib.js
@@ -374,6 +374,25 @@ var authCommandsLib = {
]
},
{
+ testname: "authSchemaUpgrade",
+ command: {authSchemaUpgrade: 1},
+ testcases: [
+ {
+ runOnDb: adminDbName,
+ roles: {
+ userAdminAnyDatabase: 1,
+ root: 1,
+ __system: 1
+ },
+ privileges: [
+ { resource: {cluster: true}, actions: ["authSchemaUpgrade"] }
+ ]
+ },
+ { runOnDb: firstDbName, roles: {} },
+ { runOnDb: secondDbName, roles: {} }
+ ]
+ },
+ {
testname: "buildInfo",
command: {buildInfo: 1},
testcases: [
diff --git a/jstests/multiVersion/libs/auth_helpers.js b/jstests/multiVersion/libs/auth_helpers.js
index 39566da3d1b..cebb2b03e5f 100644
--- a/jstests/multiVersion/libs/auth_helpers.js
+++ b/jstests/multiVersion/libs/auth_helpers.js
@@ -9,10 +9,11 @@ var getUserDoc = function(db, username){
// Verify that the user document for username in db
// has MONGODB-CR credentials (or not) and SCRAM-SHA-1
// credentials (or not).
-var verifyUserDoc = function(db, username, hasCR, hasSCRAM){
+var verifyUserDoc = function(db, username, hasCR, hasSCRAM, hasExternal = false){
var userDoc = getUserDoc(db, username);
assert.eq(hasCR, 'MONGODB-CR' in userDoc.credentials);
assert.eq(hasSCRAM, 'SCRAM-SHA-1' in userDoc.credentials);
+ assert.eq(hasExternal, 'external' in userDoc.credentials);
}
// Verify that that we can authenticate (or not) using MONGODB-CR
@@ -22,4 +23,5 @@ var verifyAuth = function(db, username, password, passCR, passSCRAM){
user: username, pwd: password}));
assert.eq(passSCRAM, db.auth({mechanism: 'SCRAM-SHA-1',
user: username, pwd: password}));
+ db.logout();
}
diff --git a/src/mongo/db/auth/action_types.txt b/src/mongo/db/auth/action_types.txt
index 0e2c2aed753..2d55b878687 100644
--- a/src/mongo/db/auth/action_types.txt
+++ b/src/mongo/db/auth/action_types.txt
@@ -11,6 +11,7 @@
"auditLogRotate", # Not used for permissions checks, but to id the event in logs.
"authCheck", # Not used for permissions checks, but to id the authorization-checking event in logs.
"authenticate", # Not used for permission checks, but to id authentication events in logs.
+"authSchemaUpgrade",
"bypassDocumentValidation", # For bypassDocumentValidation command option.
"changeCustomData",
"changePassword",
diff --git a/src/mongo/db/auth/role_graph_builtin_roles.cpp b/src/mongo/db/auth/role_graph_builtin_roles.cpp
index eff0ce4f9fa..94e3b157346 100644
--- a/src/mongo/db/auth/role_graph_builtin_roles.cpp
+++ b/src/mongo/db/auth/role_graph_builtin_roles.cpp
@@ -291,6 +291,9 @@ void addUserAdminAnyDbPrivileges(PrivilegeVector* privileges) {
privileges, Privilege(ResourcePattern::forClusterResource(), ActionType::listDatabases));
Privilege::addPrivilegeToPrivilegeVector(
privileges,
+ Privilege(ResourcePattern::forClusterResource(), ActionType::authSchemaUpgrade));
+ Privilege::addPrivilegeToPrivilegeVector(
+ privileges,
Privilege(ResourcePattern::forClusterResource(), ActionType::invalidateUserCache));
diff --git a/src/mongo/db/auth/user_management_commands_parser.cpp b/src/mongo/db/auth/user_management_commands_parser.cpp
index 82130e2a530..54109d14bf7 100644
--- a/src/mongo/db/auth/user_management_commands_parser.cpp
+++ b/src/mongo/db/auth/user_management_commands_parser.cpp
@@ -662,5 +662,47 @@ Status parseMergeAuthzCollectionsCommand(const BSONObj& cmdObj,
return Status::OK();
}
+Status parseAuthSchemaUpgradeCommand(const BSONObj& cmdObj,
+ const std::string& dbname,
+ AuthSchemaUpgradeArgs* parsedArgs) {
+ static const int minUpgradeSteps = 1;
+ static const int maxUpgradeSteps = 2;
+
+ unordered_set<std::string> validFieldNames;
+ validFieldNames.insert("authSchemaUpgrade");
+ validFieldNames.insert("maxSteps");
+ validFieldNames.insert("upgradeShards");
+ validFieldNames.insert("writeConcern");
+
+ Status status = _checkNoExtraFields(cmdObj, "authSchemaUpgrade", validFieldNames);
+ if (!status.isOK()) {
+ return status;
+ }
+
+ status = bsonExtractBooleanFieldWithDefault(
+ cmdObj, "upgradeShards", true, &parsedArgs->shouldUpgradeShards);
+ if (!status.isOK()) {
+ return status;
+ }
+
+ long long steps;
+ status = bsonExtractIntegerFieldWithDefault(cmdObj, "maxSteps", maxUpgradeSteps, &steps);
+ if (!status.isOK())
+ return status;
+ if (steps < minUpgradeSteps || steps > maxUpgradeSteps) {
+ return Status(ErrorCodes::BadValue,
+ mongoutils::str::stream() << "Legal values for \"maxSteps\" are at least "
+ << minUpgradeSteps << " and no more than "
+ << maxUpgradeSteps << "; found " << steps);
+ }
+ parsedArgs->maxSteps = static_cast<int>(steps);
+
+ status = _extractWriteConcern(cmdObj, &parsedArgs->writeConcern);
+ if (!status.isOK()) {
+ return status;
+ }
+
+ return Status::OK();
+}
} // namespace auth
} // namespace mongo
diff --git a/src/mongo/db/auth/user_management_commands_parser.h b/src/mongo/db/auth/user_management_commands_parser.h
index c55210e8978..2bc278feecb 100644
--- a/src/mongo/db/auth/user_management_commands_parser.h
+++ b/src/mongo/db/auth/user_management_commands_parser.h
@@ -223,5 +223,19 @@ struct MergeAuthzCollectionsArgs {
Status parseMergeAuthzCollectionsCommand(const BSONObj& cmdObj,
MergeAuthzCollectionsArgs* parsedArgs);
+struct AuthSchemaUpgradeArgs {
+ int maxSteps = 3;
+ bool shouldUpgradeShards = true;
+ BSONObj writeConcern;
+};
+
+/**
+ * Takes a command object describing an invocation of the "authSchemaUpgrade" command and
+ * parses out the write concern, maximum steps to take and whether or not shard servers should
+ * also be upgraded, in the sharded deployment case.
+ */
+Status parseAuthSchemaUpgradeCommand(const BSONObj& cmdObj,
+ const std::string& dbname,
+ AuthSchemaUpgradeArgs* parsedArgs);
} // namespace auth
} // namespace mongo
diff --git a/src/mongo/db/commands/user_management_commands.cpp b/src/mongo/db/commands/user_management_commands.cpp
index cb63736f7c0..a5b1d2373b2 100644
--- a/src/mongo/db/commands/user_management_commands.cpp
+++ b/src/mongo/db/commands/user_management_commands.cpp
@@ -2722,4 +2722,226 @@ public:
} cmdMergeAuthzCollections;
+/**
+ * Logs that the auth schema upgrade failed because of "status" and returns "status".
+ */
+Status logUpgradeFailed(const Status& status) {
+ log() << "Auth schema upgrade failed with " << status;
+ return status;
+}
+
+/**
+ * Updates a single user document from MONGODB-CR to SCRAM credentials.
+ *
+ * Throws a DBException on errors.
+ */
+void updateUserCredentials(OperationContext* txn,
+ const StringData& sourceDB,
+ const BSONObj& userDoc,
+ const BSONObj& writeConcern) {
+ // Skip users in $external, SERVER-18475
+ if (userDoc["db"].String() == "$external") {
+ return;
+ }
+
+ BSONElement credentialsElement = userDoc["credentials"];
+ uassert(18806,
+ mongoutils::str::stream()
+ << "While preparing to upgrade user doc from "
+ "2.6/3.0 user data schema to the 3.0+ SCRAM only schema, found a user doc "
+ "with missing or incorrectly formatted credentials: " << userDoc.toString(),
+ credentialsElement.type() == Object);
+
+ BSONObj credentialsObj = credentialsElement.Obj();
+ BSONElement mongoCRElement = credentialsObj["MONGODB-CR"];
+ BSONElement scramElement = credentialsObj["SCRAM-SHA-1"];
+
+ // Ignore any user documents that already have SCRAM credentials. This should only
+ // occur if a previous authSchemaUpgrade was interrupted halfway.
+ if (!scramElement.eoo()) {
+ return;
+ }
+
+ uassert(18744,
+ mongoutils::str::stream()
+ << "While preparing to upgrade user doc from "
+ "2.6/3.0 user data schema to the 3.0+ SCRAM only schema, found a user doc "
+ "missing MONGODB-CR credentials :" << userDoc.toString(),
+ !mongoCRElement.eoo());
+
+ std::string hashedPassword = mongoCRElement.String();
+
+ BSONObj query = BSON("_id" << userDoc["_id"].String());
+ BSONObjBuilder updateBuilder;
+ {
+ BSONObjBuilder toSetBuilder(updateBuilder.subobjStart("$set"));
+ toSetBuilder << "credentials"
+ << BSON("SCRAM-SHA-1" << scram::generateCredentials(
+ hashedPassword, saslGlobalParams.scramIterationCount));
+ }
+
+ uassertStatusOK(updateOneAuthzDocument(txn,
+ NamespaceString("admin", "system.users"),
+ query,
+ updateBuilder.obj(),
+ true,
+ writeConcern));
+}
+
+/** Loop through all the user documents in the admin.system.users collection.
+ * For each user document:
+ * 1. Compute SCRAM credentials based on the MONGODB-CR hash
+ * 2. Remove the MONGODB-CR hash
+ * 3. Add SCRAM credentials to the user document credentials section
+ */
+Status updateCredentials(OperationContext* txn, const BSONObj& writeConcern) {
+ // Loop through and update the user documents in admin.system.users.
+ Status status = queryAuthzDocument(
+ txn,
+ NamespaceString("admin", "system.users"),
+ BSONObj(),
+ BSONObj(),
+ stdx::bind(updateUserCredentials, txn, "admin", stdx::placeholders::_1, writeConcern));
+ if (!status.isOK())
+ return logUpgradeFailed(status);
+
+ // Update the schema version document.
+ status =
+ updateOneAuthzDocument(txn,
+ AuthorizationManager::versionCollectionNamespace,
+ AuthorizationManager::versionDocumentQuery,
+ BSON("$set" << BSON(AuthorizationManager::schemaVersionFieldName
+ << AuthorizationManager::schemaVersion28SCRAM)),
+ true,
+ writeConcern);
+ if (!status.isOK())
+ return logUpgradeFailed(status);
+
+ return Status::OK();
+}
+
+/**
+ * Performs one step in the process of upgrading the stored authorization data to the
+ * newest schema.
+ *
+ * On success, returns Status::OK(), and *isDone will indicate whether there are more
+ * steps to perform.
+ *
+ * If the authorization data is already fully upgraded, returns Status::OK and sets *isDone
+ * to true, so this is safe to call on a fully upgraded system.
+ *
+ * On failure, returns a status other than Status::OK(). In this case, is is typically safe
+ * to try again.
+ */
+Status upgradeAuthSchemaStep(OperationContext* txn,
+ AuthorizationManager* authzManager,
+ const BSONObj& writeConcern,
+ bool* isDone) {
+ int authzVersion;
+ Status status = authzManager->getAuthorizationVersion(txn, &authzVersion);
+ if (!status.isOK()) {
+ return status;
+ }
+
+ switch (authzVersion) {
+ case AuthorizationManager::schemaVersion26Final:
+ case AuthorizationManager::schemaVersion28SCRAM: {
+ Status status = updateCredentials(txn, writeConcern);
+ if (status.isOK())
+ *isDone = true;
+ return status;
+ }
+ default:
+ return Status(ErrorCodes::AuthSchemaIncompatible,
+ mongoutils::str::stream()
+ << "Do not know how to upgrade auth schema from version "
+ << authzVersion);
+ }
+}
+
+/**
+ * Performs up to maxSteps steps in the process of upgrading the stored authorization data
+ * to the newest schema. Behaves as if by repeatedly calling upgradeSchemaStep up to
+ * maxSteps times until either it completes the upgrade or returns a non-OK status.
+ *
+ * Invalidates the user cache before the first step and after each attempted step.
+ *
+ * Returns Status::OK() to indicate that the upgrade process has completed successfully.
+ * Returns ErrorCodes::OperationIncomplete to indicate that progress was made, but that more
+ * steps must be taken to complete the process. Other returns indicate a failure to make
+ * progress performing the upgrade, and the specific code and message in the returned status
+ * may provide additional information.
+ */
+Status upgradeAuthSchema(OperationContext* txn,
+ AuthorizationManager* authzManager,
+ int maxSteps,
+ const BSONObj& writeConcern) {
+ if (maxSteps < 1) {
+ return Status(ErrorCodes::BadValue,
+ "Minimum value for maxSteps parameter to upgradeAuthSchema is 1");
+ }
+ authzManager->invalidateUserCache();
+ for (int i = 0; i < maxSteps; ++i) {
+ bool isDone;
+ Status status = upgradeAuthSchemaStep(txn, authzManager, writeConcern, &isDone);
+ authzManager->invalidateUserCache();
+ if (!status.isOK() || isDone) {
+ return status;
+ }
+ }
+ return Status(ErrorCodes::OperationIncomplete,
+ mongoutils::str::stream() << "Auth schema upgrade incomplete after " << maxSteps
+ << " successful steps.");
+}
+
+class CmdAuthSchemaUpgrade : public Command {
+public:
+ CmdAuthSchemaUpgrade() : Command("authSchemaUpgrade") {}
+
+ virtual bool slaveOk() const {
+ return false;
+ }
+
+ virtual bool adminOnly() const {
+ return true;
+ }
+
+ virtual bool isWriteCommandForConfigServer() const {
+ return true;
+ }
+
+ virtual void help(stringstream& ss) const {
+ ss << "Upgrades the auth data storage schema";
+ }
+
+ virtual Status checkAuthForCommand(ClientBasic* client,
+ const std::string& dbname,
+ const BSONObj& cmdObj) {
+ return auth::checkAuthForAuthSchemaUpgradeCommand(client);
+ }
+
+ virtual bool run(OperationContext* txn,
+ const string& dbname,
+ BSONObj& cmdObj,
+ int options,
+ string& errmsg,
+ BSONObjBuilder& result) {
+ auth::AuthSchemaUpgradeArgs parsedArgs;
+ Status status = auth::parseAuthSchemaUpgradeCommand(cmdObj, dbname, &parsedArgs);
+ if (!status.isOK()) {
+ return appendCommandStatus(result, status);
+ }
+
+ ServiceContext* serviceContext = txn->getClient()->getServiceContext();
+ AuthorizationManager* authzManager = AuthorizationManager::get(serviceContext);
+
+ stdx::lock_guard<stdx::mutex> lk(getAuthzDataMutex(serviceContext));
+
+ status = upgradeAuthSchema(txn, authzManager, parsedArgs.maxSteps, parsedArgs.writeConcern);
+ if (status.isOK())
+ result.append("done", true);
+ return appendCommandStatus(result, status);
+ }
+
+} cmdAuthSchemaUpgrade;
} // namespace mongo
diff --git a/src/mongo/db/commands/user_management_commands.h b/src/mongo/db/commands/user_management_commands.h
index 63c7a7d8d6c..8ae4e233db9 100644
--- a/src/mongo/db/commands/user_management_commands.h
+++ b/src/mongo/db/commands/user_management_commands.h
@@ -141,6 +141,7 @@ Status checkAuthForGetUserCacheGenerationCommand(ClientBasic* client);
Status checkAuthForMergeAuthzCollectionsCommand(ClientBasic* client, const BSONObj& cmdObj);
+Status checkAuthForAuthSchemaUpgradeCommand(ClientBasic* client);
} // namespace auth
} // namespace mongo
diff --git a/src/mongo/db/commands/user_management_commands_common.cpp b/src/mongo/db/commands/user_management_commands_common.cpp
index 64aee9eca51..09445c5aea7 100644
--- a/src/mongo/db/commands/user_management_commands_common.cpp
+++ b/src/mongo/db/commands/user_management_commands_common.cpp
@@ -522,6 +522,14 @@ Status checkAuthForMergeAuthzCollectionsCommand(ClientBasic* client, const BSONO
return Status::OK();
}
+Status checkAuthForAuthSchemaUpgradeCommand(ClientBasic* client) {
+ AuthorizationSession* authzSession = AuthorizationSession::get(client);
+ if (!authzSession->isAuthorizedForActionsOnResource(ResourcePattern::forClusterResource(),
+ ActionType::authSchemaUpgrade)) {
+ return Status(ErrorCodes::Unauthorized, "Not authorized to run authSchemaUpgrade command.");
+ }
+ return Status::OK();
+}
} // namespace auth
} // namespace mongo
diff --git a/src/mongo/s/commands/cluster_user_management_commands.cpp b/src/mongo/s/commands/cluster_user_management_commands.cpp
index 75fca86dc7a..afb2559aa7c 100644
--- a/src/mongo/s/commands/cluster_user_management_commands.cpp
+++ b/src/mongo/s/commands/cluster_user_management_commands.cpp
@@ -42,6 +42,8 @@
#include "mongo/db/commands.h"
#include "mongo/db/jsobj.h"
#include "mongo/s/catalog/catalog_manager.h"
+#include "mongo/s/catalog/type_shard.h"
+#include "mongo/s/client/shard_registry.h"
#include "mongo/s/grid.h"
namespace mongo {
@@ -803,4 +805,97 @@ public:
} cmdMergeAuthzCollections;
+namespace {
+/**
+ * Runs the authSchemaUpgrade on all shards, with the given maxSteps and writeConcern
+ * parameters.
+ *
+ * Upgrades each shard serially, and stops on first failure. Returned error indicates that
+ * failure.
+ */
+Status runUpgradeOnAllShards(OperationContext* txn, int maxSteps, const BSONObj& writeConcern) {
+ BSONObjBuilder cmdObjBuilder;
+ cmdObjBuilder.append("authSchemaUpgrade", 1);
+ cmdObjBuilder.append("maxSteps", maxSteps);
+ if (!writeConcern.isEmpty()) {
+ cmdObjBuilder.append("writeConcern", writeConcern);
+ }
+ const BSONObj cmdObj = cmdObjBuilder.done();
+
+ // Upgrade each shard in turn, stopping on first failure.
+ vector<ShardType> allShards;
+ Status status = grid.catalogManager(txn)->getAllShards(txn, &allShards);
+ if (!status.isOK()) {
+ return status;
+ }
+ auto shardRegistry = grid.shardRegistry();
+ for (const auto& shardEntry : allShards) {
+ auto cmdResult = shardRegistry->runCommandWithNotMasterRetries(
+ txn, shardEntry.getName(), "admin", cmdObj);
+
+ if (!cmdResult.isOK()) {
+ return Status(status.code(),
+ str::stream() << "Failed to run authSchemaUpgrade on shard "
+ << shardEntry.getName() << causedBy(status));
+ }
+ }
+
+ return Status::OK();
+}
+} // namespace
+
+class CmdAuthSchemaUpgrade : public Command {
+public:
+ CmdAuthSchemaUpgrade() : Command("authSchemaUpgrade") {}
+
+ virtual bool slaveOk() const {
+ return false;
+ }
+
+ virtual bool adminOnly() const {
+ return true;
+ }
+
+ virtual bool isWriteCommandForConfigServer() const {
+ return true;
+ }
+
+ virtual void help(stringstream& ss) const {
+ ss << "Upgrades the auth data storage schema";
+ }
+
+ virtual Status checkAuthForCommand(ClientBasic* client,
+ const std::string& dbname,
+ const BSONObj& cmdObj) {
+ return auth::checkAuthForAuthSchemaUpgradeCommand(client);
+ }
+
+ bool run(OperationContext* txn,
+ const string& dbname,
+ BSONObj& cmdObj,
+ int options,
+ string& errmsg,
+ BSONObjBuilder& result) {
+ // Run the authSchemaUpgrade command on the config servers
+ if (!grid.catalogManager(txn)
+ ->runUserManagementWriteCommand(txn, this->name, dbname, cmdObj, &result)) {
+ return false;
+ }
+
+ auth::AuthSchemaUpgradeArgs parsedArgs;
+ Status status = auth::parseAuthSchemaUpgradeCommand(cmdObj, dbname, &parsedArgs);
+ if (!status.isOK()) {
+ return appendCommandStatus(result, status);
+ }
+
+ // Optionally run the authSchemaUpgrade command on the individual shards
+ if (parsedArgs.shouldUpgradeShards) {
+ status = runUpgradeOnAllShards(txn, parsedArgs.maxSteps, parsedArgs.writeConcern);
+ if (!status.isOK())
+ return appendCommandStatus(result, status);
+ }
+ return true;
+ }
+} cmdAuthSchemaUpgrade;
+
} // namespace mongo