summaryrefslogtreecommitdiff
path: root/src/mongo
diff options
context:
space:
mode:
authorRishab Joshi <rishab.joshi@mongodb.com>2021-02-19 06:20:44 +0000
committerEvergreen Agent <no-reply@evergreen.mongodb.com>2021-02-19 09:32:47 +0000
commitb785ca3a3d785814ac7201d5a7d52e718df7e689 (patch)
treeca0f6c2aa5a3ccd142ca8894fda6939f240c398d /src/mongo
parenta40e23d8613ad123e26112a61e26963909f1fa07 (diff)
downloadmongo-b785ca3a3d785814ac7201d5a7d52e718df7e689.tar.gz
SERVER-54072 Added internal-client checks for aggregation command.
Diffstat (limited to 'src/mongo')
-rw-r--r--src/mongo/db/commands/run_aggregate.cpp27
-rw-r--r--src/mongo/db/pipeline/aggregate_command.idl1
-rw-r--r--src/mongo/db/pipeline/aggregation_request_helper.cpp26
-rw-r--r--src/mongo/db/pipeline/aggregation_request_helper.h8
-rw-r--r--src/mongo/db/pipeline/document_source.h33
-rw-r--r--src/mongo/db/pipeline/document_source_add_fields.cpp6
-rw-r--r--src/mongo/db/pipeline/document_source_bucket.cpp3
-rw-r--r--src/mongo/db/pipeline/document_source_bucket_auto.cpp3
-rw-r--r--src/mongo/db/pipeline/document_source_change_stream.cpp3
-rw-r--r--src/mongo/db/pipeline/document_source_coll_stats.cpp3
-rw-r--r--src/mongo/db/pipeline/document_source_count.cpp3
-rw-r--r--src/mongo/db/pipeline/document_source_current_op.cpp3
-rw-r--r--src/mongo/db/pipeline/document_source_facet.cpp3
-rw-r--r--src/mongo/db/pipeline/document_source_geo_near.cpp3
-rw-r--r--src/mongo/db/pipeline/document_source_graph_lookup.cpp3
-rw-r--r--src/mongo/db/pipeline/document_source_group.cpp3
-rw-r--r--src/mongo/db/pipeline/document_source_index_stats.cpp3
-rw-r--r--src/mongo/db/pipeline/document_source_internal_inhibit_optimization.cpp3
-rw-r--r--src/mongo/db/pipeline/document_source_internal_split_pipeline.cpp3
-rw-r--r--src/mongo/db/pipeline/document_source_internal_unpack_bucket.cpp3
-rw-r--r--src/mongo/db/pipeline/document_source_limit.cpp3
-rw-r--r--src/mongo/db/pipeline/document_source_list_local_sessions.cpp3
-rw-r--r--src/mongo/db/pipeline/document_source_list_sessions.cpp3
-rw-r--r--src/mongo/db/pipeline/document_source_lookup.cpp3
-rw-r--r--src/mongo/db/pipeline/document_source_match.cpp3
-rw-r--r--src/mongo/db/pipeline/document_source_merge.cpp3
-rw-r--r--src/mongo/db/pipeline/document_source_operation_metrics.cpp3
-rw-r--r--src/mongo/db/pipeline/document_source_out.cpp3
-rw-r--r--src/mongo/db/pipeline/document_source_plan_cache_stats.cpp3
-rw-r--r--src/mongo/db/pipeline/document_source_project.cpp6
-rw-r--r--src/mongo/db/pipeline/document_source_redact.cpp3
-rw-r--r--src/mongo/db/pipeline/document_source_replace_root.cpp6
-rw-r--r--src/mongo/db/pipeline/document_source_sample.cpp3
-rw-r--r--src/mongo/db/pipeline/document_source_set_window_fields.cpp2
-rw-r--r--src/mongo/db/pipeline/document_source_skip.cpp3
-rw-r--r--src/mongo/db/pipeline/document_source_sort.cpp3
-rw-r--r--src/mongo/db/pipeline/document_source_sort_by_count.cpp3
-rw-r--r--src/mongo/db/pipeline/document_source_union_with.cpp3
-rw-r--r--src/mongo/db/pipeline/document_source_unwind.cpp3
-rw-r--r--src/mongo/db/pipeline/lite_parsed_document_source.cpp25
-rw-r--r--src/mongo/db/pipeline/lite_parsed_document_source.h23
-rw-r--r--src/mongo/db/pipeline/lite_parsed_pipeline.cpp51
-rw-r--r--src/mongo/db/pipeline/lite_parsed_pipeline.h40
-rw-r--r--src/mongo/db/views/view_catalog.cpp10
-rw-r--r--src/mongo/s/query/cluster_aggregate.cpp25
-rw-r--r--src/mongo/s/query/document_source_merge_cursors.cpp3
46 files changed, 277 insertions, 105 deletions
diff --git a/src/mongo/db/commands/run_aggregate.cpp b/src/mongo/db/commands/run_aggregate.cpp
index e33a2c7db68..ef536849333 100644
--- a/src/mongo/db/commands/run_aggregate.cpp
+++ b/src/mongo/db/commands/run_aggregate.cpp
@@ -516,6 +516,22 @@ std::vector<std::unique_ptr<Pipeline, PipelineDeleter>> createExchangePipelinesI
return pipelines;
}
+
+/**
+ * Performs validations related to API versioning before running the aggregation command.
+ * Throws uassert if any of the validations fails
+ * - validation on each stage on the pipeline
+ * - validation on 'AggregateCommand' request
+ */
+void performAPIVersionChecks(const OperationContext* opCtx,
+ const AggregateCommand& request,
+ const LiteParsedPipeline& liteParsedPipeline) {
+ invariant(opCtx);
+
+ liteParsedPipeline.validatePipelineStagesforAPIVersion(opCtx);
+ aggregation_request_helper::validateRequestForAPIVersion(opCtx, request);
+}
+
} // namespace
Status runAggregate(OperationContext* opCtx,
@@ -534,14 +550,9 @@ Status runAggregate(OperationContext* opCtx,
const BSONObj& cmdObj,
const PrivilegeVector& privileges,
rpc::ReplyBuilderInterface* result) {
- // If 'apiStrict: true', validates that the pipeline does not contain stages which are not in
- // this API version.
- auto apiParameters = APIParameters::get(opCtx);
- if (apiParameters.getAPIStrict().value_or(false)) {
- auto apiVersion = apiParameters.getAPIVersion();
- invariant(apiVersion);
- liteParsedPipeline.validatePipelineStagesIfAPIStrict(*apiVersion);
- }
+
+ // Performs API versioning checks.
+ performAPIVersionChecks(opCtx, request, liteParsedPipeline);
// For operations on views, this will be the underlying namespace.
NamespaceString nss = request.getNamespace();
diff --git a/src/mongo/db/pipeline/aggregate_command.idl b/src/mongo/db/pipeline/aggregate_command.idl
index 10c3bf0b7e2..e904d2c6b31 100644
--- a/src/mongo/db/pipeline/aggregate_command.idl
+++ b/src/mongo/db/pipeline/aggregate_command.idl
@@ -134,7 +134,6 @@ commands:
description: "An optional exchange specification for this request. If set it means that the request represents a producer running as a part of the exchange machinery. This is an internal option; we do not expect it to be set on requests from users or drivers."
type: ExchangeSpec
optional: true
- unstable: true
runtimeConstants:
description: "A legacy way to specify constant variables available during execution. 'let' is now preferred."
type: LegacyRuntimeConstants
diff --git a/src/mongo/db/pipeline/aggregation_request_helper.cpp b/src/mongo/db/pipeline/aggregation_request_helper.cpp
index a3b574566bd..971d1afb75e 100644
--- a/src/mongo/db/pipeline/aggregation_request_helper.cpp
+++ b/src/mongo/db/pipeline/aggregation_request_helper.cpp
@@ -179,6 +179,32 @@ void validate(const BSONObj& cmdObj, boost::optional<ExplainOptions::Verbosity>
<< "' option is not permitted in read-only mode.",
(!hasAllowDiskUseElem || !storageGlobalParams.readOnly));
}
+
+void validateRequestForAPIVersion(const OperationContext* opCtx, const AggregateCommand& request) {
+ invariant(opCtx);
+
+ auto apiParameters = APIParameters::get(opCtx);
+ bool apiStrict = apiParameters.getAPIStrict().value_or(false);
+ const auto apiVersion = apiParameters.getAPIVersion().value_or("");
+ auto client = opCtx->getClient();
+
+ // An internal client could be one of the following :
+ // - Does not have any transport session
+ // - The transport session tag is internal
+ bool isInternalClient =
+ !client->session() || (client->session()->getTags() & transport::Session::kInternalClient);
+
+ // Checks that the 'exchange' or 'fromMongos' option can only be specified by the internal
+ // client.
+ if ((request.getExchange() || request.getFromMongos()) && apiStrict && apiVersion == "1") {
+ uassert(ErrorCodes::APIStrictError,
+ str::stream() << "'exchange' and 'fromMongos' option cannot be specified with "
+ "'apiStrict: true' in API Version "
+ << apiVersion,
+ isInternalClient);
+ }
+}
+
} // namespace aggregation_request_helper
// Custom serializers/deserializers for AggregateCommand.
diff --git a/src/mongo/db/pipeline/aggregation_request_helper.h b/src/mongo/db/pipeline/aggregation_request_helper.h
index 9c021abdd4b..c4cb3adbc48 100644
--- a/src/mongo/db/pipeline/aggregation_request_helper.h
+++ b/src/mongo/db/pipeline/aggregation_request_helper.h
@@ -48,6 +48,7 @@ template <typename T>
class StatusWith;
class Document;
class AggregateCommand;
+class OperationContext;
namespace aggregation_request_helper {
@@ -111,6 +112,13 @@ NamespaceString parseNs(const std::string& dbname, const BSONObj& cmdObj);
Document serializeToCommandDoc(const AggregateCommand& request);
BSONObj serializeToCommandObj(const AggregateCommand& request);
+
+/**
+ * Validates if 'AggregateCommand' specs complies with API versioning. Throws uassert in case of
+ * any failure.
+ */
+void validateRequestForAPIVersion(const OperationContext* opCtx, const AggregateCommand& request);
+
} // namespace aggregation_request_helper
/**
diff --git a/src/mongo/db/pipeline/document_source.h b/src/mongo/db/pipeline/document_source.h
index e4797743036..c0d50feadbe 100644
--- a/src/mongo/db/pipeline/document_source.h
+++ b/src/mongo/db/pipeline/document_source.h
@@ -86,16 +86,19 @@ class Document;
* LiteParsedDocumentSourceDefault::parse,
* DocumentSourceFoo::createFromBson);
*/
-#define REGISTER_DOCUMENT_SOURCE(key, liteParser, fullParser) \
- REGISTER_DOCUMENT_SOURCE_CONDITIONALLY(key, liteParser, fullParser, boost::none, true)
+#define REGISTER_DOCUMENT_SOURCE(key, liteParser, fullParser, allowedWithApiStrict) \
+ REGISTER_DOCUMENT_SOURCE_CONDITIONALLY( \
+ key, liteParser, fullParser, allowedWithApiStrict, boost::none, true)
/**
* Like REGISTER_DOCUMENT_SOURCE, except the parser will only be enabled when FCV >= minVersion.
* We store minVersion in the parserMap, so that changing FCV at runtime correctly enables/disables
* the parser.
*/
-#define REGISTER_DOCUMENT_SOURCE_WITH_MIN_VERSION(key, liteParser, fullParser, minVersion) \
- REGISTER_DOCUMENT_SOURCE_CONDITIONALLY(key, liteParser, fullParser, minVersion, true)
+#define REGISTER_DOCUMENT_SOURCE_WITH_MIN_VERSION( \
+ key, liteParser, fullParser, allowedWithApiStrict, minVersion) \
+ REGISTER_DOCUMENT_SOURCE_CONDITIONALLY( \
+ key, liteParser, fullParser, allowedWithApiStrict, minVersion, true)
/**
* Like REGISTER_DOCUMENT_SOURCE_WITH_MIN_VERSION, except you can also specify a condition,
@@ -109,13 +112,14 @@ class Document;
*
* This is the most general REGISTER_DOCUMENT_SOURCE* macro, which all others should delegate to.
*/
-#define REGISTER_DOCUMENT_SOURCE_CONDITIONALLY(key, liteParser, fullParser, minVersion, ...) \
- MONGO_INITIALIZER(addToDocSourceParserMap_##key)(InitializerContext*) { \
- if (!__VA_ARGS__) { \
- return; \
- } \
- LiteParsedDocumentSource::registerParser("$" #key, liteParser); \
- DocumentSource::registerParser("$" #key, fullParser, minVersion); \
+#define REGISTER_DOCUMENT_SOURCE_CONDITIONALLY( \
+ key, liteParser, fullParser, allowedWithApiStrict, minVersion, ...) \
+ MONGO_INITIALIZER(addToDocSourceParserMap_##key)(InitializerContext*) { \
+ if (!__VA_ARGS__) { \
+ return; \
+ } \
+ LiteParsedDocumentSource::registerParser("$" #key, liteParser, allowedWithApiStrict); \
+ DocumentSource::registerParser("$" #key, fullParser, minVersion); \
}
/**
@@ -123,7 +127,12 @@ class Document;
*/
#define REGISTER_TEST_DOCUMENT_SOURCE(key, liteParser, fullParser) \
REGISTER_DOCUMENT_SOURCE_CONDITIONALLY( \
- key, liteParser, fullParser, boost::none, ::mongo::getTestCommandsEnabled())
+ key, \
+ liteParser, \
+ fullParser, \
+ LiteParsedDocumentSource::AllowedWithApiStrict::kInternal, \
+ boost::none, \
+ ::mongo::getTestCommandsEnabled())
class DocumentSource : public RefCountable {
public:
diff --git a/src/mongo/db/pipeline/document_source_add_fields.cpp b/src/mongo/db/pipeline/document_source_add_fields.cpp
index 53d012cd258..ec245402c2a 100644
--- a/src/mongo/db/pipeline/document_source_add_fields.cpp
+++ b/src/mongo/db/pipeline/document_source_add_fields.cpp
@@ -42,10 +42,12 @@ using boost::intrusive_ptr;
REGISTER_DOCUMENT_SOURCE(addFields,
LiteParsedDocumentSourceDefault::parse,
- DocumentSourceAddFields::createFromBson);
+ DocumentSourceAddFields::createFromBson,
+ LiteParsedDocumentSource::AllowedWithApiStrict::kAlways);
REGISTER_DOCUMENT_SOURCE(set,
LiteParsedDocumentSourceDefault::parse,
- DocumentSourceAddFields::createFromBson);
+ DocumentSourceAddFields::createFromBson,
+ LiteParsedDocumentSource::AllowedWithApiStrict::kAlways);
intrusive_ptr<DocumentSource> DocumentSourceAddFields::create(
BSONObj addFieldsSpec,
diff --git a/src/mongo/db/pipeline/document_source_bucket.cpp b/src/mongo/db/pipeline/document_source_bucket.cpp
index d5761bf3c85..5fe098ae418 100644
--- a/src/mongo/db/pipeline/document_source_bucket.cpp
+++ b/src/mongo/db/pipeline/document_source_bucket.cpp
@@ -42,7 +42,8 @@ using std::vector;
REGISTER_DOCUMENT_SOURCE(bucket,
LiteParsedDocumentSourceDefault::parse,
- DocumentSourceBucket::createFromBson);
+ DocumentSourceBucket::createFromBson,
+ LiteParsedDocumentSource::AllowedWithApiStrict::kAlways);
namespace {
intrusive_ptr<ExpressionConstant> getExpressionConstant(ExpressionContext* const expCtx,
diff --git a/src/mongo/db/pipeline/document_source_bucket_auto.cpp b/src/mongo/db/pipeline/document_source_bucket_auto.cpp
index f5fea67e862..1ffac313f28 100644
--- a/src/mongo/db/pipeline/document_source_bucket_auto.cpp
+++ b/src/mongo/db/pipeline/document_source_bucket_auto.cpp
@@ -44,7 +44,8 @@ using std::vector;
REGISTER_DOCUMENT_SOURCE(bucketAuto,
LiteParsedDocumentSourceDefault::parse,
- DocumentSourceBucketAuto::createFromBson);
+ DocumentSourceBucketAuto::createFromBson,
+ LiteParsedDocumentSource::AllowedWithApiStrict::kAlways);
namespace {
diff --git a/src/mongo/db/pipeline/document_source_change_stream.cpp b/src/mongo/db/pipeline/document_source_change_stream.cpp
index 78d510b625d..d1e51781de3 100644
--- a/src/mongo/db/pipeline/document_source_change_stream.cpp
+++ b/src/mongo/db/pipeline/document_source_change_stream.cpp
@@ -68,7 +68,8 @@ using std::vector;
// will not serialize themselves.
REGISTER_DOCUMENT_SOURCE(changeStream,
DocumentSourceChangeStream::LiteParsed::parse,
- DocumentSourceChangeStream::createFromBson);
+ DocumentSourceChangeStream::createFromBson,
+ LiteParsedDocumentSource::AllowedWithApiStrict::kAlways);
constexpr StringData DocumentSourceChangeStream::kDocumentKeyField;
constexpr StringData DocumentSourceChangeStream::kFullDocumentBeforeChangeField;
diff --git a/src/mongo/db/pipeline/document_source_coll_stats.cpp b/src/mongo/db/pipeline/document_source_coll_stats.cpp
index 1c2d295b27b..30b7255e1ce 100644
--- a/src/mongo/db/pipeline/document_source_coll_stats.cpp
+++ b/src/mongo/db/pipeline/document_source_coll_stats.cpp
@@ -43,7 +43,8 @@ namespace mongo {
REGISTER_DOCUMENT_SOURCE(collStats,
DocumentSourceCollStats::LiteParsed::parse,
- DocumentSourceCollStats::createFromBson);
+ DocumentSourceCollStats::createFromBson,
+ LiteParsedDocumentSource::AllowedWithApiStrict::kNeverInVersion1);
const char* DocumentSourceCollStats::getSourceName() const {
return kStageName.rawData();
diff --git a/src/mongo/db/pipeline/document_source_count.cpp b/src/mongo/db/pipeline/document_source_count.cpp
index 1107e2e6eeb..988bf414da3 100644
--- a/src/mongo/db/pipeline/document_source_count.cpp
+++ b/src/mongo/db/pipeline/document_source_count.cpp
@@ -45,7 +45,8 @@ using std::string;
REGISTER_DOCUMENT_SOURCE(count,
LiteParsedDocumentSourceDefault::parse,
- DocumentSourceCount::createFromBson);
+ DocumentSourceCount::createFromBson,
+ LiteParsedDocumentSource::AllowedWithApiStrict::kAlways);
list<intrusive_ptr<DocumentSource>> DocumentSourceCount::createFromBson(
BSONElement elem, const 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 e0d40c619c0..da1dda80226 100644
--- a/src/mongo/db/pipeline/document_source_current_op.cpp
+++ b/src/mongo/db/pipeline/document_source_current_op.cpp
@@ -54,7 +54,8 @@ using boost::intrusive_ptr;
REGISTER_DOCUMENT_SOURCE(currentOp,
DocumentSourceCurrentOp::LiteParsed::parse,
- DocumentSourceCurrentOp::createFromBson);
+ DocumentSourceCurrentOp::createFromBson,
+ LiteParsedDocumentSource::AllowedWithApiStrict::kNeverInVersion1);
constexpr StringData DocumentSourceCurrentOp::kStageName;
diff --git a/src/mongo/db/pipeline/document_source_facet.cpp b/src/mongo/db/pipeline/document_source_facet.cpp
index 8c5e3da99e0..2cee70a250c 100644
--- a/src/mongo/db/pipeline/document_source_facet.cpp
+++ b/src/mongo/db/pipeline/document_source_facet.cpp
@@ -123,7 +123,8 @@ std::unique_ptr<DocumentSourceFacet::LiteParsed> DocumentSourceFacet::LiteParsed
REGISTER_DOCUMENT_SOURCE(facet,
DocumentSourceFacet::LiteParsed::parse,
- DocumentSourceFacet::createFromBson);
+ DocumentSourceFacet::createFromBson,
+ LiteParsedDocumentSource::AllowedWithApiStrict::kAlways);
intrusive_ptr<DocumentSourceFacet> DocumentSourceFacet::create(
std::vector<FacetPipeline> facetPipelines,
diff --git a/src/mongo/db/pipeline/document_source_geo_near.cpp b/src/mongo/db/pipeline/document_source_geo_near.cpp
index a682d8fbeaa..a5b49788551 100644
--- a/src/mongo/db/pipeline/document_source_geo_near.cpp
+++ b/src/mongo/db/pipeline/document_source_geo_near.cpp
@@ -46,7 +46,8 @@ constexpr StringData DocumentSourceGeoNear::kKeyFieldName;
REGISTER_DOCUMENT_SOURCE(geoNear,
LiteParsedDocumentSourceDefault::parse,
- DocumentSourceGeoNear::createFromBson);
+ DocumentSourceGeoNear::createFromBson,
+ LiteParsedDocumentSource::AllowedWithApiStrict::kAlways);
Value DocumentSourceGeoNear::serialize(boost::optional<ExplainOptions::Verbosity> explain) const {
MutableDocument result;
diff --git a/src/mongo/db/pipeline/document_source_graph_lookup.cpp b/src/mongo/db/pipeline/document_source_graph_lookup.cpp
index b9b10cf15a4..eca4b8b8211 100644
--- a/src/mongo/db/pipeline/document_source_graph_lookup.cpp
+++ b/src/mongo/db/pipeline/document_source_graph_lookup.cpp
@@ -111,7 +111,8 @@ std::unique_ptr<DocumentSourceGraphLookUp::LiteParsed> DocumentSourceGraphLookUp
REGISTER_DOCUMENT_SOURCE(graphLookup,
DocumentSourceGraphLookUp::LiteParsed::parse,
- DocumentSourceGraphLookUp::createFromBson);
+ DocumentSourceGraphLookUp::createFromBson,
+ LiteParsedDocumentSource::AllowedWithApiStrict::kAlways);
const char* DocumentSourceGraphLookUp::getSourceName() const {
return kStageName.rawData();
diff --git a/src/mongo/db/pipeline/document_source_group.cpp b/src/mongo/db/pipeline/document_source_group.cpp
index f056395b8a3..7987265b028 100644
--- a/src/mongo/db/pipeline/document_source_group.cpp
+++ b/src/mongo/db/pipeline/document_source_group.cpp
@@ -126,7 +126,8 @@ constexpr StringData DocumentSourceGroup::kStageName;
REGISTER_DOCUMENT_SOURCE(group,
LiteParsedDocumentSourceDefault::parse,
- DocumentSourceGroup::createFromBson);
+ DocumentSourceGroup::createFromBson,
+ LiteParsedDocumentSource::AllowedWithApiStrict::kAlways);
const char* DocumentSourceGroup::getSourceName() const {
return kStageName.rawData();
diff --git a/src/mongo/db/pipeline/document_source_index_stats.cpp b/src/mongo/db/pipeline/document_source_index_stats.cpp
index 5612135cd78..ad6d600e74d 100644
--- a/src/mongo/db/pipeline/document_source_index_stats.cpp
+++ b/src/mongo/db/pipeline/document_source_index_stats.cpp
@@ -41,7 +41,8 @@ using boost::intrusive_ptr;
REGISTER_DOCUMENT_SOURCE(indexStats,
DocumentSourceIndexStats::LiteParsed::parse,
- DocumentSourceIndexStats::createFromBson);
+ DocumentSourceIndexStats::createFromBson,
+ LiteParsedDocumentSource::AllowedWithApiStrict::kNeverInVersion1);
const char* DocumentSourceIndexStats::getSourceName() const {
return kStageName.rawData();
diff --git a/src/mongo/db/pipeline/document_source_internal_inhibit_optimization.cpp b/src/mongo/db/pipeline/document_source_internal_inhibit_optimization.cpp
index f5729e4570a..10f10171f06 100644
--- a/src/mongo/db/pipeline/document_source_internal_inhibit_optimization.cpp
+++ b/src/mongo/db/pipeline/document_source_internal_inhibit_optimization.cpp
@@ -35,7 +35,8 @@ namespace mongo {
REGISTER_DOCUMENT_SOURCE(_internalInhibitOptimization,
LiteParsedDocumentSourceDefault::parse,
- DocumentSourceInternalInhibitOptimization::createFromBson);
+ DocumentSourceInternalInhibitOptimization::createFromBson,
+ LiteParsedDocumentSource::AllowedWithApiStrict::kNeverInVersion1);
constexpr StringData DocumentSourceInternalInhibitOptimization::kStageName;
diff --git a/src/mongo/db/pipeline/document_source_internal_split_pipeline.cpp b/src/mongo/db/pipeline/document_source_internal_split_pipeline.cpp
index 4857fac3d58..c2721ff77d3 100644
--- a/src/mongo/db/pipeline/document_source_internal_split_pipeline.cpp
+++ b/src/mongo/db/pipeline/document_source_internal_split_pipeline.cpp
@@ -35,7 +35,8 @@ namespace mongo {
REGISTER_DOCUMENT_SOURCE(_internalSplitPipeline,
LiteParsedDocumentSourceDefault::parse,
- DocumentSourceInternalSplitPipeline::createFromBson);
+ DocumentSourceInternalSplitPipeline::createFromBson,
+ LiteParsedDocumentSource::AllowedWithApiStrict::kNeverInVersion1);
constexpr StringData DocumentSourceInternalSplitPipeline::kStageName;
diff --git a/src/mongo/db/pipeline/document_source_internal_unpack_bucket.cpp b/src/mongo/db/pipeline/document_source_internal_unpack_bucket.cpp
index 6326ec32419..4abb46215a5 100644
--- a/src/mongo/db/pipeline/document_source_internal_unpack_bucket.cpp
+++ b/src/mongo/db/pipeline/document_source_internal_unpack_bucket.cpp
@@ -42,7 +42,8 @@ namespace mongo {
REGISTER_DOCUMENT_SOURCE(_internalUnpackBucket,
LiteParsedDocumentSourceDefault::parse,
- DocumentSourceInternalUnpackBucket::createFromBson);
+ DocumentSourceInternalUnpackBucket::createFromBson,
+ LiteParsedDocumentSource::AllowedWithApiStrict::kInternal);
namespace {
/**
diff --git a/src/mongo/db/pipeline/document_source_limit.cpp b/src/mongo/db/pipeline/document_source_limit.cpp
index ef08bc707ad..e6ef0d71405 100644
--- a/src/mongo/db/pipeline/document_source_limit.cpp
+++ b/src/mongo/db/pipeline/document_source_limit.cpp
@@ -48,7 +48,8 @@ DocumentSourceLimit::DocumentSourceLimit(const intrusive_ptr<ExpressionContext>&
REGISTER_DOCUMENT_SOURCE(limit,
LiteParsedDocumentSourceDefault::parse,
- DocumentSourceLimit::createFromBson);
+ DocumentSourceLimit::createFromBson,
+ LiteParsedDocumentSource::AllowedWithApiStrict::kAlways);
constexpr StringData DocumentSourceLimit::kStageName;
diff --git a/src/mongo/db/pipeline/document_source_list_local_sessions.cpp b/src/mongo/db/pipeline/document_source_list_local_sessions.cpp
index 24efef4263a..f9c329ff65e 100644
--- a/src/mongo/db/pipeline/document_source_list_local_sessions.cpp
+++ b/src/mongo/db/pipeline/document_source_list_local_sessions.cpp
@@ -39,7 +39,8 @@ namespace mongo {
REGISTER_DOCUMENT_SOURCE(listLocalSessions,
DocumentSourceListLocalSessions::LiteParsed::parse,
- DocumentSourceListLocalSessions::createFromBson);
+ DocumentSourceListLocalSessions::createFromBson,
+ LiteParsedDocumentSource::AllowedWithApiStrict::kNeverInVersion1);
DocumentSource::GetNextResult DocumentSourceListLocalSessions::doGetNext() {
while (!_ids.empty()) {
diff --git a/src/mongo/db/pipeline/document_source_list_sessions.cpp b/src/mongo/db/pipeline/document_source_list_sessions.cpp
index 5eba38b1510..996856b3657 100644
--- a/src/mongo/db/pipeline/document_source_list_sessions.cpp
+++ b/src/mongo/db/pipeline/document_source_list_sessions.cpp
@@ -40,7 +40,8 @@ namespace mongo {
REGISTER_DOCUMENT_SOURCE(listSessions,
DocumentSourceListSessions::LiteParsed::parse,
- DocumentSourceListSessions::createFromBson);
+ DocumentSourceListSessions::createFromBson,
+ LiteParsedDocumentSource::AllowedWithApiStrict::kNeverInVersion1);
boost::intrusive_ptr<DocumentSource> DocumentSourceListSessions::createFromBson(
BSONElement elem, const boost::intrusive_ptr<ExpressionContext>& pExpCtx) {
diff --git a/src/mongo/db/pipeline/document_source_lookup.cpp b/src/mongo/db/pipeline/document_source_lookup.cpp
index 1961941287c..1b62deebf6e 100644
--- a/src/mongo/db/pipeline/document_source_lookup.cpp
+++ b/src/mongo/db/pipeline/document_source_lookup.cpp
@@ -253,7 +253,8 @@ PrivilegeVector DocumentSourceLookUp::LiteParsed::requiredPrivileges(
REGISTER_DOCUMENT_SOURCE(lookup,
DocumentSourceLookUp::LiteParsed::parse,
- DocumentSourceLookUp::createFromBson);
+ DocumentSourceLookUp::createFromBson,
+ LiteParsedDocumentSource::AllowedWithApiStrict::kAlways);
const char* DocumentSourceLookUp::getSourceName() const {
return kStageName.rawData();
diff --git a/src/mongo/db/pipeline/document_source_match.cpp b/src/mongo/db/pipeline/document_source_match.cpp
index e64151cb77f..d292f7a64de 100644
--- a/src/mongo/db/pipeline/document_source_match.cpp
+++ b/src/mongo/db/pipeline/document_source_match.cpp
@@ -57,7 +57,8 @@ using std::vector;
REGISTER_DOCUMENT_SOURCE(match,
LiteParsedDocumentSourceDefault::parse,
- DocumentSourceMatch::createFromBson);
+ DocumentSourceMatch::createFromBson,
+ LiteParsedDocumentSource::AllowedWithApiStrict::kAlways);
const char* DocumentSourceMatch::getSourceName() const {
return kStageName.rawData();
diff --git a/src/mongo/db/pipeline/document_source_merge.cpp b/src/mongo/db/pipeline/document_source_merge.cpp
index 72e3089bd71..36f652c66cd 100644
--- a/src/mongo/db/pipeline/document_source_merge.cpp
+++ b/src/mongo/db/pipeline/document_source_merge.cpp
@@ -49,7 +49,8 @@ using namespace fmt::literals;
MONGO_FAIL_POINT_DEFINE(hangWhileBuildingDocumentSourceMergeBatch);
REGISTER_DOCUMENT_SOURCE(merge,
DocumentSourceMerge::LiteParsed::parse,
- DocumentSourceMerge::createFromBson);
+ DocumentSourceMerge::createFromBson,
+ LiteParsedDocumentSource::AllowedWithApiStrict::kAlways);
namespace {
using MergeStrategyDescriptor = DocumentSourceMerge::MergeStrategyDescriptor;
diff --git a/src/mongo/db/pipeline/document_source_operation_metrics.cpp b/src/mongo/db/pipeline/document_source_operation_metrics.cpp
index c2037bc2e12..5855894bf3b 100644
--- a/src/mongo/db/pipeline/document_source_operation_metrics.cpp
+++ b/src/mongo/db/pipeline/document_source_operation_metrics.cpp
@@ -43,7 +43,8 @@ using boost::intrusive_ptr;
REGISTER_DOCUMENT_SOURCE(operationMetrics,
DocumentSourceOperationMetrics::LiteParsed::parse,
- DocumentSourceOperationMetrics::createFromBson);
+ DocumentSourceOperationMetrics::createFromBson,
+ LiteParsedDocumentSource::AllowedWithApiStrict::kNeverInVersion1);
const char* DocumentSourceOperationMetrics::getSourceName() const {
return kStageName.rawData();
diff --git a/src/mongo/db/pipeline/document_source_out.cpp b/src/mongo/db/pipeline/document_source_out.cpp
index bdb2991d146..1fef7bb2bd2 100644
--- a/src/mongo/db/pipeline/document_source_out.cpp
+++ b/src/mongo/db/pipeline/document_source_out.cpp
@@ -51,7 +51,8 @@ MONGO_FAIL_POINT_DEFINE(hangWhileBuildingDocumentSourceOutBatch);
MONGO_FAIL_POINT_DEFINE(outWaitAfterTempCollectionCreation);
REGISTER_DOCUMENT_SOURCE(out,
DocumentSourceOut::LiteParsed::parse,
- DocumentSourceOut::createFromBson);
+ DocumentSourceOut::createFromBson,
+ LiteParsedDocumentSource::AllowedWithApiStrict::kAlways);
DocumentSourceOut::~DocumentSourceOut() {
DESTRUCTOR_GUARD(
diff --git a/src/mongo/db/pipeline/document_source_plan_cache_stats.cpp b/src/mongo/db/pipeline/document_source_plan_cache_stats.cpp
index 1d871a9521b..bfbe60b4ec8 100644
--- a/src/mongo/db/pipeline/document_source_plan_cache_stats.cpp
+++ b/src/mongo/db/pipeline/document_source_plan_cache_stats.cpp
@@ -35,7 +35,8 @@ namespace mongo {
REGISTER_DOCUMENT_SOURCE(planCacheStats,
DocumentSourcePlanCacheStats::LiteParsed::parse,
- DocumentSourcePlanCacheStats::createFromBson);
+ DocumentSourcePlanCacheStats::createFromBson,
+ LiteParsedDocumentSource::AllowedWithApiStrict::kNeverInVersion1);
boost::intrusive_ptr<DocumentSource> DocumentSourcePlanCacheStats::createFromBson(
BSONElement spec, const boost::intrusive_ptr<ExpressionContext>& pExpCtx) {
diff --git a/src/mongo/db/pipeline/document_source_project.cpp b/src/mongo/db/pipeline/document_source_project.cpp
index 6ea0e53e055..3630cae3440 100644
--- a/src/mongo/db/pipeline/document_source_project.cpp
+++ b/src/mongo/db/pipeline/document_source_project.cpp
@@ -45,11 +45,13 @@ using boost::intrusive_ptr;
REGISTER_DOCUMENT_SOURCE(project,
LiteParsedDocumentSourceDefault::parse,
- DocumentSourceProject::createFromBson);
+ DocumentSourceProject::createFromBson,
+ LiteParsedDocumentSource::AllowedWithApiStrict::kAlways);
REGISTER_DOCUMENT_SOURCE(unset,
LiteParsedDocumentSourceDefault::parse,
- DocumentSourceProject::createFromBson);
+ DocumentSourceProject::createFromBson,
+ LiteParsedDocumentSource::AllowedWithApiStrict::kAlways);
namespace {
BSONObj buildExclusionProjectionSpecification(const std::vector<BSONElement>& unsetSpec) {
diff --git a/src/mongo/db/pipeline/document_source_redact.cpp b/src/mongo/db/pipeline/document_source_redact.cpp
index efe6316c69b..8fcc1a6c826 100644
--- a/src/mongo/db/pipeline/document_source_redact.cpp
+++ b/src/mongo/db/pipeline/document_source_redact.cpp
@@ -51,7 +51,8 @@ DocumentSourceRedact::DocumentSourceRedact(const intrusive_ptr<ExpressionContext
REGISTER_DOCUMENT_SOURCE(redact,
LiteParsedDocumentSourceDefault::parse,
- DocumentSourceRedact::createFromBson);
+ DocumentSourceRedact::createFromBson,
+ LiteParsedDocumentSource::AllowedWithApiStrict::kAlways);
const char* DocumentSourceRedact::getSourceName() const {
return kStageName.rawData();
diff --git a/src/mongo/db/pipeline/document_source_replace_root.cpp b/src/mongo/db/pipeline/document_source_replace_root.cpp
index 2d3e1de27af..084d7398fb9 100644
--- a/src/mongo/db/pipeline/document_source_replace_root.cpp
+++ b/src/mongo/db/pipeline/document_source_replace_root.cpp
@@ -76,10 +76,12 @@ Document ReplaceRootTransformation::applyTransformation(const Document& input) {
REGISTER_DOCUMENT_SOURCE(replaceRoot,
LiteParsedDocumentSourceDefault::parse,
- DocumentSourceReplaceRoot::createFromBson);
+ DocumentSourceReplaceRoot::createFromBson,
+ LiteParsedDocumentSource::AllowedWithApiStrict::kAlways);
REGISTER_DOCUMENT_SOURCE(replaceWith,
LiteParsedDocumentSourceDefault::parse,
- DocumentSourceReplaceRoot::createFromBson);
+ DocumentSourceReplaceRoot::createFromBson,
+ LiteParsedDocumentSource::AllowedWithApiStrict::kAlways);
intrusive_ptr<DocumentSource> DocumentSourceReplaceRoot::createFromBson(
BSONElement elem, const intrusive_ptr<ExpressionContext>& expCtx) {
diff --git a/src/mongo/db/pipeline/document_source_sample.cpp b/src/mongo/db/pipeline/document_source_sample.cpp
index bfb667f5983..57d5661c388 100644
--- a/src/mongo/db/pipeline/document_source_sample.cpp
+++ b/src/mongo/db/pipeline/document_source_sample.cpp
@@ -48,7 +48,8 @@ DocumentSourceSample::DocumentSourceSample(const intrusive_ptr<ExpressionContext
REGISTER_DOCUMENT_SOURCE(sample,
LiteParsedDocumentSourceDefault::parse,
- DocumentSourceSample::createFromBson);
+ DocumentSourceSample::createFromBson,
+ LiteParsedDocumentSource::AllowedWithApiStrict::kAlways);
DocumentSource::GetNextResult DocumentSourceSample::doGetNext() {
if (_size == 0)
diff --git a/src/mongo/db/pipeline/document_source_set_window_fields.cpp b/src/mongo/db/pipeline/document_source_set_window_fields.cpp
index a089596ccb6..d8d691eff34 100644
--- a/src/mongo/db/pipeline/document_source_set_window_fields.cpp
+++ b/src/mongo/db/pipeline/document_source_set_window_fields.cpp
@@ -49,6 +49,7 @@ REGISTER_DOCUMENT_SOURCE_CONDITIONALLY(
setWindowFields,
LiteParsedDocumentSourceDefault::parse,
document_source_set_window_fields::createFromBson,
+ LiteParsedDocumentSource::AllowedWithApiStrict::kAlways,
boost::none,
::mongo::feature_flags::gFeatureFlagWindowFunctions.isEnabledAndIgnoreFCV());
@@ -56,6 +57,7 @@ REGISTER_DOCUMENT_SOURCE_CONDITIONALLY(
_internalSetWindowFields,
LiteParsedDocumentSourceDefault::parse,
DocumentSourceInternalSetWindowFields::createFromBson,
+ LiteParsedDocumentSource::AllowedWithApiStrict::kInternal,
boost::none,
::mongo::feature_flags::gFeatureFlagWindowFunctions.isEnabledAndIgnoreFCV());
diff --git a/src/mongo/db/pipeline/document_source_skip.cpp b/src/mongo/db/pipeline/document_source_skip.cpp
index 33fff89bcef..b5c91d23059 100644
--- a/src/mongo/db/pipeline/document_source_skip.cpp
+++ b/src/mongo/db/pipeline/document_source_skip.cpp
@@ -49,7 +49,8 @@ DocumentSourceSkip::DocumentSourceSkip(const intrusive_ptr<ExpressionContext>& p
REGISTER_DOCUMENT_SOURCE(skip,
LiteParsedDocumentSourceDefault::parse,
- DocumentSourceSkip::createFromBson);
+ DocumentSourceSkip::createFromBson,
+ LiteParsedDocumentSource::AllowedWithApiStrict::kAlways);
constexpr StringData DocumentSourceSkip::kStageName;
diff --git a/src/mongo/db/pipeline/document_source_sort.cpp b/src/mongo/db/pipeline/document_source_sort.cpp
index 3e5bdf80e6a..f27dc68c0dd 100644
--- a/src/mongo/db/pipeline/document_source_sort.cpp
+++ b/src/mongo/db/pipeline/document_source_sort.cpp
@@ -73,7 +73,8 @@ DocumentSourceSort::DocumentSourceSort(const boost::intrusive_ptr<ExpressionCont
REGISTER_DOCUMENT_SOURCE(sort,
LiteParsedDocumentSourceDefault::parse,
- DocumentSourceSort::createFromBson);
+ DocumentSourceSort::createFromBson,
+ LiteParsedDocumentSource::AllowedWithApiStrict::kAlways);
DocumentSource::GetNextResult DocumentSourceSort::doGetNext() {
if (!_populated) {
diff --git a/src/mongo/db/pipeline/document_source_sort_by_count.cpp b/src/mongo/db/pipeline/document_source_sort_by_count.cpp
index 5efce02e3a0..d35ec06f33e 100644
--- a/src/mongo/db/pipeline/document_source_sort_by_count.cpp
+++ b/src/mongo/db/pipeline/document_source_sort_by_count.cpp
@@ -44,7 +44,8 @@ using std::list;
REGISTER_DOCUMENT_SOURCE(sortByCount,
LiteParsedDocumentSourceDefault::parse,
- DocumentSourceSortByCount::createFromBson);
+ DocumentSourceSortByCount::createFromBson,
+ LiteParsedDocumentSource::AllowedWithApiStrict::kAlways);
list<intrusive_ptr<DocumentSource>> DocumentSourceSortByCount::createFromBson(
BSONElement elem, const intrusive_ptr<ExpressionContext>& pExpCtx) {
diff --git a/src/mongo/db/pipeline/document_source_union_with.cpp b/src/mongo/db/pipeline/document_source_union_with.cpp
index bb89ec69a0d..17570253cda 100644
--- a/src/mongo/db/pipeline/document_source_union_with.cpp
+++ b/src/mongo/db/pipeline/document_source_union_with.cpp
@@ -44,7 +44,8 @@ namespace mongo {
REGISTER_DOCUMENT_SOURCE(unionWith,
DocumentSourceUnionWith::LiteParsed::parse,
- DocumentSourceUnionWith::createFromBson);
+ DocumentSourceUnionWith::createFromBson,
+ LiteParsedDocumentSource::AllowedWithApiStrict::kAlways);
namespace {
std::unique_ptr<Pipeline, PipelineDeleter> buildPipelineFromViewDefinition(
diff --git a/src/mongo/db/pipeline/document_source_unwind.cpp b/src/mongo/db/pipeline/document_source_unwind.cpp
index 95816bbb68e..b08e6f7aec3 100644
--- a/src/mongo/db/pipeline/document_source_unwind.cpp
+++ b/src/mongo/db/pipeline/document_source_unwind.cpp
@@ -168,7 +168,8 @@ DocumentSourceUnwind::DocumentSourceUnwind(const intrusive_ptr<ExpressionContext
REGISTER_DOCUMENT_SOURCE(unwind,
LiteParsedDocumentSourceDefault::parse,
- DocumentSourceUnwind::createFromBson);
+ DocumentSourceUnwind::createFromBson,
+ LiteParsedDocumentSource::AllowedWithApiStrict::kAlways);
const char* DocumentSourceUnwind::getSourceName() const {
return kStageName.rawData();
diff --git a/src/mongo/db/pipeline/lite_parsed_document_source.cpp b/src/mongo/db/pipeline/lite_parsed_document_source.cpp
index a21064b8387..7ef5d1399c6 100644
--- a/src/mongo/db/pipeline/lite_parsed_document_source.cpp
+++ b/src/mongo/db/pipeline/lite_parsed_document_source.cpp
@@ -42,12 +42,19 @@ namespace {
// Empty vector used by LiteParsedDocumentSources which do not have a sub pipeline.
inline static std::vector<LiteParsedPipeline> kNoSubPipeline = {};
-StringMap<Parser> parserMap;
+struct LiteParserInfo {
+ Parser parser;
+ LiteParsedDocumentSource::AllowedWithApiStrict allowedWithApiStrict;
+};
+
+StringMap<LiteParserInfo> parserMap;
} // namespace
-void LiteParsedDocumentSource::registerParser(const std::string& name, Parser parser) {
- parserMap[name] = parser;
+void LiteParsedDocumentSource::registerParser(const std::string& name,
+ Parser parser,
+ AllowedWithApiStrict flag) {
+ parserMap[name] = {parser, flag};
// Initialize a counter for this document source to track how many times it is used.
aggStageCounters.stageCounterMap[name] = std::make_unique<AggStageCounters::StageCounter>(name);
}
@@ -66,7 +73,17 @@ std::unique_ptr<LiteParsedDocumentSource> LiteParsedDocumentSource::parse(
str::stream() << "Unrecognized pipeline stage name: '" << stageName << "'",
it != parserMap.end());
- return it->second(nss, specElem);
+ return it->second.parser(nss, specElem);
+}
+
+LiteParsedDocumentSource::AllowedWithApiStrict LiteParsedDocumentSource::getApiVersionAllowanceFlag(
+ std::string stageName) {
+ auto it = parserMap.find(stageName);
+ uassert(5407200,
+ str::stream() << "Unrecognized pipeline stage name: '" << stageName << "'",
+ it != parserMap.end());
+
+ return it->second.allowedWithApiStrict;
}
const std::vector<LiteParsedPipeline>& LiteParsedDocumentSource::getSubPipelines() const {
diff --git a/src/mongo/db/pipeline/lite_parsed_document_source.h b/src/mongo/db/pipeline/lite_parsed_document_source.h
index 38c01ab02c3..b289effe0fb 100644
--- a/src/mongo/db/pipeline/lite_parsed_document_source.h
+++ b/src/mongo/db/pipeline/lite_parsed_document_source.h
@@ -53,6 +53,18 @@ class LiteParsedPipeline;
*/
class LiteParsedDocumentSource {
public:
+ /**
+ * Flags to mark stages with different allowance constrains when API versioning is enabled.
+ */
+ enum class AllowedWithApiStrict {
+ // The stage is always allowed in the pipeline regardless of API versions.
+ kAlways,
+ // The stage is allowed only for internal client when 'apiStrict' is set to true.
+ kInternal,
+ // The stage is never allowed in API version '1' when 'apiStrict' is set to true.
+ kNeverInVersion1
+ };
+
LiteParsedDocumentSource(std::string parseTimeName)
: _parseTimeName(std::move(parseTimeName)) {}
@@ -71,11 +83,20 @@ public:
/**
* Registers a DocumentSource with a spec parsing function, so that when a stage with the given
* name is encountered, it will call 'parser' to construct that stage's specification object.
+ * The flag 'allowedWithApiStrict' is used to control the allowance of the stage when
+ * 'apiStrict' is set to true.
*
* DO NOT call this method directly. Instead, use the REGISTER_DOCUMENT_SOURCE macro defined in
* document_source.h.
*/
- static void registerParser(const std::string& name, Parser parser);
+ static void registerParser(const std::string& name,
+ Parser parser,
+ AllowedWithApiStrict allowedWithApiStrict);
+
+ /**
+ * Returns the 'ApiVersionAllowanceFlag' flag value for the specified stage name.
+ */
+ static AllowedWithApiStrict getApiVersionAllowanceFlag(std::string stageName);
/**
* Constructs a LiteParsedDocumentSource from the user-supplied BSON, or throws a
diff --git a/src/mongo/db/pipeline/lite_parsed_pipeline.cpp b/src/mongo/db/pipeline/lite_parsed_pipeline.cpp
index c737d837ba7..38619be9e78 100644
--- a/src/mongo/db/pipeline/lite_parsed_pipeline.cpp
+++ b/src/mongo/db/pipeline/lite_parsed_pipeline.cpp
@@ -124,18 +124,53 @@ void LiteParsedPipeline::tickGlobalStageCounters() const {
}
}
-void LiteParsedPipeline::validatePipelineStagesIfAPIStrict(const std::string& version) const {
+void LiteParsedPipeline::validatePipelineStagesforAPIVersion(const OperationContext* opCtx) const {
+ invariant(opCtx);
+
+ using AllowanceFlags = LiteParsedDocumentSource::AllowedWithApiStrict;
+
+ auto apiParameters = APIParameters::get(opCtx);
+ bool apiStrict = apiParameters.getAPIStrict().value_or(false);
+
+ // These checks gets applied only when apiStrict is set to true.
+ if (!apiStrict) {
+ return;
+ }
+
+ auto apiVersion = apiParameters.getAPIVersion().value_or("");
+ auto client = opCtx->getClient();
+
+ // An internal client could be one of the following :
+ // - Does not have any transport session
+ // - The transport session tag is internal
+ bool isInternalClient =
+ !client->session() || (client->session()->getTags() & transport::Session::kInternalClient);
+
+
for (auto&& stage : _stageSpecs) {
- if (version == "1") {
+ const auto& stageName = stage->getParseTimeName();
+ const auto& flag = LiteParsedDocumentSource::getApiVersionAllowanceFlag(stageName);
+
+ // Checks that the stage is allowed in API version 1.
+ if (apiVersion == "1") {
uassert(ErrorCodes::APIStrictError,
- str::stream() << "stage " << stage->getParseTimeName()
+ str::stream() << "stage " << stageName
<< " is not allowed with 'apiStrict: true' in API Version "
- << version,
- isStageInAPIVersion1(stage->getParseTimeName()));
+ << apiVersion,
+ AllowanceFlags::kNeverInVersion1 != flag);
+ }
- for (auto&& subPipeline : stage->getSubPipelines()) {
- subPipeline.validatePipelineStagesIfAPIStrict(version);
- }
+ // Checks that the internal stage can be specified only by the internal client.
+ if (AllowanceFlags::kInternal == flag) {
+ uassert(ErrorCodes::APIStrictError,
+ str::stream() << "Internal stage " << stageName
+ << " cannot be specified with 'apiStrict: true' in API Version "
+ << apiVersion,
+ isInternalClient);
+ }
+
+ for (auto&& subPipeline : stage->getSubPipelines()) {
+ subPipeline.validatePipelineStagesforAPIVersion(opCtx);
}
}
}
diff --git a/src/mongo/db/pipeline/lite_parsed_pipeline.h b/src/mongo/db/pipeline/lite_parsed_pipeline.h
index 00a01ff6ba5..49a1215862e 100644
--- a/src/mongo/db/pipeline/lite_parsed_pipeline.h
+++ b/src/mongo/db/pipeline/lite_parsed_pipeline.h
@@ -170,27 +170,31 @@ public:
void tickGlobalStageCounters() const;
/**
- * Returns true if 'stageName' is in API Version 1.
- */
- static bool isStageInAPIVersion1(const std::string& stageName) {
- // These stages are excluded from API Version1 with 'apiStrict: true'.
- static const stdx::unordered_set<std::string> stagesExcluded = {"$collStats",
- "$currentOp",
- "$indexStats",
- "$listLocalSessions",
- "$listSessions",
- "$planCacheStats",
- "$search",
- "$searchBeta"};
-
- return (stagesExcluded.find(stageName) == stagesExcluded.end());
- }
+ * Performs API versioning validations on the aggregate pipeline stages.
+ */
+ void validatePipelineStagesforAPIVersion(const OperationContext* opCtx) const;
+
+ /**
+ * Validates if 'AggregateCommand' specs complies with API versioning. Throws uassert in case of
+ * any failure.
+ */
+ void validateRequestForAPIVersion(const OperationContext* opCtx,
+ const AggregateCommand& request) const;
/**
- * Throws 'APIStrictError' if the pipeline contains the stages which are not in API Version
- * 'version'.
+ * Performs validations related to API versioning before running the aggregation command.
+ * Throws uassert if any of the validations fails
+ * - validation on each stage on the pipeline
+ * - validation on 'AggregateCommand' request
*/
- void validatePipelineStagesIfAPIStrict(const std::string& version) const;
+ void performAPIVersionChecks(const OperationContext* opCtx,
+ const AggregateCommand& request) const {
+
+ invariant(opCtx);
+
+ validatePipelineStagesforAPIVersion(opCtx);
+ validateRequestForAPIVersion(opCtx, request);
+ }
private:
std::vector<std::unique_ptr<LiteParsedDocumentSource>> _stageSpecs;
diff --git a/src/mongo/db/views/view_catalog.cpp b/src/mongo/db/views/view_catalog.cpp
index 0132c61ee2c..80cc04f8603 100644
--- a/src/mongo/db/views/view_catalog.cpp
+++ b/src/mongo/db/views/view_catalog.cpp
@@ -397,14 +397,8 @@ StatusWith<stdx::unordered_set<NamespaceString>> ViewCatalog::validatePipeline(
const LiteParsedPipeline liteParsedPipeline(viewDef.viewOn(), viewDef.pipeline());
const auto involvedNamespaces = liteParsedPipeline.getInvolvedNamespaces();
- // If 'apiStrict: true', validates that the pipeline does not contain stages which are not in
- // this API version.
- auto apiParameters = APIParameters::get(opCtx);
- if (apiParameters.getAPIStrict().value_or(false)) {
- auto apiVersion = apiParameters.getAPIVersion();
- invariant(apiVersion);
- liteParsedPipeline.validatePipelineStagesIfAPIStrict(*apiVersion);
- }
+ // Perform API versioning validation checks on stages of the pipeline.
+ liteParsedPipeline.validatePipelineStagesforAPIVersion(opCtx);
// Verify that this is a legitimate pipeline specification by making sure it parses
// correctly. In order to parse a pipeline we need to resolve any namespaces involved to a
diff --git a/src/mongo/s/query/cluster_aggregate.cpp b/src/mongo/s/query/cluster_aggregate.cpp
index fa0d714d31c..1a2cdb8467f 100644
--- a/src/mongo/s/query/cluster_aggregate.cpp
+++ b/src/mongo/s/query/cluster_aggregate.cpp
@@ -188,6 +188,21 @@ void updateHostsTargetedMetrics(OperationContext* opCtx,
}
}
+/**
+ * Performs validations related to API versioning before running the aggregation command.
+ * Throws uassert if any of the validations fails
+ * - validation on each stage on the pipeline
+ * - validation on 'AggregateCommand' request
+ */
+void performAPIVersionChecks(const OperationContext* opCtx,
+ const AggregateCommand& request,
+ const LiteParsedPipeline& liteParsedPipeline) {
+ invariant(opCtx);
+
+ liteParsedPipeline.validatePipelineStagesforAPIVersion(opCtx);
+ aggregation_request_helper::validateRequestForAPIVersion(opCtx, request);
+}
+
} // namespace
Status ClusterAggregate::runAggregate(OperationContext* opCtx,
@@ -204,14 +219,8 @@ Status ClusterAggregate::runAggregate(OperationContext* opCtx,
const LiteParsedPipeline& liteParsedPipeline,
const PrivilegeVector& privileges,
BSONObjBuilder* result) {
- // If 'apiStrict: true', validates that the pipeline does not contain stages which are not in
- // this API version.
- auto apiParameters = APIParameters::get(opCtx);
- if (apiParameters.getAPIStrict().value_or(false)) {
- auto apiVersion = apiParameters.getAPIVersion().value_or("");
- if (!apiVersion.empty())
- liteParsedPipeline.validatePipelineStagesIfAPIStrict(apiVersion);
- }
+ // Perform API versioning validation checks.
+ performAPIVersionChecks(opCtx, request, liteParsedPipeline);
uassert(51028, "Cannot specify exchange option to a mongos", !request.getExchange());
uassert(51143,
diff --git a/src/mongo/s/query/document_source_merge_cursors.cpp b/src/mongo/s/query/document_source_merge_cursors.cpp
index abc183b7cae..a08e966817a 100644
--- a/src/mongo/s/query/document_source_merge_cursors.cpp
+++ b/src/mongo/s/query/document_source_merge_cursors.cpp
@@ -40,7 +40,8 @@ namespace mongo {
REGISTER_DOCUMENT_SOURCE(mergeCursors,
LiteParsedDocumentSourceDefault::parse,
- DocumentSourceMergeCursors::createFromBson);
+ DocumentSourceMergeCursors::createFromBson,
+ LiteParsedDocumentSource::AllowedWithApiStrict::kInternal);
constexpr StringData DocumentSourceMergeCursors::kStageName;