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