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