Redfish: Add support to upload/replace/view LDAP Certificate

Implements CertificateCollection schema to upload/list
existing LDAP certificates

Implements Certificate schema to view existing LDAP
certificate

Implements ReplaceCertificate action to replace existing
LDAP certificate.

Tested:
1. Tested schema with validator and no issues
2. Privilege map for certificate service is not yet pubished
3. POST on /redfish/v1/AccountService/LDAP/Certificates
curl -c cjar -b cjar -k -H "X-Auth-Token: $bmc_token" -H "Content-Type: application/octet-stream" -X POST -T testcert.pem https://${bmc}/redfish/v1/AccountServie/LDAP/Certificates
{
  "@odata.context": "/redfish/v1/$metadata#Certificate.Certificate",
  "@odata.id": "/redfish/v1/AccountService/LDAP/Certificates/1",
  "@odata.type": "#Certificate.v1_0_0.Certificate",
  "CertificateString": "-----BEGIN CERTIFICATE---------END CERTIFICATE-----\n",
  "Description": "LDAP Certificate",
  "Id": "1",
  "Issuer": {
    "City": "SomeCity",
    "CommonName": "www.company.com",
    "Country": "US",
    "Organization": "MyCompany",
    "State": "VA",
    "organizationUnit": "MyDivision"
  },
  "KeyUsage": [
    "KeyAgreement",
    "ServerAuthentication"
  ],
  "Name": "LDAP Certificate",
  "Subject": {
    "City": "SomeCity",
    "CommonName": "www.company.com",
    "Organization": "MyCompany",
    "State": "VA",
    "organizationUnit": "MyDivision"
  },
  "ValidNotAfter": "2029-03-14T02:11:02+00:00",
  "ValidNotBefore": "2019-03-17T02:11:02+00:00"
}

4. GET on /redfish/v1/AccountService/LDAP/Certificates/
{
  "@odata.context":
  "/redfish/v1/$metadata#CertificateCollection.CertificateCollection",
  "@odata.id": "/redfish/v1/AccountService/LDAP/Certificates",
  "@odata.type": "#CertificateCollection.CertificatesCollection",
  "Description": "A Collection of LDAP certificate instances",
  "Members": [
    {
      "@odata.id": "/redfish/v1/AccountService/LDAP/Certificates/1"
    }
  ],
  "Members@odata.count": 1,
  "Name": "LDAP Certificate Collection"
}

5.GET on /redfish/v1/CertificateService/CertificateLocations/
{
  "@odata.context":
  "/redfish/v1/$metadata#CertificateLocations.CertificateLocations",
  "@odata.id": "/redfish/v1/CertificateService/CertificateLocations",
  "@odata.type": "#CertificateLocations.v1_0_0.CertificateLocations",
  "Description": "Defines a resource that an administrator can use in order to
   locate all certificates installed on a given service",
  "Id": "CertificateLocations",
  "Links": {
    "Certificates": [
      {
        "@odata.id":
"/redfish/v1/Managers/bmc/NetworkProtocol/HTTPS/Certificates/1"
      },
      {
        "@odata.id": "/redfish/v1/AccountService/LDAP/Certificates/1"
      }
    ],
    "Certificates@odata.count": 2
  },
  "Name": "Certificate Locations"
}

