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