/**
* 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 .
*
* 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.
*/
#define MONGO_LOG_DEFAULT_COMPONENT ::mongo::logger::LogComponent::kCommand
#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"
#include "mongo/db/repl/multicmd.h"
#include "mongo/db/repl/repl_coordinator_global.h"
#include "mongo/util/log.h"
#include "mongo/util/mongoutils/str.h"
#include "mongo/util/version.h"
namespace mongo {
namespace {
Status checkReplicaMemberVersions() {
repl::ReplicationCoordinator* replCoord = repl::getGlobalReplicationCoordinator();
if (replCoord->getReplicationMode() != repl::ReplicationCoordinator::modeReplSet)
return Status::OK();
std::list rsMembers;
std::vector rsMemberHosts = replCoord->getOtherNodesInReplSet();
for (size_t i = 0; i < rsMemberHosts.size(); ++i) {
rsMembers.push_back(repl::Target(rsMemberHosts[i].toString()));
}
try {
multiCommand(BSON("buildInfo" << 1), rsMembers);
}
catch (const DBException& ex) {
return ex.toStatus();
}
for (std::list::const_iterator iter = rsMembers.begin();
iter != rsMembers.end();
++iter) {
if (!iter->ok) {
logger::LogstreamBuilder wlog = warning();
wlog << "During authSchemaUpgrade, could not run buildInfo command on " <<
iter->toHost;
if (!iter->result.isEmpty())
wlog << "; response was " << iter->result.toString();
wlog << "; ignoring.";
continue;
}
const char* version = iter->result["version"].valuestrsafe();
if (!*version) {
return Status(ErrorCodes::RemoteValidationError, mongoutils::str::stream() <<
"Missing or non-string \"version\" field in result of buildInfo "
"command sent to " << iter->toHost << "; found " <<
iter->result["version"]);
}
if (!isSameMajorVersion(version)) {
BSONArray foundVersionArray = toVersionArray(version);
return Status(ErrorCodes::RemoteValidationError, mongoutils::str::stream() <<
"To upgrade auth schema in a replica set, all members must be "
"running the same release series of mongod; found " <<
foundVersionArray["0"] << '.' << foundVersionArray["1"] <<
" on host " << iter->toHost << " but expected " <<
versionArray["0"] << '.' << versionArray["1"]);
}
}
return Status::OK();
}
class CmdAuthSchemaUpgradeD : public CmdAuthSchemaUpgrade {
virtual bool run(
OperationContext* txn,
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 = checkReplicaMemberVersions();
if (!status.isOK())
return appendCommandStatus(result, status);
status = authzManager->upgradeSchema(txn, maxSteps, writeConcern);
if (status.isOK())
result.append("done", true);
return appendCommandStatus(result, status);
}
} cmdAuthSchemaUpgradeStep;
} // namespace
} // namespace mongo