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