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