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