summaryrefslogtreecommitdiff
path: root/src/mongo/db/pipeline
diff options
context:
space:
mode:
authorJames Wahlin <james@mongodb.com>2017-07-03 15:33:36 -0400
committerJames Wahlin <james@mongodb.com>2017-07-25 12:24:49 -0400
commit5dcaad5f137eebc1915c0fc7b5078da4aa86f915 (patch)
tree3994b41708bce7cf5cbc5b7c9ba422db77f9bfb3 /src/mongo/db/pipeline
parent079763d2cd06776edf81f3ecf6c32ab66d1742ec (diff)
downloadmongo-5dcaad5f137eebc1915c0fc7b5078da4aa86f915.tar.gz
SERVER-29371 DocumentSource classes should provide auth requirements
Diffstat (limited to 'src/mongo/db/pipeline')
-rw-r--r--src/mongo/db/pipeline/SConscript12
-rw-r--r--src/mongo/db/pipeline/aggregation_request.cpp15
-rw-r--r--src/mongo/db/pipeline/aggregation_request_test.cpp7
-rw-r--r--src/mongo/db/pipeline/document_source_change_notification.h5
-rw-r--r--src/mongo/db/pipeline/document_source_coll_stats.h15
-rw-r--r--src/mongo/db/pipeline/document_source_current_op.cpp33
-rw-r--r--src/mongo/db/pipeline/document_source_current_op.h30
-rw-r--r--src/mongo/db/pipeline/document_source_facet.cpp16
-rw-r--r--src/mongo/db/pipeline/document_source_facet.h12
-rw-r--r--src/mongo/db/pipeline/document_source_graph_lookup.cpp7
-rw-r--r--src/mongo/db/pipeline/document_source_index_stats.cpp2
-rw-r--r--src/mongo/db/pipeline/document_source_index_stats.h25
-rw-r--r--src/mongo/db/pipeline/document_source_lookup.cpp25
-rw-r--r--src/mongo/db/pipeline/document_source_lookup.h40
-rw-r--r--src/mongo/db/pipeline/document_source_lookup_test.cpp3
-rw-r--r--src/mongo/db/pipeline/document_source_out.cpp15
-rw-r--r--src/mongo/db/pipeline/lite_parsed_document_source.h33
-rw-r--r--src/mongo/db/pipeline/lite_parsed_pipeline.h13
18 files changed, 268 insertions, 40 deletions
diff --git a/src/mongo/db/pipeline/SConscript b/src/mongo/db/pipeline/SConscript
index 85c78e5a0c9..0d67ccbb2df 100644
--- a/src/mongo/db/pipeline/SConscript
+++ b/src/mongo/db/pipeline/SConscript
@@ -254,7 +254,6 @@ docSourceEnv.Library(
'document_source_sort.cpp',
'document_source_sort_by_count.cpp',
'document_source_unwind.cpp',
- 'lite_parsed_document_source.cpp',
],
LIBDEPS=[
'$BUILD_DIR/mongo/client/clientdriver',
@@ -262,6 +261,7 @@ docSourceEnv.Library(
'$BUILD_DIR/mongo/db/index/key_generator',
'$BUILD_DIR/mongo/db/matcher/expression_algo',
'$BUILD_DIR/mongo/db/matcher/expressions',
+ '$BUILD_DIR/mongo/db/pipeline/lite_parsed_document_source',
'$BUILD_DIR/mongo/db/service_context',
'$BUILD_DIR/mongo/db/stats/top',
'$BUILD_DIR/mongo/db/storage/encryption_hooks',
@@ -278,6 +278,16 @@ docSourceEnv.Library(
)
env.Library(
+ target='lite_parsed_document_source',
+ source=[
+ 'lite_parsed_document_source.cpp',
+ ],
+ LIBDEPS=[
+ 'aggregation_request',
+ ]
+)
+
+env.Library(
target='pipeline',
source=[
'pipeline.cpp',
diff --git a/src/mongo/db/pipeline/aggregation_request.cpp b/src/mongo/db/pipeline/aggregation_request.cpp
index 844907bd89a..8947c6f9a03 100644
--- a/src/mongo/db/pipeline/aggregation_request.cpp
+++ b/src/mongo/db/pipeline/aggregation_request.cpp
@@ -216,10 +216,14 @@ StatusWith<AggregationRequest> AggregationRequest::parseFromBSON(
request.setExplain(explainVerbosity);
}
- if (!hasCursorElem && !request.getExplain()) {
+ // 'hasExplainElem' implies an aggregate command-level explain option, which does not require
+ // a cursor argument.
+ if (!hasCursorElem && !hasExplainElem) {
return {ErrorCodes::FailedToParse,
- str::stream() << "The '" << kCursorName
- << "' option is required, except for aggregation explain"};
+ str::stream()
+ << "The '"
+ << kCursorName
+ << "' option is required, except for aggregate with the explain argument"};
}
if (request.getExplain() && !request.getReadConcern().isEmpty()) {
@@ -277,8 +281,9 @@ Document AggregationRequest::serializeToCommandObj() const {
_bypassDocumentValidation ? Value(true) : Value()},
// Only serialize a collation if one was specified.
{kCollationName, _collation.isEmpty() ? Value() : Value(_collation)},
- // Only serialize batchSize when explain is false.
- {kCursorName, _explainMode ? Value() : Value(Document{{kBatchSizeName, _batchSize}})},
+ // Only serialize batchSize if not an explain, otherwise serialize an empty cursor object.
+ {kCursorName,
+ _explainMode ? Value(Document()) : Value(Document{{kBatchSizeName, _batchSize}})},
// Only serialize a hint if one was specified.
{kHintName, _hint.isEmpty() ? Value() : Value(_hint)},
// Only serialize a comment if one was specified.
diff --git a/src/mongo/db/pipeline/aggregation_request_test.cpp b/src/mongo/db/pipeline/aggregation_request_test.cpp
index 0b1330009da..109bb6083ef 100644
--- a/src/mongo/db/pipeline/aggregation_request_test.cpp
+++ b/src/mongo/db/pipeline/aggregation_request_test.cpp
@@ -98,7 +98,7 @@ TEST(AggregationRequestTest, ShouldParseExplicitExplainFalseWithCursorOption) {
TEST(AggregationRequestTest, ShouldParseWithSeparateQueryPlannerExplainModeArg) {
NamespaceString nss("a.collection");
- const BSONObj inputBson = fromjson("{pipeline: []}");
+ const BSONObj inputBson = fromjson("{pipeline: [], cursor: {}}");
auto request = unittest::assertGet(AggregationRequest::parseFromBSON(
nss, inputBson, ExplainOptions::Verbosity::kQueryPlanner));
ASSERT_TRUE(request.getExplain());
@@ -236,7 +236,7 @@ TEST(AggregationRequestTest, ShouldAcceptHintAsString) {
<< "a_1"));
}
-TEST(AggregationRequestTest, ShouldNotSerializeBatchSizeOrExplainWhenExplainSet) {
+TEST(AggregationRequestTest, ShouldNotSerializeBatchSizeWhenExplainSet) {
NamespaceString nss("a.collection");
AggregationRequest request(nss, {});
request.setBatchSize(10);
@@ -244,7 +244,8 @@ TEST(AggregationRequestTest, ShouldNotSerializeBatchSizeOrExplainWhenExplainSet)
auto expectedSerialization =
Document{{AggregationRequest::kCommandName, nss.coll()},
- {AggregationRequest::kPipelineName, Value(std::vector<Value>{})}};
+ {AggregationRequest::kPipelineName, Value(std::vector<Value>{})},
+ {AggregationRequest::kCursorName, Value(Document())}};
ASSERT_DOCUMENT_EQ(request.serializeToCommandObj(), expectedSerialization);
}
diff --git a/src/mongo/db/pipeline/document_source_change_notification.h b/src/mongo/db/pipeline/document_source_change_notification.h
index c6748ab3781..e5a1186f04e 100644
--- a/src/mongo/db/pipeline/document_source_change_notification.h
+++ b/src/mongo/db/pipeline/document_source_change_notification.h
@@ -53,6 +53,11 @@ public:
stdx::unordered_set<NamespaceString> getInvolvedNamespaces() const final {
return stdx::unordered_set<NamespaceString>();
}
+
+ // TODO SERVER-29138: Add required privileges.
+ PrivilegeVector requiredPrivileges(bool isMongos) const final {
+ return {};
+ }
};
class Transformation : public DocumentSourceSingleDocumentTransformation::TransformerInterface {
diff --git a/src/mongo/db/pipeline/document_source_coll_stats.h b/src/mongo/db/pipeline/document_source_coll_stats.h
index ad8673643e6..2506c1be823 100644
--- a/src/mongo/db/pipeline/document_source_coll_stats.h
+++ b/src/mongo/db/pipeline/document_source_coll_stats.h
@@ -42,16 +42,29 @@ public:
public:
static std::unique_ptr<LiteParsed> parse(const AggregationRequest& request,
const BSONElement& spec) {
- return stdx::make_unique<LiteParsed>();
+ return stdx::make_unique<LiteParsed>(request.getNamespaceString());
}
+ explicit LiteParsed(NamespaceString nss) : _nss(std::move(nss)) {}
+
bool isCollStats() const final {
return true;
}
+ PrivilegeVector requiredPrivileges(bool isMongos) const final {
+ return {Privilege(ResourcePattern::forExactNamespace(_nss), ActionType::collStats)};
+ }
+
stdx::unordered_set<NamespaceString> getInvolvedNamespaces() const final {
return stdx::unordered_set<NamespaceString>();
}
+
+ bool isInitialSource() const final {
+ return true;
+ }
+
+ private:
+ const NamespaceString _nss;
};
DocumentSourceCollStats(const boost::intrusive_ptr<ExpressionContext>& pExpCtx)
diff --git a/src/mongo/db/pipeline/document_source_current_op.cpp b/src/mongo/db/pipeline/document_source_current_op.cpp
index 6a37192f8ad..b49d7de702c 100644
--- a/src/mongo/db/pipeline/document_source_current_op.cpp
+++ b/src/mongo/db/pipeline/document_source_current_op.cpp
@@ -48,9 +48,40 @@ const StringData kShardFieldName = "shard"_sd;
using boost::intrusive_ptr;
REGISTER_DOCUMENT_SOURCE(currentOp,
- LiteParsedDocumentSourceDefault::parse,
+ DocumentSourceCurrentOp::LiteParsed::parse,
DocumentSourceCurrentOp::createFromBson);
+std::unique_ptr<DocumentSourceCurrentOp::LiteParsed> DocumentSourceCurrentOp::LiteParsed::parse(
+ const AggregationRequest& request, const BSONElement& spec) {
+ // Need to check the value of allUsers; if true then inprog privilege is required.
+ if (spec.type() != BSONType::Object) {
+ uasserted(ErrorCodes::TypeMismatch,
+ str::stream() << "$currentOp options must be specified in an object, but found: "
+ << typeName(spec.type()));
+ }
+
+ bool allUsers = false;
+
+ // Check the spec for all fields named 'allUsers'. If any of them are 'true', we require
+ // the 'inprog' privilege. This avoids the possibility that a spec with multiple
+ // allUsers fields might allow an unauthorized user to view all operations.
+ for (auto&& elem : spec.embeddedObject()) {
+ if (elem.fieldNameStringData() == "allUsers"_sd) {
+ if (elem.type() != BSONType::Bool) {
+ uasserted(ErrorCodes::TypeMismatch,
+ str::stream() << "The 'allUsers' parameter of the $currentOp stage "
+ "must be a boolean value, but found: "
+ << typeName(elem.type()));
+ }
+
+ allUsers = allUsers || elem.boolean();
+ }
+ }
+
+ return stdx::make_unique<DocumentSourceCurrentOp::LiteParsed>(allUsers);
+}
+
+
const char* DocumentSourceCurrentOp::getSourceName() const {
return "$currentOp";
}
diff --git a/src/mongo/db/pipeline/document_source_current_op.h b/src/mongo/db/pipeline/document_source_current_op.h
index 24ef1ba964d..4526134c13f 100644
--- a/src/mongo/db/pipeline/document_source_current_op.h
+++ b/src/mongo/db/pipeline/document_source_current_op.h
@@ -34,6 +34,36 @@ namespace mongo {
class DocumentSourceCurrentOp final : public DocumentSourceNeedsMongod {
public:
+ class LiteParsed final : public LiteParsedDocumentSource {
+ public:
+ static std::unique_ptr<LiteParsed> parse(const AggregationRequest& request,
+ const BSONElement& spec);
+
+ explicit LiteParsed(bool allUsers) : _allUsers(allUsers) {}
+
+ stdx::unordered_set<NamespaceString> getInvolvedNamespaces() const final {
+ return stdx::unordered_set<NamespaceString>();
+ }
+
+ PrivilegeVector requiredPrivileges(bool isMongos) const final {
+ PrivilegeVector privileges;
+
+ // In a sharded cluster, we always need the inprog privilege to run $currentOp.
+ if (isMongos || _allUsers) {
+ privileges.push_back({ResourcePattern::forClusterResource(), ActionType::inprog});
+ }
+
+ return privileges;
+ }
+
+ bool isInitialSource() const final {
+ return true;
+ }
+
+ private:
+ const bool _allUsers;
+ };
+
using TruncationMode = MongodInterface::CurrentOpTruncateMode;
using ConnMode = MongodInterface::CurrentOpConnectionsMode;
using UserMode = MongodInterface::CurrentOpUserMode;
diff --git a/src/mongo/db/pipeline/document_source_facet.cpp b/src/mongo/db/pipeline/document_source_facet.cpp
index 222d78e69c3..992f3daac11 100644
--- a/src/mongo/db/pipeline/document_source_facet.cpp
+++ b/src/mongo/db/pipeline/document_source_facet.cpp
@@ -118,12 +118,24 @@ vector<pair<string, vector<BSONObj>>> extractRawPipelines(const BSONElement& ele
std::unique_ptr<DocumentSourceFacet::LiteParsed> DocumentSourceFacet::LiteParsed::parse(
const AggregationRequest& request, const BSONElement& spec) {
std::vector<LiteParsedPipeline> liteParsedPipelines;
+
for (auto&& rawPipeline : extractRawPipelines(spec)) {
liteParsedPipelines.emplace_back(
AggregationRequest(request.getNamespaceString(), rawPipeline.second));
}
- return std::unique_ptr<DocumentSourceFacet::LiteParsed>(
- new DocumentSourceFacet::LiteParsed(std::move(liteParsedPipelines)));
+
+ PrivilegeVector requiredPrivileges;
+ for (auto&& pipeline : liteParsedPipelines) {
+
+ // A correct isMongos flag is only required for DocumentSourceCurrentOp which is disallowed
+ // in $facet pipelines.
+ const bool unusedIsMongosFlag = false;
+ Privilege::addPrivilegesToPrivilegeVector(&requiredPrivileges,
+ pipeline.requiredPrivileges(unusedIsMongosFlag));
+ }
+
+ return stdx::make_unique<DocumentSourceFacet::LiteParsed>(std::move(liteParsedPipelines),
+ std::move(requiredPrivileges));
}
stdx::unordered_set<NamespaceString> DocumentSourceFacet::LiteParsed::getInvolvedNamespaces()
diff --git a/src/mongo/db/pipeline/document_source_facet.h b/src/mongo/db/pipeline/document_source_facet.h
index 37c2fc5cb8f..f06f73c7b27 100644
--- a/src/mongo/db/pipeline/document_source_facet.h
+++ b/src/mongo/db/pipeline/document_source_facet.h
@@ -71,13 +71,19 @@ public:
static std::unique_ptr<LiteParsed> parse(const AggregationRequest& request,
const BSONElement& spec);
+ LiteParsed(std::vector<LiteParsedPipeline> liteParsedPipelines, PrivilegeVector privileges)
+ : _liteParsedPipelines(std::move(liteParsedPipelines)),
+ _requiredPrivileges(std::move(privileges)) {}
+
+ PrivilegeVector requiredPrivileges(bool isMongos) const final {
+ return _requiredPrivileges;
+ }
+
stdx::unordered_set<NamespaceString> getInvolvedNamespaces() const final;
private:
- LiteParsed(std::vector<LiteParsedPipeline> liteParsedPipelines)
- : _liteParsedPipelines(std::move(liteParsedPipelines)) {}
-
const std::vector<LiteParsedPipeline> _liteParsedPipelines;
+ const PrivilegeVector _requiredPrivileges;
};
static boost::intrusive_ptr<DocumentSource> createFromBson(
diff --git a/src/mongo/db/pipeline/document_source_graph_lookup.cpp b/src/mongo/db/pipeline/document_source_graph_lookup.cpp
index 19ce53b3769..bac942e1f31 100644
--- a/src/mongo/db/pipeline/document_source_graph_lookup.cpp
+++ b/src/mongo/db/pipeline/document_source_graph_lookup.cpp
@@ -71,7 +71,12 @@ std::unique_ptr<LiteParsedDocumentSourceForeignCollections> DocumentSourceGraphL
uassert(ErrorCodes::InvalidNamespace,
str::stream() << "invalid $graphLookup namespace: " << nss.ns(),
nss.isValid());
- return stdx::make_unique<LiteParsedDocumentSourceForeignCollections>(std::move(nss));
+
+ PrivilegeVector privileges{
+ Privilege(ResourcePattern::forExactNamespace(nss), ActionType::find)};
+
+ return stdx::make_unique<LiteParsedDocumentSourceForeignCollections>(std::move(nss),
+ std::move(privileges));
}
REGISTER_DOCUMENT_SOURCE(graphLookup,
diff --git a/src/mongo/db/pipeline/document_source_index_stats.cpp b/src/mongo/db/pipeline/document_source_index_stats.cpp
index e6d5b28171c..786f8f59cf0 100644
--- a/src/mongo/db/pipeline/document_source_index_stats.cpp
+++ b/src/mongo/db/pipeline/document_source_index_stats.cpp
@@ -39,7 +39,7 @@ namespace mongo {
using boost::intrusive_ptr;
REGISTER_DOCUMENT_SOURCE(indexStats,
- LiteParsedDocumentSourceDefault::parse,
+ DocumentSourceIndexStats::LiteParsed::parse,
DocumentSourceIndexStats::createFromBson);
const char* DocumentSourceIndexStats::getSourceName() const {
diff --git a/src/mongo/db/pipeline/document_source_index_stats.h b/src/mongo/db/pipeline/document_source_index_stats.h
index e802e0d7016..7d25aca6c9f 100644
--- a/src/mongo/db/pipeline/document_source_index_stats.h
+++ b/src/mongo/db/pipeline/document_source_index_stats.h
@@ -39,6 +39,31 @@ namespace mongo {
*/
class DocumentSourceIndexStats final : public DocumentSourceNeedsMongod {
public:
+ class LiteParsed final : public LiteParsedDocumentSource {
+ public:
+ static std::unique_ptr<LiteParsed> parse(const AggregationRequest& request,
+ const BSONElement& spec) {
+ return stdx::make_unique<LiteParsed>(request.getNamespaceString());
+ }
+
+ explicit LiteParsed(NamespaceString nss) : _nss(std::move(nss)) {}
+
+ stdx::unordered_set<NamespaceString> getInvolvedNamespaces() const final {
+ return stdx::unordered_set<NamespaceString>();
+ }
+
+ PrivilegeVector requiredPrivileges(bool isMongos) const final {
+ return {Privilege(ResourcePattern::forExactNamespace(_nss), ActionType::indexStats)};
+ }
+
+ bool isInitialSource() const final {
+ return true;
+ }
+
+ private:
+ const NamespaceString _nss;
+ };
+
// virtuals from DocumentSource
GetNextResult getNext() final;
const char* getSourceName() const final;
diff --git a/src/mongo/db/pipeline/document_source_lookup.cpp b/src/mongo/db/pipeline/document_source_lookup.cpp
index ec2d37db6de..6fa34fba0df 100644
--- a/src/mongo/db/pipeline/document_source_lookup.cpp
+++ b/src/mongo/db/pipeline/document_source_lookup.cpp
@@ -37,7 +37,6 @@
#include "mongo/db/pipeline/document_path_support.h"
#include "mongo/db/pipeline/expression.h"
#include "mongo/db/pipeline/expression_context.h"
-#include "mongo/db/pipeline/lite_parsed_pipeline.h"
#include "mongo/db/pipeline/value.h"
#include "mongo/stdx/memory.h"
@@ -117,7 +116,7 @@ DocumentSourceLookUp::DocumentSourceLookUp(NamespaceString fromNs,
}
}
-std::unique_ptr<LiteParsedDocumentSourceForeignCollections> DocumentSourceLookUp::liteParse(
+std::unique_ptr<DocumentSourceLookUp::LiteParsed> DocumentSourceLookUp::LiteParsed::parse(
const AggregationRequest& request, const BSONElement& spec) {
uassert(ErrorCodes::FailedToParse,
str::stream() << "the $lookup stage specification must be an object, but found "
@@ -134,29 +133,33 @@ std::unique_ptr<LiteParsedDocumentSourceForeignCollections> DocumentSourceLookUp
<< typeName(specObj["from"].type()),
fromElement.type() == BSONType::String);
- NamespaceString nss(request.getNamespaceString().db(), fromElement.valueStringData());
+ NamespaceString fromNss(request.getNamespaceString().db(), fromElement.valueStringData());
uassert(ErrorCodes::InvalidNamespace,
- str::stream() << "invalid $lookup namespace: " << nss.ns(),
- nss.isValid());
+ str::stream() << "invalid $lookup namespace: " << fromNss.ns(),
+ fromNss.isValid());
stdx::unordered_set<NamespaceString> foreignNssSet;
// Recursively lite parse the nested pipeline, if one exists.
auto pipelineElem = specObj["pipeline"];
+ boost::optional<LiteParsedPipeline> liteParsedPipeline;
if (pipelineElem) {
auto pipeline = uassertStatusOK(AggregationRequest::parsePipelineFromBSON(pipelineElem));
- AggregationRequest foreignAggReq(nss, std::move(pipeline));
- LiteParsedPipeline liteParsedPipeline(foreignAggReq);
- auto pipelineInvolvedNamespaces = liteParsedPipeline.getInvolvedNamespaces();
+ AggregationRequest foreignAggReq(fromNss, std::move(pipeline));
+ liteParsedPipeline = LiteParsedPipeline(foreignAggReq);
+
+ auto pipelineInvolvedNamespaces = liteParsedPipeline->getInvolvedNamespaces();
foreignNssSet.insert(pipelineInvolvedNamespaces.begin(), pipelineInvolvedNamespaces.end());
}
- foreignNssSet.insert(std::move(nss));
- return stdx::make_unique<LiteParsedDocumentSourceForeignCollections>(std::move(foreignNssSet));
+ foreignNssSet.insert(fromNss);
+
+ return stdx::make_unique<DocumentSourceLookUp::LiteParsed>(
+ std::move(fromNss), std::move(foreignNssSet), std::move(liteParsedPipeline));
}
REGISTER_DOCUMENT_SOURCE(lookup,
- DocumentSourceLookUp::liteParse,
+ DocumentSourceLookUp::LiteParsed::parse,
DocumentSourceLookUp::createFromBson);
const char* DocumentSourceLookUp::getSourceName() const {
diff --git a/src/mongo/db/pipeline/document_source_lookup.h b/src/mongo/db/pipeline/document_source_lookup.h
index 12965eee2d0..bb4fae8df2e 100644
--- a/src/mongo/db/pipeline/document_source_lookup.h
+++ b/src/mongo/db/pipeline/document_source_lookup.h
@@ -28,10 +28,13 @@
#pragma once
+#include <boost/optional.hpp>
+
#include "mongo/db/pipeline/document_source.h"
#include "mongo/db/pipeline/document_source_match.h"
#include "mongo/db/pipeline/document_source_unwind.h"
#include "mongo/db/pipeline/expression.h"
+#include "mongo/db/pipeline/lite_parsed_pipeline.h"
#include "mongo/db/pipeline/lookup_set_cache.h"
#include "mongo/db/pipeline/value_comparator.h"
@@ -44,8 +47,41 @@ namespace mongo {
class DocumentSourceLookUp final : public DocumentSourceNeedsMongod,
public SplittableDocumentSource {
public:
- static std::unique_ptr<LiteParsedDocumentSourceForeignCollections> liteParse(
- const AggregationRequest& request, const BSONElement& spec);
+ class LiteParsed final : public LiteParsedDocumentSource {
+ public:
+ static std::unique_ptr<LiteParsed> parse(const AggregationRequest& request,
+ const BSONElement& spec);
+
+ LiteParsed(NamespaceString fromNss,
+ stdx::unordered_set<NamespaceString> foreignNssSet,
+ boost::optional<LiteParsedPipeline> liteParsedPipeline)
+ : _fromNss{std::move(fromNss)},
+ _foreignNssSet(std::move(foreignNssSet)),
+ _liteParsedPipeline(std::move(liteParsedPipeline)) {}
+
+ stdx::unordered_set<NamespaceString> getInvolvedNamespaces() const final {
+ return {_foreignNssSet};
+ }
+
+ PrivilegeVector requiredPrivileges(bool isMongos) const final {
+ PrivilegeVector requiredPrivileges;
+ Privilege::addPrivilegeToPrivilegeVector(
+ &requiredPrivileges,
+ Privilege(ResourcePattern::forExactNamespace(_fromNss), ActionType::find));
+
+ if (_liteParsedPipeline) {
+ Privilege::addPrivilegesToPrivilegeVector(
+ &requiredPrivileges, _liteParsedPipeline->requiredPrivileges(isMongos));
+ }
+
+ return requiredPrivileges;
+ }
+
+ private:
+ const NamespaceString _fromNss;
+ const stdx::unordered_set<NamespaceString> _foreignNssSet;
+ const boost::optional<LiteParsedPipeline> _liteParsedPipeline;
+ };
GetNextResult getNext() final;
const char* getSourceName() const final;
diff --git a/src/mongo/db/pipeline/document_source_lookup_test.cpp b/src/mongo/db/pipeline/document_source_lookup_test.cpp
index c9f56aa2295..0ca5f1a475a 100644
--- a/src/mongo/db/pipeline/document_source_lookup_test.cpp
+++ b/src/mongo/db/pipeline/document_source_lookup_test.cpp
@@ -160,7 +160,8 @@ TEST_F(DocumentSourceLookUpTest, LiteParsedDocumentSourceLookupContainsExpectedN
NamespaceString nss("test.test");
std::vector<BSONObj> pipeline;
AggregationRequest aggRequest(nss, pipeline);
- auto liteParsedLookup = DocumentSourceLookUp::liteParse(aggRequest, stageSpec.firstElement());
+ auto liteParsedLookup =
+ DocumentSourceLookUp::LiteParsed::parse(aggRequest, stageSpec.firstElement());
auto namespaceSet = liteParsedLookup->getInvolvedNamespaces();
diff --git a/src/mongo/db/pipeline/document_source_out.cpp b/src/mongo/db/pipeline/document_source_out.cpp
index a2e0ceea44b..069257abcb9 100644
--- a/src/mongo/db/pipeline/document_source_out.cpp
+++ b/src/mongo/db/pipeline/document_source_out.cpp
@@ -48,16 +48,25 @@ DocumentSourceOut::~DocumentSourceOut() {
std::unique_ptr<LiteParsedDocumentSourceForeignCollections> DocumentSourceOut::liteParse(
const AggregationRequest& request, const BSONElement& spec) {
- uassert(40325,
+ uassert(ErrorCodes::TypeMismatch,
str::stream() << "$out stage requires a string argument, but found "
<< typeName(spec.type()),
spec.type() == BSONType::String);
NamespaceString targetNss(request.getNamespaceString().db(), spec.valueStringData());
- uassert(40326,
+ uassert(ErrorCodes::InvalidNamespace,
str::stream() << "Invalid $out target namespace, " << targetNss.ns(),
targetNss.isValid());
- return stdx::make_unique<LiteParsedDocumentSourceForeignCollections>(std::move(targetNss));
+
+ ActionSet actions{ActionType::remove, ActionType::insert};
+ if (request.shouldBypassDocumentValidation()) {
+ actions.addAction(ActionType::bypassDocumentValidation);
+ }
+
+ PrivilegeVector privileges{Privilege(ResourcePattern::forExactNamespace(targetNss), actions)};
+
+ return stdx::make_unique<LiteParsedDocumentSourceForeignCollections>(std::move(targetNss),
+ std::move(privileges));
}
REGISTER_DOCUMENT_SOURCE(out, DocumentSourceOut::liteParse, DocumentSourceOut::createFromBson);
diff --git a/src/mongo/db/pipeline/lite_parsed_document_source.h b/src/mongo/db/pipeline/lite_parsed_document_source.h
index dae9ee222ce..489e8bc1aa7 100644
--- a/src/mongo/db/pipeline/lite_parsed_document_source.h
+++ b/src/mongo/db/pipeline/lite_parsed_document_source.h
@@ -32,6 +32,7 @@
#include <memory>
#include <vector>
+#include "mongo/db/auth/privilege.h"
#include "mongo/db/namespace_string.h"
#include "mongo/db/pipeline/aggregation_request.h"
#include "mongo/stdx/functional.h"
@@ -85,6 +86,11 @@ public:
virtual stdx::unordered_set<NamespaceString> getInvolvedNamespaces() const = 0;
/**
+ * Returns a list of the privileges required for this stage.
+ */
+ virtual PrivilegeVector requiredPrivileges(bool isMongos) const = 0;
+
+ /**
* Returns true if this is a $collStats stage.
*/
virtual bool isCollStats() const {
@@ -97,6 +103,13 @@ public:
virtual bool isChangeNotification() const {
return false;
}
+
+ /**
+ * Returns true if this stage does not require an input source.
+ */
+ virtual bool isInitialSource() const {
+ return false;
+ }
};
class LiteParsedDocumentSourceDefault final : public LiteParsedDocumentSource {
@@ -116,6 +129,10 @@ public:
stdx::unordered_set<NamespaceString> getInvolvedNamespaces() const final {
return stdx::unordered_set<NamespaceString>();
}
+
+ PrivilegeVector requiredPrivileges(bool isMongos) const final {
+ return {};
+ }
};
/**
@@ -123,18 +140,24 @@ public:
*/
class LiteParsedDocumentSourceForeignCollections : public LiteParsedDocumentSource {
public:
- explicit LiteParsedDocumentSourceForeignCollections(NamespaceString foreignNss)
- : _foreignNssSet{std::move(foreignNss)} {}
+ LiteParsedDocumentSourceForeignCollections(NamespaceString foreignNss,
+ PrivilegeVector privileges)
+ : _foreignNssSet{std::move(foreignNss)}, _requiredPrivileges(std::move(privileges)) {}
- explicit LiteParsedDocumentSourceForeignCollections(
- stdx::unordered_set<NamespaceString> foreignNssSet)
- : _foreignNssSet(std::move(foreignNssSet)) {}
+ LiteParsedDocumentSourceForeignCollections(stdx::unordered_set<NamespaceString> foreignNssSet,
+ PrivilegeVector privileges)
+ : _foreignNssSet(std::move(foreignNssSet)), _requiredPrivileges(std::move(privileges)) {}
stdx::unordered_set<NamespaceString> getInvolvedNamespaces() const final {
return {_foreignNssSet};
}
+ PrivilegeVector requiredPrivileges(bool isMongos) const final {
+ return _requiredPrivileges;
+ }
+
private:
stdx::unordered_set<NamespaceString> _foreignNssSet;
+ PrivilegeVector _requiredPrivileges;
};
} // namespace mongo
diff --git a/src/mongo/db/pipeline/lite_parsed_pipeline.h b/src/mongo/db/pipeline/lite_parsed_pipeline.h
index ca2e9dfda77..07e5c947cc3 100644
--- a/src/mongo/db/pipeline/lite_parsed_pipeline.h
+++ b/src/mongo/db/pipeline/lite_parsed_pipeline.h
@@ -72,6 +72,19 @@ public:
}
/**
+ * Returns a list of the priviliges required for this pipeline.
+ */
+ PrivilegeVector requiredPrivileges(bool isMongos) const {
+ PrivilegeVector requiredPrivileges;
+ for (auto&& spec : _stageSpecs) {
+ Privilege::addPrivilegesToPrivilegeVector(&requiredPrivileges,
+ spec->requiredPrivileges(isMongos));
+ }
+
+ return requiredPrivileges;
+ }
+
+ /**
* Returns true if the pipeline begins with a $collStats stage.
*/
bool startsWithCollStats() const {