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