xref: /openbmc/bmcweb/features/redfish/lib/certificate_service.hpp (revision 720487803a1890e8d4e5d91463e7ec62b4b23f74)
1 #pragma once
2 
3 #include <app.hpp>
4 #include <boost/convert.hpp>
5 #include <boost/convert/strtol.hpp>
6 
7 #include <variant>
8 namespace redfish
9 {
10 namespace certs
11 {
12 constexpr char const* httpsObjectPath =
13     "/xyz/openbmc_project/certs/server/https";
14 constexpr char const* certInstallIntf = "xyz.openbmc_project.Certs.Install";
15 constexpr char const* certReplaceIntf = "xyz.openbmc_project.Certs.Replace";
16 constexpr char const* objDeleteIntf = "xyz.openbmc_project.Object.Delete";
17 constexpr char const* certPropIntf = "xyz.openbmc_project.Certs.Certificate";
18 constexpr char const* dbusPropIntf = "org.freedesktop.DBus.Properties";
19 constexpr char const* dbusObjManagerIntf = "org.freedesktop.DBus.ObjectManager";
20 constexpr char const* ldapObjectPath = "/xyz/openbmc_project/certs/client/ldap";
21 constexpr char const* httpsServiceName =
22     "xyz.openbmc_project.Certs.Manager.Server.Https";
23 constexpr char const* ldapServiceName =
24     "xyz.openbmc_project.Certs.Manager.Client.Ldap";
25 constexpr char const* authorityServiceName =
26     "xyz.openbmc_project.Certs.Manager.Authority.Ldap";
27 constexpr char const* authorityObjectPath =
28     "/xyz/openbmc_project/certs/authority/ldap";
29 } // namespace certs
30 
31 /**
32  * The Certificate schema defines a Certificate Service which represents the
33  * actions available to manage certificates and links to where certificates
34  * are installed.
35  */
36 
37 // TODO: Issue#61 No entries are available for Certificate
38 // service at https://www.dmtf.org/standards/redfish
39 // "redfish standard registries". Need to modify after DMTF
40 // publish Privilege details for certificate service
41 
42 inline void requestRoutesCertificateService(App& app)
43 {
44     BMCWEB_ROUTE(app, "/redfish/v1/CertificateService/")
45         .privileges({{"Login"}})
46         .methods(
47             boost::beast::http::verb::
48                 get)([](const crow::Request& req,
49                         const std::shared_ptr<bmcweb::AsyncResp>& asyncResp) {
50             asyncResp->res.jsonValue = {
51                 {"@odata.type",
52                  "#CertificateService.v1_0_0.CertificateService"},
53                 {"@odata.id", "/redfish/v1/CertificateService"},
54                 {"Id", "CertificateService"},
55                 {"Name", "Certificate Service"},
56                 {"Description", "Actions available to manage certificates"}};
57             // /redfish/v1/CertificateService/CertificateLocations is something
58             // only ConfigureManager can access then only display when the user
59             // has permissions ConfigureManager
60             Privileges effectiveUserPrivileges =
61                 redfish::getUserPrivileges(req.userRole);
62             if (isOperationAllowedWithPrivileges({{"ConfigureManager"}},
63                                                  effectiveUserPrivileges))
64             {
65                 asyncResp->res.jsonValue["CertificateLocations"] = {
66                     {"@odata.id",
67                      "/redfish/v1/CertificateService/CertificateLocations"}};
68             }
69             asyncResp->res.jsonValue["Actions"]
70                                     ["#CertificateService.ReplaceCertificate"] =
71                 {{"target", "/redfish/v1/CertificateService/Actions/"
72                             "CertificateService.ReplaceCertificate"},
73                  {"CertificateType@Redfish.AllowableValues", {"PEM"}}};
74             asyncResp->res
75                 .jsonValue["Actions"]["#CertificateService.GenerateCSR"] = {
76                 {"target", "/redfish/v1/CertificateService/Actions/"
77                            "CertificateService.GenerateCSR"}};
78         });
79 } // requestRoutesCertificateService
80 
81 /**
82  * @brief Find the ID specified in the URL
83  * Finds the numbers specified after the last "/" in the URL and returns.
84  * @param[in] path URL
85  * @return -1 on failure and number on success
86  */
87 inline long getIDFromURL(const std::string_view url)
88 {
89     std::size_t found = url.rfind('/');
90     if (found == std::string::npos)
91     {
92         return -1;
93     }
94 
95     if ((found + 1) < url.length())
96     {
97         std::string_view str = url.substr(found + 1);
98 
99         return boost::convert<long>(str, boost::cnv::strtol()).value_or(-1);
100     }
101 
102     return -1;
103 }
104 
105 inline std::string getCertificateFromReqBody(
106     const std::shared_ptr<bmcweb::AsyncResp>& asyncResp,
107     const crow::Request& req)
108 {
109     nlohmann::json reqJson = nlohmann::json::parse(req.body, nullptr, false);
110 
111     if (reqJson.is_discarded())
112     {
113         // We did not receive JSON request, proceed as it is RAW data
114         return req.body;
115     }
116 
117     std::string certificate;
118     std::optional<std::string> certificateType = "PEM";
119 
120     if (!json_util::readJson(reqJson, asyncResp->res, "CertificateString",
121                              certificate, "CertificateType", certificateType))
122     {
123         BMCWEB_LOG_ERROR << "Required parameters are missing";
124         messages::internalError(asyncResp->res);
125         return std::string();
126     }
127 
128     if (*certificateType != "PEM")
129     {
130         messages::propertyValueNotInList(asyncResp->res, *certificateType,
131                                          "CertificateType");
132         return std::string();
133     }
134 
135     return certificate;
136 }
137 
138 /**
139  * Class to create a temporary certificate file for uploading to system
140  */
141 class CertificateFile
142 {
143   public:
144     CertificateFile() = delete;
145     CertificateFile(const CertificateFile&) = delete;
146     CertificateFile& operator=(const CertificateFile&) = delete;
147     CertificateFile(CertificateFile&&) = delete;
148     CertificateFile& operator=(CertificateFile&&) = delete;
149     CertificateFile(const std::string& certString)
150     {
151         std::array<char, 18> dirTemplate = {'/', 't', 'm', 'p', '/', 'C',
152                                             'e', 'r', 't', 's', '.', 'X',
153                                             'X', 'X', 'X', 'X', 'X', '\0'};
154         char* tempDirectory = mkdtemp(dirTemplate.data());
155         if (tempDirectory)
156         {
157             certDirectory = tempDirectory;
158             certificateFile = certDirectory / "cert.pem";
159             std::ofstream out(certificateFile, std::ofstream::out |
160                                                    std::ofstream::binary |
161                                                    std::ofstream::trunc);
162             out << certString;
163             out.close();
164             BMCWEB_LOG_DEBUG << "Creating certificate file" << certificateFile;
165         }
166     }
167     ~CertificateFile()
168     {
169         if (std::filesystem::exists(certDirectory))
170         {
171             BMCWEB_LOG_DEBUG << "Removing certificate file" << certificateFile;
172             std::error_code ec;
173             std::filesystem::remove_all(certDirectory, ec);
174             if (ec)
175             {
176                 BMCWEB_LOG_ERROR << "Failed to remove temp directory"
177                                  << certDirectory;
178             }
179         }
180     }
181     std::string getCertFilePath()
182     {
183         return certificateFile;
184     }
185 
186   private:
187     std::filesystem::path certificateFile;
188     std::filesystem::path certDirectory;
189 };
190 
191 static std::unique_ptr<sdbusplus::bus::match::match> csrMatcher;
192 /**
193  * @brief Read data from CSR D-bus object and set to response
194  *
195  * @param[in] asyncResp Shared pointer to the response message
196  * @param[in] certURI Link to certifiate collection URI
197  * @param[in] service D-Bus service name
198  * @param[in] certObjPath certificate D-Bus object path
199  * @param[in] csrObjPath CSR D-Bus object path
200  * @return None
201  */
202 static void getCSR(const std::shared_ptr<bmcweb::AsyncResp>& asyncResp,
203                    const std::string& certURI, const std::string& service,
204                    const std::string& certObjPath,
205                    const std::string& csrObjPath)
206 {
207     BMCWEB_LOG_DEBUG << "getCSR CertObjectPath" << certObjPath
208                      << " CSRObjectPath=" << csrObjPath
209                      << " service=" << service;
210     crow::connections::systemBus->async_method_call(
211         [asyncResp, certURI](const boost::system::error_code ec,
212                              const std::string& csr) {
213             if (ec)
214             {
215                 BMCWEB_LOG_ERROR << "DBUS response error: " << ec;
216                 messages::internalError(asyncResp->res);
217                 return;
218             }
219             if (csr.empty())
220             {
221                 BMCWEB_LOG_ERROR << "CSR read is empty";
222                 messages::internalError(asyncResp->res);
223                 return;
224             }
225             asyncResp->res.jsonValue["CSRString"] = csr;
226             asyncResp->res.jsonValue["CertificateCollection"] = {
227                 {"@odata.id", certURI}};
228         },
229         service, csrObjPath, "xyz.openbmc_project.Certs.CSR", "CSR");
230 }
231 
232 /**
233  * Action to Generate CSR
234  */
235 inline void requestRoutesCertificateActionGenerateCSR(App& app)
236 {
237     BMCWEB_ROUTE(app, "/redfish/v1/CertificateService/Actions/"
238                       "CertificateService.GenerateCSR/")
239         .privileges({{"ConfigureComponents"}})
240         .methods(boost::beast::http::verb::post)(
241             [](const crow::Request& req,
242                const std::shared_ptr<bmcweb::AsyncResp>& asyncResp) {
243                 static const int rsaKeyBitLength = 2048;
244 
245                 // Required parameters
246                 std::string city;
247                 std::string commonName;
248                 std::string country;
249                 std::string organization;
250                 std::string organizationalUnit;
251                 std::string state;
252                 nlohmann::json certificateCollection;
253 
254                 // Optional parameters
255                 std::optional<std::vector<std::string>> optAlternativeNames =
256                     std::vector<std::string>();
257                 std::optional<std::string> optContactPerson = "";
258                 std::optional<std::string> optChallengePassword = "";
259                 std::optional<std::string> optEmail = "";
260                 std::optional<std::string> optGivenName = "";
261                 std::optional<std::string> optInitials = "";
262                 std::optional<int64_t> optKeyBitLength = rsaKeyBitLength;
263                 std::optional<std::string> optKeyCurveId = "secp384r1";
264                 std::optional<std::string> optKeyPairAlgorithm = "EC";
265                 std::optional<std::vector<std::string>> optKeyUsage =
266                     std::vector<std::string>();
267                 std::optional<std::string> optSurname = "";
268                 std::optional<std::string> optUnstructuredName = "";
269                 if (!json_util::readJson(
270                         req, asyncResp->res, "City", city, "CommonName",
271                         commonName, "ContactPerson", optContactPerson,
272                         "Country", country, "Organization", organization,
273                         "OrganizationalUnit", organizationalUnit, "State",
274                         state, "CertificateCollection", certificateCollection,
275                         "AlternativeNames", optAlternativeNames,
276                         "ChallengePassword", optChallengePassword, "Email",
277                         optEmail, "GivenName", optGivenName, "Initials",
278                         optInitials, "KeyBitLength", optKeyBitLength,
279                         "KeyCurveId", optKeyCurveId, "KeyPairAlgorithm",
280                         optKeyPairAlgorithm, "KeyUsage", optKeyUsage, "Surname",
281                         optSurname, "UnstructuredName", optUnstructuredName))
282                 {
283                     return;
284                 }
285 
286                 // bmcweb has no way to store or decode a private key challenge
287                 // password, which will likely cause bmcweb to crash on startup
288                 // if this is not set on a post so not allowing the user to set
289                 // value
290                 if (*optChallengePassword != "")
291                 {
292                     messages::actionParameterNotSupported(
293                         asyncResp->res, "GenerateCSR", "ChallengePassword");
294                     return;
295                 }
296 
297                 std::string certURI;
298                 if (!redfish::json_util::readJson(certificateCollection,
299                                                   asyncResp->res, "@odata.id",
300                                                   certURI))
301                 {
302                     return;
303                 }
304 
305                 std::string objectPath;
306                 std::string service;
307                 if (boost::starts_with(certURI,
308                                        "/redfish/v1/Managers/bmc/"
309                                        "NetworkProtocol/HTTPS/Certificates"))
310                 {
311                     objectPath = certs::httpsObjectPath;
312                     service = certs::httpsServiceName;
313                 }
314                 else if (boost::starts_with(
315                              certURI,
316                              "/redfish/v1/AccountService/LDAP/Certificates"))
317                 {
318                     objectPath = certs::ldapObjectPath;
319                     service = certs::ldapServiceName;
320                 }
321                 else
322                 {
323                     messages::actionParameterNotSupported(
324                         asyncResp->res, "CertificateCollection", "GenerateCSR");
325                     return;
326                 }
327 
328                 // supporting only EC and RSA algorithm
329                 if (*optKeyPairAlgorithm != "EC" &&
330                     *optKeyPairAlgorithm != "RSA")
331                 {
332                     messages::actionParameterNotSupported(
333                         asyncResp->res, "KeyPairAlgorithm", "GenerateCSR");
334                     return;
335                 }
336 
337                 // supporting only 2048 key bit length for RSA algorithm due to
338                 // time consumed in generating private key
339                 if (*optKeyPairAlgorithm == "RSA" &&
340                     *optKeyBitLength != rsaKeyBitLength)
341                 {
342                     messages::propertyValueNotInList(
343                         asyncResp->res, std::to_string(*optKeyBitLength),
344                         "KeyBitLength");
345                     return;
346                 }
347 
348                 // validate KeyUsage supporting only 1 type based on URL
349                 if (boost::starts_with(certURI,
350                                        "/redfish/v1/Managers/bmc/"
351                                        "NetworkProtocol/HTTPS/Certificates"))
352                 {
353                     if (optKeyUsage->size() == 0)
354                     {
355                         optKeyUsage->push_back("ServerAuthentication");
356                     }
357                     else if (optKeyUsage->size() == 1)
358                     {
359                         if ((*optKeyUsage)[0] != "ServerAuthentication")
360                         {
361                             messages::propertyValueNotInList(
362                                 asyncResp->res, (*optKeyUsage)[0], "KeyUsage");
363                             return;
364                         }
365                     }
366                     else
367                     {
368                         messages::actionParameterNotSupported(
369                             asyncResp->res, "KeyUsage", "GenerateCSR");
370                         return;
371                     }
372                 }
373                 else if (boost::starts_with(
374                              certURI,
375                              "/redfish/v1/AccountService/LDAP/Certificates"))
376                 {
377                     if (optKeyUsage->size() == 0)
378                     {
379                         optKeyUsage->push_back("ClientAuthentication");
380                     }
381                     else if (optKeyUsage->size() == 1)
382                     {
383                         if ((*optKeyUsage)[0] != "ClientAuthentication")
384                         {
385                             messages::propertyValueNotInList(
386                                 asyncResp->res, (*optKeyUsage)[0], "KeyUsage");
387                             return;
388                         }
389                     }
390                     else
391                     {
392                         messages::actionParameterNotSupported(
393                             asyncResp->res, "KeyUsage", "GenerateCSR");
394                         return;
395                     }
396                 }
397 
398                 // Only allow one CSR matcher at a time so setting retry
399                 // time-out and timer expiry to 10 seconds for now.
400                 static const int timeOut = 10;
401                 if (csrMatcher)
402                 {
403                     messages::serviceTemporarilyUnavailable(
404                         asyncResp->res, std::to_string(timeOut));
405                     return;
406                 }
407 
408                 // Make this static so it survives outside this method
409                 static boost::asio::steady_timer timeout(*req.ioService);
410                 timeout.expires_after(std::chrono::seconds(timeOut));
411                 timeout.async_wait([asyncResp](
412                                        const boost::system::error_code& ec) {
413                     csrMatcher = nullptr;
414                     if (ec)
415                     {
416                         // operation_aborted is expected if timer is canceled
417                         // before completion.
418                         if (ec != boost::asio::error::operation_aborted)
419                         {
420                             BMCWEB_LOG_ERROR << "Async_wait failed " << ec;
421                         }
422                         return;
423                     }
424                     BMCWEB_LOG_ERROR << "Timed out waiting for Generating CSR";
425                     messages::internalError(asyncResp->res);
426                 });
427 
428                 // create a matcher to wait on CSR object
429                 BMCWEB_LOG_DEBUG << "create matcher with path " << objectPath;
430                 std::string match(
431                     "type='signal',"
432                     "interface='org.freedesktop.DBus.ObjectManager',"
433                     "path='" +
434                     objectPath +
435                     "',"
436                     "member='InterfacesAdded'");
437                 csrMatcher = std::make_unique<sdbusplus::bus::match::match>(
438                     *crow::connections::systemBus, match,
439                     [asyncResp, service, objectPath,
440                      certURI](sdbusplus::message::message& m) {
441                         timeout.cancel();
442                         if (m.is_method_error())
443                         {
444                             BMCWEB_LOG_ERROR << "Dbus method error!!!";
445                             messages::internalError(asyncResp->res);
446                             return;
447                         }
448                         std::vector<std::pair<
449                             std::string,
450                             std::vector<std::pair<std::string,
451                                                   std::variant<std::string>>>>>
452                             interfacesProperties;
453                         sdbusplus::message::object_path csrObjectPath;
454                         m.read(csrObjectPath, interfacesProperties);
455                         BMCWEB_LOG_DEBUG << "CSR object added"
456                                          << csrObjectPath.str;
457                         for (auto& interface : interfacesProperties)
458                         {
459                             if (interface.first ==
460                                 "xyz.openbmc_project.Certs.CSR")
461                             {
462                                 getCSR(asyncResp, certURI, service, objectPath,
463                                        csrObjectPath.str);
464                                 break;
465                             }
466                         }
467                     });
468                 crow::connections::systemBus->async_method_call(
469                     [asyncResp](const boost::system::error_code& ec,
470                                 const std::string&) {
471                         if (ec)
472                         {
473                             BMCWEB_LOG_ERROR << "DBUS response error: "
474                                              << ec.message();
475                             messages::internalError(asyncResp->res);
476                             return;
477                         }
478                     },
479                     service, objectPath, "xyz.openbmc_project.Certs.CSR.Create",
480                     "GenerateCSR", *optAlternativeNames, *optChallengePassword,
481                     city, commonName, *optContactPerson, country, *optEmail,
482                     *optGivenName, *optInitials, *optKeyBitLength,
483                     *optKeyCurveId, *optKeyPairAlgorithm, *optKeyUsage,
484                     organization, organizationalUnit, state, *optSurname,
485                     *optUnstructuredName);
486             });
487 } // requestRoutesCertificateActionGenerateCSR
488 
489 /**
490  * @brief Parse and update Certificate Issue/Subject property
491  *
492  * @param[in] asyncResp Shared pointer to the response message
493  * @param[in] str  Issuer/Subject value in key=value pairs
494  * @param[in] type Issuer/Subject
495  * @return None
496  */
497 static void updateCertIssuerOrSubject(nlohmann::json& out,
498                                       const std::string_view value)
499 {
500     // example: O=openbmc-project.xyz,CN=localhost
501     std::string_view::iterator i = value.begin();
502     while (i != value.end())
503     {
504         std::string_view::iterator tokenBegin = i;
505         while (i != value.end() && *i != '=')
506         {
507             ++i;
508         }
509         if (i == value.end())
510         {
511             break;
512         }
513         const std::string_view key(tokenBegin,
514                                    static_cast<size_t>(i - tokenBegin));
515         ++i;
516         tokenBegin = i;
517         while (i != value.end() && *i != ',')
518         {
519             ++i;
520         }
521         const std::string_view val(tokenBegin,
522                                    static_cast<size_t>(i - tokenBegin));
523         if (key == "L")
524         {
525             out["City"] = val;
526         }
527         else if (key == "CN")
528         {
529             out["CommonName"] = val;
530         }
531         else if (key == "C")
532         {
533             out["Country"] = val;
534         }
535         else if (key == "O")
536         {
537             out["Organization"] = val;
538         }
539         else if (key == "OU")
540         {
541             out["OrganizationalUnit"] = val;
542         }
543         else if (key == "ST")
544         {
545             out["State"] = val;
546         }
547         // skip comma character
548         if (i != value.end())
549         {
550             ++i;
551         }
552     }
553 }
554 
555 /**
556  * @brief Retrieve the certificates properties and append to the response
557  * message
558  *
559  * @param[in] asyncResp Shared pointer to the response message
560  * @param[in] objectPath  Path of the D-Bus service object
561  * @param[in] certId  Id of the certificate
562  * @param[in] certURL  URL of the certificate object
563  * @param[in] name  name of the certificate
564  * @return None
565  */
566 static void getCertificateProperties(
567     const std::shared_ptr<bmcweb::AsyncResp>& asyncResp,
568     const std::string& objectPath, const std::string& service, long certId,
569     const std::string& certURL, const std::string& name)
570 {
571     using PropertyType =
572         std::variant<std::string, uint64_t, std::vector<std::string>>;
573     using PropertiesMap = boost::container::flat_map<std::string, PropertyType>;
574     BMCWEB_LOG_DEBUG << "getCertificateProperties Path=" << objectPath
575                      << " certId=" << certId << " certURl=" << certURL;
576     crow::connections::systemBus->async_method_call(
577         [asyncResp, certURL, certId, name](const boost::system::error_code ec,
578                                            const PropertiesMap& properties) {
579             if (ec)
580             {
581                 BMCWEB_LOG_ERROR << "DBUS response error: " << ec;
582                 messages::resourceNotFound(asyncResp->res, name,
583                                            std::to_string(certId));
584                 return;
585             }
586             asyncResp->res.jsonValue = {
587                 {"@odata.id", certURL},
588                 {"@odata.type", "#Certificate.v1_0_0.Certificate"},
589                 {"Id", std::to_string(certId)},
590                 {"Name", name},
591                 {"Description", name}};
592             for (const auto& property : properties)
593             {
594                 if (property.first == "CertificateString")
595                 {
596                     asyncResp->res.jsonValue["CertificateString"] = "";
597                     const std::string* value =
598                         std::get_if<std::string>(&property.second);
599                     if (value)
600                     {
601                         asyncResp->res.jsonValue["CertificateString"] = *value;
602                     }
603                 }
604                 else if (property.first == "KeyUsage")
605                 {
606                     nlohmann::json& keyUsage =
607                         asyncResp->res.jsonValue["KeyUsage"];
608                     keyUsage = nlohmann::json::array();
609                     const std::vector<std::string>* value =
610                         std::get_if<std::vector<std::string>>(&property.second);
611                     if (value)
612                     {
613                         for (const std::string& usage : *value)
614                         {
615                             keyUsage.push_back(usage);
616                         }
617                     }
618                 }
619                 else if (property.first == "Issuer")
620                 {
621                     const std::string* value =
622                         std::get_if<std::string>(&property.second);
623                     if (value)
624                     {
625                         updateCertIssuerOrSubject(
626                             asyncResp->res.jsonValue["Issuer"], *value);
627                     }
628                 }
629                 else if (property.first == "Subject")
630                 {
631                     const std::string* value =
632                         std::get_if<std::string>(&property.second);
633                     if (value)
634                     {
635                         updateCertIssuerOrSubject(
636                             asyncResp->res.jsonValue["Subject"], *value);
637                     }
638                 }
639                 else if (property.first == "ValidNotAfter")
640                 {
641                     const uint64_t* value =
642                         std::get_if<uint64_t>(&property.second);
643                     if (value)
644                     {
645                         std::time_t time = static_cast<std::time_t>(*value);
646                         asyncResp->res.jsonValue["ValidNotAfter"] =
647                             crow::utility::getDateTime(time);
648                     }
649                 }
650                 else if (property.first == "ValidNotBefore")
651                 {
652                     const uint64_t* value =
653                         std::get_if<uint64_t>(&property.second);
654                     if (value)
655                     {
656                         std::time_t time = static_cast<std::time_t>(*value);
657                         asyncResp->res.jsonValue["ValidNotBefore"] =
658                             crow::utility::getDateTime(time);
659                     }
660                 }
661             }
662             asyncResp->res.addHeader("Location", certURL);
663         },
664         service, objectPath, certs::dbusPropIntf, "GetAll",
665         certs::certPropIntf);
666 }
667 
668 using GetObjectType =
669     std::vector<std::pair<std::string, std::vector<std::string>>>;
670 
671 /**
672  * Action to replace an existing certificate
673  */
674 inline void requestRoutesCertificateActionsReplaceCertificate(App& app)
675 {
676     BMCWEB_ROUTE(app, "/redfish/v1/CertificateService/Actions/"
677                       "CertificateService.ReplaceCertificate/")
678         .privileges({{"ConfigureManager"}})
679         .methods(
680             boost::beast::http::verb::
681                 post)([](const crow::Request& req,
682                          const std::shared_ptr<bmcweb::AsyncResp>& asyncResp) {
683             std::string certificate;
684             nlohmann::json certificateUri;
685             std::optional<std::string> certificateType = "PEM";
686 
687             if (!json_util::readJson(req, asyncResp->res, "CertificateString",
688                                      certificate, "CertificateUri",
689                                      certificateUri, "CertificateType",
690                                      certificateType))
691             {
692                 BMCWEB_LOG_ERROR << "Required parameters are missing";
693                 messages::internalError(asyncResp->res);
694                 return;
695             }
696 
697             if (!certificateType)
698             {
699                 // should never happen, but it never hurts to be paranoid.
700                 return;
701             }
702             if (certificateType != "PEM")
703             {
704                 messages::actionParameterNotSupported(
705                     asyncResp->res, "CertificateType", "ReplaceCertificate");
706                 return;
707             }
708 
709             std::string certURI;
710             if (!redfish::json_util::readJson(certificateUri, asyncResp->res,
711                                               "@odata.id", certURI))
712             {
713                 messages::actionParameterMissing(
714                     asyncResp->res, "ReplaceCertificate", "CertificateUri");
715                 return;
716             }
717 
718             BMCWEB_LOG_INFO << "Certificate URI to replace" << certURI;
719             long id = getIDFromURL(certURI);
720             if (id < 0)
721             {
722                 messages::actionParameterValueFormatError(
723                     asyncResp->res, certURI, "CertificateUri",
724                     "ReplaceCertificate");
725                 return;
726             }
727             std::string objectPath;
728             std::string name;
729             std::string service;
730             if (boost::starts_with(certURI,
731                                    "/redfish/v1/Managers/bmc/NetworkProtocol/"
732                                    "HTTPS/Certificates/"))
733             {
734                 objectPath = std::string(certs::httpsObjectPath) + "/" +
735                              std::to_string(id);
736                 name = "HTTPS certificate";
737                 service = certs::httpsServiceName;
738             }
739             else if (boost::starts_with(
740                          certURI,
741                          "/redfish/v1/AccountService/LDAP/Certificates/"))
742             {
743                 objectPath = std::string(certs::ldapObjectPath) + "/" +
744                              std::to_string(id);
745                 name = "LDAP certificate";
746                 service = certs::ldapServiceName;
747             }
748             else if (boost::starts_with(
749                          certURI,
750                          "/redfish/v1/Managers/bmc/Truststore/Certificates/"))
751             {
752                 objectPath = std::string(certs::authorityObjectPath) + "/" +
753                              std::to_string(id);
754                 name = "TrustStore certificate";
755                 service = certs::authorityServiceName;
756             }
757             else
758             {
759                 messages::actionParameterNotSupported(
760                     asyncResp->res, "CertificateUri", "ReplaceCertificate");
761                 return;
762             }
763 
764             std::shared_ptr<CertificateFile> certFile =
765                 std::make_shared<CertificateFile>(certificate);
766             crow::connections::systemBus->async_method_call(
767                 [asyncResp, certFile, objectPath, service, certURI, id,
768                  name](const boost::system::error_code ec) {
769                     if (ec)
770                     {
771                         BMCWEB_LOG_ERROR << "DBUS response error: " << ec;
772                         messages::resourceNotFound(asyncResp->res, name,
773                                                    std::to_string(id));
774                         return;
775                     }
776                     getCertificateProperties(asyncResp, objectPath, service, id,
777                                              certURI, name);
778                     BMCWEB_LOG_DEBUG << "HTTPS certificate install file="
779                                      << certFile->getCertFilePath();
780                 },
781                 service, objectPath, certs::certReplaceIntf, "Replace",
782                 certFile->getCertFilePath());
783         });
784 } // requestRoutesCertificateActionsReplaceCertificate
785 
786 /**
787  * Certificate resource describes a certificate used to prove the identity
788  * of a component, account or service.
789  */
790 
791 inline void requestRoutesHTTPSCertificate(App& app)
792 {
793     BMCWEB_ROUTE(
794         app,
795         "/redfish/v1/Managers/bmc/NetworkProtocol/HTTPS/Certificates/<str>/")
796         .privileges({{"ConfigureManager"}})
797         .methods(
798             boost::beast::http::verb::
799                 get)([](const crow::Request& req,
800                         const std::shared_ptr<bmcweb::AsyncResp>& asyncResp,
801                         const std::string& param) -> void {
802             if (param.empty())
803             {
804                 messages::internalError(asyncResp->res);
805                 return;
806             }
807             long id = getIDFromURL(req.url);
808 
809             BMCWEB_LOG_DEBUG << "HTTPSCertificate::doGet ID="
810                              << std::to_string(id);
811             std::string certURL =
812                 "/redfish/v1/Managers/bmc/NetworkProtocol/HTTPS/Certificates/" +
813                 std::to_string(id);
814             std::string objectPath = certs::httpsObjectPath;
815             objectPath += "/";
816             objectPath += std::to_string(id);
817             getCertificateProperties(asyncResp, objectPath,
818                                      certs::httpsServiceName, id, certURL,
819                                      "HTTPS Certificate");
820         });
821 }
822 
823 /**
824  * Collection of HTTPS certificates
825  */
826 inline void requestRoutesHTTPSCertificateCollection(App& app)
827 {
828     BMCWEB_ROUTE(app,
829                  "/redfish/v1/Managers/bmc/NetworkProtocol/HTTPS/Certificates/")
830         .privileges({{"ConfigureManager"}})
831         .methods(
832             boost::beast::http::verb::
833                 get)([](const crow::Request&,
834                         const std::shared_ptr<bmcweb::AsyncResp>& asyncResp) {
835             asyncResp->res.jsonValue = {
836                 {"@odata.id",
837                  "/redfish/v1/Managers/bmc/NetworkProtocol/HTTPS/Certificates"},
838                 {"@odata.type", "#CertificateCollection.CertificateCollection"},
839                 {"Name", "HTTPS Certificates Collection"},
840                 {"Description", "A Collection of HTTPS certificate instances"}};
841 
842             crow::connections::systemBus->async_method_call(
843                 [asyncResp](const boost::system::error_code ec,
844                             const ManagedObjectType& certs) {
845                     if (ec)
846                     {
847                         BMCWEB_LOG_ERROR << "DBUS response error: " << ec;
848                         messages::internalError(asyncResp->res);
849                         return;
850                     }
851                     nlohmann::json& members =
852                         asyncResp->res.jsonValue["Members"];
853                     members = nlohmann::json::array();
854                     for (const auto& cert : certs)
855                     {
856                         long id = getIDFromURL(cert.first.str);
857                         if (id >= 0)
858                         {
859                             members.push_back(
860                                 {{"@odata.id",
861                                   "/redfish/v1/Managers/bmc/"
862                                   "NetworkProtocol/HTTPS/Certificates/" +
863                                       std::to_string(id)}});
864                         }
865                     }
866                     asyncResp->res.jsonValue["Members@odata.count"] =
867                         members.size();
868                 },
869                 certs::httpsServiceName, certs::httpsObjectPath,
870                 certs::dbusObjManagerIntf, "GetManagedObjects");
871         });
872 
873     BMCWEB_ROUTE(app,
874                  "/redfish/v1/Managers/bmc/NetworkProtocol/HTTPS/Certificates/")
875         .privileges({{"ConfigureManager"}})
876         .methods(boost::beast::http::verb::post)(
877             [](const crow::Request& req,
878                const std::shared_ptr<bmcweb::AsyncResp>& asyncResp) {
879                 BMCWEB_LOG_DEBUG << "HTTPSCertificateCollection::doPost";
880 
881                 asyncResp->res.jsonValue = {
882                     {"Name", "HTTPS Certificate"},
883                     {"Description", "HTTPS Certificate"}};
884 
885                 std::string certFileBody =
886                     getCertificateFromReqBody(asyncResp, req);
887 
888                 if (certFileBody.empty())
889                 {
890                     BMCWEB_LOG_ERROR
891                         << "Cannot get certificate from request body.";
892                     messages::unrecognizedRequestBody(asyncResp->res);
893                     return;
894                 }
895 
896                 std::shared_ptr<CertificateFile> certFile =
897                     std::make_shared<CertificateFile>(certFileBody);
898 
899                 crow::connections::systemBus->async_method_call(
900                     [asyncResp, certFile](const boost::system::error_code ec,
901                                           const std::string& objectPath) {
902                         if (ec)
903                         {
904                             BMCWEB_LOG_ERROR << "DBUS response error: " << ec;
905                             messages::internalError(asyncResp->res);
906                             return;
907                         }
908                         long certId = getIDFromURL(objectPath);
909                         if (certId < 0)
910                         {
911                             BMCWEB_LOG_ERROR << "Invalid objectPath value"
912                                              << objectPath;
913                             messages::internalError(asyncResp->res);
914                             return;
915                         }
916                         std::string certURL =
917                             "/redfish/v1/Managers/bmc/NetworkProtocol/HTTPS/"
918                             "Certificates/" +
919                             std::to_string(certId);
920                         getCertificateProperties(
921                             asyncResp, objectPath, certs::httpsServiceName,
922                             certId, certURL, "HTTPS Certificate");
923                         BMCWEB_LOG_DEBUG << "HTTPS certificate install file="
924                                          << certFile->getCertFilePath();
925                     },
926                     certs::httpsServiceName, certs::httpsObjectPath,
927                     certs::certInstallIntf, "Install",
928                     certFile->getCertFilePath());
929             });
930 } // requestRoutesHTTPSCertificateCollection
931 
932 /**
933  * @brief Retrieve the certificates installed list and append to the
934  * response
935  *
936  * @param[in] asyncResp Shared pointer to the response message
937  * @param[in] certURL  Path of the certificate object
938  * @param[in] path  Path of the D-Bus service object
939  * @return None
940  */
941 void getCertificateLocations(
942     const std::shared_ptr<bmcweb::AsyncResp>& asyncResp,
943     const std::string& certURL, const std::string& path,
944     const std::string& service)
945 {
946     BMCWEB_LOG_DEBUG << "getCertificateLocations URI=" << certURL
947                      << " Path=" << path << " service= " << service;
948     crow::connections::systemBus->async_method_call(
949         [asyncResp, certURL](const boost::system::error_code ec,
950                              const ManagedObjectType& certs) {
951             if (ec)
952             {
953                 BMCWEB_LOG_WARNING
954                     << "Certificate collection query failed: " << ec
955                     << ", skipping " << certURL;
956                 return;
957             }
958             nlohmann::json& links =
959                 asyncResp->res.jsonValue["Links"]["Certificates"];
960             for (auto& cert : certs)
961             {
962                 long id = getIDFromURL(cert.first.str);
963                 if (id >= 0)
964                 {
965                     links.push_back(
966                         {{"@odata.id", certURL + std::to_string(id)}});
967                 }
968             }
969             asyncResp->res.jsonValue["Links"]["Certificates@odata.count"] =
970                 links.size();
971         },
972         service, path, certs::dbusObjManagerIntf, "GetManagedObjects");
973 }
974 
975 /**
976  * The certificate location schema defines a resource that an administrator
977  * can use in order to locate all certificates installed on a given service.
978  */
979 inline void requestRoutesCertificateLocations(App& app)
980 {
981     BMCWEB_ROUTE(app, "/redfish/v1/CertificateService/CertificateLocations/")
982         .privileges({{"ConfigureManager"}})
983         .methods(
984             boost::beast::http::verb::
985                 get)([](const crow::Request&,
986                         const std::shared_ptr<bmcweb::AsyncResp>& asyncResp) {
987             asyncResp->res.jsonValue = {
988                 {"@odata.id",
989                  "/redfish/v1/CertificateService/CertificateLocations"},
990                 {"@odata.type",
991                  "#CertificateLocations.v1_0_0.CertificateLocations"},
992                 {"Name", "Certificate Locations"},
993                 {"Id", "CertificateLocations"},
994                 {"Description",
995                  "Defines a resource that an administrator can use in order to "
996                  "locate all certificates installed on a given service"}};
997 
998             nlohmann::json& links =
999                 asyncResp->res.jsonValue["Links"]["Certificates"];
1000             links = nlohmann::json::array();
1001             getCertificateLocations(
1002                 asyncResp,
1003                 "/redfish/v1/Managers/bmc/NetworkProtocol/HTTPS/Certificates/",
1004                 certs::httpsObjectPath, certs::httpsServiceName);
1005             getCertificateLocations(
1006                 asyncResp, "/redfish/v1/AccountService/LDAP/Certificates/",
1007                 certs::ldapObjectPath, certs::ldapServiceName);
1008             getCertificateLocations(
1009                 asyncResp, "/redfish/v1/Managers/bmc/Truststore/Certificates/",
1010                 certs::authorityObjectPath, certs::authorityServiceName);
1011         });
1012 }
1013 // requestRoutesCertificateLocations
1014 
1015 /**
1016  * Collection of LDAP certificates
1017  */
1018 inline void requestRoutesLDAPCertificateCollection(App& app)
1019 {
1020     BMCWEB_ROUTE(app, "/redfish/v1/AccountService/LDAP/Certificates/")
1021         .privileges({{"ConfigureManager"}})
1022         .methods(boost::beast::http::verb::get)(
1023             [](const crow::Request&,
1024                const std::shared_ptr<bmcweb::AsyncResp>& asyncResp) {
1025                 asyncResp->res.jsonValue = {
1026                     {"@odata.id",
1027                      "/redfish/v1/AccountService/LDAP/Certificates"},
1028                     {"@odata.type",
1029                      "#CertificateCollection.CertificateCollection"},
1030                     {"Name", "LDAP Certificates Collection"},
1031                     {"Description",
1032                      "A Collection of LDAP certificate instances"}};
1033 
1034                 crow::connections::systemBus->async_method_call(
1035                     [asyncResp](const boost::system::error_code ec,
1036                                 const ManagedObjectType& certs) {
1037                         nlohmann::json& members =
1038                             asyncResp->res.jsonValue["Members"];
1039                         nlohmann::json& count =
1040                             asyncResp->res.jsonValue["Members@odata.count"];
1041                         members = nlohmann::json::array();
1042                         count = 0;
1043                         if (ec)
1044                         {
1045                             BMCWEB_LOG_WARNING
1046                                 << "LDAP certificate query failed: " << ec;
1047                             return;
1048                         }
1049                         for (const auto& cert : certs)
1050                         {
1051                             long id = getIDFromURL(cert.first.str);
1052                             if (id >= 0)
1053                             {
1054                                 members.push_back(
1055                                     {{"@odata.id", "/redfish/v1/AccountService/"
1056                                                    "LDAP/Certificates/" +
1057                                                        std::to_string(id)}});
1058                             }
1059                         }
1060                         count = members.size();
1061                     },
1062                     certs::ldapServiceName, certs::ldapObjectPath,
1063                     certs::dbusObjManagerIntf, "GetManagedObjects");
1064             });
1065 
1066     BMCWEB_ROUTE(app, "/redfish/v1/AccountService/LDAP/Certificates/")
1067         .privileges({{"ConfigureManager"}})
1068         .methods(boost::beast::http::verb::post)(
1069             [](const crow::Request& req,
1070                const std::shared_ptr<bmcweb::AsyncResp>& asyncResp) {
1071                 std::string certFileBody =
1072                     getCertificateFromReqBody(asyncResp, req);
1073 
1074                 if (certFileBody.empty())
1075                 {
1076                     BMCWEB_LOG_ERROR
1077                         << "Cannot get certificate from request body.";
1078                     messages::unrecognizedRequestBody(asyncResp->res);
1079                     return;
1080                 }
1081 
1082                 std::shared_ptr<CertificateFile> certFile =
1083                     std::make_shared<CertificateFile>(certFileBody);
1084 
1085                 crow::connections::systemBus->async_method_call(
1086                     [asyncResp, certFile](const boost::system::error_code ec,
1087                                           const std::string& objectPath) {
1088                         if (ec)
1089                         {
1090                             BMCWEB_LOG_ERROR << "DBUS response error: " << ec;
1091                             messages::internalError(asyncResp->res);
1092                             return;
1093                         }
1094                         long certId = getIDFromURL(objectPath);
1095                         if (certId < 0)
1096                         {
1097                             BMCWEB_LOG_ERROR << "Invalid objectPath value"
1098                                              << objectPath;
1099                             messages::internalError(asyncResp->res);
1100                             return;
1101                         }
1102                         std::string certURL =
1103                             "/redfish/v1/AccountService/LDAP/Certificates/" +
1104                             std::to_string(certId);
1105                         getCertificateProperties(asyncResp, objectPath,
1106                                                  certs::ldapServiceName, certId,
1107                                                  certURL, "LDAP Certificate");
1108                         BMCWEB_LOG_DEBUG << "LDAP certificate install file="
1109                                          << certFile->getCertFilePath();
1110                     },
1111                     certs::ldapServiceName, certs::ldapObjectPath,
1112                     certs::certInstallIntf, "Install",
1113                     certFile->getCertFilePath());
1114             });
1115 } // requestRoutesLDAPCertificateCollection
1116 
1117 /**
1118  * Certificate resource describes a certificate used to prove the identity
1119  * of a component, account or service.
1120  */
1121 inline void requestRoutesLDAPCertificate(App& app)
1122 {
1123     BMCWEB_ROUTE(app, "/redfish/v1/AccountService/LDAP/Certificates/<str>/")
1124         .privileges({{"ConfigureManager"}})
1125         .methods(boost::beast::http::verb::get)(
1126             [](const crow::Request& req,
1127                const std::shared_ptr<bmcweb::AsyncResp>& asyncResp,
1128                const std::string&) {
1129                 long id = getIDFromURL(req.url);
1130                 if (id < 0)
1131                 {
1132                     BMCWEB_LOG_ERROR << "Invalid url value" << req.url;
1133                     messages::internalError(asyncResp->res);
1134                     return;
1135                 }
1136                 BMCWEB_LOG_DEBUG << "LDAP Certificate ID="
1137                                  << std::to_string(id);
1138                 std::string certURL =
1139                     "/redfish/v1/AccountService/LDAP/Certificates/" +
1140                     std::to_string(id);
1141                 std::string objectPath = certs::ldapObjectPath;
1142                 objectPath += "/";
1143                 objectPath += std::to_string(id);
1144                 getCertificateProperties(asyncResp, objectPath,
1145                                          certs::ldapServiceName, id, certURL,
1146                                          "LDAP Certificate");
1147             });
1148 } // requestRoutesLDAPCertificate
1149 /**
1150  * Collection of TrustStoreCertificate certificates
1151  */
1152 inline void requestRoutesTrustStoreCertificateCollection(App& app)
1153 {
1154     BMCWEB_ROUTE(app, "/redfish/v1/Managers/bmc/Truststore/Certificates/")
1155         .privileges({{"ConfigureManager"}})
1156         .methods(boost::beast::http::verb::get)(
1157             [](const crow::Request&,
1158                const std::shared_ptr<bmcweb::AsyncResp>& asyncResp) {
1159                 asyncResp->res.jsonValue = {
1160                     {"@odata.id",
1161                      "/redfish/v1/Managers/bmc/Truststore/Certificates/"},
1162                     {"@odata.type",
1163                      "#CertificateCollection.CertificateCollection"},
1164                     {"Name", "TrustStore Certificates Collection"},
1165                     {"Description",
1166                      "A Collection of TrustStore certificate instances"}};
1167 
1168                 crow::connections::systemBus->async_method_call(
1169                     [asyncResp](const boost::system::error_code ec,
1170                                 const ManagedObjectType& certs) {
1171                         if (ec)
1172                         {
1173                             BMCWEB_LOG_ERROR << "DBUS response error: " << ec;
1174                             messages::internalError(asyncResp->res);
1175                             return;
1176                         }
1177                         nlohmann::json& members =
1178                             asyncResp->res.jsonValue["Members"];
1179                         members = nlohmann::json::array();
1180                         for (const auto& cert : certs)
1181                         {
1182                             long id = getIDFromURL(cert.first.str);
1183                             if (id >= 0)
1184                             {
1185                                 members.push_back(
1186                                     {{"@odata.id", "/redfish/v1/Managers/bmc/"
1187                                                    "Truststore/Certificates/" +
1188                                                        std::to_string(id)}});
1189                             }
1190                         }
1191                         asyncResp->res.jsonValue["Members@odata.count"] =
1192                             members.size();
1193                     },
1194                     certs::authorityServiceName, certs::authorityObjectPath,
1195                     certs::dbusObjManagerIntf, "GetManagedObjects");
1196             });
1197 
1198     BMCWEB_ROUTE(app, "/redfish/v1/Managers/bmc/Truststore/Certificates/")
1199         .privileges({{"ConfigureManager"}})
1200         .methods(boost::beast::http::verb::post)(
1201             [](const crow::Request& req,
1202                const std::shared_ptr<bmcweb::AsyncResp>& asyncResp) {
1203                 std::string certFileBody =
1204                     getCertificateFromReqBody(asyncResp, req);
1205 
1206                 if (certFileBody.empty())
1207                 {
1208                     BMCWEB_LOG_ERROR
1209                         << "Cannot get certificate from request body.";
1210                     messages::unrecognizedRequestBody(asyncResp->res);
1211                     return;
1212                 }
1213 
1214                 std::shared_ptr<CertificateFile> certFile =
1215                     std::make_shared<CertificateFile>(certFileBody);
1216                 crow::connections::systemBus->async_method_call(
1217                     [asyncResp, certFile](const boost::system::error_code ec,
1218                                           const std::string& objectPath) {
1219                         if (ec)
1220                         {
1221                             BMCWEB_LOG_ERROR << "DBUS response error: " << ec;
1222                             messages::internalError(asyncResp->res);
1223                             return;
1224                         }
1225                         long certId = getIDFromURL(objectPath);
1226                         if (certId < 0)
1227                         {
1228                             BMCWEB_LOG_ERROR << "Invalid objectPath value"
1229                                              << objectPath;
1230                             messages::internalError(asyncResp->res);
1231                             return;
1232                         }
1233                         std::string certURL = "/redfish/v1/Managers/bmc/"
1234                                               "Truststore/Certificates/" +
1235                                               std::to_string(certId);
1236 
1237                         getCertificateProperties(
1238                             asyncResp, objectPath, certs::authorityServiceName,
1239                             certId, certURL, "TrustStore Certificate");
1240                         BMCWEB_LOG_DEBUG
1241                             << "TrustStore certificate install file="
1242                             << certFile->getCertFilePath();
1243                     },
1244                     certs::authorityServiceName, certs::authorityObjectPath,
1245                     certs::certInstallIntf, "Install",
1246                     certFile->getCertFilePath());
1247             });
1248 } // requestRoutesTrustStoreCertificateCollection
1249 
1250 /**
1251  * Certificate resource describes a certificate used to prove the identity
1252  * of a component, account or service.
1253  */
1254 inline void requestRoutesTrustStoreCertificate(App& app)
1255 {
1256     BMCWEB_ROUTE(app, "/redfish/v1/Managers/bmc/Truststore/Certificates/<str>/")
1257         .privileges({{"ConfigureManager"}})
1258         .methods(boost::beast::http::verb::get)(
1259             [](const crow::Request& req,
1260                const std::shared_ptr<bmcweb::AsyncResp>& asyncResp,
1261                const std::string&) {
1262                 long id = getIDFromURL(req.url);
1263                 if (id < 0)
1264                 {
1265                     BMCWEB_LOG_ERROR << "Invalid url value" << req.url;
1266                     messages::internalError(asyncResp->res);
1267                     return;
1268                 }
1269                 BMCWEB_LOG_DEBUG << "TrustStoreCertificate::doGet ID="
1270                                  << std::to_string(id);
1271                 std::string certURL =
1272                     "/redfish/v1/Managers/bmc/Truststore/Certificates/" +
1273                     std::to_string(id);
1274                 std::string objectPath = certs::authorityObjectPath;
1275                 objectPath += "/";
1276                 objectPath += std::to_string(id);
1277                 getCertificateProperties(asyncResp, objectPath,
1278                                          certs::authorityServiceName, id,
1279                                          certURL, "TrustStore Certificate");
1280             });
1281 
1282     BMCWEB_ROUTE(app, "/redfish/v1/Managers/bmc/Truststore/Certificates/<str>/")
1283         .privileges({{"ConfigureManager"}})
1284         .methods(boost::beast::http::verb::delete_)(
1285             [](const crow::Request& req,
1286                const std::shared_ptr<bmcweb::AsyncResp>& asyncResp,
1287                const std::string& param) {
1288                 if (param.empty())
1289                 {
1290                     messages::internalError(asyncResp->res);
1291                     return;
1292                 }
1293 
1294                 long id = getIDFromURL(req.url);
1295                 if (id < 0)
1296                 {
1297                     BMCWEB_LOG_ERROR << "Invalid url value: " << req.url;
1298                     messages::resourceNotFound(asyncResp->res,
1299                                                "TrustStore Certificate",
1300                                                std::string(req.url));
1301                     return;
1302                 }
1303                 BMCWEB_LOG_DEBUG << "TrustStoreCertificate::doDelete ID="
1304                                  << std::to_string(id);
1305                 std::string certPath = certs::authorityObjectPath;
1306                 certPath += "/";
1307                 certPath += std::to_string(id);
1308 
1309                 crow::connections::systemBus->async_method_call(
1310                     [asyncResp, id](const boost::system::error_code ec) {
1311                         if (ec)
1312                         {
1313                             messages::resourceNotFound(asyncResp->res,
1314                                                        "TrustStore Certificate",
1315                                                        std::to_string(id));
1316                             return;
1317                         }
1318                         BMCWEB_LOG_INFO << "Certificate deleted";
1319                         asyncResp->res.result(
1320                             boost::beast::http::status::no_content);
1321                     },
1322                     certs::authorityServiceName, certPath, certs::objDeleteIntf,
1323                     "Delete");
1324             });
1325 } // requestRoutesTrustStoreCertificate
1326 } // namespace redfish
1327