diff options
author | James Wahlin <james@mongodb.com> | 2017-07-03 15:33:36 -0400 |
---|---|---|
committer | James Wahlin <james@mongodb.com> | 2017-07-25 12:24:49 -0400 |
commit | 5dcaad5f137eebc1915c0fc7b5078da4aa86f915 (patch) | |
tree | 3994b41708bce7cf5cbc5b7c9ba422db77f9bfb3 /src/mongo/db/pipeline | |
parent | 079763d2cd06776edf81f3ecf6c32ab66d1742ec (diff) | |
download | mongo-5dcaad5f137eebc1915c0fc7b5078da4aa86f915.tar.gz |
SERVER-29371 DocumentSource classes should provide auth requirements
Diffstat (limited to 'src/mongo/db/pipeline')
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 { |