xref: /openbmc/phosphor-user-manager/test/user_mgr_test.cpp (revision 90a120bcdbd72d345de047f13288612864154e78)
1 #include "mock_user_mgr.hpp"
2 #include "user_mgr.hpp"
3 
4 #include <sdbusplus/test/sdbus_mock.hpp>
5 #include <xyz/openbmc_project/Common/error.hpp>
6 #include <xyz/openbmc_project/User/Common/error.hpp>
7 
8 #include <exception>
9 #include <filesystem>
10 #include <fstream>
11 
12 #include <gtest/gtest.h>
13 
14 namespace phosphor
15 {
16 namespace user
17 {
18 
19 using ::testing::Return;
20 
21 using InternalFailure =
22     sdbusplus::xyz::openbmc_project::Common::Error::InternalFailure;
23 
24 class TestUserMgr : public testing::Test
25 {
26   public:
27     sdbusplus::SdBusMock sdBusMock;
28     sdbusplus::bus_t bus;
29     MockManager mockManager;
30 
31     TestUserMgr() :
32         bus(sdbusplus::get_mocked_new(&sdBusMock)), mockManager(bus, objpath)
33     {}
34 
35     void createLocalUser(const std::string& userName,
36                          std::vector<std::string> groupNames,
37                          const std::string& priv, bool enabled)
38     {
39         sdbusplus::message::object_path tempObjPath(usersObjPath);
40         tempObjPath /= userName;
41         std::string userObj(tempObjPath);
42         mockManager.usersList.emplace(
43             userName, std::make_unique<phosphor::user::Users>(
44                           mockManager.bus, userObj.c_str(), groupNames, priv,
45                           enabled, mockManager));
46     }
47 
48     DbusUserObj createPrivilegeMapperDbusObject(void)
49     {
50         DbusUserObj object;
51         DbusUserObjValue objValue;
52 
53         DbusUserObjPath objPath("/xyz/openbmc_project/user/ldap/openldap");
54         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);
58         object.emplace(objPath, objValue);
59 
60         DbusUserObjPath objectPath(
61             "/xyz/openbmc_project/user/ldap/openldap/role_map/1");
62         std::string group = "ldapGroup";
63         std::string priv = "priv-admin";
64         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);
69         object.emplace(objectPath, objValue);
70 
71         return object;
72     }
73 
74     DbusUserObj createLdapConfigObjectWithoutPrivilegeMapper(void)
75     {
76         DbusUserObj object;
77         DbusUserObjValue objValue;
78 
79         DbusUserObjPath objPath("/xyz/openbmc_project/user/ldap/openldap");
80         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);
84         object.emplace(objPath, objValue);
85         return object;
86     }
87 };
88 
89 TEST_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 
99 TEST_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"]));
115     EXPECT_EQ(false, std::get<bool>(userInfo["UserPasswordExpired"]));
116     EXPECT_EQ(false, std::get<bool>(userInfo["RemoteUser"]));
117 }
118 
119 TEST_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 
136 TEST_F(TestUserMgr, ldapUserWithoutPrivMapper)
137 {
138     UserInfoMap userInfo;
139     std::string userName = "ldapUser";
140     std::string ldapGroup = "ldapGroup";
141 
142     EXPECT_CALL(mockManager, getLdapGroupName(userName))
143         .WillRepeatedly(Return(ldapGroup));
144     // Create LDAP config object without privilege mapper
145     DbusUserObj object = createLdapConfigObjectWithoutPrivilegeMapper();
146     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 }
152 
153 TEST(GetCSVFromVector, EmptyVectorReturnsEmptyString)
154 {
155     EXPECT_EQ(getCSVFromVector({}), "");
156 }
157 
158 TEST(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 
165 TEST(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 
176 TEST(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 
188 namespace
189 {
190 inline constexpr const char* objectRootInTest = "/xyz/openbmc_project/user";
191 
192 // Fake config; referenced config on real BMC
193 inline 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)
212 password	[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
213 password	[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
214 password	[success=ok default=die]	pam_ipmicheck.so spec_grp_name=ipmi use_authtok
215 password	[success=ok ignore=ignore default=die]	pam_pwhistory.so debug enforce_for_root remember=0 use_authtok
216 password	[success=ok default=die]	pam_unix.so sha512 use_authtok
217 password	[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
219 password	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
223 password	required			pam_permit.so
224 # and here are more per-package modules (the "Additional" block)
225 )";
226 } // namespace
227 
228 void 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 
240 void removeFile(const std::string& filePath)
241 {
242     std::filesystem::remove(filePath);
243 }
244 
245 class 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;
256 
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));
262 
263         ON_CALL(*this, executeUserRename).WillByDefault(testing::Return());
264 
265         ON_CALL(*this, executeUserModify(testing::_, testing::_, testing::_))
266             .WillByDefault(testing::Return());
267 
268         ON_CALL(*this, executeUserModifyUserEnable)
269             .WillByDefault(testing::Return());
270     }
271 
272     ~UserMgrInTest() override
273     {
274         EXPECT_NO_THROW(removeFile(tempPamConfigFile));
275     }
276 
277     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 
284     MOCK_METHOD(void, executeUserRename, (const char*, const char*),
285                 (override));
286 
287     MOCK_METHOD(void, executeUserModify, (const char*, const char*, bool),
288                 (override));
289 
290     MOCK_METHOD(void, executeUserModifyUserEnable, (const char*, bool),
291                 (override));
292 
293     MOCK_METHOD(std::vector<std::string>, getFailedAttempt, (const char*),
294                 (override));
295 
296   protected:
297     static sdbusplus::bus_t busInTest;
298     std::string tempPamConfigFile;
299 };
300 
301 sdbusplus::bus_t UserMgrInTest::busInTest = sdbusplus::bus::new_default();
302 
303 TEST_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 
312 TEST_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 
323 TEST_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 
335 TEST_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 
346 TEST_F(UserMgrInTest, IsUserExistEmptyInputThrowsError)
347 {
348     EXPECT_THROW(
349         isUserExist(""),
350         sdbusplus::xyz::openbmc_project::Common::Error::InvalidArgument);
351 }
352 
353 TEST_F(UserMgrInTest, ThrowForUserDoesNotExistThrowsError)
354 {
355     EXPECT_THROW(throwForUserDoesNotExist("whatever"),
356                  sdbusplus::xyz::openbmc_project::User::Common::Error::
357                      UserNameDoesNotExist);
358 }
359 
360 TEST_F(UserMgrInTest, ThrowForUserExistsThrowsError)
361 {
362     EXPECT_THROW(
363         throwForUserExists("root"),
364         sdbusplus::xyz::openbmc_project::User::Common::Error::UserNameExists);
365 }
366 
367 TEST_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 
377 TEST_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 
387 TEST_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 
396 TEST_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 
403 TEST_F(UserMgrInTest, UserDeleteNotRootFailedWithInternalFailure)
404 {
405     EXPECT_THROW(
406         UserMgr::executeUserDelete("user0"),
407         sdbusplus::xyz::openbmc_project::Common::Error::InternalFailure);
408 }
409 
410 TEST_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 
419 TEST_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 
429 TEST_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 
445 TEST_F(UserMgrInTest, ThrowForInvalidPrivilegeThrowsWhenPrivilegeIsInvalid)
446 {
447     EXPECT_THROW(
448         throwForInvalidPrivilege("whatever"),
449         sdbusplus::xyz::openbmc_project::Common::Error::InvalidArgument);
450 }
451 
452 TEST_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 }
458 
459 TEST_F(UserMgrInTest, ThrowForInvalidGroupsThrowsWhenGroupIsInvalid)
460 {
461     EXPECT_THROW(
462         throwForInvalidGroups({"whatever"}),
463         sdbusplus::xyz::openbmc_project::Common::Error::InvalidArgument);
464 }
465 
466 TEST_F(UserMgrInTest, ThrowForInvalidGroupsNoThrowWhenGroupIsValid)
467 {
468     EXPECT_NO_THROW(throwForInvalidGroups({"ipmi"}));
469     EXPECT_NO_THROW(throwForInvalidGroups({"ssh"}));
470     EXPECT_NO_THROW(throwForInvalidGroups({"redfish"}));
471     EXPECT_NO_THROW(throwForInvalidGroups({"web"}));
472 }
473 
474 TEST_F(UserMgrInTest, RenameUserOnSuccess)
475 {
476     std::string username = "user001";
477     EXPECT_NO_THROW(
478         UserMgr::createUser(username, {"redfish", "ssh"}, "priv-user", true));
479     std::string newUsername = "user002";
480 
481     EXPECT_NO_THROW(UserMgr::renameUser(username, newUsername));
482 
483     // old username doesn't exist
484     EXPECT_THROW(getUserInfo(username),
485                  sdbusplus::xyz::openbmc_project::User::Common::Error::
486                      UserNameDoesNotExist);
487 
488     UserInfoMap userInfo = getUserInfo(newUsername);
489     EXPECT_EQ(std::get<Privilege>(userInfo["UserPrivilege"]), "priv-user");
490     EXPECT_THAT(std::get<GroupList>(userInfo["UserGroups"]),
491                 testing::UnorderedElementsAre("redfish", "ssh"));
492     EXPECT_EQ(std::get<UserEnabled>(userInfo["UserEnabled"]), true);
493 
494     EXPECT_NO_THROW(UserMgr::deleteUser(newUsername));
495 }
496 
497 TEST_F(UserMgrInTest, RenameUserThrowsInternalFailureIfExecuteUserModifyFails)
498 {
499     std::string username = "user001";
500     EXPECT_NO_THROW(
501         UserMgr::createUser(username, {"redfish", "ssh"}, "priv-user", true));
502     std::string newUsername = "user002";
503 
504     EXPECT_CALL(*this, executeUserRename(testing::StrEq(username),
505                                          testing::StrEq(newUsername)))
506         .WillOnce(testing::Throw(
507             sdbusplus::xyz::openbmc_project::Common::Error::InternalFailure()));
508     EXPECT_THROW(
509         UserMgr::renameUser(username, newUsername),
510         sdbusplus::xyz::openbmc_project::Common::Error::InternalFailure);
511 
512     // The original user is unchanged
513     UserInfoMap userInfo = getUserInfo(username);
514     EXPECT_EQ(std::get<Privilege>(userInfo["UserPrivilege"]), "priv-user");
515     EXPECT_THAT(std::get<GroupList>(userInfo["UserGroups"]),
516                 testing::UnorderedElementsAre("redfish", "ssh"));
517     EXPECT_EQ(std::get<UserEnabled>(userInfo["UserEnabled"]), true);
518 
519     EXPECT_NO_THROW(UserMgr::deleteUser(username));
520 }
521 
522 TEST_F(UserMgrInTest, DefaultUserModifyFailedWithInternalFailure)
523 {
524     EXPECT_THROW(
525         UserMgr::executeUserRename("user0", "user1"),
526         sdbusplus::xyz::openbmc_project::Common::Error::InternalFailure);
527     EXPECT_THROW(
528         UserMgr::executeUserModify("user0", "ssh", true),
529         sdbusplus::xyz::openbmc_project::Common::Error::InternalFailure);
530 }
531 
532 TEST_F(UserMgrInTest, UpdateGroupsAndPrivOnSuccess)
533 {
534     std::string username = "user001";
535     EXPECT_NO_THROW(
536         UserMgr::createUser(username, {"redfish", "ssh"}, "priv-user", true));
537     EXPECT_NO_THROW(
538         updateGroupsAndPriv(username, {"ipmi", "ssh"}, "priv-admin"));
539     UserInfoMap userInfo = getUserInfo(username);
540     EXPECT_EQ(std::get<Privilege>(userInfo["UserPrivilege"]), "priv-admin");
541     EXPECT_THAT(std::get<GroupList>(userInfo["UserGroups"]),
542                 testing::UnorderedElementsAre("ipmi", "ssh"));
543     EXPECT_EQ(std::get<UserEnabled>(userInfo["UserEnabled"]), true);
544     EXPECT_NO_THROW(UserMgr::deleteUser(username));
545 }
546 
547 TEST_F(UserMgrInTest,
548        UpdateGroupsAndPrivThrowsInternalFailureIfExecuteUserModifyFail)
549 {
550     std::string username = "user001";
551     EXPECT_NO_THROW(
552         UserMgr::createUser(username, {"redfish", "ssh"}, "priv-user", true));
553     EXPECT_CALL(*this, executeUserModify(testing::StrEq(username), testing::_,
554                                          testing::_))
555         .WillOnce(testing::Throw(
556             sdbusplus::xyz::openbmc_project::Common::Error::InternalFailure()));
557     EXPECT_THROW(
558         updateGroupsAndPriv(username, {"ipmi", "ssh"}, "priv-admin"),
559         sdbusplus::xyz::openbmc_project::Common::Error::InternalFailure);
560     EXPECT_NO_THROW(UserMgr::deleteUser(username));
561 }
562 
563 TEST_F(UserMgrInTest, MinPasswordLengthReturnsIfValueIsTheSame)
564 {
565     initializeAccountPolicy();
566     EXPECT_EQ(AccountPolicyIface::minPasswordLength(), 8);
567     UserMgr::minPasswordLength(8);
568     EXPECT_EQ(AccountPolicyIface::minPasswordLength(), 8);
569 }
570 
571 TEST_F(UserMgrInTest,
572        MinPasswordLengthRejectsTooShortPasswordWithInvalidArgument)
573 {
574     initializeAccountPolicy();
575     EXPECT_EQ(AccountPolicyIface::minPasswordLength(), 8);
576     EXPECT_THROW(
577         UserMgr::minPasswordLength(minPasswdLength - 1),
578         sdbusplus::xyz::openbmc_project::Common::Error::InvalidArgument);
579     EXPECT_EQ(AccountPolicyIface::minPasswordLength(), 8);
580 }
581 
582 TEST_F(UserMgrInTest, MinPasswordLengthOnSuccess)
583 {
584     initializeAccountPolicy();
585     EXPECT_EQ(AccountPolicyIface::minPasswordLength(), 8);
586     UserMgr::minPasswordLength(16);
587     EXPECT_EQ(AccountPolicyIface::minPasswordLength(), 16);
588 }
589 
590 TEST_F(UserMgrInTest, MinPasswordLengthOnFailure)
591 {
592     EXPECT_NO_THROW(dumpStringToFile("whatever", tempPamConfigFile));
593     initializeAccountPolicy();
594     EXPECT_EQ(AccountPolicyIface::minPasswordLength(), 8);
595     EXPECT_THROW(
596         UserMgr::minPasswordLength(16),
597         sdbusplus::xyz::openbmc_project::Common::Error::InternalFailure);
598     EXPECT_EQ(AccountPolicyIface::minPasswordLength(), 8);
599 }
600 
601 TEST_F(UserMgrInTest, RememberOldPasswordTimesReturnsIfValueIsTheSame)
602 {
603     initializeAccountPolicy();
604     EXPECT_EQ(AccountPolicyIface::rememberOldPasswordTimes(), 0);
605     UserMgr::rememberOldPasswordTimes(8);
606     EXPECT_EQ(AccountPolicyIface::rememberOldPasswordTimes(), 8);
607     UserMgr::rememberOldPasswordTimes(8);
608     EXPECT_EQ(AccountPolicyIface::rememberOldPasswordTimes(), 8);
609 }
610 
611 TEST_F(UserMgrInTest, RememberOldPasswordTimesOnSuccess)
612 {
613     initializeAccountPolicy();
614     EXPECT_EQ(AccountPolicyIface::rememberOldPasswordTimes(), 0);
615     UserMgr::rememberOldPasswordTimes(16);
616     EXPECT_EQ(AccountPolicyIface::rememberOldPasswordTimes(), 16);
617 }
618 
619 TEST_F(UserMgrInTest, RememberOldPasswordTimesOnFailure)
620 {
621     EXPECT_NO_THROW(dumpStringToFile("whatever", tempPamConfigFile));
622     initializeAccountPolicy();
623     EXPECT_EQ(AccountPolicyIface::rememberOldPasswordTimes(), 0);
624     EXPECT_THROW(
625         UserMgr::rememberOldPasswordTimes(16),
626         sdbusplus::xyz::openbmc_project::Common::Error::InternalFailure);
627     EXPECT_EQ(AccountPolicyIface::rememberOldPasswordTimes(), 0);
628 }
629 
630 TEST_F(UserMgrInTest, MaxLoginAttemptBeforeLockoutReturnsIfValueIsTheSame)
631 {
632     initializeAccountPolicy();
633     EXPECT_EQ(AccountPolicyIface::maxLoginAttemptBeforeLockout(), 2);
634     UserMgr::maxLoginAttemptBeforeLockout(2);
635     EXPECT_EQ(AccountPolicyIface::maxLoginAttemptBeforeLockout(), 2);
636 }
637 
638 TEST_F(UserMgrInTest, MaxLoginAttemptBeforeLockoutOnSuccess)
639 {
640     initializeAccountPolicy();
641     EXPECT_EQ(AccountPolicyIface::maxLoginAttemptBeforeLockout(), 2);
642     UserMgr::maxLoginAttemptBeforeLockout(16);
643     EXPECT_EQ(AccountPolicyIface::maxLoginAttemptBeforeLockout(), 16);
644 }
645 
646 TEST_F(UserMgrInTest, MaxLoginAttemptBeforeLockoutOnFailure)
647 {
648     EXPECT_NO_THROW(dumpStringToFile("whatever", tempPamConfigFile));
649     initializeAccountPolicy();
650     EXPECT_THROW(
651         UserMgr::maxLoginAttemptBeforeLockout(16),
652         sdbusplus::xyz::openbmc_project::Common::Error::InternalFailure);
653     EXPECT_EQ(AccountPolicyIface::rememberOldPasswordTimes(), 0);
654 }
655 
656 TEST_F(UserMgrInTest, AccountUnlockTimeoutReturnsIfValueIsTheSame)
657 {
658     initializeAccountPolicy();
659     EXPECT_EQ(AccountPolicyIface::accountUnlockTimeout(), 3);
660     UserMgr::accountUnlockTimeout(3);
661     EXPECT_EQ(AccountPolicyIface::accountUnlockTimeout(), 3);
662 }
663 
664 TEST_F(UserMgrInTest, AccountUnlockTimeoutOnSuccess)
665 {
666     initializeAccountPolicy();
667     EXPECT_EQ(AccountPolicyIface::accountUnlockTimeout(), 3);
668     UserMgr::accountUnlockTimeout(16);
669     EXPECT_EQ(AccountPolicyIface::accountUnlockTimeout(), 16);
670 }
671 
672 TEST_F(UserMgrInTest, AccountUnlockTimeoutOnFailure)
673 {
674     initializeAccountPolicy();
675     EXPECT_NO_THROW(dumpStringToFile("whatever", tempPamConfigFile));
676     EXPECT_THROW(
677         UserMgr::accountUnlockTimeout(16),
678         sdbusplus::xyz::openbmc_project::Common::Error::InternalFailure);
679     EXPECT_EQ(AccountPolicyIface::accountUnlockTimeout(), 3);
680 }
681 
682 TEST_F(UserMgrInTest, UserEnableOnSuccess)
683 {
684     std::string username = "user001";
685     EXPECT_NO_THROW(
686         UserMgr::createUser(username, {"redfish", "ssh"}, "priv-user", true));
687     UserInfoMap userInfo = getUserInfo(username);
688     EXPECT_EQ(std::get<UserEnabled>(userInfo["UserEnabled"]), true);
689 
690     EXPECT_NO_THROW(userEnable(username, false));
691 
692     userInfo = getUserInfo(username);
693     EXPECT_EQ(std::get<UserEnabled>(userInfo["UserEnabled"]), false);
694 
695     EXPECT_NO_THROW(UserMgr::deleteUser(username));
696 }
697 
698 TEST_F(UserMgrInTest, UserEnableThrowsInternalFailureIfExecuteUserModifyFail)
699 {
700     std::string username = "user001";
701     EXPECT_NO_THROW(
702         UserMgr::createUser(username, {"redfish", "ssh"}, "priv-user", true));
703     UserInfoMap userInfo = getUserInfo(username);
704     EXPECT_EQ(std::get<UserEnabled>(userInfo["UserEnabled"]), true);
705 
706     EXPECT_CALL(*this, executeUserModifyUserEnable(testing::StrEq(username),
707                                                    testing::Eq(false)))
708         .WillOnce(testing::Throw(
709             sdbusplus::xyz::openbmc_project::Common::Error::InternalFailure()));
710     EXPECT_THROW(
711         userEnable(username, false),
712         sdbusplus::xyz::openbmc_project::Common::Error::InternalFailure);
713 
714     userInfo = getUserInfo(username);
715     // Stay unchanged
716     EXPECT_EQ(std::get<UserEnabled>(userInfo["UserEnabled"]), true);
717 
718     EXPECT_NO_THROW(UserMgr::deleteUser(username));
719 }
720 
721 TEST_F(
722     UserMgrInTest,
723     UserLockedForFailedAttemptReturnsFalseIfMaxLoginAttemptBeforeLockoutIsZero)
724 {
725     EXPECT_FALSE(userLockedForFailedAttempt("whatever"));
726 }
727 
728 TEST_F(UserMgrInTest, UserLockedForFailedAttemptZeroFailuresReturnsFalse)
729 {
730     std::string username = "user001";
731     initializeAccountPolicy();
732     // Example output from BMC
733     // root@s7106:~# pam_tally2 -u root
734     // Login           Failures Latest failure     From
735     // root                0
736     std::vector<std::string> output = {"whatever", "root\t0"};
737     EXPECT_CALL(*this, getFailedAttempt(testing::StrEq(username.c_str())))
738         .WillOnce(testing::Return(output));
739 
740     EXPECT_FALSE(userLockedForFailedAttempt(username));
741 }
742 
743 TEST_F(UserMgrInTest, UserLockedForFailedAttemptFailIfGetFailedAttemptFail)
744 {
745     std::string username = "user001";
746     initializeAccountPolicy();
747     EXPECT_CALL(*this, getFailedAttempt(testing::StrEq(username.c_str())))
748         .WillOnce(testing::Throw(
749             sdbusplus::xyz::openbmc_project::Common::Error::InternalFailure()));
750 
751     EXPECT_THROW(
752         userLockedForFailedAttempt(username),
753         sdbusplus::xyz::openbmc_project::Common::Error::InternalFailure);
754 }
755 
756 TEST_F(UserMgrInTest,
757        UserLockedForFailedAttemptThrowsInternalFailureIfFailAttemptsOutOfRange)
758 {
759     std::string username = "user001";
760     initializeAccountPolicy();
761     std::vector<std::string> output = {"whatever", "root\t1000000"};
762     EXPECT_CALL(*this, getFailedAttempt(testing::StrEq(username.c_str())))
763         .WillOnce(testing::Return(output));
764 
765     EXPECT_THROW(
766         userLockedForFailedAttempt(username),
767         sdbusplus::xyz::openbmc_project::Common::Error::InternalFailure);
768 }
769 
770 TEST_F(UserMgrInTest,
771        UserLockedForFailedAttemptThrowsInternalFailureIfNoFailDateTime)
772 {
773     std::string username = "user001";
774     initializeAccountPolicy();
775     std::vector<std::string> output = {"whatever", "root\t2"};
776     EXPECT_CALL(*this, getFailedAttempt(testing::StrEq(username.c_str())))
777         .WillOnce(testing::Return(output));
778 
779     EXPECT_THROW(
780         userLockedForFailedAttempt(username),
781         sdbusplus::xyz::openbmc_project::Common::Error::InternalFailure);
782 }
783 
784 TEST_F(UserMgrInTest,
785        UserLockedForFailedAttemptThrowsInternalFailureIfWrongDateFormat)
786 {
787     std::string username = "user001";
788     initializeAccountPolicy();
789 
790     // Choose a date in the past.
791     std::vector<std::string> output = {"whatever",
792                                        "root\t2\t10/24/2002\t00:00:00"};
793     EXPECT_CALL(*this, getFailedAttempt(testing::StrEq(username.c_str())))
794         .WillOnce(testing::Return(output));
795 
796     EXPECT_THROW(
797         userLockedForFailedAttempt(username),
798         sdbusplus::xyz::openbmc_project::Common::Error::InternalFailure);
799 }
800 
801 TEST_F(UserMgrInTest,
802        UserLockedForFailedAttemptReturnsFalseIfLastFailTimeHasTimedOut)
803 {
804     std::string username = "user001";
805     initializeAccountPolicy();
806 
807     // Choose a date in the past.
808     std::vector<std::string> output = {"whatever",
809                                        "root\t2\t10/24/02\t00:00:00"};
810     EXPECT_CALL(*this, getFailedAttempt(testing::StrEq(username.c_str())))
811         .WillOnce(testing::Return(output));
812 
813     EXPECT_EQ(userLockedForFailedAttempt(username), false);
814 }
815 
816 } // namespace user
817 } // namespace phosphor
818