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