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