xref: /openbmc/bmcweb/features/redfish/lib/account_service.hpp (revision d4b5443f2c8bafc6e0ace4115a302734ec3b2c77)
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 constexpr const char* ldapConfigObject =
29     "/xyz/openbmc_project/user/ldap/openldap";
30 constexpr const char* ADConfigObject =
31     "/xyz/openbmc_project/user/ldap/active_directory";
32 
33 constexpr const char* ldapRootObject = "/xyz/openbmc_project/user/ldap";
34 constexpr const char* ldapDbusService = "xyz.openbmc_project.Ldap.Config";
35 constexpr const char* ldapConfigInterface =
36     "xyz.openbmc_project.User.Ldap.Config";
37 constexpr const char* ldapCreateInterface =
38     "xyz.openbmc_project.User.Ldap.Create";
39 constexpr const char* ldapEnableInterface = "xyz.openbmc_project.Object.Enable";
40 constexpr const char* dbusObjManagerIntf = "org.freedesktop.DBus.ObjectManager";
41 constexpr const char* propertyInterface = "org.freedesktop.DBus.Properties";
42 constexpr const char* mapperBusName = "xyz.openbmc_project.ObjectMapper";
43 constexpr const char* mapperObjectPath = "/xyz/openbmc_project/object_mapper";
44 constexpr const char* mapperIntf = "xyz.openbmc_project.ObjectMapper";
45 
46 struct LDAPRoleMapData
47 {
48     std::string groupName;
49     std::string privilege;
50 };
51 
52 struct LDAPConfigData
53 {
54     std::string uri{};
55     std::string bindDN{};
56     std::string baseDN{};
57     std::string searchScope{};
58     std::string serverType{};
59     bool serviceEnabled = false;
60     std::string userNameAttribute{};
61     std::string groupAttribute{};
62     std::vector<std::pair<std::string, LDAPRoleMapData>> groupRoleList;
63 };
64 
65 using ManagedObjectType = std::vector<std::pair<
66     sdbusplus::message::object_path,
67     boost::container::flat_map<
68         std::string, boost::container::flat_map<
69                          std::string, std::variant<bool, std::string>>>>>;
70 using GetObjectType =
71     std::vector<std::pair<std::string, std::vector<std::string>>>;
72 
73 inline std::string getRoleIdFromPrivilege(std::string_view role)
74 {
75     if (role == "priv-admin")
76     {
77         return "Administrator";
78     }
79     else if (role == "priv-callback")
80     {
81         return "Callback";
82     }
83     else if (role == "priv-user")
84     {
85         return "User";
86     }
87     else if (role == "priv-operator")
88     {
89         return "Operator";
90     }
91     return "";
92 }
93 inline std::string getPrivilegeFromRoleId(std::string_view role)
94 {
95     if (role == "Administrator")
96     {
97         return "priv-admin";
98     }
99     else if (role == "Callback")
100     {
101         return "priv-callback";
102     }
103     else if (role == "User")
104     {
105         return "priv-user";
106     }
107     else if (role == "Operator")
108     {
109         return "priv-operator";
110     }
111     return "";
112 }
113 
114 void parseLDAPConfigData(nlohmann::json& json_response,
115                          const LDAPConfigData& confData,
116                          const std::string& ldapType)
117 {
118     std::string service =
119         (ldapType == "LDAP") ? "LDAPService" : "ActiveDirectoryService";
120     nlohmann::json ldap = {
121         {"AccountProviderType", service},
122         {"ServiceEnabled", confData.serviceEnabled},
123         {"ServiceAddresses", nlohmann::json::array({confData.uri})},
124         {"Authentication",
125          {{"AuthenticationType", "UsernameAndPassword"},
126           {"Username", confData.bindDN},
127           {"Password", nullptr}}},
128         {"LDAPService",
129          {{"SearchSettings",
130            {{"BaseDistinguishedNames",
131              nlohmann::json::array({confData.baseDN})},
132             {"UsernameAttribute", confData.userNameAttribute},
133             {"GroupsAttribute", confData.groupAttribute}}}}},
134     };
135 
136     json_response[ldapType].update(std::move(ldap));
137 
138     nlohmann::json& roleMapArray = json_response[ldapType]["RemoteRoleMapping"];
139     roleMapArray = nlohmann::json::array();
140     for (auto& obj : confData.groupRoleList)
141     {
142         BMCWEB_LOG_DEBUG << "Pushing the data groupName="
143                          << obj.second.groupName << "\n";
144         roleMapArray.push_back(
145             {nlohmann::json::array({"RemoteGroup", obj.second.groupName}),
146              nlohmann::json::array(
147                  {"LocalRole", getRoleIdFromPrivilege(obj.second.privilege)})});
148     }
149 }
150 
151 /**
152  * Function that retrieves all properties for LDAP config object
153  * into JSON
154  */
155 template <typename CallbackFunc>
156 inline void getLDAPConfigData(const std::string& ldapType,
157                               CallbackFunc&& callback)
158 {
159 
160     const std::array<const char*, 2> interfaces = {ldapEnableInterface,
161                                                    ldapConfigInterface};
162 
163     crow::connections::systemBus->async_method_call(
164         [callback, ldapType](const boost::system::error_code ec,
165                              const GetObjectType& resp) {
166             LDAPConfigData confData{};
167             if (ec || resp.empty())
168             {
169                 BMCWEB_LOG_ERROR << "DBUS response error during getting of "
170                                     "service name: "
171                                  << ec;
172                 callback(false, confData, ldapType);
173                 return;
174             }
175             std::string service = resp.begin()->first;
176             crow::connections::systemBus->async_method_call(
177                 [callback, ldapType](const boost::system::error_code error_code,
178                                      const ManagedObjectType& ldapObjects) {
179                     LDAPConfigData confData{};
180                     if (error_code)
181                     {
182                         callback(false, confData, ldapType);
183                         BMCWEB_LOG_ERROR << "D-Bus responses error: "
184                                          << error_code;
185                         return;
186                     }
187 
188                     std::string ldapDbusType;
189                     std::string searchString;
190 
191                     if (ldapType == "LDAP")
192                     {
193                         ldapDbusType = "xyz.openbmc_project.User.Ldap.Config."
194                                        "Type.OpenLdap";
195                         searchString = "openldap";
196                     }
197                     else if (ldapType == "ActiveDirectory")
198                     {
199                         ldapDbusType =
200                             "xyz.openbmc_project.User.Ldap.Config.Type."
201                             "ActiveDirectory";
202                         searchString = "active_directory";
203                     }
204                     else
205                     {
206                         BMCWEB_LOG_ERROR
207                             << "Can't get the DbusType for the given type="
208                             << ldapType;
209                         callback(false, confData, ldapType);
210                         return;
211                     }
212 
213                     std::string ldapEnableInterfaceStr = ldapEnableInterface;
214                     std::string ldapConfigInterfaceStr = ldapConfigInterface;
215 
216                     for (const auto& object : ldapObjects)
217                     {
218                         // let's find the object whose ldap type is equal to the
219                         // given type
220                         if (object.first.str.find(searchString) ==
221                             std::string::npos)
222                         {
223                             continue;
224                         }
225 
226                         for (const auto& interface : object.second)
227                         {
228                             if (interface.first == ldapEnableInterfaceStr)
229                             {
230                                 // rest of the properties are string.
231                                 for (const auto& property : interface.second)
232                                 {
233                                     if (property.first == "Enabled")
234                                     {
235                                         const bool* value =
236                                             std::get_if<bool>(&property.second);
237                                         if (value == nullptr)
238                                         {
239                                             continue;
240                                         }
241                                         confData.serviceEnabled = *value;
242                                         break;
243                                     }
244                                 }
245                             }
246                             else if (interface.first == ldapConfigInterfaceStr)
247                             {
248 
249                                 for (const auto& property : interface.second)
250                                 {
251                                     const std::string* value =
252                                         std::get_if<std::string>(
253                                             &property.second);
254                                     if (value == nullptr)
255                                     {
256                                         continue;
257                                     }
258                                     if (property.first == "LDAPServerURI")
259                                     {
260                                         confData.uri = *value;
261                                     }
262                                     else if (property.first == "LDAPBindDN")
263                                     {
264                                         confData.bindDN = *value;
265                                     }
266                                     else if (property.first == "LDAPBaseDN")
267                                     {
268                                         confData.baseDN = *value;
269                                     }
270                                     else if (property.first ==
271                                              "LDAPSearchScope")
272                                     {
273                                         confData.searchScope = *value;
274                                     }
275                                     else if (property.first ==
276                                              "GroupNameAttribute")
277                                     {
278                                         confData.groupAttribute = *value;
279                                     }
280                                     else if (property.first ==
281                                              "UserNameAttribute")
282                                     {
283                                         confData.userNameAttribute = *value;
284                                     }
285                                     else if (property.first == "LDAPType")
286                                     {
287                                         confData.serverType = *value;
288                                     }
289                                 }
290                             }
291                             else if (interface.first ==
292                                      "xyz.openbmc_project.User."
293                                      "PrivilegeMapperEntry")
294                             {
295                                 LDAPRoleMapData roleMapData{};
296                                 for (const auto& property : interface.second)
297                                 {
298                                     const std::string* value =
299                                         std::get_if<std::string>(
300                                             &property.second);
301 
302                                     if (value == nullptr)
303                                     {
304                                         continue;
305                                     }
306 
307                                     if (property.first == "GroupName")
308                                     {
309                                         roleMapData.groupName = *value;
310                                     }
311                                     else if (property.first == "Privilege")
312                                     {
313                                         roleMapData.privilege = *value;
314                                     }
315                                 }
316 
317                                 confData.groupRoleList.push_back(std::make_pair(
318                                     object.first.str, roleMapData));
319                             }
320                         }
321                     }
322                     callback(true, confData, ldapType);
323                 },
324                 service, ldapRootObject, dbusObjManagerIntf,
325                 "GetManagedObjects");
326         },
327         mapperBusName, mapperObjectPath, mapperIntf, "GetObject",
328         ldapConfigObject, interfaces);
329 }
330 
331 class AccountService : public Node
332 {
333   public:
334     AccountService(CrowApp& app) : Node(app, "/redfish/v1/AccountService/")
335     {
336         entityPrivileges = {
337             {boost::beast::http::verb::get,
338              {{"ConfigureUsers"}, {"ConfigureManager"}}},
339             {boost::beast::http::verb::head, {{"Login"}}},
340             {boost::beast::http::verb::patch, {{"ConfigureUsers"}}},
341             {boost::beast::http::verb::put, {{"ConfigureUsers"}}},
342             {boost::beast::http::verb::delete_, {{"ConfigureUsers"}}},
343             {boost::beast::http::verb::post, {{"ConfigureUsers"}}}};
344     }
345 
346   private:
347     /**
348      * @brief parses the authentication section under the LDAP
349      * @param input JSON data
350      * @param asyncResp pointer to the JSON response
351      * @param userName  userName to be filled from the given JSON.
352      * @param password  password to be filled from the given JSON.
353      */
354     void
355         parseLDAPAuthenticationJson(nlohmann::json input,
356                                     const std::shared_ptr<AsyncResp>& asyncResp,
357                                     std::optional<std::string>& username,
358                                     std::optional<std::string>& password)
359     {
360         std::optional<std::string> authType;
361 
362         if (!json_util::readJson(input, asyncResp->res, "AuthenticationType",
363                                  authType, "Username", username, "Password",
364                                  password))
365         {
366             return;
367         }
368         if (!authType)
369         {
370             return;
371         }
372         if (*authType != "UsernameAndPassword")
373         {
374             messages::propertyValueNotInList(asyncResp->res, *authType,
375                                              "AuthenticationType");
376             return;
377         }
378     }
379     /**
380      * @brief parses the LDAPService section under the LDAP
381      * @param input JSON data
382      * @param asyncResp pointer to the JSON response
383      * @param baseDNList baseDN to be filled from the given JSON.
384      * @param userNameAttribute  userName to be filled from the given JSON.
385      * @param groupaAttribute  password to be filled from the given JSON.
386      */
387 
388     void parseLDAPServiceJson(
389         nlohmann::json input, const std::shared_ptr<AsyncResp>& asyncResp,
390         std::optional<std::vector<std::string>>& baseDNList,
391         std::optional<std::string>& userNameAttribute,
392         std::optional<std::string>& groupsAttribute)
393     {
394         std::optional<nlohmann::json> searchSettings;
395 
396         if (!json_util::readJson(input, asyncResp->res, "SearchSettings",
397                                  searchSettings))
398         {
399             return;
400         }
401         if (!searchSettings)
402         {
403             return;
404         }
405         if (!json_util::readJson(*searchSettings, asyncResp->res,
406                                  "BaseDistinguishedNames", baseDNList,
407                                  "UsernameAttribute", userNameAttribute,
408                                  "GroupsAttribute", groupsAttribute))
409         {
410             return;
411         }
412     }
413     /**
414      * @brief updates the LDAP server address and updates the
415               json response with the new value.
416      * @param serviceAddressList address to be updated.
417      * @param asyncResp pointer to the JSON response
418      * @param ldapServerElementName Type of LDAP
419      server(openLDAP/ActiveDirectory)
420      */
421 
422     void handleServiceAddressPatch(
423         const std::vector<std::string>& serviceAddressList,
424         const std::shared_ptr<AsyncResp>& asyncResp,
425         const std::string& ldapServerElementName,
426         const std::string& ldapConfigObject)
427     {
428         crow::connections::systemBus->async_method_call(
429             [asyncResp, ldapServerElementName,
430              serviceAddressList](const boost::system::error_code ec) {
431                 if (ec)
432                 {
433                     BMCWEB_LOG_DEBUG
434                         << "Error Occured in updating the service address";
435                     messages::internalError(asyncResp->res);
436                     return;
437                 }
438                 std::vector<std::string> modifiedserviceAddressList = {
439                     serviceAddressList.front()};
440                 asyncResp->res
441                     .jsonValue[ldapServerElementName]["ServiceAddresses"] =
442                     modifiedserviceAddressList;
443                 if ((serviceAddressList).size() > 1)
444                 {
445                     messages::propertyValueModified(asyncResp->res,
446                                                     "ServiceAddresses",
447                                                     serviceAddressList.front());
448                 }
449                 BMCWEB_LOG_DEBUG << "Updated the service address";
450             },
451             ldapDbusService, ldapConfigObject, propertyInterface, "Set",
452             ldapConfigInterface, "LDAPServerURI",
453             std::variant<std::string>(serviceAddressList.front()));
454     }
455     /**
456      * @brief updates the LDAP Bind DN and updates the
457               json response with the new value.
458      * @param username name of the user which needs to be updated.
459      * @param asyncResp pointer to the JSON response
460      * @param ldapServerElementName Type of LDAP
461      server(openLDAP/ActiveDirectory)
462      */
463 
464     void handleUserNamePatch(const std::string& username,
465                              const std::shared_ptr<AsyncResp>& asyncResp,
466                              const std::string& ldapServerElementName,
467                              const std::string& ldapConfigObject)
468     {
469         crow::connections::systemBus->async_method_call(
470             [asyncResp, username,
471              ldapServerElementName](const boost::system::error_code ec) {
472                 if (ec)
473                 {
474                     BMCWEB_LOG_DEBUG
475                         << "Error occured in updating the username";
476                     messages::internalError(asyncResp->res);
477                     return;
478                 }
479                 asyncResp->res.jsonValue[ldapServerElementName]
480                                         ["Authentication"]["Username"] =
481                     username;
482                 BMCWEB_LOG_DEBUG << "Updated the username";
483             },
484             ldapDbusService, ldapConfigObject, propertyInterface, "Set",
485             ldapConfigInterface, "LDAPBindDN",
486             std::variant<std::string>(username));
487     }
488 
489     /**
490      * @brief updates the LDAP password
491      * @param password : ldap password which needs to be updated.
492      * @param asyncResp pointer to the JSON response
493      * @param ldapServerElementName Type of LDAP
494      *        server(openLDAP/ActiveDirectory)
495      */
496 
497     void handlePasswordPatch(const std::string& password,
498                              const std::shared_ptr<AsyncResp>& asyncResp,
499                              const std::string& ldapServerElementName,
500                              const std::string& ldapConfigObject)
501     {
502         crow::connections::systemBus->async_method_call(
503             [asyncResp, password,
504              ldapServerElementName](const boost::system::error_code ec) {
505                 if (ec)
506                 {
507                     BMCWEB_LOG_DEBUG
508                         << "Error occured in updating the password";
509                     messages::internalError(asyncResp->res);
510                     return;
511                 }
512                 asyncResp->res.jsonValue[ldapServerElementName]
513                                         ["Authentication"]["Password"] = "";
514                 BMCWEB_LOG_DEBUG << "Updated the password";
515             },
516             ldapDbusService, ldapConfigObject, propertyInterface, "Set",
517             ldapConfigInterface, "LDAPBindDNPassword",
518             std::variant<std::string>(password));
519     }
520 
521     /**
522      * @brief updates the LDAP BaseDN and updates the
523               json response with the new value.
524      * @param baseDNList baseDN list which needs to be updated.
525      * @param asyncResp pointer to the JSON response
526      * @param ldapServerElementName Type of LDAP
527      server(openLDAP/ActiveDirectory)
528      */
529 
530     void handleBaseDNPatch(const std::vector<std::string>& baseDNList,
531                            const std::shared_ptr<AsyncResp>& asyncResp,
532                            const std::string& ldapServerElementName,
533                            const std::string& ldapConfigObject)
534     {
535         crow::connections::systemBus->async_method_call(
536             [asyncResp, baseDNList,
537              ldapServerElementName](const boost::system::error_code ec) {
538                 if (ec)
539                 {
540                     BMCWEB_LOG_DEBUG << "Error Occured in Updating the base DN";
541                     messages::internalError(asyncResp->res);
542                     return;
543                 }
544                 auto& serverTypeJson =
545                     asyncResp->res.jsonValue[ldapServerElementName];
546                 auto& searchSettingsJson =
547                     serverTypeJson["LDAPService"]["SearchSettings"];
548                 std::vector<std::string> modifiedBaseDNList = {
549                     baseDNList.front()};
550                 searchSettingsJson["BaseDistinguishedNames"] =
551                     modifiedBaseDNList;
552                 if (baseDNList.size() > 1)
553                 {
554                     messages::propertyValueModified(asyncResp->res,
555                                                     "BaseDistinguishedNames",
556                                                     baseDNList.front());
557                 }
558                 BMCWEB_LOG_DEBUG << "Updated the base DN";
559             },
560             ldapDbusService, ldapConfigObject, propertyInterface, "Set",
561             ldapConfigInterface, "LDAPBaseDN",
562             std::variant<std::string>(baseDNList.front()));
563     }
564     /**
565      * @brief updates the LDAP user name attribute and updates the
566               json response with the new value.
567      * @param userNameAttribute attribute to be updated.
568      * @param asyncResp pointer to the JSON response
569      * @param ldapServerElementName Type of LDAP
570      server(openLDAP/ActiveDirectory)
571      */
572 
573     void handleUserNameAttrPatch(const std::string& userNameAttribute,
574                                  const std::shared_ptr<AsyncResp>& asyncResp,
575                                  const std::string& ldapServerElementName,
576                                  const std::string& ldapConfigObject)
577     {
578         crow::connections::systemBus->async_method_call(
579             [asyncResp, userNameAttribute,
580              ldapServerElementName](const boost::system::error_code ec) {
581                 if (ec)
582                 {
583                     BMCWEB_LOG_DEBUG << "Error Occured in Updating the "
584                                         "username attribute";
585                     messages::internalError(asyncResp->res);
586                     return;
587                 }
588                 auto& serverTypeJson =
589                     asyncResp->res.jsonValue[ldapServerElementName];
590                 auto& searchSettingsJson =
591                     serverTypeJson["LDAPService"]["SearchSettings"];
592                 searchSettingsJson["UsernameAttribute"] = userNameAttribute;
593                 BMCWEB_LOG_DEBUG << "Updated the user name attr.";
594             },
595             ldapDbusService, ldapConfigObject, propertyInterface, "Set",
596             ldapConfigInterface, "UserNameAttribute",
597             std::variant<std::string>(userNameAttribute));
598     }
599     /**
600      * @brief updates the LDAP group attribute and updates the
601               json response with the new value.
602      * @param groupsAttribute attribute to be updated.
603      * @param asyncResp pointer to the JSON response
604      * @param ldapServerElementName Type of LDAP
605      server(openLDAP/ActiveDirectory)
606      */
607 
608     void handleGroupNameAttrPatch(const std::string& groupsAttribute,
609                                   const std::shared_ptr<AsyncResp>& asyncResp,
610                                   const std::string& ldapServerElementName,
611                                   const std::string& ldapConfigObject)
612     {
613         crow::connections::systemBus->async_method_call(
614             [asyncResp, groupsAttribute,
615              ldapServerElementName](const boost::system::error_code ec) {
616                 if (ec)
617                 {
618                     BMCWEB_LOG_DEBUG << "Error Occured in Updating the "
619                                         "groupname attribute";
620                     messages::internalError(asyncResp->res);
621                     return;
622                 }
623                 auto& serverTypeJson =
624                     asyncResp->res.jsonValue[ldapServerElementName];
625                 auto& searchSettingsJson =
626                     serverTypeJson["LDAPService"]["SearchSettings"];
627                 searchSettingsJson["GroupsAttribute"] = groupsAttribute;
628                 BMCWEB_LOG_DEBUG << "Updated the groupname attr";
629             },
630             ldapDbusService, ldapConfigObject, propertyInterface, "Set",
631             ldapConfigInterface, "GroupNameAttribute",
632             std::variant<std::string>(groupsAttribute));
633     }
634     /**
635      * @brief updates the LDAP service enable and updates the
636               json response with the new value.
637      * @param input JSON data.
638      * @param asyncResp pointer to the JSON response
639      * @param ldapServerElementName Type of LDAP
640      server(openLDAP/ActiveDirectory)
641      */
642 
643     void handleServiceEnablePatch(bool serviceEnabled,
644                                   const std::shared_ptr<AsyncResp>& asyncResp,
645                                   const std::string& ldapServerElementName,
646                                   const std::string& ldapConfigObject)
647     {
648         crow::connections::systemBus->async_method_call(
649             [asyncResp, serviceEnabled,
650              ldapServerElementName](const boost::system::error_code ec) {
651                 if (ec)
652                 {
653                     BMCWEB_LOG_DEBUG
654                         << "Error Occured in Updating the service enable";
655                     messages::internalError(asyncResp->res);
656                     return;
657                 }
658                 asyncResp->res
659                     .jsonValue[ldapServerElementName]["ServiceEnabled"] =
660                     serviceEnabled;
661                 BMCWEB_LOG_DEBUG << "Updated Service enable = "
662                                  << serviceEnabled;
663             },
664             ldapDbusService, ldapConfigObject, propertyInterface, "Set",
665             ldapEnableInterface, "Enabled", std::variant<bool>(serviceEnabled));
666     }
667 
668     /**
669      * @brief Get the required values from the given JSON, validates the
670      *        value and create the LDAP config object.
671      * @param input JSON data
672      * @param asyncResp pointer to the JSON response
673      * @param serverType Type of LDAP server(openLDAP/ActiveDirectory)
674      */
675 
676     void handleLDAPPatch(nlohmann::json& input,
677                          const std::shared_ptr<AsyncResp>& asyncResp,
678                          const crow::Request& req,
679                          const std::vector<std::string>& params,
680                          const std::string& serverType)
681     {
682         std::string dbusObjectPath;
683         if (serverType == "ActiveDirectory")
684         {
685             dbusObjectPath = ADConfigObject;
686         }
687         else if (serverType == "LDAP")
688         {
689             dbusObjectPath = ldapConfigObject;
690         }
691 
692         std::optional<nlohmann::json> authentication;
693         std::optional<nlohmann::json> ldapService;
694         std::optional<std::string> accountProviderType;
695         std::optional<std::vector<std::string>> serviceAddressList;
696         std::optional<bool> serviceEnabled;
697         std::optional<std::vector<std::string>> baseDNList;
698         std::optional<std::string> userNameAttribute;
699         std::optional<std::string> groupsAttribute;
700         std::optional<std::string> userName;
701         std::optional<std::string> password;
702 
703         if (!json_util::readJson(input, asyncResp->res, "Authentication",
704                                  authentication, "LDAPService", ldapService,
705                                  "ServiceAddresses", serviceAddressList,
706                                  "AccountProviderType", accountProviderType,
707                                  "ServiceEnabled", serviceEnabled))
708         {
709             return;
710         }
711 
712         if (authentication)
713         {
714             parseLDAPAuthenticationJson(*authentication, asyncResp, userName,
715                                         password);
716         }
717         if (ldapService)
718         {
719             parseLDAPServiceJson(*ldapService, asyncResp, baseDNList,
720                                  userNameAttribute, groupsAttribute);
721         }
722         if (accountProviderType)
723         {
724             messages::propertyNotWritable(asyncResp->res,
725                                           "AccountProviderType");
726         }
727         if (serviceAddressList)
728         {
729             if ((*serviceAddressList).size() == 0)
730             {
731                 messages::propertyValueNotInList(asyncResp->res, "[]",
732                                                  "ServiceAddress");
733                 return;
734             }
735         }
736         if (baseDNList)
737         {
738             if ((*baseDNList).size() == 0)
739             {
740                 messages::propertyValueNotInList(asyncResp->res, "[]",
741                                                  "BaseDistinguishedNames");
742                 return;
743             }
744         }
745 
746         // nothing to update, then return
747         if (!userName && !password && !serviceAddressList && !baseDNList &&
748             !userNameAttribute && !groupsAttribute && !serviceEnabled)
749         {
750             return;
751         }
752 
753         // Get the existing resource first then keep modifying
754         // whenever any property gets updated.
755         getLDAPConfigData(serverType, [this, asyncResp, userName, password,
756                                        baseDNList, userNameAttribute,
757                                        groupsAttribute, accountProviderType,
758                                        serviceAddressList, serviceEnabled,
759                                        dbusObjectPath](
760                                           bool success, LDAPConfigData confData,
761                                           const std::string& serverType) {
762             if (!success)
763             {
764                 messages::internalError(asyncResp->res);
765                 return;
766             }
767             parseLDAPConfigData(asyncResp->res.jsonValue, confData, serverType);
768             if (confData.serviceEnabled)
769             {
770                 // Disable the service first and update the rest of
771                 // the properties.
772                 handleServiceEnablePatch(false, asyncResp, serverType,
773                                          dbusObjectPath);
774             }
775 
776             if (serviceAddressList)
777             {
778                 handleServiceAddressPatch(*serviceAddressList, asyncResp,
779                                           serverType, dbusObjectPath);
780             }
781             if (userName)
782             {
783                 handleUserNamePatch(*userName, asyncResp, serverType,
784                                     dbusObjectPath);
785             }
786             if (password)
787             {
788                 handlePasswordPatch(*password, asyncResp, serverType,
789                                     dbusObjectPath);
790             }
791 
792             if (baseDNList)
793             {
794                 handleBaseDNPatch(*baseDNList, asyncResp, serverType,
795                                   dbusObjectPath);
796             }
797             if (userNameAttribute)
798             {
799                 handleUserNameAttrPatch(*userNameAttribute, asyncResp,
800                                         serverType, dbusObjectPath);
801             }
802             if (groupsAttribute)
803             {
804                 handleGroupNameAttrPatch(*groupsAttribute, asyncResp,
805                                          serverType, dbusObjectPath);
806             }
807             if (serviceEnabled)
808             {
809                 // if user has given the value as true then enable
810                 // the service. if user has given false then no-op
811                 // as service is already stopped.
812                 if (*serviceEnabled)
813                 {
814                     handleServiceEnablePatch(*serviceEnabled, asyncResp,
815                                              serverType, dbusObjectPath);
816                 }
817             }
818             else
819             {
820                 // if user has not given the service enabled value
821                 // then revert it to the same state as it was
822                 // before.
823                 handleServiceEnablePatch(confData.serviceEnabled, asyncResp,
824                                          serverType, dbusObjectPath);
825             }
826         });
827     }
828 
829     void doGet(crow::Response& res, const crow::Request& req,
830                const std::vector<std::string>& params) override
831     {
832         auto asyncResp = std::make_shared<AsyncResp>(res);
833         res.jsonValue = {
834             {"@odata.context", "/redfish/v1/"
835                                "$metadata#AccountService.AccountService"},
836             {"@odata.id", "/redfish/v1/AccountService"},
837             {"@odata.type", "#AccountService."
838                             "v1_4_0.AccountService"},
839             {"Id", "AccountService"},
840             {"Name", "Account Service"},
841             {"Description", "Account Service"},
842             {"ServiceEnabled", true},
843             {"MaxPasswordLength", 20},
844             {"Accounts",
845              {{"@odata.id", "/redfish/v1/AccountService/Accounts"}}},
846             {"Roles", {{"@odata.id", "/redfish/v1/AccountService/Roles"}}},
847             {"LDAP",
848              {{"Certificates",
849                {{"@odata.id",
850                  "/redfish/v1/AccountService/LDAP/Certificates"}}}}}};
851         crow::connections::systemBus->async_method_call(
852             [asyncResp](
853                 const boost::system::error_code ec,
854                 const std::vector<std::pair<
855                     std::string, std::variant<uint32_t, uint16_t, uint8_t>>>&
856                     propertiesList) {
857                 if (ec)
858                 {
859                     messages::internalError(asyncResp->res);
860                     return;
861                 }
862                 BMCWEB_LOG_DEBUG << "Got " << propertiesList.size()
863                                  << "properties for AccountService";
864                 for (const std::pair<std::string,
865                                      std::variant<uint32_t, uint16_t, uint8_t>>&
866                          property : propertiesList)
867                 {
868                     if (property.first == "MinPasswordLength")
869                     {
870                         const uint8_t* value =
871                             std::get_if<uint8_t>(&property.second);
872                         if (value != nullptr)
873                         {
874                             asyncResp->res.jsonValue["MinPasswordLength"] =
875                                 *value;
876                         }
877                     }
878                     if (property.first == "AccountUnlockTimeout")
879                     {
880                         const uint32_t* value =
881                             std::get_if<uint32_t>(&property.second);
882                         if (value != nullptr)
883                         {
884                             asyncResp->res.jsonValue["AccountLockoutDuration"] =
885                                 *value;
886                         }
887                     }
888                     if (property.first == "MaxLoginAttemptBeforeLockout")
889                     {
890                         const uint16_t* value =
891                             std::get_if<uint16_t>(&property.second);
892                         if (value != nullptr)
893                         {
894                             asyncResp->res
895                                 .jsonValue["AccountLockoutThreshold"] = *value;
896                         }
897                     }
898                 }
899             },
900             "xyz.openbmc_project.User.Manager", "/xyz/openbmc_project/user",
901             "org.freedesktop.DBus.Properties", "GetAll",
902             "xyz.openbmc_project.User.AccountPolicy");
903 
904         auto callback = [asyncResp](bool success, LDAPConfigData& confData,
905                                     const std::string& ldapType) {
906             parseLDAPConfigData(asyncResp->res.jsonValue, confData, ldapType);
907         };
908 
909         getLDAPConfigData("LDAP", callback);
910         getLDAPConfigData("ActiveDirectory", callback);
911     }
912 
913     void doPatch(crow::Response& res, const crow::Request& req,
914                  const std::vector<std::string>& params) override
915     {
916         auto asyncResp = std::make_shared<AsyncResp>(res);
917 
918         std::optional<uint32_t> unlockTimeout;
919         std::optional<uint16_t> lockoutThreshold;
920         std::optional<uint16_t> minPasswordLength;
921         std::optional<uint16_t> maxPasswordLength;
922         std::optional<nlohmann::json> ldapObject;
923         std::optional<nlohmann::json> activeDirectoryObject;
924 
925         if (!json_util::readJson(req, res, "AccountLockoutDuration",
926                                  unlockTimeout, "AccountLockoutThreshold",
927                                  lockoutThreshold, "MaxPasswordLength",
928                                  maxPasswordLength, "MinPasswordLength",
929                                  minPasswordLength, "LDAP", ldapObject,
930                                  "ActiveDirectory", activeDirectoryObject))
931         {
932             return;
933         }
934 
935         if (minPasswordLength)
936         {
937             messages::propertyNotWritable(asyncResp->res, "MinPasswordLength");
938         }
939 
940         if (maxPasswordLength)
941         {
942             messages::propertyNotWritable(asyncResp->res, "MaxPasswordLength");
943         }
944 
945         if (ldapObject)
946         {
947             handleLDAPPatch(*ldapObject, asyncResp, req, params, "LDAP");
948         }
949 
950         if (activeDirectoryObject)
951         {
952             handleLDAPPatch(*activeDirectoryObject, asyncResp, req, params,
953                             "ActiveDirectory");
954         }
955 
956         if (unlockTimeout)
957         {
958             crow::connections::systemBus->async_method_call(
959                 [asyncResp](const boost::system::error_code ec) {
960                     if (ec)
961                     {
962                         messages::internalError(asyncResp->res);
963                         return;
964                     }
965                     messages::success(asyncResp->res);
966                 },
967                 "xyz.openbmc_project.User.Manager", "/xyz/openbmc_project/user",
968                 "org.freedesktop.DBus.Properties", "Set",
969                 "xyz.openbmc_project.User.AccountPolicy",
970                 "AccountUnlockTimeout", std::variant<uint32_t>(*unlockTimeout));
971         }
972         if (lockoutThreshold)
973         {
974             crow::connections::systemBus->async_method_call(
975                 [asyncResp](const boost::system::error_code ec) {
976                     if (ec)
977                     {
978                         messages::internalError(asyncResp->res);
979                         return;
980                     }
981                     messages::success(asyncResp->res);
982                 },
983                 "xyz.openbmc_project.User.Manager", "/xyz/openbmc_project/user",
984                 "org.freedesktop.DBus.Properties", "Set",
985                 "xyz.openbmc_project.User.AccountPolicy",
986                 "MaxLoginAttemptBeforeLockout",
987                 std::variant<uint16_t>(*lockoutThreshold));
988         }
989     }
990 };
991 
992 class AccountsCollection : public Node
993 {
994   public:
995     AccountsCollection(CrowApp& app) :
996         Node(app, "/redfish/v1/AccountService/Accounts/")
997     {
998         entityPrivileges = {
999             {boost::beast::http::verb::get,
1000              {{"ConfigureUsers"}, {"ConfigureManager"}}},
1001             {boost::beast::http::verb::head, {{"Login"}}},
1002             {boost::beast::http::verb::patch, {{"ConfigureUsers"}}},
1003             {boost::beast::http::verb::put, {{"ConfigureUsers"}}},
1004             {boost::beast::http::verb::delete_, {{"ConfigureUsers"}}},
1005             {boost::beast::http::verb::post, {{"ConfigureUsers"}}}};
1006     }
1007 
1008   private:
1009     void doGet(crow::Response& res, const crow::Request& req,
1010                const std::vector<std::string>& params) override
1011     {
1012         auto asyncResp = std::make_shared<AsyncResp>(res);
1013         res.jsonValue = {{"@odata.context",
1014                           "/redfish/v1/"
1015                           "$metadata#ManagerAccountCollection."
1016                           "ManagerAccountCollection"},
1017                          {"@odata.id", "/redfish/v1/AccountService/Accounts"},
1018                          {"@odata.type", "#ManagerAccountCollection."
1019                                          "ManagerAccountCollection"},
1020                          {"Name", "Accounts Collection"},
1021                          {"Description", "BMC User Accounts"}};
1022 
1023         crow::connections::systemBus->async_method_call(
1024             [asyncResp](const boost::system::error_code ec,
1025                         const ManagedObjectType& users) {
1026                 if (ec)
1027                 {
1028                     messages::internalError(asyncResp->res);
1029                     return;
1030                 }
1031 
1032                 nlohmann::json& memberArray =
1033                     asyncResp->res.jsonValue["Members"];
1034                 memberArray = nlohmann::json::array();
1035 
1036                 asyncResp->res.jsonValue["Members@odata.count"] = users.size();
1037                 for (auto& user : users)
1038                 {
1039                     const std::string& path =
1040                         static_cast<const std::string&>(user.first);
1041                     std::size_t lastIndex = path.rfind("/");
1042                     if (lastIndex == std::string::npos)
1043                     {
1044                         lastIndex = 0;
1045                     }
1046                     else
1047                     {
1048                         lastIndex += 1;
1049                     }
1050                     memberArray.push_back(
1051                         {{"@odata.id", "/redfish/v1/AccountService/Accounts/" +
1052                                            path.substr(lastIndex)}});
1053                 }
1054             },
1055             "xyz.openbmc_project.User.Manager", "/xyz/openbmc_project/user",
1056             "org.freedesktop.DBus.ObjectManager", "GetManagedObjects");
1057     }
1058     void doPost(crow::Response& res, const crow::Request& req,
1059                 const std::vector<std::string>& params) override
1060     {
1061         auto asyncResp = std::make_shared<AsyncResp>(res);
1062 
1063         std::string username;
1064         std::string password;
1065         std::optional<std::string> roleId("User");
1066         std::optional<bool> enabled = true;
1067         if (!json_util::readJson(req, res, "UserName", username, "Password",
1068                                  password, "RoleId", roleId, "Enabled",
1069                                  enabled))
1070         {
1071             return;
1072         }
1073 
1074         std::string priv = getPrivilegeFromRoleId(*roleId);
1075         if (priv.empty())
1076         {
1077             messages::propertyValueNotInList(asyncResp->res, *roleId, "RoleId");
1078             return;
1079         }
1080         roleId = priv;
1081 
1082         crow::connections::systemBus->async_method_call(
1083             [asyncResp, username, password{std::move(password)}](
1084                 const boost::system::error_code ec) {
1085                 if (ec)
1086                 {
1087                     messages::resourceAlreadyExists(
1088                         asyncResp->res, "#ManagerAccount.v1_0_3.ManagerAccount",
1089                         "UserName", username);
1090                     return;
1091                 }
1092 
1093                 if (!pamUpdatePassword(username, password))
1094                 {
1095                     // At this point we have a user that's been created, but
1096                     // the password set failed.  Something is wrong, so
1097                     // delete the user that we've already created
1098                     crow::connections::systemBus->async_method_call(
1099                         [asyncResp](const boost::system::error_code ec) {
1100                             if (ec)
1101                             {
1102                                 messages::internalError(asyncResp->res);
1103                                 return;
1104                             }
1105 
1106                             messages::invalidObject(asyncResp->res, "Password");
1107                         },
1108                         "xyz.openbmc_project.User.Manager",
1109                         "/xyz/openbmc_project/user/" + username,
1110                         "xyz.openbmc_project.Object.Delete", "Delete");
1111 
1112                     BMCWEB_LOG_ERROR << "pamUpdatePassword Failed";
1113                     return;
1114                 }
1115 
1116                 messages::created(asyncResp->res);
1117                 asyncResp->res.addHeader(
1118                     "Location",
1119                     "/redfish/v1/AccountService/Accounts/" + username);
1120             },
1121             "xyz.openbmc_project.User.Manager", "/xyz/openbmc_project/user",
1122             "xyz.openbmc_project.User.Manager", "CreateUser", username,
1123             std::array<const char*, 4>{"ipmi", "redfish", "ssh", "web"},
1124             *roleId, *enabled);
1125     }
1126 };
1127 
1128 class ManagerAccount : public Node
1129 {
1130   public:
1131     ManagerAccount(CrowApp& app) :
1132         Node(app, "/redfish/v1/AccountService/Accounts/<str>/", std::string())
1133     {
1134         entityPrivileges = {
1135             {boost::beast::http::verb::get,
1136              {{"ConfigureUsers"}, {"ConfigureManager"}, {"ConfigureSelf"}}},
1137             {boost::beast::http::verb::head, {{"Login"}}},
1138             {boost::beast::http::verb::patch, {{"ConfigureUsers"}}},
1139             {boost::beast::http::verb::put, {{"ConfigureUsers"}}},
1140             {boost::beast::http::verb::delete_, {{"ConfigureUsers"}}},
1141             {boost::beast::http::verb::post, {{"ConfigureUsers"}}}};
1142     }
1143 
1144   private:
1145     void doGet(crow::Response& res, const crow::Request& req,
1146                const std::vector<std::string>& params) override
1147     {
1148         res.jsonValue = {
1149             {"@odata.context",
1150              "/redfish/v1/$metadata#ManagerAccount.ManagerAccount"},
1151             {"@odata.type", "#ManagerAccount.v1_0_3.ManagerAccount"},
1152             {"Name", "User Account"},
1153             {"Description", "User Account"},
1154             {"Password", nullptr},
1155             {"RoleId", "Administrator"}};
1156 
1157         auto asyncResp = std::make_shared<AsyncResp>(res);
1158 
1159         if (params.size() != 1)
1160         {
1161             messages::internalError(asyncResp->res);
1162             return;
1163         }
1164 
1165         crow::connections::systemBus->async_method_call(
1166             [asyncResp, accountName{std::string(params[0])}](
1167                 const boost::system::error_code ec,
1168                 const ManagedObjectType& users) {
1169                 if (ec)
1170                 {
1171                     messages::internalError(asyncResp->res);
1172                     return;
1173                 }
1174                 auto userIt = users.begin();
1175 
1176                 for (; userIt != users.end(); userIt++)
1177                 {
1178                     if (boost::ends_with(userIt->first.str, "/" + accountName))
1179                     {
1180                         break;
1181                     }
1182                 }
1183                 if (userIt == users.end())
1184                 {
1185                     messages::resourceNotFound(asyncResp->res, "ManagerAccount",
1186                                                accountName);
1187                     return;
1188                 }
1189                 for (const auto& interface : userIt->second)
1190                 {
1191                     if (interface.first ==
1192                         "xyz.openbmc_project.User.Attributes")
1193                     {
1194                         for (const auto& property : interface.second)
1195                         {
1196                             if (property.first == "UserEnabled")
1197                             {
1198                                 const bool* userEnabled =
1199                                     std::get_if<bool>(&property.second);
1200                                 if (userEnabled == nullptr)
1201                                 {
1202                                     BMCWEB_LOG_ERROR
1203                                         << "UserEnabled wasn't a bool";
1204                                     messages::internalError(asyncResp->res);
1205                                     return;
1206                                 }
1207                                 asyncResp->res.jsonValue["Enabled"] =
1208                                     *userEnabled;
1209                             }
1210                             else if (property.first ==
1211                                      "UserLockedForFailedAttempt")
1212                             {
1213                                 const bool* userLocked =
1214                                     std::get_if<bool>(&property.second);
1215                                 if (userLocked == nullptr)
1216                                 {
1217                                     BMCWEB_LOG_ERROR << "UserLockedForF"
1218                                                         "ailedAttempt "
1219                                                         "wasn't a bool";
1220                                     messages::internalError(asyncResp->res);
1221                                     return;
1222                                 }
1223                                 asyncResp->res.jsonValue["Locked"] =
1224                                     *userLocked;
1225                                 asyncResp->res.jsonValue
1226                                     ["Locked@Redfish.AllowableValues"] = {
1227                                     "false"};
1228                             }
1229                             else if (property.first == "UserPrivilege")
1230                             {
1231                                 const std::string* userPrivPtr =
1232                                     std::get_if<std::string>(&property.second);
1233                                 if (userPrivPtr == nullptr)
1234                                 {
1235                                     BMCWEB_LOG_ERROR
1236                                         << "UserPrivilege wasn't a "
1237                                            "string";
1238                                     messages::internalError(asyncResp->res);
1239                                     return;
1240                                 }
1241                                 std::string role =
1242                                     getRoleIdFromPrivilege(*userPrivPtr);
1243                                 if (role.empty())
1244                                 {
1245                                     BMCWEB_LOG_ERROR << "Invalid user role";
1246                                     messages::internalError(asyncResp->res);
1247                                     return;
1248                                 }
1249                                 asyncResp->res.jsonValue["RoleId"] = role;
1250 
1251                                 asyncResp->res.jsonValue["Links"]["Role"] = {
1252                                     {"@odata.id", "/redfish/v1/AccountService/"
1253                                                   "Roles/" +
1254                                                       role}};
1255                             }
1256                         }
1257                     }
1258                 }
1259 
1260                 asyncResp->res.jsonValue["@odata.id"] =
1261                     "/redfish/v1/AccountService/Accounts/" + accountName;
1262                 asyncResp->res.jsonValue["Id"] = accountName;
1263                 asyncResp->res.jsonValue["UserName"] = accountName;
1264             },
1265             "xyz.openbmc_project.User.Manager", "/xyz/openbmc_project/user",
1266             "org.freedesktop.DBus.ObjectManager", "GetManagedObjects");
1267     }
1268 
1269     void doPatch(crow::Response& res, const crow::Request& req,
1270                  const std::vector<std::string>& params) override
1271     {
1272         auto asyncResp = std::make_shared<AsyncResp>(res);
1273         if (params.size() != 1)
1274         {
1275             messages::internalError(asyncResp->res);
1276             return;
1277         }
1278 
1279         std::optional<std::string> newUserName;
1280         std::optional<std::string> password;
1281         std::optional<bool> enabled;
1282         std::optional<std::string> roleId;
1283         std::optional<bool> locked;
1284         if (!json_util::readJson(req, res, "UserName", newUserName, "Password",
1285                                  password, "RoleId", roleId, "Enabled", enabled,
1286                                  "Locked", locked))
1287         {
1288             return;
1289         }
1290 
1291         const std::string& username = params[0];
1292 
1293         if (!newUserName)
1294         {
1295             // If the username isn't being updated, we can update the
1296             // properties directly
1297             updateUserProperties(asyncResp, username, password, enabled, roleId,
1298                                  locked);
1299             return;
1300         }
1301         else
1302         {
1303             crow::connections::systemBus->async_method_call(
1304                 [this, asyncResp, username, password(std::move(password)),
1305                  roleId(std::move(roleId)), enabled(std::move(enabled)),
1306                  newUser{std::string(*newUserName)}, locked(std::move(locked))](
1307                     const boost::system::error_code ec) {
1308                     if (ec)
1309                     {
1310                         BMCWEB_LOG_ERROR << "D-Bus responses error: " << ec;
1311                         messages::resourceNotFound(
1312                             asyncResp->res,
1313                             "#ManagerAccount.v1_0_3.ManagerAccount", username);
1314                         return;
1315                     }
1316 
1317                     updateUserProperties(asyncResp, newUser, password, enabled,
1318                                          roleId, locked);
1319                 },
1320                 "xyz.openbmc_project.User.Manager", "/xyz/openbmc_project/user",
1321                 "xyz.openbmc_project.User.Manager", "RenameUser", username,
1322                 *newUserName);
1323         }
1324     }
1325 
1326     void updateUserProperties(std::shared_ptr<AsyncResp> asyncResp,
1327                               const std::string& username,
1328                               std::optional<std::string> password,
1329                               std::optional<bool> enabled,
1330                               std::optional<std::string> roleId,
1331                               std::optional<bool> locked)
1332     {
1333         if (password)
1334         {
1335             if (!pamUpdatePassword(username, *password))
1336             {
1337                 BMCWEB_LOG_ERROR << "pamUpdatePassword Failed";
1338                 messages::internalError(asyncResp->res);
1339                 return;
1340             }
1341         }
1342 
1343         std::string dbusObjectPath = "/xyz/openbmc_project/user/" + username;
1344         dbus::utility::escapePathForDbus(dbusObjectPath);
1345 
1346         dbus::utility::checkDbusPathExists(
1347             dbusObjectPath,
1348             [dbusObjectPath(std::move(dbusObjectPath)), username,
1349              password(std::move(password)), roleId(std::move(roleId)),
1350              enabled(std::move(enabled)), locked(std::move(locked)),
1351              asyncResp{std::move(asyncResp)}](int rc) {
1352                 if (!rc)
1353                 {
1354                     messages::invalidObject(asyncResp->res, username.c_str());
1355                     return;
1356                 }
1357                 if (enabled)
1358                 {
1359                     crow::connections::systemBus->async_method_call(
1360                         [asyncResp](const boost::system::error_code ec) {
1361                             if (ec)
1362                             {
1363                                 BMCWEB_LOG_ERROR << "D-Bus responses error: "
1364                                                  << ec;
1365                                 messages::internalError(asyncResp->res);
1366                                 return;
1367                             }
1368                             messages::success(asyncResp->res);
1369                             return;
1370                         },
1371                         "xyz.openbmc_project.User.Manager",
1372                         dbusObjectPath.c_str(),
1373                         "org.freedesktop.DBus.Properties", "Set",
1374                         "xyz.openbmc_project.User.Attributes", "UserEnabled",
1375                         std::variant<bool>{*enabled});
1376                 }
1377 
1378                 if (roleId)
1379                 {
1380                     std::string priv = getPrivilegeFromRoleId(*roleId);
1381                     if (priv.empty())
1382                     {
1383                         messages::propertyValueNotInList(asyncResp->res,
1384                                                          *roleId, "RoleId");
1385                         return;
1386                     }
1387 
1388                     crow::connections::systemBus->async_method_call(
1389                         [asyncResp](const boost::system::error_code ec) {
1390                             if (ec)
1391                             {
1392                                 BMCWEB_LOG_ERROR << "D-Bus responses error: "
1393                                                  << ec;
1394                                 messages::internalError(asyncResp->res);
1395                                 return;
1396                             }
1397                             messages::success(asyncResp->res);
1398                         },
1399                         "xyz.openbmc_project.User.Manager",
1400                         dbusObjectPath.c_str(),
1401                         "org.freedesktop.DBus.Properties", "Set",
1402                         "xyz.openbmc_project.User.Attributes", "UserPrivilege",
1403                         std::variant<std::string>{priv});
1404                 }
1405 
1406                 if (locked)
1407                 {
1408                     // admin can unlock the account which is locked by
1409                     // successive authentication failures but admin should
1410                     // not be allowed to lock an account.
1411                     if (*locked)
1412                     {
1413                         messages::propertyValueNotInList(asyncResp->res, "true",
1414                                                          "Locked");
1415                         return;
1416                     }
1417 
1418                     crow::connections::systemBus->async_method_call(
1419                         [asyncResp](const boost::system::error_code ec) {
1420                             if (ec)
1421                             {
1422                                 BMCWEB_LOG_ERROR << "D-Bus responses error: "
1423                                                  << ec;
1424                                 messages::internalError(asyncResp->res);
1425                                 return;
1426                             }
1427                             messages::success(asyncResp->res);
1428                             return;
1429                         },
1430                         "xyz.openbmc_project.User.Manager",
1431                         dbusObjectPath.c_str(),
1432                         "org.freedesktop.DBus.Properties", "Set",
1433                         "xyz.openbmc_project.User.Attributes",
1434                         "UserLockedForFailedAttempt",
1435                         sdbusplus::message::variant<bool>{*locked});
1436                 }
1437             });
1438     }
1439 
1440     void doDelete(crow::Response& res, const crow::Request& req,
1441                   const std::vector<std::string>& params) override
1442     {
1443         auto asyncResp = std::make_shared<AsyncResp>(res);
1444 
1445         if (params.size() != 1)
1446         {
1447             messages::internalError(asyncResp->res);
1448             return;
1449         }
1450 
1451         const std::string userPath = "/xyz/openbmc_project/user/" + params[0];
1452 
1453         crow::connections::systemBus->async_method_call(
1454             [asyncResp, username{std::move(params[0])}](
1455                 const boost::system::error_code ec) {
1456                 if (ec)
1457                 {
1458                     messages::resourceNotFound(
1459                         asyncResp->res, "#ManagerAccount.v1_0_3.ManagerAccount",
1460                         username);
1461                     return;
1462                 }
1463 
1464                 messages::accountRemoved(asyncResp->res);
1465             },
1466             "xyz.openbmc_project.User.Manager", userPath,
1467             "xyz.openbmc_project.Object.Delete", "Delete");
1468     }
1469 };
1470 
1471 } // namespace redfish
1472