6.GET on  /redfish/v1/AccountService/LDAP/Certificates/1
{
  "@odata.context": "/redfish/v1/$metadata#Certificate.Certificate",
  "@odata.id": "/redfish/v1/AccountService/LDAP/Certificates/1",
  "@odata.type": "#Certificate.v1_0_0.Certificate",
  "CertificateString": "-----BEGINCERTIFICATE-----\n ... -----ENDCERTIFICATE-----\n",
  "CertificateType": "PEM",
  "Description": "LDAP Certificate",
  "Id": "1",
  "Issuer": {
    "CommonName": "localhost",
    "Organization": "openbmc-project.xyz"
  },
  "KeyUsage": [],
  "Name": "LDAP Certificate",
  "Subject": {
    "CommonName": "localhost"
}

7.Replace certificate
POST on /redfish/v1/CertificateService/Actions/CertificateService.ReplaceCertificate/
{
  "@odata.context": "/redfish/v1/$metadata#Certificate.Certificate",
  "@odata.id": "/redfish/v1/AccountService/LDAP/Certificates/1",
  "@odata.type": "#Certificate.v1_0_0.Certificate",
 ...
}
8.GET on AccountService
curl -k -H "X-Auth-Token: $bmc_token" -X GET
https://${bmc}/redfish/v1/AccountService
{
  "@odata.context": "/redfish/v1/$metadata#AccountService.AccountService",
  "@odata.id": "/redfish/v1/AccountService",
  "@odata.type": "#AccountService.v1_4_0.AccountService",
  "Id": "AccountService",
  "LDAP": {
    "AccountProviderType": "LDAPService",
    "Certificates": {
      "@odata.id": "/redfish/v1/AccountService/LDAP/Certificates"
    },

Change-Id: I056a4cea8b0377e156b660984857cdfadbfe1b2c
Signed-off-by: Marri Devender Rao <devenrao@in.ibm.com>
diff --git a/redfish-core/lib/certificate_service.hpp b/redfish-core/lib/certificate_service.hpp
index 8045e4e..4a077c8 100644
--- a/redfish-core/lib/certificate_service.hpp
+++ b/redfish-core/lib/certificate_service.hpp
@@ -32,6 +32,11 @@
 constexpr char const *mapperBusName = "xyz.openbmc_project.ObjectMapper";
 constexpr char const *mapperObjectPath = "/xyz/openbmc_project/object_mapper";
 constexpr char const *mapperIntf = "xyz.openbmc_project.ObjectMapper";
+constexpr char const *ldapObjectPath = "/xyz/openbmc_project/certs/client/ldap";
+constexpr char const *httpsServiceName =
+    "xyz.openbmc_project.Certs.Manager.Server.Https";
+constexpr char const *ldapServiceName =
+    "xyz.openbmc_project.Certs.Manager.Client.Ldap";
 } // namespace certs
 
 /**
@@ -80,6 +85,7 @@
         res.end();
     }
 }; // CertificateService
+
 /**
  * @brief Find the ID specified in the URL
  * Finds the numbers specified after the last "/" in the URL and returns.
@@ -98,7 +104,7 @@
         char *endPtr;
         std::string_view str = url.substr(found + 1);
         long value = std::strtol(str.data(), &endPtr, 10);
-        if (endPtr != &str.back())
+        if (endPtr != str.end())
         {
             return -1;
         }
@@ -237,7 +243,8 @@
  */
 static void getCertificateProperties(
     const std::shared_ptr<AsyncResp> &asyncResp, const std::string &objectPath,
-    long certId, const std::string &certURL, const std::string &name)
+    const std::string &service, long certId, const std::string &certURL,
+    const std::string &name)
 {
     using PropertyType =
         std::variant<std::string, uint64_t, std::vector<std::string>>;
@@ -245,123 +252,96 @@
     BMCWEB_LOG_DEBUG << "getCertificateProperties Path=" << objectPath
                      << " certId=" << certId << " certURl=" << certURL;
     crow::connections::systemBus->async_method_call(
-        [asyncResp, objectPath, certURL, certId,
-         name](const boost::system::error_code ec, const GetObjectType &resp) {
+        [asyncResp, certURL, certId, name](const boost::system::error_code ec,
+                                           const PropertiesMap &properties) {
             if (ec)
             {
                 BMCWEB_LOG_ERROR << "DBUS response error: " << ec;
                 messages::internalError(asyncResp->res);
                 return;
             }
-            if (resp.size() > 1 || resp.empty())
+            asyncResp->res.jsonValue = {
+                {"@odata.id", certURL},
+                {"@odata.type", "#Certificate.v1_0_0.Certificate"},
+                {"@odata.context",
+                 "/redfish/v1/$metadata#Certificate.Certificate"},
+                {"Id", std::to_string(certId)},
+                {"Name", name},
+                {"Description", name}};
+            for (const auto &property : properties)
             {
-                BMCWEB_LOG_ERROR << "Invalid number of objects found "
-                                 << resp.size();
-                messages::internalError(asyncResp->res);
-                return;
+                if (property.first == "CertificateString")
+                {
+                    asyncResp->res.jsonValue["CertificateString"] = "";
+                    const std::string *value =
+                        std::get_if<std::string>(&property.second);
+                    if (value)
+                    {
+                        asyncResp->res.jsonValue["CertificateString"] = *value;
+                    }
+                }
+                else if (property.first == "KeyUsage")
+                {
+                    nlohmann::json &keyUsage =
+                        asyncResp->res.jsonValue["KeyUsage"];
+                    keyUsage = nlohmann::json::array();
+                    const std::vector<std::string> *value =
+                        std::get_if<std::vector<std::string>>(&property.second);
+                    if (value)
+                    {
+                        for (const std::string &usage : *value)
+                        {
+                            keyUsage.push_back(usage);
+                        }
+                    }
+                }
+                else if (property.first == "Issuer")
+                {
+                    const std::string *value =
+                        std::get_if<std::string>(&property.second);
+                    if (value)
+                    {
+                        updateCertIssuerOrSubject(
+                            asyncResp->res.jsonValue["Issuer"], *value);
+                    }
+                }
+                else if (property.first == "Subject")
+                {
+                    const std::string *value =
+                        std::get_if<std::string>(&property.second);
+                    if (value)
+                    {
+                        updateCertIssuerOrSubject(
+                            asyncResp->res.jsonValue["Subject"], *value);
+                    }
+                }
+                else if (property.first == "ValidNotAfter")
+                {
+                    const uint64_t *value =
+                        std::get_if<uint64_t>(&property.second);
+                    if (value)
+                    {
+                        std::time_t time = static_cast<std::time_t>(*value);
+                        asyncResp->res.jsonValue["ValidNotAfter"] =
+                            crow::utility::getDateTime(time);
+                    }
+                }
+                else if (property.first == "ValidNotBefore")
+                {
+                    const uint64_t *value =
+                        std::get_if<uint64_t>(&property.second);
+                    if (value)
+                    {
+                        std::time_t time = static_cast<std::time_t>(*value);
+                        asyncResp->res.jsonValue["ValidNotBefore"] =
+                            crow::utility::getDateTime(time);
+                    }
+                }
             }
-            const std::string &service = resp.begin()->first;
-            crow::connections::systemBus->async_method_call(
-                [asyncResp, certURL, certId,
-                 name](const boost::system::error_code ec,
-                       const PropertiesMap &properties) {
-                    if (ec)
-                    {
-                        BMCWEB_LOG_ERROR << "DBUS response error: " << ec;
-                        messages::internalError(asyncResp->res);
-                        return;
-                    }
-                    asyncResp->res.jsonValue = {
-                        {"@odata.id", certURL},
-                        {"@odata.type", "#Certificate.v1_0_0.Certificate"},
-                        {"@odata.context",
-                         "/redfish/v1/$metadata#Certificate.Certificate"},
-                        {"Id", std::to_string(certId)},
-                        {"Name", name},
-                        {"Description", name}};
-                    for (const auto &property : properties)
-                    {
-                        if (property.first == "CertificateString")
-                        {
-                            asyncResp->res.jsonValue["CertificateString"] = "";
-                            const std::string *value =
-                                std::get_if<std::string>(&property.second);
-                            if (value)
-                            {
-                                asyncResp->res.jsonValue["CertificateString"] =
-                                    *value;
-                            }
-                        }
-                        else if (property.first == "KeyUsage")
-                        {
-                            nlohmann::json &keyUsage =
-                                asyncResp->res.jsonValue["KeyUsage"];
-                            keyUsage = nlohmann::json::array();
-                            const std::vector<std::string> *value =
-                                std::get_if<std::vector<std::string>>(
-                                    &property.second);
-                            if (value)
-                            {
-                                for (const std::string &usage : *value)
-                                {
-                                    keyUsage.push_back(usage);
-                                }
-                            }
-                        }
-                        else if (property.first == "Issuer")
-                        {
-                            const std::string *value =
-                                std::get_if<std::string>(&property.second);
-                            if (value)
-                            {
-                                updateCertIssuerOrSubject(
-                                    asyncResp->res.jsonValue["Issuer"], *value);
-                            }
-                        }
-                        else if (property.first == "Subject")
-                        {
-                            const std::string *value =
-                                std::get_if<std::string>(&property.second);
-                            if (value)
-                            {
-                                updateCertIssuerOrSubject(
-                                    asyncResp->res.jsonValue["Subject"],
-                                    *value);
-                            }
-                        }
-                        else if (property.first == "ValidNotAfter")
-                        {
-                            const uint64_t *value =
-                                std::get_if<uint64_t>(&property.second);
-                            if (value)
-                            {
-                                std::time_t time =
-                                    static_cast<std::time_t>(*value);
-                                asyncResp->res.jsonValue["ValidNotAfter"] =
-                                    crow::utility::getDateTime(time);
-                            }
-                        }
-                        else if (property.first == "ValidNotBefore")
-                        {
-                            const uint64_t *value =
-                                std::get_if<uint64_t>(&property.second);
-                            if (value)
-                            {
-                                std::time_t time =
-                                    static_cast<std::time_t>(*value);
-                                asyncResp->res.jsonValue["ValidNotBefore"] =
-                                    crow::utility::getDateTime(time);
-                            }
-                        }
-                    }
-                    asyncResp->res.addHeader("Location", certURL);
-                },
-                service, objectPath, certs::dbusPropIntf, "GetAll",
-                certs::certPropIntf);
+            asyncResp->res.addHeader("Location", certURL);
         },
-        certs::mapperBusName, certs::mapperObjectPath, certs::mapperIntf,
-        "GetObject", objectPath,
-        std::array<const char *, 1>{certs::certPropIntf});
+        service, objectPath, certs::dbusPropIntf, "GetAll",
+        certs::certPropIntf);
 }
 
 using GetObjectType =
@@ -424,17 +404,6 @@
             return;
         }
 
