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 19b9b2e0b2SEd Tanous #include <openbmc_dbus_rest.hpp> 20*a840879dSEd Tanous #include <utils/json_utils.hpp> 21b9b2e0b2SEd Tanous 221abe55efSEd Tanous namespace redfish 231abe55efSEd Tanous { 2488d16c9aSLewanczyk, Dawid 25b9b2e0b2SEd Tanous using ManagedObjectType = std::vector<std::pair< 26b9b2e0b2SEd Tanous sdbusplus::message::object_path, 27b9b2e0b2SEd Tanous boost::container::flat_map< 28b9b2e0b2SEd Tanous std::string, boost::container::flat_map< 29b9b2e0b2SEd Tanous std::string, sdbusplus::message::variant<bool>>>>>; 30b9b2e0b2SEd Tanous 311abe55efSEd Tanous class AccountService : public Node 321abe55efSEd Tanous { 3388d16c9aSLewanczyk, Dawid public: 341abe55efSEd Tanous AccountService(CrowApp& app) : Node(app, "/redfish/v1/AccountService/") 351abe55efSEd Tanous { 36c1a46bd2SBorawski.Lukasz Node::json["@odata.id"] = "/redfish/v1/AccountService"; 37c1a46bd2SBorawski.Lukasz Node::json["@odata.type"] = "#AccountService.v1_1_0.AccountService"; 38c1a46bd2SBorawski.Lukasz Node::json["@odata.context"] = 3988d16c9aSLewanczyk, Dawid "/redfish/v1/$metadata#AccountService.AccountService"; 40c1a46bd2SBorawski.Lukasz Node::json["Id"] = "AccountService"; 41c1a46bd2SBorawski.Lukasz Node::json["Description"] = "BMC User Accounts"; 42c1a46bd2SBorawski.Lukasz Node::json["Name"] = "Account Service"; 43c1a46bd2SBorawski.Lukasz Node::json["ServiceEnabled"] = true; 44c1a46bd2SBorawski.Lukasz Node::json["MinPasswordLength"] = 1; 45c1a46bd2SBorawski.Lukasz Node::json["MaxPasswordLength"] = 20; 461abe55efSEd Tanous Node::json["Accounts"]["@odata.id"] = 471abe55efSEd Tanous "/redfish/v1/AccountService/Accounts"; 48c1a46bd2SBorawski.Lukasz Node::json["Roles"]["@odata.id"] = "/redfish/v1/AccountService/Roles"; 493ebd75f7SEd Tanous 503ebd75f7SEd Tanous entityPrivileges = { 514b1b8683SBorawski.Lukasz {boost::beast::http::verb::get, 524b1b8683SBorawski.Lukasz {{"ConfigureUsers"}, {"ConfigureManager"}}}, 53e0d918bcSEd Tanous {boost::beast::http::verb::head, {{"Login"}}}, 54e0d918bcSEd Tanous {boost::beast::http::verb::patch, {{"ConfigureUsers"}}}, 55e0d918bcSEd Tanous {boost::beast::http::verb::put, {{"ConfigureUsers"}}}, 56e0d918bcSEd Tanous {boost::beast::http::verb::delete_, {{"ConfigureUsers"}}}, 57e0d918bcSEd Tanous {boost::beast::http::verb::post, {{"ConfigureUsers"}}}}; 5888d16c9aSLewanczyk, Dawid } 5988d16c9aSLewanczyk, Dawid 6088d16c9aSLewanczyk, Dawid private: 6155c7b7a2SEd Tanous void doGet(crow::Response& res, const crow::Request& req, 621abe55efSEd Tanous const std::vector<std::string>& params) override 631abe55efSEd Tanous { 6455c7b7a2SEd Tanous res.jsonValue = Node::json; 6588d16c9aSLewanczyk, Dawid res.end(); 6688d16c9aSLewanczyk, Dawid } 6788d16c9aSLewanczyk, Dawid }; 68b9b2e0b2SEd Tanous class AccountsCollection : public Node 69b9b2e0b2SEd Tanous { 70b9b2e0b2SEd Tanous public: 71b9b2e0b2SEd Tanous AccountsCollection(CrowApp& app) : 72b9b2e0b2SEd Tanous Node(app, "/redfish/v1/AccountService/Accounts/") 73b9b2e0b2SEd Tanous { 74b9b2e0b2SEd Tanous 75b9b2e0b2SEd Tanous Node::json = {{"@odata.context", "/redfish/v1/" 76b9b2e0b2SEd Tanous "$metadata#ManagerAccountCollection." 77b9b2e0b2SEd Tanous "ManagerAccountCollection"}, 78b9b2e0b2SEd Tanous {"@odata.id", "/redfish/v1/AccountService/Accounts"}, 79b9b2e0b2SEd Tanous {"@odata.type", "#ManagerAccountCollection." 80b9b2e0b2SEd Tanous "ManagerAccountCollection"}, 81b9b2e0b2SEd Tanous {"Name", "Accounts Collection"}, 82b9b2e0b2SEd Tanous {"Description", "BMC User Accounts"}}; 83b9b2e0b2SEd Tanous 84b9b2e0b2SEd Tanous entityPrivileges = { 85b9b2e0b2SEd Tanous {boost::beast::http::verb::get, 86b9b2e0b2SEd Tanous {{"ConfigureUsers"}, {"ConfigureManager"}}}, 87b9b2e0b2SEd Tanous {boost::beast::http::verb::head, {{"Login"}}}, 88b9b2e0b2SEd Tanous {boost::beast::http::verb::patch, {{"ConfigureUsers"}}}, 89b9b2e0b2SEd Tanous {boost::beast::http::verb::put, {{"ConfigureUsers"}}}, 90b9b2e0b2SEd Tanous {boost::beast::http::verb::delete_, {{"ConfigureUsers"}}}, 91b9b2e0b2SEd Tanous {boost::beast::http::verb::post, {{"ConfigureUsers"}}}}; 92b9b2e0b2SEd Tanous } 93b9b2e0b2SEd Tanous 94b9b2e0b2SEd Tanous private: 95b9b2e0b2SEd Tanous void doGet(crow::Response& res, const crow::Request& req, 96b9b2e0b2SEd Tanous const std::vector<std::string>& params) override 97b9b2e0b2SEd Tanous { 98b9b2e0b2SEd Tanous res.jsonValue = Node::json; 99b9b2e0b2SEd Tanous auto asyncResp = std::make_shared<AsyncResp>(res); 100b9b2e0b2SEd Tanous crow::connections::systemBus->async_method_call( 101b9b2e0b2SEd Tanous [asyncResp](const boost::system::error_code ec, 102b9b2e0b2SEd Tanous const ManagedObjectType& users) { 103b9b2e0b2SEd Tanous if (ec) 104b9b2e0b2SEd Tanous { 105b9b2e0b2SEd Tanous asyncResp->res.result( 106b9b2e0b2SEd Tanous boost::beast::http::status::internal_server_error); 107b9b2e0b2SEd Tanous return; 108b9b2e0b2SEd Tanous } 109b9b2e0b2SEd Tanous 110b9b2e0b2SEd Tanous nlohmann::json& memberArray = 111b9b2e0b2SEd Tanous asyncResp->res.jsonValue["Members"]; 112b9b2e0b2SEd Tanous memberArray = nlohmann::json::array(); 113b9b2e0b2SEd Tanous 114b9b2e0b2SEd Tanous asyncResp->res.jsonValue["Members@odata.count"] = users.size(); 115b9b2e0b2SEd Tanous for (auto& user : users) 116b9b2e0b2SEd Tanous { 117b9b2e0b2SEd Tanous const std::string& path = 118b9b2e0b2SEd Tanous static_cast<const std::string&>(user.first); 119b9b2e0b2SEd Tanous std::size_t lastIndex = path.rfind("/"); 120b9b2e0b2SEd Tanous if (lastIndex == std::string::npos) 121b9b2e0b2SEd Tanous { 122b9b2e0b2SEd Tanous lastIndex = 0; 123b9b2e0b2SEd Tanous } 124b9b2e0b2SEd Tanous else 125b9b2e0b2SEd Tanous { 126b9b2e0b2SEd Tanous lastIndex += 1; 127b9b2e0b2SEd Tanous } 128b9b2e0b2SEd Tanous memberArray.push_back( 129b9b2e0b2SEd Tanous {{"@odata.id", "/redfish/v1/AccountService/Accounts/" + 130b9b2e0b2SEd Tanous path.substr(lastIndex)}}); 131b9b2e0b2SEd Tanous } 132b9b2e0b2SEd Tanous }, 133b9b2e0b2SEd Tanous "xyz.openbmc_project.User.Manager", "/xyz/openbmc_project/user", 134b9b2e0b2SEd Tanous "org.freedesktop.DBus.ObjectManager", "GetManagedObjects"); 135b9b2e0b2SEd Tanous } 136b9b2e0b2SEd Tanous }; 137b9b2e0b2SEd Tanous 138*a840879dSEd Tanous template <typename Callback> 139*a840879dSEd Tanous inline void checkDbusPathExists(const std::string& path, Callback&& callback) 140*a840879dSEd Tanous { 141*a840879dSEd Tanous using GetObjectType = 142*a840879dSEd Tanous std::vector<std::pair<std::string, std::vector<std::string>>>; 143*a840879dSEd Tanous 144*a840879dSEd Tanous crow::connections::systemBus->async_method_call( 145*a840879dSEd Tanous [callback{std::move(callback)}](const boost::system::error_code ec, 146*a840879dSEd Tanous const GetObjectType& object_names) { 147*a840879dSEd Tanous callback(ec || object_names.size() == 0); 148*a840879dSEd Tanous }, 149*a840879dSEd Tanous "xyz.openbmc_project.ObjectMapper", 150*a840879dSEd Tanous "/xyz/openbmc_project/object_mapper", 151*a840879dSEd Tanous "xyz.openbmc_project.ObjectMapper", "GetObject", path, 152*a840879dSEd Tanous std::array<std::string, 0>()); 153*a840879dSEd Tanous } 154*a840879dSEd Tanous 155b9b2e0b2SEd Tanous class ManagerAccount : public Node 156b9b2e0b2SEd Tanous { 157b9b2e0b2SEd Tanous public: 158b9b2e0b2SEd Tanous ManagerAccount(CrowApp& app) : 159b9b2e0b2SEd Tanous Node(app, "/redfish/v1/AccountService/Accounts/<str>/", std::string()) 160b9b2e0b2SEd Tanous { 161b9b2e0b2SEd Tanous Node::json = {{"@odata.context", 162b9b2e0b2SEd Tanous "/redfish/v1/$metadata#ManagerAccount.ManagerAccount"}, 163b9b2e0b2SEd Tanous {"@odata.type", "#ManagerAccount.v1_0_3.ManagerAccount"}, 164b9b2e0b2SEd Tanous 165b9b2e0b2SEd Tanous {"Name", "User Account"}, 166b9b2e0b2SEd Tanous {"Description", "User Account"}, 167b9b2e0b2SEd Tanous {"Enabled", false}, 168b9b2e0b2SEd Tanous {"Password", nullptr}, 169b9b2e0b2SEd Tanous {"RoleId", "Administrator"}, 170b9b2e0b2SEd Tanous {"Links", 171b9b2e0b2SEd Tanous {{"Role", 172b9b2e0b2SEd Tanous {{"@odata.id", "/redfish/v1/AccountService/Roles/" 173b9b2e0b2SEd Tanous "Administrator"}}}}}}; 174b9b2e0b2SEd Tanous 175b9b2e0b2SEd Tanous entityPrivileges = { 176b9b2e0b2SEd Tanous {boost::beast::http::verb::get, 177b9b2e0b2SEd Tanous {{"ConfigureUsers"}, {"ConfigureManager"}, {"ConfigureSelf"}}}, 178b9b2e0b2SEd Tanous {boost::beast::http::verb::head, {{"Login"}}}, 179b9b2e0b2SEd Tanous {boost::beast::http::verb::patch, {{"ConfigureUsers"}}}, 180b9b2e0b2SEd Tanous {boost::beast::http::verb::put, {{"ConfigureUsers"}}}, 181b9b2e0b2SEd Tanous {boost::beast::http::verb::delete_, {{"ConfigureUsers"}}}, 182b9b2e0b2SEd Tanous {boost::beast::http::verb::post, {{"ConfigureUsers"}}}}; 183b9b2e0b2SEd Tanous } 184b9b2e0b2SEd Tanous 185b9b2e0b2SEd Tanous private: 186b9b2e0b2SEd Tanous void doGet(crow::Response& res, const crow::Request& req, 187b9b2e0b2SEd Tanous const std::vector<std::string>& params) override 188b9b2e0b2SEd Tanous { 189b9b2e0b2SEd Tanous res.jsonValue = Node::json; 190b9b2e0b2SEd Tanous auto asyncResp = std::make_shared<AsyncResp>(res); 191b9b2e0b2SEd Tanous 192b9b2e0b2SEd Tanous if (params.size() != 1) 193b9b2e0b2SEd Tanous { 194b9b2e0b2SEd Tanous res.result(boost::beast::http::status::internal_server_error); 195b9b2e0b2SEd Tanous return; 196b9b2e0b2SEd Tanous } 197b9b2e0b2SEd Tanous 198b9b2e0b2SEd Tanous crow::connections::systemBus->async_method_call( 199b9b2e0b2SEd Tanous [asyncResp, accountName{std::string(params[0])}]( 200b9b2e0b2SEd Tanous const boost::system::error_code ec, 201b9b2e0b2SEd Tanous const ManagedObjectType& users) { 202b9b2e0b2SEd Tanous if (ec) 203b9b2e0b2SEd Tanous { 204b9b2e0b2SEd Tanous asyncResp->res.result( 205b9b2e0b2SEd Tanous boost::beast::http::status::internal_server_error); 206b9b2e0b2SEd Tanous return; 207b9b2e0b2SEd Tanous } 208b9b2e0b2SEd Tanous 209b9b2e0b2SEd Tanous for (auto& user : users) 210b9b2e0b2SEd Tanous { 211b9b2e0b2SEd Tanous const std::string& path = 212b9b2e0b2SEd Tanous static_cast<const std::string&>(user.first); 213b9b2e0b2SEd Tanous std::size_t lastIndex = path.rfind("/"); 214b9b2e0b2SEd Tanous if (lastIndex == std::string::npos) 215b9b2e0b2SEd Tanous { 216b9b2e0b2SEd Tanous lastIndex = 0; 217b9b2e0b2SEd Tanous } 218b9b2e0b2SEd Tanous else 219b9b2e0b2SEd Tanous { 220b9b2e0b2SEd Tanous lastIndex += 1; 221b9b2e0b2SEd Tanous } 222b9b2e0b2SEd Tanous if (path.substr(lastIndex) == accountName) 223b9b2e0b2SEd Tanous { 224b9b2e0b2SEd Tanous asyncResp->res.jsonValue["@odata.id"] = 225b9b2e0b2SEd Tanous "/redfish/v1/AccountService/Accounts/" + 226b9b2e0b2SEd Tanous accountName; 227b9b2e0b2SEd Tanous asyncResp->res.jsonValue["Id"] = accountName; 228b9b2e0b2SEd Tanous asyncResp->res.jsonValue["UserName"] = accountName; 229b9b2e0b2SEd Tanous 230b9b2e0b2SEd Tanous return; 231b9b2e0b2SEd Tanous } 232b9b2e0b2SEd Tanous } 233b9b2e0b2SEd Tanous 234b9b2e0b2SEd Tanous asyncResp->res.result(boost::beast::http::status::not_found); 235b9b2e0b2SEd Tanous }, 236b9b2e0b2SEd Tanous "xyz.openbmc_project.User.Manager", "/xyz/openbmc_project/user", 237b9b2e0b2SEd Tanous "org.freedesktop.DBus.ObjectManager", "GetManagedObjects"); 238b9b2e0b2SEd Tanous } 239*a840879dSEd Tanous 240*a840879dSEd Tanous void doPatch(crow::Response& res, const crow::Request& req, 241*a840879dSEd Tanous const std::vector<std::string>& params) override 242*a840879dSEd Tanous { 243*a840879dSEd Tanous auto asyncResp = std::make_shared<AsyncResp>(res); 244*a840879dSEd Tanous 245*a840879dSEd Tanous if (params.size() != 1) 246*a840879dSEd Tanous { 247*a840879dSEd Tanous res.result(boost::beast::http::status::internal_server_error); 248*a840879dSEd Tanous return; 249*a840879dSEd Tanous } 250*a840879dSEd Tanous 251*a840879dSEd Tanous nlohmann::json patchRequest; 252*a840879dSEd Tanous if (!json_util::processJsonFromRequest(res, req, patchRequest)) 253*a840879dSEd Tanous { 254*a840879dSEd Tanous return; 255*a840879dSEd Tanous } 256*a840879dSEd Tanous 257*a840879dSEd Tanous // Check the user exists before updating the fields 258*a840879dSEd Tanous checkDbusPathExists( 259*a840879dSEd Tanous "/xyz/openbmc_project/users/" + params[0], 260*a840879dSEd Tanous [username{std::string(params[0])}, 261*a840879dSEd Tanous patchRequest(std::move(patchRequest)), 262*a840879dSEd Tanous asyncResp](bool userExists) { 263*a840879dSEd Tanous if (!userExists) 264*a840879dSEd Tanous { 265*a840879dSEd Tanous messages::addMessageToErrorJson( 266*a840879dSEd Tanous asyncResp->res.jsonValue, 267*a840879dSEd Tanous messages::resourceNotFound( 268*a840879dSEd Tanous "#ManagerAccount.v1_0_3.ManagerAccount", username)); 269*a840879dSEd Tanous 270*a840879dSEd Tanous asyncResp->res.result( 271*a840879dSEd Tanous boost::beast::http::status::not_found); 272*a840879dSEd Tanous return; 273*a840879dSEd Tanous } 274*a840879dSEd Tanous 275*a840879dSEd Tanous for (const auto& item : patchRequest.items()) 276*a840879dSEd Tanous { 277*a840879dSEd Tanous if (item.key() == "Password") 278*a840879dSEd Tanous { 279*a840879dSEd Tanous const std::string* passStr = 280*a840879dSEd Tanous item.value().get_ptr<const std::string*>(); 281*a840879dSEd Tanous if (passStr == nullptr) 282*a840879dSEd Tanous { 283*a840879dSEd Tanous messages::addMessageToErrorJson( 284*a840879dSEd Tanous asyncResp->res.jsonValue, 285*a840879dSEd Tanous messages::propertyValueFormatError( 286*a840879dSEd Tanous item.value().dump(), "Password")); 287*a840879dSEd Tanous return; 288*a840879dSEd Tanous } 289*a840879dSEd Tanous BMCWEB_LOG_DEBUG << "Updating user: " << username 290*a840879dSEd Tanous << " to password " << *passStr; 291*a840879dSEd Tanous if (!pamUpdatePassword(username, *passStr)) 292*a840879dSEd Tanous { 293*a840879dSEd Tanous BMCWEB_LOG_ERROR << "pamUpdatePassword Failed"; 294*a840879dSEd Tanous asyncResp->res.result(boost::beast::http::status:: 295*a840879dSEd Tanous internal_server_error); 296*a840879dSEd Tanous return; 297*a840879dSEd Tanous } 298*a840879dSEd Tanous } 299*a840879dSEd Tanous else if (item.key() == "Enabled") 300*a840879dSEd Tanous { 301*a840879dSEd Tanous const bool* enabledBool = 302*a840879dSEd Tanous item.value().get_ptr<const bool*>(); 303*a840879dSEd Tanous 304*a840879dSEd Tanous if (enabledBool == nullptr) 305*a840879dSEd Tanous { 306*a840879dSEd Tanous messages::addMessageToErrorJson( 307*a840879dSEd Tanous asyncResp->res.jsonValue, 308*a840879dSEd Tanous messages::propertyValueFormatError( 309*a840879dSEd Tanous item.value().dump(), "Enabled")); 310*a840879dSEd Tanous return; 311*a840879dSEd Tanous } 312*a840879dSEd Tanous crow::connections::systemBus->async_method_call( 313*a840879dSEd Tanous [asyncResp](const boost::system::error_code ec) { 314*a840879dSEd Tanous if (ec) 315*a840879dSEd Tanous { 316*a840879dSEd Tanous BMCWEB_LOG_ERROR 317*a840879dSEd Tanous << "D-Bus responses error: " << ec; 318*a840879dSEd Tanous asyncResp->res.result( 319*a840879dSEd Tanous boost::beast::http::status:: 320*a840879dSEd Tanous internal_server_error); 321*a840879dSEd Tanous return; 322*a840879dSEd Tanous } 323*a840879dSEd Tanous // TODO Consider support polling mechanism to 324*a840879dSEd Tanous // verify status of host and chassis after 325*a840879dSEd Tanous // execute the requested action. 326*a840879dSEd Tanous BMCWEB_LOG_DEBUG << "Response with no content"; 327*a840879dSEd Tanous asyncResp->res.result( 328*a840879dSEd Tanous boost::beast::http::status::no_content); 329*a840879dSEd Tanous }, 330*a840879dSEd Tanous "xyz.openbmc_project.User.Manager", 331*a840879dSEd Tanous "/xyz/openbmc_project/users/" + username, 332*a840879dSEd Tanous "org.freedesktop.DBus.Properties", "Set", 333*a840879dSEd Tanous "xyz.openbmc_project.User.Attributes" 334*a840879dSEd Tanous "UserEnabled", 335*a840879dSEd Tanous sdbusplus::message::variant<bool>{*enabledBool}); 336*a840879dSEd Tanous } 337*a840879dSEd Tanous else 338*a840879dSEd Tanous { 339*a840879dSEd Tanous messages::addMessageToErrorJson( 340*a840879dSEd Tanous asyncResp->res.jsonValue, 341*a840879dSEd Tanous messages::propertyNotWritable(item.key())); 342*a840879dSEd Tanous asyncResp->res.result( 343*a840879dSEd Tanous boost::beast::http::status::bad_request); 344*a840879dSEd Tanous return; 345*a840879dSEd Tanous } 346*a840879dSEd Tanous } 347*a840879dSEd Tanous }); 348*a840879dSEd Tanous } 349b9b2e0b2SEd Tanous }; 35088d16c9aSLewanczyk, Dawid 35188d16c9aSLewanczyk, Dawid } // namespace redfish 352