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