-        if (!boost::starts_with(
-                certURI,
-                "/redfish/v1/Managers/bmc/NetworkProtocol/HTTPS/Certificates/"))
-        {
-            BMCWEB_LOG_ERROR << "Unsupported certificate URI" << certURI;
-            messages::actionParameterValueFormatError(asyncResp->res, certURI,
-                                                      "CertificateUri",
-                                                      "ReplaceCertificate");
-            return;
-        }
-
         BMCWEB_LOG_INFO << "Certificate URI to replace" << certURI;
         long id = getIDFromURL(certURI);
         if (id < 0)
@@ -446,6 +415,7 @@
         }
         std::string objectPath;
         std::string name;
+        std::string service;
         if (boost::starts_with(
                 certURI,
                 "/redfish/v1/Managers/bmc/NetworkProtocol/HTTPS/Certificates/"))
@@ -453,6 +423,15 @@
             objectPath =
                 std::string(certs::httpsObjectPath) + "/" + std::to_string(id);
             name = "HTTPS certificate";
+            service = certs::httpsServiceName;
+        }
+        else if (boost::starts_with(
+                     certURI, "/redfish/v1/AccountService/LDAP/Certificates/"))
+        {
+            objectPath =
+                std::string(certs::ldapObjectPath) + "/" + std::to_string(id);
+            name = "LDAP certificate";
+            service = certs::ldapServiceName;
         }
         else
         {
@@ -463,44 +442,22 @@
 
         std::shared_ptr<CertificateFile> certFile =
             std::make_shared<CertificateFile>(certificate);
-
         crow::connections::systemBus->async_method_call(
-            [asyncResp, objectPath, certFile, id, certURI, name](
-                const boost::system::error_code ec, const GetObjectType &resp) {
+            [asyncResp, certFile, objectPath, service, certURI, id,
+             name](const boost::system::error_code ec) {
                 if (ec)
                 {
                     BMCWEB_LOG_ERROR << "DBUS response error: " << ec;
                     messages::internalError(asyncResp->res);
                     return;
                 }
-                if (resp.size() > 1 || resp.empty())
-                {
-                    BMCWEB_LOG_ERROR << "Invalid number of objects found "
-                                     << resp.size();
-                    messages::internalError(asyncResp->res);
-                    return;
-                }
-                const std::string &service = resp.begin()->first;
-                crow::connections::systemBus->async_method_call(
-                    [asyncResp, certFile, objectPath, certURI, id,
-                     name](const boost::system::error_code ec) {
-                        if (ec)
-                        {
-                            BMCWEB_LOG_ERROR << "DBUS response error: " << ec;
-                            messages::internalError(asyncResp->res);
-                            return;
-                        }
-                        getCertificateProperties(asyncResp, objectPath, id,
-                                                 certURI, name);
-                        BMCWEB_LOG_DEBUG << "HTTPS certificate install file="
-                                         << certFile->getCertFilePath();
-                    },
-                    service, objectPath, certs::certReplaceIntf, "Replace",
-                    certFile->getCertFilePath());
+                getCertificateProperties(asyncResp, objectPath, service, id,
+                                         certURI, name);
+                BMCWEB_LOG_DEBUG << "HTTPS certificate install file="
+                                 << certFile->getCertFilePath();
             },
-            certs::mapperBusName, certs::mapperObjectPath, certs::mapperIntf,
-            "GetObject", objectPath,
-            std::array<std::string, 1>({certs::certReplaceIntf}));
+            service, objectPath, certs::certReplaceIntf, "Replace",
+            certFile->getCertFilePath());
     }
 }; // CertificateActionsReplaceCertificate
 
