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