1 /* 2 // Copyright (c) 2018 Intel Corporation 3 // 4 // Licensed under the Apache License, Version 2.0 (the "License"); 5 // you may not use this file except in compliance with the License. 6 // You may obtain a copy of the License at 7 // 8 // http://www.apache.org/licenses/LICENSE-2.0 9 // 10 // Unless required by applicable law or agreed to in writing, software 11 // distributed under the License is distributed on an "AS IS" BASIS, 12 // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 // See the License for the specific language governing permissions and 14 // limitations under the License. 15 */ 16 #pragma once 17 #include "node.hpp" 18 19 #include <dbus_utility.hpp> 20 #include <error_messages.hpp> 21 #include <openbmc_dbus_rest.hpp> 22 #include <utils/json_utils.hpp> 23 #include <variant> 24 25 namespace redfish 26 { 27 28 constexpr const char* ldapConfigObject = 29 "/xyz/openbmc_project/user/ldap/openldap"; 30 constexpr const char* ADConfigObject = 31 "/xyz/openbmc_project/user/ldap/active_directory"; 32 33 constexpr const char* ldapRootObject = "/xyz/openbmc_project/user/ldap"; 34 constexpr const char* ldapDbusService = "xyz.openbmc_project.Ldap.Config"; 35 constexpr const char* ldapConfigInterface = 36 "xyz.openbmc_project.User.Ldap.Config"; 37 constexpr const char* ldapCreateInterface = 38 "xyz.openbmc_project.User.Ldap.Create"; 39 constexpr const char* ldapEnableInterface = "xyz.openbmc_project.Object.Enable"; 40 constexpr const char* dbusObjManagerIntf = "org.freedesktop.DBus.ObjectManager"; 41 constexpr const char* propertyInterface = "org.freedesktop.DBus.Properties"; 42 constexpr const char* mapperBusName = "xyz.openbmc_project.ObjectMapper"; 43 constexpr const char* mapperObjectPath = "/xyz/openbmc_project/object_mapper"; 44 constexpr const char* mapperIntf = "xyz.openbmc_project.ObjectMapper"; 45 46 struct LDAPRoleMapData 47 { 48 std::string groupName; 49 std::string privilege; 50 }; 51 52 struct LDAPConfigData 53 { 54 std::string uri{}; 55 std::string bindDN{}; 56 std::string baseDN{}; 57 std::string searchScope{}; 58 std::string serverType{}; 59 bool serviceEnabled = false; 60 std::string userNameAttribute{}; 61 std::string groupAttribute{}; 62 std::vector<std::pair<std::string, LDAPRoleMapData>> groupRoleList; 63 }; 64 65 using ManagedObjectType = std::vector<std::pair< 66 sdbusplus::message::object_path, 67 boost::container::flat_map< 68 std::string, boost::container::flat_map< 69 std::string, std::variant<bool, std::string>>>>>; 70 using GetObjectType = 71 std::vector<std::pair<std::string, std::vector<std::string>>>; 72 73 inline std::string getRoleIdFromPrivilege(std::string_view role) 74 { 75 if (role == "priv-admin") 76 { 77 return "Administrator"; 78 } 79 else if (role == "priv-callback") 80 { 81 return "Callback"; 82 } 83 else if (role == "priv-user") 84 { 85 return "User"; 86 } 87 else if (role == "priv-operator") 88 { 89 return "Operator"; 90 } 91 return ""; 92 } 93 inline std::string getPrivilegeFromRoleId(std::string_view role) 94 { 95 if (role == "Administrator") 96 { 97 return "priv-admin"; 98 } 99 else if (role == "Callback") 100 { 101 return "priv-callback"; 102 } 103 else if (role == "User") 104 { 105 return "priv-user"; 106 } 107 else if (role == "Operator") 108 { 109 return "priv-operator"; 110 } 111 return ""; 112 } 113 114 void parseLDAPConfigData(nlohmann::json& json_response, 115 const LDAPConfigData& confData, 116 const std::string& ldapType) 117 { 118 std::string service = 119 (ldapType == "LDAP") ? "LDAPService" : "ActiveDirectoryService"; 120 nlohmann::json ldap = { 121 {"AccountProviderType", service}, 122 {"ServiceEnabled", confData.serviceEnabled}, 123 {"ServiceAddresses", nlohmann::json::array({confData.uri})}, 124 {"Authentication", 125 {{"AuthenticationType", "UsernameAndPassword"}, 126 {"Username", confData.bindDN}, 127 {"Password", nullptr}}}, 128 {"LDAPService", 129 {{"SearchSettings", 130 {{"BaseDistinguishedNames", 131 nlohmann::json::array({confData.baseDN})}, 132 {"UsernameAttribute", confData.userNameAttribute}, 133 {"GroupsAttribute", confData.groupAttribute}}}}}, 134 }; 135 136 json_response[ldapType].update(std::move(ldap)); 137 138 nlohmann::json& roleMapArray = json_response[ldapType]["RemoteRoleMapping"]; 139 roleMapArray = nlohmann::json::array(); 140 for (auto& obj : confData.groupRoleList) 141 { 142 BMCWEB_LOG_DEBUG << "Pushing the data groupName=" 143 << obj.second.groupName << "\n"; 144 roleMapArray.push_back( 145 {nlohmann::json::array({"RemoteGroup", obj.second.groupName}), 146 nlohmann::json::array( 147 {"LocalRole", getRoleIdFromPrivilege(obj.second.privilege)})}); 148 } 149 } 150 151 /** 152 * Function that retrieves all properties for LDAP config object 153 * into JSON 154 */ 155 template <typename CallbackFunc> 156 inline void getLDAPConfigData(const std::string& ldapType, 157 CallbackFunc&& callback) 158 { 159 160 const std::array<const char*, 2> interfaces = {ldapEnableInterface, 161 ldapConfigInterface}; 162 163 crow::connections::systemBus->async_method_call( 164 [callback, ldapType](const boost::system::error_code ec, 165 const GetObjectType& resp) { 166 LDAPConfigData confData{}; 167 if (ec || resp.empty()) 168 { 169 BMCWEB_LOG_ERROR << "DBUS response error during getting of " 170 "service name: " 171 << ec; 172 callback(false, confData, ldapType); 173 return; 174 } 175 std::string service = resp.begin()->first; 176 crow::connections::systemBus->async_method_call( 177 [callback, ldapType](const boost::system::error_code error_code, 178 const ManagedObjectType& ldapObjects) { 179 LDAPConfigData confData{}; 180 if (error_code) 181 { 182 callback(false, confData, ldapType); 183 BMCWEB_LOG_ERROR << "D-Bus responses error: " 184 << error_code; 185 return; 186 } 187 188 std::string ldapDbusType; 189 std::string searchString; 190 191 if (ldapType == "LDAP") 192 { 193 ldapDbusType = "xyz.openbmc_project.User.Ldap.Config." 194 "Type.OpenLdap"; 195 searchString = "openldap"; 196 } 197 else if (ldapType == "ActiveDirectory") 198 { 199 ldapDbusType = 200 "xyz.openbmc_project.User.Ldap.Config.Type." 201 "ActiveDirectory"; 202 searchString = "active_directory"; 203 } 204 else 205 { 206 BMCWEB_LOG_ERROR 207 << "Can't get the DbusType for the given type=" 208 << ldapType; 209 callback(false, confData, ldapType); 210 return; 211 } 212 213 std::string ldapEnableInterfaceStr = ldapEnableInterface; 214 std::string ldapConfigInterfaceStr = ldapConfigInterface; 215 216 for (const auto& object : ldapObjects) 217 { 218 // let's find the object whose ldap type is equal to the 219 // given type 220 if (object.first.str.find(searchString) == 221 std::string::npos) 222 { 223 continue; 224 } 225 226 for (const auto& interface : object.second) 227 { 228 if (interface.first == ldapEnableInterfaceStr) 229 { 230 // rest of the properties are string. 231 for (const auto& property : interface.second) 232 { 233 if (property.first == "Enabled") 234 { 235 const bool* value = 236 std::get_if<bool>(&property.second); 237 if (value == nullptr) 238 { 239 continue; 240 } 241 confData.serviceEnabled = *value; 242 break; 243 } 244 } 245 } 246 else if (interface.first == ldapConfigInterfaceStr) 247 { 248 249 for (const auto& property : interface.second) 250 { 251 const std::string* value = 252 std::get_if<std::string>( 253 &property.second); 254 if (value == nullptr) 255 { 256 continue; 257 } 258 if (property.first == "LDAPServerURI") 259 { 260 confData.uri = *value; 261 } 262 else if (property.first == "LDAPBindDN") 263 { 264 confData.bindDN = *value; 265 } 266 else if (property.first == "LDAPBaseDN") 267 { 268 confData.baseDN = *value; 269 } 270 else if (property.first == 271 "LDAPSearchScope") 272 { 273 confData.searchScope = *value; 274 } 275 else if (property.first == 276 "GroupNameAttribute") 277 { 278 confData.groupAttribute = *value; 279 } 280 else if (property.first == 281 "UserNameAttribute") 282 { 283 confData.userNameAttribute = *value; 284 } 285 else if (property.first == "LDAPType") 286 { 287 confData.serverType = *value; 288 } 289 } 290 } 291 else if (interface.first == 292 "xyz.openbmc_project.User." 293 "PrivilegeMapperEntry") 294 { 295 LDAPRoleMapData roleMapData{}; 296 for (const auto& property : interface.second) 297 { 298 const std::string* value = 299 std::get_if<std::string>( 300 &property.second); 301 302 if (value == nullptr) 303 { 304 continue; 305 } 306 307 if (property.first == "GroupName") 308 { 309 roleMapData.groupName = *value; 310 } 311 else if (property.first == "Privilege") 312 { 313 roleMapData.privilege = *value; 314 } 315 } 316 317 confData.groupRoleList.push_back(std::make_pair( 318 object.first.str, roleMapData)); 319 } 320 } 321 } 322 callback(true, confData, ldapType); 323 }, 324 service, ldapRootObject, dbusObjManagerIntf, 325 "GetManagedObjects"); 326 }, 327 mapperBusName, mapperObjectPath, mapperIntf, "GetObject", 328 ldapConfigObject, interfaces); 329 } 330 331 class AccountService : public Node 332 { 333 public: 334 AccountService(CrowApp& app) : Node(app, "/redfish/v1/AccountService/") 335 { 336 entityPrivileges = { 337 {boost::beast::http::verb::get, 338 {{"ConfigureUsers"}, {"ConfigureManager"}}}, 339 {boost::beast::http::verb::head, {{"Login"}}}, 340 {boost::beast::http::verb::patch, {{"ConfigureUsers"}}}, 341 {boost::beast::http::verb::put, {{"ConfigureUsers"}}}, 342 {boost::beast::http::verb::delete_, {{"ConfigureUsers"}}}, 343 {boost::beast::http::verb::post, {{"ConfigureUsers"}}}}; 344 } 345 346 private: 347 /** 348 * @brief parses the authentication section under the LDAP 349 * @param input JSON data 350 * @param asyncResp pointer to the JSON response 351 * @param userName userName to be filled from the given JSON. 352 * @param password password to be filled from the given JSON. 353 */ 354 void 355 parseLDAPAuthenticationJson(nlohmann::json input, 356 const std::shared_ptr<AsyncResp>& asyncResp, 357 std::optional<std::string>& username, 358 std::optional<std::string>& password) 359 { 360 std::optional<std::string> authType; 361 362 if (!json_util::readJson(input, asyncResp->res, "AuthenticationType", 363 authType, "Username", username, "Password", 364 password)) 365 { 366 return; 367 } 368 if (!authType) 369 { 370 return; 371 } 372 if (*authType != "UsernameAndPassword") 373 { 374 messages::propertyValueNotInList(asyncResp->res, *authType, 375 "AuthenticationType"); 376 return; 377 } 378 } 379 /** 380 * @brief parses the LDAPService section under the LDAP 381 * @param input JSON data 382 * @param asyncResp pointer to the JSON response 383 * @param baseDNList baseDN to be filled from the given JSON. 384 * @param userNameAttribute userName to be filled from the given JSON. 385 * @param groupaAttribute password to be filled from the given JSON. 386 */ 387 388 void parseLDAPServiceJson( 389 nlohmann::json input, const std::shared_ptr<AsyncResp>& asyncResp, 390 std::optional<std::vector<std::string>>& baseDNList, 391 std::optional<std::string>& userNameAttribute, 392 std::optional<std::string>& groupsAttribute) 393 { 394 std::optional<nlohmann::json> searchSettings; 395 396 if (!json_util::readJson(input, asyncResp->res, "SearchSettings", 397 searchSettings)) 398 { 399 return; 400 } 401 if (!searchSettings) 402 { 403 return; 404 } 405 if (!json_util::readJson(*searchSettings, asyncResp->res, 406 "BaseDistinguishedNames", baseDNList, 407 "UsernameAttribute", userNameAttribute, 408 "GroupsAttribute", groupsAttribute)) 409 { 410 return; 411 } 412 } 413 /** 414 * @brief updates the LDAP server address and updates the 415 json response with the new value. 416 * @param serviceAddressList address to be updated. 417 * @param asyncResp pointer to the JSON response 418 * @param ldapServerElementName Type of LDAP 419 server(openLDAP/ActiveDirectory) 420 */ 421 422 void handleServiceAddressPatch( 423 const std::vector<std::string>& serviceAddressList, 424 const std::shared_ptr<AsyncResp>& asyncResp, 425 const std::string& ldapServerElementName, 426 const std::string& ldapConfigObject) 427 { 428 crow::connections::systemBus->async_method_call( 429 [asyncResp, ldapServerElementName, 430 serviceAddressList](const boost::system::error_code ec) { 431 if (ec) 432 { 433 BMCWEB_LOG_DEBUG 434 << "Error Occured in updating the service address"; 435 messages::internalError(asyncResp->res); 436 return; 437 } 438 std::vector<std::string> modifiedserviceAddressList = { 439 serviceAddressList.front()}; 440 asyncResp->res 441 .jsonValue[ldapServerElementName]["ServiceAddresses"] = 442 modifiedserviceAddressList; 443 if ((serviceAddressList).size() > 1) 444 { 445 messages::propertyValueModified(asyncResp->res, 446 "ServiceAddresses", 447 serviceAddressList.front()); 448 } 449 BMCWEB_LOG_DEBUG << "Updated the service address"; 450 }, 451 ldapDbusService, ldapConfigObject, propertyInterface, "Set", 452 ldapConfigInterface, "LDAPServerURI", 453 std::variant<std::string>(serviceAddressList.front())); 454 } 455 /** 456 * @brief updates the LDAP Bind DN and updates the 457 json response with the new value. 458 * @param username name of the user which needs to be updated. 459 * @param asyncResp pointer to the JSON response 460 * @param ldapServerElementName Type of LDAP 461 server(openLDAP/ActiveDirectory) 462 */ 463 464 void handleUserNamePatch(const std::string& username, 465 const std::shared_ptr<AsyncResp>& asyncResp, 466 const std::string& ldapServerElementName, 467 const std::string& ldapConfigObject) 468 { 469 crow::connections::systemBus->async_method_call( 470 [asyncResp, username, 471 ldapServerElementName](const boost::system::error_code ec) { 472 if (ec) 473 { 474 BMCWEB_LOG_DEBUG 475 << "Error occured in updating the username"; 476 messages::internalError(asyncResp->res); 477 return; 478 } 479 asyncResp->res.jsonValue[ldapServerElementName] 480 ["Authentication"]["Username"] = 481 username; 482 BMCWEB_LOG_DEBUG << "Updated the username"; 483 }, 484 ldapDbusService, ldapConfigObject, propertyInterface, "Set", 485 ldapConfigInterface, "LDAPBindDN", 486 std::variant<std::string>(username)); 487 } 488 489 /** 490 * @brief updates the LDAP password 491 * @param password : ldap password which needs to be updated. 492 * @param asyncResp pointer to the JSON response 493 * @param ldapServerElementName Type of LDAP 494 * server(openLDAP/ActiveDirectory) 495 */ 496 497 void handlePasswordPatch(const std::string& password, 498 const std::shared_ptr<AsyncResp>& asyncResp, 499 const std::string& ldapServerElementName, 500 const std::string& ldapConfigObject) 501 { 502 crow::connections::systemBus->async_method_call( 503 [asyncResp, password, 504 ldapServerElementName](const boost::system::error_code ec) { 505 if (ec) 506 { 507 BMCWEB_LOG_DEBUG 508 << "Error occured in updating the password"; 509 messages::internalError(asyncResp->res); 510 return; 511 } 512 asyncResp->res.jsonValue[ldapServerElementName] 513 ["Authentication"]["Password"] = ""; 514 BMCWEB_LOG_DEBUG << "Updated the password"; 515 }, 516 ldapDbusService, ldapConfigObject, propertyInterface, "Set", 517 ldapConfigInterface, "LDAPBindDNPassword", 518 std::variant<std::string>(password)); 519 } 520 521 /** 522 * @brief updates the LDAP BaseDN and updates the 523 json response with the new value. 524 * @param baseDNList baseDN list which needs to be updated. 525 * @param asyncResp pointer to the JSON response 526 * @param ldapServerElementName Type of LDAP 527 server(openLDAP/ActiveDirectory) 528 */ 529 530 void handleBaseDNPatch(const std::vector<std::string>& baseDNList, 531 const std::shared_ptr<AsyncResp>& asyncResp, 532 const std::string& ldapServerElementName, 533 const std::string& ldapConfigObject) 534 { 535 crow::connections::systemBus->async_method_call( 536 [asyncResp, baseDNList, 537 ldapServerElementName](const boost::system::error_code ec) { 538 if (ec) 539 { 540 BMCWEB_LOG_DEBUG << "Error Occured in Updating the base DN"; 541 messages::internalError(asyncResp->res); 542 return; 543 } 544 auto& serverTypeJson = 545 asyncResp->res.jsonValue[ldapServerElementName]; 546 auto& searchSettingsJson = 547 serverTypeJson["LDAPService"]["SearchSettings"]; 548 std::vector<std::string> modifiedBaseDNList = { 549 baseDNList.front()}; 550 searchSettingsJson["BaseDistinguishedNames"] = 551 modifiedBaseDNList; 552 if (baseDNList.size() > 1) 553 { 554 messages::propertyValueModified(asyncResp->res, 555 "BaseDistinguishedNames", 556 baseDNList.front()); 557 } 558 BMCWEB_LOG_DEBUG << "Updated the base DN"; 559 }, 560 ldapDbusService, ldapConfigObject, propertyInterface, "Set", 561 ldapConfigInterface, "LDAPBaseDN", 562 std::variant<std::string>(baseDNList.front())); 563 } 564 /** 565 * @brief updates the LDAP user name attribute and updates the 566 json response with the new value. 567 * @param userNameAttribute attribute to be updated. 568 * @param asyncResp pointer to the JSON response 569 * @param ldapServerElementName Type of LDAP 570 server(openLDAP/ActiveDirectory) 571 */ 572 573 void handleUserNameAttrPatch(const std::string& userNameAttribute, 574 const std::shared_ptr<AsyncResp>& asyncResp, 575 const std::string& ldapServerElementName, 576 const std::string& ldapConfigObject) 577 { 578 crow::connections::systemBus->async_method_call( 579 [asyncResp, userNameAttribute, 580 ldapServerElementName](const boost::system::error_code ec) { 581 if (ec) 582 { 583 BMCWEB_LOG_DEBUG << "Error Occured in Updating the " 584 "username attribute"; 585 messages::internalError(asyncResp->res); 586 return; 587 } 588 auto& serverTypeJson = 589 asyncResp->res.jsonValue[ldapServerElementName]; 590 auto& searchSettingsJson = 591 serverTypeJson["LDAPService"]["SearchSettings"]; 592 searchSettingsJson["UsernameAttribute"] = userNameAttribute; 593 BMCWEB_LOG_DEBUG << "Updated the user name attr."; 594 }, 595 ldapDbusService, ldapConfigObject, propertyInterface, "Set", 596 ldapConfigInterface, "UserNameAttribute", 597 std::variant<std::string>(userNameAttribute)); 598 } 599 /** 600 * @brief updates the LDAP group attribute and updates the 601 json response with the new value. 602 * @param groupsAttribute attribute to be updated. 603 * @param asyncResp pointer to the JSON response 604 * @param ldapServerElementName Type of LDAP 605 server(openLDAP/ActiveDirectory) 606 */ 607 608 void handleGroupNameAttrPatch(const std::string& groupsAttribute, 609 const std::shared_ptr<AsyncResp>& asyncResp, 610 const std::string& ldapServerElementName, 611 const std::string& ldapConfigObject) 612 { 613 crow::connections::systemBus->async_method_call( 614 [asyncResp, groupsAttribute, 615 ldapServerElementName](const boost::system::error_code ec) { 616 if (ec) 617 { 618 BMCWEB_LOG_DEBUG << "Error Occured in Updating the " 619 "groupname attribute"; 620 messages::internalError(asyncResp->res); 621 return; 622 } 623 auto& serverTypeJson = 624 asyncResp->res.jsonValue[ldapServerElementName]; 625 auto& searchSettingsJson = 626 serverTypeJson["LDAPService"]["SearchSettings"]; 627 searchSettingsJson["GroupsAttribute"] = groupsAttribute; 628 BMCWEB_LOG_DEBUG << "Updated the groupname attr"; 629 }, 630 ldapDbusService, ldapConfigObject, propertyInterface, "Set", 631 ldapConfigInterface, "GroupNameAttribute", 632 std::variant<std::string>(groupsAttribute)); 633 } 634 /** 635 * @brief updates the LDAP service enable and updates the 636 json response with the new value. 637 * @param input JSON data. 638 * @param asyncResp pointer to the JSON response 639 * @param ldapServerElementName Type of LDAP 640 server(openLDAP/ActiveDirectory) 641 */ 642 643 void handleServiceEnablePatch(bool serviceEnabled, 644 const std::shared_ptr<AsyncResp>& asyncResp, 645 const std::string& ldapServerElementName, 646 const std::string& ldapConfigObject) 647 { 648 crow::connections::systemBus->async_method_call( 649 [asyncResp, serviceEnabled, 650 ldapServerElementName](const boost::system::error_code ec) { 651 if (ec) 652 { 653 BMCWEB_LOG_DEBUG 654 << "Error Occured in Updating the service enable"; 655 messages::internalError(asyncResp->res); 656 return; 657 } 658 asyncResp->res 659 .jsonValue[ldapServerElementName]["ServiceEnabled"] = 660 serviceEnabled; 661 BMCWEB_LOG_DEBUG << "Updated Service enable = " 662 << serviceEnabled; 663 }, 664 ldapDbusService, ldapConfigObject, propertyInterface, "Set", 665 ldapEnableInterface, "Enabled", std::variant<bool>(serviceEnabled)); 666 } 667 668 /** 669 * @brief Get the required values from the given JSON, validates the 670 * value and create the LDAP config object. 671 * @param input JSON data 672 * @param asyncResp pointer to the JSON response 673 * @param serverType Type of LDAP server(openLDAP/ActiveDirectory) 674 */ 675 676 void handleLDAPPatch(nlohmann::json& input, 677 const std::shared_ptr<AsyncResp>& asyncResp, 678 const crow::Request& req, 679 const std::vector<std::string>& params, 680 const std::string& serverType) 681 { 682 std::string dbusObjectPath; 683 if (serverType == "ActiveDirectory") 684 { 685 dbusObjectPath = ADConfigObject; 686 } 687 else if (serverType == "LDAP") 688 { 689 dbusObjectPath = ldapConfigObject; 690 } 691 692 std::optional<nlohmann::json> authentication; 693 std::optional<nlohmann::json> ldapService; 694 std::optional<std::string> accountProviderType; 695 std::optional<std::vector<std::string>> serviceAddressList; 696 std::optional<bool> serviceEnabled; 697 std::optional<std::vector<std::string>> baseDNList; 698 std::optional<std::string> userNameAttribute; 699 std::optional<std::string> groupsAttribute; 700 std::optional<std::string> userName; 701 std::optional<std::string> password; 702 703 if (!json_util::readJson(input, asyncResp->res, "Authentication", 704 authentication, "LDAPService", ldapService, 705 "ServiceAddresses", serviceAddressList, 706 "AccountProviderType", accountProviderType, 707 "ServiceEnabled", serviceEnabled)) 708 { 709 return; 710 } 711 712 if (authentication) 713 { 714 parseLDAPAuthenticationJson(*authentication, asyncResp, userName, 715 password); 716 } 717 if (ldapService) 718 { 719 parseLDAPServiceJson(*ldapService, asyncResp, baseDNList, 720 userNameAttribute, groupsAttribute); 721 } 722 if (accountProviderType) 723 { 724 messages::propertyNotWritable(asyncResp->res, 725 "AccountProviderType"); 726 } 727 if (serviceAddressList) 728 { 729 if ((*serviceAddressList).size() == 0) 730 { 731 messages::propertyValueNotInList(asyncResp->res, "[]", 732 "ServiceAddress"); 733 return; 734 } 735 } 736 if (baseDNList) 737 { 738 if ((*baseDNList).size() == 0) 739 { 740 messages::propertyValueNotInList(asyncResp->res, "[]", 741 "BaseDistinguishedNames"); 742 return; 743 } 744 } 745 746 // nothing to update, then return 747 if (!userName && !password && !serviceAddressList && !baseDNList && 748 !userNameAttribute && !groupsAttribute && !serviceEnabled) 749 { 750 return; 751 } 752 753 // Get the existing resource first then keep modifying 754 // whenever any property gets updated. 755 getLDAPConfigData(serverType, [this, asyncResp, userName, password, 756 baseDNList, userNameAttribute, 757 groupsAttribute, accountProviderType, 758 serviceAddressList, serviceEnabled, 759 dbusObjectPath]( 760 bool success, LDAPConfigData confData, 761 const std::string& serverType) { 762 if (!success) 763 { 764 messages::internalError(asyncResp->res); 765 return; 766 } 767 parseLDAPConfigData(asyncResp->res.jsonValue, confData, serverType); 768 if (confData.serviceEnabled) 769 { 770 // Disable the service first and update the rest of 771 // the properties. 772 handleServiceEnablePatch(false, asyncResp, serverType, 773 dbusObjectPath); 774 } 775 776 if (serviceAddressList) 777 { 778 handleServiceAddressPatch(*serviceAddressList, asyncResp, 779 serverType, dbusObjectPath); 780 } 781 if (userName) 782 { 783 handleUserNamePatch(*userName, asyncResp, serverType, 784 dbusObjectPath); 785 } 786 if (password) 787 { 788 handlePasswordPatch(*password, asyncResp, serverType, 789 dbusObjectPath); 790 } 791 792 if (baseDNList) 793 { 794 handleBaseDNPatch(*baseDNList, asyncResp, serverType, 795 dbusObjectPath); 796 } 797 if (userNameAttribute) 798 { 799 handleUserNameAttrPatch(*userNameAttribute, asyncResp, 800 serverType, dbusObjectPath); 801 } 802 if (groupsAttribute) 803 { 804 handleGroupNameAttrPatch(*groupsAttribute, asyncResp, 805 serverType, dbusObjectPath); 806 } 807 if (serviceEnabled) 808 { 809 // if user has given the value as true then enable 810 // the service. if user has given false then no-op 811 // as service is already stopped. 812 if (*serviceEnabled) 813 { 814 handleServiceEnablePatch(*serviceEnabled, asyncResp, 815 serverType, dbusObjectPath); 816 } 817 } 818 else 819 { 820 // if user has not given the service enabled value 821 // then revert it to the same state as it was 822 // before. 823 handleServiceEnablePatch(confData.serviceEnabled, asyncResp, 824 serverType, dbusObjectPath); 825 } 826 }); 827 } 828 829 void doGet(crow::Response& res, const crow::Request& req, 830 const std::vector<std::string>& params) override 831 { 832 auto asyncResp = std::make_shared<AsyncResp>(res); 833 res.jsonValue = { 834 {"@odata.context", "/redfish/v1/" 835 "$metadata#AccountService.AccountService"}, 836 {"@odata.id", "/redfish/v1/AccountService"}, 837 {"@odata.type", "#AccountService." 838 "v1_4_0.AccountService"}, 839 {"Id", "AccountService"}, 840 {"Name", "Account Service"}, 841 {"Description", "Account Service"}, 842 {"ServiceEnabled", true}, 843 {"MaxPasswordLength", 20}, 844 {"Accounts", 845 {{"@odata.id", "/redfish/v1/AccountService/Accounts"}}}, 846 {"Roles", {{"@odata.id", "/redfish/v1/AccountService/Roles"}}}, 847 {"LDAP", 848 {{"Certificates", 849 {{"@odata.id", 850 "/redfish/v1/AccountService/LDAP/Certificates"}}}}}}; 851 crow::connections::systemBus->async_method_call( 852 [asyncResp]( 853 const boost::system::error_code ec, 854 const std::vector<std::pair< 855 std::string, std::variant<uint32_t, uint16_t, uint8_t>>>& 856 propertiesList) { 857 if (ec) 858 { 859 messages::internalError(asyncResp->res); 860 return; 861 } 862 BMCWEB_LOG_DEBUG << "Got " << propertiesList.size() 863 << "properties for AccountService"; 864 for (const std::pair<std::string, 865 std::variant<uint32_t, uint16_t, uint8_t>>& 866 property : propertiesList) 867 { 868 if (property.first == "MinPasswordLength") 869 { 870 const uint8_t* value = 871 std::get_if<uint8_t>(&property.second); 872 if (value != nullptr) 873 { 874 asyncResp->res.jsonValue["MinPasswordLength"] = 875 *value; 876 } 877 } 878 if (property.first == "AccountUnlockTimeout") 879 { 880 const uint32_t* value = 881 std::get_if<uint32_t>(&property.second); 882 if (value != nullptr) 883 { 884 asyncResp->res.jsonValue["AccountLockoutDuration"] = 885 *value; 886 } 887 } 888 if (property.first == "MaxLoginAttemptBeforeLockout") 889 { 890 const uint16_t* value = 891 std::get_if<uint16_t>(&property.second); 892 if (value != nullptr) 893 { 894 asyncResp->res 895 .jsonValue["AccountLockoutThreshold"] = *value; 896 } 897 } 898 } 899 }, 900 "xyz.openbmc_project.User.Manager", "/xyz/openbmc_project/user", 901 "org.freedesktop.DBus.Properties", "GetAll", 902 "xyz.openbmc_project.User.AccountPolicy"); 903 904 auto callback = [asyncResp](bool success, LDAPConfigData& confData, 905 const std::string& ldapType) { 906 parseLDAPConfigData(asyncResp->res.jsonValue, confData, ldapType); 907 }; 908 909 getLDAPConfigData("LDAP", callback); 910 getLDAPConfigData("ActiveDirectory", callback); 911 } 912 913 void doPatch(crow::Response& res, const crow::Request& req, 914 const std::vector<std::string>& params) override 915 { 916 auto asyncResp = std::make_shared<AsyncResp>(res); 917 918 std::optional<uint32_t> unlockTimeout; 919 std::optional<uint16_t> lockoutThreshold; 920 std::optional<uint16_t> minPasswordLength; 921 std::optional<uint16_t> maxPasswordLength; 922 std::optional<nlohmann::json> ldapObject; 923 std::optional<nlohmann::json> activeDirectoryObject; 924 925 if (!json_util::readJson(req, res, "AccountLockoutDuration", 926 unlockTimeout, "AccountLockoutThreshold", 927 lockoutThreshold, "MaxPasswordLength", 928 maxPasswordLength, "MinPasswordLength", 929 minPasswordLength, "LDAP", ldapObject, 930 "ActiveDirectory", activeDirectoryObject)) 931 { 932 return; 933 } 934 935 if (minPasswordLength) 936 { 937 messages::propertyNotWritable(asyncResp->res, "MinPasswordLength"); 938 } 939 940 if (maxPasswordLength) 941 { 942 messages::propertyNotWritable(asyncResp->res, "MaxPasswordLength"); 943 } 944 945 if (ldapObject) 946 { 947 handleLDAPPatch(*ldapObject, asyncResp, req, params, "LDAP"); 948 } 949 950 if (activeDirectoryObject) 951 { 952 handleLDAPPatch(*activeDirectoryObject, asyncResp, req, params, 953 "ActiveDirectory"); 954 } 955 956 if (unlockTimeout) 957 { 958 crow::connections::systemBus->async_method_call( 959 [asyncResp](const boost::system::error_code ec) { 960 if (ec) 961 { 962 messages::internalError(asyncResp->res); 963 return; 964 } 965 messages::success(asyncResp->res); 966 }, 967 "xyz.openbmc_project.User.Manager", "/xyz/openbmc_project/user", 968 "org.freedesktop.DBus.Properties", "Set", 969 "xyz.openbmc_project.User.AccountPolicy", 970 "AccountUnlockTimeout", std::variant<uint32_t>(*unlockTimeout)); 971 } 972 if (lockoutThreshold) 973 { 974 crow::connections::systemBus->async_method_call( 975 [asyncResp](const boost::system::error_code ec) { 976 if (ec) 977 { 978 messages::internalError(asyncResp->res); 979 return; 980 } 981 messages::success(asyncResp->res); 982 }, 983 "xyz.openbmc_project.User.Manager", "/xyz/openbmc_project/user", 984 "org.freedesktop.DBus.Properties", "Set", 985 "xyz.openbmc_project.User.AccountPolicy", 986 "MaxLoginAttemptBeforeLockout", 987 std::variant<uint16_t>(*lockoutThreshold)); 988 } 989 } 990 }; 991 992 class AccountsCollection : public Node 993 { 994 public: 995 AccountsCollection(CrowApp& app) : 996 Node(app, "/redfish/v1/AccountService/Accounts/") 997 { 998 entityPrivileges = { 999 {boost::beast::http::verb::get, 1000 {{"ConfigureUsers"}, {"ConfigureManager"}}}, 1001 {boost::beast::http::verb::head, {{"Login"}}}, 1002 {boost::beast::http::verb::patch, {{"ConfigureUsers"}}}, 1003 {boost::beast::http::verb::put, {{"ConfigureUsers"}}}, 1004 {boost::beast::http::verb::delete_, {{"ConfigureUsers"}}}, 1005 {boost::beast::http::verb::post, {{"ConfigureUsers"}}}}; 1006 } 1007 1008 private: 1009 void doGet(crow::Response& res, const crow::Request& req, 1010 const std::vector<std::string>& params) override 1011 { 1012 auto asyncResp = std::make_shared<AsyncResp>(res); 1013 res.jsonValue = {{"@odata.context", 1014 "/redfish/v1/" 1015 "$metadata#ManagerAccountCollection." 1016 "ManagerAccountCollection"}, 1017 {"@odata.id", "/redfish/v1/AccountService/Accounts"}, 1018 {"@odata.type", "#ManagerAccountCollection." 1019 "ManagerAccountCollection"}, 1020 {"Name", "Accounts Collection"}, 1021 {"Description", "BMC User Accounts"}}; 1022 1023 crow::connections::systemBus->async_method_call( 1024 [asyncResp](const boost::system::error_code ec, 1025 const ManagedObjectType& users) { 1026 if (ec) 1027 { 1028 messages::internalError(asyncResp->res); 1029 return; 1030 } 1031 1032 nlohmann::json& memberArray = 1033 asyncResp->res.jsonValue["Members"]; 1034 memberArray = nlohmann::json::array(); 1035 1036 asyncResp->res.jsonValue["Members@odata.count"] = users.size(); 1037 for (auto& user : users) 1038 { 1039 const std::string& path = 1040 static_cast<const std::string&>(user.first); 1041 std::size_t lastIndex = path.rfind("/"); 1042 if (lastIndex == std::string::npos) 1043 { 1044 lastIndex = 0; 1045 } 1046 else 1047 { 1048 lastIndex += 1; 1049 } 1050 memberArray.push_back( 1051 {{"@odata.id", "/redfish/v1/AccountService/Accounts/" + 1052 path.substr(lastIndex)}}); 1053 } 1054 }, 1055 "xyz.openbmc_project.User.Manager", "/xyz/openbmc_project/user", 1056 "org.freedesktop.DBus.ObjectManager", "GetManagedObjects"); 1057 } 1058 void doPost(crow::Response& res, const crow::Request& req, 1059 const std::vector<std::string>& params) override 1060 { 1061 auto asyncResp = std::make_shared<AsyncResp>(res); 1062 1063 std::string username; 1064 std::string password; 1065 std::optional<std::string> roleId("User"); 1066 std::optional<bool> enabled = true; 1067 if (!json_util::readJson(req, res, "UserName", username, "Password", 1068 password, "RoleId", roleId, "Enabled", 1069 enabled)) 1070 { 1071 return; 1072 } 1073 1074 std::string priv = getPrivilegeFromRoleId(*roleId); 1075 if (priv.empty()) 1076 { 1077 messages::propertyValueNotInList(asyncResp->res, *roleId, "RoleId"); 1078 return; 1079 } 1080 roleId = priv; 1081 1082 // Reading AllGroups property 1083 crow::connections::systemBus->async_method_call( 1084 [asyncResp, username, password{std::move(password)}, roleId, 1085 enabled](const boost::system::error_code ec, 1086 const std::variant<std::vector<std::string>>& allGroups) { 1087 if (ec) 1088 { 1089 BMCWEB_LOG_DEBUG << "ERROR with async_method_call"; 1090 messages::internalError(asyncResp->res); 1091 return; 1092 } 1093 1094 const std::vector<std::string>* allGroupsList = 1095 std::get_if<std::vector<std::string>>(&allGroups); 1096 1097 if (allGroupsList == nullptr || allGroupsList->empty()) 1098 { 1099 messages::internalError(asyncResp->res); 1100 return; 1101 } 1102 1103 crow::connections::systemBus->async_method_call( 1104 [asyncResp, username, password{std::move(password)}]( 1105 const boost::system::error_code ec) { 1106 if (ec) 1107 { 1108 messages::resourceAlreadyExists( 1109 asyncResp->res, 1110 "#ManagerAccount.v1_0_3.ManagerAccount", 1111 "UserName", username); 1112 return; 1113 } 1114 1115 if (!pamUpdatePassword(username, password)) 1116 { 1117 // At this point we have a user that's been created, 1118 // but the password set failed.Something is wrong, 1119 // so delete the user that we've already created 1120 crow::connections::systemBus->async_method_call( 1121 [asyncResp]( 1122 const boost::system::error_code ec) { 1123 if (ec) 1124 { 1125 messages::internalError(asyncResp->res); 1126 return; 1127 } 1128 1129 messages::invalidObject(asyncResp->res, 1130 "Password"); 1131 }, 1132 "xyz.openbmc_project.User.Manager", 1133 "/xyz/openbmc_project/user/" + username, 1134 "xyz.openbmc_project.Object.Delete", "Delete"); 1135 1136 BMCWEB_LOG_ERROR << "pamUpdatePassword Failed"; 1137 return; 1138 } 1139 1140 messages::created(asyncResp->res); 1141 asyncResp->res.addHeader( 1142 "Location", 1143 "/redfish/v1/AccountService/Accounts/" + username); 1144 }, 1145 "xyz.openbmc_project.User.Manager", 1146 "/xyz/openbmc_project/user", 1147 "xyz.openbmc_project.User.Manager", "CreateUser", username, 1148 *allGroupsList, *roleId, *enabled); 1149 }, 1150 "xyz.openbmc_project.User.Manager", "/xyz/openbmc_project/user", 1151 "org.freedesktop.DBus.Properties", "Get", 1152 "xyz.openbmc_project.User.Manager", "AllGroups"); 1153 } 1154 }; 1155 1156 class ManagerAccount : public Node 1157 { 1158 public: 1159 ManagerAccount(CrowApp& app) : 1160 Node(app, "/redfish/v1/AccountService/Accounts/<str>/", std::string()) 1161 { 1162 entityPrivileges = { 1163 {boost::beast::http::verb::get, 1164 {{"ConfigureUsers"}, {"ConfigureManager"}, {"ConfigureSelf"}}}, 1165 {boost::beast::http::verb::head, {{"Login"}}}, 1166 {boost::beast::http::verb::patch, {{"ConfigureUsers"}}}, 1167 {boost::beast::http::verb::put, {{"ConfigureUsers"}}}, 1168 {boost::beast::http::verb::delete_, {{"ConfigureUsers"}}}, 1169 {boost::beast::http::verb::post, {{"ConfigureUsers"}}}}; 1170 } 1171 1172 private: 1173 void doGet(crow::Response& res, const crow::Request& req, 1174 const std::vector<std::string>& params) override 1175 { 1176 res.jsonValue = { 1177 {"@odata.context", 1178 "/redfish/v1/$metadata#ManagerAccount.ManagerAccount"}, 1179 {"@odata.type", "#ManagerAccount.v1_0_3.ManagerAccount"}, 1180 {"Name", "User Account"}, 1181 {"Description", "User Account"}, 1182 {"Password", nullptr}, 1183 {"RoleId", "Administrator"}}; 1184 1185 auto asyncResp = std::make_shared<AsyncResp>(res); 1186 1187 if (params.size() != 1) 1188 { 1189 messages::internalError(asyncResp->res); 1190 return; 1191 } 1192 1193 crow::connections::systemBus->async_method_call( 1194 [asyncResp, accountName{std::string(params[0])}]( 1195 const boost::system::error_code ec, 1196 const ManagedObjectType& users) { 1197 if (ec) 1198 { 1199 messages::internalError(asyncResp->res); 1200 return; 1201 } 1202 auto userIt = users.begin(); 1203 1204 for (; userIt != users.end(); userIt++) 1205 { 1206 if (boost::ends_with(userIt->first.str, "/" + accountName)) 1207 { 1208 break; 1209 } 1210 } 1211 if (userIt == users.end()) 1212 { 1213 messages::resourceNotFound(asyncResp->res, "ManagerAccount", 1214 accountName); 1215 return; 1216 } 1217 for (const auto& interface : userIt->second) 1218 { 1219 if (interface.first == 1220 "xyz.openbmc_project.User.Attributes") 1221 { 1222 for (const auto& property : interface.second) 1223 { 1224 if (property.first == "UserEnabled") 1225 { 1226 const bool* userEnabled = 1227 std::get_if<bool>(&property.second); 1228 if (userEnabled == nullptr) 1229 { 1230 BMCWEB_LOG_ERROR 1231 << "UserEnabled wasn't a bool"; 1232 messages::internalError(asyncResp->res); 1233 return; 1234 } 1235 asyncResp->res.jsonValue["Enabled"] = 1236 *userEnabled; 1237 } 1238 else if (property.first == 1239 "UserLockedForFailedAttempt") 1240 { 1241 const bool* userLocked = 1242 std::get_if<bool>(&property.second); 1243 if (userLocked == nullptr) 1244 { 1245 BMCWEB_LOG_ERROR << "UserLockedForF" 1246 "ailedAttempt " 1247 "wasn't a bool"; 1248 messages::internalError(asyncResp->res); 1249 return; 1250 } 1251 asyncResp->res.jsonValue["Locked"] = 1252 *userLocked; 1253 asyncResp->res.jsonValue 1254 ["Locked@Redfish.AllowableValues"] = { 1255 "false"}; 1256 } 1257 else if (property.first == "UserPrivilege") 1258 { 1259 const std::string* userPrivPtr = 1260 std::get_if<std::string>(&property.second); 1261 if (userPrivPtr == nullptr) 1262 { 1263 BMCWEB_LOG_ERROR 1264 << "UserPrivilege wasn't a " 1265 "string"; 1266 messages::internalError(asyncResp->res); 1267 return; 1268 } 1269 std::string role = 1270 getRoleIdFromPrivilege(*userPrivPtr); 1271 if (role.empty()) 1272 { 1273 BMCWEB_LOG_ERROR << "Invalid user role"; 1274 messages::internalError(asyncResp->res); 1275 return; 1276 } 1277 asyncResp->res.jsonValue["RoleId"] = role; 1278 1279 asyncResp->res.jsonValue["Links"]["Role"] = { 1280 {"@odata.id", "/redfish/v1/AccountService/" 1281 "Roles/" + 1282 role}}; 1283 } 1284 } 1285 } 1286 } 1287 1288 asyncResp->res.jsonValue["@odata.id"] = 1289 "/redfish/v1/AccountService/Accounts/" + accountName; 1290 asyncResp->res.jsonValue["Id"] = accountName; 1291 asyncResp->res.jsonValue["UserName"] = accountName; 1292 }, 1293 "xyz.openbmc_project.User.Manager", "/xyz/openbmc_project/user", 1294 "org.freedesktop.DBus.ObjectManager", "GetManagedObjects"); 1295 } 1296 1297 void doPatch(crow::Response& res, const crow::Request& req, 1298 const std::vector<std::string>& params) override 1299 { 1300 auto asyncResp = std::make_shared<AsyncResp>(res); 1301 if (params.size() != 1) 1302 { 1303 messages::internalError(asyncResp->res); 1304 return; 1305 } 1306 1307 std::optional<std::string> newUserName; 1308 std::optional<std::string> password; 1309 std::optional<bool> enabled; 1310 std::optional<std::string> roleId; 1311 std::optional<bool> locked; 1312 if (!json_util::readJson(req, res, "UserName", newUserName, "Password", 1313 password, "RoleId", roleId, "Enabled", enabled, 1314 "Locked", locked)) 1315 { 1316 return; 1317 } 1318 1319 const std::string& username = params[0]; 1320 1321 if (!newUserName) 1322 { 1323 // If the username isn't being updated, we can update the 1324 // properties directly 1325 updateUserProperties(asyncResp, username, password, enabled, roleId, 1326 locked); 1327 return; 1328 } 1329 else 1330 { 1331 crow::connections::systemBus->async_method_call( 1332 [this, asyncResp, username, password(std::move(password)), 1333 roleId(std::move(roleId)), enabled(std::move(enabled)), 1334 newUser{std::string(*newUserName)}, locked(std::move(locked))]( 1335 const boost::system::error_code ec) { 1336 if (ec) 1337 { 1338 BMCWEB_LOG_ERROR << "D-Bus responses error: " << ec; 1339 messages::resourceNotFound( 1340 asyncResp->res, 1341 "#ManagerAccount.v1_0_3.ManagerAccount", username); 1342 return; 1343 } 1344 1345 updateUserProperties(asyncResp, newUser, password, enabled, 1346 roleId, locked); 1347 }, 1348 "xyz.openbmc_project.User.Manager", "/xyz/openbmc_project/user", 1349 "xyz.openbmc_project.User.Manager", "RenameUser", username, 1350 *newUserName); 1351 } 1352 } 1353 1354 void updateUserProperties(std::shared_ptr<AsyncResp> asyncResp, 1355 const std::string& username, 1356 std::optional<std::string> password, 1357 std::optional<bool> enabled, 1358 std::optional<std::string> roleId, 1359 std::optional<bool> locked) 1360 { 1361 if (password) 1362 { 1363 if (!pamUpdatePassword(username, *password)) 1364 { 1365 BMCWEB_LOG_ERROR << "pamUpdatePassword Failed"; 1366 messages::internalError(asyncResp->res); 1367 return; 1368 } 1369 } 1370 1371 std::string dbusObjectPath = "/xyz/openbmc_project/user/" + username; 1372 dbus::utility::escapePathForDbus(dbusObjectPath); 1373 1374 dbus::utility::checkDbusPathExists( 1375 dbusObjectPath, 1376 [dbusObjectPath(std::move(dbusObjectPath)), username, 1377 password(std::move(password)), roleId(std::move(roleId)), 1378 enabled(std::move(enabled)), locked(std::move(locked)), 1379 asyncResp{std::move(asyncResp)}](int rc) { 1380 if (!rc) 1381 { 1382 messages::invalidObject(asyncResp->res, username.c_str()); 1383 return; 1384 } 1385 if (enabled) 1386 { 1387 crow::connections::systemBus->async_method_call( 1388 [asyncResp](const boost::system::error_code ec) { 1389 if (ec) 1390 { 1391 BMCWEB_LOG_ERROR << "D-Bus responses error: " 1392 << ec; 1393 messages::internalError(asyncResp->res); 1394 return; 1395 } 1396 messages::success(asyncResp->res); 1397 return; 1398 }, 1399 "xyz.openbmc_project.User.Manager", 1400 dbusObjectPath.c_str(), 1401 "org.freedesktop.DBus.Properties", "Set", 1402 "xyz.openbmc_project.User.Attributes", "UserEnabled", 1403 std::variant<bool>{*enabled}); 1404 } 1405 1406 if (roleId) 1407 { 1408 std::string priv = getPrivilegeFromRoleId(*roleId); 1409 if (priv.empty()) 1410 { 1411 messages::propertyValueNotInList(asyncResp->res, 1412 *roleId, "RoleId"); 1413 return; 1414 } 1415 1416 crow::connections::systemBus->async_method_call( 1417 [asyncResp](const boost::system::error_code ec) { 1418 if (ec) 1419 { 1420 BMCWEB_LOG_ERROR << "D-Bus responses error: " 1421 << ec; 1422 messages::internalError(asyncResp->res); 1423 return; 1424 } 1425 messages::success(asyncResp->res); 1426 }, 1427 "xyz.openbmc_project.User.Manager", 1428 dbusObjectPath.c_str(), 1429 "org.freedesktop.DBus.Properties", "Set", 1430 "xyz.openbmc_project.User.Attributes", "UserPrivilege", 1431 std::variant<std::string>{priv}); 1432 } 1433 1434 if (locked) 1435 { 1436 // admin can unlock the account which is locked by 1437 // successive authentication failures but admin should 1438 // not be allowed to lock an account. 1439 if (*locked) 1440 { 1441 messages::propertyValueNotInList(asyncResp->res, "true", 1442 "Locked"); 1443 return; 1444 } 1445 1446 crow::connections::systemBus->async_method_call( 1447 [asyncResp](const boost::system::error_code ec) { 1448 if (ec) 1449 { 1450 BMCWEB_LOG_ERROR << "D-Bus responses error: " 1451 << ec; 1452 messages::internalError(asyncResp->res); 1453 return; 1454 } 1455 messages::success(asyncResp->res); 1456 return; 1457 }, 1458 "xyz.openbmc_project.User.Manager", 1459 dbusObjectPath.c_str(), 1460 "org.freedesktop.DBus.Properties", "Set", 1461 "xyz.openbmc_project.User.Attributes", 1462 "UserLockedForFailedAttempt", 1463 sdbusplus::message::variant<bool>{*locked}); 1464 } 1465 }); 1466 } 1467 1468 void doDelete(crow::Response& res, const crow::Request& req, 1469 const std::vector<std::string>& params) override 1470 { 1471 auto asyncResp = std::make_shared<AsyncResp>(res); 1472 1473 if (params.size() != 1) 1474 { 1475 messages::internalError(asyncResp->res); 1476 return; 1477 } 1478 1479 const std::string userPath = "/xyz/openbmc_project/user/" + params[0]; 1480 1481 crow::connections::systemBus->async_method_call( 1482 [asyncResp, username{std::move(params[0])}]( 1483 const boost::system::error_code ec) { 1484 if (ec) 1485 { 1486 messages::resourceNotFound( 1487 asyncResp->res, "#ManagerAccount.v1_0_3.ManagerAccount", 1488 username); 1489 return; 1490 } 1491 1492 messages::accountRemoved(asyncResp->res); 1493 }, 1494 "xyz.openbmc_project.User.Manager", userPath, 1495 "xyz.openbmc_project.Object.Delete", "Delete"); 1496 } 1497 }; 1498 1499 } // namespace redfish 1500