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