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 
18 #include <app.hpp>
19 #include <dbus_utility.hpp>
20 #include <error_messages.hpp>
21 #include <openbmc_dbus_rest.hpp>
22 #include <persistent_data.hpp>
23 #include <query.hpp>
24 #include <registries/privilege_registry.hpp>
25 #include <sdbusplus/asio/property.hpp>
26 #include <utils/json_utils.hpp>
27 
28 namespace redfish
29 {
30 
31 constexpr const char* ldapConfigObjectName =
32     "/xyz/openbmc_project/user/ldap/openldap";
33 constexpr const char* adConfigObject =
34     "/xyz/openbmc_project/user/ldap/active_directory";
35 
36 constexpr const char* rootUserDbusPath = "/xyz/openbmc_project/user/";
37 constexpr const char* ldapRootObject = "/xyz/openbmc_project/user/ldap";
38 constexpr const char* ldapDbusService = "xyz.openbmc_project.Ldap.Config";
39 constexpr const char* ldapConfigInterface =
40     "xyz.openbmc_project.User.Ldap.Config";
41 constexpr const char* ldapCreateInterface =
42     "xyz.openbmc_project.User.Ldap.Create";
43 constexpr const char* ldapEnableInterface = "xyz.openbmc_project.Object.Enable";
44 constexpr const char* ldapPrivMapperInterface =
45     "xyz.openbmc_project.User.PrivilegeMapper";
46 constexpr const char* dbusObjManagerIntf = "org.freedesktop.DBus.ObjectManager";
47 constexpr const char* propertyInterface = "org.freedesktop.DBus.Properties";
48 constexpr const char* mapperBusName = "xyz.openbmc_project.ObjectMapper";
49 constexpr const char* mapperObjectPath = "/xyz/openbmc_project/object_mapper";
50 constexpr const char* mapperIntf = "xyz.openbmc_project.ObjectMapper";
51 
52 struct LDAPRoleMapData
53 {
54     std::string groupName;
55     std::string privilege;
56 };
57 
58 struct LDAPConfigData
59 {
60     std::string uri{};
61     std::string bindDN{};
62     std::string baseDN{};
63     std::string searchScope{};
64     std::string serverType{};
65     bool serviceEnabled = false;
66     std::string userNameAttribute{};
67     std::string groupAttribute{};
68     std::vector<std::pair<std::string, LDAPRoleMapData>> groupRoleList;
69 };
70 
71 inline std::string getRoleIdFromPrivilege(std::string_view role)
72 {
73     if (role == "priv-admin")
74     {
75         return "Administrator";
76     }
77     if (role == "priv-user")
78     {
79         return "ReadOnly";
80     }
81     if (role == "priv-operator")
82     {
83         return "Operator";
84     }
85     if (role.empty() || (role == "priv-noaccess"))
86     {
87         return "NoAccess";
88     }
89     return "";
90 }
91 inline std::string getPrivilegeFromRoleId(std::string_view role)
92 {
93     if (role == "Administrator")
94     {
95         return "priv-admin";
96     }
97     if (role == "ReadOnly")
98     {
99         return "priv-user";
100     }
101     if (role == "Operator")
102     {
103         return "priv-operator";
104     }
105     if ((role == "NoAccess") || (role.empty()))
106     {
107         return "priv-noaccess";
108     }
109     return "";
110 }
111 
112 inline void userErrorMessageHandler(
113     const sd_bus_error* e, const std::shared_ptr<bmcweb::AsyncResp>& asyncResp,
114     const std::string& newUser, const std::string& username)
115 {
116     if (e == nullptr)
117     {
118         messages::internalError(asyncResp->res);
119         return;
120     }
121 
122     const char* errorMessage = e->name;
123     if (strcmp(errorMessage,
124                "xyz.openbmc_project.User.Common.Error.UserNameExists") == 0)
125     {
126         messages::resourceAlreadyExists(asyncResp->res,
127                                         "#ManagerAccount.v1_4_0.ManagerAccount",
128                                         "UserName", newUser);
129     }
130     else if (strcmp(errorMessage, "xyz.openbmc_project.User.Common.Error."
131                                   "UserNameDoesNotExist") == 0)
132     {
133         messages::resourceNotFound(
134             asyncResp->res, "#ManagerAccount.v1_4_0.ManagerAccount", username);
135     }
136     else if ((strcmp(errorMessage,
137                      "xyz.openbmc_project.Common.Error.InvalidArgument") ==
138               0) ||
139              (strcmp(
140                   errorMessage,
141                   "xyz.openbmc_project.User.Common.Error.UserNameGroupFail") ==
142               0))
143     {
144         messages::propertyValueFormatError(asyncResp->res, newUser, "UserName");
145     }
146     else if (strcmp(errorMessage,
147                     "xyz.openbmc_project.User.Common.Error.NoResource") == 0)
148     {
149         messages::createLimitReachedForResource(asyncResp->res);
150     }
151     else
152     {
153         messages::internalError(asyncResp->res);
154     }
155 }
156 
157 inline void parseLDAPConfigData(nlohmann::json& jsonResponse,
158                                 const LDAPConfigData& confData,
159                                 const std::string& ldapType)
160 {
161     std::string service =
162         (ldapType == "LDAP") ? "LDAPService" : "ActiveDirectoryService";
163 
164     nlohmann::json& ldap = jsonResponse[ldapType];
165 
166     ldap["ServiceEnabled"] = confData.serviceEnabled;
167     ldap["ServiceAddresses"] = nlohmann::json::array({confData.uri});
168     ldap["Authentication"]["AuthenticationType"] = "UsernameAndPassword";
169     ldap["Authentication"]["Username"] = confData.bindDN;
170     ldap["Authentication"]["Password"] = nullptr;
171 
172     ldap["LDAPService"]["SearchSettings"]["BaseDistinguishedNames"] =
173         nlohmann::json::array({confData.baseDN});
174     ldap["LDAPService"]["SearchSettings"]["UsernameAttribute"] =
175         confData.userNameAttribute;
176     ldap["LDAPService"]["SearchSettings"]["GroupsAttribute"] =
177         confData.groupAttribute;
178 
179     nlohmann::json& roleMapArray = ldap["RemoteRoleMapping"];
180     roleMapArray = nlohmann::json::array();
181     for (const auto& obj : confData.groupRoleList)
182     {
183         BMCWEB_LOG_DEBUG << "Pushing the data groupName="
184                          << obj.second.groupName << "\n";
185         roleMapArray.push_back(
186             {nlohmann::json::array({"RemoteGroup", obj.second.groupName}),
187              nlohmann::json::array(
188                  {"LocalRole", getRoleIdFromPrivilege(obj.second.privilege)})});
189     }
190 }
191 
192 /**
193  *  @brief validates given JSON input and then calls appropriate method to
194  * create, to delete or to set Rolemapping object based on the given input.
195  *
196  */
197 inline void handleRoleMapPatch(
198     const std::shared_ptr<bmcweb::AsyncResp>& asyncResp,
199     const std::vector<std::pair<std::string, LDAPRoleMapData>>& roleMapObjData,
200     const std::string& serverType, const std::vector<nlohmann::json>& input)
201 {
202     for (size_t index = 0; index < input.size(); index++)
203     {
204         const nlohmann::json& thisJson = input[index];
205 
206         if (thisJson.is_null())
207         {
208             // delete the existing object
209             if (index < roleMapObjData.size())
210             {
211                 crow::connections::systemBus->async_method_call(
212                     [asyncResp, roleMapObjData, serverType,
213                      index](const boost::system::error_code ec) {
214                         if (ec)
215                         {
216                             BMCWEB_LOG_ERROR << "DBUS response error: " << ec;
217                             messages::internalError(asyncResp->res);
218                             return;
219                         }
220                         asyncResp->res
221                             .jsonValue[serverType]["RemoteRoleMapping"][index] =
222                             nullptr;
223                     },
224                     ldapDbusService, roleMapObjData[index].first,
225                     "xyz.openbmc_project.Object.Delete", "Delete");
226             }
227             else
228             {
229                 BMCWEB_LOG_ERROR << "Can't delete the object";
230                 messages::propertyValueTypeError(
231                     asyncResp->res,
232                     thisJson.dump(2, ' ', true,
233                                   nlohmann::json::error_handler_t::replace),
234                     "RemoteRoleMapping/" + std::to_string(index));
235                 return;
236             }
237         }
238         else if (thisJson.empty())
239         {
240             // Don't do anything for the empty objects,parse next json
241             // eg {"RemoteRoleMapping",[{}]}
242         }
243         else
244         {
245             // update/create the object
246             std::optional<std::string> remoteGroup;
247             std::optional<std::string> localRole;
248 
249             // This is a copy, but it's required in this case because of how
250             // readJson is structured
251             nlohmann::json thisJsonCopy = thisJson;
252             if (!json_util::readJson(thisJsonCopy, asyncResp->res,
253                                      "RemoteGroup", remoteGroup, "LocalRole",
254                                      localRole))
255             {
256                 continue;
257             }
258 
259             // Update existing RoleMapping Object
260             if (index < roleMapObjData.size())
261             {
262                 BMCWEB_LOG_DEBUG << "Update Role Map Object";
263                 // If "RemoteGroup" info is provided
264                 if (remoteGroup)
265                 {
266                     crow::connections::systemBus->async_method_call(
267                         [asyncResp, roleMapObjData, serverType, index,
268                          remoteGroup](const boost::system::error_code ec) {
269                             if (ec)
270                             {
271                                 BMCWEB_LOG_ERROR << "DBUS response error: "
272                                                  << ec;
273                                 messages::internalError(asyncResp->res);
274                                 return;
275                             }
276                             asyncResp->res
277                                 .jsonValue[serverType]["RemoteRoleMapping"]
278                                           [index]["RemoteGroup"] = *remoteGroup;
279                         },
280                         ldapDbusService, roleMapObjData[index].first,
281                         propertyInterface, "Set",
282                         "xyz.openbmc_project.User.PrivilegeMapperEntry",
283                         "GroupName",
284                         dbus::utility::DbusVariantType(
285                             std::move(*remoteGroup)));
286                 }
287 
288                 // If "LocalRole" info is provided
289                 if (localRole)
290                 {
291                     crow::connections::systemBus->async_method_call(
292                         [asyncResp, roleMapObjData, serverType, index,
293                          localRole](const boost::system::error_code ec) {
294                             if (ec)
295                             {
296                                 BMCWEB_LOG_ERROR << "DBUS response error: "
297                                                  << ec;
298                                 messages::internalError(asyncResp->res);
299                                 return;
300                             }
301                             asyncResp->res
302                                 .jsonValue[serverType]["RemoteRoleMapping"]
303                                           [index]["LocalRole"] = *localRole;
304                         },
305                         ldapDbusService, roleMapObjData[index].first,
306                         propertyInterface, "Set",
307                         "xyz.openbmc_project.User.PrivilegeMapperEntry",
308                         "Privilege",
309                         dbus::utility::DbusVariantType(
310                             getPrivilegeFromRoleId(std::move(*localRole))));
311                 }
312             }
313             // Create a new RoleMapping Object.
314             else
315             {
316                 BMCWEB_LOG_DEBUG
317                     << "setRoleMappingProperties: Creating new Object";
318                 std::string pathString =
319                     "RemoteRoleMapping/" + std::to_string(index);
320 
321                 if (!localRole)
322                 {
323                     messages::propertyMissing(asyncResp->res,
324                                               pathString + "/LocalRole");
325                     continue;
326                 }
327                 if (!remoteGroup)
328                 {
329                     messages::propertyMissing(asyncResp->res,
330                                               pathString + "/RemoteGroup");
331                     continue;
332                 }
333 
334                 std::string dbusObjectPath;
335                 if (serverType == "ActiveDirectory")
336                 {
337                     dbusObjectPath = adConfigObject;
338                 }
339                 else if (serverType == "LDAP")
340                 {
341                     dbusObjectPath = ldapConfigObjectName;
342                 }
343 
344                 BMCWEB_LOG_DEBUG << "Remote Group=" << *remoteGroup
345                                  << ",LocalRole=" << *localRole;
346 
347                 crow::connections::systemBus->async_method_call(
348                     [asyncResp, serverType, localRole,
349                      remoteGroup](const boost::system::error_code ec) {
350                         if (ec)
351                         {
352                             BMCWEB_LOG_ERROR << "DBUS response error: " << ec;
353                             messages::internalError(asyncResp->res);
354                             return;
355                         }
356                         nlohmann::json& remoteRoleJson =
357                             asyncResp->res
358                                 .jsonValue[serverType]["RemoteRoleMapping"];
359                         nlohmann::json::object_t roleMapEntry;
360                         roleMapEntry["LocalRole"] = *localRole;
361                         roleMapEntry["RemoteGroup"] = *remoteGroup;
362                         remoteRoleJson.push_back(std::move(roleMapEntry));
363                     },
364                     ldapDbusService, dbusObjectPath, ldapPrivMapperInterface,
365                     "Create", *remoteGroup,
366                     getPrivilegeFromRoleId(std::move(*localRole)));
367             }
368         }
369     }
370 }
371 
372 /**
373  * Function that retrieves all properties for LDAP config object
374  * into JSON
375  */
376 template <typename CallbackFunc>
377 inline void getLDAPConfigData(const std::string& ldapType,
378                               CallbackFunc&& callback)
379 {
380 
381     const std::array<const char*, 2> interfaces = {ldapEnableInterface,
382                                                    ldapConfigInterface};
383 
384     crow::connections::systemBus->async_method_call(
385         [callback, ldapType](const boost::system::error_code ec,
386                              const dbus::utility::MapperGetObject& resp) {
387             if (ec || resp.empty())
388             {
389                 BMCWEB_LOG_ERROR
390                     << "DBUS response error during getting of service name: "
391                     << ec;
392                 LDAPConfigData empty{};
393                 callback(false, empty, ldapType);
394                 return;
395             }
396             std::string service = resp.begin()->first;
397             crow::connections::systemBus->async_method_call(
398                 [callback, ldapType](
399                     const boost::system::error_code errorCode,
400                     const dbus::utility::ManagedObjectType& ldapObjects) {
401                     LDAPConfigData confData{};
402                     if (errorCode)
403                     {
404                         callback(false, confData, ldapType);
405                         BMCWEB_LOG_ERROR << "D-Bus responses error: "
406                                          << errorCode;
407                         return;
408                     }
409 
410                     std::string ldapDbusType;
411                     std::string searchString;
412 
413                     if (ldapType == "LDAP")
414                     {
415                         ldapDbusType =
416                             "xyz.openbmc_project.User.Ldap.Config.Type.OpenLdap";
417                         searchString = "openldap";
418                     }
419                     else if (ldapType == "ActiveDirectory")
420                     {
421                         ldapDbusType =
422                             "xyz.openbmc_project.User.Ldap.Config.Type.ActiveDirectory";
423                         searchString = "active_directory";
424                     }
425                     else
426                     {
427                         BMCWEB_LOG_ERROR
428                             << "Can't get the DbusType for the given type="
429                             << ldapType;
430                         callback(false, confData, ldapType);
431                         return;
432                     }
433 
434                     std::string ldapEnableInterfaceStr = ldapEnableInterface;
435                     std::string ldapConfigInterfaceStr = ldapConfigInterface;
436 
437                     for (const auto& object : ldapObjects)
438                     {
439                         // let's find the object whose ldap type is equal to the
440                         // given type
441                         if (object.first.str.find(searchString) ==
442                             std::string::npos)
443                         {
444                             continue;
445                         }
446 
447                         for (const auto& interface : object.second)
448                         {
449                             if (interface.first == ldapEnableInterfaceStr)
450                             {
451                                 // rest of the properties are string.
452                                 for (const auto& property : interface.second)
453                                 {
454                                     if (property.first == "Enabled")
455                                     {
456                                         const bool* value =
457                                             std::get_if<bool>(&property.second);
458                                         if (value == nullptr)
459                                         {
460                                             continue;
461                                         }
462                                         confData.serviceEnabled = *value;
463                                         break;
464                                     }
465                                 }
466                             }
467                             else if (interface.first == ldapConfigInterfaceStr)
468                             {
469 
470                                 for (const auto& property : interface.second)
471                                 {
472                                     const std::string* strValue =
473                                         std::get_if<std::string>(
474                                             &property.second);
475                                     if (strValue == nullptr)
476                                     {
477                                         continue;
478                                     }
479                                     if (property.first == "LDAPServerURI")
480                                     {
481                                         confData.uri = *strValue;
482                                     }
483                                     else if (property.first == "LDAPBindDN")
484                                     {
485                                         confData.bindDN = *strValue;
486                                     }
487                                     else if (property.first == "LDAPBaseDN")
488                                     {
489                                         confData.baseDN = *strValue;
490                                     }
491                                     else if (property.first ==
492                                              "LDAPSearchScope")
493                                     {
494                                         confData.searchScope = *strValue;
495                                     }
496                                     else if (property.first ==
497                                              "GroupNameAttribute")
498                                     {
499                                         confData.groupAttribute = *strValue;
500                                     }
501                                     else if (property.first ==
502                                              "UserNameAttribute")
503                                     {
504                                         confData.userNameAttribute = *strValue;
505                                     }
506                                     else if (property.first == "LDAPType")
507                                     {
508                                         confData.serverType = *strValue;
509                                     }
510                                 }
511                             }
512                             else if (
513                                 interface.first ==
514                                 "xyz.openbmc_project.User.PrivilegeMapperEntry")
515                             {
516                                 LDAPRoleMapData roleMapData{};
517                                 for (const auto& property : interface.second)
518                                 {
519                                     const std::string* strValue =
520                                         std::get_if<std::string>(
521                                             &property.second);
522 
523                                     if (strValue == nullptr)
524                                     {
525                                         continue;
526                                     }
527 
528                                     if (property.first == "GroupName")
529                                     {
530                                         roleMapData.groupName = *strValue;
531                                     }
532                                     else if (property.first == "Privilege")
533                                     {
534                                         roleMapData.privilege = *strValue;
535                                     }
536                                 }
537 
538                                 confData.groupRoleList.emplace_back(
539                                     object.first.str, roleMapData);
540                             }
541                         }
542                     }
543                     callback(true, confData, ldapType);
544                 },
545                 service, ldapRootObject, dbusObjManagerIntf,
546                 "GetManagedObjects");
547         },
548         mapperBusName, mapperObjectPath, mapperIntf, "GetObject",
549         ldapConfigObjectName, interfaces);
550 }
551 
552 /**
553  * @brief parses the authentication section under the LDAP
554  * @param input JSON data
555  * @param asyncResp pointer to the JSON response
556  * @param userName  userName to be filled from the given JSON.
557  * @param password  password to be filled from the given JSON.
558  */
559 inline void parseLDAPAuthenticationJson(
560     nlohmann::json input, const std::shared_ptr<bmcweb::AsyncResp>& asyncResp,
561     std::optional<std::string>& username, std::optional<std::string>& password)
562 {
563     std::optional<std::string> authType;
564 
565     if (!json_util::readJson(input, asyncResp->res, "AuthenticationType",
566                              authType, "Username", username, "Password",
567                              password))
568     {
569         return;
570     }
571     if (!authType)
572     {
573         return;
574     }
575     if (*authType != "UsernameAndPassword")
576     {
577         messages::propertyValueNotInList(asyncResp->res, *authType,
578                                          "AuthenticationType");
579         return;
580     }
581 }
582 /**
583  * @brief parses the LDAPService section under the LDAP
584  * @param input JSON data
585  * @param asyncResp pointer to the JSON response
586  * @param baseDNList baseDN to be filled from the given JSON.
587  * @param userNameAttribute  userName to be filled from the given JSON.
588  * @param groupaAttribute  password to be filled from the given JSON.
589  */
590 
591 inline void
592     parseLDAPServiceJson(nlohmann::json input,
593                          const std::shared_ptr<bmcweb::AsyncResp>& asyncResp,
594                          std::optional<std::vector<std::string>>& baseDNList,
595                          std::optional<std::string>& userNameAttribute,
596                          std::optional<std::string>& groupsAttribute)
597 {
598     std::optional<nlohmann::json> searchSettings;
599 
600     if (!json_util::readJson(input, asyncResp->res, "SearchSettings",
601                              searchSettings))
602     {
603         return;
604     }
605     if (!searchSettings)
606     {
607         return;
608     }
609     if (!json_util::readJson(*searchSettings, asyncResp->res,
610                              "BaseDistinguishedNames", baseDNList,
611                              "UsernameAttribute", userNameAttribute,
612                              "GroupsAttribute", groupsAttribute))
613     {
614         return;
615     }
616 }
617 /**
618  * @brief updates the LDAP server address and updates the
619           json response with the new value.
620  * @param serviceAddressList address to be updated.
621  * @param asyncResp pointer to the JSON response
622  * @param ldapServerElementName Type of LDAP
623  server(openLDAP/ActiveDirectory)
624  */
625 
626 inline void handleServiceAddressPatch(
627     const std::vector<std::string>& serviceAddressList,
628     const std::shared_ptr<bmcweb::AsyncResp>& asyncResp,
629     const std::string& ldapServerElementName,
630     const std::string& ldapConfigObject)
631 {
632     crow::connections::systemBus->async_method_call(
633         [asyncResp, ldapServerElementName,
634          serviceAddressList](const boost::system::error_code ec) {
635             if (ec)
636             {
637                 BMCWEB_LOG_DEBUG
638                     << "Error Occurred in updating the service address";
639                 messages::internalError(asyncResp->res);
640                 return;
641             }
642             std::vector<std::string> modifiedserviceAddressList = {
643                 serviceAddressList.front()};
644             asyncResp->res
645                 .jsonValue[ldapServerElementName]["ServiceAddresses"] =
646                 modifiedserviceAddressList;
647             if ((serviceAddressList).size() > 1)
648             {
649                 messages::propertyValueModified(asyncResp->res,
650                                                 "ServiceAddresses",
651                                                 serviceAddressList.front());
652             }
653             BMCWEB_LOG_DEBUG << "Updated the service address";
654         },
655         ldapDbusService, ldapConfigObject, propertyInterface, "Set",
656         ldapConfigInterface, "LDAPServerURI",
657         dbus::utility::DbusVariantType(serviceAddressList.front()));
658 }
659 /**
660  * @brief updates the LDAP Bind DN and updates the
661           json response with the new value.
662  * @param username name of the user which needs to be updated.
663  * @param asyncResp pointer to the JSON response
664  * @param ldapServerElementName Type of LDAP
665  server(openLDAP/ActiveDirectory)
666  */
667 
668 inline void
669     handleUserNamePatch(const std::string& username,
670                         const std::shared_ptr<bmcweb::AsyncResp>& asyncResp,
671                         const std::string& ldapServerElementName,
672                         const std::string& ldapConfigObject)
673 {
674     crow::connections::systemBus->async_method_call(
675         [asyncResp, username,
676          ldapServerElementName](const boost::system::error_code ec) {
677             if (ec)
678             {
679                 BMCWEB_LOG_DEBUG << "Error occurred in updating the username";
680                 messages::internalError(asyncResp->res);
681                 return;
682             }
683             asyncResp->res.jsonValue[ldapServerElementName]["Authentication"]
684                                     ["Username"] = username;
685             BMCWEB_LOG_DEBUG << "Updated the username";
686         },
687         ldapDbusService, ldapConfigObject, propertyInterface, "Set",
688         ldapConfigInterface, "LDAPBindDN",
689         dbus::utility::DbusVariantType(username));
690 }
691 
692 /**
693  * @brief updates the LDAP password
694  * @param password : ldap password which needs to be updated.
695  * @param asyncResp pointer to the JSON response
696  * @param ldapServerElementName Type of LDAP
697  *        server(openLDAP/ActiveDirectory)
698  */
699 
700 inline void
701     handlePasswordPatch(const std::string& password,
702                         const std::shared_ptr<bmcweb::AsyncResp>& asyncResp,
703                         const std::string& ldapServerElementName,
704                         const std::string& ldapConfigObject)
705 {
706     crow::connections::systemBus->async_method_call(
707         [asyncResp, password,
708          ldapServerElementName](const boost::system::error_code ec) {
709             if (ec)
710             {
711                 BMCWEB_LOG_DEBUG << "Error occurred in updating the password";
712                 messages::internalError(asyncResp->res);
713                 return;
714             }
715             asyncResp->res.jsonValue[ldapServerElementName]["Authentication"]
716                                     ["Password"] = "";
717             BMCWEB_LOG_DEBUG << "Updated the password";
718         },
719         ldapDbusService, ldapConfigObject, propertyInterface, "Set",
720         ldapConfigInterface, "LDAPBindDNPassword",
721         dbus::utility::DbusVariantType(password));
722 }
723 
724 /**
725  * @brief updates the LDAP BaseDN and updates the
726           json response with the new value.
727  * @param baseDNList baseDN list which needs to be updated.
728  * @param asyncResp pointer to the JSON response
729  * @param ldapServerElementName Type of LDAP
730  server(openLDAP/ActiveDirectory)
731  */
732 
733 inline void
734     handleBaseDNPatch(const std::vector<std::string>& baseDNList,
735                       const std::shared_ptr<bmcweb::AsyncResp>& asyncResp,
736                       const std::string& ldapServerElementName,
737                       const std::string& ldapConfigObject)
738 {
739     crow::connections::systemBus->async_method_call(
740         [asyncResp, baseDNList,
741          ldapServerElementName](const boost::system::error_code ec) {
742             if (ec)
743             {
744                 BMCWEB_LOG_DEBUG << "Error Occurred in Updating the base DN";
745                 messages::internalError(asyncResp->res);
746                 return;
747             }
748             auto& serverTypeJson =
749                 asyncResp->res.jsonValue[ldapServerElementName];
750             auto& searchSettingsJson =
751                 serverTypeJson["LDAPService"]["SearchSettings"];
752             std::vector<std::string> modifiedBaseDNList = {baseDNList.front()};
753             searchSettingsJson["BaseDistinguishedNames"] = modifiedBaseDNList;
754             if (baseDNList.size() > 1)
755             {
756                 messages::propertyValueModified(asyncResp->res,
757                                                 "BaseDistinguishedNames",
758                                                 baseDNList.front());
759             }
760             BMCWEB_LOG_DEBUG << "Updated the base DN";
761         },
762         ldapDbusService, ldapConfigObject, propertyInterface, "Set",
763         ldapConfigInterface, "LDAPBaseDN",
764         dbus::utility::DbusVariantType(baseDNList.front()));
765 }
766 /**
767  * @brief updates the LDAP user name attribute and updates the
768           json response with the new value.
769  * @param userNameAttribute attribute to be updated.
770  * @param asyncResp pointer to the JSON response
771  * @param ldapServerElementName Type of LDAP
772  server(openLDAP/ActiveDirectory)
773  */
774 
775 inline void
776     handleUserNameAttrPatch(const std::string& userNameAttribute,
777                             const std::shared_ptr<bmcweb::AsyncResp>& asyncResp,
778                             const std::string& ldapServerElementName,
779                             const std::string& ldapConfigObject)
780 {
781     crow::connections::systemBus->async_method_call(
782         [asyncResp, userNameAttribute,
783          ldapServerElementName](const boost::system::error_code ec) {
784             if (ec)
785             {
786                 BMCWEB_LOG_DEBUG << "Error Occurred in Updating the "
787                                     "username attribute";
788                 messages::internalError(asyncResp->res);
789                 return;
790             }
791             auto& serverTypeJson =
792                 asyncResp->res.jsonValue[ldapServerElementName];
793             auto& searchSettingsJson =
794                 serverTypeJson["LDAPService"]["SearchSettings"];
795             searchSettingsJson["UsernameAttribute"] = userNameAttribute;
796             BMCWEB_LOG_DEBUG << "Updated the user name attr.";
797         },
798         ldapDbusService, ldapConfigObject, propertyInterface, "Set",
799         ldapConfigInterface, "UserNameAttribute",
800         dbus::utility::DbusVariantType(userNameAttribute));
801 }
802 /**
803  * @brief updates the LDAP group attribute and updates the
804           json response with the new value.
805  * @param groupsAttribute attribute to be updated.
806  * @param asyncResp pointer to the JSON response
807  * @param ldapServerElementName Type of LDAP
808  server(openLDAP/ActiveDirectory)
809  */
810 
811 inline void handleGroupNameAttrPatch(
812     const std::string& groupsAttribute,
813     const std::shared_ptr<bmcweb::AsyncResp>& asyncResp,
814     const std::string& ldapServerElementName,
815     const std::string& ldapConfigObject)
816 {
817     crow::connections::systemBus->async_method_call(
818         [asyncResp, groupsAttribute,
819          ldapServerElementName](const boost::system::error_code ec) {
820             if (ec)
821             {
822                 BMCWEB_LOG_DEBUG << "Error Occurred in Updating the "
823                                     "groupname attribute";
824                 messages::internalError(asyncResp->res);
825                 return;
826             }
827             auto& serverTypeJson =
828                 asyncResp->res.jsonValue[ldapServerElementName];
829             auto& searchSettingsJson =
830                 serverTypeJson["LDAPService"]["SearchSettings"];
831             searchSettingsJson["GroupsAttribute"] = groupsAttribute;
832             BMCWEB_LOG_DEBUG << "Updated the groupname attr";
833         },
834         ldapDbusService, ldapConfigObject, propertyInterface, "Set",
835         ldapConfigInterface, "GroupNameAttribute",
836         dbus::utility::DbusVariantType(groupsAttribute));
837 }
838 /**
839  * @brief updates the LDAP service enable and updates the
840           json response with the new value.
841  * @param input JSON data.
842  * @param asyncResp pointer to the JSON response
843  * @param ldapServerElementName Type of LDAP
844  server(openLDAP/ActiveDirectory)
845  */
846 
847 inline void handleServiceEnablePatch(
848     bool serviceEnabled, const std::shared_ptr<bmcweb::AsyncResp>& asyncResp,
849     const std::string& ldapServerElementName,
850     const std::string& ldapConfigObject)
851 {
852     crow::connections::systemBus->async_method_call(
853         [asyncResp, serviceEnabled,
854          ldapServerElementName](const boost::system::error_code ec) {
855             if (ec)
856             {
857                 BMCWEB_LOG_DEBUG
858                     << "Error Occurred in Updating the service enable";
859                 messages::internalError(asyncResp->res);
860                 return;
861             }
862             asyncResp->res.jsonValue[ldapServerElementName]["ServiceEnabled"] =
863                 serviceEnabled;
864             BMCWEB_LOG_DEBUG << "Updated Service enable = " << serviceEnabled;
865         },
866         ldapDbusService, ldapConfigObject, propertyInterface, "Set",
867         ldapEnableInterface, "Enabled",
868         dbus::utility::DbusVariantType(serviceEnabled));
869 }
870 
871 inline void
872     handleAuthMethodsPatch(nlohmann::json& input,
873                            const std::shared_ptr<bmcweb::AsyncResp>& asyncResp)
874 {
875     std::optional<bool> basicAuth;
876     std::optional<bool> cookie;
877     std::optional<bool> sessionToken;
878     std::optional<bool> xToken;
879     std::optional<bool> tls;
880 
881     if (!json_util::readJson(input, asyncResp->res, "BasicAuth", basicAuth,
882                              "Cookie", cookie, "SessionToken", sessionToken,
883                              "XToken", xToken, "TLS", tls))
884     {
885         BMCWEB_LOG_ERROR << "Cannot read values from AuthMethod tag";
886         return;
887     }
888 
889     // Make a copy of methods configuration
890     persistent_data::AuthConfigMethods authMethodsConfig =
891         persistent_data::SessionStore::getInstance().getAuthMethodsConfig();
892 
893     if (basicAuth)
894     {
895 #ifndef BMCWEB_ENABLE_BASIC_AUTHENTICATION
896         messages::actionNotSupported(
897             asyncResp->res,
898             "Setting BasicAuth when basic-auth feature is disabled");
899         return;
900 #endif
901         authMethodsConfig.basic = *basicAuth;
902     }
903 
904     if (cookie)
905     {
906 #ifndef BMCWEB_ENABLE_COOKIE_AUTHENTICATION
907         messages::actionNotSupported(
908             asyncResp->res,
909             "Setting Cookie when cookie-auth feature is disabled");
910         return;
911 #endif
912         authMethodsConfig.cookie = *cookie;
913     }
914 
915     if (sessionToken)
916     {
917 #ifndef BMCWEB_ENABLE_SESSION_AUTHENTICATION
918         messages::actionNotSupported(
919             asyncResp->res,
920             "Setting SessionToken when session-auth feature is disabled");
921         return;
922 #endif
923         authMethodsConfig.sessionToken = *sessionToken;
924     }
925 
926     if (xToken)
927     {
928 #ifndef BMCWEB_ENABLE_XTOKEN_AUTHENTICATION
929         messages::actionNotSupported(
930             asyncResp->res,
931             "Setting XToken when xtoken-auth feature is disabled");
932         return;
933 #endif
934         authMethodsConfig.xtoken = *xToken;
935     }
936 
937     if (tls)
938     {
939 #ifndef BMCWEB_ENABLE_MUTUAL_TLS_AUTHENTICATION
940         messages::actionNotSupported(
941             asyncResp->res,
942             "Setting TLS when mutual-tls-auth feature is disabled");
943         return;
944 #endif
945         authMethodsConfig.tls = *tls;
946     }
947 
948     if (!authMethodsConfig.basic && !authMethodsConfig.cookie &&
949         !authMethodsConfig.sessionToken && !authMethodsConfig.xtoken &&
950         !authMethodsConfig.tls)
951     {
952         // Do not allow user to disable everything
953         messages::actionNotSupported(asyncResp->res,
954                                      "of disabling all available methods");
955         return;
956     }
957 
958     persistent_data::SessionStore::getInstance().updateAuthMethodsConfig(
959         authMethodsConfig);
960     // Save configuration immediately
961     persistent_data::getConfig().writeData();
962 
963     messages::success(asyncResp->res);
964 }
965 
966 /**
967  * @brief Get the required values from the given JSON, validates the
968  *        value and create the LDAP config object.
969  * @param input JSON data
970  * @param asyncResp pointer to the JSON response
971  * @param serverType Type of LDAP server(openLDAP/ActiveDirectory)
972  */
973 
974 inline void handleLDAPPatch(nlohmann::json& input,
975                             const std::shared_ptr<bmcweb::AsyncResp>& asyncResp,
976                             const std::string& serverType)
977 {
978     std::string dbusObjectPath;
979     if (serverType == "ActiveDirectory")
980     {
981         dbusObjectPath = adConfigObject;
982     }
983     else if (serverType == "LDAP")
984     {
985         dbusObjectPath = ldapConfigObjectName;
986     }
987     else
988     {
989         return;
990     }
991 
992     std::optional<nlohmann::json> authentication;
993     std::optional<nlohmann::json> ldapService;
994     std::optional<std::vector<std::string>> serviceAddressList;
995     std::optional<bool> serviceEnabled;
996     std::optional<std::vector<std::string>> baseDNList;
997     std::optional<std::string> userNameAttribute;
998     std::optional<std::string> groupsAttribute;
999     std::optional<std::string> userName;
1000     std::optional<std::string> password;
1001     std::optional<std::vector<nlohmann::json>> remoteRoleMapData;
1002 
1003     if (!json_util::readJson(input, asyncResp->res, "Authentication",
1004                              authentication, "LDAPService", ldapService,
1005                              "ServiceAddresses", serviceAddressList,
1006                              "ServiceEnabled", serviceEnabled,
1007                              "RemoteRoleMapping", remoteRoleMapData))
1008     {
1009         return;
1010     }
1011 
1012     if (authentication)
1013     {
1014         parseLDAPAuthenticationJson(*authentication, asyncResp, userName,
1015                                     password);
1016     }
1017     if (ldapService)
1018     {
1019         parseLDAPServiceJson(*ldapService, asyncResp, baseDNList,
1020                              userNameAttribute, groupsAttribute);
1021     }
1022     if (serviceAddressList)
1023     {
1024         if (serviceAddressList->empty())
1025         {
1026             messages::propertyValueNotInList(asyncResp->res, "[]",
1027                                              "ServiceAddress");
1028             return;
1029         }
1030     }
1031     if (baseDNList)
1032     {
1033         if (baseDNList->empty())
1034         {
1035             messages::propertyValueNotInList(asyncResp->res, "[]",
1036                                              "BaseDistinguishedNames");
1037             return;
1038         }
1039     }
1040 
1041     // nothing to update, then return
1042     if (!userName && !password && !serviceAddressList && !baseDNList &&
1043         !userNameAttribute && !groupsAttribute && !serviceEnabled &&
1044         !remoteRoleMapData)
1045     {
1046         return;
1047     }
1048 
1049     // Get the existing resource first then keep modifying
1050     // whenever any property gets updated.
1051     getLDAPConfigData(serverType, [asyncResp, userName, password, baseDNList,
1052                                    userNameAttribute, groupsAttribute,
1053                                    serviceAddressList, serviceEnabled,
1054                                    dbusObjectPath, remoteRoleMapData](
1055                                       bool success,
1056                                       const LDAPConfigData& confData,
1057                                       const std::string& serverT) {
1058         if (!success)
1059         {
1060             messages::internalError(asyncResp->res);
1061             return;
1062         }
1063         parseLDAPConfigData(asyncResp->res.jsonValue, confData, serverT);
1064         if (confData.serviceEnabled)
1065         {
1066             // Disable the service first and update the rest of
1067             // the properties.
1068             handleServiceEnablePatch(false, asyncResp, serverT, dbusObjectPath);
1069         }
1070 
1071         if (serviceAddressList)
1072         {
1073             handleServiceAddressPatch(*serviceAddressList, asyncResp, serverT,
1074                                       dbusObjectPath);
1075         }
1076         if (userName)
1077         {
1078             handleUserNamePatch(*userName, asyncResp, serverT, dbusObjectPath);
1079         }
1080         if (password)
1081         {
1082             handlePasswordPatch(*password, asyncResp, serverT, dbusObjectPath);
1083         }
1084 
1085         if (baseDNList)
1086         {
1087             handleBaseDNPatch(*baseDNList, asyncResp, serverT, dbusObjectPath);
1088         }
1089         if (userNameAttribute)
1090         {
1091             handleUserNameAttrPatch(*userNameAttribute, asyncResp, serverT,
1092                                     dbusObjectPath);
1093         }
1094         if (groupsAttribute)
1095         {
1096             handleGroupNameAttrPatch(*groupsAttribute, asyncResp, serverT,
1097                                      dbusObjectPath);
1098         }
1099         if (serviceEnabled)
1100         {
1101             // if user has given the value as true then enable
1102             // the service. if user has given false then no-op
1103             // as service is already stopped.
1104             if (*serviceEnabled)
1105             {
1106                 handleServiceEnablePatch(*serviceEnabled, asyncResp, serverT,
1107                                          dbusObjectPath);
1108             }
1109         }
1110         else
1111         {
1112             // if user has not given the service enabled value
1113             // then revert it to the same state as it was
1114             // before.
1115             handleServiceEnablePatch(confData.serviceEnabled, asyncResp,
1116                                      serverT, dbusObjectPath);
1117         }
1118 
1119         if (remoteRoleMapData)
1120         {
1121             handleRoleMapPatch(asyncResp, confData.groupRoleList, serverT,
1122                                *remoteRoleMapData);
1123         }
1124     });
1125 }
1126 
1127 inline void updateUserProperties(std::shared_ptr<bmcweb::AsyncResp> asyncResp,
1128                                  const std::string& username,
1129                                  std::optional<std::string> password,
1130                                  std::optional<bool> enabled,
1131                                  std::optional<std::string> roleId,
1132                                  std::optional<bool> locked)
1133 {
1134     sdbusplus::message::object_path tempObjPath(rootUserDbusPath);
1135     tempObjPath /= username;
1136     std::string dbusObjectPath(tempObjPath);
1137 
1138     dbus::utility::checkDbusPathExists(
1139         dbusObjectPath,
1140         [dbusObjectPath, username, password(std::move(password)),
1141          roleId(std::move(roleId)), enabled, locked,
1142          asyncResp{std::move(asyncResp)}](int rc) {
1143             if (rc <= 0)
1144             {
1145                 messages::resourceNotFound(
1146                     asyncResp->res, "#ManagerAccount.v1_4_0.ManagerAccount",
1147                     username);
1148                 return;
1149             }
1150 
1151             if (password)
1152             {
1153                 int retval = pamUpdatePassword(username, *password);
1154 
1155                 if (retval == PAM_USER_UNKNOWN)
1156                 {
1157                     messages::resourceNotFound(
1158                         asyncResp->res, "#ManagerAccount.v1_4_0.ManagerAccount",
1159                         username);
1160                 }
1161                 else if (retval == PAM_AUTHTOK_ERR)
1162                 {
1163                     // If password is invalid
1164                     messages::propertyValueFormatError(asyncResp->res,
1165                                                        *password, "Password");
1166                     BMCWEB_LOG_ERROR << "pamUpdatePassword Failed";
1167                 }
1168                 else if (retval != PAM_SUCCESS)
1169                 {
1170                     messages::internalError(asyncResp->res);
1171                     return;
1172                 }
1173                 else
1174                 {
1175                     messages::success(asyncResp->res);
1176                 }
1177             }
1178 
1179             if (enabled)
1180             {
1181                 crow::connections::systemBus->async_method_call(
1182                     [asyncResp](const boost::system::error_code ec) {
1183                         if (ec)
1184                         {
1185                             BMCWEB_LOG_ERROR << "D-Bus responses error: " << ec;
1186                             messages::internalError(asyncResp->res);
1187                             return;
1188                         }
1189                         messages::success(asyncResp->res);
1190                         return;
1191                     },
1192                     "xyz.openbmc_project.User.Manager", dbusObjectPath,
1193                     "org.freedesktop.DBus.Properties", "Set",
1194                     "xyz.openbmc_project.User.Attributes", "UserEnabled",
1195                     dbus::utility::DbusVariantType{*enabled});
1196             }
1197 
1198             if (roleId)
1199             {
1200                 std::string priv = getPrivilegeFromRoleId(*roleId);
1201                 if (priv.empty())
1202                 {
1203                     messages::propertyValueNotInList(asyncResp->res, *roleId,
1204                                                      "RoleId");
1205                     return;
1206                 }
1207                 if (priv == "priv-noaccess")
1208                 {
1209                     priv = "";
1210                 }
1211 
1212                 crow::connections::systemBus->async_method_call(
1213                     [asyncResp](const boost::system::error_code ec) {
1214                         if (ec)
1215                         {
1216                             BMCWEB_LOG_ERROR << "D-Bus responses error: " << ec;
1217                             messages::internalError(asyncResp->res);
1218                             return;
1219                         }
1220                         messages::success(asyncResp->res);
1221                     },
1222                     "xyz.openbmc_project.User.Manager", dbusObjectPath,
1223                     "org.freedesktop.DBus.Properties", "Set",
1224                     "xyz.openbmc_project.User.Attributes", "UserPrivilege",
1225                     dbus::utility::DbusVariantType{priv});
1226             }
1227 
1228             if (locked)
1229             {
1230                 // admin can unlock the account which is locked by
1231                 // successive authentication failures but admin should
1232                 // not be allowed to lock an account.
1233                 if (*locked)
1234                 {
1235                     messages::propertyValueNotInList(asyncResp->res, "true",
1236                                                      "Locked");
1237                     return;
1238                 }
1239 
1240                 crow::connections::systemBus->async_method_call(
1241                     [asyncResp](const boost::system::error_code ec) {
1242                         if (ec)
1243                         {
1244                             BMCWEB_LOG_ERROR << "D-Bus responses error: " << ec;
1245                             messages::internalError(asyncResp->res);
1246                             return;
1247                         }
1248                         messages::success(asyncResp->res);
1249                         return;
1250                     },
1251                     "xyz.openbmc_project.User.Manager", dbusObjectPath,
1252                     "org.freedesktop.DBus.Properties", "Set",
1253                     "xyz.openbmc_project.User.Attributes",
1254                     "UserLockedForFailedAttempt",
1255                     dbus::utility::DbusVariantType{*locked});
1256             }
1257         });
1258 }
1259 
1260 inline void requestAccountServiceRoutes(App& app)
1261 {
1262 
1263     BMCWEB_ROUTE(app, "/redfish/v1/AccountService/")
1264         .privileges(redfish::privileges::getAccountService)
1265         .methods(boost::beast::http::verb::get)([&app](const crow::Request& req,
1266                                                        const std::shared_ptr<
1267                                                            bmcweb::AsyncResp>&
1268                                                            asyncResp) -> void {
1269             if (!redfish::setUpRedfishRoute(app, req, asyncResp->res))
1270             {
1271                 return;
1272             }
1273             const persistent_data::AuthConfigMethods& authMethodsConfig =
1274                 persistent_data::SessionStore::getInstance()
1275                     .getAuthMethodsConfig();
1276 
1277             nlohmann::json& json = asyncResp->res.jsonValue;
1278             json["@odata.id"] = "/redfish/v1/AccountService";
1279             json["@odata.type"] = "#AccountService."
1280                                   "v1_10_0.AccountService";
1281             json["Id"] = "AccountService";
1282             json["Name"] = "Account Service";
1283             json["Description"] = "Account Service";
1284             json["ServiceEnabled"] = true;
1285             json["MaxPasswordLength"] = 20;
1286             json["Accounts"]["@odata.id"] =
1287                 "/redfish/v1/AccountService/Accounts";
1288             json["Roles"]["@odata.id"] = "/redfish/v1/AccountService/Roles";
1289             json["Oem"]["OpenBMC"]["@odata.type"] =
1290                 "#OemAccountService.v1_0_0.AccountService";
1291             json["Oem"]["OpenBMC"]["@odata.id"] =
1292                 "/redfish/v1/AccountService#/Oem/OpenBMC";
1293             json["Oem"]["OpenBMC"]["AuthMethods"]["BasicAuth"] =
1294                 authMethodsConfig.basic;
1295             json["Oem"]["OpenBMC"]["AuthMethods"]["SessionToken"] =
1296                 authMethodsConfig.sessionToken;
1297             json["Oem"]["OpenBMC"]["AuthMethods"]["XToken"] =
1298                 authMethodsConfig.xtoken;
1299             json["Oem"]["OpenBMC"]["AuthMethods"]["Cookie"] =
1300                 authMethodsConfig.cookie;
1301             json["Oem"]["OpenBMC"]["AuthMethods"]["TLS"] =
1302                 authMethodsConfig.tls;
1303 
1304             // /redfish/v1/AccountService/LDAP/Certificates is something only
1305             // ConfigureManager can access then only display when the user has
1306             // permissions ConfigureManager
1307             Privileges effectiveUserPrivileges =
1308                 redfish::getUserPrivileges(req.userRole);
1309 
1310             if (isOperationAllowedWithPrivileges({{"ConfigureManager"}},
1311                                                  effectiveUserPrivileges))
1312             {
1313                 asyncResp->res.jsonValue["LDAP"]["Certificates"]["@odata.id"] =
1314                     "/redfish/v1/AccountService/LDAP/Certificates";
1315             }
1316             crow::connections::systemBus->async_method_call(
1317                 [asyncResp](
1318                     const boost::system::error_code ec,
1319                     const dbus::utility::DBusPropertiesMap& propertiesList) {
1320                     if (ec)
1321                     {
1322                         messages::internalError(asyncResp->res);
1323                         return;
1324                     }
1325                     BMCWEB_LOG_DEBUG << "Got " << propertiesList.size()
1326                                      << "properties for AccountService";
1327                     for (const std::pair<std::string,
1328                                          dbus::utility::DbusVariantType>&
1329                              property : propertiesList)
1330                     {
1331                         if (property.first == "MinPasswordLength")
1332                         {
1333                             const uint8_t* value =
1334                                 std::get_if<uint8_t>(&property.second);
1335                             if (value != nullptr)
1336                             {
1337                                 asyncResp->res.jsonValue["MinPasswordLength"] =
1338                                     *value;
1339                             }
1340                         }
1341                         if (property.first == "AccountUnlockTimeout")
1342                         {
1343                             const uint32_t* value =
1344                                 std::get_if<uint32_t>(&property.second);
1345                             if (value != nullptr)
1346                             {
1347                                 asyncResp->res
1348                                     .jsonValue["AccountLockoutDuration"] =
1349                                     *value;
1350                             }
1351                         }
1352                         if (property.first == "MaxLoginAttemptBeforeLockout")
1353                         {
1354                             const uint16_t* value =
1355                                 std::get_if<uint16_t>(&property.second);
1356                             if (value != nullptr)
1357                             {
1358                                 asyncResp->res
1359                                     .jsonValue["AccountLockoutThreshold"] =
1360                                     *value;
1361                             }
1362                         }
1363                     }
1364                 },
1365                 "xyz.openbmc_project.User.Manager", "/xyz/openbmc_project/user",
1366                 "org.freedesktop.DBus.Properties", "GetAll",
1367                 "xyz.openbmc_project.User.AccountPolicy");
1368 
1369             auto callback = [asyncResp](bool success, LDAPConfigData& confData,
1370                                         const std::string& ldapType) {
1371                 if (!success)
1372                 {
1373                     return;
1374                 }
1375                 parseLDAPConfigData(asyncResp->res.jsonValue, confData,
1376                                     ldapType);
1377             };
1378 
1379             getLDAPConfigData("LDAP", callback);
1380             getLDAPConfigData("ActiveDirectory", callback);
1381         });
1382 
1383     BMCWEB_ROUTE(app, "/redfish/v1/AccountService/")
1384         .privileges(redfish::privileges::patchAccountService)
1385         .methods(boost::beast::http::verb::patch)(
1386             [&app](
1387                 const crow::Request& req,
1388                 const std::shared_ptr<bmcweb::AsyncResp>& asyncResp) -> void {
1389                 if (!redfish::setUpRedfishRoute(app, req, asyncResp->res))
1390                 {
1391                     return;
1392                 }
1393                 std::optional<uint32_t> unlockTimeout;
1394                 std::optional<uint16_t> lockoutThreshold;
1395                 std::optional<uint8_t> minPasswordLength;
1396                 std::optional<uint16_t> maxPasswordLength;
1397                 std::optional<nlohmann::json> ldapObject;
1398                 std::optional<nlohmann::json> activeDirectoryObject;
1399                 std::optional<nlohmann::json> oemObject;
1400 
1401                 if (!json_util::readJsonPatch(
1402                         req, asyncResp->res, "AccountLockoutDuration",
1403                         unlockTimeout, "AccountLockoutThreshold",
1404                         lockoutThreshold, "MaxPasswordLength",
1405                         maxPasswordLength, "MinPasswordLength",
1406                         minPasswordLength, "LDAP", ldapObject,
1407                         "ActiveDirectory", activeDirectoryObject, "Oem",
1408                         oemObject))
1409                 {
1410                     return;
1411                 }
1412 
1413                 if (minPasswordLength)
1414                 {
1415                     crow::connections::systemBus->async_method_call(
1416                         [asyncResp](const boost::system::error_code ec) {
1417                             if (ec)
1418                             {
1419                                 messages::internalError(asyncResp->res);
1420                                 return;
1421                             }
1422                             messages::success(asyncResp->res);
1423                         },
1424                         "xyz.openbmc_project.User.Manager",
1425                         "/xyz/openbmc_project/user",
1426                         "org.freedesktop.DBus.Properties", "Set",
1427                         "xyz.openbmc_project.User.AccountPolicy",
1428                         "MinPasswordLength",
1429                         dbus::utility::DbusVariantType(*minPasswordLength));
1430                 }
1431 
1432                 if (maxPasswordLength)
1433                 {
1434                     messages::propertyNotWritable(asyncResp->res,
1435                                                   "MaxPasswordLength");
1436                 }
1437 
1438                 if (ldapObject)
1439                 {
1440                     handleLDAPPatch(*ldapObject, asyncResp, "LDAP");
1441                 }
1442 
1443                 if (std::optional<nlohmann::json> oemOpenBMCObject;
1444                     oemObject &&
1445                     json_util::readJson(*oemObject, asyncResp->res, "OpenBMC",
1446                                         oemOpenBMCObject))
1447                 {
1448                     if (std::optional<nlohmann::json> authMethodsObject;
1449                         oemOpenBMCObject &&
1450                         json_util::readJson(*oemOpenBMCObject, asyncResp->res,
1451                                             "AuthMethods", authMethodsObject))
1452                     {
1453                         if (authMethodsObject)
1454                         {
1455                             handleAuthMethodsPatch(*authMethodsObject,
1456                                                    asyncResp);
1457                         }
1458                     }
1459                 }
1460 
1461                 if (activeDirectoryObject)
1462                 {
1463                     handleLDAPPatch(*activeDirectoryObject, asyncResp,
1464                                     "ActiveDirectory");
1465                 }
1466 
1467                 if (unlockTimeout)
1468                 {
1469                     crow::connections::systemBus->async_method_call(
1470                         [asyncResp](const boost::system::error_code ec) {
1471                             if (ec)
1472                             {
1473                                 messages::internalError(asyncResp->res);
1474                                 return;
1475                             }
1476                             messages::success(asyncResp->res);
1477                         },
1478                         "xyz.openbmc_project.User.Manager",
1479                         "/xyz/openbmc_project/user",
1480                         "org.freedesktop.DBus.Properties", "Set",
1481                         "xyz.openbmc_project.User.AccountPolicy",
1482                         "AccountUnlockTimeout",
1483                         dbus::utility::DbusVariantType(*unlockTimeout));
1484                 }
1485                 if (lockoutThreshold)
1486                 {
1487                     crow::connections::systemBus->async_method_call(
1488                         [asyncResp](const boost::system::error_code ec) {
1489                             if (ec)
1490                             {
1491                                 messages::internalError(asyncResp->res);
1492                                 return;
1493                             }
1494                             messages::success(asyncResp->res);
1495                         },
1496                         "xyz.openbmc_project.User.Manager",
1497                         "/xyz/openbmc_project/user",
1498                         "org.freedesktop.DBus.Properties", "Set",
1499                         "xyz.openbmc_project.User.AccountPolicy",
1500                         "MaxLoginAttemptBeforeLockout",
1501                         dbus::utility::DbusVariantType(*lockoutThreshold));
1502                 }
1503             });
1504 
1505     BMCWEB_ROUTE(app, "/redfish/v1/AccountService/Accounts/")
1506         .privileges(redfish::privileges::getManagerAccountCollection)
1507         .methods(boost::beast::http::verb::get)(
1508             [&app](
1509                 const crow::Request& req,
1510                 const std::shared_ptr<bmcweb::AsyncResp>& asyncResp) -> void {
1511                 if (!redfish::setUpRedfishRoute(app, req, asyncResp->res))
1512                 {
1513                     return;
1514                 }
1515 
1516                 asyncResp->res.jsonValue["@odata.id"] =
1517                     "/redfish/v1/AccountService/Accounts";
1518                 asyncResp->res.jsonValue["@odata.type"] =
1519                     "#ManagerAccountCollection."
1520                     "ManagerAccountCollection";
1521                 asyncResp->res.jsonValue["Name"] = "Accounts Collection";
1522                 asyncResp->res.jsonValue["Description"] = "BMC User Accounts";
1523 
1524                 Privileges effectiveUserPrivileges =
1525                     redfish::getUserPrivileges(req.userRole);
1526 
1527                 std::string thisUser;
1528                 if (req.session)
1529                 {
1530                     thisUser = req.session->username;
1531                 }
1532                 crow::connections::systemBus->async_method_call(
1533                     [asyncResp, thisUser, effectiveUserPrivileges](
1534                         const boost::system::error_code ec,
1535                         const dbus::utility::ManagedObjectType& users) {
1536                         if (ec)
1537                         {
1538                             messages::internalError(asyncResp->res);
1539                             return;
1540                         }
1541 
1542                         bool userCanSeeAllAccounts =
1543                             effectiveUserPrivileges.isSupersetOf(
1544                                 {"ConfigureUsers"});
1545 
1546                         bool userCanSeeSelf =
1547                             effectiveUserPrivileges.isSupersetOf(
1548                                 {"ConfigureSelf"});
1549 
1550                         nlohmann::json& memberArray =
1551                             asyncResp->res.jsonValue["Members"];
1552                         memberArray = nlohmann::json::array();
1553 
1554                         for (const auto& userpath : users)
1555                         {
1556                             std::string user = userpath.first.filename();
1557                             if (user.empty())
1558                             {
1559                                 messages::internalError(asyncResp->res);
1560                                 BMCWEB_LOG_ERROR << "Invalid firmware ID";
1561 
1562                                 return;
1563                             }
1564 
1565                             // As clarified by Redfish here:
1566                             // https://redfishforum.com/thread/281/manageraccountcollection-change-allows-account-enumeration
1567                             // Users without ConfigureUsers, only see their own
1568                             // account. Users with ConfigureUsers, see all
1569                             // accounts.
1570                             if (userCanSeeAllAccounts ||
1571                                 (thisUser == user && userCanSeeSelf))
1572                             {
1573                                 nlohmann::json::object_t member;
1574                                 member["@odata.id"] =
1575                                     "/redfish/v1/AccountService/Accounts/" +
1576                                     user;
1577                                 memberArray.push_back(std::move(member));
1578                             }
1579                         }
1580                         asyncResp->res.jsonValue["Members@odata.count"] =
1581                             memberArray.size();
1582                     },
1583                     "xyz.openbmc_project.User.Manager",
1584                     "/xyz/openbmc_project/user",
1585                     "org.freedesktop.DBus.ObjectManager", "GetManagedObjects");
1586             });
1587 
1588     BMCWEB_ROUTE(app, "/redfish/v1/AccountService/Accounts/")
1589         .privileges(redfish::privileges::postManagerAccountCollection)
1590         .methods(
1591             boost::beast::http::verb::post)([&app](const crow::Request& req,
1592                                                    const std::shared_ptr<
1593                                                        bmcweb::AsyncResp>&
1594                                                        asyncResp) -> void {
1595             if (!redfish::setUpRedfishRoute(app, req, asyncResp->res))
1596             {
1597                 return;
1598             }
1599             std::string username;
1600             std::string password;
1601             std::optional<std::string> roleId("User");
1602             std::optional<bool> enabled = true;
1603             if (!json_util::readJsonPatch(req, asyncResp->res, "UserName",
1604                                           username, "Password", password,
1605                                           "RoleId", roleId, "Enabled", enabled))
1606             {
1607                 return;
1608             }
1609 
1610             std::string priv = getPrivilegeFromRoleId(*roleId);
1611             if (priv.empty())
1612             {
1613                 messages::propertyValueNotInList(asyncResp->res, *roleId,
1614                                                  "RoleId");
1615                 return;
1616             }
1617             // TODO: Following override will be reverted once support in
1618             // phosphor-user-manager is added. In order to avoid dependency
1619             // issues, this is added in bmcweb, which will removed, once
1620             // phosphor-user-manager supports priv-noaccess.
1621             if (priv == "priv-noaccess")
1622             {
1623                 roleId = "";
1624             }
1625             else
1626             {
1627                 roleId = priv;
1628             }
1629 
1630             // Reading AllGroups property
1631             sdbusplus::asio::getProperty<std::vector<std::string>>(
1632                 *crow::connections::systemBus,
1633                 "xyz.openbmc_project.User.Manager", "/xyz/openbmc_project/user",
1634                 "xyz.openbmc_project.User.Manager", "AllGroups",
1635                 [asyncResp, username, password{std::move(password)}, roleId,
1636                  enabled](const boost::system::error_code ec,
1637                           const std::vector<std::string>& allGroupsList) {
1638                     if (ec)
1639                     {
1640                         BMCWEB_LOG_DEBUG << "ERROR with async_method_call";
1641                         messages::internalError(asyncResp->res);
1642                         return;
1643                     }
1644 
1645                     if (allGroupsList.empty())
1646                     {
1647                         messages::internalError(asyncResp->res);
1648                         return;
1649                     }
1650 
1651                     crow::connections::systemBus->async_method_call(
1652                         [asyncResp, username,
1653                          password](const boost::system::error_code ec2,
1654                                    sdbusplus::message::message& m) {
1655                             if (ec2)
1656                             {
1657                                 userErrorMessageHandler(
1658                                     m.get_error(), asyncResp, username, "");
1659                                 return;
1660                             }
1661 
1662                             if (pamUpdatePassword(username, password) !=
1663                                 PAM_SUCCESS)
1664                             {
1665                                 // At this point we have a user that's been
1666                                 // created, but the password set
1667                                 // failed.Something is wrong, so delete the user
1668                                 // that we've already created
1669                                 sdbusplus::message::object_path tempObjPath(
1670                                     rootUserDbusPath);
1671                                 tempObjPath /= username;
1672                                 const std::string userPath(tempObjPath);
1673 
1674                                 crow::connections::systemBus->async_method_call(
1675                                     [asyncResp, password](
1676                                         const boost::system::error_code ec3) {
1677                                         if (ec3)
1678                                         {
1679                                             messages::internalError(
1680                                                 asyncResp->res);
1681                                             return;
1682                                         }
1683 
1684                                         // If password is invalid
1685                                         messages::propertyValueFormatError(
1686                                             asyncResp->res, password,
1687                                             "Password");
1688                                     },
1689                                     "xyz.openbmc_project.User.Manager",
1690                                     userPath,
1691                                     "xyz.openbmc_project.Object.Delete",
1692                                     "Delete");
1693 
1694                                 BMCWEB_LOG_ERROR << "pamUpdatePassword Failed";
1695                                 return;
1696                             }
1697 
1698                             messages::created(asyncResp->res);
1699                             asyncResp->res.addHeader(
1700                                 "Location",
1701                                 "/redfish/v1/AccountService/Accounts/" +
1702                                     username);
1703                         },
1704                         "xyz.openbmc_project.User.Manager",
1705                         "/xyz/openbmc_project/user",
1706                         "xyz.openbmc_project.User.Manager", "CreateUser",
1707                         username, allGroupsList, *roleId, *enabled);
1708                 });
1709         });
1710 
1711     BMCWEB_ROUTE(app, "/redfish/v1/AccountService/Accounts/<str>/")
1712         .privileges(redfish::privileges::getManagerAccount)
1713         .methods(
1714             boost::beast::http::verb::
1715                 get)([&app]([[maybe_unused]] const crow::Request& req,
1716                             const std::shared_ptr<bmcweb::AsyncResp>& asyncResp,
1717                             const std::string& accountName) -> void {
1718             if (!redfish::setUpRedfishRoute(app, req, asyncResp->res))
1719             {
1720                 return;
1721             }
1722 #ifdef BMCWEB_INSECURE_DISABLE_AUTHENTICATION
1723             // If authentication is disabled, there are no user accounts
1724             messages::resourceNotFound(asyncResp->res,
1725                                        "#ManagerAccount.v1_4_0.ManagerAccount",
1726                                        accountName);
1727             return;
1728 
1729 #endif // BMCWEB_INSECURE_DISABLE_AUTHENTICATION
1730             if (req.session == nullptr)
1731             {
1732                 messages::internalError(asyncResp->res);
1733                 return;
1734             }
1735             if (req.session->username != accountName)
1736             {
1737                 // At this point we've determined that the user is trying to
1738                 // modify a user that isn't them.  We need to verify that they
1739                 // have permissions to modify other users, so re-run the auth
1740                 // check with the same permissions, minus ConfigureSelf.
1741                 Privileges effectiveUserPrivileges =
1742                     redfish::getUserPrivileges(req.userRole);
1743                 Privileges requiredPermissionsToChangeNonSelf = {
1744                     "ConfigureUsers", "ConfigureManager"};
1745                 if (!effectiveUserPrivileges.isSupersetOf(
1746                         requiredPermissionsToChangeNonSelf))
1747                 {
1748                     BMCWEB_LOG_DEBUG << "GET Account denied access";
1749                     messages::insufficientPrivilege(asyncResp->res);
1750                     return;
1751                 }
1752             }
1753 
1754             crow::connections::systemBus->async_method_call(
1755                 [asyncResp,
1756                  accountName](const boost::system::error_code ec,
1757                               const dbus::utility::ManagedObjectType& users) {
1758                     if (ec)
1759                     {
1760                         messages::internalError(asyncResp->res);
1761                         return;
1762                     }
1763                     const auto userIt = std::find_if(
1764                         users.begin(), users.end(),
1765                         [accountName](
1766                             const std::pair<sdbusplus::message::object_path,
1767                                             dbus::utility::DBusInteracesMap>&
1768                                 user) {
1769                             return accountName == user.first.filename();
1770                         });
1771 
1772                     if (userIt == users.end())
1773                     {
1774                         messages::resourceNotFound(
1775                             asyncResp->res, "ManagerAccount", accountName);
1776                         return;
1777                     }
1778 
1779                     asyncResp->res.jsonValue["@odata.type"] =
1780                         "#ManagerAccount.v1_4_0.ManagerAccount";
1781                     asyncResp->res.jsonValue["Name"] = "User Account";
1782                     asyncResp->res.jsonValue["Description"] = "User Account";
1783                     asyncResp->res.jsonValue["Password"] = nullptr;
1784                     asyncResp->res.jsonValue["AccountTypes"] = {"Redfish"};
1785 
1786                     for (const auto& interface : userIt->second)
1787                     {
1788                         if (interface.first ==
1789                             "xyz.openbmc_project.User.Attributes")
1790                         {
1791                             for (const auto& property : interface.second)
1792                             {
1793                                 if (property.first == "UserEnabled")
1794                                 {
1795                                     const bool* userEnabled =
1796                                         std::get_if<bool>(&property.second);
1797                                     if (userEnabled == nullptr)
1798                                     {
1799                                         BMCWEB_LOG_ERROR
1800                                             << "UserEnabled wasn't a bool";
1801                                         messages::internalError(asyncResp->res);
1802                                         return;
1803                                     }
1804                                     asyncResp->res.jsonValue["Enabled"] =
1805                                         *userEnabled;
1806                                 }
1807                                 else if (property.first ==
1808                                          "UserLockedForFailedAttempt")
1809                                 {
1810                                     const bool* userLocked =
1811                                         std::get_if<bool>(&property.second);
1812                                     if (userLocked == nullptr)
1813                                     {
1814                                         BMCWEB_LOG_ERROR << "UserLockedForF"
1815                                                             "ailedAttempt "
1816                                                             "wasn't a bool";
1817                                         messages::internalError(asyncResp->res);
1818                                         return;
1819                                     }
1820                                     asyncResp->res.jsonValue["Locked"] =
1821                                         *userLocked;
1822                                     asyncResp->res.jsonValue
1823                                         ["Locked@Redfish.AllowableValues"] = {
1824                                         "false"}; // can only unlock accounts
1825                                 }
1826                                 else if (property.first == "UserPrivilege")
1827                                 {
1828                                     const std::string* userPrivPtr =
1829                                         std::get_if<std::string>(
1830                                             &property.second);
1831                                     if (userPrivPtr == nullptr)
1832                                     {
1833                                         BMCWEB_LOG_ERROR
1834                                             << "UserPrivilege wasn't a "
1835                                                "string";
1836                                         messages::internalError(asyncResp->res);
1837                                         return;
1838                                     }
1839                                     std::string role =
1840                                         getRoleIdFromPrivilege(*userPrivPtr);
1841                                     if (role.empty())
1842                                     {
1843                                         BMCWEB_LOG_ERROR << "Invalid user role";
1844                                         messages::internalError(asyncResp->res);
1845                                         return;
1846                                     }
1847                                     asyncResp->res.jsonValue["RoleId"] = role;
1848 
1849                                     nlohmann::json& roleEntry =
1850                                         asyncResp->res
1851                                             .jsonValue["Links"]["Role"];
1852                                     roleEntry["@odata.id"] =
1853                                         "/redfish/v1/AccountService/Roles/" +
1854                                         role;
1855                                 }
1856                                 else if (property.first ==
1857                                          "UserPasswordExpired")
1858                                 {
1859                                     const bool* userPasswordExpired =
1860                                         std::get_if<bool>(&property.second);
1861                                     if (userPasswordExpired == nullptr)
1862                                     {
1863                                         BMCWEB_LOG_ERROR
1864                                             << "UserPasswordExpired wasn't a bool";
1865                                         messages::internalError(asyncResp->res);
1866                                         return;
1867                                     }
1868                                     asyncResp->res
1869                                         .jsonValue["PasswordChangeRequired"] =
1870                                         *userPasswordExpired;
1871                                 }
1872                             }
1873                         }
1874                     }
1875 
1876                     asyncResp->res.jsonValue["@odata.id"] =
1877                         "/redfish/v1/AccountService/Accounts/" + accountName;
1878                     asyncResp->res.jsonValue["Id"] = accountName;
1879                     asyncResp->res.jsonValue["UserName"] = accountName;
1880                 },
1881                 "xyz.openbmc_project.User.Manager", "/xyz/openbmc_project/user",
1882                 "org.freedesktop.DBus.ObjectManager", "GetManagedObjects");
1883         });
1884 
1885     BMCWEB_ROUTE(app, "/redfish/v1/AccountService/Accounts/<str>/")
1886         // TODO this privilege should be using the generated endpoints, but
1887         // because of the special handling of ConfigureSelf, it's not able to
1888         // yet
1889         .privileges({{"ConfigureUsers"}, {"ConfigureSelf"}})
1890         .methods(boost::beast::http::verb::patch)(
1891             [&app](const crow::Request& req,
1892                    const std::shared_ptr<bmcweb::AsyncResp>& asyncResp,
1893                    const std::string& username) -> void {
1894                 if (!redfish::setUpRedfishRoute(app, req, asyncResp->res))
1895                 {
1896                     return;
1897                 }
1898 #ifdef BMCWEB_INSECURE_DISABLE_AUTHENTICATION
1899                 // If authentication is disabled, there are no user accounts
1900                 messages::resourceNotFound(
1901                     asyncResp->res, "#ManagerAccount.v1_4_0.ManagerAccount",
1902                     username);
1903                 return;
1904 
1905 #endif // BMCWEB_INSECURE_DISABLE_AUTHENTICATION
1906                 std::optional<std::string> newUserName;
1907                 std::optional<std::string> password;
1908                 std::optional<bool> enabled;
1909                 std::optional<std::string> roleId;
1910                 std::optional<bool> locked;
1911 
1912                 if (req.session == nullptr)
1913                 {
1914                     messages::internalError(asyncResp->res);
1915                     return;
1916                 }
1917 
1918                 Privileges effectiveUserPrivileges =
1919                     redfish::getUserPrivileges(req.userRole);
1920                 Privileges configureUsers = {"ConfigureUsers"};
1921                 bool userHasConfigureUsers =
1922                     effectiveUserPrivileges.isSupersetOf(configureUsers);
1923                 if (userHasConfigureUsers)
1924                 {
1925                     // Users with ConfigureUsers can modify for all users
1926                     if (!json_util::readJsonPatch(
1927                             req, asyncResp->res, "UserName", newUserName,
1928                             "Password", password, "RoleId", roleId, "Enabled",
1929                             enabled, "Locked", locked))
1930                     {
1931                         return;
1932                     }
1933                 }
1934                 else
1935                 {
1936                     // ConfigureSelf accounts can only modify their own account
1937                     if (username != req.session->username)
1938                     {
1939                         messages::insufficientPrivilege(asyncResp->res);
1940                         return;
1941                     }
1942 
1943                     // ConfigureSelf accounts can only modify their password
1944                     if (!json_util::readJsonPatch(req, asyncResp->res,
1945                                                   "Password", password))
1946                     {
1947                         return;
1948                     }
1949                 }
1950 
1951                 // if user name is not provided in the patch method or if it
1952                 // matches the user name in the URI, then we are treating it as
1953                 // updating user properties other then username. If username
1954                 // provided doesn't match the URI, then we are treating this as
1955                 // user rename request.
1956                 if (!newUserName || (newUserName.value() == username))
1957                 {
1958                     updateUserProperties(asyncResp, username, password, enabled,
1959                                          roleId, locked);
1960                     return;
1961                 }
1962                 crow::connections::systemBus->async_method_call(
1963                     [asyncResp, username, password(std::move(password)),
1964                      roleId(std::move(roleId)), enabled,
1965                      newUser{std::string(*newUserName)},
1966                      locked](const boost::system::error_code ec,
1967                              sdbusplus::message::message& m) {
1968                         if (ec)
1969                         {
1970                             userErrorMessageHandler(m.get_error(), asyncResp,
1971                                                     newUser, username);
1972                             return;
1973                         }
1974 
1975                         updateUserProperties(asyncResp, newUser, password,
1976                                              enabled, roleId, locked);
1977                     },
1978                     "xyz.openbmc_project.User.Manager",
1979                     "/xyz/openbmc_project/user",
1980                     "xyz.openbmc_project.User.Manager", "RenameUser", username,
1981                     *newUserName);
1982             });
1983 
1984     BMCWEB_ROUTE(app, "/redfish/v1/AccountService/Accounts/<str>/")
1985         .privileges(redfish::privileges::deleteManagerAccount)
1986         .methods(boost::beast::http::verb::delete_)(
1987             [&app](const crow::Request& req,
1988                    const std::shared_ptr<bmcweb::AsyncResp>& asyncResp,
1989                    const std::string& username) -> void {
1990                 if (!redfish::setUpRedfishRoute(app, req, asyncResp->res))
1991                 {
1992                     return;
1993                 }
1994 
1995 #ifdef BMCWEB_INSECURE_DISABLE_AUTHENTICATION
1996                 // If authentication is disabled, there are no user accounts
1997                 messages::resourceNotFound(
1998                     asyncResp->res, "#ManagerAccount.v1_4_0.ManagerAccount",
1999                     username);
2000                 return;
2001 
2002 #endif // BMCWEB_INSECURE_DISABLE_AUTHENTICATION
2003                 sdbusplus::message::object_path tempObjPath(rootUserDbusPath);
2004                 tempObjPath /= username;
2005                 const std::string userPath(tempObjPath);
2006 
2007                 crow::connections::systemBus->async_method_call(
2008                     [asyncResp, username](const boost::system::error_code ec) {
2009                         if (ec)
2010                         {
2011                             messages::resourceNotFound(
2012                                 asyncResp->res,
2013                                 "#ManagerAccount.v1_4_0.ManagerAccount",
2014                                 username);
2015                             return;
2016                         }
2017 
2018                         messages::accountRemoved(asyncResp->res);
2019                     },
2020                     "xyz.openbmc_project.User.Manager", userPath,
2021                     "xyz.openbmc_project.Object.Delete", "Delete");
2022             });
2023 }
2024 
2025 } // namespace redfish
2026