summaryrefslogtreecommitdiff
path: root/src/mongo/db/commands
diff options
context:
space:
mode:
authorBilly Donahue <billy.donahue@mongodb.com>2018-04-05 16:53:12 -0400
committerBilly Donahue <billy.donahue@mongodb.com>2018-04-05 17:00:34 -0400
commit8b5a91133ea5ab272fed72b5d2ca0574899c270b (patch)
tree5dfd80a40781d1a3d0a9d8fc106795153f69f0b4 /src/mongo/db/commands
parentbafdf4c440267d22bbf8052689fbcc4f1d0a123d (diff)
downloadmongo-8b5a91133ea5ab272fed72b5d2ca0574899c270b.tar.gz
SERVER-33881 move checkAuthorization to CommandInvocation
Explain commands already have a parsed _innerInvocation so they can now use that for their auth check.
Diffstat (limited to 'src/mongo/db/commands')
-rw-r--r--src/mongo/db/commands/explain_cmd.cpp37
-rw-r--r--src/mongo/db/commands/oplog_application_checks.cpp13
-rw-r--r--src/mongo/db/commands/write_commands/write_commands.cpp50
-rw-r--r--src/mongo/db/commands/write_commands/write_commands_common.cpp118
-rw-r--r--src/mongo/db/commands/write_commands/write_commands_common.h9
5 files changed, 98 insertions, 129 deletions
diff --git a/src/mongo/db/commands/explain_cmd.cpp b/src/mongo/db/commands/explain_cmd.cpp
index 38bcfc2c7f5..0b2ad475968 100644
--- a/src/mongo/db/commands/explain_cmd.cpp
+++ b/src/mongo/db/commands/explain_cmd.cpp
@@ -87,37 +87,13 @@ public:
return explainedCommand->parseNs(dbname, explainedObj);
}
- /**
- * You are authorized to run an explain if you are authorized to run
- * the command that you are explaining. The auth check is performed recursively
- * on the nested command.
- */
- Status checkAuthForRequest(OperationContext* opCtx,
- const OpMsgRequest& request) const override {
- CommandHelpers::uassertNoDocumentSequences(getName(), request);
- const BSONObj& cmdObj = request.body;
- if (Object != cmdObj.firstElement().type()) {
- return Status(ErrorCodes::BadValue, "explain command requires a nested object");
- }
- auto explainedObj = cmdObj.firstElement().Obj();
- auto explainedCommand = CommandHelpers::findCommand(explainedObj.firstElementFieldName());
- if (!explainedCommand) {
- return Status(ErrorCodes::CommandNotFound,
- str::stream() << "unknown command: "
- << explainedObj.firstElementFieldName());
- }
- return explainedCommand->checkAuthForRequest(
- opCtx,
- OpMsgRequest::fromDBAndBody(request.getDatabase().toString(), std::move(explainedObj)));
- }
-
private:
class Invocation;
};
class CmdExplain::Invocation final : public CommandInvocation {
public:
- Invocation(CmdExplain* explainCommand,
+ Invocation(const CmdExplain* explainCommand,
const OpMsgRequest& request,
ExplainOptions::Verbosity verbosity,
std::unique_ptr<OpMsgRequest> innerRequest,
@@ -163,8 +139,13 @@ public:
return command()->secondaryAllowed(context);
}
+ /**
+ * You are authorized to run an explain if you are authorized to run
+ * the command that you are explaining. The auth check is performed recursively
+ * on the nested command.
+ */
void doCheckAuthorization(OperationContext* opCtx) const override {
- uassertStatusOK(command()->checkAuthForRequest(opCtx, *_outerRequest));
+ _innerInvocation->checkAuthorization(opCtx, *_innerRequest);
}
private:
@@ -186,6 +167,9 @@ std::unique_ptr<CommandInvocation> CmdExplain::parse(OperationContext* opCtx,
std::string dbname = request.getDatabase().toString();
const BSONObj& cmdObj = request.body;
ExplainOptions::Verbosity verbosity = uassertStatusOK(ExplainOptions::parseCmdBSON(cmdObj));
+ uassert(ErrorCodes::BadValue,
+ "explain command requires a nested object",
+ cmdObj.firstElement().type() == Object);
auto explainedObj = cmdObj.firstElement().Obj();
if (auto innerDb = explainedObj["$db"]) {
uassert(ErrorCodes::InvalidNamespace,
@@ -199,7 +183,6 @@ std::unique_ptr<CommandInvocation> CmdExplain::parse(OperationContext* opCtx,
str::stream() << "Explain failed due to unknown command: "
<< explainedObj.firstElementFieldName(),
explainedCommand);
-
auto innerRequest =
stdx::make_unique<OpMsgRequest>(OpMsgRequest::fromDBAndBody(dbname, explainedObj));
auto innerInvocation = explainedCommand->parse(opCtx, *innerRequest);
diff --git a/src/mongo/db/commands/oplog_application_checks.cpp b/src/mongo/db/commands/oplog_application_checks.cpp
index 4ef23c19a2c..1a001805f55 100644
--- a/src/mongo/db/commands/oplog_application_checks.cpp
+++ b/src/mongo/db/commands/oplog_application_checks.cpp
@@ -89,8 +89,17 @@ Status OplogApplicationChecks::checkOperationAuthorization(OperationContext* opC
dbNameForAuthCheck = "admin";
}
- return Command::checkAuthorization(
- commandInOplogEntry, opCtx, OpMsgRequest::fromDBAndBody(dbNameForAuthCheck, o));
+ // TODO reuse the parse result for when we run() later. Note that when running,
+ // we must use a potentially different dbname.
+ return [&] {
+ try {
+ auto request = OpMsgRequest::fromDBAndBody(dbNameForAuthCheck, o);
+ commandInOplogEntry->parse(opCtx, request)->checkAuthorization(opCtx, request);
+ return Status::OK();
+ } catch (const DBException& e) {
+ return e.toStatus();
+ }
+ }();
}
if (opType == "i"_sd) {
diff --git a/src/mongo/db/commands/write_commands/write_commands.cpp b/src/mongo/db/commands/write_commands/write_commands.cpp
index 0f9919e038a..6f0f2c72a6e 100644
--- a/src/mongo/db/commands/write_commands/write_commands.cpp
+++ b/src/mongo/db/commands/write_commands/write_commands.cpp
@@ -71,17 +71,6 @@ void redactTooLongLog(mutablebson::Document* cmdObj, StringData fieldName) {
}
}
-Status checkAuthForWriteCommand(Client* client,
- BatchedCommandRequest::BatchType batchType,
- const OpMsgRequest& request) {
- Status status =
- auth::checkAuthForWriteCommand(AuthorizationSession::get(client), batchType, request);
- if (!status.isOK()) {
- LastError::get(client).setLastError(status.code(), status.reason());
- }
- return status;
-}
-
bool shouldSkipOutput(OperationContext* opCtx) {
const WriteConcernOptions& writeConcern = opCtx->getWriteConcern();
return writeConcern.wMode.empty() && writeConcern.wNumNodes == 0 &&
@@ -202,7 +191,7 @@ public:
explicit WriteCommand(StringData name) : Command(name) {}
std::unique_ptr<CommandInvocation> parse(OperationContext* opCtx,
- const OpMsgRequest& request) override;
+ const OpMsgRequest& request) final;
AllowedOnSecondary secondaryAllowed(ServiceContext*) const final {
return AllowedOnSecondary::kNever;
@@ -229,6 +218,8 @@ public:
private:
class Invocation;
+
+ virtual BatchedCommandRequest::BatchType writeType() const = 0;
};
class WriteCommand::Invocation : public CommandInvocation {
@@ -277,7 +268,13 @@ private:
}
void doCheckAuthorization(OperationContext* opCtx) const final {
- uassertStatusOK(command()->checkAuthForRequest(opCtx, *_request));
+ try {
+ auth::checkAuthForWriteCommand(
+ AuthorizationSession::get(opCtx->getClient()), command()->writeType(), *_request);
+ } catch (const DBException& e) {
+ LastError::get(opCtx->getClient()).setLastError(e.code(), e.reason());
+ throw;
+ }
}
const WriteCommand* command() const {
@@ -306,11 +303,6 @@ public:
return "insert documents";
}
- Status checkAuthForRequest(OperationContext* opCtx, const OpMsgRequest& request) const final {
- return checkAuthForWriteCommand(
- opCtx->getClient(), BatchedCommandRequest::BatchType_Insert, request);
- }
-
void runImpl(OperationContext* opCtx,
const OpMsgRequest& request,
BSONObjBuilder& result) const final {
@@ -323,6 +315,10 @@ public:
std::move(reply),
&result);
}
+
+ BatchedCommandRequest::BatchType writeType() const override {
+ return BatchedCommandRequest::BatchType_Insert;
+ }
} cmdInsert;
class CmdUpdate final : public WriteCommand {
@@ -337,11 +333,6 @@ public:
return "update documents";
}
- Status checkAuthForRequest(OperationContext* opCtx, const OpMsgRequest& request) const final {
- return checkAuthForWriteCommand(
- opCtx->getClient(), BatchedCommandRequest::BatchType_Update, request);
- }
-
void runImpl(OperationContext* opCtx,
const OpMsgRequest& request,
BSONObjBuilder& result) const final {
@@ -388,6 +379,10 @@ public:
Explain::explainStages(exec.get(), collection.getCollection(), verbosity, out);
return Status::OK();
}
+
+ BatchedCommandRequest::BatchType writeType() const override {
+ return BatchedCommandRequest::BatchType_Update;
+ }
} cmdUpdate;
class CmdDelete final : public WriteCommand {
@@ -402,11 +397,6 @@ public:
return "delete documents";
}
- Status checkAuthForRequest(OperationContext* opCtx, const OpMsgRequest& request) const final {
- return checkAuthForWriteCommand(
- opCtx->getClient(), BatchedCommandRequest::BatchType_Delete, request);
- }
-
void runImpl(OperationContext* opCtx,
const OpMsgRequest& request,
BSONObjBuilder& result) const final {
@@ -449,6 +439,10 @@ public:
Explain::explainStages(exec.get(), collection.getCollection(), verbosity, out);
return Status::OK();
}
+
+ BatchedCommandRequest::BatchType writeType() const override {
+ return BatchedCommandRequest::BatchType_Delete;
+ }
} cmdDelete;
} // namespace
diff --git a/src/mongo/db/commands/write_commands/write_commands_common.cpp b/src/mongo/db/commands/write_commands/write_commands_common.cpp
index 98acc3dd66b..5abef4c1db6 100644
--- a/src/mongo/db/commands/write_commands/write_commands_common.cpp
+++ b/src/mongo/db/commands/write_commands/write_commands_common.cpp
@@ -30,6 +30,7 @@
#include "mongo/db/commands/write_commands/write_commands_common.h"
+#include <algorithm>
#include <string>
#include <vector>
@@ -44,52 +45,28 @@ namespace mongo {
namespace auth {
namespace {
-using write_ops::Delete;
-using write_ops::Insert;
-using write_ops::Update;
-using write_ops::UpdateOpEntry;
-
-/**
- * Helper to determine whether or not there are any upserts in the batch
- */
-bool containsUpserts(const std::vector<UpdateOpEntry>& updates) {
- for (auto&& update : updates) {
- if (update.getUpsert())
- return true;
- }
-
- return false;
-}
-
/**
* Helper to extract the namespace being indexed from a raw BSON write command.
*
* TODO: Remove when we have parsing hooked before authorization.
*/
-StatusWith<NamespaceString> getIndexedNss(const std::vector<BSONObj>& documentsToInsert) {
- if (documentsToInsert.empty()) {
- return {ErrorCodes::FailedToParse, "index write batch is empty"};
- }
-
- const std::string nsToIndex = documentsToInsert.front()["ns"].str();
- if (nsToIndex.empty()) {
- return {ErrorCodes::FailedToParse,
- "index write batch contains an invalid index descriptor"};
- }
-
- if (documentsToInsert.size() != 1) {
- return {ErrorCodes::FailedToParse,
- "index write batches may only contain a single index descriptor"};
- }
-
- return {NamespaceString(std::move(nsToIndex))};
+NamespaceString getIndexedNss(const std::vector<BSONObj>& documentsToInsert) {
+ uassert(ErrorCodes::FailedToParse, "index write batch is empty", !documentsToInsert.empty());
+ std::string nsToIndex = documentsToInsert.front()["ns"].str();
+ uassert(ErrorCodes::FailedToParse,
+ "index write batch contains an invalid index descriptor",
+ !nsToIndex.empty());
+ uassert(ErrorCodes::FailedToParse,
+ "index write batches may only contain a single index descriptor",
+ documentsToInsert.size() == 1);
+ return NamespaceString(std::move(nsToIndex));
}
} // namespace
-Status checkAuthForWriteCommand(AuthorizationSession* authzSession,
- BatchedCommandRequest::BatchType cmdType,
- const OpMsgRequest& request) {
+void checkAuthForWriteCommand(AuthorizationSession* authzSession,
+ BatchedCommandRequest::BatchType cmdType,
+ const OpMsgRequest& request) {
std::vector<Privilege> privileges;
ActionSet actionsOnCommandNSS;
@@ -98,46 +75,49 @@ Status checkAuthForWriteCommand(AuthorizationSession* authzSession,
}
NamespaceString cmdNSS;
- if (cmdType == BatchedCommandRequest::BatchType_Insert) {
- auto op = Insert::parse(IDLParserErrorContext("insert"), request);
- cmdNSS = op.getNamespace();
- if (!op.getNamespace().isSystemDotIndexes()) {
- actionsOnCommandNSS.addAction(ActionType::insert);
- } else {
- // Special-case indexes until we have a command
- const auto swNssToIndex = getIndexedNss(op.getDocuments());
- if (!swNssToIndex.isOK()) {
- return swNssToIndex.getStatus();
- }
- const auto& nssToIndex = swNssToIndex.getValue();
- privileges.push_back(
- Privilege(ResourcePattern::forExactNamespace(nssToIndex), ActionType::createIndex));
+ switch (cmdType) {
+ case BatchedCommandRequest::BatchType_Insert: {
+ auto op = write_ops::Insert::parse(IDLParserErrorContext("insert"), request);
+ cmdNSS = op.getNamespace();
+ if (!op.getNamespace().isSystemDotIndexes()) {
+ actionsOnCommandNSS.addAction(ActionType::insert);
+ } else {
+ // Special-case indexes until we have a command
+ auto nssToIndex = getIndexedNss(op.getDocuments());
+ privileges.push_back(Privilege(ResourcePattern::forExactNamespace(nssToIndex),
+ ActionType::createIndex));
+ }
+ break;
}
- } else if (cmdType == BatchedCommandRequest::BatchType_Update) {
- auto op = Update::parse(IDLParserErrorContext("update"), request);
- cmdNSS = op.getNamespace();
- actionsOnCommandNSS.addAction(ActionType::update);
-
- // Upsert also requires insert privs
- if (containsUpserts(op.getUpdates())) {
- actionsOnCommandNSS.addAction(ActionType::insert);
+ case BatchedCommandRequest::BatchType_Update: {
+ auto op = write_ops::Update::parse(IDLParserErrorContext("update"), request);
+ cmdNSS = op.getNamespace();
+ actionsOnCommandNSS.addAction(ActionType::update);
+ // Upsert also requires insert privs
+ const auto& updates = op.getUpdates();
+ if (std::any_of(
+ updates.begin(), updates.end(), [](auto&& x) { return x.getUpsert(); })) {
+ actionsOnCommandNSS.addAction(ActionType::insert);
+ }
+ break;
+ }
+ case BatchedCommandRequest::BatchType_Delete: {
+ auto op = write_ops::Delete::parse(IDLParserErrorContext("delete"), request);
+ cmdNSS = op.getNamespace();
+ actionsOnCommandNSS.addAction(ActionType::remove);
+ break;
}
- } else {
- fassert(17251, cmdType == BatchedCommandRequest::BatchType_Delete);
- auto op = Delete::parse(IDLParserErrorContext("delete"), request);
- cmdNSS = op.getNamespace();
- actionsOnCommandNSS.addAction(ActionType::remove);
}
if (!actionsOnCommandNSS.empty()) {
- privileges.emplace_back(ResourcePattern::forExactNamespace(cmdNSS), actionsOnCommandNSS);
+ privileges.push_back(
+ Privilege(ResourcePattern::forExactNamespace(cmdNSS), actionsOnCommandNSS));
}
- if (authzSession->isAuthorizedForPrivileges(privileges))
- return Status::OK();
-
- return Status(ErrorCodes::Unauthorized, "unauthorized");
+ uassert(ErrorCodes::Unauthorized,
+ "unauthorized",
+ authzSession->isAuthorizedForPrivileges(privileges));
}
} // namespace auth
diff --git a/src/mongo/db/commands/write_commands/write_commands_common.h b/src/mongo/db/commands/write_commands/write_commands_common.h
index 897ee283fe1..03685223b57 100644
--- a/src/mongo/db/commands/write_commands/write_commands_common.h
+++ b/src/mongo/db/commands/write_commands/write_commands_common.h
@@ -41,9 +41,12 @@
namespace mongo {
namespace auth {
-Status checkAuthForWriteCommand(AuthorizationSession* authzSession,
- BatchedCommandRequest::BatchType cmdType,
- const OpMsgRequest& request);
+/**
+ * Throws if write command shouldn't proceed.
+ */
+void checkAuthForWriteCommand(AuthorizationSession* authzSession,
+ BatchedCommandRequest::BatchType cmdType,
+ const OpMsgRequest& request);
} // namespace auth
} // namespace mongo