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