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::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->empty())
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->empty())
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->empty())
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<std::pair<
453                         std::string,
454                         std::vector<std::pair<std::string,
455                                               dbus::utility::DbusVariantType>>>>
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 PropertiesMap =
573         boost::container::flat_map<std::string, dbus::utility::DbusVariantType>;
574     BMCWEB_LOG_DEBUG << "getCertificateProperties Path=" << objectPath
575                      << " certId=" << certId << " certURl=" << certURL;
576     crow::connections::systemBus->async_method_call(
577         [asyncResp, certURL, certId, name](const boost::system::error_code ec,
578                                            const PropertiesMap& properties) {
579             if (ec)
580             {
581                 BMCWEB_LOG_ERROR << "DBUS response error: " << ec;
582                 messages::resourceNotFound(asyncResp->res, name,
583                                            std::to_string(certId));
584                 return;
585             }
586             asyncResp->res.jsonValue = {
587                 {"@odata.id", certURL},
588                 {"@odata.type", "#Certificate.v1_0_0.Certificate"},
589                 {"Id", std::to_string(certId)},
590                 {"Name", name},
591                 {"Description", name}};
592             for (const auto& property : properties)
593             {
594                 if (property.first == "CertificateString")
595                 {
596                     asyncResp->res.jsonValue["CertificateString"] = "";
597                     const std::string* value =
598                         std::get_if<std::string>(&property.second);
599                     if (value)
600                     {
601                         asyncResp->res.jsonValue["CertificateString"] = *value;
602                     }
603                 }
604                 else if (property.first == "KeyUsage")
605                 {
606                     nlohmann::json& keyUsage =
607                         asyncResp->res.jsonValue["KeyUsage"];
608                     keyUsage = nlohmann::json::array();
609                     const std::vector<std::string>* value =
610                         std::get_if<std::vector<std::string>>(&property.second);
611                     if (value)
612                     {
613                         for (const std::string& usage : *value)
614                         {
615                             keyUsage.push_back(usage);
616                         }
617                     }
618                 }
619                 else if (property.first == "Issuer")
620                 {
621                     const std::string* value =
622                         std::get_if<std::string>(&property.second);
623                     if (value)
624                     {
625                         updateCertIssuerOrSubject(
626                             asyncResp->res.jsonValue["Issuer"], *value);
627                     }
628                 }
629                 else if (property.first == "Subject")
630                 {
631                     const std::string* value =
632                         std::get_if<std::string>(&property.second);
633                     if (value)
634                     {
635                         updateCertIssuerOrSubject(
636                             asyncResp->res.jsonValue["Subject"], *value);
637                     }
638                 }
639                 else if (property.first == "ValidNotAfter")
640                 {
641                     const uint64_t* value =
642                         std::get_if<uint64_t>(&property.second);
643                     if (value)
644                     {
645                         asyncResp->res.jsonValue["ValidNotAfter"] =
646                             crow::utility::getDateTimeUint(*value);
647                     }
648                 }
649                 else if (property.first == "ValidNotBefore")
650                 {
651                     const uint64_t* value =
652                         std::get_if<uint64_t>(&property.second);
653                     if (value)
654                     {
655                         asyncResp->res.jsonValue["ValidNotBefore"] =
656                             crow::utility::getDateTimeUint(*value);
657                     }
658                 }
659             }
660             asyncResp->res.addHeader("Location", certURL);
661         },
662         service, objectPath, certs::dbusPropIntf, "GetAll",
663         certs::certPropIntf);
664 }
665 
666 using GetObjectType =
667     std::vector<std::pair<std::string, std::vector<std::string>>>;
668 
669 /**
670  * Action to replace an existing certificate
671  */
672 inline void requestRoutesCertificateActionsReplaceCertificate(App& app)
673 {
674     BMCWEB_ROUTE(
675         app,
676         "/redfish/v1/CertificateService/Actions/CertificateService.ReplaceCertificate/")
677         .privileges(redfish::privileges::postCertificateService)
678         .methods(
679             boost::beast::http::verb::
680                 post)([](const crow::Request& req,
681                          const std::shared_ptr<bmcweb::AsyncResp>& asyncResp) {
682             std::string certificate;
683             nlohmann::json certificateUri;
684             std::optional<std::string> certificateType = "PEM";
685 
686             if (!json_util::readJson(req, asyncResp->res, "CertificateString",
687                                      certificate, "CertificateUri",
688                                      certificateUri, "CertificateType",
689                                      certificateType))
690             {
691                 BMCWEB_LOG_ERROR << "Required parameters are missing";
692                 messages::internalError(asyncResp->res);
693                 return;
694             }
695 
696             if (!certificateType)
697             {
698                 // should never happen, but it never hurts to be paranoid.
699                 return;
700             }
701             if (certificateType != "PEM")
702             {
703                 messages::actionParameterNotSupported(
704                     asyncResp->res, "CertificateType", "ReplaceCertificate");
705                 return;
706             }
707 
708             std::string certURI;
709             if (!redfish::json_util::readJson(certificateUri, asyncResp->res,
710                                               "@odata.id", certURI))
711             {
712                 messages::actionParameterMissing(
713                     asyncResp->res, "ReplaceCertificate", "CertificateUri");
714                 return;
715             }
716 
717             BMCWEB_LOG_INFO << "Certificate URI to replace" << certURI;
718             long id = getIDFromURL(certURI);
719             if (id < 0)
720             {
721                 messages::actionParameterValueFormatError(
722                     asyncResp->res, certURI, "CertificateUri",
723                     "ReplaceCertificate");
724                 return;
725             }
726             std::string objectPath;
727             std::string name;
728             std::string service;
729             if (boost::starts_with(
730                     certURI,
731                     "/redfish/v1/Managers/bmc/NetworkProtocol/HTTPS/Certificates/"))
732             {
733                 objectPath = std::string(certs::httpsObjectPath) + "/" +
734                              std::to_string(id);
735                 name = "HTTPS certificate";
736                 service = certs::httpsServiceName;
737             }
738             else if (boost::starts_with(
739                          certURI,
740                          "/redfish/v1/AccountService/LDAP/Certificates/"))
741             {
742                 objectPath = std::string(certs::ldapObjectPath) + "/" +
743                              std::to_string(id);
744                 name = "LDAP certificate";
745                 service = certs::ldapServiceName;
746             }
747             else if (boost::starts_with(
748                          certURI,
749                          "/redfish/v1/Managers/bmc/Truststore/Certificates/"))
750             {
751                 objectPath = std::string(certs::authorityObjectPath) + "/" +
752                              std::to_string(id);
753                 name = "TrustStore certificate";
754                 service = certs::authorityServiceName;
755             }
756             else
757             {
758                 messages::actionParameterNotSupported(
759                     asyncResp->res, "CertificateUri", "ReplaceCertificate");
760                 return;
761             }
762 
763             std::shared_ptr<CertificateFile> certFile =
764                 std::make_shared<CertificateFile>(certificate);
765             crow::connections::systemBus->async_method_call(
766                 [asyncResp, certFile, objectPath, service, certURI, id,
767                  name](const boost::system::error_code ec) {
768                     if (ec)
769                     {
770                         BMCWEB_LOG_ERROR << "DBUS response error: " << ec;
771                         messages::resourceNotFound(asyncResp->res, name,
772                                                    std::to_string(id));
773                         return;
774                     }
775                     getCertificateProperties(asyncResp, objectPath, service, id,
776                                              certURI, name);
777                     BMCWEB_LOG_DEBUG << "HTTPS certificate install file="
778                                      << certFile->getCertFilePath();
779                 },
780                 service, objectPath, certs::certReplaceIntf, "Replace",
781                 certFile->getCertFilePath());
782         });
783 } // requestRoutesCertificateActionsReplaceCertificate
784 
785 /**
786  * Certificate resource describes a certificate used to prove the identity
787  * of a component, account or service.
788  */
789 
790 inline void requestRoutesHTTPSCertificate(App& app)
791 {
792     BMCWEB_ROUTE(
793         app,
794         "/redfish/v1/Managers/bmc/NetworkProtocol/HTTPS/Certificates/<str>/")
795         .privileges(redfish::privileges::getCertificate)
796         .methods(
797             boost::beast::http::verb::
798                 get)([](const crow::Request& req,
799                         const std::shared_ptr<bmcweb::AsyncResp>& asyncResp,
800                         const std::string& param) -> void {
801             if (param.empty())
802             {
803                 messages::internalError(asyncResp->res);
804                 return;
805             }
806             long id = getIDFromURL(req.url);
807 
808             BMCWEB_LOG_DEBUG << "HTTPSCertificate::doGet ID="
809                              << std::to_string(id);
810             std::string certURL =
811                 "/redfish/v1/Managers/bmc/NetworkProtocol/HTTPS/Certificates/" +
812                 std::to_string(id);
813             std::string objectPath = certs::httpsObjectPath;
814             objectPath += "/";
815             objectPath += std::to_string(id);
816             getCertificateProperties(asyncResp, objectPath,
817                                      certs::httpsServiceName, id, certURL,
818                                      "HTTPS Certificate");
819         });
820 }
821 
822 /**
823  * Collection of HTTPS certificates
824  */
825 inline void requestRoutesHTTPSCertificateCollection(App& app)
826 {
827     BMCWEB_ROUTE(app,
828                  "/redfish/v1/Managers/bmc/NetworkProtocol/HTTPS/Certificates/")
829         .privileges(redfish::privileges::getCertificateCollection)
830         .methods(
831             boost::beast::http::verb::
832                 get)([](const crow::Request&,
833                         const std::shared_ptr<bmcweb::AsyncResp>& asyncResp) {
834             asyncResp->res.jsonValue = {
835                 {"@odata.id",
836                  "/redfish/v1/Managers/bmc/NetworkProtocol/HTTPS/Certificates"},
837                 {"@odata.type", "#CertificateCollection.CertificateCollection"},
838                 {"Name", "HTTPS Certificates Collection"},
839                 {"Description", "A Collection of HTTPS certificate instances"}};
840 
841             crow::connections::systemBus->async_method_call(
842                 [asyncResp](const boost::system::error_code ec,
843                             const dbus::utility::ManagedObjectType& certs) {
844                     if (ec)
845                     {
846                         BMCWEB_LOG_ERROR << "DBUS response error: " << ec;
847                         messages::internalError(asyncResp->res);
848                         return;
849                     }
850                     nlohmann::json& members =
851                         asyncResp->res.jsonValue["Members"];
852                     members = nlohmann::json::array();
853                     for (const auto& cert : certs)
854                     {
855                         long id = getIDFromURL(cert.first.str);
856                         if (id >= 0)
857                         {
858                             members.push_back(
859                                 {{"@odata.id",
860                                   "/redfish/v1/Managers/bmc/NetworkProtocol/HTTPS/Certificates/" +
861                                       std::to_string(id)}});
862                         }
863                     }
864                     asyncResp->res.jsonValue["Members@odata.count"] =
865                         members.size();
866                 },
867                 certs::httpsServiceName, certs::httpsObjectPath,
868                 certs::dbusObjManagerIntf, "GetManagedObjects");
869         });
870 
871     BMCWEB_ROUTE(app,
872                  "/redfish/v1/Managers/bmc/NetworkProtocol/HTTPS/Certificates/")
873         .privileges(redfish::privileges::postCertificateCollection)
874         .methods(
875             boost::beast::http::verb::
876                 post)([](const crow::Request& req,
877                          const std::shared_ptr<bmcweb::AsyncResp>& asyncResp) {
878             BMCWEB_LOG_DEBUG << "HTTPSCertificateCollection::doPost";
879 
880             asyncResp->res.jsonValue = {{"Name", "HTTPS Certificate"},
881                                         {"Description", "HTTPS Certificate"}};
882 
883             std::string certFileBody =
884                 getCertificateFromReqBody(asyncResp, req);
885 
886             if (certFileBody.empty())
887             {
888                 BMCWEB_LOG_ERROR << "Cannot get certificate from request body.";
889                 messages::unrecognizedRequestBody(asyncResp->res);
890                 return;
891             }
892 
893             std::shared_ptr<CertificateFile> certFile =
894                 std::make_shared<CertificateFile>(certFileBody);
895 
896             crow::connections::systemBus->async_method_call(
897                 [asyncResp, certFile](const boost::system::error_code ec,
898                                       const std::string& objectPath) {
899                     if (ec)
900                     {
901                         BMCWEB_LOG_ERROR << "DBUS response error: " << ec;
902                         messages::internalError(asyncResp->res);
903                         return;
904                     }
905                     long certId = getIDFromURL(objectPath);
906                     if (certId < 0)
907                     {
908                         BMCWEB_LOG_ERROR << "Invalid objectPath value"
909                                          << objectPath;
910                         messages::internalError(asyncResp->res);
911                         return;
912                     }
913                     std::string certURL =
914                         "/redfish/v1/Managers/bmc/NetworkProtocol/HTTPS/Certificates/" +
915                         std::to_string(certId);
916                     getCertificateProperties(asyncResp, objectPath,
917                                              certs::httpsServiceName, certId,
918                                              certURL, "HTTPS Certificate");
919                     BMCWEB_LOG_DEBUG << "HTTPS certificate install file="
920                                      << certFile->getCertFilePath();
921                 },
922                 certs::httpsServiceName, certs::httpsObjectPath,
923                 certs::certInstallIntf, "Install", certFile->getCertFilePath());
924         });
925 } // requestRoutesHTTPSCertificateCollection
926 
927 /**
928  * @brief Retrieve the certificates installed list and append to the
929  * response
930  *
931  * @param[in] asyncResp Shared pointer to the response message
932  * @param[in] certURL  Path of the certificate object
933  * @param[in] path  Path of the D-Bus service object
934  * @return None
935  */
936 inline void
937     getCertificateLocations(const std::shared_ptr<bmcweb::AsyncResp>& asyncResp,
938                             const std::string& certURL, const std::string& path,
939                             const std::string& service)
940 {
941     BMCWEB_LOG_DEBUG << "getCertificateLocations URI=" << certURL
942                      << " Path=" << path << " service= " << service;
943     crow::connections::systemBus->async_method_call(
944         [asyncResp, certURL](const boost::system::error_code ec,
945                              const dbus::utility::ManagedObjectType& certs) {
946             if (ec)
947             {
948                 BMCWEB_LOG_WARNING
949                     << "Certificate collection query failed: " << ec
950                     << ", skipping " << certURL;
951                 return;
952             }
953             nlohmann::json& links =
954                 asyncResp->res.jsonValue["Links"]["Certificates"];
955             for (const auto& cert : certs)
956             {
957                 long id = getIDFromURL(cert.first.str);
958                 if (id >= 0)
959                 {
960                     links.push_back(
961                         {{"@odata.id", certURL + std::to_string(id)}});
962                 }
963             }
964             asyncResp->res.jsonValue["Links"]["Certificates@odata.count"] =
965                 links.size();
966         },
967         service, path, certs::dbusObjManagerIntf, "GetManagedObjects");
968 }
969 
970 /**
971  * The certificate location schema defines a resource that an administrator
972  * can use in order to locate all certificates installed on a given service.
973  */
974 inline void requestRoutesCertificateLocations(App& app)
975 {
976     BMCWEB_ROUTE(app, "/redfish/v1/CertificateService/CertificateLocations/")
977         .privileges(redfish::privileges::getCertificateLocations)
978         .methods(
979             boost::beast::http::verb::
980                 get)([](const crow::Request&,
981                         const std::shared_ptr<bmcweb::AsyncResp>& asyncResp) {
982             asyncResp->res.jsonValue = {
983                 {"@odata.id",
984                  "/redfish/v1/CertificateService/CertificateLocations"},
985                 {"@odata.type",
986                  "#CertificateLocations.v1_0_0.CertificateLocations"},
987                 {"Name", "Certificate Locations"},
988                 {"Id", "CertificateLocations"},
989                 {"Description",
990                  "Defines a resource that an administrator can use in order to "
991                  "locate all certificates installed on a given service"}};
992 
993             nlohmann::json& links =
994                 asyncResp->res.jsonValue["Links"]["Certificates"];
995             links = nlohmann::json::array();
996             getCertificateLocations(
997                 asyncResp,
998                 "/redfish/v1/Managers/bmc/NetworkProtocol/HTTPS/Certificates/",
999                 certs::httpsObjectPath, certs::httpsServiceName);
1000             getCertificateLocations(
1001                 asyncResp, "/redfish/v1/AccountService/LDAP/Certificates/",
1002                 certs::ldapObjectPath, certs::ldapServiceName);
1003             getCertificateLocations(
1004                 asyncResp, "/redfish/v1/Managers/bmc/Truststore/Certificates/",
1005                 certs::authorityObjectPath, certs::authorityServiceName);
1006         });
1007 }
1008 // requestRoutesCertificateLocations
1009 
1010 /**
1011  * Collection of LDAP certificates
1012  */
1013 inline void requestRoutesLDAPCertificateCollection(App& app)
1014 {
1015     BMCWEB_ROUTE(app, "/redfish/v1/AccountService/LDAP/Certificates/")
1016         .privileges(redfish::privileges::getCertificateCollection)
1017         .methods(
1018             boost::beast::http::verb::
1019                 get)([](const crow::Request&,
1020                         const std::shared_ptr<bmcweb::AsyncResp>& asyncResp) {
1021             asyncResp->res.jsonValue = {
1022                 {"@odata.id", "/redfish/v1/AccountService/LDAP/Certificates"},
1023                 {"@odata.type", "#CertificateCollection.CertificateCollection"},
1024                 {"Name", "LDAP Certificates Collection"},
1025                 {"Description", "A Collection of LDAP certificate instances"}};
1026 
1027             crow::connections::systemBus->async_method_call(
1028                 [asyncResp](const boost::system::error_code ec,
1029                             const dbus::utility::ManagedObjectType& certs) {
1030                     nlohmann::json& members =
1031                         asyncResp->res.jsonValue["Members"];
1032                     nlohmann::json& count =
1033                         asyncResp->res.jsonValue["Members@odata.count"];
1034                     members = nlohmann::json::array();
1035                     count = 0;
1036                     if (ec)
1037                     {
1038                         BMCWEB_LOG_WARNING << "LDAP certificate query failed: "
1039                                            << ec;
1040                         return;
1041                     }
1042                     for (const auto& cert : certs)
1043                     {
1044                         long id = getIDFromURL(cert.first.str);
1045                         if (id >= 0)
1046                         {
1047                             members.push_back(
1048                                 {{"@odata.id",
1049                                   "/redfish/v1/AccountService/LDAP/Certificates/" +
1050                                       std::to_string(id)}});
1051                         }
1052                     }
1053                     count = members.size();
1054                 },
1055                 certs::ldapServiceName, certs::ldapObjectPath,
1056                 certs::dbusObjManagerIntf, "GetManagedObjects");
1057         });
1058 
1059     BMCWEB_ROUTE(app, "/redfish/v1/AccountService/LDAP/Certificates/")
1060         .privileges(redfish::privileges::postCertificateCollection)
1061         .methods(boost::beast::http::verb::post)(
1062             [](const crow::Request& req,
1063                const std::shared_ptr<bmcweb::AsyncResp>& asyncResp) {
1064                 std::string certFileBody =
1065                     getCertificateFromReqBody(asyncResp, req);
1066 
1067                 if (certFileBody.empty())
1068                 {
1069                     BMCWEB_LOG_ERROR
1070                         << "Cannot get certificate from request body.";
1071                     messages::unrecognizedRequestBody(asyncResp->res);
1072                     return;
1073                 }
1074 
1075                 std::shared_ptr<CertificateFile> certFile =
1076                     std::make_shared<CertificateFile>(certFileBody);
1077 
1078                 crow::connections::systemBus->async_method_call(
1079                     [asyncResp, certFile](const boost::system::error_code ec,
1080                                           const std::string& objectPath) {
1081                         if (ec)
1082                         {
1083                             BMCWEB_LOG_ERROR << "DBUS response error: " << ec;
1084                             messages::internalError(asyncResp->res);
1085                             return;
1086                         }
1087                         long certId = getIDFromURL(objectPath);
1088                         if (certId < 0)
1089                         {
1090                             BMCWEB_LOG_ERROR << "Invalid objectPath value"
1091                                              << objectPath;
1092                             messages::internalError(asyncResp->res);
1093                             return;
1094                         }
1095                         std::string certURL =
1096                             "/redfish/v1/AccountService/LDAP/Certificates/" +
1097                             std::to_string(certId);
1098                         getCertificateProperties(asyncResp, objectPath,
1099                                                  certs::ldapServiceName, certId,
1100                                                  certURL, "LDAP Certificate");
1101                         BMCWEB_LOG_DEBUG << "LDAP certificate install file="
1102                                          << certFile->getCertFilePath();
1103                     },
1104                     certs::ldapServiceName, certs::ldapObjectPath,
1105                     certs::certInstallIntf, "Install",
1106                     certFile->getCertFilePath());
1107             });
1108 } // requestRoutesLDAPCertificateCollection
1109 
1110 /**
1111  * Certificate resource describes a certificate used to prove the identity
1112  * of a component, account or service.
1113  */
1114 inline void requestRoutesLDAPCertificate(App& app)
1115 {
1116     BMCWEB_ROUTE(app, "/redfish/v1/AccountService/LDAP/Certificates/<str>/")
1117         .privileges(redfish::privileges::getCertificate)
1118         .methods(boost::beast::http::verb::get)(
1119             [](const crow::Request& req,
1120                const std::shared_ptr<bmcweb::AsyncResp>& asyncResp,
1121                const std::string&) {
1122                 long id = getIDFromURL(req.url);
1123                 if (id < 0)
1124                 {
1125                     BMCWEB_LOG_ERROR << "Invalid url value" << req.url;
1126                     messages::internalError(asyncResp->res);
1127                     return;
1128                 }
1129                 BMCWEB_LOG_DEBUG << "LDAP Certificate ID="
1130                                  << std::to_string(id);
1131                 std::string certURL =
1132                     "/redfish/v1/AccountService/LDAP/Certificates/" +
1133                     std::to_string(id);
1134                 std::string objectPath = certs::ldapObjectPath;
1135                 objectPath += "/";
1136                 objectPath += std::to_string(id);
1137                 getCertificateProperties(asyncResp, objectPath,
1138                                          certs::ldapServiceName, id, certURL,
1139                                          "LDAP Certificate");
1140             });
1141 } // requestRoutesLDAPCertificate
1142 /**
1143  * Collection of TrustStoreCertificate certificates
1144  */
1145 inline void requestRoutesTrustStoreCertificateCollection(App& app)
1146 {
1147     BMCWEB_ROUTE(app, "/redfish/v1/Managers/bmc/Truststore/Certificates/")
1148         .privileges(redfish::privileges::getCertificate)
1149         .methods(
1150             boost::beast::http::verb::
1151                 get)([](const crow::Request&,
1152                         const std::shared_ptr<bmcweb::AsyncResp>& asyncResp) {
1153             asyncResp->res.jsonValue = {
1154                 {"@odata.id",
1155                  "/redfish/v1/Managers/bmc/Truststore/Certificates/"},
1156                 {"@odata.type", "#CertificateCollection.CertificateCollection"},
1157                 {"Name", "TrustStore Certificates Collection"},
1158                 {"Description",
1159                  "A Collection of TrustStore certificate instances"}};
1160 
1161             crow::connections::systemBus->async_method_call(
1162                 [asyncResp](const boost::system::error_code ec,
1163                             const dbus::utility::ManagedObjectType& certs) {
1164                     if (ec)
1165                     {
1166                         BMCWEB_LOG_ERROR << "DBUS response error: " << ec;
1167                         messages::internalError(asyncResp->res);
1168                         return;
1169                     }
1170                     nlohmann::json& members =
1171                         asyncResp->res.jsonValue["Members"];
1172                     members = nlohmann::json::array();
1173                     for (const auto& cert : certs)
1174                     {
1175                         long id = getIDFromURL(cert.first.str);
1176                         if (id >= 0)
1177                         {
1178                             members.push_back(
1179                                 {{"@odata.id",
1180                                   "/redfish/v1/Managers/bmc/Truststore/Certificates/" +
1181                                       std::to_string(id)}});
1182                         }
1183                     }
1184                     asyncResp->res.jsonValue["Members@odata.count"] =
1185                         members.size();
1186                 },
1187                 certs::authorityServiceName, certs::authorityObjectPath,
1188                 certs::dbusObjManagerIntf, "GetManagedObjects");
1189         });
1190 
1191     BMCWEB_ROUTE(app, "/redfish/v1/Managers/bmc/Truststore/Certificates/")
1192         .privileges(redfish::privileges::postCertificateCollection)
1193         .methods(
1194             boost::beast::http::verb::
1195                 post)([](const crow::Request& req,
1196                          const std::shared_ptr<bmcweb::AsyncResp>& asyncResp) {
1197             std::string certFileBody =
1198                 getCertificateFromReqBody(asyncResp, req);
1199 
1200             if (certFileBody.empty())
1201             {
1202                 BMCWEB_LOG_ERROR << "Cannot get certificate from request body.";
1203                 messages::unrecognizedRequestBody(asyncResp->res);
1204                 return;
1205             }
1206 
1207             std::shared_ptr<CertificateFile> certFile =
1208                 std::make_shared<CertificateFile>(certFileBody);
1209             crow::connections::systemBus->async_method_call(
1210                 [asyncResp, certFile](const boost::system::error_code ec,
1211                                       const std::string& objectPath) {
1212                     if (ec)
1213                     {
1214                         BMCWEB_LOG_ERROR << "DBUS response error: " << ec;
1215                         messages::internalError(asyncResp->res);
1216                         return;
1217                     }
1218                     long certId = getIDFromURL(objectPath);
1219                     if (certId < 0)
1220                     {
1221                         BMCWEB_LOG_ERROR << "Invalid objectPath value"
1222                                          << objectPath;
1223                         messages::internalError(asyncResp->res);
1224                         return;
1225                     }
1226                     std::string certURL =
1227                         "/redfish/v1/Managers/bmc/Truststore/Certificates/" +
1228                         std::to_string(certId);
1229 
1230                     getCertificateProperties(
1231                         asyncResp, objectPath, certs::authorityServiceName,
1232                         certId, certURL, "TrustStore Certificate");
1233                     BMCWEB_LOG_DEBUG << "TrustStore certificate install file="
1234                                      << certFile->getCertFilePath();
1235                 },
1236                 certs::authorityServiceName, certs::authorityObjectPath,
1237                 certs::certInstallIntf, "Install", certFile->getCertFilePath());
1238         });
1239 } // requestRoutesTrustStoreCertificateCollection
1240 
1241 /**
1242  * Certificate resource describes a certificate used to prove the identity
1243  * of a component, account or service.
1244  */
1245 inline void requestRoutesTrustStoreCertificate(App& app)
1246 {
1247     BMCWEB_ROUTE(app, "/redfish/v1/Managers/bmc/Truststore/Certificates/<str>/")
1248         .privileges(redfish::privileges::getCertificate)
1249         .methods(boost::beast::http::verb::get)(
1250             [](const crow::Request& req,
1251                const std::shared_ptr<bmcweb::AsyncResp>& asyncResp,
1252                const std::string&) {
1253                 long id = getIDFromURL(req.url);
1254                 if (id < 0)
1255                 {
1256                     BMCWEB_LOG_ERROR << "Invalid url value" << req.url;
1257                     messages::internalError(asyncResp->res);
1258                     return;
1259                 }
1260                 BMCWEB_LOG_DEBUG << "TrustStoreCertificate::doGet ID="
1261                                  << std::to_string(id);
1262                 std::string certURL =
1263                     "/redfish/v1/Managers/bmc/Truststore/Certificates/" +
1264                     std::to_string(id);
1265                 std::string objectPath = certs::authorityObjectPath;
1266                 objectPath += "/";
1267                 objectPath += std::to_string(id);
1268                 getCertificateProperties(asyncResp, objectPath,
1269                                          certs::authorityServiceName, id,
1270                                          certURL, "TrustStore Certificate");
1271             });
1272 
1273     BMCWEB_ROUTE(app, "/redfish/v1/Managers/bmc/Truststore/Certificates/<str>/")
1274         .privileges(redfish::privileges::deleteCertificate)
1275         .methods(boost::beast::http::verb::delete_)(
1276             [](const crow::Request& req,
1277                const std::shared_ptr<bmcweb::AsyncResp>& asyncResp,
1278                const std::string& param) {
1279                 if (param.empty())
1280                 {
1281                     messages::internalError(asyncResp->res);
1282                     return;
1283                 }
1284 
1285                 long id = getIDFromURL(req.url);
1286                 if (id < 0)
1287                 {
1288                     BMCWEB_LOG_ERROR << "Invalid url value: " << req.url;
1289                     messages::resourceNotFound(asyncResp->res,
1290                                                "TrustStore Certificate",
1291                                                std::string(req.url));
1292                     return;
1293                 }
1294                 BMCWEB_LOG_DEBUG << "TrustStoreCertificate::doDelete ID="
1295                                  << std::to_string(id);
1296                 std::string certPath = certs::authorityObjectPath;
1297                 certPath += "/";
1298                 certPath += std::to_string(id);
1299 
1300                 crow::connections::systemBus->async_method_call(
1301                     [asyncResp, id](const boost::system::error_code ec) {
1302                         if (ec)
1303                         {
1304                             messages::resourceNotFound(asyncResp->res,
1305                                                        "TrustStore Certificate",
1306                                                        std::to_string(id));
1307                             return;
1308                         }
1309                         BMCWEB_LOG_INFO << "Certificate deleted";
1310                         asyncResp->res.result(
1311                             boost::beast::http::status::no_content);
1312                     },
1313                     certs::authorityServiceName, certPath, certs::objDeleteIntf,
1314                     "Delete");
1315             });
1316 } // requestRoutesTrustStoreCertificate
1317 } // namespace redfish
1318