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