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