xref: /openbmc/phosphor-user-manager/test/user_mgr_test.cpp (revision 2d042d143b655e1133e1eeeb6a438460bec83e6c)
1 #include "mock_user_mgr.hpp"
2 #include "user_mgr.hpp"
3 
4 #include <sdbusplus/test/sdbus_mock.hpp>
5 #include <xyz/openbmc_project/Common/error.hpp>
6 #include <xyz/openbmc_project/User/Common/error.hpp>
7 
8 #include <exception>
9 #include <filesystem>
10 #include <fstream>
11 #include <vector>
12 
13 #include <gmock/gmock.h>
14 #include <gtest/gtest.h>
15 
16 namespace phosphor
17 {
18 namespace user
19 {
20 
21 using ::testing::Return;
22 using ::testing::Throw;
23 
24 using InternalFailure =
25     sdbusplus::xyz::openbmc_project::Common::Error::InternalFailure;
26 using UserNameDoesNotExist =
27     sdbusplus::xyz::openbmc_project::User::Common::Error::UserNameDoesNotExist;
28 
29 class TestUserMgr : public testing::Test
30 {
31   public:
32     sdbusplus::SdBusMock sdBusMock;
33     sdbusplus::bus_t bus;
34     MockManager mockManager;
35 
36     TestUserMgr() :
37         bus(sdbusplus::get_mocked_new(&sdBusMock)), mockManager(bus, objpath)
38     {}
39 
40     void createLocalUser(const std::string& userName,
41                          std::vector<std::string> groupNames,
42                          const std::string& priv, bool enabled)
43     {
44         sdbusplus::message::object_path tempObjPath(usersObjPath);
45         tempObjPath /= userName;
46         std::string userObj(tempObjPath);
47         mockManager.usersList.emplace(
48             userName, std::make_unique<phosphor::user::Users>(
49                           mockManager.bus, userObj.c_str(), groupNames, priv,
50                           enabled, mockManager));
51     }
52 
53     DbusUserObj createPrivilegeMapperDbusObject(void)
54     {
55         DbusUserObj object;
56         DbusUserObjValue objValue;
57 
58         DbusUserObjPath objPath("/xyz/openbmc_project/user/ldap/openldap");
59         DbusUserPropVariant enabled(true);
60         DbusUserObjProperties property = {std::make_pair("Enabled", enabled)};
61         std::string intf = "xyz.openbmc_project.Object.Enable";
62         objValue.emplace(intf, property);
63         object.emplace(objPath, objValue);
64 
65         DbusUserObjPath objectPath(
66             "/xyz/openbmc_project/user/ldap/openldap/role_map/1");
67         std::string group = "ldapGroup";
68         std::string priv = "priv-admin";
69         DbusUserObjProperties properties = {std::make_pair("GroupName", group),
70                                             std::make_pair("Privilege", priv)};
71         std::string interface = "xyz.openbmc_project.User.PrivilegeMapperEntry";
72 
73         objValue.emplace(interface, properties);
74         object.emplace(objectPath, objValue);
75 
76         return object;
77     }
78 
79     DbusUserObj createLdapConfigObjectWithoutPrivilegeMapper(void)
80     {
81         DbusUserObj object;
82         DbusUserObjValue objValue;
83 
84         DbusUserObjPath objPath("/xyz/openbmc_project/user/ldap/openldap");
85         DbusUserPropVariant enabled(true);
86         DbusUserObjProperties property = {std::make_pair("Enabled", enabled)};
87         std::string intf = "xyz.openbmc_project.Object.Enable";
88         objValue.emplace(intf, property);
89         object.emplace(objPath, objValue);
90         return object;
91     }
92 };
93 
94 TEST_F(TestUserMgr, ldapEntryDoesNotExist)
95 {
96     std::string userName = "user";
97     UserInfoMap userInfo;
98 
99     EXPECT_CALL(mockManager, getPrimaryGroup(userName))
100         .WillRepeatedly(Throw(UserNameDoesNotExist()));
101     EXPECT_THROW(userInfo = mockManager.getUserInfo(userName),
102                  UserNameDoesNotExist);
103 }
104 
105 TEST_F(TestUserMgr, localUser)
106 {
107     UserInfoMap userInfo;
108     std::string userName = "testUser";
109     std::string privilege = "priv-admin";
110     std::vector<std::string> groups{"testGroup"};
111     // Create local user
112     createLocalUser(userName, groups, privilege, true);
113     EXPECT_CALL(mockManager, userLockedForFailedAttempt(userName)).Times(1);
114     userInfo = mockManager.getUserInfo(userName);
115 
116     EXPECT_EQ(privilege, std::get<std::string>(userInfo["UserPrivilege"]));
117     EXPECT_EQ(groups,
118               std::get<std::vector<std::string>>(userInfo["UserGroups"]));
119     EXPECT_EQ(true, std::get<bool>(userInfo["UserEnabled"]));
120     EXPECT_EQ(false, std::get<bool>(userInfo["UserLockedForFailedAttempt"]));
121     EXPECT_EQ(false, std::get<bool>(userInfo["UserPasswordExpired"]));
122     EXPECT_EQ(false, std::get<bool>(userInfo["RemoteUser"]));
123 }
124 
125 TEST_F(TestUserMgr, ldapUserWithPrivMapper)
126 {
127     UserInfoMap userInfo;
128     std::string userName = "ldapUser";
129     std::string ldapGroup = "ldapGroup";
130     gid_t primaryGid = 1000;
131 
132     EXPECT_CALL(mockManager, getPrimaryGroup(userName))
133         .WillRepeatedly(Return(primaryGid));
134     // Create privilege mapper dbus object
135     DbusUserObj object = createPrivilegeMapperDbusObject();
136     EXPECT_CALL(mockManager, getPrivilegeMapperObject())
137         .WillRepeatedly(Return(object));
138     EXPECT_CALL(mockManager, isGroupMember(userName, primaryGid, ldapGroup))
139         .WillRepeatedly(Return(true));
140     userInfo = mockManager.getUserInfo(userName);
141     EXPECT_EQ(true, std::get<bool>(userInfo["RemoteUser"]));
142     EXPECT_EQ("priv-admin", std::get<std::string>(userInfo["UserPrivilege"]));
143 }
144 
145 TEST_F(TestUserMgr, ldapUserWithoutPrivMapper)
146 {
147     using ::testing::_;
148 
149     UserInfoMap userInfo;
150     std::string userName = "ldapUser";
151     std::string ldapGroup = "ldapGroup";
152     gid_t primaryGid = 1000;
153 
154     EXPECT_CALL(mockManager, getPrimaryGroup(userName))
155         .WillRepeatedly(Return(primaryGid));
156     // Create LDAP config object without privilege mapper
157     DbusUserObj object = createLdapConfigObjectWithoutPrivilegeMapper();
158     EXPECT_CALL(mockManager, getPrivilegeMapperObject())
159         .WillRepeatedly(Return(object));
160     EXPECT_CALL(mockManager, isGroupMember(_, _, _)).Times(0);
161     userInfo = mockManager.getUserInfo(userName);
162     EXPECT_EQ(true, std::get<bool>(userInfo["RemoteUser"]));
163     EXPECT_EQ("priv-user", std::get<std::string>(userInfo["UserPrivilege"]));
164 }
165 
166 TEST(GetCSVFromVector, EmptyVectorReturnsEmptyString)
167 {
168     EXPECT_EQ(getCSVFromVector({}), "");
169 }
170 
171 TEST(GetCSVFromVector, ElementsAreJoinedByComma)
172 {
173     EXPECT_EQ(getCSVFromVector(std::vector<std::string>{"123"}), "123");
174     EXPECT_EQ(getCSVFromVector(std::vector<std::string>{"123", "456"}),
175               "123,456");
176 }
177 
178 TEST(RemoveStringFromCSV, WithoutDeleteStringReturnsFalse)
179 {
180     std::string expected = "whatever,https";
181     std::string str = expected;
182     EXPECT_FALSE(removeStringFromCSV(str, "ssh"));
183     EXPECT_EQ(str, expected);
184 
185     std::string empty;
186     EXPECT_FALSE(removeStringFromCSV(empty, "ssh"));
187 }
188 
189 TEST(RemoveStringFromCSV, WithDeleteStringReturnsTrue)
190 {
191     std::string expected = "whatever";
192     std::string str = "whatever,https";
193     EXPECT_TRUE(removeStringFromCSV(str, "https"));
194     EXPECT_EQ(str, expected);
195 
196     str = "https";
197     EXPECT_TRUE(removeStringFromCSV(str, "https"));
198     EXPECT_EQ(str, "");
199 }
200 
201 namespace
202 {
203 inline constexpr const char* objectRootInTest = "/xyz/openbmc_project/user";
204 
205 // Fake configs; referenced configs on real BMC
206 inline constexpr const char* rawConfig = R"(
207 #
208 # /etc/pam.d/common-password - password-related modules common to all services
209 #
210 # This file is included from other service-specific PAM config files,
211 # and should contain a list of modules that define the services to be
212 # used to change user passwords.  The default is pam_unix.
213 
214 # Explanation of pam_unix options:
215 #
216 # The "sha512" option enables salted SHA512 passwords.  Without this option,
217 # the default is Unix crypt.  Prior releases used the option "md5".
218 #
219 # The "obscure" option replaces the old `OBSCURE_CHECKS_ENAB' option in
220 # login.defs.
221 #
222 # See the pam_unix manpage for other options.
223 
224 # here are the per-package modules (the "Primary" block)
225 password	[success=ok default=die]	pam_faillock.so authsucc
226 password	[success=ok default=die]	pam_pwquality.so debug
227 password	[success=ok default=die]	pam_ipmicheck.so spec_grp_name=ipmi use_authtok
228 password	[success=ok ignore=ignore default=die]	pam_pwhistory.so debug enforce_for_root remember=0 use_authtok
229 password	[success=ok default=die]	pam_unix.so sha512 use_authtok
230 password	[success=1 default=die] 	pam_ipmisave.so spec_grp_name=ipmi spec_pass_file=/etc/ipmi_pass key_file=/etc/key_file
231 # here's the fallback if no module succeeds
232 password	requisite			pam_deny.so
233 # prime the stack with a positive return value if there isn't one already;
234 # this avoids us returning an error just because nothing sets a success code
235 # since the modules above will each just jump around
236 password	required			pam_permit.so
237 # and here are more per-package modules (the "Additional" block)
238 )";
239 inline constexpr const char* rawFailLockConfig = R"(
240 deny=2
241 unlock_time=3
242 )";
243 inline constexpr const char* rawPWQualityConfig = R"(
244 enforce_for_root
245 minlen=8
246 difok=0
247 lcredit=0
248 ocredit=0
249 dcredit=0
250 ucredit=0
251 )";
252 } // namespace
253 
254 void dumpStringToFile(const std::string& str, const std::string& filePath)
255 {
256     std::ofstream outputFileStream;
257 
258     outputFileStream.exceptions(std::ofstream::failbit | std::ofstream::badbit |
259                                 std::ofstream::eofbit);
260 
261     outputFileStream.open(filePath, std::ios::out);
262     outputFileStream << str << "\n" << std::flush;
263     outputFileStream.close();
264 }
265 
266 void removeFile(const std::string& filePath)
267 {
268     std::filesystem::remove(filePath);
269 }
270 
271 class UserMgrInTest : public testing::Test, public UserMgr
272 {
273   public:
274     UserMgrInTest() : UserMgr(busInTest, objectRootInTest)
275     {
276         tempPamConfigFile = "/tmp/test-data-XXXXXX";
277         mktemp(tempPamConfigFile.data());
278         EXPECT_NO_THROW(dumpStringToFile(rawConfig, tempPamConfigFile));
279         tempFaillockConfigFile = "/tmp/test-data-XXXXXX";
280         mktemp(tempFaillockConfigFile.data());
281         EXPECT_NO_THROW(
282             dumpStringToFile(rawFailLockConfig, tempFaillockConfigFile));
283         tempPWQualityConfigFile = "/tmp/test-data-XXXXXX";
284         mktemp(tempPWQualityConfigFile.data());
285         EXPECT_NO_THROW(
286             dumpStringToFile(rawPWQualityConfig, tempPWQualityConfigFile));
287         // Set config files to test files
288         pamPasswdConfigFile = tempPamConfigFile;
289         faillockConfigFile = tempFaillockConfigFile;
290         pwQualityConfigFile = tempPWQualityConfigFile;
291 
292         ON_CALL(*this, executeUserAdd).WillByDefault(testing::Return());
293 
294         ON_CALL(*this, executeUserDelete).WillByDefault(testing::Return());
295 
296         ON_CALL(*this, getIpmiUsersCount).WillByDefault(testing::Return(0));
297 
298         ON_CALL(*this, executeUserRename).WillByDefault(testing::Return());
299 
300         ON_CALL(*this, executeUserModify(testing::_, testing::_, testing::_))
301             .WillByDefault(testing::Return());
302 
303         ON_CALL(*this, executeUserModifyUserEnable)
304             .WillByDefault(testing::Return());
305 
306         ON_CALL(*this, executeGroupCreation(testing::_))
307             .WillByDefault(testing::Return());
308 
309         ON_CALL(*this, executeGroupDeletion(testing::_))
310             .WillByDefault(testing::Return());
311 
312         ON_CALL(*this, executeGroupCreation).WillByDefault(testing::Return());
313 
314         ON_CALL(*this, executeGroupDeletion).WillByDefault(testing::Return());
315     }
316 
317     ~UserMgrInTest() override
318     {
319         EXPECT_NO_THROW(removeFile(tempPamConfigFile));
320         EXPECT_NO_THROW(removeFile(tempFaillockConfigFile));
321         EXPECT_NO_THROW(removeFile(tempPWQualityConfigFile));
322     }
323 
324     MOCK_METHOD(void, executeUserAdd, (const char*, const char*, bool, bool),
325                 (override));
326 
327     MOCK_METHOD(void, executeUserDelete, (const char*), (override));
328 
329     MOCK_METHOD(size_t, getIpmiUsersCount, (), (override));
330 
331     MOCK_METHOD(void, executeUserRename, (const char*, const char*),
332                 (override));
333 
334     MOCK_METHOD(void, executeUserModify, (const char*, const char*, bool),
335                 (override));
336 
337     MOCK_METHOD(void, executeUserModifyUserEnable, (const char*, bool),
338                 (override));
339 
340     MOCK_METHOD(std::vector<std::string>, getFailedAttempt, (const char*),
341                 (override));
342 
343     MOCK_METHOD(void, executeGroupCreation, (const char*), (override));
344 
345     MOCK_METHOD(void, executeGroupDeletion, (const char*), (override));
346 
347   protected:
348     static sdbusplus::bus_t busInTest;
349     std::string tempPamConfigFile;
350     std::string tempFaillockConfigFile;
351     std::string tempPWQualityConfigFile;
352 };
353 
354 sdbusplus::bus_t UserMgrInTest::busInTest = sdbusplus::bus::new_default();
355 
356 TEST_F(UserMgrInTest, GetPamModuleArgValueOnSuccess)
357 {
358     std::string remember;
359     EXPECT_EQ(getPamModuleArgValue("pam_pwhistory.so", "remember", remember),
360               0);
361     EXPECT_EQ(remember, "0");
362 }
363 
364 TEST_F(UserMgrInTest, GetPamModuleConfValueOnSuccess)
365 {
366     std::string minlen;
367     EXPECT_EQ(getPamModuleConfValue(tempPWQualityConfigFile, "minlen", minlen),
368               0);
369     EXPECT_EQ(minlen, "8");
370     std::string deny;
371     EXPECT_EQ(getPamModuleConfValue(tempFaillockConfigFile, "deny", deny), 0);
372     EXPECT_EQ(deny, "2");
373 }
374 
375 TEST_F(UserMgrInTest, SetPamModuleArgValueOnSuccess)
376 {
377     EXPECT_EQ(setPamModuleArgValue("pam_pwhistory.so", "remember", "1"), 0);
378     std::string remember;
379     EXPECT_EQ(getPamModuleArgValue("pam_pwhistory.so", "remember", remember),
380               0);
381     EXPECT_EQ(remember, "1");
382 }
383 
384 TEST_F(UserMgrInTest, SetPamModuleConfValueOnSuccess)
385 {
386     EXPECT_EQ(setPamModuleConfValue(tempPWQualityConfigFile, "minlen", "16"),
387               0);
388     std::string minlen;
389     EXPECT_EQ(getPamModuleConfValue(tempPWQualityConfigFile, "minlen", minlen),
390               0);
391     EXPECT_EQ(minlen, "16");
392 
393     EXPECT_EQ(setPamModuleConfValue(tempFaillockConfigFile, "deny", "3"), 0);
394     std::string deny;
395     EXPECT_EQ(getPamModuleConfValue(tempFaillockConfigFile, "deny", deny), 0);
396     EXPECT_EQ(deny, "3");
397 }
398 
399 TEST_F(UserMgrInTest, GetPamModuleArgValueOnFailure)
400 {
401     EXPECT_NO_THROW(dumpStringToFile("whatever", tempPamConfigFile));
402     std::string remember;
403     EXPECT_EQ(getPamModuleArgValue("pam_pwhistory.so", "remember", remember),
404               -1);
405 
406     EXPECT_NO_THROW(removeFile(tempPamConfigFile));
407     EXPECT_EQ(getPamModuleArgValue("pam_pwhistory.so", "remember", remember),
408               -1);
409 }
410 
411 TEST_F(UserMgrInTest, GetPamModuleConfValueOnFailure)
412 {
413     EXPECT_NO_THROW(dumpStringToFile("whatever", tempPWQualityConfigFile));
414     std::string minlen;
415     EXPECT_EQ(getPamModuleConfValue(tempPWQualityConfigFile, "minlen", minlen),
416               -1);
417 
418     EXPECT_NO_THROW(removeFile(tempPWQualityConfigFile));
419     EXPECT_EQ(getPamModuleConfValue(tempPWQualityConfigFile, "minlen", minlen),
420               -1);
421 
422     EXPECT_NO_THROW(dumpStringToFile("whatever", tempFaillockConfigFile));
423     std::string deny;
424     EXPECT_EQ(getPamModuleConfValue(tempFaillockConfigFile, "deny", deny), -1);
425 
426     EXPECT_NO_THROW(removeFile(tempFaillockConfigFile));
427     EXPECT_EQ(getPamModuleConfValue(tempFaillockConfigFile, "deny", deny), -1);
428 }
429 
430 TEST_F(UserMgrInTest, SetPamModuleArgValueOnFailure)
431 {
432     EXPECT_NO_THROW(dumpStringToFile("whatever", tempPamConfigFile));
433     EXPECT_EQ(setPamModuleArgValue("pam_pwhistory.so", "remember", "1"), -1);
434 
435     EXPECT_NO_THROW(removeFile(tempPamConfigFile));
436     EXPECT_EQ(setPamModuleArgValue("pam_pwhistory.so", "remember", "1"), -1);
437 }
438 
439 TEST_F(UserMgrInTest, SetPamModuleConfValueOnFailure)
440 {
441     EXPECT_NO_THROW(dumpStringToFile("whatever", tempPWQualityConfigFile));
442     EXPECT_EQ(setPamModuleConfValue(tempPWQualityConfigFile, "minlen", "16"),
443               -1);
444 
445     EXPECT_NO_THROW(removeFile(tempPWQualityConfigFile));
446     EXPECT_EQ(setPamModuleConfValue(tempPWQualityConfigFile, "minlen", "16"),
447               -1);
448 
449     EXPECT_NO_THROW(dumpStringToFile("whatever", tempFaillockConfigFile));
450     EXPECT_EQ(setPamModuleConfValue(tempFaillockConfigFile, "deny", "3"), -1);
451 
452     EXPECT_NO_THROW(removeFile(tempFaillockConfigFile));
453     EXPECT_EQ(setPamModuleConfValue(tempFaillockConfigFile, "deny", "3"), -1);
454 }
455 
456 TEST_F(UserMgrInTest, IsUserExistEmptyInputThrowsError)
457 {
458     EXPECT_THROW(
459         isUserExist(""),
460         sdbusplus::xyz::openbmc_project::Common::Error::InvalidArgument);
461 }
462 
463 TEST_F(UserMgrInTest, ThrowForUserDoesNotExistThrowsError)
464 {
465     EXPECT_THROW(throwForUserDoesNotExist("whatever"),
466                  sdbusplus::xyz::openbmc_project::User::Common::Error::
467                      UserNameDoesNotExist);
468 }
469 
470 TEST_F(UserMgrInTest, ThrowForUserExistsThrowsError)
471 {
472     EXPECT_THROW(
473         throwForUserExists("root"),
474         sdbusplus::xyz::openbmc_project::User::Common::Error::UserNameExists);
475 }
476 
477 TEST_F(
478     UserMgrInTest,
479     ThrowForUserNameConstraintsExceedIpmiMaxUserNameLenThrowsUserNameGroupFail)
480 {
481     std::string strWith17Chars(17, 'A');
482     EXPECT_THROW(throwForUserNameConstraints(strWith17Chars, {"ipmi"}),
483                  sdbusplus::xyz::openbmc_project::User::Common::Error::
484                      UserNameGroupFail);
485 }
486 
487 TEST_F(
488     UserMgrInTest,
489     ThrowForUserNameConstraintsExceedSystemMaxUserNameLenThrowsInvalidArgument)
490 {
491     std::string strWith31Chars(31, 'A');
492     EXPECT_THROW(
493         throwForUserNameConstraints(strWith31Chars, {}),
494         sdbusplus::xyz::openbmc_project::Common::Error::InvalidArgument);
495 }
496 
497 TEST_F(UserMgrInTest,
498        ThrowForUserNameConstraintsRegexMismatchThrowsInvalidArgument)
499 {
500     std::string startWithNumber = "0ABC";
501     EXPECT_THROW(
502         throwForUserNameConstraints(startWithNumber, {"ipmi"}),
503         sdbusplus::xyz::openbmc_project::Common::Error::InvalidArgument);
504 }
505 
506 TEST_F(UserMgrInTest, UserAddNotRootFailedWithInternalFailure)
507 {
508     EXPECT_THROW(
509         UserMgr::executeUserAdd("user0", "ipmi,ssh", true, true),
510         sdbusplus::xyz::openbmc_project::Common::Error::InternalFailure);
511 }
512 
513 TEST_F(UserMgrInTest, UserDeleteNotRootFailedWithInternalFailure)
514 {
515     EXPECT_THROW(
516         UserMgr::executeUserDelete("user0"),
517         sdbusplus::xyz::openbmc_project::Common::Error::InternalFailure);
518 }
519 
520 TEST_F(UserMgrInTest,
521        ThrowForMaxGrpUserCountThrowsNoResourceWhenIpmiUserExceedLimit)
522 {
523     EXPECT_CALL(*this, getIpmiUsersCount()).WillOnce(Return(ipmiMaxUsers));
524     EXPECT_THROW(
525         throwForMaxGrpUserCount({"ipmi"}),
526         sdbusplus::xyz::openbmc_project::User::Common::Error::NoResource);
527 }
528 
529 TEST_F(UserMgrInTest, CreateUserThrowsInternalFailureWhenExecuteUserAddFails)
530 {
531     EXPECT_CALL(*this, executeUserAdd)
532         .WillOnce(testing::Throw(
533             sdbusplus::xyz::openbmc_project::Common::Error::InternalFailure()));
534     EXPECT_THROW(
535         createUser("whatever", {"redfish"}, "", true),
536         sdbusplus::xyz::openbmc_project::Common::Error::InternalFailure);
537 }
538 
539 TEST_F(UserMgrInTest, DeleteUserThrowsInternalFailureWhenExecuteUserDeleteFails)
540 {
541     std::string username = "user";
542     EXPECT_NO_THROW(
543         UserMgr::createUser(username, {"redfish", "ssh"}, "priv-user", true));
544     EXPECT_CALL(*this, executeUserDelete(testing::StrEq(username)))
545         .WillOnce(testing::Throw(
546             sdbusplus::xyz::openbmc_project::Common::Error::InternalFailure()))
547         .WillOnce(testing::DoDefault());
548 
549     EXPECT_THROW(
550         deleteUser(username),
551         sdbusplus::xyz::openbmc_project::Common::Error::InternalFailure);
552     EXPECT_NO_THROW(UserMgr::deleteUser(username));
553 }
554 
555 TEST_F(UserMgrInTest, ThrowForInvalidPrivilegeThrowsWhenPrivilegeIsInvalid)
556 {
557     EXPECT_THROW(
558         throwForInvalidPrivilege("whatever"),
559         sdbusplus::xyz::openbmc_project::Common::Error::InvalidArgument);
560 }
561 
562 TEST_F(UserMgrInTest, ThrowForInvalidPrivilegeNoThrowWhenPrivilegeIsValid)
563 {
564     EXPECT_NO_THROW(throwForInvalidPrivilege("priv-admin"));
565     EXPECT_NO_THROW(throwForInvalidPrivilege("priv-operator"));
566     EXPECT_NO_THROW(throwForInvalidPrivilege("priv-user"));
567 }
568 
569 TEST_F(UserMgrInTest, ThrowForInvalidGroupsThrowsWhenGroupIsInvalid)
570 {
571     EXPECT_THROW(
572         throwForInvalidGroups({"whatever"}),
573         sdbusplus::xyz::openbmc_project::Common::Error::InvalidArgument);
574 }
575 
576 TEST_F(UserMgrInTest, ThrowForInvalidGroupsNoThrowWhenGroupIsValid)
577 {
578     EXPECT_NO_THROW(throwForInvalidGroups({"ipmi"}));
579     EXPECT_NO_THROW(throwForInvalidGroups({"ssh"}));
580     EXPECT_NO_THROW(throwForInvalidGroups({"redfish"}));
581     EXPECT_NO_THROW(throwForInvalidGroups({"web"}));
582 }
583 
584 TEST_F(UserMgrInTest, RenameUserOnSuccess)
585 {
586     std::string username = "user001";
587     EXPECT_NO_THROW(
588         UserMgr::createUser(username, {"redfish", "ssh"}, "priv-user", true));
589     std::string newUsername = "user002";
590 
591     EXPECT_NO_THROW(UserMgr::renameUser(username, newUsername));
592 
593     // old username doesn't exist
594     EXPECT_THROW(getUserInfo(username),
595                  sdbusplus::xyz::openbmc_project::User::Common::Error::
596                      UserNameDoesNotExist);
597 
598     UserInfoMap userInfo = getUserInfo(newUsername);
599     EXPECT_EQ(std::get<Privilege>(userInfo["UserPrivilege"]), "priv-user");
600     EXPECT_THAT(std::get<GroupList>(userInfo["UserGroups"]),
601                 testing::UnorderedElementsAre("redfish", "ssh"));
602     EXPECT_EQ(std::get<UserEnabled>(userInfo["UserEnabled"]), true);
603 
604     EXPECT_NO_THROW(UserMgr::deleteUser(newUsername));
605 }
606 
607 TEST_F(UserMgrInTest, RenameUserThrowsInternalFailureIfExecuteUserModifyFails)
608 {
609     std::string username = "user001";
610     EXPECT_NO_THROW(
611         UserMgr::createUser(username, {"redfish", "ssh"}, "priv-user", true));
612     std::string newUsername = "user002";
613 
614     EXPECT_CALL(*this, executeUserRename(testing::StrEq(username),
615                                          testing::StrEq(newUsername)))
616         .WillOnce(testing::Throw(
617             sdbusplus::xyz::openbmc_project::Common::Error::InternalFailure()));
618     EXPECT_THROW(
619         UserMgr::renameUser(username, newUsername),
620         sdbusplus::xyz::openbmc_project::Common::Error::InternalFailure);
621 
622     // The original user is unchanged
623     UserInfoMap userInfo = getUserInfo(username);
624     EXPECT_EQ(std::get<Privilege>(userInfo["UserPrivilege"]), "priv-user");
625     EXPECT_THAT(std::get<GroupList>(userInfo["UserGroups"]),
626                 testing::UnorderedElementsAre("redfish", "ssh"));
627     EXPECT_EQ(std::get<UserEnabled>(userInfo["UserEnabled"]), true);
628 
629     EXPECT_NO_THROW(UserMgr::deleteUser(username));
630 }
631 
632 TEST_F(UserMgrInTest, DefaultUserModifyFailedWithInternalFailure)
633 {
634     EXPECT_THROW(
635         UserMgr::executeUserRename("user0", "user1"),
636         sdbusplus::xyz::openbmc_project::Common::Error::InternalFailure);
637     EXPECT_THROW(
638         UserMgr::executeUserModify("user0", "ssh", true),
639         sdbusplus::xyz::openbmc_project::Common::Error::InternalFailure);
640 }
641 
642 TEST_F(UserMgrInTest, UpdateGroupsAndPrivOnSuccess)
643 {
644     std::string username = "user001";
645     EXPECT_NO_THROW(
646         UserMgr::createUser(username, {"redfish", "ssh"}, "priv-user", true));
647     EXPECT_NO_THROW(
648         updateGroupsAndPriv(username, {"ipmi", "ssh"}, "priv-admin"));
649     UserInfoMap userInfo = getUserInfo(username);
650     EXPECT_EQ(std::get<Privilege>(userInfo["UserPrivilege"]), "priv-admin");
651     EXPECT_THAT(std::get<GroupList>(userInfo["UserGroups"]),
652                 testing::UnorderedElementsAre("ipmi", "ssh"));
653     EXPECT_EQ(std::get<UserEnabled>(userInfo["UserEnabled"]), true);
654     EXPECT_NO_THROW(UserMgr::deleteUser(username));
655 }
656 
657 TEST_F(UserMgrInTest,
658        UpdateGroupsAndPrivThrowsInternalFailureIfExecuteUserModifyFail)
659 {
660     std::string username = "user001";
661     EXPECT_NO_THROW(
662         UserMgr::createUser(username, {"redfish", "ssh"}, "priv-user", true));
663     EXPECT_CALL(*this, executeUserModify(testing::StrEq(username), testing::_,
664                                          testing::_))
665         .WillOnce(testing::Throw(
666             sdbusplus::xyz::openbmc_project::Common::Error::InternalFailure()));
667     EXPECT_THROW(
668         updateGroupsAndPriv(username, {"ipmi", "ssh"}, "priv-admin"),
669         sdbusplus::xyz::openbmc_project::Common::Error::InternalFailure);
670     EXPECT_NO_THROW(UserMgr::deleteUser(username));
671 }
672 
673 TEST_F(UserMgrInTest, MinPasswordLengthReturnsIfValueIsTheSame)
674 {
675     initializeAccountPolicy();
676     EXPECT_EQ(AccountPolicyIface::minPasswordLength(), 8);
677     UserMgr::minPasswordLength(8);
678     EXPECT_EQ(AccountPolicyIface::minPasswordLength(), 8);
679 }
680 
681 TEST_F(UserMgrInTest,
682        MinPasswordLengthRejectsTooShortPasswordWithInvalidArgument)
683 {
684     initializeAccountPolicy();
685     EXPECT_EQ(AccountPolicyIface::minPasswordLength(), 8);
686     EXPECT_THROW(
687         UserMgr::minPasswordLength(minPasswdLength - 1),
688         sdbusplus::xyz::openbmc_project::Common::Error::InvalidArgument);
689     EXPECT_EQ(AccountPolicyIface::minPasswordLength(), 8);
690 }
691 
692 TEST_F(UserMgrInTest, MinPasswordLengthOnSuccess)
693 {
694     initializeAccountPolicy();
695     EXPECT_EQ(AccountPolicyIface::minPasswordLength(), 8);
696     UserMgr::minPasswordLength(16);
697     EXPECT_EQ(AccountPolicyIface::minPasswordLength(), 16);
698 }
699 
700 TEST_F(UserMgrInTest, MinPasswordLengthOnFailure)
701 {
702     EXPECT_NO_THROW(dumpStringToFile("whatever", tempPWQualityConfigFile));
703     initializeAccountPolicy();
704     EXPECT_EQ(AccountPolicyIface::minPasswordLength(), 8);
705     EXPECT_THROW(
706         UserMgr::minPasswordLength(16),
707         sdbusplus::xyz::openbmc_project::Common::Error::InternalFailure);
708     EXPECT_EQ(AccountPolicyIface::minPasswordLength(), 8);
709 }
710 
711 TEST_F(UserMgrInTest, RememberOldPasswordTimesReturnsIfValueIsTheSame)
712 {
713     initializeAccountPolicy();
714     EXPECT_EQ(AccountPolicyIface::rememberOldPasswordTimes(), 0);
715     UserMgr::rememberOldPasswordTimes(8);
716     EXPECT_EQ(AccountPolicyIface::rememberOldPasswordTimes(), 8);
717     UserMgr::rememberOldPasswordTimes(8);
718     EXPECT_EQ(AccountPolicyIface::rememberOldPasswordTimes(), 8);
719 }
720 
721 TEST_F(UserMgrInTest, RememberOldPasswordTimesOnSuccess)
722 {
723     initializeAccountPolicy();
724     EXPECT_EQ(AccountPolicyIface::rememberOldPasswordTimes(), 0);
725     UserMgr::rememberOldPasswordTimes(16);
726     EXPECT_EQ(AccountPolicyIface::rememberOldPasswordTimes(), 16);
727 }
728 
729 TEST_F(UserMgrInTest, RememberOldPasswordTimesOnFailure)
730 {
731     EXPECT_NO_THROW(dumpStringToFile("whatever", tempPamConfigFile));
732     initializeAccountPolicy();
733     EXPECT_EQ(AccountPolicyIface::rememberOldPasswordTimes(), 0);
734     EXPECT_THROW(
735         UserMgr::rememberOldPasswordTimes(16),
736         sdbusplus::xyz::openbmc_project::Common::Error::InternalFailure);
737     EXPECT_EQ(AccountPolicyIface::rememberOldPasswordTimes(), 0);
738 }
739 
740 TEST_F(UserMgrInTest, MaxLoginAttemptBeforeLockoutReturnsIfValueIsTheSame)
741 {
742     initializeAccountPolicy();
743     EXPECT_EQ(AccountPolicyIface::maxLoginAttemptBeforeLockout(), 2);
744     UserMgr::maxLoginAttemptBeforeLockout(2);
745     EXPECT_EQ(AccountPolicyIface::maxLoginAttemptBeforeLockout(), 2);
746 }
747 
748 TEST_F(UserMgrInTest, MaxLoginAttemptBeforeLockoutOnSuccess)
749 {
750     initializeAccountPolicy();
751     EXPECT_EQ(AccountPolicyIface::maxLoginAttemptBeforeLockout(), 2);
752     UserMgr::maxLoginAttemptBeforeLockout(16);
753     EXPECT_EQ(AccountPolicyIface::maxLoginAttemptBeforeLockout(), 16);
754 }
755 
756 TEST_F(UserMgrInTest, MaxLoginAttemptBeforeLockoutOnFailure)
757 {
758     initializeAccountPolicy();
759     EXPECT_NO_THROW(dumpStringToFile("whatever", tempFaillockConfigFile));
760     EXPECT_THROW(
761         UserMgr::maxLoginAttemptBeforeLockout(16),
762         sdbusplus::xyz::openbmc_project::Common::Error::InternalFailure);
763     EXPECT_EQ(AccountPolicyIface::maxLoginAttemptBeforeLockout(), 2);
764 }
765 
766 TEST_F(UserMgrInTest, AccountUnlockTimeoutReturnsIfValueIsTheSame)
767 {
768     initializeAccountPolicy();
769     EXPECT_EQ(AccountPolicyIface::accountUnlockTimeout(), 3);
770     UserMgr::accountUnlockTimeout(3);
771     EXPECT_EQ(AccountPolicyIface::accountUnlockTimeout(), 3);
772 }
773 
774 TEST_F(UserMgrInTest, AccountUnlockTimeoutOnSuccess)
775 {
776     initializeAccountPolicy();
777     EXPECT_EQ(AccountPolicyIface::accountUnlockTimeout(), 3);
778     UserMgr::accountUnlockTimeout(16);
779     EXPECT_EQ(AccountPolicyIface::accountUnlockTimeout(), 16);
780 }
781 
782 TEST_F(UserMgrInTest, AccountUnlockTimeoutOnFailure)
783 {
784     initializeAccountPolicy();
785     EXPECT_NO_THROW(dumpStringToFile("whatever", tempFaillockConfigFile));
786     EXPECT_THROW(
787         UserMgr::accountUnlockTimeout(16),
788         sdbusplus::xyz::openbmc_project::Common::Error::InternalFailure);
789     EXPECT_EQ(AccountPolicyIface::accountUnlockTimeout(), 3);
790 }
791 
792 TEST_F(UserMgrInTest, UserEnableOnSuccess)
793 {
794     std::string username = "user001";
795     EXPECT_NO_THROW(
796         UserMgr::createUser(username, {"redfish", "ssh"}, "priv-user", true));
797     UserInfoMap userInfo = getUserInfo(username);
798     EXPECT_EQ(std::get<UserEnabled>(userInfo["UserEnabled"]), true);
799 
800     EXPECT_NO_THROW(userEnable(username, false));
801 
802     userInfo = getUserInfo(username);
803     EXPECT_EQ(std::get<UserEnabled>(userInfo["UserEnabled"]), false);
804 
805     EXPECT_NO_THROW(UserMgr::deleteUser(username));
806 }
807 
808 TEST_F(UserMgrInTest, UserEnableThrowsInternalFailureIfExecuteUserModifyFail)
809 {
810     std::string username = "user001";
811     EXPECT_NO_THROW(
812         UserMgr::createUser(username, {"redfish", "ssh"}, "priv-user", true));
813     UserInfoMap userInfo = getUserInfo(username);
814     EXPECT_EQ(std::get<UserEnabled>(userInfo["UserEnabled"]), true);
815 
816     EXPECT_CALL(*this, executeUserModifyUserEnable(testing::StrEq(username),
817                                                    testing::Eq(false)))
818         .WillOnce(testing::Throw(
819             sdbusplus::xyz::openbmc_project::Common::Error::InternalFailure()));
820     EXPECT_THROW(
821         userEnable(username, false),
822         sdbusplus::xyz::openbmc_project::Common::Error::InternalFailure);
823 
824     userInfo = getUserInfo(username);
825     // Stay unchanged
826     EXPECT_EQ(std::get<UserEnabled>(userInfo["UserEnabled"]), true);
827 
828     EXPECT_NO_THROW(UserMgr::deleteUser(username));
829 }
830 
831 TEST_F(
832     UserMgrInTest,
833     UserLockedForFailedAttemptReturnsFalseIfMaxLoginAttemptBeforeLockoutIsZero)
834 {
835     EXPECT_FALSE(userLockedForFailedAttempt("whatever"));
836 }
837 
838 TEST_F(UserMgrInTest, UserLockedForFailedAttemptZeroFailuresReturnsFalse)
839 {
840     std::string username = "user001";
841     initializeAccountPolicy();
842     // Example output from BMC
843     // root:~# faillock --user root
844     // root:
845     // When   Type   Source   Valid
846     std::vector<std::string> output = {"whatever",
847                                        "When   Type   Source   Valid"};
848     EXPECT_CALL(*this, getFailedAttempt(testing::StrEq(username.c_str())))
849         .WillOnce(testing::Return(output));
850 
851     EXPECT_FALSE(userLockedForFailedAttempt(username));
852 }
853 
854 TEST_F(UserMgrInTest, UserLockedForFailedAttemptFailIfGetFailedAttemptFail)
855 {
856     std::string username = "user001";
857     initializeAccountPolicy();
858     EXPECT_CALL(*this, getFailedAttempt(testing::StrEq(username.c_str())))
859         .WillOnce(testing::Throw(
860             sdbusplus::xyz::openbmc_project::Common::Error::InternalFailure()));
861 
862     EXPECT_THROW(
863         userLockedForFailedAttempt(username),
864         sdbusplus::xyz::openbmc_project::Common::Error::InternalFailure);
865 }
866 
867 TEST_F(UserMgrInTest,
868        UserLockedForFailedAttemptThrowsInternalFailureIfWrongDateFormat)
869 {
870     std::string username = "user001";
871     initializeAccountPolicy();
872 
873     // Choose a date in the past.
874     std::vector<std::string> output = {"whatever",
875                                        "10/24/2002 00:00:00 type source V"};
876     EXPECT_CALL(*this, getFailedAttempt(testing::StrEq(username.c_str())))
877         .WillOnce(testing::Return(output));
878 
879     EXPECT_THROW(
880         userLockedForFailedAttempt(username),
881         sdbusplus::xyz::openbmc_project::Common::Error::InternalFailure);
882 }
883 
884 TEST_F(UserMgrInTest,
885        UserLockedForFailedAttemptReturnsFalseIfLastFailTimeHasTimedOut)
886 {
887     std::string username = "user001";
888     initializeAccountPolicy();
889 
890     // Choose a date in the past.
891     std::vector<std::string> output = {"whatever",
892                                        "2002-10-24 00:00:00 type source V"};
893     EXPECT_CALL(*this, getFailedAttempt(testing::StrEq(username.c_str())))
894         .WillOnce(testing::Return(output));
895 
896     EXPECT_EQ(userLockedForFailedAttempt(username), false);
897 }
898 
899 TEST_F(UserMgrInTest, CheckAndThrowForDisallowedGroupCreationOnSuccess)
900 {
901     // Base Redfish Roles
902     EXPECT_NO_THROW(
903         checkAndThrowForDisallowedGroupCreation("openbmc_rfr_Administrator"));
904     EXPECT_NO_THROW(
905         checkAndThrowForDisallowedGroupCreation("openbmc_rfr_Operator"));
906     EXPECT_NO_THROW(
907         checkAndThrowForDisallowedGroupCreation("openbmc_rfr_ReadOnly"));
908     // Base Redfish Privileges
909     EXPECT_NO_THROW(
910         checkAndThrowForDisallowedGroupCreation("openbmc_rfp_Login"));
911     EXPECT_NO_THROW(checkAndThrowForDisallowedGroupCreation(
912         "openbmc_rfp_ConfigureManager"));
913     EXPECT_NO_THROW(
914         checkAndThrowForDisallowedGroupCreation("openbmc_rfp_ConfigureUsers"));
915     EXPECT_NO_THROW(
916         checkAndThrowForDisallowedGroupCreation("openbmc_rfp_ConfigureSelf"));
917     EXPECT_NO_THROW(checkAndThrowForDisallowedGroupCreation(
918         "openbmc_rfp_ConfigureComponents"));
919     // OEM Redfish Roles
920     EXPECT_NO_THROW(
921         checkAndThrowForDisallowedGroupCreation("openbmc_orfr_PowerService"));
922     // OEM Redfish Privileges
923     EXPECT_NO_THROW(
924         checkAndThrowForDisallowedGroupCreation("openbmc_orfp_PowerService"));
925 }
926 
927 TEST_F(UserMgrInTest,
928        CheckAndThrowForDisallowedGroupCreationThrowsIfGroupNameTooLong)
929 {
930     std::string groupName(maxSystemGroupNameLength + 1, 'A');
931     EXPECT_THROW(
932         checkAndThrowForDisallowedGroupCreation(groupName),
933         sdbusplus::xyz::openbmc_project::Common::Error::InvalidArgument);
934 }
935 
936 TEST_F(
937     UserMgrInTest,
938     CheckAndThrowForDisallowedGroupCreationThrowsIfGroupNameHasDisallowedCharacters)
939 {
940     EXPECT_THROW(
941         checkAndThrowForDisallowedGroupCreation("openbmc_rfp_?owerService"),
942         sdbusplus::xyz::openbmc_project::Common::Error::InvalidArgument);
943     EXPECT_THROW(
944         checkAndThrowForDisallowedGroupCreation("openbmc_rfp_-owerService"),
945         sdbusplus::xyz::openbmc_project::Common::Error::InvalidArgument);
946 }
947 
948 TEST_F(
949     UserMgrInTest,
950     CheckAndThrowForDisallowedGroupCreationThrowsIfGroupNameHasDisallowedPrefix)
951 {
952     EXPECT_THROW(
953         checkAndThrowForDisallowedGroupCreation("google_rfp_"),
954         sdbusplus::xyz::openbmc_project::Common::Error::InvalidArgument);
955     EXPECT_THROW(
956         checkAndThrowForDisallowedGroupCreation("com_rfp_"),
957         sdbusplus::xyz::openbmc_project::Common::Error::InvalidArgument);
958 }
959 
960 TEST_F(UserMgrInTest, CheckAndThrowForMaxGroupCountOnSuccess)
961 {
962     EXPECT_THAT(allGroups().size(), 4);
963     for (size_t i = 0; i < maxSystemGroupCount - 4; ++i)
964     {
965         std::string groupName = "openbmc_rfr_role";
966         groupName += std::to_string(i);
967         EXPECT_NO_THROW(createGroup(groupName));
968     }
969     EXPECT_THROW(
970         createGroup("openbmc_rfr_AnotherRole"),
971         sdbusplus::xyz::openbmc_project::User::Common::Error::NoResource);
972     for (size_t i = 0; i < maxSystemGroupCount - 4; ++i)
973     {
974         std::string groupName = "openbmc_rfr_role";
975         groupName += std::to_string(i);
976         EXPECT_NO_THROW(deleteGroup(groupName));
977     }
978 }
979 
980 TEST_F(UserMgrInTest, CheckAndThrowForGroupExist)
981 {
982     std::string groupName = "openbmc_rfr_role";
983     EXPECT_NO_THROW(createGroup(groupName));
984     EXPECT_THROW(
985         createGroup(groupName),
986         sdbusplus::xyz::openbmc_project::User::Common::Error::GroupNameExists);
987     EXPECT_NO_THROW(deleteGroup(groupName));
988 }
989 
990 TEST_F(UserMgrInTest, ByDefaultAllGroupsArePredefinedGroups)
991 {
992     EXPECT_THAT(allGroups(),
993                 testing::UnorderedElementsAre("web", "redfish", "ipmi", "ssh"));
994 }
995 
996 TEST_F(UserMgrInTest, DeleteGroupThrowsIfGroupIsNotAllowedToChange)
997 {
998     EXPECT_THROW(
999         deleteGroup("ipmi"),
1000         sdbusplus::xyz::openbmc_project::Common::Error::InvalidArgument);
1001     EXPECT_THROW(
1002         deleteGroup("web"),
1003         sdbusplus::xyz::openbmc_project::Common::Error::InvalidArgument);
1004     EXPECT_THROW(
1005         deleteGroup("redfish"),
1006         sdbusplus::xyz::openbmc_project::Common::Error::InvalidArgument);
1007     EXPECT_THROW(
1008         deleteGroup("ssh"),
1009         sdbusplus::xyz::openbmc_project::Common::Error::InvalidArgument);
1010 }
1011 
1012 TEST_F(UserMgrInTest,
1013        CreateGroupThrowsInternalFailureWhenExecuteGroupCreateFails)
1014 {
1015     EXPECT_CALL(*this, executeGroupCreation)
1016         .WillOnce(testing::Throw(
1017             sdbusplus::xyz::openbmc_project::Common::Error::InternalFailure()));
1018     EXPECT_THROW(
1019         createGroup("openbmc_rfr_role1"),
1020         sdbusplus::xyz::openbmc_project::Common::Error::InternalFailure);
1021 }
1022 
1023 TEST_F(UserMgrInTest,
1024        DeleteGroupThrowsInternalFailureWhenExecuteGroupDeleteFails)
1025 {
1026     std::string groupName = "openbmc_rfr_role1";
1027     EXPECT_NO_THROW(UserMgr::createGroup(groupName));
1028     EXPECT_CALL(*this, executeGroupDeletion(testing::StrEq(groupName)))
1029         .WillOnce(testing::Throw(
1030             sdbusplus::xyz::openbmc_project::Common::Error::InternalFailure()))
1031         .WillOnce(testing::DoDefault());
1032 
1033     EXPECT_THROW(
1034         deleteGroup(groupName),
1035         sdbusplus::xyz::openbmc_project::Common::Error::InternalFailure);
1036     EXPECT_NO_THROW(UserMgr::deleteGroup(groupName));
1037 }
1038 
1039 TEST_F(UserMgrInTest, CheckAndThrowForGroupNotExist)
1040 {
1041     EXPECT_THROW(deleteGroup("whatever"),
1042                  sdbusplus::xyz::openbmc_project::User::Common::Error::
1043                      GroupNameDoesNotExist);
1044 }
1045 
1046 TEST(ReadAllGroupsOnSystemTest, OnlyReturnsPredefinedGroups)
1047 {
1048     EXPECT_THAT(UserMgr::readAllGroupsOnSystem(),
1049                 testing::UnorderedElementsAre("web", "redfish", "ipmi", "ssh"));
1050 }
1051 
1052 } // namespace user
1053 } // namespace phosphor
1054