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 *certPropIntf = "xyz.openbmc_project.Certs.Certificate"; 30 constexpr char const *dbusPropIntf = "org.freedesktop.DBus.Properties"; 31 constexpr char const *dbusObjManagerIntf = "org.freedesktop.DBus.ObjectManager"; 32 constexpr char const *ldapObjectPath = "/xyz/openbmc_project/certs/client/ldap"; 33 constexpr char const *httpsServiceName = 34 "xyz.openbmc_project.Certs.Manager.Server.Https"; 35 constexpr char const *ldapServiceName = 36 "xyz.openbmc_project.Certs.Manager.Client.Ldap"; 37 constexpr char const *authorityServiceName = 38 "xyz.openbmc_project.Certs.Manager.Authority.Ldap"; 39 constexpr char const *authorityObjectPath = 40 "/xyz/openbmc_project/certs/authority/ldap"; 41 } // namespace certs 42 43 /** 44 * The Certificate schema defines a Certificate Service which represents the 45 * actions available to manage certificates and links to where certificates 46 * are installed. 47 */ 48 class CertificateService : public Node 49 { 50 public: 51 CertificateService(CrowApp &app) : 52 Node(app, "/redfish/v1/CertificateService/") 53 { 54 // TODO: Issue#61 No entries are available for Certificate 55 // sevice at https://www.dmtf.org/standards/redfish 56 // "redfish standard registries". Need to modify after DMTF 57 // publish Privilege details for certificate service 58 entityPrivileges = { 59 {boost::beast::http::verb::get, {{"Login"}}}, 60 {boost::beast::http::verb::head, {{"Login"}}}, 61 {boost::beast::http::verb::patch, {{"ConfigureComponents"}}}, 62 {boost::beast::http::verb::put, {{"ConfigureComponents"}}}, 63 {boost::beast::http::verb::delete_, {{"ConfigureComponents"}}}, 64 {boost::beast::http::verb::post, {{"ConfigureComponents"}}}}; 65 } 66 67 private: 68 void doGet(crow::Response &res, const crow::Request &req, 69 const std::vector<std::string> ¶ms) override 70 { 71 res.jsonValue = { 72 {"@odata.type", "#CertificateService.v1_0_0.CertificateService"}, 73 {"@odata.id", "/redfish/v1/CertificateService"}, 74 {"@odata.context", 75 "/redfish/v1/$metadata#CertificateService.CertificateService"}, 76 {"Id", "CertificateService"}, 77 {"Name", "Certificate Service"}, 78 {"Description", "Actions available to manage certificates"}}; 79 res.jsonValue["CertificateLocations"] = { 80 {"@odata.id", 81 "/redfish/v1/CertificateService/CertificateLocations"}}; 82 res.jsonValue["Actions"]["#CertificateService.ReplaceCertificate"] = { 83 {"target", "/redfish/v1/CertificateService/Actions/" 84 "CertificateService.ReplaceCertificate"}, 85 {"CertificateType@Redfish.AllowableValues", {"PEM"}}}; 86 res.jsonValue["Actions"]["#CertificateService.GenerateCSR"] = { 87 {"target", "/redfish/v1/CertificateService/Actions/" 88 "CertificateService.GenerateCSR"}}; 89 res.end(); 90 } 91 }; // CertificateService 92 93 /** 94 * @brief Find the ID specified in the URL 95 * Finds the numbers specified after the last "/" in the URL and returns. 96 * @param[in] path URL 97 * @return -1 on failure and number on success 98 */ 99 long getIDFromURL(const std::string_view url) 100 { 101 std::size_t found = url.rfind("/"); 102 if (found == std::string::npos) 103 { 104 return -1; 105 } 106 if ((found + 1) < url.length()) 107 { 108 char *endPtr; 109 std::string_view str = url.substr(found + 1); 110 long value = std::strtol(str.data(), &endPtr, 10); 111 if (endPtr != str.end()) 112 { 113 return -1; 114 } 115 return value; 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 res.addHeader("Retry-After", std::to_string(TIME_OUT)); 425 messages::serviceTemporarilyUnavailable(asyncResp->res, 426 std::to_string(TIME_OUT)); 427 return; 428 } 429 430 // Make this static so it survives outside this method 431 static boost::asio::steady_timer timeout(*req.ioService); 432 timeout.expires_after(std::chrono::seconds(TIME_OUT)); 433 timeout.async_wait([asyncResp](const boost::system::error_code &ec) { 434 csrMatcher = nullptr; 435 if (ec) 436 { 437 // operation_aborted is expected if timer is canceled before 438 // completion. 439 if (ec != boost::asio::error::operation_aborted) 440 { 441 BMCWEB_LOG_ERROR << "Async_wait failed " << ec; 442 } 443 return; 444 } 445 BMCWEB_LOG_ERROR << "Timed out waiting for Generating CSR"; 446 messages::internalError(asyncResp->res); 447 }); 448 449 // create a matcher to wait on CSR object 450 BMCWEB_LOG_DEBUG << "create matcher with path " << objectPath; 451 std::string match("type='signal'," 452 "interface='org.freedesktop.DBus.ObjectManager'," 453 "path='" + 454 objectPath + 455 "'," 456 "member='InterfacesAdded'"); 457 csrMatcher = std::make_unique<sdbusplus::bus::match::match>( 458 *crow::connections::systemBus, match, 459 [asyncResp, service, objectPath, 460 certURI](sdbusplus::message::message &m) { 461 timeout.cancel(); 462 if (m.is_method_error()) 463 { 464 BMCWEB_LOG_ERROR << "Dbus method error!!!"; 465 messages::internalError(asyncResp->res); 466 return; 467 } 468 std::vector<std::pair< 469 std::string, std::vector<std::pair< 470 std::string, std::variant<std::string>>>>> 471 interfacesProperties; 472 sdbusplus::message::object_path csrObjectPath; 473 m.read(csrObjectPath, interfacesProperties); 474 BMCWEB_LOG_DEBUG << "CSR object added" << csrObjectPath.str; 475 for (auto &interface : interfacesProperties) 476 { 477 if (interface.first == "xyz.openbmc_project.Certs.CSR") 478 { 479 getCSR(asyncResp, certURI, service, objectPath, 480 csrObjectPath.str); 481 break; 482 } 483 } 484 }); 485 crow::connections::systemBus->async_method_call( 486 [asyncResp](const boost::system::error_code &ec, 487 const std::string &path) { 488 if (ec) 489 { 490 BMCWEB_LOG_ERROR << "DBUS response error: " << ec.message(); 491 messages::internalError(asyncResp->res); 492 return; 493 } 494 }, 495 service, objectPath, "xyz.openbmc_project.Certs.CSR.Create", 496 "GenerateCSR", *optAlternativeNames, *optChallengePassword, city, 497 commonName, *optContactPerson, country, *optEmail, *optGivenName, 498 *optInitials, *optKeyBitLength, *optKeyCurveId, 499 *optKeyPairAlgorithm, *optKeyUsage, organization, 500 organizationalUnit, state, *optSurname, *optUnstructuredName); 501 } 502 }; // CertificateActionGenerateCSR 503 504 /** 505 * @brief Parse and update Certficate Issue/Subject property 506 * 507 * @param[in] asyncResp Shared pointer to the response message 508 * @param[in] str Issuer/Subject value in key=value pairs 509 * @param[in] type Issuer/Subject 510 * @return None 511 */ 512 static void updateCertIssuerOrSubject(nlohmann::json &out, 513 const std::string_view value) 514 { 515 // example: O=openbmc-project.xyz,CN=localhost 516 std::string_view::iterator i = value.begin(); 517 while (i != value.end()) 518 { 519 std::string_view::iterator tokenBegin = i; 520 while (i != value.end() && *i != '=') 521 { 522 i++; 523 } 524 if (i == value.end()) 525 { 526 break; 527 } 528 const std::string_view key(tokenBegin, 529 static_cast<size_t>(i - tokenBegin)); 530 i++; 531 tokenBegin = i; 532 while (i != value.end() && *i != ',') 533 { 534 i++; 535 } 536 const std::string_view val(tokenBegin, 537 static_cast<size_t>(i - tokenBegin)); 538 if (key == "L") 539 { 540 out["City"] = val; 541 } 542 else if (key == "CN") 543 { 544 out["CommonName"] = val; 545 } 546 else if (key == "C") 547 { 548 out["Country"] = val; 549 } 550 else if (key == "O") 551 { 552 out["Organization"] = val; 553 } 554 else if (key == "OU") 555 { 556 out["OrganizationalUnit"] = val; 557 } 558 else if (key == "ST") 559 { 560 out["State"] = val; 561 } 562 // skip comma character 563 if (i != value.end()) 564 { 565 i++; 566 } 567 } 568 } 569 570 /** 571 * @brief Retrieve the certificates properties and append to the response 572 * message 573 * 574 * @param[in] asyncResp Shared pointer to the response message 575 * @param[in] objectPath Path of the D-Bus service object 576 * @param[in] certId Id of the certificate 577 * @param[in] certURL URL of the certificate object 578 * @param[in] name name of the certificate 579 * @return None 580 */ 581 static void getCertificateProperties( 582 const std::shared_ptr<AsyncResp> &asyncResp, const std::string &objectPath, 583 const std::string &service, long certId, const std::string &certURL, 584 const std::string &name) 585 { 586 using PropertyType = 587 std::variant<std::string, uint64_t, std::vector<std::string>>; 588 using PropertiesMap = boost::container::flat_map<std::string, PropertyType>; 589 BMCWEB_LOG_DEBUG << "getCertificateProperties Path=" << objectPath 590 << " certId=" << certId << " certURl=" << certURL; 591 crow::connections::systemBus->async_method_call( 592 [asyncResp, certURL, certId, name](const boost::system::error_code ec, 593 const PropertiesMap &properties) { 594 if (ec) 595 { 596 BMCWEB_LOG_ERROR << "DBUS response error: " << ec; 597 messages::resourceNotFound(asyncResp->res, name, 598 std::to_string(certId)); 599 return; 600 } 601 asyncResp->res.jsonValue = { 602 {"@odata.id", certURL}, 603 {"@odata.type", "#Certificate.v1_0_0.Certificate"}, 604 {"@odata.context", 605 "/redfish/v1/$metadata#Certificate.Certificate"}, 606 {"Id", std::to_string(certId)}, 607 {"Name", name}, 608 {"Description", name}}; 609 for (const auto &property : properties) 610 { 611 if (property.first == "CertificateString") 612 { 613 asyncResp->res.jsonValue["CertificateString"] = ""; 614 const std::string *value = 615 std::get_if<std::string>(&property.second); 616 if (value) 617 { 618 asyncResp->res.jsonValue["CertificateString"] = *value; 619 } 620 } 621 else if (property.first == "KeyUsage") 622 { 623 nlohmann::json &keyUsage = 624 asyncResp->res.jsonValue["KeyUsage"]; 625 keyUsage = nlohmann::json::array(); 626 const std::vector<std::string> *value = 627 std::get_if<std::vector<std::string>>(&property.second); 628 if (value) 629 { 630 for (const std::string &usage : *value) 631 { 632 keyUsage.push_back(usage); 633 } 634 } 635 } 636 else if (property.first == "Issuer") 637 { 638 const std::string *value = 639 std::get_if<std::string>(&property.second); 640 if (value) 641 { 642 updateCertIssuerOrSubject( 643 asyncResp->res.jsonValue["Issuer"], *value); 644 } 645 } 646 else if (property.first == "Subject") 647 { 648 const std::string *value = 649 std::get_if<std::string>(&property.second); 650 if (value) 651 { 652 updateCertIssuerOrSubject( 653 asyncResp->res.jsonValue["Subject"], *value); 654 } 655 } 656 else if (property.first == "ValidNotAfter") 657 { 658 const uint64_t *value = 659 std::get_if<uint64_t>(&property.second); 660 if (value) 661 { 662 std::time_t time = static_cast<std::time_t>(*value); 663 asyncResp->res.jsonValue["ValidNotAfter"] = 664 crow::utility::getDateTime(time); 665 } 666 } 667 else if (property.first == "ValidNotBefore") 668 { 669 const uint64_t *value = 670 std::get_if<uint64_t>(&property.second); 671 if (value) 672 { 673 std::time_t time = static_cast<std::time_t>(*value); 674 asyncResp->res.jsonValue["ValidNotBefore"] = 675 crow::utility::getDateTime(time); 676 } 677 } 678 } 679 asyncResp->res.addHeader("Location", certURL); 680 }, 681 service, objectPath, certs::dbusPropIntf, "GetAll", 682 certs::certPropIntf); 683 } 684 685 using GetObjectType = 686 std::vector<std::pair<std::string, std::vector<std::string>>>; 687 688 /** 689 * Action to replace an existing certificate 690 */ 691 class CertificateActionsReplaceCertificate : public Node 692 { 693 public: 694 CertificateActionsReplaceCertificate(CrowApp &app) : 695 Node(app, "/redfish/v1/CertificateService/Actions/" 696 "CertificateService.ReplaceCertificate/") 697 { 698 entityPrivileges = { 699 {boost::beast::http::verb::get, {{"Login"}}}, 700 {boost::beast::http::verb::head, {{"Login"}}}, 701 {boost::beast::http::verb::patch, {{"ConfigureComponents"}}}, 702 {boost::beast::http::verb::put, {{"ConfigureComponents"}}}, 703 {boost::beast::http::verb::delete_, {{"ConfigureComponents"}}}, 704 {boost::beast::http::verb::post, {{"ConfigureComponents"}}}}; 705 } 706 707 private: 708 void doPost(crow::Response &res, const crow::Request &req, 709 const std::vector<std::string> ¶ms) override 710 { 711 std::string certificate; 712 nlohmann::json certificateUri; 713 std::optional<std::string> certificateType = "PEM"; 714 auto asyncResp = std::make_shared<AsyncResp>(res); 715 if (!json_util::readJson(req, asyncResp->res, "CertificateString", 716 certificate, "CertificateUri", certificateUri, 717 "CertificateType", certificateType)) 718 { 719 BMCWEB_LOG_ERROR << "Required parameters are missing"; 720 messages::internalError(asyncResp->res); 721 return; 722 } 723 724 if (!certificateType) 725 { 726 // should never happen, but it never hurts to be paranoid. 727 return; 728 } 729 if (certificateType != "PEM") 730 { 731 messages::actionParameterNotSupported( 732 asyncResp->res, "CertificateType", "ReplaceCertificate"); 733 return; 734 } 735 736 std::string certURI; 737 if (!redfish::json_util::readJson(certificateUri, asyncResp->res, 738 "@odata.id", certURI)) 739 { 740 messages::actionParameterMissing( 741 asyncResp->res, "ReplaceCertificate", "CertificateUri"); 742 return; 743 } 744 745 BMCWEB_LOG_INFO << "Certificate URI to replace" << certURI; 746 long id = getIDFromURL(certURI); 747 if (id < 0) 748 { 749 messages::actionParameterValueFormatError(asyncResp->res, certURI, 750 "CertificateUri", 751 "ReplaceCertificate"); 752 return; 753 } 754 std::string objectPath; 755 std::string name; 756 std::string service; 757 if (boost::starts_with( 758 certURI, 759 "/redfish/v1/Managers/bmc/NetworkProtocol/HTTPS/Certificates/")) 760 { 761 objectPath = 762 std::string(certs::httpsObjectPath) + "/" + std::to_string(id); 763 name = "HTTPS certificate"; 764 service = certs::httpsServiceName; 765 } 766 else if (boost::starts_with( 767 certURI, "/redfish/v1/AccountService/LDAP/Certificates/")) 768 { 769 objectPath = 770 std::string(certs::ldapObjectPath) + "/" + std::to_string(id); 771 name = "LDAP certificate"; 772 service = certs::ldapServiceName; 773 } 774 else if (boost::starts_with( 775 certURI, 776 "/redfish/v1/Managers/bmc/Truststore/Certificates/")) 777 { 778 objectPath = std::string(certs::authorityObjectPath) + "/" + 779 std::to_string(id); 780 name = "TrustStore certificate"; 781 service = certs::authorityServiceName; 782 } 783 else 784 { 785 messages::actionParameterNotSupported( 786 asyncResp->res, "CertificateUri", "ReplaceCertificate"); 787 return; 788 } 789 790 std::shared_ptr<CertificateFile> certFile = 791 std::make_shared<CertificateFile>(certificate); 792 crow::connections::systemBus->async_method_call( 793 [asyncResp, certFile, objectPath, service, certURI, id, 794 name](const boost::system::error_code ec) { 795 if (ec) 796 { 797 BMCWEB_LOG_ERROR << "DBUS response error: " << ec; 798 messages::resourceNotFound(asyncResp->res, name, 799 std::to_string(id)); 800 return; 801 } 802 getCertificateProperties(asyncResp, objectPath, service, id, 803 certURI, name); 804 BMCWEB_LOG_DEBUG << "HTTPS certificate install file=" 805 << certFile->getCertFilePath(); 806 }, 807 service, objectPath, certs::certReplaceIntf, "Replace", 808 certFile->getCertFilePath()); 809 } 810 }; // CertificateActionsReplaceCertificate 811 812 /** 813 * Certificate resource describes a certificate used to prove the identity 814 * of a component, account or service. 815 */ 816 class HTTPSCertificate : public Node 817 { 818 public: 819 template <typename CrowApp> 820 HTTPSCertificate(CrowApp &app) : 821 Node(app, 822 "/redfish/v1/Managers/bmc/NetworkProtocol/HTTPS/Certificates/" 823 "<str>/", 824 std::string()) 825 { 826 entityPrivileges = { 827 {boost::beast::http::verb::get, {{"Login"}}}, 828 {boost::beast::http::verb::head, {{"Login"}}}, 829 {boost::beast::http::verb::patch, {{"ConfigureComponents"}}}, 830 {boost::beast::http::verb::put, {{"ConfigureComponents"}}}, 831 {boost::beast::http::verb::delete_, {{"ConfigureComponents"}}}, 832 {boost::beast::http::verb::post, {{"ConfigureComponents"}}}}; 833 } 834 835 void doGet(crow::Response &res, const crow::Request &req, 836 const std::vector<std::string> ¶ms) override 837 { 838 auto asyncResp = std::make_shared<AsyncResp>(res); 839 if (params.size() != 1) 840 { 841 messages::internalError(asyncResp->res); 842 return; 843 } 844 long id = getIDFromURL(req.url); 845 846 BMCWEB_LOG_DEBUG << "HTTPSCertificate::doGet ID=" << std::to_string(id); 847 std::string certURL = 848 "/redfish/v1/Managers/bmc/NetworkProtocol/HTTPS/Certificates/" + 849 std::to_string(id); 850 std::string objectPath = certs::httpsObjectPath; 851 objectPath += "/"; 852 objectPath += std::to_string(id); 853 getCertificateProperties(asyncResp, objectPath, certs::httpsServiceName, 854 id, certURL, "HTTPS Certificate"); 855 } 856 857 }; // namespace redfish 858 859 /** 860 * Collection of HTTPS certificates 861 */ 862 class HTTPSCertificateCollection : public Node 863 { 864 public: 865 template <typename CrowApp> 866 HTTPSCertificateCollection(CrowApp &app) : 867 Node(app, 868 "/redfish/v1/Managers/bmc/NetworkProtocol/HTTPS/Certificates/") 869 { 870 entityPrivileges = { 871 {boost::beast::http::verb::get, {{"Login"}}}, 872 {boost::beast::http::verb::head, {{"Login"}}}, 873 {boost::beast::http::verb::patch, {{"ConfigureComponents"}}}, 874 {boost::beast::http::verb::put, {{"ConfigureComponents"}}}, 875 {boost::beast::http::verb::delete_, {{"ConfigureComponents"}}}, 876 {boost::beast::http::verb::post, {{"ConfigureComponents"}}}}; 877 } 878 void doGet(crow::Response &res, const crow::Request &req, 879 const std::vector<std::string> ¶ms) override 880 { 881 res.jsonValue = { 882 {"@odata.id", 883 "/redfish/v1/Managers/bmc/NetworkProtocol/HTTPS/Certificates"}, 884 {"@odata.type", "#CertificateCollection.CertificateCollection"}, 885 {"@odata.context", 886 "/redfish/v1/" 887 "$metadata#CertificateCollection.CertificateCollection"}, 888 {"Name", "HTTPS Certificates Collection"}, 889 {"Description", "A Collection of HTTPS certificate instances"}}; 890 auto asyncResp = std::make_shared<AsyncResp>(res); 891 crow::connections::systemBus->async_method_call( 892 [asyncResp](const boost::system::error_code ec, 893 const ManagedObjectType &certs) { 894 if (ec) 895 { 896 BMCWEB_LOG_ERROR << "DBUS response error: " << ec; 897 messages::internalError(asyncResp->res); 898 return; 899 } 900 nlohmann::json &members = asyncResp->res.jsonValue["Members"]; 901 members = nlohmann::json::array(); 902 for (const auto &cert : certs) 903 { 904 long id = getIDFromURL(cert.first.str); 905 if (id >= 0) 906 { 907 members.push_back( 908 {{"@odata.id", 909 "/redfish/v1/Managers/bmc/" 910 "NetworkProtocol/HTTPS/Certificates/" + 911 std::to_string(id)}}); 912 } 913 } 914 asyncResp->res.jsonValue["Members@odata.count"] = 915 members.size(); 916 }, 917 certs::httpsServiceName, certs::httpsObjectPath, 918 certs::dbusObjManagerIntf, "GetManagedObjects"); 919 } 920 921 void doPost(crow::Response &res, const crow::Request &req, 922 const std::vector<std::string> ¶ms) override 923 { 924 BMCWEB_LOG_DEBUG << "HTTPSCertificateCollection::doPost"; 925 auto asyncResp = std::make_shared<AsyncResp>(res); 926 asyncResp->res.jsonValue = {{"Name", "HTTPS Certificate"}, 927 {"Description", "HTTPS Certificate"}}; 928 929 std::string certFileBody = getCertificateFromReqBody(asyncResp, req); 930 931 if (certFileBody.empty()) 932 { 933 return; 934 } 935 936 std::shared_ptr<CertificateFile> certFile = 937 std::make_shared<CertificateFile>(certFileBody); 938 939 crow::connections::systemBus->async_method_call( 940 [asyncResp, certFile](const boost::system::error_code ec, 941 const std::string &objectPath) { 942 if (ec) 943 { 944 BMCWEB_LOG_ERROR << "DBUS response error: " << ec; 945 messages::internalError(asyncResp->res); 946 return; 947 } 948 long certId = getIDFromURL(objectPath); 949 if (certId < 0) 950 { 951 BMCWEB_LOG_ERROR << "Invalid objectPath value" 952 << objectPath; 953 messages::internalError(asyncResp->res); 954 return; 955 } 956 std::string certURL = 957 "/redfish/v1/Managers/bmc/NetworkProtocol/HTTPS/" 958 "Certificates/" + 959 std::to_string(certId); 960 getCertificateProperties(asyncResp, objectPath, 961 certs::httpsServiceName, certId, 962 certURL, "HTTPS Certificate"); 963 BMCWEB_LOG_DEBUG << "HTTPS certificate install file=" 964 << certFile->getCertFilePath(); 965 }, 966 certs::httpsServiceName, certs::httpsObjectPath, 967 certs::certInstallIntf, "Install", certFile->getCertFilePath()); 968 } 969 }; // HTTPSCertificateCollection 970 971 /** 972 * The certificate location schema defines a resource that an administrator 973 * can use in order to locate all certificates installed on a given service. 974 */ 975 class CertificateLocations : public Node 976 { 977 public: 978 template <typename CrowApp> 979 CertificateLocations(CrowApp &app) : 980 Node(app, "/redfish/v1/CertificateService/CertificateLocations/") 981 { 982 entityPrivileges = { 983 {boost::beast::http::verb::get, {{"Login"}}}, 984 {boost::beast::http::verb::head, {{"Login"}}}, 985 {boost::beast::http::verb::patch, {{"ConfigureComponents"}}}, 986 {boost::beast::http::verb::put, {{"ConfigureComponents"}}}, 987 {boost::beast::http::verb::delete_, {{"ConfigureComponents"}}}, 988 {boost::beast::http::verb::post, {{"ConfigureComponents"}}}}; 989 } 990 991 private: 992 void doGet(crow::Response &res, const crow::Request &req, 993 const std::vector<std::string> ¶ms) override 994 { 995 res.jsonValue = { 996 {"@odata.id", 997 "/redfish/v1/CertificateService/CertificateLocations"}, 998 {"@odata.type", 999 "#CertificateLocations.v1_0_0.CertificateLocations"}, 1000 {"@odata.context", 1001 "/redfish/v1/$metadata#CertificateLocations.CertificateLocations"}, 1002 {"Name", "Certificate Locations"}, 1003 {"Id", "CertificateLocations"}, 1004 {"Description", 1005 "Defines a resource that an administrator can use in order to " 1006 "locate all certificates installed on a given service"}}; 1007 auto asyncResp = std::make_shared<AsyncResp>(res); 1008 nlohmann::json &links = 1009 asyncResp->res.jsonValue["Links"]["Certificates"]; 1010 links = nlohmann::json::array(); 1011 getCertificateLocations( 1012 asyncResp, 1013 "/redfish/v1/Managers/bmc/NetworkProtocol/HTTPS/Certificates/", 1014 certs::httpsObjectPath, certs::httpsServiceName); 1015 getCertificateLocations(asyncResp, 1016 "/redfish/v1/AccountService/LDAP/Certificates/", 1017 certs::ldapObjectPath, certs::ldapServiceName); 1018 getCertificateLocations( 1019 asyncResp, "/redfish/v1/Managers/bmc/Truststore/Certificates/", 1020 certs::authorityObjectPath, certs::authorityServiceName); 1021 } 1022 /** 1023 * @brief Retrieve the certificates installed list and append to the 1024 * response 1025 * 1026 * @param[in] asyncResp Shared pointer to the response message 1027 * @param[in] certURL Path of the certificate object 1028 * @param[in] path Path of the D-Bus service object 1029 * @return None 1030 */ 1031 void getCertificateLocations(std::shared_ptr<AsyncResp> &asyncResp, 1032 const std::string &certURL, 1033 const std::string &path, 1034 const std::string &service) 1035 { 1036 BMCWEB_LOG_DEBUG << "getCertificateLocations URI=" << certURL 1037 << " Path=" << path << " service= " << service; 1038 crow::connections::systemBus->async_method_call( 1039 [asyncResp, certURL](const boost::system::error_code ec, 1040 const ManagedObjectType &certs) { 1041 if (ec) 1042 { 1043 BMCWEB_LOG_ERROR << "DBUS response error: " << ec; 1044 messages::internalError(asyncResp->res); 1045 return; 1046 } 1047 nlohmann::json &links = 1048 asyncResp->res.jsonValue["Links"]["Certificates"]; 1049 for (auto &cert : certs) 1050 { 1051 long id = getIDFromURL(cert.first.str); 1052 if (id >= 0) 1053 { 1054 links.push_back( 1055 {{"@odata.id", certURL + std::to_string(id)}}); 1056 } 1057 } 1058 asyncResp->res.jsonValue["Links"]["Certificates@odata.count"] = 1059 links.size(); 1060 }, 1061 service, path, certs::dbusObjManagerIntf, "GetManagedObjects"); 1062 } 1063 }; // CertificateLocations 1064 1065 /** 1066 * Collection of LDAP certificates 1067 */ 1068 class LDAPCertificateCollection : public Node 1069 { 1070 public: 1071 template <typename CrowApp> 1072 LDAPCertificateCollection(CrowApp &app) : 1073 Node(app, "/redfish/v1/AccountService/LDAP/Certificates/") 1074 { 1075 entityPrivileges = { 1076 {boost::beast::http::verb::get, {{"Login"}}}, 1077 {boost::beast::http::verb::head, {{"Login"}}}, 1078 {boost::beast::http::verb::patch, {{"ConfigureComponents"}}}, 1079 {boost::beast::http::verb::put, {{"ConfigureComponents"}}}, 1080 {boost::beast::http::verb::delete_, {{"ConfigureComponents"}}}, 1081 {boost::beast::http::verb::post, {{"ConfigureComponents"}}}}; 1082 } 1083 void doGet(crow::Response &res, const crow::Request &req, 1084 const std::vector<std::string> ¶ms) override 1085 { 1086 res.jsonValue = { 1087 {"@odata.id", "/redfish/v1/AccountService/LDAP/Certificates"}, 1088 {"@odata.type", "#CertificateCollection.CertificateCollection"}, 1089 {"@odata.context", 1090 "/redfish/v1/" 1091 "$metadata#CertificateCollection.CertificateCollection"}, 1092 {"Name", "LDAP Certificates Collection"}, 1093 {"Description", "A Collection of LDAP certificate instances"}}; 1094 auto asyncResp = std::make_shared<AsyncResp>(res); 1095 crow::connections::systemBus->async_method_call( 1096 [asyncResp](const boost::system::error_code ec, 1097 const ManagedObjectType &certs) { 1098 if (ec) 1099 { 1100 BMCWEB_LOG_ERROR << "DBUS response error: " << ec; 1101 messages::internalError(asyncResp->res); 1102 return; 1103 } 1104 nlohmann::json &members = asyncResp->res.jsonValue["Members"]; 1105 members = nlohmann::json::array(); 1106 for (const auto &cert : certs) 1107 { 1108 long id = getIDFromURL(cert.first.str); 1109 if (id >= 0) 1110 { 1111 members.push_back( 1112 {{"@odata.id", "/redfish/v1/AccountService/" 1113 "LDAP/Certificates/" + 1114 std::to_string(id)}}); 1115 } 1116 } 1117 asyncResp->res.jsonValue["Members@odata.count"] = 1118 members.size(); 1119 }, 1120 certs::ldapServiceName, certs::ldapObjectPath, 1121 certs::dbusObjManagerIntf, "GetManagedObjects"); 1122 } 1123 1124 void doPost(crow::Response &res, const crow::Request &req, 1125 const std::vector<std::string> ¶ms) override 1126 { 1127 auto asyncResp = std::make_shared<AsyncResp>(res); 1128 std::string certFileBody = getCertificateFromReqBody(asyncResp, req); 1129 1130 if (certFileBody.empty()) 1131 { 1132 return; 1133 } 1134 1135 std::shared_ptr<CertificateFile> certFile = 1136 std::make_shared<CertificateFile>(certFileBody); 1137 1138 crow::connections::systemBus->async_method_call( 1139 [asyncResp, certFile](const boost::system::error_code ec, 1140 const std::string &objectPath) { 1141 if (ec) 1142 { 1143 BMCWEB_LOG_ERROR << "DBUS response error: " << ec; 1144 messages::internalError(asyncResp->res); 1145 return; 1146 } 1147 long certId = getIDFromURL(objectPath); 1148 if (certId < 0) 1149 { 1150 BMCWEB_LOG_ERROR << "Invalid objectPath value" 1151 << objectPath; 1152 messages::internalError(asyncResp->res); 1153 return; 1154 } 1155 std::string certURL = 1156 "/redfish/v1/AccountService/LDAP/Certificates/" + 1157 std::to_string(certId); 1158 getCertificateProperties(asyncResp, objectPath, 1159 certs::ldapServiceName, certId, 1160 certURL, "LDAP Certificate"); 1161 BMCWEB_LOG_DEBUG << "LDAP certificate install file=" 1162 << certFile->getCertFilePath(); 1163 }, 1164 certs::ldapServiceName, certs::ldapObjectPath, 1165 certs::certInstallIntf, "Install", certFile->getCertFilePath()); 1166 } 1167 }; // LDAPCertificateCollection 1168 1169 /** 1170 * Certificate resource describes a certificate used to prove the identity 1171 * of a component, account or service. 1172 */ 1173 class LDAPCertificate : public Node 1174 { 1175 public: 1176 template <typename CrowApp> 1177 LDAPCertificate(CrowApp &app) : 1178 Node(app, "/redfish/v1/AccountService/LDAP/Certificates/<str>/", 1179 std::string()) 1180 { 1181 entityPrivileges = { 1182 {boost::beast::http::verb::get, {{"Login"}}}, 1183 {boost::beast::http::verb::head, {{"Login"}}}, 1184 {boost::beast::http::verb::patch, {{"ConfigureComponents"}}}, 1185 {boost::beast::http::verb::put, {{"ConfigureComponents"}}}, 1186 {boost::beast::http::verb::delete_, {{"ConfigureComponents"}}}, 1187 {boost::beast::http::verb::post, {{"ConfigureComponents"}}}}; 1188 } 1189 1190 void doGet(crow::Response &res, const crow::Request &req, 1191 const std::vector<std::string> ¶ms) override 1192 { 1193 auto asyncResp = std::make_shared<AsyncResp>(res); 1194 long id = getIDFromURL(req.url); 1195 if (id < 0) 1196 { 1197 BMCWEB_LOG_ERROR << "Invalid url value" << req.url; 1198 messages::internalError(asyncResp->res); 1199 return; 1200 } 1201 BMCWEB_LOG_DEBUG << "LDAP Certificate ID=" << std::to_string(id); 1202 std::string certURL = "/redfish/v1/AccountService/LDAP/Certificates/" + 1203 std::to_string(id); 1204 std::string objectPath = certs::ldapObjectPath; 1205 objectPath += "/"; 1206 objectPath += std::to_string(id); 1207 getCertificateProperties(asyncResp, objectPath, certs::ldapServiceName, 1208 id, certURL, "LDAP Certificate"); 1209 } 1210 }; // LDAPCertificate 1211 /** 1212 * Collection of TrustStoreCertificate certificates 1213 */ 1214 class TrustStoreCertificateCollection : public Node 1215 { 1216 public: 1217 template <typename CrowApp> 1218 TrustStoreCertificateCollection(CrowApp &app) : 1219 Node(app, "/redfish/v1/Managers/bmc/Truststore/Certificates/") 1220 { 1221 entityPrivileges = { 1222 {boost::beast::http::verb::get, {{"Login"}}}, 1223 {boost::beast::http::verb::head, {{"Login"}}}, 1224 {boost::beast::http::verb::patch, {{"ConfigureComponents"}}}, 1225 {boost::beast::http::verb::put, {{"ConfigureComponents"}}}, 1226 {boost::beast::http::verb::delete_, {{"ConfigureComponents"}}}, 1227 {boost::beast::http::verb::post, {{"ConfigureComponents"}}}}; 1228 } 1229 void doGet(crow::Response &res, const crow::Request &req, 1230 const std::vector<std::string> ¶ms) override 1231 { 1232 res.jsonValue = { 1233 {"@odata.id", "/redfish/v1/Managers/bmc/Truststore/Certificates/"}, 1234 {"@odata.type", "#CertificateCollection.CertificateCollection"}, 1235 {"@odata.context", 1236 "/redfish/v1/" 1237 "$metadata#CertificateCollection.CertificateCollection"}, 1238 {"Name", "TrustStore Certificates Collection"}, 1239 {"Description", 1240 "A Collection of TrustStore certificate instances"}}; 1241 auto asyncResp = std::make_shared<AsyncResp>(res); 1242 crow::connections::systemBus->async_method_call( 1243 [asyncResp](const boost::system::error_code ec, 1244 const ManagedObjectType &certs) { 1245 if (ec) 1246 { 1247 BMCWEB_LOG_ERROR << "DBUS response error: " << ec; 1248 messages::internalError(asyncResp->res); 1249 return; 1250 } 1251 nlohmann::json &members = asyncResp->res.jsonValue["Members"]; 1252 members = nlohmann::json::array(); 1253 for (const auto &cert : certs) 1254 { 1255 long id = getIDFromURL(cert.first.str); 1256 if (id >= 0) 1257 { 1258 members.push_back( 1259 {{"@odata.id", "/redfish/v1/Managers/bmc/" 1260 "Truststore/Certificates/" + 1261 std::to_string(id)}}); 1262 } 1263 } 1264 asyncResp->res.jsonValue["Members@odata.count"] = 1265 members.size(); 1266 }, 1267 certs::authorityServiceName, certs::authorityObjectPath, 1268 certs::dbusObjManagerIntf, "GetManagedObjects"); 1269 } 1270 1271 void doPost(crow::Response &res, const crow::Request &req, 1272 const std::vector<std::string> ¶ms) override 1273 { 1274 std::shared_ptr<CertificateFile> certFile = 1275 std::make_shared<CertificateFile>(req.body); 1276 auto asyncResp = std::make_shared<AsyncResp>(res); 1277 crow::connections::systemBus->async_method_call( 1278 [asyncResp, certFile](const boost::system::error_code ec, 1279 const std::string &objectPath) { 1280 if (ec) 1281 { 1282 BMCWEB_LOG_ERROR << "DBUS response error: " << ec; 1283 messages::internalError(asyncResp->res); 1284 return; 1285 } 1286 long certId = getIDFromURL(objectPath); 1287 if (certId < 0) 1288 { 1289 BMCWEB_LOG_ERROR << "Invalid objectPath value" 1290 << objectPath; 1291 messages::internalError(asyncResp->res); 1292 return; 1293 } 1294 std::string certURL = "/redfish/v1/Managers/bmc/" 1295 "Truststore/Certificates/" + 1296 std::to_string(certId); 1297 1298 getCertificateProperties(asyncResp, objectPath, 1299 certs::authorityServiceName, certId, 1300 certURL, "TrustStore Certificate"); 1301 BMCWEB_LOG_DEBUG << "TrustStore certificate install file=" 1302 << certFile->getCertFilePath(); 1303 }, 1304 certs::authorityServiceName, certs::authorityObjectPath, 1305 certs::certInstallIntf, "Install", certFile->getCertFilePath()); 1306 } 1307 }; // TrustStoreCertificateCollection 1308 1309 /** 1310 * Certificate resource describes a certificate used to prove the identity 1311 * of a component, account or service. 1312 */ 1313 class TrustStoreCertificate : public Node 1314 { 1315 public: 1316 template <typename CrowApp> 1317 TrustStoreCertificate(CrowApp &app) : 1318 Node(app, "/redfish/v1/Managers/bmc/Truststore/Certificates/<str>/", 1319 std::string()) 1320 { 1321 entityPrivileges = { 1322 {boost::beast::http::verb::get, {{"Login"}}}, 1323 {boost::beast::http::verb::head, {{"Login"}}}, 1324 {boost::beast::http::verb::patch, {{"ConfigureComponents"}}}, 1325 {boost::beast::http::verb::put, {{"ConfigureComponents"}}}, 1326 {boost::beast::http::verb::delete_, {{"ConfigureComponents"}}}, 1327 {boost::beast::http::verb::post, {{"ConfigureComponents"}}}}; 1328 } 1329 1330 void doGet(crow::Response &res, const crow::Request &req, 1331 const std::vector<std::string> ¶ms) override 1332 { 1333 auto asyncResp = std::make_shared<AsyncResp>(res); 1334 long id = getIDFromURL(req.url); 1335 if (id < 0) 1336 { 1337 BMCWEB_LOG_ERROR << "Invalid url value" << req.url; 1338 messages::internalError(asyncResp->res); 1339 return; 1340 } 1341 BMCWEB_LOG_DEBUG << "TrustStoreCertificate::doGet ID=" 1342 << std::to_string(id); 1343 std::string certURL = 1344 "/redfish/v1/Managers/bmc/Truststore/Certificates/" + 1345 std::to_string(id); 1346 std::string objectPath = certs::authorityObjectPath; 1347 objectPath += "/"; 1348 objectPath += std::to_string(id); 1349 getCertificateProperties(asyncResp, objectPath, 1350 certs::authorityServiceName, id, certURL, 1351 "TrustStore Certificate"); 1352 } 1353 }; // TrustStoreCertificate 1354 } // namespace redfish 1355