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::internalError(asyncResp->res); 261 return; 262 } 263 asyncResp->res.jsonValue = { 264 {"@odata.id", certURL}, 265 {"@odata.type", "#Certificate.v1_0_0.Certificate"}, 266 {"@odata.context", 267 "/redfish/v1/$metadata#Certificate.Certificate"}, 268 {"Id", std::to_string(certId)}, 269 {"Name", name}, 270 {"Description", name}}; 271 for (const auto &property : properties) 272 { 273 if (property.first == "CertificateString") 274 { 275 asyncResp->res.jsonValue["CertificateString"] = ""; 276 const std::string *value = 277 std::get_if<std::string>(&property.second); 278 if (value) 279 { 280 asyncResp->res.jsonValue["CertificateString"] = *value; 281 } 282 } 283 else if (property.first == "KeyUsage") 284 { 285 nlohmann::json &keyUsage = 286 asyncResp->res.jsonValue["KeyUsage"]; 287 keyUsage = nlohmann::json::array(); 288 const std::vector<std::string> *value = 289 std::get_if<std::vector<std::string>>(&property.second); 290 if (value) 291 { 292 for (const std::string &usage : *value) 293 { 294 keyUsage.push_back(usage); 295 } 296 } 297 } 298 else if (property.first == "Issuer") 299 { 300 const std::string *value = 301 std::get_if<std::string>(&property.second); 302 if (value) 303 { 304 updateCertIssuerOrSubject( 305 asyncResp->res.jsonValue["Issuer"], *value); 306 } 307 } 308 else if (property.first == "Subject") 309 { 310 const std::string *value = 311 std::get_if<std::string>(&property.second); 312 if (value) 313 { 314 updateCertIssuerOrSubject( 315 asyncResp->res.jsonValue["Subject"], *value); 316 } 317 } 318 else if (property.first == "ValidNotAfter") 319 { 320 const uint64_t *value = 321 std::get_if<uint64_t>(&property.second); 322 if (value) 323 { 324 std::time_t time = static_cast<std::time_t>(*value); 325 asyncResp->res.jsonValue["ValidNotAfter"] = 326 crow::utility::getDateTime(time); 327 } 328 } 329 else if (property.first == "ValidNotBefore") 330 { 331 const uint64_t *value = 332 std::get_if<uint64_t>(&property.second); 333 if (value) 334 { 335 std::time_t time = static_cast<std::time_t>(*value); 336 asyncResp->res.jsonValue["ValidNotBefore"] = 337 crow::utility::getDateTime(time); 338 } 339 } 340 } 341 asyncResp->res.addHeader("Location", certURL); 342 }, 343 service, objectPath, certs::dbusPropIntf, "GetAll", 344 certs::certPropIntf); 345 } 346 347 using GetObjectType = 348 std::vector<std::pair<std::string, std::vector<std::string>>>; 349 350 /** 351 * Action to replace an existing certificate 352 */ 353 class CertificateActionsReplaceCertificate : public Node 354 { 355 public: 356 CertificateActionsReplaceCertificate(CrowApp &app) : 357 Node(app, "/redfish/v1/CertificateService/Actions/" 358 "CertificateService.ReplaceCertificate/") 359 { 360 entityPrivileges = { 361 {boost::beast::http::verb::get, {{"Login"}}}, 362 {boost::beast::http::verb::head, {{"Login"}}}, 363 {boost::beast::http::verb::patch, {{"ConfigureComponents"}}}, 364 {boost::beast::http::verb::put, {{"ConfigureComponents"}}}, 365 {boost::beast::http::verb::delete_, {{"ConfigureComponents"}}}, 366 {boost::beast::http::verb::post, {{"ConfigureComponents"}}}}; 367 } 368 369 private: 370 void doPost(crow::Response &res, const crow::Request &req, 371 const std::vector<std::string> ¶ms) override 372 { 373 std::string certificate; 374 nlohmann::json certificateUri; 375 std::optional<std::string> certificateType = "PEM"; 376 auto asyncResp = std::make_shared<AsyncResp>(res); 377 if (!json_util::readJson(req, asyncResp->res, "CertificateString", 378 certificate, "CertificateUri", certificateUri, 379 "CertificateType", certificateType)) 380 { 381 BMCWEB_LOG_ERROR << "Required parameters are missing"; 382 messages::internalError(asyncResp->res); 383 return; 384 } 385 386 if (!certificateType) 387 { 388 // should never happen, but it never hurts to be paranoid. 389 return; 390 } 391 if (certificateType != "PEM") 392 { 393 messages::actionParameterNotSupported( 394 asyncResp->res, "CertificateType", "ReplaceCertificate"); 395 return; 396 } 397 398 std::string certURI; 399 if (!redfish::json_util::readJson(certificateUri, asyncResp->res, 400 "@odata.id", certURI)) 401 { 402 messages::actionParameterMissing( 403 asyncResp->res, "ReplaceCertificate", "CertificateUri"); 404 return; 405 } 406 407 BMCWEB_LOG_INFO << "Certificate URI to replace" << certURI; 408 long id = getIDFromURL(certURI); 409 if (id < 0) 410 { 411 messages::actionParameterValueFormatError(asyncResp->res, certURI, 412 "CertificateUri", 413 "ReplaceCertificate"); 414 return; 415 } 416 std::string objectPath; 417 std::string name; 418 std::string service; 419 if (boost::starts_with( 420 certURI, 421 "/redfish/v1/Managers/bmc/NetworkProtocol/HTTPS/Certificates/")) 422 { 423 objectPath = 424 std::string(certs::httpsObjectPath) + "/" + std::to_string(id); 425 name = "HTTPS certificate"; 426 service = certs::httpsServiceName; 427 } 428 else if (boost::starts_with( 429 certURI, "/redfish/v1/AccountService/LDAP/Certificates/")) 430 { 431 objectPath = 432 std::string(certs::ldapObjectPath) + "/" + std::to_string(id); 433 name = "LDAP certificate"; 434 service = certs::ldapServiceName; 435 } 436 else 437 { 438 messages::actionParameterNotSupported( 439 asyncResp->res, "CertificateUri", "ReplaceCertificate"); 440 return; 441 } 442 443 std::shared_ptr<CertificateFile> certFile = 444 std::make_shared<CertificateFile>(certificate); 445 crow::connections::systemBus->async_method_call( 446 [asyncResp, certFile, objectPath, service, certURI, id, 447 name](const boost::system::error_code ec) { 448 if (ec) 449 { 450 BMCWEB_LOG_ERROR << "DBUS response error: " << ec; 451 messages::internalError(asyncResp->res); 452 return; 453 } 454 getCertificateProperties(asyncResp, objectPath, service, id, 455 certURI, name); 456 BMCWEB_LOG_DEBUG << "HTTPS certificate install file=" 457 << certFile->getCertFilePath(); 458 }, 459 service, objectPath, certs::certReplaceIntf, "Replace", 460 certFile->getCertFilePath()); 461 } 462 }; // CertificateActionsReplaceCertificate 463 464 /** 465 * Certificate resource describes a certificate used to prove the identity 466 * of a component, account or service. 467 */ 468 class HTTPSCertificate : public Node 469 { 470 public: 471 template <typename CrowApp> 472 HTTPSCertificate(CrowApp &app) : 473 Node(app, 474 "/redfish/v1/Managers/bmc/NetworkProtocol/HTTPS/Certificates/" 475 "<str>/", 476 std::string()) 477 { 478 entityPrivileges = { 479 {boost::beast::http::verb::get, {{"Login"}}}, 480 {boost::beast::http::verb::head, {{"Login"}}}, 481 {boost::beast::http::verb::patch, {{"ConfigureComponents"}}}, 482 {boost::beast::http::verb::put, {{"ConfigureComponents"}}}, 483 {boost::beast::http::verb::delete_, {{"ConfigureComponents"}}}, 484 {boost::beast::http::verb::post, {{"ConfigureComponents"}}}}; 485 } 486 487 void doGet(crow::Response &res, const crow::Request &req, 488 const std::vector<std::string> ¶ms) override 489 { 490 auto asyncResp = std::make_shared<AsyncResp>(res); 491 if (params.size() != 1) 492 { 493 messages::internalError(asyncResp->res); 494 return; 495 } 496 long id = getIDFromURL(req.url); 497 498 BMCWEB_LOG_DEBUG << "HTTPSCertificate::doGet ID=" << std::to_string(id); 499 std::string certURL = 500 "/redfish/v1/Managers/bmc/NetworkProtocol/HTTPS/Certificates/" + 501 std::to_string(id); 502 std::string objectPath = certs::httpsObjectPath; 503 objectPath += "/"; 504 objectPath += std::to_string(id); 505 getCertificateProperties(asyncResp, objectPath, certs::httpsServiceName, 506 id, certURL, "HTTPS Certificate"); 507 } 508 509 }; // namespace redfish 510 511 /** 512 * Collection of HTTPS certificates 513 */ 514 class HTTPSCertificateCollection : public Node 515 { 516 public: 517 template <typename CrowApp> 518 HTTPSCertificateCollection(CrowApp &app) : 519 Node(app, 520 "/redfish/v1/Managers/bmc/NetworkProtocol/HTTPS/Certificates/") 521 { 522 entityPrivileges = { 523 {boost::beast::http::verb::get, {{"Login"}}}, 524 {boost::beast::http::verb::head, {{"Login"}}}, 525 {boost::beast::http::verb::patch, {{"ConfigureComponents"}}}, 526 {boost::beast::http::verb::put, {{"ConfigureComponents"}}}, 527 {boost::beast::http::verb::delete_, {{"ConfigureComponents"}}}, 528 {boost::beast::http::verb::post, {{"ConfigureComponents"}}}}; 529 } 530 void doGet(crow::Response &res, const crow::Request &req, 531 const std::vector<std::string> ¶ms) override 532 { 533 res.jsonValue = { 534 {"@odata.id", 535 "/redfish/v1/Managers/bmc/NetworkProtocol/HTTPS/Certificates"}, 536 {"@odata.type", "#CertificateCollection.CertificateCollection"}, 537 {"@odata.context", 538 "/redfish/v1/" 539 "$metadata#CertificateCollection.CertificateCollection"}, 540 {"Name", "HTTPS Certificates Collection"}, 541 {"Description", "A Collection of HTTPS certificate instances"}}; 542 auto asyncResp = std::make_shared<AsyncResp>(res); 543 crow::connections::systemBus->async_method_call( 544 [asyncResp](const boost::system::error_code ec, 545 const ManagedObjectType &certs) { 546 if (ec) 547 { 548 BMCWEB_LOG_ERROR << "DBUS response error: " << ec; 549 messages::internalError(asyncResp->res); 550 return; 551 } 552 nlohmann::json &members = asyncResp->res.jsonValue["Members"]; 553 members = nlohmann::json::array(); 554 for (const auto &cert : certs) 555 { 556 long id = getIDFromURL(cert.first.str); 557 if (id >= 0) 558 { 559 members.push_back( 560 {{"@odata.id", 561 "/redfish/v1/Managers/bmc/" 562 "NetworkProtocol/HTTPS/Certificates/" + 563 std::to_string(id)}}); 564 } 565 } 566 asyncResp->res.jsonValue["Members@odata.count"] = 567 members.size(); 568 }, 569 certs::httpsServiceName, certs::httpsObjectPath, 570 certs::dbusObjManagerIntf, "GetManagedObjects"); 571 } 572 573 void doPost(crow::Response &res, const crow::Request &req, 574 const std::vector<std::string> ¶ms) override 575 { 576 BMCWEB_LOG_DEBUG << "HTTPSCertificateCollection::doPost"; 577 auto asyncResp = std::make_shared<AsyncResp>(res); 578 asyncResp->res.jsonValue = {{"Name", "HTTPS Certificate"}, 579 {"Description", "HTTPS Certificate"}}; 580 581 std::shared_ptr<CertificateFile> certFile = 582 std::make_shared<CertificateFile>(req.body); 583 584 crow::connections::systemBus->async_method_call( 585 [asyncResp, certFile](const boost::system::error_code ec) { 586 if (ec) 587 { 588 BMCWEB_LOG_ERROR << "DBUS response error: " << ec; 589 messages::internalError(asyncResp->res); 590 return; 591 } 592 // TODO: Issue#84 supporting only 1 certificate 593 long certId = 1; 594 std::string certURL = 595 "/redfish/v1/Managers/bmc/NetworkProtocol/HTTPS/" 596 "Certificates/" + 597 std::to_string(certId); 598 std::string objectPath = std::string(certs::httpsObjectPath) + 599 "/" + std::to_string(certId); 600 getCertificateProperties(asyncResp, objectPath, 601 certs::httpsServiceName, certId, 602 certURL, "HTTPS Certificate"); 603 BMCWEB_LOG_DEBUG << "HTTPS certificate install file=" 604 << certFile->getCertFilePath(); 605 }, 606 certs::httpsServiceName, certs::httpsObjectPath, 607 certs::certInstallIntf, "Install", certFile->getCertFilePath()); 608 } 609 }; // HTTPSCertificateCollection 610 611 /** 612 * The certificate location schema defines a resource that an administrator 613 * can use in order to locate all certificates installed on a given service. 614 */ 615 class CertificateLocations : public Node 616 { 617 public: 618 template <typename CrowApp> 619 CertificateLocations(CrowApp &app) : 620 Node(app, "/redfish/v1/CertificateService/CertificateLocations/") 621 { 622 entityPrivileges = { 623 {boost::beast::http::verb::get, {{"Login"}}}, 624 {boost::beast::http::verb::head, {{"Login"}}}, 625 {boost::beast::http::verb::patch, {{"ConfigureComponents"}}}, 626 {boost::beast::http::verb::put, {{"ConfigureComponents"}}}, 627 {boost::beast::http::verb::delete_, {{"ConfigureComponents"}}}, 628 {boost::beast::http::verb::post, {{"ConfigureComponents"}}}}; 629 } 630 631 private: 632 void doGet(crow::Response &res, const crow::Request &req, 633 const std::vector<std::string> ¶ms) override 634 { 635 res.jsonValue = { 636 {"@odata.id", 637 "/redfish/v1/CertificateService/CertificateLocations"}, 638 {"@odata.type", 639 "#CertificateLocations.v1_0_0.CertificateLocations"}, 640 {"@odata.context", 641 "/redfish/v1/$metadata#CertificateLocations.CertificateLocations"}, 642 {"Name", "Certificate Locations"}, 643 {"Id", "CertificateLocations"}, 644 {"Description", 645 "Defines a resource that an administrator can use in order to " 646 "locate all certificates installed on a given service"}}; 647 auto asyncResp = std::make_shared<AsyncResp>(res); 648 nlohmann::json &links = 649 asyncResp->res.jsonValue["Links"]["Certificates"]; 650 links = nlohmann::json::array(); 651 getCertificateLocations( 652 asyncResp, 653 "/redfish/v1/Managers/bmc/NetworkProtocol/HTTPS/Certificates/", 654 certs::httpsObjectPath, certs::httpsServiceName); 655 getCertificateLocations(asyncResp, 656 "/redfish/v1/AccountService/LDAP/Certificates/", 657 certs::ldapObjectPath, certs::ldapServiceName); 658 } 659 /** 660 * @brief Retrieve the certificates installed list and append to the 661 * response 662 * 663 * @param[in] asyncResp Shared pointer to the response message 664 * @param[in] certURL Path of the certificate object 665 * @param[in] path Path of the D-Bus service object 666 * @return None 667 */ 668 void getCertificateLocations(std::shared_ptr<AsyncResp> &asyncResp, 669 const std::string &certURL, 670 const std::string &path, 671 const std::string &service) 672 { 673 BMCWEB_LOG_DEBUG << "getCertificateLocations URI=" << certURL 674 << " Path=" << path << " service= " << service; 675 crow::connections::systemBus->async_method_call( 676 [asyncResp, certURL](const boost::system::error_code ec, 677 const ManagedObjectType &certs) { 678 if (ec) 679 { 680 BMCWEB_LOG_ERROR << "DBUS response error: " << ec; 681 messages::internalError(asyncResp->res); 682 return; 683 } 684 nlohmann::json &links = 685 asyncResp->res.jsonValue["Links"]["Certificates"]; 686 for (auto &cert : certs) 687 { 688 long id = getIDFromURL(cert.first.str); 689 if (id >= 0) 690 { 691 links.push_back( 692 {{"@odata.id", certURL + std::to_string(id)}}); 693 } 694 } 695 asyncResp->res.jsonValue["Links"]["Certificates@odata.count"] = 696 links.size(); 697 }, 698 service, path, certs::dbusObjManagerIntf, "GetManagedObjects"); 699 } 700 }; // CertificateLocations 701 702 /** 703 * Collection of LDAP certificates 704 */ 705 class LDAPCertificateCollection : public Node 706 { 707 public: 708 template <typename CrowApp> 709 LDAPCertificateCollection(CrowApp &app) : 710 Node(app, "/redfish/v1/AccountService/LDAP/Certificates/") 711 { 712 entityPrivileges = { 713 {boost::beast::http::verb::get, {{"Login"}}}, 714 {boost::beast::http::verb::head, {{"Login"}}}, 715 {boost::beast::http::verb::patch, {{"ConfigureComponents"}}}, 716 {boost::beast::http::verb::put, {{"ConfigureComponents"}}}, 717 {boost::beast::http::verb::delete_, {{"ConfigureComponents"}}}, 718 {boost::beast::http::verb::post, {{"ConfigureComponents"}}}}; 719 } 720 void doGet(crow::Response &res, const crow::Request &req, 721 const std::vector<std::string> ¶ms) override 722 { 723 res.jsonValue = { 724 {"@odata.id", "/redfish/v1/AccountService/LDAP/Certificates"}, 725 {"@odata.type", "#CertificateCollection.CertificateCollection"}, 726 {"@odata.context", 727 "/redfish/v1/" 728 "$metadata#CertificateCollection.CertificateCollection"}, 729 {"Name", "LDAP Certificates Collection"}, 730 {"Description", "A Collection of LDAP certificate instances"}}; 731 auto asyncResp = std::make_shared<AsyncResp>(res); 732 crow::connections::systemBus->async_method_call( 733 [asyncResp](const boost::system::error_code ec, 734 const ManagedObjectType &certs) { 735 if (ec) 736 { 737 BMCWEB_LOG_ERROR << "DBUS response error: " << ec; 738 messages::internalError(asyncResp->res); 739 return; 740 } 741 nlohmann::json &members = asyncResp->res.jsonValue["Members"]; 742 members = nlohmann::json::array(); 743 for (const auto &cert : certs) 744 { 745 long id = getIDFromURL(cert.first.str); 746 if (id >= 0) 747 { 748 members.push_back( 749 {{"@odata.id", "/redfish/v1/AccountService/" 750 "LDAP/Certificates/" + 751 std::to_string(id)}}); 752 } 753 } 754 asyncResp->res.jsonValue["Members@odata.count"] = 755 members.size(); 756 }, 757 certs::ldapServiceName, certs::ldapObjectPath, 758 certs::dbusObjManagerIntf, "GetManagedObjects"); 759 } 760 761 void doPost(crow::Response &res, const crow::Request &req, 762 const std::vector<std::string> ¶ms) override 763 { 764 std::shared_ptr<CertificateFile> certFile = 765 std::make_shared<CertificateFile>(req.body); 766 auto asyncResp = std::make_shared<AsyncResp>(res); 767 crow::connections::systemBus->async_method_call( 768 [asyncResp, certFile](const boost::system::error_code ec) { 769 if (ec) 770 { 771 BMCWEB_LOG_ERROR << "DBUS response error: " << ec; 772 messages::internalError(asyncResp->res); 773 return; 774 } 775 //// TODO: Issue#84 supporting only 1 certificate 776 long certId = 1; 777 std::string certURL = 778 "/redfish/v1/AccountService/LDAP/Certificates/" + 779 std::to_string(certId); 780 std::string objectPath = std::string(certs::ldapObjectPath) + 781 "/" + std::to_string(certId); 782 getCertificateProperties(asyncResp, objectPath, 783 certs::ldapServiceName, certId, 784 certURL, "LDAP Certificate"); 785 BMCWEB_LOG_DEBUG << "LDAP certificate install file=" 786 << certFile->getCertFilePath(); 787 }, 788 certs::ldapServiceName, certs::ldapObjectPath, 789 certs::certInstallIntf, "Install", certFile->getCertFilePath()); 790 } 791 }; // LDAPCertificateCollection 792 793 /** 794 * Certificate resource describes a certificate used to prove the identity 795 * of a component, account or service. 796 */ 797 class LDAPCertificate : public Node 798 { 799 public: 800 template <typename CrowApp> 801 LDAPCertificate(CrowApp &app) : 802 Node(app, "/redfish/v1/AccountService/LDAP/Certificates/<str>/", 803 std::string()) 804 { 805 entityPrivileges = { 806 {boost::beast::http::verb::get, {{"Login"}}}, 807 {boost::beast::http::verb::head, {{"Login"}}}, 808 {boost::beast::http::verb::patch, {{"ConfigureComponents"}}}, 809 {boost::beast::http::verb::put, {{"ConfigureComponents"}}}, 810 {boost::beast::http::verb::delete_, {{"ConfigureComponents"}}}, 811 {boost::beast::http::verb::post, {{"ConfigureComponents"}}}}; 812 } 813 814 void doGet(crow::Response &res, const crow::Request &req, 815 const std::vector<std::string> ¶ms) override 816 { 817 auto asyncResp = std::make_shared<AsyncResp>(res); 818 long id = getIDFromURL(req.url); 819 if (id < 0) 820 { 821 BMCWEB_LOG_ERROR << "Invalid url value" << req.url; 822 messages::internalError(asyncResp->res); 823 return; 824 } 825 BMCWEB_LOG_DEBUG << "LDAP Certificate ID=" << std::to_string(id); 826 std::string certURL = "/redfish/v1/AccountService/LDAP/Certificates/" + 827 std::to_string(id); 828 std::string objectPath = certs::ldapObjectPath; 829 objectPath += "/"; 830 objectPath += std::to_string(id); 831 getCertificateProperties(asyncResp, objectPath, certs::ldapServiceName, 832 id, certURL, "LDAP Certificate"); 833 } 834 }; // LDAPCertificate 835 } // namespace redfish 836