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