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