summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorGabriel Marks <gabriel.marks@mongodb.com>2022-02-10 18:34:23 +0000
committerEvergreen Agent <no-reply@evergreen.mongodb.com>2022-02-10 21:19:11 +0000
commit26292d02824c0e804eea510302019add57b6cccc (patch)
treef8f0668fcc0be5e990cf8818f8d9fac36894032c
parent7ddbbd244cb5f42f2f551d237e0a4523d91e948f (diff)
downloadmongo-26292d02824c0e804eea510302019add57b6cccc.tar.gz
SERVER-63174 Add AuthorizationSession::mayBypassWriteBlockingMode()
-rw-r--r--src/mongo/db/auth/action_type.idl1
-rw-r--r--src/mongo/db/auth/authorization_session.h4
-rw-r--r--src/mongo/db/auth/authorization_session_for_test.cpp2
-rw-r--r--src/mongo/db/auth/authorization_session_impl.cpp63
-rw-r--r--src/mongo/db/auth/authorization_session_impl.h18
-rw-r--r--src/mongo/db/auth/authorization_session_test.cpp62
-rw-r--r--src/mongo/db/auth/authz_session_external_state_server_common.cpp3
-rw-r--r--src/mongo/db/auth/builtin_roles.cpp7
-rw-r--r--src/mongo/db/dollar_tenant_decoration_test.cpp2
-rw-r--r--src/mongo/embedded/embedded_auth_session.cpp4
10 files changed, 135 insertions, 31 deletions
diff --git a/src/mongo/db/auth/action_type.idl b/src/mongo/db/auth/action_type.idl
index c5acfbfd528..b861ff4d78e 100644
--- a/src/mongo/db/auth/action_type.idl
+++ b/src/mongo/db/auth/action_type.idl
@@ -54,6 +54,7 @@ enums:
authenticate : "authenticate" # ID only
authSchemaUpgrade : "authSchemaUpgrade"
bypassDocumentValidation : "bypassDocumentValidation"
+ bypassWriteBlockingMode : "bypassWriteBlockingMode"
changeCustomData : "changeCustomData"
changePassword : "changePassword"
changeOwnPassword : "changeOwnPassword"
diff --git a/src/mongo/db/auth/authorization_session.h b/src/mongo/db/auth/authorization_session.h
index b23fbfcf171..b6cab1ae951 100644
--- a/src/mongo/db/auth/authorization_session.h
+++ b/src/mongo/db/auth/authorization_session.h
@@ -303,6 +303,10 @@ public:
// Verify the authorization contract. If contract == nullptr, no check is performed.
virtual void verifyContract(const AuthorizationContract* contract) const = 0;
+ // Returns true if any user has the privilege to bypass write blocking mode for the cluster
+ // resource.
+ virtual bool mayBypassWriteBlockingMode() const = 0;
+
protected:
virtual std::tuple<std::vector<UserName>*, std::vector<RoleName>*> _getImpersonations() = 0;
};
diff --git a/src/mongo/db/auth/authorization_session_for_test.cpp b/src/mongo/db/auth/authorization_session_for_test.cpp
index 9d2eacbd121..2bf9fae9e2b 100644
--- a/src/mongo/db/auth/authorization_session_for_test.cpp
+++ b/src/mongo/db/auth/authorization_session_for_test.cpp
@@ -58,7 +58,7 @@ void AuthorizationSessionForTest::assumePrivilegesForDB(PrivilegeVector privileg
_authenticatedUsers.add(userHandle);
_testUsers.emplace_back(std::move(userHandle));
- _buildAuthenticatedRolesVector();
+ _updateInternalAuthorizationState();
}
diff --git a/src/mongo/db/auth/authorization_session_impl.cpp b/src/mongo/db/auth/authorization_session_impl.cpp
index e8b2f908556..70fa0e11309 100644
--- a/src/mongo/db/auth/authorization_session_impl.cpp
+++ b/src/mongo/db/auth/authorization_session_impl.cpp
@@ -151,11 +151,16 @@ void validateSecurityTokenUserPrivileges(const User::ResourcePrivilegeMap& privs
}
MONGO_FAIL_POINT_DEFINE(allowMultipleUsersWithApiStrict);
+
+const Privilege kBypassWriteBlockingModeOnClusterPrivilege(ResourcePattern::forClusterResource(),
+ ActionType::bypassWriteBlockingMode);
} // namespace
AuthorizationSessionImpl::AuthorizationSessionImpl(
std::unique_ptr<AuthzSessionExternalState> externalState, InstallMockForTestingOrAuthImpl)
- : _externalState(std::move(externalState)), _impersonationFlag(false) {}
+ : _externalState(std::move(externalState)),
+ _impersonationFlag(false),
+ _mayBypassWriteBlockingMode(false) {}
AuthorizationSessionImpl::~AuthorizationSessionImpl() {
invariant(_authenticatedUsers.count() == 0,
@@ -179,7 +184,7 @@ void AuthorizationSessionImpl::startRequest(OperationContext* opCtx) {
"security token based user still authenticated at start of request, "
"clearing from authentication state",
"user"_attr = users.getNames().get().toBSON(true /* encode tenant */));
- _buildAuthenticatedRolesVector();
+ _updateInternalAuthorizationState();
}
_authenticationMode = AuthenticationMode::kNone;
}
@@ -278,7 +283,7 @@ Status AuthorizationSessionImpl::addAndAuthorizeUser(OperationContext* opCtx,
// If there are any users and roles in the impersonation data, clear it out.
clearImpersonatedUserData();
- _buildAuthenticatedRolesVector();
+ _updateInternalAuthorizationState();
return Status::OK();
} catch (const DBException& ex) {
@@ -332,7 +337,7 @@ void AuthorizationSessionImpl::logoutSecurityTokenUser(Client* client) {
// Explicitly skip auditing the logout event,
// security tokens don't represent a permanent login.
clearImpersonatedUserData();
- _buildAuthenticatedRolesVector();
+ _updateInternalAuthorizationState();
}
void AuthorizationSessionImpl::logoutAllDatabases(Client* client, StringData reason) {
@@ -350,7 +355,7 @@ void AuthorizationSessionImpl::logoutAllDatabases(Client* client, StringData rea
audit::logLogout(client, reason, users.toBSON(), BSONArray());
clearImpersonatedUserData();
- _buildAuthenticatedRolesVector();
+ _updateInternalAuthorizationState();
}
@@ -372,7 +377,7 @@ void AuthorizationSessionImpl::logoutDatabase(Client* client,
std::swap(_authenticatedUsers, updatedUsers);
clearImpersonatedUserData();
- _buildAuthenticatedRolesVector();
+ _updateInternalAuthorizationState();
}
UserNameIterator AuthorizationSessionImpl::getAuthenticatedUserNames() {
@@ -390,7 +395,7 @@ RoleNameIterator AuthorizationSessionImpl::getAuthenticatedRoleNames() {
void AuthorizationSessionImpl::grantInternalAuthorization(Client* client) {
stdx::lock_guard<Client> lk(*client);
_authenticatedUsers.add(*internalSecurity.getUser());
- _buildAuthenticatedRolesVector();
+ _updateInternalAuthorizationState();
}
/**
@@ -788,22 +793,7 @@ void AuthorizationSessionImpl::_refreshUserInfoAsNeeded(OperationContext* opCtx)
++it;
}
- _buildAuthenticatedRolesVector();
-}
-
-void AuthorizationSessionImpl::_buildAuthenticatedRolesVector() {
- _authenticatedRoleNames.clear();
- for (const auto& userHandle : _authenticatedUsers) {
- RoleNameIterator roles = userHandle->getIndirectRoles();
- while (roles.more()) {
- RoleName roleName = roles.next();
- _authenticatedRoleNames.push_back(RoleName(roleName.getRole(), roleName.getDB()));
- }
- }
-
- if (_authenticatedUsers.count() == 0) {
- _authenticationMode = AuthenticationMode::kNone;
- }
+ _updateInternalAuthorizationState();
}
bool AuthorizationSessionImpl::isAuthorizedForAnyActionOnAnyResourceInDB(StringData db) {
@@ -1104,9 +1094,36 @@ void AuthorizationSessionImpl::verifyContract(const AuthorizationContract* contr
tempContract.addPrivilege(Privilege(ResourcePattern::forClusterResource(),
{ActionType::advanceClusterTime, ActionType::internal}));
+ // Implicitly checked often to keep mayBypassWriteBlockingMode() fast
+ tempContract.addPrivilege(kBypassWriteBlockingModeOnClusterPrivilege);
+
uassert(5452401,
"Authorization Session contains more authorization checks then permitted by contract.",
tempContract.contains(_contract));
}
+void AuthorizationSessionImpl::_updateInternalAuthorizationState() {
+ // Update the authenticated role names vector to reflect current state.
+ _authenticatedRoleNames.clear();
+ for (const auto& userHandle : _authenticatedUsers) {
+ RoleNameIterator roles = userHandle->getIndirectRoles();
+ while (roles.more()) {
+ RoleName roleName = roles.next();
+ _authenticatedRoleNames.push_back(RoleName(roleName.getRole(), roleName.getDB()));
+ }
+ }
+
+ if (_authenticatedUsers.count() == 0) {
+ _authenticationMode = AuthenticationMode::kNone;
+ }
+
+ // Update cached _mayBypassWriteBlockingMode to reflect current state.
+ _mayBypassWriteBlockingMode =
+ _isAuthorizedForPrivilege(kBypassWriteBlockingModeOnClusterPrivilege);
+}
+
+bool AuthorizationSessionImpl::mayBypassWriteBlockingMode() const {
+ return MONGO_unlikely(_mayBypassWriteBlockingMode);
+}
+
} // namespace mongo
diff --git a/src/mongo/db/auth/authorization_session_impl.h b/src/mongo/db/auth/authorization_session_impl.h
index 77b9b253f80..eefbcbfabe5 100644
--- a/src/mongo/db/auth/authorization_session_impl.h
+++ b/src/mongo/db/auth/authorization_session_impl.h
@@ -157,13 +157,21 @@ public:
void verifyContract(const AuthorizationContract* contract) const override;
+ bool mayBypassWriteBlockingMode() const override;
+
protected:
friend class AuthorizationSessionImplTestHelper;
- // 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
- // logged in or logged out, as well as when the user cache is determined to be out of date.
- void _buildAuthenticatedRolesVector();
+ // Updates internal cached authorization state, i.e.:
+ // - _mayBypassWriteBlockingMode, reflecting whether the connection is authorized for the
+ // privilege of bypassing write blocking mode on cluster resource.
+ // - _authenticatedRoleNames, which stores all roles held by users who are authenticated on this
+ // connection.
+ // - _authenticationMode -- we just update this to None if there are no users on the connection.
+ // This function is called whenever the user state changes to keep the internal state up to
+ // date.
+ void _updateInternalAuthorizationState();
+
// All Users who have been authenticated on this connection.
UserSet _authenticatedUsers;
@@ -212,5 +220,7 @@ private:
// 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;
+
+ bool _mayBypassWriteBlockingMode;
};
} // namespace mongo
diff --git a/src/mongo/db/auth/authorization_session_test.cpp b/src/mongo/db/auth/authorization_session_test.cpp
index a6b54fc11f7..51633ca828c 100644
--- a/src/mongo/db/auth/authorization_session_test.cpp
+++ b/src/mongo/db/auth/authorization_session_test.cpp
@@ -1631,5 +1631,67 @@ TEST_F(SystemBucketsTest, CanCheckIfHasAnyPrivilegeInResourceDBForSystemBuckets)
ASSERT_TRUE(authzSession->isAuthorizedForAnyActionOnAnyResourceInDB(sb_db_other));
}
+TEST_F(AuthorizationSessionTest, MayBypassWriteBlockingModeIsSetCorrectly) {
+ ASSERT_FALSE(authzSession->mayBypassWriteBlockingMode());
+
+ // Add a user without the restore role and ensure we can't bypass
+ ASSERT_OK(managerState->insertPrivilegeDocument(_opCtx.get(),
+ BSON("user"
+ << "spencer"
+ << "db"
+ << "test"
+ << "credentials" << credentials << "roles"
+ << BSON_ARRAY(BSON("role"
+ << "readWrite"
+ << "db"
+ << "test"))),
+ BSONObj()));
+ ASSERT_OK(authzSession->addAndAuthorizeUser(_opCtx.get(), UserName("spencer", "test")));
+ ASSERT_FALSE(authzSession->mayBypassWriteBlockingMode());
+
+ // Add a user with restore role on admin db and ensure we can bypass
+ ASSERT_OK(managerState->insertPrivilegeDocument(_opCtx.get(),
+ BSON("user"
+ << "gmarks"
+ << "db"
+ << "admin"
+ << "credentials" << credentials << "roles"
+ << BSON_ARRAY(BSON("role"
+ << "restore"
+ << "db"
+ << "admin"))),
+ BSONObj()));
+ ASSERT_OK(authzSession->addAndAuthorizeUser(_opCtx.get(), UserName("gmarks", "admin")));
+ ASSERT_TRUE(authzSession->mayBypassWriteBlockingMode());
+
+ // Remove that user by logging out of the admin db and ensure we can't bypass anymore
+ authzSession->logoutDatabase(_client.get(), "admin", "");
+ ASSERT_FALSE(authzSession->mayBypassWriteBlockingMode());
+
+ // Add a user with the root role, which should confer restore role for cluster resource, and
+ // ensure we can bypass
+ ASSERT_OK(managerState->insertPrivilegeDocument(_opCtx.get(),
+ BSON("user"
+ << "admin"
+ << "db"
+ << "admin"
+ << "credentials" << credentials << "roles"
+ << BSON_ARRAY(BSON("role"
+ << "root"
+ << "db"
+ << "admin"))),
+ BSONObj()));
+ ASSERT_OK(authzSession->addAndAuthorizeUser(_opCtx.get(), UserName("admin", "admin")));
+ ASSERT_TRUE(authzSession->mayBypassWriteBlockingMode());
+
+ // Remove non-privileged user by logging out of test db and ensure we can still bypass
+ authzSession->logoutDatabase(_client.get(), "test", "");
+ ASSERT_TRUE(authzSession->mayBypassWriteBlockingMode());
+
+ // Remove privileged user by logging out of admin db and ensure we cannot bypass
+ authzSession->logoutDatabase(_client.get(), "admin", "");
+ ASSERT_FALSE(authzSession->mayBypassWriteBlockingMode());
+}
+
} // namespace
} // namespace mongo
diff --git a/src/mongo/db/auth/authz_session_external_state_server_common.cpp b/src/mongo/db/auth/authz_session_external_state_server_common.cpp
index 024afd07a48..8db1a60b36b 100644
--- a/src/mongo/db/auth/authz_session_external_state_server_common.cpp
+++ b/src/mongo/db/auth/authz_session_external_state_server_common.cpp
@@ -82,6 +82,9 @@ bool AuthzSessionExternalStateServerCommon::serverIsArbiter() const {
}
bool AuthzSessionExternalStateServerCommon::shouldAllowLocalhost() const {
+ if (!haveClient()) {
+ return false;
+ }
Client* client = Client::getCurrent();
return _allowLocalhost && client->getIsLocalHostConnection();
}
diff --git a/src/mongo/db/auth/builtin_roles.cpp b/src/mongo/db/auth/builtin_roles.cpp
index 7cc84ba4c9d..9cc88c20cb3 100644
--- a/src/mongo/db/auth/builtin_roles.cpp
+++ b/src/mongo/db/auth/builtin_roles.cpp
@@ -674,11 +674,14 @@ void addRestorePrivileges(PrivilegeVector* privileges) {
ResourcePattern::forExactNamespace(AuthorizationManager::rolesCollectionNamespace),
ActionType::createIndex));
- // Need to be able to force UUID consistency in sharded restores
Privilege::addPrivilegeToPrivilegeVector(
privileges,
Privilege(ResourcePattern::forClusterResource(),
- {ActionType::forceUUID, ActionType::useUUID}));
+ {// Need to be able to force UUID consistency in sharded restores
+ ActionType::forceUUID,
+ ActionType::useUUID,
+ // Need to be able to bypass write blocking mode for C2C replication
+ ActionType::bypassWriteBlockingMode}));
}
void addRootRolePrivileges(PrivilegeVector* privileges) {
diff --git a/src/mongo/db/dollar_tenant_decoration_test.cpp b/src/mongo/db/dollar_tenant_decoration_test.cpp
index 25bb3c4b956..c19a3415139 100644
--- a/src/mongo/db/dollar_tenant_decoration_test.cpp
+++ b/src/mongo/db/dollar_tenant_decoration_test.cpp
@@ -58,7 +58,7 @@ public:
auto* as =
dynamic_cast<AuthorizationSessionImpl*>(AuthorizationSession::get(opCtx->getClient()));
as->_authenticatedUsers.add(std::move(user));
- as->_buildAuthenticatedRolesVector();
+ as->_updateInternalAuthorizationState();
}
};
diff --git a/src/mongo/embedded/embedded_auth_session.cpp b/src/mongo/embedded/embedded_auth_session.cpp
index 9b7f07c1658..9c9e2899c24 100644
--- a/src/mongo/embedded/embedded_auth_session.cpp
+++ b/src/mongo/embedded/embedded_auth_session.cpp
@@ -223,6 +223,10 @@ public:
UASSERT_NOT_IMPLEMENTED;
}
+ bool mayBypassWriteBlockingMode() const override {
+ return true;
+ }
+
protected:
std::tuple<std::vector<UserName>*, std::vector<RoleName>*> _getImpersonations() override {
UASSERT_NOT_IMPLEMENTED;