diff options
author | auto-revert-processor <dev-prod-dag@mongodb.com> | 2022-11-14 10:04:23 +0000 |
---|---|---|
committer | Evergreen Agent <no-reply@evergreen.mongodb.com> | 2022-11-14 10:35:57 +0000 |
commit | 16bf50d195697115e1ccf5dcb9c87a826643c613 (patch) | |
tree | c520affe5b7f8572c413f5633472e0bd0b7981dc | |
parent | 2bfdf69af4ab1b08b07e757a61668cf9fe620fb4 (diff) | |
download | mongo-16bf50d195697115e1ccf5dcb9c87a826643c613.tar.gz |
Revert "SERVER-70700 Use UserRequest to in authorization workflow"
This reverts commit 8728459da343c79cc2f8157856a5b8e03c1bfdf1.
33 files changed, 205 insertions, 242 deletions
diff --git a/src/mongo/db/auth/authorization_manager.h b/src/mongo/db/auth/authorization_manager.h index 0f4f89e24dc..69077aebe6a 100644 --- a/src/mongo/db/auth/authorization_manager.h +++ b/src/mongo/db/auth/authorization_manager.h @@ -305,14 +305,14 @@ public: std::vector<BSONObj>* result) = 0; /** - * Returns a Status or UserHandle for the given userRequest. If the user cache already has a + * Returns a Status or UserHandle for the given userName. If the user cache already has a * user object for this user, it returns a handle from the cache, otherwise it reads the - * user document from the AuthzManagerExternalState - this may block for a long time. + * user document from disk or LDAP - this may block for a long time. * * The returned user may be invalid by the time the caller gets access to it. */ virtual StatusWith<UserHandle> acquireUser(OperationContext* opCtx, - const UserRequest& userRequest) = 0; + const UserName& userName) = 0; /** * Validate the ID associated with a known user while refreshing session cache. diff --git a/src/mongo/db/auth/authorization_manager_impl.cpp b/src/mongo/db/auth/authorization_manager_impl.cpp index 9a50ec921ab..db1081b8bf3 100644 --- a/src/mongo/db/auth/authorization_manager_impl.cpp +++ b/src/mongo/db/auth/authorization_manager_impl.cpp @@ -68,8 +68,7 @@ namespace mongo { namespace { std::shared_ptr<UserHandle> createSystemUserHandle() { - UserRequest request(UserName("__system", "local"), boost::none); - auto user = std::make_shared<UserHandle>(User(std::move(request))); + auto user = std::make_shared<UserHandle>(User(UserName("__system", "local"))); ActionSet allActions; allActions.addAllActions(); @@ -285,6 +284,29 @@ bool appliesToAuthzData(StringData op, const NamespaceString& nss, const BSONObj } } +/** + * Returns true if roles for this user were provided by the client, and can be obtained from + * the connection. + */ +bool shouldUseRolesFromConnection(OperationContext* opCtx, const UserName& userName) { +#ifdef MONGO_CONFIG_SSL + if (!opCtx || !opCtx->getClient() || !opCtx->getClient()->session()) { + return false; + } + + if (!allowRolesFromX509Certificates) { + return false; + } + + auto& sslPeerInfo = SSLPeerInfo::forSession(opCtx->getClient()->session()); + return sslPeerInfo.subjectName.toString() == userName.getUser() && + userName.getDB() == "$external"_sd && !sslPeerInfo.roles.empty(); +#else + return false; +#endif +} + + std::unique_ptr<AuthorizationManager> authorizationManagerCreateImpl( ServiceContext* serviceContext) { return std::make_unique<AuthorizationManagerImpl>(serviceContext, @@ -472,17 +494,29 @@ MONGO_FAIL_POINT_DEFINE(authUserCacheSleep); } // namespace StatusWith<UserHandle> AuthorizationManagerImpl::acquireUser(OperationContext* opCtx, - const UserRequest& request) try { - const auto& userName = request.name; - + const UserName& userName) try { auto systemUser = internalSecurity.getUser(); if (userName == (*systemUser)->getName()) { - uassert(ErrorCodes::OperationFailed, - "Attempted to acquire system user with predefined roles", - request.roles == boost::none); return *systemUser; } + UserRequest request(userName, boost::none); + +#ifdef MONGO_CONFIG_SSL + // Clients connected via TLS may present an X.509 certificate which contains an authorization + // grant. If this is the case, the roles must be provided to the external state, for expansion + // into privileges. + if (shouldUseRolesFromConnection(opCtx, userName)) { + auto& sslPeerInfo = SSLPeerInfo::forSession(opCtx->getClient()->session()); + request.roles = std::set<RoleName>(); + + // In order to be hashable, the role names must be converted from unordered_set to a set. + std::copy(sslPeerInfo.roles.begin(), + sslPeerInfo.roles.end(), + std::inserter(*request.roles, request.roles->begin())); + } +#endif + if (authUserCacheBypass.shouldFail()) { // Bypass cache and force a fresh load of the user. auto loadedUser = uassertStatusOK(_externalState->getUserObject(opCtx, request)); @@ -525,7 +559,7 @@ StatusWith<UserHandle> AuthorizationManagerImpl::reacquireUser(OperationContext* // Make a good faith effort to acquire an up-to-date user object, since the one // we've cached is marked "out-of-date." - auto swUserHandle = acquireUser(opCtx, user->getUserRequest()); + auto swUserHandle = acquireUser(opCtx, userName); if (!swUserHandle.isOK()) { return swUserHandle.getStatus(); } @@ -611,18 +645,6 @@ void AuthorizationManagerImpl::_pinnedUsersThreadRoutine() noexcept try { } } - // Find UserRequests for UserNames we need to pin if they exist in the cache. - std::map<UserName, UserRequest> pinNow; - _userCache.peekLatestCachedIf([&](const UserRequest& request, const User& user) { - if (std::any_of(usersToPin.begin(), usersToPin.end(), [&](const auto& userName) { - return user.getName() == userName; - })) { - pinNow.emplace(request.name, request); - } - // Don't need any output vector. - return false; - }); - for (const auto& userName : usersToPin) { if (std::any_of(pinnedUsers.begin(), pinnedUsers.end(), [&](const auto& user) { return user->getName() == userName; @@ -630,14 +652,7 @@ void AuthorizationManagerImpl::_pinnedUsersThreadRoutine() noexcept try { continue; } - auto request = ([&] { - if (auto it = pinNow.find(userName); it != pinNow.end()) { - return it->second; - } - return UserRequest(userName, boost::none); - })(); - - auto swUser = acquireUser(opCtx.get(), request); + auto swUser = acquireUser(opCtx.get(), userName); if (swUser.isOK()) { LOGV2_DEBUG(20232, 2, "Pinned user", "user"_attr = userName); diff --git a/src/mongo/db/auth/authorization_manager_impl.h b/src/mongo/db/auth/authorization_manager_impl.h index 17502fd0067..fcec7b97e32 100644 --- a/src/mongo/db/auth/authorization_manager_impl.h +++ b/src/mongo/db/auth/authorization_manager_impl.h @@ -97,8 +97,7 @@ public: bool showBuiltinRoles, std::vector<BSONObj>* result) override; - StatusWith<UserHandle> acquireUser(OperationContext* opCtx, - const UserRequest& userRequest) override; + StatusWith<UserHandle> acquireUser(OperationContext* opCtx, const UserName& userName) override; StatusWith<UserHandle> reacquireUser(OperationContext* opCtx, const UserHandle& user) override; /** diff --git a/src/mongo/db/auth/authorization_manager_test.cpp b/src/mongo/db/auth/authorization_manager_test.cpp index 3363aead426..f91f3f0a329 100644 --- a/src/mongo/db/auth/authorization_manager_test.cpp +++ b/src/mongo/db/auth/authorization_manager_test.cpp @@ -140,7 +140,7 @@ TEST_F(AuthorizationManagerTest, testAcquireV2User) { << "admin"))), BSONObj())); - auto swu = authzManager->acquireUser(opCtx.get(), {{"v2read", "test"}, boost::none}); + auto swu = authzManager->acquireUser(opCtx.get(), UserName("v2read", "test")); ASSERT_OK(swu.getStatus()); auto v2read = std::move(swu.getValue()); ASSERT_EQUALS(UserName("v2read", "test"), v2read->getName()); @@ -153,7 +153,7 @@ TEST_F(AuthorizationManagerTest, testAcquireV2User) { ASSERT(testDBPrivilege.getActions().contains(ActionType::find)); // Make sure user's refCount is 0 at the end of the test to avoid an assertion failure - swu = authzManager->acquireUser(opCtx.get(), {{"v2cluster", "admin"}, boost::none}); + swu = authzManager->acquireUser(opCtx.get(), UserName("v2cluster", "admin")); ASSERT_OK(swu.getStatus()); auto v2cluster = std::move(swu.getValue()); ASSERT_EQUALS(UserName("v2cluster", "admin"), v2cluster->getName()); @@ -169,19 +169,24 @@ TEST_F(AuthorizationManagerTest, testAcquireV2User) { #ifdef MONGO_CONFIG_SSL TEST_F(AuthorizationManagerTest, testLocalX509Authorization) { - std::set<RoleName> roles({{"read", "test"}, {"readWrite", "test"}}); - UserRequest request(UserName("CN=mongodb.com", "$external"), roles); + setX509PeerInfo(session, + SSLPeerInfo(buildX509Name(), + boost::none, + {RoleName("read", "test"), RoleName("readWrite", "test")})); - auto swu = authzManager->acquireUser(opCtx.get(), request); + auto swu = authzManager->acquireUser(opCtx.get(), UserName("CN=mongodb.com", "$external")); ASSERT_OK(swu.getStatus()); auto x509User = std::move(swu.getValue()); ASSERT(x509User.isValid()); - std::set<RoleName> gotRoles; - for (auto it = x509User->getRoles(); it.more();) { - gotRoles.emplace(it.next()); + stdx::unordered_set<RoleName> expectedRoles{RoleName("read", "test"), + RoleName("readWrite", "test")}; + RoleNameIterator roles = x509User->getRoles(); + stdx::unordered_set<RoleName> acquiredRoles; + while (roles.more()) { + acquiredRoles.insert(roles.next()); } - ASSERT_TRUE(roles == gotRoles); + ASSERT_TRUE(expectedRoles == acquiredRoles); const User::ResourcePrivilegeMap& privileges = x509User->getPrivileges(); ASSERT_FALSE(privileges.empty()); @@ -197,16 +202,14 @@ TEST_F(AuthorizationManagerTest, testLocalX509AuthorizationInvalidUser) { {RoleName("read", "test"), RoleName("write", "test")})); ASSERT_NOT_OK( - authzManager->acquireUser(opCtx.get(), {{"CN=10gen.com", "$external"}, boost::none}) - .getStatus()); + authzManager->acquireUser(opCtx.get(), UserName("CN=10gen.com", "$external")).getStatus()); } TEST_F(AuthorizationManagerTest, testLocalX509AuthenticationNoAuthorization) { setX509PeerInfo(session, {}); - ASSERT_NOT_OK( - authzManager->acquireUser(opCtx.get(), {{"CN=mongodb.com", "$external"}, boost::none}) - .getStatus()); + ASSERT_NOT_OK(authzManager->acquireUser(opCtx.get(), UserName("CN=mongodb.com", "$external")) + .getStatus()); } #endif @@ -237,7 +240,7 @@ TEST_F(AuthorizationManagerTest, testAcquireV2UserWithUnrecognizedActions) { << "insert")))), BSONObj())); - auto swu = authzManager->acquireUser(opCtx.get(), {{"myUser", "test"}, boost::none}); + auto swu = authzManager->acquireUser(opCtx.get(), UserName("myUser", "test")); ASSERT_OK(swu.getStatus()); auto myUser = std::move(swu.getValue()); ASSERT_EQUALS(UserName("myUser", "test"), myUser->getName()); @@ -314,12 +317,14 @@ TEST_F(AuthorizationManagerTest, testRefreshExternalV2User) { std::vector<UserHandle> checkedOutUsers; checkedOutUsers.reserve(userDocs.size()); for (const auto& userDoc : userDocs) { - const UserName userName(userDoc.getStringField(kUserFieldName), - userDoc.getStringField(kDbFieldName)); - auto swUser = authzManager->acquireUser(opCtx.get(), {userName, boost::none}); + auto swUser = authzManager->acquireUser( + opCtx.get(), + UserName(userDoc.getStringField(kUserFieldName), userDoc.getStringField(kDbFieldName))); ASSERT_OK(swUser.getStatus()); auto user = std::move(swUser.getValue()); - ASSERT_EQUALS(userName, user->getName()); + ASSERT_EQUALS( + UserName(userDoc.getStringField(kUserFieldName), userDoc.getStringField(kDbFieldName)), + user->getName()); ASSERT(user.isValid()); RoleNameIterator cachedUserRoles = user->getRoles(); @@ -359,12 +364,14 @@ TEST_F(AuthorizationManagerTest, testRefreshExternalV2User) { // Retrieve all users from the cache and verify that only the external ones contain the newly // added role. for (const auto& userDoc : userDocs) { - const UserName userName(userDoc.getStringField(kUserFieldName), - userDoc.getStringField(kDbFieldName)); - auto swUser = authzManager->acquireUser(opCtx.get(), {userName, boost::none}); + auto swUser = authzManager->acquireUser( + opCtx.get(), + UserName(userDoc.getStringField(kUserFieldName), userDoc.getStringField(kDbFieldName))); ASSERT_OK(swUser.getStatus()); auto user = std::move(swUser.getValue()); - ASSERT_EQUALS(userName, user->getName()); + ASSERT_EQUALS( + UserName(userDoc.getStringField(kUserFieldName), userDoc.getStringField(kDbFieldName)), + user->getName()); ASSERT(user.isValid()); RoleNameIterator cachedUserRolesIt = user->getRoles(); diff --git a/src/mongo/db/auth/authorization_session.h b/src/mongo/db/auth/authorization_session.h index 6bca9cd0192..777da1ad56d 100644 --- a/src/mongo/db/auth/authorization_session.h +++ b/src/mongo/db/auth/authorization_session.h @@ -144,7 +144,7 @@ public: * Adds the User identified by "UserName" to the authorization session, acquiring privileges * for it in the process. */ - virtual Status addAndAuthorizeUser(OperationContext* opCtx, const UserRequest& userRequest) = 0; + virtual Status addAndAuthorizeUser(OperationContext* opCtx, const UserName& userName) = 0; // Returns the authenticated user with the given name. Returns NULL // if no such user is found. diff --git a/src/mongo/db/auth/authorization_session_for_test.cpp b/src/mongo/db/auth/authorization_session_for_test.cpp index e88dd65ebc4..620716c5e4f 100644 --- a/src/mongo/db/auth/authorization_session_for_test.cpp +++ b/src/mongo/db/auth/authorization_session_for_test.cpp @@ -48,8 +48,7 @@ void AuthorizationSessionForTest::assumePrivilegesForDB(Privilege privilege, Str void AuthorizationSessionForTest::assumePrivilegesForDB(PrivilegeVector privileges, StringData dbName) { - UserRequest request(UserName("authorizationSessionForTestUser"_sd, dbName), boost::none); - _authenticatedUser = UserHandle(User(request)); + _authenticatedUser = UserHandle(User(UserName("authorizationSessionForTestUser", dbName))); _authenticatedUser.value()->addPrivileges(privileges); _authenticationMode = AuthorizationSession::AuthenticationMode::kConnection; _updateInternalAuthorizationState(); diff --git a/src/mongo/db/auth/authorization_session_impl.cpp b/src/mongo/db/auth/authorization_session_impl.cpp index 85b8bcc0c6e..90083b297ba 100644 --- a/src/mongo/db/auth/authorization_session_impl.cpp +++ b/src/mongo/db/auth/authorization_session_impl.cpp @@ -200,9 +200,7 @@ void AuthorizationSessionImpl::startContractTracking() { } Status AuthorizationSessionImpl::addAndAuthorizeUser(OperationContext* opCtx, - const UserRequest& userRequest) try { - const auto& userName = userRequest.name; - + const UserName& userName) try { // Check before we start to reveal as little as possible. Note that we do not need the lock // because only the Client thread can mutate _authenticatedUser. if (_authenticatedUser) { @@ -234,7 +232,7 @@ Status AuthorizationSessionImpl::addAndAuthorizeUser(OperationContext* opCtx, } AuthorizationManager* authzManager = AuthorizationManager::get(opCtx->getServiceContext()); - auto user = uassertStatusOK(authzManager->acquireUser(opCtx, userRequest)); + auto user = uassertStatusOK(authzManager->acquireUser(opCtx, userName)); auto restrictionStatus = user->validateRestrictions(opCtx); if (!restrictionStatus.isOK()) { diff --git a/src/mongo/db/auth/authorization_session_impl.h b/src/mongo/db/auth/authorization_session_impl.h index e9e19c63bb7..94a13c59249 100644 --- a/src/mongo/db/auth/authorization_session_impl.h +++ b/src/mongo/db/auth/authorization_session_impl.h @@ -76,7 +76,7 @@ public: void startContractTracking() override; - Status addAndAuthorizeUser(OperationContext* opCtx, const UserRequest& userRequest) override; + Status addAndAuthorizeUser(OperationContext* opCtx, const UserName& userName) override; User* lookupUser(const UserName& name) override; diff --git a/src/mongo/db/auth/authorization_session_test.cpp b/src/mongo/db/auth/authorization_session_test.cpp index b1a980f5396..1336554897a 100644 --- a/src/mongo/db/auth/authorization_session_test.cpp +++ b/src/mongo/db/auth/authorization_session_test.cpp @@ -172,53 +172,37 @@ const ResourcePattern otherProfileCollResource( const ResourcePattern thirdProfileCollResource( ResourcePattern::forExactNamespace(NamespaceString("third.system.profile"))); -const UserName kUser1Test("user1"_sd, "test"_sd); -const UserRequest kUser1TestRequest(kUser1Test, boost::none); -const UserName kUser2Test("user2"_sd, "test"_sd); -const UserRequest kUser2TestRequest(kUser2Test, boost::none); - TEST_F(AuthorizationSessionTest, MultiAuthSameUserAllowed) { authzSession->startContractTracking(); - ASSERT_OK(createUser(kUser1Test, {})); - ASSERT_OK(authzSession->addAndAuthorizeUser(_opCtx.get(), kUser1TestRequest)); - ASSERT_OK(authzSession->addAndAuthorizeUser(_opCtx.get(), kUser1TestRequest)); + ASSERT_OK(createUser({"user1", "test"}, {})); + ASSERT_OK(authzSession->addAndAuthorizeUser(_opCtx.get(), {"user1", "test"})); + ASSERT_OK(authzSession->addAndAuthorizeUser(_opCtx.get(), {"user1", "test"})); authzSession->logoutAllDatabases(_client.get(), "Test finished"); } TEST_F(AuthorizationSessionTest, MultiAuthSameDBDisallowed) { authzSession->startContractTracking(); - ASSERT_OK(createUser(kUser1Test, {})); - ASSERT_OK(createUser(kUser2Test, {})); + ASSERT_OK(createUser({"user1", "test"}, {})); + ASSERT_OK(createUser({"user2", "test"}, {})); - ASSERT_OK(authzSession->addAndAuthorizeUser(_opCtx.get(), kUser1TestRequest)); - ASSERT_NOT_OK(authzSession->addAndAuthorizeUser(_opCtx.get(), kUser2TestRequest)); + ASSERT_OK(authzSession->addAndAuthorizeUser(_opCtx.get(), {"user1", "test"})); + ASSERT_NOT_OK(authzSession->addAndAuthorizeUser(_opCtx.get(), {"user2", "test"})); authzSession->logoutAllDatabases(_client.get(), "Test finished"); } -const UserName kUserTest1("user"_sd, "test1"_sd); -const UserRequest kUserTest1Request(kUserTest1, boost::none); -const UserName kUserTest2("user"_sd, "test2"_sd); -const UserRequest kUserTest2Request(kUserTest2, boost::none); - TEST_F(AuthorizationSessionTest, MultiAuthMultiDBDisallowed) { authzSession->startContractTracking(); - ASSERT_OK(createUser(kUserTest1, {})); - ASSERT_OK(createUser(kUserTest2, {})); + ASSERT_OK(createUser({"user", "test1"}, {})); + ASSERT_OK(createUser({"user", "test2"}, {})); - ASSERT_OK(authzSession->addAndAuthorizeUser(_opCtx.get(), kUserTest1Request)); - ASSERT_NOT_OK(authzSession->addAndAuthorizeUser(_opCtx.get(), kUserTest2Request)); + ASSERT_OK(authzSession->addAndAuthorizeUser(_opCtx.get(), {"user", "test1"})); + ASSERT_NOT_OK(authzSession->addAndAuthorizeUser(_opCtx.get(), {"user", "test2"})); authzSession->logoutAllDatabases(_client.get(), "Test finished"); } -const UserName kSpencerTest("spencer"_sd, "test"_sd); -const UserRequest kSpencerTestRequest(kSpencerTest, boost::none); - -const UserName kAdminAdmin("admin"_sd, "admin"_sd); -const UserRequest kAdminAdminRequest(kAdminAdmin, boost::none); - TEST_F(AuthorizationSessionTest, AddUserAndCheckAuthorization) { authzSession->startContractTracking(); @@ -234,11 +218,11 @@ TEST_F(AuthorizationSessionTest, AddUserAndCheckAuthorization) { // Check that you can't authorize a user that doesn't exist. ASSERT_EQUALS(ErrorCodes::UserNotFound, - authzSession->addAndAuthorizeUser(_opCtx.get(), kSpencerTestRequest)); + authzSession->addAndAuthorizeUser(_opCtx.get(), UserName("spencer", "test"))); // Add a user with readWrite and dbAdmin on the test DB - ASSERT_OK(createUser(kSpencerTest, {{"readWrite", "test"}, {"dbAdmin", "test"}})); - ASSERT_OK(authzSession->addAndAuthorizeUser(_opCtx.get(), kSpencerTestRequest)); + ASSERT_OK(createUser({"spencer", "test"}, {{"readWrite", "test"}, {"dbAdmin", "test"}})); + ASSERT_OK(authzSession->addAndAuthorizeUser(_opCtx.get(), UserName("spencer", "test"))); ASSERT_TRUE( authzSession->isAuthorizedForActionsOnResource(testFooCollResource, ActionType::insert)); @@ -249,8 +233,8 @@ TEST_F(AuthorizationSessionTest, AddUserAndCheckAuthorization) { authzSession->logoutDatabase(_client.get(), "test", "Kill the test!"); // Add an admin user with readWriteAnyDatabase - ASSERT_OK(createUser(kAdminAdmin, {{"readWriteAnyDatabase", "admin"}})); - ASSERT_OK(authzSession->addAndAuthorizeUser(_opCtx.get(), kAdminAdminRequest)); + ASSERT_OK(createUser({"admin", "admin"}, {{"readWriteAnyDatabase", "admin"}})); + ASSERT_OK(authzSession->addAndAuthorizeUser(_opCtx.get(), UserName("admin", "admin"))); ASSERT_TRUE(authzSession->isAuthorizedForActionsOnResource( ResourcePattern::forExactNamespace(NamespaceString("anydb.somecollection")), @@ -302,9 +286,9 @@ TEST_F(AuthorizationSessionTest, AddUserAndCheckAuthorization) { TEST_F(AuthorizationSessionTest, DuplicateRolesOK) { // Add a user with doubled-up readWrite and single dbAdmin on the test DB - ASSERT_OK(createUser(kSpencerTest, + ASSERT_OK(createUser({"spencer", "test"}, {{"readWrite", "test"}, {"dbAdmin", "test"}, {"readWrite", "test"}})); - ASSERT_OK(authzSession->addAndAuthorizeUser(_opCtx.get(), kSpencerTestRequest)); + ASSERT_OK(authzSession->addAndAuthorizeUser(_opCtx.get(), UserName("spencer", "test"))); ASSERT_TRUE( authzSession->isAuthorizedForActionsOnResource(testFooCollResource, ActionType::insert)); @@ -315,24 +299,14 @@ TEST_F(AuthorizationSessionTest, DuplicateRolesOK) { authzSession->logoutDatabase(_client.get(), "test", "Kill the test!"); } -const UserName kRWTest("rw"_sd, "test"_sd); -const UserName kUserAdminTest("useradmin"_sd, "test"_sd); -const UserName kRWAnyTest("rwany"_sd, "test"_sd); -const UserName kUserAdminAnyTest("useradminany"_sd, "test"_sd); - -const UserRequest kRWTestRequest(kRWTest, boost::none); -const UserRequest kUserAdminTestRequest(kUserAdminTest, boost::none); -const UserRequest kRWAnyTestRequest(kRWAnyTest, boost::none); -const UserRequest kUserAdminAnyTestRequest(kUserAdminAnyTest, boost::none); - TEST_F(AuthorizationSessionTest, SystemCollectionsAccessControl) { - ASSERT_OK(createUser(kRWTest, {{"readWrite", "test"}, {"dbAdmin", "test"}})); - ASSERT_OK(createUser(kUserAdminTest, {{"userAdmin", "test"}})); - ASSERT_OK(createUser(kRWAnyTest, + ASSERT_OK(createUser({"rw", "test"}, {{"readWrite", "test"}, {"dbAdmin", "test"}})); + ASSERT_OK(createUser({"useradmin", "test"}, {{"userAdmin", "test"}})); + ASSERT_OK(createUser({"rwany", "test"}, {{"readWriteAnyDatabase", "admin"}, {"dbAdminAnyDatabase", "admin"}})); - ASSERT_OK(createUser(kUserAdminAnyTest, {{"userAdminAnyDatabase", "admin"}})); + ASSERT_OK(createUser({"useradminany", "test"}, {{"userAdminAnyDatabase", "admin"}})); - ASSERT_OK(authzSession->addAndAuthorizeUser(_opCtx.get(), kRWAnyTestRequest)); + ASSERT_OK(authzSession->addAndAuthorizeUser(_opCtx.get(), UserName("rwany", "test"))); ASSERT_FALSE( authzSession->isAuthorizedForActionsOnResource(testUsersCollResource, ActionType::insert)); @@ -348,7 +322,7 @@ TEST_F(AuthorizationSessionTest, SystemCollectionsAccessControl) { authzSession->isAuthorizedForActionsOnResource(otherProfileCollResource, ActionType::find)); authzSession->logoutDatabase(_client.get(), "test", "Kill the test!"); - ASSERT_OK(authzSession->addAndAuthorizeUser(_opCtx.get(), kUserAdminAnyTestRequest)); + ASSERT_OK(authzSession->addAndAuthorizeUser(_opCtx.get(), UserName("useradminany", "test"))); ASSERT_FALSE( authzSession->isAuthorizedForActionsOnResource(testUsersCollResource, ActionType::insert)); ASSERT_TRUE( @@ -363,7 +337,7 @@ TEST_F(AuthorizationSessionTest, SystemCollectionsAccessControl) { authzSession->isAuthorizedForActionsOnResource(otherProfileCollResource, ActionType::find)); authzSession->logoutDatabase(_client.get(), "test", "Kill the test!"); - ASSERT_OK(authzSession->addAndAuthorizeUser(_opCtx.get(), kRWTestRequest)); + ASSERT_OK(authzSession->addAndAuthorizeUser(_opCtx.get(), UserName("rw", "test"))); ASSERT_FALSE( authzSession->isAuthorizedForActionsOnResource(testUsersCollResource, ActionType::insert)); @@ -379,7 +353,7 @@ TEST_F(AuthorizationSessionTest, SystemCollectionsAccessControl) { authzSession->isAuthorizedForActionsOnResource(otherProfileCollResource, ActionType::find)); authzSession->logoutDatabase(_client.get(), "test", "Kill the test!"); - ASSERT_OK(authzSession->addAndAuthorizeUser(_opCtx.get(), kUserAdminTestRequest)); + ASSERT_OK(authzSession->addAndAuthorizeUser(_opCtx.get(), UserName("useradmin", "test"))); ASSERT_FALSE( authzSession->isAuthorizedForActionsOnResource(testUsersCollResource, ActionType::insert)); ASSERT_FALSE( @@ -397,15 +371,15 @@ TEST_F(AuthorizationSessionTest, SystemCollectionsAccessControl) { TEST_F(AuthorizationSessionTest, InvalidateUser) { // Add a readWrite user - ASSERT_OK(createUser(kSpencerTest, {{"readWrite", "test"}})); - ASSERT_OK(authzSession->addAndAuthorizeUser(_opCtx.get(), kSpencerTestRequest)); + ASSERT_OK(createUser({"spencer", "test"}, {{"readWrite", "test"}})); + ASSERT_OK(authzSession->addAndAuthorizeUser(_opCtx.get(), UserName("spencer", "test"))); ASSERT_TRUE( authzSession->isAuthorizedForActionsOnResource(testFooCollResource, ActionType::find)); ASSERT_TRUE( authzSession->isAuthorizedForActionsOnResource(testFooCollResource, ActionType::insert)); - User* user = authzSession->lookupUser(kSpencerTest); + User* user = authzSession->lookupUser(UserName("spencer", "test")); // Change the user to be read-only int ignored; @@ -414,7 +388,7 @@ TEST_F(AuthorizationSessionTest, InvalidateUser) { BSONObj(), BSONObj(), &ignored)); - ASSERT_OK(createUser(kSpencerTest, {{"read", "test"}})); + ASSERT_OK(createUser({"spencer", "test"}, {{"read", "test"}})); // Make sure that invalidating the user causes the session to reload its privileges. authzManager->invalidateUserByName(_opCtx.get(), user->getName()); @@ -424,7 +398,7 @@ TEST_F(AuthorizationSessionTest, InvalidateUser) { ASSERT_FALSE( authzSession->isAuthorizedForActionsOnResource(testFooCollResource, ActionType::insert)); - user = authzSession->lookupUser(kSpencerTest); + user = authzSession->lookupUser(UserName("spencer", "test")); // Delete the user. ASSERT_OK(managerState->remove(_opCtx.get(), @@ -439,21 +413,21 @@ TEST_F(AuthorizationSessionTest, InvalidateUser) { authzSession->isAuthorizedForActionsOnResource(testFooCollResource, ActionType::find)); ASSERT_FALSE( authzSession->isAuthorizedForActionsOnResource(testFooCollResource, ActionType::insert)); - ASSERT_FALSE(authzSession->lookupUser(kSpencerTest)); + ASSERT_FALSE(authzSession->lookupUser(UserName("spencer", "test"))); authzSession->logoutDatabase(_client.get(), "test", "Kill the test!"); } TEST_F(AuthorizationSessionTest, UseOldUserInfoInFaceOfConnectivityProblems) { // Add a readWrite user - ASSERT_OK(createUser(kSpencerTest, {{"readWrite", "test"}})); - ASSERT_OK(authzSession->addAndAuthorizeUser(_opCtx.get(), kSpencerTestRequest)); + ASSERT_OK(createUser({"spencer", "test"}, {{"readWrite", "test"}})); + ASSERT_OK(authzSession->addAndAuthorizeUser(_opCtx.get(), UserName("spencer", "test"))); ASSERT_TRUE( authzSession->isAuthorizedForActionsOnResource(testFooCollResource, ActionType::find)); ASSERT_TRUE( authzSession->isAuthorizedForActionsOnResource(testFooCollResource, ActionType::insert)); - User* user = authzSession->lookupUser(kSpencerTest); + User* user = authzSession->lookupUser(UserName("spencer", "test")); // Change the user to be read-only int ignored; @@ -463,7 +437,7 @@ TEST_F(AuthorizationSessionTest, UseOldUserInfoInFaceOfConnectivityProblems) { BSONObj(), BSONObj(), &ignored)); - ASSERT_OK(createUser(kSpencerTest, {{"read", "test"}})); + ASSERT_OK(createUser({"spencer", "test"}, {{"read", "test"}})); // Even though the user's privileges have been reduced, since we've configured user // document lookup to fail, the authz session should continue to use its known out-of-date @@ -517,7 +491,7 @@ TEST_F(AuthorizationSessionTest, AcquireUserObtainsAndValidatesAuthenticationRes std::make_unique<RestrictionEnvironment>( SockAddr::create(clientSource, 5555, AF_UNSPEC), SockAddr::create(serverAddress, 27017, AF_UNSPEC))); - ASSERT_OK(authzSession->addAndAuthorizeUser(_opCtx.get(), kSpencerTestRequest)); + ASSERT_OK(authzSession->addAndAuthorizeUser(_opCtx.get(), UserName("spencer", "test"))); authzSession->logoutDatabase(_client.get(), "test", "Kill the test!"); }; @@ -526,11 +500,11 @@ TEST_F(AuthorizationSessionTest, AcquireUserObtainsAndValidatesAuthenticationRes std::make_unique<RestrictionEnvironment>( SockAddr::create(clientSource, 5555, AF_UNSPEC), SockAddr::create(serverAddress, 27017, AF_UNSPEC))); - ASSERT_NOT_OK(authzSession->addAndAuthorizeUser(_opCtx.get(), kSpencerTestRequest)); + ASSERT_NOT_OK(authzSession->addAndAuthorizeUser(_opCtx.get(), UserName("spencer", "test"))); }; // The empty RestrictionEnvironment will cause addAndAuthorizeUser to fail. - ASSERT_NOT_OK(authzSession->addAndAuthorizeUser(_opCtx.get(), kSpencerTestRequest)); + ASSERT_NOT_OK(authzSession->addAndAuthorizeUser(_opCtx.get(), UserName("spencer", "test"))); // A clientSource from the 192.168.0.0/24 block will succeed in connecting to a server // listening on 192.168.0.2. @@ -1085,26 +1059,28 @@ TEST_F(AuthorizationSessionTest, UnauthorizedSessionIsCoauthorizedWithNobody) { } TEST_F(AuthorizationSessionTest, UnauthorizedSessionIsNotCoauthorizedWithAnybody) { - ASSERT_FALSE(authzSession->isCoauthorizedWith(kSpencerTest)); + ASSERT_FALSE(authzSession->isCoauthorizedWith(UserName("spencer", "test"))); } TEST_F(AuthorizationSessionTest, UnauthorizedSessionIsCoauthorizedWithAnybodyWhenAuthIsDisabled) { authzManager->setAuthEnabled(false); - ASSERT_TRUE(authzSession->isCoauthorizedWith(kSpencerTest)); + ASSERT_TRUE(authzSession->isCoauthorizedWith(UserName("spencer", "test"))); } TEST_F(AuthorizationSessionTest, AuthorizedSessionIsNotCoauthorizedNobody) { - ASSERT_OK(createUser(kSpencerTest, {})); - ASSERT_OK(authzSession->addAndAuthorizeUser(_opCtx.get(), kSpencerTestRequest)); + UserName user("spencer", "test"); + ASSERT_OK(createUser(user, {})); + ASSERT_OK(authzSession->addAndAuthorizeUser(_opCtx.get(), user)); ASSERT_FALSE(authzSession->isCoauthorizedWith(boost::none)); authzSession->logoutDatabase(_client.get(), "test", "Kill the test!"); } TEST_F(AuthorizationSessionTest, AuthorizedSessionIsCoauthorizedNobodyWhenAuthIsDisabled) { + UserName user("spencer", "test"); authzManager->setAuthEnabled(false); - ASSERT_OK(createUser(kSpencerTest, {})); - ASSERT_OK(authzSession->addAndAuthorizeUser(_opCtx.get(), kSpencerTestRequest)); - ASSERT_TRUE(authzSession->isCoauthorizedWith(kSpencerTest)); + ASSERT_OK(createUser(user, {})); + ASSERT_OK(authzSession->addAndAuthorizeUser(_opCtx.get(), user)); + ASSERT_TRUE(authzSession->isCoauthorizedWith(user)); authzSession->logoutDatabase(_client.get(), "test", "Kill the test!"); } @@ -1472,9 +1448,6 @@ TEST_F(SystemBucketsTest, CanCheckIfHasAnyPrivilegeInResourceDBForSystemBuckets) ASSERT_TRUE(authzSession->isAuthorizedForAnyActionOnAnyResourceInDB(sb_db_other)); } -const UserName kGMarksAdmin("gmarks"_sd, "admin"_sd); -const UserRequest kGMarksAdminRequest(kGMarksAdmin, boost::none); - TEST_F(AuthorizationSessionTest, MayBypassWriteBlockingModeIsSetCorrectly) { ASSERT_FALSE(authzSession->mayBypassWriteBlockingMode()); @@ -1490,7 +1463,7 @@ TEST_F(AuthorizationSessionTest, MayBypassWriteBlockingModeIsSetCorrectly) { << "db" << "test"))), BSONObj())); - ASSERT_OK(authzSession->addAndAuthorizeUser(_opCtx.get(), kSpencerTestRequest)); + 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 @@ -1507,7 +1480,7 @@ TEST_F(AuthorizationSessionTest, MayBypassWriteBlockingModeIsSetCorrectly) { BSONObj())); authzSession->logoutDatabase(_client.get(), "test", "End of test"); - ASSERT_OK(authzSession->addAndAuthorizeUser(_opCtx.get(), kGMarksAdminRequest)); + 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 @@ -1529,7 +1502,7 @@ TEST_F(AuthorizationSessionTest, MayBypassWriteBlockingModeIsSetCorrectly) { BSONObj())); authzSession->logoutDatabase(_client.get(), "admin", ""); - ASSERT_OK(authzSession->addAndAuthorizeUser(_opCtx.get(), kAdminAdminRequest)); + 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 diff --git a/src/mongo/db/auth/authz_manager_external_state_local.cpp b/src/mongo/db/auth/authz_manager_external_state_local.cpp index bf2b0238eac..9f98e5f7032 100644 --- a/src/mongo/db/auth/authz_manager_external_state_local.cpp +++ b/src/mongo/db/auth/authz_manager_external_state_local.cpp @@ -293,7 +293,7 @@ StatusWith<User> AuthzManagerExternalStateLocal::getUserObject(OperationContext* const UserRequest& userReq) try { const UserName& userName = userReq.name; std::vector<RoleName> directRoles; - User user(userReq); + User user(userReq.name); auto rolesLock = _lockRoles(opCtx, userName.getTenant()); diff --git a/src/mongo/db/auth/authz_manager_external_state_s.cpp b/src/mongo/db/auth/authz_manager_external_state_s.cpp index 4f415fd7247..61705c9c9b9 100644 --- a/src/mongo/db/auth/authz_manager_external_state_s.cpp +++ b/src/mongo/db/auth/authz_manager_external_state_s.cpp @@ -118,7 +118,7 @@ StatusWith<User> AuthzManagerExternalStateMongos::getUserObject(OperationContext return status; } - User user(userReq); + User user(userReq.name); V2UserDocumentParser dp; dp.setTenantId(getActiveTenant(opCtx)); status = dp.initializeUserFromUserDocument(userDoc, &user); diff --git a/src/mongo/db/auth/sasl_commands.cpp b/src/mongo/db/auth/sasl_commands.cpp index 4d5755c5d95..5c00f9c23c9 100644 --- a/src/mongo/db/auth/sasl_commands.cpp +++ b/src/mongo/db/auth/sasl_commands.cpp @@ -184,9 +184,9 @@ SaslReply doSaslStep(OperationContext* opCtx, } if (mechanism.isSuccess()) { - auto request = mechanism.getUserRequest(); + UserName userName(mechanism.getPrincipalName(), mechanism.getAuthenticationDatabase()); uassertStatusOK( - AuthorizationSession::get(opCtx->getClient())->addAndAuthorizeUser(opCtx, request)); + AuthorizationSession::get(opCtx->getClient())->addAndAuthorizeUser(opCtx, userName)); if (!serverGlobalParams.quiet.load()) { auto attrs = makeLogAttributes(); diff --git a/src/mongo/db/auth/sasl_mechanism_registry.cpp b/src/mongo/db/auth/sasl_mechanism_registry.cpp index e49a1c07185..4ba33279435 100644 --- a/src/mongo/db/auth/sasl_mechanism_registry.cpp +++ b/src/mongo/db/auth/sasl_mechanism_registry.cpp @@ -103,7 +103,7 @@ void SASLServerMechanismRegistry::advertiseMechanismNamesForUser(OperationContex AuthorizationManager* authManager = AuthorizationManager::get(opCtx->getServiceContext()); UserHandle user; - const auto swUser = authManager->acquireUser(opCtx, UserRequest(userName, boost::none)); + const auto swUser = authManager->acquireUser(opCtx, userName); if (!swUser.isOK()) { auto& status = swUser.getStatus(); if (status.code() == ErrorCodes::UserNotFound) { diff --git a/src/mongo/db/auth/sasl_mechanism_registry.h b/src/mongo/db/auth/sasl_mechanism_registry.h index 2fe16292b80..06b0850125b 100644 --- a/src/mongo/db/auth/sasl_mechanism_registry.h +++ b/src/mongo/db/auth/sasl_mechanism_registry.h @@ -206,10 +206,6 @@ public: return Status::OK(); } - virtual UserRequest getUserRequest() const { - return UserRequest(UserName(getPrincipalName(), getAuthenticationDatabase()), boost::none); - } - protected: /** * Mechanism provided step implementation. diff --git a/src/mongo/db/auth/sasl_mechanism_registry_test.cpp b/src/mongo/db/auth/sasl_mechanism_registry_test.cpp index a7bb4d75440..2f39ddea4f0 100644 --- a/src/mongo/db/auth/sasl_mechanism_registry_test.cpp +++ b/src/mongo/db/auth/sasl_mechanism_registry_test.cpp @@ -225,8 +225,7 @@ public: << "roles" << BSONArray()), BSONObj())); - UserRequest systemLocal(UserName("__system"_sd, "local"_sd), boost::none); - internalSecurity.setUser(std::make_shared<UserHandle>(User(systemLocal))); + internalSecurity.setUser(std::make_shared<UserHandle>(User(UserName("__system", "local")))); } BSONObj getMechsFor(const UserName user) { diff --git a/src/mongo/db/auth/sasl_plain_server_conversation.cpp b/src/mongo/db/auth/sasl_plain_server_conversation.cpp index 8ec01b8f0c7..08401c59835 100644 --- a/src/mongo/db/auth/sasl_plain_server_conversation.cpp +++ b/src/mongo/db/auth/sasl_plain_server_conversation.cpp @@ -126,8 +126,7 @@ StatusWith<std::tuple<bool, std::string>> SASLPlainServerMechanism::stepImpl( // The authentication database is also the source database for the user. auto swUser = authManager->acquireUser( - opCtx, - UserRequest({ServerMechanismBase::_principalName, _authenticationDatabase}, boost::none)); + opCtx, UserName(ServerMechanismBase::_principalName, _authenticationDatabase)); if (!swUser.isOK()) { return swUser.getStatus(); diff --git a/src/mongo/db/auth/sasl_scram_server_conversation.cpp b/src/mongo/db/auth/sasl_scram_server_conversation.cpp index 3271626aaf7..87ff7a789db 100644 --- a/src/mongo/db/auth/sasl_scram_server_conversation.cpp +++ b/src/mongo/db/auth/sasl_scram_server_conversation.cpp @@ -203,7 +203,7 @@ StatusWith<std::tuple<bool, std::string>> SaslSCRAMServerMechanism<Policy>::_fir // The authentication database is also the source database for the user. auto authManager = AuthorizationManager::get(opCtx->getServiceContext()); - auto swUser = authManager->acquireUser(opCtx, UserRequest(user, boost::none)); + auto swUser = authManager->acquireUser(opCtx, user); if (!swUser.isOK()) { return swUser.getStatus(); } diff --git a/src/mongo/db/auth/security_key_test.cpp b/src/mongo/db/auth/security_key_test.cpp index 7a33cfbbad7..e64aded89f6 100644 --- a/src/mongo/db/auth/security_key_test.cpp +++ b/src/mongo/db/auth/security_key_test.cpp @@ -160,8 +160,7 @@ TEST(SecurityFile, Test) { } TEST(SecurityKey, Test) { - UserRequest systemLocal(UserName("__system"_sd, "local"_sd), boost::none); - internalSecurity.setUser(std::make_shared<UserHandle>(User(systemLocal))); + internalSecurity.setUser(std::make_shared<UserHandle>(User(UserName("__system", "local")))); for (const auto& testCase : testCases) { TestFile file(testCase.fileContents, testCase.mode != TestCase::FailureMode::Permissions); diff --git a/src/mongo/db/auth/security_token_authentication_guard.cpp b/src/mongo/db/auth/security_token_authentication_guard.cpp index a8f86fa240d..5be6de3dc75 100644 --- a/src/mongo/db/auth/security_token_authentication_guard.cpp +++ b/src/mongo/db/auth/security_token_authentication_guard.cpp @@ -41,9 +41,9 @@ namespace auth { SecurityTokenAuthenticationGuard::SecurityTokenAuthenticationGuard( OperationContext* opCtx, const ValidatedTenancyScope& token) { if (token.hasAuthenticatedUser()) { - UserRequest request(token.authenticatedUser(), boost::none); + const auto& userName = token.authenticatedUser(); auto* client = opCtx->getClient(); - uassertStatusOK(AuthorizationSession::get(client)->addAndAuthorizeUser(opCtx, request)); + uassertStatusOK(AuthorizationSession::get(client)->addAndAuthorizeUser(opCtx, userName)); _client = client; LOGV2_DEBUG(5838100, diff --git a/src/mongo/db/auth/user.cpp b/src/mongo/db/auth/user.cpp index c50de72a546..a80e833c9e7 100644 --- a/src/mongo/db/auth/user.cpp +++ b/src/mongo/db/auth/user.cpp @@ -66,8 +66,8 @@ SHA256Block computeDigest(const UserName& name) { } // namespace -User::User(UserRequest request) - : _request(std::move(request)), _isInvalidated(false), _digest(computeDigest(_request.name)) {} +User::User(const UserName& name) + : _name(name), _isInvalidated(false), _digest(computeDigest(_name)) {} template <> User::SCRAMCredentials<SHA1Block>& User::CredentialData::scram<SHA1Block>() { @@ -201,10 +201,10 @@ void User::reportForUsersInfo(BSONObjBuilder* builder, bool showCredentials, bool showPrivileges, bool showAuthenticationRestrictions) const { - builder->append(kIdFieldName, getName().getUnambiguousName()); + builder->append(kIdFieldName, _name.getUnambiguousName()); UUID::fromCDR(ConstDataRange(_id)).appendToBuilder(builder, kUserIdFieldName); - builder->append(kUserFieldName, getName().getUser()); - builder->append(kDbFieldName, getName().getDB()); + builder->append(kUserFieldName, _name.getUser()); + builder->append(kDbFieldName, _name.getDB()); BSONArrayBuilder mechanismNamesBuilder(builder->subarrayStart(kMechanismsFieldName)); for (const StringData& mechanism : _credentials.toMechanismsVector()) { diff --git a/src/mongo/db/auth/user.h b/src/mongo/db/auth/user.h index 83dcc398775..52b9e4e38d9 100644 --- a/src/mongo/db/auth/user.h +++ b/src/mongo/db/auth/user.h @@ -47,36 +47,6 @@ namespace mongo { /** - * Represents the properties required to request a UserHandle. - * This type is hashable and may be used as a key describing requests - */ -struct UserRequest { - UserRequest(UserName name, boost::optional<std::set<RoleName>> roles) - : name(std::move(name)), roles(std::move(roles)) {} - - - template <typename H> - friend H AbslHashValue(H h, const UserRequest& key) { - auto state = H::combine(std::move(h), key.name); - if (key.roles) { - for (const auto& role : *key.roles) { - state = H::combine(std::move(state), role); - } - } - return state; - } - - bool operator==(const UserRequest& key) const { - return name == key.name && roles == key.roles; - } - - // The name of the requested user - UserName name; - // Any authorization grants which should override and be used in favor of roles acquisition. - boost::optional<std::set<RoleName>> roles; -}; - -/** * Represents a MongoDB user. Stores information about the user necessary for access control * checks and authentications, such as what privileges this user has, as well as what roles * the user belongs to. @@ -188,7 +158,7 @@ public: using ResourcePrivilegeMap = stdx::unordered_map<ResourcePattern, Privilege>; - explicit User(UserRequest request); + explicit User(const UserName& name); User(User&&) = default; User& operator=(User&&) = default; @@ -200,15 +170,11 @@ public: _id = std::move(id); } - const UserRequest& getUserRequest() const { - return _request; - } - /** * Returns the user name for this user. */ const UserName& getName() const { - return _request.name; + return _name; } /** @@ -360,8 +326,8 @@ private: // Unique ID (often UUID) for this user. May be empty for legacy users. UserId _id; - // The original UserRequest which resolved into this user - UserRequest _request; + // The full user name (as specified by the administrator) + UserName _name; // User was explicitly invalidated bool _isInvalidated; @@ -388,6 +354,36 @@ private: RestrictionDocuments _indirectRestrictions; }; +/** + * Represents the properties required to request a UserHandle. + * This type is hashable and may be used as a key describing requests + */ +struct UserRequest { + UserRequest(const UserName& name, boost::optional<std::set<RoleName>> roles) + : name(name), roles(std::move(roles)) {} + + + template <typename H> + friend H AbslHashValue(H h, const UserRequest& key) { + auto state = H::combine(std::move(h), key.name); + if (key.roles) { + for (const auto& role : *key.roles) { + state = H::combine(std::move(state), role); + } + } + return state; + } + + bool operator==(const UserRequest& key) const { + return name == key.name && roles == key.roles; + } + + // The name of the requested user + UserName name; + // Any authorization grants which should override and be used in favor of roles acquisition. + boost::optional<std::set<RoleName>> roles; +}; + using UserCache = ReadThroughCache<UserRequest, User>; using UserHandle = UserCache::ValueHandle; diff --git a/src/mongo/db/auth/user_document_parser_test.cpp b/src/mongo/db/auth/user_document_parser_test.cpp index 9773927ad50..00ff394a222 100644 --- a/src/mongo/db/auth/user_document_parser_test.cpp +++ b/src/mongo/db/auth/user_document_parser_test.cpp @@ -63,8 +63,8 @@ public: BSONObj sha1_creds, sha256_creds; void setUp() { - user.reset(new User(UserRequest(UserName("spencer", "test"), boost::none))); - adminUser.reset(new User(UserRequest(UserName("admin", "admin"), boost::none))); + user.reset(new User(UserName("spencer", "test"))); + adminUser.reset(new User(UserName("admin", "admin"))); sha1_creds = scram::Secrets<SHA1Block>::generateCredentials( "a", saslGlobalParams.scramSHA1IterationCount.load()); diff --git a/src/mongo/db/auth/validated_tenancy_scope_test.cpp b/src/mongo/db/auth/validated_tenancy_scope_test.cpp index 66c48653916..94b35e9626b 100644 --- a/src/mongo/db/auth/validated_tenancy_scope_test.cpp +++ b/src/mongo/db/auth/validated_tenancy_scope_test.cpp @@ -48,7 +48,7 @@ public: * Synthesize a user with the useTenant privilege and add them to the authorization session. */ static void grantUseTenant(Client& client) { - User user(UserRequest(UserName("useTenant"_sd, "admin"_sd), boost::none)); + User user(UserName("useTenant", "admin")); user.setPrivileges( {Privilege(ResourcePattern::forClusterResource(), ActionType::useTenant)}); auto* as = dynamic_cast<AuthorizationSessionImpl*>(AuthorizationSession::get(client)); diff --git a/src/mongo/db/commands/SConscript b/src/mongo/db/commands/SConscript index 143fdd94491..052dfba1186 100644 --- a/src/mongo/db/commands/SConscript +++ b/src/mongo/db/commands/SConscript @@ -176,7 +176,6 @@ env.Library( '$BUILD_DIR/mongo/db/auth/auth', '$BUILD_DIR/mongo/db/auth/auth_options', '$BUILD_DIR/mongo/db/auth/authentication_session', - '$BUILD_DIR/mongo/db/auth/authorization_manager_global', '$BUILD_DIR/mongo/db/auth/cluster_auth_mode', '$BUILD_DIR/mongo/db/commands', '$BUILD_DIR/mongo/db/server_base', diff --git a/src/mongo/db/commands/authentication_commands.cpp b/src/mongo/db/commands/authentication_commands.cpp index 8feb2d6320c..7c2c5d231a9 100644 --- a/src/mongo/db/commands/authentication_commands.cpp +++ b/src/mongo/db/commands/authentication_commands.cpp @@ -44,7 +44,6 @@ #include "mongo/config.h" #include "mongo/db/auth/auth_options_gen.h" #include "mongo/db/auth/authentication_session.h" -#include "mongo/db/auth/authorization_manager_global_parameters_gen.h" #include "mongo/db/auth/authorization_session.h" #include "mongo/db/auth/cluster_auth_mode.h" #include "mongo/db/auth/privilege.h" @@ -243,7 +242,7 @@ void _authenticateX509(OperationContext* opCtx, AuthenticationSession* session) "No verified subject name available from client", !clientName.empty()); - UserName userName = ([&] { + auto user = [&] { if (session->getUserName().empty()) { auto user = UserName(clientName.toString(), session->getDatabase().toString()); session->updateUserName(user); @@ -254,7 +253,7 @@ void _authenticateX509(OperationContext* opCtx, AuthenticationSession* session) session->getUserName() == clientName.toString()); return UserName(session->getUserName().toString(), session->getDatabase().toString()); } - })(); + }(); uassert(ErrorCodes::ProtocolError, "SSL support is required for the MONGODB-X509 mechanism.", @@ -270,7 +269,7 @@ void _authenticateX509(OperationContext* opCtx, AuthenticationSession* session) uassert(ErrorCodes::ProtocolError, "X.509 authentication must always use the $external database.", - userName.getDB() == kExternalDB); + user.getDB() == kExternalDB); auto isInternalClient = [&]() -> bool { return opCtx->getClient()->session()->getTags() & transport::Session::kInternalClient; @@ -278,27 +277,11 @@ void _authenticateX509(OperationContext* opCtx, AuthenticationSession* session) const auto clusterAuthMode = ClusterAuthMode::get(opCtx->getServiceContext()); - boost::optional<std::set<RoleName>> roles; - if (allowRolesFromX509Certificates) { - auto& sslPeerInfo = SSLPeerInfo::forSession(opCtx->getClient()->session()); - if (!sslPeerInfo.roles.empty() && - (sslPeerInfo.subjectName.toString() == userName.getUser())) { - roles = std::set<RoleName>(); - - // In order to be hashable, the role names must be converted from unordered_set to a - // set. - std::copy(sslPeerInfo.roles.begin(), - sslPeerInfo.roles.end(), - std::inserter(*roles, roles->begin())); - } - } - UserRequest request(std::move(userName), std::move(roles)); - auto authorizeExternalUser = [&] { uassert(ErrorCodes::BadValue, kX509AuthenticationDisabledMessage, !isX509AuthDisabled(opCtx->getServiceContext())); - uassertStatusOK(authorizationSession->addAndAuthorizeUser(opCtx, request)); + uassertStatusOK(authorizationSession->addAndAuthorizeUser(opCtx, user)); }; if (sslConfiguration->isClusterMember(clientName)) { diff --git a/src/mongo/db/commands/user_management_commands.cpp b/src/mongo/db/commands/user_management_commands.cpp index 409a2883fd3..328b214ae5e 100644 --- a/src/mongo/db/commands/user_management_commands.cpp +++ b/src/mongo/db/commands/user_management_commands.cpp @@ -139,7 +139,7 @@ Status getCurrentUserRoles(OperationContext* opCtx, AuthorizationManager* authzManager, const UserName& userName, stdx::unordered_set<RoleName>* roles) { - auto swUser = authzManager->acquireUser(opCtx, UserRequest(userName, boost::none)); + auto swUser = authzManager->acquireUser(opCtx, userName); if (!swUser.isOK()) { return swUser.getStatus(); } @@ -1385,8 +1385,7 @@ UsersInfoReply CmdUMCTyped<UsersInfoCommand, UMCInfoParams>::Invocation::typedRu } else { // Custom data is not required in the output, so it can be generated from a cached // user object. - auto swUserHandle = - authzManager->acquireUser(opCtx, UserRequest(userName, boost::none)); + auto swUserHandle = authzManager->acquireUser(opCtx, userName); if (swUserHandle.getStatus().code() == ErrorCodes::UserNotFound) { continue; } diff --git a/src/mongo/db/session/SConscript b/src/mongo/db/session/SConscript index a0021e3ac81..9862c8a660b 100644 --- a/src/mongo/db/session/SConscript +++ b/src/mongo/db/session/SConscript @@ -155,7 +155,6 @@ env.Library( '$BUILD_DIR/mongo/db/api_parameters', '$BUILD_DIR/mongo/db/auth/auth', '$BUILD_DIR/mongo/db/auth/authprivilege', - '$BUILD_DIR/mongo/db/auth/user', '$BUILD_DIR/mongo/idl/idl_parser', '$BUILD_DIR/mongo/rpc/client_metadata', 'logical_session_id_helpers', diff --git a/src/mongo/db/session/kill_sessions.cpp b/src/mongo/db/session/kill_sessions.cpp index 99d8fd5122a..7626afa111b 100644 --- a/src/mongo/db/session/kill_sessions.cpp +++ b/src/mongo/db/session/kill_sessions.cpp @@ -109,8 +109,12 @@ KillAllSessionsByPatternItem makeKillAllSessionsByPattern(OperationContext* opCt const KillAllSessionsUser& kasu) { KillAllSessionsByPatternItem item = makeKillAllSessionsByPattern(opCtx); - User user(UserRequest(UserName(kasu.getUser(), kasu.getDb()), boost::none)); - item.pattern.setUid(user.getDigest()); + auto authMgr = AuthorizationManager::get(opCtx->getServiceContext()); + + UserName un(kasu.getUser(), kasu.getDb()); + + auto user = uassertStatusOK(authMgr->acquireUser(opCtx, un)); + item.pattern.setUid(user->getDigest()); return item; } diff --git a/src/mongo/db/session/logical_session_id_test.cpp b/src/mongo/db/session/logical_session_id_test.cpp index 1142e4335ef..736525ecf0f 100644 --- a/src/mongo/db/session/logical_session_id_test.cpp +++ b/src/mongo/db/session/logical_session_id_test.cpp @@ -111,7 +111,7 @@ public: << "db" << "test"))), BSONObj())); - ASSERT_OK(authzSession->addAndAuthorizeUser(_opCtx.get(), {un, boost::none})); + ASSERT_OK(authzSession->addAndAuthorizeUser(_opCtx.get(), un)); return authzSession->lookupUser(un); } @@ -126,7 +126,7 @@ public: << "db" << "admin"))), BSONObj())); - ASSERT_OK(authzSession->addAndAuthorizeUser(_opCtx.get(), {un, boost::none})); + ASSERT_OK(authzSession->addAndAuthorizeUser(_opCtx.get(), un)); return authzSession->lookupUser(un); } }; diff --git a/src/mongo/embedded/embedded_auth_manager.cpp b/src/mongo/embedded/embedded_auth_manager.cpp index 255fc535210..ec2c7dc9184 100644 --- a/src/mongo/embedded/embedded_auth_manager.cpp +++ b/src/mongo/embedded/embedded_auth_manager.cpp @@ -108,7 +108,7 @@ public: UASSERT_NOT_IMPLEMENTED; } - StatusWith<UserHandle> acquireUser(OperationContext*, const UserRequest&) override { + StatusWith<UserHandle> acquireUser(OperationContext*, const UserName&) override { UASSERT_NOT_IMPLEMENTED; } diff --git a/src/mongo/embedded/embedded_auth_session.cpp b/src/mongo/embedded/embedded_auth_session.cpp index cae0689cb44..d086a0a3309 100644 --- a/src/mongo/embedded/embedded_auth_session.cpp +++ b/src/mongo/embedded/embedded_auth_session.cpp @@ -70,7 +70,7 @@ public: void startContractTracking() override {} - Status addAndAuthorizeUser(OperationContext*, const UserRequest&) override { + Status addAndAuthorizeUser(OperationContext*, const UserName&) override { UASSERT_NOT_IMPLEMENTED; } diff --git a/src/mongo/rpc/op_msg_test.cpp b/src/mongo/rpc/op_msg_test.cpp index df6aa2e04a3..d5287b18144 100644 --- a/src/mongo/rpc/op_msg_test.cpp +++ b/src/mongo/rpc/op_msg_test.cpp @@ -61,7 +61,7 @@ public: * Synthesize a user with the useTenant privilege and add them to the authorization session. */ static void grantUseTenant(Client& client) { - User user(UserRequest(UserName("useTenant", "admin"), boost::none)); + User user(UserName("useTenant", "admin")); user.setPrivileges( {Privilege(ResourcePattern::forClusterResource(), ActionType::useTenant)}); auto* as = dynamic_cast<AuthorizationSessionImpl*>(AuthorizationSession::get(client)); diff --git a/src/mongo/util/net/network_interface_ssl_test.cpp b/src/mongo/util/net/network_interface_ssl_test.cpp index cc4fae14287..fadbb694257 100644 --- a/src/mongo/util/net/network_interface_ssl_test.cpp +++ b/src/mongo/util/net/network_interface_ssl_test.cpp @@ -60,8 +60,7 @@ public: NetworkInterfaceIntegrationFixture::setUp(); // Setup an internal user so that we can use it for external auth - UserRequest systemLocal(UserName("__system"_sd, "local"_sd), boost::none); - auto user = std::make_shared<UserHandle>(User(systemLocal)); + auto user = std::make_shared<UserHandle>(User(UserName("__system", "local"))); internalSecurity.setUser(user); |