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