blob: 111bcec598c4136ad655c9e8ee90e7459257fc4b [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
Kowalski, Kamil58eb2382019-08-12 11:54:31 +0200120std::string
121 getCertificateFromReqBody(const std::shared_ptr<AsyncResp> &asyncResp,
122 const crow::Request &req)
123{
124 nlohmann::json reqJson = nlohmann::json::parse(req.body, nullptr, false);
125
126 if (reqJson.is_discarded())
127 {
128 // We did not receive JSON request, proceed as it is RAW data
129 return req.body;
130 }
131
132 std::string certificate;
133 std::optional<std::string> certificateType = "PEM";
134
135 if (!json_util::readJson(reqJson, asyncResp->res, "CertificateString",
136 certificate, "CertificateType", certificateType))
137 {
138 BMCWEB_LOG_ERROR << "Required parameters are missing";
139 messages::internalError(asyncResp->res);
140 return std::string();
141 }
142
143 if (*certificateType != "PEM")
144 {
145 messages::propertyValueNotInList(asyncResp->res, *certificateType,
146 "CertificateType");
147 return std::string();
148 }
149
150 return certificate;
151}
152
Marri Devender Rao5968cae2019-01-21 10:27:12 -0600153/**
154 * Class to create a temporary certificate file for uploading to system
155 */
156class CertificateFile
157{
158 public:
159 CertificateFile() = delete;
160 CertificateFile(const CertificateFile &) = delete;
161 CertificateFile &operator=(const CertificateFile &) = delete;
162 CertificateFile(CertificateFile &&) = delete;
163 CertificateFile &operator=(CertificateFile &&) = delete;
164 CertificateFile(const std::string &certString)
165 {
166 char dirTemplate[] = "/tmp/Certs.XXXXXX";
167 char *tempDirectory = mkdtemp(dirTemplate);
168 if (tempDirectory)
169 {
170 certDirectory = tempDirectory;
171 certificateFile = certDirectory / "cert.pem";
172 std::ofstream out(certificateFile, std::ofstream::out |
173 std::ofstream::binary |
174 std::ofstream::trunc);
175 out << certString;
176 out.close();
177 BMCWEB_LOG_DEBUG << "Creating certificate file" << certificateFile;
178 }
179 }
180 ~CertificateFile()
181 {
182 if (std::filesystem::exists(certDirectory))
183 {
184 BMCWEB_LOG_DEBUG << "Removing certificate file" << certificateFile;
185 try
186 {
187 std::filesystem::remove_all(certDirectory);
188 }
189 catch (const std::filesystem::filesystem_error &e)
190 {
191 BMCWEB_LOG_ERROR << "Failed to remove temp directory"
192 << certDirectory;
193 }
194 }
195 }
196 std::string getCertFilePath()
197 {
198 return certificateFile;
199 }
200
201 private:
202 std::filesystem::path certificateFile;
203 std::filesystem::path certDirectory;
204};
205
Marri Devender Rao30215812019-03-18 08:59:21 -0500206static std::unique_ptr<sdbusplus::bus::match::match> csrMatcher;
207/**
208 * @brief Read data from CSR D-bus object and set to response
209 *
210 * @param[in] asyncResp Shared pointer to the response message
211 * @param[in] certURI Link to certifiate collection URI
212 * @param[in] service D-Bus service name
213 * @param[in] certObjPath certificate D-Bus object path
214 * @param[in] csrObjPath CSR D-Bus object path
215 * @return None
216 */
217static void getCSR(const std::shared_ptr<AsyncResp> &asyncResp,
218 const std::string &certURI, const std::string &service,
219 const std::string &certObjPath,
220 const std::string &csrObjPath)
221{
222 BMCWEB_LOG_DEBUG << "getCSR CertObjectPath" << certObjPath
223 << " CSRObjectPath=" << csrObjPath
224 << " service=" << service;
225 crow::connections::systemBus->async_method_call(
226 [asyncResp, certURI](const boost::system::error_code ec,
227 const std::string &csr) {
228 if (ec)
229 {
230 BMCWEB_LOG_ERROR << "DBUS response error: " << ec;
231 messages::internalError(asyncResp->res);
232 return;
233 }
234 if (csr.empty())
235 {
236 BMCWEB_LOG_ERROR << "CSR read is empty";
237 messages::internalError(asyncResp->res);
238 return;
239 }
240 asyncResp->res.jsonValue["CSRString"] = csr;
241 asyncResp->res.jsonValue["CertificateCollection"] = {
242 {"@odata.id", certURI}};
243 },
244 service, csrObjPath, "xyz.openbmc_project.Certs.CSR", "CSR");
245}
246
247/**
248 * Action to Generate CSR
249 */
250class CertificateActionGenerateCSR : public Node
251{
252 public:
253 CertificateActionGenerateCSR(CrowApp &app) :
254 Node(app, "/redfish/v1/CertificateService/Actions/"
255 "CertificateService.GenerateCSR/")
256 {
257 entityPrivileges = {
258 {boost::beast::http::verb::get, {{"Login"}}},
259 {boost::beast::http::verb::head, {{"Login"}}},
260 {boost::beast::http::verb::patch, {{"ConfigureComponents"}}},
261 {boost::beast::http::verb::put, {{"ConfigureComponents"}}},
262 {boost::beast::http::verb::delete_, {{"ConfigureComponents"}}},
263 {boost::beast::http::verb::post, {{"ConfigureComponents"}}}};
264 }
265
266 private:
267 void doPost(crow::Response &res, const crow::Request &req,
268 const std::vector<std::string> &params) override
269 {
270 static const int RSA_KEY_BIT_LENGTH = 2048;
271 auto asyncResp = std::make_shared<AsyncResp>(res);
272 // Required parameters
273 std::string city;
274 std::string commonName;
275 std::string country;
276 std::string organization;
277 std::string organizationalUnit;
278 std::string state;
279 nlohmann::json certificateCollection;
280
281 // Optional parameters
282 std::optional<std::vector<std::string>> optAlternativeNames =
283 std::vector<std::string>();
284 std::optional<std::string> optContactPerson = "";
285 std::optional<std::string> optChallengePassword = "";
286 std::optional<std::string> optEmail = "";
287 std::optional<std::string> optGivenName = "";
288 std::optional<std::string> optInitials = "";
289 std::optional<int64_t> optKeyBitLength = RSA_KEY_BIT_LENGTH;
290 std::optional<std::string> optKeyCurveId = "prime256v1";
291 std::optional<std::string> optKeyPairAlgorithm = "EC";
292 std::optional<std::vector<std::string>> optKeyUsage =
293 std::vector<std::string>();
294 std::optional<std::string> optSurname = "";
295 std::optional<std::string> optUnstructuredName = "";
296 if (!json_util::readJson(
297 req, asyncResp->res, "City", city, "CommonName", commonName,
298 "ContactPerson", optContactPerson, "Country", country,
299 "Organization", organization, "OrganizationalUnit",
300 organizationalUnit, "State", state, "CertificateCollection",
301 certificateCollection, "AlternativeNames", optAlternativeNames,
302 "ChallengePassword", optChallengePassword, "Email", optEmail,
303 "GivenName", optGivenName, "Initials", optInitials,
304 "KeyBitLength", optKeyBitLength, "KeyCurveId", optKeyCurveId,
305 "KeyPairAlgorithm", optKeyPairAlgorithm, "KeyUsage",
306 optKeyUsage, "Surname", optSurname, "UnstructuredName",
307 optUnstructuredName))
308 {
309 return;
310 }
311
312 // bmcweb has no way to store or decode a private key challenge
313 // password, which will likely cause bmcweb to crash on startup if this
314 // is not set on a post so not allowing the user to set value
315 if (*optChallengePassword != "")
316 {
317 messages::actionParameterNotSupported(asyncResp->res, "GenerateCSR",
318 "ChallengePassword");
319 return;
320 }
321
322 std::string certURI;
323 if (!redfish::json_util::readJson(certificateCollection, asyncResp->res,
324 "@odata.id", certURI))
325 {
326 return;
327 }
328
329 std::string objectPath;
330 std::string service;
331 if (boost::starts_with(
332 certURI,
333 "/redfish/v1/Managers/bmc/NetworkProtocol/HTTPS/Certificates"))
334 {
335 objectPath = certs::httpsObjectPath;
336 service = certs::httpsServiceName;
337 }
Marri Devender Rao3b7f0142019-05-17 02:53:23 -0500338 else if (boost::starts_with(
339 certURI, "/redfish/v1/AccountService/LDAP/Certificates"))
340 {
341 objectPath = certs::ldapObjectPath;
342 service = certs::ldapServiceName;
343 }
Marri Devender Rao30215812019-03-18 08:59:21 -0500344 else
345 {
346 messages::actionParameterNotSupported(
347 asyncResp->res, "CertificateCollection", "GenerateCSR");
348 return;
349 }
350
351 // supporting only EC and RSA algorithm
352 if (*optKeyPairAlgorithm != "EC" && *optKeyPairAlgorithm != "RSA")
353 {
354 messages::actionParameterNotSupported(
355 asyncResp->res, "KeyPairAlgorithm", "GenerateCSR");
356 return;
357 }
358
359 // supporting only 2048 key bit length for RSA algorithm due to time
360 // consumed in generating private key
361 if (*optKeyPairAlgorithm == "RSA" &&
362 *optKeyBitLength != RSA_KEY_BIT_LENGTH)
363 {
364 messages::propertyValueNotInList(asyncResp->res,
365 std::to_string(*optKeyBitLength),
366 "KeyBitLength");
367 return;
368 }
369
370 // validate KeyUsage supporting only 1 type based on URL
371 if (boost::starts_with(
372 certURI,
373 "/redfish/v1/Managers/bmc/NetworkProtocol/HTTPS/Certificates"))
374 {
375 if (optKeyUsage->size() == 0)
376 {
377 optKeyUsage->push_back("ServerAuthentication");
378 }
379 else if (optKeyUsage->size() == 1)
380 {
381 if ((*optKeyUsage)[0] != "ServerAuthentication")
382 {
383 messages::propertyValueNotInList(
384 asyncResp->res, (*optKeyUsage)[0], "KeyUsage");
385 return;
386 }
387 }
388 else
389 {
390 messages::actionParameterNotSupported(
391 asyncResp->res, "KeyUsage", "GenerateCSR");
392 return;
393 }
394 }
Marri Devender Rao3b7f0142019-05-17 02:53:23 -0500395 else if (boost::starts_with(
396 certURI, "/redfish/v1/AccountService/LDAP/Certificates"))
397 {
398 if (optKeyUsage->size() == 0)
399 {
400 optKeyUsage->push_back("ClientAuthentication");
401 }
402 else if (optKeyUsage->size() == 1)
403 {
404 if ((*optKeyUsage)[0] != "ClientAuthentication")
405 {
406 messages::propertyValueNotInList(
407 asyncResp->res, (*optKeyUsage)[0], "KeyUsage");
408 return;
409 }
410 }
411 else
412 {
413 messages::actionParameterNotSupported(
414 asyncResp->res, "KeyUsage", "GenerateCSR");
415 return;
416 }
417 }
Marri Devender Rao30215812019-03-18 08:59:21 -0500418
419 // Only allow one CSR matcher at a time so setting retry time-out and
420 // timer expiry to 10 seconds for now.
421 static const int TIME_OUT = 10;
422 if (csrMatcher)
423 {
424 res.addHeader("Retry-After", std::to_string(TIME_OUT));
425 messages::serviceTemporarilyUnavailable(asyncResp->res,
426 std::to_string(TIME_OUT));
427 return;
428 }
429
430 // Make this static so it survives outside this method
431 static boost::asio::steady_timer timeout(*req.ioService);
432 timeout.expires_after(std::chrono::seconds(TIME_OUT));
433 timeout.async_wait([asyncResp](const boost::system::error_code &ec) {
434 csrMatcher = nullptr;
435 if (ec)
436 {
437 // operation_aborted is expected if timer is canceled before
438 // completion.
439 if (ec != boost::asio::error::operation_aborted)
440 {
441 BMCWEB_LOG_ERROR << "Async_wait failed " << ec;
442 }
443 return;
444 }
445 BMCWEB_LOG_ERROR << "Timed out waiting for Generating CSR";
446 messages::internalError(asyncResp->res);
447 });
448
449 // create a matcher to wait on CSR object
450 BMCWEB_LOG_DEBUG << "create matcher with path " << objectPath;
451 std::string match("type='signal',"
452 "interface='org.freedesktop.DBus.ObjectManager',"
453 "path='" +
454 objectPath +
455 "',"
456 "member='InterfacesAdded'");
457 csrMatcher = std::make_unique<sdbusplus::bus::match::match>(
458 *crow::connections::systemBus, match,
459 [asyncResp, service, objectPath,
460 certURI](sdbusplus::message::message &m) {
461 boost::system::error_code ec;
462 timeout.cancel(ec);
463 if (ec)
464 {
465 BMCWEB_LOG_ERROR << "error canceling timer " << ec;
466 csrMatcher = nullptr;
467 }
468 if (m.is_method_error())
469 {
470 BMCWEB_LOG_ERROR << "Dbus method error!!!";
471 messages::internalError(asyncResp->res);
472 return;
473 }
474 std::vector<std::pair<
475 std::string, std::vector<std::pair<
476 std::string, std::variant<std::string>>>>>
477 interfacesProperties;
478 sdbusplus::message::object_path csrObjectPath;
479 m.read(csrObjectPath, interfacesProperties);
480 BMCWEB_LOG_DEBUG << "CSR object added" << csrObjectPath.str;
481 for (auto &interface : interfacesProperties)
482 {
483 if (interface.first == "xyz.openbmc_project.Certs.CSR")
484 {
485 getCSR(asyncResp, certURI, service, objectPath,
486 csrObjectPath.str);
487 break;
488 }
489 }
490 });
491 crow::connections::systemBus->async_method_call(
492 [asyncResp](const boost::system::error_code ec,
493 const std::string &path) {
494 if (ec)
495 {
496 BMCWEB_LOG_ERROR << "DBUS response error: " << ec.message();
497 messages::internalError(asyncResp->res);
498 return;
499 }
500 },
501 service, objectPath, "xyz.openbmc_project.Certs.CSR.Create",
502 "GenerateCSR", *optAlternativeNames, *optChallengePassword, city,
503 commonName, *optContactPerson, country, *optEmail, *optGivenName,
504 *optInitials, *optKeyBitLength, *optKeyCurveId,
505 *optKeyPairAlgorithm, *optKeyUsage, organization,
506 organizationalUnit, state, *optSurname, *optUnstructuredName);
507 }
508}; // CertificateActionGenerateCSR
509
Marri Devender Rao5968cae2019-01-21 10:27:12 -0600510/**
511 * @brief Parse and update Certficate Issue/Subject property
512 *
513 * @param[in] asyncResp Shared pointer to the response message
514 * @param[in] str Issuer/Subject value in key=value pairs
515 * @param[in] type Issuer/Subject
516 * @return None
517 */
518static void updateCertIssuerOrSubject(nlohmann::json &out,
519 const std::string_view value)
520{
521 // example: O=openbmc-project.xyz,CN=localhost
522 std::string_view::iterator i = value.begin();
523 while (i != value.end())
524 {
525 std::string_view::iterator tokenBegin = i;
526 while (i != value.end() && *i != '=')
527 {
528 i++;
529 }
530 if (i == value.end())
531 {
532 break;
533 }
534 const std::string_view key(tokenBegin, i - tokenBegin);
535 i++;
536 tokenBegin = i;
537 while (i != value.end() && *i != ',')
538 {
539 i++;
540 }
541 const std::string_view val(tokenBegin, i - tokenBegin);
542 if (key == "L")
543 {
544 out["City"] = val;
545 }
546 else if (key == "CN")
547 {
548 out["CommonName"] = val;
549 }
550 else if (key == "C")
551 {
552 out["Country"] = val;
553 }
554 else if (key == "O")
555 {
556 out["Organization"] = val;
557 }
558 else if (key == "OU")
559 {
560 out["OrganizationalUnit"] = val;
561 }
562 else if (key == "ST")
563 {
564 out["State"] = val;
565 }
566 // skip comma character
567 if (i != value.end())
568 {
569 i++;
570 }
571 }
572}
573
574/**
575 * @brief Retrieve the certificates properties and append to the response
576 * message
577 *
578 * @param[in] asyncResp Shared pointer to the response message
579 * @param[in] objectPath Path of the D-Bus service object
580 * @param[in] certId Id of the certificate
581 * @param[in] certURL URL of the certificate object
582 * @param[in] name name of the certificate
583 * @return None
584 */
585static void getCertificateProperties(
586 const std::shared_ptr<AsyncResp> &asyncResp, const std::string &objectPath,
Marri Devender Rao37cce912019-02-20 01:05:22 -0600587 const std::string &service, long certId, const std::string &certURL,
588 const std::string &name)
Marri Devender Rao5968cae2019-01-21 10:27:12 -0600589{
590 using PropertyType =
591 std::variant<std::string, uint64_t, std::vector<std::string>>;
592 using PropertiesMap = boost::container::flat_map<std::string, PropertyType>;
593 BMCWEB_LOG_DEBUG << "getCertificateProperties Path=" << objectPath
594 << " certId=" << certId << " certURl=" << certURL;
595 crow::connections::systemBus->async_method_call(
Marri Devender Rao37cce912019-02-20 01:05:22 -0600596 [asyncResp, certURL, certId, name](const boost::system::error_code ec,
597 const PropertiesMap &properties) {
Marri Devender Rao5968cae2019-01-21 10:27:12 -0600598 if (ec)
599 {
600 BMCWEB_LOG_ERROR << "DBUS response error: " << ec;
Marri Devender Rao8aae75a2019-07-16 03:26:50 -0500601 messages::resourceNotFound(asyncResp->res, name,
602 std::to_string(certId));
Marri Devender Rao5968cae2019-01-21 10:27:12 -0600603 return;
604 }
Marri Devender Rao37cce912019-02-20 01:05:22 -0600605 asyncResp->res.jsonValue = {
606 {"@odata.id", certURL},
607 {"@odata.type", "#Certificate.v1_0_0.Certificate"},
608 {"@odata.context",
609 "/redfish/v1/$metadata#Certificate.Certificate"},
610 {"Id", std::to_string(certId)},
611 {"Name", name},
612 {"Description", name}};
613 for (const auto &property : properties)
Marri Devender Rao5968cae2019-01-21 10:27:12 -0600614 {
Marri Devender Rao37cce912019-02-20 01:05:22 -0600615 if (property.first == "CertificateString")
616 {
617 asyncResp->res.jsonValue["CertificateString"] = "";
618 const std::string *value =
619 std::get_if<std::string>(&property.second);
620 if (value)
621 {
622 asyncResp->res.jsonValue["CertificateString"] = *value;
623 }
624 }
625 else if (property.first == "KeyUsage")
626 {
627 nlohmann::json &keyUsage =
628 asyncResp->res.jsonValue["KeyUsage"];
629 keyUsage = nlohmann::json::array();
630 const std::vector<std::string> *value =
631 std::get_if<std::vector<std::string>>(&property.second);
632 if (value)
633 {
634 for (const std::string &usage : *value)
635 {
636 keyUsage.push_back(usage);
637 }
638 }
639 }
640 else if (property.first == "Issuer")
641 {
642 const std::string *value =
643 std::get_if<std::string>(&property.second);
644 if (value)
645 {
646 updateCertIssuerOrSubject(
647 asyncResp->res.jsonValue["Issuer"], *value);
648 }
649 }
650 else if (property.first == "Subject")
651 {
652 const std::string *value =
653 std::get_if<std::string>(&property.second);
654 if (value)
655 {
656 updateCertIssuerOrSubject(
657 asyncResp->res.jsonValue["Subject"], *value);
658 }
659 }
660 else if (property.first == "ValidNotAfter")
661 {
662 const uint64_t *value =
663 std::get_if<uint64_t>(&property.second);
664 if (value)
665 {
666 std::time_t time = static_cast<std::time_t>(*value);
667 asyncResp->res.jsonValue["ValidNotAfter"] =
668 crow::utility::getDateTime(time);
669 }
670 }
671 else if (property.first == "ValidNotBefore")
672 {
673 const uint64_t *value =
674 std::get_if<uint64_t>(&property.second);
675 if (value)
676 {
677 std::time_t time = static_cast<std::time_t>(*value);
678 asyncResp->res.jsonValue["ValidNotBefore"] =
679 crow::utility::getDateTime(time);
680 }
681 }
Marri Devender Rao5968cae2019-01-21 10:27:12 -0600682 }
Marri Devender Rao37cce912019-02-20 01:05:22 -0600683 asyncResp->res.addHeader("Location", certURL);
Marri Devender Rao5968cae2019-01-21 10:27:12 -0600684 },
Marri Devender Rao37cce912019-02-20 01:05:22 -0600685 service, objectPath, certs::dbusPropIntf, "GetAll",
686 certs::certPropIntf);
Marri Devender Rao5968cae2019-01-21 10:27:12 -0600687}
688
689using GetObjectType =
690 std::vector<std::pair<std::string, std::vector<std::string>>>;
691
692/**
693 * Action to replace an existing certificate
694 */
695class CertificateActionsReplaceCertificate : public Node
696{
697 public:
698 CertificateActionsReplaceCertificate(CrowApp &app) :
699 Node(app, "/redfish/v1/CertificateService/Actions/"
700 "CertificateService.ReplaceCertificate/")
701 {
702 entityPrivileges = {
703 {boost::beast::http::verb::get, {{"Login"}}},
704 {boost::beast::http::verb::head, {{"Login"}}},
705 {boost::beast::http::verb::patch, {{"ConfigureComponents"}}},
706 {boost::beast::http::verb::put, {{"ConfigureComponents"}}},
707 {boost::beast::http::verb::delete_, {{"ConfigureComponents"}}},
708 {boost::beast::http::verb::post, {{"ConfigureComponents"}}}};
709 }
710
711 private:
712 void doPost(crow::Response &res, const crow::Request &req,
713 const std::vector<std::string> &params) override
714 {
715 std::string certificate;
716 nlohmann::json certificateUri;
717 std::optional<std::string> certificateType = "PEM";
718 auto asyncResp = std::make_shared<AsyncResp>(res);
719 if (!json_util::readJson(req, asyncResp->res, "CertificateString",
720 certificate, "CertificateUri", certificateUri,
721 "CertificateType", certificateType))
722 {
723 BMCWEB_LOG_ERROR << "Required parameters are missing";
724 messages::internalError(asyncResp->res);
725 return;
726 }
727
728 if (!certificateType)
729 {
730 // should never happen, but it never hurts to be paranoid.
731 return;
732 }
733 if (certificateType != "PEM")
734 {
735 messages::actionParameterNotSupported(
736 asyncResp->res, "CertificateType", "ReplaceCertificate");
737 return;
738 }
739
740 std::string certURI;
741 if (!redfish::json_util::readJson(certificateUri, asyncResp->res,
742 "@odata.id", certURI))
743 {
744 messages::actionParameterMissing(
745 asyncResp->res, "ReplaceCertificate", "CertificateUri");
746 return;
747 }
748
Marri Devender Rao5968cae2019-01-21 10:27:12 -0600749 BMCWEB_LOG_INFO << "Certificate URI to replace" << certURI;
750 long id = getIDFromURL(certURI);
751 if (id < 0)
752 {
753 messages::actionParameterValueFormatError(asyncResp->res, certURI,
754 "CertificateUri",
755 "ReplaceCertificate");
756 return;
757 }
758 std::string objectPath;
759 std::string name;
Marri Devender Rao37cce912019-02-20 01:05:22 -0600760 std::string service;
Marri Devender Rao5968cae2019-01-21 10:27:12 -0600761 if (boost::starts_with(
762 certURI,
763 "/redfish/v1/Managers/bmc/NetworkProtocol/HTTPS/Certificates/"))
764 {
765 objectPath =
766 std::string(certs::httpsObjectPath) + "/" + std::to_string(id);
767 name = "HTTPS certificate";
Marri Devender Rao37cce912019-02-20 01:05:22 -0600768 service = certs::httpsServiceName;
769 }
770 else if (boost::starts_with(
771 certURI, "/redfish/v1/AccountService/LDAP/Certificates/"))
772 {
773 objectPath =
774 std::string(certs::ldapObjectPath) + "/" + std::to_string(id);
775 name = "LDAP certificate";
776 service = certs::ldapServiceName;
Marri Devender Rao5968cae2019-01-21 10:27:12 -0600777 }
Marri Devender Raocfcd5f62019-05-17 08:34:37 -0500778 else if (boost::starts_with(
779 certURI,
780 "/redfish/v1/Managers/bmc/Truststore/Certificates/"))
781 {
782 objectPath = std::string(certs::authorityObjectPath) + "/" +
783 std::to_string(id);
784 name = "TrustStore certificate";
785 service = certs::authorityServiceName;
786 }
Marri Devender Rao5968cae2019-01-21 10:27:12 -0600787 else
788 {
789 messages::actionParameterNotSupported(
790 asyncResp->res, "CertificateUri", "ReplaceCertificate");
791 return;
792 }
793
794 std::shared_ptr<CertificateFile> certFile =
795 std::make_shared<CertificateFile>(certificate);
Marri Devender Rao5968cae2019-01-21 10:27:12 -0600796 crow::connections::systemBus->async_method_call(
Marri Devender Rao37cce912019-02-20 01:05:22 -0600797 [asyncResp, certFile, objectPath, service, certURI, id,
798 name](const boost::system::error_code ec) {
Marri Devender Rao5968cae2019-01-21 10:27:12 -0600799 if (ec)
800 {
801 BMCWEB_LOG_ERROR << "DBUS response error: " << ec;
Marri Devender Rao8aae75a2019-07-16 03:26:50 -0500802 messages::resourceNotFound(asyncResp->res, name,
803 std::to_string(id));
Marri Devender Rao5968cae2019-01-21 10:27:12 -0600804 return;
805 }
Marri Devender Rao37cce912019-02-20 01:05:22 -0600806 getCertificateProperties(asyncResp, objectPath, service, id,
807 certURI, name);
808 BMCWEB_LOG_DEBUG << "HTTPS certificate install file="
809 << certFile->getCertFilePath();
Marri Devender Rao5968cae2019-01-21 10:27:12 -0600810 },
Marri Devender Rao37cce912019-02-20 01:05:22 -0600811 service, objectPath, certs::certReplaceIntf, "Replace",
812 certFile->getCertFilePath());
Marri Devender Rao5968cae2019-01-21 10:27:12 -0600813 }
814}; // CertificateActionsReplaceCertificate
815
816/**
817 * Certificate resource describes a certificate used to prove the identity
818 * of a component, account or service.
819 */
820class HTTPSCertificate : public Node
821{
822 public:
823 template <typename CrowApp>
824 HTTPSCertificate(CrowApp &app) :
825 Node(app,
826 "/redfish/v1/Managers/bmc/NetworkProtocol/HTTPS/Certificates/"
827 "<str>/",
828 std::string())
829 {
830 entityPrivileges = {
831 {boost::beast::http::verb::get, {{"Login"}}},
832 {boost::beast::http::verb::head, {{"Login"}}},
833 {boost::beast::http::verb::patch, {{"ConfigureComponents"}}},
834 {boost::beast::http::verb::put, {{"ConfigureComponents"}}},
835 {boost::beast::http::verb::delete_, {{"ConfigureComponents"}}},
836 {boost::beast::http::verb::post, {{"ConfigureComponents"}}}};
837 }
838
839 void doGet(crow::Response &res, const crow::Request &req,
840 const std::vector<std::string> &params) override
841 {
842 auto asyncResp = std::make_shared<AsyncResp>(res);
843 if (params.size() != 1)
844 {
845 messages::internalError(asyncResp->res);
846 return;
847 }
848 long id = getIDFromURL(req.url);
849
850 BMCWEB_LOG_DEBUG << "HTTPSCertificate::doGet ID=" << std::to_string(id);
851 std::string certURL =
852 "/redfish/v1/Managers/bmc/NetworkProtocol/HTTPS/Certificates/" +
853 std::to_string(id);
854 std::string objectPath = certs::httpsObjectPath;
855 objectPath += "/";
856 objectPath += std::to_string(id);
Marri Devender Rao37cce912019-02-20 01:05:22 -0600857 getCertificateProperties(asyncResp, objectPath, certs::httpsServiceName,
858 id, certURL, "HTTPS Certificate");
Marri Devender Rao5968cae2019-01-21 10:27:12 -0600859 }
860
861}; // namespace redfish
862
863/**
864 * Collection of HTTPS certificates
865 */
866class HTTPSCertificateCollection : public Node
867{
868 public:
869 template <typename CrowApp>
870 HTTPSCertificateCollection(CrowApp &app) :
871 Node(app,
872 "/redfish/v1/Managers/bmc/NetworkProtocol/HTTPS/Certificates/")
873 {
874 entityPrivileges = {
875 {boost::beast::http::verb::get, {{"Login"}}},
876 {boost::beast::http::verb::head, {{"Login"}}},
877 {boost::beast::http::verb::patch, {{"ConfigureComponents"}}},
878 {boost::beast::http::verb::put, {{"ConfigureComponents"}}},
879 {boost::beast::http::verb::delete_, {{"ConfigureComponents"}}},
880 {boost::beast::http::verb::post, {{"ConfigureComponents"}}}};
881 }
882 void doGet(crow::Response &res, const crow::Request &req,
883 const std::vector<std::string> &params) override
884 {
885 res.jsonValue = {
886 {"@odata.id",
887 "/redfish/v1/Managers/bmc/NetworkProtocol/HTTPS/Certificates"},
888 {"@odata.type", "#CertificateCollection.CertificateCollection"},
889 {"@odata.context",
890 "/redfish/v1/"
891 "$metadata#CertificateCollection.CertificateCollection"},
892 {"Name", "HTTPS Certificates Collection"},
893 {"Description", "A Collection of HTTPS certificate instances"}};
894 auto asyncResp = std::make_shared<AsyncResp>(res);
895 crow::connections::systemBus->async_method_call(
896 [asyncResp](const boost::system::error_code ec,
Marri Devender Rao37cce912019-02-20 01:05:22 -0600897 const ManagedObjectType &certs) {
Marri Devender Rao5968cae2019-01-21 10:27:12 -0600898 if (ec)
899 {
900 BMCWEB_LOG_ERROR << "DBUS response error: " << ec;
901 messages::internalError(asyncResp->res);
902 return;
903 }
Marri Devender Rao37cce912019-02-20 01:05:22 -0600904 nlohmann::json &members = asyncResp->res.jsonValue["Members"];
905 members = nlohmann::json::array();
906 for (const auto &cert : certs)
Marri Devender Rao5968cae2019-01-21 10:27:12 -0600907 {
Marri Devender Rao37cce912019-02-20 01:05:22 -0600908 long id = getIDFromURL(cert.first.str);
909 if (id >= 0)
910 {
911 members.push_back(
912 {{"@odata.id",
913 "/redfish/v1/Managers/bmc/"
914 "NetworkProtocol/HTTPS/Certificates/" +
915 std::to_string(id)}});
916 }
Marri Devender Rao5968cae2019-01-21 10:27:12 -0600917 }
Marri Devender Rao37cce912019-02-20 01:05:22 -0600918 asyncResp->res.jsonValue["Members@odata.count"] =
919 members.size();
Marri Devender Rao5968cae2019-01-21 10:27:12 -0600920 },
Marri Devender Rao37cce912019-02-20 01:05:22 -0600921 certs::httpsServiceName, certs::httpsObjectPath,
922 certs::dbusObjManagerIntf, "GetManagedObjects");
Marri Devender Rao5968cae2019-01-21 10:27:12 -0600923 }
924
925 void doPost(crow::Response &res, const crow::Request &req,
926 const std::vector<std::string> &params) override
927 {
928 BMCWEB_LOG_DEBUG << "HTTPSCertificateCollection::doPost";
929 auto asyncResp = std::make_shared<AsyncResp>(res);
930 asyncResp->res.jsonValue = {{"Name", "HTTPS Certificate"},
931 {"Description", "HTTPS Certificate"}};
932
Kowalski, Kamil58eb2382019-08-12 11:54:31 +0200933 std::string certFileBody = getCertificateFromReqBody(asyncResp, req);
934
935 if (certFileBody.empty())
936 {
937 return;
938 }
939
Marri Devender Rao5968cae2019-01-21 10:27:12 -0600940 std::shared_ptr<CertificateFile> certFile =
Kowalski, Kamil58eb2382019-08-12 11:54:31 +0200941 std::make_shared<CertificateFile>(certFileBody);
Marri Devender Rao5968cae2019-01-21 10:27:12 -0600942
943 crow::connections::systemBus->async_method_call(
Marri Devender Rao37cce912019-02-20 01:05:22 -0600944 [asyncResp, certFile](const boost::system::error_code ec) {
Marri Devender Rao5968cae2019-01-21 10:27:12 -0600945 if (ec)
946 {
947 BMCWEB_LOG_ERROR << "DBUS response error: " << ec;
948 messages::internalError(asyncResp->res);
949 return;
950 }
Marri Devender Rao37cce912019-02-20 01:05:22 -0600951 // TODO: Issue#84 supporting only 1 certificate
952 long certId = 1;
953 std::string certURL =
954 "/redfish/v1/Managers/bmc/NetworkProtocol/HTTPS/"
955 "Certificates/" +
956 std::to_string(certId);
957 std::string objectPath = std::string(certs::httpsObjectPath) +
958 "/" + std::to_string(certId);
959 getCertificateProperties(asyncResp, objectPath,
960 certs::httpsServiceName, certId,
961 certURL, "HTTPS Certificate");
962 BMCWEB_LOG_DEBUG << "HTTPS certificate install file="
963 << certFile->getCertFilePath();
Marri Devender Rao5968cae2019-01-21 10:27:12 -0600964 },
Marri Devender Rao37cce912019-02-20 01:05:22 -0600965 certs::httpsServiceName, certs::httpsObjectPath,
966 certs::certInstallIntf, "Install", certFile->getCertFilePath());
Marri Devender Rao5968cae2019-01-21 10:27:12 -0600967 }
968}; // HTTPSCertificateCollection
969
970/**
Marri Devender Rao5968cae2019-01-21 10:27:12 -0600971 * The certificate location schema defines a resource that an administrator
972 * can use in order to locate all certificates installed on a given service.
973 */
974class CertificateLocations : public Node
975{
976 public:
977 template <typename CrowApp>
978 CertificateLocations(CrowApp &app) :
979 Node(app, "/redfish/v1/CertificateService/CertificateLocations/")
980 {
981 entityPrivileges = {
982 {boost::beast::http::verb::get, {{"Login"}}},
983 {boost::beast::http::verb::head, {{"Login"}}},
984 {boost::beast::http::verb::patch, {{"ConfigureComponents"}}},
985 {boost::beast::http::verb::put, {{"ConfigureComponents"}}},
986 {boost::beast::http::verb::delete_, {{"ConfigureComponents"}}},
987 {boost::beast::http::verb::post, {{"ConfigureComponents"}}}};
988 }
989
990 private:
991 void doGet(crow::Response &res, const crow::Request &req,
992 const std::vector<std::string> &params) override
993 {
994 res.jsonValue = {
995 {"@odata.id",
996 "/redfish/v1/CertificateService/CertificateLocations"},
997 {"@odata.type",
998 "#CertificateLocations.v1_0_0.CertificateLocations"},
999 {"@odata.context",
1000 "/redfish/v1/$metadata#CertificateLocations.CertificateLocations"},
1001 {"Name", "Certificate Locations"},
1002 {"Id", "CertificateLocations"},
1003 {"Description",
1004 "Defines a resource that an administrator can use in order to "
1005 "locate all certificates installed on a given service"}};
1006 auto asyncResp = std::make_shared<AsyncResp>(res);
1007 nlohmann::json &links =
1008 asyncResp->res.jsonValue["Links"]["Certificates"];
1009 links = nlohmann::json::array();
1010 getCertificateLocations(
1011 asyncResp,
1012 "/redfish/v1/Managers/bmc/NetworkProtocol/HTTPS/Certificates/",
Marri Devender Rao37cce912019-02-20 01:05:22 -06001013 certs::httpsObjectPath, certs::httpsServiceName);
1014 getCertificateLocations(asyncResp,
1015 "/redfish/v1/AccountService/LDAP/Certificates/",
1016 certs::ldapObjectPath, certs::ldapServiceName);
Marri Devender Raocfcd5f62019-05-17 08:34:37 -05001017 getCertificateLocations(
1018 asyncResp, "/redfish/v1/Managers/bmc/Truststore/Certificates/",
1019 certs::authorityObjectPath, certs::authorityServiceName);
Marri Devender Rao37cce912019-02-20 01:05:22 -06001020 }
1021 /**
1022 * @brief Retrieve the certificates installed list and append to the
1023 * response
1024 *
1025 * @param[in] asyncResp Shared pointer to the response message
1026 * @param[in] certURL Path of the certificate object
1027 * @param[in] path Path of the D-Bus service object
1028 * @return None
1029 */
1030 void getCertificateLocations(std::shared_ptr<AsyncResp> &asyncResp,
1031 const std::string &certURL,
1032 const std::string &path,
1033 const std::string &service)
1034 {
1035 BMCWEB_LOG_DEBUG << "getCertificateLocations URI=" << certURL
1036 << " Path=" << path << " service= " << service;
1037 crow::connections::systemBus->async_method_call(
1038 [asyncResp, certURL](const boost::system::error_code ec,
1039 const ManagedObjectType &certs) {
1040 if (ec)
1041 {
1042 BMCWEB_LOG_ERROR << "DBUS response error: " << ec;
1043 messages::internalError(asyncResp->res);
1044 return;
1045 }
1046 nlohmann::json &links =
1047 asyncResp->res.jsonValue["Links"]["Certificates"];
1048 for (auto &cert : certs)
1049 {
1050 long id = getIDFromURL(cert.first.str);
1051 if (id >= 0)
1052 {
1053 links.push_back(
1054 {{"@odata.id", certURL + std::to_string(id)}});
1055 }
1056 }
1057 asyncResp->res.jsonValue["Links"]["Certificates@odata.count"] =
1058 links.size();
1059 },
1060 service, path, certs::dbusObjManagerIntf, "GetManagedObjects");
Marri Devender Rao5968cae2019-01-21 10:27:12 -06001061 }
1062}; // CertificateLocations
Marri Devender Rao37cce912019-02-20 01:05:22 -06001063
1064/**
1065 * Collection of LDAP certificates
1066 */
1067class LDAPCertificateCollection : public Node
1068{
1069 public:
1070 template <typename CrowApp>
1071 LDAPCertificateCollection(CrowApp &app) :
1072 Node(app, "/redfish/v1/AccountService/LDAP/Certificates/")
1073 {
1074 entityPrivileges = {
1075 {boost::beast::http::verb::get, {{"Login"}}},
1076 {boost::beast::http::verb::head, {{"Login"}}},
1077 {boost::beast::http::verb::patch, {{"ConfigureComponents"}}},
1078 {boost::beast::http::verb::put, {{"ConfigureComponents"}}},
1079 {boost::beast::http::verb::delete_, {{"ConfigureComponents"}}},
1080 {boost::beast::http::verb::post, {{"ConfigureComponents"}}}};
1081 }
1082 void doGet(crow::Response &res, const crow::Request &req,
1083 const std::vector<std::string> &params) override
1084 {
1085 res.jsonValue = {
1086 {"@odata.id", "/redfish/v1/AccountService/LDAP/Certificates"},
1087 {"@odata.type", "#CertificateCollection.CertificateCollection"},
1088 {"@odata.context",
1089 "/redfish/v1/"
1090 "$metadata#CertificateCollection.CertificateCollection"},
1091 {"Name", "LDAP Certificates Collection"},
1092 {"Description", "A Collection of LDAP certificate instances"}};
1093 auto asyncResp = std::make_shared<AsyncResp>(res);
1094 crow::connections::systemBus->async_method_call(
1095 [asyncResp](const boost::system::error_code ec,
1096 const ManagedObjectType &certs) {
1097 if (ec)
1098 {
1099 BMCWEB_LOG_ERROR << "DBUS response error: " << ec;
1100 messages::internalError(asyncResp->res);
1101 return;
1102 }
1103 nlohmann::json &members = asyncResp->res.jsonValue["Members"];
1104 members = nlohmann::json::array();
1105 for (const auto &cert : certs)
1106 {
1107 long id = getIDFromURL(cert.first.str);
1108 if (id >= 0)
1109 {
1110 members.push_back(
1111 {{"@odata.id", "/redfish/v1/AccountService/"
1112 "LDAP/Certificates/" +
1113 std::to_string(id)}});
1114 }
1115 }
1116 asyncResp->res.jsonValue["Members@odata.count"] =
1117 members.size();
1118 },
1119 certs::ldapServiceName, certs::ldapObjectPath,
1120 certs::dbusObjManagerIntf, "GetManagedObjects");
1121 }
1122
1123 void doPost(crow::Response &res, const crow::Request &req,
1124 const std::vector<std::string> &params) override
1125 {
Marri Devender Rao37cce912019-02-20 01:05:22 -06001126 auto asyncResp = std::make_shared<AsyncResp>(res);
Kowalski, Kamil58eb2382019-08-12 11:54:31 +02001127 std::string certFileBody = getCertificateFromReqBody(asyncResp, req);
1128
1129 if (certFileBody.empty())
1130 {
1131 return;
1132 }
1133
1134 std::shared_ptr<CertificateFile> certFile =
1135 std::make_shared<CertificateFile>(certFileBody);
1136
Marri Devender Rao37cce912019-02-20 01:05:22 -06001137 crow::connections::systemBus->async_method_call(
1138 [asyncResp, certFile](const boost::system::error_code ec) {
1139 if (ec)
1140 {
1141 BMCWEB_LOG_ERROR << "DBUS response error: " << ec;
1142 messages::internalError(asyncResp->res);
1143 return;
1144 }
1145 //// TODO: Issue#84 supporting only 1 certificate
1146 long certId = 1;
1147 std::string certURL =
1148 "/redfish/v1/AccountService/LDAP/Certificates/" +
1149 std::to_string(certId);
1150 std::string objectPath = std::string(certs::ldapObjectPath) +
1151 "/" + std::to_string(certId);
1152 getCertificateProperties(asyncResp, objectPath,
1153 certs::ldapServiceName, certId,
1154 certURL, "LDAP Certificate");
1155 BMCWEB_LOG_DEBUG << "LDAP certificate install file="
1156 << certFile->getCertFilePath();
1157 },
1158 certs::ldapServiceName, certs::ldapObjectPath,
1159 certs::certInstallIntf, "Install", certFile->getCertFilePath());
1160 }
1161}; // LDAPCertificateCollection
1162
1163/**
1164 * Certificate resource describes a certificate used to prove the identity
1165 * of a component, account or service.
1166 */
1167class LDAPCertificate : public Node
1168{
1169 public:
1170 template <typename CrowApp>
1171 LDAPCertificate(CrowApp &app) :
1172 Node(app, "/redfish/v1/AccountService/LDAP/Certificates/<str>/",
1173 std::string())
1174 {
1175 entityPrivileges = {
1176 {boost::beast::http::verb::get, {{"Login"}}},
1177 {boost::beast::http::verb::head, {{"Login"}}},
1178 {boost::beast::http::verb::patch, {{"ConfigureComponents"}}},
1179 {boost::beast::http::verb::put, {{"ConfigureComponents"}}},
1180 {boost::beast::http::verb::delete_, {{"ConfigureComponents"}}},
1181 {boost::beast::http::verb::post, {{"ConfigureComponents"}}}};
1182 }
1183
1184 void doGet(crow::Response &res, const crow::Request &req,
1185 const std::vector<std::string> &params) override
1186 {
1187 auto asyncResp = std::make_shared<AsyncResp>(res);
1188 long id = getIDFromURL(req.url);
1189 if (id < 0)
1190 {
1191 BMCWEB_LOG_ERROR << "Invalid url value" << req.url;
1192 messages::internalError(asyncResp->res);
1193 return;
1194 }
1195 BMCWEB_LOG_DEBUG << "LDAP Certificate ID=" << std::to_string(id);
1196 std::string certURL = "/redfish/v1/AccountService/LDAP/Certificates/" +
1197 std::to_string(id);
1198 std::string objectPath = certs::ldapObjectPath;
1199 objectPath += "/";
1200 objectPath += std::to_string(id);
1201 getCertificateProperties(asyncResp, objectPath, certs::ldapServiceName,
1202 id, certURL, "LDAP Certificate");
1203 }
1204}; // LDAPCertificate
Marri Devender Raocfcd5f62019-05-17 08:34:37 -05001205/**
1206 * Collection of TrustStoreCertificate certificates
1207 */
1208class TrustStoreCertificateCollection : public Node
1209{
1210 public:
1211 template <typename CrowApp>
1212 TrustStoreCertificateCollection(CrowApp &app) :
1213 Node(app, "/redfish/v1/Managers/bmc/Truststore/Certificates/")
1214 {
1215 entityPrivileges = {
1216 {boost::beast::http::verb::get, {{"Login"}}},
1217 {boost::beast::http::verb::head, {{"Login"}}},
1218 {boost::beast::http::verb::patch, {{"ConfigureComponents"}}},
1219 {boost::beast::http::verb::put, {{"ConfigureComponents"}}},
1220 {boost::beast::http::verb::delete_, {{"ConfigureComponents"}}},
1221 {boost::beast::http::verb::post, {{"ConfigureComponents"}}}};
1222 }
1223 void doGet(crow::Response &res, const crow::Request &req,
1224 const std::vector<std::string> &params) override
1225 {
1226 res.jsonValue = {
1227 {"@odata.id", "/redfish/v1/Managers/bmc/Truststore/Certificates/"},
1228 {"@odata.type", "#CertificateCollection.CertificateCollection"},
1229 {"@odata.context",
1230 "/redfish/v1/"
1231 "$metadata#CertificateCollection.CertificateCollection"},
1232 {"Name", "TrustStore Certificates Collection"},
1233 {"Description",
1234 "A Collection of TrustStore certificate instances"}};
1235 auto asyncResp = std::make_shared<AsyncResp>(res);
1236 crow::connections::systemBus->async_method_call(
1237 [asyncResp](const boost::system::error_code ec,
1238 const ManagedObjectType &certs) {
1239 if (ec)
1240 {
1241 BMCWEB_LOG_ERROR << "DBUS response error: " << ec;
1242 messages::internalError(asyncResp->res);
1243 return;
1244 }
1245 nlohmann::json &members = asyncResp->res.jsonValue["Members"];
1246 members = nlohmann::json::array();
1247 for (const auto &cert : certs)
1248 {
1249 long id = getIDFromURL(cert.first.str);
1250 if (id >= 0)
1251 {
1252 members.push_back(
1253 {{"@odata.id", "/redfish/v1/Managers/bmc/"
1254 "Truststore/Certificates/" +
1255 std::to_string(id)}});
1256 }
1257 }
1258 asyncResp->res.jsonValue["Members@odata.count"] =
1259 members.size();
1260 },
1261 certs::authorityServiceName, certs::authorityObjectPath,
1262 certs::dbusObjManagerIntf, "GetManagedObjects");
1263 }
1264
1265 void doPost(crow::Response &res, const crow::Request &req,
1266 const std::vector<std::string> &params) override
1267 {
1268 std::shared_ptr<CertificateFile> certFile =
1269 std::make_shared<CertificateFile>(req.body);
1270 auto asyncResp = std::make_shared<AsyncResp>(res);
1271 crow::connections::systemBus->async_method_call(
1272 [asyncResp, certFile](const boost::system::error_code ec) {
1273 if (ec)
1274 {
1275 BMCWEB_LOG_ERROR << "DBUS response error: " << ec;
1276 messages::internalError(asyncResp->res);
1277 return;
1278 }
1279 //// TODO: Issue#84 supporting only 1 certificate
1280 long certId = 1;
1281 std::string certURL = "/redfish/v1/Managers/bmc/"
1282 "Truststore/Certificates/" +
1283 std::to_string(certId);
1284 std::string objectPath =
1285 std::string(certs::authorityObjectPath) + "/" +
1286 std::to_string(certId);
1287 getCertificateProperties(asyncResp, objectPath,
1288 certs::authorityServiceName, certId,
1289 certURL, "TrustStore Certificate");
1290 BMCWEB_LOG_DEBUG << "TrustStore certificate install file="
1291 << certFile->getCertFilePath();
1292 },
1293 certs::authorityServiceName, certs::authorityObjectPath,
1294 certs::certInstallIntf, "Install", certFile->getCertFilePath());
1295 }
1296}; // TrustStoreCertificateCollection
1297
1298/**
1299 * Certificate resource describes a certificate used to prove the identity
1300 * of a component, account or service.
1301 */
1302class TrustStoreCertificate : public Node
1303{
1304 public:
1305 template <typename CrowApp>
1306 TrustStoreCertificate(CrowApp &app) :
1307 Node(app, "/redfish/v1/Managers/bmc/Truststore/Certificates/<str>/",
1308 std::string())
1309 {
1310 entityPrivileges = {
1311 {boost::beast::http::verb::get, {{"Login"}}},
1312 {boost::beast::http::verb::head, {{"Login"}}},
1313 {boost::beast::http::verb::patch, {{"ConfigureComponents"}}},
1314 {boost::beast::http::verb::put, {{"ConfigureComponents"}}},
1315 {boost::beast::http::verb::delete_, {{"ConfigureComponents"}}},
1316 {boost::beast::http::verb::post, {{"ConfigureComponents"}}}};
1317 }
1318
1319 void doGet(crow::Response &res, const crow::Request &req,
1320 const std::vector<std::string> &params) override
1321 {
1322 auto asyncResp = std::make_shared<AsyncResp>(res);
1323 long id = getIDFromURL(req.url);
1324 if (id < 0)
1325 {
1326 BMCWEB_LOG_ERROR << "Invalid url value" << req.url;
1327 messages::internalError(asyncResp->res);
1328 return;
1329 }
1330 BMCWEB_LOG_DEBUG << "TrustStoreCertificate::doGet ID="
1331 << std::to_string(id);
1332 std::string certURL =
1333 "/redfish/v1/Managers/bmc/Truststore/Certificates/" +
1334 std::to_string(id);
1335 std::string objectPath = certs::authorityObjectPath;
1336 objectPath += "/";
1337 objectPath += std::to_string(id);
1338 getCertificateProperties(asyncResp, objectPath,
1339 certs::authorityServiceName, id, certURL,
1340 "TrustStore Certificate");
1341 }
1342}; // TrustStoreCertificate
Marri Devender Rao5968cae2019-01-21 10:27:12 -06001343} // namespace redfish