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