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 1924c8542dSRatan Gupta #include <dbus_utility.hpp> 2065b0dc32SEd Tanous #include <error_messages.hpp> 21b9b2e0b2SEd Tanous #include <openbmc_dbus_rest.hpp> 22a840879dSEd Tanous #include <utils/json_utils.hpp> 23abf2add6SEd Tanous #include <variant> 24b9b2e0b2SEd Tanous 251abe55efSEd Tanous namespace redfish 261abe55efSEd Tanous { 2788d16c9aSLewanczyk, Dawid 28b9b2e0b2SEd Tanous using ManagedObjectType = std::vector<std::pair< 29b9b2e0b2SEd Tanous sdbusplus::message::object_path, 30b9b2e0b2SEd Tanous boost::container::flat_map< 31abf2add6SEd Tanous std::string, boost::container::flat_map< 32abf2add6SEd Tanous std::string, std::variant<bool, std::string>>>>>; 3384e12cb7SAppaRao Puli 3484e12cb7SAppaRao Puli inline std::string getPrivilegeFromRoleId(boost::beast::string_view role) 3584e12cb7SAppaRao Puli { 3684e12cb7SAppaRao Puli if (role == "priv-admin") 3784e12cb7SAppaRao Puli { 3884e12cb7SAppaRao Puli return "Administrator"; 3984e12cb7SAppaRao Puli } 4084e12cb7SAppaRao Puli else if (role == "priv-callback") 4184e12cb7SAppaRao Puli { 4284e12cb7SAppaRao Puli return "Callback"; 4384e12cb7SAppaRao Puli } 4484e12cb7SAppaRao Puli else if (role == "priv-user") 4584e12cb7SAppaRao Puli { 4684e12cb7SAppaRao Puli return "User"; 4784e12cb7SAppaRao Puli } 4884e12cb7SAppaRao Puli else if (role == "priv-operator") 4984e12cb7SAppaRao Puli { 5084e12cb7SAppaRao Puli return "Operator"; 5184e12cb7SAppaRao Puli } 5284e12cb7SAppaRao Puli return ""; 5384e12cb7SAppaRao Puli } 5484e12cb7SAppaRao Puli inline std::string getRoleIdFromPrivilege(boost::beast::string_view role) 5584e12cb7SAppaRao Puli { 5684e12cb7SAppaRao Puli if (role == "Administrator") 5784e12cb7SAppaRao Puli { 5884e12cb7SAppaRao Puli return "priv-admin"; 5984e12cb7SAppaRao Puli } 6084e12cb7SAppaRao Puli else if (role == "Callback") 6184e12cb7SAppaRao Puli { 6284e12cb7SAppaRao Puli return "priv-callback"; 6384e12cb7SAppaRao Puli } 6484e12cb7SAppaRao Puli else if (role == "User") 6584e12cb7SAppaRao Puli { 6684e12cb7SAppaRao Puli return "priv-user"; 6784e12cb7SAppaRao Puli } 6884e12cb7SAppaRao Puli else if (role == "Operator") 6984e12cb7SAppaRao Puli { 7084e12cb7SAppaRao Puli return "priv-operator"; 7184e12cb7SAppaRao Puli } 7284e12cb7SAppaRao Puli return ""; 7384e12cb7SAppaRao Puli } 74b9b2e0b2SEd Tanous 751abe55efSEd Tanous class AccountService : public Node 761abe55efSEd Tanous { 7788d16c9aSLewanczyk, Dawid public: 781abe55efSEd Tanous AccountService(CrowApp& app) : Node(app, "/redfish/v1/AccountService/") 791abe55efSEd Tanous { 803ebd75f7SEd Tanous entityPrivileges = { 814b1b8683SBorawski.Lukasz {boost::beast::http::verb::get, 824b1b8683SBorawski.Lukasz {{"ConfigureUsers"}, {"ConfigureManager"}}}, 83e0d918bcSEd Tanous {boost::beast::http::verb::head, {{"Login"}}}, 84e0d918bcSEd Tanous {boost::beast::http::verb::patch, {{"ConfigureUsers"}}}, 85e0d918bcSEd Tanous {boost::beast::http::verb::put, {{"ConfigureUsers"}}}, 86e0d918bcSEd Tanous {boost::beast::http::verb::delete_, {{"ConfigureUsers"}}}, 87e0d918bcSEd Tanous {boost::beast::http::verb::post, {{"ConfigureUsers"}}}}; 8888d16c9aSLewanczyk, Dawid } 8988d16c9aSLewanczyk, Dawid 9088d16c9aSLewanczyk, Dawid private: 9155c7b7a2SEd Tanous void doGet(crow::Response& res, const crow::Request& req, 921abe55efSEd Tanous const std::vector<std::string>& params) override 931abe55efSEd Tanous { 943d958bbcSAppaRao Puli auto asyncResp = std::make_shared<AsyncResp>(res); 953d958bbcSAppaRao Puli res.jsonValue = { 963d958bbcSAppaRao Puli {"@odata.context", "/redfish/v1/" 973d958bbcSAppaRao Puli "$metadata#AccountService.AccountService"}, 983d958bbcSAppaRao Puli {"@odata.id", "/redfish/v1/AccountService"}, 993d958bbcSAppaRao Puli {"@odata.type", "#AccountService." 1003d958bbcSAppaRao Puli "v1_1_0.AccountService"}, 1013d958bbcSAppaRao Puli {"Id", "AccountService"}, 1023d958bbcSAppaRao Puli {"Name", "Account Service"}, 1033d958bbcSAppaRao Puli {"Description", "Account Service"}, 1043d958bbcSAppaRao Puli {"ServiceEnabled", true}, 1053d958bbcSAppaRao Puli {"MaxPasswordLength", 31}, 1063d958bbcSAppaRao Puli {"Accounts", 1073d958bbcSAppaRao Puli {{"@odata.id", "/redfish/v1/AccountService/Accounts"}}}, 1083d958bbcSAppaRao Puli {"Roles", {{"@odata.id", "/redfish/v1/AccountService/Roles"}}}}; 1090f74e643SEd Tanous 1103d958bbcSAppaRao Puli crow::connections::systemBus->async_method_call( 1113d958bbcSAppaRao Puli [asyncResp]( 1123d958bbcSAppaRao Puli const boost::system::error_code ec, 1133d958bbcSAppaRao Puli const std::vector<std::pair< 114abf2add6SEd Tanous std::string, std::variant<uint32_t, uint16_t, uint8_t>>>& 1153d958bbcSAppaRao Puli propertiesList) { 1163d958bbcSAppaRao Puli if (ec) 1173d958bbcSAppaRao Puli { 1183d958bbcSAppaRao Puli messages::internalError(asyncResp->res); 1193d958bbcSAppaRao Puli return; 1203d958bbcSAppaRao Puli } 1213d958bbcSAppaRao Puli BMCWEB_LOG_DEBUG << "Got " << propertiesList.size() 1223d958bbcSAppaRao Puli << "properties for AccountService"; 1233d958bbcSAppaRao Puli for (const std::pair<std::string, 124abf2add6SEd Tanous std::variant<uint32_t, uint16_t, uint8_t>>& 1253d958bbcSAppaRao Puli property : propertiesList) 1263d958bbcSAppaRao Puli { 1273d958bbcSAppaRao Puli if (property.first == "MinPasswordLength") 1283d958bbcSAppaRao Puli { 1293d958bbcSAppaRao Puli const uint8_t* value = 130abf2add6SEd Tanous std::get_if<uint8_t>(&property.second); 1313d958bbcSAppaRao Puli if (value != nullptr) 1323d958bbcSAppaRao Puli { 1333d958bbcSAppaRao Puli asyncResp->res.jsonValue["MinPasswordLength"] = 1343d958bbcSAppaRao Puli *value; 1353d958bbcSAppaRao Puli } 1363d958bbcSAppaRao Puli } 1373d958bbcSAppaRao Puli if (property.first == "AccountUnlockTimeout") 1383d958bbcSAppaRao Puli { 1393d958bbcSAppaRao Puli const uint32_t* value = 140abf2add6SEd Tanous std::get_if<uint32_t>(&property.second); 1413d958bbcSAppaRao Puli if (value != nullptr) 1423d958bbcSAppaRao Puli { 1433d958bbcSAppaRao Puli asyncResp->res.jsonValue["AccountLockoutDuration"] = 1443d958bbcSAppaRao Puli *value; 1453d958bbcSAppaRao Puli } 1463d958bbcSAppaRao Puli } 1473d958bbcSAppaRao Puli if (property.first == "MaxLoginAttemptBeforeLockout") 1483d958bbcSAppaRao Puli { 1493d958bbcSAppaRao Puli const uint16_t* value = 150abf2add6SEd Tanous std::get_if<uint16_t>(&property.second); 1513d958bbcSAppaRao Puli if (value != nullptr) 1523d958bbcSAppaRao Puli { 1533d958bbcSAppaRao Puli asyncResp->res 1543d958bbcSAppaRao Puli .jsonValue["AccountLockoutThreshold"] = *value; 1553d958bbcSAppaRao Puli } 1563d958bbcSAppaRao Puli } 1573d958bbcSAppaRao Puli } 1583d958bbcSAppaRao Puli }, 1593d958bbcSAppaRao Puli "xyz.openbmc_project.User.Manager", "/xyz/openbmc_project/user", 1603d958bbcSAppaRao Puli "org.freedesktop.DBus.Properties", "GetAll", 1613d958bbcSAppaRao Puli "xyz.openbmc_project.User.AccountPolicy"); 1623d958bbcSAppaRao Puli } 1633d958bbcSAppaRao Puli void doPatch(crow::Response& res, const crow::Request& req, 1643d958bbcSAppaRao Puli const std::vector<std::string>& params) override 1653d958bbcSAppaRao Puli { 1663d958bbcSAppaRao Puli auto asyncResp = std::make_shared<AsyncResp>(res); 1673d958bbcSAppaRao Puli 1683d958bbcSAppaRao Puli std::optional<uint32_t> unlockTimeout; 1693d958bbcSAppaRao Puli std::optional<uint16_t> lockoutThreshold; 170*19fb6e71SRatan Gupta std::optional<uint16_t> minPasswordLength; 171*19fb6e71SRatan Gupta std::optional<uint16_t> maxPasswordLength; 172*19fb6e71SRatan Gupta 1733d958bbcSAppaRao Puli if (!json_util::readJson(req, res, "AccountLockoutDuration", 1743d958bbcSAppaRao Puli unlockTimeout, "AccountLockoutThreshold", 175*19fb6e71SRatan Gupta lockoutThreshold, "MaxPasswordLength", 176*19fb6e71SRatan Gupta maxPasswordLength, "MinPasswordLength", 177*19fb6e71SRatan Gupta minPasswordLength)) 1783d958bbcSAppaRao Puli { 1793d958bbcSAppaRao Puli return; 1803d958bbcSAppaRao Puli } 181*19fb6e71SRatan Gupta 182*19fb6e71SRatan Gupta if (minPasswordLength) 183*19fb6e71SRatan Gupta { 184*19fb6e71SRatan Gupta messages::propertyNotWritable(asyncResp->res, "MinPasswordLength"); 185*19fb6e71SRatan Gupta } 186*19fb6e71SRatan Gupta 187*19fb6e71SRatan Gupta if (maxPasswordLength) 188*19fb6e71SRatan Gupta { 189*19fb6e71SRatan Gupta messages::propertyNotWritable(asyncResp->res, "MaxPasswordLength"); 190*19fb6e71SRatan Gupta } 191*19fb6e71SRatan Gupta 1923d958bbcSAppaRao Puli if (unlockTimeout) 1933d958bbcSAppaRao Puli { 1943d958bbcSAppaRao Puli crow::connections::systemBus->async_method_call( 1953d958bbcSAppaRao Puli [asyncResp](const boost::system::error_code ec) { 1963d958bbcSAppaRao Puli if (ec) 1973d958bbcSAppaRao Puli { 1983d958bbcSAppaRao Puli messages::internalError(asyncResp->res); 1993d958bbcSAppaRao Puli return; 2003d958bbcSAppaRao Puli } 201add6133bSRatan Gupta messages::success(asyncResp->res); 2023d958bbcSAppaRao Puli }, 2033d958bbcSAppaRao Puli "xyz.openbmc_project.User.Manager", "/xyz/openbmc_project/user", 2043d958bbcSAppaRao Puli "org.freedesktop.DBus.Properties", "Set", 2053d958bbcSAppaRao Puli "xyz.openbmc_project.User.AccountPolicy", 206abf2add6SEd Tanous "AccountUnlockTimeout", std::variant<uint32_t>(*unlockTimeout)); 2073d958bbcSAppaRao Puli } 2083d958bbcSAppaRao Puli if (lockoutThreshold) 2093d958bbcSAppaRao Puli { 2103d958bbcSAppaRao Puli crow::connections::systemBus->async_method_call( 2113d958bbcSAppaRao Puli [asyncResp](const boost::system::error_code ec) { 2123d958bbcSAppaRao Puli if (ec) 2133d958bbcSAppaRao Puli { 2143d958bbcSAppaRao Puli messages::internalError(asyncResp->res); 2153d958bbcSAppaRao Puli return; 2163d958bbcSAppaRao Puli } 217add6133bSRatan Gupta messages::success(asyncResp->res); 2183d958bbcSAppaRao Puli }, 2193d958bbcSAppaRao Puli "xyz.openbmc_project.User.Manager", "/xyz/openbmc_project/user", 2203d958bbcSAppaRao Puli "org.freedesktop.DBus.Properties", "Set", 2213d958bbcSAppaRao Puli "xyz.openbmc_project.User.AccountPolicy", 2223d958bbcSAppaRao Puli "MaxLoginAttemptBeforeLockout", 223abf2add6SEd Tanous std::variant<uint16_t>(*lockoutThreshold)); 2243d958bbcSAppaRao Puli } 22588d16c9aSLewanczyk, Dawid } 22688d16c9aSLewanczyk, Dawid }; 227b9b2e0b2SEd Tanous class AccountsCollection : public Node 228b9b2e0b2SEd Tanous { 229b9b2e0b2SEd Tanous public: 230b9b2e0b2SEd Tanous AccountsCollection(CrowApp& app) : 231b9b2e0b2SEd Tanous Node(app, "/redfish/v1/AccountService/Accounts/") 232b9b2e0b2SEd Tanous { 233b9b2e0b2SEd Tanous entityPrivileges = { 234b9b2e0b2SEd Tanous {boost::beast::http::verb::get, 235b9b2e0b2SEd Tanous {{"ConfigureUsers"}, {"ConfigureManager"}}}, 236b9b2e0b2SEd Tanous {boost::beast::http::verb::head, {{"Login"}}}, 237b9b2e0b2SEd Tanous {boost::beast::http::verb::patch, {{"ConfigureUsers"}}}, 238b9b2e0b2SEd Tanous {boost::beast::http::verb::put, {{"ConfigureUsers"}}}, 239b9b2e0b2SEd Tanous {boost::beast::http::verb::delete_, {{"ConfigureUsers"}}}, 240b9b2e0b2SEd Tanous {boost::beast::http::verb::post, {{"ConfigureUsers"}}}}; 241b9b2e0b2SEd Tanous } 242b9b2e0b2SEd Tanous 243b9b2e0b2SEd Tanous private: 244b9b2e0b2SEd Tanous void doGet(crow::Response& res, const crow::Request& req, 245b9b2e0b2SEd Tanous const std::vector<std::string>& params) override 246b9b2e0b2SEd Tanous { 247b9b2e0b2SEd Tanous auto asyncResp = std::make_shared<AsyncResp>(res); 2480f74e643SEd Tanous res.jsonValue = {{"@odata.context", 2490f74e643SEd Tanous "/redfish/v1/" 2500f74e643SEd Tanous "$metadata#ManagerAccountCollection." 2510f74e643SEd Tanous "ManagerAccountCollection"}, 2520f74e643SEd Tanous {"@odata.id", "/redfish/v1/AccountService/Accounts"}, 2530f74e643SEd Tanous {"@odata.type", "#ManagerAccountCollection." 2540f74e643SEd Tanous "ManagerAccountCollection"}, 2550f74e643SEd Tanous {"Name", "Accounts Collection"}, 2560f74e643SEd Tanous {"Description", "BMC User Accounts"}}; 2570f74e643SEd Tanous 258b9b2e0b2SEd Tanous crow::connections::systemBus->async_method_call( 259b9b2e0b2SEd Tanous [asyncResp](const boost::system::error_code ec, 260b9b2e0b2SEd Tanous const ManagedObjectType& users) { 261b9b2e0b2SEd Tanous if (ec) 262b9b2e0b2SEd Tanous { 263f12894f8SJason M. Bills messages::internalError(asyncResp->res); 264b9b2e0b2SEd Tanous return; 265b9b2e0b2SEd Tanous } 266b9b2e0b2SEd Tanous 267b9b2e0b2SEd Tanous nlohmann::json& memberArray = 268b9b2e0b2SEd Tanous asyncResp->res.jsonValue["Members"]; 269b9b2e0b2SEd Tanous memberArray = nlohmann::json::array(); 270b9b2e0b2SEd Tanous 271b9b2e0b2SEd Tanous asyncResp->res.jsonValue["Members@odata.count"] = users.size(); 272b9b2e0b2SEd Tanous for (auto& user : users) 273b9b2e0b2SEd Tanous { 274b9b2e0b2SEd Tanous const std::string& path = 275b9b2e0b2SEd Tanous static_cast<const std::string&>(user.first); 276b9b2e0b2SEd Tanous std::size_t lastIndex = path.rfind("/"); 277b9b2e0b2SEd Tanous if (lastIndex == std::string::npos) 278b9b2e0b2SEd Tanous { 279b9b2e0b2SEd Tanous lastIndex = 0; 280b9b2e0b2SEd Tanous } 281b9b2e0b2SEd Tanous else 282b9b2e0b2SEd Tanous { 283b9b2e0b2SEd Tanous lastIndex += 1; 284b9b2e0b2SEd Tanous } 285b9b2e0b2SEd Tanous memberArray.push_back( 286b9b2e0b2SEd Tanous {{"@odata.id", "/redfish/v1/AccountService/Accounts/" + 287b9b2e0b2SEd Tanous path.substr(lastIndex)}}); 288b9b2e0b2SEd Tanous } 289b9b2e0b2SEd Tanous }, 290b9b2e0b2SEd Tanous "xyz.openbmc_project.User.Manager", "/xyz/openbmc_project/user", 291b9b2e0b2SEd Tanous "org.freedesktop.DBus.ObjectManager", "GetManagedObjects"); 292b9b2e0b2SEd Tanous } 29304ae99ecSEd Tanous void doPost(crow::Response& res, const crow::Request& req, 29404ae99ecSEd Tanous const std::vector<std::string>& params) override 29504ae99ecSEd Tanous { 29604ae99ecSEd Tanous auto asyncResp = std::make_shared<AsyncResp>(res); 29704ae99ecSEd Tanous 2989712f8acSEd Tanous std::string username; 2999712f8acSEd Tanous std::string password; 300a24526dcSEd Tanous std::optional<std::string> roleId("User"); 301a24526dcSEd Tanous std::optional<bool> enabled = true; 3029712f8acSEd Tanous if (!json_util::readJson(req, res, "UserName", username, "Password", 3039712f8acSEd Tanous password, "RoleId", roleId, "Enabled", 3049712f8acSEd Tanous enabled)) 30504ae99ecSEd Tanous { 30604ae99ecSEd Tanous return; 30704ae99ecSEd Tanous } 30804ae99ecSEd Tanous 30984e12cb7SAppaRao Puli std::string priv = getRoleIdFromPrivilege(*roleId); 31084e12cb7SAppaRao Puli if (priv.empty()) 31104ae99ecSEd Tanous { 312f12894f8SJason M. Bills messages::propertyValueNotInList(asyncResp->res, *roleId, "RoleId"); 31304ae99ecSEd Tanous return; 31404ae99ecSEd Tanous } 3159712f8acSEd Tanous roleId = priv; 31604ae99ecSEd Tanous 31704ae99ecSEd Tanous crow::connections::systemBus->async_method_call( 3189712f8acSEd Tanous [asyncResp, username, password{std::move(password)}]( 31904ae99ecSEd Tanous const boost::system::error_code ec) { 32004ae99ecSEd Tanous if (ec) 32104ae99ecSEd Tanous { 32204ae99ecSEd Tanous messages::resourceAlreadyExists( 323f12894f8SJason M. Bills asyncResp->res, "#ManagerAccount.v1_0_3.ManagerAccount", 324f12894f8SJason M. Bills "UserName", username); 32504ae99ecSEd Tanous return; 32604ae99ecSEd Tanous } 32704ae99ecSEd Tanous 32804ae99ecSEd Tanous if (!pamUpdatePassword(username, password)) 32904ae99ecSEd Tanous { 33004ae99ecSEd Tanous // At this point we have a user that's been created, but the 33104ae99ecSEd Tanous // password set failed. Something is wrong, so delete the 33204ae99ecSEd Tanous // user that we've already created 33304ae99ecSEd Tanous crow::connections::systemBus->async_method_call( 33404ae99ecSEd Tanous [asyncResp](const boost::system::error_code ec) { 33504ae99ecSEd Tanous if (ec) 33604ae99ecSEd Tanous { 337f12894f8SJason M. Bills messages::internalError(asyncResp->res); 33804ae99ecSEd Tanous return; 33904ae99ecSEd Tanous } 34004ae99ecSEd Tanous 341f12894f8SJason M. Bills messages::invalidObject(asyncResp->res, "Password"); 34204ae99ecSEd Tanous }, 34304ae99ecSEd Tanous "xyz.openbmc_project.User.Manager", 34404ae99ecSEd Tanous "/xyz/openbmc_project/user/" + username, 34504ae99ecSEd Tanous "xyz.openbmc_project.Object.Delete", "Delete"); 34604ae99ecSEd Tanous 34704ae99ecSEd Tanous BMCWEB_LOG_ERROR << "pamUpdatePassword Failed"; 34804ae99ecSEd Tanous return; 34904ae99ecSEd Tanous } 35004ae99ecSEd Tanous 351f12894f8SJason M. Bills messages::created(asyncResp->res); 35204ae99ecSEd Tanous asyncResp->res.addHeader( 35304ae99ecSEd Tanous "Location", 35404ae99ecSEd Tanous "/redfish/v1/AccountService/Accounts/" + username); 35504ae99ecSEd Tanous }, 35604ae99ecSEd Tanous "xyz.openbmc_project.User.Manager", "/xyz/openbmc_project/user", 3579712f8acSEd Tanous "xyz.openbmc_project.User.Manager", "CreateUser", username, 35804ae99ecSEd Tanous std::array<const char*, 4>{"ipmi", "redfish", "ssh", "web"}, 3599712f8acSEd Tanous *roleId, *enabled); 36004ae99ecSEd Tanous } 361b9b2e0b2SEd Tanous }; 362b9b2e0b2SEd Tanous 363a840879dSEd Tanous template <typename Callback> 364a840879dSEd Tanous inline void checkDbusPathExists(const std::string& path, Callback&& callback) 365a840879dSEd Tanous { 366a840879dSEd Tanous using GetObjectType = 367a840879dSEd Tanous std::vector<std::pair<std::string, std::vector<std::string>>>; 368a840879dSEd Tanous 369a840879dSEd Tanous crow::connections::systemBus->async_method_call( 370a840879dSEd Tanous [callback{std::move(callback)}](const boost::system::error_code ec, 371a840879dSEd Tanous const GetObjectType& object_names) { 37284e12cb7SAppaRao Puli callback(!ec && object_names.size() != 0); 373a840879dSEd Tanous }, 374a840879dSEd Tanous "xyz.openbmc_project.ObjectMapper", 375a840879dSEd Tanous "/xyz/openbmc_project/object_mapper", 376a840879dSEd Tanous "xyz.openbmc_project.ObjectMapper", "GetObject", path, 377a840879dSEd Tanous std::array<std::string, 0>()); 378a840879dSEd Tanous } 379a840879dSEd Tanous 380b9b2e0b2SEd Tanous class ManagerAccount : public Node 381b9b2e0b2SEd Tanous { 382b9b2e0b2SEd Tanous public: 383b9b2e0b2SEd Tanous ManagerAccount(CrowApp& app) : 384b9b2e0b2SEd Tanous Node(app, "/redfish/v1/AccountService/Accounts/<str>/", std::string()) 385b9b2e0b2SEd Tanous { 386b9b2e0b2SEd Tanous entityPrivileges = { 387b9b2e0b2SEd Tanous {boost::beast::http::verb::get, 388b9b2e0b2SEd Tanous {{"ConfigureUsers"}, {"ConfigureManager"}, {"ConfigureSelf"}}}, 389b9b2e0b2SEd Tanous {boost::beast::http::verb::head, {{"Login"}}}, 390b9b2e0b2SEd Tanous {boost::beast::http::verb::patch, {{"ConfigureUsers"}}}, 391b9b2e0b2SEd Tanous {boost::beast::http::verb::put, {{"ConfigureUsers"}}}, 392b9b2e0b2SEd Tanous {boost::beast::http::verb::delete_, {{"ConfigureUsers"}}}, 393b9b2e0b2SEd Tanous {boost::beast::http::verb::post, {{"ConfigureUsers"}}}}; 394b9b2e0b2SEd Tanous } 395b9b2e0b2SEd Tanous 396b9b2e0b2SEd Tanous private: 397b9b2e0b2SEd Tanous void doGet(crow::Response& res, const crow::Request& req, 398b9b2e0b2SEd Tanous const std::vector<std::string>& params) override 399b9b2e0b2SEd Tanous { 4000f74e643SEd Tanous res.jsonValue = { 4010f74e643SEd Tanous {"@odata.context", 4020f74e643SEd Tanous "/redfish/v1/$metadata#ManagerAccount.ManagerAccount"}, 4030f74e643SEd Tanous {"@odata.type", "#ManagerAccount.v1_0_3.ManagerAccount"}, 4040f74e643SEd Tanous {"Name", "User Account"}, 4050f74e643SEd Tanous {"Description", "User Account"}, 4060f74e643SEd Tanous {"Password", nullptr}, 40784e12cb7SAppaRao Puli {"RoleId", "Administrator"}}; 4080f74e643SEd Tanous 409b9b2e0b2SEd Tanous auto asyncResp = std::make_shared<AsyncResp>(res); 410b9b2e0b2SEd Tanous 411b9b2e0b2SEd Tanous if (params.size() != 1) 412b9b2e0b2SEd Tanous { 413f12894f8SJason M. Bills messages::internalError(asyncResp->res); 414b9b2e0b2SEd Tanous return; 415b9b2e0b2SEd Tanous } 416b9b2e0b2SEd Tanous 417b9b2e0b2SEd Tanous crow::connections::systemBus->async_method_call( 418b9b2e0b2SEd Tanous [asyncResp, accountName{std::string(params[0])}]( 419b9b2e0b2SEd Tanous const boost::system::error_code ec, 420b9b2e0b2SEd Tanous const ManagedObjectType& users) { 421b9b2e0b2SEd Tanous if (ec) 422b9b2e0b2SEd Tanous { 423f12894f8SJason M. Bills messages::internalError(asyncResp->res); 424b9b2e0b2SEd Tanous return; 425b9b2e0b2SEd Tanous } 42684e12cb7SAppaRao Puli auto userIt = users.begin(); 427b9b2e0b2SEd Tanous 42884e12cb7SAppaRao Puli for (; userIt != users.end(); userIt++) 429b9b2e0b2SEd Tanous { 43084e12cb7SAppaRao Puli if (boost::ends_with(userIt->first.str, "/" + accountName)) 431b9b2e0b2SEd Tanous { 43284e12cb7SAppaRao Puli break; 433b9b2e0b2SEd Tanous } 434b9b2e0b2SEd Tanous } 43584e12cb7SAppaRao Puli if (userIt == users.end()) 436b9b2e0b2SEd Tanous { 43784e12cb7SAppaRao Puli messages::resourceNotFound(asyncResp->res, "ManagerAccount", 43884e12cb7SAppaRao Puli accountName); 43984e12cb7SAppaRao Puli return; 44084e12cb7SAppaRao Puli } 44184e12cb7SAppaRao Puli for (const auto& interface : userIt->second) 44265b0dc32SEd Tanous { 44365b0dc32SEd Tanous if (interface.first == 44465b0dc32SEd Tanous "xyz.openbmc_project.User.Attributes") 44565b0dc32SEd Tanous { 44665b0dc32SEd Tanous for (const auto& property : interface.second) 44765b0dc32SEd Tanous { 44865b0dc32SEd Tanous if (property.first == "UserEnabled") 44965b0dc32SEd Tanous { 45065b0dc32SEd Tanous const bool* userEnabled = 451abf2add6SEd Tanous std::get_if<bool>(&property.second); 45265b0dc32SEd Tanous if (userEnabled == nullptr) 45365b0dc32SEd Tanous { 45465b0dc32SEd Tanous BMCWEB_LOG_ERROR 45565b0dc32SEd Tanous << "UserEnabled wasn't a bool"; 45684e12cb7SAppaRao Puli messages::internalError(asyncResp->res); 45784e12cb7SAppaRao Puli return; 45865b0dc32SEd Tanous } 45965b0dc32SEd Tanous asyncResp->res.jsonValue["Enabled"] = 46065b0dc32SEd Tanous *userEnabled; 46165b0dc32SEd Tanous } 46265b0dc32SEd Tanous else if (property.first == 46365b0dc32SEd Tanous "UserLockedForFailedAttempt") 46465b0dc32SEd Tanous { 46565b0dc32SEd Tanous const bool* userLocked = 466abf2add6SEd Tanous std::get_if<bool>(&property.second); 46765b0dc32SEd Tanous if (userLocked == nullptr) 46865b0dc32SEd Tanous { 46984e12cb7SAppaRao Puli BMCWEB_LOG_ERROR << "UserLockedForF" 47084e12cb7SAppaRao Puli "ailedAttempt " 47184e12cb7SAppaRao Puli "wasn't a bool"; 47284e12cb7SAppaRao Puli messages::internalError(asyncResp->res); 47384e12cb7SAppaRao Puli return; 47465b0dc32SEd Tanous } 47565b0dc32SEd Tanous asyncResp->res.jsonValue["Locked"] = 47665b0dc32SEd Tanous *userLocked; 47724c8542dSRatan Gupta asyncResp->res.jsonValue 47824c8542dSRatan Gupta ["Locked@Redfish.AllowableValues"] = { 47924c8542dSRatan Gupta false}; 48065b0dc32SEd Tanous } 48184e12cb7SAppaRao Puli else if (property.first == "UserPrivilege") 48284e12cb7SAppaRao Puli { 48384e12cb7SAppaRao Puli const std::string* userRolePtr = 484abf2add6SEd Tanous std::get_if<std::string>(&property.second); 48584e12cb7SAppaRao Puli if (userRolePtr == nullptr) 48684e12cb7SAppaRao Puli { 48784e12cb7SAppaRao Puli BMCWEB_LOG_ERROR 48884e12cb7SAppaRao Puli << "UserPrivilege wasn't a " 48984e12cb7SAppaRao Puli "string"; 49084e12cb7SAppaRao Puli messages::internalError(asyncResp->res); 49184e12cb7SAppaRao Puli return; 49284e12cb7SAppaRao Puli } 49384e12cb7SAppaRao Puli std::string priv = 49484e12cb7SAppaRao Puli getPrivilegeFromRoleId(*userRolePtr); 49584e12cb7SAppaRao Puli if (priv.empty()) 49684e12cb7SAppaRao Puli { 49784e12cb7SAppaRao Puli BMCWEB_LOG_ERROR << "Invalid user role"; 49884e12cb7SAppaRao Puli messages::internalError(asyncResp->res); 49984e12cb7SAppaRao Puli return; 50084e12cb7SAppaRao Puli } 50184e12cb7SAppaRao Puli asyncResp->res.jsonValue["RoleId"] = priv; 50284e12cb7SAppaRao Puli 50384e12cb7SAppaRao Puli asyncResp->res.jsonValue["Links"]["Role"] = { 50484e12cb7SAppaRao Puli {"@odata.id", "/redfish/v1/AccountService/" 50584e12cb7SAppaRao Puli "Roles/" + 50684e12cb7SAppaRao Puli priv}}; 50784e12cb7SAppaRao Puli } 50865b0dc32SEd Tanous } 50965b0dc32SEd Tanous } 51065b0dc32SEd Tanous } 51165b0dc32SEd Tanous 512b9b2e0b2SEd Tanous asyncResp->res.jsonValue["@odata.id"] = 51384e12cb7SAppaRao Puli "/redfish/v1/AccountService/Accounts/" + accountName; 514b9b2e0b2SEd Tanous asyncResp->res.jsonValue["Id"] = accountName; 515b9b2e0b2SEd Tanous asyncResp->res.jsonValue["UserName"] = accountName; 516b9b2e0b2SEd Tanous }, 517b9b2e0b2SEd Tanous "xyz.openbmc_project.User.Manager", "/xyz/openbmc_project/user", 518b9b2e0b2SEd Tanous "org.freedesktop.DBus.ObjectManager", "GetManagedObjects"); 519b9b2e0b2SEd Tanous } 520a840879dSEd Tanous 521a840879dSEd Tanous void doPatch(crow::Response& res, const crow::Request& req, 522a840879dSEd Tanous const std::vector<std::string>& params) override 523a840879dSEd Tanous { 524a840879dSEd Tanous auto asyncResp = std::make_shared<AsyncResp>(res); 525a840879dSEd Tanous if (params.size() != 1) 526a840879dSEd Tanous { 527f12894f8SJason M. Bills messages::internalError(asyncResp->res); 528a840879dSEd Tanous return; 529a840879dSEd Tanous } 530a840879dSEd Tanous 531a24526dcSEd Tanous std::optional<std::string> newUserName; 532a24526dcSEd Tanous std::optional<std::string> password; 533a24526dcSEd Tanous std::optional<bool> enabled; 534a24526dcSEd Tanous std::optional<std::string> roleId; 53524c8542dSRatan Gupta std::optional<bool> locked; 53684e12cb7SAppaRao Puli if (!json_util::readJson(req, res, "UserName", newUserName, "Password", 53724c8542dSRatan Gupta password, "RoleId", roleId, "Enabled", enabled, 53824c8542dSRatan Gupta "Locked", locked)) 539a840879dSEd Tanous { 540a840879dSEd Tanous return; 541a840879dSEd Tanous } 542a840879dSEd Tanous 54384e12cb7SAppaRao Puli const std::string& username = params[0]; 54484e12cb7SAppaRao Puli 54584e12cb7SAppaRao Puli if (!newUserName) 546a840879dSEd Tanous { 54784e12cb7SAppaRao Puli // If the username isn't being updated, we can update the properties 54884e12cb7SAppaRao Puli // directly 54924c8542dSRatan Gupta updateUserProperties(asyncResp, username, password, enabled, roleId, 55024c8542dSRatan Gupta locked); 55184e12cb7SAppaRao Puli return; 55284e12cb7SAppaRao Puli } 55384e12cb7SAppaRao Puli else 55484e12cb7SAppaRao Puli { 55584e12cb7SAppaRao Puli crow::connections::systemBus->async_method_call( 55684e12cb7SAppaRao Puli [this, asyncResp, username, password(std::move(password)), 55784e12cb7SAppaRao Puli roleId(std::move(roleId)), enabled(std::move(enabled)), 55824c8542dSRatan Gupta newUser{std::string(*newUserName)}, locked(std::move(locked))]( 55984e12cb7SAppaRao Puli const boost::system::error_code ec) { 56084e12cb7SAppaRao Puli if (ec) 56184e12cb7SAppaRao Puli { 56284e12cb7SAppaRao Puli BMCWEB_LOG_ERROR << "D-Bus responses error: " << ec; 563a840879dSEd Tanous messages::resourceNotFound( 56484e12cb7SAppaRao Puli asyncResp->res, 56584e12cb7SAppaRao Puli "#ManagerAccount.v1_0_3.ManagerAccount", username); 566a840879dSEd Tanous return; 567a840879dSEd Tanous } 568a840879dSEd Tanous 56984e12cb7SAppaRao Puli updateUserProperties(asyncResp, newUser, password, enabled, 57024c8542dSRatan Gupta roleId, locked); 57184e12cb7SAppaRao Puli }, 57284e12cb7SAppaRao Puli "xyz.openbmc_project.User.Manager", "/xyz/openbmc_project/user", 57384e12cb7SAppaRao Puli "xyz.openbmc_project.User.Manager", "RenameUser", username, 57484e12cb7SAppaRao Puli *newUserName); 57584e12cb7SAppaRao Puli } 57684e12cb7SAppaRao Puli } 57784e12cb7SAppaRao Puli 57884e12cb7SAppaRao Puli void updateUserProperties(std::shared_ptr<AsyncResp> asyncResp, 57984e12cb7SAppaRao Puli const std::string& username, 580a24526dcSEd Tanous std::optional<std::string> password, 581a24526dcSEd Tanous std::optional<bool> enabled, 58224c8542dSRatan Gupta std::optional<std::string> roleId, 58324c8542dSRatan Gupta std::optional<bool> locked) 58484e12cb7SAppaRao Puli { 5859712f8acSEd Tanous if (password) 586a840879dSEd Tanous { 5879712f8acSEd Tanous if (!pamUpdatePassword(username, *password)) 588a840879dSEd Tanous { 589a840879dSEd Tanous BMCWEB_LOG_ERROR << "pamUpdatePassword Failed"; 590f12894f8SJason M. Bills messages::internalError(asyncResp->res); 591a840879dSEd Tanous return; 592a840879dSEd Tanous } 593a840879dSEd Tanous } 594a840879dSEd Tanous 59524c8542dSRatan Gupta std::string dbusObjectPath = "/xyz/openbmc_project/user/" + username; 59624c8542dSRatan Gupta dbus::utility::escapePathForDbus(dbusObjectPath); 59724c8542dSRatan Gupta 59824c8542dSRatan Gupta checkDbusPathExists( 59924c8542dSRatan Gupta dbusObjectPath, 60024c8542dSRatan Gupta [dbusObjectPath(std::move(dbusObjectPath)), username, 60124c8542dSRatan Gupta password(std::move(password)), roleId(std::move(roleId)), 60224c8542dSRatan Gupta enabled(std::move(enabled)), locked(std::move(locked)), 60324c8542dSRatan Gupta asyncResp{std::move(asyncResp)}](int rc) { 60424c8542dSRatan Gupta if (!rc) 60524c8542dSRatan Gupta { 60624c8542dSRatan Gupta messages::invalidObject(asyncResp->res, username.c_str()); 60724c8542dSRatan Gupta return; 60824c8542dSRatan Gupta } 6099712f8acSEd Tanous if (enabled) 610a840879dSEd Tanous { 611a840879dSEd Tanous crow::connections::systemBus->async_method_call( 612a840879dSEd Tanous [asyncResp](const boost::system::error_code ec) { 613a840879dSEd Tanous if (ec) 614a840879dSEd Tanous { 61524c8542dSRatan Gupta BMCWEB_LOG_ERROR << "D-Bus responses error: " 61624c8542dSRatan Gupta << ec; 617f12894f8SJason M. Bills messages::internalError(asyncResp->res); 618a840879dSEd Tanous return; 619a840879dSEd Tanous } 62084e12cb7SAppaRao Puli messages::success(asyncResp->res); 62184e12cb7SAppaRao Puli return; 62284e12cb7SAppaRao Puli }, 62384e12cb7SAppaRao Puli "xyz.openbmc_project.User.Manager", 62424c8542dSRatan Gupta dbusObjectPath.c_str(), 62584e12cb7SAppaRao Puli "org.freedesktop.DBus.Properties", "Set", 62684e12cb7SAppaRao Puli "xyz.openbmc_project.User.Attributes", "UserEnabled", 627abf2add6SEd Tanous std::variant<bool>{*enabled}); 62884e12cb7SAppaRao Puli } 62984e12cb7SAppaRao Puli 63084e12cb7SAppaRao Puli if (roleId) 63184e12cb7SAppaRao Puli { 63284e12cb7SAppaRao Puli std::string priv = getRoleIdFromPrivilege(*roleId); 63384e12cb7SAppaRao Puli if (priv.empty()) 63484e12cb7SAppaRao Puli { 63524c8542dSRatan Gupta messages::propertyValueNotInList(asyncResp->res, 63624c8542dSRatan Gupta *roleId, "RoleId"); 63784e12cb7SAppaRao Puli return; 63884e12cb7SAppaRao Puli } 63984e12cb7SAppaRao Puli 64084e12cb7SAppaRao Puli crow::connections::systemBus->async_method_call( 64184e12cb7SAppaRao Puli [asyncResp](const boost::system::error_code ec) { 64284e12cb7SAppaRao Puli if (ec) 64384e12cb7SAppaRao Puli { 64424c8542dSRatan Gupta BMCWEB_LOG_ERROR << "D-Bus responses error: " 64524c8542dSRatan Gupta << ec; 64684e12cb7SAppaRao Puli messages::internalError(asyncResp->res); 64784e12cb7SAppaRao Puli return; 64884e12cb7SAppaRao Puli } 649f12894f8SJason M. Bills messages::success(asyncResp->res); 650a840879dSEd Tanous }, 651a840879dSEd Tanous "xyz.openbmc_project.User.Manager", 65224c8542dSRatan Gupta dbusObjectPath.c_str(), 653a840879dSEd Tanous "org.freedesktop.DBus.Properties", "Set", 65484e12cb7SAppaRao Puli "xyz.openbmc_project.User.Attributes", "UserPrivilege", 655abf2add6SEd Tanous std::variant<std::string>{priv}); 656a840879dSEd Tanous } 65724c8542dSRatan Gupta 65824c8542dSRatan Gupta if (locked) 65924c8542dSRatan Gupta { 66024c8542dSRatan Gupta // admin can unlock the account which is locked by 66124c8542dSRatan Gupta // successive authentication failures but admin should not 66224c8542dSRatan Gupta // be allowed to lock an account. 66324c8542dSRatan Gupta if (*locked) 66424c8542dSRatan Gupta { 66524c8542dSRatan Gupta messages::propertyValueNotInList(asyncResp->res, "true", 66624c8542dSRatan Gupta "Locked"); 66724c8542dSRatan Gupta return; 66824c8542dSRatan Gupta } 66924c8542dSRatan Gupta 67024c8542dSRatan Gupta crow::connections::systemBus->async_method_call( 67124c8542dSRatan Gupta [asyncResp](const boost::system::error_code ec) { 67224c8542dSRatan Gupta if (ec) 67324c8542dSRatan Gupta { 67424c8542dSRatan Gupta BMCWEB_LOG_ERROR << "D-Bus responses error: " 67524c8542dSRatan Gupta << ec; 67624c8542dSRatan Gupta messages::internalError(asyncResp->res); 67724c8542dSRatan Gupta return; 67824c8542dSRatan Gupta } 67924c8542dSRatan Gupta messages::success(asyncResp->res); 68024c8542dSRatan Gupta return; 68124c8542dSRatan Gupta }, 68224c8542dSRatan Gupta "xyz.openbmc_project.User.Manager", 68324c8542dSRatan Gupta dbusObjectPath.c_str(), 68424c8542dSRatan Gupta "org.freedesktop.DBus.Properties", "Set", 68524c8542dSRatan Gupta "xyz.openbmc_project.User.Attributes", 68624c8542dSRatan Gupta "UserLockedForFailedAttempt", 68724c8542dSRatan Gupta sdbusplus::message::variant<bool>{*locked}); 68824c8542dSRatan Gupta } 68924c8542dSRatan Gupta }); 690a840879dSEd Tanous } 69106e086d9SEd Tanous 69206e086d9SEd Tanous void doDelete(crow::Response& res, const crow::Request& req, 69306e086d9SEd Tanous const std::vector<std::string>& params) override 69406e086d9SEd Tanous { 69506e086d9SEd Tanous auto asyncResp = std::make_shared<AsyncResp>(res); 69606e086d9SEd Tanous 69706e086d9SEd Tanous if (params.size() != 1) 69806e086d9SEd Tanous { 699f12894f8SJason M. Bills messages::internalError(asyncResp->res); 70006e086d9SEd Tanous return; 70106e086d9SEd Tanous } 70206e086d9SEd Tanous 70306e086d9SEd Tanous const std::string userPath = "/xyz/openbmc_project/user/" + params[0]; 70406e086d9SEd Tanous 70506e086d9SEd Tanous crow::connections::systemBus->async_method_call( 70606e086d9SEd Tanous [asyncResp, username{std::move(params[0])}]( 70706e086d9SEd Tanous const boost::system::error_code ec) { 70806e086d9SEd Tanous if (ec) 70906e086d9SEd Tanous { 71006e086d9SEd Tanous messages::resourceNotFound( 711f12894f8SJason M. Bills asyncResp->res, "#ManagerAccount.v1_0_3.ManagerAccount", 712f12894f8SJason M. Bills username); 71306e086d9SEd Tanous return; 71406e086d9SEd Tanous } 71506e086d9SEd Tanous 716f12894f8SJason M. Bills messages::accountRemoved(asyncResp->res); 71706e086d9SEd Tanous }, 71806e086d9SEd Tanous "xyz.openbmc_project.User.Manager", userPath, 71906e086d9SEd Tanous "xyz.openbmc_project.Object.Delete", "Delete"); 72006e086d9SEd Tanous } 72184e12cb7SAppaRao Puli }; 72288d16c9aSLewanczyk, Dawid 72388d16c9aSLewanczyk, Dawid } // namespace redfish 724