xref: /openbmc/bmcweb/features/redfish/lib/account_service.hpp (revision 19fb6e719849aea2f0cafdee64cd7da8a7a2b67b)
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