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