blob: f07b00c1e311b724d7a80fcf26d070df7cc84865 [file] [log] [blame]
#include "mock_user_mgr.hpp"
#include "user_mgr.hpp"
#include <sdbusplus/test/sdbus_mock.hpp>
#include <xyz/openbmc_project/Common/error.hpp>
#include <xyz/openbmc_project/User/Common/error.hpp>
#include <exception>
#include <filesystem>
#include <fstream>
#include <vector>
#include <gmock/gmock.h>
#include <gtest/gtest.h>
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<std::string> 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<phosphor::user::Users>(
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<std::string> 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<std::string>(userInfo["UserPrivilege"]));
EXPECT_EQ(groups,
std::get<std::vector<std::string>>(userInfo["UserGroups"]));
EXPECT_EQ(true, std::get<bool>(userInfo["UserEnabled"]));
EXPECT_EQ(false, std::get<bool>(userInfo["UserLockedForFailedAttempt"]));
EXPECT_EQ(false, std::get<bool>(userInfo["UserPasswordExpired"]));
EXPECT_EQ(false, std::get<bool>(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<bool>(userInfo["RemoteUser"]));
EXPECT_EQ("priv-admin", std::get<std::string>(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<bool>(userInfo["RemoteUser"]));
EXPECT_EQ("priv-user", std::get<std::string>(userInfo["UserPrivilege"]));
}
TEST(GetCSVFromVector, EmptyVectorReturnsEmptyString)
{
EXPECT_EQ(getCSVFromVector({}), "");
}
TEST(GetCSVFromVector, ElementsAreJoinedByComma)
{
EXPECT_EQ(getCSVFromVector(std::vector<std::string>{"123"}), "123");
EXPECT_EQ(getCSVFromVector(std::vector<std::string>{"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<std::string>, 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<Privilege>(userInfo["UserPrivilege"]), "priv-user");
EXPECT_THAT(std::get<GroupList>(userInfo["UserGroups"]),
testing::UnorderedElementsAre("redfish", "ssh"));
EXPECT_EQ(std::get<UserEnabled>(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<Privilege>(userInfo["UserPrivilege"]), "priv-user");
EXPECT_THAT(std::get<GroupList>(userInfo["UserGroups"]),
testing::UnorderedElementsAre("redfish", "ssh"));
EXPECT_EQ(std::get<UserEnabled>(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<Privilege>(userInfo["UserPrivilege"]), "priv-admin");
EXPECT_THAT(std::get<GroupList>(userInfo["UserGroups"]),
testing::UnorderedElementsAre("ipmi", "ssh"));
EXPECT_EQ(std::get<UserEnabled>(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<UserEnabled>(userInfo["UserEnabled"]), true);
EXPECT_NO_THROW(userEnable(username, false));
userInfo = getUserInfo(username);
EXPECT_EQ(std::get<UserEnabled>(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<UserEnabled>(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<UserEnabled>(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<std::string> 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<std::string> 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<std::string> 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