#include "mock_user_mgr.hpp" #include "user_mgr.hpp" #include #include #include #include #include #include #include #include #include namespace phosphor { namespace user { using ::testing::Return; using ::testing::Throw; using InternalFailure = sdbusplus::xyz::openbmc_project::Common::Error::InternalFailure; using UserNameDoesNotExist = sdbusplus::xyz::openbmc_project::User::Common::Error::UserNameDoesNotExist; class TestUserMgr : public testing::Test { public: sdbusplus::SdBusMock sdBusMock; sdbusplus::bus_t bus; MockManager mockManager; TestUserMgr() : bus(sdbusplus::get_mocked_new(&sdBusMock)), mockManager(bus, objpath) {} void createLocalUser(const std::string& userName, std::vector groupNames, const std::string& priv, bool enabled) { sdbusplus::message::object_path tempObjPath(usersObjPath); tempObjPath /= userName; std::string userObj(tempObjPath); if (enabled) { ON_CALL(mockManager, isUserEnabled) .WillByDefault(testing::Return(true)); } else { ON_CALL(mockManager, isUserEnabled) .WillByDefault(testing::Return(false)); } mockManager.usersList.emplace( userName, std::make_unique( mockManager.bus, userObj.c_str(), groupNames, priv, enabled, mockManager)); } DbusUserObj createPrivilegeMapperDbusObject(void) { DbusUserObj object; DbusUserObjValue objValue; DbusUserObjPath objPath("/xyz/openbmc_project/user/ldap/openldap"); DbusUserPropVariant enabled(true); DbusUserObjProperties property = {std::make_pair("Enabled", enabled)}; std::string intf = "xyz.openbmc_project.Object.Enable"; objValue.emplace(intf, property); object.emplace(objPath, objValue); DbusUserObjPath objectPath( "/xyz/openbmc_project/user/ldap/openldap/role_map/1"); std::string group = "ldapGroup"; std::string priv = "priv-admin"; DbusUserObjProperties properties = {std::make_pair("GroupName", group), std::make_pair("Privilege", priv)}; std::string interface = "xyz.openbmc_project.User.PrivilegeMapperEntry"; objValue.emplace(interface, properties); object.emplace(objectPath, objValue); return object; } DbusUserObj createLdapConfigObjectWithoutPrivilegeMapper(void) { DbusUserObj object; DbusUserObjValue objValue; DbusUserObjPath objPath("/xyz/openbmc_project/user/ldap/openldap"); DbusUserPropVariant enabled(true); DbusUserObjProperties property = {std::make_pair("Enabled", enabled)}; std::string intf = "xyz.openbmc_project.Object.Enable"; objValue.emplace(intf, property); object.emplace(objPath, objValue); return object; } }; TEST_F(TestUserMgr, ldapEntryDoesNotExist) { std::string userName = "user"; UserInfoMap userInfo; EXPECT_CALL(mockManager, getPrimaryGroup(userName)) .WillRepeatedly(Throw(UserNameDoesNotExist())); EXPECT_THROW(userInfo = mockManager.getUserInfo(userName), UserNameDoesNotExist); } TEST_F(TestUserMgr, localUser) { UserInfoMap userInfo; std::string userName = "testUser"; std::string privilege = "priv-admin"; std::vector groups{"testGroup"}; // Create local user createLocalUser(userName, groups, privilege, true); EXPECT_CALL(mockManager, userLockedForFailedAttempt(userName)).Times(1); userInfo = mockManager.getUserInfo(userName); EXPECT_EQ(privilege, std::get(userInfo["UserPrivilege"])); EXPECT_EQ(groups, std::get>(userInfo["UserGroups"])); EXPECT_EQ(true, std::get(userInfo["UserEnabled"])); EXPECT_EQ(false, std::get(userInfo["UserLockedForFailedAttempt"])); EXPECT_EQ(false, std::get(userInfo["UserPasswordExpired"])); EXPECT_EQ(false, std::get(userInfo["RemoteUser"])); } TEST_F(TestUserMgr, ldapUserWithPrivMapper) { UserInfoMap userInfo; std::string userName = "ldapUser"; std::string ldapGroup = "ldapGroup"; gid_t primaryGid = 1000; EXPECT_CALL(mockManager, getPrimaryGroup(userName)) .WillRepeatedly(Return(primaryGid)); // Create privilege mapper dbus object DbusUserObj object = createPrivilegeMapperDbusObject(); EXPECT_CALL(mockManager, getPrivilegeMapperObject()) .WillRepeatedly(Return(object)); EXPECT_CALL(mockManager, isGroupMember(userName, primaryGid, ldapGroup)) .WillRepeatedly(Return(true)); userInfo = mockManager.getUserInfo(userName); EXPECT_EQ(true, std::get(userInfo["RemoteUser"])); EXPECT_EQ("priv-admin", std::get(userInfo["UserPrivilege"])); } TEST_F(TestUserMgr, ldapUserWithoutPrivMapper) { using ::testing::_; UserInfoMap userInfo; std::string userName = "ldapUser"; std::string ldapGroup = "ldapGroup"; gid_t primaryGid = 1000; EXPECT_CALL(mockManager, getPrimaryGroup(userName)) .WillRepeatedly(Return(primaryGid)); // Create LDAP config object without privilege mapper DbusUserObj object = createLdapConfigObjectWithoutPrivilegeMapper(); EXPECT_CALL(mockManager, getPrivilegeMapperObject()) .WillRepeatedly(Return(object)); EXPECT_CALL(mockManager, isGroupMember(_, _, _)).Times(0); userInfo = mockManager.getUserInfo(userName); EXPECT_EQ(true, std::get(userInfo["RemoteUser"])); EXPECT_EQ("priv-user", std::get(userInfo["UserPrivilege"])); } TEST(GetCSVFromVector, EmptyVectorReturnsEmptyString) { EXPECT_EQ(getCSVFromVector({}), ""); } TEST(GetCSVFromVector, ElementsAreJoinedByComma) { EXPECT_EQ(getCSVFromVector(std::vector{"123"}), "123"); EXPECT_EQ(getCSVFromVector(std::vector{"123", "456"}), "123,456"); } TEST(RemoveStringFromCSV, WithoutDeleteStringReturnsFalse) { std::string expected = "whatever,https"; std::string str = expected; EXPECT_FALSE(removeStringFromCSV(str, "ssh")); EXPECT_EQ(str, expected); std::string empty; EXPECT_FALSE(removeStringFromCSV(empty, "ssh")); } TEST(RemoveStringFromCSV, WithDeleteStringReturnsTrue) { std::string expected = "whatever"; std::string str = "whatever,https"; EXPECT_TRUE(removeStringFromCSV(str, "https")); EXPECT_EQ(str, expected); str = "https"; EXPECT_TRUE(removeStringFromCSV(str, "https")); EXPECT_EQ(str, ""); } namespace { inline constexpr const char* objectRootInTest = "/xyz/openbmc_project/user"; // Fake configs; referenced configs on real BMC inline constexpr const char* rawFailLockConfig = R"( deny=2 unlock_time=3 )"; inline constexpr const char* rawPWHistoryConfig = R"( enforce_for_root remember=0 )"; inline constexpr const char* rawPWQualityConfig = R"( enforce_for_root minlen=8 difok=0 lcredit=0 ocredit=0 dcredit=0 ucredit=0 )"; } // namespace void dumpStringToFile(const std::string& str, const std::string& filePath) { std::ofstream outputFileStream; outputFileStream.exceptions(std::ofstream::failbit | std::ofstream::badbit | std::ofstream::eofbit); outputFileStream.open(filePath, std::ios::out); outputFileStream << str << "\n" << std::flush; outputFileStream.close(); } void removeFile(const std::string& filePath) { std::filesystem::remove(filePath); } class UserMgrInTest : public testing::Test, public UserMgr { public: UserMgrInTest() : UserMgr(busInTest, objectRootInTest) { tempFaillockConfigFile = "/tmp/test-data-XXXXXX"; mktemp(tempFaillockConfigFile.data()); EXPECT_NO_THROW( dumpStringToFile(rawFailLockConfig, tempFaillockConfigFile)); tempPWHistoryConfigFile = "/tmp/test-data-XXXXXX"; mktemp(tempPWHistoryConfigFile.data()); EXPECT_NO_THROW( dumpStringToFile(rawPWHistoryConfig, tempPWHistoryConfigFile)); tempPWQualityConfigFile = "/tmp/test-data-XXXXXX"; mktemp(tempPWQualityConfigFile.data()); EXPECT_NO_THROW( dumpStringToFile(rawPWQualityConfig, tempPWQualityConfigFile)); // Set config files to test files faillockConfigFile = tempFaillockConfigFile; pwHistoryConfigFile = tempPWHistoryConfigFile; pwQualityConfigFile = tempPWQualityConfigFile; ON_CALL(*this, executeUserAdd(testing::_, testing::_, testing::_, testing::Eq(true))) .WillByDefault([this]() { ON_CALL(*this, isUserEnabled).WillByDefault(testing::Return(true)); testing::Return(); }); ON_CALL(*this, executeUserAdd(testing::_, testing::_, testing::_, testing::Eq(false))) .WillByDefault([this]() { ON_CALL(*this, isUserEnabled).WillByDefault(testing::Return(false)); testing::Return(); }); ON_CALL(*this, executeUserDelete).WillByDefault(testing::Return()); ON_CALL(*this, executeUserClearFailRecords) .WillByDefault(testing::Return()); ON_CALL(*this, getIpmiUsersCount).WillByDefault(testing::Return(0)); ON_CALL(*this, executeUserRename).WillByDefault(testing::Return()); ON_CALL(*this, executeUserModify(testing::_, testing::_, testing::_)) .WillByDefault(testing::Return()); ON_CALL(*this, executeUserModifyUserEnable(testing::_, testing::Eq(true))) .WillByDefault([this]() { ON_CALL(*this, isUserEnabled).WillByDefault(testing::Return(true)); testing::Return(); }); ON_CALL(*this, executeUserModifyUserEnable(testing::_, testing::Eq(false))) .WillByDefault([this]() { ON_CALL(*this, isUserEnabled).WillByDefault(testing::Return(false)); testing::Return(); }); ON_CALL(*this, executeGroupCreation(testing::_)) .WillByDefault(testing::Return()); ON_CALL(*this, executeGroupDeletion(testing::_)) .WillByDefault(testing::Return()); ON_CALL(*this, executeGroupCreation).WillByDefault(testing::Return()); ON_CALL(*this, executeGroupDeletion).WillByDefault(testing::Return()); } ~UserMgrInTest() override { EXPECT_NO_THROW(removeFile(tempFaillockConfigFile)); EXPECT_NO_THROW(removeFile(tempPWHistoryConfigFile)); EXPECT_NO_THROW(removeFile(tempPWQualityConfigFile)); } MOCK_METHOD(void, executeUserAdd, (const char*, const char*, bool, bool), (override)); MOCK_METHOD(void, executeUserDelete, (const char*), (override)); MOCK_METHOD(void, executeUserClearFailRecords, (const char*), (override)); MOCK_METHOD(size_t, getIpmiUsersCount, (), (override)); MOCK_METHOD(void, executeUserRename, (const char*, const char*), (override)); MOCK_METHOD(void, executeUserModify, (const char*, const char*, bool), (override)); MOCK_METHOD(void, executeUserModifyUserEnable, (const char*, bool), (override)); MOCK_METHOD(std::vector, getFailedAttempt, (const char*), (override)); MOCK_METHOD(void, executeGroupCreation, (const char*), (override)); MOCK_METHOD(void, executeGroupDeletion, (const char*), (override)); MOCK_METHOD(bool, isUserEnabled, (const std::string& userName), (override)); protected: static sdbusplus::bus_t busInTest; std::string tempFaillockConfigFile; std::string tempPWHistoryConfigFile; std::string tempPWQualityConfigFile; }; sdbusplus::bus_t UserMgrInTest::busInTest = sdbusplus::bus::new_default(); TEST_F(UserMgrInTest, GetPamModuleConfValueOnSuccess) { std::string minlen; EXPECT_EQ(getPamModuleConfValue(tempPWQualityConfigFile, "minlen", minlen), 0); EXPECT_EQ(minlen, "8"); std::string deny; EXPECT_EQ(getPamModuleConfValue(tempFaillockConfigFile, "deny", deny), 0); EXPECT_EQ(deny, "2"); std::string remember; EXPECT_EQ( getPamModuleConfValue(tempPWHistoryConfigFile, "remember", remember), 0); EXPECT_EQ(remember, "0"); } TEST_F(UserMgrInTest, SetPamModuleConfValueOnSuccess) { EXPECT_EQ(setPamModuleConfValue(tempPWQualityConfigFile, "minlen", "16"), 0); std::string minlen; EXPECT_EQ(getPamModuleConfValue(tempPWQualityConfigFile, "minlen", minlen), 0); EXPECT_EQ(minlen, "16"); EXPECT_EQ(setPamModuleConfValue(tempFaillockConfigFile, "deny", "3"), 0); std::string deny; EXPECT_EQ(getPamModuleConfValue(tempFaillockConfigFile, "deny", deny), 0); EXPECT_EQ(deny, "3"); EXPECT_EQ(setPamModuleConfValue(tempPWHistoryConfigFile, "remember", "1"), 0); std::string remember; EXPECT_EQ( getPamModuleConfValue(tempPWHistoryConfigFile, "remember", remember), 0); EXPECT_EQ(remember, "1"); } TEST_F(UserMgrInTest, SetPamModuleConfValueTempFileOnSuccess) { EXPECT_EQ(setPamModuleConfValue(tempPWQualityConfigFile, "minlen", "16"), 0); std::string tmpFile = tempPWQualityConfigFile + "_tmp"; EXPECT_FALSE(std::filesystem::exists(tmpFile)); EXPECT_EQ(setPamModuleConfValue(tempFaillockConfigFile, "deny", "3"), 0); tmpFile = tempFaillockConfigFile + "_tmp"; EXPECT_FALSE(std::filesystem::exists(tmpFile)); EXPECT_EQ(setPamModuleConfValue(tempPWHistoryConfigFile, "remember", "1"), 0); tmpFile = tempPWHistoryConfigFile + "_tmp"; EXPECT_FALSE(std::filesystem::exists(tmpFile)); } TEST_F(UserMgrInTest, GetPamModuleConfValueOnFailure) { EXPECT_NO_THROW(dumpStringToFile("whatever", tempPWQualityConfigFile)); std::string minlen; EXPECT_EQ(getPamModuleConfValue(tempPWQualityConfigFile, "minlen", minlen), -1); EXPECT_NO_THROW(removeFile(tempPWQualityConfigFile)); EXPECT_EQ(getPamModuleConfValue(tempPWQualityConfigFile, "minlen", minlen), -1); EXPECT_NO_THROW(dumpStringToFile("whatever", tempFaillockConfigFile)); std::string deny; EXPECT_EQ(getPamModuleConfValue(tempFaillockConfigFile, "deny", deny), -1); EXPECT_NO_THROW(removeFile(tempFaillockConfigFile)); EXPECT_EQ(getPamModuleConfValue(tempFaillockConfigFile, "deny", deny), -1); EXPECT_NO_THROW(dumpStringToFile("whatever", tempPWHistoryConfigFile)); std::string remember; EXPECT_EQ( getPamModuleConfValue(tempPWHistoryConfigFile, "remember", remember), -1); EXPECT_NO_THROW(removeFile(tempPWHistoryConfigFile)); EXPECT_EQ( getPamModuleConfValue(tempPWHistoryConfigFile, "remember", remember), -1); } TEST_F(UserMgrInTest, SetPamModuleConfValueOnFailure) { EXPECT_NO_THROW(dumpStringToFile("whatever", tempPWQualityConfigFile)); EXPECT_EQ(setPamModuleConfValue(tempPWQualityConfigFile, "minlen", "16"), -1); EXPECT_NO_THROW(removeFile(tempPWQualityConfigFile)); EXPECT_EQ(setPamModuleConfValue(tempPWQualityConfigFile, "minlen", "16"), -1); EXPECT_NO_THROW(dumpStringToFile("whatever", tempFaillockConfigFile)); EXPECT_EQ(setPamModuleConfValue(tempFaillockConfigFile, "deny", "3"), -1); EXPECT_NO_THROW(removeFile(tempFaillockConfigFile)); EXPECT_EQ(setPamModuleConfValue(tempFaillockConfigFile, "deny", "3"), -1); EXPECT_NO_THROW(dumpStringToFile("whatever", tempPWHistoryConfigFile)); EXPECT_EQ(setPamModuleConfValue(tempPWHistoryConfigFile, "remember", "1"), -1); EXPECT_NO_THROW(removeFile(tempPWHistoryConfigFile)); EXPECT_EQ(setPamModuleConfValue(tempPWHistoryConfigFile, "remember", "1"), -1); } TEST_F(UserMgrInTest, SetPamModuleConfValueTempFileOnFailure) { EXPECT_NO_THROW(dumpStringToFile("whatever", tempPWQualityConfigFile)); EXPECT_EQ(setPamModuleConfValue(tempPWQualityConfigFile, "minlen", "16"), -1); std::string tmpFile = tempPWQualityConfigFile + "_tmp"; EXPECT_FALSE(std::filesystem::exists(tmpFile)); EXPECT_NO_THROW(removeFile(tempPWQualityConfigFile)); EXPECT_EQ(setPamModuleConfValue(tempPWQualityConfigFile, "minlen", "16"), -1); EXPECT_FALSE(std::filesystem::exists(tmpFile)); EXPECT_NO_THROW(dumpStringToFile("whatever", tempFaillockConfigFile)); EXPECT_EQ(setPamModuleConfValue(tempFaillockConfigFile, "deny", "3"), -1); tmpFile = tempFaillockConfigFile + "_tmp"; EXPECT_FALSE(std::filesystem::exists(tmpFile)); EXPECT_NO_THROW(removeFile(tempFaillockConfigFile)); EXPECT_EQ(setPamModuleConfValue(tempFaillockConfigFile, "deny", "3"), -1); EXPECT_FALSE(std::filesystem::exists(tmpFile)); EXPECT_NO_THROW(dumpStringToFile("whatever", tempPWHistoryConfigFile)); EXPECT_EQ(setPamModuleConfValue(tempPWHistoryConfigFile, "remember", "1"), -1); tmpFile = tempPWHistoryConfigFile + "_tmp"; EXPECT_FALSE(std::filesystem::exists(tmpFile)); EXPECT_NO_THROW(removeFile(tempPWHistoryConfigFile)); EXPECT_EQ(setPamModuleConfValue(tempPWHistoryConfigFile, "remember", "1"), -1); EXPECT_FALSE(std::filesystem::exists(tmpFile)); } TEST_F(UserMgrInTest, IsUserExistEmptyInputThrowsError) { EXPECT_THROW( isUserExist(""), sdbusplus::xyz::openbmc_project::Common::Error::InvalidArgument); } TEST_F(UserMgrInTest, ThrowForUserDoesNotExistThrowsError) { EXPECT_THROW(throwForUserDoesNotExist("whatever"), sdbusplus::xyz::openbmc_project::User::Common::Error:: UserNameDoesNotExist); } TEST_F(UserMgrInTest, ThrowForUserExistsThrowsError) { EXPECT_THROW( throwForUserExists("root"), sdbusplus::xyz::openbmc_project::User::Common::Error::UserNameExists); } TEST_F( UserMgrInTest, ThrowForUserNameConstraintsExceedIpmiMaxUserNameLenThrowsUserNameGroupFail) { std::string strWith17Chars(17, 'A'); EXPECT_THROW(throwForUserNameConstraints(strWith17Chars, {"ipmi"}), sdbusplus::xyz::openbmc_project::User::Common::Error:: UserNameGroupFail); } TEST_F( UserMgrInTest, ThrowForUserNameConstraintsExceedSystemMaxUserNameLenThrowsInvalidArgument) { std::string strWith31Chars(31, 'A'); EXPECT_THROW( throwForUserNameConstraints(strWith31Chars, {}), sdbusplus::xyz::openbmc_project::Common::Error::InvalidArgument); } TEST_F(UserMgrInTest, ThrowForUserNameConstraintsRegexMismatchThrowsInvalidArgument) { std::string startWithNumber = "0ABC"; std::string startWithDisallowedCharacter = "[test"; EXPECT_THROW( throwForUserNameConstraints(startWithNumber, {"ipmi"}), sdbusplus::xyz::openbmc_project::Common::Error::InvalidArgument); EXPECT_THROW( throwForUserNameConstraints(startWithDisallowedCharacter, {"ipmi"}), sdbusplus::xyz::openbmc_project::Common::Error::InvalidArgument); } TEST_F(UserMgrInTest, UserAddNotRootFailedWithInternalFailure) { EXPECT_THROW( UserMgr::executeUserAdd("user0", "ipmi,ssh", true, true), sdbusplus::xyz::openbmc_project::Common::Error::InternalFailure); } TEST_F(UserMgrInTest, UserDeleteNotRootFailedWithInternalFailure) { EXPECT_THROW( UserMgr::executeUserDelete("user0"), sdbusplus::xyz::openbmc_project::Common::Error::InternalFailure); } TEST_F(UserMgrInTest, ThrowForMaxGrpUserCountThrowsNoResourceWhenIpmiUserExceedLimit) { EXPECT_CALL(*this, getIpmiUsersCount()).WillOnce(Return(ipmiMaxUsers)); EXPECT_THROW( throwForMaxGrpUserCount({"ipmi"}), sdbusplus::xyz::openbmc_project::User::Common::Error::NoResource); } TEST_F(UserMgrInTest, CreateUserThrowsInternalFailureWhenExecuteUserAddFails) { EXPECT_CALL(*this, executeUserAdd) .WillOnce(testing::Throw( sdbusplus::xyz::openbmc_project::Common::Error::InternalFailure())); EXPECT_THROW( createUser("whatever", {"redfish"}, "", true), sdbusplus::xyz::openbmc_project::Common::Error::InternalFailure); } TEST_F(UserMgrInTest, DeleteUserThrowsInternalFailureWhenExecuteUserDeleteFails) { std::string username = "user"; EXPECT_NO_THROW( UserMgr::createUser(username, {"redfish", "ssh"}, "priv-user", true)); EXPECT_CALL(*this, executeUserDelete(testing::StrEq(username))) .WillOnce(testing::Throw( sdbusplus::xyz::openbmc_project::Common::Error::InternalFailure())) .WillOnce(testing::DoDefault()); EXPECT_THROW( deleteUser(username), sdbusplus::xyz::openbmc_project::Common::Error::InternalFailure); EXPECT_NO_THROW(UserMgr::deleteUser(username)); } TEST_F(UserMgrInTest, DeleteUserThrowsInternalFailureWhenExecuteUserClearFailRecords) { const char* username = "user"; EXPECT_NO_THROW( UserMgr::createUser(username, {"redfish", "ssh"}, "priv-user", true)); EXPECT_CALL(*this, executeUserClearFailRecords(testing::StrEq(username))) .WillOnce(testing::Throw( sdbusplus::xyz::openbmc_project::Common::Error::InternalFailure())) .WillOnce(testing::DoDefault()); EXPECT_THROW( deleteUser(username), sdbusplus::xyz::openbmc_project::Common::Error::InternalFailure); EXPECT_NO_THROW(UserMgr::deleteUser(username)); } TEST_F(UserMgrInTest, ThrowForInvalidPrivilegeThrowsWhenPrivilegeIsInvalid) { EXPECT_THROW( throwForInvalidPrivilege("whatever"), sdbusplus::xyz::openbmc_project::Common::Error::InvalidArgument); } TEST_F(UserMgrInTest, ThrowForInvalidPrivilegeNoThrowWhenPrivilegeIsValid) { EXPECT_NO_THROW(throwForInvalidPrivilege("priv-admin")); EXPECT_NO_THROW(throwForInvalidPrivilege("priv-operator")); EXPECT_NO_THROW(throwForInvalidPrivilege("priv-user")); } TEST_F(UserMgrInTest, ThrowForInvalidGroupsThrowsWhenGroupIsInvalid) { EXPECT_THROW( throwForInvalidGroups({"whatever"}), sdbusplus::xyz::openbmc_project::Common::Error::InvalidArgument); EXPECT_THROW( throwForInvalidGroups({"web"}), sdbusplus::xyz::openbmc_project::Common::Error::InvalidArgument); } TEST_F(UserMgrInTest, ThrowForInvalidGroupsNoThrowWhenGroupIsValid) { EXPECT_NO_THROW(throwForInvalidGroups({"ipmi"})); EXPECT_NO_THROW(throwForInvalidGroups({"ssh"})); EXPECT_NO_THROW(throwForInvalidGroups({"redfish"})); EXPECT_NO_THROW(throwForInvalidGroups({"hostconsole"})); } TEST_F(UserMgrInTest, RenameUserOnSuccess) { std::string username = "user001"; EXPECT_NO_THROW( UserMgr::createUser(username, {"redfish", "ssh"}, "priv-user", true)); std::string newUsername = "user002"; EXPECT_NO_THROW(UserMgr::renameUser(username, newUsername)); // old username doesn't exist EXPECT_THROW(getUserInfo(username), sdbusplus::xyz::openbmc_project::User::Common::Error:: UserNameDoesNotExist); UserInfoMap userInfo = getUserInfo(newUsername); EXPECT_EQ(std::get(userInfo["UserPrivilege"]), "priv-user"); EXPECT_THAT(std::get(userInfo["UserGroups"]), testing::UnorderedElementsAre("redfish", "ssh")); EXPECT_EQ(std::get(userInfo["UserEnabled"]), true); EXPECT_NO_THROW(UserMgr::deleteUser(newUsername)); } TEST_F(UserMgrInTest, RenameUserThrowsInternalFailureIfExecuteUserModifyFails) { std::string username = "user001"; EXPECT_NO_THROW( UserMgr::createUser(username, {"redfish", "ssh"}, "priv-user", true)); std::string newUsername = "user002"; EXPECT_CALL(*this, executeUserRename(testing::StrEq(username), testing::StrEq(newUsername))) .WillOnce(testing::Throw( sdbusplus::xyz::openbmc_project::Common::Error::InternalFailure())); EXPECT_THROW( UserMgr::renameUser(username, newUsername), sdbusplus::xyz::openbmc_project::Common::Error::InternalFailure); // The original user is unchanged UserInfoMap userInfo = getUserInfo(username); EXPECT_EQ(std::get(userInfo["UserPrivilege"]), "priv-user"); EXPECT_THAT(std::get(userInfo["UserGroups"]), testing::UnorderedElementsAre("redfish", "ssh")); EXPECT_EQ(std::get(userInfo["UserEnabled"]), true); EXPECT_NO_THROW(UserMgr::deleteUser(username)); } TEST_F(UserMgrInTest, DefaultUserModifyFailedWithInternalFailure) { EXPECT_THROW( UserMgr::executeUserRename("user0", "user1"), sdbusplus::xyz::openbmc_project::Common::Error::InternalFailure); EXPECT_THROW( UserMgr::executeUserModify("user0", "ssh", true), sdbusplus::xyz::openbmc_project::Common::Error::InternalFailure); } TEST_F(UserMgrInTest, UpdateGroupsAndPrivOnSuccess) { std::string username = "user001"; EXPECT_NO_THROW( UserMgr::createUser(username, {"redfish", "ssh"}, "priv-user", true)); EXPECT_NO_THROW( updateGroupsAndPriv(username, {"ipmi", "ssh"}, "priv-admin")); UserInfoMap userInfo = getUserInfo(username); EXPECT_EQ(std::get(userInfo["UserPrivilege"]), "priv-admin"); EXPECT_THAT(std::get(userInfo["UserGroups"]), testing::UnorderedElementsAre("ipmi", "ssh")); EXPECT_EQ(std::get(userInfo["UserEnabled"]), true); EXPECT_NO_THROW(UserMgr::deleteUser(username)); } TEST_F(UserMgrInTest, UpdateGroupsAndPrivThrowsInternalFailureIfExecuteUserModifyFail) { std::string username = "user001"; EXPECT_NO_THROW( UserMgr::createUser(username, {"redfish", "ssh"}, "priv-user", true)); EXPECT_CALL(*this, executeUserModify(testing::StrEq(username), testing::_, testing::_)) .WillOnce(testing::Throw( sdbusplus::xyz::openbmc_project::Common::Error::InternalFailure())); EXPECT_THROW( updateGroupsAndPriv(username, {"ipmi", "ssh"}, "priv-admin"), sdbusplus::xyz::openbmc_project::Common::Error::InternalFailure); EXPECT_NO_THROW(UserMgr::deleteUser(username)); } TEST_F(UserMgrInTest, MinPasswordLengthReturnsIfValueIsTheSame) { initializeAccountPolicy(); EXPECT_EQ(AccountPolicyIface::minPasswordLength(), 8); UserMgr::minPasswordLength(8); EXPECT_EQ(AccountPolicyIface::minPasswordLength(), 8); } TEST_F(UserMgrInTest, MinPasswordLengthRejectsTooShortPasswordWithInvalidArgument) { initializeAccountPolicy(); EXPECT_EQ(AccountPolicyIface::minPasswordLength(), 8); EXPECT_THROW( UserMgr::minPasswordLength(minPasswdLength - 1), sdbusplus::xyz::openbmc_project::Common::Error::InvalidArgument); EXPECT_EQ(AccountPolicyIface::minPasswordLength(), 8); } TEST_F(UserMgrInTest, MinPasswordLengthOnSuccess) { initializeAccountPolicy(); EXPECT_EQ(AccountPolicyIface::minPasswordLength(), 8); UserMgr::minPasswordLength(16); EXPECT_EQ(AccountPolicyIface::minPasswordLength(), 16); } TEST_F(UserMgrInTest, MinPasswordLengthOnFailure) { EXPECT_NO_THROW(dumpStringToFile("whatever", tempPWQualityConfigFile)); initializeAccountPolicy(); EXPECT_EQ(AccountPolicyIface::minPasswordLength(), 8); EXPECT_THROW( UserMgr::minPasswordLength(16), sdbusplus::xyz::openbmc_project::Common::Error::InternalFailure); EXPECT_EQ(AccountPolicyIface::minPasswordLength(), 8); } TEST_F(UserMgrInTest, RememberOldPasswordTimesReturnsIfValueIsTheSame) { initializeAccountPolicy(); EXPECT_EQ(AccountPolicyIface::rememberOldPasswordTimes(), 0); UserMgr::rememberOldPasswordTimes(8); EXPECT_EQ(AccountPolicyIface::rememberOldPasswordTimes(), 8); UserMgr::rememberOldPasswordTimes(8); EXPECT_EQ(AccountPolicyIface::rememberOldPasswordTimes(), 8); } TEST_F(UserMgrInTest, RememberOldPasswordTimesOnSuccess) { initializeAccountPolicy(); EXPECT_EQ(AccountPolicyIface::rememberOldPasswordTimes(), 0); UserMgr::rememberOldPasswordTimes(16); EXPECT_EQ(AccountPolicyIface::rememberOldPasswordTimes(), 16); } TEST_F(UserMgrInTest, RememberOldPasswordTimesOnFailure) { EXPECT_NO_THROW(dumpStringToFile("whatever", tempPWHistoryConfigFile)); initializeAccountPolicy(); EXPECT_EQ(AccountPolicyIface::rememberOldPasswordTimes(), 0); EXPECT_THROW( UserMgr::rememberOldPasswordTimes(16), sdbusplus::xyz::openbmc_project::Common::Error::InternalFailure); EXPECT_EQ(AccountPolicyIface::rememberOldPasswordTimes(), 0); } TEST_F(UserMgrInTest, MaxLoginAttemptBeforeLockoutReturnsIfValueIsTheSame) { initializeAccountPolicy(); EXPECT_EQ(AccountPolicyIface::maxLoginAttemptBeforeLockout(), 2); UserMgr::maxLoginAttemptBeforeLockout(2); EXPECT_EQ(AccountPolicyIface::maxLoginAttemptBeforeLockout(), 2); } TEST_F(UserMgrInTest, MaxLoginAttemptBeforeLockoutOnSuccess) { initializeAccountPolicy(); EXPECT_EQ(AccountPolicyIface::maxLoginAttemptBeforeLockout(), 2); UserMgr::maxLoginAttemptBeforeLockout(16); EXPECT_EQ(AccountPolicyIface::maxLoginAttemptBeforeLockout(), 16); } TEST_F(UserMgrInTest, MaxLoginAttemptBeforeLockoutOnFailure) { initializeAccountPolicy(); EXPECT_NO_THROW(dumpStringToFile("whatever", tempFaillockConfigFile)); EXPECT_THROW( UserMgr::maxLoginAttemptBeforeLockout(16), sdbusplus::xyz::openbmc_project::Common::Error::InternalFailure); EXPECT_EQ(AccountPolicyIface::maxLoginAttemptBeforeLockout(), 2); } TEST_F(UserMgrInTest, AccountUnlockTimeoutReturnsIfValueIsTheSame) { initializeAccountPolicy(); EXPECT_EQ(AccountPolicyIface::accountUnlockTimeout(), 3); UserMgr::accountUnlockTimeout(3); EXPECT_EQ(AccountPolicyIface::accountUnlockTimeout(), 3); } TEST_F(UserMgrInTest, AccountUnlockTimeoutOnSuccess) { initializeAccountPolicy(); EXPECT_EQ(AccountPolicyIface::accountUnlockTimeout(), 3); UserMgr::accountUnlockTimeout(16); EXPECT_EQ(AccountPolicyIface::accountUnlockTimeout(), 16); } TEST_F(UserMgrInTest, AccountUnlockTimeoutOnFailure) { initializeAccountPolicy(); EXPECT_NO_THROW(dumpStringToFile("whatever", tempFaillockConfigFile)); EXPECT_THROW( UserMgr::accountUnlockTimeout(16), sdbusplus::xyz::openbmc_project::Common::Error::InternalFailure); EXPECT_EQ(AccountPolicyIface::accountUnlockTimeout(), 3); } TEST_F(UserMgrInTest, UserEnableOnSuccess) { std::string username = "user001"; EXPECT_NO_THROW( UserMgr::createUser(username, {"redfish", "ssh"}, "priv-user", true)); UserInfoMap userInfo = getUserInfo(username); EXPECT_EQ(std::get(userInfo["UserEnabled"]), true); EXPECT_NO_THROW(userEnable(username, false)); userInfo = getUserInfo(username); EXPECT_EQ(std::get(userInfo["UserEnabled"]), false); EXPECT_NO_THROW(UserMgr::deleteUser(username)); } TEST_F(UserMgrInTest, CreateDeleteUserSuccessForHostConsole) { std::string username = "user001"; EXPECT_NO_THROW( UserMgr::createUser(username, {"hostconsole"}, "priv-user", true)); EXPECT_NO_THROW(UserMgr::deleteUser(username)); EXPECT_NO_THROW( UserMgr::createUser(username, {"hostconsole"}, "priv-admin", true)); EXPECT_NO_THROW(UserMgr::deleteUser(username)); EXPECT_NO_THROW( UserMgr::createUser(username, {"hostconsole"}, "priv-operator", true)); EXPECT_NO_THROW(UserMgr::deleteUser(username)); } TEST_F(UserMgrInTest, UserEnableThrowsInternalFailureIfExecuteUserModifyFail) { std::string username = "user001"; EXPECT_NO_THROW( UserMgr::createUser(username, {"redfish", "ssh"}, "priv-user", true)); UserInfoMap userInfo = getUserInfo(username); EXPECT_EQ(std::get(userInfo["UserEnabled"]), true); EXPECT_CALL(*this, executeUserModifyUserEnable(testing::StrEq(username), testing::Eq(false))) .WillOnce(testing::Throw( sdbusplus::xyz::openbmc_project::Common::Error::InternalFailure())); EXPECT_THROW( userEnable(username, false), sdbusplus::xyz::openbmc_project::Common::Error::InternalFailure); userInfo = getUserInfo(username); // Stay unchanged EXPECT_EQ(std::get(userInfo["UserEnabled"]), true); EXPECT_NO_THROW(UserMgr::deleteUser(username)); } TEST_F( UserMgrInTest, UserLockedForFailedAttemptReturnsFalseIfMaxLoginAttemptBeforeLockoutIsZero) { EXPECT_FALSE(userLockedForFailedAttempt("whatever")); } TEST_F(UserMgrInTest, UserLockedForFailedAttemptZeroFailuresReturnsFalse) { std::string username = "user001"; initializeAccountPolicy(); // Example output from BMC // root:~# faillock --user root // root: // When Type Source Valid std::vector output = {"whatever", "When Type Source Valid"}; EXPECT_CALL(*this, getFailedAttempt(testing::StrEq(username.c_str()))) .WillOnce(testing::Return(output)); EXPECT_FALSE(userLockedForFailedAttempt(username)); } TEST_F(UserMgrInTest, UserLockedForFailedAttemptFailIfGetFailedAttemptFail) { std::string username = "user001"; initializeAccountPolicy(); EXPECT_CALL(*this, getFailedAttempt(testing::StrEq(username.c_str()))) .WillOnce(testing::Throw( sdbusplus::xyz::openbmc_project::Common::Error::InternalFailure())); EXPECT_THROW( userLockedForFailedAttempt(username), sdbusplus::xyz::openbmc_project::Common::Error::InternalFailure); } TEST_F(UserMgrInTest, UserLockedForFailedAttemptThrowsInternalFailureIfWrongDateFormat) { std::string username = "user001"; initializeAccountPolicy(); // Choose a date in the past. std::vector output = {"whatever", "10/24/2002 00:00:00 type source V"}; EXPECT_CALL(*this, getFailedAttempt(testing::StrEq(username.c_str()))) .WillOnce(testing::Return(output)); EXPECT_THROW( userLockedForFailedAttempt(username), sdbusplus::xyz::openbmc_project::Common::Error::InternalFailure); } TEST_F(UserMgrInTest, UserLockedForFailedAttemptReturnsFalseIfLastFailTimeHasTimedOut) { std::string username = "user001"; initializeAccountPolicy(); // Choose a date in the past. std::vector output = {"whatever", "2002-10-24 00:00:00 type source V"}; EXPECT_CALL(*this, getFailedAttempt(testing::StrEq(username.c_str()))) .WillOnce(testing::Return(output)); EXPECT_EQ(userLockedForFailedAttempt(username), false); } TEST_F(UserMgrInTest, CheckAndThrowForDisallowedGroupCreationOnSuccess) { // Base Redfish Roles EXPECT_NO_THROW( checkAndThrowForDisallowedGroupCreation("openbmc_rfr_Administrator")); EXPECT_NO_THROW( checkAndThrowForDisallowedGroupCreation("openbmc_rfr_Operator")); EXPECT_NO_THROW( checkAndThrowForDisallowedGroupCreation("openbmc_rfr_ReadOnly")); // Base Redfish Privileges EXPECT_NO_THROW( checkAndThrowForDisallowedGroupCreation("openbmc_rfp_Login")); EXPECT_NO_THROW(checkAndThrowForDisallowedGroupCreation( "openbmc_rfp_ConfigureManager")); EXPECT_NO_THROW( checkAndThrowForDisallowedGroupCreation("openbmc_rfp_ConfigureUsers")); EXPECT_NO_THROW( checkAndThrowForDisallowedGroupCreation("openbmc_rfp_ConfigureSelf")); EXPECT_NO_THROW(checkAndThrowForDisallowedGroupCreation( "openbmc_rfp_ConfigureComponents")); // OEM Redfish Roles EXPECT_NO_THROW( checkAndThrowForDisallowedGroupCreation("openbmc_orfr_PowerService")); // OEM Redfish Privileges EXPECT_NO_THROW( checkAndThrowForDisallowedGroupCreation("openbmc_orfp_PowerService")); } TEST_F(UserMgrInTest, CheckAndThrowForDisallowedGroupCreationThrowsIfGroupNameTooLong) { std::string groupName(maxSystemGroupNameLength + 1, 'A'); EXPECT_THROW( checkAndThrowForDisallowedGroupCreation(groupName), sdbusplus::xyz::openbmc_project::Common::Error::InvalidArgument); } TEST_F( UserMgrInTest, CheckAndThrowForDisallowedGroupCreationThrowsIfGroupNameHasDisallowedCharacters) { EXPECT_THROW( checkAndThrowForDisallowedGroupCreation("openbmc_rfp_?owerService"), sdbusplus::xyz::openbmc_project::Common::Error::InvalidArgument); EXPECT_THROW( checkAndThrowForDisallowedGroupCreation("openbmc_rfp_-owerService"), sdbusplus::xyz::openbmc_project::Common::Error::InvalidArgument); } TEST_F( UserMgrInTest, CheckAndThrowForDisallowedGroupCreationThrowsIfGroupNameHasDisallowedPrefix) { EXPECT_THROW( checkAndThrowForDisallowedGroupCreation("google_rfp_"), sdbusplus::xyz::openbmc_project::Common::Error::InvalidArgument); EXPECT_THROW( checkAndThrowForDisallowedGroupCreation("com_rfp_"), sdbusplus::xyz::openbmc_project::Common::Error::InvalidArgument); } TEST_F(UserMgrInTest, CheckAndThrowForMaxGroupCountOnSuccess) { constexpr size_t predefGroupCount = 4; EXPECT_THAT(allGroups().size(), predefGroupCount); for (size_t i = 0; i < maxSystemGroupCount - predefGroupCount; ++i) { std::string groupName = "openbmc_rfr_role"; groupName += std::to_string(i); EXPECT_NO_THROW(createGroup(groupName)); } EXPECT_THROW( createGroup("openbmc_rfr_AnotherRole"), sdbusplus::xyz::openbmc_project::User::Common::Error::NoResource); for (size_t i = 0; i < maxSystemGroupCount - predefGroupCount; ++i) { std::string groupName = "openbmc_rfr_role"; groupName += std::to_string(i); EXPECT_NO_THROW(deleteGroup(groupName)); } } TEST_F(UserMgrInTest, CheckAndThrowForGroupExist) { std::string groupName = "openbmc_rfr_role"; EXPECT_NO_THROW(createGroup(groupName)); EXPECT_THROW( createGroup(groupName), sdbusplus::xyz::openbmc_project::User::Common::Error::GroupNameExists); EXPECT_NO_THROW(deleteGroup(groupName)); } TEST_F(UserMgrInTest, ByDefaultAllGroupsArePredefinedGroups) { EXPECT_THAT(allGroups(), testing::UnorderedElementsAre( "redfish", "ipmi", "ssh", "hostconsole")); } TEST_F(UserMgrInTest, AddGroupThrowsIfPreDefinedGroupAdd) { EXPECT_THROW( createGroup("ipmi"), sdbusplus::xyz::openbmc_project::User::Common::Error::GroupNameExists); EXPECT_THROW( createGroup("redfish"), sdbusplus::xyz::openbmc_project::User::Common::Error::GroupNameExists); EXPECT_THROW( createGroup("ssh"), sdbusplus::xyz::openbmc_project::User::Common::Error::GroupNameExists); EXPECT_THROW( createGroup("hostconsole"), sdbusplus::xyz::openbmc_project::User::Common::Error::GroupNameExists); } TEST_F(UserMgrInTest, DeleteGroupThrowsIfGroupIsNotAllowedToChange) { EXPECT_THROW( deleteGroup("ipmi"), sdbusplus::xyz::openbmc_project::Common::Error::InvalidArgument); EXPECT_THROW( deleteGroup("redfish"), sdbusplus::xyz::openbmc_project::Common::Error::InvalidArgument); EXPECT_THROW( deleteGroup("ssh"), sdbusplus::xyz::openbmc_project::Common::Error::InvalidArgument); EXPECT_THROW( deleteGroup("hostconsole"), sdbusplus::xyz::openbmc_project::Common::Error::InvalidArgument); } TEST_F(UserMgrInTest, CreateGroupThrowsInternalFailureWhenExecuteGroupCreateFails) { EXPECT_CALL(*this, executeGroupCreation) .WillOnce(testing::Throw( sdbusplus::xyz::openbmc_project::Common::Error::InternalFailure())); EXPECT_THROW( createGroup("openbmc_rfr_role1"), sdbusplus::xyz::openbmc_project::Common::Error::InternalFailure); } TEST_F(UserMgrInTest, DeleteGroupThrowsInternalFailureWhenExecuteGroupDeleteFails) { std::string groupName = "openbmc_rfr_role1"; EXPECT_NO_THROW(UserMgr::createGroup(groupName)); EXPECT_CALL(*this, executeGroupDeletion(testing::StrEq(groupName))) .WillOnce(testing::Throw( sdbusplus::xyz::openbmc_project::Common::Error::InternalFailure())) .WillOnce(testing::DoDefault()); EXPECT_THROW( deleteGroup(groupName), sdbusplus::xyz::openbmc_project::Common::Error::InternalFailure); EXPECT_NO_THROW(UserMgr::deleteGroup(groupName)); } TEST_F(UserMgrInTest, CheckAndThrowForGroupNotExist) { EXPECT_THROW(deleteGroup("whatever"), sdbusplus::xyz::openbmc_project::User::Common::Error:: GroupNameDoesNotExist); } TEST(ReadAllGroupsOnSystemTest, OnlyReturnsPredefinedGroups) { EXPECT_THAT( UserMgr::readAllGroupsOnSystem(), testing::UnorderedElementsAre("redfish", "ipmi", "ssh", "hostconsole")); } } // namespace user } // namespace phosphor