188d16c9aSLewanczyk, Dawid /* 288d16c9aSLewanczyk, Dawid // Copyright (c) 2018 Intel Corporation 388d16c9aSLewanczyk, Dawid // 488d16c9aSLewanczyk, Dawid // Licensed under the Apache License, Version 2.0 (the "License"); 588d16c9aSLewanczyk, Dawid // you may not use this file except in compliance with the License. 688d16c9aSLewanczyk, Dawid // You may obtain a copy of the License at 788d16c9aSLewanczyk, Dawid // 888d16c9aSLewanczyk, Dawid // http://www.apache.org/licenses/LICENSE-2.0 988d16c9aSLewanczyk, Dawid // 1088d16c9aSLewanczyk, Dawid // Unless required by applicable law or agreed to in writing, software 1188d16c9aSLewanczyk, Dawid // distributed under the License is distributed on an "AS IS" BASIS, 1288d16c9aSLewanczyk, Dawid // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 1388d16c9aSLewanczyk, Dawid // See the License for the specific language governing permissions and 1488d16c9aSLewanczyk, Dawid // limitations under the License. 1588d16c9aSLewanczyk, Dawid */ 1688d16c9aSLewanczyk, Dawid #pragma once 1788d16c9aSLewanczyk, Dawid #include "node.hpp" 1888d16c9aSLewanczyk, Dawid 1965b0dc32SEd Tanous #include <error_messages.hpp> 20b9b2e0b2SEd Tanous #include <openbmc_dbus_rest.hpp> 21a840879dSEd Tanous #include <utils/json_utils.hpp> 22b9b2e0b2SEd Tanous 231abe55efSEd Tanous namespace redfish 241abe55efSEd Tanous { 2588d16c9aSLewanczyk, Dawid 26b9b2e0b2SEd Tanous using ManagedObjectType = std::vector<std::pair< 27b9b2e0b2SEd Tanous sdbusplus::message::object_path, 28b9b2e0b2SEd Tanous boost::container::flat_map< 29b9b2e0b2SEd Tanous std::string, boost::container::flat_map< 30b9b2e0b2SEd Tanous std::string, sdbusplus::message::variant<bool>>>>>; 31b9b2e0b2SEd Tanous 321abe55efSEd Tanous class AccountService : public Node 331abe55efSEd Tanous { 3488d16c9aSLewanczyk, Dawid public: 351abe55efSEd Tanous AccountService(CrowApp& app) : Node(app, "/redfish/v1/AccountService/") 361abe55efSEd Tanous { 37c1a46bd2SBorawski.Lukasz Node::json["@odata.id"] = "/redfish/v1/AccountService"; 38c1a46bd2SBorawski.Lukasz Node::json["@odata.type"] = "#AccountService.v1_1_0.AccountService"; 39c1a46bd2SBorawski.Lukasz Node::json["@odata.context"] = 4088d16c9aSLewanczyk, Dawid "/redfish/v1/$metadata#AccountService.AccountService"; 41c1a46bd2SBorawski.Lukasz Node::json["Id"] = "AccountService"; 42c1a46bd2SBorawski.Lukasz Node::json["Description"] = "BMC User Accounts"; 43c1a46bd2SBorawski.Lukasz Node::json["Name"] = "Account Service"; 44c1a46bd2SBorawski.Lukasz Node::json["ServiceEnabled"] = true; 45c1a46bd2SBorawski.Lukasz Node::json["MinPasswordLength"] = 1; 46c1a46bd2SBorawski.Lukasz Node::json["MaxPasswordLength"] = 20; 471abe55efSEd Tanous Node::json["Accounts"]["@odata.id"] = 481abe55efSEd Tanous "/redfish/v1/AccountService/Accounts"; 49c1a46bd2SBorawski.Lukasz Node::json["Roles"]["@odata.id"] = "/redfish/v1/AccountService/Roles"; 503ebd75f7SEd Tanous 513ebd75f7SEd Tanous entityPrivileges = { 524b1b8683SBorawski.Lukasz {boost::beast::http::verb::get, 534b1b8683SBorawski.Lukasz {{"ConfigureUsers"}, {"ConfigureManager"}}}, 54e0d918bcSEd Tanous {boost::beast::http::verb::head, {{"Login"}}}, 55e0d918bcSEd Tanous {boost::beast::http::verb::patch, {{"ConfigureUsers"}}}, 56e0d918bcSEd Tanous {boost::beast::http::verb::put, {{"ConfigureUsers"}}}, 57e0d918bcSEd Tanous {boost::beast::http::verb::delete_, {{"ConfigureUsers"}}}, 58e0d918bcSEd Tanous {boost::beast::http::verb::post, {{"ConfigureUsers"}}}}; 5988d16c9aSLewanczyk, Dawid } 6088d16c9aSLewanczyk, Dawid 6188d16c9aSLewanczyk, Dawid private: 6255c7b7a2SEd Tanous void doGet(crow::Response& res, const crow::Request& req, 631abe55efSEd Tanous const std::vector<std::string>& params) override 641abe55efSEd Tanous { 6555c7b7a2SEd Tanous res.jsonValue = Node::json; 6688d16c9aSLewanczyk, Dawid res.end(); 6788d16c9aSLewanczyk, Dawid } 6888d16c9aSLewanczyk, Dawid }; 69b9b2e0b2SEd Tanous class AccountsCollection : public Node 70b9b2e0b2SEd Tanous { 71b9b2e0b2SEd Tanous public: 72b9b2e0b2SEd Tanous AccountsCollection(CrowApp& app) : 73b9b2e0b2SEd Tanous Node(app, "/redfish/v1/AccountService/Accounts/") 74b9b2e0b2SEd Tanous { 75b9b2e0b2SEd Tanous 76b9b2e0b2SEd Tanous Node::json = {{"@odata.context", "/redfish/v1/" 77b9b2e0b2SEd Tanous "$metadata#ManagerAccountCollection." 78b9b2e0b2SEd Tanous "ManagerAccountCollection"}, 79b9b2e0b2SEd Tanous {"@odata.id", "/redfish/v1/AccountService/Accounts"}, 80b9b2e0b2SEd Tanous {"@odata.type", "#ManagerAccountCollection." 81b9b2e0b2SEd Tanous "ManagerAccountCollection"}, 82b9b2e0b2SEd Tanous {"Name", "Accounts Collection"}, 83b9b2e0b2SEd Tanous {"Description", "BMC User Accounts"}}; 84b9b2e0b2SEd Tanous 85b9b2e0b2SEd Tanous entityPrivileges = { 86b9b2e0b2SEd Tanous {boost::beast::http::verb::get, 87b9b2e0b2SEd Tanous {{"ConfigureUsers"}, {"ConfigureManager"}}}, 88b9b2e0b2SEd Tanous {boost::beast::http::verb::head, {{"Login"}}}, 89b9b2e0b2SEd Tanous {boost::beast::http::verb::patch, {{"ConfigureUsers"}}}, 90b9b2e0b2SEd Tanous {boost::beast::http::verb::put, {{"ConfigureUsers"}}}, 91b9b2e0b2SEd Tanous {boost::beast::http::verb::delete_, {{"ConfigureUsers"}}}, 92b9b2e0b2SEd Tanous {boost::beast::http::verb::post, {{"ConfigureUsers"}}}}; 93b9b2e0b2SEd Tanous } 94b9b2e0b2SEd Tanous 95b9b2e0b2SEd Tanous private: 96b9b2e0b2SEd Tanous void doGet(crow::Response& res, const crow::Request& req, 97b9b2e0b2SEd Tanous const std::vector<std::string>& params) override 98b9b2e0b2SEd Tanous { 99b9b2e0b2SEd Tanous res.jsonValue = Node::json; 100b9b2e0b2SEd Tanous auto asyncResp = std::make_shared<AsyncResp>(res); 101b9b2e0b2SEd Tanous crow::connections::systemBus->async_method_call( 102b9b2e0b2SEd Tanous [asyncResp](const boost::system::error_code ec, 103b9b2e0b2SEd Tanous const ManagedObjectType& users) { 104b9b2e0b2SEd Tanous if (ec) 105b9b2e0b2SEd Tanous { 106b9b2e0b2SEd Tanous asyncResp->res.result( 107b9b2e0b2SEd Tanous boost::beast::http::status::internal_server_error); 108b9b2e0b2SEd Tanous return; 109b9b2e0b2SEd Tanous } 110b9b2e0b2SEd Tanous 111b9b2e0b2SEd Tanous nlohmann::json& memberArray = 112b9b2e0b2SEd Tanous asyncResp->res.jsonValue["Members"]; 113b9b2e0b2SEd Tanous memberArray = nlohmann::json::array(); 114b9b2e0b2SEd Tanous 115b9b2e0b2SEd Tanous asyncResp->res.jsonValue["Members@odata.count"] = users.size(); 116b9b2e0b2SEd Tanous for (auto& user : users) 117b9b2e0b2SEd Tanous { 118b9b2e0b2SEd Tanous const std::string& path = 119b9b2e0b2SEd Tanous static_cast<const std::string&>(user.first); 120b9b2e0b2SEd Tanous std::size_t lastIndex = path.rfind("/"); 121b9b2e0b2SEd Tanous if (lastIndex == std::string::npos) 122b9b2e0b2SEd Tanous { 123b9b2e0b2SEd Tanous lastIndex = 0; 124b9b2e0b2SEd Tanous } 125b9b2e0b2SEd Tanous else 126b9b2e0b2SEd Tanous { 127b9b2e0b2SEd Tanous lastIndex += 1; 128b9b2e0b2SEd Tanous } 129b9b2e0b2SEd Tanous memberArray.push_back( 130b9b2e0b2SEd Tanous {{"@odata.id", "/redfish/v1/AccountService/Accounts/" + 131b9b2e0b2SEd Tanous path.substr(lastIndex)}}); 132b9b2e0b2SEd Tanous } 133b9b2e0b2SEd Tanous }, 134b9b2e0b2SEd Tanous "xyz.openbmc_project.User.Manager", "/xyz/openbmc_project/user", 135b9b2e0b2SEd Tanous "org.freedesktop.DBus.ObjectManager", "GetManagedObjects"); 136b9b2e0b2SEd Tanous } 137*04ae99ecSEd Tanous void doPost(crow::Response& res, const crow::Request& req, 138*04ae99ecSEd Tanous const std::vector<std::string>& params) override 139*04ae99ecSEd Tanous { 140*04ae99ecSEd Tanous auto asyncResp = std::make_shared<AsyncResp>(res); 141*04ae99ecSEd Tanous 142*04ae99ecSEd Tanous nlohmann::json patchRequest; 143*04ae99ecSEd Tanous if (!json_util::processJsonFromRequest(res, req, patchRequest)) 144*04ae99ecSEd Tanous { 145*04ae99ecSEd Tanous return; 146*04ae99ecSEd Tanous } 147*04ae99ecSEd Tanous 148*04ae99ecSEd Tanous const std::string* username = nullptr; 149*04ae99ecSEd Tanous const std::string* password = nullptr; 150*04ae99ecSEd Tanous // Default to user 151*04ae99ecSEd Tanous std::string privilege = "priv-user"; 152*04ae99ecSEd Tanous // default to enabled 153*04ae99ecSEd Tanous bool enabled = true; 154*04ae99ecSEd Tanous for (const auto& item : patchRequest.items()) 155*04ae99ecSEd Tanous { 156*04ae99ecSEd Tanous if (item.key() == "UserName") 157*04ae99ecSEd Tanous { 158*04ae99ecSEd Tanous username = item.value().get_ptr<const std::string*>(); 159*04ae99ecSEd Tanous if (username == nullptr) 160*04ae99ecSEd Tanous { 161*04ae99ecSEd Tanous messages::addMessageToErrorJson( 162*04ae99ecSEd Tanous asyncResp->res.jsonValue, 163*04ae99ecSEd Tanous messages::propertyValueFormatError(item.value().dump(), 164*04ae99ecSEd Tanous item.key())); 165*04ae99ecSEd Tanous asyncResp->res.result( 166*04ae99ecSEd Tanous boost::beast::http::status::bad_request); 167*04ae99ecSEd Tanous return; 168*04ae99ecSEd Tanous } 169*04ae99ecSEd Tanous } 170*04ae99ecSEd Tanous else if (item.key() == "Enabled") 171*04ae99ecSEd Tanous { 172*04ae99ecSEd Tanous const bool* enabledJson = item.value().get_ptr<const bool*>(); 173*04ae99ecSEd Tanous if (enabledJson == nullptr) 174*04ae99ecSEd Tanous { 175*04ae99ecSEd Tanous messages::addMessageToErrorJson( 176*04ae99ecSEd Tanous asyncResp->res.jsonValue, 177*04ae99ecSEd Tanous messages::propertyValueFormatError(item.value().dump(), 178*04ae99ecSEd Tanous item.key())); 179*04ae99ecSEd Tanous asyncResp->res.result( 180*04ae99ecSEd Tanous boost::beast::http::status::bad_request); 181*04ae99ecSEd Tanous return; 182*04ae99ecSEd Tanous } 183*04ae99ecSEd Tanous enabled = *enabledJson; 184*04ae99ecSEd Tanous } 185*04ae99ecSEd Tanous else if (item.key() == "Password") 186*04ae99ecSEd Tanous { 187*04ae99ecSEd Tanous password = item.value().get_ptr<const std::string*>(); 188*04ae99ecSEd Tanous if (password == nullptr) 189*04ae99ecSEd Tanous { 190*04ae99ecSEd Tanous messages::addMessageToErrorJson( 191*04ae99ecSEd Tanous asyncResp->res.jsonValue, 192*04ae99ecSEd Tanous messages::propertyValueFormatError(item.value().dump(), 193*04ae99ecSEd Tanous item.key())); 194*04ae99ecSEd Tanous asyncResp->res.result( 195*04ae99ecSEd Tanous boost::beast::http::status::bad_request); 196*04ae99ecSEd Tanous return; 197*04ae99ecSEd Tanous } 198*04ae99ecSEd Tanous } 199*04ae99ecSEd Tanous else if (item.key() == "RoleId") 200*04ae99ecSEd Tanous { 201*04ae99ecSEd Tanous const std::string* roleIdJson = 202*04ae99ecSEd Tanous item.value().get_ptr<const std::string*>(); 203*04ae99ecSEd Tanous if (roleIdJson == nullptr) 204*04ae99ecSEd Tanous { 205*04ae99ecSEd Tanous messages::addMessageToErrorJson( 206*04ae99ecSEd Tanous asyncResp->res.jsonValue, 207*04ae99ecSEd Tanous messages::propertyValueFormatError(item.value().dump(), 208*04ae99ecSEd Tanous item.key())); 209*04ae99ecSEd Tanous asyncResp->res.result( 210*04ae99ecSEd Tanous boost::beast::http::status::bad_request); 211*04ae99ecSEd Tanous return; 212*04ae99ecSEd Tanous } 213*04ae99ecSEd Tanous const char* priv = getRoleIdFromPrivilege(*roleIdJson); 214*04ae99ecSEd Tanous if (priv == nullptr) 215*04ae99ecSEd Tanous { 216*04ae99ecSEd Tanous messages::addMessageToErrorJson( 217*04ae99ecSEd Tanous asyncResp->res.jsonValue, 218*04ae99ecSEd Tanous messages::propertyValueNotInList(*roleIdJson, 219*04ae99ecSEd Tanous item.key())); 220*04ae99ecSEd Tanous asyncResp->res.result( 221*04ae99ecSEd Tanous boost::beast::http::status::bad_request); 222*04ae99ecSEd Tanous return; 223*04ae99ecSEd Tanous } 224*04ae99ecSEd Tanous privilege = priv; 225*04ae99ecSEd Tanous } 226*04ae99ecSEd Tanous else 227*04ae99ecSEd Tanous { 228*04ae99ecSEd Tanous messages::addMessageToErrorJson( 229*04ae99ecSEd Tanous asyncResp->res.jsonValue, 230*04ae99ecSEd Tanous messages::propertyNotWritable(item.key())); 231*04ae99ecSEd Tanous asyncResp->res.result(boost::beast::http::status::bad_request); 232*04ae99ecSEd Tanous return; 233*04ae99ecSEd Tanous } 234*04ae99ecSEd Tanous } 235*04ae99ecSEd Tanous 236*04ae99ecSEd Tanous if (username == nullptr) 237*04ae99ecSEd Tanous { 238*04ae99ecSEd Tanous messages::addMessageToErrorJson( 239*04ae99ecSEd Tanous asyncResp->res.jsonValue, 240*04ae99ecSEd Tanous messages::createFailedMissingReqProperties("UserName")); 241*04ae99ecSEd Tanous asyncResp->res.result(boost::beast::http::status::bad_request); 242*04ae99ecSEd Tanous return; 243*04ae99ecSEd Tanous } 244*04ae99ecSEd Tanous 245*04ae99ecSEd Tanous if (password == nullptr) 246*04ae99ecSEd Tanous { 247*04ae99ecSEd Tanous messages::addMessageToErrorJson( 248*04ae99ecSEd Tanous asyncResp->res.jsonValue, 249*04ae99ecSEd Tanous messages::createFailedMissingReqProperties("Password")); 250*04ae99ecSEd Tanous asyncResp->res.result(boost::beast::http::status::bad_request); 251*04ae99ecSEd Tanous return; 252*04ae99ecSEd Tanous } 253*04ae99ecSEd Tanous 254*04ae99ecSEd Tanous crow::connections::systemBus->async_method_call( 255*04ae99ecSEd Tanous [asyncResp, username{std::string(*username)}, 256*04ae99ecSEd Tanous password{std::string(*password)}]( 257*04ae99ecSEd Tanous const boost::system::error_code ec) { 258*04ae99ecSEd Tanous if (ec) 259*04ae99ecSEd Tanous { 260*04ae99ecSEd Tanous messages::addMessageToErrorJson( 261*04ae99ecSEd Tanous asyncResp->res.jsonValue, 262*04ae99ecSEd Tanous messages::resourceAlreadyExists( 263*04ae99ecSEd Tanous "#ManagerAccount.v1_0_3.ManagerAccount", "UserName", 264*04ae99ecSEd Tanous username)); 265*04ae99ecSEd Tanous asyncResp->res.result( 266*04ae99ecSEd Tanous boost::beast::http::status::bad_request); 267*04ae99ecSEd Tanous return; 268*04ae99ecSEd Tanous } 269*04ae99ecSEd Tanous 270*04ae99ecSEd Tanous if (!pamUpdatePassword(username, password)) 271*04ae99ecSEd Tanous { 272*04ae99ecSEd Tanous // At this point we have a user that's been created, but the 273*04ae99ecSEd Tanous // password set failed. Something is wrong, so delete the 274*04ae99ecSEd Tanous // user that we've already created 275*04ae99ecSEd Tanous crow::connections::systemBus->async_method_call( 276*04ae99ecSEd Tanous [asyncResp](const boost::system::error_code ec) { 277*04ae99ecSEd Tanous if (ec) 278*04ae99ecSEd Tanous { 279*04ae99ecSEd Tanous asyncResp->res.result( 280*04ae99ecSEd Tanous boost::beast::http::status:: 281*04ae99ecSEd Tanous internal_server_error); 282*04ae99ecSEd Tanous return; 283*04ae99ecSEd Tanous } 284*04ae99ecSEd Tanous 285*04ae99ecSEd Tanous asyncResp->res.result( 286*04ae99ecSEd Tanous boost::beast::http::status::bad_request); 287*04ae99ecSEd Tanous }, 288*04ae99ecSEd Tanous "xyz.openbmc_project.User.Manager", 289*04ae99ecSEd Tanous "/xyz/openbmc_project/user/" + username, 290*04ae99ecSEd Tanous "xyz.openbmc_project.Object.Delete", "Delete"); 291*04ae99ecSEd Tanous 292*04ae99ecSEd Tanous BMCWEB_LOG_ERROR << "pamUpdatePassword Failed"; 293*04ae99ecSEd Tanous return; 294*04ae99ecSEd Tanous } 295*04ae99ecSEd Tanous 296*04ae99ecSEd Tanous messages::addMessageToJsonRoot(asyncResp->res.jsonValue, 297*04ae99ecSEd Tanous messages::created()); 298*04ae99ecSEd Tanous asyncResp->res.addHeader( 299*04ae99ecSEd Tanous "Location", 300*04ae99ecSEd Tanous "/redfish/v1/AccountService/Accounts/" + username); 301*04ae99ecSEd Tanous }, 302*04ae99ecSEd Tanous "xyz.openbmc_project.User.Manager", "/xyz/openbmc_project/user", 303*04ae99ecSEd Tanous "xyz.openbmc_project.User.Manager", "CreateUser", *username, 304*04ae99ecSEd Tanous std::array<const char*, 4>{"ipmi", "redfish", "ssh", "web"}, 305*04ae99ecSEd Tanous privilege, enabled); 306*04ae99ecSEd Tanous } 307*04ae99ecSEd Tanous 308*04ae99ecSEd Tanous static const char* getRoleIdFromPrivilege(boost::beast::string_view role) 309*04ae99ecSEd Tanous { 310*04ae99ecSEd Tanous if (role == "Administrator") 311*04ae99ecSEd Tanous { 312*04ae99ecSEd Tanous return "priv-admin"; 313*04ae99ecSEd Tanous } 314*04ae99ecSEd Tanous else if (role == "Callback") 315*04ae99ecSEd Tanous { 316*04ae99ecSEd Tanous return "priv-callback"; 317*04ae99ecSEd Tanous } 318*04ae99ecSEd Tanous else if (role == "User") 319*04ae99ecSEd Tanous { 320*04ae99ecSEd Tanous return "priv-user"; 321*04ae99ecSEd Tanous } 322*04ae99ecSEd Tanous else if (role == "Operator") 323*04ae99ecSEd Tanous { 324*04ae99ecSEd Tanous return "priv-operator"; 325*04ae99ecSEd Tanous } 326*04ae99ecSEd Tanous return nullptr; 327*04ae99ecSEd Tanous } 328b9b2e0b2SEd Tanous }; 329b9b2e0b2SEd Tanous 330a840879dSEd Tanous template <typename Callback> 331a840879dSEd Tanous inline void checkDbusPathExists(const std::string& path, Callback&& callback) 332a840879dSEd Tanous { 333a840879dSEd Tanous using GetObjectType = 334a840879dSEd Tanous std::vector<std::pair<std::string, std::vector<std::string>>>; 335a840879dSEd Tanous 336a840879dSEd Tanous crow::connections::systemBus->async_method_call( 337a840879dSEd Tanous [callback{std::move(callback)}](const boost::system::error_code ec, 338a840879dSEd Tanous const GetObjectType& object_names) { 339a840879dSEd Tanous callback(ec || object_names.size() == 0); 340a840879dSEd Tanous }, 341a840879dSEd Tanous "xyz.openbmc_project.ObjectMapper", 342a840879dSEd Tanous "/xyz/openbmc_project/object_mapper", 343a840879dSEd Tanous "xyz.openbmc_project.ObjectMapper", "GetObject", path, 344a840879dSEd Tanous std::array<std::string, 0>()); 345a840879dSEd Tanous } 346a840879dSEd Tanous 347b9b2e0b2SEd Tanous class ManagerAccount : public Node 348b9b2e0b2SEd Tanous { 349b9b2e0b2SEd Tanous public: 350b9b2e0b2SEd Tanous ManagerAccount(CrowApp& app) : 351b9b2e0b2SEd Tanous Node(app, "/redfish/v1/AccountService/Accounts/<str>/", std::string()) 352b9b2e0b2SEd Tanous { 353b9b2e0b2SEd Tanous Node::json = {{"@odata.context", 354b9b2e0b2SEd Tanous "/redfish/v1/$metadata#ManagerAccount.ManagerAccount"}, 355b9b2e0b2SEd Tanous {"@odata.type", "#ManagerAccount.v1_0_3.ManagerAccount"}, 356b9b2e0b2SEd Tanous 357b9b2e0b2SEd Tanous {"Name", "User Account"}, 358b9b2e0b2SEd Tanous {"Description", "User Account"}, 359b9b2e0b2SEd Tanous {"Password", nullptr}, 360b9b2e0b2SEd Tanous {"RoleId", "Administrator"}, 361b9b2e0b2SEd Tanous {"Links", 362b9b2e0b2SEd Tanous {{"Role", 363b9b2e0b2SEd Tanous {{"@odata.id", "/redfish/v1/AccountService/Roles/" 364b9b2e0b2SEd Tanous "Administrator"}}}}}}; 365b9b2e0b2SEd Tanous 366b9b2e0b2SEd Tanous entityPrivileges = { 367b9b2e0b2SEd Tanous {boost::beast::http::verb::get, 368b9b2e0b2SEd Tanous {{"ConfigureUsers"}, {"ConfigureManager"}, {"ConfigureSelf"}}}, 369b9b2e0b2SEd Tanous {boost::beast::http::verb::head, {{"Login"}}}, 370b9b2e0b2SEd Tanous {boost::beast::http::verb::patch, {{"ConfigureUsers"}}}, 371b9b2e0b2SEd Tanous {boost::beast::http::verb::put, {{"ConfigureUsers"}}}, 372b9b2e0b2SEd Tanous {boost::beast::http::verb::delete_, {{"ConfigureUsers"}}}, 373b9b2e0b2SEd Tanous {boost::beast::http::verb::post, {{"ConfigureUsers"}}}}; 374b9b2e0b2SEd Tanous } 375b9b2e0b2SEd Tanous 376b9b2e0b2SEd Tanous private: 377b9b2e0b2SEd Tanous void doGet(crow::Response& res, const crow::Request& req, 378b9b2e0b2SEd Tanous const std::vector<std::string>& params) override 379b9b2e0b2SEd Tanous { 380b9b2e0b2SEd Tanous res.jsonValue = Node::json; 381b9b2e0b2SEd Tanous auto asyncResp = std::make_shared<AsyncResp>(res); 382b9b2e0b2SEd Tanous 383b9b2e0b2SEd Tanous if (params.size() != 1) 384b9b2e0b2SEd Tanous { 385b9b2e0b2SEd Tanous res.result(boost::beast::http::status::internal_server_error); 386b9b2e0b2SEd Tanous return; 387b9b2e0b2SEd Tanous } 388b9b2e0b2SEd Tanous 389b9b2e0b2SEd Tanous crow::connections::systemBus->async_method_call( 390b9b2e0b2SEd Tanous [asyncResp, accountName{std::string(params[0])}]( 391b9b2e0b2SEd Tanous const boost::system::error_code ec, 392b9b2e0b2SEd Tanous const ManagedObjectType& users) { 393b9b2e0b2SEd Tanous if (ec) 394b9b2e0b2SEd Tanous { 395b9b2e0b2SEd Tanous asyncResp->res.result( 396b9b2e0b2SEd Tanous boost::beast::http::status::internal_server_error); 397b9b2e0b2SEd Tanous return; 398b9b2e0b2SEd Tanous } 399b9b2e0b2SEd Tanous 400b9b2e0b2SEd Tanous for (auto& user : users) 401b9b2e0b2SEd Tanous { 402b9b2e0b2SEd Tanous const std::string& path = 403b9b2e0b2SEd Tanous static_cast<const std::string&>(user.first); 404b9b2e0b2SEd Tanous std::size_t lastIndex = path.rfind("/"); 405b9b2e0b2SEd Tanous if (lastIndex == std::string::npos) 406b9b2e0b2SEd Tanous { 407b9b2e0b2SEd Tanous lastIndex = 0; 408b9b2e0b2SEd Tanous } 409b9b2e0b2SEd Tanous else 410b9b2e0b2SEd Tanous { 411b9b2e0b2SEd Tanous lastIndex += 1; 412b9b2e0b2SEd Tanous } 413b9b2e0b2SEd Tanous if (path.substr(lastIndex) == accountName) 414b9b2e0b2SEd Tanous { 41565b0dc32SEd Tanous for (const auto& interface : user.second) 41665b0dc32SEd Tanous { 41765b0dc32SEd Tanous if (interface.first == 41865b0dc32SEd Tanous "xyz.openbmc_project.User.Attributes") 41965b0dc32SEd Tanous { 42065b0dc32SEd Tanous for (const auto& property : interface.second) 42165b0dc32SEd Tanous { 42265b0dc32SEd Tanous if (property.first == "UserEnabled") 42365b0dc32SEd Tanous { 42465b0dc32SEd Tanous const bool* userEnabled = 42565b0dc32SEd Tanous mapbox::getPtr<const bool>( 42665b0dc32SEd Tanous property.second); 42765b0dc32SEd Tanous if (userEnabled == nullptr) 42865b0dc32SEd Tanous { 42965b0dc32SEd Tanous BMCWEB_LOG_ERROR 43065b0dc32SEd Tanous << "UserEnabled wasn't a bool"; 43165b0dc32SEd Tanous continue; 43265b0dc32SEd Tanous } 43365b0dc32SEd Tanous asyncResp->res.jsonValue["Enabled"] = 43465b0dc32SEd Tanous *userEnabled; 43565b0dc32SEd Tanous } 43665b0dc32SEd Tanous else if (property.first == 43765b0dc32SEd Tanous "UserLockedForFailedAttempt") 43865b0dc32SEd Tanous { 43965b0dc32SEd Tanous const bool* userLocked = 44065b0dc32SEd Tanous mapbox::getPtr<const bool>( 44165b0dc32SEd Tanous property.second); 44265b0dc32SEd Tanous if (userLocked == nullptr) 44365b0dc32SEd Tanous { 44465b0dc32SEd Tanous BMCWEB_LOG_ERROR 44565b0dc32SEd Tanous << "UserEnabled wasn't a bool"; 44665b0dc32SEd Tanous continue; 44765b0dc32SEd Tanous } 44865b0dc32SEd Tanous asyncResp->res.jsonValue["Locked"] = 44965b0dc32SEd Tanous *userLocked; 45065b0dc32SEd Tanous } 45165b0dc32SEd Tanous } 45265b0dc32SEd Tanous } 45365b0dc32SEd Tanous } 45465b0dc32SEd Tanous 455b9b2e0b2SEd Tanous asyncResp->res.jsonValue["@odata.id"] = 456b9b2e0b2SEd Tanous "/redfish/v1/AccountService/Accounts/" + 457b9b2e0b2SEd Tanous accountName; 458b9b2e0b2SEd Tanous asyncResp->res.jsonValue["Id"] = accountName; 459b9b2e0b2SEd Tanous asyncResp->res.jsonValue["UserName"] = accountName; 460b9b2e0b2SEd Tanous 461b9b2e0b2SEd Tanous return; 462b9b2e0b2SEd Tanous } 463b9b2e0b2SEd Tanous } 464b9b2e0b2SEd Tanous 465b9b2e0b2SEd Tanous asyncResp->res.result(boost::beast::http::status::not_found); 466b9b2e0b2SEd Tanous }, 467b9b2e0b2SEd Tanous "xyz.openbmc_project.User.Manager", "/xyz/openbmc_project/user", 468b9b2e0b2SEd Tanous "org.freedesktop.DBus.ObjectManager", "GetManagedObjects"); 469b9b2e0b2SEd Tanous } 470a840879dSEd Tanous 471a840879dSEd Tanous void doPatch(crow::Response& res, const crow::Request& req, 472a840879dSEd Tanous const std::vector<std::string>& params) override 473a840879dSEd Tanous { 474a840879dSEd Tanous auto asyncResp = std::make_shared<AsyncResp>(res); 475a840879dSEd Tanous 476a840879dSEd Tanous if (params.size() != 1) 477a840879dSEd Tanous { 478a840879dSEd Tanous res.result(boost::beast::http::status::internal_server_error); 479a840879dSEd Tanous return; 480a840879dSEd Tanous } 481a840879dSEd Tanous 482a840879dSEd Tanous nlohmann::json patchRequest; 483a840879dSEd Tanous if (!json_util::processJsonFromRequest(res, req, patchRequest)) 484a840879dSEd Tanous { 485a840879dSEd Tanous return; 486a840879dSEd Tanous } 487a840879dSEd Tanous 488a840879dSEd Tanous // Check the user exists before updating the fields 489a840879dSEd Tanous checkDbusPathExists( 490a840879dSEd Tanous "/xyz/openbmc_project/users/" + params[0], 491a840879dSEd Tanous [username{std::string(params[0])}, 492a840879dSEd Tanous patchRequest(std::move(patchRequest)), 493a840879dSEd Tanous asyncResp](bool userExists) { 494a840879dSEd Tanous if (!userExists) 495a840879dSEd Tanous { 496a840879dSEd Tanous messages::addMessageToErrorJson( 497a840879dSEd Tanous asyncResp->res.jsonValue, 498a840879dSEd Tanous messages::resourceNotFound( 499a840879dSEd Tanous "#ManagerAccount.v1_0_3.ManagerAccount", username)); 500a840879dSEd Tanous 501a840879dSEd Tanous asyncResp->res.result( 502a840879dSEd Tanous boost::beast::http::status::not_found); 503a840879dSEd Tanous return; 504a840879dSEd Tanous } 505a840879dSEd Tanous 506a840879dSEd Tanous for (const auto& item : patchRequest.items()) 507a840879dSEd Tanous { 508a840879dSEd Tanous if (item.key() == "Password") 509a840879dSEd Tanous { 510a840879dSEd Tanous const std::string* passStr = 511a840879dSEd Tanous item.value().get_ptr<const std::string*>(); 512a840879dSEd Tanous if (passStr == nullptr) 513a840879dSEd Tanous { 514a840879dSEd Tanous messages::addMessageToErrorJson( 515a840879dSEd Tanous asyncResp->res.jsonValue, 516a840879dSEd Tanous messages::propertyValueFormatError( 517a840879dSEd Tanous item.value().dump(), "Password")); 518a840879dSEd Tanous return; 519a840879dSEd Tanous } 520a840879dSEd Tanous BMCWEB_LOG_DEBUG << "Updating user: " << username 521a840879dSEd Tanous << " to password " << *passStr; 522a840879dSEd Tanous if (!pamUpdatePassword(username, *passStr)) 523a840879dSEd Tanous { 524a840879dSEd Tanous BMCWEB_LOG_ERROR << "pamUpdatePassword Failed"; 525a840879dSEd Tanous asyncResp->res.result(boost::beast::http::status:: 526a840879dSEd Tanous internal_server_error); 527a840879dSEd Tanous return; 528a840879dSEd Tanous } 529a840879dSEd Tanous } 530a840879dSEd Tanous else if (item.key() == "Enabled") 531a840879dSEd Tanous { 532a840879dSEd Tanous const bool* enabledBool = 533a840879dSEd Tanous item.value().get_ptr<const bool*>(); 534a840879dSEd Tanous 535a840879dSEd Tanous if (enabledBool == nullptr) 536a840879dSEd Tanous { 537a840879dSEd Tanous messages::addMessageToErrorJson( 538a840879dSEd Tanous asyncResp->res.jsonValue, 539a840879dSEd Tanous messages::propertyValueFormatError( 540a840879dSEd Tanous item.value().dump(), "Enabled")); 541a840879dSEd Tanous return; 542a840879dSEd Tanous } 543a840879dSEd Tanous crow::connections::systemBus->async_method_call( 544a840879dSEd Tanous [asyncResp](const boost::system::error_code ec) { 545a840879dSEd Tanous if (ec) 546a840879dSEd Tanous { 547a840879dSEd Tanous BMCWEB_LOG_ERROR 548a840879dSEd Tanous << "D-Bus responses error: " << ec; 549a840879dSEd Tanous asyncResp->res.result( 550a840879dSEd Tanous boost::beast::http::status:: 551a840879dSEd Tanous internal_server_error); 552a840879dSEd Tanous return; 553a840879dSEd Tanous } 554a840879dSEd Tanous // TODO Consider support polling mechanism to 555a840879dSEd Tanous // verify status of host and chassis after 556a840879dSEd Tanous // execute the requested action. 557a840879dSEd Tanous BMCWEB_LOG_DEBUG << "Response with no content"; 558a840879dSEd Tanous asyncResp->res.result( 559a840879dSEd Tanous boost::beast::http::status::no_content); 560a840879dSEd Tanous }, 561a840879dSEd Tanous "xyz.openbmc_project.User.Manager", 562a840879dSEd Tanous "/xyz/openbmc_project/users/" + username, 563a840879dSEd Tanous "org.freedesktop.DBus.Properties", "Set", 564a840879dSEd Tanous "xyz.openbmc_project.User.Attributes" 565a840879dSEd Tanous "UserEnabled", 566a840879dSEd Tanous sdbusplus::message::variant<bool>{*enabledBool}); 567a840879dSEd Tanous } 568a840879dSEd Tanous else 569a840879dSEd Tanous { 570a840879dSEd Tanous messages::addMessageToErrorJson( 571a840879dSEd Tanous asyncResp->res.jsonValue, 572a840879dSEd Tanous messages::propertyNotWritable(item.key())); 573a840879dSEd Tanous asyncResp->res.result( 574a840879dSEd Tanous boost::beast::http::status::bad_request); 575a840879dSEd Tanous return; 576a840879dSEd Tanous } 577a840879dSEd Tanous } 578a840879dSEd Tanous }); 579a840879dSEd Tanous } 58006e086d9SEd Tanous 58106e086d9SEd Tanous void doDelete(crow::Response& res, const crow::Request& req, 58206e086d9SEd Tanous const std::vector<std::string>& params) override 58306e086d9SEd Tanous { 58406e086d9SEd Tanous auto asyncResp = std::make_shared<AsyncResp>(res); 58506e086d9SEd Tanous 58606e086d9SEd Tanous if (params.size() != 1) 58706e086d9SEd Tanous { 58806e086d9SEd Tanous res.result(boost::beast::http::status::internal_server_error); 58906e086d9SEd Tanous return; 59006e086d9SEd Tanous } 59106e086d9SEd Tanous 59206e086d9SEd Tanous const std::string userPath = "/xyz/openbmc_project/user/" + params[0]; 59306e086d9SEd Tanous 59406e086d9SEd Tanous crow::connections::systemBus->async_method_call( 59506e086d9SEd Tanous [asyncResp, username{std::move(params[0])}]( 59606e086d9SEd Tanous const boost::system::error_code ec) { 59706e086d9SEd Tanous if (ec) 59806e086d9SEd Tanous { 59906e086d9SEd Tanous messages::addMessageToErrorJson( 60006e086d9SEd Tanous asyncResp->res.jsonValue, 60106e086d9SEd Tanous messages::resourceNotFound( 60206e086d9SEd Tanous "#ManagerAccount.v1_0_3.ManagerAccount", username)); 60306e086d9SEd Tanous asyncResp->res.result( 60406e086d9SEd Tanous boost::beast::http::status::not_found); 60506e086d9SEd Tanous return; 60606e086d9SEd Tanous } 60706e086d9SEd Tanous 60806e086d9SEd Tanous messages::addMessageToJsonRoot(asyncResp->res.jsonValue, 60906e086d9SEd Tanous messages::accountRemoved()); 61006e086d9SEd Tanous }, 61106e086d9SEd Tanous "xyz.openbmc_project.User.Manager", userPath, 61206e086d9SEd Tanous "xyz.openbmc_project.Object.Delete", "Delete"); 61306e086d9SEd Tanous } 614b9b2e0b2SEd Tanous }; 61588d16c9aSLewanczyk, Dawid 61688d16c9aSLewanczyk, Dawid } // namespace redfish 617