Skip to content

Commit

Permalink
Revert "SERVER-53127 Let AggregationRequestHelper::parseFromBSON() re…
Browse files Browse the repository at this point in the history
…turn an AggregateCommand"

This reverts commit 6feae12.
  • Loading branch information
benety authored and Evergreen Agent committed Feb 6, 2021
1 parent 7664a85 commit d77297f
Show file tree
Hide file tree
Showing 13 changed files with 119 additions and 86 deletions.
7 changes: 5 additions & 2 deletions src/mongo/db/auth/authorization_session_impl.cpp
Expand Up @@ -90,14 +90,17 @@ Status checkAuthForCreateOrModifyView(AuthorizationSession* authzSession,
return Status::OK();
}

auto request = aggregation_request_helper::parseFromBSON(
auto status = aggregation_request_helper::parseFromBSON(
viewNs,
BSON("aggregate" << viewOnNs.coll() << "pipeline" << viewPipeline << "cursor" << BSONObj()
<< "$db" << viewOnNs.db()),
boost::none,
false);
if (!status.isOK())
return status.getStatus();

auto statusWithPrivs = authzSession->getPrivilegesForAggregate(viewOnNs, request, isMongos);
auto statusWithPrivs =
authzSession->getPrivilegesForAggregate(viewOnNs, status.getValue(), isMongos);
PrivilegeVector privileges = uassertStatusOK(statusWithPrivs);
if (!authzSession->isAuthorizedForPrivileges(privileges)) {
return Status(ErrorCodes::Unauthorized, "unauthorized");
Expand Down
7 changes: 5 additions & 2 deletions src/mongo/db/commands/count_cmd.cpp
Expand Up @@ -168,12 +168,15 @@ class CmdCount : public BasicCommand {
viewAggCmd,
verbosity,
APIParameters::get(opCtx).getAPIStrict().value_or(false));
if (!viewAggRequest.isOK()) {
return viewAggRequest.getStatus();
}

// An empty PrivilegeVector is acceptable because these privileges are only checked on
// getMore and explain will not open a cursor.
return runAggregate(opCtx,
viewAggRequest.getNamespace(),
viewAggRequest,
viewAggRequest.getValue().getNamespace(),
viewAggRequest.getValue(),
viewAggregation.getValue(),
PrivilegeVector(),
result);
Expand Down
11 changes: 9 additions & 2 deletions src/mongo/db/commands/distinct.cpp
Expand Up @@ -166,11 +166,18 @@ class DistinctCommand : public BasicCommand {
viewAggCmd,
verbosity,
APIParameters::get(opCtx).getAPIStrict().value_or(false));
if (!viewAggRequest.isOK()) {
return viewAggRequest.getStatus();
}

// An empty PrivilegeVector is acceptable because these privileges are only checked on
// getMore and explain will not open a cursor.
return runAggregate(
opCtx, nss, viewAggRequest, viewAggregation.getValue(), PrivilegeVector(), result);
return runAggregate(opCtx,
nss,
viewAggRequest.getValue(),
viewAggregation.getValue(),
PrivilegeVector(),
result);
}

const auto& collection = ctx->getCollection();
Expand Down
4 changes: 2 additions & 2 deletions src/mongo/db/commands/find_cmd.cpp
Expand Up @@ -274,11 +274,11 @@ class FindCmd final : public Command {
auto viewAggCmd = OpMsgRequest::fromDBAndBody(_dbName, viewAggregationCommand).body;
// Create the agg request equivalent of the find operation, with the explain
// verbosity included.
auto aggRequest = aggregation_request_helper::parseFromBSON(
auto aggRequest = uassertStatusOK(aggregation_request_helper::parseFromBSON(
nss,
viewAggCmd,
verbosity,
APIParameters::get(opCtx).getAPIStrict().value_or(false));
APIParameters::get(opCtx).getAPIStrict().value_or(false)));

try {
// An empty PrivilegeVector is acceptable because these privileges are only
Expand Down
4 changes: 2 additions & 2 deletions src/mongo/db/commands/pipeline_command.cpp
Expand Up @@ -72,11 +72,11 @@ class PipelineCommand final : public Command {
OperationContext* opCtx,
const OpMsgRequest& opMsgRequest,
boost::optional<ExplainOptions::Verbosity> explainVerbosity) override {
const auto aggregationRequest = aggregation_request_helper::parseFromBSON(
const auto aggregationRequest = uassertStatusOK(aggregation_request_helper::parseFromBSON(
opMsgRequest.getDatabase().toString(),
opMsgRequest.body,
explainVerbosity,
APIParameters::get(opCtx).getAPIStrict().value_or(false));
APIParameters::get(opCtx).getAPIStrict().value_or(false)));

auto privileges =
uassertStatusOK(AuthorizationSession::get(opCtx->getClient())
Expand Down
4 changes: 2 additions & 2 deletions src/mongo/db/error_labels.cpp
Expand Up @@ -102,8 +102,8 @@ bool ErrorLabelBuilder::isResumableChangeStreamError() const {
// Do enough parsing to confirm that this is a well-formed pipeline with a $changeStream.
const auto swLitePipe = [&nss, &cmdObj, apiStrict]() -> StatusWith<LiteParsedPipeline> {
try {
auto aggRequest =
aggregation_request_helper::parseFromBSON(nss, cmdObj, boost::none, apiStrict);
auto aggRequest = uassertStatusOK(
aggregation_request_helper::parseFromBSON(nss, cmdObj, boost::none, apiStrict));
return LiteParsedPipeline(aggRequest);
} catch (const DBException& ex) {
return ex.toStatus();
Expand Down
105 changes: 56 additions & 49 deletions src/mongo/db/pipeline/aggregation_request_helper.cpp
Expand Up @@ -46,15 +46,11 @@
namespace mongo {
namespace aggregation_request_helper {

/**
* Validate the aggregate command object.
*/
void validate(const BSONObj& cmdObj, boost::optional<ExplainOptions::Verbosity> explainVerbosity);

AggregateCommand parseFromBSON(const std::string& dbName,
const BSONObj& cmdObj,
boost::optional<ExplainOptions::Verbosity> explainVerbosity,
bool apiStrict) {
StatusWith<AggregateCommand> parseFromBSON(
const std::string& dbName,
const BSONObj& cmdObj,
boost::optional<ExplainOptions::Verbosity> explainVerbosity,
bool apiStrict) {
return parseFromBSON(parseNs(dbName, cmdObj), cmdObj, explainVerbosity, apiStrict);
}

Expand All @@ -63,29 +59,22 @@ StatusWith<AggregateCommand> parseFromBSONForTests(
const BSONObj& cmdObj,
boost::optional<ExplainOptions::Verbosity> explainVerbosity,
bool apiStrict) {
try {
return parseFromBSON(nss, cmdObj, explainVerbosity, apiStrict);
} catch (const AssertionException&) {
return exceptionToStatus();
}
return parseFromBSON(nss, cmdObj, explainVerbosity, apiStrict);
}

StatusWith<AggregateCommand> parseFromBSONForTests(
const std::string& dbName,
const BSONObj& cmdObj,
boost::optional<ExplainOptions::Verbosity> explainVerbosity,
bool apiStrict) {
try {
return parseFromBSON(dbName, cmdObj, explainVerbosity, apiStrict);
} catch (const AssertionException&) {
return exceptionToStatus();
}
return parseFromBSON(dbName, cmdObj, explainVerbosity, apiStrict);
}

AggregateCommand parseFromBSON(NamespaceString nss,
const BSONObj& cmdObj,
boost::optional<ExplainOptions::Verbosity> explainVerbosity,
bool apiStrict) {
StatusWith<AggregateCommand> parseFromBSON(
NamespaceString nss,
const BSONObj& cmdObj,
boost::optional<ExplainOptions::Verbosity> explainVerbosity,
bool apiStrict) {

// if the command object lacks field 'aggregate' or '$db', we will use the namespace in 'nss'.
bool cmdObjChanged = false;
Expand All @@ -98,18 +87,28 @@ AggregateCommand parseFromBSON(NamespaceString nss,
}

AggregateCommand request(nss);
request = AggregateCommand::parse(IDLParserErrorContext("aggregate", apiStrict),
cmdObjChanged ? cmdObjBob.obj() : cmdObj);
try {
request = AggregateCommand::parse(IDLParserErrorContext("aggregate", apiStrict),
cmdObjChanged ? cmdObjBob.obj() : cmdObj);
} catch (const AssertionException&) {
return exceptionToStatus();
}

if (explainVerbosity) {
uassert(ErrorCodes::FailedToParse,
if (cmdObj.hasField(AggregateCommand::kExplainFieldName)) {
return {
ErrorCodes::FailedToParse,
str::stream() << "The '" << AggregateCommand::kExplainFieldName
<< "' option is illegal when a explain verbosity is also provided",
!cmdObj.hasField(AggregateCommand::kExplainFieldName));
<< "' option is illegal when a explain verbosity is also provided"};
}

request.setExplain(explainVerbosity);
}

validate(cmdObj, explainVerbosity);
auto status = validate(cmdObj, explainVerbosity);
if (!status.isOK()) {
return status;
}

return request;
}
Expand Down Expand Up @@ -148,7 +147,8 @@ Document serializeToCommandDoc(const AggregateCommand& request) {
return Document(request.toBSON(BSONObj()).getOwned());
}

void validate(const BSONObj& cmdObj, boost::optional<ExplainOptions::Verbosity> explainVerbosity) {
Status validate(const BSONObj& cmdObj,
boost::optional<ExplainOptions::Verbosity> explainVerbosity) {
bool hasAllowDiskUseElem = cmdObj.hasField(AggregateCommand::kAllowDiskUseFieldName);
bool hasCursorElem = cmdObj.hasField(AggregateCommand::kCursorFieldName);
bool hasExplainElem = cmdObj.hasField(AggregateCommand::kExplainFieldName);
Expand All @@ -159,25 +159,32 @@ void validate(const BSONObj& cmdObj, boost::optional<ExplainOptions::Verbosity>

// 'hasExplainElem' implies an aggregate command-level explain option, which does not require
// a cursor argument.
uassert(ErrorCodes::FailedToParse,
str::stream() << "The '" << AggregateCommand::kCursorFieldName
<< "' option is required, except for aggregate with the explain argument",
hasCursorElem || hasExplainElem);

uassert(ErrorCodes::FailedToParse,
str::stream() << "Aggregation explain does not support the'"
<< WriteConcernOptions::kWriteConcernField << "' option",
!hasExplain || !cmdObj[WriteConcernOptions::kWriteConcernField]);

uassert(ErrorCodes::FailedToParse,
str::stream() << "Cannot specify '" << AggregateCommand::kNeedsMergeFieldName
<< "' without '" << AggregateCommand::kFromMongosFieldName << "'",
(!hasNeedsMergeElem || hasFromMongosElem));

uassert(ErrorCodes::IllegalOperation,
str::stream() << "The '" << AggregateCommand::kAllowDiskUseFieldName
<< "' option is not permitted in read-only mode.",
(!hasAllowDiskUseElem || !storageGlobalParams.readOnly));
if (!hasCursorElem && !hasExplainElem) {
return {ErrorCodes::FailedToParse,
str::stream()
<< "The '" << AggregateCommand::kCursorFieldName
<< "' option is required, except for aggregate with the explain argument"};
}

if (hasExplain && cmdObj[WriteConcernOptions::kWriteConcernField]) {
return {ErrorCodes::FailedToParse,
str::stream() << "Aggregation explain does not support the'"
<< WriteConcernOptions::kWriteConcernField << "' option"};
}

if (hasNeedsMergeElem && !hasFromMongosElem) {
return {ErrorCodes::FailedToParse,
str::stream() << "Cannot specify '" << AggregateCommand::kNeedsMergeFieldName
<< "' without '" << AggregateCommand::kFromMongosFieldName << "'"};
}

if (hasAllowDiskUseElem && storageGlobalParams.readOnly) {
return {ErrorCodes::IllegalOperation,
str::stream() << "The '" << AggregateCommand::kAllowDiskUseFieldName
<< "' option is not permitted in read-only mode."};
}

return Status::OK();
}
} // namespace aggregation_request_helper

Expand Down
29 changes: 18 additions & 11 deletions src/mongo/db/pipeline/aggregation_request_helper.h
Expand Up @@ -58,18 +58,19 @@ static constexpr StringData kBatchSizeField = "batchSize"_sd;
static constexpr long long kDefaultBatchSize = 101;

/**
* Create a new instance of AggregateCommand by parsing the raw command object. Throws an exception
* if a required field was missing, if there was an unrecognized field name, or if there was a bad
* value for one of the fields.
* Create a new instance of AggregateCommand by parsing the raw command object. Returns a
* non-OK status if a required field was missing, if there was an unrecognized field name or if
* there was a bad value for one of the fields.
*
* If we are parsing a request for an explained aggregation with an explain verbosity provided,
* then 'explainVerbosity' contains this information. In this case, 'cmdObj' may not itself
* contain the explain specifier. Otherwise, 'explainVerbosity' should be boost::none.
*/
AggregateCommand parseFromBSON(NamespaceString nss,
const BSONObj& cmdObj,
boost::optional<ExplainOptions::Verbosity> explainVerbosity,
bool apiStrict);
StatusWith<AggregateCommand> parseFromBSON(
NamespaceString nss,
const BSONObj& cmdObj,
boost::optional<ExplainOptions::Verbosity> explainVerbosity,
bool apiStrict);

StatusWith<AggregateCommand> parseFromBSONForTests(
NamespaceString nss,
Expand All @@ -81,10 +82,11 @@ StatusWith<AggregateCommand> parseFromBSONForTests(
* Convenience overload which constructs the request's NamespaceString from the given database
* name and command object.
*/
AggregateCommand parseFromBSON(const std::string& dbName,
const BSONObj& cmdObj,
boost::optional<ExplainOptions::Verbosity> explainVerbosity,
bool apiStrict);
StatusWith<AggregateCommand> parseFromBSON(
const std::string& dbName,
const BSONObj& cmdObj,
boost::optional<ExplainOptions::Verbosity> explainVerbosity,
bool apiStrict);

StatusWith<AggregateCommand> parseFromBSONForTests(
const std::string& dbName,
Expand All @@ -111,6 +113,11 @@ NamespaceString parseNs(const std::string& dbname, const BSONObj& cmdObj);
Document serializeToCommandDoc(const AggregateCommand& request);

BSONObj serializeToCommandObj(const AggregateCommand& request);

/**
* Validate the aggregate command object.
*/
Status validate(const BSONObj& cmdObj, boost::optional<ExplainOptions::Verbosity> explainVerbosity);
} // namespace aggregation_request_helper

/**
Expand Down
4 changes: 2 additions & 2 deletions src/mongo/db/s/periodic_sharded_index_consistency_checker.cpp
Expand Up @@ -141,8 +141,8 @@ void PeriodicShardedIndexConsistencyChecker::_launchShardedIndexConsistencyCheck
continue;
}

auto request = aggregation_request_helper::parseFromBSON(
nss, aggRequestBSON, boost::none, false);
auto request = uassertStatusOK(aggregation_request_helper::parseFromBSON(
nss, aggRequestBSON, boost::none, false));

auto catalogCache = Grid::get(opCtx)->catalogCache();
shardVersionRetry(
Expand Down
9 changes: 6 additions & 3 deletions src/mongo/s/commands/cluster_count_cmd.cpp
Expand Up @@ -129,11 +129,11 @@ class ClusterCountCmd : public ErrmsgCommandDeprecated {
auto aggCmdOnView =
uassertStatusOK(countCommandAsAggregationCommand(countRequest, nss));
auto aggCmdOnViewObj = OpMsgRequest::fromDBAndBody(nss.db(), aggCmdOnView).body;
auto aggRequestOnView = aggregation_request_helper::parseFromBSON(
auto aggRequestOnView = uassertStatusOK(aggregation_request_helper::parseFromBSON(
nss,
aggCmdOnViewObj,
boost::none,
APIParameters::get(opCtx).getAPIStrict().value_or(false));
APIParameters::get(opCtx).getAPIStrict().value_or(false)));

auto resolvedAggRequest = ex->asExpandedViewAggregation(aggRequestOnView);
auto resolvedAggCmd =
Expand Down Expand Up @@ -248,12 +248,15 @@ class ClusterCountCmd : public ErrmsgCommandDeprecated {
aggCmdOnViewObj,
verbosity,
APIParameters::get(opCtx).getAPIStrict().value_or(false));
if (!aggRequestOnView.isOK()) {
return aggRequestOnView.getStatus();
}

auto bodyBuilder = result->getBodyBuilder();
// An empty PrivilegeVector is acceptable because these privileges are only checked on
// getMore and explain will not open a cursor.
return ClusterAggregate::retryOnViewError(opCtx,
aggRequestOnView,
aggRequestOnView.getValue(),
*ex.extraInfo<ResolvedView>(),
nss,
PrivilegeVector(),
Expand Down
9 changes: 6 additions & 3 deletions src/mongo/s/commands/cluster_distinct_cmd.cpp
Expand Up @@ -138,13 +138,15 @@ class DistinctCmd : public BasicCommand {
viewAggCmd,
verbosity,
APIParameters::get(opCtx).getAPIStrict().value_or(false));

if (!aggRequestOnView.isOK()) {
return aggRequestOnView.getStatus();
}

auto bodyBuilder = result->getBodyBuilder();
// An empty PrivilegeVector is acceptable because these privileges are only checked on
// getMore and explain will not open a cursor.
return ClusterAggregate::retryOnViewError(opCtx,
aggRequestOnView,
aggRequestOnView.getValue(),
*ex.extraInfo<ResolvedView>(),
nss,
PrivilegeVector(),
Expand Down Expand Up @@ -214,8 +216,9 @@ class DistinctCmd : public BasicCommand {
viewAggCmd,
boost::none,
APIParameters::get(opCtx).getAPIStrict().value_or(false));
uassertStatusOK(aggRequestOnView.getStatus());

auto resolvedAggRequest = ex->asExpandedViewAggregation(aggRequestOnView);
auto resolvedAggRequest = ex->asExpandedViewAggregation(aggRequestOnView.getValue());
auto resolvedAggCmd =
aggregation_request_helper::serializeToCommandObj(resolvedAggRequest);

Expand Down

0 comments on commit d77297f

Please sign in to comment.