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