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