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