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