xref: /openbmc/bmcweb/features/redfish/lib/account_service.hpp (revision 4d64ce347e01fe4aa849e79504024eb98bbdc7aa)
1 /*
2 // Copyright (c) 2018 Intel Corporation
3 //
4 // Licensed under the Apache License, Version 2.0 (the "License");
5 // you may not use this file except in compliance with the License.
6 // You may obtain a copy of the License at
7 //
8 //      http://www.apache.org/licenses/LICENSE-2.0
9 //
10 // Unless required by applicable law or agreed to in writing, software
11 // distributed under the License is distributed on an "AS IS" BASIS,
12 // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 // See the License for the specific language governing permissions and
14 // limitations under the License.
15 */
16 #pragma once
17 #include "node.hpp"
18 
19 #include <dbus_utility.hpp>
20 #include <error_messages.hpp>
21 #include <openbmc_dbus_rest.hpp>
22 #include <utils/json_utils.hpp>
23 #include <variant>
24 
25 namespace redfish
26 {
27 
28 using ManagedObjectType = std::vector<std::pair<
29     sdbusplus::message::object_path,
30     boost::container::flat_map<
31         std::string, boost::container::flat_map<
32                          std::string, std::variant<bool, std::string>>>>>;
33 
34 inline std::string getPrivilegeFromRoleId(boost::beast::string_view role)
35 {
36     if (role == "priv-admin")
37     {
38         return "Administrator";
39     }
40     else if (role == "priv-callback")
41     {
42         return "Callback";
43     }
44     else if (role == "priv-user")
45     {
46         return "User";
47     }
48     else if (role == "priv-operator")
49     {
50         return "Operator";
51     }
52     return "";
53 }
54 inline std::string getRoleIdFromPrivilege(boost::beast::string_view role)
55 {
56     if (role == "Administrator")
57     {
58         return "priv-admin";
59     }
60     else if (role == "Callback")
61     {
62         return "priv-callback";
63     }
64     else if (role == "User")
65     {
66         return "priv-user";
67     }
68     else if (role == "Operator")
69     {
70         return "priv-operator";
71     }
72     return "";
73 }
74 
75 class AccountService : public Node
76 {
77   public:
78     AccountService(CrowApp& app) : Node(app, "/redfish/v1/AccountService/")
79     {
80         entityPrivileges = {
81             {boost::beast::http::verb::get,
82              {{"ConfigureUsers"}, {"ConfigureManager"}}},
83             {boost::beast::http::verb::head, {{"Login"}}},
84             {boost::beast::http::verb::patch, {{"ConfigureUsers"}}},
85             {boost::beast::http::verb::put, {{"ConfigureUsers"}}},
86             {boost::beast::http::verb::delete_, {{"ConfigureUsers"}}},
87             {boost::beast::http::verb::post, {{"ConfigureUsers"}}}};
88     }
89 
90   private:
91     void doGet(crow::Response& res, const crow::Request& req,
92                const std::vector<std::string>& params) override
93     {
94         auto asyncResp = std::make_shared<AsyncResp>(res);
95         res.jsonValue = {
96             {"@odata.context", "/redfish/v1/"
97                                "$metadata#AccountService.AccountService"},
98             {"@odata.id", "/redfish/v1/AccountService"},
99             {"@odata.type", "#AccountService."
100                             "v1_1_0.AccountService"},
101             {"Id", "AccountService"},
102             {"Name", "Account Service"},
103             {"Description", "Account Service"},
104             {"ServiceEnabled", true},
105             {"MaxPasswordLength", 20},
106             {"Accounts",
107              {{"@odata.id", "/redfish/v1/AccountService/Accounts"}}},
108             {"Roles", {{"@odata.id", "/redfish/v1/AccountService/Roles"}}}};
109 
110         crow::connections::systemBus->async_method_call(
111             [asyncResp](
112                 const boost::system::error_code ec,
113                 const std::vector<std::pair<
114                     std::string, std::variant<uint32_t, uint16_t, uint8_t>>>&
115                     propertiesList) {
116                 if (ec)
117                 {
118                     messages::internalError(asyncResp->res);
119                     return;
120                 }
121                 BMCWEB_LOG_DEBUG << "Got " << propertiesList.size()
122                                  << "properties for AccountService";
123                 for (const std::pair<std::string,
124                                      std::variant<uint32_t, uint16_t, uint8_t>>&
125                          property : propertiesList)
126                 {
127                     if (property.first == "MinPasswordLength")
128                     {
129                         const uint8_t* value =
130                             std::get_if<uint8_t>(&property.second);
131                         if (value != nullptr)
132                         {
133                             asyncResp->res.jsonValue["MinPasswordLength"] =
134                                 *value;
135                         }
136                     }
137                     if (property.first == "AccountUnlockTimeout")
138                     {
139                         const uint32_t* value =
140                             std::get_if<uint32_t>(&property.second);
141                         if (value != nullptr)
142                         {
143                             asyncResp->res.jsonValue["AccountLockoutDuration"] =
144                                 *value;
145                         }
146                     }
147                     if (property.first == "MaxLoginAttemptBeforeLockout")
148                     {
149                         const uint16_t* value =
150                             std::get_if<uint16_t>(&property.second);
151                         if (value != nullptr)
152                         {
153                             asyncResp->res
154                                 .jsonValue["AccountLockoutThreshold"] = *value;
155                         }
156                     }
157                 }
158             },
159             "xyz.openbmc_project.User.Manager", "/xyz/openbmc_project/user",
160             "org.freedesktop.DBus.Properties", "GetAll",
161             "xyz.openbmc_project.User.AccountPolicy");
162     }
163     void doPatch(crow::Response& res, const crow::Request& req,
164                  const std::vector<std::string>& params) override
165     {
166         auto asyncResp = std::make_shared<AsyncResp>(res);
167 
168         std::optional<uint32_t> unlockTimeout;
169         std::optional<uint16_t> lockoutThreshold;
170         std::optional<uint16_t> minPasswordLength;
171         std::optional<uint16_t> maxPasswordLength;
172 
173         if (!json_util::readJson(req, res, "AccountLockoutDuration",
174                                  unlockTimeout, "AccountLockoutThreshold",
175                                  lockoutThreshold, "MaxPasswordLength",
176                                  maxPasswordLength, "MinPasswordLength",
177                                  minPasswordLength))
178         {
179             return;
180         }
181 
182         if (minPasswordLength)
183         {
184             messages::propertyNotWritable(asyncResp->res, "MinPasswordLength");
185         }
186 
187         if (maxPasswordLength)
188         {
189             messages::propertyNotWritable(asyncResp->res, "MaxPasswordLength");
190         }
191 
192         if (unlockTimeout)
193         {
194             crow::connections::systemBus->async_method_call(
195                 [asyncResp](const boost::system::error_code ec) {
196                     if (ec)
197                     {
198                         messages::internalError(asyncResp->res);
199                         return;
200                     }
201                     messages::success(asyncResp->res);
202                 },
203                 "xyz.openbmc_project.User.Manager", "/xyz/openbmc_project/user",
204                 "org.freedesktop.DBus.Properties", "Set",
205                 "xyz.openbmc_project.User.AccountPolicy",
206                 "AccountUnlockTimeout", std::variant<uint32_t>(*unlockTimeout));
207         }
208         if (lockoutThreshold)
209         {
210             crow::connections::systemBus->async_method_call(
211                 [asyncResp](const boost::system::error_code ec) {
212                     if (ec)
213                     {
214                         messages::internalError(asyncResp->res);
215                         return;
216                     }
217                     messages::success(asyncResp->res);
218                 },
219                 "xyz.openbmc_project.User.Manager", "/xyz/openbmc_project/user",
220                 "org.freedesktop.DBus.Properties", "Set",
221                 "xyz.openbmc_project.User.AccountPolicy",
222                 "MaxLoginAttemptBeforeLockout",
223                 std::variant<uint16_t>(*lockoutThreshold));
224         }
225     }
226 };
227 
228 class AccountsCollection : public Node
229 {
230   public:
231     AccountsCollection(CrowApp& app) :
232         Node(app, "/redfish/v1/AccountService/Accounts/")
233     {
234         entityPrivileges = {
235             {boost::beast::http::verb::get,
236              {{"ConfigureUsers"}, {"ConfigureManager"}}},
237             {boost::beast::http::verb::head, {{"Login"}}},
238             {boost::beast::http::verb::patch, {{"ConfigureUsers"}}},
239             {boost::beast::http::verb::put, {{"ConfigureUsers"}}},
240             {boost::beast::http::verb::delete_, {{"ConfigureUsers"}}},
241             {boost::beast::http::verb::post, {{"ConfigureUsers"}}}};
242     }
243 
244   private:
245     void doGet(crow::Response& res, const crow::Request& req,
246                const std::vector<std::string>& params) override
247     {
248         auto asyncResp = std::make_shared<AsyncResp>(res);
249         res.jsonValue = {{"@odata.context",
250                           "/redfish/v1/"
251                           "$metadata#ManagerAccountCollection."
252                           "ManagerAccountCollection"},
253                          {"@odata.id", "/redfish/v1/AccountService/Accounts"},
254                          {"@odata.type", "#ManagerAccountCollection."
255                                          "ManagerAccountCollection"},
256                          {"Name", "Accounts Collection"},
257                          {"Description", "BMC User Accounts"}};
258 
259         crow::connections::systemBus->async_method_call(
260             [asyncResp](const boost::system::error_code ec,
261                         const ManagedObjectType& users) {
262                 if (ec)
263                 {
264                     messages::internalError(asyncResp->res);
265                     return;
266                 }
267 
268                 nlohmann::json& memberArray =
269                     asyncResp->res.jsonValue["Members"];
270                 memberArray = nlohmann::json::array();
271 
272                 asyncResp->res.jsonValue["Members@odata.count"] = users.size();
273                 for (auto& user : users)
274                 {
275                     const std::string& path =
276                         static_cast<const std::string&>(user.first);
277                     std::size_t lastIndex = path.rfind("/");
278                     if (lastIndex == std::string::npos)
279                     {
280                         lastIndex = 0;
281                     }
282                     else
283                     {
284                         lastIndex += 1;
285                     }
286                     memberArray.push_back(
287                         {{"@odata.id", "/redfish/v1/AccountService/Accounts/" +
288                                            path.substr(lastIndex)}});
289                 }
290             },
291             "xyz.openbmc_project.User.Manager", "/xyz/openbmc_project/user",
292             "org.freedesktop.DBus.ObjectManager", "GetManagedObjects");
293     }
294     void doPost(crow::Response& res, const crow::Request& req,
295                 const std::vector<std::string>& params) override
296     {
297         auto asyncResp = std::make_shared<AsyncResp>(res);
298 
299         std::string username;
300         std::string password;
301         std::optional<std::string> roleId("User");
302         std::optional<bool> enabled = true;
303         if (!json_util::readJson(req, res, "UserName", username, "Password",
304                                  password, "RoleId", roleId, "Enabled",
305                                  enabled))
306         {
307             return;
308         }
309 
310         std::string priv = getRoleIdFromPrivilege(*roleId);
311         if (priv.empty())
312         {
313             messages::propertyValueNotInList(asyncResp->res, *roleId, "RoleId");
314             return;
315         }
316         roleId = priv;
317 
318         crow::connections::systemBus->async_method_call(
319             [asyncResp, username, password{std::move(password)}](
320                 const boost::system::error_code ec) {
321                 if (ec)
322                 {
323                     messages::resourceAlreadyExists(
324                         asyncResp->res, "#ManagerAccount.v1_0_3.ManagerAccount",
325                         "UserName", username);
326                     return;
327                 }
328 
329                 if (!pamUpdatePassword(username, password))
330                 {
331                     // At this point we have a user that's been created, but the
332                     // password set failed.  Something is wrong, so delete the
333                     // user that we've already created
334                     crow::connections::systemBus->async_method_call(
335                         [asyncResp](const boost::system::error_code ec) {
336                             if (ec)
337                             {
338                                 messages::internalError(asyncResp->res);
339                                 return;
340                             }
341 
342                             messages::invalidObject(asyncResp->res, "Password");
343                         },
344                         "xyz.openbmc_project.User.Manager",
345                         "/xyz/openbmc_project/user/" + username,
346                         "xyz.openbmc_project.Object.Delete", "Delete");
347 
348                     BMCWEB_LOG_ERROR << "pamUpdatePassword Failed";
349                     return;
350                 }
351 
352                 messages::created(asyncResp->res);
353                 asyncResp->res.addHeader(
354                     "Location",
355                     "/redfish/v1/AccountService/Accounts/" + username);
356             },
357             "xyz.openbmc_project.User.Manager", "/xyz/openbmc_project/user",
358             "xyz.openbmc_project.User.Manager", "CreateUser", username,
359             std::array<const char*, 4>{"ipmi", "redfish", "ssh", "web"},
360             *roleId, *enabled);
361     }
362 };
363 
364 template <typename Callback>
365 inline void checkDbusPathExists(const std::string& path, Callback&& callback)
366 {
367     using GetObjectType =
368         std::vector<std::pair<std::string, std::vector<std::string>>>;
369 
370     crow::connections::systemBus->async_method_call(
371         [callback{std::move(callback)}](const boost::system::error_code ec,
372                                         const GetObjectType& object_names) {
373             callback(!ec && object_names.size() != 0);
374         },
375         "xyz.openbmc_project.ObjectMapper",
376         "/xyz/openbmc_project/object_mapper",
377         "xyz.openbmc_project.ObjectMapper", "GetObject", path,
378         std::array<std::string, 0>());
379 }
380 
381 class ManagerAccount : public Node
382 {
383   public:
384     ManagerAccount(CrowApp& app) :
385         Node(app, "/redfish/v1/AccountService/Accounts/<str>/", std::string())
386     {
387         entityPrivileges = {
388             {boost::beast::http::verb::get,
389              {{"ConfigureUsers"}, {"ConfigureManager"}, {"ConfigureSelf"}}},
390             {boost::beast::http::verb::head, {{"Login"}}},
391             {boost::beast::http::verb::patch, {{"ConfigureUsers"}}},
392             {boost::beast::http::verb::put, {{"ConfigureUsers"}}},
393             {boost::beast::http::verb::delete_, {{"ConfigureUsers"}}},
394             {boost::beast::http::verb::post, {{"ConfigureUsers"}}}};
395     }
396 
397   private:
398     void doGet(crow::Response& res, const crow::Request& req,
399                const std::vector<std::string>& params) override
400     {
401         res.jsonValue = {
402             {"@odata.context",
403              "/redfish/v1/$metadata#ManagerAccount.ManagerAccount"},
404             {"@odata.type", "#ManagerAccount.v1_0_3.ManagerAccount"},
405             {"Name", "User Account"},
406             {"Description", "User Account"},
407             {"Password", nullptr},
408             {"RoleId", "Administrator"}};
409 
410         auto asyncResp = std::make_shared<AsyncResp>(res);
411 
412         if (params.size() != 1)
413         {
414             messages::internalError(asyncResp->res);
415             return;
416         }
417 
418         crow::connections::systemBus->async_method_call(
419             [asyncResp, accountName{std::string(params[0])}](
420                 const boost::system::error_code ec,
421                 const ManagedObjectType& users) {
422                 if (ec)
423                 {
424                     messages::internalError(asyncResp->res);
425                     return;
426                 }
427                 auto userIt = users.begin();
428 
429                 for (; userIt != users.end(); userIt++)
430                 {
431                     if (boost::ends_with(userIt->first.str, "/" + accountName))
432                     {
433                         break;
434                     }
435                 }
436                 if (userIt == users.end())
437                 {
438                     messages::resourceNotFound(asyncResp->res, "ManagerAccount",
439                                                accountName);
440                     return;
441                 }
442                 for (const auto& interface : userIt->second)
443                 {
444                     if (interface.first ==
445                         "xyz.openbmc_project.User.Attributes")
446                     {
447                         for (const auto& property : interface.second)
448                         {
449                             if (property.first == "UserEnabled")
450                             {
451                                 const bool* userEnabled =
452                                     std::get_if<bool>(&property.second);
453                                 if (userEnabled == nullptr)
454                                 {
455                                     BMCWEB_LOG_ERROR
456                                         << "UserEnabled wasn't a bool";
457                                     messages::internalError(asyncResp->res);
458                                     return;
459                                 }
460                                 asyncResp->res.jsonValue["Enabled"] =
461                                     *userEnabled;
462                             }
463                             else if (property.first ==
464                                      "UserLockedForFailedAttempt")
465                             {
466                                 const bool* userLocked =
467                                     std::get_if<bool>(&property.second);
468                                 if (userLocked == nullptr)
469                                 {
470                                     BMCWEB_LOG_ERROR << "UserLockedForF"
471                                                         "ailedAttempt "
472                                                         "wasn't a bool";
473                                     messages::internalError(asyncResp->res);
474                                     return;
475                                 }
476                                 asyncResp->res.jsonValue["Locked"] =
477                                     *userLocked;
478                                 asyncResp->res.jsonValue
479                                     ["Locked@Redfish.AllowableValues"] = {
480                                     "false"};
481                             }
482                             else if (property.first == "UserPrivilege")
483                             {
484                                 const std::string* userRolePtr =
485                                     std::get_if<std::string>(&property.second);
486                                 if (userRolePtr == nullptr)
487                                 {
488                                     BMCWEB_LOG_ERROR
489                                         << "UserPrivilege wasn't a "
490                                            "string";
491                                     messages::internalError(asyncResp->res);
492                                     return;
493                                 }
494                                 std::string priv =
495                                     getPrivilegeFromRoleId(*userRolePtr);
496                                 if (priv.empty())
497                                 {
498                                     BMCWEB_LOG_ERROR << "Invalid user role";
499                                     messages::internalError(asyncResp->res);
500                                     return;
501                                 }
502                                 asyncResp->res.jsonValue["RoleId"] = priv;
503 
504                                 asyncResp->res.jsonValue["Links"]["Role"] = {
505                                     {"@odata.id", "/redfish/v1/AccountService/"
506                                                   "Roles/" +
507                                                       priv}};
508                             }
509                         }
510                     }
511                 }
512 
513                 asyncResp->res.jsonValue["@odata.id"] =
514                     "/redfish/v1/AccountService/Accounts/" + accountName;
515                 asyncResp->res.jsonValue["Id"] = accountName;
516                 asyncResp->res.jsonValue["UserName"] = accountName;
517             },
518             "xyz.openbmc_project.User.Manager", "/xyz/openbmc_project/user",
519             "org.freedesktop.DBus.ObjectManager", "GetManagedObjects");
520     }
521 
522     void doPatch(crow::Response& res, const crow::Request& req,
523                  const std::vector<std::string>& params) override
524     {
525         auto asyncResp = std::make_shared<AsyncResp>(res);
526         if (params.size() != 1)
527         {
528             messages::internalError(asyncResp->res);
529             return;
530         }
531 
532         std::optional<std::string> newUserName;
533         std::optional<std::string> password;
534         std::optional<bool> enabled;
535         std::optional<std::string> roleId;
536         std::optional<bool> locked;
537         if (!json_util::readJson(req, res, "UserName", newUserName, "Password",
538                                  password, "RoleId", roleId, "Enabled", enabled,
539                                  "Locked", locked))
540         {
541             return;
542         }
543 
544         const std::string& username = params[0];
545 
546         if (!newUserName)
547         {
548             // If the username isn't being updated, we can update the properties
549             // directly
550             updateUserProperties(asyncResp, username, password, enabled, roleId,
551                                  locked);
552             return;
553         }
554         else
555         {
556             crow::connections::systemBus->async_method_call(
557                 [this, asyncResp, username, password(std::move(password)),
558                  roleId(std::move(roleId)), enabled(std::move(enabled)),
559                  newUser{std::string(*newUserName)}, locked(std::move(locked))](
560                     const boost::system::error_code ec) {
561                     if (ec)
562                     {
563                         BMCWEB_LOG_ERROR << "D-Bus responses error: " << ec;
564                         messages::resourceNotFound(
565                             asyncResp->res,
566                             "#ManagerAccount.v1_0_3.ManagerAccount", username);
567                         return;
568                     }
569 
570                     updateUserProperties(asyncResp, newUser, password, enabled,
571                                          roleId, locked);
572                 },
573                 "xyz.openbmc_project.User.Manager", "/xyz/openbmc_project/user",
574                 "xyz.openbmc_project.User.Manager", "RenameUser", username,
575                 *newUserName);
576         }
577     }
578 
579     void updateUserProperties(std::shared_ptr<AsyncResp> asyncResp,
580                               const std::string& username,
581                               std::optional<std::string> password,
582                               std::optional<bool> enabled,
583                               std::optional<std::string> roleId,
584                               std::optional<bool> locked)
585     {
586         if (password)
587         {
588             if (!pamUpdatePassword(username, *password))
589             {
590                 BMCWEB_LOG_ERROR << "pamUpdatePassword Failed";
591                 messages::internalError(asyncResp->res);
592                 return;
593             }
594         }
595 
596         std::string dbusObjectPath = "/xyz/openbmc_project/user/" + username;
597         dbus::utility::escapePathForDbus(dbusObjectPath);
598 
599         checkDbusPathExists(
600             dbusObjectPath,
601             [dbusObjectPath(std::move(dbusObjectPath)), username,
602              password(std::move(password)), roleId(std::move(roleId)),
603              enabled(std::move(enabled)), locked(std::move(locked)),
604              asyncResp{std::move(asyncResp)}](int rc) {
605                 if (!rc)
606                 {
607                     messages::invalidObject(asyncResp->res, username.c_str());
608                     return;
609                 }
610                 if (enabled)
611                 {
612                     crow::connections::systemBus->async_method_call(
613                         [asyncResp](const boost::system::error_code ec) {
614                             if (ec)
615                             {
616                                 BMCWEB_LOG_ERROR << "D-Bus responses error: "
617                                                  << ec;
618                                 messages::internalError(asyncResp->res);
619                                 return;
620                             }
621                             messages::success(asyncResp->res);
622                             return;
623                         },
624                         "xyz.openbmc_project.User.Manager",
625                         dbusObjectPath.c_str(),
626                         "org.freedesktop.DBus.Properties", "Set",
627                         "xyz.openbmc_project.User.Attributes", "UserEnabled",
628                         std::variant<bool>{*enabled});
629                 }
630 
631                 if (roleId)
632                 {
633                     std::string priv = getRoleIdFromPrivilege(*roleId);
634                     if (priv.empty())
635                     {
636                         messages::propertyValueNotInList(asyncResp->res,
637                                                          *roleId, "RoleId");
638                         return;
639                     }
640 
641                     crow::connections::systemBus->async_method_call(
642                         [asyncResp](const boost::system::error_code ec) {
643                             if (ec)
644                             {
645                                 BMCWEB_LOG_ERROR << "D-Bus responses error: "
646                                                  << ec;
647                                 messages::internalError(asyncResp->res);
648                                 return;
649                             }
650                             messages::success(asyncResp->res);
651                         },
652                         "xyz.openbmc_project.User.Manager",
653                         dbusObjectPath.c_str(),
654                         "org.freedesktop.DBus.Properties", "Set",
655                         "xyz.openbmc_project.User.Attributes", "UserPrivilege",
656                         std::variant<std::string>{priv});
657                 }
658 
659                 if (locked)
660                 {
661                     // admin can unlock the account which is locked by
662                     // successive authentication failures but admin should not
663                     // be allowed to lock an account.
664                     if (*locked)
665                     {
666                         messages::propertyValueNotInList(asyncResp->res, "true",
667                                                          "Locked");
668                         return;
669                     }
670 
671                     crow::connections::systemBus->async_method_call(
672                         [asyncResp](const boost::system::error_code ec) {
673                             if (ec)
674                             {
675                                 BMCWEB_LOG_ERROR << "D-Bus responses error: "
676                                                  << ec;
677                                 messages::internalError(asyncResp->res);
678                                 return;
679                             }
680                             messages::success(asyncResp->res);
681                             return;
682                         },
683                         "xyz.openbmc_project.User.Manager",
684                         dbusObjectPath.c_str(),
685                         "org.freedesktop.DBus.Properties", "Set",
686                         "xyz.openbmc_project.User.Attributes",
687                         "UserLockedForFailedAttempt",
688                         sdbusplus::message::variant<bool>{*locked});
689                 }
690             });
691     }
692 
693     void doDelete(crow::Response& res, const crow::Request& req,
694                   const std::vector<std::string>& params) override
695     {
696         auto asyncResp = std::make_shared<AsyncResp>(res);
697 
698         if (params.size() != 1)
699         {
700             messages::internalError(asyncResp->res);
701             return;
702         }
703 
704         const std::string userPath = "/xyz/openbmc_project/user/" + params[0];
705 
706         crow::connections::systemBus->async_method_call(
707             [asyncResp, username{std::move(params[0])}](
708                 const boost::system::error_code ec) {
709                 if (ec)
710                 {
711                     messages::resourceNotFound(
712                         asyncResp->res, "#ManagerAccount.v1_0_3.ManagerAccount",
713                         username);
714                     return;
715                 }
716 
717                 messages::accountRemoved(asyncResp->res);
718             },
719             "xyz.openbmc_project.User.Manager", userPath,
720             "xyz.openbmc_project.Object.Delete", "Delete");
721     }
722 };
723 
724 } // namespace redfish
725