summaryrefslogtreecommitdiff
path: root/src/mongo/db
diff options
context:
space:
mode:
authorIsabella Siu <isabella.siu@10gen.com>2019-01-11 11:16:24 -0500
committerIsabella Siu <isabella.siu@10gen.com>2019-02-08 14:34:32 -0500
commit8e5e745e98d33633e7d24a2629f22cdba79d9851 (patch)
tree9e02d92dadcf67140fe8707d792e55cf12b5443e /src/mongo/db
parent7a7baa2539ec169335086e45c7d0b85ba7cdb877 (diff)
downloadmongo-8e5e745e98d33633e7d24a2629f22cdba79d9851.tar.gz
SERVER-37836 re-evaluate authorization for originating command in getMore
Diffstat (limited to 'src/mongo/db')
-rw-r--r--src/mongo/db/auth/authorization_session.h21
-rw-r--r--src/mongo/db/auth/authorization_session_impl.cpp87
-rw-r--r--src/mongo/db/auth/authorization_session_impl.h11
-rw-r--r--src/mongo/db/auth/authorization_session_test.cpp205
-rw-r--r--src/mongo/db/clientcursor.cpp1
-rw-r--r--src/mongo/db/clientcursor.h23
-rw-r--r--src/mongo/db/commands/count_cmd.cpp3
-rw-r--r--src/mongo/db/commands/current_op.cpp13
-rw-r--r--src/mongo/db/commands/distinct.cpp10
-rw-r--r--src/mongo/db/commands/find_cmd.cpp9
-rw-r--r--src/mongo/db/commands/getmore_cmd.cpp11
-rw-r--r--src/mongo/db/commands/list_collections.cpp6
-rw-r--r--src/mongo/db/commands/list_indexes.cpp3
-rw-r--r--src/mongo/db/commands/pipeline_command.cpp26
-rw-r--r--src/mongo/db/commands/repair_cursor.cpp3
-rw-r--r--src/mongo/db/commands/run_aggregate.cpp7
-rw-r--r--src/mongo/db/commands/run_aggregate.h5
-rw-r--r--src/mongo/db/commands/user_management_commands.cpp2
-rw-r--r--src/mongo/db/query/find.cpp3
19 files changed, 281 insertions, 168 deletions
diff --git a/src/mongo/db/auth/authorization_session.h b/src/mongo/db/auth/authorization_session.h
index e4ce0845695..25e40608eb7 100644
--- a/src/mongo/db/auth/authorization_session.h
+++ b/src/mongo/db/auth/authorization_session.h
@@ -219,11 +219,12 @@ public:
virtual Status checkAuthForKillCursors(const NamespaceString& cursorNss,
UserNameIterator cursorOwner) = 0;
- // Checks if this connection has the privileges necessary to run the aggregation pipeline
- // specified in 'cmdObj' on the namespace 'ns' either directly on mongoD or via mongoS.
- virtual Status checkAuthForAggregate(const NamespaceString& ns,
- const BSONObj& cmdObj,
- bool isMongos) = 0;
+ // Attempts to get the privileges necessary to run the aggregation pipeline specified in
+ // 'cmdObj' on the namespace 'ns' either directly on mongoD or via mongoS. Returns a non-ok
+ // status if it is unable to parse the pipeline.
+ virtual StatusWith<PrivilegeVector> getPrivilegesForAggregate(const NamespaceString& ns,
+ const BSONObj& cmdObj,
+ bool isMongos) = 0;
// Checks if this connection has the privileges necessary to create 'ns' with the options
// supplied in 'cmdObj' either directly on mongoD or via mongoS.
@@ -245,6 +246,12 @@ public:
// from a role.
virtual Status checkAuthorizedToRevokePrivilege(const Privilege& privilege) = 0;
+ // Checks if the current session is authorized to list the collections in the given
+ // database. If it is, return a privilegeVector containing the privileges used to authorize
+ // this command.
+ virtual StatusWith<PrivilegeVector> checkAuthorizedToListCollections(StringData dbname,
+ const BSONObj& cmdObj) = 0;
+
// Checks if this connection is using the localhost bypass
virtual bool isUsingLocalhostBypass() = 0;
@@ -271,10 +278,6 @@ public:
// is allowed to change his/her own password
virtual bool isAuthorizedToChangeOwnPasswordAsUser(const UserName& userName) = 0;
- // Returns true if the current session is authorized to list the collections in the given
- // database.
- virtual bool isAuthorizedToListCollections(StringData dbname, const BSONObj& cmdObj) = 0;
-
// Returns true if the current session is authenticated as the given user and that user
// is allowed to change his/her own customData.
virtual bool isAuthorizedToChangeOwnCustomDataAsUser(const UserName& userName) = 0;
diff --git a/src/mongo/db/auth/authorization_session_impl.cpp b/src/mongo/db/auth/authorization_session_impl.cpp
index c54b632d943..72f879f7344 100644
--- a/src/mongo/db/auth/authorization_session_impl.cpp
+++ b/src/mongo/db/auth/authorization_session_impl.cpp
@@ -87,10 +87,15 @@ Status checkAuthForCreateOrModifyView(AuthorizationSession* authzSession,
// This check performs some validation but it is not exhaustive and may allow for an invalid
// pipeline specification. In this case the authorization check will succeed but the pipeline
// will fail to parse later in Command::run().
- return authzSession->checkAuthForAggregate(
+ auto statusWithPrivs = authzSession->getPrivilegesForAggregate(
viewOnNs,
BSON("aggregate" << viewOnNs.coll() << "pipeline" << viewPipeline << "cursor" << BSONObj()),
isMongos);
+ PrivilegeVector privileges = uassertStatusOK(statusWithPrivs);
+ if (!authzSession->isAuthorizedForPrivileges(privileges)) {
+ return Status(ErrorCodes::Unauthorized, "unauthorized");
+ }
+ return Status::OK();
}
} // namespace
@@ -236,23 +241,19 @@ PrivilegeVector AuthorizationSessionImpl::getDefaultPrivileges() {
return defaultPrivileges;
}
-Status AuthorizationSessionImpl::checkAuthForAggregate(const NamespaceString& nss,
- const BSONObj& cmdObj,
- bool isMongos) {
+StatusWith<PrivilegeVector> AuthorizationSessionImpl::getPrivilegesForAggregate(
+ const NamespaceString& nss, const BSONObj& cmdObj, bool isMongos) {
if (!nss.isValid()) {
return Status(ErrorCodes::InvalidNamespace,
mongoutils::str::stream() << "Invalid input namespace, " << nss.ns());
}
+ PrivilegeVector privileges;
+
// If this connection does not need to be authenticated (for instance, if auth is disabled),
- // return Status::OK() immediately.
+ // returns an empty requirements set.
if (_externalState->shouldIgnoreAuthChecks()) {
- return Status::OK();
- }
-
- // We require at least one authenticated user when running aggregate with auth enabled.
- if (!isAuthenticated()) {
- return Status(ErrorCodes::Unauthorized, "unauthorized");
+ return privileges;
}
auto statusWithAggRequest = AggregationRequest::parseFromBSON(nss, cmdObj);
@@ -265,44 +266,28 @@ Status AuthorizationSessionImpl::checkAuthForAggregate(const NamespaceString& ns
// If the aggregation pipeline is empty, confirm the user is authorized for find on 'nss'.
if (pipeline.empty()) {
- if (!isAuthorizedForPrivilege(
- Privilege(ResourcePattern::forExactNamespace(nss), ActionType::find))) {
- return Status(ErrorCodes::Unauthorized, "unauthorized");
- }
-
- return Status::OK();
+ Privilege currentPriv =
+ Privilege(ResourcePattern::forExactNamespace(nss), ActionType::find);
+ Privilege::addPrivilegeToPrivilegeVector(&privileges, currentPriv);
+ return privileges;
}
- // Confirm the user is authorized for the pipeline's initial document source. We confirm a user
- // is authorized incrementally rather than once for the entire pipeline. This will prevent a
- // malicious user, who doesn't have access to the initial document source, from consuming the
- // resources needed to parse a potentially large pipeline.
- auto liteParsedFirstDocumentSource = LiteParsedDocumentSource::parse(aggRequest, pipeline[0]);
- if (!liteParsedFirstDocumentSource->isInitialSource() &&
- !isAuthorizedForPrivilege(
- Privilege(ResourcePattern::forExactNamespace(nss), ActionType::find))) {
- return Status(ErrorCodes::Unauthorized, "unauthorized");
- }
-
- // We have done the work to lite parse the first stage. Given that, we check required privileges
- // for it using 'liteParsedFirstDocumentSource' regardless of whether is an initial source or
- // not.
- if (!isAuthorizedForPrivileges(liteParsedFirstDocumentSource->requiredPrivileges(isMongos))) {
- return Status(ErrorCodes::Unauthorized, "unauthorized");
+ // If the first stage of the pipeline is not an initial source, the pipeline is implicitly
+ // reading documents from the underlying collection. The client must be authorized to do so.
+ auto liteParsedDocSource = LiteParsedDocumentSource::parse(aggRequest, pipeline[0]);
+ if (!liteParsedDocSource->isInitialSource()) {
+ Privilege currentPriv =
+ Privilege(ResourcePattern::forExactNamespace(nss), ActionType::find);
+ Privilege::addPrivilegeToPrivilegeVector(&privileges, currentPriv);
}
- // Confirm privileges for the remainder of the pipepline. Start with the second stage as we have
- // already authorized the first.
- auto pipelineIter = pipeline.begin() + 1;
-
- for (; pipelineIter != pipeline.end(); ++pipelineIter) {
- auto liteParsedDocSource = LiteParsedDocumentSource::parse(aggRequest, *pipelineIter);
- if (!isAuthorizedForPrivileges(liteParsedDocSource->requiredPrivileges(isMongos))) {
- return Status(ErrorCodes::Unauthorized, "unauthorized");
- }
+ // Confirm privileges for the pipeline.
+ for (auto&& pipelineStage : pipeline) {
+ liteParsedDocSource = LiteParsedDocumentSource::parse(aggRequest, pipelineStage);
+ PrivilegeVector currentPrivs = liteParsedDocSource->requiredPrivileges(isMongos);
+ Privilege::addPrivilegesToPrivilegeVector(&privileges, currentPrivs);
}
-
- return Status::OK();
+ return privileges;
}
Status AuthorizationSessionImpl::checkAuthForFind(const NamespaceString& ns, bool hasTerm) {
@@ -699,16 +684,20 @@ bool AuthorizationSessionImpl::isAuthorizedToChangeOwnCustomDataAsUser(const Use
ActionType::changeOwnCustomData);
}
-bool AuthorizationSessionImpl::isAuthorizedToListCollections(StringData dbname,
- const BSONObj& cmdObj) {
+StatusWith<PrivilegeVector> AuthorizationSessionImpl::checkAuthorizedToListCollections(
+ StringData dbname, const BSONObj& cmdObj) {
if (cmdObj["authorizedCollections"].trueValue() && cmdObj["nameOnly"].trueValue() &&
AuthorizationSessionImpl::isAuthorizedForAnyActionOnAnyResourceInDB(dbname)) {
- return true;
+ return PrivilegeVector();
}
// Check for the listCollections ActionType on the database.
- return AuthorizationSessionImpl::isAuthorizedForActionsOnResource(
- ResourcePattern::forDatabaseName(dbname), ActionType::listCollections);
+ PrivilegeVector privileges = {
+ Privilege(ResourcePattern::forDatabaseName(dbname), ActionType::listCollections)};
+ if (AuthorizationSessionImpl::isAuthorizedForPrivileges(privileges)) {
+ return privileges;
+ }
+ return Status(ErrorCodes::Unauthorized, "unauthorized");
}
bool AuthorizationSessionImpl::isAuthenticatedAsUserWithRole(const RoleName& roleName) {
diff --git a/src/mongo/db/auth/authorization_session_impl.h b/src/mongo/db/auth/authorization_session_impl.h
index f8ca7c286db..0b432c3fe7d 100644
--- a/src/mongo/db/auth/authorization_session_impl.h
+++ b/src/mongo/db/auth/authorization_session_impl.h
@@ -122,9 +122,9 @@ public:
Status checkAuthForKillCursors(const NamespaceString& cursorNss,
UserNameIterator cursorOwner) override;
- Status checkAuthForAggregate(const NamespaceString& ns,
- const BSONObj& cmdObj,
- bool isMongos) override;
+ StatusWith<PrivilegeVector> getPrivilegesForAggregate(const NamespaceString& ns,
+ const BSONObj& cmdObj,
+ bool isMongos) override;
Status checkAuthForCreate(const NamespaceString& ns,
const BSONObj& cmdObj,
@@ -134,6 +134,9 @@ public:
const BSONObj& cmdObj,
bool isMongos) override;
+ StatusWith<PrivilegeVector> checkAuthorizedToListCollections(StringData dbname,
+ const BSONObj& cmdObj) override;
+
Status checkAuthorizedToGrantPrivilege(const Privilege& privilege) override;
Status checkAuthorizedToRevokePrivilege(const Privilege& privilege) override;
@@ -152,8 +155,6 @@ public:
bool isAuthorizedToChangeOwnPasswordAsUser(const UserName& userName) override;
- bool isAuthorizedToListCollections(StringData dbname, const BSONObj& cmdObj) override;
-
bool isAuthorizedToChangeOwnCustomDataAsUser(const UserName& userName) override;
bool isAuthenticatedAsUserWithRole(const RoleName& roleName) override;
diff --git a/src/mongo/db/auth/authorization_session_test.cpp b/src/mongo/db/auth/authorization_session_test.cpp
index e842e27a06c..b492704232a 100644
--- a/src/mongo/db/auth/authorization_session_test.cpp
+++ b/src/mongo/db/auth/authorization_session_test.cpp
@@ -640,16 +640,19 @@ TEST_F(AuthorizationSessionTest, CheckAuthForAggregateFailsIfPipelineIsNotAnArra
authzSession->assumePrivilegesForDB(Privilege(testFooCollResource, {ActionType::find}));
BSONObj cmdObjIntPipeline = BSON("aggregate" << testFooNss.coll() << "pipeline" << 7);
- ASSERT_EQ(ErrorCodes::TypeMismatch,
- authzSession->checkAuthForAggregate(testFooNss, cmdObjIntPipeline, false));
+ ASSERT_EQ(
+ ErrorCodes::TypeMismatch,
+ authzSession->getPrivilegesForAggregate(testFooNss, cmdObjIntPipeline, false).getStatus());
BSONObj cmdObjObjPipeline = BSON("aggregate" << testFooNss.coll() << "pipeline" << BSONObj());
- ASSERT_EQ(ErrorCodes::TypeMismatch,
- authzSession->checkAuthForAggregate(testFooNss, cmdObjObjPipeline, false));
+ ASSERT_EQ(
+ ErrorCodes::TypeMismatch,
+ authzSession->getPrivilegesForAggregate(testFooNss, cmdObjObjPipeline, false).getStatus());
BSONObj cmdObjNoPipeline = BSON("aggregate" << testFooNss.coll());
- ASSERT_EQ(ErrorCodes::TypeMismatch,
- authzSession->checkAuthForAggregate(testFooNss, cmdObjNoPipeline, false));
+ ASSERT_EQ(
+ ErrorCodes::TypeMismatch,
+ authzSession->getPrivilegesForAggregate(testFooNss, cmdObjNoPipeline, false).getStatus());
}
TEST_F(AuthorizationSessionTest, CheckAuthForAggregateFailsIfPipelineFirstStageIsNotAnObject) {
@@ -658,19 +661,22 @@ TEST_F(AuthorizationSessionTest, CheckAuthForAggregateFailsIfPipelineFirstStageI
BSONObj cmdObjFirstStageInt =
BSON("aggregate" << testFooNss.coll() << "pipeline" << BSON_ARRAY(7));
ASSERT_EQ(ErrorCodes::TypeMismatch,
- authzSession->checkAuthForAggregate(testFooNss, cmdObjFirstStageInt, false));
+ authzSession->getPrivilegesForAggregate(testFooNss, cmdObjFirstStageInt, false)
+ .getStatus());
BSONObj cmdObjFirstStageArray =
BSON("aggregate" << testFooNss.coll() << "pipeline" << BSON_ARRAY(BSONArray()));
ASSERT_EQ(ErrorCodes::TypeMismatch,
- authzSession->checkAuthForAggregate(testFooNss, cmdObjFirstStageArray, false));
+ authzSession->getPrivilegesForAggregate(testFooNss, cmdObjFirstStageArray, false)
+ .getStatus());
}
TEST_F(AuthorizationSessionTest, CannotAggregateEmptyPipelineWithoutFindAction) {
BSONObj cmdObj = BSON("aggregate" << testFooNss.coll() << "pipeline" << BSONArray() << "cursor"
<< BSONObj());
- ASSERT_EQ(ErrorCodes::Unauthorized,
- authzSession->checkAuthForAggregate(testFooNss, cmdObj, false));
+ PrivilegeVector privileges =
+ uassertStatusOK(authzSession->getPrivilegesForAggregate(testFooNss, cmdObj, false));
+ ASSERT_FALSE(authzSession->isAuthorizedForPrivileges(privileges));
}
TEST_F(AuthorizationSessionTest, CanAggregateEmptyPipelineWithFindAction) {
@@ -678,7 +684,9 @@ TEST_F(AuthorizationSessionTest, CanAggregateEmptyPipelineWithFindAction) {
BSONObj cmdObj = BSON("aggregate" << testFooNss.coll() << "pipeline" << BSONArray() << "cursor"
<< BSONObj());
- ASSERT_OK(authzSession->checkAuthForAggregate(testFooNss, cmdObj, false));
+ PrivilegeVector privileges =
+ uassertStatusOK(authzSession->getPrivilegesForAggregate(testFooNss, cmdObj, false));
+ ASSERT_TRUE(authzSession->isAuthorizedForPrivileges(privileges));
}
TEST_F(AuthorizationSessionTest, CannotAggregateWithoutFindActionIfFirstStageNotIndexOrCollStats) {
@@ -689,8 +697,9 @@ TEST_F(AuthorizationSessionTest, CannotAggregateWithoutFindActionIfFirstStageNot
<< BSON("$indexStats" << BSONObj()));
BSONObj cmdObj =
BSON("aggregate" << testFooNss.coll() << "pipeline" << pipeline << "cursor" << BSONObj());
- ASSERT_EQ(ErrorCodes::Unauthorized,
- authzSession->checkAuthForAggregate(testFooNss, cmdObj, false));
+ PrivilegeVector privileges =
+ uassertStatusOK(authzSession->getPrivilegesForAggregate(testFooNss, cmdObj, false));
+ ASSERT_FALSE(authzSession->isAuthorizedForPrivileges(privileges));
}
TEST_F(AuthorizationSessionTest, CannotAggregateWithFindActionIfPipelineContainsIndexOrCollStats) {
@@ -699,8 +708,9 @@ TEST_F(AuthorizationSessionTest, CannotAggregateWithFindActionIfPipelineContains
<< BSON("$indexStats" << BSONObj()));
BSONObj cmdObj =
BSON("aggregate" << testFooNss.coll() << "pipeline" << pipeline << "cursor" << BSONObj());
- ASSERT_EQ(ErrorCodes::Unauthorized,
- authzSession->checkAuthForAggregate(testFooNss, cmdObj, false));
+ PrivilegeVector privileges =
+ uassertStatusOK(authzSession->getPrivilegesForAggregate(testFooNss, cmdObj, false));
+ ASSERT_FALSE(authzSession->isAuthorizedForPrivileges(privileges));
}
TEST_F(AuthorizationSessionTest, CannotAggregateCollStatsWithoutCollStatsAction) {
@@ -709,8 +719,9 @@ TEST_F(AuthorizationSessionTest, CannotAggregateCollStatsWithoutCollStatsAction)
BSONArray pipeline = BSON_ARRAY(BSON("$collStats" << BSONObj()));
BSONObj cmdObj =
BSON("aggregate" << testFooNss.coll() << "pipeline" << pipeline << "cursor" << BSONObj());
- ASSERT_EQ(ErrorCodes::Unauthorized,
- authzSession->checkAuthForAggregate(testFooNss, cmdObj, false));
+ PrivilegeVector privileges =
+ uassertStatusOK(authzSession->getPrivilegesForAggregate(testFooNss, cmdObj, false));
+ ASSERT_FALSE(authzSession->isAuthorizedForPrivileges(privileges));
}
TEST_F(AuthorizationSessionTest, CanAggregateCollStatsWithCollStatsAction) {
@@ -719,7 +730,9 @@ TEST_F(AuthorizationSessionTest, CanAggregateCollStatsWithCollStatsAction) {
BSONArray pipeline = BSON_ARRAY(BSON("$collStats" << BSONObj()));
BSONObj cmdObj =
BSON("aggregate" << testFooNss.coll() << "pipeline" << pipeline << "cursor" << BSONObj());
- ASSERT_OK(authzSession->checkAuthForAggregate(testFooNss, cmdObj, false));
+ PrivilegeVector privileges =
+ uassertStatusOK(authzSession->getPrivilegesForAggregate(testFooNss, cmdObj, false));
+ ASSERT_TRUE(authzSession->isAuthorizedForPrivileges(privileges));
}
TEST_F(AuthorizationSessionTest, CannotAggregateIndexStatsWithoutIndexStatsAction) {
@@ -728,8 +741,9 @@ TEST_F(AuthorizationSessionTest, CannotAggregateIndexStatsWithoutIndexStatsActio
BSONArray pipeline = BSON_ARRAY(BSON("$indexStats" << BSONObj()));
BSONObj cmdObj =
BSON("aggregate" << testFooNss.coll() << "pipeline" << pipeline << "cursor" << BSONObj());
- ASSERT_EQ(ErrorCodes::Unauthorized,
- authzSession->checkAuthForAggregate(testFooNss, cmdObj, false));
+ PrivilegeVector privileges =
+ uassertStatusOK(authzSession->getPrivilegesForAggregate(testFooNss, cmdObj, false));
+ ASSERT_FALSE(authzSession->isAuthorizedForPrivileges(privileges));
}
TEST_F(AuthorizationSessionTest, CanAggregateIndexStatsWithIndexStatsAction) {
@@ -738,7 +752,9 @@ TEST_F(AuthorizationSessionTest, CanAggregateIndexStatsWithIndexStatsAction) {
BSONArray pipeline = BSON_ARRAY(BSON("$indexStats" << BSONObj()));
BSONObj cmdObj =
BSON("aggregate" << testFooNss.coll() << "pipeline" << pipeline << "cursor" << BSONObj());
- ASSERT_OK(authzSession->checkAuthForAggregate(testFooNss, cmdObj, false));
+ PrivilegeVector privileges =
+ uassertStatusOK(authzSession->getPrivilegesForAggregate(testFooNss, cmdObj, false));
+ ASSERT_TRUE(authzSession->isAuthorizedForPrivileges(privileges));
}
TEST_F(AuthorizationSessionTest, CanAggregateCurrentOpAllUsersFalseWithoutInprogActionOnMongoD) {
@@ -747,7 +763,9 @@ TEST_F(AuthorizationSessionTest, CanAggregateCurrentOpAllUsersFalseWithoutInprog
BSONArray pipeline = BSON_ARRAY(BSON("$currentOp" << BSON("allUsers" << false)));
BSONObj cmdObj =
BSON("aggregate" << testFooNss.coll() << "pipeline" << pipeline << "cursor" << BSONObj());
- ASSERT_OK(authzSession->checkAuthForAggregate(testFooNss, cmdObj, false));
+ PrivilegeVector privileges =
+ uassertStatusOK(authzSession->getPrivilegesForAggregate(testFooNss, cmdObj, false));
+ ASSERT_TRUE(authzSession->isAuthorizedForPrivileges(privileges));
}
TEST_F(AuthorizationSessionTest, CannotAggregateCurrentOpAllUsersFalseWithoutInprogActionOnMongoS) {
@@ -756,17 +774,16 @@ TEST_F(AuthorizationSessionTest, CannotAggregateCurrentOpAllUsersFalseWithoutInp
BSONArray pipeline = BSON_ARRAY(BSON("$currentOp" << BSON("allUsers" << false)));
BSONObj cmdObj =
BSON("aggregate" << testFooNss.coll() << "pipeline" << pipeline << "cursor" << BSONObj());
- ASSERT_EQ(ErrorCodes::Unauthorized,
- authzSession->checkAuthForAggregate(testFooNss, cmdObj, true));
+ PrivilegeVector privileges =
+ uassertStatusOK(authzSession->getPrivilegesForAggregate(testFooNss, cmdObj, true));
+ ASSERT_FALSE(authzSession->isAuthorizedForPrivileges(privileges));
}
TEST_F(AuthorizationSessionTest, CannotAggregateCurrentOpAllUsersFalseIfNotAuthenticatedOnMongoD) {
BSONArray pipeline = BSON_ARRAY(BSON("$currentOp" << BSON("allUsers" << false)));
BSONObj cmdObj =
BSON("aggregate" << testFooNss.coll() << "pipeline" << pipeline << "cursor" << BSONObj());
-
- ASSERT_EQ(ErrorCodes::Unauthorized,
- authzSession->checkAuthForAggregate(testFooNss, cmdObj, false));
+ ASSERT_FALSE(authzSession->isAuthenticated());
}
TEST_F(AuthorizationSessionTest, CannotAggregateCurrentOpAllUsersFalseIfNotAuthenticatedOnMongoS) {
@@ -774,8 +791,9 @@ TEST_F(AuthorizationSessionTest, CannotAggregateCurrentOpAllUsersFalseIfNotAuthe
BSONObj cmdObj =
BSON("aggregate" << testFooNss.coll() << "pipeline" << pipeline << "cursor" << BSONObj());
- ASSERT_EQ(ErrorCodes::Unauthorized,
- authzSession->checkAuthForAggregate(testFooNss, cmdObj, true));
+ PrivilegeVector privileges =
+ uassertStatusOK(authzSession->getPrivilegesForAggregate(testFooNss, cmdObj, true));
+ ASSERT_FALSE(authzSession->isAuthorizedForPrivileges(privileges));
}
TEST_F(AuthorizationSessionTest, CannotAggregateCurrentOpAllUsersTrueWithoutInprogActionOnMongoD) {
@@ -784,8 +802,9 @@ TEST_F(AuthorizationSessionTest, CannotAggregateCurrentOpAllUsersTrueWithoutInpr
BSONArray pipeline = BSON_ARRAY(BSON("$currentOp" << BSON("allUsers" << true)));
BSONObj cmdObj =
BSON("aggregate" << testFooNss.coll() << "pipeline" << pipeline << "cursor" << BSONObj());
- ASSERT_EQ(ErrorCodes::Unauthorized,
- authzSession->checkAuthForAggregate(testFooNss, cmdObj, false));
+ PrivilegeVector privileges =
+ uassertStatusOK(authzSession->getPrivilegesForAggregate(testFooNss, cmdObj, false));
+ ASSERT_FALSE(authzSession->isAuthorizedForPrivileges(privileges));
}
TEST_F(AuthorizationSessionTest, CannotAggregateCurrentOpAllUsersTrueWithoutInprogActionOnMongoS) {
@@ -794,8 +813,9 @@ TEST_F(AuthorizationSessionTest, CannotAggregateCurrentOpAllUsersTrueWithoutInpr
BSONArray pipeline = BSON_ARRAY(BSON("$currentOp" << BSON("allUsers" << true)));
BSONObj cmdObj =
BSON("aggregate" << testFooNss.coll() << "pipeline" << pipeline << "cursor" << BSONObj());
- ASSERT_EQ(ErrorCodes::Unauthorized,
- authzSession->checkAuthForAggregate(testFooNss, cmdObj, true));
+ PrivilegeVector privileges =
+ uassertStatusOK(authzSession->getPrivilegesForAggregate(testFooNss, cmdObj, true));
+ ASSERT_FALSE(authzSession->isAuthorizedForPrivileges(privileges));
}
TEST_F(AuthorizationSessionTest, CanAggregateCurrentOpAllUsersTrueWithInprogActionOnMongoD) {
@@ -805,7 +825,9 @@ TEST_F(AuthorizationSessionTest, CanAggregateCurrentOpAllUsersTrueWithInprogActi
BSONArray pipeline = BSON_ARRAY(BSON("$currentOp" << BSON("allUsers" << true)));
BSONObj cmdObj =
BSON("aggregate" << testFooNss.coll() << "pipeline" << pipeline << "cursor" << BSONObj());
- ASSERT_OK(authzSession->checkAuthForAggregate(testFooNss, cmdObj, false));
+ PrivilegeVector privileges =
+ uassertStatusOK(authzSession->getPrivilegesForAggregate(testFooNss, cmdObj, false));
+ ASSERT_TRUE(authzSession->isAuthorizedForPrivileges(privileges));
}
TEST_F(AuthorizationSessionTest, CanAggregateCurrentOpAllUsersTrueWithInprogActionOnMongoS) {
@@ -815,7 +837,9 @@ TEST_F(AuthorizationSessionTest, CanAggregateCurrentOpAllUsersTrueWithInprogActi
BSONArray pipeline = BSON_ARRAY(BSON("$currentOp" << BSON("allUsers" << true)));
BSONObj cmdObj =
BSON("aggregate" << testFooNss.coll() << "pipeline" << pipeline << "cursor" << BSONObj());
- ASSERT_OK(authzSession->checkAuthForAggregate(testFooNss, cmdObj, true));
+ PrivilegeVector privileges =
+ uassertStatusOK(authzSession->getPrivilegesForAggregate(testFooNss, cmdObj, true));
+ ASSERT_TRUE(authzSession->isAuthorizedForPrivileges(privileges));
}
TEST_F(AuthorizationSessionTest, CannotSpoofAllUsersTrueWithoutInprogActionOnMongoD) {
@@ -825,8 +849,9 @@ TEST_F(AuthorizationSessionTest, CannotSpoofAllUsersTrueWithoutInprogActionOnMon
BSON_ARRAY(BSON("$currentOp" << BSON("allUsers" << false << "allUsers" << true)));
BSONObj cmdObj =
BSON("aggregate" << testFooNss.coll() << "pipeline" << pipeline << "cursor" << BSONObj());
- ASSERT_EQ(ErrorCodes::Unauthorized,
- authzSession->checkAuthForAggregate(testFooNss, cmdObj, false));
+ PrivilegeVector privileges =
+ uassertStatusOK(authzSession->getPrivilegesForAggregate(testFooNss, cmdObj, false));
+ ASSERT_FALSE(authzSession->isAuthorizedForPrivileges(privileges));
}
TEST_F(AuthorizationSessionTest, CannotSpoofAllUsersTrueWithoutInprogActionOnMongoS) {
@@ -836,8 +861,9 @@ TEST_F(AuthorizationSessionTest, CannotSpoofAllUsersTrueWithoutInprogActionOnMon
BSON_ARRAY(BSON("$currentOp" << BSON("allUsers" << false << "allUsers" << true)));
BSONObj cmdObj =
BSON("aggregate" << testFooNss.coll() << "pipeline" << pipeline << "cursor" << BSONObj());
- ASSERT_EQ(ErrorCodes::Unauthorized,
- authzSession->checkAuthForAggregate(testFooNss, cmdObj, true));
+ PrivilegeVector privileges =
+ uassertStatusOK(authzSession->getPrivilegesForAggregate(testFooNss, cmdObj, true));
+ ASSERT_FALSE(authzSession->isAuthorizedForPrivileges(privileges));
}
TEST_F(AuthorizationSessionTest, AddPrivilegesForStageFailsIfOutNamespaceIsNotValid) {
@@ -847,7 +873,7 @@ TEST_F(AuthorizationSessionTest, AddPrivilegesForStageFailsIfOutNamespaceIsNotVa
<< ""));
BSONObj cmdObj =
BSON("aggregate" << testFooNss.coll() << "pipeline" << pipeline << "cursor" << BSONObj());
- ASSERT_THROWS_CODE(authzSession->checkAuthForAggregate(testFooNss, cmdObj, false),
+ ASSERT_THROWS_CODE(authzSession->getPrivilegesForAggregate(testFooNss, cmdObj, false),
AssertionException,
ErrorCodes::InvalidNamespace);
}
@@ -859,20 +885,19 @@ TEST_F(AuthorizationSessionTest, CannotAggregateOutWithoutInsertAndRemoveOnTarge
BSONArray pipeline = BSON_ARRAY(BSON("$out" << testBarNss.coll()));
BSONObj cmdObj =
BSON("aggregate" << testFooNss.coll() << "pipeline" << pipeline << "cursor" << BSONObj());
- ASSERT_EQ(ErrorCodes::Unauthorized,
- authzSession->checkAuthForAggregate(testFooNss, cmdObj, false));
+ PrivilegeVector privileges =
+ uassertStatusOK(authzSession->getPrivilegesForAggregate(testFooNss, cmdObj, false));
+ ASSERT_FALSE(authzSession->isAuthorizedForPrivileges(privileges));
// We have insert but not remove on the $out namespace.
authzSession->assumePrivilegesForDB({Privilege(testFooCollResource, {ActionType::find}),
Privilege(testBarCollResource, {ActionType::insert})});
- ASSERT_EQ(ErrorCodes::Unauthorized,
- authzSession->checkAuthForAggregate(testFooNss, cmdObj, false));
+ ASSERT_FALSE(authzSession->isAuthorizedForPrivileges(privileges));
// We have remove but not insert on the $out namespace.
authzSession->assumePrivilegesForDB({Privilege(testFooCollResource, {ActionType::find}),
Privilege(testBarCollResource, {ActionType::remove})});
- ASSERT_EQ(ErrorCodes::Unauthorized,
- authzSession->checkAuthForAggregate(testFooNss, cmdObj, false));
+ ASSERT_FALSE(authzSession->isAuthorizedForPrivileges(privileges));
}
TEST_F(AuthorizationSessionTest, CanAggregateOutWithInsertAndRemoveOnTargetNamespace) {
@@ -883,15 +908,18 @@ TEST_F(AuthorizationSessionTest, CanAggregateOutWithInsertAndRemoveOnTargetNames
BSONArray pipeline = BSON_ARRAY(BSON("$out" << testBarNss.coll()));
BSONObj cmdObj =
BSON("aggregate" << testFooNss.coll() << "pipeline" << pipeline << "cursor" << BSONObj());
- ASSERT_OK(authzSession->checkAuthForAggregate(testFooNss, cmdObj, false));
+ PrivilegeVector privileges =
+ uassertStatusOK(authzSession->getPrivilegesForAggregate(testFooNss, cmdObj, false));
+ ASSERT_TRUE(authzSession->isAuthorizedForPrivileges(privileges));
BSONObj cmdObjNoBypassDocumentValidation = BSON(
"aggregate" << testFooNss.coll() << "pipeline" << pipeline << "bypassDocumentValidation"
<< false
<< "cursor"
<< BSONObj());
- ASSERT_OK(
- authzSession->checkAuthForAggregate(testFooNss, cmdObjNoBypassDocumentValidation, false));
+ privileges = uassertStatusOK(authzSession->getPrivilegesForAggregate(
+ testFooNss, cmdObjNoBypassDocumentValidation, false));
+ ASSERT_TRUE(authzSession->isAuthorizedForPrivileges(privileges));
}
TEST_F(AuthorizationSessionTest,
@@ -905,8 +933,9 @@ TEST_F(AuthorizationSessionTest,
BSON("aggregate" << testFooNss.coll() << "pipeline" << pipeline << "cursor" << BSONObj()
<< "bypassDocumentValidation"
<< true);
- ASSERT_EQ(ErrorCodes::Unauthorized,
- authzSession->checkAuthForAggregate(testFooNss, cmdObj, false));
+ PrivilegeVector privileges =
+ uassertStatusOK(authzSession->getPrivilegesForAggregate(testFooNss, cmdObj, false));
+ ASSERT_FALSE(authzSession->isAuthorizedForPrivileges(privileges));
}
TEST_F(AuthorizationSessionTest,
@@ -922,7 +951,9 @@ TEST_F(AuthorizationSessionTest,
BSON("aggregate" << testFooNss.coll() << "pipeline" << pipeline << "cursor" << BSONObj()
<< "bypassDocumentValidation"
<< true);
- ASSERT_OK(authzSession->checkAuthForAggregate(testFooNss, cmdObj, false));
+ PrivilegeVector privileges =
+ uassertStatusOK(authzSession->getPrivilegesForAggregate(testFooNss, cmdObj, true));
+ ASSERT_TRUE(authzSession->isAuthorizedForPrivileges(privileges));
}
TEST_F(AuthorizationSessionTest, CannotAggregateLookupWithoutFindOnJoinedNamespace) {
@@ -931,8 +962,9 @@ TEST_F(AuthorizationSessionTest, CannotAggregateLookupWithoutFindOnJoinedNamespa
BSONArray pipeline = BSON_ARRAY(BSON("$lookup" << BSON("from" << testBarNss.coll())));
BSONObj cmdObj =
BSON("aggregate" << testFooNss.coll() << "pipeline" << pipeline << "cursor" << BSONObj());
- ASSERT_EQ(ErrorCodes::Unauthorized,
- authzSession->checkAuthForAggregate(testFooNss, cmdObj, false));
+ PrivilegeVector privileges =
+ uassertStatusOK(authzSession->getPrivilegesForAggregate(testFooNss, cmdObj, false));
+ ASSERT_FALSE(authzSession->isAuthorizedForPrivileges(privileges));
}
TEST_F(AuthorizationSessionTest, CanAggregateLookupWithFindOnJoinedNamespace) {
@@ -942,7 +974,9 @@ TEST_F(AuthorizationSessionTest, CanAggregateLookupWithFindOnJoinedNamespace) {
BSONArray pipeline = BSON_ARRAY(BSON("$lookup" << BSON("from" << testBarNss.coll())));
BSONObj cmdObj =
BSON("aggregate" << testFooNss.coll() << "pipeline" << pipeline << "cursor" << BSONObj());
- ASSERT_OK(authzSession->checkAuthForAggregate(testFooNss, cmdObj, false));
+ PrivilegeVector privileges =
+ uassertStatusOK(authzSession->getPrivilegesForAggregate(testFooNss, cmdObj, true));
+ ASSERT_TRUE(authzSession->isAuthorizedForPrivileges(privileges));
}
@@ -955,8 +989,9 @@ TEST_F(AuthorizationSessionTest, CannotAggregateLookupWithoutFindOnNestedJoinedN
BSON("$lookup" << BSON("from" << testBarNss.coll() << "pipeline" << nestedPipeline)));
BSONObj cmdObj =
BSON("aggregate" << testFooNss.coll() << "pipeline" << pipeline << "cursor" << BSONObj());
- ASSERT_EQ(ErrorCodes::Unauthorized,
- authzSession->checkAuthForAggregate(testFooNss, cmdObj, false));
+ PrivilegeVector privileges =
+ uassertStatusOK(authzSession->getPrivilegesForAggregate(testFooNss, cmdObj, false));
+ ASSERT_FALSE(authzSession->isAuthorizedForPrivileges(privileges));
}
TEST_F(AuthorizationSessionTest, CanAggregateLookupWithFindOnNestedJoinedNamespace) {
@@ -969,7 +1004,9 @@ TEST_F(AuthorizationSessionTest, CanAggregateLookupWithFindOnNestedJoinedNamespa
BSON("$lookup" << BSON("from" << testBarNss.coll() << "pipeline" << nestedPipeline)));
BSONObj cmdObj =
BSON("aggregate" << testFooNss.coll() << "pipeline" << pipeline << "cursor" << BSONObj());
- ASSERT_OK(authzSession->checkAuthForAggregate(testFooNss, cmdObj, false));
+ PrivilegeVector privileges =
+ uassertStatusOK(authzSession->getPrivilegesForAggregate(testFooNss, cmdObj, false));
+ ASSERT_TRUE(authzSession->isAuthorizedForPrivileges(privileges));
}
TEST_F(AuthorizationSessionTest, CheckAuthForAggregateWithDeeplyNestedLookup) {
@@ -1010,7 +1047,9 @@ TEST_F(AuthorizationSessionTest, CheckAuthForAggregateWithDeeplyNestedLookup) {
pipelineBuilder.doneFast();
cmdBuilder << "cursor" << BSONObj();
- ASSERT_OK(authzSession->checkAuthForAggregate(testFooNss, cmdBuilder.obj(), false));
+ PrivilegeVector privileges = uassertStatusOK(
+ authzSession->getPrivilegesForAggregate(testFooNss, cmdBuilder.obj(), false));
+ ASSERT_TRUE(authzSession->isAuthorizedForPrivileges(privileges));
}
@@ -1020,8 +1059,9 @@ TEST_F(AuthorizationSessionTest, CannotAggregateGraphLookupWithoutFindOnJoinedNa
BSONArray pipeline = BSON_ARRAY(BSON("$graphLookup" << BSON("from" << testBarNss.coll())));
BSONObj cmdObj =
BSON("aggregate" << testFooNss.coll() << "pipeline" << pipeline << "cursor" << BSONObj());
- ASSERT_EQ(ErrorCodes::Unauthorized,
- authzSession->checkAuthForAggregate(testFooNss, cmdObj, false));
+ PrivilegeVector privileges =
+ uassertStatusOK(authzSession->getPrivilegesForAggregate(testFooNss, cmdObj, false));
+ ASSERT_FALSE(authzSession->isAuthorizedForPrivileges(privileges));
}
TEST_F(AuthorizationSessionTest, CanAggregateGraphLookupWithFindOnJoinedNamespace) {
@@ -1031,7 +1071,9 @@ TEST_F(AuthorizationSessionTest, CanAggregateGraphLookupWithFindOnJoinedNamespac
BSONArray pipeline = BSON_ARRAY(BSON("$graphLookup" << BSON("from" << testBarNss.coll())));
BSONObj cmdObj =
BSON("aggregate" << testFooNss.coll() << "pipeline" << pipeline << "cursor" << BSONObj());
- ASSERT_OK(authzSession->checkAuthForAggregate(testFooNss, cmdObj, false));
+ PrivilegeVector privileges =
+ uassertStatusOK(authzSession->getPrivilegesForAggregate(testFooNss, cmdObj, false));
+ ASSERT_TRUE(authzSession->isAuthorizedForPrivileges(privileges));
}
TEST_F(AuthorizationSessionTest,
@@ -1044,20 +1086,19 @@ TEST_F(AuthorizationSessionTest,
"[{$graphLookup: {from: 'qux'}}]}}"));
BSONObj cmdObj =
BSON("aggregate" << testFooNss.coll() << "pipeline" << pipeline << "cursor" << BSONObj());
- ASSERT_EQ(ErrorCodes::Unauthorized,
- authzSession->checkAuthForAggregate(testFooNss, cmdObj, false));
+ PrivilegeVector privileges =
+ uassertStatusOK(authzSession->getPrivilegesForAggregate(testFooNss, cmdObj, false));
+ ASSERT_FALSE(authzSession->isAuthorizedForPrivileges(privileges));
// We have find on the $lookup namespace but not on the $graphLookup namespace.
authzSession->assumePrivilegesForDB({Privilege(testFooCollResource, {ActionType::find}),
Privilege(testBarCollResource, {ActionType::find})});
- ASSERT_EQ(ErrorCodes::Unauthorized,
- authzSession->checkAuthForAggregate(testFooNss, cmdObj, false));
+ ASSERT_FALSE(authzSession->isAuthorizedForPrivileges(privileges));
// We have find on the $graphLookup namespace but not on the $lookup namespace.
authzSession->assumePrivilegesForDB({Privilege(testFooCollResource, {ActionType::find}),
Privilege(testQuxCollResource, {ActionType::find})});
- ASSERT_EQ(ErrorCodes::Unauthorized,
- authzSession->checkAuthForAggregate(testFooNss, cmdObj, false));
+ ASSERT_FALSE(authzSession->isAuthorizedForPrivileges(privileges));
}
TEST_F(AuthorizationSessionTest,
@@ -1071,7 +1112,9 @@ TEST_F(AuthorizationSessionTest,
"[{$graphLookup: {from: 'qux'}}]}}"));
BSONObj cmdObj =
BSON("aggregate" << testFooNss.coll() << "pipeline" << pipeline << "cursor" << BSONObj());
- ASSERT_OK(authzSession->checkAuthForAggregate(testFooNss, cmdObj, false));
+ PrivilegeVector privileges =
+ uassertStatusOK(authzSession->getPrivilegesForAggregate(testFooNss, cmdObj, true));
+ ASSERT_TRUE(authzSession->isAuthorizedForPrivileges(privileges));
}
TEST_F(AuthorizationSessionTest, UnauthorizedSessionIsCoauthorizedWithEmptyUserSet) {
@@ -1225,9 +1268,12 @@ TEST_F(AuthorizationSessionTest,
TEST_F(AuthorizationSessionTest, CannotListCollectionsWithoutListCollectionsPrivilege) {
BSONObj cmd = BSON("listCollections" << 1);
// With no privileges, there is not authorization to list collections
- ASSERT_FALSE(authzSession->isAuthorizedToListCollections(testFooNss.db(), cmd));
- ASSERT_FALSE(authzSession->isAuthorizedToListCollections(testBarNss.db(), cmd));
- ASSERT_FALSE(authzSession->isAuthorizedToListCollections(testQuxNss.db(), cmd));
+ ASSERT_EQ(ErrorCodes::Unauthorized,
+ authzSession->checkAuthorizedToListCollections(testFooNss.db(), cmd).getStatus());
+ ASSERT_EQ(ErrorCodes::Unauthorized,
+ authzSession->checkAuthorizedToListCollections(testBarNss.db(), cmd).getStatus());
+ ASSERT_EQ(ErrorCodes::Unauthorized,
+ authzSession->checkAuthorizedToListCollections(testQuxNss.db(), cmd).getStatus());
}
TEST_F(AuthorizationSessionTest, CanListCollectionsWithListCollectionsPrivilege) {
@@ -1235,9 +1281,9 @@ TEST_F(AuthorizationSessionTest, CanListCollectionsWithListCollectionsPrivilege)
// The listCollections privilege authorizes the list collections command.
authzSession->assumePrivilegesForDB(Privilege(testDBResource, {ActionType::listCollections}));
- ASSERT_TRUE(authzSession->isAuthorizedToListCollections(testFooNss.db(), cmd));
- ASSERT_TRUE(authzSession->isAuthorizedToListCollections(testBarNss.db(), cmd));
- ASSERT_TRUE(authzSession->isAuthorizedToListCollections(testQuxNss.db(), cmd));
+ ASSERT_OK(authzSession->checkAuthorizedToListCollections(testFooNss.db(), cmd).getStatus());
+ ASSERT_OK(authzSession->checkAuthorizedToListCollections(testBarNss.db(), cmd).getStatus());
+ ASSERT_OK(authzSession->checkAuthorizedToListCollections(testQuxNss.db(), cmd).getStatus());
}
TEST_F(AuthorizationSessionTest, CanListOwnCollectionsWithPrivilege) {
@@ -1246,11 +1292,12 @@ TEST_F(AuthorizationSessionTest, CanListOwnCollectionsWithPrivilege) {
// The listCollections privilege authorizes the list collections command.
authzSession->assumePrivilegesForDB(Privilege(testFooCollResource, {ActionType::find}));
- ASSERT_TRUE(authzSession->isAuthorizedToListCollections(testFooNss.db(), cmd));
- ASSERT_TRUE(authzSession->isAuthorizedToListCollections(testBarNss.db(), cmd));
- ASSERT_TRUE(authzSession->isAuthorizedToListCollections(testQuxNss.db(), cmd));
+ ASSERT_OK(authzSession->checkAuthorizedToListCollections(testFooNss.db(), cmd).getStatus());
+ ASSERT_OK(authzSession->checkAuthorizedToListCollections(testBarNss.db(), cmd).getStatus());
+ ASSERT_OK(authzSession->checkAuthorizedToListCollections(testQuxNss.db(), cmd).getStatus());
- ASSERT_FALSE(authzSession->isAuthorizedToListCollections("other", cmd));
+ ASSERT_EQ(ErrorCodes::Unauthorized,
+ authzSession->checkAuthorizedToListCollections("other", cmd).getStatus());
}
TEST_F(AuthorizationSessionTest, CanCheckIfHasAnyPrivilegeOnResource) {
diff --git a/src/mongo/db/clientcursor.cpp b/src/mongo/db/clientcursor.cpp
index 41c2b814d5c..fef5105a9a5 100644
--- a/src/mongo/db/clientcursor.cpp
+++ b/src/mongo/db/clientcursor.cpp
@@ -90,6 +90,7 @@ ClientCursor::ClientCursor(ClientCursorParams params,
_txnNumber(operationUsingCursor->getTxnNumber()),
_readConcernArgs(params.readConcernArgs),
_originatingCommand(params.originatingCommandObj),
+ _originatingPrivileges(std::move(params.originatingPrivileges)),
_queryOptions(params.queryOptions),
_lockPolicy(params.lockPolicy),
_exec(std::move(params.exec)),
diff --git a/src/mongo/db/clientcursor.h b/src/mongo/db/clientcursor.h
index 8bbc5491ee2..e4f447ddc59 100644
--- a/src/mongo/db/clientcursor.h
+++ b/src/mongo/db/clientcursor.h
@@ -32,6 +32,7 @@
#include <boost/optional.hpp>
+#include "mongo/db/auth/privilege.h"
#include "mongo/db/auth/user_name.h"
#include "mongo/db/cursor_id.h"
#include "mongo/db/jsobj.h"
@@ -80,7 +81,8 @@ struct ClientCursorParams {
UserNameIterator authenticatedUsersIter,
repl::ReadConcernArgs readConcernArgs,
BSONObj originatingCommandObj,
- LockPolicy lockPolicy)
+ LockPolicy lockPolicy,
+ PrivilegeVector originatingPrivileges)
: exec(std::move(planExecutor)),
nss(std::move(nss)),
readConcernArgs(readConcernArgs),
@@ -88,7 +90,8 @@ struct ClientCursorParams {
? exec->getCanonicalQuery()->getQueryRequest().getOptions()
: 0),
originatingCommandObj(originatingCommandObj.getOwned()),
- lockPolicy(lockPolicy) {
+ lockPolicy(lockPolicy),
+ originatingPrivileges(std::move(originatingPrivileges)) {
while (authenticatedUsersIter.more()) {
authenticatedUsers.emplace_back(authenticatedUsersIter.next());
}
@@ -115,6 +118,7 @@ struct ClientCursorParams {
int queryOptions = 0;
BSONObj originatingCommandObj;
const LockPolicy lockPolicy;
+ PrivilegeVector originatingPrivileges;
};
/**
@@ -186,11 +190,23 @@ public:
return _queryOptions & QueryOption_AwaitData;
}
+ /**
+ * Returns the original command object which created this cursor.
+ */
const BSONObj& getOriginatingCommandObj() const {
return _originatingCommand;
}
/**
+ * Returns the privileges required to run a getMore against this cursor. This is the same as the
+ * set of privileges which would have been required to create the cursor in the first place.
+ */
+ const PrivilegeVector& getOriginatingPrivileges() const& {
+ return _originatingPrivileges;
+ }
+ void getOriginatingPrivileges() && = delete;
+
+ /**
* Returns the total number of query results returned by the cursor so far.
*/
std::uint64_t nReturnedSoFar() const {
@@ -369,6 +385,9 @@ private:
// Holds an owned copy of the command specification received from the client.
const BSONObj _originatingCommand;
+ // The privileges required for the _originatingCommand.
+ const PrivilegeVector _originatingPrivileges;
+
// See the QueryOptions enum in dbclientinterface.h.
const int _queryOptions = 0;
diff --git a/src/mongo/db/commands/count_cmd.cpp b/src/mongo/db/commands/count_cmd.cpp
index a6b323b11f7..818ea1400e4 100644
--- a/src/mongo/db/commands/count_cmd.cpp
+++ b/src/mongo/db/commands/count_cmd.cpp
@@ -146,10 +146,13 @@ public:
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.getValue().getNamespaceString(),
viewAggRequest.getValue(),
viewAggregation.getValue(),
+ PrivilegeVector(),
result);
}
diff --git a/src/mongo/db/commands/current_op.cpp b/src/mongo/db/commands/current_op.cpp
index 0e76cfcd4a9..28eed7396e7 100644
--- a/src/mongo/db/commands/current_op.cpp
+++ b/src/mongo/db/commands/current_op.cpp
@@ -70,8 +70,17 @@ public:
rpc::OpMsgReplyBuilder replyBuilder;
- auto status = runAggregate(
- opCtx, request.getNamespaceString(), request, std::move(aggCmdObj), &replyBuilder);
+ PrivilegeVector privileges;
+ if (!aggCmdObj["$ownOps"].trueValue()) {
+ privileges = {Privilege(ResourcePattern::forClusterResource(), ActionType::inprog)};
+ }
+
+ auto status = runAggregate(opCtx,
+ request.getNamespaceString(),
+ request,
+ std::move(aggCmdObj),
+ privileges,
+ &replyBuilder);
if (!status.isOK()) {
return status;
diff --git a/src/mongo/db/commands/distinct.cpp b/src/mongo/db/commands/distinct.cpp
index 4d0f1dd70ea..f26feb2cf80 100644
--- a/src/mongo/db/commands/distinct.cpp
+++ b/src/mongo/db/commands/distinct.cpp
@@ -144,8 +144,14 @@ public:
return viewAggRequest.getStatus();
}
- return runAggregate(
- opCtx, nss, viewAggRequest.getValue(), viewAggregation.getValue(), result);
+ // 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.getValue(),
+ viewAggregation.getValue(),
+ PrivilegeVector(),
+ result);
}
Collection* const collection = ctx->getCollection();
diff --git a/src/mongo/db/commands/find_cmd.cpp b/src/mongo/db/commands/find_cmd.cpp
index cfcdecc9fb8..e9a20684a6b 100644
--- a/src/mongo/db/commands/find_cmd.cpp
+++ b/src/mongo/db/commands/find_cmd.cpp
@@ -192,8 +192,10 @@ public:
AggregationRequest::parseFromBSON(nss, viewAggregationCommand, verbosity));
try {
- uassertStatusOK(
- runAggregate(opCtx, nss, aggRequest, viewAggregationCommand, result));
+ // An empty PrivilegeVector is acceptable because these privileges are only
+ // checked on getMore and explain will not open a cursor.
+ uassertStatusOK(runAggregate(
+ opCtx, nss, aggRequest, viewAggregationCommand, PrivilegeVector(), result));
} catch (DBException& error) {
if (error.code() == ErrorCodes::InvalidPipelineOperator) {
uasserted(ErrorCodes::InvalidPipelineOperator,
@@ -404,7 +406,8 @@ public:
AuthorizationSession::get(opCtx->getClient())->getAuthenticatedUserNames(),
repl::ReadConcernArgs::get(opCtx),
_request.body,
- ClientCursorParams::LockPolicy::kLockExternally});
+ ClientCursorParams::LockPolicy::kLockExternally,
+ {Privilege(ResourcePattern::forExactNamespace(nss), ActionType::find)}});
cursorId = pinnedCursor.getCursor()->cursorid();
invariant(!exec);
diff --git a/src/mongo/db/commands/getmore_cmd.cpp b/src/mongo/db/commands/getmore_cmd.cpp
index 8db406d77e2..c91eef49164 100644
--- a/src/mongo/db/commands/getmore_cmd.cpp
+++ b/src/mongo/db/commands/getmore_cmd.cpp
@@ -363,13 +363,20 @@ public:
// A user can only call getMore on their own cursor. If there were multiple users
// authenticated when the cursor was created, then at least one of them must be
// authenticated in order to run getMore on the cursor.
- if (!AuthorizationSession::get(opCtx->getClient())
- ->isCoauthorizedWith(cursorPin->getAuthenticatedUsers())) {
+ auto authzSession = AuthorizationSession::get(opCtx->getClient());
+ if (!authzSession->isCoauthorizedWith(cursorPin->getAuthenticatedUsers())) {
uasserted(ErrorCodes::Unauthorized,
str::stream() << "cursor id " << _request.cursorid
<< " was not created by the authenticated user");
}
+ // Ensure that the client still has the privileges to run the originating command.
+ if (!authzSession->isAuthorizedForPrivileges(cursorPin->getOriginatingPrivileges())) {
+ uasserted(ErrorCodes::Unauthorized,
+ str::stream() << "not authorized for getMore with cursor id "
+ << _request.cursorid);
+ }
+
if (_request.nss != cursorPin->nss()) {
uasserted(ErrorCodes::Unauthorized,
str::stream() << "Requested getMore on namespace '" << _request.nss.ns()
diff --git a/src/mongo/db/commands/list_collections.cpp b/src/mongo/db/commands/list_collections.cpp
index 4277b8455aa..a702a8f6807 100644
--- a/src/mongo/db/commands/list_collections.cpp
+++ b/src/mongo/db/commands/list_collections.cpp
@@ -233,7 +233,7 @@ public:
AuthorizationSession* authzSession = AuthorizationSession::get(client);
- if (authzSession->isAuthorizedToListCollections(dbname, cmdObj)) {
+ if (authzSession->checkAuthorizedToListCollections(dbname, cmdObj).isOK()) {
return Status::OK();
}
@@ -386,7 +386,9 @@ public:
AuthorizationSession::get(opCtx->getClient())->getAuthenticatedUserNames(),
repl::ReadConcernArgs::get(opCtx),
jsobj,
- ClientCursorParams::LockPolicy::kLocksInternally});
+ ClientCursorParams::LockPolicy::kLocksInternally,
+ uassertStatusOK(AuthorizationSession::get(opCtx->getClient())
+ ->checkAuthorizedToListCollections(dbname, jsobj))});
appendCursorResponseObject(
pinnedCursor.getCursor()->cursorid(), cursorNss.ns(), firstBatch.arr(), &result);
diff --git a/src/mongo/db/commands/list_indexes.cpp b/src/mongo/db/commands/list_indexes.cpp
index b1d2796bb31..2210639210d 100644
--- a/src/mongo/db/commands/list_indexes.cpp
+++ b/src/mongo/db/commands/list_indexes.cpp
@@ -226,7 +226,8 @@ public:
AuthorizationSession::get(opCtx->getClient())->getAuthenticatedUserNames(),
repl::ReadConcernArgs::get(opCtx),
cmdObj,
- ClientCursorParams::LockPolicy::kLocksInternally});
+ ClientCursorParams::LockPolicy::kLocksInternally,
+ {Privilege(ResourcePattern::forExactNamespace(nss), ActionType::listIndexes)}});
appendCursorResponseObject(
pinnedCursor.getCursor()->cursorid(), nss.ns(), firstBatch.arr(), &result);
diff --git a/src/mongo/db/commands/pipeline_command.cpp b/src/mongo/db/commands/pipeline_command.cpp
index edcd4af42b1..ef5dc99606b 100644
--- a/src/mongo/db/commands/pipeline_command.cpp
+++ b/src/mongo/db/commands/pipeline_command.cpp
@@ -53,15 +53,24 @@ public:
std::unique_ptr<CommandInvocation> parse(OperationContext* opCtx,
const OpMsgRequest& opMsgRequest) override {
// TODO: Parsing to a Pipeline and/or AggregationRequest here.
- return std::make_unique<Invocation>(this, opMsgRequest);
+
+ auto privileges =
+ uassertStatusOK(AuthorizationSession::get(opCtx->getClient())
+ ->getPrivilegesForAggregate(
+ AggregationRequest::parseNs(
+ opMsgRequest.getDatabase().toString(), opMsgRequest.body),
+ opMsgRequest.body,
+ false));
+ return std::make_unique<Invocation>(this, opMsgRequest, std::move(privileges));
}
class Invocation final : public CommandInvocation {
public:
- Invocation(Command* cmd, const OpMsgRequest& request)
+ Invocation(Command* cmd, const OpMsgRequest& request, PrivilegeVector privileges)
: CommandInvocation(cmd),
_request(request),
- _dbName(request.getDatabase().toString()) {}
+ _dbName(request.getDatabase().toString()),
+ _privileges(std::move(privileges)) {}
private:
bool supportsWriteConcern() const override {
@@ -90,11 +99,11 @@ public:
void run(OperationContext* opCtx, rpc::ReplyBuilderInterface* reply) override {
const auto aggregationRequest = uassertStatusOK(
AggregationRequest::parseFromBSON(_dbName, _request.body, boost::none));
-
uassertStatusOK(runAggregate(opCtx,
aggregationRequest.getNamespaceString(),
aggregationRequest,
_request.body,
+ _privileges,
reply));
}
@@ -112,17 +121,20 @@ public:
aggregationRequest.getNamespaceString(),
aggregationRequest,
_request.body,
+ _privileges,
result));
}
void doCheckAuthorization(OperationContext* opCtx) const override {
- const auto nss = ns();
- uassertStatusOK(AuthorizationSession::get(opCtx->getClient())
- ->checkAuthForAggregate(nss, _request.body, false));
+ uassert(ErrorCodes::Unauthorized,
+ "unauthorized",
+ AuthorizationSession::get(opCtx->getClient())
+ ->isAuthorizedForPrivileges(_privileges));
}
const OpMsgRequest& _request;
const std::string _dbName;
+ const PrivilegeVector _privileges;
};
std::string help() const override {
diff --git a/src/mongo/db/commands/repair_cursor.cpp b/src/mongo/db/commands/repair_cursor.cpp
index b21d2c2e23c..04c4d29fc49 100644
--- a/src/mongo/db/commands/repair_cursor.cpp
+++ b/src/mongo/db/commands/repair_cursor.cpp
@@ -108,7 +108,8 @@ public:
AuthorizationSession::get(opCtx->getClient())->getAuthenticatedUserNames(),
repl::ReadConcernArgs::get(opCtx),
cmdObj,
- ClientCursorParams::LockPolicy::kLockExternally});
+ ClientCursorParams::LockPolicy::kLockExternally,
+ {Privilege(parseResourcePattern(dbname, cmdObj), ActionType::find)}});
appendCursorResponseObject(
pinnedCursor.getCursor()->cursorid(), ns.ns(), BSONArray(), &result);
diff --git a/src/mongo/db/commands/run_aggregate.cpp b/src/mongo/db/commands/run_aggregate.cpp
index 09ac63f0349..a8cefeb2caf 100644
--- a/src/mongo/db/commands/run_aggregate.cpp
+++ b/src/mongo/db/commands/run_aggregate.cpp
@@ -392,6 +392,7 @@ Status runAggregate(OperationContext* opCtx,
const NamespaceString& origNss,
const AggregationRequest& request,
const BSONObj& cmdObj,
+ const PrivilegeVector& privileges,
rpc::ReplyBuilderInterface* result) {
// For operations on views, this will be the underlying namespace.
NamespaceString nss = request.getNamespaceString();
@@ -516,7 +517,7 @@ Status runAggregate(OperationContext* opCtx,
auto newRequest = resolvedView.asExpandedViewAggregation(request);
auto newCmd = newRequest.serializeToCommandObj().toBson();
- auto status = runAggregate(opCtx, origNss, newRequest, newCmd, result);
+ auto status = runAggregate(opCtx, origNss, newRequest, newCmd, privileges, result);
{
// Set the namespace of the curop back to the view namespace so ctx records
@@ -628,7 +629,6 @@ Status runAggregate(OperationContext* opCtx,
p.deleteUnderlying();
}
});
-
for (size_t idx = 0; idx < execs.size(); ++idx) {
ClientCursorParams cursorParams(
std::move(execs[idx]),
@@ -636,7 +636,8 @@ Status runAggregate(OperationContext* opCtx,
AuthorizationSession::get(opCtx->getClient())->getAuthenticatedUserNames(),
repl::ReadConcernArgs::get(opCtx),
cmdObj,
- ClientCursorParams::LockPolicy::kLocksInternally);
+ ClientCursorParams::LockPolicy::kLocksInternally,
+ privileges);
if (expCtx->tailableMode == TailableModeEnum::kTailable) {
cursorParams.setTailable(true);
} else if (expCtx->tailableMode == TailableModeEnum::kTailableAndAwaitData) {
diff --git a/src/mongo/db/commands/run_aggregate.h b/src/mongo/db/commands/run_aggregate.h
index 941d6036009..983161ac568 100644
--- a/src/mongo/db/commands/run_aggregate.h
+++ b/src/mongo/db/commands/run_aggregate.h
@@ -32,6 +32,7 @@
#include "mongo/bson/bsonobj.h"
#include "mongo/bson/bsonobjbuilder.h"
+#include "mongo/db/auth/privilege.h"
#include "mongo/db/namespace_string.h"
#include "mongo/db/operation_context.h"
#include "mongo/db/pipeline/aggregation_request.h"
@@ -45,12 +46,16 @@ namespace mongo {
* The raw aggregate command parameters should be passed in 'cmdObj', and will be reported as the
* originatingCommand in subsequent getMores on the resulting agg cursor.
*
+ * 'privileges' contains the privileges that were required to run this aggregation, to be used later
+ * for re-checking privileges for getMore commands.
+ *
* On success, fills out 'result' with the command response.
*/
Status runAggregate(OperationContext* opCtx,
const NamespaceString& nss,
const AggregationRequest& request,
const BSONObj& cmdObj,
+ const PrivilegeVector& privileges,
rpc::ReplyBuilderInterface* result);
} // namespace mongo
diff --git a/src/mongo/db/commands/user_management_commands.cpp b/src/mongo/db/commands/user_management_commands.cpp
index f1cd1574494..b2b911c880a 100644
--- a/src/mongo/db/commands/user_management_commands.cpp
+++ b/src/mongo/db/commands/user_management_commands.cpp
@@ -1374,10 +1374,12 @@ public:
rpc::OpMsgReplyBuilder replyBuilder;
AggregationRequest aggRequest(AuthorizationManager::usersCollectionNamespace,
std::move(pipeline));
+ // Impose no cursor privilege requirements, as cursor is drained internally
uassertStatusOK(runAggregate(opCtx,
AuthorizationManager::usersCollectionNamespace,
aggRequest,
aggRequest.serializeToCommandObj().toBson(),
+ PrivilegeVector(),
&replyBuilder));
auto bodyBuilder = replyBuilder.getBodyBuilder();
CommandHelpers::appendSimpleCommandStatus(bodyBuilder, true);
diff --git a/src/mongo/db/query/find.cpp b/src/mongo/db/query/find.cpp
index da3ff98fb0c..a6aa3fc48ac 100644
--- a/src/mongo/db/query/find.cpp
+++ b/src/mongo/db/query/find.cpp
@@ -676,7 +676,8 @@ std::string runQuery(OperationContext* opCtx,
AuthorizationSession::get(opCtx->getClient())->getAuthenticatedUserNames(),
readConcernArgs,
upconvertedQuery,
- ClientCursorParams::LockPolicy::kLockExternally});
+ ClientCursorParams::LockPolicy::kLockExternally,
+ {Privilege(ResourcePattern::forExactNamespace(nss), ActionType::find)}});
ccId = pinnedCursor.getCursor()->cursorid();
LOG(5) << "caching executor with cursorid " << ccId << " after returning " << numResults