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 config; referenced config 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_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 226 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 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 } // namespace 240 241 void dumpStringToFile(const std::string& str, const std::string& filePath) 242 { 243 std::ofstream outputFileStream; 244 245 outputFileStream.exceptions(std::ofstream::failbit | std::ofstream::badbit | 246 std::ofstream::eofbit); 247 248 outputFileStream.open(filePath, std::ios::out); 249 outputFileStream << str << "\n" << std::flush; 250 outputFileStream.close(); 251 } 252 253 void removeFile(const std::string& filePath) 254 { 255 std::filesystem::remove(filePath); 256 } 257 258 class UserMgrInTest : public testing::Test, public UserMgr 259 { 260 public: 261 UserMgrInTest() : UserMgr(busInTest, objectRootInTest) 262 { 263 tempPamConfigFile = "/tmp/test-data-XXXXXX"; 264 mktemp(tempPamConfigFile.data()); 265 EXPECT_NO_THROW(dumpStringToFile(rawConfig, tempPamConfigFile)); 266 // Set config files to test files 267 pamPasswdConfigFile = tempPamConfigFile; 268 pamAuthConfigFile = tempPamConfigFile; 269 270 ON_CALL(*this, executeUserAdd).WillByDefault(testing::Return()); 271 272 ON_CALL(*this, executeUserDelete).WillByDefault(testing::Return()); 273 274 ON_CALL(*this, getIpmiUsersCount).WillByDefault(testing::Return(0)); 275 276 ON_CALL(*this, executeUserRename).WillByDefault(testing::Return()); 277 278 ON_CALL(*this, executeUserModify(testing::_, testing::_, testing::_)) 279 .WillByDefault(testing::Return()); 280 281 ON_CALL(*this, executeUserModifyUserEnable) 282 .WillByDefault(testing::Return()); 283 284 ON_CALL(*this, executeGroupCreation(testing::_)) 285 .WillByDefault(testing::Return()); 286 287 ON_CALL(*this, executeGroupDeletion(testing::_)) 288 .WillByDefault(testing::Return()); 289 290 ON_CALL(*this, executeGroupCreation).WillByDefault(testing::Return()); 291 292 ON_CALL(*this, executeGroupDeletion).WillByDefault(testing::Return()); 293 } 294 295 ~UserMgrInTest() override 296 { 297 EXPECT_NO_THROW(removeFile(tempPamConfigFile)); 298 } 299 300 MOCK_METHOD(void, executeUserAdd, (const char*, const char*, bool, bool), 301 (override)); 302 303 MOCK_METHOD(void, executeUserDelete, (const char*), (override)); 304 305 MOCK_METHOD(size_t, getIpmiUsersCount, (), (override)); 306 307 MOCK_METHOD(void, executeUserRename, (const char*, const char*), 308 (override)); 309 310 MOCK_METHOD(void, executeUserModify, (const char*, const char*, bool), 311 (override)); 312 313 MOCK_METHOD(void, executeUserModifyUserEnable, (const char*, bool), 314 (override)); 315 316 MOCK_METHOD(std::vector<std::string>, getFailedAttempt, (const char*), 317 (override)); 318 319 MOCK_METHOD(void, executeGroupCreation, (const char*), (override)); 320 321 MOCK_METHOD(void, executeGroupDeletion, (const char*), (override)); 322 323 protected: 324 static sdbusplus::bus_t busInTest; 325 std::string tempPamConfigFile; 326 }; 327 328 sdbusplus::bus_t UserMgrInTest::busInTest = sdbusplus::bus::new_default(); 329 330 TEST_F(UserMgrInTest, GetPamModuleArgValueOnSuccess) 331 { 332 std::string minLen; 333 EXPECT_EQ(getPamModuleArgValue("pam_tally2.so", "minlen", minLen), 0); 334 EXPECT_EQ(minLen, "8"); 335 EXPECT_EQ(getPamModuleArgValue("pam_cracklib.so", "minlen", minLen), 0); 336 EXPECT_EQ(minLen, "8"); 337 } 338 339 TEST_F(UserMgrInTest, SetPamModuleArgValueOnSuccess) 340 { 341 EXPECT_EQ(setPamModuleArgValue("pam_cracklib.so", "minlen", "16"), 0); 342 EXPECT_EQ(setPamModuleArgValue("pam_tally2.so", "minlen", "16"), 0); 343 std::string minLen; 344 EXPECT_EQ(getPamModuleArgValue("pam_tally2.so", "minlen", minLen), 0); 345 EXPECT_EQ(minLen, "16"); 346 EXPECT_EQ(getPamModuleArgValue("pam_cracklib.so", "minlen", minLen), 0); 347 EXPECT_EQ(minLen, "16"); 348 } 349 350 TEST_F(UserMgrInTest, GetPamModuleArgValueOnFailure) 351 { 352 EXPECT_NO_THROW(dumpStringToFile("whatever", tempPamConfigFile)); 353 std::string minLen; 354 EXPECT_EQ(getPamModuleArgValue("pam_tally2.so", "minlen", minLen), -1); 355 EXPECT_EQ(getPamModuleArgValue("pam_cracklib.so", "minlen", minLen), -1); 356 357 EXPECT_NO_THROW(removeFile(tempPamConfigFile)); 358 EXPECT_EQ(getPamModuleArgValue("pam_tally2.so", "minlen", minLen), -1); 359 EXPECT_EQ(getPamModuleArgValue("pam_cracklib.so", "minlen", minLen), -1); 360 } 361 362 TEST_F(UserMgrInTest, SetPamModuleArgValueOnFailure) 363 { 364 EXPECT_NO_THROW(dumpStringToFile("whatever", tempPamConfigFile)); 365 EXPECT_EQ(setPamModuleArgValue("pam_cracklib.so", "minlen", "16"), -1); 366 EXPECT_EQ(setPamModuleArgValue("pam_tally2.so", "minlen", "16"), -1); 367 368 EXPECT_NO_THROW(removeFile(tempPamConfigFile)); 369 EXPECT_EQ(setPamModuleArgValue("pam_cracklib.so", "minlen", "16"), -1); 370 EXPECT_EQ(setPamModuleArgValue("pam_tally2.so", "minlen", "16"), -1); 371 } 372 373 TEST_F(UserMgrInTest, IsUserExistEmptyInputThrowsError) 374 { 375 EXPECT_THROW( 376 isUserExist(""), 377 sdbusplus::xyz::openbmc_project::Common::Error::InvalidArgument); 378 } 379 380 TEST_F(UserMgrInTest, ThrowForUserDoesNotExistThrowsError) 381 { 382 EXPECT_THROW(throwForUserDoesNotExist("whatever"), 383 sdbusplus::xyz::openbmc_project::User::Common::Error:: 384 UserNameDoesNotExist); 385 } 386 387 TEST_F(UserMgrInTest, ThrowForUserExistsThrowsError) 388 { 389 EXPECT_THROW( 390 throwForUserExists("root"), 391 sdbusplus::xyz::openbmc_project::User::Common::Error::UserNameExists); 392 } 393 394 TEST_F( 395 UserMgrInTest, 396 ThrowForUserNameConstraintsExceedIpmiMaxUserNameLenThrowsUserNameGroupFail) 397 { 398 std::string strWith17Chars(17, 'A'); 399 EXPECT_THROW(throwForUserNameConstraints(strWith17Chars, {"ipmi"}), 400 sdbusplus::xyz::openbmc_project::User::Common::Error:: 401 UserNameGroupFail); 402 } 403 404 TEST_F( 405 UserMgrInTest, 406 ThrowForUserNameConstraintsExceedSystemMaxUserNameLenThrowsInvalidArgument) 407 { 408 std::string strWith31Chars(31, 'A'); 409 EXPECT_THROW( 410 throwForUserNameConstraints(strWith31Chars, {}), 411 sdbusplus::xyz::openbmc_project::Common::Error::InvalidArgument); 412 } 413 414 TEST_F(UserMgrInTest, 415 ThrowForUserNameConstraintsRegexMismatchThrowsInvalidArgument) 416 { 417 std::string startWithNumber = "0ABC"; 418 EXPECT_THROW( 419 throwForUserNameConstraints(startWithNumber, {"ipmi"}), 420 sdbusplus::xyz::openbmc_project::Common::Error::InvalidArgument); 421 } 422 423 TEST_F(UserMgrInTest, UserAddNotRootFailedWithInternalFailure) 424 { 425 EXPECT_THROW( 426 UserMgr::executeUserAdd("user0", "ipmi,ssh", true, true), 427 sdbusplus::xyz::openbmc_project::Common::Error::InternalFailure); 428 } 429 430 TEST_F(UserMgrInTest, UserDeleteNotRootFailedWithInternalFailure) 431 { 432 EXPECT_THROW( 433 UserMgr::executeUserDelete("user0"), 434 sdbusplus::xyz::openbmc_project::Common::Error::InternalFailure); 435 } 436 437 TEST_F(UserMgrInTest, 438 ThrowForMaxGrpUserCountThrowsNoResourceWhenIpmiUserExceedLimit) 439 { 440 EXPECT_CALL(*this, getIpmiUsersCount()).WillOnce(Return(ipmiMaxUsers)); 441 EXPECT_THROW( 442 throwForMaxGrpUserCount({"ipmi"}), 443 sdbusplus::xyz::openbmc_project::User::Common::Error::NoResource); 444 } 445 446 TEST_F(UserMgrInTest, CreateUserThrowsInternalFailureWhenExecuteUserAddFails) 447 { 448 EXPECT_CALL(*this, executeUserAdd) 449 .WillOnce(testing::Throw( 450 sdbusplus::xyz::openbmc_project::Common::Error::InternalFailure())); 451 EXPECT_THROW( 452 createUser("whatever", {"redfish"}, "", true), 453 sdbusplus::xyz::openbmc_project::Common::Error::InternalFailure); 454 } 455 456 TEST_F(UserMgrInTest, DeleteUserThrowsInternalFailureWhenExecuteUserDeleteFails) 457 { 458 std::string username = "user"; 459 EXPECT_NO_THROW( 460 UserMgr::createUser(username, {"redfish", "ssh"}, "priv-user", true)); 461 EXPECT_CALL(*this, executeUserDelete(testing::StrEq(username))) 462 .WillOnce(testing::Throw( 463 sdbusplus::xyz::openbmc_project::Common::Error::InternalFailure())) 464 .WillOnce(testing::DoDefault()); 465 466 EXPECT_THROW( 467 deleteUser(username), 468 sdbusplus::xyz::openbmc_project::Common::Error::InternalFailure); 469 EXPECT_NO_THROW(UserMgr::deleteUser(username)); 470 } 471 472 TEST_F(UserMgrInTest, ThrowForInvalidPrivilegeThrowsWhenPrivilegeIsInvalid) 473 { 474 EXPECT_THROW( 475 throwForInvalidPrivilege("whatever"), 476 sdbusplus::xyz::openbmc_project::Common::Error::InvalidArgument); 477 } 478 479 TEST_F(UserMgrInTest, ThrowForInvalidPrivilegeNoThrowWhenPrivilegeIsValid) 480 { 481 EXPECT_NO_THROW(throwForInvalidPrivilege("priv-admin")); 482 EXPECT_NO_THROW(throwForInvalidPrivilege("priv-operator")); 483 EXPECT_NO_THROW(throwForInvalidPrivilege("priv-user")); 484 } 485 486 TEST_F(UserMgrInTest, ThrowForInvalidGroupsThrowsWhenGroupIsInvalid) 487 { 488 EXPECT_THROW( 489 throwForInvalidGroups({"whatever"}), 490 sdbusplus::xyz::openbmc_project::Common::Error::InvalidArgument); 491 } 492 493 TEST_F(UserMgrInTest, ThrowForInvalidGroupsNoThrowWhenGroupIsValid) 494 { 495 EXPECT_NO_THROW(throwForInvalidGroups({"ipmi"})); 496 EXPECT_NO_THROW(throwForInvalidGroups({"ssh"})); 497 EXPECT_NO_THROW(throwForInvalidGroups({"redfish"})); 498 EXPECT_NO_THROW(throwForInvalidGroups({"web"})); 499 } 500 501 TEST_F(UserMgrInTest, RenameUserOnSuccess) 502 { 503 std::string username = "user001"; 504 EXPECT_NO_THROW( 505 UserMgr::createUser(username, {"redfish", "ssh"}, "priv-user", true)); 506 std::string newUsername = "user002"; 507 508 EXPECT_NO_THROW(UserMgr::renameUser(username, newUsername)); 509 510 // old username doesn't exist 511 EXPECT_THROW(getUserInfo(username), 512 sdbusplus::xyz::openbmc_project::User::Common::Error:: 513 UserNameDoesNotExist); 514 515 UserInfoMap userInfo = getUserInfo(newUsername); 516 EXPECT_EQ(std::get<Privilege>(userInfo["UserPrivilege"]), "priv-user"); 517 EXPECT_THAT(std::get<GroupList>(userInfo["UserGroups"]), 518 testing::UnorderedElementsAre("redfish", "ssh")); 519 EXPECT_EQ(std::get<UserEnabled>(userInfo["UserEnabled"]), true); 520 521 EXPECT_NO_THROW(UserMgr::deleteUser(newUsername)); 522 } 523 524 TEST_F(UserMgrInTest, RenameUserThrowsInternalFailureIfExecuteUserModifyFails) 525 { 526 std::string username = "user001"; 527 EXPECT_NO_THROW( 528 UserMgr::createUser(username, {"redfish", "ssh"}, "priv-user", true)); 529 std::string newUsername = "user002"; 530 531 EXPECT_CALL(*this, executeUserRename(testing::StrEq(username), 532 testing::StrEq(newUsername))) 533 .WillOnce(testing::Throw( 534 sdbusplus::xyz::openbmc_project::Common::Error::InternalFailure())); 535 EXPECT_THROW( 536 UserMgr::renameUser(username, newUsername), 537 sdbusplus::xyz::openbmc_project::Common::Error::InternalFailure); 538 539 // The original user is unchanged 540 UserInfoMap userInfo = getUserInfo(username); 541 EXPECT_EQ(std::get<Privilege>(userInfo["UserPrivilege"]), "priv-user"); 542 EXPECT_THAT(std::get<GroupList>(userInfo["UserGroups"]), 543 testing::UnorderedElementsAre("redfish", "ssh")); 544 EXPECT_EQ(std::get<UserEnabled>(userInfo["UserEnabled"]), true); 545 546 EXPECT_NO_THROW(UserMgr::deleteUser(username)); 547 } 548 549 TEST_F(UserMgrInTest, DefaultUserModifyFailedWithInternalFailure) 550 { 551 EXPECT_THROW( 552 UserMgr::executeUserRename("user0", "user1"), 553 sdbusplus::xyz::openbmc_project::Common::Error::InternalFailure); 554 EXPECT_THROW( 555 UserMgr::executeUserModify("user0", "ssh", true), 556 sdbusplus::xyz::openbmc_project::Common::Error::InternalFailure); 557 } 558 559 TEST_F(UserMgrInTest, UpdateGroupsAndPrivOnSuccess) 560 { 561 std::string username = "user001"; 562 EXPECT_NO_THROW( 563 UserMgr::createUser(username, {"redfish", "ssh"}, "priv-user", true)); 564 EXPECT_NO_THROW( 565 updateGroupsAndPriv(username, {"ipmi", "ssh"}, "priv-admin")); 566 UserInfoMap userInfo = getUserInfo(username); 567 EXPECT_EQ(std::get<Privilege>(userInfo["UserPrivilege"]), "priv-admin"); 568 EXPECT_THAT(std::get<GroupList>(userInfo["UserGroups"]), 569 testing::UnorderedElementsAre("ipmi", "ssh")); 570 EXPECT_EQ(std::get<UserEnabled>(userInfo["UserEnabled"]), true); 571 EXPECT_NO_THROW(UserMgr::deleteUser(username)); 572 } 573 574 TEST_F(UserMgrInTest, 575 UpdateGroupsAndPrivThrowsInternalFailureIfExecuteUserModifyFail) 576 { 577 std::string username = "user001"; 578 EXPECT_NO_THROW( 579 UserMgr::createUser(username, {"redfish", "ssh"}, "priv-user", true)); 580 EXPECT_CALL(*this, executeUserModify(testing::StrEq(username), testing::_, 581 testing::_)) 582 .WillOnce(testing::Throw( 583 sdbusplus::xyz::openbmc_project::Common::Error::InternalFailure())); 584 EXPECT_THROW( 585 updateGroupsAndPriv(username, {"ipmi", "ssh"}, "priv-admin"), 586 sdbusplus::xyz::openbmc_project::Common::Error::InternalFailure); 587 EXPECT_NO_THROW(UserMgr::deleteUser(username)); 588 } 589 590 TEST_F(UserMgrInTest, MinPasswordLengthReturnsIfValueIsTheSame) 591 { 592 initializeAccountPolicy(); 593 EXPECT_EQ(AccountPolicyIface::minPasswordLength(), 8); 594 UserMgr::minPasswordLength(8); 595 EXPECT_EQ(AccountPolicyIface::minPasswordLength(), 8); 596 } 597 598 TEST_F(UserMgrInTest, 599 MinPasswordLengthRejectsTooShortPasswordWithInvalidArgument) 600 { 601 initializeAccountPolicy(); 602 EXPECT_EQ(AccountPolicyIface::minPasswordLength(), 8); 603 EXPECT_THROW( 604 UserMgr::minPasswordLength(minPasswdLength - 1), 605 sdbusplus::xyz::openbmc_project::Common::Error::InvalidArgument); 606 EXPECT_EQ(AccountPolicyIface::minPasswordLength(), 8); 607 } 608 609 TEST_F(UserMgrInTest, MinPasswordLengthOnSuccess) 610 { 611 initializeAccountPolicy(); 612 EXPECT_EQ(AccountPolicyIface::minPasswordLength(), 8); 613 UserMgr::minPasswordLength(16); 614 EXPECT_EQ(AccountPolicyIface::minPasswordLength(), 16); 615 } 616 617 TEST_F(UserMgrInTest, MinPasswordLengthOnFailure) 618 { 619 EXPECT_NO_THROW(dumpStringToFile("whatever", tempPamConfigFile)); 620 initializeAccountPolicy(); 621 EXPECT_EQ(AccountPolicyIface::minPasswordLength(), 8); 622 EXPECT_THROW( 623 UserMgr::minPasswordLength(16), 624 sdbusplus::xyz::openbmc_project::Common::Error::InternalFailure); 625 EXPECT_EQ(AccountPolicyIface::minPasswordLength(), 8); 626 } 627 628 TEST_F(UserMgrInTest, RememberOldPasswordTimesReturnsIfValueIsTheSame) 629 { 630 initializeAccountPolicy(); 631 EXPECT_EQ(AccountPolicyIface::rememberOldPasswordTimes(), 0); 632 UserMgr::rememberOldPasswordTimes(8); 633 EXPECT_EQ(AccountPolicyIface::rememberOldPasswordTimes(), 8); 634 UserMgr::rememberOldPasswordTimes(8); 635 EXPECT_EQ(AccountPolicyIface::rememberOldPasswordTimes(), 8); 636 } 637 638 TEST_F(UserMgrInTest, RememberOldPasswordTimesOnSuccess) 639 { 640 initializeAccountPolicy(); 641 EXPECT_EQ(AccountPolicyIface::rememberOldPasswordTimes(), 0); 642 UserMgr::rememberOldPasswordTimes(16); 643 EXPECT_EQ(AccountPolicyIface::rememberOldPasswordTimes(), 16); 644 } 645 646 TEST_F(UserMgrInTest, RememberOldPasswordTimesOnFailure) 647 { 648 EXPECT_NO_THROW(dumpStringToFile("whatever", tempPamConfigFile)); 649 initializeAccountPolicy(); 650 EXPECT_EQ(AccountPolicyIface::rememberOldPasswordTimes(), 0); 651 EXPECT_THROW( 652 UserMgr::rememberOldPasswordTimes(16), 653 sdbusplus::xyz::openbmc_project::Common::Error::InternalFailure); 654 EXPECT_EQ(AccountPolicyIface::rememberOldPasswordTimes(), 0); 655 } 656 657 TEST_F(UserMgrInTest, MaxLoginAttemptBeforeLockoutReturnsIfValueIsTheSame) 658 { 659 initializeAccountPolicy(); 660 EXPECT_EQ(AccountPolicyIface::maxLoginAttemptBeforeLockout(), 2); 661 UserMgr::maxLoginAttemptBeforeLockout(2); 662 EXPECT_EQ(AccountPolicyIface::maxLoginAttemptBeforeLockout(), 2); 663 } 664 665 TEST_F(UserMgrInTest, MaxLoginAttemptBeforeLockoutOnSuccess) 666 { 667 initializeAccountPolicy(); 668 EXPECT_EQ(AccountPolicyIface::maxLoginAttemptBeforeLockout(), 2); 669 UserMgr::maxLoginAttemptBeforeLockout(16); 670 EXPECT_EQ(AccountPolicyIface::maxLoginAttemptBeforeLockout(), 16); 671 } 672 673 TEST_F(UserMgrInTest, MaxLoginAttemptBeforeLockoutOnFailure) 674 { 675 EXPECT_NO_THROW(dumpStringToFile("whatever", tempPamConfigFile)); 676 initializeAccountPolicy(); 677 EXPECT_THROW( 678 UserMgr::maxLoginAttemptBeforeLockout(16), 679 sdbusplus::xyz::openbmc_project::Common::Error::InternalFailure); 680 EXPECT_EQ(AccountPolicyIface::rememberOldPasswordTimes(), 0); 681 } 682 683 TEST_F(UserMgrInTest, AccountUnlockTimeoutReturnsIfValueIsTheSame) 684 { 685 initializeAccountPolicy(); 686 EXPECT_EQ(AccountPolicyIface::accountUnlockTimeout(), 3); 687 UserMgr::accountUnlockTimeout(3); 688 EXPECT_EQ(AccountPolicyIface::accountUnlockTimeout(), 3); 689 } 690 691 TEST_F(UserMgrInTest, AccountUnlockTimeoutOnSuccess) 692 { 693 initializeAccountPolicy(); 694 EXPECT_EQ(AccountPolicyIface::accountUnlockTimeout(), 3); 695 UserMgr::accountUnlockTimeout(16); 696 EXPECT_EQ(AccountPolicyIface::accountUnlockTimeout(), 16); 697 } 698 699 TEST_F(UserMgrInTest, AccountUnlockTimeoutOnFailure) 700 { 701 initializeAccountPolicy(); 702 EXPECT_NO_THROW(dumpStringToFile("whatever", tempPamConfigFile)); 703 EXPECT_THROW( 704 UserMgr::accountUnlockTimeout(16), 705 sdbusplus::xyz::openbmc_project::Common::Error::InternalFailure); 706 EXPECT_EQ(AccountPolicyIface::accountUnlockTimeout(), 3); 707 } 708 709 TEST_F(UserMgrInTest, UserEnableOnSuccess) 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_NO_THROW(userEnable(username, false)); 718 719 userInfo = getUserInfo(username); 720 EXPECT_EQ(std::get<UserEnabled>(userInfo["UserEnabled"]), false); 721 722 EXPECT_NO_THROW(UserMgr::deleteUser(username)); 723 } 724 725 TEST_F(UserMgrInTest, UserEnableThrowsInternalFailureIfExecuteUserModifyFail) 726 { 727 std::string username = "user001"; 728 EXPECT_NO_THROW( 729 UserMgr::createUser(username, {"redfish", "ssh"}, "priv-user", true)); 730 UserInfoMap userInfo = getUserInfo(username); 731 EXPECT_EQ(std::get<UserEnabled>(userInfo["UserEnabled"]), true); 732 733 EXPECT_CALL(*this, executeUserModifyUserEnable(testing::StrEq(username), 734 testing::Eq(false))) 735 .WillOnce(testing::Throw( 736 sdbusplus::xyz::openbmc_project::Common::Error::InternalFailure())); 737 EXPECT_THROW( 738 userEnable(username, false), 739 sdbusplus::xyz::openbmc_project::Common::Error::InternalFailure); 740 741 userInfo = getUserInfo(username); 742 // Stay unchanged 743 EXPECT_EQ(std::get<UserEnabled>(userInfo["UserEnabled"]), true); 744 745 EXPECT_NO_THROW(UserMgr::deleteUser(username)); 746 } 747 748 TEST_F( 749 UserMgrInTest, 750 UserLockedForFailedAttemptReturnsFalseIfMaxLoginAttemptBeforeLockoutIsZero) 751 { 752 EXPECT_FALSE(userLockedForFailedAttempt("whatever")); 753 } 754 755 TEST_F(UserMgrInTest, UserLockedForFailedAttemptZeroFailuresReturnsFalse) 756 { 757 std::string username = "user001"; 758 initializeAccountPolicy(); 759 // Example output from BMC 760 // root@s7106:~# pam_tally2 -u root 761 // Login Failures Latest failure From 762 // root 0 763 std::vector<std::string> output = {"whatever", "root\t0"}; 764 EXPECT_CALL(*this, getFailedAttempt(testing::StrEq(username.c_str()))) 765 .WillOnce(testing::Return(output)); 766 767 EXPECT_FALSE(userLockedForFailedAttempt(username)); 768 } 769 770 TEST_F(UserMgrInTest, UserLockedForFailedAttemptFailIfGetFailedAttemptFail) 771 { 772 std::string username = "user001"; 773 initializeAccountPolicy(); 774 EXPECT_CALL(*this, getFailedAttempt(testing::StrEq(username.c_str()))) 775 .WillOnce(testing::Throw( 776 sdbusplus::xyz::openbmc_project::Common::Error::InternalFailure())); 777 778 EXPECT_THROW( 779 userLockedForFailedAttempt(username), 780 sdbusplus::xyz::openbmc_project::Common::Error::InternalFailure); 781 } 782 783 TEST_F(UserMgrInTest, 784 UserLockedForFailedAttemptThrowsInternalFailureIfFailAttemptsOutOfRange) 785 { 786 std::string username = "user001"; 787 initializeAccountPolicy(); 788 std::vector<std::string> output = {"whatever", "root\t1000000"}; 789 EXPECT_CALL(*this, getFailedAttempt(testing::StrEq(username.c_str()))) 790 .WillOnce(testing::Return(output)); 791 792 EXPECT_THROW( 793 userLockedForFailedAttempt(username), 794 sdbusplus::xyz::openbmc_project::Common::Error::InternalFailure); 795 } 796 797 TEST_F(UserMgrInTest, 798 UserLockedForFailedAttemptThrowsInternalFailureIfNoFailDateTime) 799 { 800 std::string username = "user001"; 801 initializeAccountPolicy(); 802 std::vector<std::string> output = {"whatever", "root\t2"}; 803 EXPECT_CALL(*this, getFailedAttempt(testing::StrEq(username.c_str()))) 804 .WillOnce(testing::Return(output)); 805 806 EXPECT_THROW( 807 userLockedForFailedAttempt(username), 808 sdbusplus::xyz::openbmc_project::Common::Error::InternalFailure); 809 } 810 811 TEST_F(UserMgrInTest, 812 UserLockedForFailedAttemptThrowsInternalFailureIfWrongDateFormat) 813 { 814 std::string username = "user001"; 815 initializeAccountPolicy(); 816 817 // Choose a date in the past. 818 std::vector<std::string> output = {"whatever", 819 "root\t2\t10/24/2002\t00:00:00"}; 820 EXPECT_CALL(*this, getFailedAttempt(testing::StrEq(username.c_str()))) 821 .WillOnce(testing::Return(output)); 822 823 EXPECT_THROW( 824 userLockedForFailedAttempt(username), 825 sdbusplus::xyz::openbmc_project::Common::Error::InternalFailure); 826 } 827 828 TEST_F(UserMgrInTest, 829 UserLockedForFailedAttemptReturnsFalseIfLastFailTimeHasTimedOut) 830 { 831 std::string username = "user001"; 832 initializeAccountPolicy(); 833 834 // Choose a date in the past. 835 std::vector<std::string> output = {"whatever", 836 "root\t2\t10/24/02\t00:00:00"}; 837 EXPECT_CALL(*this, getFailedAttempt(testing::StrEq(username.c_str()))) 838 .WillOnce(testing::Return(output)); 839 840 EXPECT_EQ(userLockedForFailedAttempt(username), false); 841 } 842 843 TEST_F(UserMgrInTest, CheckAndThrowForDisallowedGroupCreationOnSuccess) 844 { 845 // Base Redfish Roles 846 EXPECT_NO_THROW( 847 checkAndThrowForDisallowedGroupCreation("openbmc_rfr_Administrator")); 848 EXPECT_NO_THROW( 849 checkAndThrowForDisallowedGroupCreation("openbmc_rfr_Operator")); 850 EXPECT_NO_THROW( 851 checkAndThrowForDisallowedGroupCreation("openbmc_rfr_ReadOnly")); 852 // Base Redfish Privileges 853 EXPECT_NO_THROW( 854 checkAndThrowForDisallowedGroupCreation("openbmc_rfp_Login")); 855 EXPECT_NO_THROW(checkAndThrowForDisallowedGroupCreation( 856 "openbmc_rfp_ConfigureManager")); 857 EXPECT_NO_THROW( 858 checkAndThrowForDisallowedGroupCreation("openbmc_rfp_ConfigureUsers")); 859 EXPECT_NO_THROW( 860 checkAndThrowForDisallowedGroupCreation("openbmc_rfp_ConfigureSelf")); 861 EXPECT_NO_THROW(checkAndThrowForDisallowedGroupCreation( 862 "openbmc_rfp_ConfigureComponents")); 863 // OEM Redfish Roles 864 EXPECT_NO_THROW( 865 checkAndThrowForDisallowedGroupCreation("openbmc_orfr_PowerService")); 866 // OEM Redfish Privileges 867 EXPECT_NO_THROW( 868 checkAndThrowForDisallowedGroupCreation("openbmc_orfp_PowerService")); 869 } 870 871 TEST_F(UserMgrInTest, 872 CheckAndThrowForDisallowedGroupCreationThrowsIfGroupNameTooLong) 873 { 874 std::string groupName(maxSystemGroupNameLength + 1, 'A'); 875 EXPECT_THROW( 876 checkAndThrowForDisallowedGroupCreation(groupName), 877 sdbusplus::xyz::openbmc_project::Common::Error::InvalidArgument); 878 } 879 880 TEST_F( 881 UserMgrInTest, 882 CheckAndThrowForDisallowedGroupCreationThrowsIfGroupNameHasDisallowedCharacters) 883 { 884 885 EXPECT_THROW( 886 checkAndThrowForDisallowedGroupCreation("openbmc_rfp_?owerService"), 887 sdbusplus::xyz::openbmc_project::Common::Error::InvalidArgument); 888 EXPECT_THROW( 889 checkAndThrowForDisallowedGroupCreation("openbmc_rfp_-owerService"), 890 sdbusplus::xyz::openbmc_project::Common::Error::InvalidArgument); 891 } 892 893 TEST_F( 894 UserMgrInTest, 895 CheckAndThrowForDisallowedGroupCreationThrowsIfGroupNameHasDisallowedPrefix) 896 { 897 898 EXPECT_THROW( 899 checkAndThrowForDisallowedGroupCreation("google_rfp_"), 900 sdbusplus::xyz::openbmc_project::Common::Error::InvalidArgument); 901 EXPECT_THROW( 902 checkAndThrowForDisallowedGroupCreation("com_rfp_"), 903 sdbusplus::xyz::openbmc_project::Common::Error::InvalidArgument); 904 } 905 906 TEST_F(UserMgrInTest, CheckAndThrowForMaxGroupCountOnSuccess) 907 { 908 EXPECT_THAT(allGroups().size(), 4); 909 for (size_t i = 0; i < maxSystemGroupCount - 4; ++i) 910 { 911 std::string groupName = "openbmc_rfr_role"; 912 groupName += std::to_string(i); 913 EXPECT_NO_THROW(createGroup(groupName)); 914 } 915 EXPECT_THROW( 916 createGroup("openbmc_rfr_AnotherRole"), 917 sdbusplus::xyz::openbmc_project::User::Common::Error::NoResource); 918 for (size_t i = 0; i < maxSystemGroupCount - 4; ++i) 919 { 920 std::string groupName = "openbmc_rfr_role"; 921 groupName += std::to_string(i); 922 EXPECT_NO_THROW(deleteGroup(groupName)); 923 } 924 } 925 926 TEST_F(UserMgrInTest, CheckAndThrowForGroupExist) 927 { 928 std::string groupName = "openbmc_rfr_role"; 929 EXPECT_NO_THROW(createGroup(groupName)); 930 EXPECT_THROW( 931 createGroup(groupName), 932 sdbusplus::xyz::openbmc_project::User::Common::Error::GroupNameExists); 933 EXPECT_NO_THROW(deleteGroup(groupName)); 934 } 935 936 TEST_F(UserMgrInTest, ByDefaultAllGroupsArePredefinedGroups) 937 { 938 EXPECT_THAT(allGroups(), 939 testing::UnorderedElementsAre("web", "redfish", "ipmi", "ssh")); 940 } 941 942 TEST_F(UserMgrInTest, DeleteGroupThrowsIfGroupIsNotAllowedToChange) 943 { 944 EXPECT_THROW( 945 deleteGroup("ipmi"), 946 sdbusplus::xyz::openbmc_project::Common::Error::InvalidArgument); 947 EXPECT_THROW( 948 deleteGroup("web"), 949 sdbusplus::xyz::openbmc_project::Common::Error::InvalidArgument); 950 EXPECT_THROW( 951 deleteGroup("redfish"), 952 sdbusplus::xyz::openbmc_project::Common::Error::InvalidArgument); 953 EXPECT_THROW( 954 deleteGroup("ssh"), 955 sdbusplus::xyz::openbmc_project::Common::Error::InvalidArgument); 956 } 957 958 TEST_F(UserMgrInTest, 959 CreateGroupThrowsInternalFailureWhenExecuteGroupCreateFails) 960 { 961 EXPECT_CALL(*this, executeGroupCreation) 962 .WillOnce(testing::Throw( 963 sdbusplus::xyz::openbmc_project::Common::Error::InternalFailure())); 964 EXPECT_THROW( 965 createGroup("openbmc_rfr_role1"), 966 sdbusplus::xyz::openbmc_project::Common::Error::InternalFailure); 967 } 968 969 TEST_F(UserMgrInTest, 970 DeleteGroupThrowsInternalFailureWhenExecuteGroupDeleteFails) 971 { 972 std::string groupName = "openbmc_rfr_role1"; 973 EXPECT_NO_THROW(UserMgr::createGroup(groupName)); 974 EXPECT_CALL(*this, executeGroupDeletion(testing::StrEq(groupName))) 975 .WillOnce(testing::Throw( 976 sdbusplus::xyz::openbmc_project::Common::Error::InternalFailure())) 977 .WillOnce(testing::DoDefault()); 978 979 EXPECT_THROW( 980 deleteGroup(groupName), 981 sdbusplus::xyz::openbmc_project::Common::Error::InternalFailure); 982 EXPECT_NO_THROW(UserMgr::deleteGroup(groupName)); 983 } 984 985 TEST_F(UserMgrInTest, CheckAndThrowForGroupNotExist) 986 { 987 EXPECT_THROW(deleteGroup("whatever"), 988 sdbusplus::xyz::openbmc_project::User::Common::Error:: 989 GroupNameDoesNotExist); 990 } 991 992 TEST(ReadAllGroupsOnSystemTest, OnlyReturnsPredefinedGroups) 993 { 994 EXPECT_THAT(UserMgr::readAllGroupsOnSystem(), 995 testing::UnorderedElementsAre("web", "redfish", "ipmi", "ssh")); 996 } 997 998 } // namespace user 999 } // namespace phosphor 1000