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