@@ -545,8 +502,8 @@
         std::string objectPath = certs::httpsObjectPath;
         objectPath += "/";
         objectPath += std::to_string(id);
-        getCertificateProperties(asyncResp, objectPath, id, certURL,
-                                 "HTTPS Certificate");
+        getCertificateProperties(asyncResp, objectPath, certs::httpsServiceName,
+                                 id, certURL, "HTTPS Certificate");
     }
 
 }; // namespace redfish
@@ -585,54 +542,32 @@
         auto asyncResp = std::make_shared<AsyncResp>(res);
         crow::connections::systemBus->async_method_call(
             [asyncResp](const boost::system::error_code ec,
-                        const GetObjectType &resp) {
+                        const ManagedObjectType &certs) {
                 if (ec)
                 {
                     BMCWEB_LOG_ERROR << "DBUS response error: " << ec;
                     messages::internalError(asyncResp->res);
                     return;
                 }
-                if (resp.size() > 1 || resp.empty())
+                nlohmann::json &members = asyncResp->res.jsonValue["Members"];
+                members = nlohmann::json::array();
+                for (const auto &cert : certs)
                 {
-                    BMCWEB_LOG_ERROR << "Invalid number of objects found "
-                                     << resp.size();
-                    messages::internalError(asyncResp->res);
-                    return;
+                    long id = getIDFromURL(cert.first.str);
+                    if (id >= 0)
+                    {
+                        members.push_back(
+                            {{"@odata.id",
+                              "/redfish/v1/Managers/bmc/"
+                              "NetworkProtocol/HTTPS/Certificates/" +
+                                  std::to_string(id)}});
+                    }
                 }
-                const std::string &service = resp.begin()->first;
-                crow::connections::systemBus->async_method_call(
-                    [asyncResp](const boost::system::error_code ec,
-                                const ManagedObjectType &certs) {
-                        if (ec)
-                        {
-                            BMCWEB_LOG_ERROR << "DBUS response error: " << ec;
-                            messages::internalError(asyncResp->res);
-                            return;
-                        }
-                        nlohmann::json &members =
-                            asyncResp->res.jsonValue["Members"];
-                        members = nlohmann::json::array();
-                        for (const auto &cert : certs)
-                        {
-                            long id = getIDFromURL(cert.first.str);
-                            if (id != -1)
-                            {
-                                members.push_back(
-                                    {{"@odata.id",
-                                      "/redfish/v1/Managers/bmc/"
-                                      "NetworkProtocol/HTTPS/Certificates/" +
-                                          std::to_string(id)}});
-                            }
-                        }
-                        asyncResp->res.jsonValue["Members@odata.count"] =
-                            members.size();
-                    },
-                    service, certs::httpsObjectPath, certs::dbusObjManagerIntf,
-                    "GetManagedObjects");
+                asyncResp->res.jsonValue["Members@odata.count"] =
+                    members.size();
             },
-            certs::mapperBusName, certs::mapperObjectPath, certs::mapperIntf,
-            "GetObject", certs::httpsObjectPath,
-            std::array<const char *, 1>{certs::certInstallIntf});
+            certs::httpsServiceName, certs::httpsObjectPath,
+            certs::dbusObjManagerIntf, "GetManagedObjects");
     }
 
     void doPost(crow::Response &res, const crow::Request &req,
@@ -647,115 +582,33 @@
             std::make_shared<CertificateFile>(req.body);
 
         crow::connections::systemBus->async_method_call(
-            [asyncResp, certFile](const boost::system::error_code ec,
-                                  const GetObjectType &resp) {
+            [asyncResp, certFile](const boost::system::error_code ec) {
                 if (ec)
                 {
                     BMCWEB_LOG_ERROR << "DBUS response error: " << ec;
                     messages::internalError(asyncResp->res);
                     return;
                 }
-                if (resp.size() > 1 || resp.empty())
-                {
-                    BMCWEB_LOG_ERROR << "Invalid number of objects found "
-                                     << resp.size();
-                    messages::internalError(asyncResp->res);
-                    return;
-                }
-                const std::string &service = resp.begin()->first;
-                crow::connections::systemBus->async_method_call(
-                    [asyncResp, certFile](const boost::system::error_code ec) {
-                        if (ec)
-                        {
-                            BMCWEB_LOG_ERROR << "DBUS response error: " << ec;
-                            messages::internalError(asyncResp->res);
-                            return;
-                        }
-                        // TODO: Issue#84 supporting only 1 certificate
-                        long certId = 1;
-                        std::string certURL =
-                            "/redfish/v1/Managers/bmc/NetworkProtocol/HTTPS/"
-                            "Certificates/" +
-                            std::to_string(certId);
-                        std::string objectPath =
-                            std::string(certs::httpsObjectPath) + "/" +
-                            std::to_string(certId);
-                        getCertificateProperties(asyncResp, objectPath, certId,
-                                                 certURL, "HTTPS Certificate");
-                        BMCWEB_LOG_DEBUG << "HTTPS certificate install file="
-                                         << certFile->getCertFilePath();
-                    },
-                    service, certs::httpsObjectPath, certs::certInstallIntf,
-                    "Install", certFile->getCertFilePath());
+                // TODO: Issue#84 supporting only 1 certificate
+                long certId = 1;
+                std::string certURL =
+                    "/redfish/v1/Managers/bmc/NetworkProtocol/HTTPS/"
+                    "Certificates/" +
+                    std::to_string(certId);
+                std::string objectPath = std::string(certs::httpsObjectPath) +
+                                         "/" + std::to_string(certId);
+                getCertificateProperties(asyncResp, objectPath,
+                                         certs::httpsServiceName, certId,
+                                         certURL, "HTTPS Certificate");
+                BMCWEB_LOG_DEBUG << "HTTPS certificate install file="
+                                 << certFile->getCertFilePath();
             },
-            certs::mapperBusName, certs::mapperObjectPath, certs::mapperIntf,
-            "GetObject", certs::httpsObjectPath,
-            std::array<const char *, 1>{certs::certInstallIntf});
+            certs::httpsServiceName, certs::httpsObjectPath,
+            certs::certInstallIntf, "Install", certFile->getCertFilePath());
     }
 }; // HTTPSCertificateCollection
 
 /**
- * @brief Retrieve the certificates installed list and append to the response
- *
- * @param[in] asyncResp Shared pointer to the response message
- * @param[in] certURL  Path of the certificate object
- * @param[in] path  Path of the D-Bus service object
- * @return None
- */
-static void getCertificateLocations(std::shared_ptr<AsyncResp> &asyncResp,
-                                    const std::string &certURL,
-                                    const std::string &path)
-{
-    BMCWEB_LOG_DEBUG << "getCertificateLocations URI=" << certURL
-                     << " Path=" << path;
-    crow::connections::systemBus->async_method_call(
-        [asyncResp, path, certURL](const boost::system::error_code ec,
-                                   const GetObjectType &resp) {
-            if (ec)
-            {
-                BMCWEB_LOG_ERROR << "DBUS response error: " << ec;
-                messages::internalError(asyncResp->res);
-                return;
-            }
-            if (resp.size() > 1 || resp.empty())
-            {
-                BMCWEB_LOG_ERROR << "Invalid number of objects found "
-                                 << resp.size();
-                messages::internalError(asyncResp->res);
-                return;
-            }
-            const std::string &service = resp.begin()->first;
-            crow::connections::systemBus->async_method_call(
-                [asyncResp, certURL](const boost::system::error_code ec,
-                                     const ManagedObjectType &certs) {
-                    if (ec)
-                    {
-                        BMCWEB_LOG_ERROR << "DBUS response error: " << ec;
-                        messages::internalError(asyncResp->res);
-                        return;
-                    }
-                    nlohmann::json &links =
-                        asyncResp->res.jsonValue["Links"]["Certificates"];
-                    for (auto &cert : certs)
-                    {
-                        long id = getIDFromURL(cert.first.str);
-                        if (id != -1)
-                        {
-                            links.push_back(
-                                {{"@odata.id", certURL + std::to_string(id)}});
-                        }
-                    }
-                    asyncResp->res
-                        .jsonValue["Links"]["Certificates@odata.count"] =
-                        links.size();
-                },
-                service, path, certs::dbusObjManagerIntf, "GetManagedObjects");
-        },
-        certs::mapperBusName, certs::mapperObjectPath, certs::mapperIntf,
-        "GetObject", path, std::array<std::string, 0>());
-}
-
-/**
  * The certificate location schema defines a resource that an administrator
  * can use in order to locate all certificates installed on a given service.
  */
