Remove Node class from Account Service

This is a progression of 7e860f1550c8686eec42f7a75bc5f2ef51e756ad, which
correctly noted that AccountService has a number of class specific
variables.  This commit removes the Node class from those in line with
the aformentioned patchset, and at the same time removes the need for
the isAllowedWithoutConfigureSelf method, which was relying on state
captured to do some complex rule checking.  Fortunately, it is
relatively easy to check current permissions at runtime using the
Privileges::isSupersetOf check against the current users role.  This
significantly reduces the complexity of the code, while still giving the
same result (users with only ConfigureSelf cannot see or modify other
users).  Ideally these two things, isAllowedWithoutConfigureSelf, and
the Node moving would've been done in separate commits, but given that
the former would've required moving a number of features out of the node
derived class anyway, separating them would lead to essentially the same
diff twice, hence why they are combined for easier review.

Tested:
Ran Redfish service validator.  No new errors.  (UUID error present that
appears to be unrelated)

Change-Id: Iad919dbc7ab7e8d47cc1160999ed9f43f685fa56
Signed-off-by: Ed Tanous <edtanous@google.com>
diff --git a/redfish-core/lib/account_service.hpp b/redfish-core/lib/account_service.hpp
index 2e81104..4c639ad 100644
--- a/redfish-core/lib/account_service.hpp
+++ b/redfish-core/lib/account_service.hpp
@@ -558,1398 +558,1262 @@
         ldapConfigObjectName, interfaces);
 }
 
-class AccountService : public Node
+/**
+ * @brief parses the authentication section under the LDAP
+ * @param input JSON data
+ * @param asyncResp pointer to the JSON response
+ * @param userName  userName to be filled from the given JSON.
+ * @param password  password to be filled from the given JSON.
+ */
+void parseLDAPAuthenticationJson(
+    nlohmann::json input, const std::shared_ptr<bmcweb::AsyncResp>& asyncResp,
+    std::optional<std::string>& username, std::optional<std::string>& password)
 {
-  public:
-    AccountService(App& app) : Node(app, "/redfish/v1/AccountService/")
+    std::optional<std::string> authType;
+
+    if (!json_util::readJson(input, asyncResp->res, "AuthenticationType",
+                             authType, "Username", username, "Password",
+                             password))
     {
-        entityPrivileges = {
-            {boost::beast::http::verb::get, {{"Login"}}},
-            {boost::beast::http::verb::head, {{"Login"}}},
-            {boost::beast::http::verb::patch, {{"ConfigureUsers"}}},
-            {boost::beast::http::verb::put, {{"ConfigureUsers"}}},
-            {boost::beast::http::verb::delete_, {{"ConfigureUsers"}}},
-            {boost::beast::http::verb::post, {{"ConfigureUsers"}}}};
+        return;
     }
-
-  private:
-    /**
-     * @brief parses the authentication section under the LDAP
-     * @param input JSON data
-     * @param asyncResp pointer to the JSON response
-     * @param userName  userName to be filled from the given JSON.
-     * @param password  password to be filled from the given JSON.
-     */
-    void parseLDAPAuthenticationJson(
-        nlohmann::json input,
-        const std::shared_ptr<bmcweb::AsyncResp>& asyncResp,
-        std::optional<std::string>& username,
-        std::optional<std::string>& password)
+    if (!authType)
     {
-        std::optional<std::string> authType;
-
-        if (!json_util::readJson(input, asyncResp->res, "AuthenticationType",
-                                 authType, "Username", username, "Password",
-                                 password))
-        {
-            return;
-        }
-        if (!authType)
-        {
-            return;
-        }
-        if (*authType != "UsernameAndPassword")
-        {
-            messages::propertyValueNotInList(asyncResp->res, *authType,
-                                             "AuthenticationType");
-            return;
-        }
+        return;
     }
-    /**
-     * @brief parses the LDAPService section under the LDAP
-     * @param input JSON data
-     * @param asyncResp pointer to the JSON response
-     * @param baseDNList baseDN to be filled from the given JSON.
-     * @param userNameAttribute  userName to be filled from the given JSON.
-     * @param groupaAttribute  password to be filled from the given JSON.
-     */
-
-    void parseLDAPServiceJson(
-        nlohmann::json input,
-        const std::shared_ptr<bmcweb::AsyncResp>& asyncResp,
-        std::optional<std::vector<std::string>>& baseDNList,
-        std::optional<std::string>& userNameAttribute,
-        std::optional<std::string>& groupsAttribute)
+    if (*authType != "UsernameAndPassword")
     {
-        std::optional<nlohmann::json> searchSettings;
-
-        if (!json_util::readJson(input, asyncResp->res, "SearchSettings",
-                                 searchSettings))
-        {
-            return;
-        }
-        if (!searchSettings)
-        {
-            return;
-        }
-        if (!json_util::readJson(*searchSettings, asyncResp->res,
-                                 "BaseDistinguishedNames", baseDNList,
-                                 "UsernameAttribute", userNameAttribute,
-                                 "GroupsAttribute", groupsAttribute))
-        {
-            return;
-        }
+        messages::propertyValueNotInList(asyncResp->res, *authType,
+                                         "AuthenticationType");
+        return;
     }
-    /**
-     * @brief updates the LDAP server address and updates the
-              json response with the new value.
-     * @param serviceAddressList address to be updated.
-     * @param asyncResp pointer to the JSON response
-     * @param ldapServerElementName Type of LDAP
-     server(openLDAP/ActiveDirectory)
-     */
+}
+/**
+ * @brief parses the LDAPService section under the LDAP
+ * @param input JSON data
+ * @param asyncResp pointer to the JSON response
+ * @param baseDNList baseDN to be filled from the given JSON.
+ * @param userNameAttribute  userName to be filled from the given JSON.
+ * @param groupaAttribute  password to be filled from the given JSON.
+ */
 
-    void handleServiceAddressPatch(
-        const std::vector<std::string>& serviceAddressList,
-        const std::shared_ptr<bmcweb::AsyncResp>& asyncResp,
-        const std::string& ldapServerElementName,
-        const std::string& ldapConfigObject)
+void parseLDAPServiceJson(nlohmann::json input,
+                          const std::shared_ptr<bmcweb::AsyncResp>& asyncResp,
+                          std::optional<std::vector<std::string>>& baseDNList,
+                          std::optional<std::string>& userNameAttribute,
+                          std::optional<std::string>& groupsAttribute)
+{
+    std::optional<nlohmann::json> searchSettings;
+
+    if (!json_util::readJson(input, asyncResp->res, "SearchSettings",
+                             searchSettings))
     {
-        crow::connections::systemBus->async_method_call(
-            [asyncResp, ldapServerElementName,
-             serviceAddressList](const boost::system::error_code ec) {
-                if (ec)
-                {
-                    BMCWEB_LOG_DEBUG
-                        << "Error Occurred in updating the service address";
-                    messages::internalError(asyncResp->res);
-                    return;
-                }
-                std::vector<std::string> modifiedserviceAddressList = {
-                    serviceAddressList.front()};
-                asyncResp->res
-                    .jsonValue[ldapServerElementName]["ServiceAddresses"] =
-                    modifiedserviceAddressList;
-                if ((serviceAddressList).size() > 1)
-                {
-                    messages::propertyValueModified(asyncResp->res,
-                                                    "ServiceAddresses",
-                                                    serviceAddressList.front());
-                }
-                BMCWEB_LOG_DEBUG << "Updated the service address";
-            },
-            ldapDbusService, ldapConfigObject, propertyInterface, "Set",
-            ldapConfigInterface, "LDAPServerURI",
-            std::variant<std::string>(serviceAddressList.front()));
+        return;
     }
-    /**
-     * @brief updates the LDAP Bind DN and updates the
-              json response with the new value.
-     * @param username name of the user which needs to be updated.
-     * @param asyncResp pointer to the JSON response
-     * @param ldapServerElementName Type of LDAP
-     server(openLDAP/ActiveDirectory)
-     */
-
-    void
-        handleUserNamePatch(const std::string& username,
-                            const std::shared_ptr<bmcweb::AsyncResp>& asyncResp,
-                            const std::string& ldapServerElementName,
-                            const std::string& ldapConfigObject)
+    if (!searchSettings)
     {
-        crow::connections::systemBus->async_method_call(
-            [asyncResp, username,
-             ldapServerElementName](const boost::system::error_code ec) {
-                if (ec)
-                {
-                    BMCWEB_LOG_DEBUG
-                        << "Error occurred in updating the username";
-                    messages::internalError(asyncResp->res);
-                    return;
-                }
-                asyncResp->res.jsonValue[ldapServerElementName]
-                                        ["Authentication"]["Username"] =
-                    username;
-                BMCWEB_LOG_DEBUG << "Updated the username";
-            },
-            ldapDbusService, ldapConfigObject, propertyInterface, "Set",
-            ldapConfigInterface, "LDAPBindDN",
-            std::variant<std::string>(username));
+        return;
     }
-
-    /**
-     * @brief updates the LDAP password
-     * @param password : ldap password which needs to be updated.
-     * @param asyncResp pointer to the JSON response
-     * @param ldapServerElementName Type of LDAP
-     *        server(openLDAP/ActiveDirectory)
-     */
-
-    void
-        handlePasswordPatch(const std::string& password,
-                            const std::shared_ptr<bmcweb::AsyncResp>& asyncResp,
-                            const std::string& ldapServerElementName,
-                            const std::string& ldapConfigObject)
+    if (!json_util::readJson(*searchSettings, asyncResp->res,
+                             "BaseDistinguishedNames", baseDNList,
+                             "UsernameAttribute", userNameAttribute,
+                             "GroupsAttribute", groupsAttribute))
     {
-        crow::connections::systemBus->async_method_call(
-            [asyncResp, password,
-             ldapServerElementName](const boost::system::error_code ec) {
-                if (ec)
-                {
-                    BMCWEB_LOG_DEBUG
-                        << "Error occurred in updating the password";
-                    messages::internalError(asyncResp->res);
-                    return;
-                }
-                asyncResp->res.jsonValue[ldapServerElementName]
-                                        ["Authentication"]["Password"] = "";
-                BMCWEB_LOG_DEBUG << "Updated the password";
-            },
-            ldapDbusService, ldapConfigObject, propertyInterface, "Set",
-            ldapConfigInterface, "LDAPBindDNPassword",
-            std::variant<std::string>(password));
+        return;
     }
+}
+/**
+ * @brief updates the LDAP server address and updates the
+          json response with the new value.
+ * @param serviceAddressList address to be updated.
+ * @param asyncResp pointer to the JSON response
+ * @param ldapServerElementName Type of LDAP
+ server(openLDAP/ActiveDirectory)
+ */
 
-    /**
-     * @brief updates the LDAP BaseDN and updates the
-              json response with the new value.
-     * @param baseDNList baseDN list which needs to be updated.
-     * @param asyncResp pointer to the JSON response
-     * @param ldapServerElementName Type of LDAP
-     server(openLDAP/ActiveDirectory)
-     */
+void handleServiceAddressPatch(
+    const std::vector<std::string>& serviceAddressList,
+    const std::shared_ptr<bmcweb::AsyncResp>& asyncResp,
+    const std::string& ldapServerElementName,
+    const std::string& ldapConfigObject)
+{
+    crow::connections::systemBus->async_method_call(
+        [asyncResp, ldapServerElementName,
+         serviceAddressList](const boost::system::error_code ec) {
+            if (ec)
+            {
+                BMCWEB_LOG_DEBUG
+                    << "Error Occurred in updating the service address";
+                messages::internalError(asyncResp->res);
+                return;
+            }
+            std::vector<std::string> modifiedserviceAddressList = {
+                serviceAddressList.front()};
+            asyncResp->res
+                .jsonValue[ldapServerElementName]["ServiceAddresses"] =
+                modifiedserviceAddressList;
+            if ((serviceAddressList).size() > 1)
+            {
+                messages::propertyValueModified(asyncResp->res,
+                                                "ServiceAddresses",
+                                                serviceAddressList.front());
+            }
+            BMCWEB_LOG_DEBUG << "Updated the service address";
+        },
+        ldapDbusService, ldapConfigObject, propertyInterface, "Set",
+        ldapConfigInterface, "LDAPServerURI",
+        std::variant<std::string>(serviceAddressList.front()));
+}
+/**
+ * @brief updates the LDAP Bind DN and updates the
+          json response with the new value.
+ * @param username name of the user which needs to be updated.
+ * @param asyncResp pointer to the JSON response
+ * @param ldapServerElementName Type of LDAP
+ server(openLDAP/ActiveDirectory)
+ */
 
