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