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 inline 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 inline 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 inline 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 inline 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 handleError(const std::string_view dbusErrorName, 469 const std::string& id, const std::string& certificate, 470 const std::shared_ptr<bmcweb::AsyncResp>& asyncResp) 471 { 472 if (dbusErrorName == "org.freedesktop.DBus.Error.UnknownObject") 473 { 474 messages::resourceNotFound(asyncResp->res, "Certificate", id); 475 } 476 else if (dbusErrorName == 477 "xyz.openbmc_project.Certs.Error.InvalidCertificate") 478 { 479 messages::propertyValueIncorrect(asyncResp->res, "Certificate", 480 certificate); 481 } 482 else 483 { 484 messages::internalError(asyncResp->res); 485 } 486 } 487 488 inline void handleReplaceCertificateAction( 489 App& app, const crow::Request& req, 490 const std::shared_ptr<bmcweb::AsyncResp>& asyncResp) 491 { 492 if (!redfish::setUpRedfishRoute(app, req, asyncResp)) 493 { 494 return; 495 } 496 std::string certificate; 497 std::string certURI; 498 std::optional<std::string> certificateType = "PEM"; 499 500 if (!json_util::readJsonAction(req, asyncResp->res, "CertificateString", 501 certificate, "CertificateUri/@odata.id", 502 certURI, "CertificateType", certificateType)) 503 { 504 BMCWEB_LOG_ERROR("Required parameters are missing"); 505 return; 506 } 507 508 if (!certificateType) 509 { 510 // should never happen, but it never hurts to be paranoid. 511 return; 512 } 513 if (certificateType != "PEM") 514 { 515 messages::actionParameterNotSupported(asyncResp->res, "CertificateType", 516 "ReplaceCertificate"); 517 return; 518 } 519 520 BMCWEB_LOG_INFO("Certificate URI to replace: {}", certURI); 521 522 boost::system::result<boost::urls::url> parsedUrl = 523 boost::urls::parse_relative_ref(certURI); 524 if (!parsedUrl) 525 { 526 messages::actionParameterValueFormatError( 527 asyncResp->res, certURI, "CertificateUri", "ReplaceCertificate"); 528 return; 529 } 530 531 std::string id; 532 sdbusplus::message::object_path objectPath; 533 std::string name; 534 std::string service; 535 if (crow::utility::readUrlSegments(*parsedUrl, "redfish", "v1", "Managers", 536 "bmc", "NetworkProtocol", "HTTPS", 537 "Certificates", std::ref(id))) 538 { 539 objectPath = sdbusplus::message::object_path(certs::httpsObjectPath) / 540 id; 541 name = "HTTPS certificate"; 542 service = certs::httpsServiceName; 543 } 544 else if (crow::utility::readUrlSegments(*parsedUrl, "redfish", "v1", 545 "AccountService", "LDAP", 546 "Certificates", std::ref(id))) 547 { 548 objectPath = sdbusplus::message::object_path(certs::ldapObjectPath) / 549 id; 550 name = "LDAP certificate"; 551 service = certs::ldapServiceName; 552 } 553 else if (crow::utility::readUrlSegments(*parsedUrl, "redfish", "v1", 554 "Managers", "bmc", "Truststore", 555 "Certificates", std::ref(id))) 556 { 557 objectPath = 558 sdbusplus::message::object_path(certs::authorityObjectPath) / id; 559 name = "TrustStore certificate"; 560 service = certs::authorityServiceName; 561 } 562 else 563 { 564 messages::actionParameterNotSupported(asyncResp->res, "CertificateUri", 565 "ReplaceCertificate"); 566 return; 567 } 568 569 std::shared_ptr<CertificateFile> certFile = 570 std::make_shared<CertificateFile>(certificate); 571 crow::connections::systemBus->async_method_call( 572 [asyncResp, certFile, objectPath, service, url{*parsedUrl}, id, name, 573 certificate](const boost::system::error_code& ec, 574 sdbusplus::message_t& m) { 575 if (ec) 576 { 577 BMCWEB_LOG_ERROR("DBUS response error: {}", ec); 578 const sd_bus_error* dbusError = m.get_error(); 579 if ((dbusError != nullptr) && (dbusError->name != nullptr)) 580 { 581 handleError(dbusError->name, id, certificate, asyncResp); 582 } 583 else 584 { 585 messages::internalError(asyncResp->res); 586 } 587 return; 588 } 589 getCertificateProperties(asyncResp, objectPath, service, id, url, 590 name); 591 BMCWEB_LOG_DEBUG("HTTPS certificate install file={}", 592 certFile->getCertFilePath()); 593 }, 594 service, objectPath, certs::certReplaceIntf, "Replace", 595 certFile->getCertFilePath()); 596 } 597 598 // NOLINTNEXTLINE(cppcoreguidelines-avoid-non-const-global-variables) 599 static std::unique_ptr<sdbusplus::bus::match_t> csrMatcher; 600 /** 601 * @brief Read data from CSR D-bus object and set to response 602 * 603 * @param[in] asyncResp Shared pointer to the response message 604 * @param[in] certURI Link to certificate collection URI 605 * @param[in] service D-Bus service name 606 * @param[in] certObjPath certificate D-Bus object path 607 * @param[in] csrObjPath CSR D-Bus object path 608 * @return None 609 */ 610 inline void getCSR(const std::shared_ptr<bmcweb::AsyncResp>& asyncResp, 611 const std::string& certURI, const std::string& service, 612 const std::string& certObjPath, 613 const std::string& csrObjPath) 614 { 615 BMCWEB_LOG_DEBUG("getCSR CertObjectPath{} CSRObjectPath={} service={}", 616 certObjPath, csrObjPath, service); 617 crow::connections::systemBus->async_method_call( 618 [asyncResp, 619 certURI](const boost::system::error_code& ec, const std::string& csr) { 620 if (ec) 621 { 622 BMCWEB_LOG_ERROR("DBUS response error: {}", ec); 623 messages::internalError(asyncResp->res); 624 return; 625 } 626 if (csr.empty()) 627 { 628 BMCWEB_LOG_ERROR("CSR read is empty"); 629 messages::internalError(asyncResp->res); 630 return; 631 } 632 asyncResp->res.jsonValue["CSRString"] = csr; 633 asyncResp->res.jsonValue["CertificateCollection"]["@odata.id"] = 634 certURI; 635 }, 636 service, csrObjPath, "xyz.openbmc_project.Certs.CSR", "CSR"); 637 } 638 639 inline void 640 handleGenerateCSRAction(App& app, const crow::Request& req, 641 const std::shared_ptr<bmcweb::AsyncResp>& asyncResp) 642 { 643 if (!redfish::setUpRedfishRoute(app, req, asyncResp)) 644 { 645 return; 646 } 647 static const int rsaKeyBitLength = 2048; 648 649 // Required parameters 650 std::string city; 651 std::string commonName; 652 std::string country; 653 std::string organization; 654 std::string organizationalUnit; 655 std::string state; 656 std::string certURI; 657 658 // Optional parameters 659 std::optional<std::vector<std::string>> optAlternativeNames = 660 std::vector<std::string>(); 661 std::optional<std::string> optContactPerson = ""; 662 std::optional<std::string> optChallengePassword = ""; 663 std::optional<std::string> optEmail = ""; 664 std::optional<std::string> optGivenName = ""; 665 std::optional<std::string> optInitials = ""; 666 std::optional<int64_t> optKeyBitLength = rsaKeyBitLength; 667 std::optional<std::string> optKeyCurveId = "secp384r1"; 668 std::optional<std::string> optKeyPairAlgorithm = "EC"; 669 std::optional<std::vector<std::string>> optKeyUsage = 670 std::vector<std::string>(); 671 std::optional<std::string> optSurname = ""; 672 std::optional<std::string> optUnstructuredName = ""; 673 if (!json_util::readJsonAction( 674 req, asyncResp->res, "City", city, "CommonName", commonName, 675 "ContactPerson", optContactPerson, "Country", country, 676 "Organization", organization, "OrganizationalUnit", 677 organizationalUnit, "State", state, 678 "CertificateCollection/@odata.id", certURI, "AlternativeNames", 679 optAlternativeNames, "ChallengePassword", optChallengePassword, 680 "Email", optEmail, "GivenName", optGivenName, "Initials", 681 optInitials, "KeyBitLength", optKeyBitLength, "KeyCurveId", 682 optKeyCurveId, "KeyPairAlgorithm", optKeyPairAlgorithm, "KeyUsage", 683 optKeyUsage, "Surname", optSurname, "UnstructuredName", 684 optUnstructuredName)) 685 { 686 return; 687 } 688 689 // bmcweb has no way to store or decode a private key challenge 690 // password, which will likely cause bmcweb to crash on startup 691 // if this is not set on a post so not allowing the user to set 692 // value 693 if (!optChallengePassword->empty()) 694 { 695 messages::actionParameterNotSupported(asyncResp->res, "GenerateCSR", 696 "ChallengePassword"); 697 return; 698 } 699 700 std::string objectPath; 701 std::string service; 702 if (certURI.starts_with(std::format( 703 "/redfish/v1/Managers/{}/NetworkProtocol/HTTPS/Certificates", 704 BMCWEB_REDFISH_MANAGER_URI_NAME))) 705 { 706 objectPath = certs::httpsObjectPath; 707 service = certs::httpsServiceName; 708 } 709 else if (certURI.starts_with( 710 "/redfish/v1/AccountService/LDAP/Certificates")) 711 { 712 objectPath = certs::ldapObjectPath; 713 service = certs::ldapServiceName; 714 } 715 else 716 { 717 messages::actionParameterNotSupported( 718 asyncResp->res, "CertificateCollection", "GenerateCSR"); 719 return; 720 } 721 722 // supporting only EC and RSA algorithm 723 if (*optKeyPairAlgorithm != "EC" && *optKeyPairAlgorithm != "RSA") 724 { 725 messages::actionParameterNotSupported( 726 asyncResp->res, "KeyPairAlgorithm", "GenerateCSR"); 727 return; 728 } 729 730 // supporting only 2048 key bit length for RSA algorithm due to 731 // time consumed in generating private key 732 if (*optKeyPairAlgorithm == "RSA" && *optKeyBitLength != rsaKeyBitLength) 733 { 734 messages::propertyValueNotInList(asyncResp->res, *optKeyBitLength, 735 "KeyBitLength"); 736 return; 737 } 738 739 // validate KeyUsage supporting only 1 type based on URL 740 if (certURI.starts_with(std::format( 741 "/redfish/v1/Managers/{}/NetworkProtocol/HTTPS/Certificates", 742 BMCWEB_REDFISH_MANAGER_URI_NAME))) 743 { 744 if (optKeyUsage->empty()) 745 { 746 optKeyUsage->emplace_back("ServerAuthentication"); 747 } 748 else if (optKeyUsage->size() == 1) 749 { 750 if ((*optKeyUsage)[0] != "ServerAuthentication") 751 { 752 messages::propertyValueNotInList(asyncResp->res, 753 (*optKeyUsage)[0], "KeyUsage"); 754 return; 755 } 756 } 757 else 758 { 759 messages::actionParameterNotSupported(asyncResp->res, "KeyUsage", 760 "GenerateCSR"); 761 return; 762 } 763 } 764 else if (certURI.starts_with( 765 "/redfish/v1/AccountService/LDAP/Certificates")) 766 { 767 if (optKeyUsage->empty()) 768 { 769 optKeyUsage->emplace_back("ClientAuthentication"); 770 } 771 else if (optKeyUsage->size() == 1) 772 { 773 if ((*optKeyUsage)[0] != "ClientAuthentication") 774 { 775 messages::propertyValueNotInList(asyncResp->res, 776 (*optKeyUsage)[0], "KeyUsage"); 777 return; 778 } 779 } 780 else 781 { 782 messages::actionParameterNotSupported(asyncResp->res, "KeyUsage", 783 "GenerateCSR"); 784 return; 785 } 786 } 787 788 // Only allow one CSR matcher at a time so setting retry 789 // time-out and timer expiry to 10 seconds for now. 790 static const int timeOut = 10; 791 if (csrMatcher) 792 { 793 messages::serviceTemporarilyUnavailable(asyncResp->res, 794 std::to_string(timeOut)); 795 return; 796 } 797 798 if (req.ioService == nullptr) 799 { 800 messages::internalError(asyncResp->res); 801 return; 802 } 803 804 // Make this static so it survives outside this method 805 static boost::asio::steady_timer timeout(*req.ioService); 806 timeout.expires_after(std::chrono::seconds(timeOut)); 807 timeout.async_wait([asyncResp](const boost::system::error_code& ec) { 808 csrMatcher = nullptr; 809 if (ec) 810 { 811 // operation_aborted is expected if timer is canceled 812 // before completion. 813 if (ec != boost::asio::error::operation_aborted) 814 { 815 BMCWEB_LOG_ERROR("Async_wait failed {}", ec); 816 } 817 return; 818 } 819 BMCWEB_LOG_ERROR("Timed out waiting for Generating CSR"); 820 messages::internalError(asyncResp->res); 821 }); 822 823 // create a matcher to wait on CSR object 824 BMCWEB_LOG_DEBUG("create matcher with path {}", objectPath); 825 std::string match("type='signal'," 826 "interface='org.freedesktop.DBus.ObjectManager'," 827 "path='" + 828 objectPath + 829 "'," 830 "member='InterfacesAdded'"); 831 csrMatcher = std::make_unique<sdbusplus::bus::match_t>( 832 *crow::connections::systemBus, match, 833 [asyncResp, service, objectPath, certURI](sdbusplus::message_t& m) { 834 timeout.cancel(); 835 if (m.is_method_error()) 836 { 837 BMCWEB_LOG_ERROR("Dbus method error!!!"); 838 messages::internalError(asyncResp->res); 839 return; 840 } 841 842 dbus::utility::DBusInterfacesMap interfacesProperties; 843 844 sdbusplus::message::object_path csrObjectPath; 845 m.read(csrObjectPath, interfacesProperties); 846 BMCWEB_LOG_DEBUG("CSR object added{}", csrObjectPath.str); 847 for (const auto& interface : interfacesProperties) 848 { 849 if (interface.first == "xyz.openbmc_project.Certs.CSR") 850 { 851 getCSR(asyncResp, certURI, service, objectPath, 852 csrObjectPath.str); 853 break; 854 } 855 } 856 }); 857 crow::connections::systemBus->async_method_call( 858 [asyncResp](const boost::system::error_code& ec, const std::string&) { 859 if (ec) 860 { 861 BMCWEB_LOG_ERROR("DBUS response error: {}", ec.message()); 862 messages::internalError(asyncResp->res); 863 return; 864 } 865 }, 866 service, objectPath, "xyz.openbmc_project.Certs.CSR.Create", 867 "GenerateCSR", *optAlternativeNames, *optChallengePassword, city, 868 commonName, *optContactPerson, country, *optEmail, *optGivenName, 869 *optInitials, *optKeyBitLength, *optKeyCurveId, *optKeyPairAlgorithm, 870 *optKeyUsage, organization, organizationalUnit, state, *optSurname, 871 *optUnstructuredName); 872 } 873 874 inline void requestRoutesCertificateService(App& app) 875 { 876 BMCWEB_ROUTE(app, "/redfish/v1/CertificateService/") 877 .privileges(redfish::privileges::getCertificateService) 878 .methods(boost::beast::http::verb::get)( 879 std::bind_front(handleCertificateServiceGet, std::ref(app))); 880 881 BMCWEB_ROUTE(app, "/redfish/v1/CertificateService/CertificateLocations/") 882 .privileges(redfish::privileges::getCertificateLocations) 883 .methods(boost::beast::http::verb::get)( 884 std::bind_front(handleCertificateLocationsGet, std::ref(app))); 885 886 BMCWEB_ROUTE( 887 app, 888 "/redfish/v1/CertificateService/Actions/CertificateService.ReplaceCertificate/") 889 .privileges(redfish::privileges::postCertificateService) 890 .methods(boost::beast::http::verb::post)( 891 std::bind_front(handleReplaceCertificateAction, std::ref(app))); 892 893 BMCWEB_ROUTE( 894 app, 895 "/redfish/v1/CertificateService/Actions/CertificateService.GenerateCSR/") 896 .privileges(redfish::privileges::postCertificateService) 897 .methods(boost::beast::http::verb::post)( 898 std::bind_front(handleGenerateCSRAction, std::ref(app))); 899 } // requestRoutesCertificateService 900 901 inline void handleHTTPSCertificateCollectionGet( 902 App& app, const crow::Request& req, 903 const std::shared_ptr<bmcweb::AsyncResp>& asyncResp, 904 const std::string& managerId) 905 { 906 if (!redfish::setUpRedfishRoute(app, req, asyncResp)) 907 { 908 return; 909 } 910 911 if (managerId != BMCWEB_REDFISH_MANAGER_URI_NAME) 912 { 913 messages::resourceNotFound(asyncResp->res, "Manager", managerId); 914 return; 915 } 916 917 asyncResp->res.jsonValue["@odata.id"] = boost::urls::format( 918 "/redfish/v1/Managers/{}/NetworkProtocol/HTTPS/Certificates", 919 BMCWEB_REDFISH_MANAGER_URI_NAME); 920 asyncResp->res.jsonValue["@odata.type"] = 921 "#CertificateCollection.CertificateCollection"; 922 asyncResp->res.jsonValue["Name"] = "HTTPS Certificates Collection"; 923 asyncResp->res.jsonValue["Description"] = 924 "A Collection of HTTPS certificate instances"; 925 926 getCertificateList(asyncResp, certs::httpsObjectPath, 927 "/Members"_json_pointer, 928 "/Members@odata.count"_json_pointer); 929 } 930 931 inline void handleHTTPSCertificateCollectionPost( 932 App& app, const crow::Request& req, 933 const std::shared_ptr<bmcweb::AsyncResp>& asyncResp, 934 const std::string& managerId) 935 { 936 if (!redfish::setUpRedfishRoute(app, req, asyncResp)) 937 { 938 return; 939 } 940 941 if (managerId != BMCWEB_REDFISH_MANAGER_URI_NAME) 942 { 943 messages::resourceNotFound(asyncResp->res, "Manager", managerId); 944 return; 945 } 946 947 BMCWEB_LOG_DEBUG("HTTPSCertificateCollection::doPost"); 948 949 asyncResp->res.jsonValue["Name"] = "HTTPS Certificate"; 950 asyncResp->res.jsonValue["Description"] = "HTTPS Certificate"; 951 952 std::string certHttpBody = getCertificateFromReqBody(asyncResp, req); 953 954 if (certHttpBody.empty()) 955 { 956 BMCWEB_LOG_ERROR("Cannot get certificate from request body."); 957 messages::unrecognizedRequestBody(asyncResp->res); 958 return; 959 } 960 961 std::shared_ptr<CertificateFile> certFile = 962 std::make_shared<CertificateFile>(certHttpBody); 963 964 crow::connections::systemBus->async_method_call( 965 [asyncResp, certFile](const boost::system::error_code& ec, 966 const std::string& objectPath) { 967 if (ec) 968 { 969 BMCWEB_LOG_ERROR("DBUS response error: {}", ec); 970 messages::internalError(asyncResp->res); 971 return; 972 } 973 974 sdbusplus::message::object_path path(objectPath); 975 std::string certId = path.filename(); 976 const boost::urls::url certURL = boost::urls::format( 977 "/redfish/v1/Managers/{}/NetworkProtocol/HTTPS/Certificates/{}", 978 BMCWEB_REDFISH_MANAGER_URI_NAME, certId); 979 getCertificateProperties(asyncResp, objectPath, 980 certs::httpsServiceName, certId, certURL, 981 "HTTPS Certificate"); 982 BMCWEB_LOG_DEBUG("HTTPS certificate install file={}", 983 certFile->getCertFilePath()); 984 }, 985 certs::httpsServiceName, certs::httpsObjectPath, certs::certInstallIntf, 986 "Install", certFile->getCertFilePath()); 987 } 988 989 inline void handleHTTPSCertificateGet( 990 App& app, const crow::Request& req, 991 const std::shared_ptr<bmcweb::AsyncResp>& asyncResp, 992 const std::string& managerId, const std::string& certId) 993 { 994 if (!redfish::setUpRedfishRoute(app, req, asyncResp)) 995 { 996 return; 997 } 998 999 if (managerId != BMCWEB_REDFISH_MANAGER_URI_NAME) 1000 { 1001 messages::resourceNotFound(asyncResp->res, "Manager", managerId); 1002 return; 1003 } 1004 1005 BMCWEB_LOG_DEBUG("HTTPS Certificate ID={}", certId); 1006 const boost::urls::url certURL = boost::urls::format( 1007 "/redfish/v1/Managers/{}/NetworkProtocol/HTTPS/Certificates/{}", 1008 BMCWEB_REDFISH_MANAGER_URI_NAME, certId); 1009 std::string objPath = 1010 sdbusplus::message::object_path(certs::httpsObjectPath) / certId; 1011 getCertificateProperties(asyncResp, objPath, certs::httpsServiceName, 1012 certId, certURL, "HTTPS Certificate"); 1013 } 1014 1015 inline void requestRoutesHTTPSCertificate(App& app) 1016 { 1017 BMCWEB_ROUTE( 1018 app, "/redfish/v1/Managers/<str>/NetworkProtocol/HTTPS/Certificates/") 1019 .privileges(redfish::privileges::getCertificateCollection) 1020 .methods(boost::beast::http::verb::get)(std::bind_front( 1021 handleHTTPSCertificateCollectionGet, std::ref(app))); 1022 1023 BMCWEB_ROUTE( 1024 app, "/redfish/v1/Managers/<str>/NetworkProtocol/HTTPS/Certificates/") 1025 .privileges(redfish::privileges::postCertificateCollection) 1026 .methods(boost::beast::http::verb::post)(std::bind_front( 1027 handleHTTPSCertificateCollectionPost, std::ref(app))); 1028 1029 BMCWEB_ROUTE( 1030 app, 1031 "/redfish/v1/Managers/<str>/NetworkProtocol/HTTPS/Certificates/<str>/") 1032 .privileges(redfish::privileges::getCertificate) 1033 .methods(boost::beast::http::verb::get)( 1034 std::bind_front(handleHTTPSCertificateGet, std::ref(app))); 1035 } 1036 1037 inline void handleLDAPCertificateCollectionGet( 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 1046 asyncResp->res.jsonValue["@odata.id"] = 1047 "/redfish/v1/AccountService/LDAP/Certificates"; 1048 asyncResp->res.jsonValue["@odata.type"] = 1049 "#CertificateCollection.CertificateCollection"; 1050 asyncResp->res.jsonValue["Name"] = "LDAP Certificates Collection"; 1051 asyncResp->res.jsonValue["Description"] = 1052 "A Collection of LDAP certificate instances"; 1053 1054 getCertificateList(asyncResp, certs::ldapObjectPath, 1055 "/Members"_json_pointer, 1056 "/Members@odata.count"_json_pointer); 1057 } 1058 1059 inline void handleLDAPCertificateCollectionPost( 1060 App& app, const crow::Request& req, 1061 const std::shared_ptr<bmcweb::AsyncResp>& asyncResp) 1062 { 1063 if (!redfish::setUpRedfishRoute(app, req, asyncResp)) 1064 { 1065 return; 1066 } 1067 std::string certHttpBody = getCertificateFromReqBody(asyncResp, req); 1068 1069 if (certHttpBody.empty()) 1070 { 1071 BMCWEB_LOG_ERROR("Cannot get certificate from request body."); 1072 messages::unrecognizedRequestBody(asyncResp->res); 1073 return; 1074 } 1075 1076 std::shared_ptr<CertificateFile> certFile = 1077 std::make_shared<CertificateFile>(certHttpBody); 1078 1079 crow::connections::systemBus->async_method_call( 1080 [asyncResp, certFile](const boost::system::error_code& ec, 1081 const std::string& objectPath) { 1082 if (ec) 1083 { 1084 BMCWEB_LOG_ERROR("DBUS response error: {}", ec); 1085 messages::internalError(asyncResp->res); 1086 return; 1087 } 1088 1089 sdbusplus::message::object_path path(objectPath); 1090 std::string certId = path.filename(); 1091 const boost::urls::url certURL = boost::urls::format( 1092 "/redfish/v1/AccountService/LDAP/Certificates/{}", certId); 1093 getCertificateProperties(asyncResp, objectPath, 1094 certs::ldapServiceName, certId, certURL, 1095 "LDAP Certificate"); 1096 BMCWEB_LOG_DEBUG("LDAP certificate install file={}", 1097 certFile->getCertFilePath()); 1098 }, 1099 certs::ldapServiceName, certs::ldapObjectPath, certs::certInstallIntf, 1100 "Install", certFile->getCertFilePath()); 1101 } 1102 1103 inline void handleLDAPCertificateGet( 1104 App& app, const crow::Request& req, 1105 const std::shared_ptr<bmcweb::AsyncResp>& asyncResp, const std::string& id) 1106 { 1107 if (!redfish::setUpRedfishRoute(app, req, asyncResp)) 1108 { 1109 return; 1110 } 1111 1112 BMCWEB_LOG_DEBUG("LDAP Certificate ID={}", id); 1113 const boost::urls::url certURL = boost::urls::format( 1114 "/redfish/v1/AccountService/LDAP/Certificates/{}", id); 1115 std::string objPath = 1116 sdbusplus::message::object_path(certs::ldapObjectPath) / id; 1117 getCertificateProperties(asyncResp, objPath, certs::ldapServiceName, id, 1118 certURL, "LDAP Certificate"); 1119 } 1120 1121 inline void handleLDAPCertificateDelete( 1122 App& app, const crow::Request& req, 1123 const std::shared_ptr<bmcweb::AsyncResp>& asyncResp, const std::string& id) 1124 { 1125 if (!redfish::setUpRedfishRoute(app, req, asyncResp)) 1126 { 1127 return; 1128 } 1129 1130 BMCWEB_LOG_DEBUG("Delete LDAP Certificate ID={}", id); 1131 std::string objPath = 1132 sdbusplus::message::object_path(certs::ldapObjectPath) / id; 1133 1134 deleteCertificate(asyncResp, certs::ldapServiceName, objPath); 1135 } 1136 1137 inline void requestRoutesLDAPCertificate(App& app) 1138 { 1139 BMCWEB_ROUTE(app, "/redfish/v1/AccountService/LDAP/Certificates/") 1140 .privileges(redfish::privileges::getCertificateCollection) 1141 .methods(boost::beast::http::verb::get)( 1142 std::bind_front(handleLDAPCertificateCollectionGet, std::ref(app))); 1143 1144 BMCWEB_ROUTE(app, "/redfish/v1/AccountService/LDAP/Certificates/") 1145 .privileges(redfish::privileges::postCertificateCollection) 1146 .methods(boost::beast::http::verb::post)(std::bind_front( 1147 handleLDAPCertificateCollectionPost, std::ref(app))); 1148 1149 BMCWEB_ROUTE(app, "/redfish/v1/AccountService/LDAP/Certificates/<str>/") 1150 .privileges(redfish::privileges::getCertificate) 1151 .methods(boost::beast::http::verb::get)( 1152 std::bind_front(handleLDAPCertificateGet, std::ref(app))); 1153 1154 BMCWEB_ROUTE(app, "/redfish/v1/AccountService/LDAP/Certificates/<str>/") 1155 .privileges(redfish::privileges::deleteCertificate) 1156 .methods(boost::beast::http::verb::delete_)( 1157 std::bind_front(handleLDAPCertificateDelete, std::ref(app))); 1158 } // requestRoutesLDAPCertificate 1159 1160 inline void handleTrustStoreCertificateCollectionGet( 1161 App& app, const crow::Request& req, 1162 const std::shared_ptr<bmcweb::AsyncResp>& asyncResp, 1163 const std::string& managerId) 1164 { 1165 if (!redfish::setUpRedfishRoute(app, req, asyncResp)) 1166 { 1167 return; 1168 } 1169 1170 if (managerId != BMCWEB_REDFISH_MANAGER_URI_NAME) 1171 { 1172 messages::resourceNotFound(asyncResp->res, "Manager", managerId); 1173 return; 1174 } 1175 1176 asyncResp->res.jsonValue["@odata.id"] = 1177 boost::urls::format("/redfish/v1/Managers/{}/Truststore/Certificates/", 1178 BMCWEB_REDFISH_MANAGER_URI_NAME); 1179 asyncResp->res.jsonValue["@odata.type"] = 1180 "#CertificateCollection.CertificateCollection"; 1181 asyncResp->res.jsonValue["Name"] = "TrustStore Certificates Collection"; 1182 asyncResp->res.jsonValue["Description"] = 1183 "A Collection of TrustStore certificate instances"; 1184 1185 getCertificateList(asyncResp, certs::authorityObjectPath, 1186 "/Members"_json_pointer, 1187 "/Members@odata.count"_json_pointer); 1188 } 1189 1190 inline void handleTrustStoreCertificateCollectionPost( 1191 App& app, const crow::Request& req, 1192 const std::shared_ptr<bmcweb::AsyncResp>& asyncResp, 1193 const std::string& managerId) 1194 { 1195 if (!redfish::setUpRedfishRoute(app, req, asyncResp)) 1196 { 1197 return; 1198 } 1199 1200 if (managerId != BMCWEB_REDFISH_MANAGER_URI_NAME) 1201 { 1202 messages::resourceNotFound(asyncResp->res, "Manager", managerId); 1203 return; 1204 } 1205 1206 std::string certHttpBody = getCertificateFromReqBody(asyncResp, req); 1207 1208 if (certHttpBody.empty()) 1209 { 1210 BMCWEB_LOG_ERROR("Cannot get certificate from request body."); 1211 messages::unrecognizedRequestBody(asyncResp->res); 1212 return; 1213 } 1214 1215 std::shared_ptr<CertificateFile> certFile = 1216 std::make_shared<CertificateFile>(certHttpBody); 1217 crow::connections::systemBus->async_method_call( 1218 [asyncResp, certFile](const boost::system::error_code& ec, 1219 const std::string& objectPath) { 1220 if (ec) 1221 { 1222 BMCWEB_LOG_ERROR("DBUS response error: {}", ec); 1223 messages::internalError(asyncResp->res); 1224 return; 1225 } 1226 1227 sdbusplus::message::object_path path(objectPath); 1228 std::string certId = path.filename(); 1229 const boost::urls::url certURL = boost::urls::format( 1230 "/redfish/v1/Managers/{}/Truststore/Certificates/{}", 1231 BMCWEB_REDFISH_MANAGER_URI_NAME, certId); 1232 getCertificateProperties(asyncResp, objectPath, 1233 certs::authorityServiceName, certId, 1234 certURL, "TrustStore Certificate"); 1235 BMCWEB_LOG_DEBUG("TrustStore certificate install file={}", 1236 certFile->getCertFilePath()); 1237 }, 1238 certs::authorityServiceName, certs::authorityObjectPath, 1239 certs::certInstallIntf, "Install", certFile->getCertFilePath()); 1240 } 1241 1242 inline void handleTrustStoreCertificateGet( 1243 App& app, const crow::Request& req, 1244 const std::shared_ptr<bmcweb::AsyncResp>& asyncResp, 1245 const std::string& managerId, const std::string& certId) 1246 { 1247 if (!redfish::setUpRedfishRoute(app, req, asyncResp)) 1248 { 1249 return; 1250 } 1251 1252 if (managerId != BMCWEB_REDFISH_MANAGER_URI_NAME) 1253 { 1254 messages::resourceNotFound(asyncResp->res, "Manager", managerId); 1255 return; 1256 } 1257 1258 BMCWEB_LOG_DEBUG("Truststore Certificate ID={}", certId); 1259 const boost::urls::url certURL = boost::urls::format( 1260 "/redfish/v1/Managers/{}/Truststore/Certificates/{}", 1261 BMCWEB_REDFISH_MANAGER_URI_NAME, certId); 1262 std::string objPath = 1263 sdbusplus::message::object_path(certs::authorityObjectPath) / certId; 1264 getCertificateProperties(asyncResp, objPath, certs::authorityServiceName, 1265 certId, certURL, "TrustStore Certificate"); 1266 } 1267 1268 inline void handleTrustStoreCertificateDelete( 1269 App& app, const crow::Request& req, 1270 const std::shared_ptr<bmcweb::AsyncResp>& asyncResp, 1271 const std::string& managerId, const std::string& certId) 1272 { 1273 if (!redfish::setUpRedfishRoute(app, req, asyncResp)) 1274 { 1275 return; 1276 } 1277 1278 if (managerId != BMCWEB_REDFISH_MANAGER_URI_NAME) 1279 { 1280 messages::resourceNotFound(asyncResp->res, "Manager", managerId); 1281 return; 1282 } 1283 1284 BMCWEB_LOG_DEBUG("Delete TrustStore Certificate ID={}", certId); 1285 std::string objPath = 1286 sdbusplus::message::object_path(certs::authorityObjectPath) / certId; 1287 1288 deleteCertificate(asyncResp, certs::authorityServiceName, objPath); 1289 } 1290 1291 inline void requestRoutesTrustStoreCertificate(App& app) 1292 { 1293 BMCWEB_ROUTE(app, "/redfish/v1/Managers/<str>/Truststore/Certificates/") 1294 .privileges(redfish::privileges::getCertificate) 1295 .methods(boost::beast::http::verb::get)(std::bind_front( 1296 handleTrustStoreCertificateCollectionGet, std::ref(app))); 1297 1298 BMCWEB_ROUTE(app, "/redfish/v1/Managers/<str>/Truststore/Certificates/") 1299 .privileges(redfish::privileges::postCertificateCollection) 1300 .methods(boost::beast::http::verb::post)(std::bind_front( 1301 handleTrustStoreCertificateCollectionPost, std::ref(app))); 1302 1303 BMCWEB_ROUTE(app, 1304 "/redfish/v1/Managers/<str>/Truststore/Certificates/<str>/") 1305 .privileges(redfish::privileges::getCertificate) 1306 .methods(boost::beast::http::verb::get)( 1307 std::bind_front(handleTrustStoreCertificateGet, std::ref(app))); 1308 1309 BMCWEB_ROUTE(app, 1310 "/redfish/v1/Managers/<str>/Truststore/Certificates/<str>/") 1311 .privileges(redfish::privileges::deleteCertificate) 1312 .methods(boost::beast::http::verb::delete_)( 1313 std::bind_front(handleTrustStoreCertificateDelete, std::ref(app))); 1314 } // requestRoutesTrustStoreCertificate 1315 } // namespace redfish 1316