xref: /openbmc/bmcweb/features/redfish/lib/account_service.hpp (revision 599c71d8dcc62cf6244abdf48c93c5f76a0ddc7d)
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         // Reading AllGroups property
1083         crow::connections::systemBus->async_method_call(
1084             [asyncResp, username, password{std::move(password)}, roleId,
1085              enabled](const boost::system::error_code ec,
1086                       const std::variant<std::vector<std::string>>& allGroups) {
1087                 if (ec)
1088                 {
1089                     BMCWEB_LOG_DEBUG << "ERROR with async_method_call";
1090                     messages::internalError(asyncResp->res);
1091                     return;
1092                 }
1093 
1094                 const std::vector<std::string>* allGroupsList =
1095                     std::get_if<std::vector<std::string>>(&allGroups);
1096 
1097                 if (allGroupsList == nullptr || allGroupsList->empty())
1098                 {
1099                     messages::internalError(asyncResp->res);
1100                     return;
1101                 }
1102 
1103                 crow::connections::systemBus->async_method_call(
1104                     [asyncResp, username, password{std::move(password)}](
1105                         const boost::system::error_code ec) {
1106                         if (ec)
1107                         {
1108                             messages::resourceAlreadyExists(
1109                                 asyncResp->res,
1110                                 "#ManagerAccount.v1_0_3.ManagerAccount",
1111                                 "UserName", username);
1112                             return;
1113                         }
1114 
1115                         if (!pamUpdatePassword(username, password))
1116                         {
1117                             // At this point we have a user that's been created,
1118                             // but the password set failed.Something is wrong,
1119                             // so delete the user that we've already created
1120                             crow::connections::systemBus->async_method_call(
1121                                 [asyncResp](
1122                                     const boost::system::error_code ec) {
1123                                     if (ec)
1124                                     {
1125                                         messages::internalError(asyncResp->res);
1126                                         return;
1127                                     }
1128 
1129                                     messages::invalidObject(asyncResp->res,
1130                                                             "Password");
1131                                 },
1132                                 "xyz.openbmc_project.User.Manager",
1133                                 "/xyz/openbmc_project/user/" + username,
1134                                 "xyz.openbmc_project.Object.Delete", "Delete");
1135 
1136                             BMCWEB_LOG_ERROR << "pamUpdatePassword Failed";
1137                             return;
1138                         }
1139 
1140                         messages::created(asyncResp->res);
1141                         asyncResp->res.addHeader(
1142                             "Location",
1143                             "/redfish/v1/AccountService/Accounts/" + username);
1144                     },
1145                     "xyz.openbmc_project.User.Manager",
1146                     "/xyz/openbmc_project/user",
1147                     "xyz.openbmc_project.User.Manager", "CreateUser", username,
1148                     *allGroupsList, *roleId, *enabled);
1149             },
1150             "xyz.openbmc_project.User.Manager", "/xyz/openbmc_project/user",
1151             "org.freedesktop.DBus.Properties", "Get",
1152             "xyz.openbmc_project.User.Manager", "AllGroups");
1153     }
1154 };
1155 
1156 class ManagerAccount : public Node
1157 {
1158   public:
1159     ManagerAccount(CrowApp& app) :
1160         Node(app, "/redfish/v1/AccountService/Accounts/<str>/", std::string())
1161     {
1162         entityPrivileges = {
1163             {boost::beast::http::verb::get,
1164              {{"ConfigureUsers"}, {"ConfigureManager"}, {"ConfigureSelf"}}},
1165             {boost::beast::http::verb::head, {{"Login"}}},
1166             {boost::beast::http::verb::patch, {{"ConfigureUsers"}}},
1167             {boost::beast::http::verb::put, {{"ConfigureUsers"}}},
1168             {boost::beast::http::verb::delete_, {{"ConfigureUsers"}}},
1169             {boost::beast::http::verb::post, {{"ConfigureUsers"}}}};
1170     }
1171 
1172   private:
1173     void doGet(crow::Response& res, const crow::Request& req,
1174                const std::vector<std::string>& params) override
1175     {
1176         res.jsonValue = {
1177             {"@odata.context",
1178              "/redfish/v1/$metadata#ManagerAccount.ManagerAccount"},
1179             {"@odata.type", "#ManagerAccount.v1_0_3.ManagerAccount"},
1180             {"Name", "User Account"},
1181             {"Description", "User Account"},
1182             {"Password", nullptr},
1183             {"RoleId", "Administrator"}};
1184 
1185         auto asyncResp = std::make_shared<AsyncResp>(res);
1186 
1187         if (params.size() != 1)
1188         {
1189             messages::internalError(asyncResp->res);
1190             return;
1191         }
1192 
1193         crow::connections::systemBus->async_method_call(
1194             [asyncResp, accountName{std::string(params[0])}](
1195                 const boost::system::error_code ec,
1196                 const ManagedObjectType& users) {
1197                 if (ec)
1198                 {
1199                     messages::internalError(asyncResp->res);
1200                     return;
1201                 }
1202                 auto userIt = users.begin();
1203 
1204                 for (; userIt != users.end(); userIt++)
1205                 {
1206                     if (boost::ends_with(userIt->first.str, "/" + accountName))
1207                     {
1208                         break;
1209                     }
1210                 }
1211                 if (userIt == users.end())
1212                 {
1213                     messages::resourceNotFound(asyncResp->res, "ManagerAccount",
1214                                                accountName);
1215                     return;
1216                 }
1217                 for (const auto& interface : userIt->second)
1218                 {
1219                     if (interface.first ==
1220                         "xyz.openbmc_project.User.Attributes")
1221                     {
1222                         for (const auto& property : interface.second)
1223                         {
1224                             if (property.first == "UserEnabled")
1225                             {
1226                                 const bool* userEnabled =
1227                                     std::get_if<bool>(&property.second);
1228                                 if (userEnabled == nullptr)
1229                                 {
1230                                     BMCWEB_LOG_ERROR
1231                                         << "UserEnabled wasn't a bool";
1232                                     messages::internalError(asyncResp->res);
1233                                     return;
1234                                 }
1235                                 asyncResp->res.jsonValue["Enabled"] =
1236                                     *userEnabled;
1237                             }
1238                             else if (property.first ==
1239                                      "UserLockedForFailedAttempt")
1240                             {
1241                                 const bool* userLocked =
1242                                     std::get_if<bool>(&property.second);
1243                                 if (userLocked == nullptr)
1244                                 {
1245                                     BMCWEB_LOG_ERROR << "UserLockedForF"
1246                                                         "ailedAttempt "
1247                                                         "wasn't a bool";
1248                                     messages::internalError(asyncResp->res);
1249                                     return;
1250                                 }
1251                                 asyncResp->res.jsonValue["Locked"] =
1252                                     *userLocked;
1253                                 asyncResp->res.jsonValue
1254                                     ["Locked@Redfish.AllowableValues"] = {
1255                                     "false"};
1256                             }
1257                             else if (property.first == "UserPrivilege")
1258                             {
1259                                 const std::string* userPrivPtr =
1260                                     std::get_if<std::string>(&property.second);
1261                                 if (userPrivPtr == nullptr)
1262                                 {
1263                                     BMCWEB_LOG_ERROR
1264                                         << "UserPrivilege wasn't a "
1265                                            "string";
1266                                     messages::internalError(asyncResp->res);
1267                                     return;
1268                                 }
1269                                 std::string role =
1270                                     getRoleIdFromPrivilege(*userPrivPtr);
1271                                 if (role.empty())
1272                                 {
1273                                     BMCWEB_LOG_ERROR << "Invalid user role";
1274                                     messages::internalError(asyncResp->res);
1275                                     return;
1276                                 }
1277                                 asyncResp->res.jsonValue["RoleId"] = role;
1278 
1279                                 asyncResp->res.jsonValue["Links"]["Role"] = {
1280                                     {"@odata.id", "/redfish/v1/AccountService/"
1281                                                   "Roles/" +
1282                                                       role}};
1283                             }
1284                         }
1285                     }
1286                 }
1287 
1288                 asyncResp->res.jsonValue["@odata.id"] =
1289                     "/redfish/v1/AccountService/Accounts/" + accountName;
1290                 asyncResp->res.jsonValue["Id"] = accountName;
1291                 asyncResp->res.jsonValue["UserName"] = accountName;
1292             },
1293             "xyz.openbmc_project.User.Manager", "/xyz/openbmc_project/user",
1294             "org.freedesktop.DBus.ObjectManager", "GetManagedObjects");
1295     }
1296 
1297     void doPatch(crow::Response& res, const crow::Request& req,
1298                  const std::vector<std::string>& params) override
1299     {
1300         auto asyncResp = std::make_shared<AsyncResp>(res);
1301         if (params.size() != 1)
1302         {
1303             messages::internalError(asyncResp->res);
1304             return;
1305         }
1306 
1307         std::optional<std::string> newUserName;
1308         std::optional<std::string> password;
1309         std::optional<bool> enabled;
1310         std::optional<std::string> roleId;
1311         std::optional<bool> locked;
1312         if (!json_util::readJson(req, res, "UserName", newUserName, "Password",
1313                                  password, "RoleId", roleId, "Enabled", enabled,
1314                                  "Locked", locked))
1315         {
1316             return;
1317         }
1318 
1319         const std::string& username = params[0];
1320 
1321         if (!newUserName)
1322         {
1323             // If the username isn't being updated, we can update the
1324             // properties directly
1325             updateUserProperties(asyncResp, username, password, enabled, roleId,
1326                                  locked);
1327             return;
1328         }
1329         else
1330         {
1331             crow::connections::systemBus->async_method_call(
1332                 [this, asyncResp, username, password(std::move(password)),
1333                  roleId(std::move(roleId)), enabled(std::move(enabled)),
1334                  newUser{std::string(*newUserName)}, locked(std::move(locked))](
1335                     const boost::system::error_code ec) {
1336                     if (ec)
1337                     {
1338                         BMCWEB_LOG_ERROR << "D-Bus responses error: " << ec;
1339                         messages::resourceNotFound(
1340                             asyncResp->res,
1341                             "#ManagerAccount.v1_0_3.ManagerAccount", username);
1342                         return;
1343                     }
1344 
1345                     updateUserProperties(asyncResp, newUser, password, enabled,
1346                                          roleId, locked);
1347                 },
1348                 "xyz.openbmc_project.User.Manager", "/xyz/openbmc_project/user",
1349                 "xyz.openbmc_project.User.Manager", "RenameUser", username,
1350                 *newUserName);
1351         }
1352     }
1353 
1354     void updateUserProperties(std::shared_ptr<AsyncResp> asyncResp,
1355                               const std::string& username,
1356                               std::optional<std::string> password,
1357                               std::optional<bool> enabled,
1358                               std::optional<std::string> roleId,
1359                               std::optional<bool> locked)
1360     {
1361         if (password)
1362         {
1363             if (!pamUpdatePassword(username, *password))
1364             {
1365                 BMCWEB_LOG_ERROR << "pamUpdatePassword Failed";
1366                 messages::internalError(asyncResp->res);
1367                 return;
1368             }
1369         }
1370 
1371         std::string dbusObjectPath = "/xyz/openbmc_project/user/" + username;
1372         dbus::utility::escapePathForDbus(dbusObjectPath);
1373 
1374         dbus::utility::checkDbusPathExists(
1375             dbusObjectPath,
1376             [dbusObjectPath(std::move(dbusObjectPath)), username,
1377              password(std::move(password)), roleId(std::move(roleId)),
1378              enabled(std::move(enabled)), locked(std::move(locked)),
1379              asyncResp{std::move(asyncResp)}](int rc) {
1380                 if (!rc)
1381                 {
1382                     messages::invalidObject(asyncResp->res, username.c_str());
1383                     return;
1384                 }
1385                 if (enabled)
1386                 {
1387                     crow::connections::systemBus->async_method_call(
1388                         [asyncResp](const boost::system::error_code ec) {
1389                             if (ec)
1390                             {
1391                                 BMCWEB_LOG_ERROR << "D-Bus responses error: "
1392                                                  << ec;
1393                                 messages::internalError(asyncResp->res);
1394                                 return;
1395                             }
1396                             messages::success(asyncResp->res);
1397                             return;
1398                         },
1399                         "xyz.openbmc_project.User.Manager",
1400                         dbusObjectPath.c_str(),
1401                         "org.freedesktop.DBus.Properties", "Set",
1402                         "xyz.openbmc_project.User.Attributes", "UserEnabled",
1403                         std::variant<bool>{*enabled});
1404                 }
1405 
1406                 if (roleId)
1407                 {
1408                     std::string priv = getPrivilegeFromRoleId(*roleId);
1409                     if (priv.empty())
1410                     {
1411                         messages::propertyValueNotInList(asyncResp->res,
1412                                                          *roleId, "RoleId");
1413                         return;
1414                     }
1415 
1416                     crow::connections::systemBus->async_method_call(
1417                         [asyncResp](const boost::system::error_code ec) {
1418                             if (ec)
1419                             {
1420                                 BMCWEB_LOG_ERROR << "D-Bus responses error: "
1421                                                  << ec;
1422                                 messages::internalError(asyncResp->res);
1423                                 return;
1424                             }
1425                             messages::success(asyncResp->res);
1426                         },
1427                         "xyz.openbmc_project.User.Manager",
1428                         dbusObjectPath.c_str(),
1429                         "org.freedesktop.DBus.Properties", "Set",
1430                         "xyz.openbmc_project.User.Attributes", "UserPrivilege",
1431                         std::variant<std::string>{priv});
1432                 }
1433 
1434                 if (locked)
1435                 {
1436                     // admin can unlock the account which is locked by
1437                     // successive authentication failures but admin should
1438                     // not be allowed to lock an account.
1439                     if (*locked)
1440                     {
1441                         messages::propertyValueNotInList(asyncResp->res, "true",
1442                                                          "Locked");
1443                         return;
1444                     }
1445 
1446                     crow::connections::systemBus->async_method_call(
1447                         [asyncResp](const boost::system::error_code ec) {
1448                             if (ec)
1449                             {
1450                                 BMCWEB_LOG_ERROR << "D-Bus responses error: "
1451                                                  << ec;
1452                                 messages::internalError(asyncResp->res);
1453                                 return;
1454                             }
1455                             messages::success(asyncResp->res);
1456                             return;
1457                         },
1458                         "xyz.openbmc_project.User.Manager",
1459                         dbusObjectPath.c_str(),
1460                         "org.freedesktop.DBus.Properties", "Set",
1461                         "xyz.openbmc_project.User.Attributes",
1462                         "UserLockedForFailedAttempt",
1463                         sdbusplus::message::variant<bool>{*locked});
1464                 }
1465             });
1466     }
1467 
1468     void doDelete(crow::Response& res, const crow::Request& req,
1469                   const std::vector<std::string>& params) override
1470     {
1471         auto asyncResp = std::make_shared<AsyncResp>(res);
1472 
1473         if (params.size() != 1)
1474         {
1475             messages::internalError(asyncResp->res);
1476             return;
1477         }
1478 
1479         const std::string userPath = "/xyz/openbmc_project/user/" + params[0];
1480 
1481         crow::connections::systemBus->async_method_call(
1482             [asyncResp, username{std::move(params[0])}](
1483                 const boost::system::error_code ec) {
1484                 if (ec)
1485                 {
1486                     messages::resourceNotFound(
1487                         asyncResp->res, "#ManagerAccount.v1_0_3.ManagerAccount",
1488                         username);
1489                     return;
1490                 }
1491 
1492                 messages::accountRemoved(asyncResp->res);
1493             },
1494             "xyz.openbmc_project.User.Manager", userPath,
1495             "xyz.openbmc_project.Object.Delete", "Delete");
1496     }
1497 };
1498 
1499 } // namespace redfish
1500