1 /* 2 // Copyright (c) 2018 IBM Corporation 3 // 4 // Licensed under the Apache License, Version 2.0 (the "License"); 5 // you may not use this file except in compliance with the License. 6 // You may obtain a copy of the License at 7 // 8 // http://www.apache.org/licenses/LICENSE-2.0 9 // 10 // Unless required by applicable law or agreed to in writing, software 11 // distributed under the License is distributed on an "AS IS" BASIS, 12 // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 // See the License for the specific language governing permissions and 14 // limitations under the License. 15 */ 16 #pragma once 17 18 #include "node.hpp" 19 20 #include <variant> 21 namespace redfish 22 { 23 namespace certs 24 { 25 constexpr char const *httpsObjectPath = 26 "/xyz/openbmc_project/certs/server/https"; 27 constexpr char const *certInstallIntf = "xyz.openbmc_project.Certs.Install"; 28 constexpr char const *certReplaceIntf = "xyz.openbmc_project.Certs.Replace"; 29 constexpr char const *certPropIntf = "xyz.openbmc_project.Certs.Certificate"; 30 constexpr char const *dbusPropIntf = "org.freedesktop.DBus.Properties"; 31 constexpr char const *dbusObjManagerIntf = "org.freedesktop.DBus.ObjectManager"; 32 constexpr char const *mapperBusName = "xyz.openbmc_project.ObjectMapper"; 33 constexpr char const *mapperObjectPath = "/xyz/openbmc_project/object_mapper"; 34 constexpr char const *mapperIntf = "xyz.openbmc_project.ObjectMapper"; 35 constexpr char const *ldapObjectPath = "/xyz/openbmc_project/certs/client/ldap"; 36 constexpr char const *httpsServiceName = 37 "xyz.openbmc_project.Certs.Manager.Server.Https"; 38 constexpr char const *ldapServiceName = 39 "xyz.openbmc_project.Certs.Manager.Client.Ldap"; 40 } // namespace certs 41 42 /** 43 * The Certificate schema defines a Certificate Service which represents the 44 * actions available to manage certificates and links to where certificates 45 * are installed. 46 */ 47 class CertificateService : public Node 48 { 49 public: 50 CertificateService(CrowApp &app) : 51 Node(app, "/redfish/v1/CertificateService/") 52 { 53 // TODO: Issue#61 No entries are available for Certificate 54 // sevice at https://www.dmtf.org/standards/redfish 55 // "redfish standard registries". Need to modify after DMTF 56 // publish Privilege details for certificate service 57 entityPrivileges = { 58 {boost::beast::http::verb::get, {{"Login"}}}, 59 {boost::beast::http::verb::head, {{"Login"}}}, 60 {boost::beast::http::verb::patch, {{"ConfigureComponents"}}}, 61 {boost::beast::http::verb::put, {{"ConfigureComponents"}}}, 62 {boost::beast::http::verb::delete_, {{"ConfigureComponents"}}}, 63 {boost::beast::http::verb::post, {{"ConfigureComponents"}}}}; 64 } 65 66 private: 67 void doGet(crow::Response &res, const crow::Request &req, 68 const std::vector<std::string> ¶ms) override 69 { 70 res.jsonValue = { 71 {"@odata.type", "#CertificateService.v1_0_0.CertificateService"}, 72 {"@odata.id", "/redfish/v1/CertificateService"}, 73 {"@odata.context", 74 "/redfish/v1/$metadata#CertificateService.CertificateService"}, 75 {"Id", "CertificateService"}, 76 {"Name", "Certificate Service"}, 77 {"Description", "Actions available to manage certificates"}}; 78 res.jsonValue["CertificateLocations"] = { 79 {"@odata.id", 80 "/redfish/v1/CertificateService/CertificateLocations"}}; 81 res.jsonValue["Actions"]["#CertificateService.ReplaceCertificate"] = { 82 {"target", "/redfish/v1/CertificateService/Actions/" 83 "CertificateService.ReplaceCertificate"}, 84 {"CertificateType@Redfish.AllowableValues", {"PEM"}}}; 85 res.end(); 86 } 87 }; // CertificateService 88 89 /** 90 * @brief Find the ID specified in the URL 91 * Finds the numbers specified after the last "/" in the URL and returns. 92 * @param[in] path URL 93 * @return -1 on failure and number on success 94 */ 95 long getIDFromURL(const std::string_view url) 96 { 97 std::size_t found = url.rfind("/"); 98 if (found == std::string::npos) 99 { 100 return -1; 101 } 102 if ((found + 1) < url.length()) 103 { 104 char *endPtr; 105 std::string_view str = url.substr(found + 1); 106 long value = std::strtol(str.data(), &endPtr, 10); 107 if (endPtr != str.end()) 108 { 109 return -1; 110 } 111 return value; 112 } 113 return -1; 114 } 115 116 /** 117 * Class to create a temporary certificate file for uploading to system 118 */ 119 class CertificateFile 120 { 121 public: 122 CertificateFile() = delete; 123 CertificateFile(const CertificateFile &) = delete; 124 CertificateFile &operator=(const CertificateFile &) = delete; 125 CertificateFile(CertificateFile &&) = delete; 126 CertificateFile &operator=(CertificateFile &&) = delete; 127 CertificateFile(const std::string &certString) 128 { 129 char dirTemplate[] = "/tmp/Certs.XXXXXX"; 130 char *tempDirectory = mkdtemp(dirTemplate); 131 if (tempDirectory) 132 { 133 certDirectory = tempDirectory; 134 certificateFile = certDirectory / "cert.pem"; 135 std::ofstream out(certificateFile, std::ofstream::out | 136 std::ofstream::binary | 137 std::ofstream::trunc); 138 out << certString; 139 out.close(); 140 BMCWEB_LOG_DEBUG << "Creating certificate file" << certificateFile; 141 } 142 } 143 ~CertificateFile() 144 { 145 if (std::filesystem::exists(certDirectory)) 146 { 147 BMCWEB_LOG_DEBUG << "Removing certificate file" << certificateFile; 148 try 149 { 150 std::filesystem::remove_all(certDirectory); 151 } 152 catch (const std::filesystem::filesystem_error &e) 153 { 154 BMCWEB_LOG_ERROR << "Failed to remove temp directory" 155 << certDirectory; 156 } 157 } 158 } 159 std::string getCertFilePath() 160 { 161 return certificateFile; 162 } 163 164 private: 165 std::filesystem::path certificateFile; 166 std::filesystem::path certDirectory; 167 }; 168 169 /** 170 * @brief Parse and update Certficate Issue/Subject property 171 * 172 * @param[in] asyncResp Shared pointer to the response message 173 * @param[in] str Issuer/Subject value in key=value pairs 174 * @param[in] type Issuer/Subject 175 * @return None 176 */ 177 static void updateCertIssuerOrSubject(nlohmann::json &out, 178 const std::string_view value) 179 { 180 // example: O=openbmc-project.xyz,CN=localhost 181 std::string_view::iterator i = value.begin(); 182 while (i != value.end()) 183 { 184 std::string_view::iterator tokenBegin = i; 185 while (i != value.end() && *i != '=') 186 { 187 i++; 188 } 189 if (i == value.end()) 190 { 191 break; 192 } 193 const std::string_view key(tokenBegin, i - tokenBegin); 194 i++; 195 tokenBegin = i; 196 while (i != value.end() && *i != ',') 197 { 198 i++; 199 } 200 const std::string_view val(tokenBegin, i - tokenBegin); 201 if (key == "L") 202 { 203 out["City"] = val; 204 } 205 else if (key == "CN") 206 { 207 out["CommonName"] = val; 208 } 209 else if (key == "C") 210 { 211 out["Country"] = val; 212 } 213 else if (key == "O") 214 { 215 out["Organization"] = val; 216 } 217 else if (key == "OU") 218 { 219 out["OrganizationalUnit"] = val; 220 } 221 else if (key == "ST") 222 { 223 out["State"] = val; 224 } 225 // skip comma character 226 if (i != value.end()) 227 { 228 i++; 229 } 230 } 231 } 232 233 /** 234 * @brief Retrieve the certificates properties and append to the response 235 * message 236 * 237 * @param[in] asyncResp Shared pointer to the response message 238 * @param[in] objectPath Path of the D-Bus service object 239 * @param[in] certId Id of the certificate 240 * @param[in] certURL URL of the certificate object 241 * @param[in] name name of the certificate 242 * @return None 243 */ 244 static void getCertificateProperties( 245 const std::shared_ptr<AsyncResp> &asyncResp, const std::string &objectPath, 246 const std::string &service, long certId, const std::string &certURL, 247 const std::string &name) 248 { 249 using PropertyType = 250 std::variant<std::string, uint64_t, std::vector<std::string>>; 251 using PropertiesMap = boost::container::flat_map<std::string, PropertyType>; 252 BMCWEB_LOG_DEBUG << "getCertificateProperties Path=" << objectPath 253 << " certId=" << certId << " certURl=" << certURL; 254 crow::connections::systemBus->async_method_call( 255 [asyncResp, certURL, certId, name](const boost::system::error_code ec, 256 const PropertiesMap &properties) { 257 if (ec) 258 { 259 BMCWEB_LOG_ERROR << "DBUS response error: " << ec; 260 messages::resourceNotFound(asyncResp->res, name, 261 std::to_string(certId)); 262 return; 263 } 264 asyncResp->res.jsonValue = { 265 {"@odata.id", certURL}, 266 {"@odata.type", "#Certificate.v1_0_0.Certificate"}, 267 {"@odata.context", 268 "/redfish/v1/$metadata#Certificate.Certificate"}, 269 {"Id", std::to_string(certId)}, 270 {"Name", name}, 271 {"Description", name}}; 272 for (const auto &property : properties) 273 { 274 if (property.first == "CertificateString") 275 { 276 asyncResp->res.jsonValue["CertificateString"] = ""; 277 const std::string *value = 278 std::get_if<std::string>(&property.second); 279 if (value) 280 { 281 asyncResp->res.jsonValue["CertificateString"] = *value; 282 } 283 } 284 else if (property.first == "KeyUsage") 285 { 286 nlohmann::json &keyUsage = 287 asyncResp->res.jsonValue["KeyUsage"]; 288 keyUsage = nlohmann::json::array(); 289 const std::vector<std::string> *value = 290 std::get_if<std::vector<std::string>>(&property.second); 291 if (value) 292 { 293 for (const std::string &usage : *value) 294 { 295 keyUsage.push_back(usage); 296 } 297 } 298 } 299 else if (property.first == "Issuer") 300 { 301 const std::string *value = 302 std::get_if<std::string>(&property.second); 303 if (value) 304 { 305 updateCertIssuerOrSubject( 306 asyncResp->res.jsonValue["Issuer"], *value); 307 } 308 } 309 else if (property.first == "Subject") 310 { 311 const std::string *value = 312 std::get_if<std::string>(&property.second); 313 if (value) 314 { 315 updateCertIssuerOrSubject( 316 asyncResp->res.jsonValue["Subject"], *value); 317 } 318 } 319 else if (property.first == "ValidNotAfter") 320 { 321 const uint64_t *value = 322 std::get_if<uint64_t>(&property.second); 323 if (value) 324 { 325 std::time_t time = static_cast<std::time_t>(*value); 326 asyncResp->res.jsonValue["ValidNotAfter"] = 327 crow::utility::getDateTime(time); 328 } 329 } 330 else if (property.first == "ValidNotBefore") 331 { 332 const uint64_t *value = 333 std::get_if<uint64_t>(&property.second); 334 if (value) 335 { 336 std::time_t time = static_cast<std::time_t>(*value); 337 asyncResp->res.jsonValue["ValidNotBefore"] = 338 crow::utility::getDateTime(time); 339 } 340 } 341 } 342 asyncResp->res.addHeader("Location", certURL); 343 }, 344 service, objectPath, certs::dbusPropIntf, "GetAll", 345 certs::certPropIntf); 346 } 347 348 using GetObjectType = 349 std::vector<std::pair<std::string, std::vector<std::string>>>; 350 351 /** 352 * Action to replace an existing certificate 353 */ 354 class CertificateActionsReplaceCertificate : public Node 355 { 356 public: 357 CertificateActionsReplaceCertificate(CrowApp &app) : 358 Node(app, "/redfish/v1/CertificateService/Actions/" 359 "CertificateService.ReplaceCertificate/") 360 { 361 entityPrivileges = { 362 {boost::beast::http::verb::get, {{"Login"}}}, 363 {boost::beast::http::verb::head, {{"Login"}}}, 364 {boost::beast::http::verb::patch, {{"ConfigureComponents"}}}, 365 {boost::beast::http::verb::put, {{"ConfigureComponents"}}}, 366 {boost::beast::http::verb::delete_, {{"ConfigureComponents"}}}, 367 {boost::beast::http::verb::post, {{"ConfigureComponents"}}}}; 368 } 369 370 private: 371 void doPost(crow::Response &res, const crow::Request &req, 372 const std::vector<std::string> ¶ms) override 373 { 374 std::string certificate; 375 nlohmann::json certificateUri; 376 std::optional<std::string> certificateType = "PEM"; 377 auto asyncResp = std::make_shared<AsyncResp>(res); 378 if (!json_util::readJson(req, asyncResp->res, "CertificateString", 379 certificate, "CertificateUri", certificateUri, 380 "CertificateType", certificateType)) 381 { 382 BMCWEB_LOG_ERROR << "Required parameters are missing"; 383 messages::internalError(asyncResp->res); 384 return; 385 } 386 387 if (!certificateType) 388 { 389 // should never happen, but it never hurts to be paranoid. 390 return; 391 } 392 if (certificateType != "PEM") 393 { 394 messages::actionParameterNotSupported( 395 asyncResp->res, "CertificateType", "ReplaceCertificate"); 396 return; 397 } 398 399 std::string certURI; 400 if (!redfish::json_util::readJson(certificateUri, asyncResp->res, 401 "@odata.id", certURI)) 402 { 403 messages::actionParameterMissing( 404 asyncResp->res, "ReplaceCertificate", "CertificateUri"); 405 return; 406 } 407 408 BMCWEB_LOG_INFO << "Certificate URI to replace" << certURI; 409 long id = getIDFromURL(certURI); 410 if (id < 0) 411 { 412 messages::actionParameterValueFormatError(asyncResp->res, certURI, 413 "CertificateUri", 414 "ReplaceCertificate"); 415 return; 416 } 417 std::string objectPath; 418 std::string name; 419 std::string service; 420 if (boost::starts_with( 421 certURI, 422 "/redfish/v1/Managers/bmc/NetworkProtocol/HTTPS/Certificates/")) 423 { 424 objectPath = 425 std::string(certs::httpsObjectPath) + "/" + std::to_string(id); 426 name = "HTTPS certificate"; 427 service = certs::httpsServiceName; 428 } 429 else if (boost::starts_with( 430 certURI, "/redfish/v1/AccountService/LDAP/Certificates/")) 431 { 432 objectPath = 433 std::string(certs::ldapObjectPath) + "/" + std::to_string(id); 434 name = "LDAP certificate"; 435 service = certs::ldapServiceName; 436 } 437 else 438 { 439 messages::actionParameterNotSupported( 440 asyncResp->res, "CertificateUri", "ReplaceCertificate"); 441 return; 442 } 443 444 std::shared_ptr<CertificateFile> certFile = 445 std::make_shared<CertificateFile>(certificate); 446 crow::connections::systemBus->async_method_call( 447 [asyncResp, certFile, objectPath, service, certURI, id, 448 name](const boost::system::error_code ec) { 449 if (ec) 450 { 451 BMCWEB_LOG_ERROR << "DBUS response error: " << ec; 452 messages::resourceNotFound(asyncResp->res, name, 453 std::to_string(id)); 454 return; 455 } 456 getCertificateProperties(asyncResp, objectPath, service, id, 457 certURI, name); 458 BMCWEB_LOG_DEBUG << "HTTPS certificate install file=" 459 << certFile->getCertFilePath(); 460 }, 461 service, objectPath, certs::certReplaceIntf, "Replace", 462 certFile->getCertFilePath()); 463 } 464 }; // CertificateActionsReplaceCertificate 465 466 /** 467 * Certificate resource describes a certificate used to prove the identity 468 * of a component, account or service. 469 */ 470 class HTTPSCertificate : public Node 471 { 472 public: 473 template <typename CrowApp> 474 HTTPSCertificate(CrowApp &app) : 475 Node(app, 476 "/redfish/v1/Managers/bmc/NetworkProtocol/HTTPS/Certificates/" 477 "<str>/", 478 std::string()) 479 { 480 entityPrivileges = { 481 {boost::beast::http::verb::get, {{"Login"}}}, 482 {boost::beast::http::verb::head, {{"Login"}}}, 483 {boost::beast::http::verb::patch, {{"ConfigureComponents"}}}, 484 {boost::beast::http::verb::put, {{"ConfigureComponents"}}}, 485 {boost::beast::http::verb::delete_, {{"ConfigureComponents"}}}, 486 {boost::beast::http::verb::post, {{"ConfigureComponents"}}}}; 487 } 488 489 void doGet(crow::Response &res, const crow::Request &req, 490 const std::vector<std::string> ¶ms) override 491 { 492 auto asyncResp = std::make_shared<AsyncResp>(res); 493 if (params.size() != 1) 494 { 495 messages::internalError(asyncResp->res); 496 return; 497 } 498 long id = getIDFromURL(req.url); 499 500 BMCWEB_LOG_DEBUG << "HTTPSCertificate::doGet ID=" << std::to_string(id); 501 std::string certURL = 502 "/redfish/v1/Managers/bmc/NetworkProtocol/HTTPS/Certificates/" + 503 std::to_string(id); 504 std::string objectPath = certs::httpsObjectPath; 505 objectPath += "/"; 506 objectPath += std::to_string(id); 507 getCertificateProperties(asyncResp, objectPath, certs::httpsServiceName, 508 id, certURL, "HTTPS Certificate"); 509 } 510 511 }; // namespace redfish 512 513 /** 514 * Collection of HTTPS certificates 515 */ 516 class HTTPSCertificateCollection : public Node 517 { 518 public: 519 template <typename CrowApp> 520 HTTPSCertificateCollection(CrowApp &app) : 521 Node(app, 522 "/redfish/v1/Managers/bmc/NetworkProtocol/HTTPS/Certificates/") 523 { 524 entityPrivileges = { 525 {boost::beast::http::verb::get, {{"Login"}}}, 526 {boost::beast::http::verb::head, {{"Login"}}}, 527 {boost::beast::http::verb::patch, {{"ConfigureComponents"}}}, 528 {boost::beast::http::verb::put, {{"ConfigureComponents"}}}, 529 {boost::beast::http::verb::delete_, {{"ConfigureComponents"}}}, 530 {boost::beast::http::verb::post, {{"ConfigureComponents"}}}}; 531 } 532 void doGet(crow::Response &res, const crow::Request &req, 533 const std::vector<std::string> ¶ms) override 534 { 535 res.jsonValue = { 536 {"@odata.id", 537 "/redfish/v1/Managers/bmc/NetworkProtocol/HTTPS/Certificates"}, 538 {"@odata.type", "#CertificateCollection.CertificateCollection"}, 539 {"@odata.context", 540 "/redfish/v1/" 541 "$metadata#CertificateCollection.CertificateCollection"}, 542 {"Name", "HTTPS Certificates Collection"}, 543 {"Description", "A Collection of HTTPS certificate instances"}}; 544 auto asyncResp = std::make_shared<AsyncResp>(res); 545 crow::connections::systemBus->async_method_call( 546 [asyncResp](const boost::system::error_code ec, 547 const ManagedObjectType &certs) { 548 if (ec) 549 { 550 BMCWEB_LOG_ERROR << "DBUS response error: " << ec; 551 messages::internalError(asyncResp->res); 552 return; 553 } 554 nlohmann::json &members = asyncResp->res.jsonValue["Members"]; 555 members = nlohmann::json::array(); 556 for (const auto &cert : certs) 557 { 558 long id = getIDFromURL(cert.first.str); 559 if (id >= 0) 560 { 561 members.push_back( 562 {{"@odata.id", 563 "/redfish/v1/Managers/bmc/" 564 "NetworkProtocol/HTTPS/Certificates/" + 565 std::to_string(id)}}); 566 } 567 } 568 asyncResp->res.jsonValue["Members@odata.count"] = 569 members.size(); 570 }, 571 certs::httpsServiceName, certs::httpsObjectPath, 572 certs::dbusObjManagerIntf, "GetManagedObjects"); 573 } 574 575 void doPost(crow::Response &res, const crow::Request &req, 576 const std::vector<std::string> ¶ms) override 577 { 578 BMCWEB_LOG_DEBUG << "HTTPSCertificateCollection::doPost"; 579 auto asyncResp = std::make_shared<AsyncResp>(res); 580 asyncResp->res.jsonValue = {{"Name", "HTTPS Certificate"}, 581 {"Description", "HTTPS Certificate"}}; 582 583 std::shared_ptr<CertificateFile> certFile = 584 std::make_shared<CertificateFile>(req.body); 585 586 crow::connections::systemBus->async_method_call( 587 [asyncResp, certFile](const boost::system::error_code ec) { 588 if (ec) 589 { 590 BMCWEB_LOG_ERROR << "DBUS response error: " << ec; 591 messages::internalError(asyncResp->res); 592 return; 593 } 594 // TODO: Issue#84 supporting only 1 certificate 595 long certId = 1; 596 std::string certURL = 597 "/redfish/v1/Managers/bmc/NetworkProtocol/HTTPS/" 598 "Certificates/" + 599 std::to_string(certId); 600 std::string objectPath = std::string(certs::httpsObjectPath) + 601 "/" + std::to_string(certId); 602 getCertificateProperties(asyncResp, objectPath, 603 certs::httpsServiceName, certId, 604 certURL, "HTTPS Certificate"); 605 BMCWEB_LOG_DEBUG << "HTTPS certificate install file=" 606 << certFile->getCertFilePath(); 607 }, 608 certs::httpsServiceName, certs::httpsObjectPath, 609 certs::certInstallIntf, "Install", certFile->getCertFilePath()); 610 } 611 }; // HTTPSCertificateCollection 612 613 /** 614 * The certificate location schema defines a resource that an administrator 615 * can use in order to locate all certificates installed on a given service. 616 */ 617 class CertificateLocations : public Node 618 { 619 public: 620 template <typename CrowApp> 621 CertificateLocations(CrowApp &app) : 622 Node(app, "/redfish/v1/CertificateService/CertificateLocations/") 623 { 624 entityPrivileges = { 625 {boost::beast::http::verb::get, {{"Login"}}}, 626 {boost::beast::http::verb::head, {{"Login"}}}, 627 {boost::beast::http::verb::patch, {{"ConfigureComponents"}}}, 628 {boost::beast::http::verb::put, {{"ConfigureComponents"}}}, 629 {boost::beast::http::verb::delete_, {{"ConfigureComponents"}}}, 630 {boost::beast::http::verb::post, {{"ConfigureComponents"}}}}; 631 } 632 633 private: 634 void doGet(crow::Response &res, const crow::Request &req, 635 const std::vector<std::string> ¶ms) override 636 { 637 res.jsonValue = { 638 {"@odata.id", 639 "/redfish/v1/CertificateService/CertificateLocations"}, 640 {"@odata.type", 641 "#CertificateLocations.v1_0_0.CertificateLocations"}, 642 {"@odata.context", 643 "/redfish/v1/$metadata#CertificateLocations.CertificateLocations"}, 644 {"Name", "Certificate Locations"}, 645 {"Id", "CertificateLocations"}, 646 {"Description", 647 "Defines a resource that an administrator can use in order to " 648 "locate all certificates installed on a given service"}}; 649 auto asyncResp = std::make_shared<AsyncResp>(res); 650 nlohmann::json &links = 651 asyncResp->res.jsonValue["Links"]["Certificates"]; 652 links = nlohmann::json::array(); 653 getCertificateLocations( 654 asyncResp, 655 "/redfish/v1/Managers/bmc/NetworkProtocol/HTTPS/Certificates/", 656 certs::httpsObjectPath, certs::httpsServiceName); 657 getCertificateLocations(asyncResp, 658 "/redfish/v1/AccountService/LDAP/Certificates/", 659 certs::ldapObjectPath, certs::ldapServiceName); 660 } 661 /** 662 * @brief Retrieve the certificates installed list and append to the 663 * response 664 * 665 * @param[in] asyncResp Shared pointer to the response message 666 * @param[in] certURL Path of the certificate object 667 * @param[in] path Path of the D-Bus service object 668 * @return None 669 */ 670 void getCertificateLocations(std::shared_ptr<AsyncResp> &asyncResp, 671 const std::string &certURL, 672 const std::string &path, 673 const std::string &service) 674 { 675 BMCWEB_LOG_DEBUG << "getCertificateLocations URI=" << certURL 676 << " Path=" << path << " service= " << service; 677 crow::connections::systemBus->async_method_call( 678 [asyncResp, certURL](const boost::system::error_code ec, 679 const ManagedObjectType &certs) { 680 if (ec) 681 { 682 BMCWEB_LOG_ERROR << "DBUS response error: " << ec; 683 messages::internalError(asyncResp->res); 684 return; 685 } 686 nlohmann::json &links = 687 asyncResp->res.jsonValue["Links"]["Certificates"]; 688 for (auto &cert : certs) 689 { 690 long id = getIDFromURL(cert.first.str); 691 if (id >= 0) 692 { 693 links.push_back( 694 {{"@odata.id", certURL + std::to_string(id)}}); 695 } 696 } 697 asyncResp->res.jsonValue["Links"]["Certificates@odata.count"] = 698 links.size(); 699 }, 700 service, path, certs::dbusObjManagerIntf, "GetManagedObjects"); 701 } 702 }; // CertificateLocations 703 704 /** 705 * Collection of LDAP certificates 706 */ 707 class LDAPCertificateCollection : public Node 708 { 709 public: 710 template <typename CrowApp> 711 LDAPCertificateCollection(CrowApp &app) : 712 Node(app, "/redfish/v1/AccountService/LDAP/Certificates/") 713 { 714 entityPrivileges = { 715 {boost::beast::http::verb::get, {{"Login"}}}, 716 {boost::beast::http::verb::head, {{"Login"}}}, 717 {boost::beast::http::verb::patch, {{"ConfigureComponents"}}}, 718 {boost::beast::http::verb::put, {{"ConfigureComponents"}}}, 719 {boost::beast::http::verb::delete_, {{"ConfigureComponents"}}}, 720 {boost::beast::http::verb::post, {{"ConfigureComponents"}}}}; 721 } 722 void doGet(crow::Response &res, const crow::Request &req, 723 const std::vector<std::string> ¶ms) override 724 { 725 res.jsonValue = { 726 {"@odata.id", "/redfish/v1/AccountService/LDAP/Certificates"}, 727 {"@odata.type", "#CertificateCollection.CertificateCollection"}, 728 {"@odata.context", 729 "/redfish/v1/" 730 "$metadata#CertificateCollection.CertificateCollection"}, 731 {"Name", "LDAP Certificates Collection"}, 732 {"Description", "A Collection of LDAP certificate instances"}}; 733 auto asyncResp = std::make_shared<AsyncResp>(res); 734 crow::connections::systemBus->async_method_call( 735 [asyncResp](const boost::system::error_code ec, 736 const ManagedObjectType &certs) { 737 if (ec) 738 { 739 BMCWEB_LOG_ERROR << "DBUS response error: " << ec; 740 messages::internalError(asyncResp->res); 741 return; 742 } 743 nlohmann::json &members = asyncResp->res.jsonValue["Members"]; 744 members = nlohmann::json::array(); 745 for (const auto &cert : certs) 746 { 747 long id = getIDFromURL(cert.first.str); 748 if (id >= 0) 749 { 750 members.push_back( 751 {{"@odata.id", "/redfish/v1/AccountService/" 752 "LDAP/Certificates/" + 753 std::to_string(id)}}); 754 } 755 } 756 asyncResp->res.jsonValue["Members@odata.count"] = 757 members.size(); 758 }, 759 certs::ldapServiceName, certs::ldapObjectPath, 760 certs::dbusObjManagerIntf, "GetManagedObjects"); 761 } 762 763 void doPost(crow::Response &res, const crow::Request &req, 764 const std::vector<std::string> ¶ms) override 765 { 766 std::shared_ptr<CertificateFile> certFile = 767 std::make_shared<CertificateFile>(req.body); 768 auto asyncResp = std::make_shared<AsyncResp>(res); 769 crow::connections::systemBus->async_method_call( 770 [asyncResp, certFile](const boost::system::error_code ec) { 771 if (ec) 772 { 773 BMCWEB_LOG_ERROR << "DBUS response error: " << ec; 774 messages::internalError(asyncResp->res); 775 return; 776 } 777 //// TODO: Issue#84 supporting only 1 certificate 778 long certId = 1; 779 std::string certURL = 780 "/redfish/v1/AccountService/LDAP/Certificates/" + 781 std::to_string(certId); 782 std::string objectPath = std::string(certs::ldapObjectPath) + 783 "/" + std::to_string(certId); 784 getCertificateProperties(asyncResp, objectPath, 785 certs::ldapServiceName, certId, 786 certURL, "LDAP Certificate"); 787 BMCWEB_LOG_DEBUG << "LDAP certificate install file=" 788 << certFile->getCertFilePath(); 789 }, 790 certs::ldapServiceName, certs::ldapObjectPath, 791 certs::certInstallIntf, "Install", certFile->getCertFilePath()); 792 } 793 }; // LDAPCertificateCollection 794 795 /** 796 * Certificate resource describes a certificate used to prove the identity 797 * of a component, account or service. 798 */ 799 class LDAPCertificate : public Node 800 { 801 public: 802 template <typename CrowApp> 803 LDAPCertificate(CrowApp &app) : 804 Node(app, "/redfish/v1/AccountService/LDAP/Certificates/<str>/", 805 std::string()) 806 { 807 entityPrivileges = { 808 {boost::beast::http::verb::get, {{"Login"}}}, 809 {boost::beast::http::verb::head, {{"Login"}}}, 810 {boost::beast::http::verb::patch, {{"ConfigureComponents"}}}, 811 {boost::beast::http::verb::put, {{"ConfigureComponents"}}}, 812 {boost::beast::http::verb::delete_, {{"ConfigureComponents"}}}, 813 {boost::beast::http::verb::post, {{"ConfigureComponents"}}}}; 814 } 815 816 void doGet(crow::Response &res, const crow::Request &req, 817 const std::vector<std::string> ¶ms) override 818 { 819 auto asyncResp = std::make_shared<AsyncResp>(res); 820 long id = getIDFromURL(req.url); 821 if (id < 0) 822 { 823 BMCWEB_LOG_ERROR << "Invalid url value" << req.url; 824 messages::internalError(asyncResp->res); 825 return; 826 } 827 BMCWEB_LOG_DEBUG << "LDAP Certificate ID=" << std::to_string(id); 828 std::string certURL = "/redfish/v1/AccountService/LDAP/Certificates/" + 829 std::to_string(id); 830 std::string objectPath = certs::ldapObjectPath; 831 objectPath += "/"; 832 objectPath += std::to_string(id); 833 getCertificateProperties(asyncResp, objectPath, certs::ldapServiceName, 834 id, certURL, "LDAP Certificate"); 835 } 836 }; // LDAPCertificate 837 } // namespace redfish 838