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