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