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 21 using InternalFailure = 22 sdbusplus::xyz::openbmc_project::Common::Error::InternalFailure; 23 24 class TestUserMgr : public testing::Test 25 { 26 public: 27 sdbusplus::SdBusMock sdBusMock; 28 sdbusplus::bus_t bus; 29 MockManager mockManager; 30 31 TestUserMgr() : 32 bus(sdbusplus::get_mocked_new(&sdBusMock)), mockManager(bus, objpath) 33 {} 34 35 void createLocalUser(const std::string& userName, 36 std::vector<std::string> groupNames, 37 const std::string& priv, bool enabled) 38 { 39 sdbusplus::message::object_path tempObjPath(usersObjPath); 40 tempObjPath /= userName; 41 std::string userObj(tempObjPath); 42 mockManager.usersList.emplace( 43 userName, std::make_unique<phosphor::user::Users>( 44 mockManager.bus, userObj.c_str(), groupNames, priv, 45 enabled, mockManager)); 46 } 47 48 DbusUserObj createPrivilegeMapperDbusObject(void) 49 { 50 DbusUserObj object; 51 DbusUserObjValue objValue; 52 53 DbusUserObjPath objPath("/xyz/openbmc_project/user/ldap/openldap"); 54 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); 58 object.emplace(objPath, objValue); 59 60 DbusUserObjPath objectPath( 61 "/xyz/openbmc_project/user/ldap/openldap/role_map/1"); 62 std::string group = "ldapGroup"; 63 std::string priv = "priv-admin"; 64 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); 69 object.emplace(objectPath, objValue); 70 71 return object; 72 } 73 74 DbusUserObj createLdapConfigObjectWithoutPrivilegeMapper(void) 75 { 76 DbusUserObj object; 77 DbusUserObjValue objValue; 78 79 DbusUserObjPath objPath("/xyz/openbmc_project/user/ldap/openldap"); 80 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); 84 object.emplace(objPath, objValue); 85 return object; 86 } 87 }; 88 89 TEST_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 99 TEST_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"])); 115 EXPECT_EQ(false, std::get<bool>(userInfo["UserPasswordExpired"])); 116 EXPECT_EQ(false, std::get<bool>(userInfo["RemoteUser"])); 117 } 118 119 TEST_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 136 TEST_F(TestUserMgr, ldapUserWithoutPrivMapper) 137 { 138 UserInfoMap userInfo; 139 std::string userName = "ldapUser"; 140 std::string ldapGroup = "ldapGroup"; 141 142 EXPECT_CALL(mockManager, getLdapGroupName(userName)) 143 .WillRepeatedly(Return(ldapGroup)); 144 // Create LDAP config object without privilege mapper 145 DbusUserObj object = createLdapConfigObjectWithoutPrivilegeMapper(); 146 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 } 152 153 TEST(GetCSVFromVector, EmptyVectorReturnsEmptyString) 154 { 155 EXPECT_EQ(getCSVFromVector({}), ""); 156 } 157 158 TEST(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 165 TEST(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 176 TEST(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 188 namespace 189 { 190 inline constexpr const char* objectRootInTest = "/xyz/openbmc_project/user"; 191 192 // Fake config; referenced config on real BMC 193 inline 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) 212 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 213 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 214 password [success=ok default=die] pam_ipmicheck.so spec_grp_name=ipmi use_authtok 215 password [success=ok ignore=ignore default=die] pam_pwhistory.so debug enforce_for_root remember=0 use_authtok 216 password [success=ok default=die] pam_unix.so sha512 use_authtok 217 password [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 219 password 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 223 password required pam_permit.so 224 # and here are more per-package modules (the "Additional" block) 225 )"; 226 } // namespace 227 228 void 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 240 void removeFile(const std::string& filePath) 241 { 242 std::filesystem::remove(filePath); 243 } 244 245 class 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; 256 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)); 262 263 ON_CALL(*this, executeUserRename).WillByDefault(testing::Return()); 264 265 ON_CALL(*this, executeUserModify(testing::_, testing::_, testing::_)) 266 .WillByDefault(testing::Return()); 267 268 ON_CALL(*this, executeUserModifyUserEnable) 269 .WillByDefault(testing::Return()); 270 } 271 272 ~UserMgrInTest() override 273 { 274 EXPECT_NO_THROW(removeFile(tempPamConfigFile)); 275 } 276 277 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 284 MOCK_METHOD(void, executeUserRename, (const char*, const char*), 285 (override)); 286 287 MOCK_METHOD(void, executeUserModify, (const char*, const char*, bool), 288 (override)); 289 290 MOCK_METHOD(void, executeUserModifyUserEnable, (const char*, bool), 291 (override)); 292 293 MOCK_METHOD(std::vector<std::string>, getFailedAttempt, (const char*), 294 (override)); 295 296 protected: 297 static sdbusplus::bus_t busInTest; 298 std::string tempPamConfigFile; 299 }; 300 301 sdbusplus::bus_t UserMgrInTest::busInTest = sdbusplus::bus::new_default(); 302 303 TEST_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 312 TEST_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 323 TEST_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 335 TEST_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 346 TEST_F(UserMgrInTest, IsUserExistEmptyInputThrowsError) 347 { 348 EXPECT_THROW( 349 isUserExist(""), 350 sdbusplus::xyz::openbmc_project::Common::Error::InvalidArgument); 351 } 352 353 TEST_F(UserMgrInTest, ThrowForUserDoesNotExistThrowsError) 354 { 355 EXPECT_THROW(throwForUserDoesNotExist("whatever"), 356 sdbusplus::xyz::openbmc_project::User::Common::Error:: 357 UserNameDoesNotExist); 358 } 359 360 TEST_F(UserMgrInTest, ThrowForUserExistsThrowsError) 361 { 362 EXPECT_THROW( 363 throwForUserExists("root"), 364 sdbusplus::xyz::openbmc_project::User::Common::Error::UserNameExists); 365 } 366 367 TEST_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 377 TEST_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 387 TEST_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 396 TEST_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 403 TEST_F(UserMgrInTest, UserDeleteNotRootFailedWithInternalFailure) 404 { 405 EXPECT_THROW( 406 UserMgr::executeUserDelete("user0"), 407 sdbusplus::xyz::openbmc_project::Common::Error::InternalFailure); 408 } 409 410 TEST_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 419 TEST_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 429 TEST_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 445 TEST_F(UserMgrInTest, ThrowForInvalidPrivilegeThrowsWhenPrivilegeIsInvalid) 446 { 447 EXPECT_THROW( 448 throwForInvalidPrivilege("whatever"), 449 sdbusplus::xyz::openbmc_project::Common::Error::InvalidArgument); 450 } 451 452 TEST_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")); 457 EXPECT_NO_THROW(throwForInvalidPrivilege("priv-noaccess")); 458 } 459 460 TEST_F(UserMgrInTest, ThrowForInvalidGroupsThrowsWhenGroupIsInvalid) 461 { 462 EXPECT_THROW( 463 throwForInvalidGroups({"whatever"}), 464 sdbusplus::xyz::openbmc_project::Common::Error::InvalidArgument); 465 } 466 467 TEST_F(UserMgrInTest, ThrowForInvalidGroupsNoThrowWhenGroupIsValid) 468 { 469 EXPECT_NO_THROW(throwForInvalidGroups({"ipmi"})); 470 EXPECT_NO_THROW(throwForInvalidGroups({"ssh"})); 471 EXPECT_NO_THROW(throwForInvalidGroups({"redfish"})); 472 EXPECT_NO_THROW(throwForInvalidGroups({"web"})); 473 } 474 475 TEST_F(UserMgrInTest, RenameUserOnSuccess) 476 { 477 std::string username = "user001"; 478 EXPECT_NO_THROW( 479 UserMgr::createUser(username, {"redfish", "ssh"}, "priv-user", true)); 480 std::string newUsername = "user002"; 481 482 EXPECT_NO_THROW(UserMgr::renameUser(username, newUsername)); 483 484 // old username doesn't exist 485 EXPECT_THROW(getUserInfo(username), 486 sdbusplus::xyz::openbmc_project::User::Common::Error:: 487 UserNameDoesNotExist); 488 489 UserInfoMap userInfo = getUserInfo(newUsername); 490 EXPECT_EQ(std::get<Privilege>(userInfo["UserPrivilege"]), "priv-user"); 491 EXPECT_THAT(std::get<GroupList>(userInfo["UserGroups"]), 492 testing::UnorderedElementsAre("redfish", "ssh")); 493 EXPECT_EQ(std::get<UserEnabled>(userInfo["UserEnabled"]), true); 494 495 EXPECT_NO_THROW(UserMgr::deleteUser(newUsername)); 496 } 497 498 TEST_F(UserMgrInTest, RenameUserThrowsInternalFailureIfExecuteUserModifyFails) 499 { 500 std::string username = "user001"; 501 EXPECT_NO_THROW( 502 UserMgr::createUser(username, {"redfish", "ssh"}, "priv-user", true)); 503 std::string newUsername = "user002"; 504 505 EXPECT_CALL(*this, executeUserRename(testing::StrEq(username), 506 testing::StrEq(newUsername))) 507 .WillOnce(testing::Throw( 508 sdbusplus::xyz::openbmc_project::Common::Error::InternalFailure())); 509 EXPECT_THROW( 510 UserMgr::renameUser(username, newUsername), 511 sdbusplus::xyz::openbmc_project::Common::Error::InternalFailure); 512 513 // The original user is unchanged 514 UserInfoMap userInfo = getUserInfo(username); 515 EXPECT_EQ(std::get<Privilege>(userInfo["UserPrivilege"]), "priv-user"); 516 EXPECT_THAT(std::get<GroupList>(userInfo["UserGroups"]), 517 testing::UnorderedElementsAre("redfish", "ssh")); 518 EXPECT_EQ(std::get<UserEnabled>(userInfo["UserEnabled"]), true); 519 520 EXPECT_NO_THROW(UserMgr::deleteUser(username)); 521 } 522 523 TEST_F(UserMgrInTest, DefaultUserModifyFailedWithInternalFailure) 524 { 525 EXPECT_THROW( 526 UserMgr::executeUserRename("user0", "user1"), 527 sdbusplus::xyz::openbmc_project::Common::Error::InternalFailure); 528 EXPECT_THROW( 529 UserMgr::executeUserModify("user0", "ssh", true), 530 sdbusplus::xyz::openbmc_project::Common::Error::InternalFailure); 531 } 532 533 TEST_F(UserMgrInTest, UpdateGroupsAndPrivOnSuccess) 534 { 535 std::string username = "user001"; 536 EXPECT_NO_THROW( 537 UserMgr::createUser(username, {"redfish", "ssh"}, "priv-user", true)); 538 EXPECT_NO_THROW( 539 updateGroupsAndPriv(username, {"ipmi", "ssh"}, "priv-admin")); 540 UserInfoMap userInfo = getUserInfo(username); 541 EXPECT_EQ(std::get<Privilege>(userInfo["UserPrivilege"]), "priv-admin"); 542 EXPECT_THAT(std::get<GroupList>(userInfo["UserGroups"]), 543 testing::UnorderedElementsAre("ipmi", "ssh")); 544 EXPECT_EQ(std::get<UserEnabled>(userInfo["UserEnabled"]), true); 545 EXPECT_NO_THROW(UserMgr::deleteUser(username)); 546 } 547 548 TEST_F(UserMgrInTest, 549 UpdateGroupsAndPrivThrowsInternalFailureIfExecuteUserModifyFail) 550 { 551 std::string username = "user001"; 552 EXPECT_NO_THROW( 553 UserMgr::createUser(username, {"redfish", "ssh"}, "priv-user", true)); 554 EXPECT_CALL(*this, executeUserModify(testing::StrEq(username), testing::_, 555 testing::_)) 556 .WillOnce(testing::Throw( 557 sdbusplus::xyz::openbmc_project::Common::Error::InternalFailure())); 558 EXPECT_THROW( 559 updateGroupsAndPriv(username, {"ipmi", "ssh"}, "priv-admin"), 560 sdbusplus::xyz::openbmc_project::Common::Error::InternalFailure); 561 EXPECT_NO_THROW(UserMgr::deleteUser(username)); 562 } 563 564 TEST_F(UserMgrInTest, MinPasswordLengthReturnsIfValueIsTheSame) 565 { 566 initializeAccountPolicy(); 567 EXPECT_EQ(AccountPolicyIface::minPasswordLength(), 8); 568 UserMgr::minPasswordLength(8); 569 EXPECT_EQ(AccountPolicyIface::minPasswordLength(), 8); 570 } 571 572 TEST_F(UserMgrInTest, 573 MinPasswordLengthRejectsTooShortPasswordWithInvalidArgument) 574 { 575 initializeAccountPolicy(); 576 EXPECT_EQ(AccountPolicyIface::minPasswordLength(), 8); 577 EXPECT_THROW( 578 UserMgr::minPasswordLength(minPasswdLength - 1), 579 sdbusplus::xyz::openbmc_project::Common::Error::InvalidArgument); 580 EXPECT_EQ(AccountPolicyIface::minPasswordLength(), 8); 581 } 582 583 TEST_F(UserMgrInTest, MinPasswordLengthOnSuccess) 584 { 585 initializeAccountPolicy(); 586 EXPECT_EQ(AccountPolicyIface::minPasswordLength(), 8); 587 UserMgr::minPasswordLength(16); 588 EXPECT_EQ(AccountPolicyIface::minPasswordLength(), 16); 589 } 590 591 TEST_F(UserMgrInTest, MinPasswordLengthOnFailure) 592 { 593 EXPECT_NO_THROW(dumpStringToFile("whatever", tempPamConfigFile)); 594 initializeAccountPolicy(); 595 EXPECT_EQ(AccountPolicyIface::minPasswordLength(), 8); 596 EXPECT_THROW( 597 UserMgr::minPasswordLength(16), 598 sdbusplus::xyz::openbmc_project::Common::Error::InternalFailure); 599 EXPECT_EQ(AccountPolicyIface::minPasswordLength(), 8); 600 } 601 602 TEST_F(UserMgrInTest, RememberOldPasswordTimesReturnsIfValueIsTheSame) 603 { 604 initializeAccountPolicy(); 605 EXPECT_EQ(AccountPolicyIface::rememberOldPasswordTimes(), 0); 606 UserMgr::rememberOldPasswordTimes(8); 607 EXPECT_EQ(AccountPolicyIface::rememberOldPasswordTimes(), 8); 608 UserMgr::rememberOldPasswordTimes(8); 609 EXPECT_EQ(AccountPolicyIface::rememberOldPasswordTimes(), 8); 610 } 611 612 TEST_F(UserMgrInTest, RememberOldPasswordTimesOnSuccess) 613 { 614 initializeAccountPolicy(); 615 EXPECT_EQ(AccountPolicyIface::rememberOldPasswordTimes(), 0); 616 UserMgr::rememberOldPasswordTimes(16); 617 EXPECT_EQ(AccountPolicyIface::rememberOldPasswordTimes(), 16); 618 } 619 620 TEST_F(UserMgrInTest, RememberOldPasswordTimesOnFailure) 621 { 622 EXPECT_NO_THROW(dumpStringToFile("whatever", tempPamConfigFile)); 623 initializeAccountPolicy(); 624 EXPECT_EQ(AccountPolicyIface::rememberOldPasswordTimes(), 0); 625 EXPECT_THROW( 626 UserMgr::rememberOldPasswordTimes(16), 627 sdbusplus::xyz::openbmc_project::Common::Error::InternalFailure); 628 EXPECT_EQ(AccountPolicyIface::rememberOldPasswordTimes(), 0); 629 } 630 631 TEST_F(UserMgrInTest, MaxLoginAttemptBeforeLockoutReturnsIfValueIsTheSame) 632 { 633 initializeAccountPolicy(); 634 EXPECT_EQ(AccountPolicyIface::maxLoginAttemptBeforeLockout(), 2); 635 UserMgr::maxLoginAttemptBeforeLockout(2); 636 EXPECT_EQ(AccountPolicyIface::maxLoginAttemptBeforeLockout(), 2); 637 } 638 639 TEST_F(UserMgrInTest, MaxLoginAttemptBeforeLockoutOnSuccess) 640 { 641 initializeAccountPolicy(); 642 EXPECT_EQ(AccountPolicyIface::maxLoginAttemptBeforeLockout(), 2); 643 UserMgr::maxLoginAttemptBeforeLockout(16); 644 EXPECT_EQ(AccountPolicyIface::maxLoginAttemptBeforeLockout(), 16); 645 } 646 647 TEST_F(UserMgrInTest, MaxLoginAttemptBeforeLockoutOnFailure) 648 { 649 EXPECT_NO_THROW(dumpStringToFile("whatever", tempPamConfigFile)); 650 initializeAccountPolicy(); 651 EXPECT_THROW( 652 UserMgr::maxLoginAttemptBeforeLockout(16), 653 sdbusplus::xyz::openbmc_project::Common::Error::InternalFailure); 654 EXPECT_EQ(AccountPolicyIface::rememberOldPasswordTimes(), 0); 655 } 656 657 TEST_F(UserMgrInTest, AccountUnlockTimeoutReturnsIfValueIsTheSame) 658 { 659 initializeAccountPolicy(); 660 EXPECT_EQ(AccountPolicyIface::accountUnlockTimeout(), 3); 661 UserMgr::accountUnlockTimeout(3); 662 EXPECT_EQ(AccountPolicyIface::accountUnlockTimeout(), 3); 663 } 664 665 TEST_F(UserMgrInTest, AccountUnlockTimeoutOnSuccess) 666 { 667 initializeAccountPolicy(); 668 EXPECT_EQ(AccountPolicyIface::accountUnlockTimeout(), 3); 669 UserMgr::accountUnlockTimeout(16); 670 EXPECT_EQ(AccountPolicyIface::accountUnlockTimeout(), 16); 671 } 672 673 TEST_F(UserMgrInTest, AccountUnlockTimeoutOnFailure) 674 { 675 initializeAccountPolicy(); 676 EXPECT_NO_THROW(dumpStringToFile("whatever", tempPamConfigFile)); 677 EXPECT_THROW( 678 UserMgr::accountUnlockTimeout(16), 679 sdbusplus::xyz::openbmc_project::Common::Error::InternalFailure); 680 EXPECT_EQ(AccountPolicyIface::accountUnlockTimeout(), 3); 681 } 682 683 TEST_F(UserMgrInTest, UserEnableOnSuccess) 684 { 685 std::string username = "user001"; 686 EXPECT_NO_THROW( 687 UserMgr::createUser(username, {"redfish", "ssh"}, "priv-user", true)); 688 UserInfoMap userInfo = getUserInfo(username); 689 EXPECT_EQ(std::get<UserEnabled>(userInfo["UserEnabled"]), true); 690 691 EXPECT_NO_THROW(userEnable(username, false)); 692 693 userInfo = getUserInfo(username); 694 EXPECT_EQ(std::get<UserEnabled>(userInfo["UserEnabled"]), false); 695 696 EXPECT_NO_THROW(UserMgr::deleteUser(username)); 697 } 698 699 TEST_F(UserMgrInTest, UserEnableThrowsInternalFailureIfExecuteUserModifyFail) 700 { 701 std::string username = "user001"; 702 EXPECT_NO_THROW( 703 UserMgr::createUser(username, {"redfish", "ssh"}, "priv-user", true)); 704 UserInfoMap userInfo = getUserInfo(username); 705 EXPECT_EQ(std::get<UserEnabled>(userInfo["UserEnabled"]), true); 706 707 EXPECT_CALL(*this, executeUserModifyUserEnable(testing::StrEq(username), 708 testing::Eq(false))) 709 .WillOnce(testing::Throw( 710 sdbusplus::xyz::openbmc_project::Common::Error::InternalFailure())); 711 EXPECT_THROW( 712 userEnable(username, false), 713 sdbusplus::xyz::openbmc_project::Common::Error::InternalFailure); 714 715 userInfo = getUserInfo(username); 716 // Stay unchanged 717 EXPECT_EQ(std::get<UserEnabled>(userInfo["UserEnabled"]), true); 718 719 EXPECT_NO_THROW(UserMgr::deleteUser(username)); 720 } 721 722 TEST_F( 723 UserMgrInTest, 724 UserLockedForFailedAttemptReturnsFalseIfMaxLoginAttemptBeforeLockoutIsZero) 725 { 726 EXPECT_FALSE(userLockedForFailedAttempt("whatever")); 727 } 728 729 TEST_F(UserMgrInTest, UserLockedForFailedAttemptZeroFailuresReturnsFalse) 730 { 731 std::string username = "user001"; 732 initializeAccountPolicy(); 733 // Example output from BMC 734 // root@s7106:~# pam_tally2 -u root 735 // Login Failures Latest failure From 736 // root 0 737 std::vector<std::string> output = {"whatever", "root\t0"}; 738 EXPECT_CALL(*this, getFailedAttempt(testing::StrEq(username.c_str()))) 739 .WillOnce(testing::Return(output)); 740 741 EXPECT_FALSE(userLockedForFailedAttempt(username)); 742 } 743 744 TEST_F(UserMgrInTest, UserLockedForFailedAttemptFailIfGetFailedAttemptFail) 745 { 746 std::string username = "user001"; 747 initializeAccountPolicy(); 748 EXPECT_CALL(*this, getFailedAttempt(testing::StrEq(username.c_str()))) 749 .WillOnce(testing::Throw( 750 sdbusplus::xyz::openbmc_project::Common::Error::InternalFailure())); 751 752 EXPECT_THROW( 753 userLockedForFailedAttempt(username), 754 sdbusplus::xyz::openbmc_project::Common::Error::InternalFailure); 755 } 756 757 TEST_F(UserMgrInTest, 758 UserLockedForFailedAttemptThrowsInternalFailureIfFailAttemptsOutOfRange) 759 { 760 std::string username = "user001"; 761 initializeAccountPolicy(); 762 std::vector<std::string> output = {"whatever", "root\t1000000"}; 763 EXPECT_CALL(*this, getFailedAttempt(testing::StrEq(username.c_str()))) 764 .WillOnce(testing::Return(output)); 765 766 EXPECT_THROW( 767 userLockedForFailedAttempt(username), 768 sdbusplus::xyz::openbmc_project::Common::Error::InternalFailure); 769 } 770 771 TEST_F(UserMgrInTest, 772 UserLockedForFailedAttemptThrowsInternalFailureIfNoFailDateTime) 773 { 774 std::string username = "user001"; 775 initializeAccountPolicy(); 776 std::vector<std::string> output = {"whatever", "root\t2"}; 777 EXPECT_CALL(*this, getFailedAttempt(testing::StrEq(username.c_str()))) 778 .WillOnce(testing::Return(output)); 779 780 EXPECT_THROW( 781 userLockedForFailedAttempt(username), 782 sdbusplus::xyz::openbmc_project::Common::Error::InternalFailure); 783 } 784 785 TEST_F(UserMgrInTest, 786 UserLockedForFailedAttemptThrowsInternalFailureIfWrongDateFormat) 787 { 788 std::string username = "user001"; 789 initializeAccountPolicy(); 790 791 // Choose a date in the past. 792 std::vector<std::string> output = {"whatever", 793 "root\t2\t10/24/2002\t00:00:00"}; 794 EXPECT_CALL(*this, getFailedAttempt(testing::StrEq(username.c_str()))) 795 .WillOnce(testing::Return(output)); 796 797 EXPECT_THROW( 798 userLockedForFailedAttempt(username), 799 sdbusplus::xyz::openbmc_project::Common::Error::InternalFailure); 800 } 801 802 TEST_F(UserMgrInTest, 803 UserLockedForFailedAttemptReturnsFalseIfLastFailTimeHasTimedOut) 804 { 805 std::string username = "user001"; 806 initializeAccountPolicy(); 807 808 // Choose a date in the past. 809 std::vector<std::string> output = {"whatever", 810 "root\t2\t10/24/02\t00:00:00"}; 811 EXPECT_CALL(*this, getFailedAttempt(testing::StrEq(username.c_str()))) 812 .WillOnce(testing::Return(output)); 813 814 EXPECT_EQ(userLockedForFailedAttempt(username), false); 815 } 816 817 } // namespace user 818 } // namespace phosphor 819