xref: /openbmc/bmcweb/include/google/google_service_root.hpp (revision 177612aaa0633cf9d5aef0b763a43135cf552d9b)
1 // SPDX-License-Identifier: Apache-2.0
2 // SPDX-FileCopyrightText: Copyright OpenBMC Authors
3 #pragma once
4 
5 #include "app.hpp"
6 #include "async_resp.hpp"
7 #include "dbus_utility.hpp"
8 #include "error_messages.hpp"
9 #include "http_request.hpp"
10 #include "logging.hpp"
11 #include "utils/collection.hpp"
12 #include "utils/hex_utils.hpp"
13 #include "utils/json_utils.hpp"
14 
15 #include <boost/beast/http/verb.hpp>
16 #include <boost/system/error_code.hpp>
17 #include <boost/url/format.hpp>
18 #include <boost/url/url.hpp>
19 #include <nlohmann/json.hpp>
20 #include <sdbusplus/message/native_types.hpp>
21 
22 #include <array>
23 #include <cstdint>
24 #include <functional>
25 #include <memory>
26 #include <string_view>
27 #include <utility>
28 #include <vector>
29 
30 namespace crow
31 {
32 namespace google_api
33 {
34 
handleGoogleV1Get(const crow::Request &,const std::shared_ptr<bmcweb::AsyncResp> & asyncResp)35 inline void handleGoogleV1Get(
36     const crow::Request& /*req*/,
37     const std::shared_ptr<bmcweb::AsyncResp>& asyncResp)
38 {
39     asyncResp->res.jsonValue["@odata.type"] =
40         "#GoogleServiceRoot.v1_0_0.GoogleServiceRoot";
41     asyncResp->res.jsonValue["@odata.id"] = "/google/v1";
42     asyncResp->res.jsonValue["Id"] = "Google Rest RootService";
43     asyncResp->res.jsonValue["Name"] = "Google Service Root";
44     asyncResp->res.jsonValue["Version"] = "1.0.0";
45     asyncResp->res.jsonValue["RootOfTrustCollection"]["@odata.id"] =
46         "/google/v1/RootOfTrustCollection";
47 }
48 
handleRootOfTrustCollectionGet(const crow::Request &,const std::shared_ptr<bmcweb::AsyncResp> & asyncResp)49 inline void handleRootOfTrustCollectionGet(
50     const crow::Request& /*req*/,
51     const std::shared_ptr<bmcweb::AsyncResp>& asyncResp)
52 {
53     asyncResp->res.jsonValue["@odata.id"] = "/google/v1/RootOfTrustCollection";
54     asyncResp->res.jsonValue["@odata.type"] =
55         "#RootOfTrustCollection.RootOfTrustCollection";
56     const std::array<std::string_view, 1> interfaces{
57         "xyz.openbmc_project.Control.Hoth"};
58     redfish::collection_util::getCollectionMembers(
59         asyncResp, boost::urls::url("/google/v1/RootOfTrustCollection"),
60         interfaces, "/xyz/openbmc_project");
61 }
62 
63 // Helper struct to identify a resolved D-Bus object interface
64 struct ResolvedEntity
65 {
66     std::string id;
67     std::string service;
68     std::string object;
69     std::string interface;
70 };
71 
72 using ResolvedEntityHandler = std::function<void(
73     const std::string&, const std::shared_ptr<bmcweb::AsyncResp>&,
74     const ResolvedEntity&)>;
75 
hothGetSubtreeCallback(const std::string & command,const std::shared_ptr<bmcweb::AsyncResp> & asyncResp,const std::string & rotId,const ResolvedEntityHandler & entityHandler,const boost::system::error_code & ec,const dbus::utility::MapperGetSubTreeResponse & subtree)76 inline void hothGetSubtreeCallback(
77     const std::string& command,
78     const std::shared_ptr<bmcweb::AsyncResp>& asyncResp,
79     const std::string& rotId, const ResolvedEntityHandler& entityHandler,
80     const boost::system::error_code& ec,
81     const dbus::utility::MapperGetSubTreeResponse& subtree)
82 {
83     if (ec)
84     {
85         redfish::messages::internalError(asyncResp->res);
86         return;
87     }
88     for (const auto& [path, services] : subtree)
89     {
90         sdbusplus::message::object_path objPath(path);
91         if (objPath.filename() != rotId || services.empty())
92         {
93             continue;
94         }
95 
96         ResolvedEntity resolvedEntity = {
97             .id = rotId,
98             .service = services[0].first,
99             .object = path,
100             .interface = "xyz.openbmc_project.Control.Hoth"};
101         entityHandler(command, asyncResp, resolvedEntity);
102         return;
103     }
104 
105     // Couldn't find an object with that name.  return an error
106     redfish::messages::resourceNotFound(asyncResp->res, "RootOfTrust", rotId);
107 }
108 
resolveRoT(const std::string & command,const std::shared_ptr<bmcweb::AsyncResp> & asyncResp,const std::string & rotId,ResolvedEntityHandler && entityHandler)109 inline void resolveRoT(const std::string& command,
110                        const std::shared_ptr<bmcweb::AsyncResp>& asyncResp,
111                        const std::string& rotId,
112                        ResolvedEntityHandler&& entityHandler)
113 {
114     constexpr std::array<std::string_view, 1> hothIfaces = {
115         "xyz.openbmc_project.Control.Hoth"};
116     dbus::utility::getSubTree(
117         "/xyz/openbmc_project", 0, hothIfaces,
118         [command, asyncResp, rotId, entityHandler{std::move(entityHandler)}](
119             const boost::system::error_code& ec,
120             const dbus::utility::MapperGetSubTreeResponse& subtree) {
121             hothGetSubtreeCallback(command, asyncResp, rotId, entityHandler, ec,
122                                    subtree);
123         });
124 }
125 
populateRootOfTrustEntity(const std::string &,const std::shared_ptr<bmcweb::AsyncResp> & asyncResp,const ResolvedEntity & resolvedEntity)126 inline void populateRootOfTrustEntity(
127     const std::string& /*unused*/,
128     const std::shared_ptr<bmcweb::AsyncResp>& asyncResp,
129     const ResolvedEntity& resolvedEntity)
130 {
131     asyncResp->res.jsonValue["@odata.type"] = "#RootOfTrust.v1_0_0.RootOfTrust";
132     asyncResp->res.jsonValue["@odata.id"] = boost::urls::format(
133         "/google/v1/RootOfTrustCollection/{}", resolvedEntity.id);
134 
135     asyncResp->res.jsonValue["Status"]["State"] = "Enabled";
136     asyncResp->res.jsonValue["Id"] = resolvedEntity.id;
137     // Need to fix this later to a stabler property.
138     asyncResp->res.jsonValue["Name"] = resolvedEntity.id;
139     asyncResp->res.jsonValue["Description"] = "Google Root Of Trust";
140     asyncResp->res.jsonValue["Actions"]["#RootOfTrust.SendCommand"]["target"] =
141         "/google/v1/RootOfTrustCollection/" + resolvedEntity.id +
142         "/Actions/RootOfTrust.SendCommand";
143 
144     asyncResp->res.jsonValue["Location"]["PartLocation"]["ServiceLabel"] =
145         resolvedEntity.id;
146     asyncResp->res.jsonValue["Location"]["PartLocation"]["LocationType"] =
147         "Embedded";
148 }
149 
handleRootOfTrustGet(const crow::Request &,const std::shared_ptr<bmcweb::AsyncResp> & asyncResp,const std::string & param)150 inline void handleRootOfTrustGet(
151     const crow::Request& /*req*/,
152     const std::shared_ptr<bmcweb::AsyncResp>& asyncResp,
153     const std::string& param)
154 {
155     std::string emptyCommand;
156     resolveRoT(emptyCommand, asyncResp, param, populateRootOfTrustEntity);
157 }
158 
invocationCallback(const std::shared_ptr<bmcweb::AsyncResp> & asyncResp,const boost::system::error_code & ec,const std::vector<uint8_t> & responseBytes)159 inline void invocationCallback(
160     const std::shared_ptr<bmcweb::AsyncResp>& asyncResp,
161     const boost::system::error_code& ec,
162     const std::vector<uint8_t>& responseBytes)
163 {
164     if (ec)
165     {
166         BMCWEB_LOG_ERROR("RootOfTrust.Actions.SendCommand failed: {}",
167                          ec.message());
168         redfish::messages::internalError(asyncResp->res);
169         return;
170     }
171 
172     asyncResp->res.jsonValue["CommandResponse"] =
173         bytesToHexString(responseBytes);
174 }
175 
invokeRoTCommand(const std::string & command,const std::shared_ptr<bmcweb::AsyncResp> & asyncResp,const ResolvedEntity & resolvedEntity)176 inline void invokeRoTCommand(
177     const std::string& command,
178     const std::shared_ptr<bmcweb::AsyncResp>& asyncResp,
179     const ResolvedEntity& resolvedEntity)
180 {
181     std::vector<uint8_t> bytes = hexStringToBytes(command);
182     if (bytes.empty())
183     {
184         BMCWEB_LOG_DEBUG("Invalid command: {}", command);
185         redfish::messages::actionParameterValueTypeError(command, "Command",
186                                                          "SendCommand");
187         return;
188     }
189 
190     dbus::utility::async_method_call(
191         asyncResp,
192         [asyncResp{asyncResp}](const boost::system::error_code& ec,
193                                const std::vector<uint8_t>& responseBytes) {
194             invocationCallback(asyncResp, ec, responseBytes);
195         },
196         resolvedEntity.service, resolvedEntity.object, resolvedEntity.interface,
197         "SendHostCommand", bytes);
198 }
199 
handleRoTSendCommandPost(const crow::Request & request,const std::shared_ptr<bmcweb::AsyncResp> & asyncResp,const std::string & rotId)200 inline void handleRoTSendCommandPost(
201     const crow::Request& request,
202     const std::shared_ptr<bmcweb::AsyncResp>& asyncResp,
203     const std::string& rotId)
204 {
205     std::string command;
206     if (!redfish::json_util::readJsonAction(request, asyncResp->res, "Command",
207                                             command))
208     {
209         BMCWEB_LOG_DEBUG("Missing property Command.");
210         redfish::messages::actionParameterMissing(asyncResp->res, "SendCommand",
211                                                   "Command");
212         return;
213     }
214 
215     resolveRoT(command, asyncResp, rotId, invokeRoTCommand);
216 }
217 
requestRoutes(App & app)218 inline void requestRoutes(App& app)
219 {
220     BMCWEB_ROUTE(app, "/google/v1/")
221         .methods(boost::beast::http::verb::get)(handleGoogleV1Get);
222 
223     BMCWEB_ROUTE(app, "/google/v1/RootOfTrustCollection")
224         .privileges({{"ConfigureManager"}})
225         .methods(boost::beast::http::verb::get)(handleRootOfTrustCollectionGet);
226 
227     BMCWEB_ROUTE(app, "/google/v1/RootOfTrustCollection/<str>")
228         .privileges({{"ConfigureManager"}})
229         .methods(boost::beast::http::verb::get)(handleRootOfTrustGet);
230 
231     BMCWEB_ROUTE(
232         app,
233         "/google/v1/RootOfTrustCollection/<str>/Actions/RootOfTrust.SendCommand")
234         .privileges({{"ConfigureManager"}})
235         .methods(boost::beast::http::verb::post)(handleRoTSendCommandPost);
236 }
237 
238 } // namespace google_api
239 } // namespace crow
240