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