xref: /openbmc/phosphor-user-manager/test/user_mgr_test.cpp (revision 2a137f4de7d678cdc87f5d6b62c85fa7b1d95ec1)
1 #include "mock_user_mgr.hpp"
2 #include "user_mgr.hpp"
3 
4 #include <unistd.h>
5 
6 #include <sdbusplus/test/sdbus_mock.hpp>
7 #include <xyz/openbmc_project/Common/error.hpp>
8 #include <xyz/openbmc_project/User/Common/error.hpp>
9 
10 #include <chrono>
11 #include <exception>
12 #include <filesystem>
13 #include <fstream>
14 #include <vector>
15 
16 #include <gmock/gmock.h>
17 #include <gtest/gtest.h>
18 
19 namespace phosphor
20 {
21 namespace user
22 {
23 
24 using ::testing::_;
25 using ::testing::Invoke;
26 using ::testing::Return;
27 using ::testing::Throw;
28 
29 using InternalFailure =
30     sdbusplus::xyz::openbmc_project::Common::Error::InternalFailure;
31 using UserNameDoesNotExist =
32     sdbusplus::xyz::openbmc_project::User::Common::Error::UserNameDoesNotExist;
33 
34 namespace
35 {
36 inline static constexpr auto secondsPerDay =
37     std::chrono::duration_cast<std::chrono::seconds>(std::chrono::days{1})
38         .count();
39 
40 uint64_t getEpochTimeNow()
41 {
42     using namespace std::chrono;
43 
44     return duration_cast<seconds>(system_clock::now().time_since_epoch())
45         .count();
46 }
47 
48 std::string getNextUserName()
49 {
50     static std::string userName{"testUserName"};
51     static int id{0};
52 
53     return userName + std::to_string(id++);
54 }
55 
56 struct PasswordInfo
57 {
58     long lastChangeDate;
59     long maxAge;
60 };
61 
62 struct PasswordExpirationInfo
63 {
64     long lastChangeDate;
65     long oldmaxAge;
66     long newMaxAge;
67     uint64_t passwordExpiration;
68 };
69 
70 void fillPasswordExpiration(
71     const long lastChangeDaysAgo, const long oldPasswordAge,
72     const long nextPasswordChangeInDays, PasswordExpirationInfo& info)
73 {
74     using namespace std::chrono;
75 
76     info.lastChangeDate =
77         duration_cast<days>(seconds{getEpochTimeNow()}).count() -
78         lastChangeDaysAgo;
79 
80     info.oldmaxAge = oldPasswordAge;
81     info.newMaxAge = nextPasswordChangeInDays + lastChangeDaysAgo;
82 
83     info.passwordExpiration =
84         getEpochTimeNow() + nextPasswordChangeInDays * secondsPerDay;
85 }
86 
87 } // namespace
88 
89 class TestUserMgr : public testing::Test
90 {
91   public:
92     testing::NiceMock<sdbusplus::SdBusMock> sdBusMock;
93     sdbusplus::bus_t bus;
94     MockManager mockManager;
95 
96     TestUserMgr() :
97         bus(sdbusplus::get_mocked_new(&sdBusMock)), mockManager(bus, objpath)
98     {}
99 
100     void createLocalUser(const std::string& userName,
101                          std::vector<std::string> groupNames,
102                          const std::string& priv, bool enabled)
103     {
104         sdbusplus::message::object_path tempObjPath(usersObjPath);
105         tempObjPath /= userName;
106         std::string userObj(tempObjPath);
107         if (enabled)
108         {
109             ON_CALL(mockManager, isUserEnabled)
110                 .WillByDefault(testing::Return(true));
111         }
112         else
113         {
114             ON_CALL(mockManager, isUserEnabled)
115                 .WillByDefault(testing::Return(false));
116         }
117         mockManager.usersList.emplace(
118             userName, std::make_unique<phosphor::user::Users>(
119                           mockManager.bus, userObj.c_str(), groupNames, priv,
120                           enabled, std::nullopt, mockManager));
121     }
122 
123     DbusUserObj createPrivilegeMapperDbusObject(void)
124     {
125         DbusUserObj object;
126         DbusUserObjValue objValue;
127 
128         DbusUserObjPath objPath("/xyz/openbmc_project/user/ldap/openldap");
129         DbusUserPropVariant enabled(true);
130         DbusUserObjProperties property = {std::make_pair("Enabled", enabled)};
131         std::string intf = "xyz.openbmc_project.Object.Enable";
132         objValue.emplace(intf, property);
133         object.emplace(objPath, objValue);
134 
135         DbusUserObjPath objectPath(
136             "/xyz/openbmc_project/user/ldap/openldap/role_map/1");
137         std::string group = "ldapGroup";
138         std::string priv = "priv-admin";
139         DbusUserObjProperties properties = {std::make_pair("GroupName", group),
140                                             std::make_pair("Privilege", priv)};
141         std::string interface = "xyz.openbmc_project.User.PrivilegeMapperEntry";
142 
143         objValue.emplace(interface, properties);
144         object.emplace(objectPath, objValue);
145 
146         return object;
147     }
148 
149     DbusUserObj createLdapConfigObjectWithoutPrivilegeMapper(void)
150     {
151         DbusUserObj object;
152         DbusUserObjValue objValue;
153 
154         DbusUserObjPath objPath("/xyz/openbmc_project/user/ldap/openldap");
155         DbusUserPropVariant enabled(true);
156         DbusUserObjProperties property = {std::make_pair("Enabled", enabled)};
157         std::string intf = "xyz.openbmc_project.Object.Enable";
158         objValue.emplace(intf, property);
159         object.emplace(objPath, objValue);
160         return object;
161     }
162 
163     auto& getUser(const std::string& userName)
164     {
165         return *mockManager.usersList[userName].get();
166     }
167 
168     void testPasswordExpirationSet(const std::string& userName,
169                                    const PasswordInfo& oldInfo,
170                                    const PasswordInfo& newInfo)
171     {
172         EXPECT_CALL(mockManager, getShadowData(testing::StrEq(userName), _))
173             .WillOnce(Invoke([&oldInfo](auto, struct spwd& spwd) {
174                 spwd.sp_lstchg = oldInfo.lastChangeDate;
175                 spwd.sp_max = oldInfo.maxAge;
176             }));
177 
178         EXPECT_CALL(mockManager, executeUserPasswordExpiration(
179                                      testing::StrEq(userName),
180                                      newInfo.lastChangeDate, newInfo.maxAge))
181             .Times(1);
182 
183         createLocalUser(userName, {"ssh"}, "priv-admin", true);
184 
185         const auto expirationTime =
186             (newInfo.lastChangeDate + newInfo.maxAge) * secondsPerDay;
187 
188         auto& user = getUser(userName);
189         EXPECT_EQ(expirationTime, user.passwordExpiration(expirationTime));
190         EXPECT_EQ(expirationTime, user.passwordExpiration());
191     }
192 
193     void testPasswordExpirationReset(const std::string& userName,
194                                      const PasswordInfo& info)
195     {
196         EXPECT_CALL(mockManager, getShadowData(testing::StrEq(userName), _))
197             .WillOnce(Invoke([&info](auto, struct spwd& spwd) {
198                 spwd.sp_lstchg = info.lastChangeDate;
199                 spwd.sp_max = info.maxAge;
200             }));
201 
202         EXPECT_CALL(mockManager,
203                     executeUserPasswordExpiration(
204                         testing::StrEq(userName), info.lastChangeDate,
205                         mockManager.getUnexpiringPasswordAge()))
206             .Times(1);
207 
208         createLocalUser(userName, {"ssh"}, "priv-admin", true);
209 
210         const auto expirationTime = UserMgr::getUnexpiringPasswordTime();
211 
212         auto& user = getUser(userName);
213         EXPECT_EQ(expirationTime, user.passwordExpiration(expirationTime));
214         EXPECT_EQ(expirationTime, user.passwordExpiration());
215     }
216 
217     void testPasswordExpirationGet(const std::string& userName,
218                                    const PasswordInfo& info,
219                                    const uint64_t expectedPasswordExpiration)
220     {
221         EXPECT_CALL(mockManager, getShadowData(testing::StrEq(userName), _))
222             .WillOnce(Invoke([&info](auto, struct spwd& spwd) {
223                 spwd.sp_lstchg = info.lastChangeDate;
224                 spwd.sp_max = info.maxAge;
225             }));
226 
227         createLocalUser(userName, {"ssh"}, "priv-admin", true);
228 
229         EXPECT_EQ(mockManager.getPasswordExpiration(userName),
230                   expectedPasswordExpiration);
231     }
232 };
233 
234 TEST_F(TestUserMgr, ldapEntryDoesNotExist)
235 {
236     std::string userName = "user";
237     UserInfoMap userInfo;
238 
239     EXPECT_CALL(mockManager, getPrimaryGroup(userName))
240         .WillRepeatedly(Throw(UserNameDoesNotExist()));
241     EXPECT_THROW(userInfo = mockManager.getUserInfo(userName),
242                  UserNameDoesNotExist);
243 }
244 
245 TEST_F(TestUserMgr, localUser)
246 {
247     UserInfoMap userInfo;
248     std::string userName = "testUser";
249     std::string privilege = "priv-admin";
250     std::vector<std::string> groups{"testGroup"};
251     // Create local user
252     createLocalUser(userName, groups, privilege, true);
253     EXPECT_CALL(mockManager, userLockedForFailedAttempt(userName)).Times(1);
254     userInfo = mockManager.getUserInfo(userName);
255 
256     EXPECT_EQ(privilege, std::get<std::string>(userInfo["UserPrivilege"]));
257     EXPECT_EQ(groups,
258               std::get<std::vector<std::string>>(userInfo["UserGroups"]));
259     EXPECT_EQ(true, std::get<bool>(userInfo["UserEnabled"]));
260     EXPECT_EQ(false, std::get<bool>(userInfo["UserLockedForFailedAttempt"]));
261     EXPECT_EQ(false, std::get<bool>(userInfo["UserPasswordExpired"]));
262     // check password expiration against default value
263     EXPECT_EQ(std::numeric_limits<uint64_t>::max(),
264               std::get<PasswordExpiration>(userInfo["PasswordExpiration"]));
265     EXPECT_EQ(false, std::get<bool>(userInfo["RemoteUser"]));
266 }
267 
268 TEST_F(TestUserMgr, ldapUserWithPrivMapper)
269 {
270     UserInfoMap userInfo;
271     std::string userName = "ldapUser";
272     std::string ldapGroup = "ldapGroup";
273     gid_t primaryGid = 1000;
274 
275     EXPECT_CALL(mockManager, getPrimaryGroup(userName))
276         .WillRepeatedly(Return(primaryGid));
277     // Create privilege mapper dbus object
278     DbusUserObj object = createPrivilegeMapperDbusObject();
279     EXPECT_CALL(mockManager, getPrivilegeMapperObject())
280         .WillRepeatedly(Return(object));
281     EXPECT_CALL(mockManager, isGroupMember(userName, primaryGid, ldapGroup))
282         .WillRepeatedly(Return(true));
283     userInfo = mockManager.getUserInfo(userName);
284     EXPECT_EQ(true, std::get<bool>(userInfo["RemoteUser"]));
285     EXPECT_EQ("priv-admin", std::get<std::string>(userInfo["UserPrivilege"]));
286 }
287 
288 TEST_F(TestUserMgr, ldapUserWithoutPrivMapper)
289 {
290     using ::testing::_;
291 
292     UserInfoMap userInfo;
293     std::string userName = "ldapUser";
294     std::string ldapGroup = "ldapGroup";
295     gid_t primaryGid = 1000;
296 
297     EXPECT_CALL(mockManager, getPrimaryGroup(userName))
298         .WillRepeatedly(Return(primaryGid));
299     // Create LDAP config object without privilege mapper
300     DbusUserObj object = createLdapConfigObjectWithoutPrivilegeMapper();
301     EXPECT_CALL(mockManager, getPrivilegeMapperObject())
302         .WillRepeatedly(Return(object));
303     EXPECT_CALL(mockManager, isGroupMember(_, _, _)).Times(0);
304     userInfo = mockManager.getUserInfo(userName);
305     EXPECT_EQ(true, std::get<bool>(userInfo["RemoteUser"]));
306     EXPECT_EQ("", std::get<std::string>(userInfo["UserPrivilege"]));
307 }
308 
309 TEST_F(TestUserMgr, PasswordExpiration)
310 {
311     testPasswordExpirationSet(getNextUserName(), {2, 10}, {2, 3});
312 }
313 
314 TEST_F(TestUserMgr, PasswordExpirationLastChangeNegative)
315 {
316     using namespace std::chrono;
317 
318     const long lastChangeDate =
319         duration_cast<days>(seconds{getEpochTimeNow()}).count();
320 
321     testPasswordExpirationSet(getNextUserName(), {-2, 15}, {lastChangeDate, 3});
322 }
323 
324 TEST_F(TestUserMgr, PasswordExpirationLastChangeZero)
325 {
326     using namespace std::chrono;
327 
328     const long lastChangeDate =
329         duration_cast<days>(seconds{getEpochTimeNow()}).count();
330 
331     testPasswordExpirationSet(getNextUserName(), {0, 7}, {lastChangeDate, 6});
332 }
333 
334 TEST_F(TestUserMgr, PasswordExpirationLastMaxAgeNegative)
335 {
336     testPasswordExpirationSet(getNextUserName(), {10, -5}, {10, 6});
337 }
338 
339 TEST_F(TestUserMgr, PasswordExpirationReset)
340 {
341     testPasswordExpirationReset(getNextUserName(), {2, 10});
342 }
343 
344 TEST_F(TestUserMgr, PasswordExpirationResetLastChangeNegative)
345 {
346     testPasswordExpirationReset(getNextUserName(), {-5, 8});
347 }
348 
349 TEST_F(TestUserMgr, PasswordExpirationResetLastChangeZero)
350 {
351     testPasswordExpirationReset(getNextUserName(), {0, 13});
352 }
353 
354 TEST_F(TestUserMgr, PasswordExpirationResetMaxAgeNegative)
355 {
356     testPasswordExpirationReset(getNextUserName(), {2, -2});
357 }
358 
359 TEST_F(TestUserMgr, PasswordExpirationGet)
360 {
361     constexpr long lastChangeDate = 7;
362     constexpr long passwordAge = 4;
363     constexpr uint64_t expirationTime =
364         (lastChangeDate + passwordAge) * secondsPerDay;
365 
366     testPasswordExpirationGet(getNextUserName(), {lastChangeDate, passwordAge},
367                               expirationTime);
368 }
369 
370 TEST_F(TestUserMgr, PasswordExpirationSetDefault)
371 {
372     const std::string userName = getNextUserName();
373 
374     createLocalUser(userName, {"ssh"}, "priv-admin", true);
375 
376     auto& user = getUser(userName);
377 
378     EXPECT_EQ(user.passwordExpiration(UserMgr::getUnexpiringPasswordTime()),
379               UserMgr::getUnexpiringPasswordTime());
380 
381     EXPECT_EQ(user.passwordExpiration(UserMgr::getDefaultPasswordExpiration()),
382               UserMgr::getDefaultPasswordExpiration());
383 }
384 
385 TEST_F(TestUserMgr, PasswordExpirationGetDefault)
386 {
387     const std::string userName = getNextUserName();
388 
389     createLocalUser(userName, {"ssh"}, "priv-admin", true);
390 
391     auto& user = getUser(userName);
392 
393     EXPECT_EQ(user.passwordExpiration(),
394               UserMgr::getDefaultPasswordExpiration());
395 }
396 
397 TEST_F(TestUserMgr, PasswordExpirationGetLastChangeNegative)
398 {
399     testPasswordExpirationGet(getNextUserName(), {-5, 8},
400                               UserMgr::getUnexpiringPasswordTime());
401 }
402 
403 TEST_F(TestUserMgr, PasswordExpirationGetLastChangeZero)
404 {
405     using namespace std::chrono;
406 
407     const std::string userName = getNextUserName();
408     constexpr long lastChangeDate = 0;
409     constexpr long passwordAge = 4;
410 
411     EXPECT_CALL(mockManager, getShadowData(testing::StrEq(userName), _))
412         .WillOnce(Invoke([](auto, struct spwd& spwd) {
413             spwd.sp_lstchg = lastChangeDate;
414             spwd.sp_max = passwordAge;
415         }));
416 
417     createLocalUser(userName, {"ssh"}, "priv-admin", true);
418 
419     auto expirationTime =
420         duration_cast<minutes>(seconds{getEpochTimeNow()}).count();
421     auto time = duration_cast<minutes>(
422                     seconds{mockManager.getPasswordExpiration(userName)})
423                     .count();
424 
425     // compare expiration time in minutes to avoid situation where times
426     // measured in second can be different
427     EXPECT_EQ(time, expirationTime);
428 }
429 
430 TEST_F(TestUserMgr, PasswordExpirationGetMaxAgeNegative)
431 {
432     testPasswordExpirationGet(getNextUserName(), {12, -2},
433                               UserMgr::getUnexpiringPasswordTime());
434 }
435 
436 TEST_F(TestUserMgr, PasswordExpirationShadowFail)
437 {
438     const std::string userName = getNextUserName();
439 
440     EXPECT_CALL(mockManager, getShadowData(testing::StrEq(userName), _))
441         .WillOnce([]() {
442             throw sdbusplus::xyz::openbmc_project::Common::Error::
443                 InternalFailure();
444         });
445 
446     EXPECT_CALL(mockManager, executeUserPasswordExpiration(_, _, _)).Times(0);
447 
448     createLocalUser(userName, {"ssh"}, "priv-admin", true);
449     auto& user = getUser(userName);
450 
451     const auto oldTime = user.passwordExpiration();
452 
453     EXPECT_THROW(
454         user.passwordExpiration(0),
455         sdbusplus::xyz::openbmc_project::Common::Error::InternalFailure);
456     EXPECT_EQ(oldTime, user.passwordExpiration());
457 }
458 
459 TEST_F(TestUserMgr, PasswordExpirationInvalidDate)
460 {
461     const std::string userName = getNextUserName();
462 
463     EXPECT_CALL(mockManager, getShadowData(testing::StrEq(userName), _))
464         .WillOnce(Invoke([](auto, struct spwd& spwd) {
465             spwd.sp_lstchg = 2;
466             spwd.sp_max = 2;
467         }));
468 
469     EXPECT_CALL(mockManager, executeUserPasswordExpiration(_, _, _)).Times(0);
470 
471     createLocalUser(userName, {"ssh"}, "priv-admin", true);
472     auto& user = getUser(userName);
473 
474     const auto oldTime = user.passwordExpiration();
475 
476     EXPECT_THROW(
477         user.passwordExpiration(1),
478         sdbusplus::xyz::openbmc_project::Common::Error::InvalidArgument);
479     EXPECT_EQ(oldTime, user.passwordExpiration());
480 }
481 
482 TEST_F(TestUserMgr, PasswordExpirationExecFail)
483 {
484     const std::string userName = getNextUserName();
485 
486     constexpr long lastChangeDate = 3;
487     EXPECT_CALL(mockManager, getShadowData(testing::StrEq(userName), _))
488         .WillOnce(Invoke([](auto, struct spwd& spwd) {
489             spwd.sp_lstchg = lastChangeDate;
490             spwd.sp_max = 5;
491         }));
492 
493     constexpr long passwordAge = 11;
494     EXPECT_CALL(mockManager,
495                 executeUserPasswordExpiration(testing::StrEq(userName),
496                                               lastChangeDate, passwordAge))
497         .WillOnce([]() { throw std::exception(); });
498 
499     createLocalUser(userName, {"ssh"}, "priv-admin", true);
500     auto& user = getUser(userName);
501 
502     const auto oldTime = user.passwordExpiration();
503     const auto expirationTime = (lastChangeDate + passwordAge) * secondsPerDay;
504 
505     EXPECT_THROW(
506         user.passwordExpiration(expirationTime),
507         sdbusplus::xyz::openbmc_project::Common::Error::InternalFailure);
508     EXPECT_EQ(oldTime, user.passwordExpiration());
509 }
510 
511 TEST(GetCSVFromVector, EmptyVectorReturnsEmptyString)
512 {
513     EXPECT_EQ(getCSVFromVector({}), "");
514 }
515 
516 TEST(GetCSVFromVector, ElementsAreJoinedByComma)
517 {
518     EXPECT_EQ(getCSVFromVector(std::vector<std::string>{"123"}), "123");
519     EXPECT_EQ(getCSVFromVector(std::vector<std::string>{"123", "456"}),
520               "123,456");
521 }
522 
523 TEST(RemoveStringFromCSV, WithoutDeleteStringReturnsFalse)
524 {
525     std::string expected = "whatever,https";
526     std::string str = expected;
527     EXPECT_FALSE(removeStringFromCSV(str, "ssh"));
528     EXPECT_EQ(str, expected);
529 
530     std::string empty;
531     EXPECT_FALSE(removeStringFromCSV(empty, "ssh"));
532 }
533 
534 TEST(RemoveStringFromCSV, WithDeleteStringReturnsTrue)
535 {
536     std::string expected = "whatever";
537     std::string str = "whatever,https";
538     EXPECT_TRUE(removeStringFromCSV(str, "https"));
539     EXPECT_EQ(str, expected);
540 
541     str = "https";
542     EXPECT_TRUE(removeStringFromCSV(str, "https"));
543     EXPECT_EQ(str, "");
544 }
545 
546 namespace
547 {
548 inline constexpr const char* objectRootInTest = "/xyz/openbmc_project/user";
549 
550 // Fake configs; referenced configs on real BMC
551 inline constexpr const char* rawFailLockConfig = R"(
552 deny=2
553 unlock_time=3
554 )";
555 inline constexpr const char* rawPWHistoryConfig = R"(
556 enforce_for_root
557 remember=0
558 )";
559 inline constexpr const char* rawPWQualityConfig = R"(
560 enforce_for_root
561 minlen=8
562 difok=0
563 lcredit=0
564 ocredit=0
565 dcredit=0
566 ucredit=0
567 )";
568 } // namespace
569 
570 void dumpStringToFile(const std::string& str, const std::string& filePath)
571 {
572     std::ofstream outputFileStream;
573 
574     outputFileStream.exceptions(
575         std::ofstream::failbit | std::ofstream::badbit | std::ofstream::eofbit);
576 
577     outputFileStream.open(filePath, std::ios::out);
578     outputFileStream << str << "\n" << std::flush;
579     outputFileStream.close();
580 }
581 
582 void removeFile(const std::string& filePath)
583 {
584     std::filesystem::remove(filePath);
585 }
586 
587 class UserMgrInTest : public testing::Test, public UserMgr
588 {
589   public:
590     UserMgrInTest() : UserMgr(busInTest, objectRootInTest)
591     {
592         {
593             tempFaillockConfigFile = tempFilePath;
594             int fd = mkstemp(tempFaillockConfigFile.data());
595             EXPECT_NE(-1, fd);
596             EXPECT_NO_THROW(
597                 dumpStringToFile(rawFailLockConfig, tempFaillockConfigFile));
598             if (fd != -1)
599             {
600                 close(fd);
601             }
602         }
603 
604         {
605             tempPWHistoryConfigFile = tempFilePath;
606             int fd = mkstemp(tempPWHistoryConfigFile.data());
607             EXPECT_NE(-1, fd);
608             EXPECT_NO_THROW(
609                 dumpStringToFile(rawPWHistoryConfig, tempPWHistoryConfigFile));
610             if (fd != -1)
611             {
612                 close(fd);
613             }
614         }
615 
616         {
617             tempPWQualityConfigFile = tempFilePath;
618             int fd = mkstemp(tempPWQualityConfigFile.data());
619             EXPECT_NE(-1, fd);
620             EXPECT_NO_THROW(
621                 dumpStringToFile(rawPWQualityConfig, tempPWQualityConfigFile));
622             if (fd != -1)
623             {
624                 close(fd);
625             }
626         }
627 
628         // Set config files to test files
629         faillockConfigFile = tempFaillockConfigFile;
630         pwHistoryConfigFile = tempPWHistoryConfigFile;
631         pwQualityConfigFile = tempPWQualityConfigFile;
632 
633         ON_CALL(*this, executeUserAdd(testing::_, testing::_, testing::_,
634                                       testing::Eq(true)))
635             .WillByDefault([this]() {
636                 ON_CALL(*this, isUserEnabled)
637                     .WillByDefault(testing::Return(true));
638                 testing::Return();
639             });
640 
641         ON_CALL(*this, executeUserAdd(testing::_, testing::_, testing::_,
642                                       testing::Eq(false)))
643             .WillByDefault([this]() {
644                 ON_CALL(*this, isUserEnabled)
645                     .WillByDefault(testing::Return(false));
646                 testing::Return();
647             });
648 
649         ON_CALL(*this, executeUserDelete).WillByDefault(testing::Return());
650 
651         ON_CALL(*this, executeUserClearFailRecords)
652             .WillByDefault(testing::Return());
653 
654         ON_CALL(*this, getIpmiUsersCount).WillByDefault(testing::Return(0));
655 
656         ON_CALL(*this, executeUserRename).WillByDefault(testing::Return());
657 
658         ON_CALL(*this, executeUserModify(testing::_, testing::_, testing::_))
659             .WillByDefault(testing::Return());
660 
661         ON_CALL(*this,
662                 executeUserModifyUserEnable(testing::_, testing::Eq(true)))
663             .WillByDefault([this]() {
664                 ON_CALL(*this, isUserEnabled)
665                     .WillByDefault(testing::Return(true));
666                 testing::Return();
667             });
668 
669         ON_CALL(*this,
670                 executeUserModifyUserEnable(testing::_, testing::Eq(false)))
671             .WillByDefault([this]() {
672                 ON_CALL(*this, isUserEnabled)
673                     .WillByDefault(testing::Return(false));
674                 testing::Return();
675             });
676 
677         ON_CALL(*this, executeGroupCreation(testing::_))
678             .WillByDefault(testing::Return());
679 
680         ON_CALL(*this, executeGroupDeletion(testing::_))
681             .WillByDefault(testing::Return());
682 
683         ON_CALL(*this, executeGroupCreation).WillByDefault(testing::Return());
684 
685         ON_CALL(*this, executeGroupDeletion).WillByDefault(testing::Return());
686     }
687 
688     ~UserMgrInTest() override
689     {
690         EXPECT_NO_THROW(removeFile(tempFaillockConfigFile));
691         EXPECT_NO_THROW(removeFile(tempPWHistoryConfigFile));
692         EXPECT_NO_THROW(removeFile(tempPWQualityConfigFile));
693     }
694 
695     MOCK_METHOD(void, executeUserAdd, (const char*, const char*, bool, bool),
696                 (override));
697 
698     MOCK_METHOD(void, executeUserDelete, (const char*), (override));
699 
700     MOCK_METHOD(void, executeUserClearFailRecords, (const char*), (override));
701 
702     MOCK_METHOD(size_t, getIpmiUsersCount, (), (override));
703 
704     MOCK_METHOD(void, executeUserRename, (const char*, const char*),
705                 (override));
706 
707     MOCK_METHOD(void, executeUserModify, (const char*, const char*, bool),
708                 (override));
709 
710     MOCK_METHOD(void, executeUserModifyUserEnable, (const char*, bool),
711                 (override));
712 
713     MOCK_METHOD(std::vector<std::string>, getFailedAttempt, (const char*),
714                 (override));
715 
716     MOCK_METHOD(void, executeGroupCreation, (const char*), (override));
717 
718     MOCK_METHOD(void, executeGroupDeletion, (const char*), (override));
719 
720     MOCK_METHOD(bool, isUserEnabled, (const std::string& userName), (override));
721 
722     MOCK_METHOD(void, getShadowData, (const std::string&, struct spwd& spwd),
723                 (const, override));
724 
725     MOCK_METHOD(void, executeUserPasswordExpiration,
726                 (const char*, const long int, const long int),
727                 (const, override));
728 
729   protected:
730     static constexpr auto tempFilePath = "/tmp/test-data-XXXXXX";
731 
732     static sdbusplus::bus_t busInTest;
733     std::string tempFaillockConfigFile;
734     std::string tempPWHistoryConfigFile;
735     std::string tempPWQualityConfigFile;
736 
737     void setUpCreateUser(const std::string& userName, bool enabled)
738     {
739         EXPECT_CALL(*this, getIpmiUsersCount).WillOnce(testing::Return(0));
740 
741         EXPECT_CALL(*this,
742                     executeUserAdd(testing::StrEq(userName), _, _, enabled))
743             .Times(1);
744     }
745 
746     void setUpGetUserInfo(const std::string& userName, bool enabled)
747     {
748         EXPECT_CALL(*this, isUserEnabled(userName))
749             .WillOnce(testing::Return(enabled));
750     }
751 
752     void setUpSetPasswordExpiration(const std::string& userName,
753                                     const PasswordExpirationInfo& info)
754     {
755         EXPECT_CALL(*this, getShadowData(testing::StrEq(userName), _))
756             .WillOnce(Invoke([&info](auto, struct spwd& spwd) {
757                 spwd.sp_lstchg = info.lastChangeDate;
758                 spwd.sp_max = info.oldmaxAge;
759             }));
760 
761         EXPECT_CALL(*this, executeUserPasswordExpiration(
762                                testing::StrEq(userName), info.lastChangeDate,
763                                info.newMaxAge))
764             .Times(1);
765     }
766 
767     void setUpDeleteUser(const std::string& userName)
768     {
769         EXPECT_CALL(*this,
770                     executeUserClearFailRecords(testing::StrEq(userName)))
771             .Times(1);
772 
773         EXPECT_CALL(*this, executeUserDelete(testing::StrEq(userName)))
774             .Times(1);
775     }
776 };
777 
778 sdbusplus::bus_t UserMgrInTest::busInTest = sdbusplus::bus::new_default();
779 
780 TEST_F(UserMgrInTest, GetPamModuleConfValueOnSuccess)
781 {
782     std::string minlen;
783     EXPECT_EQ(getPamModuleConfValue(tempPWQualityConfigFile, "minlen", minlen),
784               0);
785     EXPECT_EQ(minlen, "8");
786     std::string deny;
787     EXPECT_EQ(getPamModuleConfValue(tempFaillockConfigFile, "deny", deny), 0);
788     EXPECT_EQ(deny, "2");
789     std::string remember;
790     EXPECT_EQ(
791         getPamModuleConfValue(tempPWHistoryConfigFile, "remember", remember),
792         0);
793     EXPECT_EQ(remember, "0");
794 }
795 
796 TEST_F(UserMgrInTest, SetPamModuleConfValueOnSuccess)
797 {
798     EXPECT_EQ(setPamModuleConfValue(tempPWQualityConfigFile, "minlen", "16"),
799               0);
800     std::string minlen;
801     EXPECT_EQ(getPamModuleConfValue(tempPWQualityConfigFile, "minlen", minlen),
802               0);
803     EXPECT_EQ(minlen, "16");
804 
805     EXPECT_EQ(setPamModuleConfValue(tempFaillockConfigFile, "deny", "3"), 0);
806     std::string deny;
807     EXPECT_EQ(getPamModuleConfValue(tempFaillockConfigFile, "deny", deny), 0);
808     EXPECT_EQ(deny, "3");
809 
810     EXPECT_EQ(setPamModuleConfValue(tempPWHistoryConfigFile, "remember", "1"),
811               0);
812     std::string remember;
813     EXPECT_EQ(
814         getPamModuleConfValue(tempPWHistoryConfigFile, "remember", remember),
815         0);
816     EXPECT_EQ(remember, "1");
817 }
818 
819 TEST_F(UserMgrInTest, SetPamModuleConfValueTempFileOnSuccess)
820 {
821     EXPECT_EQ(setPamModuleConfValue(tempPWQualityConfigFile, "minlen", "16"),
822               0);
823 
824     std::string tmpFile = tempPWQualityConfigFile + "_tmp";
825     EXPECT_FALSE(std::filesystem::exists(tmpFile));
826 
827     EXPECT_EQ(setPamModuleConfValue(tempFaillockConfigFile, "deny", "3"), 0);
828 
829     tmpFile = tempFaillockConfigFile + "_tmp";
830     EXPECT_FALSE(std::filesystem::exists(tmpFile));
831 
832     EXPECT_EQ(setPamModuleConfValue(tempPWHistoryConfigFile, "remember", "1"),
833               0);
834 
835     tmpFile = tempPWHistoryConfigFile + "_tmp";
836     EXPECT_FALSE(std::filesystem::exists(tmpFile));
837 }
838 
839 TEST_F(UserMgrInTest, GetPamModuleConfValueOnFailure)
840 {
841     EXPECT_NO_THROW(dumpStringToFile("whatever", tempPWQualityConfigFile));
842     std::string minlen;
843     EXPECT_EQ(getPamModuleConfValue(tempPWQualityConfigFile, "minlen", minlen),
844               -1);
845 
846     EXPECT_NO_THROW(removeFile(tempPWQualityConfigFile));
847     EXPECT_EQ(getPamModuleConfValue(tempPWQualityConfigFile, "minlen", minlen),
848               -1);
849 
850     EXPECT_NO_THROW(dumpStringToFile("whatever", tempFaillockConfigFile));
851     std::string deny;
852     EXPECT_EQ(getPamModuleConfValue(tempFaillockConfigFile, "deny", deny), -1);
853 
854     EXPECT_NO_THROW(removeFile(tempFaillockConfigFile));
855     EXPECT_EQ(getPamModuleConfValue(tempFaillockConfigFile, "deny", deny), -1);
856 
857     EXPECT_NO_THROW(dumpStringToFile("whatever", tempPWHistoryConfigFile));
858     std::string remember;
859     EXPECT_EQ(
860         getPamModuleConfValue(tempPWHistoryConfigFile, "remember", remember),
861         -1);
862 
863     EXPECT_NO_THROW(removeFile(tempPWHistoryConfigFile));
864     EXPECT_EQ(
865         getPamModuleConfValue(tempPWHistoryConfigFile, "remember", remember),
866         -1);
867 }
868 
869 TEST_F(UserMgrInTest, SetPamModuleConfValueOnFailure)
870 {
871     EXPECT_NO_THROW(dumpStringToFile("whatever", tempPWQualityConfigFile));
872     EXPECT_EQ(setPamModuleConfValue(tempPWQualityConfigFile, "minlen", "16"),
873               -1);
874 
875     EXPECT_NO_THROW(removeFile(tempPWQualityConfigFile));
876     EXPECT_EQ(setPamModuleConfValue(tempPWQualityConfigFile, "minlen", "16"),
877               -1);
878 
879     EXPECT_NO_THROW(dumpStringToFile("whatever", tempFaillockConfigFile));
880     EXPECT_EQ(setPamModuleConfValue(tempFaillockConfigFile, "deny", "3"), -1);
881 
882     EXPECT_NO_THROW(removeFile(tempFaillockConfigFile));
883     EXPECT_EQ(setPamModuleConfValue(tempFaillockConfigFile, "deny", "3"), -1);
884 
885     EXPECT_NO_THROW(dumpStringToFile("whatever", tempPWHistoryConfigFile));
886     EXPECT_EQ(setPamModuleConfValue(tempPWHistoryConfigFile, "remember", "1"),
887               -1);
888 
889     EXPECT_NO_THROW(removeFile(tempPWHistoryConfigFile));
890     EXPECT_EQ(setPamModuleConfValue(tempPWHistoryConfigFile, "remember", "1"),
891               -1);
892 }
893 
894 TEST_F(UserMgrInTest, SetPamModuleConfValueTempFileOnFailure)
895 {
896     EXPECT_NO_THROW(dumpStringToFile("whatever", tempPWQualityConfigFile));
897     EXPECT_EQ(setPamModuleConfValue(tempPWQualityConfigFile, "minlen", "16"),
898               -1);
899 
900     std::string tmpFile = tempPWQualityConfigFile + "_tmp";
901     EXPECT_FALSE(std::filesystem::exists(tmpFile));
902 
903     EXPECT_NO_THROW(removeFile(tempPWQualityConfigFile));
904     EXPECT_EQ(setPamModuleConfValue(tempPWQualityConfigFile, "minlen", "16"),
905               -1);
906 
907     EXPECT_FALSE(std::filesystem::exists(tmpFile));
908 
909     EXPECT_NO_THROW(dumpStringToFile("whatever", tempFaillockConfigFile));
910     EXPECT_EQ(setPamModuleConfValue(tempFaillockConfigFile, "deny", "3"), -1);
911 
912     tmpFile = tempFaillockConfigFile + "_tmp";
913     EXPECT_FALSE(std::filesystem::exists(tmpFile));
914 
915     EXPECT_NO_THROW(removeFile(tempFaillockConfigFile));
916     EXPECT_EQ(setPamModuleConfValue(tempFaillockConfigFile, "deny", "3"), -1);
917 
918     EXPECT_FALSE(std::filesystem::exists(tmpFile));
919 
920     EXPECT_NO_THROW(dumpStringToFile("whatever", tempPWHistoryConfigFile));
921     EXPECT_EQ(setPamModuleConfValue(tempPWHistoryConfigFile, "remember", "1"),
922               -1);
923 
924     tmpFile = tempPWHistoryConfigFile + "_tmp";
925     EXPECT_FALSE(std::filesystem::exists(tmpFile));
926 
927     EXPECT_NO_THROW(removeFile(tempPWHistoryConfigFile));
928     EXPECT_EQ(setPamModuleConfValue(tempPWHistoryConfigFile, "remember", "1"),
929               -1);
930 
931     EXPECT_FALSE(std::filesystem::exists(tmpFile));
932 }
933 
934 TEST_F(UserMgrInTest, IsUserExistEmptyInputThrowsError)
935 {
936     EXPECT_THROW(
937         isUserExist(""),
938         sdbusplus::xyz::openbmc_project::Common::Error::InvalidArgument);
939 }
940 
941 TEST_F(UserMgrInTest, ThrowForUserDoesNotExistThrowsError)
942 {
943     EXPECT_THROW(throwForUserDoesNotExist("whatever"),
944                  sdbusplus::xyz::openbmc_project::User::Common::Error::
945                      UserNameDoesNotExist);
946 }
947 
948 TEST_F(UserMgrInTest, ThrowForUserExistsThrowsError)
949 {
950     EXPECT_THROW(
951         throwForUserExists("root"),
952         sdbusplus::xyz::openbmc_project::User::Common::Error::UserNameExists);
953 }
954 
955 TEST_F(
956     UserMgrInTest,
957     ThrowForUserNameConstraintsExceedIpmiMaxUserNameLenThrowsUserNameGroupFail)
958 {
959     std::string strWith17Chars(17, 'A');
960     EXPECT_THROW(throwForUserNameConstraints(strWith17Chars, {"ipmi"}),
961                  sdbusplus::xyz::openbmc_project::User::Common::Error::
962                      UserNameGroupFail);
963 }
964 
965 TEST_F(
966     UserMgrInTest,
967     ThrowForUserNameConstraintsExceedSystemMaxUserNameLenThrowsInvalidArgument)
968 {
969     std::string strWith31Chars(101, 'A');
970     EXPECT_THROW(
971         throwForUserNameConstraints(strWith31Chars, {}),
972         sdbusplus::xyz::openbmc_project::Common::Error::InvalidArgument);
973 }
974 
975 TEST_F(UserMgrInTest,
976        ThrowForUserNameConstraintsRegexMismatchThrowsInvalidArgument)
977 {
978     std::string startWithNumber = "0ABC";
979     std::string startWithDisallowedCharacter = "[test";
980     EXPECT_THROW(
981         throwForUserNameConstraints(startWithNumber, {"ipmi"}),
982         sdbusplus::xyz::openbmc_project::Common::Error::InvalidArgument);
983     EXPECT_THROW(
984         throwForUserNameConstraints(startWithDisallowedCharacter, {"ipmi"}),
985         sdbusplus::xyz::openbmc_project::Common::Error::InvalidArgument);
986 }
987 
988 TEST_F(UserMgrInTest, UserAddNotRootFailedWithInternalFailure)
989 {
990     EXPECT_THROW(
991         UserMgr::executeUserAdd("user0", "ipmi,ssh", true, true),
992         sdbusplus::xyz::openbmc_project::Common::Error::InternalFailure);
993 }
994 
995 TEST_F(UserMgrInTest, UserDeleteNotRootFailedWithInternalFailure)
996 {
997     EXPECT_THROW(
998         UserMgr::executeUserDelete("user0"),
999         sdbusplus::xyz::openbmc_project::Common::Error::InternalFailure);
1000 }
1001 
1002 TEST_F(UserMgrInTest,
1003        ThrowForMaxGrpUserCountThrowsNoResourceWhenIpmiUserExceedLimit)
1004 {
1005     EXPECT_CALL(*this, getIpmiUsersCount()).WillOnce(Return(ipmiMaxUsers));
1006     EXPECT_THROW(
1007         throwForMaxGrpUserCount({"ipmi"}),
1008         sdbusplus::xyz::openbmc_project::User::Common::Error::NoResource);
1009 }
1010 
1011 TEST_F(UserMgrInTest, CreateUserThrowsInternalFailureWhenExecuteUserAddFails)
1012 {
1013     EXPECT_CALL(*this, executeUserAdd)
1014         .WillOnce(testing::Throw(
1015             sdbusplus::xyz::openbmc_project::Common::Error::InternalFailure()));
1016     EXPECT_THROW(
1017         createUser("whatever", {"redfish"}, "", true),
1018         sdbusplus::xyz::openbmc_project::Common::Error::InternalFailure);
1019 }
1020 
1021 TEST_F(UserMgrInTest, DeleteUserThrowsInternalFailureWhenExecuteUserDeleteFails)
1022 {
1023     std::string username = "user";
1024     EXPECT_NO_THROW(
1025         UserMgr::createUser(username, {"redfish", "ssh"}, "priv-user", true));
1026     EXPECT_CALL(*this, executeUserDelete(testing::StrEq(username)))
1027         .WillOnce(testing::Throw(
1028             sdbusplus::xyz::openbmc_project::Common::Error::InternalFailure()))
1029         .WillOnce(testing::DoDefault());
1030 
1031     EXPECT_THROW(
1032         deleteUser(username),
1033         sdbusplus::xyz::openbmc_project::Common::Error::InternalFailure);
1034     EXPECT_NO_THROW(UserMgr::deleteUser(username));
1035 }
1036 
1037 TEST_F(UserMgrInTest,
1038        DeleteUserThrowsInternalFailureWhenExecuteUserClearFailRecords)
1039 {
1040     const char* username = "user";
1041     EXPECT_NO_THROW(
1042         UserMgr::createUser(username, {"redfish", "ssh"}, "priv-user", true));
1043     EXPECT_CALL(*this, executeUserClearFailRecords(testing::StrEq(username)))
1044         .WillOnce(testing::Throw(
1045             sdbusplus::xyz::openbmc_project::Common::Error::InternalFailure()))
1046         .WillOnce(testing::DoDefault());
1047 
1048     EXPECT_THROW(
1049         deleteUser(username),
1050         sdbusplus::xyz::openbmc_project::Common::Error::InternalFailure);
1051     EXPECT_NO_THROW(UserMgr::deleteUser(username));
1052 }
1053 
1054 TEST_F(UserMgrInTest, ThrowForInvalidPrivilegeThrowsWhenPrivilegeIsInvalid)
1055 {
1056     EXPECT_THROW(
1057         throwForInvalidPrivilege("whatever"),
1058         sdbusplus::xyz::openbmc_project::Common::Error::InvalidArgument);
1059 }
1060 
1061 TEST_F(UserMgrInTest, ThrowForInvalidPrivilegeNoThrowWhenPrivilegeIsValid)
1062 {
1063     EXPECT_NO_THROW(throwForInvalidPrivilege("priv-admin"));
1064     EXPECT_NO_THROW(throwForInvalidPrivilege("priv-operator"));
1065     EXPECT_NO_THROW(throwForInvalidPrivilege("priv-user"));
1066 }
1067 
1068 TEST_F(UserMgrInTest, ThrowForInvalidGroupsThrowsWhenGroupIsInvalid)
1069 {
1070     EXPECT_THROW(
1071         throwForInvalidGroups({"whatever"}),
1072         sdbusplus::xyz::openbmc_project::Common::Error::InvalidArgument);
1073     EXPECT_THROW(
1074         throwForInvalidGroups({"web"}),
1075         sdbusplus::xyz::openbmc_project::Common::Error::InvalidArgument);
1076 }
1077 
1078 TEST_F(UserMgrInTest, ThrowForInvalidGroupsNoThrowWhenGroupIsValid)
1079 {
1080     EXPECT_NO_THROW(throwForInvalidGroups({"ipmi"}));
1081     EXPECT_NO_THROW(throwForInvalidGroups({"ssh"}));
1082     EXPECT_NO_THROW(throwForInvalidGroups({"redfish"}));
1083     EXPECT_NO_THROW(throwForInvalidGroups({"hostconsole"}));
1084 }
1085 
1086 TEST_F(UserMgrInTest, RenameUserOnSuccess)
1087 {
1088     std::string username = "user001";
1089     EXPECT_NO_THROW(
1090         UserMgr::createUser(username, {"redfish", "ssh"}, "priv-user", true));
1091     std::string newUsername = "user002";
1092 
1093     EXPECT_NO_THROW(UserMgr::renameUser(username, newUsername));
1094 
1095     // old username doesn't exist
1096     EXPECT_THROW(getUserInfo(username),
1097                  sdbusplus::xyz::openbmc_project::User::Common::Error::
1098                      UserNameDoesNotExist);
1099 
1100     UserInfoMap userInfo = getUserInfo(newUsername);
1101     EXPECT_EQ(std::get<Privilege>(userInfo["UserPrivilege"]), "priv-user");
1102     EXPECT_THAT(std::get<GroupList>(userInfo["UserGroups"]),
1103                 testing::UnorderedElementsAre("redfish", "ssh"));
1104     EXPECT_EQ(std::get<UserEnabled>(userInfo["UserEnabled"]), true);
1105 
1106     EXPECT_NO_THROW(UserMgr::deleteUser(newUsername));
1107 }
1108 
1109 TEST_F(UserMgrInTest, RenameUserThrowsInternalFailureIfExecuteUserModifyFails)
1110 {
1111     std::string username = "user001";
1112     EXPECT_NO_THROW(
1113         UserMgr::createUser(username, {"redfish", "ssh"}, "priv-user", true));
1114     std::string newUsername = "user002";
1115 
1116     EXPECT_CALL(*this, executeUserRename(testing::StrEq(username),
1117                                          testing::StrEq(newUsername)))
1118         .WillOnce(testing::Throw(
1119             sdbusplus::xyz::openbmc_project::Common::Error::InternalFailure()));
1120     EXPECT_THROW(
1121         UserMgr::renameUser(username, newUsername),
1122         sdbusplus::xyz::openbmc_project::Common::Error::InternalFailure);
1123 
1124     // The original user is unchanged
1125     UserInfoMap userInfo = getUserInfo(username);
1126     EXPECT_EQ(std::get<Privilege>(userInfo["UserPrivilege"]), "priv-user");
1127     EXPECT_THAT(std::get<GroupList>(userInfo["UserGroups"]),
1128                 testing::UnorderedElementsAre("redfish", "ssh"));
1129     EXPECT_EQ(std::get<UserEnabled>(userInfo["UserEnabled"]), true);
1130 
1131     EXPECT_NO_THROW(UserMgr::deleteUser(username));
1132 }
1133 
1134 TEST_F(UserMgrInTest, DefaultUserModifyFailedWithInternalFailure)
1135 {
1136     EXPECT_THROW(
1137         UserMgr::executeUserRename("user0", "user1"),
1138         sdbusplus::xyz::openbmc_project::Common::Error::InternalFailure);
1139     EXPECT_THROW(
1140         UserMgr::executeUserModify("user0", "ssh", true),
1141         sdbusplus::xyz::openbmc_project::Common::Error::InternalFailure);
1142 }
1143 
1144 TEST_F(UserMgrInTest, UpdateGroupsAndPrivOnSuccess)
1145 {
1146     std::string username = "user001";
1147     EXPECT_NO_THROW(
1148         UserMgr::createUser(username, {"redfish", "ssh"}, "priv-user", true));
1149     EXPECT_NO_THROW(
1150         updateGroupsAndPriv(username, {"ipmi", "ssh"}, "priv-admin"));
1151     UserInfoMap userInfo = getUserInfo(username);
1152     EXPECT_EQ(std::get<Privilege>(userInfo["UserPrivilege"]), "priv-admin");
1153     EXPECT_THAT(std::get<GroupList>(userInfo["UserGroups"]),
1154                 testing::UnorderedElementsAre("ipmi", "ssh"));
1155     EXPECT_EQ(std::get<UserEnabled>(userInfo["UserEnabled"]), true);
1156     EXPECT_NO_THROW(UserMgr::deleteUser(username));
1157 }
1158 
1159 TEST_F(UserMgrInTest,
1160        UpdateGroupsAndPrivThrowsInternalFailureIfExecuteUserModifyFail)
1161 {
1162     std::string username = "user001";
1163     EXPECT_NO_THROW(
1164         UserMgr::createUser(username, {"redfish", "ssh"}, "priv-user", true));
1165     EXPECT_CALL(*this, executeUserModify(testing::StrEq(username), testing::_,
1166                                          testing::_))
1167         .WillOnce(testing::Throw(
1168             sdbusplus::xyz::openbmc_project::Common::Error::InternalFailure()));
1169     EXPECT_THROW(
1170         updateGroupsAndPriv(username, {"ipmi", "ssh"}, "priv-admin"),
1171         sdbusplus::xyz::openbmc_project::Common::Error::InternalFailure);
1172     EXPECT_NO_THROW(UserMgr::deleteUser(username));
1173 }
1174 
1175 TEST_F(UserMgrInTest, MinPasswordLengthReturnsIfValueIsTheSame)
1176 {
1177     initializeAccountPolicy();
1178     EXPECT_EQ(AccountPolicyIface::minPasswordLength(), 8);
1179     UserMgr::minPasswordLength(8);
1180     EXPECT_EQ(AccountPolicyIface::minPasswordLength(), 8);
1181 }
1182 
1183 TEST_F(UserMgrInTest,
1184        MinPasswordLengthRejectsTooShortPasswordWithInvalidArgument)
1185 {
1186     initializeAccountPolicy();
1187     EXPECT_EQ(AccountPolicyIface::minPasswordLength(), 8);
1188     EXPECT_THROW(
1189         UserMgr::minPasswordLength(minPasswdLength - 1),
1190         sdbusplus::xyz::openbmc_project::Common::Error::InvalidArgument);
1191     EXPECT_EQ(AccountPolicyIface::minPasswordLength(), 8);
1192 }
1193 
1194 TEST_F(UserMgrInTest, MinPasswordLengthOnSuccess)
1195 {
1196     initializeAccountPolicy();
1197     EXPECT_EQ(AccountPolicyIface::minPasswordLength(), 8);
1198     UserMgr::minPasswordLength(16);
1199     EXPECT_EQ(AccountPolicyIface::minPasswordLength(), 16);
1200 }
1201 
1202 TEST_F(UserMgrInTest, MinPasswordLengthOnFailure)
1203 {
1204     EXPECT_NO_THROW(dumpStringToFile("whatever", tempPWQualityConfigFile));
1205     initializeAccountPolicy();
1206     EXPECT_EQ(AccountPolicyIface::minPasswordLength(), 8);
1207     EXPECT_THROW(
1208         UserMgr::minPasswordLength(16),
1209         sdbusplus::xyz::openbmc_project::Common::Error::InternalFailure);
1210     EXPECT_EQ(AccountPolicyIface::minPasswordLength(), 8);
1211 }
1212 
1213 TEST_F(UserMgrInTest, MinPasswordLengthGreaterThanMaxPasswordLength)
1214 {
1215     initializeAccountPolicy();
1216 
1217     EXPECT_EQ(AccountPolicyIface::minPasswordLength(), 8);
1218     EXPECT_THROW(
1219         UserMgr::minPasswordLength(maxPasswdLength + 1),
1220         sdbusplus::xyz::openbmc_project::Common::Error::InvalidArgument);
1221     EXPECT_EQ(AccountPolicyIface::minPasswordLength(), 8);
1222 }
1223 
1224 TEST_F(UserMgrInTest, RememberOldPasswordTimesReturnsIfValueIsTheSame)
1225 {
1226     initializeAccountPolicy();
1227     EXPECT_EQ(AccountPolicyIface::rememberOldPasswordTimes(), 0);
1228     UserMgr::rememberOldPasswordTimes(8);
1229     EXPECT_EQ(AccountPolicyIface::rememberOldPasswordTimes(), 8);
1230     UserMgr::rememberOldPasswordTimes(8);
1231     EXPECT_EQ(AccountPolicyIface::rememberOldPasswordTimes(), 8);
1232 }
1233 
1234 TEST_F(UserMgrInTest, RememberOldPasswordTimesOnSuccess)
1235 {
1236     initializeAccountPolicy();
1237     EXPECT_EQ(AccountPolicyIface::rememberOldPasswordTimes(), 0);
1238     UserMgr::rememberOldPasswordTimes(16);
1239     EXPECT_EQ(AccountPolicyIface::rememberOldPasswordTimes(), 16);
1240 }
1241 
1242 TEST_F(UserMgrInTest, RememberOldPasswordTimesOnFailure)
1243 {
1244     EXPECT_NO_THROW(dumpStringToFile("whatever", tempPWHistoryConfigFile));
1245     initializeAccountPolicy();
1246     EXPECT_EQ(AccountPolicyIface::rememberOldPasswordTimes(), 0);
1247     EXPECT_THROW(
1248         UserMgr::rememberOldPasswordTimes(16),
1249         sdbusplus::xyz::openbmc_project::Common::Error::InternalFailure);
1250     EXPECT_EQ(AccountPolicyIface::rememberOldPasswordTimes(), 0);
1251 }
1252 
1253 TEST_F(UserMgrInTest, MaxLoginAttemptBeforeLockoutReturnsIfValueIsTheSame)
1254 {
1255     initializeAccountPolicy();
1256     EXPECT_EQ(AccountPolicyIface::maxLoginAttemptBeforeLockout(), 2);
1257     UserMgr::maxLoginAttemptBeforeLockout(2);
1258     EXPECT_EQ(AccountPolicyIface::maxLoginAttemptBeforeLockout(), 2);
1259 }
1260 
1261 TEST_F(UserMgrInTest, MaxLoginAttemptBeforeLockoutOnSuccess)
1262 {
1263     initializeAccountPolicy();
1264     EXPECT_EQ(AccountPolicyIface::maxLoginAttemptBeforeLockout(), 2);
1265     UserMgr::maxLoginAttemptBeforeLockout(16);
1266     EXPECT_EQ(AccountPolicyIface::maxLoginAttemptBeforeLockout(), 16);
1267 }
1268 
1269 TEST_F(UserMgrInTest, MaxLoginAttemptBeforeLockoutOnFailure)
1270 {
1271     initializeAccountPolicy();
1272     EXPECT_NO_THROW(dumpStringToFile("whatever", tempFaillockConfigFile));
1273     EXPECT_THROW(
1274         UserMgr::maxLoginAttemptBeforeLockout(16),
1275         sdbusplus::xyz::openbmc_project::Common::Error::InternalFailure);
1276     EXPECT_EQ(AccountPolicyIface::maxLoginAttemptBeforeLockout(), 2);
1277 }
1278 
1279 TEST_F(UserMgrInTest, AccountUnlockTimeoutReturnsIfValueIsTheSame)
1280 {
1281     initializeAccountPolicy();
1282     EXPECT_EQ(AccountPolicyIface::accountUnlockTimeout(), 3);
1283     UserMgr::accountUnlockTimeout(3);
1284     EXPECT_EQ(AccountPolicyIface::accountUnlockTimeout(), 3);
1285 }
1286 
1287 TEST_F(UserMgrInTest, AccountUnlockTimeoutOnSuccess)
1288 {
1289     initializeAccountPolicy();
1290     EXPECT_EQ(AccountPolicyIface::accountUnlockTimeout(), 3);
1291     UserMgr::accountUnlockTimeout(16);
1292     EXPECT_EQ(AccountPolicyIface::accountUnlockTimeout(), 16);
1293 }
1294 
1295 TEST_F(UserMgrInTest, AccountUnlockTimeoutOnFailure)
1296 {
1297     initializeAccountPolicy();
1298     EXPECT_NO_THROW(dumpStringToFile("whatever", tempFaillockConfigFile));
1299     EXPECT_THROW(
1300         UserMgr::accountUnlockTimeout(16),
1301         sdbusplus::xyz::openbmc_project::Common::Error::InternalFailure);
1302     EXPECT_EQ(AccountPolicyIface::accountUnlockTimeout(), 3);
1303 }
1304 
1305 TEST_F(UserMgrInTest, UserEnableOnSuccess)
1306 {
1307     std::string username = "user001";
1308     EXPECT_NO_THROW(
1309         UserMgr::createUser(username, {"redfish", "ssh"}, "priv-user", true));
1310     UserInfoMap userInfo = getUserInfo(username);
1311     EXPECT_EQ(std::get<UserEnabled>(userInfo["UserEnabled"]), true);
1312 
1313     EXPECT_NO_THROW(userEnable(username, false));
1314 
1315     userInfo = getUserInfo(username);
1316     EXPECT_EQ(std::get<UserEnabled>(userInfo["UserEnabled"]), false);
1317 
1318     EXPECT_NO_THROW(UserMgr::deleteUser(username));
1319 }
1320 
1321 TEST_F(UserMgrInTest, CreateDeleteUserSuccessForHostConsole)
1322 {
1323     std::string username = "user001";
1324     EXPECT_NO_THROW(
1325         UserMgr::createUser(username, {"hostconsole"}, "priv-user", true));
1326     EXPECT_NO_THROW(UserMgr::deleteUser(username));
1327     EXPECT_NO_THROW(
1328         UserMgr::createUser(username, {"hostconsole"}, "priv-admin", true));
1329     EXPECT_NO_THROW(UserMgr::deleteUser(username));
1330     EXPECT_NO_THROW(
1331         UserMgr::createUser(username, {"hostconsole"}, "priv-operator", true));
1332     EXPECT_NO_THROW(UserMgr::deleteUser(username));
1333 }
1334 
1335 TEST_F(UserMgrInTest, UserEnableThrowsInternalFailureIfExecuteUserModifyFail)
1336 {
1337     std::string username = "user001";
1338     EXPECT_NO_THROW(
1339         UserMgr::createUser(username, {"redfish", "ssh"}, "priv-user", true));
1340     UserInfoMap userInfo = getUserInfo(username);
1341     EXPECT_EQ(std::get<UserEnabled>(userInfo["UserEnabled"]), true);
1342 
1343     EXPECT_CALL(*this, executeUserModifyUserEnable(testing::StrEq(username),
1344                                                    testing::Eq(false)))
1345         .WillOnce(testing::Throw(
1346             sdbusplus::xyz::openbmc_project::Common::Error::InternalFailure()));
1347     EXPECT_THROW(
1348         userEnable(username, false),
1349         sdbusplus::xyz::openbmc_project::Common::Error::InternalFailure);
1350 
1351     userInfo = getUserInfo(username);
1352     // Stay unchanged
1353     EXPECT_EQ(std::get<UserEnabled>(userInfo["UserEnabled"]), true);
1354 
1355     EXPECT_NO_THROW(UserMgr::deleteUser(username));
1356 }
1357 
1358 TEST_F(
1359     UserMgrInTest,
1360     UserLockedForFailedAttemptReturnsFalseIfMaxLoginAttemptBeforeLockoutIsZero)
1361 {
1362     EXPECT_FALSE(userLockedForFailedAttempt("whatever"));
1363 }
1364 
1365 TEST_F(UserMgrInTest, UserLockedForFailedAttemptZeroFailuresReturnsFalse)
1366 {
1367     std::string username = "user001";
1368     initializeAccountPolicy();
1369     // Example output from BMC
1370     // root:~# faillock --user root
1371     // root:
1372     // When   Type   Source   Valid
1373     std::vector<std::string> output = {"whatever",
1374                                        "When   Type   Source   Valid"};
1375     EXPECT_CALL(*this, getFailedAttempt(testing::StrEq(username.c_str())))
1376         .WillOnce(testing::Return(output));
1377 
1378     EXPECT_FALSE(userLockedForFailedAttempt(username));
1379 }
1380 
1381 TEST_F(UserMgrInTest, UserLockedForFailedAttemptFailIfGetFailedAttemptFail)
1382 {
1383     std::string username = "user001";
1384     initializeAccountPolicy();
1385     EXPECT_CALL(*this, getFailedAttempt(testing::StrEq(username.c_str())))
1386         .WillOnce(testing::Throw(
1387             sdbusplus::xyz::openbmc_project::Common::Error::InternalFailure()));
1388 
1389     EXPECT_THROW(
1390         userLockedForFailedAttempt(username),
1391         sdbusplus::xyz::openbmc_project::Common::Error::InternalFailure);
1392 }
1393 
1394 TEST_F(UserMgrInTest,
1395        UserLockedForFailedAttemptThrowsInternalFailureIfWrongDateFormat)
1396 {
1397     std::string username = "user001";
1398     initializeAccountPolicy();
1399 
1400     // Choose a date in the past.
1401     std::vector<std::string> output = {"whatever",
1402                                        "10/24/2002 00:00:00 type source V"};
1403     EXPECT_CALL(*this, getFailedAttempt(testing::StrEq(username.c_str())))
1404         .WillOnce(testing::Return(output));
1405 
1406     EXPECT_THROW(
1407         userLockedForFailedAttempt(username),
1408         sdbusplus::xyz::openbmc_project::Common::Error::InternalFailure);
1409 }
1410 
1411 TEST_F(UserMgrInTest,
1412        UserLockedForFailedAttemptReturnsFalseIfLastFailTimeHasTimedOut)
1413 {
1414     std::string username = "user001";
1415     initializeAccountPolicy();
1416 
1417     // Choose a date in the past.
1418     std::vector<std::string> output = {"whatever",
1419                                        "2002-10-24 00:00:00 type source V"};
1420     EXPECT_CALL(*this, getFailedAttempt(testing::StrEq(username.c_str())))
1421         .WillOnce(testing::Return(output));
1422 
1423     EXPECT_EQ(userLockedForFailedAttempt(username), false);
1424 }
1425 
1426 TEST_F(UserMgrInTest, CheckAndThrowForDisallowedGroupCreationOnSuccess)
1427 {
1428     // Base Redfish Roles
1429     EXPECT_NO_THROW(
1430         checkAndThrowForDisallowedGroupCreation("openbmc_rfr_Administrator"));
1431     EXPECT_NO_THROW(
1432         checkAndThrowForDisallowedGroupCreation("openbmc_rfr_Operator"));
1433     EXPECT_NO_THROW(
1434         checkAndThrowForDisallowedGroupCreation("openbmc_rfr_ReadOnly"));
1435     // Base Redfish Privileges
1436     EXPECT_NO_THROW(
1437         checkAndThrowForDisallowedGroupCreation("openbmc_rfp_Login"));
1438     EXPECT_NO_THROW(checkAndThrowForDisallowedGroupCreation(
1439         "openbmc_rfp_ConfigureManager"));
1440     EXPECT_NO_THROW(
1441         checkAndThrowForDisallowedGroupCreation("openbmc_rfp_ConfigureUsers"));
1442     EXPECT_NO_THROW(
1443         checkAndThrowForDisallowedGroupCreation("openbmc_rfp_ConfigureSelf"));
1444     EXPECT_NO_THROW(checkAndThrowForDisallowedGroupCreation(
1445         "openbmc_rfp_ConfigureComponents"));
1446     // OEM Redfish Roles
1447     EXPECT_NO_THROW(
1448         checkAndThrowForDisallowedGroupCreation("openbmc_orfr_PowerService"));
1449     // OEM Redfish Privileges
1450     EXPECT_NO_THROW(
1451         checkAndThrowForDisallowedGroupCreation("openbmc_orfp_PowerService"));
1452 }
1453 
1454 TEST_F(UserMgrInTest,
1455        CheckAndThrowForDisallowedGroupCreationThrowsIfGroupNameTooLong)
1456 {
1457     std::string groupName(maxSystemGroupNameLength + 1, 'A');
1458     EXPECT_THROW(
1459         checkAndThrowForDisallowedGroupCreation(groupName),
1460         sdbusplus::xyz::openbmc_project::Common::Error::InvalidArgument);
1461 }
1462 
1463 TEST_F(
1464     UserMgrInTest,
1465     CheckAndThrowForDisallowedGroupCreationThrowsIfGroupNameHasDisallowedCharacters)
1466 {
1467     EXPECT_THROW(
1468         checkAndThrowForDisallowedGroupCreation("openbmc_rfp_?owerService"),
1469         sdbusplus::xyz::openbmc_project::Common::Error::InvalidArgument);
1470     EXPECT_THROW(
1471         checkAndThrowForDisallowedGroupCreation("openbmc_rfp_-owerService"),
1472         sdbusplus::xyz::openbmc_project::Common::Error::InvalidArgument);
1473 }
1474 
1475 TEST_F(
1476     UserMgrInTest,
1477     CheckAndThrowForDisallowedGroupCreationThrowsIfGroupNameHasDisallowedPrefix)
1478 {
1479     EXPECT_THROW(
1480         checkAndThrowForDisallowedGroupCreation("google_rfp_"),
1481         sdbusplus::xyz::openbmc_project::Common::Error::InvalidArgument);
1482     EXPECT_THROW(
1483         checkAndThrowForDisallowedGroupCreation("com_rfp_"),
1484         sdbusplus::xyz::openbmc_project::Common::Error::InvalidArgument);
1485 }
1486 
1487 TEST_F(UserMgrInTest, CheckAndThrowForMaxGroupCountOnSuccess)
1488 {
1489     constexpr size_t predefGroupCount = 4;
1490 
1491     EXPECT_THAT(allGroups().size(), predefGroupCount);
1492     for (size_t i = 0; i < maxSystemGroupCount - predefGroupCount; ++i)
1493     {
1494         std::string groupName = "openbmc_rfr_role";
1495         groupName += std::to_string(i);
1496         EXPECT_NO_THROW(createGroup(groupName));
1497     }
1498     EXPECT_THROW(
1499         createGroup("openbmc_rfr_AnotherRole"),
1500         sdbusplus::xyz::openbmc_project::User::Common::Error::NoResource);
1501     for (size_t i = 0; i < maxSystemGroupCount - predefGroupCount; ++i)
1502     {
1503         std::string groupName = "openbmc_rfr_role";
1504         groupName += std::to_string(i);
1505         EXPECT_NO_THROW(deleteGroup(groupName));
1506     }
1507 }
1508 
1509 TEST_F(UserMgrInTest, CheckAndThrowForGroupExist)
1510 {
1511     std::string groupName = "openbmc_rfr_role";
1512     EXPECT_NO_THROW(createGroup(groupName));
1513     EXPECT_THROW(
1514         createGroup(groupName),
1515         sdbusplus::xyz::openbmc_project::User::Common::Error::GroupNameExists);
1516     EXPECT_NO_THROW(deleteGroup(groupName));
1517 }
1518 
1519 TEST_F(UserMgrInTest, ByDefaultAllGroupsArePredefinedGroups)
1520 {
1521     EXPECT_THAT(allGroups(), testing::UnorderedElementsAre(
1522                                  "redfish", "ipmi", "ssh", "hostconsole"));
1523 }
1524 
1525 TEST_F(UserMgrInTest, AddGroupThrowsIfPreDefinedGroupAdd)
1526 {
1527     EXPECT_THROW(
1528         createGroup("ipmi"),
1529         sdbusplus::xyz::openbmc_project::User::Common::Error::GroupNameExists);
1530     EXPECT_THROW(
1531         createGroup("redfish"),
1532         sdbusplus::xyz::openbmc_project::User::Common::Error::GroupNameExists);
1533     EXPECT_THROW(
1534         createGroup("ssh"),
1535         sdbusplus::xyz::openbmc_project::User::Common::Error::GroupNameExists);
1536     EXPECT_THROW(
1537         createGroup("hostconsole"),
1538         sdbusplus::xyz::openbmc_project::User::Common::Error::GroupNameExists);
1539 }
1540 
1541 TEST_F(UserMgrInTest, DeleteGroupThrowsIfGroupIsNotAllowedToChange)
1542 {
1543     EXPECT_THROW(
1544         deleteGroup("ipmi"),
1545         sdbusplus::xyz::openbmc_project::Common::Error::InvalidArgument);
1546     EXPECT_THROW(
1547         deleteGroup("redfish"),
1548         sdbusplus::xyz::openbmc_project::Common::Error::InvalidArgument);
1549     EXPECT_THROW(
1550         deleteGroup("ssh"),
1551         sdbusplus::xyz::openbmc_project::Common::Error::InvalidArgument);
1552     EXPECT_THROW(
1553         deleteGroup("hostconsole"),
1554         sdbusplus::xyz::openbmc_project::Common::Error::InvalidArgument);
1555 }
1556 
1557 TEST_F(UserMgrInTest,
1558        CreateGroupThrowsInternalFailureWhenExecuteGroupCreateFails)
1559 {
1560     EXPECT_CALL(*this, executeGroupCreation)
1561         .WillOnce(testing::Throw(
1562             sdbusplus::xyz::openbmc_project::Common::Error::InternalFailure()));
1563     EXPECT_THROW(
1564         createGroup("openbmc_rfr_role1"),
1565         sdbusplus::xyz::openbmc_project::Common::Error::InternalFailure);
1566 }
1567 
1568 TEST_F(UserMgrInTest,
1569        DeleteGroupThrowsInternalFailureWhenExecuteGroupDeleteFails)
1570 {
1571     std::string groupName = "openbmc_rfr_role1";
1572     EXPECT_NO_THROW(UserMgr::createGroup(groupName));
1573     EXPECT_CALL(*this, executeGroupDeletion(testing::StrEq(groupName)))
1574         .WillOnce(testing::Throw(
1575             sdbusplus::xyz::openbmc_project::Common::Error::InternalFailure()))
1576         .WillOnce(testing::DoDefault());
1577 
1578     EXPECT_THROW(
1579         deleteGroup(groupName),
1580         sdbusplus::xyz::openbmc_project::Common::Error::InternalFailure);
1581     EXPECT_NO_THROW(UserMgr::deleteGroup(groupName));
1582 }
1583 
1584 TEST_F(UserMgrInTest, CheckAndThrowForGroupNotExist)
1585 {
1586     EXPECT_THROW(deleteGroup("whatever"),
1587                  sdbusplus::xyz::openbmc_project::User::Common::Error::
1588                      GroupNameDoesNotExist);
1589 }
1590 
1591 TEST(ReadAllGroupsOnSystemTest, OnlyReturnsPredefinedGroups)
1592 {
1593     EXPECT_THAT(
1594         UserMgr::readAllGroupsOnSystem(),
1595         testing::UnorderedElementsAre("redfish", "ipmi", "ssh", "hostconsole"));
1596 }
1597 
1598 TEST_F(UserMgrInTest, CreateUser2)
1599 {
1600     const std::string userName = getNextUserName();
1601     const bool enabled = true;
1602 
1603     // last password change date is today
1604     // old maximum password age is 5000
1605     // set password expiration in 3 days
1606     PasswordExpirationInfo info;
1607     fillPasswordExpiration(0, 5000, 3, info);
1608 
1609     setUpCreateUser(userName, enabled);
1610     setUpSetPasswordExpiration(userName, info);
1611     setUpGetUserInfo(userName, enabled);
1612     setUpDeleteUser(userName);
1613 
1614     std::vector<std::string> groups = {"redfish", "ssh"};
1615 
1616     UserCreateMap props;
1617     props[UserProperty::GroupNames] = std::move(groups);
1618     props[UserProperty::Privilege] = "priv-user";
1619     props[UserProperty::Enabled] = enabled;
1620     props[UserProperty::PasswordExpiration] = info.passwordExpiration;
1621 
1622     EXPECT_NO_THROW(UserMgr::createUser2(userName, props));
1623 
1624     UserInfoMap userInfo = getUserInfo(userName);
1625     EXPECT_EQ(std::get<PasswordExpiration>(userInfo["PasswordExpiration"]),
1626               info.passwordExpiration);
1627 
1628     EXPECT_NO_THROW(UserMgr::deleteUser(userName));
1629 }
1630 
1631 TEST_F(UserMgrInTest, CreateUser2WithoutPasswordExpiration)
1632 {
1633     const std::string userName = getNextUserName();
1634     const bool enabled = true;
1635 
1636     setUpCreateUser(userName, enabled);
1637     setUpGetUserInfo(userName, enabled);
1638     setUpDeleteUser(userName);
1639 
1640     std::vector<std::string> groups = {"redfish", "ssh"};
1641 
1642     UserCreateMap props;
1643     props[UserProperty::GroupNames] = std::move(groups);
1644     props[UserProperty::Privilege] = "priv-user";
1645     props[UserProperty::Enabled] = enabled;
1646 
1647     EXPECT_NO_THROW(UserMgr::createUser2(userName, props));
1648 
1649     UserInfoMap userInfo = getUserInfo(userName);
1650     EXPECT_EQ(std::get<PasswordExpiration>(userInfo["PasswordExpiration"]),
1651               getDefaultPasswordExpiration());
1652 
1653     EXPECT_NO_THROW(UserMgr::deleteUser(userName));
1654 }
1655 
1656 TEST_F(UserMgrInTest, CreateUser2PasswordExpirationNotSet)
1657 {
1658     using namespace std::chrono;
1659 
1660     const std::string userName = getNextUserName();
1661     const bool enabled = true;
1662 
1663     setUpCreateUser(userName, enabled);
1664 
1665     EXPECT_CALL(*this, getShadowData(testing::StrEq(userName), _)).Times(0);
1666 
1667     EXPECT_CALL(*this,
1668                 executeUserPasswordExpiration(testing::StrEq(userName), _, _))
1669         .Times(0);
1670 
1671     setUpGetUserInfo(userName, enabled);
1672     setUpDeleteUser(userName);
1673 
1674     constexpr auto passwordExpiration = getDefaultPasswordExpiration();
1675 
1676     std::vector<std::string> groups = {"redfish", "ssh"};
1677 
1678     UserCreateMap props;
1679     props[UserProperty::GroupNames] = std::move(groups);
1680     props[UserProperty::Privilege] = "priv-user";
1681     props[UserProperty::Enabled] = enabled;
1682     props[UserProperty::PasswordExpiration] = passwordExpiration;
1683 
1684     EXPECT_NO_THROW(UserMgr::createUser2(userName, props));
1685 
1686     UserInfoMap userInfo = getUserInfo(userName);
1687     EXPECT_EQ(std::get<PasswordExpiration>(userInfo["PasswordExpiration"]),
1688               passwordExpiration);
1689 
1690     EXPECT_NO_THROW(UserMgr::deleteUser(userName));
1691 }
1692 
1693 TEST_F(UserMgrInTest, CreateUser2UnexpiringPassword)
1694 {
1695     using namespace std::chrono;
1696 
1697     const std::string userName = getNextUserName();
1698     const bool enabled = true;
1699 
1700     // last password change date is today
1701     const long lastChangeDate =
1702         duration_cast<days>(seconds{getEpochTimeNow()}).count();
1703 
1704     // password age is
1705     constexpr long passwordAge = 99999;
1706 
1707     // make password not to expire
1708     const uint64_t passwordExpiration = getUnexpiringPasswordTime();
1709 
1710     setUpCreateUser(userName, enabled);
1711 
1712     EXPECT_CALL(*this, getShadowData(testing::StrEq(userName), _))
1713         .WillOnce(Invoke([&lastChangeDate](auto, struct spwd& spwd) {
1714             spwd.sp_lstchg = lastChangeDate;
1715             spwd.sp_max = passwordAge;
1716         }));
1717 
1718     EXPECT_CALL(*this, executeUserPasswordExpiration(
1719                            testing::StrEq(userName), lastChangeDate,
1720                            getUnexpiringPasswordAge()))
1721         .Times(1);
1722 
1723     setUpGetUserInfo(userName, enabled);
1724     setUpDeleteUser(userName);
1725 
1726     std::vector<std::string> groups = {"redfish", "ssh"};
1727 
1728     UserCreateMap props;
1729     props[UserProperty::GroupNames] = std::move(groups);
1730     props[UserProperty::Privilege] = "priv-user";
1731     props[UserProperty::Enabled] = enabled;
1732     props[UserProperty::PasswordExpiration] = passwordExpiration;
1733 
1734     EXPECT_NO_THROW(UserMgr::createUser2(userName, props));
1735 
1736     UserInfoMap userInfo = getUserInfo(userName);
1737     EXPECT_EQ(std::get<PasswordExpiration>(userInfo["PasswordExpiration"]),
1738               passwordExpiration);
1739 
1740     EXPECT_NO_THROW(UserMgr::deleteUser(userName));
1741 }
1742 
1743 TEST_F(UserMgrInTest, CreateUser2Rename)
1744 {
1745     const std::string userName = getNextUserName();
1746     const std::string newUserName = getNextUserName();
1747     const bool enabled = true;
1748 
1749     // last password change date is 7 days ago
1750     // old maximum password age is 15
1751     // set password expiration in 5 days
1752     PasswordExpirationInfo info;
1753     fillPasswordExpiration(7, 15, 5, info);
1754 
1755     setUpCreateUser(userName, enabled);
1756     setUpSetPasswordExpiration(userName, info);
1757     setUpGetUserInfo(newUserName, enabled);
1758     setUpDeleteUser(newUserName);
1759 
1760     EXPECT_CALL(*this, isUserEnabled(userName))
1761         .WillOnce(testing::Return(enabled));
1762 
1763     EXPECT_CALL(*this, executeUserRename(testing::StrEq(userName),
1764                                          testing::StrEq(newUserName)))
1765         .Times(1);
1766 
1767     std::vector<std::string> groups = {"redfish", "ssh"};
1768 
1769     UserCreateMap props;
1770     props[UserProperty::GroupNames] = std::move(groups);
1771     props[UserProperty::Privilege] = "priv-user";
1772     props[UserProperty::Enabled] = enabled;
1773     props[UserProperty::PasswordExpiration] = info.passwordExpiration;
1774 
1775     EXPECT_NO_THROW(UserMgr::createUser2(userName, props));
1776 
1777     EXPECT_NO_THROW(UserMgr::renameUser(userName, newUserName));
1778 
1779     UserInfoMap userInfo = getUserInfo(newUserName);
1780     EXPECT_EQ(std::get<PasswordExpiration>(userInfo["PasswordExpiration"]),
1781               info.passwordExpiration);
1782 
1783     EXPECT_NO_THROW(UserMgr::deleteUser(newUserName));
1784 }
1785 
1786 TEST_F(UserMgrInTest, CreateUser2PasswordExpirationFail)
1787 {
1788     using namespace std::chrono;
1789 
1790     const std::string userName = getNextUserName();
1791     const bool enabled = true;
1792 
1793     setUpCreateUser(userName, enabled);
1794 
1795     EXPECT_CALL(*this, getShadowData(testing::StrEq(userName), _))
1796         .WillOnce([]() {
1797             throw sdbusplus::xyz::openbmc_project::Common::Error::
1798                 InternalFailure();
1799         });
1800 
1801     setUpDeleteUser(userName);
1802 
1803     std::vector<std::string> groups = {"redfish", "ssh"};
1804 
1805     UserCreateMap props;
1806     props[UserProperty::GroupNames] = std::move(groups);
1807     props[UserProperty::Privilege] = "priv-user";
1808     props[UserProperty::Enabled] = enabled;
1809     props[UserProperty::PasswordExpiration] = (uint64_t)1;
1810 
1811     EXPECT_THROW(
1812         UserMgr::createUser2(userName, props),
1813         sdbusplus::xyz::openbmc_project::Common::Error::InternalFailure);
1814 
1815     EXPECT_THROW(getUserInfo(userName),
1816                  sdbusplus::xyz::openbmc_project::User::Common::Error::
1817                      UserNameDoesNotExist);
1818 }
1819 
1820 } // namespace user
1821 } // namespace phosphor
1822