xref: /openbmc/bmcweb/features/redfish/lib/account_service.hpp (revision 04ae99ec1fbfea2e07c0db4174712d0873d2e91c)
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<
29b9b2e0b2SEd Tanous         std::string, boost::container::flat_map<
30b9b2e0b2SEd Tanous                          std::string, sdbusplus::message::variant<bool>>>>>;
31b9b2e0b2SEd Tanous 
321abe55efSEd Tanous class AccountService : public Node
331abe55efSEd Tanous {
3488d16c9aSLewanczyk, Dawid   public:
351abe55efSEd Tanous     AccountService(CrowApp& app) : Node(app, "/redfish/v1/AccountService/")
361abe55efSEd Tanous     {
37c1a46bd2SBorawski.Lukasz         Node::json["@odata.id"] = "/redfish/v1/AccountService";
38c1a46bd2SBorawski.Lukasz         Node::json["@odata.type"] = "#AccountService.v1_1_0.AccountService";
39c1a46bd2SBorawski.Lukasz         Node::json["@odata.context"] =
4088d16c9aSLewanczyk, Dawid             "/redfish/v1/$metadata#AccountService.AccountService";
41c1a46bd2SBorawski.Lukasz         Node::json["Id"] = "AccountService";
42c1a46bd2SBorawski.Lukasz         Node::json["Description"] = "BMC User Accounts";
43c1a46bd2SBorawski.Lukasz         Node::json["Name"] = "Account Service";
44c1a46bd2SBorawski.Lukasz         Node::json["ServiceEnabled"] = true;
45c1a46bd2SBorawski.Lukasz         Node::json["MinPasswordLength"] = 1;
46c1a46bd2SBorawski.Lukasz         Node::json["MaxPasswordLength"] = 20;
471abe55efSEd Tanous         Node::json["Accounts"]["@odata.id"] =
481abe55efSEd Tanous             "/redfish/v1/AccountService/Accounts";
49c1a46bd2SBorawski.Lukasz         Node::json["Roles"]["@odata.id"] = "/redfish/v1/AccountService/Roles";
503ebd75f7SEd Tanous 
513ebd75f7SEd Tanous         entityPrivileges = {
524b1b8683SBorawski.Lukasz             {boost::beast::http::verb::get,
534b1b8683SBorawski.Lukasz              {{"ConfigureUsers"}, {"ConfigureManager"}}},
54e0d918bcSEd Tanous             {boost::beast::http::verb::head, {{"Login"}}},
55e0d918bcSEd Tanous             {boost::beast::http::verb::patch, {{"ConfigureUsers"}}},
56e0d918bcSEd Tanous             {boost::beast::http::verb::put, {{"ConfigureUsers"}}},
57e0d918bcSEd Tanous             {boost::beast::http::verb::delete_, {{"ConfigureUsers"}}},
58e0d918bcSEd Tanous             {boost::beast::http::verb::post, {{"ConfigureUsers"}}}};
5988d16c9aSLewanczyk, Dawid     }
6088d16c9aSLewanczyk, Dawid 
6188d16c9aSLewanczyk, Dawid   private:
6255c7b7a2SEd Tanous     void doGet(crow::Response& res, const crow::Request& req,
631abe55efSEd Tanous                const std::vector<std::string>& params) override
641abe55efSEd Tanous     {
6555c7b7a2SEd Tanous         res.jsonValue = Node::json;
6688d16c9aSLewanczyk, Dawid         res.end();
6788d16c9aSLewanczyk, Dawid     }
6888d16c9aSLewanczyk, Dawid };
69b9b2e0b2SEd Tanous class AccountsCollection : public Node
70b9b2e0b2SEd Tanous {
71b9b2e0b2SEd Tanous   public:
72b9b2e0b2SEd Tanous     AccountsCollection(CrowApp& app) :
73b9b2e0b2SEd Tanous         Node(app, "/redfish/v1/AccountService/Accounts/")
74b9b2e0b2SEd Tanous     {
75b9b2e0b2SEd Tanous 
76b9b2e0b2SEd Tanous         Node::json = {{"@odata.context", "/redfish/v1/"
77b9b2e0b2SEd Tanous                                          "$metadata#ManagerAccountCollection."
78b9b2e0b2SEd Tanous                                          "ManagerAccountCollection"},
79b9b2e0b2SEd Tanous                       {"@odata.id", "/redfish/v1/AccountService/Accounts"},
80b9b2e0b2SEd Tanous                       {"@odata.type", "#ManagerAccountCollection."
81b9b2e0b2SEd Tanous                                       "ManagerAccountCollection"},
82b9b2e0b2SEd Tanous                       {"Name", "Accounts Collection"},
83b9b2e0b2SEd Tanous                       {"Description", "BMC User Accounts"}};
84b9b2e0b2SEd Tanous 
85b9b2e0b2SEd Tanous         entityPrivileges = {
86b9b2e0b2SEd Tanous             {boost::beast::http::verb::get,
87b9b2e0b2SEd Tanous              {{"ConfigureUsers"}, {"ConfigureManager"}}},
88b9b2e0b2SEd Tanous             {boost::beast::http::verb::head, {{"Login"}}},
89b9b2e0b2SEd Tanous             {boost::beast::http::verb::patch, {{"ConfigureUsers"}}},
90b9b2e0b2SEd Tanous             {boost::beast::http::verb::put, {{"ConfigureUsers"}}},
91b9b2e0b2SEd Tanous             {boost::beast::http::verb::delete_, {{"ConfigureUsers"}}},
92b9b2e0b2SEd Tanous             {boost::beast::http::verb::post, {{"ConfigureUsers"}}}};
93b9b2e0b2SEd Tanous     }
94b9b2e0b2SEd Tanous 
95b9b2e0b2SEd Tanous   private:
96b9b2e0b2SEd Tanous     void doGet(crow::Response& res, const crow::Request& req,
97b9b2e0b2SEd Tanous                const std::vector<std::string>& params) override
98b9b2e0b2SEd Tanous     {
99b9b2e0b2SEd Tanous         res.jsonValue = Node::json;
100b9b2e0b2SEd Tanous         auto asyncResp = std::make_shared<AsyncResp>(res);
101b9b2e0b2SEd Tanous         crow::connections::systemBus->async_method_call(
102b9b2e0b2SEd Tanous             [asyncResp](const boost::system::error_code ec,
103b9b2e0b2SEd Tanous                         const ManagedObjectType& users) {
104b9b2e0b2SEd Tanous                 if (ec)
105b9b2e0b2SEd Tanous                 {
106b9b2e0b2SEd Tanous                     asyncResp->res.result(
107b9b2e0b2SEd Tanous                         boost::beast::http::status::internal_server_error);
108b9b2e0b2SEd Tanous                     return;
109b9b2e0b2SEd Tanous                 }
110b9b2e0b2SEd Tanous 
111b9b2e0b2SEd Tanous                 nlohmann::json& memberArray =
112b9b2e0b2SEd Tanous                     asyncResp->res.jsonValue["Members"];
113b9b2e0b2SEd Tanous                 memberArray = nlohmann::json::array();
114b9b2e0b2SEd Tanous 
115b9b2e0b2SEd Tanous                 asyncResp->res.jsonValue["Members@odata.count"] = users.size();
116b9b2e0b2SEd Tanous                 for (auto& user : users)
117b9b2e0b2SEd Tanous                 {
118b9b2e0b2SEd Tanous                     const std::string& path =
119b9b2e0b2SEd Tanous                         static_cast<const std::string&>(user.first);
120b9b2e0b2SEd Tanous                     std::size_t lastIndex = path.rfind("/");
121b9b2e0b2SEd Tanous                     if (lastIndex == std::string::npos)
122b9b2e0b2SEd Tanous                     {
123b9b2e0b2SEd Tanous                         lastIndex = 0;
124b9b2e0b2SEd Tanous                     }
125b9b2e0b2SEd Tanous                     else
126b9b2e0b2SEd Tanous                     {
127b9b2e0b2SEd Tanous                         lastIndex += 1;
128b9b2e0b2SEd Tanous                     }
129b9b2e0b2SEd Tanous                     memberArray.push_back(
130b9b2e0b2SEd Tanous                         {{"@odata.id", "/redfish/v1/AccountService/Accounts/" +
131b9b2e0b2SEd Tanous                                            path.substr(lastIndex)}});
132b9b2e0b2SEd Tanous                 }
133b9b2e0b2SEd Tanous             },
134b9b2e0b2SEd Tanous             "xyz.openbmc_project.User.Manager", "/xyz/openbmc_project/user",
135b9b2e0b2SEd Tanous             "org.freedesktop.DBus.ObjectManager", "GetManagedObjects");
136b9b2e0b2SEd Tanous     }
137*04ae99ecSEd Tanous     void doPost(crow::Response& res, const crow::Request& req,
138*04ae99ecSEd Tanous                 const std::vector<std::string>& params) override
139*04ae99ecSEd Tanous     {
140*04ae99ecSEd Tanous         auto asyncResp = std::make_shared<AsyncResp>(res);
141*04ae99ecSEd Tanous 
142*04ae99ecSEd Tanous         nlohmann::json patchRequest;
143*04ae99ecSEd Tanous         if (!json_util::processJsonFromRequest(res, req, patchRequest))
144*04ae99ecSEd Tanous         {
145*04ae99ecSEd Tanous             return;
146*04ae99ecSEd Tanous         }
147*04ae99ecSEd Tanous 
148*04ae99ecSEd Tanous         const std::string* username = nullptr;
149*04ae99ecSEd Tanous         const std::string* password = nullptr;
150*04ae99ecSEd Tanous         // Default to user
151*04ae99ecSEd Tanous         std::string privilege = "priv-user";
152*04ae99ecSEd Tanous         // default to enabled
153*04ae99ecSEd Tanous         bool enabled = true;
154*04ae99ecSEd Tanous         for (const auto& item : patchRequest.items())
155*04ae99ecSEd Tanous         {
156*04ae99ecSEd Tanous             if (item.key() == "UserName")
157*04ae99ecSEd Tanous             {
158*04ae99ecSEd Tanous                 username = item.value().get_ptr<const std::string*>();
159*04ae99ecSEd Tanous                 if (username == nullptr)
160*04ae99ecSEd Tanous                 {
161*04ae99ecSEd Tanous                     messages::addMessageToErrorJson(
162*04ae99ecSEd Tanous                         asyncResp->res.jsonValue,
163*04ae99ecSEd Tanous                         messages::propertyValueFormatError(item.value().dump(),
164*04ae99ecSEd Tanous                                                            item.key()));
165*04ae99ecSEd Tanous                     asyncResp->res.result(
166*04ae99ecSEd Tanous                         boost::beast::http::status::bad_request);
167*04ae99ecSEd Tanous                     return;
168*04ae99ecSEd Tanous                 }
169*04ae99ecSEd Tanous             }
170*04ae99ecSEd Tanous             else if (item.key() == "Enabled")
171*04ae99ecSEd Tanous             {
172*04ae99ecSEd Tanous                 const bool* enabledJson = item.value().get_ptr<const bool*>();
173*04ae99ecSEd Tanous                 if (enabledJson == nullptr)
174*04ae99ecSEd Tanous                 {
175*04ae99ecSEd Tanous                     messages::addMessageToErrorJson(
176*04ae99ecSEd Tanous                         asyncResp->res.jsonValue,
177*04ae99ecSEd Tanous                         messages::propertyValueFormatError(item.value().dump(),
178*04ae99ecSEd Tanous                                                            item.key()));
179*04ae99ecSEd Tanous                     asyncResp->res.result(
180*04ae99ecSEd Tanous                         boost::beast::http::status::bad_request);
181*04ae99ecSEd Tanous                     return;
182*04ae99ecSEd Tanous                 }
183*04ae99ecSEd Tanous                 enabled = *enabledJson;
184*04ae99ecSEd Tanous             }
185*04ae99ecSEd Tanous             else if (item.key() == "Password")
186*04ae99ecSEd Tanous             {
187*04ae99ecSEd Tanous                 password = item.value().get_ptr<const std::string*>();
188*04ae99ecSEd Tanous                 if (password == nullptr)
189*04ae99ecSEd Tanous                 {
190*04ae99ecSEd Tanous                     messages::addMessageToErrorJson(
191*04ae99ecSEd Tanous                         asyncResp->res.jsonValue,
192*04ae99ecSEd Tanous                         messages::propertyValueFormatError(item.value().dump(),
193*04ae99ecSEd Tanous                                                            item.key()));
194*04ae99ecSEd Tanous                     asyncResp->res.result(
195*04ae99ecSEd Tanous                         boost::beast::http::status::bad_request);
196*04ae99ecSEd Tanous                     return;
197*04ae99ecSEd Tanous                 }
198*04ae99ecSEd Tanous             }
199*04ae99ecSEd Tanous             else if (item.key() == "RoleId")
200*04ae99ecSEd Tanous             {
201*04ae99ecSEd Tanous                 const std::string* roleIdJson =
202*04ae99ecSEd Tanous                     item.value().get_ptr<const std::string*>();
203*04ae99ecSEd Tanous                 if (roleIdJson == nullptr)
204*04ae99ecSEd Tanous                 {
205*04ae99ecSEd Tanous                     messages::addMessageToErrorJson(
206*04ae99ecSEd Tanous                         asyncResp->res.jsonValue,
207*04ae99ecSEd Tanous                         messages::propertyValueFormatError(item.value().dump(),
208*04ae99ecSEd Tanous                                                            item.key()));
209*04ae99ecSEd Tanous                     asyncResp->res.result(
210*04ae99ecSEd Tanous                         boost::beast::http::status::bad_request);
211*04ae99ecSEd Tanous                     return;
212*04ae99ecSEd Tanous                 }
213*04ae99ecSEd Tanous                 const char* priv = getRoleIdFromPrivilege(*roleIdJson);
214*04ae99ecSEd Tanous                 if (priv == nullptr)
215*04ae99ecSEd Tanous                 {
216*04ae99ecSEd Tanous                     messages::addMessageToErrorJson(
217*04ae99ecSEd Tanous                         asyncResp->res.jsonValue,
218*04ae99ecSEd Tanous                         messages::propertyValueNotInList(*roleIdJson,
219*04ae99ecSEd Tanous                                                          item.key()));
220*04ae99ecSEd Tanous                     asyncResp->res.result(
221*04ae99ecSEd Tanous                         boost::beast::http::status::bad_request);
222*04ae99ecSEd Tanous                     return;
223*04ae99ecSEd Tanous                 }
224*04ae99ecSEd Tanous                 privilege = priv;
225*04ae99ecSEd Tanous             }
226*04ae99ecSEd Tanous             else
227*04ae99ecSEd Tanous             {
228*04ae99ecSEd Tanous                 messages::addMessageToErrorJson(
229*04ae99ecSEd Tanous                     asyncResp->res.jsonValue,
230*04ae99ecSEd Tanous                     messages::propertyNotWritable(item.key()));
231*04ae99ecSEd Tanous                 asyncResp->res.result(boost::beast::http::status::bad_request);
232*04ae99ecSEd Tanous                 return;
233*04ae99ecSEd Tanous             }
234*04ae99ecSEd Tanous         }
235*04ae99ecSEd Tanous 
236*04ae99ecSEd Tanous         if (username == nullptr)
237*04ae99ecSEd Tanous         {
238*04ae99ecSEd Tanous             messages::addMessageToErrorJson(
239*04ae99ecSEd Tanous                 asyncResp->res.jsonValue,
240*04ae99ecSEd Tanous                 messages::createFailedMissingReqProperties("UserName"));
241*04ae99ecSEd Tanous             asyncResp->res.result(boost::beast::http::status::bad_request);
242*04ae99ecSEd Tanous             return;
243*04ae99ecSEd Tanous         }
244*04ae99ecSEd Tanous 
245*04ae99ecSEd Tanous         if (password == nullptr)
246*04ae99ecSEd Tanous         {
247*04ae99ecSEd Tanous             messages::addMessageToErrorJson(
248*04ae99ecSEd Tanous                 asyncResp->res.jsonValue,
249*04ae99ecSEd Tanous                 messages::createFailedMissingReqProperties("Password"));
250*04ae99ecSEd Tanous             asyncResp->res.result(boost::beast::http::status::bad_request);
251*04ae99ecSEd Tanous             return;
252*04ae99ecSEd Tanous         }
253*04ae99ecSEd Tanous 
254*04ae99ecSEd Tanous         crow::connections::systemBus->async_method_call(
255*04ae99ecSEd Tanous             [asyncResp, username{std::string(*username)},
256*04ae99ecSEd Tanous              password{std::string(*password)}](
257*04ae99ecSEd Tanous                 const boost::system::error_code ec) {
258*04ae99ecSEd Tanous                 if (ec)
259*04ae99ecSEd Tanous                 {
260*04ae99ecSEd Tanous                     messages::addMessageToErrorJson(
261*04ae99ecSEd Tanous                         asyncResp->res.jsonValue,
262*04ae99ecSEd Tanous                         messages::resourceAlreadyExists(
263*04ae99ecSEd Tanous                             "#ManagerAccount.v1_0_3.ManagerAccount", "UserName",
264*04ae99ecSEd Tanous                             username));
265*04ae99ecSEd Tanous                     asyncResp->res.result(
266*04ae99ecSEd Tanous                         boost::beast::http::status::bad_request);
267*04ae99ecSEd Tanous                     return;
268*04ae99ecSEd Tanous                 }
269*04ae99ecSEd Tanous 
270*04ae99ecSEd Tanous                 if (!pamUpdatePassword(username, password))
271*04ae99ecSEd Tanous                 {
272*04ae99ecSEd Tanous                     // At this point we have a user that's been created, but the
273*04ae99ecSEd Tanous                     // password set failed.  Something is wrong, so delete the
274*04ae99ecSEd Tanous                     // user that we've already created
275*04ae99ecSEd Tanous                     crow::connections::systemBus->async_method_call(
276*04ae99ecSEd Tanous                         [asyncResp](const boost::system::error_code ec) {
277*04ae99ecSEd Tanous                             if (ec)
278*04ae99ecSEd Tanous                             {
279*04ae99ecSEd Tanous                                 asyncResp->res.result(
280*04ae99ecSEd Tanous                                     boost::beast::http::status::
281*04ae99ecSEd Tanous                                         internal_server_error);
282*04ae99ecSEd Tanous                                 return;
283*04ae99ecSEd Tanous                             }
284*04ae99ecSEd Tanous 
285*04ae99ecSEd Tanous                             asyncResp->res.result(
286*04ae99ecSEd Tanous                                 boost::beast::http::status::bad_request);
287*04ae99ecSEd Tanous                         },
288*04ae99ecSEd Tanous                         "xyz.openbmc_project.User.Manager",
289*04ae99ecSEd Tanous                         "/xyz/openbmc_project/user/" + username,
290*04ae99ecSEd Tanous                         "xyz.openbmc_project.Object.Delete", "Delete");
291*04ae99ecSEd Tanous 
292*04ae99ecSEd Tanous                     BMCWEB_LOG_ERROR << "pamUpdatePassword Failed";
293*04ae99ecSEd Tanous                     return;
294*04ae99ecSEd Tanous                 }
295*04ae99ecSEd Tanous 
296*04ae99ecSEd Tanous                 messages::addMessageToJsonRoot(asyncResp->res.jsonValue,
297*04ae99ecSEd Tanous                                                messages::created());
298*04ae99ecSEd Tanous                 asyncResp->res.addHeader(
299*04ae99ecSEd Tanous                     "Location",
300*04ae99ecSEd Tanous                     "/redfish/v1/AccountService/Accounts/" + username);
301*04ae99ecSEd Tanous             },
302*04ae99ecSEd Tanous             "xyz.openbmc_project.User.Manager", "/xyz/openbmc_project/user",
303*04ae99ecSEd Tanous             "xyz.openbmc_project.User.Manager", "CreateUser", *username,
304*04ae99ecSEd Tanous             std::array<const char*, 4>{"ipmi", "redfish", "ssh", "web"},
305*04ae99ecSEd Tanous             privilege, enabled);
306*04ae99ecSEd Tanous     }
307*04ae99ecSEd Tanous 
308*04ae99ecSEd Tanous     static const char* getRoleIdFromPrivilege(boost::beast::string_view role)
309*04ae99ecSEd Tanous     {
310*04ae99ecSEd Tanous         if (role == "Administrator")
311*04ae99ecSEd Tanous         {
312*04ae99ecSEd Tanous             return "priv-admin";
313*04ae99ecSEd Tanous         }
314*04ae99ecSEd Tanous         else if (role == "Callback")
315*04ae99ecSEd Tanous         {
316*04ae99ecSEd Tanous             return "priv-callback";
317*04ae99ecSEd Tanous         }
318*04ae99ecSEd Tanous         else if (role == "User")
319*04ae99ecSEd Tanous         {
320*04ae99ecSEd Tanous             return "priv-user";
321*04ae99ecSEd Tanous         }
322*04ae99ecSEd Tanous         else if (role == "Operator")
323*04ae99ecSEd Tanous         {
324*04ae99ecSEd Tanous             return "priv-operator";
325*04ae99ecSEd Tanous         }
326*04ae99ecSEd Tanous         return nullptr;
327*04ae99ecSEd Tanous     }
328b9b2e0b2SEd Tanous };
329b9b2e0b2SEd Tanous 
330a840879dSEd Tanous template <typename Callback>
331a840879dSEd Tanous inline void checkDbusPathExists(const std::string& path, Callback&& callback)
332a840879dSEd Tanous {
333a840879dSEd Tanous     using GetObjectType =
334a840879dSEd Tanous         std::vector<std::pair<std::string, std::vector<std::string>>>;
335a840879dSEd Tanous 
336a840879dSEd Tanous     crow::connections::systemBus->async_method_call(
337a840879dSEd Tanous         [callback{std::move(callback)}](const boost::system::error_code ec,
338a840879dSEd Tanous                                         const GetObjectType& object_names) {
339a840879dSEd Tanous             callback(ec || object_names.size() == 0);
340a840879dSEd Tanous         },
341a840879dSEd Tanous         "xyz.openbmc_project.ObjectMapper",
342a840879dSEd Tanous         "/xyz/openbmc_project/object_mapper",
343a840879dSEd Tanous         "xyz.openbmc_project.ObjectMapper", "GetObject", path,
344a840879dSEd Tanous         std::array<std::string, 0>());
345a840879dSEd Tanous }
346a840879dSEd Tanous 
347b9b2e0b2SEd Tanous class ManagerAccount : public Node
348b9b2e0b2SEd Tanous {
349b9b2e0b2SEd Tanous   public:
350b9b2e0b2SEd Tanous     ManagerAccount(CrowApp& app) :
351b9b2e0b2SEd Tanous         Node(app, "/redfish/v1/AccountService/Accounts/<str>/", std::string())
352b9b2e0b2SEd Tanous     {
353b9b2e0b2SEd Tanous         Node::json = {{"@odata.context",
354b9b2e0b2SEd Tanous                        "/redfish/v1/$metadata#ManagerAccount.ManagerAccount"},
355b9b2e0b2SEd Tanous                       {"@odata.type", "#ManagerAccount.v1_0_3.ManagerAccount"},
356b9b2e0b2SEd Tanous 
357b9b2e0b2SEd Tanous                       {"Name", "User Account"},
358b9b2e0b2SEd Tanous                       {"Description", "User Account"},
359b9b2e0b2SEd Tanous                       {"Password", nullptr},
360b9b2e0b2SEd Tanous                       {"RoleId", "Administrator"},
361b9b2e0b2SEd Tanous                       {"Links",
362b9b2e0b2SEd Tanous                        {{"Role",
363b9b2e0b2SEd Tanous                          {{"@odata.id", "/redfish/v1/AccountService/Roles/"
364b9b2e0b2SEd Tanous                                         "Administrator"}}}}}};
365b9b2e0b2SEd Tanous 
366b9b2e0b2SEd Tanous         entityPrivileges = {
367b9b2e0b2SEd Tanous             {boost::beast::http::verb::get,
368b9b2e0b2SEd Tanous              {{"ConfigureUsers"}, {"ConfigureManager"}, {"ConfigureSelf"}}},
369b9b2e0b2SEd Tanous             {boost::beast::http::verb::head, {{"Login"}}},
370b9b2e0b2SEd Tanous             {boost::beast::http::verb::patch, {{"ConfigureUsers"}}},
371b9b2e0b2SEd Tanous             {boost::beast::http::verb::put, {{"ConfigureUsers"}}},
372b9b2e0b2SEd Tanous             {boost::beast::http::verb::delete_, {{"ConfigureUsers"}}},
373b9b2e0b2SEd Tanous             {boost::beast::http::verb::post, {{"ConfigureUsers"}}}};
374b9b2e0b2SEd Tanous     }
375b9b2e0b2SEd Tanous 
376b9b2e0b2SEd Tanous   private:
377b9b2e0b2SEd Tanous     void doGet(crow::Response& res, const crow::Request& req,
378b9b2e0b2SEd Tanous                const std::vector<std::string>& params) override
379b9b2e0b2SEd Tanous     {
380b9b2e0b2SEd Tanous         res.jsonValue = Node::json;
381b9b2e0b2SEd Tanous         auto asyncResp = std::make_shared<AsyncResp>(res);
382b9b2e0b2SEd Tanous 
383b9b2e0b2SEd Tanous         if (params.size() != 1)
384b9b2e0b2SEd Tanous         {
385b9b2e0b2SEd Tanous             res.result(boost::beast::http::status::internal_server_error);
386b9b2e0b2SEd Tanous             return;
387b9b2e0b2SEd Tanous         }
388b9b2e0b2SEd Tanous 
389b9b2e0b2SEd Tanous         crow::connections::systemBus->async_method_call(
390b9b2e0b2SEd Tanous             [asyncResp, accountName{std::string(params[0])}](
391b9b2e0b2SEd Tanous                 const boost::system::error_code ec,
392b9b2e0b2SEd Tanous                 const ManagedObjectType& users) {
393b9b2e0b2SEd Tanous                 if (ec)
394b9b2e0b2SEd Tanous                 {
395b9b2e0b2SEd Tanous                     asyncResp->res.result(
396b9b2e0b2SEd Tanous                         boost::beast::http::status::internal_server_error);
397b9b2e0b2SEd Tanous                     return;
398b9b2e0b2SEd Tanous                 }
399b9b2e0b2SEd Tanous 
400b9b2e0b2SEd Tanous                 for (auto& user : users)
401b9b2e0b2SEd Tanous                 {
402b9b2e0b2SEd Tanous                     const std::string& path =
403b9b2e0b2SEd Tanous                         static_cast<const std::string&>(user.first);
404b9b2e0b2SEd Tanous                     std::size_t lastIndex = path.rfind("/");
405b9b2e0b2SEd Tanous                     if (lastIndex == std::string::npos)
406b9b2e0b2SEd Tanous                     {
407b9b2e0b2SEd Tanous                         lastIndex = 0;
408b9b2e0b2SEd Tanous                     }
409b9b2e0b2SEd Tanous                     else
410b9b2e0b2SEd Tanous                     {
411b9b2e0b2SEd Tanous                         lastIndex += 1;
412b9b2e0b2SEd Tanous                     }
413b9b2e0b2SEd Tanous                     if (path.substr(lastIndex) == accountName)
414b9b2e0b2SEd Tanous                     {
41565b0dc32SEd Tanous                         for (const auto& interface : user.second)
41665b0dc32SEd Tanous                         {
41765b0dc32SEd Tanous                             if (interface.first ==
41865b0dc32SEd Tanous                                 "xyz.openbmc_project.User.Attributes")
41965b0dc32SEd Tanous                             {
42065b0dc32SEd Tanous                                 for (const auto& property : interface.second)
42165b0dc32SEd Tanous                                 {
42265b0dc32SEd Tanous                                     if (property.first == "UserEnabled")
42365b0dc32SEd Tanous                                     {
42465b0dc32SEd Tanous                                         const bool* userEnabled =
42565b0dc32SEd Tanous                                             mapbox::getPtr<const bool>(
42665b0dc32SEd Tanous                                                 property.second);
42765b0dc32SEd Tanous                                         if (userEnabled == nullptr)
42865b0dc32SEd Tanous                                         {
42965b0dc32SEd Tanous                                             BMCWEB_LOG_ERROR
43065b0dc32SEd Tanous                                                 << "UserEnabled wasn't a bool";
43165b0dc32SEd Tanous                                             continue;
43265b0dc32SEd Tanous                                         }
43365b0dc32SEd Tanous                                         asyncResp->res.jsonValue["Enabled"] =
43465b0dc32SEd Tanous                                             *userEnabled;
43565b0dc32SEd Tanous                                     }
43665b0dc32SEd Tanous                                     else if (property.first ==
43765b0dc32SEd Tanous                                              "UserLockedForFailedAttempt")
43865b0dc32SEd Tanous                                     {
43965b0dc32SEd Tanous                                         const bool* userLocked =
44065b0dc32SEd Tanous                                             mapbox::getPtr<const bool>(
44165b0dc32SEd Tanous                                                 property.second);
44265b0dc32SEd Tanous                                         if (userLocked == nullptr)
44365b0dc32SEd Tanous                                         {
44465b0dc32SEd Tanous                                             BMCWEB_LOG_ERROR
44565b0dc32SEd Tanous                                                 << "UserEnabled wasn't a bool";
44665b0dc32SEd Tanous                                             continue;
44765b0dc32SEd Tanous                                         }
44865b0dc32SEd Tanous                                         asyncResp->res.jsonValue["Locked"] =
44965b0dc32SEd Tanous                                             *userLocked;
45065b0dc32SEd Tanous                                     }
45165b0dc32SEd Tanous                                 }
45265b0dc32SEd Tanous                             }
45365b0dc32SEd Tanous                         }
45465b0dc32SEd Tanous 
455b9b2e0b2SEd Tanous                         asyncResp->res.jsonValue["@odata.id"] =
456b9b2e0b2SEd Tanous                             "/redfish/v1/AccountService/Accounts/" +
457b9b2e0b2SEd Tanous                             accountName;
458b9b2e0b2SEd Tanous                         asyncResp->res.jsonValue["Id"] = accountName;
459b9b2e0b2SEd Tanous                         asyncResp->res.jsonValue["UserName"] = accountName;
460b9b2e0b2SEd Tanous 
461b9b2e0b2SEd Tanous                         return;
462b9b2e0b2SEd Tanous                     }
463b9b2e0b2SEd Tanous                 }
464b9b2e0b2SEd Tanous 
465b9b2e0b2SEd Tanous                 asyncResp->res.result(boost::beast::http::status::not_found);
466b9b2e0b2SEd Tanous             },
467b9b2e0b2SEd Tanous             "xyz.openbmc_project.User.Manager", "/xyz/openbmc_project/user",
468b9b2e0b2SEd Tanous             "org.freedesktop.DBus.ObjectManager", "GetManagedObjects");
469b9b2e0b2SEd Tanous     }
470a840879dSEd Tanous 
471a840879dSEd Tanous     void doPatch(crow::Response& res, const crow::Request& req,
472a840879dSEd Tanous                  const std::vector<std::string>& params) override
473a840879dSEd Tanous     {
474a840879dSEd Tanous         auto asyncResp = std::make_shared<AsyncResp>(res);
475a840879dSEd Tanous 
476a840879dSEd Tanous         if (params.size() != 1)
477a840879dSEd Tanous         {
478a840879dSEd Tanous             res.result(boost::beast::http::status::internal_server_error);
479a840879dSEd Tanous             return;
480a840879dSEd Tanous         }
481a840879dSEd Tanous 
482a840879dSEd Tanous         nlohmann::json patchRequest;
483a840879dSEd Tanous         if (!json_util::processJsonFromRequest(res, req, patchRequest))
484a840879dSEd Tanous         {
485a840879dSEd Tanous             return;
486a840879dSEd Tanous         }
487a840879dSEd Tanous 
488a840879dSEd Tanous         // Check the user exists before updating the fields
489a840879dSEd Tanous         checkDbusPathExists(
490a840879dSEd Tanous             "/xyz/openbmc_project/users/" + params[0],
491a840879dSEd Tanous             [username{std::string(params[0])},
492a840879dSEd Tanous              patchRequest(std::move(patchRequest)),
493a840879dSEd Tanous              asyncResp](bool userExists) {
494a840879dSEd Tanous                 if (!userExists)
495a840879dSEd Tanous                 {
496a840879dSEd Tanous                     messages::addMessageToErrorJson(
497a840879dSEd Tanous                         asyncResp->res.jsonValue,
498a840879dSEd Tanous                         messages::resourceNotFound(
499a840879dSEd Tanous                             "#ManagerAccount.v1_0_3.ManagerAccount", username));
500a840879dSEd Tanous 
501a840879dSEd Tanous                     asyncResp->res.result(
502a840879dSEd Tanous                         boost::beast::http::status::not_found);
503a840879dSEd Tanous                     return;
504a840879dSEd Tanous                 }
505a840879dSEd Tanous 
506a840879dSEd Tanous                 for (const auto& item : patchRequest.items())
507a840879dSEd Tanous                 {
508a840879dSEd Tanous                     if (item.key() == "Password")
509a840879dSEd Tanous                     {
510a840879dSEd Tanous                         const std::string* passStr =
511a840879dSEd Tanous                             item.value().get_ptr<const std::string*>();
512a840879dSEd Tanous                         if (passStr == nullptr)
513a840879dSEd Tanous                         {
514a840879dSEd Tanous                             messages::addMessageToErrorJson(
515a840879dSEd Tanous                                 asyncResp->res.jsonValue,
516a840879dSEd Tanous                                 messages::propertyValueFormatError(
517a840879dSEd Tanous                                     item.value().dump(), "Password"));
518a840879dSEd Tanous                             return;
519a840879dSEd Tanous                         }
520a840879dSEd Tanous                         BMCWEB_LOG_DEBUG << "Updating user: " << username
521a840879dSEd Tanous                                          << " to password " << *passStr;
522a840879dSEd Tanous                         if (!pamUpdatePassword(username, *passStr))
523a840879dSEd Tanous                         {
524a840879dSEd Tanous                             BMCWEB_LOG_ERROR << "pamUpdatePassword Failed";
525a840879dSEd Tanous                             asyncResp->res.result(boost::beast::http::status::
526a840879dSEd Tanous                                                       internal_server_error);
527a840879dSEd Tanous                             return;
528a840879dSEd Tanous                         }
529a840879dSEd Tanous                     }
530a840879dSEd Tanous                     else if (item.key() == "Enabled")
531a840879dSEd Tanous                     {
532a840879dSEd Tanous                         const bool* enabledBool =
533a840879dSEd Tanous                             item.value().get_ptr<const bool*>();
534a840879dSEd Tanous 
535a840879dSEd Tanous                         if (enabledBool == nullptr)
536a840879dSEd Tanous                         {
537a840879dSEd Tanous                             messages::addMessageToErrorJson(
538a840879dSEd Tanous                                 asyncResp->res.jsonValue,
539a840879dSEd Tanous                                 messages::propertyValueFormatError(
540a840879dSEd Tanous                                     item.value().dump(), "Enabled"));
541a840879dSEd Tanous                             return;
542a840879dSEd Tanous                         }
543a840879dSEd Tanous                         crow::connections::systemBus->async_method_call(
544a840879dSEd Tanous                             [asyncResp](const boost::system::error_code ec) {
545a840879dSEd Tanous                                 if (ec)
546a840879dSEd Tanous                                 {
547a840879dSEd Tanous                                     BMCWEB_LOG_ERROR
548a840879dSEd Tanous                                         << "D-Bus responses error: " << ec;
549a840879dSEd Tanous                                     asyncResp->res.result(
550a840879dSEd Tanous                                         boost::beast::http::status::
551a840879dSEd Tanous                                             internal_server_error);
552a840879dSEd Tanous                                     return;
553a840879dSEd Tanous                                 }
554a840879dSEd Tanous                                 // TODO Consider support polling mechanism to
555a840879dSEd Tanous                                 // verify status of host and chassis after
556a840879dSEd Tanous                                 // execute the requested action.
557a840879dSEd Tanous                                 BMCWEB_LOG_DEBUG << "Response with no content";
558a840879dSEd Tanous                                 asyncResp->res.result(
559a840879dSEd Tanous                                     boost::beast::http::status::no_content);
560a840879dSEd Tanous                             },
561a840879dSEd Tanous                             "xyz.openbmc_project.User.Manager",
562a840879dSEd Tanous                             "/xyz/openbmc_project/users/" + username,
563a840879dSEd Tanous                             "org.freedesktop.DBus.Properties", "Set",
564a840879dSEd Tanous                             "xyz.openbmc_project.User.Attributes"
565a840879dSEd Tanous                             "UserEnabled",
566a840879dSEd Tanous                             sdbusplus::message::variant<bool>{*enabledBool});
567a840879dSEd Tanous                     }
568a840879dSEd Tanous                     else
569a840879dSEd Tanous                     {
570a840879dSEd Tanous                         messages::addMessageToErrorJson(
571a840879dSEd Tanous                             asyncResp->res.jsonValue,
572a840879dSEd Tanous                             messages::propertyNotWritable(item.key()));
573a840879dSEd Tanous                         asyncResp->res.result(
574a840879dSEd Tanous                             boost::beast::http::status::bad_request);
575a840879dSEd Tanous                         return;
576a840879dSEd Tanous                     }
577a840879dSEd Tanous                 }
578a840879dSEd Tanous             });
579a840879dSEd Tanous     }
58006e086d9SEd Tanous 
58106e086d9SEd Tanous     void doDelete(crow::Response& res, const crow::Request& req,
58206e086d9SEd Tanous                   const std::vector<std::string>& params) override
58306e086d9SEd Tanous     {
58406e086d9SEd Tanous         auto asyncResp = std::make_shared<AsyncResp>(res);
58506e086d9SEd Tanous 
58606e086d9SEd Tanous         if (params.size() != 1)
58706e086d9SEd Tanous         {
58806e086d9SEd Tanous             res.result(boost::beast::http::status::internal_server_error);
58906e086d9SEd Tanous             return;
59006e086d9SEd Tanous         }
59106e086d9SEd Tanous 
59206e086d9SEd Tanous         const std::string userPath = "/xyz/openbmc_project/user/" + params[0];
59306e086d9SEd Tanous 
59406e086d9SEd Tanous         crow::connections::systemBus->async_method_call(
59506e086d9SEd Tanous             [asyncResp, username{std::move(params[0])}](
59606e086d9SEd Tanous                 const boost::system::error_code ec) {
59706e086d9SEd Tanous                 if (ec)
59806e086d9SEd Tanous                 {
59906e086d9SEd Tanous                     messages::addMessageToErrorJson(
60006e086d9SEd Tanous                         asyncResp->res.jsonValue,
60106e086d9SEd Tanous                         messages::resourceNotFound(
60206e086d9SEd Tanous                             "#ManagerAccount.v1_0_3.ManagerAccount", username));
60306e086d9SEd Tanous                     asyncResp->res.result(
60406e086d9SEd Tanous                         boost::beast::http::status::not_found);
60506e086d9SEd Tanous                     return;
60606e086d9SEd Tanous                 }
60706e086d9SEd Tanous 
60806e086d9SEd Tanous                 messages::addMessageToJsonRoot(asyncResp->res.jsonValue,
60906e086d9SEd Tanous                                                messages::accountRemoved());
61006e086d9SEd Tanous             },
61106e086d9SEd Tanous             "xyz.openbmc_project.User.Manager", userPath,
61206e086d9SEd Tanous             "xyz.openbmc_project.Object.Delete", "Delete");
61306e086d9SEd Tanous     }
614b9b2e0b2SEd Tanous };
61588d16c9aSLewanczyk, Dawid 
61688d16c9aSLewanczyk, Dawid } // namespace redfish
617