blob: 474e0553e7d37208801bb288ae6b76325e90761c [file] [log] [blame]
raviteja-b8cc44052019-02-27 23:29:36 -06001#include "mock_user_mgr.hpp"
Nan Zhoue47c09d2022-10-25 00:06:41 +00002#include "user_mgr.hpp"
Patrick Williams9638afb2021-02-22 17:16:24 -06003
Ravi Teja417c0892020-08-22 08:04:01 -05004#include <sdbusplus/test/sdbus_mock.hpp>
Patrick Williams9638afb2021-02-22 17:16:24 -06005#include <xyz/openbmc_project/Common/error.hpp>
6#include <xyz/openbmc_project/User/Common/error.hpp>
7
8#include <exception>
Nan Zhoue48085d2022-10-25 00:07:04 +00009#include <filesystem>
10#include <fstream>
Patrick Williams9638afb2021-02-22 17:16:24 -060011
12#include <gtest/gtest.h>
raviteja-b8cc44052019-02-27 23:29:36 -060013
14namespace phosphor
15{
16namespace user
17{
18
19using ::testing::Return;
20
21using InternalFailure =
22 sdbusplus::xyz::openbmc_project::Common::Error::InternalFailure;
23
24class TestUserMgr : public testing::Test
25{
26 public:
Nan Zhou78d85042022-08-29 17:50:22 +000027 sdbusplus::SdBusMock sdBusMock;
Patrick Williamsb3ef4e12022-07-22 19:26:55 -050028 sdbusplus::bus_t bus;
raviteja-b8cc44052019-02-27 23:29:36 -060029 MockManager mockManager;
30
31 TestUserMgr() :
Nan Zhou78d85042022-08-29 17:50:22 +000032 bus(sdbusplus::get_mocked_new(&sdBusMock)), mockManager(bus, objpath)
Patrick Williams9638afb2021-02-22 17:16:24 -060033 {}
raviteja-b8cc44052019-02-27 23:29:36 -060034
Patrick Williams9638afb2021-02-22 17:16:24 -060035 void createLocalUser(const std::string& userName,
raviteja-b8cc44052019-02-27 23:29:36 -060036 std::vector<std::string> groupNames,
Patrick Williams9638afb2021-02-22 17:16:24 -060037 const std::string& priv, bool enabled)
raviteja-b8cc44052019-02-27 23:29:36 -060038 {
P Dheeraj Srujan Kumarb01e2fe2021-12-13 09:43:28 +053039 sdbusplus::message::object_path tempObjPath(usersObjPath);
40 tempObjPath /= userName;
41 std::string userObj(tempObjPath);
raviteja-b8cc44052019-02-27 23:29:36 -060042 mockManager.usersList.emplace(
Nan Zhou78d85042022-08-29 17:50:22 +000043 userName, std::make_unique<phosphor::user::Users>(
raviteja-b8cc44052019-02-27 23:29:36 -060044 mockManager.bus, userObj.c_str(), groupNames, priv,
Nan Zhou78d85042022-08-29 17:50:22 +000045 enabled, mockManager));
raviteja-b8cc44052019-02-27 23:29:36 -060046 }
47
48 DbusUserObj createPrivilegeMapperDbusObject(void)
49 {
50 DbusUserObj object;
51 DbusUserObjValue objValue;
Ravi Teja5fe724a2019-05-07 05:14:42 -050052
Nan Zhou78d85042022-08-29 17:50:22 +000053 DbusUserObjPath objPath("/xyz/openbmc_project/user/ldap/openldap");
Ravi Teja5fe724a2019-05-07 05:14:42 -050054 DbusUserPropVariant enabled(true);
55 DbusUserObjProperties property = {std::make_pair("Enabled", enabled)};
56 std::string intf = "xyz.openbmc_project.Object.Enable";
57 objValue.emplace(intf, property);
Nan Zhou78d85042022-08-29 17:50:22 +000058 object.emplace(objPath, objValue);
Ravi Teja5fe724a2019-05-07 05:14:42 -050059
Nan Zhou78d85042022-08-29 17:50:22 +000060 DbusUserObjPath objectPath(
Ravi Teja5fe724a2019-05-07 05:14:42 -050061 "/xyz/openbmc_project/user/ldap/openldap/role_map/1");
62 std::string group = "ldapGroup";
63 std::string priv = "priv-admin";
raviteja-b8cc44052019-02-27 23:29:36 -060064 DbusUserObjProperties properties = {std::make_pair("GroupName", group),
65 std::make_pair("Privilege", priv)};
66 std::string interface = "xyz.openbmc_project.User.PrivilegeMapperEntry";
67
68 objValue.emplace(interface, properties);
Nan Zhou78d85042022-08-29 17:50:22 +000069 object.emplace(objectPath, objValue);
raviteja-b8cc44052019-02-27 23:29:36 -060070
71 return object;
72 }
Ravi Teja5fe724a2019-05-07 05:14:42 -050073
74 DbusUserObj createLdapConfigObjectWithoutPrivilegeMapper(void)
75 {
76 DbusUserObj object;
77 DbusUserObjValue objValue;
78
Nan Zhou78d85042022-08-29 17:50:22 +000079 DbusUserObjPath objPath("/xyz/openbmc_project/user/ldap/openldap");
Ravi Teja5fe724a2019-05-07 05:14:42 -050080 DbusUserPropVariant enabled(true);
81 DbusUserObjProperties property = {std::make_pair("Enabled", enabled)};
82 std::string intf = "xyz.openbmc_project.Object.Enable";
83 objValue.emplace(intf, property);
Nan Zhou78d85042022-08-29 17:50:22 +000084 object.emplace(objPath, objValue);
Ravi Teja5fe724a2019-05-07 05:14:42 -050085 return object;
86 }
raviteja-b8cc44052019-02-27 23:29:36 -060087};
88
89TEST_F(TestUserMgr, ldapEntryDoesNotExist)
90{
91 std::string userName = "user";
92 UserInfoMap userInfo;
93
94 EXPECT_CALL(mockManager, getLdapGroupName(userName))
95 .WillRepeatedly(Return(""));
96 EXPECT_THROW(userInfo = mockManager.getUserInfo(userName), InternalFailure);
97}
98
99TEST_F(TestUserMgr, localUser)
100{
101 UserInfoMap userInfo;
102 std::string userName = "testUser";
103 std::string privilege = "priv-admin";
104 std::vector<std::string> groups{"testGroup"};
105 // Create local user
106 createLocalUser(userName, groups, privilege, true);
107 EXPECT_CALL(mockManager, userLockedForFailedAttempt(userName)).Times(1);
108 userInfo = mockManager.getUserInfo(userName);
109
110 EXPECT_EQ(privilege, std::get<std::string>(userInfo["UserPrivilege"]));
111 EXPECT_EQ(groups,
112 std::get<std::vector<std::string>>(userInfo["UserGroups"]));
113 EXPECT_EQ(true, std::get<bool>(userInfo["UserEnabled"]));
114 EXPECT_EQ(false, std::get<bool>(userInfo["UserLockedForFailedAttempt"]));
Joseph Reynolds3ab6cc22020-03-03 14:09:03 -0600115 EXPECT_EQ(false, std::get<bool>(userInfo["UserPasswordExpired"]));
raviteja-b8cc44052019-02-27 23:29:36 -0600116 EXPECT_EQ(false, std::get<bool>(userInfo["RemoteUser"]));
117}
118
119TEST_F(TestUserMgr, ldapUserWithPrivMapper)
120{
121 UserInfoMap userInfo;
122 std::string userName = "ldapUser";
123 std::string ldapGroup = "ldapGroup";
124
125 EXPECT_CALL(mockManager, getLdapGroupName(userName))
126 .WillRepeatedly(Return(ldapGroup));
127 // Create privilege mapper dbus object
128 DbusUserObj object = createPrivilegeMapperDbusObject();
129 EXPECT_CALL(mockManager, getPrivilegeMapperObject())
130 .WillRepeatedly(Return(object));
131 userInfo = mockManager.getUserInfo(userName);
132 EXPECT_EQ(true, std::get<bool>(userInfo["RemoteUser"]));
133 EXPECT_EQ("priv-admin", std::get<std::string>(userInfo["UserPrivilege"]));
134}
135
136TEST_F(TestUserMgr, ldapUserWithoutPrivMapper)
137{
138 UserInfoMap userInfo;
139 std::string userName = "ldapUser";
140 std::string ldapGroup = "ldapGroup";
raviteja-b8cc44052019-02-27 23:29:36 -0600141
142 EXPECT_CALL(mockManager, getLdapGroupName(userName))
143 .WillRepeatedly(Return(ldapGroup));
Ravi Teja5fe724a2019-05-07 05:14:42 -0500144 // Create LDAP config object without privilege mapper
145 DbusUserObj object = createLdapConfigObjectWithoutPrivilegeMapper();
raviteja-b8cc44052019-02-27 23:29:36 -0600146 EXPECT_CALL(mockManager, getPrivilegeMapperObject())
147 .WillRepeatedly(Return(object));
148 userInfo = mockManager.getUserInfo(userName);
149 EXPECT_EQ(true, std::get<bool>(userInfo["RemoteUser"]));
150 EXPECT_EQ("", std::get<std::string>(userInfo["UserPrivilege"]));
151}
Nan Zhoue47c09d2022-10-25 00:06:41 +0000152
153TEST(GetCSVFromVector, EmptyVectorReturnsEmptyString)
154{
155 EXPECT_EQ(getCSVFromVector({}), "");
156}
157
158TEST(GetCSVFromVector, ElementsAreJoinedByComma)
159{
160 EXPECT_EQ(getCSVFromVector(std::vector<std::string>{"123"}), "123");
161 EXPECT_EQ(getCSVFromVector(std::vector<std::string>{"123", "456"}),
162 "123,456");
163}
164
Nan Zhou332fb9d2022-10-25 00:07:03 +0000165TEST(RemoveStringFromCSV, WithoutDeleteStringReturnsFalse)
166{
167 std::string expected = "whatever,https";
168 std::string str = expected;
169 EXPECT_FALSE(removeStringFromCSV(str, "ssh"));
170 EXPECT_EQ(str, expected);
171
172 std::string empty;
173 EXPECT_FALSE(removeStringFromCSV(empty, "ssh"));
174}
175
176TEST(RemoveStringFromCSV, WithDeleteStringReturnsTrue)
177{
178 std::string expected = "whatever";
179 std::string str = "whatever,https";
180 EXPECT_TRUE(removeStringFromCSV(str, "https"));
181 EXPECT_EQ(str, expected);
182
183 str = "https";
184 EXPECT_TRUE(removeStringFromCSV(str, "https"));
185 EXPECT_EQ(str, "");
186}
187
Nan Zhoue48085d2022-10-25 00:07:04 +0000188namespace
189{
190inline constexpr const char* objectRootInTest = "/xyz/openbmc_project/user";
191
192// Fake config; referenced config on real BMC
193inline constexpr const char* rawConfig = R"(
194#
195# /etc/pam.d/common-password - password-related modules common to all services
196#
197# This file is included from other service-specific PAM config files,
198# and should contain a list of modules that define the services to be
199# used to change user passwords. The default is pam_unix.
200
201# Explanation of pam_unix options:
202#
203# The "sha512" option enables salted SHA512 passwords. Without this option,
204# the default is Unix crypt. Prior releases used the option "md5".
205#
206# The "obscure" option replaces the old `OBSCURE_CHECKS_ENAB' option in
207# login.defs.
208#
209# See the pam_unix manpage for other options.
210
211# here are the per-package modules (the "Primary" block)
Nan Zhou784aecd2022-10-25 00:07:16 +0000212password [success=ok default=die] pam_tally2.so debug enforce_for_root reject_username minlen=8 difok=0 lcredit=0 ocredit=0 dcredit=0 ucredit=0 deny=2 unlock_time=3 #some comments
Nan Zhoue48085d2022-10-25 00:07:04 +0000213password [success=ok default=die] pam_cracklib.so debug enforce_for_root reject_username minlen=8 difok=0 lcredit=0 ocredit=0 dcredit=0 ucredit=0 #some comments
214password [success=ok default=die] pam_ipmicheck.so spec_grp_name=ipmi use_authtok
215password [success=ok ignore=ignore default=die] pam_pwhistory.so debug enforce_for_root remember=0 use_authtok
216password [success=ok default=die] pam_unix.so sha512 use_authtok
217password [success=1 default=die] pam_ipmisave.so spec_grp_name=ipmi spec_pass_file=/etc/ipmi_pass key_file=/etc/key_file
218# here's the fallback if no module succeeds
219password requisite pam_deny.so
220# prime the stack with a positive return value if there isn't one already;
221# this avoids us returning an error just because nothing sets a success code
222# since the modules above will each just jump around
223password required pam_permit.so
224# and here are more per-package modules (the "Additional" block)
225)";
226} // namespace
227
228void dumpStringToFile(const std::string& str, const std::string& filePath)
229{
230 std::ofstream outputFileStream;
231
232 outputFileStream.exceptions(std::ofstream::failbit | std::ofstream::badbit |
233 std::ofstream::eofbit);
234
235 outputFileStream.open(filePath, std::ios::out);
236 outputFileStream << str << "\n" << std::flush;
237 outputFileStream.close();
238}
239
240void removeFile(const std::string& filePath)
241{
242 std::filesystem::remove(filePath);
243}
244
245class UserMgrInTest : public testing::Test, public UserMgr
246{
247 public:
248 UserMgrInTest() : UserMgr(busInTest, objectRootInTest)
249 {
250 tempPamConfigFile = "/tmp/test-data-XXXXXX";
251 mktemp(tempPamConfigFile.data());
252 EXPECT_NO_THROW(dumpStringToFile(rawConfig, tempPamConfigFile));
253 // Set config files to test files
254 pamPasswdConfigFile = tempPamConfigFile;
255 pamAuthConfigFile = tempPamConfigFile;
Nan Zhou49c81362022-10-25 00:07:08 +0000256
257 ON_CALL(*this, executeUserAdd).WillByDefault(testing::Return());
258
259 ON_CALL(*this, executeUserDelete).WillByDefault(testing::Return());
260
261 ON_CALL(*this, getIpmiUsersCount).WillByDefault(testing::Return(0));
Nan Zhouf25443e2022-10-25 00:07:11 +0000262
263 ON_CALL(*this, executeUserRename).WillByDefault(testing::Return());
Nan Zhoufef63032022-10-25 00:07:12 +0000264
265 ON_CALL(*this, executeUserModify(testing::_, testing::_, testing::_))
266 .WillByDefault(testing::Return());
Nan Zhou6b6f2d82022-10-25 00:07:17 +0000267
268 ON_CALL(*this, executeUserModifyUserEnable)
269 .WillByDefault(testing::Return());
Nan Zhoue48085d2022-10-25 00:07:04 +0000270 }
271
272 ~UserMgrInTest() override
273 {
274 EXPECT_NO_THROW(removeFile(tempPamConfigFile));
275 }
276
Nan Zhou49c81362022-10-25 00:07:08 +0000277 MOCK_METHOD(void, executeUserAdd, (const char*, const char*, bool, bool),
278 (override));
279
280 MOCK_METHOD(void, executeUserDelete, (const char*), (override));
281
282 MOCK_METHOD(size_t, getIpmiUsersCount, (), (override));
283
Nan Zhouf25443e2022-10-25 00:07:11 +0000284 MOCK_METHOD(void, executeUserRename, (const char*, const char*),
285 (override));
286
Nan Zhoufef63032022-10-25 00:07:12 +0000287 MOCK_METHOD(void, executeUserModify, (const char*, const char*, bool),
288 (override));
289
Nan Zhou6b6f2d82022-10-25 00:07:17 +0000290 MOCK_METHOD(void, executeUserModifyUserEnable, (const char*, bool),
291 (override));
292
Nan Zhoua2953032022-11-11 21:50:32 +0000293 MOCK_METHOD(std::vector<std::string>, getFailedAttempt, (const char*),
294 (override));
295
Nan Zhoufef63032022-10-25 00:07:12 +0000296 protected:
Nan Zhoue48085d2022-10-25 00:07:04 +0000297 static sdbusplus::bus_t busInTest;
298 std::string tempPamConfigFile;
299};
300
301sdbusplus::bus_t UserMgrInTest::busInTest = sdbusplus::bus::new_default();
302
303TEST_F(UserMgrInTest, GetPamModuleArgValueOnSuccess)
304{
305 std::string minLen;
306 EXPECT_EQ(getPamModuleArgValue("pam_tally2.so", "minlen", minLen), 0);
307 EXPECT_EQ(minLen, "8");
308 EXPECT_EQ(getPamModuleArgValue("pam_cracklib.so", "minlen", minLen), 0);
309 EXPECT_EQ(minLen, "8");
310}
311
312TEST_F(UserMgrInTest, SetPamModuleArgValueOnSuccess)
313{
314 EXPECT_EQ(setPamModuleArgValue("pam_cracklib.so", "minlen", "16"), 0);
315 EXPECT_EQ(setPamModuleArgValue("pam_tally2.so", "minlen", "16"), 0);
316 std::string minLen;
317 EXPECT_EQ(getPamModuleArgValue("pam_tally2.so", "minlen", minLen), 0);
318 EXPECT_EQ(minLen, "16");
319 EXPECT_EQ(getPamModuleArgValue("pam_cracklib.so", "minlen", minLen), 0);
320 EXPECT_EQ(minLen, "16");
321}
322
323TEST_F(UserMgrInTest, GetPamModuleArgValueOnFailure)
324{
325 EXPECT_NO_THROW(dumpStringToFile("whatever", tempPamConfigFile));
326 std::string minLen;
327 EXPECT_EQ(getPamModuleArgValue("pam_tally2.so", "minlen", minLen), -1);
328 EXPECT_EQ(getPamModuleArgValue("pam_cracklib.so", "minlen", minLen), -1);
329
330 EXPECT_NO_THROW(removeFile(tempPamConfigFile));
331 EXPECT_EQ(getPamModuleArgValue("pam_tally2.so", "minlen", minLen), -1);
332 EXPECT_EQ(getPamModuleArgValue("pam_cracklib.so", "minlen", minLen), -1);
333}
334
335TEST_F(UserMgrInTest, SetPamModuleArgValueOnFailure)
336{
337 EXPECT_NO_THROW(dumpStringToFile("whatever", tempPamConfigFile));
338 EXPECT_EQ(setPamModuleArgValue("pam_cracklib.so", "minlen", "16"), -1);
339 EXPECT_EQ(setPamModuleArgValue("pam_tally2.so", "minlen", "16"), -1);
340
341 EXPECT_NO_THROW(removeFile(tempPamConfigFile));
342 EXPECT_EQ(setPamModuleArgValue("pam_cracklib.so", "minlen", "16"), -1);
343 EXPECT_EQ(setPamModuleArgValue("pam_tally2.so", "minlen", "16"), -1);
344}
345
Nan Zhou8a11d992022-10-25 00:07:06 +0000346TEST_F(UserMgrInTest, IsUserExistEmptyInputThrowsError)
347{
348 EXPECT_THROW(
349 isUserExist(""),
350 sdbusplus::xyz::openbmc_project::Common::Error::InvalidArgument);
351}
352
353TEST_F(UserMgrInTest, ThrowForUserDoesNotExistThrowsError)
354{
355 EXPECT_THROW(throwForUserDoesNotExist("whatever"),
356 sdbusplus::xyz::openbmc_project::User::Common::Error::
357 UserNameDoesNotExist);
358}
359
360TEST_F(UserMgrInTest, ThrowForUserExistsThrowsError)
361{
362 EXPECT_THROW(
363 throwForUserExists("root"),
364 sdbusplus::xyz::openbmc_project::User::Common::Error::UserNameExists);
365}
366
Nan Zhou40e44972022-10-25 00:07:07 +0000367TEST_F(
368 UserMgrInTest,
369 ThrowForUserNameConstraintsExceedIpmiMaxUserNameLenThrowsUserNameGroupFail)
370{
371 std::string strWith17Chars(17, 'A');
372 EXPECT_THROW(throwForUserNameConstraints(strWith17Chars, {"ipmi"}),
373 sdbusplus::xyz::openbmc_project::User::Common::Error::
374 UserNameGroupFail);
375}
376
377TEST_F(
378 UserMgrInTest,
379 ThrowForUserNameConstraintsExceedSystemMaxUserNameLenThrowsInvalidArgument)
380{
381 std::string strWith31Chars(31, 'A');
382 EXPECT_THROW(
383 throwForUserNameConstraints(strWith31Chars, {}),
384 sdbusplus::xyz::openbmc_project::Common::Error::InvalidArgument);
385}
386
387TEST_F(UserMgrInTest,
388 ThrowForUserNameConstraintsRegexMismatchThrowsInvalidArgument)
389{
390 std::string startWithNumber = "0ABC";
391 EXPECT_THROW(
392 throwForUserNameConstraints(startWithNumber, {"ipmi"}),
393 sdbusplus::xyz::openbmc_project::Common::Error::InvalidArgument);
394}
395
Nan Zhou49c81362022-10-25 00:07:08 +0000396TEST_F(UserMgrInTest, UserAddNotRootFailedWithInternalFailure)
397{
398 EXPECT_THROW(
399 UserMgr::executeUserAdd("user0", "ipmi,ssh", true, true),
400 sdbusplus::xyz::openbmc_project::Common::Error::InternalFailure);
401}
402
403TEST_F(UserMgrInTest, UserDeleteNotRootFailedWithInternalFailure)
404{
405 EXPECT_THROW(
406 UserMgr::executeUserDelete("user0"),
407 sdbusplus::xyz::openbmc_project::Common::Error::InternalFailure);
408}
409
410TEST_F(UserMgrInTest,
411 ThrowForMaxGrpUserCountThrowsNoResourceWhenIpmiUserExceedLimit)
412{
413 EXPECT_CALL(*this, getIpmiUsersCount()).WillOnce(Return(ipmiMaxUsers));
414 EXPECT_THROW(
415 throwForMaxGrpUserCount({"ipmi"}),
416 sdbusplus::xyz::openbmc_project::User::Common::Error::NoResource);
417}
418
419TEST_F(UserMgrInTest, CreateUserThrowsInternalFailureWhenExecuteUserAddFails)
420{
421 EXPECT_CALL(*this, executeUserAdd)
422 .WillOnce(testing::Throw(
423 sdbusplus::xyz::openbmc_project::Common::Error::InternalFailure()));
424 EXPECT_THROW(
425 createUser("whatever", {"redfish"}, "", true),
426 sdbusplus::xyz::openbmc_project::Common::Error::InternalFailure);
427}
428
429TEST_F(UserMgrInTest, DeleteUserThrowsInternalFailureWhenExecuteUserDeleteFails)
430{
431 std::string username = "user";
432 EXPECT_NO_THROW(
433 UserMgr::createUser(username, {"redfish", "ssh"}, "priv-user", true));
434 EXPECT_CALL(*this, executeUserDelete(testing::StrEq(username)))
435 .WillOnce(testing::Throw(
436 sdbusplus::xyz::openbmc_project::Common::Error::InternalFailure()))
437 .WillOnce(testing::DoDefault());
438
439 EXPECT_THROW(
440 deleteUser(username),
441 sdbusplus::xyz::openbmc_project::Common::Error::InternalFailure);
442 EXPECT_NO_THROW(UserMgr::deleteUser(username));
443}
444
Nan Zhou589aeb42022-10-25 00:07:09 +0000445TEST_F(UserMgrInTest, ThrowForInvalidPrivilegeThrowsWhenPrivilegeIsInvalid)
446{
447 EXPECT_THROW(
448 throwForInvalidPrivilege("whatever"),
449 sdbusplus::xyz::openbmc_project::Common::Error::InvalidArgument);
450}
451
452TEST_F(UserMgrInTest, ThrowForInvalidPrivilegeNoThrowWhenPrivilegeIsValid)
453{
454 EXPECT_NO_THROW(throwForInvalidPrivilege("priv-admin"));
455 EXPECT_NO_THROW(throwForInvalidPrivilege("priv-operator"));
456 EXPECT_NO_THROW(throwForInvalidPrivilege("priv-user"));
457 EXPECT_NO_THROW(throwForInvalidPrivilege("priv-noaccess"));
458}
459
Nan Zhouecf88762022-10-25 00:07:10 +0000460TEST_F(UserMgrInTest, ThrowForInvalidGroupsThrowsWhenGroupIsInvalid)
461{
462 EXPECT_THROW(
463 throwForInvalidGroups({"whatever"}),
464 sdbusplus::xyz::openbmc_project::Common::Error::InvalidArgument);
465}
466
467TEST_F(UserMgrInTest, ThrowForInvalidGroupsNoThrowWhenGroupIsValid)
468{
469 EXPECT_NO_THROW(throwForInvalidGroups({"ipmi"}));
470 EXPECT_NO_THROW(throwForInvalidGroups({"ssh"}));
471 EXPECT_NO_THROW(throwForInvalidGroups({"redfish"}));
472 EXPECT_NO_THROW(throwForInvalidGroups({"web"}));
473}
474
Nan Zhouf25443e2022-10-25 00:07:11 +0000475TEST_F(UserMgrInTest, RenameUserOnSuccess)
476{
477 std::string username = "user001";
478 EXPECT_NO_THROW(
479 UserMgr::createUser(username, {"redfish", "ssh"}, "priv-user", true));
480 std::string newUsername = "user002";
481
482 EXPECT_NO_THROW(UserMgr::renameUser(username, newUsername));
483
484 // old username doesn't exist
485 EXPECT_THROW(getUserInfo(username),
486 sdbusplus::xyz::openbmc_project::User::Common::Error::
487 UserNameDoesNotExist);
488
489 UserInfoMap userInfo = getUserInfo(newUsername);
490 EXPECT_EQ(std::get<Privilege>(userInfo["UserPrivilege"]), "priv-user");
491 EXPECT_THAT(std::get<GroupList>(userInfo["UserGroups"]),
492 testing::UnorderedElementsAre("redfish", "ssh"));
493 EXPECT_EQ(std::get<UserEnabled>(userInfo["UserEnabled"]), true);
494
495 EXPECT_NO_THROW(UserMgr::deleteUser(newUsername));
496}
497
498TEST_F(UserMgrInTest, RenameUserThrowsInternalFailureIfExecuteUserModifyFails)
499{
500 std::string username = "user001";
501 EXPECT_NO_THROW(
502 UserMgr::createUser(username, {"redfish", "ssh"}, "priv-user", true));
503 std::string newUsername = "user002";
504
505 EXPECT_CALL(*this, executeUserRename(testing::StrEq(username),
506 testing::StrEq(newUsername)))
507 .WillOnce(testing::Throw(
508 sdbusplus::xyz::openbmc_project::Common::Error::InternalFailure()));
509 EXPECT_THROW(
510 UserMgr::renameUser(username, newUsername),
511 sdbusplus::xyz::openbmc_project::Common::Error::InternalFailure);
512
513 // The original user is unchanged
514 UserInfoMap userInfo = getUserInfo(username);
515 EXPECT_EQ(std::get<Privilege>(userInfo["UserPrivilege"]), "priv-user");
516 EXPECT_THAT(std::get<GroupList>(userInfo["UserGroups"]),
517 testing::UnorderedElementsAre("redfish", "ssh"));
518 EXPECT_EQ(std::get<UserEnabled>(userInfo["UserEnabled"]), true);
519
520 EXPECT_NO_THROW(UserMgr::deleteUser(username));
521}
522
523TEST_F(UserMgrInTest, DefaultUserModifyFailedWithInternalFailure)
524{
525 EXPECT_THROW(
526 UserMgr::executeUserRename("user0", "user1"),
527 sdbusplus::xyz::openbmc_project::Common::Error::InternalFailure);
Nan Zhoufef63032022-10-25 00:07:12 +0000528 EXPECT_THROW(
529 UserMgr::executeUserModify("user0", "ssh", true),
530 sdbusplus::xyz::openbmc_project::Common::Error::InternalFailure);
531}
532
533TEST_F(UserMgrInTest, UpdateGroupsAndPrivOnSuccess)
534{
535 std::string username = "user001";
536 EXPECT_NO_THROW(
537 UserMgr::createUser(username, {"redfish", "ssh"}, "priv-user", true));
538 EXPECT_NO_THROW(
539 updateGroupsAndPriv(username, {"ipmi", "ssh"}, "priv-admin"));
540 UserInfoMap userInfo = getUserInfo(username);
541 EXPECT_EQ(std::get<Privilege>(userInfo["UserPrivilege"]), "priv-admin");
542 EXPECT_THAT(std::get<GroupList>(userInfo["UserGroups"]),
543 testing::UnorderedElementsAre("ipmi", "ssh"));
544 EXPECT_EQ(std::get<UserEnabled>(userInfo["UserEnabled"]), true);
545 EXPECT_NO_THROW(UserMgr::deleteUser(username));
546}
547
548TEST_F(UserMgrInTest,
549 UpdateGroupsAndPrivThrowsInternalFailureIfExecuteUserModifyFail)
550{
551 std::string username = "user001";
552 EXPECT_NO_THROW(
553 UserMgr::createUser(username, {"redfish", "ssh"}, "priv-user", true));
554 EXPECT_CALL(*this, executeUserModify(testing::StrEq(username), testing::_,
555 testing::_))
556 .WillOnce(testing::Throw(
557 sdbusplus::xyz::openbmc_project::Common::Error::InternalFailure()));
558 EXPECT_THROW(
559 updateGroupsAndPriv(username, {"ipmi", "ssh"}, "priv-admin"),
560 sdbusplus::xyz::openbmc_project::Common::Error::InternalFailure);
561 EXPECT_NO_THROW(UserMgr::deleteUser(username));
Nan Zhouf25443e2022-10-25 00:07:11 +0000562}
563
Nan Zhou18031012022-11-10 22:24:58 +0000564TEST_F(UserMgrInTest, MinPasswordLengthReturnsIfValueIsTheSame)
565{
566 initializeAccountPolicy();
567 EXPECT_EQ(AccountPolicyIface::minPasswordLength(), 8);
568 UserMgr::minPasswordLength(8);
569 EXPECT_EQ(AccountPolicyIface::minPasswordLength(), 8);
570}
571
572TEST_F(UserMgrInTest,
573 MinPasswordLengthRejectsTooShortPasswordWithInvalidArgument)
574{
575 initializeAccountPolicy();
576 EXPECT_EQ(AccountPolicyIface::minPasswordLength(), 8);
577 EXPECT_THROW(
578 UserMgr::minPasswordLength(minPasswdLength - 1),
579 sdbusplus::xyz::openbmc_project::Common::Error::InvalidArgument);
580 EXPECT_EQ(AccountPolicyIface::minPasswordLength(), 8);
581}
582
583TEST_F(UserMgrInTest, MinPasswordLengthOnSuccess)
584{
585 initializeAccountPolicy();
586 EXPECT_EQ(AccountPolicyIface::minPasswordLength(), 8);
587 UserMgr::minPasswordLength(16);
588 EXPECT_EQ(AccountPolicyIface::minPasswordLength(), 16);
589}
590
591TEST_F(UserMgrInTest, MinPasswordLengthOnFailure)
592{
593 EXPECT_NO_THROW(dumpStringToFile("whatever", tempPamConfigFile));
594 initializeAccountPolicy();
595 EXPECT_EQ(AccountPolicyIface::minPasswordLength(), 8);
596 EXPECT_THROW(
597 UserMgr::minPasswordLength(16),
598 sdbusplus::xyz::openbmc_project::Common::Error::InternalFailure);
599 EXPECT_EQ(AccountPolicyIface::minPasswordLength(), 8);
600}
601
Nan Zhoua6ce1fa2022-10-25 00:07:14 +0000602TEST_F(UserMgrInTest, RememberOldPasswordTimesReturnsIfValueIsTheSame)
603{
604 initializeAccountPolicy();
605 EXPECT_EQ(AccountPolicyIface::rememberOldPasswordTimes(), 0);
606 UserMgr::rememberOldPasswordTimes(8);
607 EXPECT_EQ(AccountPolicyIface::rememberOldPasswordTimes(), 8);
608 UserMgr::rememberOldPasswordTimes(8);
609 EXPECT_EQ(AccountPolicyIface::rememberOldPasswordTimes(), 8);
610}
611
612TEST_F(UserMgrInTest, RememberOldPasswordTimesOnSuccess)
613{
614 initializeAccountPolicy();
615 EXPECT_EQ(AccountPolicyIface::rememberOldPasswordTimes(), 0);
616 UserMgr::rememberOldPasswordTimes(16);
617 EXPECT_EQ(AccountPolicyIface::rememberOldPasswordTimes(), 16);
618}
619
620TEST_F(UserMgrInTest, RememberOldPasswordTimesOnFailure)
621{
622 EXPECT_NO_THROW(dumpStringToFile("whatever", tempPamConfigFile));
623 initializeAccountPolicy();
624 EXPECT_EQ(AccountPolicyIface::rememberOldPasswordTimes(), 0);
625 EXPECT_THROW(
626 UserMgr::rememberOldPasswordTimes(16),
627 sdbusplus::xyz::openbmc_project::Common::Error::InternalFailure);
628 EXPECT_EQ(AccountPolicyIface::rememberOldPasswordTimes(), 0);
629}
630
Nan Zhoucfabe6b2022-10-25 00:07:15 +0000631TEST_F(UserMgrInTest, MaxLoginAttemptBeforeLockoutReturnsIfValueIsTheSame)
632{
633 initializeAccountPolicy();
634 EXPECT_EQ(AccountPolicyIface::maxLoginAttemptBeforeLockout(), 2);
635 UserMgr::maxLoginAttemptBeforeLockout(2);
636 EXPECT_EQ(AccountPolicyIface::maxLoginAttemptBeforeLockout(), 2);
637}
638
639TEST_F(UserMgrInTest, MaxLoginAttemptBeforeLockoutOnSuccess)
640{
641 initializeAccountPolicy();
642 EXPECT_EQ(AccountPolicyIface::maxLoginAttemptBeforeLockout(), 2);
643 UserMgr::maxLoginAttemptBeforeLockout(16);
644 EXPECT_EQ(AccountPolicyIface::maxLoginAttemptBeforeLockout(), 16);
645}
646
647TEST_F(UserMgrInTest, MaxLoginAttemptBeforeLockoutOnFailure)
648{
649 EXPECT_NO_THROW(dumpStringToFile("whatever", tempPamConfigFile));
650 initializeAccountPolicy();
651 EXPECT_THROW(
652 UserMgr::maxLoginAttemptBeforeLockout(16),
653 sdbusplus::xyz::openbmc_project::Common::Error::InternalFailure);
654 EXPECT_EQ(AccountPolicyIface::rememberOldPasswordTimes(), 0);
655}
656
Nan Zhou784aecd2022-10-25 00:07:16 +0000657TEST_F(UserMgrInTest, AccountUnlockTimeoutReturnsIfValueIsTheSame)
658{
659 initializeAccountPolicy();
660 EXPECT_EQ(AccountPolicyIface::accountUnlockTimeout(), 3);
661 UserMgr::accountUnlockTimeout(3);
662 EXPECT_EQ(AccountPolicyIface::accountUnlockTimeout(), 3);
663}
664
665TEST_F(UserMgrInTest, AccountUnlockTimeoutOnSuccess)
666{
667 initializeAccountPolicy();
668 EXPECT_EQ(AccountPolicyIface::accountUnlockTimeout(), 3);
669 UserMgr::accountUnlockTimeout(16);
670 EXPECT_EQ(AccountPolicyIface::accountUnlockTimeout(), 16);
671}
672
673TEST_F(UserMgrInTest, AccountUnlockTimeoutOnFailure)
674{
675 initializeAccountPolicy();
676 EXPECT_NO_THROW(dumpStringToFile("whatever", tempPamConfigFile));
677 EXPECT_THROW(
678 UserMgr::accountUnlockTimeout(16),
679 sdbusplus::xyz::openbmc_project::Common::Error::InternalFailure);
680 EXPECT_EQ(AccountPolicyIface::accountUnlockTimeout(), 3);
681}
682
Nan Zhou6b6f2d82022-10-25 00:07:17 +0000683TEST_F(UserMgrInTest, UserEnableOnSuccess)
684{
685 std::string username = "user001";
686 EXPECT_NO_THROW(
687 UserMgr::createUser(username, {"redfish", "ssh"}, "priv-user", true));
688 UserInfoMap userInfo = getUserInfo(username);
689 EXPECT_EQ(std::get<UserEnabled>(userInfo["UserEnabled"]), true);
690
691 EXPECT_NO_THROW(userEnable(username, false));
692
693 userInfo = getUserInfo(username);
694 EXPECT_EQ(std::get<UserEnabled>(userInfo["UserEnabled"]), false);
695
696 EXPECT_NO_THROW(UserMgr::deleteUser(username));
697}
698
699TEST_F(UserMgrInTest, UserEnableThrowsInternalFailureIfExecuteUserModifyFail)
700{
701 std::string username = "user001";
702 EXPECT_NO_THROW(
703 UserMgr::createUser(username, {"redfish", "ssh"}, "priv-user", true));
704 UserInfoMap userInfo = getUserInfo(username);
705 EXPECT_EQ(std::get<UserEnabled>(userInfo["UserEnabled"]), true);
706
707 EXPECT_CALL(*this, executeUserModifyUserEnable(testing::StrEq(username),
708 testing::Eq(false)))
709 .WillOnce(testing::Throw(
710 sdbusplus::xyz::openbmc_project::Common::Error::InternalFailure()));
711 EXPECT_THROW(
712 userEnable(username, false),
713 sdbusplus::xyz::openbmc_project::Common::Error::InternalFailure);
714
715 userInfo = getUserInfo(username);
716 // Stay unchanged
717 EXPECT_EQ(std::get<UserEnabled>(userInfo["UserEnabled"]), true);
718
719 EXPECT_NO_THROW(UserMgr::deleteUser(username));
720}
721
Nan Zhoua2953032022-11-11 21:50:32 +0000722TEST_F(
723 UserMgrInTest,
724 UserLockedForFailedAttemptReturnsFalseIfMaxLoginAttemptBeforeLockoutIsZero)
725{
726 EXPECT_FALSE(userLockedForFailedAttempt("whatever"));
727}
728
729TEST_F(UserMgrInTest, UserLockedForFailedAttemptZeroFailuresReturnsFalse)
730{
731 std::string username = "user001";
732 initializeAccountPolicy();
733 // Example output from BMC
734 // root@s7106:~# pam_tally2 -u root
735 // Login Failures Latest failure From
736 // root 0
737 std::vector<std::string> output = {"whatever", "root\t0"};
738 EXPECT_CALL(*this, getFailedAttempt(testing::StrEq(username.c_str())))
739 .WillOnce(testing::Return(output));
740
741 EXPECT_FALSE(userLockedForFailedAttempt(username));
742}
743
744TEST_F(UserMgrInTest, UserLockedForFailedAttemptFailIfGetFailedAttemptFail)
745{
746 std::string username = "user001";
747 initializeAccountPolicy();
748 EXPECT_CALL(*this, getFailedAttempt(testing::StrEq(username.c_str())))
749 .WillOnce(testing::Throw(
750 sdbusplus::xyz::openbmc_project::Common::Error::InternalFailure()));
751
752 EXPECT_THROW(
753 userLockedForFailedAttempt(username),
754 sdbusplus::xyz::openbmc_project::Common::Error::InternalFailure);
755}
756
757TEST_F(UserMgrInTest,
758 UserLockedForFailedAttemptThrowsInternalFailureIfFailAttemptsOutOfRange)
759{
760 std::string username = "user001";
761 initializeAccountPolicy();
762 std::vector<std::string> output = {"whatever", "root\t1000000"};
763 EXPECT_CALL(*this, getFailedAttempt(testing::StrEq(username.c_str())))
764 .WillOnce(testing::Return(output));
765
766 EXPECT_THROW(
767 userLockedForFailedAttempt(username),
768 sdbusplus::xyz::openbmc_project::Common::Error::InternalFailure);
769}
770
771TEST_F(UserMgrInTest,
772 UserLockedForFailedAttemptThrowsInternalFailureIfNoFailDateTime)
773{
774 std::string username = "user001";
775 initializeAccountPolicy();
776 std::vector<std::string> output = {"whatever", "root\t2"};
777 EXPECT_CALL(*this, getFailedAttempt(testing::StrEq(username.c_str())))
778 .WillOnce(testing::Return(output));
779
780 EXPECT_THROW(
781 userLockedForFailedAttempt(username),
782 sdbusplus::xyz::openbmc_project::Common::Error::InternalFailure);
783}
784
785TEST_F(UserMgrInTest,
786 UserLockedForFailedAttemptThrowsInternalFailureIfWrongDateFormat)
787{
788 std::string username = "user001";
789 initializeAccountPolicy();
790
791 // Choose a date in the past.
792 std::vector<std::string> output = {"whatever",
793 "root\t2\t10/24/2002\t00:00:00"};
794 EXPECT_CALL(*this, getFailedAttempt(testing::StrEq(username.c_str())))
795 .WillOnce(testing::Return(output));
796
797 EXPECT_THROW(
798 userLockedForFailedAttempt(username),
799 sdbusplus::xyz::openbmc_project::Common::Error::InternalFailure);
800}
801
802TEST_F(UserMgrInTest,
803 UserLockedForFailedAttemptReturnsFalseIfLastFailTimeHasTimedOut)
804{
805 std::string username = "user001";
806 initializeAccountPolicy();
807
808 // Choose a date in the past.
809 std::vector<std::string> output = {"whatever",
810 "root\t2\t10/24/02\t00:00:00"};
811 EXPECT_CALL(*this, getFailedAttempt(testing::StrEq(username.c_str())))
812 .WillOnce(testing::Return(output));
813
814 EXPECT_EQ(userLockedForFailedAttempt(username), false);
815}
816
raviteja-b8cc44052019-02-27 23:29:36 -0600817} // namespace user
818} // namespace phosphor