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< 2984e12cb7SAppaRao Puli std::string, 3084e12cb7SAppaRao Puli boost::container::flat_map< 3184e12cb7SAppaRao Puli std::string, sdbusplus::message::variant<bool, std::string>>>>>; 3284e12cb7SAppaRao Puli 3384e12cb7SAppaRao Puli inline std::string getPrivilegeFromRoleId(boost::beast::string_view role) 3484e12cb7SAppaRao Puli { 3584e12cb7SAppaRao Puli if (role == "priv-admin") 3684e12cb7SAppaRao Puli { 3784e12cb7SAppaRao Puli return "Administrator"; 3884e12cb7SAppaRao Puli } 3984e12cb7SAppaRao Puli else if (role == "priv-callback") 4084e12cb7SAppaRao Puli { 4184e12cb7SAppaRao Puli return "Callback"; 4284e12cb7SAppaRao Puli } 4384e12cb7SAppaRao Puli else if (role == "priv-user") 4484e12cb7SAppaRao Puli { 4584e12cb7SAppaRao Puli return "User"; 4684e12cb7SAppaRao Puli } 4784e12cb7SAppaRao Puli else if (role == "priv-operator") 4884e12cb7SAppaRao Puli { 4984e12cb7SAppaRao Puli return "Operator"; 5084e12cb7SAppaRao Puli } 5184e12cb7SAppaRao Puli return ""; 5284e12cb7SAppaRao Puli } 5384e12cb7SAppaRao Puli inline std::string getRoleIdFromPrivilege(boost::beast::string_view role) 5484e12cb7SAppaRao Puli { 5584e12cb7SAppaRao Puli if (role == "Administrator") 5684e12cb7SAppaRao Puli { 5784e12cb7SAppaRao Puli return "priv-admin"; 5884e12cb7SAppaRao Puli } 5984e12cb7SAppaRao Puli else if (role == "Callback") 6084e12cb7SAppaRao Puli { 6184e12cb7SAppaRao Puli return "priv-callback"; 6284e12cb7SAppaRao Puli } 6384e12cb7SAppaRao Puli else if (role == "User") 6484e12cb7SAppaRao Puli { 6584e12cb7SAppaRao Puli return "priv-user"; 6684e12cb7SAppaRao Puli } 6784e12cb7SAppaRao Puli else if (role == "Operator") 6884e12cb7SAppaRao Puli { 6984e12cb7SAppaRao Puli return "priv-operator"; 7084e12cb7SAppaRao Puli } 7184e12cb7SAppaRao Puli return ""; 7284e12cb7SAppaRao Puli } 73b9b2e0b2SEd Tanous 741abe55efSEd Tanous class AccountService : public Node 751abe55efSEd Tanous { 7688d16c9aSLewanczyk, Dawid public: 771abe55efSEd Tanous AccountService(CrowApp& app) : Node(app, "/redfish/v1/AccountService/") 781abe55efSEd Tanous { 793ebd75f7SEd Tanous entityPrivileges = { 804b1b8683SBorawski.Lukasz {boost::beast::http::verb::get, 814b1b8683SBorawski.Lukasz {{"ConfigureUsers"}, {"ConfigureManager"}}}, 82e0d918bcSEd Tanous {boost::beast::http::verb::head, {{"Login"}}}, 83e0d918bcSEd Tanous {boost::beast::http::verb::patch, {{"ConfigureUsers"}}}, 84e0d918bcSEd Tanous {boost::beast::http::verb::put, {{"ConfigureUsers"}}}, 85e0d918bcSEd Tanous {boost::beast::http::verb::delete_, {{"ConfigureUsers"}}}, 86e0d918bcSEd Tanous {boost::beast::http::verb::post, {{"ConfigureUsers"}}}}; 8788d16c9aSLewanczyk, Dawid } 8888d16c9aSLewanczyk, Dawid 8988d16c9aSLewanczyk, Dawid private: 9055c7b7a2SEd Tanous void doGet(crow::Response& res, const crow::Request& req, 911abe55efSEd Tanous const std::vector<std::string>& params) override 921abe55efSEd Tanous { 93*3d958bbcSAppaRao Puli auto asyncResp = std::make_shared<AsyncResp>(res); 94*3d958bbcSAppaRao Puli res.jsonValue = { 95*3d958bbcSAppaRao Puli {"@odata.context", "/redfish/v1/" 96*3d958bbcSAppaRao Puli "$metadata#AccountService.AccountService"}, 97*3d958bbcSAppaRao Puli {"@odata.id", "/redfish/v1/AccountService"}, 98*3d958bbcSAppaRao Puli {"@odata.type", "#AccountService." 99*3d958bbcSAppaRao Puli "v1_1_0.AccountService"}, 100*3d958bbcSAppaRao Puli {"Id", "AccountService"}, 101*3d958bbcSAppaRao Puli {"Name", "Account Service"}, 102*3d958bbcSAppaRao Puli {"Description", "Account Service"}, 103*3d958bbcSAppaRao Puli {"ServiceEnabled", true}, 104*3d958bbcSAppaRao Puli {"MaxPasswordLength", 31}, 105*3d958bbcSAppaRao Puli {"Accounts", 106*3d958bbcSAppaRao Puli {{"@odata.id", "/redfish/v1/AccountService/Accounts"}}}, 107*3d958bbcSAppaRao Puli {"Roles", {{"@odata.id", "/redfish/v1/AccountService/Roles"}}}}; 1080f74e643SEd Tanous 109*3d958bbcSAppaRao Puli crow::connections::systemBus->async_method_call( 110*3d958bbcSAppaRao Puli [asyncResp]( 111*3d958bbcSAppaRao Puli const boost::system::error_code ec, 112*3d958bbcSAppaRao Puli const std::vector<std::pair< 113*3d958bbcSAppaRao Puli std::string, 114*3d958bbcSAppaRao Puli sdbusplus::message::variant<uint32_t, uint16_t, uint8_t>>>& 115*3d958bbcSAppaRao Puli propertiesList) { 116*3d958bbcSAppaRao Puli if (ec) 117*3d958bbcSAppaRao Puli { 118*3d958bbcSAppaRao Puli messages::internalError(asyncResp->res); 119*3d958bbcSAppaRao Puli return; 120*3d958bbcSAppaRao Puli } 121*3d958bbcSAppaRao Puli BMCWEB_LOG_DEBUG << "Got " << propertiesList.size() 122*3d958bbcSAppaRao Puli << "properties for AccountService"; 123*3d958bbcSAppaRao Puli for (const std::pair<std::string, 124*3d958bbcSAppaRao Puli sdbusplus::message::variant< 125*3d958bbcSAppaRao Puli uint32_t, uint16_t, uint8_t>>& 126*3d958bbcSAppaRao Puli property : propertiesList) 127*3d958bbcSAppaRao Puli { 128*3d958bbcSAppaRao Puli if (property.first == "MinPasswordLength") 129*3d958bbcSAppaRao Puli { 130*3d958bbcSAppaRao Puli const uint8_t* value = 131*3d958bbcSAppaRao Puli sdbusplus::message::variant_ns::get_if<uint8_t>( 132*3d958bbcSAppaRao Puli &property.second); 133*3d958bbcSAppaRao Puli if (value != nullptr) 134*3d958bbcSAppaRao Puli { 135*3d958bbcSAppaRao Puli asyncResp->res.jsonValue["MinPasswordLength"] = 136*3d958bbcSAppaRao Puli *value; 137*3d958bbcSAppaRao Puli } 138*3d958bbcSAppaRao Puli } 139*3d958bbcSAppaRao Puli if (property.first == "AccountUnlockTimeout") 140*3d958bbcSAppaRao Puli { 141*3d958bbcSAppaRao Puli const uint32_t* value = 142*3d958bbcSAppaRao Puli sdbusplus::message::variant_ns::get_if<uint32_t>( 143*3d958bbcSAppaRao Puli &property.second); 144*3d958bbcSAppaRao Puli if (value != nullptr) 145*3d958bbcSAppaRao Puli { 146*3d958bbcSAppaRao Puli asyncResp->res.jsonValue["AccountLockoutDuration"] = 147*3d958bbcSAppaRao Puli *value; 148*3d958bbcSAppaRao Puli } 149*3d958bbcSAppaRao Puli } 150*3d958bbcSAppaRao Puli if (property.first == "MaxLoginAttemptBeforeLockout") 151*3d958bbcSAppaRao Puli { 152*3d958bbcSAppaRao Puli const uint16_t* value = 153*3d958bbcSAppaRao Puli sdbusplus::message::variant_ns::get_if<uint16_t>( 154*3d958bbcSAppaRao Puli &property.second); 155*3d958bbcSAppaRao Puli if (value != nullptr) 156*3d958bbcSAppaRao Puli { 157*3d958bbcSAppaRao Puli asyncResp->res 158*3d958bbcSAppaRao Puli .jsonValue["AccountLockoutThreshold"] = *value; 159*3d958bbcSAppaRao Puli } 160*3d958bbcSAppaRao Puli } 161*3d958bbcSAppaRao Puli } 162*3d958bbcSAppaRao Puli }, 163*3d958bbcSAppaRao Puli "xyz.openbmc_project.User.Manager", "/xyz/openbmc_project/user", 164*3d958bbcSAppaRao Puli "org.freedesktop.DBus.Properties", "GetAll", 165*3d958bbcSAppaRao Puli "xyz.openbmc_project.User.AccountPolicy"); 166*3d958bbcSAppaRao Puli } 167*3d958bbcSAppaRao Puli void doPatch(crow::Response& res, const crow::Request& req, 168*3d958bbcSAppaRao Puli const std::vector<std::string>& params) override 169*3d958bbcSAppaRao Puli { 170*3d958bbcSAppaRao Puli auto asyncResp = std::make_shared<AsyncResp>(res); 171*3d958bbcSAppaRao Puli 172*3d958bbcSAppaRao Puli std::optional<uint32_t> unlockTimeout; 173*3d958bbcSAppaRao Puli std::optional<uint16_t> lockoutThreshold; 174*3d958bbcSAppaRao Puli if (!json_util::readJson(req, res, "AccountLockoutDuration", 175*3d958bbcSAppaRao Puli unlockTimeout, "AccountLockoutThreshold", 176*3d958bbcSAppaRao Puli lockoutThreshold)) 177*3d958bbcSAppaRao Puli { 178*3d958bbcSAppaRao Puli return; 179*3d958bbcSAppaRao Puli } 180*3d958bbcSAppaRao Puli if (unlockTimeout) 181*3d958bbcSAppaRao Puli { 182*3d958bbcSAppaRao Puli crow::connections::systemBus->async_method_call( 183*3d958bbcSAppaRao Puli [asyncResp](const boost::system::error_code ec) { 184*3d958bbcSAppaRao Puli if (ec) 185*3d958bbcSAppaRao Puli { 186*3d958bbcSAppaRao Puli messages::internalError(asyncResp->res); 187*3d958bbcSAppaRao Puli return; 188*3d958bbcSAppaRao Puli } 189*3d958bbcSAppaRao Puli }, 190*3d958bbcSAppaRao Puli "xyz.openbmc_project.User.Manager", "/xyz/openbmc_project/user", 191*3d958bbcSAppaRao Puli "org.freedesktop.DBus.Properties", "Set", 192*3d958bbcSAppaRao Puli "xyz.openbmc_project.User.AccountPolicy", 193*3d958bbcSAppaRao Puli "AccountUnlockTimeout", 194*3d958bbcSAppaRao Puli sdbusplus::message::variant<uint32_t>(*unlockTimeout)); 195*3d958bbcSAppaRao Puli } 196*3d958bbcSAppaRao Puli if (lockoutThreshold) 197*3d958bbcSAppaRao Puli { 198*3d958bbcSAppaRao Puli crow::connections::systemBus->async_method_call( 199*3d958bbcSAppaRao Puli [asyncResp](const boost::system::error_code ec) { 200*3d958bbcSAppaRao Puli if (ec) 201*3d958bbcSAppaRao Puli { 202*3d958bbcSAppaRao Puli messages::internalError(asyncResp->res); 203*3d958bbcSAppaRao Puli return; 204*3d958bbcSAppaRao Puli } 205*3d958bbcSAppaRao Puli }, 206*3d958bbcSAppaRao Puli "xyz.openbmc_project.User.Manager", "/xyz/openbmc_project/user", 207*3d958bbcSAppaRao Puli "org.freedesktop.DBus.Properties", "Set", 208*3d958bbcSAppaRao Puli "xyz.openbmc_project.User.AccountPolicy", 209*3d958bbcSAppaRao Puli "MaxLoginAttemptBeforeLockout", 210*3d958bbcSAppaRao Puli sdbusplus::message::variant<uint16_t>(*lockoutThreshold)); 211*3d958bbcSAppaRao Puli } 21288d16c9aSLewanczyk, Dawid } 21388d16c9aSLewanczyk, Dawid }; 214b9b2e0b2SEd Tanous class AccountsCollection : public Node 215b9b2e0b2SEd Tanous { 216b9b2e0b2SEd Tanous public: 217b9b2e0b2SEd Tanous AccountsCollection(CrowApp& app) : 218b9b2e0b2SEd Tanous Node(app, "/redfish/v1/AccountService/Accounts/") 219b9b2e0b2SEd Tanous { 220b9b2e0b2SEd Tanous entityPrivileges = { 221b9b2e0b2SEd Tanous {boost::beast::http::verb::get, 222b9b2e0b2SEd Tanous {{"ConfigureUsers"}, {"ConfigureManager"}}}, 223b9b2e0b2SEd Tanous {boost::beast::http::verb::head, {{"Login"}}}, 224b9b2e0b2SEd Tanous {boost::beast::http::verb::patch, {{"ConfigureUsers"}}}, 225b9b2e0b2SEd Tanous {boost::beast::http::verb::put, {{"ConfigureUsers"}}}, 226b9b2e0b2SEd Tanous {boost::beast::http::verb::delete_, {{"ConfigureUsers"}}}, 227b9b2e0b2SEd Tanous {boost::beast::http::verb::post, {{"ConfigureUsers"}}}}; 228b9b2e0b2SEd Tanous } 229b9b2e0b2SEd Tanous 230b9b2e0b2SEd Tanous private: 231b9b2e0b2SEd Tanous void doGet(crow::Response& res, const crow::Request& req, 232b9b2e0b2SEd Tanous const std::vector<std::string>& params) override 233b9b2e0b2SEd Tanous { 234b9b2e0b2SEd Tanous auto asyncResp = std::make_shared<AsyncResp>(res); 2350f74e643SEd Tanous res.jsonValue = {{"@odata.context", 2360f74e643SEd Tanous "/redfish/v1/" 2370f74e643SEd Tanous "$metadata#ManagerAccountCollection." 2380f74e643SEd Tanous "ManagerAccountCollection"}, 2390f74e643SEd Tanous {"@odata.id", "/redfish/v1/AccountService/Accounts"}, 2400f74e643SEd Tanous {"@odata.type", "#ManagerAccountCollection." 2410f74e643SEd Tanous "ManagerAccountCollection"}, 2420f74e643SEd Tanous {"Name", "Accounts Collection"}, 2430f74e643SEd Tanous {"Description", "BMC User Accounts"}}; 2440f74e643SEd Tanous 245b9b2e0b2SEd Tanous crow::connections::systemBus->async_method_call( 246b9b2e0b2SEd Tanous [asyncResp](const boost::system::error_code ec, 247b9b2e0b2SEd Tanous const ManagedObjectType& users) { 248b9b2e0b2SEd Tanous if (ec) 249b9b2e0b2SEd Tanous { 250f12894f8SJason M. Bills messages::internalError(asyncResp->res); 251b9b2e0b2SEd Tanous return; 252b9b2e0b2SEd Tanous } 253b9b2e0b2SEd Tanous 254b9b2e0b2SEd Tanous nlohmann::json& memberArray = 255b9b2e0b2SEd Tanous asyncResp->res.jsonValue["Members"]; 256b9b2e0b2SEd Tanous memberArray = nlohmann::json::array(); 257b9b2e0b2SEd Tanous 258b9b2e0b2SEd Tanous asyncResp->res.jsonValue["Members@odata.count"] = users.size(); 259b9b2e0b2SEd Tanous for (auto& user : users) 260b9b2e0b2SEd Tanous { 261b9b2e0b2SEd Tanous const std::string& path = 262b9b2e0b2SEd Tanous static_cast<const std::string&>(user.first); 263b9b2e0b2SEd Tanous std::size_t lastIndex = path.rfind("/"); 264b9b2e0b2SEd Tanous if (lastIndex == std::string::npos) 265b9b2e0b2SEd Tanous { 266b9b2e0b2SEd Tanous lastIndex = 0; 267b9b2e0b2SEd Tanous } 268b9b2e0b2SEd Tanous else 269b9b2e0b2SEd Tanous { 270b9b2e0b2SEd Tanous lastIndex += 1; 271b9b2e0b2SEd Tanous } 272b9b2e0b2SEd Tanous memberArray.push_back( 273b9b2e0b2SEd Tanous {{"@odata.id", "/redfish/v1/AccountService/Accounts/" + 274b9b2e0b2SEd Tanous path.substr(lastIndex)}}); 275b9b2e0b2SEd Tanous } 276b9b2e0b2SEd Tanous }, 277b9b2e0b2SEd Tanous "xyz.openbmc_project.User.Manager", "/xyz/openbmc_project/user", 278b9b2e0b2SEd Tanous "org.freedesktop.DBus.ObjectManager", "GetManagedObjects"); 279b9b2e0b2SEd Tanous } 28004ae99ecSEd Tanous void doPost(crow::Response& res, const crow::Request& req, 28104ae99ecSEd Tanous const std::vector<std::string>& params) override 28204ae99ecSEd Tanous { 28304ae99ecSEd Tanous auto asyncResp = std::make_shared<AsyncResp>(res); 28404ae99ecSEd Tanous 2859712f8acSEd Tanous std::string username; 2869712f8acSEd Tanous std::string password; 287a24526dcSEd Tanous std::optional<std::string> roleId("User"); 288a24526dcSEd Tanous std::optional<bool> enabled = true; 2899712f8acSEd Tanous if (!json_util::readJson(req, res, "UserName", username, "Password", 2909712f8acSEd Tanous password, "RoleId", roleId, "Enabled", 2919712f8acSEd Tanous enabled)) 29204ae99ecSEd Tanous { 29304ae99ecSEd Tanous return; 29404ae99ecSEd Tanous } 29504ae99ecSEd Tanous 29684e12cb7SAppaRao Puli std::string priv = getRoleIdFromPrivilege(*roleId); 29784e12cb7SAppaRao Puli if (priv.empty()) 29804ae99ecSEd Tanous { 299f12894f8SJason M. Bills messages::propertyValueNotInList(asyncResp->res, *roleId, "RoleId"); 30004ae99ecSEd Tanous return; 30104ae99ecSEd Tanous } 3029712f8acSEd Tanous roleId = priv; 30304ae99ecSEd Tanous 30404ae99ecSEd Tanous crow::connections::systemBus->async_method_call( 3059712f8acSEd Tanous [asyncResp, username, password{std::move(password)}]( 30604ae99ecSEd Tanous const boost::system::error_code ec) { 30704ae99ecSEd Tanous if (ec) 30804ae99ecSEd Tanous { 30904ae99ecSEd Tanous messages::resourceAlreadyExists( 310f12894f8SJason M. Bills asyncResp->res, "#ManagerAccount.v1_0_3.ManagerAccount", 311f12894f8SJason M. Bills "UserName", username); 31204ae99ecSEd Tanous return; 31304ae99ecSEd Tanous } 31404ae99ecSEd Tanous 31504ae99ecSEd Tanous if (!pamUpdatePassword(username, password)) 31604ae99ecSEd Tanous { 31704ae99ecSEd Tanous // At this point we have a user that's been created, but the 31804ae99ecSEd Tanous // password set failed. Something is wrong, so delete the 31904ae99ecSEd Tanous // user that we've already created 32004ae99ecSEd Tanous crow::connections::systemBus->async_method_call( 32104ae99ecSEd Tanous [asyncResp](const boost::system::error_code ec) { 32204ae99ecSEd Tanous if (ec) 32304ae99ecSEd Tanous { 324f12894f8SJason M. Bills messages::internalError(asyncResp->res); 32504ae99ecSEd Tanous return; 32604ae99ecSEd Tanous } 32704ae99ecSEd Tanous 328f12894f8SJason M. Bills messages::invalidObject(asyncResp->res, "Password"); 32904ae99ecSEd Tanous }, 33004ae99ecSEd Tanous "xyz.openbmc_project.User.Manager", 33104ae99ecSEd Tanous "/xyz/openbmc_project/user/" + username, 33204ae99ecSEd Tanous "xyz.openbmc_project.Object.Delete", "Delete"); 33304ae99ecSEd Tanous 33404ae99ecSEd Tanous BMCWEB_LOG_ERROR << "pamUpdatePassword Failed"; 33504ae99ecSEd Tanous return; 33604ae99ecSEd Tanous } 33704ae99ecSEd Tanous 338f12894f8SJason M. Bills messages::created(asyncResp->res); 33904ae99ecSEd Tanous asyncResp->res.addHeader( 34004ae99ecSEd Tanous "Location", 34104ae99ecSEd Tanous "/redfish/v1/AccountService/Accounts/" + username); 34204ae99ecSEd Tanous }, 34304ae99ecSEd Tanous "xyz.openbmc_project.User.Manager", "/xyz/openbmc_project/user", 3449712f8acSEd Tanous "xyz.openbmc_project.User.Manager", "CreateUser", username, 34504ae99ecSEd Tanous std::array<const char*, 4>{"ipmi", "redfish", "ssh", "web"}, 3469712f8acSEd Tanous *roleId, *enabled); 34704ae99ecSEd Tanous } 348b9b2e0b2SEd Tanous }; 349b9b2e0b2SEd Tanous 350a840879dSEd Tanous template <typename Callback> 351a840879dSEd Tanous inline void checkDbusPathExists(const std::string& path, Callback&& callback) 352a840879dSEd Tanous { 353a840879dSEd Tanous using GetObjectType = 354a840879dSEd Tanous std::vector<std::pair<std::string, std::vector<std::string>>>; 355a840879dSEd Tanous 356a840879dSEd Tanous crow::connections::systemBus->async_method_call( 357a840879dSEd Tanous [callback{std::move(callback)}](const boost::system::error_code ec, 358a840879dSEd Tanous const GetObjectType& object_names) { 35984e12cb7SAppaRao Puli callback(!ec && object_names.size() != 0); 360a840879dSEd Tanous }, 361a840879dSEd Tanous "xyz.openbmc_project.ObjectMapper", 362a840879dSEd Tanous "/xyz/openbmc_project/object_mapper", 363a840879dSEd Tanous "xyz.openbmc_project.ObjectMapper", "GetObject", path, 364a840879dSEd Tanous std::array<std::string, 0>()); 365a840879dSEd Tanous } 366a840879dSEd Tanous 367b9b2e0b2SEd Tanous class ManagerAccount : public Node 368b9b2e0b2SEd Tanous { 369b9b2e0b2SEd Tanous public: 370b9b2e0b2SEd Tanous ManagerAccount(CrowApp& app) : 371b9b2e0b2SEd Tanous Node(app, "/redfish/v1/AccountService/Accounts/<str>/", std::string()) 372b9b2e0b2SEd Tanous { 373b9b2e0b2SEd Tanous entityPrivileges = { 374b9b2e0b2SEd Tanous {boost::beast::http::verb::get, 375b9b2e0b2SEd Tanous {{"ConfigureUsers"}, {"ConfigureManager"}, {"ConfigureSelf"}}}, 376b9b2e0b2SEd Tanous {boost::beast::http::verb::head, {{"Login"}}}, 377b9b2e0b2SEd Tanous {boost::beast::http::verb::patch, {{"ConfigureUsers"}}}, 378b9b2e0b2SEd Tanous {boost::beast::http::verb::put, {{"ConfigureUsers"}}}, 379b9b2e0b2SEd Tanous {boost::beast::http::verb::delete_, {{"ConfigureUsers"}}}, 380b9b2e0b2SEd Tanous {boost::beast::http::verb::post, {{"ConfigureUsers"}}}}; 381b9b2e0b2SEd Tanous } 382b9b2e0b2SEd Tanous 383b9b2e0b2SEd Tanous private: 384b9b2e0b2SEd Tanous void doGet(crow::Response& res, const crow::Request& req, 385b9b2e0b2SEd Tanous const std::vector<std::string>& params) override 386b9b2e0b2SEd Tanous { 3870f74e643SEd Tanous res.jsonValue = { 3880f74e643SEd Tanous {"@odata.context", 3890f74e643SEd Tanous "/redfish/v1/$metadata#ManagerAccount.ManagerAccount"}, 3900f74e643SEd Tanous {"@odata.type", "#ManagerAccount.v1_0_3.ManagerAccount"}, 3910f74e643SEd Tanous {"Name", "User Account"}, 3920f74e643SEd Tanous {"Description", "User Account"}, 3930f74e643SEd Tanous {"Password", nullptr}, 39484e12cb7SAppaRao Puli {"RoleId", "Administrator"}}; 3950f74e643SEd Tanous 396b9b2e0b2SEd Tanous auto asyncResp = std::make_shared<AsyncResp>(res); 397b9b2e0b2SEd Tanous 398b9b2e0b2SEd Tanous if (params.size() != 1) 399b9b2e0b2SEd Tanous { 400f12894f8SJason M. Bills messages::internalError(asyncResp->res); 401b9b2e0b2SEd Tanous return; 402b9b2e0b2SEd Tanous } 403b9b2e0b2SEd Tanous 404b9b2e0b2SEd Tanous crow::connections::systemBus->async_method_call( 405b9b2e0b2SEd Tanous [asyncResp, accountName{std::string(params[0])}]( 406b9b2e0b2SEd Tanous const boost::system::error_code ec, 407b9b2e0b2SEd Tanous const ManagedObjectType& users) { 408b9b2e0b2SEd Tanous if (ec) 409b9b2e0b2SEd Tanous { 410f12894f8SJason M. Bills messages::internalError(asyncResp->res); 411b9b2e0b2SEd Tanous return; 412b9b2e0b2SEd Tanous } 41384e12cb7SAppaRao Puli auto userIt = users.begin(); 414b9b2e0b2SEd Tanous 41584e12cb7SAppaRao Puli for (; userIt != users.end(); userIt++) 416b9b2e0b2SEd Tanous { 41784e12cb7SAppaRao Puli if (boost::ends_with(userIt->first.str, "/" + accountName)) 418b9b2e0b2SEd Tanous { 41984e12cb7SAppaRao Puli break; 420b9b2e0b2SEd Tanous } 421b9b2e0b2SEd Tanous } 42284e12cb7SAppaRao Puli if (userIt == users.end()) 423b9b2e0b2SEd Tanous { 42484e12cb7SAppaRao Puli messages::resourceNotFound(asyncResp->res, "ManagerAccount", 42584e12cb7SAppaRao Puli accountName); 42684e12cb7SAppaRao Puli return; 42784e12cb7SAppaRao Puli } 42884e12cb7SAppaRao Puli for (const auto& interface : userIt->second) 42965b0dc32SEd Tanous { 43065b0dc32SEd Tanous if (interface.first == 43165b0dc32SEd Tanous "xyz.openbmc_project.User.Attributes") 43265b0dc32SEd Tanous { 43365b0dc32SEd Tanous for (const auto& property : interface.second) 43465b0dc32SEd Tanous { 43565b0dc32SEd Tanous if (property.first == "UserEnabled") 43665b0dc32SEd Tanous { 43765b0dc32SEd Tanous const bool* userEnabled = 43884e12cb7SAppaRao Puli sdbusplus::message::variant_ns::get_if< 43984e12cb7SAppaRao Puli bool>(&property.second); 44065b0dc32SEd Tanous if (userEnabled == nullptr) 44165b0dc32SEd Tanous { 44265b0dc32SEd Tanous BMCWEB_LOG_ERROR 44365b0dc32SEd Tanous << "UserEnabled wasn't a bool"; 44484e12cb7SAppaRao Puli messages::internalError(asyncResp->res); 44584e12cb7SAppaRao Puli return; 44665b0dc32SEd Tanous } 44765b0dc32SEd Tanous asyncResp->res.jsonValue["Enabled"] = 44865b0dc32SEd Tanous *userEnabled; 44965b0dc32SEd Tanous } 45065b0dc32SEd Tanous else if (property.first == 45165b0dc32SEd Tanous "UserLockedForFailedAttempt") 45265b0dc32SEd Tanous { 45365b0dc32SEd Tanous const bool* userLocked = 45484e12cb7SAppaRao Puli sdbusplus::message::variant_ns::get_if< 45584e12cb7SAppaRao Puli bool>(&property.second); 45665b0dc32SEd Tanous if (userLocked == nullptr) 45765b0dc32SEd Tanous { 45884e12cb7SAppaRao Puli BMCWEB_LOG_ERROR << "UserLockedForF" 45984e12cb7SAppaRao Puli "ailedAttempt " 46084e12cb7SAppaRao Puli "wasn't a bool"; 46184e12cb7SAppaRao Puli messages::internalError(asyncResp->res); 46284e12cb7SAppaRao Puli return; 46365b0dc32SEd Tanous } 46465b0dc32SEd Tanous asyncResp->res.jsonValue["Locked"] = 46565b0dc32SEd Tanous *userLocked; 46665b0dc32SEd Tanous } 46784e12cb7SAppaRao Puli else if (property.first == "UserPrivilege") 46884e12cb7SAppaRao Puli { 46984e12cb7SAppaRao Puli const std::string* userRolePtr = 47084e12cb7SAppaRao Puli sdbusplus::message::variant_ns::get_if< 47184e12cb7SAppaRao Puli std::string>(&property.second); 47284e12cb7SAppaRao Puli if (userRolePtr == nullptr) 47384e12cb7SAppaRao Puli { 47484e12cb7SAppaRao Puli BMCWEB_LOG_ERROR 47584e12cb7SAppaRao Puli << "UserPrivilege wasn't a " 47684e12cb7SAppaRao Puli "string"; 47784e12cb7SAppaRao Puli messages::internalError(asyncResp->res); 47884e12cb7SAppaRao Puli return; 47984e12cb7SAppaRao Puli } 48084e12cb7SAppaRao Puli std::string priv = 48184e12cb7SAppaRao Puli getPrivilegeFromRoleId(*userRolePtr); 48284e12cb7SAppaRao Puli if (priv.empty()) 48384e12cb7SAppaRao Puli { 48484e12cb7SAppaRao Puli BMCWEB_LOG_ERROR << "Invalid user role"; 48584e12cb7SAppaRao Puli messages::internalError(asyncResp->res); 48684e12cb7SAppaRao Puli return; 48784e12cb7SAppaRao Puli } 48884e12cb7SAppaRao Puli asyncResp->res.jsonValue["RoleId"] = priv; 48984e12cb7SAppaRao Puli 49084e12cb7SAppaRao Puli asyncResp->res.jsonValue["Links"]["Role"] = { 49184e12cb7SAppaRao Puli {"@odata.id", "/redfish/v1/AccountService/" 49284e12cb7SAppaRao Puli "Roles/" + 49384e12cb7SAppaRao Puli priv}}; 49484e12cb7SAppaRao Puli } 49565b0dc32SEd Tanous } 49665b0dc32SEd Tanous } 49765b0dc32SEd Tanous } 49865b0dc32SEd Tanous 499b9b2e0b2SEd Tanous asyncResp->res.jsonValue["@odata.id"] = 50084e12cb7SAppaRao Puli "/redfish/v1/AccountService/Accounts/" + accountName; 501b9b2e0b2SEd Tanous asyncResp->res.jsonValue["Id"] = accountName; 502b9b2e0b2SEd Tanous asyncResp->res.jsonValue["UserName"] = accountName; 503b9b2e0b2SEd Tanous }, 504b9b2e0b2SEd Tanous "xyz.openbmc_project.User.Manager", "/xyz/openbmc_project/user", 505b9b2e0b2SEd Tanous "org.freedesktop.DBus.ObjectManager", "GetManagedObjects"); 506b9b2e0b2SEd Tanous } 507a840879dSEd Tanous 508a840879dSEd Tanous void doPatch(crow::Response& res, const crow::Request& req, 509a840879dSEd Tanous const std::vector<std::string>& params) override 510a840879dSEd Tanous { 511a840879dSEd Tanous auto asyncResp = std::make_shared<AsyncResp>(res); 512a840879dSEd Tanous if (params.size() != 1) 513a840879dSEd Tanous { 514f12894f8SJason M. Bills messages::internalError(asyncResp->res); 515a840879dSEd Tanous return; 516a840879dSEd Tanous } 517a840879dSEd Tanous 518a24526dcSEd Tanous std::optional<std::string> newUserName; 519a24526dcSEd Tanous std::optional<std::string> password; 520a24526dcSEd Tanous std::optional<bool> enabled; 521a24526dcSEd Tanous std::optional<std::string> roleId; 52284e12cb7SAppaRao Puli if (!json_util::readJson(req, res, "UserName", newUserName, "Password", 52384e12cb7SAppaRao Puli password, "RoleId", roleId, "Enabled", 5249712f8acSEd Tanous enabled)) 525a840879dSEd Tanous { 526a840879dSEd Tanous return; 527a840879dSEd Tanous } 528a840879dSEd Tanous 52984e12cb7SAppaRao Puli const std::string& username = params[0]; 53084e12cb7SAppaRao Puli 53184e12cb7SAppaRao Puli if (!newUserName) 532a840879dSEd Tanous { 53384e12cb7SAppaRao Puli // If the username isn't being updated, we can update the properties 53484e12cb7SAppaRao Puli // directly 53584e12cb7SAppaRao Puli updateUserProperties(asyncResp, username, password, enabled, 53684e12cb7SAppaRao Puli roleId); 53784e12cb7SAppaRao Puli return; 53884e12cb7SAppaRao Puli } 53984e12cb7SAppaRao Puli else 54084e12cb7SAppaRao Puli { 54184e12cb7SAppaRao Puli crow::connections::systemBus->async_method_call( 54284e12cb7SAppaRao Puli [this, asyncResp, username, password(std::move(password)), 54384e12cb7SAppaRao Puli roleId(std::move(roleId)), enabled(std::move(enabled)), 54484e12cb7SAppaRao Puli newUser{std::string(*newUserName)}]( 54584e12cb7SAppaRao Puli const boost::system::error_code ec) { 54684e12cb7SAppaRao Puli if (ec) 54784e12cb7SAppaRao Puli { 54884e12cb7SAppaRao Puli BMCWEB_LOG_ERROR << "D-Bus responses error: " << ec; 549a840879dSEd Tanous messages::resourceNotFound( 55084e12cb7SAppaRao Puli asyncResp->res, 55184e12cb7SAppaRao Puli "#ManagerAccount.v1_0_3.ManagerAccount", username); 552a840879dSEd Tanous return; 553a840879dSEd Tanous } 554a840879dSEd Tanous 55584e12cb7SAppaRao Puli updateUserProperties(asyncResp, newUser, password, enabled, 55684e12cb7SAppaRao Puli roleId); 55784e12cb7SAppaRao Puli }, 55884e12cb7SAppaRao Puli "xyz.openbmc_project.User.Manager", "/xyz/openbmc_project/user", 55984e12cb7SAppaRao Puli "xyz.openbmc_project.User.Manager", "RenameUser", username, 56084e12cb7SAppaRao Puli *newUserName); 56184e12cb7SAppaRao Puli } 56284e12cb7SAppaRao Puli } 56384e12cb7SAppaRao Puli 56484e12cb7SAppaRao Puli void updateUserProperties(std::shared_ptr<AsyncResp> asyncResp, 56584e12cb7SAppaRao Puli const std::string& username, 566a24526dcSEd Tanous std::optional<std::string> password, 567a24526dcSEd Tanous std::optional<bool> enabled, 568a24526dcSEd Tanous std::optional<std::string> roleId) 56984e12cb7SAppaRao Puli { 5709712f8acSEd Tanous if (password) 571a840879dSEd Tanous { 5729712f8acSEd Tanous if (!pamUpdatePassword(username, *password)) 573a840879dSEd Tanous { 574a840879dSEd Tanous BMCWEB_LOG_ERROR << "pamUpdatePassword Failed"; 575f12894f8SJason M. Bills messages::internalError(asyncResp->res); 576a840879dSEd Tanous return; 577a840879dSEd Tanous } 578a840879dSEd Tanous } 579a840879dSEd Tanous 5809712f8acSEd Tanous if (enabled) 581a840879dSEd Tanous { 582a840879dSEd Tanous crow::connections::systemBus->async_method_call( 583a840879dSEd Tanous [asyncResp](const boost::system::error_code ec) { 584a840879dSEd Tanous if (ec) 585a840879dSEd Tanous { 58684e12cb7SAppaRao Puli BMCWEB_LOG_ERROR << "D-Bus responses error: " << ec; 587f12894f8SJason M. Bills messages::internalError(asyncResp->res); 588a840879dSEd Tanous return; 589a840879dSEd Tanous } 59084e12cb7SAppaRao Puli messages::success(asyncResp->res); 59184e12cb7SAppaRao Puli return; 59284e12cb7SAppaRao Puli }, 59384e12cb7SAppaRao Puli "xyz.openbmc_project.User.Manager", 59484e12cb7SAppaRao Puli "/xyz/openbmc_project/user/" + username, 59584e12cb7SAppaRao Puli "org.freedesktop.DBus.Properties", "Set", 59684e12cb7SAppaRao Puli "xyz.openbmc_project.User.Attributes", "UserEnabled", 59784e12cb7SAppaRao Puli sdbusplus::message::variant<bool>{*enabled}); 59884e12cb7SAppaRao Puli } 59984e12cb7SAppaRao Puli 60084e12cb7SAppaRao Puli if (roleId) 60184e12cb7SAppaRao Puli { 60284e12cb7SAppaRao Puli std::string priv = getRoleIdFromPrivilege(*roleId); 60384e12cb7SAppaRao Puli if (priv.empty()) 60484e12cb7SAppaRao Puli { 60584e12cb7SAppaRao Puli messages::propertyValueNotInList(asyncResp->res, *roleId, 60684e12cb7SAppaRao Puli "RoleId"); 60784e12cb7SAppaRao Puli return; 60884e12cb7SAppaRao Puli } 60984e12cb7SAppaRao Puli 61084e12cb7SAppaRao Puli crow::connections::systemBus->async_method_call( 61184e12cb7SAppaRao Puli [asyncResp](const boost::system::error_code ec) { 61284e12cb7SAppaRao Puli if (ec) 61384e12cb7SAppaRao Puli { 61484e12cb7SAppaRao Puli BMCWEB_LOG_ERROR << "D-Bus responses error: " << ec; 61584e12cb7SAppaRao Puli messages::internalError(asyncResp->res); 61684e12cb7SAppaRao Puli return; 61784e12cb7SAppaRao Puli } 618f12894f8SJason M. Bills messages::success(asyncResp->res); 619a840879dSEd Tanous }, 620a840879dSEd Tanous "xyz.openbmc_project.User.Manager", 62184e12cb7SAppaRao Puli "/xyz/openbmc_project/user/" + username, 622a840879dSEd Tanous "org.freedesktop.DBus.Properties", "Set", 62384e12cb7SAppaRao Puli "xyz.openbmc_project.User.Attributes", "UserPrivilege", 62484e12cb7SAppaRao Puli sdbusplus::message::variant<std::string>{priv}); 625a840879dSEd Tanous } 626a840879dSEd Tanous } 62706e086d9SEd Tanous 62806e086d9SEd Tanous void doDelete(crow::Response& res, const crow::Request& req, 62906e086d9SEd Tanous const std::vector<std::string>& params) override 63006e086d9SEd Tanous { 63106e086d9SEd Tanous auto asyncResp = std::make_shared<AsyncResp>(res); 63206e086d9SEd Tanous 63306e086d9SEd Tanous if (params.size() != 1) 63406e086d9SEd Tanous { 635f12894f8SJason M. Bills messages::internalError(asyncResp->res); 63606e086d9SEd Tanous return; 63706e086d9SEd Tanous } 63806e086d9SEd Tanous 63906e086d9SEd Tanous const std::string userPath = "/xyz/openbmc_project/user/" + params[0]; 64006e086d9SEd Tanous 64106e086d9SEd Tanous crow::connections::systemBus->async_method_call( 64206e086d9SEd Tanous [asyncResp, username{std::move(params[0])}]( 64306e086d9SEd Tanous const boost::system::error_code ec) { 64406e086d9SEd Tanous if (ec) 64506e086d9SEd Tanous { 64606e086d9SEd Tanous messages::resourceNotFound( 647f12894f8SJason M. Bills asyncResp->res, "#ManagerAccount.v1_0_3.ManagerAccount", 648f12894f8SJason M. Bills username); 64906e086d9SEd Tanous return; 65006e086d9SEd Tanous } 65106e086d9SEd Tanous 652f12894f8SJason M. Bills messages::accountRemoved(asyncResp->res); 65306e086d9SEd Tanous }, 65406e086d9SEd Tanous "xyz.openbmc_project.User.Manager", userPath, 65506e086d9SEd Tanous "xyz.openbmc_project.Object.Delete", "Delete"); 65606e086d9SEd Tanous } 65784e12cb7SAppaRao Puli }; 65888d16c9aSLewanczyk, Dawid 65988d16c9aSLewanczyk, Dawid } // namespace redfish 660