diff options
Diffstat (limited to 'src/mongo/db')
-rw-r--r-- | src/mongo/db/auth/action_type.idl | 1 | ||||
-rw-r--r-- | src/mongo/db/auth/authorization_session.h | 4 | ||||
-rw-r--r-- | src/mongo/db/auth/authorization_session_for_test.cpp | 2 | ||||
-rw-r--r-- | src/mongo/db/auth/authorization_session_impl.cpp | 63 | ||||
-rw-r--r-- | src/mongo/db/auth/authorization_session_impl.h | 18 | ||||
-rw-r--r-- | src/mongo/db/auth/authorization_session_test.cpp | 62 | ||||
-rw-r--r-- | src/mongo/db/auth/authz_session_external_state_server_common.cpp | 3 | ||||
-rw-r--r-- | src/mongo/db/auth/builtin_roles.cpp | 7 | ||||
-rw-r--r-- | src/mongo/db/dollar_tenant_decoration_test.cpp | 2 |
9 files changed, 131 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(); } }; |