@@ -798,7 +651,185 @@
         getCertificateLocations(
             asyncResp,
             "/redfish/v1/Managers/bmc/NetworkProtocol/HTTPS/Certificates/",
-            certs::httpsObjectPath);
+            certs::httpsObjectPath, certs::httpsServiceName);
+        getCertificateLocations(asyncResp,
+                                "/redfish/v1/AccountService/LDAP/Certificates/",
+                                certs::ldapObjectPath, certs::ldapServiceName);
+    }
+    /**
+     * @brief Retrieve the certificates installed list and append to the
+     * response
+     *
+     * @param[in] asyncResp Shared pointer to the response message
+     * @param[in] certURL  Path of the certificate object
+     * @param[in] path  Path of the D-Bus service object
+     * @return None
+     */
+    void getCertificateLocations(std::shared_ptr<AsyncResp> &asyncResp,
+                                 const std::string &certURL,
+                                 const std::string &path,
+                                 const std::string &service)
+    {
+        BMCWEB_LOG_DEBUG << "getCertificateLocations URI=" << certURL
+                         << " Path=" << path << " service= " << service;
+        crow::connections::systemBus->async_method_call(
+            [asyncResp, certURL](const boost::system::error_code ec,
+                                 const ManagedObjectType &certs) {
+                if (ec)
+                {
+                    BMCWEB_LOG_ERROR << "DBUS response error: " << ec;
+                    messages::internalError(asyncResp->res);
+                    return;
+                }
+                nlohmann::json &links =
+                    asyncResp->res.jsonValue["Links"]["Certificates"];
+                for (auto &cert : certs)
+                {
+                    long id = getIDFromURL(cert.first.str);
+                    if (id >= 0)
+                    {
+                        links.push_back(
+                            {{"@odata.id", certURL + std::to_string(id)}});
+                    }
+                }
+                asyncResp->res.jsonValue["Links"]["Certificates@odata.count"] =
+                    links.size();
+            },
+            service, path, certs::dbusObjManagerIntf, "GetManagedObjects");
     }
 }; // CertificateLocations
