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