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