+
+/**
+ * Collection of LDAP certificates
+ */
+class LDAPCertificateCollection : public Node
+{
+  public:
+    template <typename CrowApp>
+    LDAPCertificateCollection(CrowApp &app) :
+        Node(app, "/redfish/v1/AccountService/LDAP/Certificates/")
+    {
+        entityPrivileges = {
+            {boost::beast::http::verb::get, {{"Login"}}},
+            {boost::beast::http::verb::head, {{"Login"}}},
+            {boost::beast::http::verb::patch, {{"ConfigureComponents"}}},
+            {boost::beast::http::verb::put, {{"ConfigureComponents"}}},
+            {boost::beast::http::verb::delete_, {{"ConfigureComponents"}}},
+            {boost::beast::http::verb::post, {{"ConfigureComponents"}}}};
+    }
+    void doGet(crow::Response &res, const crow::Request &req,
+               const std::vector<std::string> &params) override
+    {
+        res.jsonValue = {
+            {"@odata.id", "/redfish/v1/AccountService/LDAP/Certificates"},
+            {"@odata.type", "#CertificateCollection.CertificateCollection"},
+            {"@odata.context",
+             "/redfish/v1/"
+             "$metadata#CertificateCollection.CertificateCollection"},
+            {"Name", "LDAP Certificates Collection"},
+            {"Description", "A Collection of LDAP certificate instances"}};
+        auto asyncResp = std::make_shared<AsyncResp>(res);
+        crow::connections::systemBus->async_method_call(
+            [asyncResp](const boost::system::error_code ec,
+                        const ManagedObjectType &certs) {
+                if (ec)
+                {
+                    BMCWEB_LOG_ERROR << "DBUS response error: " << ec;
+                    messages::internalError(asyncResp->res);
+                    return;
+                }
+                nlohmann::json &members = asyncResp->res.jsonValue["Members"];
+                members = nlohmann::json::array();
+                for (const auto &cert : certs)
+                {
+                    long id = getIDFromURL(cert.first.str);
+                    if (id >= 0)
+                    {
+                        members.push_back(
+                            {{"@odata.id", "/redfish/v1/AccountService/"
+                                           "LDAP/Certificates/" +
+                                               std::to_string(id)}});
+                    }
+                }
+                asyncResp->res.jsonValue["Members@odata.count"] =
+                    members.size();
+            },
+            certs::ldapServiceName, certs::ldapObjectPath,
+            certs::dbusObjManagerIntf, "GetManagedObjects");
+    }
+
+    void doPost(crow::Response &res, const crow::Request &req,
+                const std::vector<std::string> &params) override
+    {
+        std::shared_ptr<CertificateFile> certFile =
+            std::make_shared<CertificateFile>(req.body);
+        auto asyncResp = std::make_shared<AsyncResp>(res);
+        crow::connections::systemBus->async_method_call(
+            [asyncResp, certFile](const boost::system::error_code ec) {
+                if (ec)
+                {
+                    BMCWEB_LOG_ERROR << "DBUS response error: " << ec;
+                    messages::internalError(asyncResp->res);
+                    return;
+                }
+                //// TODO: Issue#84 supporting only 1 certificate
+                long certId = 1;
+                std::string certURL =
+                    "/redfish/v1/AccountService/LDAP/Certificates/" +
+                    std::to_string(certId);
+                std::string objectPath = std::string(certs::ldapObjectPath) +
+                                         "/" + std::to_string(certId);
+                getCertificateProperties(asyncResp, objectPath,
+                                         certs::ldapServiceName, certId,
+                                         certURL, "LDAP Certificate");
+                BMCWEB_LOG_DEBUG << "LDAP certificate install file="
+                                 << certFile->getCertFilePath();
+            },
+            certs::ldapServiceName, certs::ldapObjectPath,
+            certs::certInstallIntf, "Install", certFile->getCertFilePath());
+    }
+}; // LDAPCertificateCollection
+
+/**
+ * Certificate resource describes a certificate used to prove the identity
+ * of a component, account or service.
+ */
+class LDAPCertificate : public Node
+{
+  public:
+    template <typename CrowApp>
+    LDAPCertificate(CrowApp &app) :
+        Node(app, "/redfish/v1/AccountService/LDAP/Certificates/<str>/",
+             std::string())
+    {
+        entityPrivileges = {
+            {boost::beast::http::verb::get, {{"Login"}}},
+            {boost::beast::http::verb::head, {{"Login"}}},
+            {boost::beast::http::verb::patch, {{"ConfigureComponents"}}},
+            {boost::beast::http::verb::put, {{"ConfigureComponents"}}},
+            {boost::beast::http::verb::delete_, {{"ConfigureComponents"}}},
+            {boost::beast::http::verb::post, {{"ConfigureComponents"}}}};
+    }
+
+    void doGet(crow::Response &res, const crow::Request &req,
+               const std::vector<std::string> &params) override
+    {
+        auto asyncResp = std::make_shared<AsyncResp>(res);
+        long id = getIDFromURL(req.url);
+        if (id < 0)
+        {
+            BMCWEB_LOG_ERROR << "Invalid url value" << req.url;
+            messages::internalError(asyncResp->res);
+            return;
+        }
+        BMCWEB_LOG_DEBUG << "LDAP Certificate ID=" << std::to_string(id);
+        std::string certURL = "/redfish/v1/AccountService/LDAP/Certificates/" +
+                              std::to_string(id);
+        std::string objectPath = certs::ldapObjectPath;
+        objectPath += "/";
+        objectPath += std::to_string(id);
+        getCertificateProperties(asyncResp, objectPath, certs::ldapServiceName,
+                                 id, certURL, "LDAP Certificate");
+    }
+}; // LDAPCertificate
 } // namespace redfish