-    void handleBaseDNPatch(const std::vector<std::string>& baseDNList,
-                           const std::shared_ptr<bmcweb::AsyncResp>& asyncResp,
-                           const std::string& ldapServerElementName,
-                           const std::string& ldapConfigObject)
-    {
-        crow::connections::systemBus->async_method_call(
-            [asyncResp, baseDNList,
-             ldapServerElementName](const boost::system::error_code ec) {
-                if (ec)
-                {
-                    BMCWEB_LOG_DEBUG
-                        << "Error Occurred in Updating the base DN";
-                    messages::internalError(asyncResp->res);
-                    return;
-                }
-                auto& serverTypeJson =
-                    asyncResp->res.jsonValue[ldapServerElementName];
-                auto& searchSettingsJson =
-                    serverTypeJson["LDAPService"]["SearchSettings"];
-                std::vector<std::string> modifiedBaseDNList = {
-                    baseDNList.front()};
-                searchSettingsJson["BaseDistinguishedNames"] =
-                    modifiedBaseDNList;
-                if (baseDNList.size() > 1)
-                {
-                    messages::propertyValueModified(asyncResp->res,
-                                                    "BaseDistinguishedNames",
-                                                    baseDNList.front());
-                }
-                BMCWEB_LOG_DEBUG << "Updated the base DN";
-            },
-            ldapDbusService, ldapConfigObject, propertyInterface, "Set",
-            ldapConfigInterface, "LDAPBaseDN",
-            std::variant<std::string>(baseDNList.front()));
-    }
-    /**
-     * @brief updates the LDAP user name attribute and updates the
-              json response with the new value.
-     * @param userNameAttribute attribute to be updated.
-     * @param asyncResp pointer to the JSON response
-     * @param ldapServerElementName Type of LDAP
-     server(openLDAP/ActiveDirectory)
-     */
-
-    void handleUserNameAttrPatch(
-        const std::string& userNameAttribute,
-        const std::shared_ptr<bmcweb::AsyncResp>& asyncResp,
-        const std::string& ldapServerElementName,
-        const std::string& ldapConfigObject)
-    {
-        crow::connections::systemBus->async_method_call(
-            [asyncResp, userNameAttribute,
-             ldapServerElementName](const boost::system::error_code ec) {
-                if (ec)
-                {
-                    BMCWEB_LOG_DEBUG << "Error Occurred in Updating the "
-                                        "username attribute";
-                    messages::internalError(asyncResp->res);
-                    return;
-                }
-                auto& serverTypeJson =
-                    asyncResp->res.jsonValue[ldapServerElementName];
-                auto& searchSettingsJson =
-                    serverTypeJson["LDAPService"]["SearchSettings"];
-                searchSettingsJson["UsernameAttribute"] = userNameAttribute;
-                BMCWEB_LOG_DEBUG << "Updated the user name attr.";
-            },
-            ldapDbusService, ldapConfigObject, propertyInterface, "Set",
-            ldapConfigInterface, "UserNameAttribute",
-            std::variant<std::string>(userNameAttribute));
-    }
-    /**
-     * @brief updates the LDAP group attribute and updates the
-              json response with the new value.
-     * @param groupsAttribute attribute to be updated.
-     * @param asyncResp pointer to the JSON response
-     * @param ldapServerElementName Type of LDAP
-     server(openLDAP/ActiveDirectory)
-     */
-
-    void handleGroupNameAttrPatch(
-        const std::string& groupsAttribute,
-        const std::shared_ptr<bmcweb::AsyncResp>& asyncResp,
-        const std::string& ldapServerElementName,
-        const std::string& ldapConfigObject)
-    {
-        crow::connections::systemBus->async_method_call(
-            [asyncResp, groupsAttribute,
-             ldapServerElementName](const boost::system::error_code ec) {
-                if (ec)
-                {
-                    BMCWEB_LOG_DEBUG << "Error Occurred in Updating the "
-                                        "groupname attribute";
-                    messages::internalError(asyncResp->res);
-                    return;
-                }
-                auto& serverTypeJson =
-                    asyncResp->res.jsonValue[ldapServerElementName];
-                auto& searchSettingsJson =
-                    serverTypeJson["LDAPService"]["SearchSettings"];
-                searchSettingsJson["GroupsAttribute"] = groupsAttribute;
-                BMCWEB_LOG_DEBUG << "Updated the groupname attr";
-            },
-            ldapDbusService, ldapConfigObject, propertyInterface, "Set",
-            ldapConfigInterface, "GroupNameAttribute",
-            std::variant<std::string>(groupsAttribute));
-    }
-    /**
-     * @brief updates the LDAP service enable and updates the
-              json response with the new value.
-     * @param input JSON data.
-     * @param asyncResp pointer to the JSON response
-     * @param ldapServerElementName Type of LDAP
-     server(openLDAP/ActiveDirectory)
-     */
-
-    void handleServiceEnablePatch(
-        bool serviceEnabled,
-        const std::shared_ptr<bmcweb::AsyncResp>& asyncResp,
-        const std::string& ldapServerElementName,
-        const std::string& ldapConfigObject)
-    {
-        crow::connections::systemBus->async_method_call(
-            [asyncResp, serviceEnabled,
-             ldapServerElementName](const boost::system::error_code ec) {
-                if (ec)
-                {
-                    BMCWEB_LOG_DEBUG
-                        << "Error Occurred in Updating the service enable";
-                    messages::internalError(asyncResp->res);
-                    return;
-                }
-                asyncResp->res
-                    .jsonValue[ldapServerElementName]["ServiceEnabled"] =
-                    serviceEnabled;
-                BMCWEB_LOG_DEBUG << "Updated Service enable = "
-                                 << serviceEnabled;
-            },
-            ldapDbusService, ldapConfigObject, propertyInterface, "Set",
-            ldapEnableInterface, "Enabled", std::variant<bool>(serviceEnabled));
-    }
-
-    void handleAuthMethodsPatch(
-        nlohmann::json& input,
-        const std::shared_ptr<bmcweb::AsyncResp>& asyncResp)
-    {
-        std::optional<bool> basicAuth;
-        std::optional<bool> cookie;
-        std::optional<bool> sessionToken;
-        std::optional<bool> xToken;
-        std::optional<bool> tls;
-
-        if (!json_util::readJson(input, asyncResp->res, "BasicAuth", basicAuth,
-                                 "Cookie", cookie, "SessionToken", sessionToken,
-                                 "XToken", xToken, "TLS", tls))
-        {
-            BMCWEB_LOG_ERROR << "Cannot read values from AuthMethod tag";
-            return;
-        }
-
-        // Make a copy of methods configuration
-        persistent_data::AuthConfigMethods authMethodsConfig =
-            persistent_data::SessionStore::getInstance().getAuthMethodsConfig();
-
-        if (basicAuth)
-        {
-#ifndef BMCWEB_ENABLE_BASIC_AUTHENTICATION
-            messages::actionNotSupported(
-                asyncResp->res, "Setting BasicAuth when basic-auth feature "
-                                "is disabled");
-            return;
-#endif
-            authMethodsConfig.basic = *basicAuth;
-        }
-
-        if (cookie)
-        {
-#ifndef BMCWEB_ENABLE_COOKIE_AUTHENTICATION
-            messages::actionNotSupported(
-                asyncResp->res, "Setting Cookie when cookie-auth feature "
-                                "is disabled");
-            return;
-#endif
-            authMethodsConfig.cookie = *cookie;
-        }
-
-        if (sessionToken)
-        {
-#ifndef BMCWEB_ENABLE_SESSION_AUTHENTICATION
-            messages::actionNotSupported(
-                asyncResp->res,
-                "Setting SessionToken when session-auth feature "
-                "is disabled");
-            return;
-#endif
-            authMethodsConfig.sessionToken = *sessionToken;
-        }
-
-        if (xToken)
-        {
-#ifndef BMCWEB_ENABLE_XTOKEN_AUTHENTICATION
-            messages::actionNotSupported(
-                asyncResp->res, "Setting XToken when xtoken-auth feature "
-                                "is disabled");
-            return;
-#endif
-            authMethodsConfig.xtoken = *xToken;
-        }
-
-        if (tls)
-        {
-#ifndef BMCWEB_ENABLE_MUTUAL_TLS_AUTHENTICATION
-            messages::actionNotSupported(
-                asyncResp->res, "Setting TLS when mutual-tls-auth feature "
-                                "is disabled");
-            return;
-#endif
-            authMethodsConfig.tls = *tls;
-        }
-
-        if (!authMethodsConfig.basic && !authMethodsConfig.cookie &&
-            !authMethodsConfig.sessionToken && !authMethodsConfig.xtoken &&
-            !authMethodsConfig.tls)
-        {
-            // Do not allow user to disable everything
-            messages::actionNotSupported(asyncResp->res,
-                                         "of disabling all available methods");
-            return;
-        }
-
-        persistent_data::SessionStore::getInstance().updateAuthMethodsConfig(
-            authMethodsConfig);
-        // Save configuration immediately
-        persistent_data::getConfig().writeData();
-
-        messages::success(asyncResp->res);
-    }
-
-    /**
-     * @brief Get the required values from the given JSON, validates the
-     *        value and create the LDAP config object.
-     * @param input JSON data
-     * @param asyncResp pointer to the JSON response
-     * @param serverType Type of LDAP server(openLDAP/ActiveDirectory)
-     */
-
-    void handleLDAPPatch(nlohmann::json& input,
+void handleUserNamePatch(const std::string& username,
                          const std::shared_ptr<bmcweb::AsyncResp>& asyncResp,
-                         const std::string& serverType)
+                         const std::string& ldapServerElementName,
+                         const std::string& ldapConfigObject)
+{
+    crow::connections::systemBus->async_method_call(
+        [asyncResp, username,
+         ldapServerElementName](const boost::system::error_code ec) {
+            if (ec)
+            {
+                BMCWEB_LOG_DEBUG << "Error occurred in updating the username";
+                messages::internalError(asyncResp->res);
+                return;
+            }
+            asyncResp->res.jsonValue[ldapServerElementName]["Authentication"]
+                                    ["Username"] = username;
+            BMCWEB_LOG_DEBUG << "Updated the username";
+        },
+        ldapDbusService, ldapConfigObject, propertyInterface, "Set",
+        ldapConfigInterface, "LDAPBindDN", std::variant<std::string>(username));
+}
+
+/**
+ * @brief updates the LDAP password
+ * @param password : ldap password which needs to be updated.
+ * @param asyncResp pointer to the JSON response
+ * @param ldapServerElementName Type of LDAP
+ *        server(openLDAP/ActiveDirectory)
+ */
+
+void handlePasswordPatch(const std::string& password,
+                         const std::shared_ptr<bmcweb::AsyncResp>& asyncResp,
+                         const std::string& ldapServerElementName,
+                         const std::string& ldapConfigObject)
+{
+    crow::connections::systemBus->async_method_call(
+        [asyncResp, password,
+         ldapServerElementName](const boost::system::error_code ec) {
+            if (ec)
+            {
+                BMCWEB_LOG_DEBUG << "Error occurred in updating the password";
+                messages::internalError(asyncResp->res);
+                return;
+            }
+            asyncResp->res.jsonValue[ldapServerElementName]["Authentication"]
+                                    ["Password"] = "";
+            BMCWEB_LOG_DEBUG << "Updated the password";
+        },
+        ldapDbusService, ldapConfigObject, propertyInterface, "Set",
+        ldapConfigInterface, "LDAPBindDNPassword",
+        std::variant<std::string>(password));
+}
+
+/**
+ * @brief updates the LDAP BaseDN and updates the
+          json response with the new value.
+ * @param baseDNList baseDN list which needs to be updated.
+ * @param asyncResp pointer to the JSON response
+ * @param ldapServerElementName Type of LDAP
+ server(openLDAP/ActiveDirectory)
+ */
+
+void handleBaseDNPatch(const std::vector<std::string>& baseDNList,
+                       const std::shared_ptr<bmcweb::AsyncResp>& asyncResp,
+                       const std::string& ldapServerElementName,
+                       const std::string& ldapConfigObject)
+{
+    crow::connections::systemBus->async_method_call(
+        [asyncResp, baseDNList,
+         ldapServerElementName](const boost::system::error_code ec) {
+            if (ec)
+            {
+                BMCWEB_LOG_DEBUG << "Error Occurred in Updating the base DN";
+                messages::internalError(asyncResp->res);
+                return;
+            }
+            auto& serverTypeJson =
+                asyncResp->res.jsonValue[ldapServerElementName];
+            auto& searchSettingsJson =
+                serverTypeJson["LDAPService"]["SearchSettings"];
+            std::vector<std::string> modifiedBaseDNList = {baseDNList.front()};
+            searchSettingsJson["BaseDistinguishedNames"] = modifiedBaseDNList;
+            if (baseDNList.size() > 1)
+            {
+                messages::propertyValueModified(asyncResp->res,
+                                                "BaseDistinguishedNames",
+                                                baseDNList.front());
+            }
+            BMCWEB_LOG_DEBUG << "Updated the base DN";
+        },
+        ldapDbusService, ldapConfigObject, propertyInterface, "Set",
+        ldapConfigInterface, "LDAPBaseDN",
+        std::variant<std::string>(baseDNList.front()));
+}
+/**
+ * @brief updates the LDAP user name attribute and updates the
+          json response with the new value.
+ * @param userNameAttribute attribute to be updated.
+ * @param asyncResp pointer to the JSON response
+ * @param ldapServerElementName Type of LDAP
+ server(openLDAP/ActiveDirectory)
+ */
+
+void handleUserNameAttrPatch(
+    const std::string& userNameAttribute,
+    const std::shared_ptr<bmcweb::AsyncResp>& asyncResp,
+    const std::string& ldapServerElementName,
+    const std::string& ldapConfigObject)
+{
+    crow::connections::systemBus->async_method_call(
+        [asyncResp, userNameAttribute,
+         ldapServerElementName](const boost::system::error_code ec) {
+            if (ec)
+            {
+                BMCWEB_LOG_DEBUG << "Error Occurred in Updating the "
+                                    "username attribute";
+                messages::internalError(asyncResp->res);
+                return;
+            }
+            auto& serverTypeJson =
+                asyncResp->res.jsonValue[ldapServerElementName];
+            auto& searchSettingsJson =
+                serverTypeJson["LDAPService"]["SearchSettings"];
+            searchSettingsJson["UsernameAttribute"] = userNameAttribute;
+            BMCWEB_LOG_DEBUG << "Updated the user name attr.";
+        },
+        ldapDbusService, ldapConfigObject, propertyInterface, "Set",
+        ldapConfigInterface, "UserNameAttribute",
+        std::variant<std::string>(userNameAttribute));
+}
+/**
+ * @brief updates the LDAP group attribute and updates the
+          json response with the new value.
+ * @param groupsAttribute attribute to be updated.
+ * @param asyncResp pointer to the JSON response
+ * @param ldapServerElementName Type of LDAP
+ server(openLDAP/ActiveDirectory)
+ */
+
+void handleGroupNameAttrPatch(
+    const std::string& groupsAttribute,
+    const std::shared_ptr<bmcweb::AsyncResp>& asyncResp,
+    const std::string& ldapServerElementName,
+    const std::string& ldapConfigObject)
+{
+    crow::connections::systemBus->async_method_call(
+        [asyncResp, groupsAttribute,
+         ldapServerElementName](const boost::system::error_code ec) {
+            if (ec)
+            {
+                BMCWEB_LOG_DEBUG << "Error Occurred in Updating the "
+                                    "groupname attribute";
+                messages::internalError(asyncResp->res);
+                return;
+            }
+            auto& serverTypeJson =
+                asyncResp->res.jsonValue[ldapServerElementName];
+            auto& searchSettingsJson =
+                serverTypeJson["LDAPService"]["SearchSettings"];
+            searchSettingsJson["GroupsAttribute"] = groupsAttribute;
+            BMCWEB_LOG_DEBUG << "Updated the groupname attr";
+        },
+        ldapDbusService, ldapConfigObject, propertyInterface, "Set",
+        ldapConfigInterface, "GroupNameAttribute",
+        std::variant<std::string>(groupsAttribute));
+}
+/**
+ * @brief updates the LDAP service enable and updates the
+          json response with the new value.
+ * @param input JSON data.
+ * @param asyncResp pointer to the JSON response
+ * @param ldapServerElementName Type of LDAP
+ server(openLDAP/ActiveDirectory)
+ */
+
+void handleServiceEnablePatch(
+    bool serviceEnabled, const std::shared_ptr<bmcweb::AsyncResp>& asyncResp,
+    const std::string& ldapServerElementName,
+    const std::string& ldapConfigObject)
+{
+    crow::connections::systemBus->async_method_call(
+        [asyncResp, serviceEnabled,
+         ldapServerElementName](const boost::system::error_code ec) {
+            if (ec)
+            {
+                BMCWEB_LOG_DEBUG
+                    << "Error Occurred in Updating the service enable";
+                messages::internalError(asyncResp->res);
+                return;
+            }
+            asyncResp->res.jsonValue[ldapServerElementName]["ServiceEnabled"] =
+                serviceEnabled;
+            BMCWEB_LOG_DEBUG << "Updated Service enable = " << serviceEnabled;
+        },
+        ldapDbusService, ldapConfigObject, propertyInterface, "Set",
+        ldapEnableInterface, "Enabled", std::variant<bool>(serviceEnabled));
+}
+
+void handleAuthMethodsPatch(nlohmann::json& input,
+                            const std::shared_ptr<bmcweb::AsyncResp>& asyncResp)
+{
+    std::optional<bool> basicAuth;
+    std::optional<bool> cookie;
+    std::optional<bool> sessionToken;
+    std::optional<bool> xToken;
+    std::optional<bool> tls;
+
+    if (!json_util::readJson(input, asyncResp->res, "BasicAuth", basicAuth,
+                             "Cookie", cookie, "SessionToken", sessionToken,
+                             "XToken", xToken, "TLS", tls))
     {
-        std::string dbusObjectPath;
-        if (serverType == "ActiveDirectory")
+        BMCWEB_LOG_ERROR << "Cannot read values from AuthMethod tag";
+        return;
+    }
+
+    // Make a copy of methods configuration
+    persistent_data::AuthConfigMethods authMethodsConfig =
+        persistent_data::SessionStore::getInstance().getAuthMethodsConfig();
+
+    if (basicAuth)
+    {
+#ifndef BMCWEB_ENABLE_BASIC_AUTHENTICATION
+        messages::actionNotSupported(
+            asyncResp->res, "Setting BasicAuth when basic-auth feature "
+                            "is disabled");
+        return;
+#endif
+        authMethodsConfig.basic = *basicAuth;
+    }
+
+    if (cookie)
+    {
+#ifndef BMCWEB_ENABLE_COOKIE_AUTHENTICATION
+        messages::actionNotSupported(asyncResp->res,
+                                     "Setting Cookie when cookie-auth feature "
+                                     "is disabled");
+        return;
+#endif
+        authMethodsConfig.cookie = *cookie;
+    }
+
+    if (sessionToken)
+    {
+#ifndef BMCWEB_ENABLE_SESSION_AUTHENTICATION
+        messages::actionNotSupported(
+            asyncResp->res, "Setting SessionToken when session-auth feature "
+                            "is disabled");
+        return;
+#endif
+        authMethodsConfig.sessionToken = *sessionToken;
+    }
+
+    if (xToken)
+    {
+#ifndef BMCWEB_ENABLE_XTOKEN_AUTHENTICATION
+        messages::actionNotSupported(asyncResp->res,
+                                     "Setting XToken when xtoken-auth feature "
+                                     "is disabled");
+        return;
+#endif
+        authMethodsConfig.xtoken = *xToken;
+    }
+
+    if (tls)
+    {
+#ifndef BMCWEB_ENABLE_MUTUAL_TLS_AUTHENTICATION
+        messages::actionNotSupported(asyncResp->res,
+                                     "Setting TLS when mutual-tls-auth feature "
+                                     "is disabled");
+        return;
+#endif
+        authMethodsConfig.tls = *tls;
+    }
+
+    if (!authMethodsConfig.basic && !authMethodsConfig.cookie &&
+        !authMethodsConfig.sessionToken && !authMethodsConfig.xtoken &&
+        !authMethodsConfig.tls)
+    {
+        // Do not allow user to disable everything
+        messages::actionNotSupported(asyncResp->res,
+                                     "of disabling all available methods");
+        return;
+    }
+
+    persistent_data::SessionStore::getInstance().updateAuthMethodsConfig(
+        authMethodsConfig);
+    // Save configuration immediately
+    persistent_data::getConfig().writeData();
+
+    messages::success(asyncResp->res);
+}
+
+/**
+ * @brief Get the required values from the given JSON, validates the
+ *        value and create the LDAP config object.
+ * @param input JSON data
+ * @param asyncResp pointer to the JSON response
+ * @param serverType Type of LDAP server(openLDAP/ActiveDirectory)
+ */
+
+inline void handleLDAPPatch(nlohmann::json& input,
+                            const std::shared_ptr<bmcweb::AsyncResp>& asyncResp,
+                            const std::string& serverType)
+{
+    std::string dbusObjectPath;
+    if (serverType == "ActiveDirectory")
+    {
+        dbusObjectPath = adConfigObject;
+    }
+    else if (serverType == "LDAP")
+    {
+        dbusObjectPath = ldapConfigObjectName;
+    }
+    else
+    {
+        return;
+    }
+
+    std::optional<nlohmann::json> authentication;
+    std::optional<nlohmann::json> ldapService;
+    std::optional<std::vector<std::string>> serviceAddressList;
+    std::optional<bool> serviceEnabled;
+    std::optional<std::vector<std::string>> baseDNList;
+    std::optional<std::string> userNameAttribute;
+    std::optional<std::string> groupsAttribute;
+    std::optional<std::string> userName;
+    std::optional<std::string> password;
+    std::optional<std::vector<nlohmann::json>> remoteRoleMapData;
+
+    if (!json_util::readJson(input, asyncResp->res, "Authentication",
+                             authentication, "LDAPService", ldapService,
+                             "ServiceAddresses", serviceAddressList,
+                             "ServiceEnabled", serviceEnabled,
+                             "RemoteRoleMapping", remoteRoleMapData))
+    {
+        return;
+    }
+
+    if (authentication)
+    {
+        parseLDAPAuthenticationJson(*authentication, asyncResp, userName,
+                                    password);
+    }
+    if (ldapService)
+    {
+        parseLDAPServiceJson(*ldapService, asyncResp, baseDNList,
+                             userNameAttribute, groupsAttribute);
+    }
+    if (serviceAddressList)
+    {
+        if ((*serviceAddressList).size() == 0)
         {
-            dbusObjectPath = adConfigObject;
-        }
-        else if (serverType == "LDAP")
-        {
-            dbusObjectPath = ldapConfigObjectName;
-        }
-        else
-        {
+            messages::propertyValueNotInList(asyncResp->res, "[]",
+                                             "ServiceAddress");
             return;
         }
-
-        std::optional<nlohmann::json> authentication;
-        std::optional<nlohmann::json> ldapService;
-        std::optional<std::vector<std::string>> serviceAddressList;
-        std::optional<bool> serviceEnabled;
-        std::optional<std::vector<std::string>> baseDNList;
-        std::optional<std::string> userNameAttribute;
-        std::optional<std::string> groupsAttribute;
-        std::optional<std::string> userName;
-        std::optional<std::string> password;
-        std::optional<std::vector<nlohmann::json>> remoteRoleMapData;
-
-        if (!json_util::readJson(input, asyncResp->res, "Authentication",
-                                 authentication, "LDAPService", ldapService,
-                                 "ServiceAddresses", serviceAddressList,
-                                 "ServiceEnabled", serviceEnabled,
-                                 "RemoteRoleMapping", remoteRoleMapData))
+    }
+    if (baseDNList)
+    {
+        if ((*baseDNList).size() == 0)
         {
+            messages::propertyValueNotInList(asyncResp->res, "[]",
+                                             "BaseDistinguishedNames");
             return;
         }
+    }
 
-        if (authentication)
+    // nothing to update, then return
+    if (!userName && !password && !serviceAddressList && !baseDNList &&
+        !userNameAttribute && !groupsAttribute && !serviceEnabled &&
+        !remoteRoleMapData)
+    {
+        return;
+    }
+
+    // Get the existing resource first then keep modifying
+    // whenever any property gets updated.
+    getLDAPConfigData(serverType, [asyncResp, userName, password, baseDNList,
+                                   userNameAttribute, groupsAttribute,
+                                   serviceAddressList, serviceEnabled,
+                                   dbusObjectPath, remoteRoleMapData](
+                                      bool success,
+                                      const LDAPConfigData& confData,
+                                      const std::string& serverT) {
+        if (!success)
         {
-            parseLDAPAuthenticationJson(*authentication, asyncResp, userName,
-                                        password);
+            messages::internalError(asyncResp->res);
+            return;
         }
-        if (ldapService)
+        parseLDAPConfigData(asyncResp->res.jsonValue, confData, serverT);
+        if (confData.serviceEnabled)
         {
-            parseLDAPServiceJson(*ldapService, asyncResp, baseDNList,
-                                 userNameAttribute, groupsAttribute);
+            // Disable the service first and update the rest of
+            // the properties.
+            handleServiceEnablePatch(false, asyncResp, serverT, dbusObjectPath);
         }
+
         if (serviceAddressList)
         {
-            if ((*serviceAddressList).size() == 0)
-            {
-                messages::propertyValueNotInList(asyncResp->res, "[]",
-                                                 "ServiceAddress");
-                return;
-            }
+            handleServiceAddressPatch(*serviceAddressList, asyncResp, serverT,
+                                      dbusObjectPath);
         }
+        if (userName)
+        {
+            handleUserNamePatch(*userName, asyncResp, serverT, dbusObjectPath);
+        }
+        if (password)
+        {
+            handlePasswordPatch(*password, asyncResp, serverT, dbusObjectPath);
+        }
+
         if (baseDNList)
         {
-            if ((*baseDNList).size() == 0)
+            handleBaseDNPatch(*baseDNList, asyncResp, serverT, dbusObjectPath);
+        }
+        if (userNameAttribute)
+        {
+            handleUserNameAttrPatch(*userNameAttribute, asyncResp, serverT,
+                                    dbusObjectPath);
+        }
+        if (groupsAttribute)
+        {
+            handleGroupNameAttrPatch(*groupsAttribute, asyncResp, serverT,
+                                     dbusObjectPath);
+        }
+        if (serviceEnabled)
+        {
+            // if user has given the value as true then enable
+            // the service. if user has given false then no-op
+            // as service is already stopped.
+            if (*serviceEnabled)
             {
-                messages::propertyValueNotInList(asyncResp->res, "[]",
-                                                 "BaseDistinguishedNames");
-                return;
+                handleServiceEnablePatch(*serviceEnabled, asyncResp, serverT,
+                                         dbusObjectPath);
             }
         }
-
-        // nothing to update, then return
-        if (!userName && !password && !serviceAddressList && !baseDNList &&
-            !userNameAttribute && !groupsAttribute && !serviceEnabled &&
-            !remoteRoleMapData)
-        {
-            return;
-        }
-
-        // Get the existing resource first then keep modifying
-        // whenever any property gets updated.
-        getLDAPConfigData(
-            serverType, [this, asyncResp, userName, password, baseDNList,
-                         userNameAttribute, groupsAttribute, serviceAddressList,
-                         serviceEnabled, dbusObjectPath, remoteRoleMapData](
-                            bool success, const LDAPConfigData& confData,
-                            const std::string& serverT) {
-                if (!success)
-                {
-                    messages::internalError(asyncResp->res);
-                    return;
-                }
-                parseLDAPConfigData(asyncResp->res.jsonValue, confData,
-                                    serverT);
-                if (confData.serviceEnabled)
-                {
-                    // Disable the service first and update the rest of
-                    // the properties.
-                    handleServiceEnablePatch(false, asyncResp, serverT,
-                                             dbusObjectPath);
-                }
-
-                if (serviceAddressList)
-                {
-                    handleServiceAddressPatch(*serviceAddressList, asyncResp,
-                                              serverT, dbusObjectPath);
-                }
-                if (userName)
-                {
-                    handleUserNamePatch(*userName, asyncResp, serverT,
-                                        dbusObjectPath);
-                }
-                if (password)
-                {
-                    handlePasswordPatch(*password, asyncResp, serverT,
-                                        dbusObjectPath);
-                }
-
-                if (baseDNList)
-                {
-                    handleBaseDNPatch(*baseDNList, asyncResp, serverT,
-                                      dbusObjectPath);
-                }
-                if (userNameAttribute)
-                {
-                    handleUserNameAttrPatch(*userNameAttribute, asyncResp,
-                                            serverT, dbusObjectPath);
-                }
-                if (groupsAttribute)
-                {
-                    handleGroupNameAttrPatch(*groupsAttribute, asyncResp,
-                                             serverT, dbusObjectPath);
-                }
-                if (serviceEnabled)
-                {
-                    // if user has given the value as true then enable
-                    // the service. if user has given false then no-op
-                    // as service is already stopped.
-                    if (*serviceEnabled)
-                    {
-                        handleServiceEnablePatch(*serviceEnabled, asyncResp,
-                                                 serverT, dbusObjectPath);
-                    }
-                }
-                else
-                {
-                    // if user has not given the service enabled value
-                    // then revert it to the same state as it was
-                    // before.
-                    handleServiceEnablePatch(confData.serviceEnabled, asyncResp,
-                                             serverT, dbusObjectPath);
-                }
-
-                if (remoteRoleMapData)
-                {
-                    handleRoleMapPatch(asyncResp, confData.groupRoleList,
-                                       serverT, *remoteRoleMapData);
-                }
-            });
-    }
-
-    void doGet(const std::shared_ptr<bmcweb::AsyncResp>& asyncResp,
-               const crow::Request&, const std::vector<std::string>&) override
-    {
-        const persistent_data::AuthConfigMethods& authMethodsConfig =
-            persistent_data::SessionStore::getInstance().getAuthMethodsConfig();
-
-        asyncResp->res.jsonValue = {
-            {"@odata.id", "/redfish/v1/AccountService"},
-            {"@odata.type", "#AccountService."
-                            "v1_5_0.AccountService"},
-            {"Id", "AccountService"},
-            {"Name", "Account Service"},
-            {"Description", "Account Service"},
-            {"ServiceEnabled", true},
-            {"MaxPasswordLength", 20},
-            {"Accounts",
-             {{"@odata.id", "/redfish/v1/AccountService/Accounts"}}},
-            {"Roles", {{"@odata.id", "/redfish/v1/AccountService/Roles"}}},
-            {"Oem",
-             {{"OpenBMC",
-               {{"@odata.type", "#OemAccountService.v1_0_0.AccountService"},
-                {"AuthMethods",
-                 {
-                     {"BasicAuth", authMethodsConfig.basic},
-                     {"SessionToken", authMethodsConfig.sessionToken},
-                     {"XToken", authMethodsConfig.xtoken},
-                     {"Cookie", authMethodsConfig.cookie},
-                     {"TLS", authMethodsConfig.tls},
-                 }}}}}},
-            {"LDAP",
-             {{"Certificates",
-               {{"@odata.id",
-                 "/redfish/v1/AccountService/LDAP/Certificates"}}}}}};
-        crow::connections::systemBus->async_method_call(
-            [asyncResp](
-                const boost::system::error_code ec,
-                const std::vector<std::pair<
-                    std::string, std::variant<uint32_t, uint16_t, uint8_t>>>&
-                    propertiesList) {
-                if (ec)
-                {
-                    messages::internalError(asyncResp->res);
-                    return;
-                }
-                BMCWEB_LOG_DEBUG << "Got " << propertiesList.size()
-                                 << "properties for AccountService";
-                for (const std::pair<std::string,
-                                     std::variant<uint32_t, uint16_t, uint8_t>>&
-                         property : propertiesList)
-                {
-                    if (property.first == "MinPasswordLength")
-                    {
-                        const uint8_t* value =
-                            std::get_if<uint8_t>(&property.second);
-                        if (value != nullptr)
-                        {
-                            asyncResp->res.jsonValue["MinPasswordLength"] =
-                                *value;
-                        }
-                    }
-                    if (property.first == "AccountUnlockTimeout")
-                    {
-                        const uint32_t* value =
-                            std::get_if<uint32_t>(&property.second);
-                        if (value != nullptr)
-                        {
-                            asyncResp->res.jsonValue["AccountLockoutDuration"] =
-                                *value;
-                        }
-                    }
-                    if (property.first == "MaxLoginAttemptBeforeLockout")
-                    {
-                        const uint16_t* value =
-                            std::get_if<uint16_t>(&property.second);
-                        if (value != nullptr)
-                        {
-                            asyncResp->res
-                                .jsonValue["AccountLockoutThreshold"] = *value;
-                        }
-                    }
-                }
-            },
-            "xyz.openbmc_project.User.Manager", "/xyz/openbmc_project/user",
-            "org.freedesktop.DBus.Properties", "GetAll",
-            "xyz.openbmc_project.User.AccountPolicy");
-
-        auto callback = [asyncResp](bool success, LDAPConfigData& confData,
-                                    const std::string& ldapType) {
-            if (!success)
-            {
-                return;
-            }
-            parseLDAPConfigData(asyncResp->res.jsonValue, confData, ldapType);
-        };
-
-        getLDAPConfigData("LDAP", callback);
-        getLDAPConfigData("ActiveDirectory", callback);
-    }
-
-    void doPatch(const std::shared_ptr<bmcweb::AsyncResp>& asyncResp,
-                 const crow::Request& req,
-                 const std::vector<std::string>&) override
-    {
-        std::optional<uint32_t> unlockTimeout;
-        std::optional<uint16_t> lockoutThreshold;
-        std::optional<uint16_t> minPasswordLength;
-        std::optional<uint16_t> maxPasswordLength;
-        std::optional<nlohmann::json> ldapObject;
-        std::optional<nlohmann::json> activeDirectoryObject;
-        std::optional<nlohmann::json> oemObject;
-
-        if (!json_util::readJson(
-                req, asyncResp->res, "AccountLockoutDuration", unlockTimeout,
-                "AccountLockoutThreshold", lockoutThreshold,
-                "MaxPasswordLength", maxPasswordLength, "MinPasswordLength",
-                minPasswordLength, "LDAP", ldapObject, "ActiveDirectory",
-                activeDirectoryObject, "Oem", oemObject))
-        {
-            return;
-        }
-
-        if (minPasswordLength)
-        {
-            messages::propertyNotWritable(asyncResp->res, "MinPasswordLength");
-        }
-
-        if (maxPasswordLength)
-        {
-            messages::propertyNotWritable(asyncResp->res, "MaxPasswordLength");
-        }
-
-        if (ldapObject)
-        {
-            handleLDAPPatch(*ldapObject, asyncResp, "LDAP");
-        }
-
-        if (std::optional<nlohmann::json> oemOpenBMCObject;
-            oemObject && json_util::readJson(*oemObject, asyncResp->res,
-                                             "OpenBMC", oemOpenBMCObject))
-        {
-            if (std::optional<nlohmann::json> authMethodsObject;
-                oemOpenBMCObject &&
-                json_util::readJson(*oemOpenBMCObject, asyncResp->res,
-                                    "AuthMethods", authMethodsObject))
-            {
-                if (authMethodsObject)
-                {
-                    handleAuthMethodsPatch(*authMethodsObject, asyncResp);
-                }
-            }
-        }
-
-        if (activeDirectoryObject)
-        {
-            handleLDAPPatch(*activeDirectoryObject, asyncResp,
-                            "ActiveDirectory");
-        }
-
-        if (unlockTimeout)
-        {
-            crow::connections::systemBus->async_method_call(
-                [asyncResp](const boost::system::error_code ec) {
-                    if (ec)
-                    {
-                        messages::internalError(asyncResp->res);
-                        return;
-                    }
-                    messages::success(asyncResp->res);
-                },
-                "xyz.openbmc_project.User.Manager", "/xyz/openbmc_project/user",
-                "org.freedesktop.DBus.Properties", "Set",
-                "xyz.openbmc_project.User.AccountPolicy",
-                "AccountUnlockTimeout", std::variant<uint32_t>(*unlockTimeout));
-        }
-        if (lockoutThreshold)
-        {
-            crow::connections::systemBus->async_method_call(
-                [asyncResp](const boost::system::error_code ec) {
-                    if (ec)
-                    {
-                        messages::internalError(asyncResp->res);
-                        return;
-                    }
-                    messages::success(asyncResp->res);
-                },
-                "xyz.openbmc_project.User.Manager", "/xyz/openbmc_project/user",
-                "org.freedesktop.DBus.Properties", "Set",
-                "xyz.openbmc_project.User.AccountPolicy",
-                "MaxLoginAttemptBeforeLockout",
-                std::variant<uint16_t>(*lockoutThreshold));
-        }
-    }
-};
-
-class AccountsCollection : public Node
-{
-  public:
-    AccountsCollection(App& app) :
-        Node(app, "/redfish/v1/AccountService/Accounts/")
-    {
-        entityPrivileges = {
-            // According to the PrivilegeRegistry, GET should actually be
-            // "Login". A "Login" only privilege would return an empty "Members"
-            // list. Not going to worry about this since none of the defined
-            // roles are just "Login". E.g. Readonly is {"Login",
-            // "ConfigureSelf"}. In the rare event anyone defines a role that
-            // has Login but not ConfigureSelf, implement this.
-            {boost::beast::http::verb::get,
-             {{"ConfigureUsers"}, {"ConfigureSelf"}}},
-            {boost::beast::http::verb::head, {{"Login"}}},
-            {boost::beast::http::verb::patch, {{"ConfigureUsers"}}},
-            {boost::beast::http::verb::put, {{"ConfigureUsers"}}},
-            {boost::beast::http::verb::delete_, {{"ConfigureUsers"}}},
-            {boost::beast::http::verb::post, {{"ConfigureUsers"}}}};
-    }
-
-  private:
-    void doGet(const std::shared_ptr<bmcweb::AsyncResp>& asyncResp,
-               const crow::Request& req,
-               const std::vector<std::string>&) override
-    {
-        asyncResp->res.jsonValue = {
-            {"@odata.id", "/redfish/v1/AccountService/Accounts"},
-            {"@odata.type", "#ManagerAccountCollection."
-                            "ManagerAccountCollection"},
-            {"Name", "Accounts Collection"},
-            {"Description", "BMC User Accounts"}};
-
-        crow::connections::systemBus->async_method_call(
-            [asyncResp, &req, this](const boost::system::error_code ec,
-                                    const ManagedObjectType& users) {
-                if (ec)
-                {
-                    messages::internalError(asyncResp->res);
-                    return;
-                }
-
-                nlohmann::json& memberArray =
-                    asyncResp->res.jsonValue["Members"];
-                memberArray = nlohmann::json::array();
-
-                for (auto& userpath : users)
-                {
-                    std::string user = userpath.first.filename();
-                    if (user.empty())
-                    {
-                        messages::internalError(asyncResp->res);
-                        BMCWEB_LOG_ERROR << "Invalid firmware ID";
-
-                        return;
-                    }
-
-                    // As clarified by Redfish here:
-                    // https://redfishforum.com/thread/281/manageraccountcollection-change-allows-account-enumeration
-                    // Users without ConfigureUsers, only see their own account.
-                    // Users with ConfigureUsers, see all accounts.
-                    if (req.session->username == user ||
-                        isAllowedWithoutConfigureSelf(req))
-                    {
-                        memberArray.push_back(
-                            {{"@odata.id",
-                              "/redfish/v1/AccountService/Accounts/" + user}});
-                    }
-                }
-                asyncResp->res.jsonValue["Members@odata.count"] =
-                    memberArray.size();
-            },
-            "xyz.openbmc_project.User.Manager", "/xyz/openbmc_project/user",
-            "org.freedesktop.DBus.ObjectManager", "GetManagedObjects");
-    }
-    void doPost(const std::shared_ptr<bmcweb::AsyncResp>& asyncResp,
-                const crow::Request& req,
-                const std::vector<std::string>&) override
-    {
-        std::string username;
-        std::string password;
-        std::optional<std::string> roleId("User");
-        std::optional<bool> enabled = true;
-        if (!json_util::readJson(req, asyncResp->res, "UserName", username,
-                                 "Password", password, "RoleId", roleId,
-                                 "Enabled", enabled))
-        {
-            return;
-        }
-
-        std::string priv = getPrivilegeFromRoleId(*roleId);
-        if (priv.empty())
-        {
-            messages::propertyValueNotInList(asyncResp->res, *roleId, "RoleId");
-            return;
-        }
-        // TODO: Following override will be reverted once support in
-        // phosphor-user-manager is added. In order to avoid dependency issues,
-        // this is added in bmcweb, which will removed, once
-        // phosphor-user-manager supports priv-noaccess.
-        if (priv == "priv-noaccess")
-        {
-            roleId = "";
-        }
         else
         {
-            roleId = priv;
+            // if user has not given the service enabled value
+            // then revert it to the same state as it was
+            // before.
+            handleServiceEnablePatch(confData.serviceEnabled, asyncResp,
+                                     serverT, dbusObjectPath);
         }
 
-        // Reading AllGroups property
-        crow::connections::systemBus->async_method_call(
-            [asyncResp, username, password{std::move(password)}, roleId,
-             enabled](const boost::system::error_code ec,
-                      const std::variant<std::vector<std::string>>& allGroups) {
-                if (ec)
+        if (remoteRoleMapData)
+        {
+            handleRoleMapPatch(asyncResp, confData.groupRoleList, serverT,
+                               *remoteRoleMapData);
+        }
+    });
+}
+
+inline void updateUserProperties(std::shared_ptr<bmcweb::AsyncResp> asyncResp,
+                                 const std::string& username,
+                                 std::optional<std::string> password,
+                                 std::optional<bool> enabled,
+                                 std::optional<std::string> roleId,
+                                 std::optional<bool> locked)
+{
+    std::string dbusObjectPath = "/xyz/openbmc_project/user/" + username;
+    dbus::utility::escapePathForDbus(dbusObjectPath);
+
+    dbus::utility::checkDbusPathExists(
+        dbusObjectPath,
+        [dbusObjectPath(std::move(dbusObjectPath)), username,
+         password(std::move(password)), roleId(std::move(roleId)), enabled,
+         locked, asyncResp{std::move(asyncResp)}](int rc) {
+            if (!rc)
+            {
+                messages::resourceNotFound(
+                    asyncResp->res, "#ManagerAccount.v1_4_0.ManagerAccount",
+                    username);
+                return;
+            }
+
+            if (password)
+            {
+                int retval = pamUpdatePassword(username, *password);
+
+                if (retval == PAM_USER_UNKNOWN)
                 {
-                    BMCWEB_LOG_DEBUG << "ERROR with async_method_call";
+                    messages::resourceNotFound(
+                        asyncResp->res, "#ManagerAccount.v1_4_0.ManagerAccount",
+                        username);
+                }
+                else if (retval == PAM_AUTHTOK_ERR)
+                {
+                    // If password is invalid
+                    messages::propertyValueFormatError(asyncResp->res,
+                                                       *password, "Password");
+                    BMCWEB_LOG_ERROR << "pamUpdatePassword Failed";
+                }
+                else if (retval != PAM_SUCCESS)
+                {
                     messages::internalError(asyncResp->res);
                     return;
                 }
+            }
 
-                const std::vector<std::string>* allGroupsList =
-                    std::get_if<std::vector<std::string>>(&allGroups);
+            if (enabled)
+            {
+                crow::connections::systemBus->async_method_call(
+                    [asyncResp](const boost::system::error_code ec) {
+                        if (ec)
+                        {
+                            BMCWEB_LOG_ERROR << "D-Bus responses error: " << ec;
+                            messages::internalError(asyncResp->res);
+                            return;
+                        }
+                        messages::success(asyncResp->res);
+                        return;
+                    },
+                    "xyz.openbmc_project.User.Manager", dbusObjectPath.c_str(),
+                    "org.freedesktop.DBus.Properties", "Set",
+                    "xyz.openbmc_project.User.Attributes", "UserEnabled",
+                    std::variant<bool>{*enabled});
+            }
 
-                if (allGroupsList == nullptr || allGroupsList->empty())
+            if (roleId)
+            {
+                std::string priv = getPrivilegeFromRoleId(*roleId);
+                if (priv.empty())
                 {
-                    messages::internalError(asyncResp->res);
+                    messages::propertyValueNotInList(asyncResp->res, *roleId,
+                                                     "RoleId");
+                    return;
+                }
+                if (priv == "priv-noaccess")
+                {
+                    priv = "";
+                }
+
+                crow::connections::systemBus->async_method_call(
+                    [asyncResp](const boost::system::error_code ec) {
+                        if (ec)
+                        {
+                            BMCWEB_LOG_ERROR << "D-Bus responses error: " << ec;
+                            messages::internalError(asyncResp->res);
+                            return;
+                        }
+                        messages::success(asyncResp->res);
+                    },
+                    "xyz.openbmc_project.User.Manager", dbusObjectPath.c_str(),
+                    "org.freedesktop.DBus.Properties", "Set",
+                    "xyz.openbmc_project.User.Attributes", "UserPrivilege",
+                    std::variant<std::string>{priv});
+            }
+
+            if (locked)
+            {
+                // admin can unlock the account which is locked by
+                // successive authentication failures but admin should
+                // not be allowed to lock an account.
+                if (*locked)
+                {
+                    messages::propertyValueNotInList(asyncResp->res, "true",
+                                                     "Locked");
                     return;
                 }
 
                 crow::connections::systemBus->async_method_call(
-                    [asyncResp, username,
-                     password](const boost::system::error_code ec2,
-                               sdbusplus::message::message& m) {
-                        if (ec2)
+                    [asyncResp](const boost::system::error_code ec) {
+                        if (ec)
                         {
-                            userErrorMessageHandler(m.get_error(), asyncResp,
-                                                    username, "");
+                            BMCWEB_LOG_ERROR << "D-Bus responses error: " << ec;
+                            messages::internalError(asyncResp->res);
                             return;
                         }
-
-                        if (pamUpdatePassword(username, password) !=
-                            PAM_SUCCESS)
-                        {
-                            // At this point we have a user that's been created,
-                            // but the password set failed.Something is wrong,
-                            // so delete the user that we've already created
-                            crow::connections::systemBus->async_method_call(
-                                [asyncResp, password](
-                                    const boost::system::error_code ec3) {
-                                    if (ec3)
-                                    {
-                                        messages::internalError(asyncResp->res);
-                                        return;
-                                    }
-
-                                    // If password is invalid
-                                    messages::propertyValueFormatError(
-                                        asyncResp->res, password, "Password");
-                                },
-                                "xyz.openbmc_project.User.Manager",
-                                "/xyz/openbmc_project/user/" + username,
-                                "xyz.openbmc_project.Object.Delete", "Delete");
-
-                            BMCWEB_LOG_ERROR << "pamUpdatePassword Failed";
-                            return;
-                        }
-
-                        messages::created(asyncResp->res);
-                        asyncResp->res.addHeader(
-                            "Location",
-                            "/redfish/v1/AccountService/Accounts/" + username);
+                        messages::success(asyncResp->res);
+                        return;
                     },
-                    "xyz.openbmc_project.User.Manager",
-                    "/xyz/openbmc_project/user",
-                    "xyz.openbmc_project.User.Manager", "CreateUser", username,
-                    *allGroupsList, *roleId, *enabled);
-            },
-            "xyz.openbmc_project.User.Manager", "/xyz/openbmc_project/user",
-            "org.freedesktop.DBus.Properties", "Get",
-            "xyz.openbmc_project.User.Manager", "AllGroups");
-    }
-};
+                    "xyz.openbmc_project.User.Manager", dbusObjectPath.c_str(),
+                    "org.freedesktop.DBus.Properties", "Set",
+                    "xyz.openbmc_project.User.Attributes",
+                    "UserLockedForFailedAttempt", std::variant<bool>{*locked});
+            }
+        });
+}
 
