blob: 5db642e94677ad36f87c4c1fe9b74760773900d7 [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;
Alexander Filippov75626582022-02-09 18:42:37 +030020using ::testing::Throw;
raviteja-b8cc44052019-02-27 23:29:36 -060021
22using InternalFailure =
23 sdbusplus::xyz::openbmc_project::Common::Error::InternalFailure;
Alexander Filippov75626582022-02-09 18:42:37 +030024using UserNameDoesNotExist =
25 sdbusplus::xyz::openbmc_project::User::Common::Error::UserNameDoesNotExist;
raviteja-b8cc44052019-02-27 23:29:36 -060026
27class TestUserMgr : public testing::Test
28{
29 public:
Nan Zhou78d85042022-08-29 17:50:22 +000030 sdbusplus::SdBusMock sdBusMock;
Patrick Williamsb3ef4e12022-07-22 19:26:55 -050031 sdbusplus::bus_t bus;
raviteja-b8cc44052019-02-27 23:29:36 -060032 MockManager mockManager;
33
34 TestUserMgr() :
Nan Zhou78d85042022-08-29 17:50:22 +000035 bus(sdbusplus::get_mocked_new(&sdBusMock)), mockManager(bus, objpath)
Patrick Williams9638afb2021-02-22 17:16:24 -060036 {}
raviteja-b8cc44052019-02-27 23:29:36 -060037
Patrick Williams9638afb2021-02-22 17:16:24 -060038 void createLocalUser(const std::string& userName,
raviteja-b8cc44052019-02-27 23:29:36 -060039 std::vector<std::string> groupNames,
Patrick Williams9638afb2021-02-22 17:16:24 -060040 const std::string& priv, bool enabled)
raviteja-b8cc44052019-02-27 23:29:36 -060041 {
P Dheeraj Srujan Kumarb01e2fe2021-12-13 09:43:28 +053042 sdbusplus::message::object_path tempObjPath(usersObjPath);
43 tempObjPath /= userName;
44 std::string userObj(tempObjPath);
raviteja-b8cc44052019-02-27 23:29:36 -060045 mockManager.usersList.emplace(
Nan Zhou78d85042022-08-29 17:50:22 +000046 userName, std::make_unique<phosphor::user::Users>(
raviteja-b8cc44052019-02-27 23:29:36 -060047 mockManager.bus, userObj.c_str(), groupNames, priv,
Nan Zhou78d85042022-08-29 17:50:22 +000048 enabled, mockManager));
raviteja-b8cc44052019-02-27 23:29:36 -060049 }
50
51 DbusUserObj createPrivilegeMapperDbusObject(void)
52 {
53 DbusUserObj object;
54 DbusUserObjValue objValue;
Ravi Teja5fe724a2019-05-07 05:14:42 -050055
Nan Zhou78d85042022-08-29 17:50:22 +000056 DbusUserObjPath objPath("/xyz/openbmc_project/user/ldap/openldap");
Ravi Teja5fe724a2019-05-07 05:14:42 -050057 DbusUserPropVariant enabled(true);
58 DbusUserObjProperties property = {std::make_pair("Enabled", enabled)};
59 std::string intf = "xyz.openbmc_project.Object.Enable";
60 objValue.emplace(intf, property);
Nan Zhou78d85042022-08-29 17:50:22 +000061 object.emplace(objPath, objValue);
Ravi Teja5fe724a2019-05-07 05:14:42 -050062
Nan Zhou78d85042022-08-29 17:50:22 +000063 DbusUserObjPath objectPath(
Ravi Teja5fe724a2019-05-07 05:14:42 -050064 "/xyz/openbmc_project/user/ldap/openldap/role_map/1");
65 std::string group = "ldapGroup";
66 std::string priv = "priv-admin";
raviteja-b8cc44052019-02-27 23:29:36 -060067 DbusUserObjProperties properties = {std::make_pair("GroupName", group),
68 std::make_pair("Privilege", priv)};
69 std::string interface = "xyz.openbmc_project.User.PrivilegeMapperEntry";
70
71 objValue.emplace(interface, properties);
Nan Zhou78d85042022-08-29 17:50:22 +000072 object.emplace(objectPath, objValue);
raviteja-b8cc44052019-02-27 23:29:36 -060073
74 return object;
75 }
Ravi Teja5fe724a2019-05-07 05:14:42 -050076
77 DbusUserObj createLdapConfigObjectWithoutPrivilegeMapper(void)
78 {
79 DbusUserObj object;
80 DbusUserObjValue objValue;
81
Nan Zhou78d85042022-08-29 17:50:22 +000082 DbusUserObjPath objPath("/xyz/openbmc_project/user/ldap/openldap");
Ravi Teja5fe724a2019-05-07 05:14:42 -050083 DbusUserPropVariant enabled(true);
84 DbusUserObjProperties property = {std::make_pair("Enabled", enabled)};
85 std::string intf = "xyz.openbmc_project.Object.Enable";
86 objValue.emplace(intf, property);
Nan Zhou78d85042022-08-29 17:50:22 +000087 object.emplace(objPath, objValue);
Ravi Teja5fe724a2019-05-07 05:14:42 -050088 return object;
89 }
raviteja-b8cc44052019-02-27 23:29:36 -060090};
91
92TEST_F(TestUserMgr, ldapEntryDoesNotExist)
93{
94 std::string userName = "user";
95 UserInfoMap userInfo;
96
Alexander Filippov75626582022-02-09 18:42:37 +030097 EXPECT_CALL(mockManager, getPrimaryGroup(userName))
98 .WillRepeatedly(Throw(UserNameDoesNotExist()));
99 EXPECT_THROW(userInfo = mockManager.getUserInfo(userName),
100 UserNameDoesNotExist);
raviteja-b8cc44052019-02-27 23:29:36 -0600101}
102
103TEST_F(TestUserMgr, localUser)
104{
105 UserInfoMap userInfo;
106 std::string userName = "testUser";
107 std::string privilege = "priv-admin";
108 std::vector<std::string> groups{"testGroup"};
109 // Create local user
110 createLocalUser(userName, groups, privilege, true);
111 EXPECT_CALL(mockManager, userLockedForFailedAttempt(userName)).Times(1);
112 userInfo = mockManager.getUserInfo(userName);
113
114 EXPECT_EQ(privilege, std::get<std::string>(userInfo["UserPrivilege"]));
115 EXPECT_EQ(groups,
116 std::get<std::vector<std::string>>(userInfo["UserGroups"]));
117 EXPECT_EQ(true, std::get<bool>(userInfo["UserEnabled"]));
118 EXPECT_EQ(false, std::get<bool>(userInfo["UserLockedForFailedAttempt"]));
Joseph Reynolds3ab6cc22020-03-03 14:09:03 -0600119 EXPECT_EQ(false, std::get<bool>(userInfo["UserPasswordExpired"]));
raviteja-b8cc44052019-02-27 23:29:36 -0600120 EXPECT_EQ(false, std::get<bool>(userInfo["RemoteUser"]));
121}
122
123TEST_F(TestUserMgr, ldapUserWithPrivMapper)
124{
125 UserInfoMap userInfo;
126 std::string userName = "ldapUser";
127 std::string ldapGroup = "ldapGroup";
Alexander Filippov75626582022-02-09 18:42:37 +0300128 gid_t primaryGid = 1000;
raviteja-b8cc44052019-02-27 23:29:36 -0600129
Alexander Filippov75626582022-02-09 18:42:37 +0300130 EXPECT_CALL(mockManager, getPrimaryGroup(userName))
131 .WillRepeatedly(Return(primaryGid));
raviteja-b8cc44052019-02-27 23:29:36 -0600132 // Create privilege mapper dbus object
133 DbusUserObj object = createPrivilegeMapperDbusObject();
134 EXPECT_CALL(mockManager, getPrivilegeMapperObject())
135 .WillRepeatedly(Return(object));
Alexander Filippov75626582022-02-09 18:42:37 +0300136 EXPECT_CALL(mockManager, isGroupMember(userName, primaryGid, ldapGroup))
137 .WillRepeatedly(Return(true));
raviteja-b8cc44052019-02-27 23:29:36 -0600138 userInfo = mockManager.getUserInfo(userName);
139 EXPECT_EQ(true, std::get<bool>(userInfo["RemoteUser"]));
140 EXPECT_EQ("priv-admin", std::get<std::string>(userInfo["UserPrivilege"]));
141}
142
143TEST_F(TestUserMgr, ldapUserWithoutPrivMapper)
144{
Alexander Filippov75626582022-02-09 18:42:37 +0300145 using ::testing::_;
146
raviteja-b8cc44052019-02-27 23:29:36 -0600147 UserInfoMap userInfo;
148 std::string userName = "ldapUser";
149 std::string ldapGroup = "ldapGroup";
Alexander Filippov75626582022-02-09 18:42:37 +0300150 gid_t primaryGid = 1000;
raviteja-b8cc44052019-02-27 23:29:36 -0600151
Alexander Filippov75626582022-02-09 18:42:37 +0300152 EXPECT_CALL(mockManager, getPrimaryGroup(userName))
153 .WillRepeatedly(Return(primaryGid));
Ravi Teja5fe724a2019-05-07 05:14:42 -0500154 // Create LDAP config object without privilege mapper
155 DbusUserObj object = createLdapConfigObjectWithoutPrivilegeMapper();
raviteja-b8cc44052019-02-27 23:29:36 -0600156 EXPECT_CALL(mockManager, getPrivilegeMapperObject())
157 .WillRepeatedly(Return(object));
Alexander Filippov75626582022-02-09 18:42:37 +0300158 EXPECT_CALL(mockManager, isGroupMember(_, _, _)).Times(0);
raviteja-b8cc44052019-02-27 23:29:36 -0600159 userInfo = mockManager.getUserInfo(userName);
160 EXPECT_EQ(true, std::get<bool>(userInfo["RemoteUser"]));
161 EXPECT_EQ("", std::get<std::string>(userInfo["UserPrivilege"]));
162}
Nan Zhoue47c09d2022-10-25 00:06:41 +0000163
164TEST(GetCSVFromVector, EmptyVectorReturnsEmptyString)
165{
166 EXPECT_EQ(getCSVFromVector({}), "");
167}
168
169TEST(GetCSVFromVector, ElementsAreJoinedByComma)
170{
171 EXPECT_EQ(getCSVFromVector(std::vector<std::string>{"123"}), "123");
172 EXPECT_EQ(getCSVFromVector(std::vector<std::string>{"123", "456"}),
173 "123,456");
174}
175
Nan Zhou332fb9d2022-10-25 00:07:03 +0000176TEST(RemoveStringFromCSV, WithoutDeleteStringReturnsFalse)
177{
178 std::string expected = "whatever,https";
179 std::string str = expected;
180 EXPECT_FALSE(removeStringFromCSV(str, "ssh"));
181 EXPECT_EQ(str, expected);
182
183 std::string empty;
184 EXPECT_FALSE(removeStringFromCSV(empty, "ssh"));
185}
186
187TEST(RemoveStringFromCSV, WithDeleteStringReturnsTrue)
188{
189 std::string expected = "whatever";
190 std::string str = "whatever,https";
191 EXPECT_TRUE(removeStringFromCSV(str, "https"));
192 EXPECT_EQ(str, expected);
193
194 str = "https";
195 EXPECT_TRUE(removeStringFromCSV(str, "https"));
196 EXPECT_EQ(str, "");
197}
198
Nan Zhoue48085d2022-10-25 00:07:04 +0000199namespace
200{
201inline constexpr const char* objectRootInTest = "/xyz/openbmc_project/user";
202
203// Fake config; referenced config on real BMC
204inline constexpr const char* rawConfig = R"(
205#
206# /etc/pam.d/common-password - password-related modules common to all services
207#
208# This file is included from other service-specific PAM config files,
209# and should contain a list of modules that define the services to be
210# used to change user passwords. The default is pam_unix.
211
212# Explanation of pam_unix options:
213#
214# The "sha512" option enables salted SHA512 passwords. Without this option,
215# the default is Unix crypt. Prior releases used the option "md5".
216#
217# The "obscure" option replaces the old `OBSCURE_CHECKS_ENAB' option in
218# login.defs.
219#
220# See the pam_unix manpage for other options.
221
222# here are the per-package modules (the "Primary" block)
Nan Zhou784aecd2022-10-25 00:07:16 +0000223password [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 +0000224password [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
225password [success=ok default=die] pam_ipmicheck.so spec_grp_name=ipmi use_authtok
226password [success=ok ignore=ignore default=die] pam_pwhistory.so debug enforce_for_root remember=0 use_authtok
227password [success=ok default=die] pam_unix.so sha512 use_authtok
228password [success=1 default=die] pam_ipmisave.so spec_grp_name=ipmi spec_pass_file=/etc/ipmi_pass key_file=/etc/key_file
229# here's the fallback if no module succeeds
230password requisite pam_deny.so
231# prime the stack with a positive return value if there isn't one already;
232# this avoids us returning an error just because nothing sets a success code
233# since the modules above will each just jump around
234password required pam_permit.so
235# and here are more per-package modules (the "Additional" block)
236)";
237} // namespace
238
239void dumpStringToFile(const std::string& str, const std::string& filePath)
240{
241 std::ofstream outputFileStream;
242
243 outputFileStream.exceptions(std::ofstream::failbit | std::ofstream::badbit |
244 std::ofstream::eofbit);
245
246 outputFileStream.open(filePath, std::ios::out);
247 outputFileStream << str << "\n" << std::flush;
248 outputFileStream.close();
249}
250
251void removeFile(const std::string& filePath)
252{
253 std::filesystem::remove(filePath);
254}
255
256class UserMgrInTest : public testing::Test, public UserMgr
257{
258 public:
259 UserMgrInTest() : UserMgr(busInTest, objectRootInTest)
260 {
261 tempPamConfigFile = "/tmp/test-data-XXXXXX";
262 mktemp(tempPamConfigFile.data());
263 EXPECT_NO_THROW(dumpStringToFile(rawConfig, tempPamConfigFile));
264 // Set config files to test files
265 pamPasswdConfigFile = tempPamConfigFile;
266 pamAuthConfigFile = tempPamConfigFile;
Nan Zhou49c81362022-10-25 00:07:08 +0000267
268 ON_CALL(*this, executeUserAdd).WillByDefault(testing::Return());
269
270 ON_CALL(*this, executeUserDelete).WillByDefault(testing::Return());
271
272 ON_CALL(*this, getIpmiUsersCount).WillByDefault(testing::Return(0));
Nan Zhouf25443e2022-10-25 00:07:11 +0000273
274 ON_CALL(*this, executeUserRename).WillByDefault(testing::Return());
Nan Zhoufef63032022-10-25 00:07:12 +0000275
276 ON_CALL(*this, executeUserModify(testing::_, testing::_, testing::_))
277 .WillByDefault(testing::Return());
Nan Zhou6b6f2d82022-10-25 00:07:17 +0000278
279 ON_CALL(*this, executeUserModifyUserEnable)
280 .WillByDefault(testing::Return());
Nan Zhoue48085d2022-10-25 00:07:04 +0000281 }
282
283 ~UserMgrInTest() override
284 {
285 EXPECT_NO_THROW(removeFile(tempPamConfigFile));
286 }
287
Nan Zhou49c81362022-10-25 00:07:08 +0000288 MOCK_METHOD(void, executeUserAdd, (const char*, const char*, bool, bool),
289 (override));
290
291 MOCK_METHOD(void, executeUserDelete, (const char*), (override));
292
293 MOCK_METHOD(size_t, getIpmiUsersCount, (), (override));
294
Nan Zhouf25443e2022-10-25 00:07:11 +0000295 MOCK_METHOD(void, executeUserRename, (const char*, const char*),
296 (override));
297
Nan Zhoufef63032022-10-25 00:07:12 +0000298 MOCK_METHOD(void, executeUserModify, (const char*, const char*, bool),
299 (override));
300
Nan Zhou6b6f2d82022-10-25 00:07:17 +0000301 MOCK_METHOD(void, executeUserModifyUserEnable, (const char*, bool),
302 (override));
303
Nan Zhoua2953032022-11-11 21:50:32 +0000304 MOCK_METHOD(std::vector<std::string>, getFailedAttempt, (const char*),
305 (override));
306
Nan Zhoufef63032022-10-25 00:07:12 +0000307 protected:
Nan Zhoue48085d2022-10-25 00:07:04 +0000308 static sdbusplus::bus_t busInTest;
309 std::string tempPamConfigFile;
310};
311
312sdbusplus::bus_t UserMgrInTest::busInTest = sdbusplus::bus::new_default();
313
314TEST_F(UserMgrInTest, GetPamModuleArgValueOnSuccess)
315{
316 std::string minLen;
317 EXPECT_EQ(getPamModuleArgValue("pam_tally2.so", "minlen", minLen), 0);
318 EXPECT_EQ(minLen, "8");
319 EXPECT_EQ(getPamModuleArgValue("pam_cracklib.so", "minlen", minLen), 0);
320 EXPECT_EQ(minLen, "8");
321}
322
323TEST_F(UserMgrInTest, SetPamModuleArgValueOnSuccess)
324{
325 EXPECT_EQ(setPamModuleArgValue("pam_cracklib.so", "minlen", "16"), 0);
326 EXPECT_EQ(setPamModuleArgValue("pam_tally2.so", "minlen", "16"), 0);
327 std::string minLen;
328 EXPECT_EQ(getPamModuleArgValue("pam_tally2.so", "minlen", minLen), 0);
329 EXPECT_EQ(minLen, "16");
330 EXPECT_EQ(getPamModuleArgValue("pam_cracklib.so", "minlen", minLen), 0);
331 EXPECT_EQ(minLen, "16");
332}
333
334TEST_F(UserMgrInTest, GetPamModuleArgValueOnFailure)
335{
336 EXPECT_NO_THROW(dumpStringToFile("whatever", tempPamConfigFile));
337 std::string minLen;
338 EXPECT_EQ(getPamModuleArgValue("pam_tally2.so", "minlen", minLen), -1);
339 EXPECT_EQ(getPamModuleArgValue("pam_cracklib.so", "minlen", minLen), -1);
340
341 EXPECT_NO_THROW(removeFile(tempPamConfigFile));
342 EXPECT_EQ(getPamModuleArgValue("pam_tally2.so", "minlen", minLen), -1);
343 EXPECT_EQ(getPamModuleArgValue("pam_cracklib.so", "minlen", minLen), -1);
344}
345
346TEST_F(UserMgrInTest, SetPamModuleArgValueOnFailure)
347{
348 EXPECT_NO_THROW(dumpStringToFile("whatever", tempPamConfigFile));
349 EXPECT_EQ(setPamModuleArgValue("pam_cracklib.so", "minlen", "16"), -1);
350 EXPECT_EQ(setPamModuleArgValue("pam_tally2.so", "minlen", "16"), -1);
351
352 EXPECT_NO_THROW(removeFile(tempPamConfigFile));
353 EXPECT_EQ(setPamModuleArgValue("pam_cracklib.so", "minlen", "16"), -1);
354 EXPECT_EQ(setPamModuleArgValue("pam_tally2.so", "minlen", "16"), -1);
355}
356
Nan Zhou8a11d992022-10-25 00:07:06 +0000357TEST_F(UserMgrInTest, IsUserExistEmptyInputThrowsError)
358{
359 EXPECT_THROW(
360 isUserExist(""),
361 sdbusplus::xyz::openbmc_project::Common::Error::InvalidArgument);
362}
363
364TEST_F(UserMgrInTest, ThrowForUserDoesNotExistThrowsError)
365{
366 EXPECT_THROW(throwForUserDoesNotExist("whatever"),
367 sdbusplus::xyz::openbmc_project::User::Common::Error::
368 UserNameDoesNotExist);
369}
370
371TEST_F(UserMgrInTest, ThrowForUserExistsThrowsError)
372{
373 EXPECT_THROW(
374 throwForUserExists("root"),
375 sdbusplus::xyz::openbmc_project::User::Common::Error::UserNameExists);
376}
377
Nan Zhou40e44972022-10-25 00:07:07 +0000378TEST_F(
379 UserMgrInTest,
380 ThrowForUserNameConstraintsExceedIpmiMaxUserNameLenThrowsUserNameGroupFail)
381{
382 std::string strWith17Chars(17, 'A');
383 EXPECT_THROW(throwForUserNameConstraints(strWith17Chars, {"ipmi"}),
384 sdbusplus::xyz::openbmc_project::User::Common::Error::
385 UserNameGroupFail);
386}
387
388TEST_F(
389 UserMgrInTest,
390 ThrowForUserNameConstraintsExceedSystemMaxUserNameLenThrowsInvalidArgument)
391{
392 std::string strWith31Chars(31, 'A');
393 EXPECT_THROW(
394 throwForUserNameConstraints(strWith31Chars, {}),
395 sdbusplus::xyz::openbmc_project::Common::Error::InvalidArgument);
396}
397
398TEST_F(UserMgrInTest,
399 ThrowForUserNameConstraintsRegexMismatchThrowsInvalidArgument)
400{
401 std::string startWithNumber = "0ABC";
402 EXPECT_THROW(
403 throwForUserNameConstraints(startWithNumber, {"ipmi"}),
404 sdbusplus::xyz::openbmc_project::Common::Error::InvalidArgument);
405}
406
Nan Zhou49c81362022-10-25 00:07:08 +0000407TEST_F(UserMgrInTest, UserAddNotRootFailedWithInternalFailure)
408{
409 EXPECT_THROW(
410 UserMgr::executeUserAdd("user0", "ipmi,ssh", true, true),
411 sdbusplus::xyz::openbmc_project::Common::Error::InternalFailure);
412}
413
414TEST_F(UserMgrInTest, UserDeleteNotRootFailedWithInternalFailure)
415{
416 EXPECT_THROW(
417 UserMgr::executeUserDelete("user0"),
418 sdbusplus::xyz::openbmc_project::Common::Error::InternalFailure);
419}
420
421TEST_F(UserMgrInTest,
422 ThrowForMaxGrpUserCountThrowsNoResourceWhenIpmiUserExceedLimit)
423{
424 EXPECT_CALL(*this, getIpmiUsersCount()).WillOnce(Return(ipmiMaxUsers));
425 EXPECT_THROW(
426 throwForMaxGrpUserCount({"ipmi"}),
427 sdbusplus::xyz::openbmc_project::User::Common::Error::NoResource);
428}
429
430TEST_F(UserMgrInTest, CreateUserThrowsInternalFailureWhenExecuteUserAddFails)
431{
432 EXPECT_CALL(*this, executeUserAdd)
433 .WillOnce(testing::Throw(
434 sdbusplus::xyz::openbmc_project::Common::Error::InternalFailure()));
435 EXPECT_THROW(
436 createUser("whatever", {"redfish"}, "", true),
437 sdbusplus::xyz::openbmc_project::Common::Error::InternalFailure);
438}
439
440TEST_F(UserMgrInTest, DeleteUserThrowsInternalFailureWhenExecuteUserDeleteFails)
441{
442 std::string username = "user";
443 EXPECT_NO_THROW(
444 UserMgr::createUser(username, {"redfish", "ssh"}, "priv-user", true));
445 EXPECT_CALL(*this, executeUserDelete(testing::StrEq(username)))
446 .WillOnce(testing::Throw(
447 sdbusplus::xyz::openbmc_project::Common::Error::InternalFailure()))
448 .WillOnce(testing::DoDefault());
449
450 EXPECT_THROW(
451 deleteUser(username),
452 sdbusplus::xyz::openbmc_project::Common::Error::InternalFailure);
453 EXPECT_NO_THROW(UserMgr::deleteUser(username));
454}
455
Nan Zhou589aeb42022-10-25 00:07:09 +0000456TEST_F(UserMgrInTest, ThrowForInvalidPrivilegeThrowsWhenPrivilegeIsInvalid)
457{
458 EXPECT_THROW(
459 throwForInvalidPrivilege("whatever"),
460 sdbusplus::xyz::openbmc_project::Common::Error::InvalidArgument);
461}
462
463TEST_F(UserMgrInTest, ThrowForInvalidPrivilegeNoThrowWhenPrivilegeIsValid)
464{
465 EXPECT_NO_THROW(throwForInvalidPrivilege("priv-admin"));
466 EXPECT_NO_THROW(throwForInvalidPrivilege("priv-operator"));
467 EXPECT_NO_THROW(throwForInvalidPrivilege("priv-user"));
Nan Zhou589aeb42022-10-25 00:07:09 +0000468}
469
Nan Zhouecf88762022-10-25 00:07:10 +0000470TEST_F(UserMgrInTest, ThrowForInvalidGroupsThrowsWhenGroupIsInvalid)
471{
472 EXPECT_THROW(
473 throwForInvalidGroups({"whatever"}),
474 sdbusplus::xyz::openbmc_project::Common::Error::InvalidArgument);
475}
476
477TEST_F(UserMgrInTest, ThrowForInvalidGroupsNoThrowWhenGroupIsValid)
478{
479 EXPECT_NO_THROW(throwForInvalidGroups({"ipmi"}));
480 EXPECT_NO_THROW(throwForInvalidGroups({"ssh"}));
481 EXPECT_NO_THROW(throwForInvalidGroups({"redfish"}));
482 EXPECT_NO_THROW(throwForInvalidGroups({"web"}));
483}
484
Nan Zhouf25443e2022-10-25 00:07:11 +0000485TEST_F(UserMgrInTest, RenameUserOnSuccess)
486{
487 std::string username = "user001";
488 EXPECT_NO_THROW(
489 UserMgr::createUser(username, {"redfish", "ssh"}, "priv-user", true));
490 std::string newUsername = "user002";
491
492 EXPECT_NO_THROW(UserMgr::renameUser(username, newUsername));
493
494 // old username doesn't exist
495 EXPECT_THROW(getUserInfo(username),
496 sdbusplus::xyz::openbmc_project::User::Common::Error::
497 UserNameDoesNotExist);
498
499 UserInfoMap userInfo = getUserInfo(newUsername);
500 EXPECT_EQ(std::get<Privilege>(userInfo["UserPrivilege"]), "priv-user");
501 EXPECT_THAT(std::get<GroupList>(userInfo["UserGroups"]),
502 testing::UnorderedElementsAre("redfish", "ssh"));
503 EXPECT_EQ(std::get<UserEnabled>(userInfo["UserEnabled"]), true);
504
505 EXPECT_NO_THROW(UserMgr::deleteUser(newUsername));
506}
507
508TEST_F(UserMgrInTest, RenameUserThrowsInternalFailureIfExecuteUserModifyFails)
509{
510 std::string username = "user001";
511 EXPECT_NO_THROW(
512 UserMgr::createUser(username, {"redfish", "ssh"}, "priv-user", true));
513 std::string newUsername = "user002";
514
515 EXPECT_CALL(*this, executeUserRename(testing::StrEq(username),
516 testing::StrEq(newUsername)))
517 .WillOnce(testing::Throw(
518 sdbusplus::xyz::openbmc_project::Common::Error::InternalFailure()));
519 EXPECT_THROW(
520 UserMgr::renameUser(username, newUsername),
521 sdbusplus::xyz::openbmc_project::Common::Error::InternalFailure);
522
523 // The original user is unchanged
524 UserInfoMap userInfo = getUserInfo(username);
525 EXPECT_EQ(std::get<Privilege>(userInfo["UserPrivilege"]), "priv-user");
526 EXPECT_THAT(std::get<GroupList>(userInfo["UserGroups"]),
527 testing::UnorderedElementsAre("redfish", "ssh"));
528 EXPECT_EQ(std::get<UserEnabled>(userInfo["UserEnabled"]), true);
529
530 EXPECT_NO_THROW(UserMgr::deleteUser(username));
531}
532
533TEST_F(UserMgrInTest, DefaultUserModifyFailedWithInternalFailure)
534{
535 EXPECT_THROW(
536 UserMgr::executeUserRename("user0", "user1"),
537 sdbusplus::xyz::openbmc_project::Common::Error::InternalFailure);
Nan Zhoufef63032022-10-25 00:07:12 +0000538 EXPECT_THROW(
539 UserMgr::executeUserModify("user0", "ssh", true),
540 sdbusplus::xyz::openbmc_project::Common::Error::InternalFailure);
541}
542
543TEST_F(UserMgrInTest, UpdateGroupsAndPrivOnSuccess)
544{
545 std::string username = "user001";
546 EXPECT_NO_THROW(
547 UserMgr::createUser(username, {"redfish", "ssh"}, "priv-user", true));
548 EXPECT_NO_THROW(
549 updateGroupsAndPriv(username, {"ipmi", "ssh"}, "priv-admin"));
550 UserInfoMap userInfo = getUserInfo(username);
551 EXPECT_EQ(std::get<Privilege>(userInfo["UserPrivilege"]), "priv-admin");
552 EXPECT_THAT(std::get<GroupList>(userInfo["UserGroups"]),
553 testing::UnorderedElementsAre("ipmi", "ssh"));
554 EXPECT_EQ(std::get<UserEnabled>(userInfo["UserEnabled"]), true);
555 EXPECT_NO_THROW(UserMgr::deleteUser(username));
556}
557
558TEST_F(UserMgrInTest,
559 UpdateGroupsAndPrivThrowsInternalFailureIfExecuteUserModifyFail)
560{
561 std::string username = "user001";
562 EXPECT_NO_THROW(
563 UserMgr::createUser(username, {"redfish", "ssh"}, "priv-user", true));
564 EXPECT_CALL(*this, executeUserModify(testing::StrEq(username), testing::_,
565 testing::_))
566 .WillOnce(testing::Throw(
567 sdbusplus::xyz::openbmc_project::Common::Error::InternalFailure()));
568 EXPECT_THROW(
569 updateGroupsAndPriv(username, {"ipmi", "ssh"}, "priv-admin"),
570 sdbusplus::xyz::openbmc_project::Common::Error::InternalFailure);
571 EXPECT_NO_THROW(UserMgr::deleteUser(username));
Nan Zhouf25443e2022-10-25 00:07:11 +0000572}
573
Nan Zhou18031012022-11-10 22:24:58 +0000574TEST_F(UserMgrInTest, MinPasswordLengthReturnsIfValueIsTheSame)
575{
576 initializeAccountPolicy();
577 EXPECT_EQ(AccountPolicyIface::minPasswordLength(), 8);
578 UserMgr::minPasswordLength(8);
579 EXPECT_EQ(AccountPolicyIface::minPasswordLength(), 8);
580}
581
582TEST_F(UserMgrInTest,
583 MinPasswordLengthRejectsTooShortPasswordWithInvalidArgument)
584{
585 initializeAccountPolicy();
586 EXPECT_EQ(AccountPolicyIface::minPasswordLength(), 8);
587 EXPECT_THROW(
588 UserMgr::minPasswordLength(minPasswdLength - 1),
589 sdbusplus::xyz::openbmc_project::Common::Error::InvalidArgument);
590 EXPECT_EQ(AccountPolicyIface::minPasswordLength(), 8);
591}
592
593TEST_F(UserMgrInTest, MinPasswordLengthOnSuccess)
594{
595 initializeAccountPolicy();
596 EXPECT_EQ(AccountPolicyIface::minPasswordLength(), 8);
597 UserMgr::minPasswordLength(16);
598 EXPECT_EQ(AccountPolicyIface::minPasswordLength(), 16);
599}
600
601TEST_F(UserMgrInTest, MinPasswordLengthOnFailure)
602{
603 EXPECT_NO_THROW(dumpStringToFile("whatever", tempPamConfigFile));
604 initializeAccountPolicy();
605 EXPECT_EQ(AccountPolicyIface::minPasswordLength(), 8);
606 EXPECT_THROW(
607 UserMgr::minPasswordLength(16),
608 sdbusplus::xyz::openbmc_project::Common::Error::InternalFailure);
609 EXPECT_EQ(AccountPolicyIface::minPasswordLength(), 8);
610}
611
Nan Zhoua6ce1fa2022-10-25 00:07:14 +0000612TEST_F(UserMgrInTest, RememberOldPasswordTimesReturnsIfValueIsTheSame)
613{
614 initializeAccountPolicy();
615 EXPECT_EQ(AccountPolicyIface::rememberOldPasswordTimes(), 0);
616 UserMgr::rememberOldPasswordTimes(8);
617 EXPECT_EQ(AccountPolicyIface::rememberOldPasswordTimes(), 8);
618 UserMgr::rememberOldPasswordTimes(8);
619 EXPECT_EQ(AccountPolicyIface::rememberOldPasswordTimes(), 8);
620}
621
622TEST_F(UserMgrInTest, RememberOldPasswordTimesOnSuccess)
623{
624 initializeAccountPolicy();
625 EXPECT_EQ(AccountPolicyIface::rememberOldPasswordTimes(), 0);
626 UserMgr::rememberOldPasswordTimes(16);
627 EXPECT_EQ(AccountPolicyIface::rememberOldPasswordTimes(), 16);
628}
629
630TEST_F(UserMgrInTest, RememberOldPasswordTimesOnFailure)
631{
632 EXPECT_NO_THROW(dumpStringToFile("whatever", tempPamConfigFile));
633 initializeAccountPolicy();
634 EXPECT_EQ(AccountPolicyIface::rememberOldPasswordTimes(), 0);
635 EXPECT_THROW(
636 UserMgr::rememberOldPasswordTimes(16),
637 sdbusplus::xyz::openbmc_project::Common::Error::InternalFailure);
638 EXPECT_EQ(AccountPolicyIface::rememberOldPasswordTimes(), 0);
639}
640
Nan Zhoucfabe6b2022-10-25 00:07:15 +0000641TEST_F(UserMgrInTest, MaxLoginAttemptBeforeLockoutReturnsIfValueIsTheSame)
642{
643 initializeAccountPolicy();
644 EXPECT_EQ(AccountPolicyIface::maxLoginAttemptBeforeLockout(), 2);
645 UserMgr::maxLoginAttemptBeforeLockout(2);
646 EXPECT_EQ(AccountPolicyIface::maxLoginAttemptBeforeLockout(), 2);
647}
648
649TEST_F(UserMgrInTest, MaxLoginAttemptBeforeLockoutOnSuccess)
650{
651 initializeAccountPolicy();
652 EXPECT_EQ(AccountPolicyIface::maxLoginAttemptBeforeLockout(), 2);
653 UserMgr::maxLoginAttemptBeforeLockout(16);
654 EXPECT_EQ(AccountPolicyIface::maxLoginAttemptBeforeLockout(), 16);
655}
656
657TEST_F(UserMgrInTest, MaxLoginAttemptBeforeLockoutOnFailure)
658{
659 EXPECT_NO_THROW(dumpStringToFile("whatever", tempPamConfigFile));
660 initializeAccountPolicy();
661 EXPECT_THROW(
662 UserMgr::maxLoginAttemptBeforeLockout(16),
663 sdbusplus::xyz::openbmc_project::Common::Error::InternalFailure);
664 EXPECT_EQ(AccountPolicyIface::rememberOldPasswordTimes(), 0);
665}
666
Nan Zhou784aecd2022-10-25 00:07:16 +0000667TEST_F(UserMgrInTest, AccountUnlockTimeoutReturnsIfValueIsTheSame)
668{
669 initializeAccountPolicy();
670 EXPECT_EQ(AccountPolicyIface::accountUnlockTimeout(), 3);
671 UserMgr::accountUnlockTimeout(3);
672 EXPECT_EQ(AccountPolicyIface::accountUnlockTimeout(), 3);
673}
674
675TEST_F(UserMgrInTest, AccountUnlockTimeoutOnSuccess)
676{
677 initializeAccountPolicy();
678 EXPECT_EQ(AccountPolicyIface::accountUnlockTimeout(), 3);
679 UserMgr::accountUnlockTimeout(16);
680 EXPECT_EQ(AccountPolicyIface::accountUnlockTimeout(), 16);
681}
682
683TEST_F(UserMgrInTest, AccountUnlockTimeoutOnFailure)
684{
685 initializeAccountPolicy();
686 EXPECT_NO_THROW(dumpStringToFile("whatever", tempPamConfigFile));
687 EXPECT_THROW(
688 UserMgr::accountUnlockTimeout(16),
689 sdbusplus::xyz::openbmc_project::Common::Error::InternalFailure);
690 EXPECT_EQ(AccountPolicyIface::accountUnlockTimeout(), 3);
691}
692
Nan Zhou6b6f2d82022-10-25 00:07:17 +0000693TEST_F(UserMgrInTest, UserEnableOnSuccess)
694{
695 std::string username = "user001";
696 EXPECT_NO_THROW(
697 UserMgr::createUser(username, {"redfish", "ssh"}, "priv-user", true));
698 UserInfoMap userInfo = getUserInfo(username);
699 EXPECT_EQ(std::get<UserEnabled>(userInfo["UserEnabled"]), true);
700
701 EXPECT_NO_THROW(userEnable(username, false));
702
703 userInfo = getUserInfo(username);
704 EXPECT_EQ(std::get<UserEnabled>(userInfo["UserEnabled"]), false);
705
706 EXPECT_NO_THROW(UserMgr::deleteUser(username));
707}
708
709TEST_F(UserMgrInTest, UserEnableThrowsInternalFailureIfExecuteUserModifyFail)
710{
711 std::string username = "user001";
712 EXPECT_NO_THROW(
713 UserMgr::createUser(username, {"redfish", "ssh"}, "priv-user", true));
714 UserInfoMap userInfo = getUserInfo(username);
715 EXPECT_EQ(std::get<UserEnabled>(userInfo["UserEnabled"]), true);
716
717 EXPECT_CALL(*this, executeUserModifyUserEnable(testing::StrEq(username),
718 testing::Eq(false)))
719 .WillOnce(testing::Throw(
720 sdbusplus::xyz::openbmc_project::Common::Error::InternalFailure()));
721 EXPECT_THROW(
722 userEnable(username, false),
723 sdbusplus::xyz::openbmc_project::Common::Error::InternalFailure);
724
725 userInfo = getUserInfo(username);
726 // Stay unchanged
727 EXPECT_EQ(std::get<UserEnabled>(userInfo["UserEnabled"]), true);
728
729 EXPECT_NO_THROW(UserMgr::deleteUser(username));
730}
731
Nan Zhoua2953032022-11-11 21:50:32 +0000732TEST_F(
733 UserMgrInTest,
734 UserLockedForFailedAttemptReturnsFalseIfMaxLoginAttemptBeforeLockoutIsZero)
735{
736 EXPECT_FALSE(userLockedForFailedAttempt("whatever"));
737}
738
739TEST_F(UserMgrInTest, UserLockedForFailedAttemptZeroFailuresReturnsFalse)
740{
741 std::string username = "user001";
742 initializeAccountPolicy();
743 // Example output from BMC
744 // root@s7106:~# pam_tally2 -u root
745 // Login Failures Latest failure From
746 // root 0
747 std::vector<std::string> output = {"whatever", "root\t0"};
748 EXPECT_CALL(*this, getFailedAttempt(testing::StrEq(username.c_str())))
749 .WillOnce(testing::Return(output));
750
751 EXPECT_FALSE(userLockedForFailedAttempt(username));
752}
753
754TEST_F(UserMgrInTest, UserLockedForFailedAttemptFailIfGetFailedAttemptFail)
755{
756 std::string username = "user001";
757 initializeAccountPolicy();
758 EXPECT_CALL(*this, getFailedAttempt(testing::StrEq(username.c_str())))
759 .WillOnce(testing::Throw(
760 sdbusplus::xyz::openbmc_project::Common::Error::InternalFailure()));
761
762 EXPECT_THROW(
763 userLockedForFailedAttempt(username),
764 sdbusplus::xyz::openbmc_project::Common::Error::InternalFailure);
765}
766
767TEST_F(UserMgrInTest,
768 UserLockedForFailedAttemptThrowsInternalFailureIfFailAttemptsOutOfRange)
769{
770 std::string username = "user001";
771 initializeAccountPolicy();
772 std::vector<std::string> output = {"whatever", "root\t1000000"};
773 EXPECT_CALL(*this, getFailedAttempt(testing::StrEq(username.c_str())))
774 .WillOnce(testing::Return(output));
775
776 EXPECT_THROW(
777 userLockedForFailedAttempt(username),
778 sdbusplus::xyz::openbmc_project::Common::Error::InternalFailure);
779}
780
781TEST_F(UserMgrInTest,
782 UserLockedForFailedAttemptThrowsInternalFailureIfNoFailDateTime)
783{
784 std::string username = "user001";
785 initializeAccountPolicy();
786 std::vector<std::string> output = {"whatever", "root\t2"};
787 EXPECT_CALL(*this, getFailedAttempt(testing::StrEq(username.c_str())))
788 .WillOnce(testing::Return(output));
789
790 EXPECT_THROW(
791 userLockedForFailedAttempt(username),
792 sdbusplus::xyz::openbmc_project::Common::Error::InternalFailure);
793}
794
795TEST_F(UserMgrInTest,
796 UserLockedForFailedAttemptThrowsInternalFailureIfWrongDateFormat)
797{
798 std::string username = "user001";
799 initializeAccountPolicy();
800
801 // Choose a date in the past.
802 std::vector<std::string> output = {"whatever",
803 "root\t2\t10/24/2002\t00:00:00"};
804 EXPECT_CALL(*this, getFailedAttempt(testing::StrEq(username.c_str())))
805 .WillOnce(testing::Return(output));
806
807 EXPECT_THROW(
808 userLockedForFailedAttempt(username),
809 sdbusplus::xyz::openbmc_project::Common::Error::InternalFailure);
810}
811
812TEST_F(UserMgrInTest,
813 UserLockedForFailedAttemptReturnsFalseIfLastFailTimeHasTimedOut)
814{
815 std::string username = "user001";
816 initializeAccountPolicy();
817
818 // Choose a date in the past.
819 std::vector<std::string> output = {"whatever",
820 "root\t2\t10/24/02\t00:00:00"};
821 EXPECT_CALL(*this, getFailedAttempt(testing::StrEq(username.c_str())))
822 .WillOnce(testing::Return(output));
823
824 EXPECT_EQ(userLockedForFailedAttempt(username), false);
825}
826
raviteja-b8cc44052019-02-27 23:29:36 -0600827} // namespace user
828} // namespace phosphor