summaryrefslogtreecommitdiff
path: root/src/mongo/db
diff options
context:
space:
mode:
authorAndy Schwerin <schwerin@10gen.com>2013-12-23 12:52:09 -0500
committerAndy Schwerin <schwerin@10gen.com>2014-01-10 13:03:51 -0500
commit74f5f12f83f5dc6e221e4218ebe6832930822627 (patch)
tree3032d4ad7e5cd9cda7ee46760b3b1043df0abad0 /src/mongo/db
parent0abcd25376977c3f63db11446ab0b18c0ce88fff (diff)
downloadmongo-74f5f12f83f5dc6e221e4218ebe6832930822627.tar.gz
SERVER-11995 Have authSchemaUpgrade sent to a mongos upgrade auth schema everywhere.
In particular, sends the commands to each shard, as well as performing the upgrade on the config servers. Also, allow user to specify maximum number of upgrade steps to take on any given shard. Defaults to "as many as necessary". To achieve these changes, separate implementations of the upgrade command are provided to mongos and mognod.
Diffstat (limited to 'src/mongo/db')
-rw-r--r--src/mongo/db/auth/authorization_manager.cpp19
-rw-r--r--src/mongo/db/auth/authorization_manager.h17
-rw-r--r--src/mongo/db/auth/authorization_manager_test.cpp15
-rw-r--r--src/mongo/db/auth/user_management_commands_parser.cpp28
-rw-r--r--src/mongo/db/auth/user_management_commands_parser.h9
-rw-r--r--src/mongo/db/commands.cpp25
-rw-r--r--src/mongo/db/commands.h5
-rw-r--r--src/mongo/db/commands/auth_schema_upgrade_d.cpp80
-rw-r--r--src/mongo/db/commands/user_management_commands.cpp80
-rw-r--r--src/mongo/db/commands/user_management_commands.h50
10 files changed, 259 insertions, 69 deletions
diff --git a/src/mongo/db/auth/authorization_manager.cpp b/src/mongo/db/auth/authorization_manager.cpp
index ef8777c1ec3..b85b7844b75 100644
--- a/src/mongo/db/auth/authorization_manager.cpp
+++ b/src/mongo/db/auth/authorization_manager.cpp
@@ -1290,6 +1290,25 @@ namespace {
}
}
+ Status AuthorizationManager::upgradeSchema(int maxSteps, const BSONObj& writeConcern) {
+
+ if (maxSteps < 1) {
+ return Status(ErrorCodes::BadValue,
+ "Minimum value for maxSteps parameter to upgradeSchema is 1");
+ }
+ invalidateUserCache();
+ for (int i = 0; i < maxSteps; ++i) {
+ bool isDone;
+ Status status = upgradeSchemaStep(writeConcern, &isDone);
+ invalidateUserCache();
+ if (!status.isOK() || isDone) {
+ return status;
+ }
+ }
+ return Status(ErrorCodes::OperationIncomplete, mongoutils::str::stream() <<
+ "Auth schema upgrade incomplete after " << maxSteps << " successful steps.");
+ }
+
namespace {
bool isAuthzNamespace(const StringData& ns) {
return (ns == AuthorizationManager::rolesCollectionNamespace.ns() ||
diff --git a/src/mongo/db/auth/authorization_manager.h b/src/mongo/db/auth/authorization_manager.h
index 976dd7aa232..e6bcefe06bd 100644
--- a/src/mongo/db/auth/authorization_manager.h
+++ b/src/mongo/db/auth/authorization_manager.h
@@ -392,7 +392,7 @@ namespace mongo {
void releaseAuthzUpdateLock();
/**
- * Perform one step in the process of upgrading the stored authorization data to the
+ * 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
@@ -407,6 +407,21 @@ namespace mongo {
Status upgradeSchemaStep(const BSONObj& writeConcern, bool* isDone);
/**
+ * 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 upgradeSchema(int maxSteps, const BSONObj& writeConcern);
+
+ /**
* Hook called by replication code to let the AuthorizationManager observe changes
* to relevant collections.
*/
diff --git a/src/mongo/db/auth/authorization_manager_test.cpp b/src/mongo/db/auth/authorization_manager_test.cpp
index 368a733f162..e0833660d98 100644
--- a/src/mongo/db/auth/authorization_manager_test.cpp
+++ b/src/mongo/db/auth/authorization_manager_test.cpp
@@ -538,12 +538,7 @@ namespace {
}
void upgradeAuthCollections() {
- bool done = false;
- int iters = 0;
- while (!done) {
- ASSERT_OK(authzManager->upgradeSchemaStep(BSONObj(), &done));
- ASSERT_LESS_THAN(iters++, 10);
- }
+ ASSERT_OK(authzManager->upgradeSchema(10, BSONObj()));
}
};
@@ -556,6 +551,14 @@ namespace {
validateV1AdminUserData(AuthorizationManager::usersBackupCollectionNamespace);
}
+ TEST_F(AuthzUpgradeTest, upgradeUserDataFromV1ToV2TakesTwoSteps) {
+ externalState->setAuthzVersion(AuthorizationManager::schemaVersion24);
+ setUpV1UserData();
+ ASSERT_EQUALS(ErrorCodes::OperationIncomplete,
+ authzManager->upgradeSchema(1, BSONObj()));
+ ASSERT_OK(authzManager->upgradeSchema(1, BSONObj()));
+ }
+
TEST_F(AuthzUpgradeTest, upgradeUserDataFromV1ToV2WithSysVerDoc) {
externalState->setAuthzVersion(AuthorizationManager::schemaVersion24);
setUpV1UserData();
diff --git a/src/mongo/db/auth/user_management_commands_parser.cpp b/src/mongo/db/auth/user_management_commands_parser.cpp
index 61023fe6a25..e62fc324007 100644
--- a/src/mongo/db/auth/user_management_commands_parser.cpp
+++ b/src/mongo/db/auth/user_management_commands_parser.cpp
@@ -643,16 +643,40 @@ namespace auth {
Status parseAuthSchemaUpgradeStepCommand(const BSONObj& cmdObj,
const std::string& dbname,
+ int* maxSteps,
+ bool* shouldUpgradeShards,
BSONObj* parsedWriteConcern) {
+ static const int minUpgradeSteps = 1;
+ static const int maxUpgradeSteps = 2;
+
unordered_set<std::string> validFieldNames;
- validFieldNames.insert("authSchemaUpgradeStep");
+ validFieldNames.insert("authSchemaUpgrade");
+ validFieldNames.insert("maxSteps");
+ validFieldNames.insert("upgradeShards");
validFieldNames.insert("writeConcern");
- Status status = _checkNoExtraFields(cmdObj, "authSchemaUpgradeStep", validFieldNames);
+ Status status = _checkNoExtraFields(cmdObj, "authSchemaUpgrade", validFieldNames);
+ if (!status.isOK()) {
+ return status;
+ }
+
+ status = bsonExtractBooleanFieldWithDefault(
+ cmdObj, "upgradeShards", true, 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);
+ }
+ *maxSteps = static_cast<int>(steps);
+
status = _extractWriteConcern(cmdObj, parsedWriteConcern);
if (!status.isOK()) {
return status;
diff --git a/src/mongo/db/auth/user_management_commands_parser.h b/src/mongo/db/auth/user_management_commands_parser.h
index befeb2731ba..0f0a9696251 100644
--- a/src/mongo/db/auth/user_management_commands_parser.h
+++ b/src/mongo/db/auth/user_management_commands_parser.h
@@ -26,6 +26,8 @@
* it in the license file.
*/
+#pragma once
+
#include <string>
#include <vector>
@@ -180,11 +182,14 @@ namespace auth {
BSONObj* parsedWriteConcern);
/**
- * Takes a command object describing an invocation of the "authSchemaUpgradeStep" command and
- * parses out the write concern.
+ * 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 parseAuthSchemaUpgradeStepCommand(const BSONObj& cmdObj,
const std::string& dbname,
+ int* maxSteps,
+ bool* shouldUpgradeShards,
BSONObj* parsedWriteConcern);
/**
* Parses the privileges described in "privileges" into a vector of Privilege objects.
diff --git a/src/mongo/db/commands.cpp b/src/mongo/db/commands.cpp
index c80aafcd45b..7d071d80e6b 100644
--- a/src/mongo/db/commands.cpp
+++ b/src/mongo/db/commands.cpp
@@ -226,6 +226,31 @@ namespace mongo {
}
}
+ Status Command::getStatusFromCommandResult(const BSONObj& result) {
+ BSONElement okElement = result["ok"];
+ BSONElement codeElement = result["code"];
+ BSONElement errmsgElement = result["errmsg"];
+ if (okElement.eoo()) {
+ return Status(ErrorCodes::CommandResultSchemaViolation,
+ mongoutils::str::stream() << "No \"ok\" field in command result " <<
+ result);
+ }
+ if (okElement.trueValue()) {
+ return Status::OK();
+ }
+ int code = codeElement.numberInt();
+ if (0 == code)
+ code = ErrorCodes::UnknownError;
+ std::string errmsg;
+ if (errmsgElement.type() == String) {
+ errmsg = errmsgElement.String();
+ }
+ else if (!errmsgElement.eoo()) {
+ errmsg = errmsgElement.toString();
+ }
+ return Status(ErrorCodes::Error(code), errmsg);
+ }
+
Status Command::checkAuthForCommand(ClientBasic* client,
const std::string& dbname,
const BSONObj& cmdObj) {
diff --git a/src/mongo/db/commands.h b/src/mongo/db/commands.h
index 46c4c1a1c2f..bf229ccc42c 100644
--- a/src/mongo/db/commands.h
+++ b/src/mongo/db/commands.h
@@ -227,6 +227,11 @@ namespace mutablebson {
// @return s.isOK()
static bool appendCommandStatus(BSONObjBuilder& result, const Status& status);
+ // Converts "result" into a Status object. The input is expected to be the object returned
+ // by running a command. Returns ErrorCodes::CommandResultSchemaViolation if "result" does
+ // not look like the result of a command.
+ static Status getStatusFromCommandResult(const BSONObj& result);
+
// Set by command line. Controls whether or not testing-only commands should be available.
static int testCommandsEnabled;
diff --git a/src/mongo/db/commands/auth_schema_upgrade_d.cpp b/src/mongo/db/commands/auth_schema_upgrade_d.cpp
new file mode 100644
index 00000000000..13596980ff7
--- /dev/null
+++ b/src/mongo/db/commands/auth_schema_upgrade_d.cpp
@@ -0,0 +1,80 @@
+/**
+ * Copyright (C) 2013 MongoDB Inc.
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU Affero General Public License, version 3,
+ * as published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU Affero General Public License for more details.
+ *
+ * You should have received a copy of the GNU Affero General Public License
+ * along with this program. If not, see <http://www.gnu.org/licenses/>.
+ *
+ * As a special exception, the copyright holders give permission to link the
+ * code of portions of this program with the OpenSSL library under certain
+ * conditions as described in each individual source file and distribute
+ * linked combinations including the program with the OpenSSL library. You
+ * must comply with the GNU Affero General Public License in all respects
+ * for all of the code used other than as permitted herein. If you modify
+ * file(s) with this exception, you may extend this exception to your
+ * version of the file(s), but you are not obligated to do so. If you do not
+ * wish to do so, delete this exception statement from your version. If you
+ * delete this exception statement from all source files in the program,
+ * then also delete it in the license file.
+ */
+
+#include "mongo/platform/basic.h"
+
+#include "mongo/db/auth/authorization_manager.h"
+#include "mongo/db/auth/authorization_manager_global.h"
+#include "mongo/db/auth/authz_documents_update_guard.h"
+#include "mongo/db/auth/user_management_commands_parser.h"
+#include "mongo/db/commands/user_management_commands.h"
+
+namespace mongo {
+namespace {
+
+ class CmdAuthSchemaUpgradeD : public CmdAuthSchemaUpgrade {
+ virtual bool run(
+ const string& dbname,
+ BSONObj& cmdObj,
+ int options,
+ string& errmsg,
+ BSONObjBuilder& result,
+ bool fromRepl) {
+
+ int maxSteps;
+ bool upgradeShardServers;
+ BSONObj writeConcern;
+ Status status = auth::parseAuthSchemaUpgradeStepCommand(
+ cmdObj,
+ dbname,
+ &maxSteps,
+ &upgradeShardServers,
+ &writeConcern);
+ if (!status.isOK()) {
+ return appendCommandStatus(result, status);
+ }
+
+ AuthorizationManager* authzManager = getGlobalAuthorizationManager();
+
+ AuthzDocumentsUpdateGuard updateGuard(authzManager);
+ if (!updateGuard.tryLock("auth schema upgrade")) {
+ return appendCommandStatus(
+ result,
+ Status(ErrorCodes::LockBusy, "Could not lock auth data update lock."));
+ }
+
+ status = authzManager->upgradeSchema(maxSteps, writeConcern);
+ if (status.isOK())
+ result.append("done", true);
+ return appendCommandStatus(result, status);
+ }
+
+ } cmdAuthSchemaUpgradeStep;
+
+} // namespace
+} // namespace mongo
diff --git a/src/mongo/db/commands/user_management_commands.cpp b/src/mongo/db/commands/user_management_commands.cpp
index ba772e08de5..794b4cbcef0 100644
--- a/src/mongo/db/commands/user_management_commands.cpp
+++ b/src/mongo/db/commands/user_management_commands.cpp
@@ -26,6 +26,10 @@
* it in the license file.
*/
+#include "mongo/platform/basic.h"
+
+#include "mongo/db/commands/user_management_commands.h"
+
#include <boost/bind.hpp>
#include <boost/function.hpp>
#include <string>
@@ -2420,67 +2424,27 @@ namespace mongo {
} cmdInvalidateUserCache;
- class CmdAuthSchemaUpgradeStep : public Command {
- public:
- CmdAuthSchemaUpgradeStep() : Command("authSchemaUpgradeStep") {}
-
- virtual bool slaveOk() const { return false; }
- virtual bool adminOnly() const { return true; }
- virtual LockType locktype() const { return NONE; }
-
- virtual void help(stringstream& ss) const {
- ss << "Performs the next step in the process of upgrading the auth schema.";
- }
-
- virtual Status checkAuthForCommand(ClientBasic* client,
- const std::string& dbname,
- const BSONObj& cmdObj) {
-
- AuthorizationSession* authzSession = client->getAuthorizationSession();
- if (!authzSession->isAuthorizedForActionsOnResource(
- ResourcePattern::forClusterResource(), ActionType::authSchemaUpgrade)) {
- return Status(ErrorCodes::Unauthorized,
- "Not authorized to run authSchemaUpgradeStep command.");
- }
- return Status::OK();
- }
-
- virtual bool run(
- const string& dbname,
- BSONObj& cmdObj,
- int options,
- string& errmsg,
- BSONObjBuilder& result,
- bool fromRepl) {
-
- BSONObj writeConcern;
- Status status = auth::parseAuthSchemaUpgradeStepCommand(cmdObj, dbname, &writeConcern);
- if (!status.isOK()) {
- return appendCommandStatus(result, status);
- }
+ CmdAuthSchemaUpgrade::CmdAuthSchemaUpgrade() : Command("authSchemaUpgrade") {}
+ CmdAuthSchemaUpgrade::~CmdAuthSchemaUpgrade() {}
- AuthorizationManager* authzManager = getGlobalAuthorizationManager();
+ bool CmdAuthSchemaUpgrade::slaveOk() const { return false; }
+ bool CmdAuthSchemaUpgrade::adminOnly() const { return true; }
+ Command::LockType CmdAuthSchemaUpgrade::locktype() const { return NONE; }
- AuthzDocumentsUpdateGuard updateGuard(authzManager);
- if (!updateGuard.tryLock("auth schema upgrade")) {
- return appendCommandStatus(
- result,
- Status(ErrorCodes::LockBusy, "Could not lock auth data update lock."));
- }
- authzManager->invalidateUserCache();
+ void CmdAuthSchemaUpgrade::help(stringstream& ss) const {
+ ss << "Upgrades the auth data storage schema";
+ }
- bool done;
- status = authzManager->upgradeSchemaStep(writeConcern, &done);
- // Must invalidate even on bad status - what if the write succeeded but the GLE failed?
- authzManager->invalidateUserCache();
- if (!status.isOK()) {
- appendCommandStatus(result, status);
- return false;
- }
+ Status CmdAuthSchemaUpgrade::checkAuthForCommand(ClientBasic* client,
+ const std::string& dbname,
+ const BSONObj& cmdObj) {
- result.append("done", done);
- return true;
+ AuthorizationSession* authzSession = client->getAuthorizationSession();
+ if (!authzSession->isAuthorizedForActionsOnResource(
+ ResourcePattern::forClusterResource(), ActionType::authSchemaUpgrade)) {
+ return Status(ErrorCodes::Unauthorized,
+ "Not authorized to run authSchemaUpgrade command.");
}
-
- } cmdAuthSchemaUpgradeStep;
+ return Status::OK();
+ }
}
diff --git a/src/mongo/db/commands/user_management_commands.h b/src/mongo/db/commands/user_management_commands.h
new file mode 100644
index 00000000000..39b74cab4b3
--- /dev/null
+++ b/src/mongo/db/commands/user_management_commands.h
@@ -0,0 +1,50 @@
+/**
+ * Copyright (C) 2013 MongoDB Inc.
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU Affero General Public License, version 3,
+ * as published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU Affero General Public License for more details.
+ *
+ * You should have received a copy of the GNU Affero General Public License
+ * along with this program. If not, see <http://www.gnu.org/licenses/>.
+ *
+ * As a special exception, the copyright holders give permission to link the
+ * code of portions of this program with the OpenSSL library under certain
+ * conditions as described in each individual source file and distribute
+ * linked combinations including the program with the OpenSSL library. You
+ * must comply with the GNU Affero General Public License in all respects
+ * for all of the code used other than as permitted herein. If you modify
+ * file(s) with this exception, you may extend this exception to your
+ * version of the file(s), but you are not obligated to do so. If you do not
+ * wish to do so, delete this exception statement from your version. If you
+ * delete this exception statement from all source files in the program,
+ * then also delete it in the license file.
+ */
+
+#pragma once
+
+#include "mongo/db/commands.h"
+
+namespace mongo {
+
+ class CmdAuthSchemaUpgrade : public Command {
+ public:
+ CmdAuthSchemaUpgrade();
+ virtual ~CmdAuthSchemaUpgrade();
+
+ virtual bool slaveOk() const;
+ virtual bool adminOnly() const;
+ virtual LockType locktype() const;
+ virtual void help(stringstream& ss) const;
+
+ virtual Status checkAuthForCommand(ClientBasic* client,
+ const std::string& dbname,
+ const BSONObj& cmdObj);
+ };
+
+} // namespace mongo