Redfish: Populate the LDAP property in AccountService schema

With this commit get request on account service gets the
LDAP/AD configuration.

RemoteRoleMapping under LDAP property will be supported by
other commit.

TestedBy: 1) Run the redfish - validator
                 => when there is no LDAP configuration
                 => After LDAP Configuration.
          2) GET request through redfish
             /redfish/v1/AccountService

Detailed test cases are at the following location.
https://pastebin.com/ibX5nyAc

Change-Id: I718d1eb4b40d3a626440487ac9a63d8c96721cee
Signed-off-by: Ratan Gupta <ratagupt@linux.vnet.ibm.com>
diff --git a/redfish-core/lib/account_service.hpp b/redfish-core/lib/account_service.hpp
index 6d38c06..4a38185 100644
--- a/redfish-core/lib/account_service.hpp
+++ b/redfish-core/lib/account_service.hpp
@@ -25,11 +25,40 @@
 namespace redfish
 {
 
+constexpr const char* ldapConfigObject =
+    "/xyz/openbmc_project/user/ldap/openldap";
+constexpr const char* ldapRootObject = "/xyz/openbmc_project/user/ldap";
+constexpr const char* ldapDbusService = "xyz.openbmc_project.Ldap.Config";
+constexpr const char* ldapConfigInterface =
+    "xyz.openbmc_project.User.Ldap.Config";
+constexpr const char* ldapCreateInterface =
+    "xyz.openbmc_project.User.Ldap.Create";
+constexpr const char* ldapEnableInterface = "xyz.openbmc_project.Object.Enable";
+constexpr const char* dbusObjManagerIntf = "org.freedesktop.DBus.ObjectManager";
+constexpr const char* propertyInterface = "org.freedesktop.DBus.Properties";
+constexpr const char* mapperBusName = "xyz.openbmc_project.ObjectMapper";
+constexpr const char* mapperObjectPath = "/xyz/openbmc_project/object_mapper";
+constexpr const char* mapperIntf = "xyz.openbmc_project.ObjectMapper";
+
+struct LDAPConfigData
+{
+    std::string uri{};
+    std::string bindDN{};
+    std::string baseDN{};
+    std::string searchScope{};
+    std::string serverType{};
+    bool serviceEnabled = false;
+    std::string userNameAttribute{};
+    std::string groupAttribute{};
+};
+
 using ManagedObjectType = std::vector<std::pair<
     sdbusplus::message::object_path,
     boost::container::flat_map<
         std::string, boost::container::flat_map<
                          std::string, std::variant<bool, std::string>>>>>;
+using GetObjectType =
+    std::vector<std::pair<std::string, std::vector<std::string>>>;
 
 inline std::string getPrivilegeFromRoleId(std::string_view role)
 {
@@ -72,6 +101,149 @@
     return "";
 }
 
+void parseLDAPConfigData(nlohmann::json& json_response,
+                         const LDAPConfigData& confData)
+{
+    std::string service = "LDAPService";
+    json_response["LDAP"] = {
+        {"AccountProviderType", service},
+        {"AccountProviderType@Redfish.AllowableValues",
+         nlohmann::json::array({service})},
+        {"ServiceEnabled", confData.serviceEnabled},
+        {"ServiceAddresses", nlohmann::json::array({confData.uri})},
+        {"Authentication",
+         {{"AuthenticationType", "UsernameAndPassword"},
+          {"AuthenticationType@Redfish.AllowableValues",
+           nlohmann::json::array({"UsernameAndPassword"})},
+          {"Username", confData.bindDN},
+          {"Password", nullptr}}},
+        {"LDAPService",
+         {{"SearchSettings",
+           {{"BaseDistinguishedNames",
+             nlohmann::json::array({confData.baseDN})},
+            {"UsernameAttribute", confData.userNameAttribute},
+            {"GroupsAttribute", confData.groupAttribute}}}}},
+    };
+}
+
+/**
+ * Function that retrieves all properties for LDAP config object
+ * into JSON
+ */
+template <typename CallbackFunc>
+inline void getLDAPConfigData(const std::string& ldapType,
+                              CallbackFunc&& callback)
+{
+    auto getConfig = [callback,
+                      ldapType](const boost::system::error_code error_code,
+                                const ManagedObjectType& ldapObjects) {
+        LDAPConfigData confData{};
+        if (error_code)
+        {
+            callback(false, confData);
+            BMCWEB_LOG_ERROR << "D-Bus responses error: " << error_code;
+            return;
+        }
+        std::string ldapConfigObjectStr = std::string(ldapConfigObject);
+        std::string ldapEnableInterfaceStr = std::string(ldapEnableInterface);
+        std::string ldapConfigInterfaceStr = std::string(ldapConfigInterface);
+        for (const auto& object : ldapObjects)
+        {
+            if (object.first == ldapConfigObjectStr)
+            {
+                for (const auto& interface : object.second)
+                {
+                    if (interface.first == ldapEnableInterfaceStr)
+                    {
+                        // rest of the properties are string.
+                        for (const auto& property : interface.second)
+                        {
+                            if (property.first == "Enabled")
+                            {
+                                const bool* value =
+                                    std::get_if<bool>(&property.second);
+                                if (value == nullptr)
+                                {
+                                    continue;
+                                }
+                                confData.serviceEnabled = *value;
+                                break;
+                            }
+                        }
+                    }
+                    else if (interface.first == ldapConfigInterfaceStr)
+                    {
+
+                        for (const auto& property : interface.second)
+                        {
+                            const std::string* value =
+                                std::get_if<std::string>(&property.second);
+                            if (value == nullptr)
+                            {
+                                continue;
+                            }
+                            if (property.first == "LDAPServerURI")
+                            {
+                                confData.uri = *value;
+                            }
+                            else if (property.first == "LDAPBindDN")
+                            {
+                                confData.bindDN = *value;
+                            }
+                            else if (property.first == "LDAPBaseDN")
+                            {
+                                confData.baseDN = *value;
+                            }
+                            else if (property.first == "LDAPSearchScope")
+                            {
+                                confData.searchScope = *value;
+                            }
+                            else if (property.first == "LDAPType")
+                            {
+                                confData.serverType = *value;
+                            }
+                            else if (property.first == "GroupNameAttribute")
+                            {
+                                confData.groupAttribute = *value;
+                            }
+                            else if (property.first == "UserNameAttribute")
+                            {
+                                confData.userNameAttribute = *value;
+                            }
+                        }
+                    }
+                }
+
+                callback(true, confData);
+                break;
+            }
+        }
+    };
+    auto getServiceName = [callback, getConfig(std::move(getConfig))](
+                              const boost::system::error_code ec,
+                              const GetObjectType& resp) {
+        LDAPConfigData confData{};
+        if (ec || resp.empty())
+        {
+            BMCWEB_LOG_ERROR
+                << "DBUS response error during getting of service name: " << ec;
+            callback(false, confData);
+            return;
+        }
+        std::string service = resp.begin()->first;
+        crow::connections::systemBus->async_method_call(
+            std::move(getConfig), service, ldapRootObject, dbusObjManagerIntf,
+            "GetManagedObjects");
+    };
+
+    const std::array<std::string, 2> interfaces = {ldapEnableInterface,
+                                                   ldapConfigInterface};
+
+    crow::connections::systemBus->async_method_call(
+        std::move(getServiceName), mapperBusName, mapperObjectPath, mapperIntf,
+        "GetObject", ldapConfigObject, interfaces);
+}
+
 class AccountService : public Node
 {
   public:
@@ -97,7 +269,7 @@
                                "$metadata#AccountService.AccountService"},
             {"@odata.id", "/redfish/v1/AccountService"},
             {"@odata.type", "#AccountService."
-                            "v1_1_0.AccountService"},
+                            "v1_3_1.AccountService"},
             {"Id", "AccountService"},
             {"Name", "Account Service"},
             {"Description", "Account Service"},
@@ -159,7 +331,15 @@
             "xyz.openbmc_project.User.Manager", "/xyz/openbmc_project/user",
             "org.freedesktop.DBus.Properties", "GetAll",
             "xyz.openbmc_project.User.AccountPolicy");
+
+        std::string ldapType = "LDAP";
+        getLDAPConfigData(
+            ldapType,
+            [asyncResp, ldapType](bool success, LDAPConfigData& confData) {
+                parseLDAPConfigData(asyncResp->res.jsonValue, confData);
+            });
     }
+
     void doPatch(crow::Response& res, const crow::Request& req,
                  const std::vector<std::string>& params) override
     {