blob: 17971dc092d87866eddf13f4cf3bca4cfc0abcc9 [file] [log] [blame]
Przemyslaw Czarnowski107077d2019-07-11 10:16:43 +02001/*
2// Copyright (c) 2018 Intel 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
Ed Tanousb9d36b42022-02-26 21:42:46 -080018#include <account_service.hpp>
John Edward Broadbent7e860f12021-04-08 15:57:16 -070019#include <app.hpp>
Adrian Ambrożewicz988fb7b2020-01-13 18:52:46 +010020#include <boost/process/async_pipe.hpp>
21#include <boost/type_traits/has_dereference.hpp>
Anna Platash9e319cf2020-11-17 10:18:31 +010022#include <boost/url/url_view.hpp>
Ed Tanous45ca1b82022-03-25 13:07:27 -070023#include <query.hpp>
Ed Tanoused398212021-06-09 17:05:54 -070024#include <registries/privilege_registry.hpp>
Ed Tanousb9d36b42022-02-26 21:42:46 -080025#include <utils/json_utils.hpp>
Przemyslaw Czarnowski107077d2019-07-11 10:16:43 +020026
27namespace redfish
Przemyslaw Czarnowski107077d2019-07-11 10:16:43 +020028{
Anna Platash9e319cf2020-11-17 10:18:31 +010029/**
30 * @brief Function extracts transfer protocol name from URI.
31 */
Ed Tanous67df0732021-10-26 11:23:56 -070032inline std::string getTransferProtocolTypeFromUri(const std::string& imageUri)
33{
34 boost::urls::result<boost::urls::url_view> url =
35 boost::urls::parse_uri(boost::string_view(imageUri));
36 if (!url)
37 {
38 return "None";
39 }
40 boost::string_view scheme = url->scheme();
41 if (scheme == "smb")
42 {
43 return "CIFS";
44 }
45 if (scheme == "https")
46 {
47 return "HTTPS";
48 }
49
50 return "None";
51}
Przemyslaw Czarnowski107077d2019-07-11 10:16:43 +020052
53/**
54 * @brief Read all known properties from VM object interfaces
55 */
Ed Tanous22db1722021-06-09 10:53:51 -070056inline void
Ed Tanous8a592812022-06-04 09:06:59 -070057 vmParseInterfaceObject(const dbus::utility::DBusInteracesMap& interfaces,
zhanghch058d1b46d2021-04-01 11:18:24 +080058 const std::shared_ptr<bmcweb::AsyncResp>& aResp)
Przemyslaw Czarnowski107077d2019-07-11 10:16:43 +020059{
Ed Tanous8a592812022-06-04 09:06:59 -070060 for (const auto& [interface, values] : interfaces)
Przemyslaw Czarnowski107077d2019-07-11 10:16:43 +020061 {
Ed Tanous711ac7a2021-12-20 09:34:41 -080062 if (interface == "xyz.openbmc_project.VirtualMedia.MountPoint")
Przemyslaw Czarnowski107077d2019-07-11 10:16:43 +020063 {
Ed Tanous711ac7a2021-12-20 09:34:41 -080064 for (const auto& [property, value] : values)
Przemyslaw Czarnowski107077d2019-07-11 10:16:43 +020065 {
Ed Tanous711ac7a2021-12-20 09:34:41 -080066 if (property == "EndpointId")
67 {
68 const std::string* endpointIdValue =
69 std::get_if<std::string>(&value);
70 if (endpointIdValue == nullptr)
71 {
72 continue;
73 }
74 if (!endpointIdValue->empty())
75 {
76 // Proxy mode
77 aResp->res
78 .jsonValue["Oem"]["OpenBMC"]["WebSocketEndpoint"] =
79 *endpointIdValue;
80 aResp->res.jsonValue["TransferProtocolType"] = "OEM";
81 }
82 }
83 if (property == "ImageURL")
Przemyslaw Czarnowski107077d2019-07-11 10:16:43 +020084 {
Anna Platash9e319cf2020-11-17 10:18:31 +010085 const std::string* imageUrlValue =
Ed Tanous711ac7a2021-12-20 09:34:41 -080086 std::get_if<std::string>(&value);
Ed Tanous26f69762022-01-25 09:49:11 -080087 if (imageUrlValue != nullptr && !imageUrlValue->empty())
Przemyslaw Czarnowskida4784d2020-11-06 09:58:25 +010088 {
Anna Platash9e319cf2020-11-17 10:18:31 +010089 std::filesystem::path filePath = *imageUrlValue;
90 if (!filePath.has_filename())
91 {
92 // this will handle https share, which not
93 // necessarily has to have filename given.
94 aResp->res.jsonValue["ImageName"] = "";
95 }
96 else
97 {
98 aResp->res.jsonValue["ImageName"] =
99 filePath.filename();
100 }
Przemyslaw Czarnowskida4784d2020-11-06 09:58:25 +0100101
Anna Platash9e319cf2020-11-17 10:18:31 +0100102 aResp->res.jsonValue["Image"] = *imageUrlValue;
Anna Platash9e319cf2020-11-17 10:18:31 +0100103 aResp->res.jsonValue["TransferProtocolType"] =
104 getTransferProtocolTypeFromUri(*imageUrlValue);
105
Ed Tanous711ac7a2021-12-20 09:34:41 -0800106 aResp->res.jsonValue["ConnectedVia"] = "URI";
Anna Platash9e319cf2020-11-17 10:18:31 +0100107 }
108 }
Ed Tanous711ac7a2021-12-20 09:34:41 -0800109 if (property == "WriteProtected")
Anna Platash9e319cf2020-11-17 10:18:31 +0100110 {
Ed Tanous711ac7a2021-12-20 09:34:41 -0800111 const bool* writeProtectedValue = std::get_if<bool>(&value);
Ed Tanouse662eae2022-01-25 10:39:19 -0800112 if (writeProtectedValue != nullptr)
Przemyslaw Czarnowski107077d2019-07-11 10:16:43 +0200113 {
Anna Platash9e319cf2020-11-17 10:18:31 +0100114 aResp->res.jsonValue["WriteProtected"] =
115 *writeProtectedValue;
Przemyslaw Czarnowski107077d2019-07-11 10:16:43 +0200116 }
117 }
118 }
119 }
Ed Tanous711ac7a2021-12-20 09:34:41 -0800120 if (interface == "xyz.openbmc_project.VirtualMedia.Process")
121 {
122 for (const auto& [property, value] : values)
123 {
124 if (property == "Active")
125 {
126 const bool* activeValue = std::get_if<bool>(&value);
Ed Tanouse662eae2022-01-25 10:39:19 -0800127 if (activeValue == nullptr)
Ed Tanous711ac7a2021-12-20 09:34:41 -0800128 {
129 BMCWEB_LOG_DEBUG << "Value Active not found";
130 return;
131 }
132 aResp->res.jsonValue["Inserted"] = *activeValue;
133
Ed Tanouse05aec52022-01-25 10:28:56 -0800134 if (*activeValue)
Ed Tanous711ac7a2021-12-20 09:34:41 -0800135 {
136 aResp->res.jsonValue["ConnectedVia"] = "Applet";
137 }
138 }
139 }
140 }
Przemyslaw Czarnowski107077d2019-07-11 10:16:43 +0200141 }
142}
143
144/**
145 * @brief Fill template for Virtual Media Item.
146 */
Ed Tanous22db1722021-06-09 10:53:51 -0700147inline nlohmann::json vmItemTemplate(const std::string& name,
Gunnar Mills1214b7e2020-06-04 10:11:30 -0500148 const std::string& resName)
Przemyslaw Czarnowski107077d2019-07-11 10:16:43 +0200149{
150 nlohmann::json item;
Ed Tanousfdb20342022-06-03 09:56:52 -0700151 item["@odata.id"] = crow::utility::urlFromPieces(
152 "redfish", "v1", "Managers", name, "VirtualMedia", resName);
Ed Tanous22db1722021-06-09 10:53:51 -0700153
Przemyslaw Czarnowskid04ba322020-01-21 12:41:56 +0100154 item["@odata.type"] = "#VirtualMedia.v1_3_0.VirtualMedia";
Przemyslaw Czarnowski107077d2019-07-11 10:16:43 +0200155 item["Name"] = "Virtual Removable Media";
156 item["Id"] = resName;
Przemyslaw Czarnowski107077d2019-07-11 10:16:43 +0200157 item["WriteProtected"] = true;
Przemyslaw Czarnowski107077d2019-07-11 10:16:43 +0200158 item["MediaTypes"] = {"CD", "USBStick"};
159 item["TransferMethod"] = "Stream";
Przemyslaw Czarnowskid04ba322020-01-21 12:41:56 +0100160 item["Oem"]["OpenBMC"]["@odata.type"] =
161 "#OemVirtualMedia.v1_0_0.VirtualMedia";
Przemyslaw Czarnowski107077d2019-07-11 10:16:43 +0200162
163 return item;
164}
165
166/**
167 * @brief Fills collection data
168 */
Ed Tanous22db1722021-06-09 10:53:51 -0700169inline void getVmResourceList(std::shared_ptr<bmcweb::AsyncResp> aResp,
Gunnar Mills1214b7e2020-06-04 10:11:30 -0500170 const std::string& service,
171 const std::string& name)
Przemyslaw Czarnowski107077d2019-07-11 10:16:43 +0200172{
173 BMCWEB_LOG_DEBUG << "Get available Virtual Media resources.";
174 crow::connections::systemBus->async_method_call(
Ed Tanous02cad962022-06-30 16:50:15 -0700175 [name, aResp{std::move(aResp)}](
176 const boost::system::error_code ec,
177 const dbus::utility::ManagedObjectType& subtree) {
Ed Tanous002d39b2022-05-31 08:59:27 -0700178 if (ec)
179 {
180 BMCWEB_LOG_DEBUG << "DBUS response error";
181 return;
182 }
183 nlohmann::json& members = aResp->res.jsonValue["Members"];
184 members = nlohmann::json::array();
185
186 for (const auto& object : subtree)
187 {
188 nlohmann::json item;
189 std::string path = object.first.filename();
190 if (path.empty())
Przemyslaw Czarnowski107077d2019-07-11 10:16:43 +0200191 {
Ed Tanous002d39b2022-05-31 08:59:27 -0700192 continue;
Przemyslaw Czarnowski107077d2019-07-11 10:16:43 +0200193 }
Przemyslaw Czarnowski107077d2019-07-11 10:16:43 +0200194
Ed Tanousfdb20342022-06-03 09:56:52 -0700195 item["@odata.id"] = crow::utility::urlFromPieces(
196 "redfish", "v1", "Managers", name, "VirtualMedia", path);
Ed Tanous002d39b2022-05-31 08:59:27 -0700197 members.emplace_back(std::move(item));
198 }
199 aResp->res.jsonValue["Members@odata.count"] = members.size();
Przemyslaw Czarnowski107077d2019-07-11 10:16:43 +0200200 },
201 service, "/xyz/openbmc_project/VirtualMedia",
202 "org.freedesktop.DBus.ObjectManager", "GetManagedObjects");
203}
204
205/**
206 * @brief Fills data for specific resource
207 */
Ed Tanous22db1722021-06-09 10:53:51 -0700208inline void getVmData(const std::shared_ptr<bmcweb::AsyncResp>& aResp,
Gunnar Mills1214b7e2020-06-04 10:11:30 -0500209 const std::string& service, const std::string& name,
210 const std::string& resName)
Przemyslaw Czarnowski107077d2019-07-11 10:16:43 +0200211{
212 BMCWEB_LOG_DEBUG << "Get Virtual Media resource data.";
213
214 crow::connections::systemBus->async_method_call(
Ed Tanous914e2d52022-01-07 11:38:34 -0800215 [resName, name,
216 aResp](const boost::system::error_code ec,
217 const dbus::utility::ManagedObjectType& subtree) {
Ed Tanous002d39b2022-05-31 08:59:27 -0700218 if (ec)
219 {
220 BMCWEB_LOG_DEBUG << "DBUS response error";
Przemyslaw Czarnowskie13c2762019-09-02 17:32:43 +0200221
Ed Tanous002d39b2022-05-31 08:59:27 -0700222 return;
223 }
224
225 for (const auto& item : subtree)
226 {
227 std::string thispath = item.first.filename();
228 if (thispath.empty())
229 {
230 continue;
Przemyslaw Czarnowski107077d2019-07-11 10:16:43 +0200231 }
232
Ed Tanous002d39b2022-05-31 08:59:27 -0700233 if (thispath != resName)
Przemyslaw Czarnowski107077d2019-07-11 10:16:43 +0200234 {
Ed Tanous002d39b2022-05-31 08:59:27 -0700235 continue;
236 }
Przemyslaw Czarnowski107077d2019-07-11 10:16:43 +0200237
Ed Tanous002d39b2022-05-31 08:59:27 -0700238 // "Legacy"/"Proxy"
239 auto mode = item.first.parent_path();
240 // "VirtualMedia"
241 auto type = mode.parent_path();
242 if (mode.filename().empty() || type.filename().empty())
243 {
244 continue;
245 }
Przemyslaw Czarnowski107077d2019-07-11 10:16:43 +0200246
Ed Tanous002d39b2022-05-31 08:59:27 -0700247 if (type.filename() != "VirtualMedia")
248 {
249 continue;
250 }
Przemyslaw Czarnowski1a6258d2021-04-14 11:02:46 +0200251
Ed Tanous002d39b2022-05-31 08:59:27 -0700252 aResp->res.jsonValue = vmItemTemplate(name, resName);
Przemyslaw Czarnowski1a6258d2021-04-14 11:02:46 +0200253
Ed Tanous002d39b2022-05-31 08:59:27 -0700254 // Check if dbus path is Legacy type
255 if (mode.filename() == "Legacy")
256 {
257 aResp->res.jsonValue["Actions"]["#VirtualMedia.InsertMedia"]
Ed Tanousfdb20342022-06-03 09:56:52 -0700258 ["target"] = crow::utility::urlFromPieces(
259 "redfish", "v1", "Managers", name, "VirtualMedia", resName,
260 "Actions", "VirtualMedia.InsertMedia");
Przemyslaw Czarnowski107077d2019-07-11 10:16:43 +0200261 }
262
Ed Tanous002d39b2022-05-31 08:59:27 -0700263 vmParseInterfaceObject(item.second, aResp);
264
265 aResp->res
266 .jsonValue["Actions"]["#VirtualMedia.EjectMedia"]["target"] =
Ed Tanousfdb20342022-06-03 09:56:52 -0700267 crow::utility::urlFromPieces("redfish", "v1", "Managers", name,
268 "VirtualMedia", resName, "Actions",
269 "VirtualMedia.EjectMedia");
Ed Tanous002d39b2022-05-31 08:59:27 -0700270 return;
271 }
272
273 messages::resourceNotFound(
274 aResp->res, "#VirtualMedia.v1_3_0.VirtualMedia", resName);
Przemyslaw Czarnowski107077d2019-07-11 10:16:43 +0200275 },
276 service, "/xyz/openbmc_project/VirtualMedia",
277 "org.freedesktop.DBus.ObjectManager", "GetManagedObjects");
278}
279
Przemyslaw Czarnowskie13c2762019-09-02 17:32:43 +0200280/**
Ed Tanous22db1722021-06-09 10:53:51 -0700281 * @brief Transfer protocols supported for InsertMedia action.
282 *
Przemyslaw Czarnowskie13c2762019-09-02 17:32:43 +0200283 */
Ed Tanous22db1722021-06-09 10:53:51 -0700284enum class TransferProtocol
Przemyslaw Czarnowskie13c2762019-09-02 17:32:43 +0200285{
Ed Tanous22db1722021-06-09 10:53:51 -0700286 https,
287 smb,
288 invalid
289};
290
291/**
292 * @brief Function extracts transfer protocol type from URI.
293 *
294 */
Ed Tanous67df0732021-10-26 11:23:56 -0700295inline std::optional<TransferProtocol>
Ed Tanousace85d62021-10-26 12:45:59 -0700296 getTransferProtocolFromUri(const boost::urls::url_view& imageUri)
Ed Tanous67df0732021-10-26 11:23:56 -0700297{
Ed Tanousace85d62021-10-26 12:45:59 -0700298 boost::string_view scheme = imageUri.scheme();
Ed Tanous67df0732021-10-26 11:23:56 -0700299 if (scheme == "smb")
300 {
301 return TransferProtocol::smb;
302 }
303 if (scheme == "https")
304 {
305 return TransferProtocol::https;
306 }
307 if (!scheme.empty())
308 {
309 return TransferProtocol::invalid;
310 }
311
312 return {};
313}
Ed Tanous22db1722021-06-09 10:53:51 -0700314
315/**
316 * @brief Function convert transfer protocol from string param.
317 *
318 */
319inline std::optional<TransferProtocol> getTransferProtocolFromParam(
320 const std::optional<std::string>& transferProtocolType)
321{
322 if (transferProtocolType == std::nullopt)
Agata Olenderc6f4e012020-03-11 15:19:07 +0100323 {
Ed Tanous22db1722021-06-09 10:53:51 -0700324 return {};
Agata Olenderc6f4e012020-03-11 15:19:07 +0100325 }
326
Ed Tanous22db1722021-06-09 10:53:51 -0700327 if (*transferProtocolType == "CIFS")
Przemyslaw Czarnowskie13c2762019-09-02 17:32:43 +0200328 {
Ed Tanous22db1722021-06-09 10:53:51 -0700329 return TransferProtocol::smb;
Przemyslaw Czarnowskie13c2762019-09-02 17:32:43 +0200330 }
331
Ed Tanous22db1722021-06-09 10:53:51 -0700332 if (*transferProtocolType == "HTTPS")
333 {
334 return TransferProtocol::https;
335 }
336
337 return TransferProtocol::invalid;
338}
339
340/**
341 * @brief Function extends URI with transfer protocol type.
342 *
343 */
344inline std::string
345 getUriWithTransferProtocol(const std::string& imageUri,
346 const TransferProtocol& transferProtocol)
347{
348 if (transferProtocol == TransferProtocol::smb)
349 {
350 return "smb://" + imageUri;
351 }
352
353 if (transferProtocol == TransferProtocol::https)
354 {
355 return "https://" + imageUri;
356 }
357
358 return imageUri;
359}
360
361/**
362 * @brief Function validate parameters of insert media request.
363 *
364 */
365inline bool
366 validateParams(crow::Response& res, std::string& imageUrl,
367 const std::optional<bool>& inserted,
368 const std::optional<std::string>& transferMethod,
369 const std::optional<std::string>& transferProtocolType)
370{
371 BMCWEB_LOG_DEBUG << "Validation started";
372 // required param imageUrl must not be empty
373 if (imageUrl.empty())
374 {
375 BMCWEB_LOG_ERROR << "Request action parameter Image is empty.";
376
377 messages::propertyValueFormatError(res, "<empty>", "Image");
378
379 return false;
380 }
381
382 // optional param inserted must be true
Ed Tanouse05aec52022-01-25 10:28:56 -0800383 if ((inserted != std::nullopt) && !*inserted)
Ed Tanous22db1722021-06-09 10:53:51 -0700384 {
385 BMCWEB_LOG_ERROR
386 << "Request action optional parameter Inserted must be true.";
387
388 messages::actionParameterNotSupported(res, "Inserted", "InsertMedia");
389
390 return false;
391 }
392
393 // optional param transferMethod must be stream
394 if ((transferMethod != std::nullopt) && (*transferMethod != "Stream"))
395 {
396 BMCWEB_LOG_ERROR << "Request action optional parameter "
397 "TransferMethod must be Stream.";
398
399 messages::actionParameterNotSupported(res, "TransferMethod",
400 "InsertMedia");
401
402 return false;
403 }
Ed Tanousace85d62021-10-26 12:45:59 -0700404 boost::urls::result<boost::urls::url_view> url =
405 boost::urls::parse_uri(boost::string_view(imageUrl));
406 if (!url)
407 {
Michal Orzel9bc55692022-06-14 15:50:50 +0200408 messages::actionParameterValueFormatError(res, imageUrl, "Image",
409 "InsertMedia");
410 return false;
Ed Tanousace85d62021-10-26 12:45:59 -0700411 }
Ed Tanous22db1722021-06-09 10:53:51 -0700412 std::optional<TransferProtocol> uriTransferProtocolType =
Ed Tanousace85d62021-10-26 12:45:59 -0700413 getTransferProtocolFromUri(*url);
Ed Tanous22db1722021-06-09 10:53:51 -0700414
415 std::optional<TransferProtocol> paramTransferProtocolType =
416 getTransferProtocolFromParam(transferProtocolType);
417
418 // ImageUrl does not contain valid protocol type
419 if (*uriTransferProtocolType == TransferProtocol::invalid)
420 {
421 BMCWEB_LOG_ERROR << "Request action parameter ImageUrl must "
422 "contain specified protocol type from list: "
423 "(smb, https).";
424
Ed Tanousace85d62021-10-26 12:45:59 -0700425 messages::resourceAtUriInUnknownFormat(res, *url);
Ed Tanous22db1722021-06-09 10:53:51 -0700426
427 return false;
428 }
429
430 // transferProtocolType should contain value from list
431 if (*paramTransferProtocolType == TransferProtocol::invalid)
432 {
433 BMCWEB_LOG_ERROR << "Request action parameter TransferProtocolType "
434 "must be provided with value from list: "
435 "(CIFS, HTTPS).";
436
437 messages::propertyValueNotInList(res, *transferProtocolType,
438 "TransferProtocolType");
439 return false;
440 }
441
442 // valid transfer protocol not provided either with URI nor param
443 if ((uriTransferProtocolType == std::nullopt) &&
444 (paramTransferProtocolType == std::nullopt))
445 {
446 BMCWEB_LOG_ERROR << "Request action parameter ImageUrl must "
447 "contain specified protocol type or param "
448 "TransferProtocolType must be provided.";
449
Ed Tanousace85d62021-10-26 12:45:59 -0700450 messages::resourceAtUriInUnknownFormat(res, *url);
Ed Tanous22db1722021-06-09 10:53:51 -0700451
452 return false;
453 }
454
455 // valid transfer protocol provided both with URI and param
456 if ((paramTransferProtocolType != std::nullopt) &&
457 (uriTransferProtocolType != std::nullopt))
458 {
459 // check if protocol is the same for URI and param
460 if (*paramTransferProtocolType != *uriTransferProtocolType)
461 {
462 BMCWEB_LOG_ERROR << "Request action parameter "
463 "TransferProtocolType must contain the "
464 "same protocol type as protocol type "
465 "provided with param imageUrl.";
466
467 messages::actionParameterValueTypeError(res, *transferProtocolType,
468 "TransferProtocolType",
469 "InsertMedia");
470
471 return false;
472 }
473 }
474
475 // validation passed
476 // add protocol to URI if needed
477 if (uriTransferProtocolType == std::nullopt)
478 {
479 imageUrl =
480 getUriWithTransferProtocol(imageUrl, *paramTransferProtocolType);
481 }
482
483 return true;
484}
485
486template <typename T>
487static void secureCleanup(T& value)
488{
Ed Tanous4ecc6182022-01-07 09:36:26 -0800489 // NOLINTNEXTLINE(cppcoreguidelines-pro-type-const-cast)
Ed Tanous22db1722021-06-09 10:53:51 -0700490 auto raw = const_cast<typename T::value_type*>(value.data());
491 explicit_bzero(raw, value.size() * sizeof(*raw));
492}
493
494class Credentials
495{
496 public:
497 Credentials(std::string&& user, std::string&& password) :
498 userBuf(std::move(user)), passBuf(std::move(password))
499 {}
500
501 ~Credentials()
502 {
503 secureCleanup(userBuf);
504 secureCleanup(passBuf);
505 }
506
507 const std::string& user()
508 {
509 return userBuf;
510 }
511
512 const std::string& password()
513 {
514 return passBuf;
515 }
516
517 Credentials() = delete;
518 Credentials(const Credentials&) = delete;
519 Credentials& operator=(const Credentials&) = delete;
Ed Tanousecd6a3a2022-01-07 09:18:40 -0800520 Credentials(Credentials&&) = delete;
521 Credentials& operator=(Credentials&&) = delete;
Ed Tanous22db1722021-06-09 10:53:51 -0700522
523 private:
524 std::string userBuf;
525 std::string passBuf;
526};
527
528class CredentialsProvider
529{
530 public:
Gunnar Mills1214b7e2020-06-04 10:11:30 -0500531 template <typename T>
Ed Tanous22db1722021-06-09 10:53:51 -0700532 struct Deleter
Adrian Ambrożewicz988fb7b2020-01-13 18:52:46 +0100533 {
Ed Tanous22db1722021-06-09 10:53:51 -0700534 void operator()(T* buff) const
535 {
536 if (buff)
537 {
538 secureCleanup(*buff);
539 delete buff;
540 }
541 }
542 };
543
544 using Buffer = std::vector<char>;
545 using SecureBuffer = std::unique_ptr<Buffer, Deleter<Buffer>>;
546 // Using explicit definition instead of std::function to avoid implicit
547 // conversions eg. stack copy instead of reference
548 using FormatterFunc = void(const std::string& username,
549 const std::string& password, Buffer& dest);
550
551 CredentialsProvider(std::string&& user, std::string&& password) :
552 credentials(std::move(user), std::move(password))
553 {}
554
555 const std::string& user()
556 {
557 return credentials.user();
Adrian Ambrożewicz988fb7b2020-01-13 18:52:46 +0100558 }
559
Ed Tanous22db1722021-06-09 10:53:51 -0700560 const std::string& password()
Adrian Ambrożewicz988fb7b2020-01-13 18:52:46 +0100561 {
Ed Tanous22db1722021-06-09 10:53:51 -0700562 return credentials.password();
563 }
Adrian Ambrożewicz988fb7b2020-01-13 18:52:46 +0100564
Ed Tanous22db1722021-06-09 10:53:51 -0700565 SecureBuffer pack(FormatterFunc formatter)
Adrian Ambrożewicz988fb7b2020-01-13 18:52:46 +0100566 {
Ed Tanous22db1722021-06-09 10:53:51 -0700567 SecureBuffer packed{new Buffer{}};
Ed Tanouse662eae2022-01-25 10:39:19 -0800568 if (formatter != nullptr)
Adrian Ambrożewicz988fb7b2020-01-13 18:52:46 +0100569 {
Ed Tanous22db1722021-06-09 10:53:51 -0700570 formatter(credentials.user(), credentials.password(), *packed);
Adrian Ambrożewicz988fb7b2020-01-13 18:52:46 +0100571 }
572
Ed Tanous22db1722021-06-09 10:53:51 -0700573 return packed;
574 }
Adrian Ambrożewicz988fb7b2020-01-13 18:52:46 +0100575
Ed Tanous22db1722021-06-09 10:53:51 -0700576 private:
577 Credentials credentials;
578};
Adrian Ambrożewicz988fb7b2020-01-13 18:52:46 +0100579
Ed Tanous22db1722021-06-09 10:53:51 -0700580// Wrapper for boost::async_pipe ensuring proper pipe cleanup
581template <typename Buffer>
582class Pipe
583{
584 public:
585 using unix_fd = sdbusplus::message::unix_fd;
Adrian Ambrożewicz988fb7b2020-01-13 18:52:46 +0100586
Ed Tanous8a592812022-06-04 09:06:59 -0700587 Pipe(boost::asio::io_context& io, Buffer&& bufferIn) :
588 impl(io), buffer{std::move(bufferIn)}
Ed Tanous22db1722021-06-09 10:53:51 -0700589 {}
Adrian Ambrożewicz988fb7b2020-01-13 18:52:46 +0100590
Ed Tanous22db1722021-06-09 10:53:51 -0700591 ~Pipe()
Adrian Ambrożewicz988fb7b2020-01-13 18:52:46 +0100592 {
Ed Tanous22db1722021-06-09 10:53:51 -0700593 // Named pipe needs to be explicitly removed
594 impl.close();
595 }
Adrian Ambrożewicz988fb7b2020-01-13 18:52:46 +0100596
Ed Tanousecd6a3a2022-01-07 09:18:40 -0800597 Pipe(const Pipe&) = delete;
598 Pipe(Pipe&&) = delete;
599 Pipe& operator=(const Pipe&) = delete;
600 Pipe& operator=(Pipe&&) = delete;
601
Ed Tanous22db1722021-06-09 10:53:51 -0700602 unix_fd fd()
Przemyslaw Czarnowskie13c2762019-09-02 17:32:43 +0200603 {
Ed Tanous22db1722021-06-09 10:53:51 -0700604 return unix_fd{impl.native_source()};
605 }
Adrian Ambrożewicz988fb7b2020-01-13 18:52:46 +0100606
Ed Tanous22db1722021-06-09 10:53:51 -0700607 template <typename WriteHandler>
608 void asyncWrite(WriteHandler&& handler)
609 {
610 impl.async_write_some(data(), std::forward<WriteHandler>(handler));
611 }
Adrian Ambrożewicz988fb7b2020-01-13 18:52:46 +0100612
Ed Tanous22db1722021-06-09 10:53:51 -0700613 private:
614 // Specialization for pointer types
615 template <typename B = Buffer>
616 typename std::enable_if<boost::has_dereference<B>::value,
617 boost::asio::const_buffer>::type
618 data()
619 {
620 return boost::asio::buffer(*buffer);
621 }
622
623 template <typename B = Buffer>
624 typename std::enable_if<!boost::has_dereference<B>::value,
625 boost::asio::const_buffer>::type
626 data()
627 {
628 return boost::asio::buffer(buffer);
629 }
630
631 const std::string name;
632 boost::process::async_pipe impl;
633 Buffer buffer;
634};
635
636/**
637 * @brief Function transceives data with dbus directly.
638 *
639 * All BMC state properties will be retrieved before sending reset request.
640 */
641inline void doMountVmLegacy(const std::shared_ptr<bmcweb::AsyncResp>& asyncResp,
642 const std::string& service, const std::string& name,
643 const std::string& imageUrl, const bool rw,
644 std::string&& userName, std::string&& password)
645{
646 using SecurePipe = Pipe<CredentialsProvider::SecureBuffer>;
647 constexpr const size_t secretLimit = 1024;
648
649 std::shared_ptr<SecurePipe> secretPipe;
Ed Tanous168e20c2021-12-13 14:39:53 -0800650 dbus::utility::DbusVariantType unixFd = -1;
Ed Tanous22db1722021-06-09 10:53:51 -0700651
652 if (!userName.empty() || !password.empty())
653 {
654 // Encapsulate in safe buffer
655 CredentialsProvider credentials(std::move(userName),
656 std::move(password));
657
658 // Payload must contain data + NULL delimiters
659 if (credentials.user().size() + credentials.password().size() + 2 >
660 secretLimit)
Adrian Ambrożewicz988fb7b2020-01-13 18:52:46 +0100661 {
Ed Tanous22db1722021-06-09 10:53:51 -0700662 BMCWEB_LOG_ERROR << "Credentials too long to handle";
663 messages::unrecognizedRequestBody(asyncResp->res);
664 return;
665 }
Adrian Ambrożewicz988fb7b2020-01-13 18:52:46 +0100666
Ed Tanous22db1722021-06-09 10:53:51 -0700667 // Pack secret
668 auto secret = credentials.pack(
669 [](const auto& user, const auto& pass, auto& buff) {
Ed Tanous002d39b2022-05-31 08:59:27 -0700670 std::copy(user.begin(), user.end(), std::back_inserter(buff));
671 buff.push_back('\0');
672 std::copy(pass.begin(), pass.end(), std::back_inserter(buff));
673 buff.push_back('\0');
674 });
Adrian Ambrożewicz988fb7b2020-01-13 18:52:46 +0100675
Ed Tanous22db1722021-06-09 10:53:51 -0700676 // Open pipe
677 secretPipe = std::make_shared<SecurePipe>(
678 crow::connections::systemBus->get_io_context(), std::move(secret));
679 unixFd = secretPipe->fd();
Adrian Ambrożewicz988fb7b2020-01-13 18:52:46 +0100680
Ed Tanous22db1722021-06-09 10:53:51 -0700681 // Pass secret over pipe
682 secretPipe->asyncWrite(
683 [asyncResp](const boost::system::error_code& ec, std::size_t) {
Ed Tanous002d39b2022-05-31 08:59:27 -0700684 if (ec)
685 {
686 BMCWEB_LOG_ERROR << "Failed to pass secret: " << ec;
687 messages::internalError(asyncResp->res);
688 }
689 });
Ed Tanous22db1722021-06-09 10:53:51 -0700690 }
Adrian Ambrożewicz988fb7b2020-01-13 18:52:46 +0100691
Ed Tanous22db1722021-06-09 10:53:51 -0700692 crow::connections::systemBus->async_method_call(
693 [asyncResp, secretPipe](const boost::system::error_code ec,
694 bool success) {
Ed Tanous002d39b2022-05-31 08:59:27 -0700695 if (ec)
696 {
697 BMCWEB_LOG_ERROR << "Bad D-Bus request error: " << ec;
698 messages::internalError(asyncResp->res);
699 }
700 else if (!success)
701 {
702 BMCWEB_LOG_ERROR << "Service responded with error";
703 messages::generalError(asyncResp->res);
704 }
Ed Tanous22db1722021-06-09 10:53:51 -0700705 },
706 service, "/xyz/openbmc_project/VirtualMedia/Legacy/" + name,
707 "xyz.openbmc_project.VirtualMedia.Legacy", "Mount", imageUrl, rw,
708 unixFd);
709}
710
711/**
712 * @brief Function transceives data with dbus directly.
713 *
714 * All BMC state properties will be retrieved before sending reset request.
715 */
716inline void doVmAction(const std::shared_ptr<bmcweb::AsyncResp>& asyncResp,
717 const std::string& service, const std::string& name,
718 bool legacy)
719{
720
721 // Legacy mount requires parameter with image
722 if (legacy)
723 {
Adrian Ambrożewiczd6da5be2020-01-13 18:31:01 +0100724 crow::connections::systemBus->async_method_call(
Ed Tanous22db1722021-06-09 10:53:51 -0700725 [asyncResp](const boost::system::error_code ec) {
Ed Tanous002d39b2022-05-31 08:59:27 -0700726 if (ec)
727 {
728 BMCWEB_LOG_ERROR << "Bad D-Bus request error: " << ec;
Ed Tanous22db1722021-06-09 10:53:51 -0700729
Ed Tanous002d39b2022-05-31 08:59:27 -0700730 messages::internalError(asyncResp->res);
731 return;
732 }
Adrian Ambrożewiczd6da5be2020-01-13 18:31:01 +0100733 },
734 service, "/xyz/openbmc_project/VirtualMedia/Legacy/" + name,
Ed Tanous22db1722021-06-09 10:53:51 -0700735 "xyz.openbmc_project.VirtualMedia.Legacy", "Unmount");
Przemyslaw Czarnowskie13c2762019-09-02 17:32:43 +0200736 }
Ed Tanous22db1722021-06-09 10:53:51 -0700737 else // proxy
Przemyslaw Czarnowskie13c2762019-09-02 17:32:43 +0200738 {
Przemyslaw Czarnowskie13c2762019-09-02 17:32:43 +0200739 crow::connections::systemBus->async_method_call(
Ed Tanous22db1722021-06-09 10:53:51 -0700740 [asyncResp](const boost::system::error_code ec) {
Ed Tanous002d39b2022-05-31 08:59:27 -0700741 if (ec)
742 {
743 BMCWEB_LOG_ERROR << "Bad D-Bus request error: " << ec;
Ed Tanous22db1722021-06-09 10:53:51 -0700744
Ed Tanous002d39b2022-05-31 08:59:27 -0700745 messages::internalError(asyncResp->res);
746 return;
747 }
Ed Tanous22db1722021-06-09 10:53:51 -0700748 },
749 service, "/xyz/openbmc_project/VirtualMedia/Proxy/" + name,
750 "xyz.openbmc_project.VirtualMedia.Proxy", "Unmount");
751 }
752}
753
Ed Tanous98be3e32021-09-16 15:05:36 -0700754struct InsertMediaActionParams
755{
756 std::string imageUrl;
757 std::optional<std::string> userName;
758 std::optional<std::string> password;
759 std::optional<std::string> transferMethod;
760 std::optional<std::string> transferProtocolType;
761 std::optional<bool> writeProtected = true;
762 std::optional<bool> inserted;
763};
764
Ed Tanous96825be2022-06-03 09:43:38 -0700765inline void handleManagersVirtualMediaActionInsertPost(
766 crow::App& app, const crow::Request& req,
767 const std::shared_ptr<bmcweb::AsyncResp>& asyncResp,
768 const std::string& name, const std::string& resName)
769{
770 if (!redfish::setUpRedfishRoute(app, req, asyncResp))
771 {
772 return;
773 }
774 if (name != "bmc")
775 {
776 messages::resourceNotFound(asyncResp->res, "VirtualMedia.Insert",
777 resName);
778
779 return;
780 }
781 InsertMediaActionParams actionParams;
782
783 // Read obligatory parameters (url of
784 // image)
785 if (!json_util::readJsonAction(
786 req, asyncResp->res, "Image", actionParams.imageUrl,
787 "WriteProtected", actionParams.writeProtected, "UserName",
788 actionParams.userName, "Password", actionParams.password,
789 "Inserted", actionParams.inserted, "TransferMethod",
790 actionParams.transferMethod, "TransferProtocolType",
791 actionParams.transferProtocolType))
792 {
793 BMCWEB_LOG_DEBUG << "Image is not provided";
794 return;
795 }
796
797 bool paramsValid = validateParams(
798 asyncResp->res, actionParams.imageUrl, actionParams.inserted,
799 actionParams.transferMethod, actionParams.transferProtocolType);
800
801 if (!paramsValid)
802 {
803 return;
804 }
805
806 crow::connections::systemBus->async_method_call(
807 [asyncResp, actionParams,
808 resName](const boost::system::error_code ec,
809 const dbus::utility::MapperGetObject& getObjectType) mutable {
810 if (ec)
811 {
812 BMCWEB_LOG_ERROR << "ObjectMapper::GetObject call failed: " << ec;
813 messages::internalError(asyncResp->res);
814
815 return;
816 }
817 std::string service = getObjectType.begin()->first;
818 BMCWEB_LOG_DEBUG << "GetObjectType: " << service;
819
820 crow::connections::systemBus->async_method_call(
821 [service, resName, actionParams,
Ed Tanous8a592812022-06-04 09:06:59 -0700822 asyncResp](const boost::system::error_code ec2,
Ed Tanous96825be2022-06-03 09:43:38 -0700823 dbus::utility::ManagedObjectType& subtree) mutable {
Ed Tanous8a592812022-06-04 09:06:59 -0700824 if (ec2)
Ed Tanous96825be2022-06-03 09:43:38 -0700825 {
826 BMCWEB_LOG_DEBUG << "DBUS response error";
827
828 return;
829 }
830
831 for (const auto& object : subtree)
832 {
833 const std::string& path =
834 static_cast<const std::string&>(object.first);
835
836 std::size_t lastIndex = path.rfind('/');
837 if (lastIndex == std::string::npos)
838 {
839 continue;
840 }
841
842 lastIndex += 1;
843
844 if (path.substr(lastIndex) == resName)
845 {
846 lastIndex = path.rfind("Proxy");
847 if (lastIndex != std::string::npos)
848 {
849 // Not possible in proxy mode
850 BMCWEB_LOG_DEBUG << "InsertMedia not "
851 "allowed in proxy mode";
852 messages::resourceNotFound(asyncResp->res,
853 "VirtualMedia.InsertMedia",
854 resName);
855
856 return;
857 }
858
859 lastIndex = path.rfind("Legacy");
860 if (lastIndex == std::string::npos)
861 {
862 continue;
863 }
864
865 // manager is irrelevant for
866 // VirtualMedia dbus calls
867 doMountVmLegacy(asyncResp, service, resName,
868 actionParams.imageUrl,
869 !(*actionParams.writeProtected),
870 std::move(*actionParams.userName),
871 std::move(*actionParams.password));
872
873 return;
874 }
875 }
876 BMCWEB_LOG_DEBUG << "Parent item not found";
877 messages::resourceNotFound(asyncResp->res, "VirtualMedia", resName);
878 },
879 service, "/xyz/openbmc_project/VirtualMedia",
880 "org.freedesktop.DBus.ObjectManager", "GetManagedObjects");
881 },
882 "xyz.openbmc_project.ObjectMapper",
883 "/xyz/openbmc_project/object_mapper",
884 "xyz.openbmc_project.ObjectMapper", "GetObject",
885 "/xyz/openbmc_project/VirtualMedia", std::array<const char*, 0>());
886}
887
888inline void handleManagersVirtualMediaActionEject(
889 crow::App& app, const crow::Request& req,
890 const std::shared_ptr<bmcweb::AsyncResp>& asyncResp,
891 const std::string& managerName, const std::string& resName)
892{
893 if (!redfish::setUpRedfishRoute(app, req, asyncResp))
894 {
895 return;
896 }
897 if (managerName != "bmc")
898 {
899 messages::resourceNotFound(asyncResp->res, "VirtualMedia.Eject",
900 resName);
901
902 return;
903 }
904
905 crow::connections::systemBus->async_method_call(
906 [asyncResp,
Ed Tanous8a592812022-06-04 09:06:59 -0700907 resName](const boost::system::error_code ec2,
Ed Tanous96825be2022-06-03 09:43:38 -0700908 const dbus::utility::MapperGetObject& getObjectType) {
Ed Tanous8a592812022-06-04 09:06:59 -0700909 if (ec2)
Ed Tanous96825be2022-06-03 09:43:38 -0700910 {
Ed Tanous8a592812022-06-04 09:06:59 -0700911 BMCWEB_LOG_ERROR << "ObjectMapper::GetObject call failed: " << ec2;
Ed Tanous96825be2022-06-03 09:43:38 -0700912 messages::internalError(asyncResp->res);
913
914 return;
915 }
916 std::string service = getObjectType.begin()->first;
917 BMCWEB_LOG_DEBUG << "GetObjectType: " << service;
918
919 crow::connections::systemBus->async_method_call(
Ed Tanous02cad962022-06-30 16:50:15 -0700920 [resName, service, asyncResp{asyncResp}](
921 const boost::system::error_code ec,
922 const dbus::utility::ManagedObjectType& subtree) {
Ed Tanous96825be2022-06-03 09:43:38 -0700923 if (ec)
924 {
925 BMCWEB_LOG_DEBUG << "DBUS response error";
926
927 return;
928 }
929
930 for (const auto& object : subtree)
931 {
932 const std::string& path =
933 static_cast<const std::string&>(object.first);
934
935 std::size_t lastIndex = path.rfind('/');
936 if (lastIndex == std::string::npos)
937 {
938 continue;
939 }
940
941 lastIndex += 1;
942
943 if (path.substr(lastIndex) == resName)
944 {
945 lastIndex = path.rfind("Proxy");
946 if (lastIndex != std::string::npos)
947 {
948 // Proxy mode
949 doVmAction(asyncResp, service, resName, false);
950 }
951
952 lastIndex = path.rfind("Legacy");
953 if (lastIndex != std::string::npos)
954 {
955 // Legacy mode
956 doVmAction(asyncResp, service, resName, true);
957 }
958
959 return;
960 }
961 }
962 BMCWEB_LOG_DEBUG << "Parent item not found";
963 messages::resourceNotFound(asyncResp->res, "VirtualMedia", resName);
964 },
965 service, "/xyz/openbmc_project/VirtualMedia",
966 "org.freedesktop.DBus.ObjectManager", "GetManagedObjects");
967 },
968 "xyz.openbmc_project.ObjectMapper",
969 "/xyz/openbmc_project/object_mapper",
970 "xyz.openbmc_project.ObjectMapper", "GetObject",
971 "/xyz/openbmc_project/VirtualMedia", std::array<const char*, 0>());
972}
973
974inline void handleManagersVirtualMediaCollectionGet(
975 crow::App& app, const crow::Request& req,
976 const std::shared_ptr<bmcweb::AsyncResp>& asyncResp,
977 const std::string& name)
978{
979 if (!redfish::setUpRedfishRoute(app, req, asyncResp))
980 {
981 return;
982 }
983 if (name != "bmc")
984 {
985 messages::resourceNotFound(asyncResp->res, "VirtualMedia", name);
986
987 return;
988 }
989
990 asyncResp->res.jsonValue["@odata.type"] =
991 "#VirtualMediaCollection.VirtualMediaCollection";
992 asyncResp->res.jsonValue["Name"] = "Virtual Media Services";
Ed Tanousfdb20342022-06-03 09:56:52 -0700993 asyncResp->res.jsonValue["@odata.id"] = crow::utility::urlFromPieces(
994 "redfish", "v1", "Managers", name, "VirtualMedia");
Ed Tanous96825be2022-06-03 09:43:38 -0700995
996 crow::connections::systemBus->async_method_call(
997 [asyncResp, name](const boost::system::error_code ec,
998 const dbus::utility::MapperGetObject& getObjectType) {
999 if (ec)
1000 {
1001 BMCWEB_LOG_ERROR << "ObjectMapper::GetObject call failed: " << ec;
1002 messages::internalError(asyncResp->res);
1003
1004 return;
1005 }
1006 std::string service = getObjectType.begin()->first;
1007 BMCWEB_LOG_DEBUG << "GetObjectType: " << service;
1008
1009 getVmResourceList(asyncResp, service, name);
1010 },
1011 "xyz.openbmc_project.ObjectMapper",
1012 "/xyz/openbmc_project/object_mapper",
1013 "xyz.openbmc_project.ObjectMapper", "GetObject",
1014 "/xyz/openbmc_project/VirtualMedia", std::array<const char*, 0>());
1015}
1016
1017inline void
1018 handleVirtualMediaGet(crow::App& app, const crow::Request& req,
1019 const std::shared_ptr<bmcweb::AsyncResp>& asyncResp,
1020 const std::string& name, const std::string& resName)
1021{
1022 if (!redfish::setUpRedfishRoute(app, req, asyncResp))
1023 {
1024 return;
1025 }
1026 if (name != "bmc")
1027 {
1028 messages::resourceNotFound(asyncResp->res, "VirtualMedia", resName);
1029
1030 return;
1031 }
1032
1033 crow::connections::systemBus->async_method_call(
1034 [asyncResp, name,
1035 resName](const boost::system::error_code ec,
1036 const dbus::utility::MapperGetObject& getObjectType) {
1037 if (ec)
1038 {
1039 BMCWEB_LOG_ERROR << "ObjectMapper::GetObject call failed: " << ec;
1040 messages::internalError(asyncResp->res);
1041
1042 return;
1043 }
1044 std::string service = getObjectType.begin()->first;
1045 BMCWEB_LOG_DEBUG << "GetObjectType: " << service;
1046
1047 getVmData(asyncResp, service, name, resName);
1048 },
1049 "xyz.openbmc_project.ObjectMapper",
1050 "/xyz/openbmc_project/object_mapper",
1051 "xyz.openbmc_project.ObjectMapper", "GetObject",
1052 "/xyz/openbmc_project/VirtualMedia", std::array<const char*, 0>());
1053}
1054
Ed Tanous22db1722021-06-09 10:53:51 -07001055inline void requestNBDVirtualMediaRoutes(App& app)
1056{
George Liu0fda0f12021-11-16 10:06:17 +08001057 BMCWEB_ROUTE(
1058 app,
1059 "/redfish/v1/Managers/<str>/VirtualMedia/<str>/Actions/VirtualMedia.InsertMedia")
Ed Tanoused398212021-06-09 17:05:54 -07001060 .privileges(redfish::privileges::postVirtualMedia)
Ed Tanous96825be2022-06-03 09:43:38 -07001061 .methods(boost::beast::http::verb::post)(std::bind_front(
1062 handleManagersVirtualMediaActionInsertPost, std::ref(app)));
Przemyslaw Czarnowskie13c2762019-09-02 17:32:43 +02001063
George Liu0fda0f12021-11-16 10:06:17 +08001064 BMCWEB_ROUTE(
1065 app,
1066 "/redfish/v1/Managers/<str>/VirtualMedia/<str>/Actions/VirtualMedia.EjectMedia")
Ed Tanoused398212021-06-09 17:05:54 -07001067 .privileges(redfish::privileges::postVirtualMedia)
Ed Tanous96825be2022-06-03 09:43:38 -07001068 .methods(boost::beast::http::verb::post)(std::bind_front(
1069 handleManagersVirtualMediaActionEject, std::ref(app)));
Ed Tanous002d39b2022-05-31 08:59:27 -07001070
Ed Tanous22db1722021-06-09 10:53:51 -07001071 BMCWEB_ROUTE(app, "/redfish/v1/Managers/<str>/VirtualMedia/")
Ed Tanoused398212021-06-09 17:05:54 -07001072 .privileges(redfish::privileges::getVirtualMediaCollection)
Ed Tanous96825be2022-06-03 09:43:38 -07001073 .methods(boost::beast::http::verb::get)(std::bind_front(
1074 handleManagersVirtualMediaCollectionGet, std::ref(app)));
Przemyslaw Czarnowski107077d2019-07-11 10:16:43 +02001075
Ed Tanous22db1722021-06-09 10:53:51 -07001076 BMCWEB_ROUTE(app, "/redfish/v1/Managers/<str>/VirtualMedia/<str>/")
Ed Tanoused398212021-06-09 17:05:54 -07001077 .privileges(redfish::privileges::getVirtualMedia)
Ed Tanous22db1722021-06-09 10:53:51 -07001078 .methods(boost::beast::http::verb::get)(
Ed Tanous96825be2022-06-03 09:43:38 -07001079 std::bind_front(handleVirtualMediaGet, std::ref(app)));
Ed Tanous22db1722021-06-09 10:53:51 -07001080}
Przemyslaw Czarnowski107077d2019-07-11 10:16:43 +02001081
1082} // namespace redfish