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