xref: /openbmc/phosphor-user-manager/test/user_mgr_test.cpp (revision 8c5c4aab519130adb56f66b1477ff5ed82731926)
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 
getEpochTimeNow()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 
getNextUserName()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 
fillPasswordExpiration(const long lastChangeDaysAgo,const long oldPasswordAge,const long nextPasswordChangeInDays,PasswordExpirationInfo & info)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 
TestUserMgr()96     TestUserMgr() :
97         bus(sdbusplus::get_mocked_new(&sdBusMock)), mockManager(bus, objpath)
98     {}
99 
createLocalUser(const std::string & userName,std::vector<std::string> groupNames,const std::string & priv,bool enabled)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 
createPrivilegeMapperDbusObject(void)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 
createLdapConfigObjectWithoutPrivilegeMapper(void)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 
getUser(const std::string & userName)163     auto& getUser(const std::string& userName)
164     {
165         return *mockManager.usersList[userName].get();
166     }
167 
testPasswordExpirationSet(const std::string & userName,const PasswordInfo & oldInfo,const PasswordInfo & newInfo)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 
testPasswordExpirationReset(const std::string & userName,const PasswordInfo & info)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 
testPasswordExpirationGet(const std::string & userName,const PasswordInfo & info,const uint64_t expectedPasswordExpiration)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 
TEST_F(TestUserMgr,ldapEntryDoesNotExist)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 
TEST_F(TestUserMgr,localUser)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 
TEST_F(TestUserMgr,ldapUserWithPrivMapper)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 
TEST_F(TestUserMgr,ldapUserWithoutPrivMapper)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 
TEST_F(TestUserMgr,PasswordExpiration)309 TEST_F(TestUserMgr, PasswordExpiration)
310 {
311     testPasswordExpirationSet(getNextUserName(), {2, 10}, {2, 3});
312 }
313 
TEST_F(TestUserMgr,PasswordExpirationLastChangeNegative)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 
TEST_F(TestUserMgr,PasswordExpirationLastChangeZero)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 
TEST_F(TestUserMgr,PasswordExpirationLastMaxAgeNegative)334 TEST_F(TestUserMgr, PasswordExpirationLastMaxAgeNegative)
335 {
336     testPasswordExpirationSet(getNextUserName(), {10, -5}, {10, 6});
337 }
338 
TEST_F(TestUserMgr,PasswordExpirationReset)339 TEST_F(TestUserMgr, PasswordExpirationReset)
340 {
341     testPasswordExpirationReset(getNextUserName(), {2, 10});
342 }
343 
TEST_F(TestUserMgr,PasswordExpirationResetLastChangeNegative)344 TEST_F(TestUserMgr, PasswordExpirationResetLastChangeNegative)
345 {
346     testPasswordExpirationReset(getNextUserName(), {-5, 8});
347 }
348 
TEST_F(TestUserMgr,PasswordExpirationResetLastChangeZero)349 TEST_F(TestUserMgr, PasswordExpirationResetLastChangeZero)
350 {
351     testPasswordExpirationReset(getNextUserName(), {0, 13});
352 }
353 
TEST_F(TestUserMgr,PasswordExpirationResetMaxAgeNegative)354 TEST_F(TestUserMgr, PasswordExpirationResetMaxAgeNegative)
355 {
356     testPasswordExpirationReset(getNextUserName(), {2, -2});
357 }
358 
TEST_F(TestUserMgr,PasswordExpirationGet)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 
TEST_F(TestUserMgr,PasswordExpirationSetDefault)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 
TEST_F(TestUserMgr,PasswordExpirationGetDefault)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 
TEST_F(TestUserMgr,PasswordExpirationGetLastChangeNegative)397 TEST_F(TestUserMgr, PasswordExpirationGetLastChangeNegative)
398 {
399     testPasswordExpirationGet(getNextUserName(), {-5, 8},
400                               UserMgr::getUnexpiringPasswordTime());
401 }
402 
TEST_F(TestUserMgr,PasswordExpirationGetLastChangeZero)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 
TEST_F(TestUserMgr,PasswordExpirationGetMaxAgeNegative)430 TEST_F(TestUserMgr, PasswordExpirationGetMaxAgeNegative)
431 {
432     testPasswordExpirationGet(getNextUserName(), {12, -2},
433                               UserMgr::getUnexpiringPasswordTime());
434 }
435 
TEST_F(TestUserMgr,PasswordExpirationShadowFail)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 
TEST_F(TestUserMgr,PasswordExpirationInvalidDate)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 
TEST_F(TestUserMgr,PasswordExpirationExecFail)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 
TEST(GetCSVFromVector,EmptyVectorReturnsEmptyString)511 TEST(GetCSVFromVector, EmptyVectorReturnsEmptyString)
512 {
513     EXPECT_EQ(getCSVFromVector({}), "");
514 }
515 
TEST(GetCSVFromVector,ElementsAreJoinedByComma)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 
TEST(RemoveStringFromCSV,WithoutDeleteStringReturnsFalse)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 
TEST(RemoveStringFromCSV,WithDeleteStringReturnsTrue)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 
dumpStringToFile(const std::string & str,const std::string & filePath)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 
removeFile(const std::string & filePath)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:
UserMgrInTest()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 
~UserMgrInTest()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     MOCK_METHOD(bool, isUserExistSystem, (const std::string& userName),
730                 (override));
731 
732   protected:
733     static constexpr auto tempFilePath = "/tmp/test-data-XXXXXX";
734 
735     static sdbusplus::bus_t busInTest;
736     std::string tempFaillockConfigFile;
737     std::string tempPWHistoryConfigFile;
738     std::string tempPWQualityConfigFile;
739 
setUpCreateUser(const std::string & userName,bool enabled)740     void setUpCreateUser(const std::string& userName, bool enabled)
741     {
742         EXPECT_CALL(*this, getIpmiUsersCount).WillOnce(testing::Return(0));
743 
744         EXPECT_CALL(*this,
745                     executeUserAdd(testing::StrEq(userName), _, _, enabled))
746             .Times(1);
747     }
748 
setUpGetUserInfo(const std::string & userName,bool enabled)749     void setUpGetUserInfo(const std::string& userName, bool enabled)
750     {
751         EXPECT_CALL(*this, isUserEnabled(userName))
752             .WillOnce(testing::Return(enabled));
753     }
754 
setUpSetPasswordExpiration(const std::string & userName,const PasswordExpirationInfo & info)755     void setUpSetPasswordExpiration(const std::string& userName,
756                                     const PasswordExpirationInfo& info)
757     {
758         EXPECT_CALL(*this, getShadowData(testing::StrEq(userName), _))
759             .WillOnce(Invoke([&info](auto, struct spwd& spwd) {
760                 spwd.sp_lstchg = info.lastChangeDate;
761                 spwd.sp_max = info.oldmaxAge;
762             }));
763 
764         EXPECT_CALL(*this, executeUserPasswordExpiration(
765                                testing::StrEq(userName), info.lastChangeDate,
766                                info.newMaxAge))
767             .Times(1);
768     }
769 
setUpDeleteUser(const std::string & userName)770     void setUpDeleteUser(const std::string& userName)
771     {
772         EXPECT_CALL(*this,
773                     executeUserClearFailRecords(testing::StrEq(userName)))
774             .Times(1);
775 
776         EXPECT_CALL(*this, executeUserDelete(testing::StrEq(userName)))
777             .Times(1);
778     }
779 };
780 
781 sdbusplus::bus_t UserMgrInTest::busInTest = sdbusplus::bus::new_default();
782 
TEST_F(UserMgrInTest,GetPamModuleConfValueOnSuccess)783 TEST_F(UserMgrInTest, GetPamModuleConfValueOnSuccess)
784 {
785     std::string minlen;
786     EXPECT_EQ(getPamModuleConfValue(tempPWQualityConfigFile, "minlen", minlen),
787               0);
788     EXPECT_EQ(minlen, "8");
789     std::string deny;
790     EXPECT_EQ(getPamModuleConfValue(tempFaillockConfigFile, "deny", deny), 0);
791     EXPECT_EQ(deny, "2");
792     std::string remember;
793     EXPECT_EQ(
794         getPamModuleConfValue(tempPWHistoryConfigFile, "remember", remember),
795         0);
796     EXPECT_EQ(remember, "0");
797 }
798 
TEST_F(UserMgrInTest,SetPamModuleConfValueOnSuccess)799 TEST_F(UserMgrInTest, SetPamModuleConfValueOnSuccess)
800 {
801     EXPECT_EQ(setPamModuleConfValue(tempPWQualityConfigFile, "minlen", "16"),
802               0);
803     std::string minlen;
804     EXPECT_EQ(getPamModuleConfValue(tempPWQualityConfigFile, "minlen", minlen),
805               0);
806     EXPECT_EQ(minlen, "16");
807 
808     EXPECT_EQ(setPamModuleConfValue(tempFaillockConfigFile, "deny", "3"), 0);
809     std::string deny;
810     EXPECT_EQ(getPamModuleConfValue(tempFaillockConfigFile, "deny", deny), 0);
811     EXPECT_EQ(deny, "3");
812 
813     EXPECT_EQ(setPamModuleConfValue(tempPWHistoryConfigFile, "remember", "1"),
814               0);
815     std::string remember;
816     EXPECT_EQ(
817         getPamModuleConfValue(tempPWHistoryConfigFile, "remember", remember),
818         0);
819     EXPECT_EQ(remember, "1");
820 }
821 
TEST_F(UserMgrInTest,SetPamModuleConfValueTempFileOnSuccess)822 TEST_F(UserMgrInTest, SetPamModuleConfValueTempFileOnSuccess)
823 {
824     EXPECT_EQ(setPamModuleConfValue(tempPWQualityConfigFile, "minlen", "16"),
825               0);
826 
827     std::string tmpFile = tempPWQualityConfigFile + "_tmp";
828     EXPECT_FALSE(std::filesystem::exists(tmpFile));
829 
830     EXPECT_EQ(setPamModuleConfValue(tempFaillockConfigFile, "deny", "3"), 0);
831 
832     tmpFile = tempFaillockConfigFile + "_tmp";
833     EXPECT_FALSE(std::filesystem::exists(tmpFile));
834 
835     EXPECT_EQ(setPamModuleConfValue(tempPWHistoryConfigFile, "remember", "1"),
836               0);
837 
838     tmpFile = tempPWHistoryConfigFile + "_tmp";
839     EXPECT_FALSE(std::filesystem::exists(tmpFile));
840 }
841 
TEST_F(UserMgrInTest,GetPamModuleConfValueOnFailure)842 TEST_F(UserMgrInTest, GetPamModuleConfValueOnFailure)
843 {
844     EXPECT_NO_THROW(dumpStringToFile("whatever", tempPWQualityConfigFile));
845     std::string minlen;
846     EXPECT_EQ(getPamModuleConfValue(tempPWQualityConfigFile, "minlen", minlen),
847               -1);
848 
849     EXPECT_NO_THROW(removeFile(tempPWQualityConfigFile));
850     EXPECT_EQ(getPamModuleConfValue(tempPWQualityConfigFile, "minlen", minlen),
851               -1);
852 
853     EXPECT_NO_THROW(dumpStringToFile("whatever", tempFaillockConfigFile));
854     std::string deny;
855     EXPECT_EQ(getPamModuleConfValue(tempFaillockConfigFile, "deny", deny), -1);
856 
857     EXPECT_NO_THROW(removeFile(tempFaillockConfigFile));
858     EXPECT_EQ(getPamModuleConfValue(tempFaillockConfigFile, "deny", deny), -1);
859 
860     EXPECT_NO_THROW(dumpStringToFile("whatever", tempPWHistoryConfigFile));
861     std::string remember;
862     EXPECT_EQ(
863         getPamModuleConfValue(tempPWHistoryConfigFile, "remember", remember),
864         -1);
865 
866     EXPECT_NO_THROW(removeFile(tempPWHistoryConfigFile));
867     EXPECT_EQ(
868         getPamModuleConfValue(tempPWHistoryConfigFile, "remember", remember),
869         -1);
870 }
871 
TEST_F(UserMgrInTest,SetPamModuleConfValueOnFailure)872 TEST_F(UserMgrInTest, SetPamModuleConfValueOnFailure)
873 {
874     EXPECT_NO_THROW(dumpStringToFile("whatever", tempPWQualityConfigFile));
875     EXPECT_EQ(setPamModuleConfValue(tempPWQualityConfigFile, "minlen", "16"),
876               -1);
877 
878     EXPECT_NO_THROW(removeFile(tempPWQualityConfigFile));
879     EXPECT_EQ(setPamModuleConfValue(tempPWQualityConfigFile, "minlen", "16"),
880               -1);
881 
882     EXPECT_NO_THROW(dumpStringToFile("whatever", tempFaillockConfigFile));
883     EXPECT_EQ(setPamModuleConfValue(tempFaillockConfigFile, "deny", "3"), -1);
884 
885     EXPECT_NO_THROW(removeFile(tempFaillockConfigFile));
886     EXPECT_EQ(setPamModuleConfValue(tempFaillockConfigFile, "deny", "3"), -1);
887 
888     EXPECT_NO_THROW(dumpStringToFile("whatever", tempPWHistoryConfigFile));
889     EXPECT_EQ(setPamModuleConfValue(tempPWHistoryConfigFile, "remember", "1"),
890               -1);
891 
892     EXPECT_NO_THROW(removeFile(tempPWHistoryConfigFile));
893     EXPECT_EQ(setPamModuleConfValue(tempPWHistoryConfigFile, "remember", "1"),
894               -1);
895 }
896 
TEST_F(UserMgrInTest,SetPamModuleConfValueTempFileOnFailure)897 TEST_F(UserMgrInTest, SetPamModuleConfValueTempFileOnFailure)
898 {
899     EXPECT_NO_THROW(dumpStringToFile("whatever", tempPWQualityConfigFile));
900     EXPECT_EQ(setPamModuleConfValue(tempPWQualityConfigFile, "minlen", "16"),
901               -1);
902 
903     std::string tmpFile = tempPWQualityConfigFile + "_tmp";
904     EXPECT_FALSE(std::filesystem::exists(tmpFile));
905 
906     EXPECT_NO_THROW(removeFile(tempPWQualityConfigFile));
907     EXPECT_EQ(setPamModuleConfValue(tempPWQualityConfigFile, "minlen", "16"),
908               -1);
909 
910     EXPECT_FALSE(std::filesystem::exists(tmpFile));
911 
912     EXPECT_NO_THROW(dumpStringToFile("whatever", tempFaillockConfigFile));
913     EXPECT_EQ(setPamModuleConfValue(tempFaillockConfigFile, "deny", "3"), -1);
914 
915     tmpFile = tempFaillockConfigFile + "_tmp";
916     EXPECT_FALSE(std::filesystem::exists(tmpFile));
917 
918     EXPECT_NO_THROW(removeFile(tempFaillockConfigFile));
919     EXPECT_EQ(setPamModuleConfValue(tempFaillockConfigFile, "deny", "3"), -1);
920 
921     EXPECT_FALSE(std::filesystem::exists(tmpFile));
922 
923     EXPECT_NO_THROW(dumpStringToFile("whatever", tempPWHistoryConfigFile));
924     EXPECT_EQ(setPamModuleConfValue(tempPWHistoryConfigFile, "remember", "1"),
925               -1);
926 
927     tmpFile = tempPWHistoryConfigFile + "_tmp";
928     EXPECT_FALSE(std::filesystem::exists(tmpFile));
929 
930     EXPECT_NO_THROW(removeFile(tempPWHistoryConfigFile));
931     EXPECT_EQ(setPamModuleConfValue(tempPWHistoryConfigFile, "remember", "1"),
932               -1);
933 
934     EXPECT_FALSE(std::filesystem::exists(tmpFile));
935 }
936 
TEST_F(UserMgrInTest,IsUserExistEmptyInputThrowsError)937 TEST_F(UserMgrInTest, IsUserExistEmptyInputThrowsError)
938 {
939     EXPECT_THROW(
940         isUserExist(""),
941         sdbusplus::xyz::openbmc_project::Common::Error::InvalidArgument);
942 }
943 
TEST_F(UserMgrInTest,ThrowForUserDoesNotExistThrowsError)944 TEST_F(UserMgrInTest, ThrowForUserDoesNotExistThrowsError)
945 {
946     EXPECT_THROW(throwForUserDoesNotExist("whatever"),
947                  sdbusplus::xyz::openbmc_project::User::Common::Error::
948                      UserNameDoesNotExist);
949 }
950 
TEST_F(UserMgrInTest,ThrowForUserExistsThrowsError)951 TEST_F(UserMgrInTest, ThrowForUserExistsThrowsError)
952 {
953     EXPECT_THROW(
954         throwForUserExists("root"),
955         sdbusplus::xyz::openbmc_project::User::Common::Error::UserNameExists);
956 }
957 
TEST_F(UserMgrInTest,ThrowForUserNameConstraintsExceedIpmiMaxUserNameLenThrowsUserNameGroupFail)958 TEST_F(
959     UserMgrInTest,
960     ThrowForUserNameConstraintsExceedIpmiMaxUserNameLenThrowsUserNameGroupFail)
961 {
962     std::string strWith17Chars(17, 'A');
963     EXPECT_THROW(throwForUserNameConstraints(strWith17Chars, {"ipmi"}),
964                  sdbusplus::xyz::openbmc_project::User::Common::Error::
965                      UserNameGroupFail);
966 }
967 
TEST_F(UserMgrInTest,ThrowForUserNameConstraintsExceedSystemMaxUserNameLenThrowsInvalidArgument)968 TEST_F(
969     UserMgrInTest,
970     ThrowForUserNameConstraintsExceedSystemMaxUserNameLenThrowsInvalidArgument)
971 {
972     std::string strWith31Chars(101, 'A');
973     EXPECT_THROW(
974         throwForUserNameConstraints(strWith31Chars, {}),
975         sdbusplus::xyz::openbmc_project::Common::Error::InvalidArgument);
976 }
977 
TEST_F(UserMgrInTest,ThrowForUserNameConstraintsRegexMismatchThrowsInvalidArgument)978 TEST_F(UserMgrInTest,
979        ThrowForUserNameConstraintsRegexMismatchThrowsInvalidArgument)
980 {
981     std::string startWithNumber = "0ABC";
982     std::string startWithDisallowedCharacter = "[test";
983     EXPECT_THROW(
984         throwForUserNameConstraints(startWithNumber, {"ipmi"}),
985         sdbusplus::xyz::openbmc_project::Common::Error::InvalidArgument);
986     EXPECT_THROW(
987         throwForUserNameConstraints(startWithDisallowedCharacter, {"ipmi"}),
988         sdbusplus::xyz::openbmc_project::Common::Error::InvalidArgument);
989 }
990 
TEST_F(UserMgrInTest,UserAddNotRootFailedWithInternalFailure)991 TEST_F(UserMgrInTest, UserAddNotRootFailedWithInternalFailure)
992 {
993     EXPECT_THROW(
994         UserMgr::executeUserAdd("user0", "ipmi,ssh", true, true),
995         sdbusplus::xyz::openbmc_project::Common::Error::InternalFailure);
996 }
997 
TEST_F(UserMgrInTest,UserDeleteNotRootFailedWithInternalFailure)998 TEST_F(UserMgrInTest, UserDeleteNotRootFailedWithInternalFailure)
999 {
1000     EXPECT_THROW(
1001         UserMgr::executeUserDelete("user0"),
1002         sdbusplus::xyz::openbmc_project::Common::Error::InternalFailure);
1003 }
1004 
TEST_F(UserMgrInTest,ThrowForMaxGrpUserCountThrowsNoResourceWhenIpmiUserExceedLimit)1005 TEST_F(UserMgrInTest,
1006        ThrowForMaxGrpUserCountThrowsNoResourceWhenIpmiUserExceedLimit)
1007 {
1008     EXPECT_CALL(*this, getIpmiUsersCount()).WillOnce(Return(ipmiMaxUsers));
1009     EXPECT_THROW(
1010         throwForMaxGrpUserCount({"ipmi"}),
1011         sdbusplus::xyz::openbmc_project::User::Common::Error::NoResource);
1012 }
1013 
TEST_F(UserMgrInTest,CreateUserThrowsInternalFailureWhenExecuteUserAddFails)1014 TEST_F(UserMgrInTest, CreateUserThrowsInternalFailureWhenExecuteUserAddFails)
1015 {
1016     std::string username = "whatever";
1017     EXPECT_CALL(*this, executeUserAdd)
1018         .WillOnce(testing::Throw(
1019             sdbusplus::xyz::openbmc_project::Common::Error::InternalFailure()));
1020     EXPECT_CALL(*this, isUserExistSystem(testing::StrEq(username)))
1021         .WillOnce(Return(false));
1022     EXPECT_THROW(
1023         createUser(username, {"redfish"}, "", true),
1024         sdbusplus::xyz::openbmc_project::Common::Error::InternalFailure);
1025     EXPECT_FALSE(isUserExist(username));
1026 }
1027 
TEST_F(UserMgrInTest,CreateUserThrowsInternalFailureWhenExecuteUserAddPartiallyFails)1028 TEST_F(UserMgrInTest,
1029        CreateUserThrowsInternalFailureWhenExecuteUserAddPartiallyFails)
1030 {
1031     std::string username = "whatever";
1032     EXPECT_CALL(*this, executeUserAdd)
1033         .WillOnce(testing::Throw(
1034             sdbusplus::xyz::openbmc_project::Common::Error::InternalFailure()));
1035     EXPECT_CALL(*this, isUserExistSystem(testing::StrEq(username)))
1036         .WillOnce(Return(true));
1037     EXPECT_CALL(*this, executeUserDelete(testing::StrEq(username)))
1038         .WillOnce(testing::DoDefault());
1039     EXPECT_THROW(
1040         createUser(username, {"redfish"}, "", true),
1041         sdbusplus::xyz::openbmc_project::Common::Error::InternalFailure);
1042     EXPECT_FALSE(isUserExist(username));
1043 }
1044 
TEST_F(UserMgrInTest,DeleteUserThrowsInternalFailureWhenExecuteUserDeleteFails)1045 TEST_F(UserMgrInTest, DeleteUserThrowsInternalFailureWhenExecuteUserDeleteFails)
1046 {
1047     std::string username = "user";
1048     EXPECT_NO_THROW(
1049         UserMgr::createUser(username, {"redfish", "ssh"}, "priv-user", true));
1050     EXPECT_CALL(*this, executeUserDelete(testing::StrEq(username)))
1051         .WillOnce(testing::Throw(
1052             sdbusplus::xyz::openbmc_project::Common::Error::InternalFailure()))
1053         .WillOnce(testing::DoDefault());
1054     EXPECT_CALL(*this, isUserExistSystem(testing::StrEq(username)))
1055         .WillOnce(Return(true)); // delete legitimately failed
1056 
1057     EXPECT_THROW(
1058         deleteUser(username),
1059         sdbusplus::xyz::openbmc_project::Common::Error::InternalFailure);
1060     EXPECT_TRUE(isUserExist(username));
1061     EXPECT_NO_THROW(UserMgr::deleteUser(username));
1062 }
1063 
TEST_F(UserMgrInTest,DeleteUserSuccessWhenExecuteUserSucceedsWithError)1064 TEST_F(UserMgrInTest, DeleteUserSuccessWhenExecuteUserSucceedsWithError)
1065 {
1066     std::string username = "user";
1067     EXPECT_NO_THROW(
1068         UserMgr::createUser(username, {"redfish", "ssh"}, "priv-user", true));
1069     EXPECT_CALL(*this, executeUserDelete(testing::StrEq(username)))
1070         .WillOnce(testing::Throw(
1071             sdbusplus::xyz::openbmc_project::Common::Error::InternalFailure()));
1072     EXPECT_CALL(*this, isUserExistSystem(testing::StrEq(username)))
1073         .WillOnce(Return(false)); // delete partly failed
1074 
1075     EXPECT_NO_THROW(deleteUser(username));
1076     EXPECT_FALSE(isUserExist(username));
1077 }
1078 
TEST_F(UserMgrInTest,DeleteUserThrowsInternalFailureWhenExecuteUserClearFailRecords)1079 TEST_F(UserMgrInTest,
1080        DeleteUserThrowsInternalFailureWhenExecuteUserClearFailRecords)
1081 {
1082     const char* username = "user";
1083     EXPECT_NO_THROW(
1084         UserMgr::createUser(username, {"redfish", "ssh"}, "priv-user", true));
1085     EXPECT_CALL(*this, executeUserClearFailRecords(testing::StrEq(username)))
1086         .WillOnce(testing::Throw(
1087             sdbusplus::xyz::openbmc_project::Common::Error::InternalFailure()))
1088         .WillOnce(testing::DoDefault());
1089     EXPECT_CALL(*this, isUserExistSystem(testing::StrEq(username)))
1090         .WillOnce(Return(true));
1091 
1092     EXPECT_THROW(
1093         deleteUser(username),
1094         sdbusplus::xyz::openbmc_project::Common::Error::InternalFailure);
1095     EXPECT_TRUE(isUserExist(username));
1096     EXPECT_NO_THROW(UserMgr::deleteUser(username));
1097 }
1098 
TEST_F(UserMgrInTest,ThrowForInvalidPrivilegeThrowsWhenPrivilegeIsInvalid)1099 TEST_F(UserMgrInTest, ThrowForInvalidPrivilegeThrowsWhenPrivilegeIsInvalid)
1100 {
1101     EXPECT_THROW(
1102         throwForInvalidPrivilege("whatever"),
1103         sdbusplus::xyz::openbmc_project::Common::Error::InvalidArgument);
1104 }
1105 
TEST_F(UserMgrInTest,ThrowForInvalidPrivilegeNoThrowWhenPrivilegeIsValid)1106 TEST_F(UserMgrInTest, ThrowForInvalidPrivilegeNoThrowWhenPrivilegeIsValid)
1107 {
1108     EXPECT_NO_THROW(throwForInvalidPrivilege("priv-admin"));
1109     EXPECT_NO_THROW(throwForInvalidPrivilege("priv-operator"));
1110     EXPECT_NO_THROW(throwForInvalidPrivilege("priv-user"));
1111 }
1112 
TEST_F(UserMgrInTest,ThrowForInvalidGroupsThrowsWhenGroupIsInvalid)1113 TEST_F(UserMgrInTest, ThrowForInvalidGroupsThrowsWhenGroupIsInvalid)
1114 {
1115     EXPECT_THROW(
1116         throwForInvalidGroups({"whatever"}),
1117         sdbusplus::xyz::openbmc_project::Common::Error::InvalidArgument);
1118     EXPECT_THROW(
1119         throwForInvalidGroups({"web"}),
1120         sdbusplus::xyz::openbmc_project::Common::Error::InvalidArgument);
1121 }
1122 
TEST_F(UserMgrInTest,ThrowForInvalidGroupsNoThrowWhenGroupIsValid)1123 TEST_F(UserMgrInTest, ThrowForInvalidGroupsNoThrowWhenGroupIsValid)
1124 {
1125     EXPECT_NO_THROW(throwForInvalidGroups({"ipmi"}));
1126     EXPECT_NO_THROW(throwForInvalidGroups({"ssh"}));
1127     EXPECT_NO_THROW(throwForInvalidGroups({"redfish"}));
1128     EXPECT_NO_THROW(throwForInvalidGroups({"hostconsole"}));
1129 }
1130 
TEST_F(UserMgrInTest,RenameUserOnSuccess)1131 TEST_F(UserMgrInTest, RenameUserOnSuccess)
1132 {
1133     std::string username = "user001";
1134     EXPECT_NO_THROW(
1135         UserMgr::createUser(username, {"redfish", "ssh"}, "priv-user", true));
1136     std::string newUsername = "user002";
1137 
1138     EXPECT_NO_THROW(UserMgr::renameUser(username, newUsername));
1139 
1140     // old username doesn't exist
1141     EXPECT_THROW(getUserInfo(username),
1142                  sdbusplus::xyz::openbmc_project::User::Common::Error::
1143                      UserNameDoesNotExist);
1144 
1145     UserInfoMap userInfo = getUserInfo(newUsername);
1146     EXPECT_EQ(std::get<Privilege>(userInfo["UserPrivilege"]), "priv-user");
1147     EXPECT_THAT(std::get<GroupList>(userInfo["UserGroups"]),
1148                 testing::UnorderedElementsAre("redfish", "ssh"));
1149     EXPECT_EQ(std::get<UserEnabled>(userInfo["UserEnabled"]), true);
1150 
1151     EXPECT_NO_THROW(UserMgr::deleteUser(newUsername));
1152 }
1153 
TEST_F(UserMgrInTest,RenameUserThrowsInternalFailureIfExecuteUserModifyFails)1154 TEST_F(UserMgrInTest, RenameUserThrowsInternalFailureIfExecuteUserModifyFails)
1155 {
1156     std::string username = "user001";
1157     EXPECT_NO_THROW(
1158         UserMgr::createUser(username, {"redfish", "ssh"}, "priv-user", true));
1159     std::string newUsername = "user002";
1160 
1161     EXPECT_CALL(*this, executeUserRename(testing::StrEq(username),
1162                                          testing::StrEq(newUsername)))
1163         .WillOnce(testing::Throw(
1164             sdbusplus::xyz::openbmc_project::Common::Error::InternalFailure()));
1165     EXPECT_CALL(*this, isUserExistSystem(testing::StrEq(newUsername)))
1166         .WillOnce(Return(false));
1167     EXPECT_THROW(
1168         UserMgr::renameUser(username, newUsername),
1169         sdbusplus::xyz::openbmc_project::Common::Error::InternalFailure);
1170 
1171     // The original user is unchanged
1172     UserInfoMap userInfo = getUserInfo(username);
1173     EXPECT_EQ(std::get<Privilege>(userInfo["UserPrivilege"]), "priv-user");
1174     EXPECT_THAT(std::get<GroupList>(userInfo["UserGroups"]),
1175                 testing::UnorderedElementsAre("redfish", "ssh"));
1176     EXPECT_EQ(std::get<UserEnabled>(userInfo["UserEnabled"]), true);
1177 
1178     EXPECT_NO_THROW(UserMgr::deleteUser(username));
1179 }
1180 
TEST_F(UserMgrInTest,RenameUserThrowsInternalFailureIfExecuteUserModifyPartiallyFails)1181 TEST_F(UserMgrInTest,
1182        RenameUserThrowsInternalFailureIfExecuteUserModifyPartiallyFails)
1183 {
1184     std::string username = "user001";
1185     EXPECT_NO_THROW(
1186         UserMgr::createUser(username, {"redfish", "ssh"}, "priv-user", true));
1187     std::string newUsername = "user002";
1188 
1189     EXPECT_CALL(*this, executeUserRename(testing::StrEq(username),
1190                                          testing::StrEq(newUsername)))
1191         .WillOnce(testing::Throw(
1192             sdbusplus::xyz::openbmc_project::Common::Error::InternalFailure()));
1193     EXPECT_CALL(*this, isUserExistSystem(testing::StrEq(newUsername)))
1194         .WillOnce(Return(true));
1195     EXPECT_THROW(
1196         UserMgr::renameUser(username, newUsername),
1197         sdbusplus::xyz::openbmc_project::Common::Error::InternalFailure);
1198 
1199     // The original user is updated
1200     UserInfoMap userInfo = getUserInfo(newUsername);
1201     EXPECT_EQ(std::get<Privilege>(userInfo["UserPrivilege"]), "priv-user");
1202     EXPECT_THAT(std::get<GroupList>(userInfo["UserGroups"]),
1203                 testing::UnorderedElementsAre("redfish", "ssh"));
1204     EXPECT_EQ(std::get<UserEnabled>(userInfo["UserEnabled"]), true);
1205 
1206     EXPECT_NO_THROW(UserMgr::deleteUser(newUsername));
1207 }
1208 
TEST_F(UserMgrInTest,DefaultUserModifyFailedWithInternalFailure)1209 TEST_F(UserMgrInTest, DefaultUserModifyFailedWithInternalFailure)
1210 {
1211     EXPECT_THROW(
1212         UserMgr::executeUserRename("user0", "user1"),
1213         sdbusplus::xyz::openbmc_project::Common::Error::InternalFailure);
1214     EXPECT_THROW(
1215         UserMgr::executeUserModify("user0", "ssh", true),
1216         sdbusplus::xyz::openbmc_project::Common::Error::InternalFailure);
1217 }
1218 
TEST_F(UserMgrInTest,UpdateGroupsAndPrivOnSuccess)1219 TEST_F(UserMgrInTest, UpdateGroupsAndPrivOnSuccess)
1220 {
1221     std::string username = "user001";
1222     EXPECT_NO_THROW(
1223         UserMgr::createUser(username, {"redfish", "ssh"}, "priv-user", true));
1224     EXPECT_NO_THROW(
1225         updateGroupsAndPriv(username, {"ipmi", "ssh"}, "priv-admin"));
1226     UserInfoMap userInfo = getUserInfo(username);
1227     EXPECT_EQ(std::get<Privilege>(userInfo["UserPrivilege"]), "priv-admin");
1228     EXPECT_THAT(std::get<GroupList>(userInfo["UserGroups"]),
1229                 testing::UnorderedElementsAre("ipmi", "ssh"));
1230     EXPECT_EQ(std::get<UserEnabled>(userInfo["UserEnabled"]), true);
1231     EXPECT_NO_THROW(UserMgr::deleteUser(username));
1232 }
1233 
TEST_F(UserMgrInTest,UpdateGroupsAndPrivThrowsInternalFailureIfExecuteUserModifyFail)1234 TEST_F(UserMgrInTest,
1235        UpdateGroupsAndPrivThrowsInternalFailureIfExecuteUserModifyFail)
1236 {
1237     std::string username = "user001";
1238     EXPECT_NO_THROW(
1239         UserMgr::createUser(username, {"redfish", "ssh"}, "priv-user", true));
1240     EXPECT_CALL(*this, executeUserModify(testing::StrEq(username), testing::_,
1241                                          testing::_))
1242         .WillOnce(testing::Throw(
1243             sdbusplus::xyz::openbmc_project::Common::Error::InternalFailure()));
1244     EXPECT_THROW(
1245         updateGroupsAndPriv(username, {"ipmi", "ssh"}, "priv-admin"),
1246         sdbusplus::xyz::openbmc_project::Common::Error::InternalFailure);
1247     EXPECT_NO_THROW(UserMgr::deleteUser(username));
1248 }
1249 
TEST_F(UserMgrInTest,MinPasswordLengthReturnsIfValueIsTheSame)1250 TEST_F(UserMgrInTest, MinPasswordLengthReturnsIfValueIsTheSame)
1251 {
1252     initializeAccountPolicy();
1253     EXPECT_EQ(AccountPolicyIface::minPasswordLength(), 8);
1254     UserMgr::minPasswordLength(8);
1255     EXPECT_EQ(AccountPolicyIface::minPasswordLength(), 8);
1256 }
1257 
TEST_F(UserMgrInTest,MinPasswordLengthRejectsTooShortPasswordWithInvalidArgument)1258 TEST_F(UserMgrInTest,
1259        MinPasswordLengthRejectsTooShortPasswordWithInvalidArgument)
1260 {
1261     initializeAccountPolicy();
1262     EXPECT_EQ(AccountPolicyIface::minPasswordLength(), 8);
1263     EXPECT_THROW(
1264         UserMgr::minPasswordLength(minPasswdLength - 1),
1265         sdbusplus::xyz::openbmc_project::Common::Error::InvalidArgument);
1266     EXPECT_EQ(AccountPolicyIface::minPasswordLength(), 8);
1267 }
1268 
TEST_F(UserMgrInTest,MinPasswordLengthOnSuccess)1269 TEST_F(UserMgrInTest, MinPasswordLengthOnSuccess)
1270 {
1271     initializeAccountPolicy();
1272     EXPECT_EQ(AccountPolicyIface::minPasswordLength(), 8);
1273     UserMgr::minPasswordLength(16);
1274     EXPECT_EQ(AccountPolicyIface::minPasswordLength(), 16);
1275 }
1276 
TEST_F(UserMgrInTest,MinPasswordLengthOnFailure)1277 TEST_F(UserMgrInTest, MinPasswordLengthOnFailure)
1278 {
1279     EXPECT_NO_THROW(dumpStringToFile("whatever", tempPWQualityConfigFile));
1280     initializeAccountPolicy();
1281     EXPECT_EQ(AccountPolicyIface::minPasswordLength(), 8);
1282     EXPECT_THROW(
1283         UserMgr::minPasswordLength(16),
1284         sdbusplus::xyz::openbmc_project::Common::Error::InternalFailure);
1285     EXPECT_EQ(AccountPolicyIface::minPasswordLength(), 8);
1286 }
1287 
TEST_F(UserMgrInTest,MinPasswordLengthGreaterThanMaxPasswordLength)1288 TEST_F(UserMgrInTest, MinPasswordLengthGreaterThanMaxPasswordLength)
1289 {
1290     initializeAccountPolicy();
1291 
1292     EXPECT_EQ(AccountPolicyIface::minPasswordLength(), 8);
1293     EXPECT_THROW(
1294         UserMgr::minPasswordLength(maxPasswdLength + 1),
1295         sdbusplus::xyz::openbmc_project::Common::Error::InvalidArgument);
1296     EXPECT_EQ(AccountPolicyIface::minPasswordLength(), 8);
1297 }
1298 
TEST_F(UserMgrInTest,RememberOldPasswordTimesReturnsIfValueIsTheSame)1299 TEST_F(UserMgrInTest, RememberOldPasswordTimesReturnsIfValueIsTheSame)
1300 {
1301     initializeAccountPolicy();
1302     EXPECT_EQ(AccountPolicyIface::rememberOldPasswordTimes(), 0);
1303     UserMgr::rememberOldPasswordTimes(8);
1304     EXPECT_EQ(AccountPolicyIface::rememberOldPasswordTimes(), 8);
1305     UserMgr::rememberOldPasswordTimes(8);
1306     EXPECT_EQ(AccountPolicyIface::rememberOldPasswordTimes(), 8);
1307 }
1308 
TEST_F(UserMgrInTest,RememberOldPasswordTimesOnSuccess)1309 TEST_F(UserMgrInTest, RememberOldPasswordTimesOnSuccess)
1310 {
1311     initializeAccountPolicy();
1312     EXPECT_EQ(AccountPolicyIface::rememberOldPasswordTimes(), 0);
1313     UserMgr::rememberOldPasswordTimes(16);
1314     EXPECT_EQ(AccountPolicyIface::rememberOldPasswordTimes(), 16);
1315 }
1316 
TEST_F(UserMgrInTest,RememberOldPasswordTimesOnFailure)1317 TEST_F(UserMgrInTest, RememberOldPasswordTimesOnFailure)
1318 {
1319     EXPECT_NO_THROW(dumpStringToFile("whatever", tempPWHistoryConfigFile));
1320     initializeAccountPolicy();
1321     EXPECT_EQ(AccountPolicyIface::rememberOldPasswordTimes(), 0);
1322     EXPECT_THROW(
1323         UserMgr::rememberOldPasswordTimes(16),
1324         sdbusplus::xyz::openbmc_project::Common::Error::InternalFailure);
1325     EXPECT_EQ(AccountPolicyIface::rememberOldPasswordTimes(), 0);
1326 }
1327 
TEST_F(UserMgrInTest,MaxLoginAttemptBeforeLockoutReturnsIfValueIsTheSame)1328 TEST_F(UserMgrInTest, MaxLoginAttemptBeforeLockoutReturnsIfValueIsTheSame)
1329 {
1330     initializeAccountPolicy();
1331     EXPECT_EQ(AccountPolicyIface::maxLoginAttemptBeforeLockout(), 2);
1332     UserMgr::maxLoginAttemptBeforeLockout(2);
1333     EXPECT_EQ(AccountPolicyIface::maxLoginAttemptBeforeLockout(), 2);
1334 }
1335 
TEST_F(UserMgrInTest,MaxLoginAttemptBeforeLockoutOnSuccess)1336 TEST_F(UserMgrInTest, MaxLoginAttemptBeforeLockoutOnSuccess)
1337 {
1338     initializeAccountPolicy();
1339     EXPECT_EQ(AccountPolicyIface::maxLoginAttemptBeforeLockout(), 2);
1340     UserMgr::maxLoginAttemptBeforeLockout(16);
1341     EXPECT_EQ(AccountPolicyIface::maxLoginAttemptBeforeLockout(), 16);
1342 }
1343 
TEST_F(UserMgrInTest,MaxLoginAttemptBeforeLockoutOnFailure)1344 TEST_F(UserMgrInTest, MaxLoginAttemptBeforeLockoutOnFailure)
1345 {
1346     initializeAccountPolicy();
1347     EXPECT_NO_THROW(dumpStringToFile("whatever", tempFaillockConfigFile));
1348     EXPECT_THROW(
1349         UserMgr::maxLoginAttemptBeforeLockout(16),
1350         sdbusplus::xyz::openbmc_project::Common::Error::InternalFailure);
1351     EXPECT_EQ(AccountPolicyIface::maxLoginAttemptBeforeLockout(), 2);
1352 }
1353 
TEST_F(UserMgrInTest,AccountUnlockTimeoutReturnsIfValueIsTheSame)1354 TEST_F(UserMgrInTest, AccountUnlockTimeoutReturnsIfValueIsTheSame)
1355 {
1356     initializeAccountPolicy();
1357     EXPECT_EQ(AccountPolicyIface::accountUnlockTimeout(), 3);
1358     UserMgr::accountUnlockTimeout(3);
1359     EXPECT_EQ(AccountPolicyIface::accountUnlockTimeout(), 3);
1360 }
1361 
TEST_F(UserMgrInTest,AccountUnlockTimeoutOnSuccess)1362 TEST_F(UserMgrInTest, AccountUnlockTimeoutOnSuccess)
1363 {
1364     initializeAccountPolicy();
1365     EXPECT_EQ(AccountPolicyIface::accountUnlockTimeout(), 3);
1366     UserMgr::accountUnlockTimeout(16);
1367     EXPECT_EQ(AccountPolicyIface::accountUnlockTimeout(), 16);
1368 }
1369 
TEST_F(UserMgrInTest,AccountUnlockTimeoutOnFailure)1370 TEST_F(UserMgrInTest, AccountUnlockTimeoutOnFailure)
1371 {
1372     initializeAccountPolicy();
1373     EXPECT_NO_THROW(dumpStringToFile("whatever", tempFaillockConfigFile));
1374     EXPECT_THROW(
1375         UserMgr::accountUnlockTimeout(16),
1376         sdbusplus::xyz::openbmc_project::Common::Error::InternalFailure);
1377     EXPECT_EQ(AccountPolicyIface::accountUnlockTimeout(), 3);
1378 }
1379 
TEST_F(UserMgrInTest,UserEnableOnSuccess)1380 TEST_F(UserMgrInTest, UserEnableOnSuccess)
1381 {
1382     std::string username = "user001";
1383     EXPECT_NO_THROW(
1384         UserMgr::createUser(username, {"redfish", "ssh"}, "priv-user", true));
1385     UserInfoMap userInfo = getUserInfo(username);
1386     EXPECT_EQ(std::get<UserEnabled>(userInfo["UserEnabled"]), true);
1387 
1388     EXPECT_NO_THROW(userEnable(username, false));
1389 
1390     userInfo = getUserInfo(username);
1391     EXPECT_EQ(std::get<UserEnabled>(userInfo["UserEnabled"]), false);
1392 
1393     EXPECT_NO_THROW(UserMgr::deleteUser(username));
1394 }
1395 
TEST_F(UserMgrInTest,CreateDeleteUserSuccessForHostConsole)1396 TEST_F(UserMgrInTest, CreateDeleteUserSuccessForHostConsole)
1397 {
1398     std::string username = "user001";
1399     EXPECT_NO_THROW(
1400         UserMgr::createUser(username, {"hostconsole"}, "priv-user", true));
1401     EXPECT_NO_THROW(UserMgr::deleteUser(username));
1402     EXPECT_NO_THROW(
1403         UserMgr::createUser(username, {"hostconsole"}, "priv-admin", true));
1404     EXPECT_NO_THROW(UserMgr::deleteUser(username));
1405     EXPECT_NO_THROW(
1406         UserMgr::createUser(username, {"hostconsole"}, "priv-operator", true));
1407     EXPECT_NO_THROW(UserMgr::deleteUser(username));
1408 }
1409 
TEST_F(UserMgrInTest,UserEnableThrowsInternalFailureIfExecuteUserModifyFail)1410 TEST_F(UserMgrInTest, UserEnableThrowsInternalFailureIfExecuteUserModifyFail)
1411 {
1412     std::string username = "user001";
1413     EXPECT_NO_THROW(
1414         UserMgr::createUser(username, {"redfish", "ssh"}, "priv-user", true));
1415     UserInfoMap userInfo = getUserInfo(username);
1416     EXPECT_EQ(std::get<UserEnabled>(userInfo["UserEnabled"]), true);
1417 
1418     EXPECT_CALL(*this, executeUserModifyUserEnable(testing::StrEq(username),
1419                                                    testing::Eq(false)))
1420         .WillOnce(testing::Throw(
1421             sdbusplus::xyz::openbmc_project::Common::Error::InternalFailure()));
1422     EXPECT_THROW(
1423         userEnable(username, false),
1424         sdbusplus::xyz::openbmc_project::Common::Error::InternalFailure);
1425 
1426     userInfo = getUserInfo(username);
1427     // Stay unchanged
1428     EXPECT_EQ(std::get<UserEnabled>(userInfo["UserEnabled"]), true);
1429 
1430     EXPECT_NO_THROW(UserMgr::deleteUser(username));
1431 }
1432 
TEST_F(UserMgrInTest,UserLockedForFailedAttemptReturnsFalseIfMaxLoginAttemptBeforeLockoutIsZero)1433 TEST_F(
1434     UserMgrInTest,
1435     UserLockedForFailedAttemptReturnsFalseIfMaxLoginAttemptBeforeLockoutIsZero)
1436 {
1437     EXPECT_FALSE(userLockedForFailedAttempt("whatever"));
1438 }
1439 
TEST_F(UserMgrInTest,UserLockedForFailedAttemptZeroFailuresReturnsFalse)1440 TEST_F(UserMgrInTest, UserLockedForFailedAttemptZeroFailuresReturnsFalse)
1441 {
1442     std::string username = "user001";
1443     initializeAccountPolicy();
1444     // Example output from BMC
1445     // root:~# faillock --user root
1446     // root:
1447     // When   Type   Source   Valid
1448     std::vector<std::string> output = {"whatever",
1449                                        "When   Type   Source   Valid"};
1450     EXPECT_CALL(*this, getFailedAttempt(testing::StrEq(username.c_str())))
1451         .WillOnce(testing::Return(output));
1452 
1453     EXPECT_FALSE(userLockedForFailedAttempt(username));
1454 }
1455 
TEST_F(UserMgrInTest,UserLockedForFailedAttemptFailIfGetFailedAttemptFail)1456 TEST_F(UserMgrInTest, UserLockedForFailedAttemptFailIfGetFailedAttemptFail)
1457 {
1458     std::string username = "user001";
1459     initializeAccountPolicy();
1460     EXPECT_CALL(*this, getFailedAttempt(testing::StrEq(username.c_str())))
1461         .WillOnce(testing::Throw(
1462             sdbusplus::xyz::openbmc_project::Common::Error::InternalFailure()));
1463 
1464     EXPECT_THROW(
1465         userLockedForFailedAttempt(username),
1466         sdbusplus::xyz::openbmc_project::Common::Error::InternalFailure);
1467 }
1468 
TEST_F(UserMgrInTest,UserLockedForFailedAttemptThrowsInternalFailureIfWrongDateFormat)1469 TEST_F(UserMgrInTest,
1470        UserLockedForFailedAttemptThrowsInternalFailureIfWrongDateFormat)
1471 {
1472     std::string username = "user001";
1473     initializeAccountPolicy();
1474 
1475     // Choose a date in the past.
1476     std::vector<std::string> output = {"whatever",
1477                                        "10/24/2002 00:00:00 type source V"};
1478     EXPECT_CALL(*this, getFailedAttempt(testing::StrEq(username.c_str())))
1479         .WillOnce(testing::Return(output));
1480 
1481     EXPECT_THROW(
1482         userLockedForFailedAttempt(username),
1483         sdbusplus::xyz::openbmc_project::Common::Error::InternalFailure);
1484 }
1485 
TEST_F(UserMgrInTest,UserLockedForFailedAttemptReturnsFalseIfLastFailTimeHasTimedOut)1486 TEST_F(UserMgrInTest,
1487        UserLockedForFailedAttemptReturnsFalseIfLastFailTimeHasTimedOut)
1488 {
1489     std::string username = "user001";
1490     initializeAccountPolicy();
1491 
1492     // Choose a date in the past.
1493     std::vector<std::string> output = {"whatever",
1494                                        "2002-10-24 00:00:00 type source V"};
1495     EXPECT_CALL(*this, getFailedAttempt(testing::StrEq(username.c_str())))
1496         .WillOnce(testing::Return(output));
1497 
1498     EXPECT_EQ(userLockedForFailedAttempt(username), false);
1499 }
1500 
TEST_F(UserMgrInTest,CheckAndThrowForDisallowedGroupCreationOnSuccess)1501 TEST_F(UserMgrInTest, CheckAndThrowForDisallowedGroupCreationOnSuccess)
1502 {
1503     // Base Redfish Roles
1504     EXPECT_NO_THROW(
1505         checkAndThrowForDisallowedGroupCreation("openbmc_rfr_Administrator"));
1506     EXPECT_NO_THROW(
1507         checkAndThrowForDisallowedGroupCreation("openbmc_rfr_Operator"));
1508     EXPECT_NO_THROW(
1509         checkAndThrowForDisallowedGroupCreation("openbmc_rfr_ReadOnly"));
1510     // Base Redfish Privileges
1511     EXPECT_NO_THROW(
1512         checkAndThrowForDisallowedGroupCreation("openbmc_rfp_Login"));
1513     EXPECT_NO_THROW(checkAndThrowForDisallowedGroupCreation(
1514         "openbmc_rfp_ConfigureManager"));
1515     EXPECT_NO_THROW(
1516         checkAndThrowForDisallowedGroupCreation("openbmc_rfp_ConfigureUsers"));
1517     EXPECT_NO_THROW(
1518         checkAndThrowForDisallowedGroupCreation("openbmc_rfp_ConfigureSelf"));
1519     EXPECT_NO_THROW(checkAndThrowForDisallowedGroupCreation(
1520         "openbmc_rfp_ConfigureComponents"));
1521     // OEM Redfish Roles
1522     EXPECT_NO_THROW(
1523         checkAndThrowForDisallowedGroupCreation("openbmc_orfr_PowerService"));
1524     // OEM Redfish Privileges
1525     EXPECT_NO_THROW(
1526         checkAndThrowForDisallowedGroupCreation("openbmc_orfp_PowerService"));
1527 }
1528 
TEST_F(UserMgrInTest,CheckAndThrowForDisallowedGroupCreationThrowsIfGroupNameTooLong)1529 TEST_F(UserMgrInTest,
1530        CheckAndThrowForDisallowedGroupCreationThrowsIfGroupNameTooLong)
1531 {
1532     std::string groupName(maxSystemGroupNameLength + 1, 'A');
1533     EXPECT_THROW(
1534         checkAndThrowForDisallowedGroupCreation(groupName),
1535         sdbusplus::xyz::openbmc_project::Common::Error::InvalidArgument);
1536 }
1537 
TEST_F(UserMgrInTest,CheckAndThrowForDisallowedGroupCreationThrowsIfGroupNameHasDisallowedCharacters)1538 TEST_F(
1539     UserMgrInTest,
1540     CheckAndThrowForDisallowedGroupCreationThrowsIfGroupNameHasDisallowedCharacters)
1541 {
1542     EXPECT_THROW(
1543         checkAndThrowForDisallowedGroupCreation("openbmc_rfp_?owerService"),
1544         sdbusplus::xyz::openbmc_project::Common::Error::InvalidArgument);
1545     EXPECT_THROW(
1546         checkAndThrowForDisallowedGroupCreation("openbmc_rfp_-owerService"),
1547         sdbusplus::xyz::openbmc_project::Common::Error::InvalidArgument);
1548 }
1549 
TEST_F(UserMgrInTest,CheckAndThrowForDisallowedGroupCreationThrowsIfGroupNameHasDisallowedPrefix)1550 TEST_F(
1551     UserMgrInTest,
1552     CheckAndThrowForDisallowedGroupCreationThrowsIfGroupNameHasDisallowedPrefix)
1553 {
1554     EXPECT_THROW(
1555         checkAndThrowForDisallowedGroupCreation("google_rfp_"),
1556         sdbusplus::xyz::openbmc_project::Common::Error::InvalidArgument);
1557     EXPECT_THROW(
1558         checkAndThrowForDisallowedGroupCreation("com_rfp_"),
1559         sdbusplus::xyz::openbmc_project::Common::Error::InvalidArgument);
1560 }
1561 
TEST_F(UserMgrInTest,CheckAndThrowForMaxGroupCountOnSuccess)1562 TEST_F(UserMgrInTest, CheckAndThrowForMaxGroupCountOnSuccess)
1563 {
1564     constexpr size_t predefGroupCount = 4;
1565 
1566     EXPECT_THAT(allGroups().size(), predefGroupCount);
1567     for (size_t i = 0; i < maxSystemGroupCount - predefGroupCount; ++i)
1568     {
1569         std::string groupName = "openbmc_rfr_role";
1570         groupName += std::to_string(i);
1571         EXPECT_NO_THROW(createGroup(groupName));
1572     }
1573     EXPECT_THROW(
1574         createGroup("openbmc_rfr_AnotherRole"),
1575         sdbusplus::xyz::openbmc_project::User::Common::Error::NoResource);
1576     for (size_t i = 0; i < maxSystemGroupCount - predefGroupCount; ++i)
1577     {
1578         std::string groupName = "openbmc_rfr_role";
1579         groupName += std::to_string(i);
1580         EXPECT_NO_THROW(deleteGroup(groupName));
1581     }
1582 }
1583 
TEST_F(UserMgrInTest,CheckAndThrowForGroupExist)1584 TEST_F(UserMgrInTest, CheckAndThrowForGroupExist)
1585 {
1586     std::string groupName = "openbmc_rfr_role";
1587     EXPECT_NO_THROW(createGroup(groupName));
1588     EXPECT_THROW(
1589         createGroup(groupName),
1590         sdbusplus::xyz::openbmc_project::User::Common::Error::GroupNameExists);
1591     EXPECT_NO_THROW(deleteGroup(groupName));
1592 }
1593 
TEST_F(UserMgrInTest,ByDefaultAllGroupsArePredefinedGroups)1594 TEST_F(UserMgrInTest, ByDefaultAllGroupsArePredefinedGroups)
1595 {
1596     EXPECT_THAT(allGroups(), testing::UnorderedElementsAre(
1597                                  "redfish", "ipmi", "ssh", "hostconsole"));
1598 }
1599 
TEST_F(UserMgrInTest,AddGroupThrowsIfPreDefinedGroupAdd)1600 TEST_F(UserMgrInTest, AddGroupThrowsIfPreDefinedGroupAdd)
1601 {
1602     EXPECT_THROW(
1603         createGroup("ipmi"),
1604         sdbusplus::xyz::openbmc_project::User::Common::Error::GroupNameExists);
1605     EXPECT_THROW(
1606         createGroup("redfish"),
1607         sdbusplus::xyz::openbmc_project::User::Common::Error::GroupNameExists);
1608     EXPECT_THROW(
1609         createGroup("ssh"),
1610         sdbusplus::xyz::openbmc_project::User::Common::Error::GroupNameExists);
1611     EXPECT_THROW(
1612         createGroup("hostconsole"),
1613         sdbusplus::xyz::openbmc_project::User::Common::Error::GroupNameExists);
1614 }
1615 
TEST_F(UserMgrInTest,DeleteGroupThrowsIfGroupIsNotAllowedToChange)1616 TEST_F(UserMgrInTest, DeleteGroupThrowsIfGroupIsNotAllowedToChange)
1617 {
1618     EXPECT_THROW(
1619         deleteGroup("ipmi"),
1620         sdbusplus::xyz::openbmc_project::Common::Error::InvalidArgument);
1621     EXPECT_THROW(
1622         deleteGroup("redfish"),
1623         sdbusplus::xyz::openbmc_project::Common::Error::InvalidArgument);
1624     EXPECT_THROW(
1625         deleteGroup("ssh"),
1626         sdbusplus::xyz::openbmc_project::Common::Error::InvalidArgument);
1627     EXPECT_THROW(
1628         deleteGroup("hostconsole"),
1629         sdbusplus::xyz::openbmc_project::Common::Error::InvalidArgument);
1630 }
1631 
TEST_F(UserMgrInTest,CreateGroupThrowsInternalFailureWhenExecuteGroupCreateFails)1632 TEST_F(UserMgrInTest,
1633        CreateGroupThrowsInternalFailureWhenExecuteGroupCreateFails)
1634 {
1635     EXPECT_CALL(*this, executeGroupCreation)
1636         .WillOnce(testing::Throw(
1637             sdbusplus::xyz::openbmc_project::Common::Error::InternalFailure()));
1638     EXPECT_THROW(
1639         createGroup("openbmc_rfr_role1"),
1640         sdbusplus::xyz::openbmc_project::Common::Error::InternalFailure);
1641 }
1642 
TEST_F(UserMgrInTest,DeleteGroupThrowsInternalFailureWhenExecuteGroupDeleteFails)1643 TEST_F(UserMgrInTest,
1644        DeleteGroupThrowsInternalFailureWhenExecuteGroupDeleteFails)
1645 {
1646     std::string groupName = "openbmc_rfr_role1";
1647     EXPECT_NO_THROW(UserMgr::createGroup(groupName));
1648     EXPECT_CALL(*this, executeGroupDeletion(testing::StrEq(groupName)))
1649         .WillOnce(testing::Throw(
1650             sdbusplus::xyz::openbmc_project::Common::Error::InternalFailure()))
1651         .WillOnce(testing::DoDefault());
1652 
1653     EXPECT_THROW(
1654         deleteGroup(groupName),
1655         sdbusplus::xyz::openbmc_project::Common::Error::InternalFailure);
1656     EXPECT_NO_THROW(UserMgr::deleteGroup(groupName));
1657 }
1658 
TEST_F(UserMgrInTest,CheckAndThrowForGroupNotExist)1659 TEST_F(UserMgrInTest, CheckAndThrowForGroupNotExist)
1660 {
1661     EXPECT_THROW(deleteGroup("whatever"),
1662                  sdbusplus::xyz::openbmc_project::User::Common::Error::
1663                      GroupNameDoesNotExist);
1664 }
1665 
TEST(ReadAllGroupsOnSystemTest,OnlyReturnsPredefinedGroups)1666 TEST(ReadAllGroupsOnSystemTest, OnlyReturnsPredefinedGroups)
1667 {
1668     EXPECT_THAT(
1669         UserMgr::readAllGroupsOnSystem(),
1670         testing::UnorderedElementsAre("redfish", "ipmi", "ssh", "hostconsole"));
1671 }
1672 
TEST_F(UserMgrInTest,CreateUser2)1673 TEST_F(UserMgrInTest, CreateUser2)
1674 {
1675     const std::string userName = getNextUserName();
1676     const bool enabled = true;
1677 
1678     // last password change date is today
1679     // old maximum password age is 5000
1680     // set password expiration in 3 days
1681     PasswordExpirationInfo info;
1682     fillPasswordExpiration(0, 5000, 3, info);
1683 
1684     setUpCreateUser(userName, enabled);
1685     setUpSetPasswordExpiration(userName, info);
1686     setUpGetUserInfo(userName, enabled);
1687     setUpDeleteUser(userName);
1688 
1689     std::vector<std::string> groups = {"redfish", "ssh"};
1690 
1691     UserCreateMap props;
1692     props[UserProperty::GroupNames] = std::move(groups);
1693     props[UserProperty::Privilege] = "priv-user";
1694     props[UserProperty::Enabled] = enabled;
1695     props[UserProperty::PasswordExpiration] = info.passwordExpiration;
1696 
1697     EXPECT_NO_THROW(UserMgr::createUser2(userName, props));
1698 
1699     UserInfoMap userInfo = getUserInfo(userName);
1700     EXPECT_EQ(std::get<PasswordExpiration>(userInfo["PasswordExpiration"]),
1701               info.passwordExpiration);
1702 
1703     EXPECT_NO_THROW(UserMgr::deleteUser(userName));
1704 }
1705 
TEST_F(UserMgrInTest,CreateUser2WithoutPasswordExpiration)1706 TEST_F(UserMgrInTest, CreateUser2WithoutPasswordExpiration)
1707 {
1708     const std::string userName = getNextUserName();
1709     const bool enabled = true;
1710 
1711     setUpCreateUser(userName, enabled);
1712     setUpGetUserInfo(userName, enabled);
1713     setUpDeleteUser(userName);
1714 
1715     std::vector<std::string> groups = {"redfish", "ssh"};
1716 
1717     UserCreateMap props;
1718     props[UserProperty::GroupNames] = std::move(groups);
1719     props[UserProperty::Privilege] = "priv-user";
1720     props[UserProperty::Enabled] = enabled;
1721 
1722     EXPECT_NO_THROW(UserMgr::createUser2(userName, props));
1723 
1724     UserInfoMap userInfo = getUserInfo(userName);
1725     EXPECT_EQ(std::get<PasswordExpiration>(userInfo["PasswordExpiration"]),
1726               getDefaultPasswordExpiration());
1727 
1728     EXPECT_NO_THROW(UserMgr::deleteUser(userName));
1729 }
1730 
TEST_F(UserMgrInTest,CreateUser2PasswordExpirationNotSet)1731 TEST_F(UserMgrInTest, CreateUser2PasswordExpirationNotSet)
1732 {
1733     using namespace std::chrono;
1734 
1735     const std::string userName = getNextUserName();
1736     const bool enabled = true;
1737 
1738     setUpCreateUser(userName, enabled);
1739 
1740     EXPECT_CALL(*this, getShadowData(testing::StrEq(userName), _)).Times(0);
1741 
1742     EXPECT_CALL(*this,
1743                 executeUserPasswordExpiration(testing::StrEq(userName), _, _))
1744         .Times(0);
1745 
1746     setUpGetUserInfo(userName, enabled);
1747     setUpDeleteUser(userName);
1748 
1749     constexpr auto passwordExpiration = getDefaultPasswordExpiration();
1750 
1751     std::vector<std::string> groups = {"redfish", "ssh"};
1752 
1753     UserCreateMap props;
1754     props[UserProperty::GroupNames] = std::move(groups);
1755     props[UserProperty::Privilege] = "priv-user";
1756     props[UserProperty::Enabled] = enabled;
1757     props[UserProperty::PasswordExpiration] = passwordExpiration;
1758 
1759     EXPECT_NO_THROW(UserMgr::createUser2(userName, props));
1760 
1761     UserInfoMap userInfo = getUserInfo(userName);
1762     EXPECT_EQ(std::get<PasswordExpiration>(userInfo["PasswordExpiration"]),
1763               passwordExpiration);
1764 
1765     EXPECT_NO_THROW(UserMgr::deleteUser(userName));
1766 }
1767 
TEST_F(UserMgrInTest,CreateUser2UnexpiringPassword)1768 TEST_F(UserMgrInTest, CreateUser2UnexpiringPassword)
1769 {
1770     using namespace std::chrono;
1771 
1772     const std::string userName = getNextUserName();
1773     const bool enabled = true;
1774 
1775     // last password change date is today
1776     const long lastChangeDate =
1777         duration_cast<days>(seconds{getEpochTimeNow()}).count();
1778 
1779     // password age is
1780     constexpr long passwordAge = 99999;
1781 
1782     // make password not to expire
1783     const uint64_t passwordExpiration = getUnexpiringPasswordTime();
1784 
1785     setUpCreateUser(userName, enabled);
1786 
1787     EXPECT_CALL(*this, getShadowData(testing::StrEq(userName), _))
1788         .WillOnce(Invoke([&lastChangeDate](auto, struct spwd& spwd) {
1789             spwd.sp_lstchg = lastChangeDate;
1790             spwd.sp_max = passwordAge;
1791         }));
1792 
1793     EXPECT_CALL(*this, executeUserPasswordExpiration(
1794                            testing::StrEq(userName), lastChangeDate,
1795                            getUnexpiringPasswordAge()))
1796         .Times(1);
1797 
1798     setUpGetUserInfo(userName, enabled);
1799     setUpDeleteUser(userName);
1800 
1801     std::vector<std::string> groups = {"redfish", "ssh"};
1802 
1803     UserCreateMap props;
1804     props[UserProperty::GroupNames] = std::move(groups);
1805     props[UserProperty::Privilege] = "priv-user";
1806     props[UserProperty::Enabled] = enabled;
1807     props[UserProperty::PasswordExpiration] = passwordExpiration;
1808 
1809     EXPECT_NO_THROW(UserMgr::createUser2(userName, props));
1810 
1811     UserInfoMap userInfo = getUserInfo(userName);
1812     EXPECT_EQ(std::get<PasswordExpiration>(userInfo["PasswordExpiration"]),
1813               passwordExpiration);
1814 
1815     EXPECT_NO_THROW(UserMgr::deleteUser(userName));
1816 }
1817 
TEST_F(UserMgrInTest,CreateUser2Rename)1818 TEST_F(UserMgrInTest, CreateUser2Rename)
1819 {
1820     const std::string userName = getNextUserName();
1821     const std::string newUserName = getNextUserName();
1822     const bool enabled = true;
1823 
1824     // last password change date is 7 days ago
1825     // old maximum password age is 15
1826     // set password expiration in 5 days
1827     PasswordExpirationInfo info;
1828     fillPasswordExpiration(7, 15, 5, info);
1829 
1830     setUpCreateUser(userName, enabled);
1831     setUpSetPasswordExpiration(userName, info);
1832     setUpGetUserInfo(newUserName, enabled);
1833     setUpDeleteUser(newUserName);
1834 
1835     EXPECT_CALL(*this, isUserEnabled(userName))
1836         .WillOnce(testing::Return(enabled));
1837 
1838     EXPECT_CALL(*this, executeUserRename(testing::StrEq(userName),
1839                                          testing::StrEq(newUserName)))
1840         .Times(1);
1841 
1842     std::vector<std::string> groups = {"redfish", "ssh"};
1843 
1844     UserCreateMap props;
1845     props[UserProperty::GroupNames] = std::move(groups);
1846     props[UserProperty::Privilege] = "priv-user";
1847     props[UserProperty::Enabled] = enabled;
1848     props[UserProperty::PasswordExpiration] = info.passwordExpiration;
1849 
1850     EXPECT_NO_THROW(UserMgr::createUser2(userName, props));
1851 
1852     EXPECT_NO_THROW(UserMgr::renameUser(userName, newUserName));
1853 
1854     UserInfoMap userInfo = getUserInfo(newUserName);
1855     EXPECT_EQ(std::get<PasswordExpiration>(userInfo["PasswordExpiration"]),
1856               info.passwordExpiration);
1857 
1858     EXPECT_NO_THROW(UserMgr::deleteUser(newUserName));
1859 }
1860 
TEST_F(UserMgrInTest,CreateUser2PasswordExpirationFail)1861 TEST_F(UserMgrInTest, CreateUser2PasswordExpirationFail)
1862 {
1863     using namespace std::chrono;
1864 
1865     const std::string userName = getNextUserName();
1866     const bool enabled = true;
1867 
1868     setUpCreateUser(userName, enabled);
1869 
1870     EXPECT_CALL(*this, getShadowData(testing::StrEq(userName), _))
1871         .WillOnce([]() {
1872             throw sdbusplus::xyz::openbmc_project::Common::Error::
1873                 InternalFailure();
1874         });
1875 
1876     setUpDeleteUser(userName);
1877 
1878     std::vector<std::string> groups = {"redfish", "ssh"};
1879 
1880     UserCreateMap props;
1881     props[UserProperty::GroupNames] = std::move(groups);
1882     props[UserProperty::Privilege] = "priv-user";
1883     props[UserProperty::Enabled] = enabled;
1884     props[UserProperty::PasswordExpiration] = (uint64_t)1;
1885 
1886     EXPECT_THROW(
1887         UserMgr::createUser2(userName, props),
1888         sdbusplus::xyz::openbmc_project::Common::Error::InternalFailure);
1889 
1890     EXPECT_THROW(getUserInfo(userName),
1891                  sdbusplus::xyz::openbmc_project::User::Common::Error::
1892                      UserNameDoesNotExist);
1893 }
1894 
1895 } // namespace user
1896 } // namespace phosphor
1897