summaryrefslogtreecommitdiff
path: root/src/mongo/db/auth
diff options
context:
space:
mode:
authorMark Benvenuto <mark.benvenuto@mongodb.com>2021-03-21 22:46:19 -0400
committerEvergreen Agent <no-reply@evergreen.mongodb.com>2021-03-22 03:34:46 +0000
commit68dbfa5edbb55fec190e508607cf2276367f4f93 (patch)
treee1999ab678707adde4dd7f436dd07c8292cc4970 /src/mongo/db/auth
parent393fe9da6a544af2aaea88eda037cfa3eea0da6d (diff)
downloadmongo-68dbfa5edbb55fec190e508607cf2276367f4f93.tar.gz
SERVER-54524 Extend Authorization Session to record all access checks and privilege checks.
Diffstat (limited to 'src/mongo/db/auth')
-rw-r--r--src/mongo/db/auth/SConscript2
-rw-r--r--src/mongo/db/auth/access_checks.idl25
-rw-r--r--src/mongo/db/auth/authorization_contract.cpp45
-rw-r--r--src/mongo/db/auth/authorization_contract.h5
-rw-r--r--src/mongo/db/auth/authorization_session.h16
-rw-r--r--src/mongo/db/auth/authorization_session_impl.cpp105
-rw-r--r--src/mongo/db/auth/authorization_session_impl.h22
-rw-r--r--src/mongo/db/auth/authorization_session_test.cpp31
-rw-r--r--src/mongo/db/auth/impersonation_session.h4
9 files changed, 240 insertions, 15 deletions
diff --git a/src/mongo/db/auth/SConscript b/src/mongo/db/auth/SConscript
index 4725a1b3362..a068a11714b 100644
--- a/src/mongo/db/auth/SConscript
+++ b/src/mongo/db/auth/SConscript
@@ -143,7 +143,6 @@ env.Library(
env.Library(
target='auth_impl_internal',
source=[
- 'access_checks.idl',
'authorization_contract.idl',
'authorization_manager_impl.cpp',
'authorization_session_impl.cpp',
@@ -211,6 +210,7 @@ env.Library(
env.Library(
target='authprivilege',
source=[
+ 'access_checks.idl',
'action_set.cpp',
'action_type.cpp',
'action_type.idl',
diff --git a/src/mongo/db/auth/access_checks.idl b/src/mongo/db/auth/access_checks.idl
index cc5b6955cc2..99ded87fd03 100644
--- a/src/mongo/db/auth/access_checks.idl
+++ b/src/mongo/db/auth/access_checks.idl
@@ -33,6 +33,25 @@ enums:
description: "List of supported access checks in AuthorizationSession"
type: string
values:
- kIsAuthenticated : "is_authenticated"
- kIsCoAuthorized : "is_coauthorized"
- kIsAuthorizedToParseNamespaceElement : "is_authorized_to_parse_namespace_element"
+ kCheckAuthorizedToListCollections : "check_authorized_to_list_collections"
+ kCheckCursorSessionPrivilege : "check_cursor_session_privilege"
+ kClearImpersonatedUserData : "clear_impersonated_user_data"
+ kGetAuthenticatedRoleNames : "get_authenticated_role_names"
+ kGetAuthenticatedUserNames : "get_authenticated_user_names"
+ kGetImpersonatedRoleNames : "get_impersonated_role_names"
+ kGetImpersonatedUserNames : "get_impersonated_user_names"
+ kGetSingleUser : "get_single_user"
+ kIsAuthenticated : "is_authenticated"
+ kIsAuthenticatedAsUserWithRole : "is_authenticated_as_user_with_role"
+ kIsAuthorizedForAnyActionOnAnyResourceInDB : "is_authorized_for_any_action_on_any_resource_in_db"
+ kIsAuthorizedForAnyActionOnResource : "is_authorized_for_any_action_on_resource"
+ kIsAuthorizedToChangeAsUser : "is_authorized_to_change_as_user"
+ kIsAuthorizedToCreateRole : "is_authorized_to_create_role"
+ kIsAuthorizedToParseNamespaceElement : "is_authorized_to_parse_namespace_element"
+ kIsCoAuthorized : "is_coauthorized"
+ kIsCoauthorizedWith : "is_coauthorized_with"
+ kIsCoauthorizedWithClient : "is_coauthorized_with_client"
+ kIsImpersonating : "is_impersonating"
+ kLookupUser : "lookup_user"
+ kShouldIgnoreAuthChecks : "should_ignore_auth_checks"
+ kIsUsingLocalhostBypass : "is_using_localhost_bypass" # Called in common code in commands.cpp dispatch \ No newline at end of file
diff --git a/src/mongo/db/auth/authorization_contract.cpp b/src/mongo/db/auth/authorization_contract.cpp
index 7a0d3985f4a..6f78c07f150 100644
--- a/src/mongo/db/auth/authorization_contract.cpp
+++ b/src/mongo/db/auth/authorization_contract.cpp
@@ -27,11 +27,26 @@
* it in the license file.
*/
+#define MONGO_LOGV2_DEFAULT_COMPONENT ::mongo::logv2::LogComponent::kAccessControl
+
#include "mongo/db/auth/authorization_contract.h"
+
+#include "mongo/bson/bsontypes.h"
+#include "mongo/db/auth/access_checks_gen.h"
+#include "mongo/db/auth/action_type_gen.h"
#include "mongo/db/auth/privilege.h"
+#include "mongo/logv2/log.h"
+#include "mongo/util/debug_util.h"
namespace mongo {
+void AuthorizationContract::clear() {
+ _checks.reset();
+ for (size_t i = 0; i < _privilegeChecks.size(); ++i) {
+ _privilegeChecks[i].removeAllActions();
+ }
+}
+
void AuthorizationContract::addAccessCheck(AccessCheckEnum check) {
_checks.set(static_cast<size_t>(check), true);
}
@@ -55,11 +70,41 @@ bool AuthorizationContract::hasPrivileges(const Privilege& p) const {
bool AuthorizationContract::contains(const AuthorizationContract& other) const {
if ((_checks | other._checks) != _checks) {
+ if (kDebugBuild) {
+ auto missingChecks = (_checks ^ other._checks) & other._checks;
+
+ BSONArrayBuilder builder;
+ for (size_t i = 0; i < missingChecks.size(); i++) {
+ if (missingChecks.test(i)) {
+ builder.append(AccessCheck_serializer(static_cast<AccessCheckEnum>(i)));
+ }
+ }
+
+ LOGV2(5452402, "Missing Auth Checks", "checks"_attr = builder.arr());
+ }
+
return false;
}
for (size_t i = 0; i < _privilegeChecks.size(); ++i) {
if (!_privilegeChecks[i].contains(other._privilegeChecks[i])) {
+ if (kDebugBuild) {
+ BSONArrayBuilder builder;
+
+ for (size_t k = 0; k < kNumActionTypes; k++) {
+ auto at = static_cast<ActionTypeEnum>(k);
+ if (other._privilegeChecks[i].contains(at) &&
+ !_privilegeChecks[i].contains(at)) {
+ builder.append(ActionType_serializer(at));
+ }
+ }
+
+ LOGV2(5452403,
+ "Missing Action Types for resource",
+ "resource"_attr = MatchType_serializer(static_cast<MatchTypeEnum>(i)),
+ "actions"_attr = builder.arr());
+ }
+
return false;
}
}
diff --git a/src/mongo/db/auth/authorization_contract.h b/src/mongo/db/auth/authorization_contract.h
index b5aba515573..2541481d72e 100644
--- a/src/mongo/db/auth/authorization_contract.h
+++ b/src/mongo/db/auth/authorization_contract.h
@@ -68,6 +68,11 @@ public:
}
/**
+ * Clear the authorization contract
+ */
+ void clear();
+
+ /**
* Add a access check to the contract.
*/
void addAccessCheck(AccessCheckEnum check);
diff --git a/src/mongo/db/auth/authorization_session.h b/src/mongo/db/auth/authorization_session.h
index 08892418c59..8ccb589f344 100644
--- a/src/mongo/db/auth/authorization_session.h
+++ b/src/mongo/db/auth/authorization_session.h
@@ -47,6 +47,7 @@
namespace mongo {
class Client;
+class AuthorizationContract;
/**
* Contains all the authorization logic for a single client connection. It contains a set of
@@ -136,6 +137,11 @@ public:
virtual void startRequest(OperationContext* opCtx) = 0;
/**
+ * Start tracking permissions and privileges in the authorization contract.
+ */
+ virtual void startContractTracking() = 0;
+
+ /**
* Adds the User identified by "UserName" to the authorization session, acquiring privileges
* for it in the process.
*/
@@ -178,13 +184,6 @@ public:
virtual void grantInternalAuthorization(Client* client) = 0;
virtual void grantInternalAuthorization(OperationContext* opCtx) = 0;
- // Generates a vector of default privileges that are granted to any user,
- // regardless of which roles that user does or does not possess.
- // If localhost exception is active, the permissions include the ability to create
- // the first user and the ability to run the commands needed to bootstrap the system
- // into a state where the first user can be created.
- virtual PrivilegeVector getDefaultPrivileges() = 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.
@@ -290,6 +289,9 @@ public:
virtual Status checkCursorSessionPrivilege(
OperationContext* const opCtx, boost::optional<LogicalSessionId> cursorSessionId) = 0;
+ // Verify the authorization contract. If contract == nullptr, no check is performed.
+ virtual void verifyContract(const AuthorizationContract* contract) const = 0;
+
protected:
virtual std::tuple<std::vector<UserName>*, std::vector<RoleName>*> _getImpersonations() = 0;
};
diff --git a/src/mongo/db/auth/authorization_session_impl.cpp b/src/mongo/db/auth/authorization_session_impl.cpp
index a6f09b04ff7..0686c974bcb 100644
--- a/src/mongo/db/auth/authorization_session_impl.cpp
+++ b/src/mongo/db/auth/authorization_session_impl.cpp
@@ -42,6 +42,7 @@
#include "mongo/db/audit.h"
#include "mongo/db/auth/action_set.h"
#include "mongo/db/auth/action_type.h"
+#include "mongo/db/auth/authorization_contract_gen.h"
#include "mongo/db/auth/authz_session_external_state.h"
#include "mongo/db/auth/privilege.h"
#include "mongo/db/bson/dotted_path_support.h"
@@ -51,6 +52,7 @@
#include "mongo/logv2/log.h"
#include "mongo/util/assert_util.h"
#include "mongo/util/str.h"
+#include "mongo/util/testing_proctor.h"
namespace mongo {
@@ -70,6 +72,24 @@ auto authorizationSessionCreateRegistration =
constexpr StringData ADMIN_DBNAME = "admin"_sd;
+bool checkContracts() {
+
+ // Only check contracts if the feature is enabled.
+ // TODO SERVER-52364 - Remove feature flag check
+ if (!serverGlobalParams.featureCompatibility.isVersionInitialized() ||
+ !feature_flags::gFeatureFlagAuthorizationContract.isEnabled(
+ serverGlobalParams.featureCompatibility)) {
+ return false;
+ }
+
+ // Only check contracts in testing modes, invalid contracts should not break customers.
+ if (!TestingProctor::instance().isEnabled()) {
+ return false;
+ }
+
+ return true;
+}
+
} // namespace
AuthorizationSessionImpl::AuthorizationSessionImpl(
@@ -90,6 +110,14 @@ void AuthorizationSessionImpl::startRequest(OperationContext* opCtx) {
_refreshUserInfoAsNeeded(opCtx);
}
+void AuthorizationSessionImpl::startContractTracking() {
+ if (!checkContracts()) {
+ return;
+ }
+
+ _contract.clear();
+}
+
Status AuthorizationSessionImpl::addAndAuthorizeUser(OperationContext* opCtx,
const UserName& userName) {
AuthorizationManager* authzManager = AuthorizationManager::get(opCtx->getServiceContext());
@@ -120,6 +148,8 @@ Status AuthorizationSessionImpl::addAndAuthorizeUser(OperationContext* opCtx,
}
User* AuthorizationSessionImpl::lookupUser(const UserName& name) {
+ _contract.addAccessCheck(AccessCheckEnum::kLookupUser);
+
auto user = _authenticatedUsers.lookup(name);
return user ? user.get() : nullptr;
}
@@ -127,6 +157,8 @@ User* AuthorizationSessionImpl::lookupUser(const UserName& name) {
User* AuthorizationSessionImpl::getSingleUser() {
UserName userName;
+ _contract.addAccessCheck(AccessCheckEnum::kGetSingleUser);
+
auto userNameItr = getAuthenticatedUserNames();
if (userNameItr.more()) {
userName = userNameItr.next();
@@ -176,10 +208,14 @@ void AuthorizationSessionImpl::logoutDatabase(Client* client,
}
UserNameIterator AuthorizationSessionImpl::getAuthenticatedUserNames() {
+ _contract.addAccessCheck(AccessCheckEnum::kGetAuthenticatedUserNames);
+
return _authenticatedUsers.getNames();
}
RoleNameIterator AuthorizationSessionImpl::getAuthenticatedRoleNames() {
+ _contract.addAccessCheck(AccessCheckEnum::kGetAuthenticatedRoleNames);
+
return makeRoleNameIterator(_authenticatedRoleNames.begin(), _authenticatedRoleNames.end());
}
@@ -197,7 +233,7 @@ void AuthorizationSessionImpl::grantInternalAuthorization(OperationContext* opCt
grantInternalAuthorization(opCtx->getClient());
}
-PrivilegeVector AuthorizationSessionImpl::getDefaultPrivileges() {
+PrivilegeVector AuthorizationSessionImpl::_getDefaultPrivileges() {
PrivilegeVector defaultPrivileges;
// If localhost exception is active (and no users exist),
@@ -242,6 +278,7 @@ PrivilegeVector AuthorizationSessionImpl::getDefaultPrivileges() {
bool AuthorizationSessionImpl::isAuthorizedToParseNamespaceElement(const BSONElement& element) {
const bool isUUID = element.type() == BinData && element.binDataType() == BinDataType::newUUID;
+ _contract.addAccessCheck(AccessCheckEnum::kIsAuthorizedToParseNamespaceElement);
uassert(ErrorCodes::InvalidNamespace,
"Failed to parse namespace element",
@@ -257,6 +294,8 @@ bool AuthorizationSessionImpl::isAuthorizedToParseNamespaceElement(const BSONEle
bool AuthorizationSessionImpl::isAuthorizedToParseNamespaceElement(
const NamespaceStringOrUUID& nss) {
+ _contract.addAccessCheck(AccessCheckEnum::kIsAuthorizedToParseNamespaceElement);
+
if (nss.uuid()) {
return isAuthorizedForActionsOnResource(ResourcePattern::forClusterResource(),
ActionType::useUUID);
@@ -265,6 +304,8 @@ bool AuthorizationSessionImpl::isAuthorizedToParseNamespaceElement(
}
bool AuthorizationSessionImpl::isAuthorizedToCreateRole(const RoleName& roleName) {
+ _contract.addAccessCheck(AccessCheckEnum::kIsAuthorizedToCreateRole);
+
// A user is allowed to create a role under either of two conditions.
// The user may create a role if the authorization system says they are allowed to.
@@ -397,6 +438,8 @@ static int buildResourceSearchList(const ResourcePattern& target,
bool AuthorizationSessionImpl::isAuthorizedToChangeAsUser(const UserName& userName,
ActionType actionType) {
+ _contract.addAccessCheck(AccessCheckEnum::kIsAuthorizedToChangeAsUser);
+
User* user = lookupUser(userName);
if (!user) {
return false;
@@ -414,6 +457,8 @@ bool AuthorizationSessionImpl::isAuthorizedToChangeAsUser(const UserName& userNa
StatusWith<PrivilegeVector> AuthorizationSessionImpl::checkAuthorizedToListCollections(
StringData dbname, const BSONObj& cmdObj) {
+ _contract.addAccessCheck(AccessCheckEnum::kCheckAuthorizedToListCollections);
+
if (cmdObj["authorizedCollections"].trueValue() && cmdObj["nameOnly"].trueValue() &&
AuthorizationSessionImpl::isAuthorizedForAnyActionOnAnyResourceInDB(dbname)) {
return PrivilegeVector();
@@ -431,6 +476,8 @@ StatusWith<PrivilegeVector> AuthorizationSessionImpl::checkAuthorizedToListColle
}
bool AuthorizationSessionImpl::isAuthenticatedAsUserWithRole(const RoleName& roleName) {
+ _contract.addAccessCheck(AccessCheckEnum::kIsAuthenticatedAsUserWithRole);
+
for (UserSet::iterator it = _authenticatedUsers.begin(); it != _authenticatedUsers.end();
++it) {
if ((*it)->hasRole(roleName)) {
@@ -441,10 +488,14 @@ bool AuthorizationSessionImpl::isAuthenticatedAsUserWithRole(const RoleName& rol
}
bool AuthorizationSessionImpl::shouldIgnoreAuthChecks() {
+ _contract.addAccessCheck(AccessCheckEnum::kShouldIgnoreAuthChecks);
+
return _externalState->shouldIgnoreAuthChecks();
}
bool AuthorizationSessionImpl::isAuthenticated() {
+ _contract.addAccessCheck(AccessCheckEnum::kIsAuthenticated);
+
return _authenticatedUsers.begin() != _authenticatedUsers.end();
}
@@ -550,6 +601,8 @@ void AuthorizationSessionImpl::_buildAuthenticatedRolesVector() {
}
bool AuthorizationSessionImpl::isAuthorizedForAnyActionOnAnyResourceInDB(StringData db) {
+ _contract.addAccessCheck(AccessCheckEnum::kIsAuthorizedForAnyActionOnAnyResourceInDB);
+
if (_externalState->shouldIgnoreAuthChecks()) {
return true;
}
@@ -595,6 +648,8 @@ bool AuthorizationSessionImpl::isAuthorizedForAnyActionOnAnyResourceInDB(StringD
}
bool AuthorizationSessionImpl::isAuthorizedForAnyActionOnResource(const ResourcePattern& resource) {
+ _contract.addAccessCheck(AccessCheckEnum::kIsAuthorizedForAnyActionOnResource);
+
if (_externalState->shouldIgnoreAuthChecks()) {
return true;
}
@@ -616,6 +671,8 @@ bool AuthorizationSessionImpl::isAuthorizedForAnyActionOnResource(const Resource
bool AuthorizationSessionImpl::_isAuthorizedForPrivilege(const Privilege& privilege) {
+ _contract.addPrivilege(privilege);
+
const ResourcePattern& target(privilege.getResourcePattern());
ResourcePattern resourceSearchList[resourceSearchListCapacity];
@@ -623,7 +680,7 @@ bool AuthorizationSessionImpl::_isAuthorizedForPrivilege(const Privilege& privil
ActionSet unmetRequirements = privilege.getActions();
- PrivilegeVector defaultPrivileges = getDefaultPrivileges();
+ PrivilegeVector defaultPrivileges = _getDefaultPrivileges();
for (PrivilegeVector::iterator it = defaultPrivileges.begin(); it != defaultPrivileges.end();
++it) {
for (int i = 0; i < resourceSearchListLength; ++i) {
@@ -660,6 +717,7 @@ void AuthorizationSessionImpl::setImpersonatedUserData(const std::vector<UserNam
}
bool AuthorizationSessionImpl::isCoauthorizedWithClient(Client* opClient, WithLock opClientLock) {
+ _contract.addAccessCheck(AccessCheckEnum::kIsCoauthorizedWithClient);
auto getUserNames = [](AuthorizationSession* authSession) {
if (authSession->isImpersonating()) {
return authSession->getImpersonatedUserNames();
@@ -684,6 +742,7 @@ bool AuthorizationSessionImpl::isCoauthorizedWithClient(Client* opClient, WithLo
}
bool AuthorizationSessionImpl::isCoauthorizedWith(UserNameIterator userNameIter) {
+ _contract.addAccessCheck(AccessCheckEnum::kIsCoauthorizedWith);
if (!getAuthorizationManager().isAuthEnabled()) {
return true;
}
@@ -706,14 +765,20 @@ bool AuthorizationSessionImpl::isCoauthorizedWith(UserNameIterator userNameIter)
}
UserNameIterator AuthorizationSessionImpl::getImpersonatedUserNames() {
+ _contract.addAccessCheck(AccessCheckEnum::kGetImpersonatedUserNames);
+
return makeUserNameIterator(_impersonatedUserNames.begin(), _impersonatedUserNames.end());
}
RoleNameIterator AuthorizationSessionImpl::getImpersonatedRoleNames() {
+ _contract.addAccessCheck(AccessCheckEnum::kGetImpersonatedRoleNames);
+
return makeRoleNameIterator(_impersonatedRoleNames.begin(), _impersonatedRoleNames.end());
}
bool AuthorizationSessionImpl::isUsingLocalhostBypass() {
+ _contract.addAccessCheck(AccessCheckEnum::kIsUsingLocalhostBypass);
+
return getAuthorizationManager().isAuthEnabled() && _externalState->shouldAllowLocalhost();
}
@@ -732,6 +797,8 @@ bool AuthorizationSessionImpl::isImpersonating() const {
auto AuthorizationSessionImpl::checkCursorSessionPrivilege(
OperationContext* const opCtx, const boost::optional<LogicalSessionId> cursorSessionId)
-> Status {
+ _contract.addAccessCheck(AccessCheckEnum::kCheckCursorSessionPrivilege);
+
auto nobodyIsLoggedIn = [authSession = this] { return !authSession->isAuthenticated(); };
auto authHasImpersonatePrivilege = [authSession = this] {
@@ -777,4 +844,38 @@ auto AuthorizationSessionImpl::checkCursorSessionPrivilege(
return Status::OK();
}
+void AuthorizationSessionImpl::verifyContract(const AuthorizationContract* contract) const {
+ if (contract == nullptr) {
+ return;
+ }
+
+ if (!checkContracts()) {
+ return;
+ }
+
+ // Make a mutable copy so that the common auth checks can be added.
+ auto tempContract = *contract;
+
+ // Certain access checks are done by code common to all commands.
+ //
+ // The first two checks are done by initializeOperationSessionInfo
+ tempContract.addAccessCheck(AccessCheckEnum::kIsUsingLocalhostBypass);
+ tempContract.addAccessCheck(AccessCheckEnum::kIsAuthenticated);
+
+ // These checks are done by auditing
+ tempContract.addAccessCheck(AccessCheckEnum::kGetAuthenticatedRoleNames);
+ tempContract.addAccessCheck(AccessCheckEnum::kGetAuthenticatedUserNames);
+ tempContract.addAccessCheck(AccessCheckEnum::kGetImpersonatedUserNames);
+ tempContract.addAccessCheck(AccessCheckEnum::kGetImpersonatedRoleNames);
+
+ // "internal" comes from readRequestMetadata and sharded clusters
+ // "advanceClusterTime" is an implicit check in clusters in metadata handling
+ tempContract.addPrivilege(Privilege(ResourcePattern::forClusterResource(),
+ {ActionType::advanceClusterTime, ActionType::internal}));
+
+ uassert(5452401,
+ "Authorization Session contains more authorization checks then permitted by contract.",
+ tempContract.contains(_contract));
+}
+
} // namespace mongo
diff --git a/src/mongo/db/auth/authorization_session_impl.h b/src/mongo/db/auth/authorization_session_impl.h
index fa1848680e9..8887a06fc4b 100644
--- a/src/mongo/db/auth/authorization_session_impl.h
+++ b/src/mongo/db/auth/authorization_session_impl.h
@@ -35,6 +35,7 @@
#include "mongo/base/status.h"
#include "mongo/db/auth/action_set.h"
#include "mongo/db/auth/action_type.h"
+#include "mongo/db/auth/authorization_contract.h"
#include "mongo/db/auth/authorization_session.h"
#include "mongo/db/auth/authz_session_external_state.h"
#include "mongo/db/auth/privilege.h"
@@ -74,6 +75,8 @@ public:
void startRequest(OperationContext* opCtx) override;
+ void startContractTracking() override;
+
Status addAndAuthorizeUser(OperationContext* opCtx, const UserName& userName) override;
User* lookupUser(const UserName& name) override;
@@ -95,8 +98,6 @@ public:
void grantInternalAuthorization(OperationContext* opCtx) override;
- PrivilegeVector getDefaultPrivileges() override;
-
StatusWith<PrivilegeVector> checkAuthorizedToListCollections(StringData dbname,
const BSONObj& cmdObj) override;
@@ -149,6 +150,8 @@ public:
Status checkCursorSessionPrivilege(OperationContext* const opCtx,
boost::optional<LogicalSessionId> cursorSessionId) override;
+ void verifyContract(const AuthorizationContract* contract) const override;
+
protected:
// Builds a vector of all roles held by users who are authenticated on this connection. The
// vector is stored in _authenticatedRoleNames. This function is called when users are
@@ -177,6 +180,15 @@ private:
return std::make_tuple(&_impersonatedUserNames, &_impersonatedRoleNames);
}
+
+ // Generates a vector of default privileges that are granted to any user,
+ // regardless of which roles that user does or does not possess.
+ // If localhost exception is active, the permissions include the ability to create
+ // the first user and the ability to run the commands needed to bootstrap the system
+ // into a state where the first user can be created.
+ PrivilegeVector _getDefaultPrivileges();
+
+private:
std::unique_ptr<AuthzSessionExternalState> _externalState;
// A vector of impersonated UserNames and a vector of those users' RoleNames.
@@ -184,5 +196,11 @@ private:
std::vector<UserName> _impersonatedUserNames;
std::vector<RoleName> _impersonatedRoleNames;
bool _impersonationFlag;
+
+ // A record of privilege checks and other authorization like function calls made on
+ // AuthorizationSession. IDL Typed Commands can optionally define a contract declaring the set
+ // of authorization checks they perform. After a command completes running, MongoDB verifies the
+ // set of checks performed is a subset of the checks declared in the contract.
+ AuthorizationContract _contract;
};
} // namespace mongo
diff --git a/src/mongo/db/auth/authorization_session_test.cpp b/src/mongo/db/auth/authorization_session_test.cpp
index 50f348d991f..689f3642e9e 100644
--- a/src/mongo/db/auth/authorization_session_test.cpp
+++ b/src/mongo/db/auth/authorization_session_test.cpp
@@ -50,6 +50,7 @@
#include "mongo/db/operation_context.h"
#include "mongo/db/pipeline/aggregation_request_helper.h"
#include "mongo/db/service_context_test_fixture.h"
+#include "mongo/idl/server_parameter_test_util.h"
#include "mongo/transport/session.h"
#include "mongo/transport/transport_layer_mock.h"
#include "mongo/unittest/unittest.h"
@@ -125,6 +126,7 @@ protected:
AuthorizationManager* authzManager;
std::unique_ptr<AuthorizationSessionForTest> authzSession;
BSONObj credentials;
+ RAIIServerParameterControllerForTest controller{"featureFlagAuthorizationContract", 1};
};
const NamespaceString testFooNss("test.foo");
@@ -238,6 +240,26 @@ TEST_F(AuthorizationSessionTest, AddUserAndCheckAuthorization) {
authzSession->isAuthorizedForActionsOnResource(testFooCollResource, ActionType::insert));
ASSERT_FALSE(
authzSession->isAuthorizedForActionsOnResource(testFooCollResource, ActionType::collMod));
+
+ // Verify we recorded the all the auth checks correctly
+ AuthorizationContract ac(
+ std::initializer_list<AccessCheckEnum>{},
+ std::initializer_list<Privilege>{
+ Privilege(ResourcePattern::forDatabaseName("ignored"),
+ {ActionType::insert, ActionType::dbStats}),
+ Privilege(ResourcePattern::forExactNamespace(NamespaceString("ignored.ignored")),
+ {ActionType::insert, ActionType::collMod}),
+ });
+
+ authzSession->verifyContract(&ac);
+
+ // Verify against a smaller contract that verifyContract fails
+ AuthorizationContract acMissing(std::initializer_list<AccessCheckEnum>{},
+ std::initializer_list<Privilege>{
+ Privilege(ResourcePattern::forDatabaseName("ignored"),
+ {ActionType::insert, ActionType::dbStats}),
+ });
+ ASSERT_THROWS_CODE(authzSession->verifyContract(&acMissing), AssertionException, 5452401);
}
TEST_F(AuthorizationSessionTest, DuplicateRolesOK) {
@@ -1343,6 +1365,15 @@ TEST_F(AuthorizationSessionTest, CanUseUUIDNamespacesWithPrivilege) {
ASSERT_THROWS_CODE(authzSession->isAuthorizedToParseNamespaceElement(invalidObj.firstElement()),
AssertionException,
ErrorCodes::InvalidNamespace);
+
+ // Verify we recorded the all the auth checks correctly
+ AuthorizationContract ac(
+ std::initializer_list<AccessCheckEnum>{
+ AccessCheckEnum::kIsAuthorizedToParseNamespaceElement},
+ std::initializer_list<Privilege>{
+ Privilege(ResourcePattern::forClusterResource(), ActionType::useUUID)});
+
+ authzSession->verifyContract(&ac);
}
diff --git a/src/mongo/db/auth/impersonation_session.h b/src/mongo/db/auth/impersonation_session.h
index bab75018f8b..9c15797b364 100644
--- a/src/mongo/db/auth/impersonation_session.h
+++ b/src/mongo/db/auth/impersonation_session.h
@@ -44,6 +44,10 @@ public:
ImpersonationSessionGuard(OperationContext* opCtx);
~ImpersonationSessionGuard();
+ bool isActive() const {
+ return _active;
+ }
+
private:
rpc::MaybeImpersonatedUserMetadata _oldImpersonationData;
OperationContext* _opCtx;