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