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