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 12 #include <gtest/gtest.h> 13 14 namespace phosphor 15 { 16 namespace user 17 { 18 19 using ::testing::Return; 20 using ::testing::Throw; 21 22 using InternalFailure = 23 sdbusplus::xyz::openbmc_project::Common::Error::InternalFailure; 24 using UserNameDoesNotExist = 25 sdbusplus::xyz::openbmc_project::User::Common::Error::UserNameDoesNotExist; 26 27 class TestUserMgr : public testing::Test 28 { 29 public: 30 sdbusplus::SdBusMock sdBusMock; 31 sdbusplus::bus_t bus; 32 MockManager mockManager; 33 34 TestUserMgr() : 35 bus(sdbusplus::get_mocked_new(&sdBusMock)), mockManager(bus, objpath) 36 {} 37 38 void createLocalUser(const std::string& userName, 39 std::vector<std::string> groupNames, 40 const std::string& priv, bool enabled) 41 { 42 sdbusplus::message::object_path tempObjPath(usersObjPath); 43 tempObjPath /= userName; 44 std::string userObj(tempObjPath); 45 mockManager.usersList.emplace( 46 userName, std::make_unique<phosphor::user::Users>( 47 mockManager.bus, userObj.c_str(), groupNames, priv, 48 enabled, mockManager)); 49 } 50 51 DbusUserObj createPrivilegeMapperDbusObject(void) 52 { 53 DbusUserObj object; 54 DbusUserObjValue objValue; 55 56 DbusUserObjPath objPath("/xyz/openbmc_project/user/ldap/openldap"); 57 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); 61 object.emplace(objPath, objValue); 62 63 DbusUserObjPath objectPath( 64 "/xyz/openbmc_project/user/ldap/openldap/role_map/1"); 65 std::string group = "ldapGroup"; 66 std::string priv = "priv-admin"; 67 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); 72 object.emplace(objectPath, objValue); 73 74 return object; 75 } 76 77 DbusUserObj createLdapConfigObjectWithoutPrivilegeMapper(void) 78 { 79 DbusUserObj object; 80 DbusUserObjValue objValue; 81 82 DbusUserObjPath objPath("/xyz/openbmc_project/user/ldap/openldap"); 83 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); 87 object.emplace(objPath, objValue); 88 return object; 89 } 90 }; 91 92 TEST_F(TestUserMgr, ldapEntryDoesNotExist) 93 { 94 std::string userName = "user"; 95 UserInfoMap userInfo; 96 97 EXPECT_CALL(mockManager, getPrimaryGroup(userName)) 98 .WillRepeatedly(Throw(UserNameDoesNotExist())); 99 EXPECT_THROW(userInfo = mockManager.getUserInfo(userName), 100 UserNameDoesNotExist); 101 } 102 103 TEST_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"])); 119 EXPECT_EQ(false, std::get<bool>(userInfo["UserPasswordExpired"])); 120 EXPECT_EQ(false, std::get<bool>(userInfo["RemoteUser"])); 121 } 122 123 TEST_F(TestUserMgr, ldapUserWithPrivMapper) 124 { 125 UserInfoMap userInfo; 126 std::string userName = "ldapUser"; 127 std::string ldapGroup = "ldapGroup"; 128 gid_t primaryGid = 1000; 129 130 EXPECT_CALL(mockManager, getPrimaryGroup(userName)) 131 .WillRepeatedly(Return(primaryGid)); 132 // Create privilege mapper dbus object 133 DbusUserObj object = createPrivilegeMapperDbusObject(); 134 EXPECT_CALL(mockManager, getPrivilegeMapperObject()) 135 .WillRepeatedly(Return(object)); 136 EXPECT_CALL(mockManager, isGroupMember(userName, primaryGid, ldapGroup)) 137 .WillRepeatedly(Return(true)); 138 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 143 TEST_F(TestUserMgr, ldapUserWithoutPrivMapper) 144 { 145 using ::testing::_; 146 147 UserInfoMap userInfo; 148 std::string userName = "ldapUser"; 149 std::string ldapGroup = "ldapGroup"; 150 gid_t primaryGid = 1000; 151 152 EXPECT_CALL(mockManager, getPrimaryGroup(userName)) 153 .WillRepeatedly(Return(primaryGid)); 154 // Create LDAP config object without privilege mapper 155 DbusUserObj object = createLdapConfigObjectWithoutPrivilegeMapper(); 156 EXPECT_CALL(mockManager, getPrivilegeMapperObject()) 157 .WillRepeatedly(Return(object)); 158 EXPECT_CALL(mockManager, isGroupMember(_, _, _)).Times(0); 159 userInfo = mockManager.getUserInfo(userName); 160 EXPECT_EQ(true, std::get<bool>(userInfo["RemoteUser"])); 161 EXPECT_EQ("", std::get<std::string>(userInfo["UserPrivilege"])); 162 } 163 164 TEST(GetCSVFromVector, EmptyVectorReturnsEmptyString) 165 { 166 EXPECT_EQ(getCSVFromVector({}), ""); 167 } 168 169 TEST(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 176 TEST(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 187 TEST(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 199 namespace 200 { 201 inline constexpr const char* objectRootInTest = "/xyz/openbmc_project/user"; 202 203 // Fake config; referenced config on real BMC 204 inline 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) 223 password [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 224 password [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 225 password [success=ok default=die] pam_ipmicheck.so spec_grp_name=ipmi use_authtok 226 password [success=ok ignore=ignore default=die] pam_pwhistory.so debug enforce_for_root remember=0 use_authtok 227 password [success=ok default=die] pam_unix.so sha512 use_authtok 228 password [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 230 password 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 234 password required pam_permit.so 235 # and here are more per-package modules (the "Additional" block) 236 )"; 237 } // namespace 238 239 void 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 251 void removeFile(const std::string& filePath) 252 { 253 std::filesystem::remove(filePath); 254 } 255 256 class 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; 267 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)); 273 274 ON_CALL(*this, executeUserRename).WillByDefault(testing::Return()); 275 276 ON_CALL(*this, executeUserModify(testing::_, testing::_, testing::_)) 277 .WillByDefault(testing::Return()); 278 279 ON_CALL(*this, executeUserModifyUserEnable) 280 .WillByDefault(testing::Return()); 281 } 282 283 ~UserMgrInTest() override 284 { 285 EXPECT_NO_THROW(removeFile(tempPamConfigFile)); 286 } 287 288 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 295 MOCK_METHOD(void, executeUserRename, (const char*, const char*), 296 (override)); 297 298 MOCK_METHOD(void, executeUserModify, (const char*, const char*, bool), 299 (override)); 300 301 MOCK_METHOD(void, executeUserModifyUserEnable, (const char*, bool), 302 (override)); 303 304 MOCK_METHOD(std::vector<std::string>, getFailedAttempt, (const char*), 305 (override)); 306 307 protected: 308 static sdbusplus::bus_t busInTest; 309 std::string tempPamConfigFile; 310 }; 311 312 sdbusplus::bus_t UserMgrInTest::busInTest = sdbusplus::bus::new_default(); 313 314 TEST_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 323 TEST_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 334 TEST_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 346 TEST_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 357 TEST_F(UserMgrInTest, IsUserExistEmptyInputThrowsError) 358 { 359 EXPECT_THROW( 360 isUserExist(""), 361 sdbusplus::xyz::openbmc_project::Common::Error::InvalidArgument); 362 } 363 364 TEST_F(UserMgrInTest, ThrowForUserDoesNotExistThrowsError) 365 { 366 EXPECT_THROW(throwForUserDoesNotExist("whatever"), 367 sdbusplus::xyz::openbmc_project::User::Common::Error:: 368 UserNameDoesNotExist); 369 } 370 371 TEST_F(UserMgrInTest, ThrowForUserExistsThrowsError) 372 { 373 EXPECT_THROW( 374 throwForUserExists("root"), 375 sdbusplus::xyz::openbmc_project::User::Common::Error::UserNameExists); 376 } 377 378 TEST_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 388 TEST_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 398 TEST_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 407 TEST_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 414 TEST_F(UserMgrInTest, UserDeleteNotRootFailedWithInternalFailure) 415 { 416 EXPECT_THROW( 417 UserMgr::executeUserDelete("user0"), 418 sdbusplus::xyz::openbmc_project::Common::Error::InternalFailure); 419 } 420 421 TEST_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 430 TEST_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 440 TEST_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 456 TEST_F(UserMgrInTest, ThrowForInvalidPrivilegeThrowsWhenPrivilegeIsInvalid) 457 { 458 EXPECT_THROW( 459 throwForInvalidPrivilege("whatever"), 460 sdbusplus::xyz::openbmc_project::Common::Error::InvalidArgument); 461 } 462 463 TEST_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")); 468 } 469 470 TEST_F(UserMgrInTest, ThrowForInvalidGroupsThrowsWhenGroupIsInvalid) 471 { 472 EXPECT_THROW( 473 throwForInvalidGroups({"whatever"}), 474 sdbusplus::xyz::openbmc_project::Common::Error::InvalidArgument); 475 } 476 477 TEST_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 485 TEST_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 508 TEST_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 533 TEST_F(UserMgrInTest, DefaultUserModifyFailedWithInternalFailure) 534 { 535 EXPECT_THROW( 536 UserMgr::executeUserRename("user0", "user1"), 537 sdbusplus::xyz::openbmc_project::Common::Error::InternalFailure); 538 EXPECT_THROW( 539 UserMgr::executeUserModify("user0", "ssh", true), 540 sdbusplus::xyz::openbmc_project::Common::Error::InternalFailure); 541 } 542 543 TEST_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 558 TEST_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)); 572 } 573 574 TEST_F(UserMgrInTest, MinPasswordLengthReturnsIfValueIsTheSame) 575 { 576 initializeAccountPolicy(); 577 EXPECT_EQ(AccountPolicyIface::minPasswordLength(), 8); 578 UserMgr::minPasswordLength(8); 579 EXPECT_EQ(AccountPolicyIface::minPasswordLength(), 8); 580 } 581 582 TEST_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 593 TEST_F(UserMgrInTest, MinPasswordLengthOnSuccess) 594 { 595 initializeAccountPolicy(); 596 EXPECT_EQ(AccountPolicyIface::minPasswordLength(), 8); 597 UserMgr::minPasswordLength(16); 598 EXPECT_EQ(AccountPolicyIface::minPasswordLength(), 16); 599 } 600 601 TEST_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 612 TEST_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 622 TEST_F(UserMgrInTest, RememberOldPasswordTimesOnSuccess) 623 { 624 initializeAccountPolicy(); 625 EXPECT_EQ(AccountPolicyIface::rememberOldPasswordTimes(), 0); 626 UserMgr::rememberOldPasswordTimes(16); 627 EXPECT_EQ(AccountPolicyIface::rememberOldPasswordTimes(), 16); 628 } 629 630 TEST_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 641 TEST_F(UserMgrInTest, MaxLoginAttemptBeforeLockoutReturnsIfValueIsTheSame) 642 { 643 initializeAccountPolicy(); 644 EXPECT_EQ(AccountPolicyIface::maxLoginAttemptBeforeLockout(), 2); 645 UserMgr::maxLoginAttemptBeforeLockout(2); 646 EXPECT_EQ(AccountPolicyIface::maxLoginAttemptBeforeLockout(), 2); 647 } 648 649 TEST_F(UserMgrInTest, MaxLoginAttemptBeforeLockoutOnSuccess) 650 { 651 initializeAccountPolicy(); 652 EXPECT_EQ(AccountPolicyIface::maxLoginAttemptBeforeLockout(), 2); 653 UserMgr::maxLoginAttemptBeforeLockout(16); 654 EXPECT_EQ(AccountPolicyIface::maxLoginAttemptBeforeLockout(), 16); 655 } 656 657 TEST_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 667 TEST_F(UserMgrInTest, AccountUnlockTimeoutReturnsIfValueIsTheSame) 668 { 669 initializeAccountPolicy(); 670 EXPECT_EQ(AccountPolicyIface::accountUnlockTimeout(), 3); 671 UserMgr::accountUnlockTimeout(3); 672 EXPECT_EQ(AccountPolicyIface::accountUnlockTimeout(), 3); 673 } 674 675 TEST_F(UserMgrInTest, AccountUnlockTimeoutOnSuccess) 676 { 677 initializeAccountPolicy(); 678 EXPECT_EQ(AccountPolicyIface::accountUnlockTimeout(), 3); 679 UserMgr::accountUnlockTimeout(16); 680 EXPECT_EQ(AccountPolicyIface::accountUnlockTimeout(), 16); 681 } 682 683 TEST_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 693 TEST_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 709 TEST_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 732 TEST_F( 733 UserMgrInTest, 734 UserLockedForFailedAttemptReturnsFalseIfMaxLoginAttemptBeforeLockoutIsZero) 735 { 736 EXPECT_FALSE(userLockedForFailedAttempt("whatever")); 737 } 738 739 TEST_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 754 TEST_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 767 TEST_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 781 TEST_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 795 TEST_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 812 TEST_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 827 } // namespace user 828 } // namespace phosphor 829