-class ManagerAccount : public Node
+inline void requestAccountServiceRoutes(App& app)
 {
-  public:
-    ManagerAccount(App& app) :
-        Node(app, "/redfish/v1/AccountService/Accounts/<str>/", std::string())
-    {
-        entityPrivileges = {
-            {boost::beast::http::verb::get,
-             {{"ConfigureUsers"}, {"ConfigureManager"}, {"ConfigureSelf"}}},
-            {boost::beast::http::verb::head, {{"Login"}}},
-            {boost::beast::http::verb::patch,
-             {{"ConfigureUsers"}, {"ConfigureSelf"}}},
-            {boost::beast::http::verb::put, {{"ConfigureUsers"}}},
-            {boost::beast::http::verb::delete_, {{"ConfigureUsers"}}},
-            {boost::beast::http::verb::post, {{"ConfigureUsers"}}}};
-    }
 
-  private:
-    void doGet(const std::shared_ptr<bmcweb::AsyncResp>& asyncResp,
-               const crow::Request& req,
-               const std::vector<std::string>& params) override
-    {
-        if (params.size() != 1)
-        {
-            messages::internalError(asyncResp->res);
-            return;
-        }
+    BMCWEB_ROUTE(app, "/redfish/v1/AccountService/")
+        .privileges({{"Login"}})
+        .methods(
+            boost::beast::http::verb::get)([](const crow::Request& /* req */,
+                                              const std::shared_ptr<
+                                                  bmcweb::AsyncResp>& asyncResp)
+                                               -> void {
+            const persistent_data::AuthConfigMethods& authMethodsConfig =
+                persistent_data::SessionStore::getInstance()
+                    .getAuthMethodsConfig();
 
-        // Perform a proper ConfigureSelf authority check.  If the
-        // user is operating on an account not their own, then their
-        // ConfigureSelf privilege does not apply.  In this case,
-        // perform the authority check again without the user's
-        // ConfigureSelf privilege.
-        if (req.session->username != params[0])
-        {
-            if (!isAllowedWithoutConfigureSelf(req))
-            {
-                BMCWEB_LOG_DEBUG << "GET Account denied access";
-                messages::insufficientPrivilege(asyncResp->res);
-                return;
-            }
-        }
-
-        crow::connections::systemBus->async_method_call(
-            [asyncResp, accountName{std::string(params[0])}](
-                const boost::system::error_code ec,
-                const ManagedObjectType& users) {
-                if (ec)
-                {
-                    messages::internalError(asyncResp->res);
-                    return;
-                }
-                auto userIt = users.begin();
-
-                for (; userIt != users.end(); userIt++)
-                {
-                    if (boost::ends_with(userIt->first.str, "/" + accountName))
-                    {
-                        break;
-                    }
-                }
-                if (userIt == users.end())
-                {
-                    messages::resourceNotFound(asyncResp->res, "ManagerAccount",
-                                               accountName);
-                    return;
-                }
-
-                asyncResp->res.jsonValue = {
-                    {"@odata.type", "#ManagerAccount.v1_4_0.ManagerAccount"},
-                    {"Name", "User Account"},
-                    {"Description", "User Account"},
-                    {"Password", nullptr},
-                    {"AccountTypes", {"Redfish"}}};
-
-                for (const auto& interface : userIt->second)
-                {
-                    if (interface.first ==
-                        "xyz.openbmc_project.User.Attributes")
-                    {
-                        for (const auto& property : interface.second)
-                        {
-                            if (property.first == "UserEnabled")
-                            {
-                                const bool* userEnabled =
-                                    std::get_if<bool>(&property.second);
-                                if (userEnabled == nullptr)
-                                {
-                                    BMCWEB_LOG_ERROR
-                                        << "UserEnabled wasn't a bool";
-                                    messages::internalError(asyncResp->res);
-                                    return;
-                                }
-                                asyncResp->res.jsonValue["Enabled"] =
-                                    *userEnabled;
-                            }
-                            else if (property.first ==
-                                     "UserLockedForFailedAttempt")
-                            {
-                                const bool* userLocked =
-                                    std::get_if<bool>(&property.second);
-                                if (userLocked == nullptr)
-                                {
-                                    BMCWEB_LOG_ERROR << "UserLockedForF"
-                                                        "ailedAttempt "
-                                                        "wasn't a bool";
-                                    messages::internalError(asyncResp->res);
-                                    return;
-                                }
-                                asyncResp->res.jsonValue["Locked"] =
-                                    *userLocked;
-                                asyncResp->res.jsonValue
-                                    ["Locked@Redfish.AllowableValues"] = {
-                                    "false"}; // can only unlock accounts
-                            }
-                            else if (property.first == "UserPrivilege")
-                            {
-                                const std::string* userPrivPtr =
-                                    std::get_if<std::string>(&property.second);
-                                if (userPrivPtr == nullptr)
-                                {
-                                    BMCWEB_LOG_ERROR
-                                        << "UserPrivilege wasn't a "
-                                           "string";
-                                    messages::internalError(asyncResp->res);
-                                    return;
-                                }
-                                std::string role =
-                                    getRoleIdFromPrivilege(*userPrivPtr);
-                                if (role.empty())
-                                {
-                                    BMCWEB_LOG_ERROR << "Invalid user role";
-                                    messages::internalError(asyncResp->res);
-                                    return;
-                                }
-                                asyncResp->res.jsonValue["RoleId"] = role;
-
-                                asyncResp->res.jsonValue["Links"]["Role"] = {
-                                    {"@odata.id", "/redfish/v1/AccountService/"
-                                                  "Roles/" +
-                                                      role}};
-                            }
-                            else if (property.first == "UserPasswordExpired")
-                            {
-                                const bool* userPasswordExpired =
-                                    std::get_if<bool>(&property.second);
-                                if (userPasswordExpired == nullptr)
-                                {
-                                    BMCWEB_LOG_ERROR << "UserPassword"
-                                                        "Expired "
-                                                        "wasn't a bool";
-                                    messages::internalError(asyncResp->res);
-                                    return;
-                                }
-                                asyncResp->res
-                                    .jsonValue["PasswordChangeRequired"] =
-                                    *userPasswordExpired;
-                            }
-                        }
-                    }
-                }
-
-                asyncResp->res.jsonValue["@odata.id"] =
-                    "/redfish/v1/AccountService/Accounts/" + accountName;
-                asyncResp->res.jsonValue["Id"] = accountName;
-                asyncResp->res.jsonValue["UserName"] = accountName;
-            },
-            "xyz.openbmc_project.User.Manager", "/xyz/openbmc_project/user",
-            "org.freedesktop.DBus.ObjectManager", "GetManagedObjects");
-    }
-
-    void doPatch(const std::shared_ptr<bmcweb::AsyncResp>& asyncResp,
-                 const crow::Request& req,
-                 const std::vector<std::string>& params) override
-    {
-
-        if (params.size() != 1)
-        {
-            messages::internalError(asyncResp->res);
-            return;
-        }
-
-        std::optional<std::string> newUserName;
-        std::optional<std::string> password;
-        std::optional<bool> enabled;
-        std::optional<std::string> roleId;
-        std::optional<bool> locked;
-        if (!json_util::readJson(req, asyncResp->res, "UserName", newUserName,
-                                 "Password", password, "RoleId", roleId,
-                                 "Enabled", enabled, "Locked", locked))
-        {
-            return;
-        }
-
-        const std::string& username = params[0];
-
-        // Perform a proper ConfigureSelf authority check.  If the
-        // session is being used to PATCH a property other than
-        // Password, then the ConfigureSelf privilege does not apply.
-        // If the user is operating on an account not their own, then
-        // their ConfigureSelf privilege does not apply.  In either
-        // case, perform the authority check again without the user's
-        // ConfigureSelf privilege.
-        if ((username != req.session->username) ||
-            (newUserName || enabled || roleId || locked))
-        {
-            if (!isAllowedWithoutConfigureSelf(req))
-            {
-                BMCWEB_LOG_WARNING << "PATCH Password denied access";
-                asyncResp->res.clear();
-                messages::insufficientPrivilege(asyncResp->res);
-                return;
-            }
-        }
-
-        // if user name is not provided in the patch method or if it
-        // matches the user name in the URI, then we are treating it as updating
-        // user properties other then username. If username provided doesn't
-        // match the URI, then we are treating this as user rename request.
-        if (!newUserName || (newUserName.value() == username))
-        {
-            updateUserProperties(asyncResp, username, password, enabled, roleId,
-                                 locked);
-            return;
-        }
-        crow::connections::systemBus->async_method_call(
-            [this, asyncResp, username, password(std::move(password)),
-             roleId(std::move(roleId)), enabled,
-             newUser{std::string(*newUserName)},
-             locked](const boost::system::error_code ec,
-                     sdbusplus::message::message& m) {
-                if (ec)
-                {
-                    userErrorMessageHandler(m.get_error(), asyncResp, newUser,
-                                            username);
-                    return;
-                }
-
-                updateUserProperties(asyncResp, newUser, password, enabled,
-                                     roleId, locked);
-            },
-            "xyz.openbmc_project.User.Manager", "/xyz/openbmc_project/user",
-            "xyz.openbmc_project.User.Manager", "RenameUser", username,
-            *newUserName);
-    }
-
-    void updateUserProperties(std::shared_ptr<bmcweb::AsyncResp> asyncResp,
-                              const std::string& username,
-                              std::optional<std::string> password,
-                              std::optional<bool> enabled,
-                              std::optional<std::string> roleId,
-                              std::optional<bool> locked)
-    {
-        std::string dbusObjectPath = "/xyz/openbmc_project/user/" + username;
-        dbus::utility::escapePathForDbus(dbusObjectPath);
-
-        dbus::utility::checkDbusPathExists(
-            dbusObjectPath,
-            [dbusObjectPath(std::move(dbusObjectPath)), username,
-             password(std::move(password)), roleId(std::move(roleId)), enabled,
-             locked, asyncResp{std::move(asyncResp)}](int rc) {
-                if (!rc)
-                {
-                    messages::resourceNotFound(
-                        asyncResp->res, "#ManagerAccount.v1_4_0.ManagerAccount",
-                        username);
-                    return;
-                }
-
-                if (password)
-                {
-                    int retval = pamUpdatePassword(username, *password);
-
-                    if (retval == PAM_USER_UNKNOWN)
-                    {
-                        messages::resourceNotFound(
-                            asyncResp->res,
-                            "#ManagerAccount.v1_4_0.ManagerAccount", username);
-                    }
-                    else if (retval == PAM_AUTHTOK_ERR)
-                    {
-                        // If password is invalid
-                        messages::propertyValueFormatError(
-                            asyncResp->res, *password, "Password");
-                        BMCWEB_LOG_ERROR << "pamUpdatePassword Failed";
-                    }
-                    else if (retval != PAM_SUCCESS)
+            asyncResp->res.jsonValue = {
+                {"@odata.id", "/redfish/v1/AccountService"},
+                {"@odata.type", "#AccountService."
+                                "v1_5_0.AccountService"},
+                {"Id", "AccountService"},
+                {"Name", "Account Service"},
+                {"Description", "Account Service"},
+                {"ServiceEnabled", true},
+                {"MaxPasswordLength", 20},
+                {"Accounts",
+                 {{"@odata.id", "/redfish/v1/AccountService/Accounts"}}},
+                {"Roles", {{"@odata.id", "/redfish/v1/AccountService/Roles"}}},
+                {"Oem",
+                 {{"OpenBMC",
+                   {{"@odata.type", "#OemAccountService.v1_0_0.AccountService"},
+                    {"AuthMethods",
+                     {
+                         {"BasicAuth", authMethodsConfig.basic},
+                         {"SessionToken", authMethodsConfig.sessionToken},
+                         {"XToken", authMethodsConfig.xtoken},
+                         {"Cookie", authMethodsConfig.cookie},
+                         {"TLS", authMethodsConfig.tls},
+                     }}}}}},
+                {"LDAP",
+                 {{"Certificates",
+                   {{"@odata.id",
+                     "/redfish/v1/AccountService/LDAP/Certificates"}}}}}};
+            crow::connections::systemBus->async_method_call(
+                [asyncResp](
+                    const boost::system::error_code ec,
+                    const std::vector<
+                        std::pair<std::string,
+                                  std::variant<uint32_t, uint16_t, uint8_t>>>&
+                        propertiesList) {
+                    if (ec)
                     {
                         messages::internalError(asyncResp->res);
                         return;
                     }
-                }
-
-                if (enabled)
-                {
-                    crow::connections::systemBus->async_method_call(
-                        [asyncResp](const boost::system::error_code ec) {
-                            if (ec)
+                    BMCWEB_LOG_DEBUG << "Got " << propertiesList.size()
+                                     << "properties for AccountService";
+                    for (const std::pair<std::string,
+                                         std::variant<uint32_t, uint16_t,
+                                                      uint8_t>>& property :
+                         propertiesList)
+                    {
+                        if (property.first == "MinPasswordLength")
+                        {
+                            const uint8_t* value =
+                                std::get_if<uint8_t>(&property.second);
+                            if (value != nullptr)
                             {
-                                BMCWEB_LOG_ERROR << "D-Bus responses error: "
-                                                 << ec;
-                                messages::internalError(asyncResp->res);
-                                return;
+                                asyncResp->res.jsonValue["MinPasswordLength"] =
+                                    *value;
                             }
-                            messages::success(asyncResp->res);
+                        }
+                        if (property.first == "AccountUnlockTimeout")
+                        {
+                            const uint32_t* value =
+                                std::get_if<uint32_t>(&property.second);
+                            if (value != nullptr)
+                            {
+                                asyncResp->res
+                                    .jsonValue["AccountLockoutDuration"] =
+                                    *value;
+                            }
+                        }
+                        if (property.first == "MaxLoginAttemptBeforeLockout")
+                        {
+                            const uint16_t* value =
+                                std::get_if<uint16_t>(&property.second);
+                            if (value != nullptr)
+                            {
+                                asyncResp->res
+                                    .jsonValue["AccountLockoutThreshold"] =
+                                    *value;
+                            }
+                        }
+                    }
+                },
+                "xyz.openbmc_project.User.Manager", "/xyz/openbmc_project/user",
+                "org.freedesktop.DBus.Properties", "GetAll",
+                "xyz.openbmc_project.User.AccountPolicy");
+
+            auto callback = [asyncResp](bool success, LDAPConfigData& confData,
+                                        const std::string& ldapType) {
+                if (!success)
+                {
+                    return;
+                }
+                parseLDAPConfigData(asyncResp->res.jsonValue, confData,
+                                    ldapType);
+            };
+
+            getLDAPConfigData("LDAP", callback);
+            getLDAPConfigData("ActiveDirectory", callback);
+        });
+
+    BMCWEB_ROUTE(app, "/redfish/v1/AccountService/Accounts/")
+        // According to the PrivilegeRegistry, GET should actually be
+        // "Login". A "Login" only privilege would return an empty "Members"
+        // list. Not going to worry about this since none of the defined
+        // roles are just "Login". E.g. Readonly is {"Login",
+        // "ConfigureSelf"}. In the rare event anyone defines a role that
+        // has Login but not ConfigureSelf, implement this.
+        .privileges({{"ConfigureUsers"}, {"ConfigureSelf"}})
+        .methods(boost::beast::http::verb::get)(
+            [](const crow::Request& req,
+               const std::shared_ptr<bmcweb::AsyncResp>& asyncResp) -> void {
+                asyncResp->res.jsonValue = {
+                    {"@odata.id", "/redfish/v1/AccountService/Accounts"},
+                    {"@odata.type", "#ManagerAccountCollection."
+                                    "ManagerAccountCollection"},
+                    {"Name", "Accounts Collection"},
+                    {"Description", "BMC User Accounts"}};
+
+                Privileges requiredPermissionsToSeeNonSelf = {
+                    {"ConfigureUsers"}};
+                Privileges effectiveUserPrivileges =
+                    redfish::getUserPrivileges(req.userRole);
+                bool userCanSeeAllAccounts =
+                    effectiveUserPrivileges.isSupersetOf(
+                        requiredPermissionsToSeeNonSelf);
+
+                std::string thisUser = req.session->username;
+
+                crow::connections::systemBus->async_method_call(
+                    [asyncResp, userCanSeeAllAccounts,
+                     thisUser](const boost::system::error_code ec,
+                               const ManagedObjectType& users) {
+                        if (ec)
+                        {
+                            messages::internalError(asyncResp->res);
                             return;
-                        },
-                        "xyz.openbmc_project.User.Manager",
-                        dbusObjectPath.c_str(),
-                        "org.freedesktop.DBus.Properties", "Set",
-                        "xyz.openbmc_project.User.Attributes", "UserEnabled",
-                        std::variant<bool>{*enabled});
-                }
+                        }
 
-                if (roleId)
-                {
-                    std::string priv = getPrivilegeFromRoleId(*roleId);
-                    if (priv.empty())
-                    {
-                        messages::propertyValueNotInList(asyncResp->res,
-                                                         *roleId, "RoleId");
-                        return;
-                    }
-                    if (priv == "priv-noaccess")
-                    {
-                        priv = "";
-                    }
+                        nlohmann::json& memberArray =
+                            asyncResp->res.jsonValue["Members"];
+                        memberArray = nlohmann::json::array();
 
-                    crow::connections::systemBus->async_method_call(
-                        [asyncResp](const boost::system::error_code ec) {
-                            if (ec)
+                        for (auto& userpath : users)
+                        {
+                            std::string user = userpath.first.filename();
+                            if (user.empty())
                             {
-                                BMCWEB_LOG_ERROR << "D-Bus responses error: "
-                                                 << ec;
                                 messages::internalError(asyncResp->res);
+                                BMCWEB_LOG_ERROR << "Invalid firmware ID";
+
                                 return;
                             }
-                            messages::success(asyncResp->res);
-                        },
-                        "xyz.openbmc_project.User.Manager",
-                        dbusObjectPath.c_str(),
-                        "org.freedesktop.DBus.Properties", "Set",
-                        "xyz.openbmc_project.User.Attributes", "UserPrivilege",
-                        std::variant<std::string>{priv});
-                }
 
-                if (locked)
-                {
-                    // admin can unlock the account which is locked by
-                    // successive authentication failures but admin should
-                    // not be allowed to lock an account.
-                    if (*locked)
-                    {
-                        messages::propertyValueNotInList(asyncResp->res, "true",
-                                                         "Locked");
-                        return;
-                    }
-
-                    crow::connections::systemBus->async_method_call(
-                        [asyncResp](const boost::system::error_code ec) {
-                            if (ec)
+                            // As clarified by Redfish here:
+                            // https://redfishforum.com/thread/281/manageraccountcollection-change-allows-account-enumeration
+                            // Users without ConfigureUsers, only see their own
+                            // account. Users with ConfigureUsers, see all
+                            // accounts.
+                            if (thisUser == user || userCanSeeAllAccounts)
                             {
-                                BMCWEB_LOG_ERROR << "D-Bus responses error: "
-                                                 << ec;
-                                messages::internalError(asyncResp->res);
-                                return;
+                                memberArray.push_back(
+                                    {{"@odata.id",
+                                      "/redfish/v1/AccountService/Accounts/" +
+                                          user}});
                             }
-                            messages::success(asyncResp->res);
-                            return;
-                        },
-                        "xyz.openbmc_project.User.Manager",
-                        dbusObjectPath.c_str(),
-                        "org.freedesktop.DBus.Properties", "Set",
-                        "xyz.openbmc_project.User.Attributes",
-                        "UserLockedForFailedAttempt",
-                        std::variant<bool>{*locked});
-                }
+                        }
+                        asyncResp->res.jsonValue["Members@odata.count"] =
+                            memberArray.size();
+                    },
+                    "xyz.openbmc_project.User.Manager",
+                    "/xyz/openbmc_project/user",
+                    "org.freedesktop.DBus.ObjectManager", "GetManagedObjects");
             });
-    }
 
-    void doDelete(const std::shared_ptr<bmcweb::AsyncResp>& asyncResp,
-                  const crow::Request&,
-                  const std::vector<std::string>& params) override
-    {
+    BMCWEB_ROUTE(app, "/redfish/v1/AccountService/Accounts/")
+        .privileges({{"ConfigureUsers"}})
+        .methods(boost::beast::http::verb::post)([](const crow::Request& req,
+                                                    const std::shared_ptr<
+                                                        bmcweb::AsyncResp>&
+                                                        asyncResp) -> void {
+            std::string username;
+            std::string password;
+            std::optional<std::string> roleId("User");
+            std::optional<bool> enabled = true;
+            if (!json_util::readJson(req, asyncResp->res, "UserName", username,
+                                     "Password", password, "RoleId", roleId,
+                                     "Enabled", enabled))
+            {
+                return;
+            }
 
-        if (params.size() != 1)
-        {
-            messages::internalError(asyncResp->res);
-            return;
-        }
+            std::string priv = getPrivilegeFromRoleId(*roleId);
+            if (priv.empty())
+            {
+                messages::propertyValueNotInList(asyncResp->res, *roleId,
+                                                 "RoleId");
+                return;
+            }
+            // TODO: Following override will be reverted once support in
+            // phosphor-user-manager is added. In order to avoid dependency
+            // issues, this is added in bmcweb, which will removed, once
+            // phosphor-user-manager supports priv-noaccess.
+            if (priv == "priv-noaccess")
+            {
+                roleId = "";
+            }
+            else
+            {
+                roleId = priv;
+            }
 
-        const std::string userPath = "/xyz/openbmc_project/user/" + params[0];
+            // Reading AllGroups property
+            crow::connections::systemBus->async_method_call(
+                [asyncResp, username, password{std::move(password)}, roleId,
+                 enabled](
+                    const boost::system::error_code ec,
+                    const std::variant<std::vector<std::string>>& allGroups) {
+                    if (ec)
+                    {
+                        BMCWEB_LOG_DEBUG << "ERROR with async_method_call";
+                        messages::internalError(asyncResp->res);
+                        return;
+                    }
 
-        crow::connections::systemBus->async_method_call(
-            [asyncResp,
-             username{params[0]}](const boost::system::error_code ec) {
-                if (ec)
+                    const std::vector<std::string>* allGroupsList =
+                        std::get_if<std::vector<std::string>>(&allGroups);
+
+                    if (allGroupsList == nullptr || allGroupsList->empty())
+                    {
+                        messages::internalError(asyncResp->res);
+                        return;
+                    }
+
+                    crow::connections::systemBus->async_method_call(
+                        [asyncResp, username,
+                         password](const boost::system::error_code ec2,
+                                   sdbusplus::message::message& m) {
+                            if (ec2)
+                            {
+                                userErrorMessageHandler(
+                                    m.get_error(), asyncResp, username, "");
+                                return;
+                            }
+
+                            if (pamUpdatePassword(username, password) !=
+                                PAM_SUCCESS)
+                            {
+                                // At this point we have a user that's been
+                                // created, but the password set
+                                // failed.Something is wrong, so delete the user
+                                // that we've already created
+                                crow::connections::systemBus->async_method_call(
+                                    [asyncResp, password](
+                                        const boost::system::error_code ec3) {
+                                        if (ec3)
+                                        {
+                                            messages::internalError(
+                                                asyncResp->res);
+                                            return;
+                                        }
+
+                                        // If password is invalid
+                                        messages::propertyValueFormatError(
+                                            asyncResp->res, password,
+                                            "Password");
+                                    },
+                                    "xyz.openbmc_project.User.Manager",
+                                    "/xyz/openbmc_project/user/" + username,
+                                    "xyz.openbmc_project.Object.Delete",
+                                    "Delete");
+
+                                BMCWEB_LOG_ERROR << "pamUpdatePassword Failed";
+                                return;
+                            }
+
+                            messages::created(asyncResp->res);
+                            asyncResp->res.addHeader(
+                                "Location",
+                                "/redfish/v1/AccountService/Accounts/" +
+                                    username);
+                        },
+                        "xyz.openbmc_project.User.Manager",
+                        "/xyz/openbmc_project/user",
+                        "xyz.openbmc_project.User.Manager", "CreateUser",
+                        username, *allGroupsList, *roleId, *enabled);
+                },
+                "xyz.openbmc_project.User.Manager", "/xyz/openbmc_project/user",
+                "org.freedesktop.DBus.Properties", "Get",
+                "xyz.openbmc_project.User.Manager", "AllGroups");
+        });
+
+    BMCWEB_ROUTE(app, "/redfish/v1/AccountService/Accounts/<str>/")
+        .privileges(
+            {{"ConfigureUsers"}, {"ConfigureManager"}, {"ConfigureSelf"}})
+        .methods(
+            boost::beast::http::verb::
+                get)([](const crow::Request& req,
+                        const std::shared_ptr<bmcweb::AsyncResp>& asyncResp,
+                        const std::string& accountName) -> void {
+            if (req.session->username != accountName)
+            {
+                // At this point we've determined that the user is trying to
+                // modify a user that isn't them.  We need to verify that they
+                // have permissions to modify other users, so re-run the auth
+                // check with the same permissions, minus ConfigureSelf.
+                Privileges effectiveUserPrivileges =
+                    redfish::getUserPrivileges(req.userRole);
+                Privileges requiredPermissionsToChangeNonSelf = {
+                    {"ConfigureUsers"}, {"ConfigureManager"}};
+                if (!effectiveUserPrivileges.isSupersetOf(
+                        requiredPermissionsToChangeNonSelf))
                 {
-                    messages::resourceNotFound(
-                        asyncResp->res, "#ManagerAccount.v1_4_0.ManagerAccount",
-                        username);
+                    BMCWEB_LOG_DEBUG << "GET Account denied access";
+                    messages::insufficientPrivilege(asyncResp->res);
+                    return;
+                }
+            }
+
+            crow::connections::systemBus->async_method_call(
+                [asyncResp, accountName](const boost::system::error_code ec,
+                                         const ManagedObjectType& users) {
+                    if (ec)
+                    {
+                        messages::internalError(asyncResp->res);
+                        return;
+                    }
+                    auto userIt = users.begin();
+
+                    for (; userIt != users.end(); userIt++)
+                    {
+                        if (boost::ends_with(userIt->first.str,
+                                             "/" + accountName))
+                        {
+                            break;
+                        }
+                    }
+                    if (userIt == users.end())
+                    {
+                        messages::resourceNotFound(
+                            asyncResp->res, "ManagerAccount", accountName);
+                        return;
+                    }
+
+                    asyncResp->res.jsonValue = {
+                        {"@odata.type",
+                         "#ManagerAccount.v1_4_0.ManagerAccount"},
+                        {"Name", "User Account"},
+                        {"Description", "User Account"},
+                        {"Password", nullptr},
+                        {"AccountTypes", {"Redfish"}}};
+
+                    for (const auto& interface : userIt->second)
+                    {
+                        if (interface.first ==
+                            "xyz.openbmc_project.User.Attributes")
+                        {
+                            for (const auto& property : interface.second)
+                            {
+                                if (property.first == "UserEnabled")
+                                {
+                                    const bool* userEnabled =
+                                        std::get_if<bool>(&property.second);
+                                    if (userEnabled == nullptr)
+                                    {
+                                        BMCWEB_LOG_ERROR
+                                            << "UserEnabled wasn't a bool";
+                                        messages::internalError(asyncResp->res);
+                                        return;
+                                    }
+                                    asyncResp->res.jsonValue["Enabled"] =
+                                        *userEnabled;
+                                }
+                                else if (property.first ==
+                                         "UserLockedForFailedAttempt")
+                                {
+                                    const bool* userLocked =
+                                        std::get_if<bool>(&property.second);
+                                    if (userLocked == nullptr)
+                                    {
+                                        BMCWEB_LOG_ERROR << "UserLockedForF"
+                                                            "ailedAttempt "
+                                                            "wasn't a bool";
+                                        messages::internalError(asyncResp->res);
+                                        return;
+                                    }
+                                    asyncResp->res.jsonValue["Locked"] =
+                                        *userLocked;
+                                    asyncResp->res.jsonValue
+                                        ["Locked@Redfish.AllowableValues"] = {
+                                        "false"}; // can only unlock accounts
+                                }
+                                else if (property.first == "UserPrivilege")
+                                {
+                                    const std::string* userPrivPtr =
+                                        std::get_if<std::string>(
+                                            &property.second);
+                                    if (userPrivPtr == nullptr)
+                                    {
+                                        BMCWEB_LOG_ERROR
+                                            << "UserPrivilege wasn't a "
+                                               "string";
+                                        messages::internalError(asyncResp->res);
+                                        return;
+                                    }
+                                    std::string role =
+                                        getRoleIdFromPrivilege(*userPrivPtr);
+                                    if (role.empty())
+                                    {
+                                        BMCWEB_LOG_ERROR << "Invalid user role";
+                                        messages::internalError(asyncResp->res);
+                                        return;
+                                    }
+                                    asyncResp->res.jsonValue["RoleId"] = role;
+
+                                    asyncResp->res.jsonValue["Links"]["Role"] =
+                                        {{"@odata.id",
+                                          "/redfish/v1/AccountService/"
+                                          "Roles/" +
+                                              role}};
+                                }
+                                else if (property.first ==
+                                         "UserPasswordExpired")
+                                {
+                                    const bool* userPasswordExpired =
+                                        std::get_if<bool>(&property.second);
+                                    if (userPasswordExpired == nullptr)
+                                    {
+                                        BMCWEB_LOG_ERROR << "UserPassword"
+                                                            "Expired "
+                                                            "wasn't a bool";
+                                        messages::internalError(asyncResp->res);
+                                        return;
+                                    }
+                                    asyncResp->res
+                                        .jsonValue["PasswordChangeRequired"] =
+                                        *userPasswordExpired;
+                                }
+                            }
+                        }
+                    }
+
+                    asyncResp->res.jsonValue["@odata.id"] =
+                        "/redfish/v1/AccountService/Accounts/" + accountName;
+                    asyncResp->res.jsonValue["Id"] = accountName;
+                    asyncResp->res.jsonValue["UserName"] = accountName;
+                },
+                "xyz.openbmc_project.User.Manager", "/xyz/openbmc_project/user",
+                "org.freedesktop.DBus.ObjectManager", "GetManagedObjects");
+        });
+
+    BMCWEB_ROUTE(app, "/redfish/v1/AccountService/Accounts/<str>/")
+        .privileges({{"ConfigureUsers"}, {"ConfigureSelf"}})
+        .methods(boost::beast::http::verb::patch)(
+            [](const crow::Request& req,
+               const std::shared_ptr<bmcweb::AsyncResp>& asyncResp,
+               const std::string& username) -> void {
+                std::optional<std::string> newUserName;
+                std::optional<std::string> password;
+                std::optional<bool> enabled;
+                std::optional<std::string> roleId;
+                std::optional<bool> locked;
+                if (!json_util::readJson(req, asyncResp->res, "UserName",
+                                         newUserName, "Password", password,
+                                         "RoleId", roleId, "Enabled", enabled,
+                                         "Locked", locked))
+                {
                     return;
                 }
 
-                messages::accountRemoved(asyncResp->res);
-            },
-            "xyz.openbmc_project.User.Manager", userPath,
-            "xyz.openbmc_project.Object.Delete", "Delete");
-    }
-};
+                // Perform a proper ConfigureSelf authority check.  If the
+                // session is being used to PATCH a property other than
+                // Password, then the ConfigureSelf privilege does not apply.
+                // If the user is operating on an account not their own, then
+                // their ConfigureSelf privilege does not apply.  In either
+                // case, perform the authority check again without the user's
+                // ConfigureSelf privilege.
+                if ((username != req.session->username))
+                {
+                    Privileges requiredPermissionsToChangeNonSelf = {
+                        {"ConfigureUsers"}};
+                    Privileges effectiveUserPrivileges =
+                        redfish::getUserPrivileges(req.userRole);
+
+                    if (!effectiveUserPrivileges.isSupersetOf(
+                            requiredPermissionsToChangeNonSelf))
+                    {
+                        messages::insufficientPrivilege(asyncResp->res);
+                        return;
+                    }
+                }
+
+                // if user name is not provided in the patch method or if it
+                // matches the user name in the URI, then we are treating it as
+                // updating user properties other then username. If username
+                // provided doesn't match the URI, then we are treating this as
+                // user rename request.
+                if (!newUserName || (newUserName.value() == username))
+                {
+                    updateUserProperties(asyncResp, username, password, enabled,
+                                         roleId, locked);
+                    return;
+                }
+                crow::connections::systemBus->async_method_call(
+                    [asyncResp, username, password(std::move(password)),
+                     roleId(std::move(roleId)), enabled,
+                     newUser{std::string(*newUserName)},
+                     locked](const boost::system::error_code ec,
+                             sdbusplus::message::message& m) {
+                        if (ec)
+                        {
+                            userErrorMessageHandler(m.get_error(), asyncResp,
+                                                    newUser, username);
+                            return;
+                        }
+
+                        updateUserProperties(asyncResp, newUser, password,
+                                             enabled, roleId, locked);
+                    },
+                    "xyz.openbmc_project.User.Manager",
+                    "/xyz/openbmc_project/user",
+                    "xyz.openbmc_project.User.Manager", "RenameUser", username,
+                    *newUserName);
+            });
+
+    BMCWEB_ROUTE(app, "/redfish/v1/AccountService/Accounts/<str>/")
+        .privileges({{"ConfigureUsers"}})
+        .methods(boost::beast::http::verb::delete_)(
+            [](const crow::Request& /*req*/,
+               const std::shared_ptr<bmcweb::AsyncResp>& asyncResp,
+               const std::string& username) -> void {
+                const std::string userPath =
+                    "/xyz/openbmc_project/user/" + username;
+
+                crow::connections::systemBus->async_method_call(
+                    [asyncResp, username](const boost::system::error_code ec) {
+                        if (ec)
+                        {
+                            messages::resourceNotFound(
+                                asyncResp->res,
+                                "#ManagerAccount.v1_4_0.ManagerAccount",
+                                username);
+                            return;
+                        }
+
+                        messages::accountRemoved(asyncResp->res);
+                    },
+                    "xyz.openbmc_project.User.Manager", userPath,
+                    "xyz.openbmc_project.Object.Delete", "Delete");
+            });
+}
 
 } // namespace redfish