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 
58             asyncResp->res.jsonValue["@odata.type"] =
59                 "#CertificateService.v1_0_0.CertificateService";
60             asyncResp->res.jsonValue["@odata.id"] =
61                 "/redfish/v1/CertificateService";
62             asyncResp->res.jsonValue["Id"] = "CertificateService";
63             asyncResp->res.jsonValue["Name"] = "Certificate Service";
64             asyncResp->res.jsonValue["Description"] =
65                 "Actions available to manage certificates";
66             // /redfish/v1/CertificateService/CertificateLocations is something
67             // only ConfigureManager can access then only display when the user
68             // has permissions ConfigureManager
69             Privileges effectiveUserPrivileges =
70                 redfish::getUserPrivileges(req.userRole);
71             if (isOperationAllowedWithPrivileges({{"ConfigureManager"}},
72                                                  effectiveUserPrivileges))
73             {
74                 asyncResp->res.jsonValue["CertificateLocations"]["@odata.id"] =
75                     "/redfish/v1/CertificateService/CertificateLocations";
76             }
77             asyncResp->res
78                 .jsonValue["Actions"]
79                           ["#CertificateService.ReplaceCertificate"] = {
80                 {"target",
81                  "/redfish/v1/CertificateService/Actions/CertificateService.ReplaceCertificate"},
82                 {"CertificateType@Redfish.AllowableValues", {"PEM"}}};
83             asyncResp->res
84                 .jsonValue["Actions"]["#CertificateService.GenerateCSR"] = {
85                 {"target",
86                  "/redfish/v1/CertificateService/Actions/CertificateService.GenerateCSR"}};
87         });
88 } // requestRoutesCertificateService
89 
90 /**
91  * @brief Find the ID specified in the URL
92  * Finds the numbers specified after the last "/" in the URL and returns.
93  * @param[in] path URL
94  * @return -1 on failure and number on success
95  */
96 inline long getIDFromURL(const std::string_view url)
97 {
98     std::size_t found = url.rfind('/');
99     if (found == std::string::npos)
100     {
101         return -1;
102     }
103 
104     if ((found + 1) < url.length())
105     {
106         std::string_view str = url.substr(found + 1);
107 
108         return boost::convert<long>(str, boost::cnv::strtol()).value_or(-1);
109     }
110 
111     return -1;
112 }
113 
114 inline std::string getCertificateFromReqBody(
115     const std::shared_ptr<bmcweb::AsyncResp>& asyncResp,
116     const crow::Request& req)
117 {
118     nlohmann::json reqJson = nlohmann::json::parse(req.body, nullptr, false);
119 
120     if (reqJson.is_discarded())
121     {
122         // We did not receive JSON request, proceed as it is RAW data
123         return req.body;
124     }
125 
126     std::string certificate;
127     std::optional<std::string> certificateType = "PEM";
128 
129     if (!json_util::readJsonPatch(req, asyncResp->res, "CertificateString",
130                                   certificate, "CertificateType",
131                                   certificateType))
132     {
133         BMCWEB_LOG_ERROR << "Required parameters are missing";
134         messages::internalError(asyncResp->res);
135         return {};
136     }
137 
138     if (*certificateType != "PEM")
139     {
140         messages::propertyValueNotInList(asyncResp->res, *certificateType,
141                                          "CertificateType");
142         return {};
143     }
144 
145     return certificate;
146 }
147 
148 /**
149  * Class to create a temporary certificate file for uploading to system
150  */
151 class CertificateFile
152 {
153   public:
154     CertificateFile() = delete;
155     CertificateFile(const CertificateFile&) = delete;
156     CertificateFile& operator=(const CertificateFile&) = delete;
157     CertificateFile(CertificateFile&&) = delete;
158     CertificateFile& operator=(CertificateFile&&) = delete;
159     CertificateFile(const std::string& certString)
160     {
161         std::array<char, 18> dirTemplate = {'/', 't', 'm', 'p', '/', 'C',
162                                             'e', 'r', 't', 's', '.', 'X',
163                                             'X', 'X', 'X', 'X', 'X', '\0'};
164         char* tempDirectory = mkdtemp(dirTemplate.data());
165         if (tempDirectory != nullptr)
166         {
167             certDirectory = tempDirectory;
168             certificateFile = certDirectory / "cert.pem";
169             std::ofstream out(certificateFile, std::ofstream::out |
170                                                    std::ofstream::binary |
171                                                    std::ofstream::trunc);
172             out << certString;
173             out.close();
174             BMCWEB_LOG_DEBUG << "Creating certificate file"
175                              << certificateFile.string();
176         }
177     }
178     ~CertificateFile()
179     {
180         if (std::filesystem::exists(certDirectory))
181         {
182             BMCWEB_LOG_DEBUG << "Removing certificate file"
183                              << certificateFile.string();
184             std::error_code ec;
185             std::filesystem::remove_all(certDirectory, ec);
186             if (ec)
187             {
188                 BMCWEB_LOG_ERROR << "Failed to remove temp directory"
189                                  << certDirectory.string();
190             }
191         }
192     }
193     std::string getCertFilePath()
194     {
195         return certificateFile;
196     }
197 
198   private:
199     std::filesystem::path certificateFile;
200     std::filesystem::path certDirectory;
201 };
202 
203 static std::unique_ptr<sdbusplus::bus::match::match> csrMatcher;
204 /**
205  * @brief Read data from CSR D-bus object and set to response
206  *
207  * @param[in] asyncResp Shared pointer to the response message
208  * @param[in] certURI Link to certifiate collection URI
209  * @param[in] service D-Bus service name
210  * @param[in] certObjPath certificate D-Bus object path
211  * @param[in] csrObjPath CSR D-Bus object path
212  * @return None
213  */
214 static void getCSR(const std::shared_ptr<bmcweb::AsyncResp>& asyncResp,
215                    const std::string& certURI, const std::string& service,
216                    const std::string& certObjPath,
217                    const std::string& csrObjPath)
218 {
219     BMCWEB_LOG_DEBUG << "getCSR CertObjectPath" << certObjPath
220                      << " CSRObjectPath=" << csrObjPath
221                      << " service=" << service;
222     crow::connections::systemBus->async_method_call(
223         [asyncResp, certURI](const boost::system::error_code ec,
224                              const std::string& csr) {
225             if (ec)
226             {
227                 BMCWEB_LOG_ERROR << "DBUS response error: " << ec;
228                 messages::internalError(asyncResp->res);
229                 return;
230             }
231             if (csr.empty())
232             {
233                 BMCWEB_LOG_ERROR << "CSR read is empty";
234                 messages::internalError(asyncResp->res);
235                 return;
236             }
237             asyncResp->res.jsonValue["CSRString"] = csr;
238             asyncResp->res.jsonValue["CertificateCollection"]["@odata.id"] =
239                 certURI;
240         },
241         service, csrObjPath, "xyz.openbmc_project.Certs.CSR", "CSR");
242 }
243 
244 /**
245  * Action to Generate CSR
246  */
247 inline void requestRoutesCertificateActionGenerateCSR(App& app)
248 {
249     BMCWEB_ROUTE(
250         app,
251         "/redfish/v1/CertificateService/Actions/CertificateService.GenerateCSR/")
252         .privileges(redfish::privileges::postCertificateService)
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["@odata.id"] = certURL;
597             asyncResp->res.jsonValue["@odata.type"] =
598                 "#Certificate.v1_0_0.Certificate";
599             asyncResp->res.jsonValue["Id"] = std::to_string(certId);
600             asyncResp->res.jsonValue["Name"] = name;
601             asyncResp->res.jsonValue["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                         if (ec.value() ==
784                             boost::system::linux_error::bad_request_descriptor)
785                         {
786                             messages::resourceNotFound(asyncResp->res, name,
787                                                        std::to_string(id));
788                             return;
789                         }
790                         messages::internalError(asyncResp->res);
791                         return;
792                     }
793                     getCertificateProperties(asyncResp, objectPath, service, id,
794                                              certURI, name);
795                     BMCWEB_LOG_DEBUG << "HTTPS certificate install file="
796                                      << certFile->getCertFilePath();
797                 },
798                 service, objectPath, certs::certReplaceIntf, "Replace",
799                 certFile->getCertFilePath());
800         });
801 } // requestRoutesCertificateActionsReplaceCertificate
802 
803 /**
804  * Certificate resource describes a certificate used to prove the identity
805  * of a component, account or service.
806  */
807 
808 inline void requestRoutesHTTPSCertificate(App& app)
809 {
810     BMCWEB_ROUTE(
811         app,
812         "/redfish/v1/Managers/bmc/NetworkProtocol/HTTPS/Certificates/<str>/")
813         .privileges(redfish::privileges::getCertificate)
814         .methods(
815             boost::beast::http::verb::
816                 get)([&app](const crow::Request& req,
817                             const std::shared_ptr<bmcweb::AsyncResp>& asyncResp,
818                             const std::string& param) -> void {
819             if (!redfish::setUpRedfishRoute(app, req, asyncResp->res))
820             {
821                 return;
822             }
823             if (param.empty())
824             {
825                 messages::internalError(asyncResp->res);
826                 return;
827             }
828             long id = getIDFromURL(req.url);
829 
830             BMCWEB_LOG_DEBUG << "HTTPSCertificate::doGet ID="
831                              << std::to_string(id);
832             std::string certURL =
833                 "/redfish/v1/Managers/bmc/NetworkProtocol/HTTPS/Certificates/" +
834                 std::to_string(id);
835             std::string objectPath = certs::httpsObjectPath;
836             objectPath += "/";
837             objectPath += std::to_string(id);
838             getCertificateProperties(asyncResp, objectPath,
839                                      certs::httpsServiceName, id, certURL,
840                                      "HTTPS Certificate");
841         });
842 }
843 
844 /**
845  * Collection of HTTPS certificates
846  */
847 inline void requestRoutesHTTPSCertificateCollection(App& app)
848 {
849     BMCWEB_ROUTE(app,
850                  "/redfish/v1/Managers/bmc/NetworkProtocol/HTTPS/Certificates/")
851         .privileges(redfish::privileges::getCertificateCollection)
852         .methods(boost::beast::http::verb::get)([&app](const crow::Request& req,
853                                                        const std::shared_ptr<
854                                                            bmcweb::AsyncResp>&
855                                                            asyncResp) {
856             if (!redfish::setUpRedfishRoute(app, req, asyncResp->res))
857             {
858                 return;
859             }
860 
861             asyncResp->res.jsonValue["@odata.id"] =
862                 "/redfish/v1/Managers/bmc/NetworkProtocol/HTTPS/Certificates";
863             asyncResp->res.jsonValue["@odata.type"] =
864                 "#CertificateCollection.CertificateCollection";
865             asyncResp->res.jsonValue["Name"] = "HTTPS Certificates Collection";
866             asyncResp->res.jsonValue["Description"] =
867                 "A Collection of HTTPS certificate instances";
868 
869             crow::connections::systemBus->async_method_call(
870                 [asyncResp](const boost::system::error_code ec,
871                             const dbus::utility::ManagedObjectType& certs) {
872                     if (ec)
873                     {
874                         BMCWEB_LOG_ERROR << "DBUS response error: " << ec;
875                         messages::internalError(asyncResp->res);
876                         return;
877                     }
878                     nlohmann::json& members =
879                         asyncResp->res.jsonValue["Members"];
880                     members = nlohmann::json::array();
881                     for (const auto& cert : certs)
882                     {
883                         long id = getIDFromURL(cert.first.str);
884                         if (id >= 0)
885                         {
886                             nlohmann::json::object_t member;
887                             member["@odata.id"] =
888                                 "/redfish/v1/Managers/bmc/NetworkProtocol/HTTPS/Certificates/" +
889                                 std::to_string(id);
890                             members.push_back(std::move(member));
891                         }
892                     }
893                     asyncResp->res.jsonValue["Members@odata.count"] =
894                         members.size();
895                 },
896                 certs::httpsServiceName, certs::httpsObjectPath,
897                 certs::dbusObjManagerIntf, "GetManagedObjects");
898         });
899 
900     BMCWEB_ROUTE(app,
901                  "/redfish/v1/Managers/bmc/NetworkProtocol/HTTPS/Certificates/")
902         .privileges(redfish::privileges::postCertificateCollection)
903         .methods(
904             boost::beast::http::verb::
905                 post)([&app](
906                           const crow::Request& req,
907                           const std::shared_ptr<bmcweb::AsyncResp>& asyncResp) {
908             if (!redfish::setUpRedfishRoute(app, req, asyncResp->res))
909             {
910                 return;
911             }
912             BMCWEB_LOG_DEBUG << "HTTPSCertificateCollection::doPost";
913 
914             asyncResp->res.jsonValue["Name"] = "HTTPS Certificate";
915             asyncResp->res.jsonValue["Description"] = "HTTPS Certificate";
916 
917             std::string certFileBody =
918                 getCertificateFromReqBody(asyncResp, req);
919 
920             if (certFileBody.empty())
921             {
922                 BMCWEB_LOG_ERROR << "Cannot get certificate from request body.";
923                 messages::unrecognizedRequestBody(asyncResp->res);
924                 return;
925             }
926 
927             std::shared_ptr<CertificateFile> certFile =
928                 std::make_shared<CertificateFile>(certFileBody);
929 
930             crow::connections::systemBus->async_method_call(
931                 [asyncResp, certFile](const boost::system::error_code ec,
932                                       const std::string& objectPath) {
933                     if (ec)
934                     {
935                         BMCWEB_LOG_ERROR << "DBUS response error: " << ec;
936                         messages::internalError(asyncResp->res);
937                         return;
938                     }
939                     long certId = getIDFromURL(objectPath);
940                     if (certId < 0)
941                     {
942                         BMCWEB_LOG_ERROR << "Invalid objectPath value"
943                                          << objectPath;
944                         messages::internalError(asyncResp->res);
945                         return;
946                     }
947                     std::string certURL =
948                         "/redfish/v1/Managers/bmc/NetworkProtocol/HTTPS/Certificates/" +
949                         std::to_string(certId);
950                     getCertificateProperties(asyncResp, objectPath,
951                                              certs::httpsServiceName, certId,
952                                              certURL, "HTTPS Certificate");
953                     BMCWEB_LOG_DEBUG << "HTTPS certificate install file="
954                                      << certFile->getCertFilePath();
955                 },
956                 certs::httpsServiceName, certs::httpsObjectPath,
957                 certs::certInstallIntf, "Install", certFile->getCertFilePath());
958         });
959 } // requestRoutesHTTPSCertificateCollection
960 
961 /**
962  * @brief Retrieve the certificates installed list and append to the
963  * response
964  *
965  * @param[in] asyncResp Shared pointer to the response message
966  * @param[in] certURL  Path of the certificate object
967  * @param[in] path  Path of the D-Bus service object
968  * @return None
969  */
970 inline void
971     getCertificateLocations(const std::shared_ptr<bmcweb::AsyncResp>& asyncResp,
972                             const std::string& certURL, const std::string& path,
973                             const std::string& service)
974 {
975     BMCWEB_LOG_DEBUG << "getCertificateLocations URI=" << certURL
976                      << " Path=" << path << " service= " << service;
977     crow::connections::systemBus->async_method_call(
978         [asyncResp, certURL](const boost::system::error_code ec,
979                              const dbus::utility::ManagedObjectType& certs) {
980             if (ec)
981             {
982                 BMCWEB_LOG_WARNING
983                     << "Certificate collection query failed: " << ec
984                     << ", skipping " << certURL;
985                 return;
986             }
987             nlohmann::json& links =
988                 asyncResp->res.jsonValue["Links"]["Certificates"];
989             for (const auto& cert : certs)
990             {
991                 long id = getIDFromURL(cert.first.str);
992                 if (id >= 0)
993                 {
994                     nlohmann::json::object_t link;
995                     link["@odata.id"] = certURL + std::to_string(id);
996                     links.push_back(std::move(link));
997                 }
998             }
999             asyncResp->res.jsonValue["Links"]["Certificates@odata.count"] =
1000                 links.size();
1001         },
1002         service, path, certs::dbusObjManagerIntf, "GetManagedObjects");
1003 }
1004 
1005 /**
1006  * The certificate location schema defines a resource that an administrator
1007  * can use in order to locate all certificates installed on a given service.
1008  */
1009 inline void requestRoutesCertificateLocations(App& app)
1010 {
1011     BMCWEB_ROUTE(app, "/redfish/v1/CertificateService/CertificateLocations/")
1012         .privileges(redfish::privileges::getCertificateLocations)
1013         .methods(boost::beast::http::verb::get)([&app](const crow::Request& req,
1014                                                        const std::shared_ptr<
1015                                                            bmcweb::AsyncResp>&
1016                                                            asyncResp) {
1017             if (!redfish::setUpRedfishRoute(app, req, asyncResp->res))
1018             {
1019                 return;
1020             }
1021             asyncResp->res.jsonValue["@odata.id"] =
1022                 "/redfish/v1/CertificateService/CertificateLocations";
1023             asyncResp->res.jsonValue["@odata.type"] =
1024                 "#CertificateLocations.v1_0_0.CertificateLocations";
1025             asyncResp->res.jsonValue["Name"] = "Certificate Locations";
1026             asyncResp->res.jsonValue["Id"] = "CertificateLocations";
1027             asyncResp->res.jsonValue["Description"] =
1028                 "Defines a resource that an administrator can use in order to "
1029                 "locate all certificates installed on a given service";
1030 
1031             nlohmann::json& links =
1032                 asyncResp->res.jsonValue["Links"]["Certificates"];
1033             links = nlohmann::json::array();
1034             getCertificateLocations(
1035                 asyncResp,
1036                 "/redfish/v1/Managers/bmc/NetworkProtocol/HTTPS/Certificates/",
1037                 certs::httpsObjectPath, certs::httpsServiceName);
1038             getCertificateLocations(
1039                 asyncResp, "/redfish/v1/AccountService/LDAP/Certificates/",
1040                 certs::ldapObjectPath, certs::ldapServiceName);
1041             getCertificateLocations(
1042                 asyncResp, "/redfish/v1/Managers/bmc/Truststore/Certificates/",
1043                 certs::authorityObjectPath, certs::authorityServiceName);
1044         });
1045 }
1046 // requestRoutesCertificateLocations
1047 
1048 /**
1049  * Collection of LDAP certificates
1050  */
1051 inline void requestRoutesLDAPCertificateCollection(App& app)
1052 {
1053     BMCWEB_ROUTE(app, "/redfish/v1/AccountService/LDAP/Certificates/")
1054         .privileges(redfish::privileges::getCertificateCollection)
1055         .methods(boost::beast::http::verb::get)([&app](const crow::Request& req,
1056                                                        const std::shared_ptr<
1057                                                            bmcweb::AsyncResp>&
1058                                                            asyncResp) {
1059             if (!redfish::setUpRedfishRoute(app, req, asyncResp->res))
1060             {
1061                 return;
1062             }
1063 
1064             asyncResp->res.jsonValue["@odata.id"] =
1065                 "/redfish/v1/AccountService/LDAP/Certificates";
1066             asyncResp->res.jsonValue["@odata.type"] =
1067                 "#CertificateCollection.CertificateCollection";
1068             asyncResp->res.jsonValue["Name"] = "LDAP Certificates Collection";
1069             asyncResp->res.jsonValue["Description"] =
1070                 "A Collection of LDAP certificate instances";
1071 
1072             crow::connections::systemBus->async_method_call(
1073                 [asyncResp](const boost::system::error_code ec,
1074                             const dbus::utility::ManagedObjectType& certs) {
1075                     nlohmann::json& members =
1076                         asyncResp->res.jsonValue["Members"];
1077                     nlohmann::json& count =
1078                         asyncResp->res.jsonValue["Members@odata.count"];
1079                     members = nlohmann::json::array();
1080                     count = 0;
1081                     if (ec)
1082                     {
1083                         BMCWEB_LOG_WARNING << "LDAP certificate query failed: "
1084                                            << ec;
1085                         return;
1086                     }
1087                     for (const auto& cert : certs)
1088                     {
1089                         long id = getIDFromURL(cert.first.str);
1090                         if (id >= 0)
1091                         {
1092                             nlohmann::json::object_t member;
1093                             member["@odata.id"] =
1094                                 "/redfish/v1/AccountService/LDAP/Certificates/" +
1095                                 std::to_string(id);
1096                             members.push_back(std::move(member));
1097                         }
1098                     }
1099                     count = members.size();
1100                 },
1101                 certs::ldapServiceName, certs::ldapObjectPath,
1102                 certs::dbusObjManagerIntf, "GetManagedObjects");
1103         });
1104 
1105     BMCWEB_ROUTE(app, "/redfish/v1/AccountService/LDAP/Certificates/")
1106         .privileges(redfish::privileges::postCertificateCollection)
1107         .methods(boost::beast::http::verb::post)(
1108             [&app](const crow::Request& req,
1109                    const std::shared_ptr<bmcweb::AsyncResp>& asyncResp) {
1110                 if (!redfish::setUpRedfishRoute(app, req, asyncResp->res))
1111                 {
1112                     return;
1113                 }
1114                 std::string certFileBody =
1115                     getCertificateFromReqBody(asyncResp, req);
1116 
1117                 if (certFileBody.empty())
1118                 {
1119                     BMCWEB_LOG_ERROR
1120                         << "Cannot get certificate from request body.";
1121                     messages::unrecognizedRequestBody(asyncResp->res);
1122                     return;
1123                 }
1124 
1125                 std::shared_ptr<CertificateFile> certFile =
1126                     std::make_shared<CertificateFile>(certFileBody);
1127 
1128                 crow::connections::systemBus->async_method_call(
1129                     [asyncResp, certFile](const boost::system::error_code ec,
1130                                           const std::string& objectPath) {
1131                         if (ec)
1132                         {
1133                             BMCWEB_LOG_ERROR << "DBUS response error: " << ec;
1134                             messages::internalError(asyncResp->res);
1135                             return;
1136                         }
1137                         long certId = getIDFromURL(objectPath);
1138                         if (certId < 0)
1139                         {
1140                             BMCWEB_LOG_ERROR << "Invalid objectPath value"
1141                                              << objectPath;
1142                             messages::internalError(asyncResp->res);
1143                             return;
1144                         }
1145                         std::string certURL =
1146                             "/redfish/v1/AccountService/LDAP/Certificates/" +
1147                             std::to_string(certId);
1148                         getCertificateProperties(asyncResp, objectPath,
1149                                                  certs::ldapServiceName, certId,
1150                                                  certURL, "LDAP Certificate");
1151                         BMCWEB_LOG_DEBUG << "LDAP certificate install file="
1152                                          << certFile->getCertFilePath();
1153                     },
1154                     certs::ldapServiceName, certs::ldapObjectPath,
1155                     certs::certInstallIntf, "Install",
1156                     certFile->getCertFilePath());
1157             });
1158 } // requestRoutesLDAPCertificateCollection
1159 
1160 /**
1161  * Certificate resource describes a certificate used to prove the identity
1162  * of a component, account or service.
1163  */
1164 inline void requestRoutesLDAPCertificate(App& app)
1165 {
1166     BMCWEB_ROUTE(app, "/redfish/v1/AccountService/LDAP/Certificates/<str>/")
1167         .privileges(redfish::privileges::getCertificate)
1168         .methods(boost::beast::http::verb::get)(
1169             [&app](const crow::Request& req,
1170                    const std::shared_ptr<bmcweb::AsyncResp>& asyncResp,
1171                    const std::string&) {
1172                 if (!redfish::setUpRedfishRoute(app, req, asyncResp->res))
1173                 {
1174                     return;
1175                 }
1176                 long id = getIDFromURL(req.url);
1177                 if (id < 0)
1178                 {
1179                     BMCWEB_LOG_ERROR << "Invalid url value" << req.url;
1180                     messages::internalError(asyncResp->res);
1181                     return;
1182                 }
1183                 BMCWEB_LOG_DEBUG << "LDAP Certificate ID="
1184                                  << std::to_string(id);
1185                 std::string certURL =
1186                     "/redfish/v1/AccountService/LDAP/Certificates/" +
1187                     std::to_string(id);
1188                 std::string objectPath = certs::ldapObjectPath;
1189                 objectPath += "/";
1190                 objectPath += std::to_string(id);
1191                 getCertificateProperties(asyncResp, objectPath,
1192                                          certs::ldapServiceName, id, certURL,
1193                                          "LDAP Certificate");
1194             });
1195 } // requestRoutesLDAPCertificate
1196 /**
1197  * Collection of TrustStoreCertificate certificates
1198  */
1199 inline void requestRoutesTrustStoreCertificateCollection(App& app)
1200 {
1201     BMCWEB_ROUTE(app, "/redfish/v1/Managers/bmc/Truststore/Certificates/")
1202         .privileges(redfish::privileges::getCertificate)
1203         .methods(boost::beast::http::verb::get)([&app](const crow::Request& req,
1204                                                        const std::shared_ptr<
1205                                                            bmcweb::AsyncResp>&
1206                                                            asyncResp) {
1207             if (!redfish::setUpRedfishRoute(app, req, asyncResp->res))
1208             {
1209                 return;
1210             }
1211 
1212             asyncResp->res.jsonValue["@odata.id"] =
1213                 "/redfish/v1/Managers/bmc/Truststore/Certificates/";
1214             asyncResp->res.jsonValue["@odata.type"] =
1215                 "#CertificateCollection.CertificateCollection";
1216             asyncResp->res.jsonValue["Name"] =
1217                 "TrustStore Certificates Collection";
1218             asyncResp->res.jsonValue["Description"] =
1219                 "A Collection of TrustStore certificate instances";
1220 
1221             crow::connections::systemBus->async_method_call(
1222                 [asyncResp](const boost::system::error_code ec,
1223                             const dbus::utility::ManagedObjectType& certs) {
1224                     if (ec)
1225                     {
1226                         BMCWEB_LOG_ERROR << "DBUS response error: " << ec;
1227                         messages::internalError(asyncResp->res);
1228                         return;
1229                     }
1230                     nlohmann::json& members =
1231                         asyncResp->res.jsonValue["Members"];
1232                     members = nlohmann::json::array();
1233                     for (const auto& cert : certs)
1234                     {
1235                         long id = getIDFromURL(cert.first.str);
1236                         if (id >= 0)
1237                         {
1238                             nlohmann::json::object_t member;
1239                             member["@odata.id"] =
1240                                 "/redfish/v1/Managers/bmc/Truststore/Certificates/" +
1241                                 std::to_string(id);
1242                             members.push_back(std::move(member));
1243                         }
1244                     }
1245                     asyncResp->res.jsonValue["Members@odata.count"] =
1246                         members.size();
1247                 },
1248                 certs::authorityServiceName, certs::authorityObjectPath,
1249                 certs::dbusObjManagerIntf, "GetManagedObjects");
1250         });
1251 
1252     BMCWEB_ROUTE(app, "/redfish/v1/Managers/bmc/Truststore/Certificates/")
1253         .privileges(redfish::privileges::postCertificateCollection)
1254         .methods(
1255             boost::beast::http::verb::
1256                 post)([&app](
1257                           const crow::Request& req,
1258                           const std::shared_ptr<bmcweb::AsyncResp>& asyncResp) {
1259             if (!redfish::setUpRedfishRoute(app, req, asyncResp->res))
1260             {
1261                 return;
1262             }
1263             std::string certFileBody =
1264                 getCertificateFromReqBody(asyncResp, req);
1265 
1266             if (certFileBody.empty())
1267             {
1268                 BMCWEB_LOG_ERROR << "Cannot get certificate from request body.";
1269                 messages::unrecognizedRequestBody(asyncResp->res);
1270                 return;
1271             }
1272 
1273             std::shared_ptr<CertificateFile> certFile =
1274                 std::make_shared<CertificateFile>(certFileBody);
1275             crow::connections::systemBus->async_method_call(
1276                 [asyncResp, certFile](const boost::system::error_code ec,
1277                                       const std::string& objectPath) {
1278                     if (ec)
1279                     {
1280                         BMCWEB_LOG_ERROR << "DBUS response error: " << ec;
1281                         messages::internalError(asyncResp->res);
1282                         return;
1283                     }
1284                     long certId = getIDFromURL(objectPath);
1285                     if (certId < 0)
1286                     {
1287                         BMCWEB_LOG_ERROR << "Invalid objectPath value"
1288                                          << objectPath;
1289                         messages::internalError(asyncResp->res);
1290                         return;
1291                     }
1292                     std::string certURL =
1293                         "/redfish/v1/Managers/bmc/Truststore/Certificates/" +
1294                         std::to_string(certId);
1295 
1296                     getCertificateProperties(
1297                         asyncResp, objectPath, certs::authorityServiceName,
1298                         certId, certURL, "TrustStore Certificate");
1299                     BMCWEB_LOG_DEBUG << "TrustStore certificate install file="
1300                                      << certFile->getCertFilePath();
1301                 },
1302                 certs::authorityServiceName, certs::authorityObjectPath,
1303                 certs::certInstallIntf, "Install", certFile->getCertFilePath());
1304         });
1305 } // requestRoutesTrustStoreCertificateCollection
1306 
1307 /**
1308  * Certificate resource describes a certificate used to prove the identity
1309  * of a component, account or service.
1310  */
1311 inline void requestRoutesTrustStoreCertificate(App& app)
1312 {
1313     BMCWEB_ROUTE(app, "/redfish/v1/Managers/bmc/Truststore/Certificates/<str>/")
1314         .privileges(redfish::privileges::getCertificate)
1315         .methods(boost::beast::http::verb::get)(
1316             [&app](const crow::Request& req,
1317                    const std::shared_ptr<bmcweb::AsyncResp>& asyncResp,
1318                    const std::string&) {
1319                 if (!redfish::setUpRedfishRoute(app, req, asyncResp->res))
1320                 {
1321                     return;
1322                 }
1323                 long id = getIDFromURL(req.url);
1324                 if (id < 0)
1325                 {
1326                     BMCWEB_LOG_ERROR << "Invalid url value" << req.url;
1327                     messages::internalError(asyncResp->res);
1328                     return;
1329                 }
1330                 BMCWEB_LOG_DEBUG << "TrustStoreCertificate::doGet ID="
1331                                  << std::to_string(id);
1332                 std::string certURL =
1333                     "/redfish/v1/Managers/bmc/Truststore/Certificates/" +
1334                     std::to_string(id);
1335                 std::string objectPath = certs::authorityObjectPath;
1336                 objectPath += "/";
1337                 objectPath += std::to_string(id);
1338                 getCertificateProperties(asyncResp, objectPath,
1339                                          certs::authorityServiceName, id,
1340                                          certURL, "TrustStore Certificate");
1341             });
1342 
1343     BMCWEB_ROUTE(app, "/redfish/v1/Managers/bmc/Truststore/Certificates/<str>/")
1344         .privileges(redfish::privileges::deleteCertificate)
1345         .methods(boost::beast::http::verb::delete_)(
1346             [&app](const crow::Request& req,
1347                    const std::shared_ptr<bmcweb::AsyncResp>& asyncResp,
1348                    const std::string& param) {
1349                 if (!redfish::setUpRedfishRoute(app, req, asyncResp->res))
1350                 {
1351                     return;
1352                 }
1353                 if (param.empty())
1354                 {
1355                     messages::internalError(asyncResp->res);
1356                     return;
1357                 }
1358 
1359                 long id = getIDFromURL(req.url);
1360                 if (id < 0)
1361                 {
1362                     BMCWEB_LOG_ERROR << "Invalid url value: " << req.url;
1363                     messages::resourceNotFound(asyncResp->res,
1364                                                "TrustStore Certificate",
1365                                                std::string(req.url));
1366                     return;
1367                 }
1368                 BMCWEB_LOG_DEBUG << "TrustStoreCertificate::doDelete ID="
1369                                  << std::to_string(id);
1370                 std::string certPath = certs::authorityObjectPath;
1371                 certPath += "/";
1372                 certPath += std::to_string(id);
1373 
1374                 crow::connections::systemBus->async_method_call(
1375                     [asyncResp, id](const boost::system::error_code ec) {
1376                         if (ec)
1377                         {
1378                             messages::resourceNotFound(asyncResp->res,
1379                                                        "TrustStore Certificate",
1380                                                        std::to_string(id));
1381                             return;
1382                         }
1383                         BMCWEB_LOG_INFO << "Certificate deleted";
1384                         asyncResp->res.result(
1385                             boost::beast::http::status::no_content);
1386                     },
1387                     certs::authorityServiceName, certPath, certs::objDeleteIntf,
1388                     "Delete");
1389             });
1390 } // requestRoutesTrustStoreCertificate
1391 } // namespace redfish
1392