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