blob: 8d2712acb82d8b82f36a05fadfe32356850ff5f9 [file] [log] [blame]
Ed Tanous40e9b922024-09-10 13:50:16 -07001// SPDX-License-Identifier: Apache-2.0
2// SPDX-FileCopyrightText: Copyright OpenBMC Authors
Ed Tanous911ac312017-08-15 09:37:42 -07003#pragma once
4
Ed Tanous3ccb3ad2023-01-13 17:40:03 -08005#include "app.hpp"
Ed Tanous81d523a2022-05-25 12:00:51 -07006#include "error_messages.hpp"
Ed Tanous3ccb3ad2023-01-13 17:40:03 -08007#include "http_request.hpp"
8#include "http_response.hpp"
9#include "query.hpp"
10#include "registries/privilege_registry.hpp"
Ed Tanous81d523a2022-05-25 12:00:51 -070011#include "utility.hpp"
12
Ed Tanousef4c65b2023-04-24 15:28:50 -070013#include <boost/url/format.hpp>
14
Ed Tanous3544d2a2023-08-06 18:12:20 -070015#include <ranges>
Ed Tanousa8c4ce92021-03-24 08:44:51 -070016#include <string>
Ed Tanous1abe55e2018-09-05 08:30:59 -070017
Ed Tanous1abe55e2018-09-05 08:30:59 -070018namespace redfish
19{
Ed Tanousa8c4ce92021-03-24 08:44:51 -070020
Ed Tanousd3355c52022-05-11 14:40:49 -070021inline void redfishGet(App& app, const crow::Request& req,
22 const std::shared_ptr<bmcweb::AsyncResp>& asyncResp)
23{
Carson Labrado3ba00072022-06-06 19:40:56 +000024 if (!redfish::setUpRedfishRoute(app, req, asyncResp))
Ed Tanousd3355c52022-05-11 14:40:49 -070025 {
26 return;
27 }
28 asyncResp->res.jsonValue["v1"] = "/redfish/v1/";
29}
30
Ed Tanous8c623a92022-05-24 11:54:51 -070031inline void redfish404(App& app, const crow::Request& req,
32 const std::shared_ptr<bmcweb::AsyncResp>& asyncResp,
33 const std::string& path)
34{
35 asyncResp->res.addHeader(boost::beast::http::field::allow, "");
36
37 // If we fall to this route, we didn't have a more specific route, so return
38 // 404
Nan Zhou686b7092022-06-15 20:02:27 +000039 if (!redfish::setUpRedfishRoute(app, req, asyncResp))
Ed Tanous8c623a92022-05-24 11:54:51 -070040 {
41 return;
42 }
43
Ed Tanous62598e32023-07-17 17:06:25 -070044 BMCWEB_LOG_WARNING("404 on path {}", path);
Ed Tanous8c623a92022-05-24 11:54:51 -070045
Ed Tanous39662a32023-02-06 15:09:46 -080046 std::string name = req.url().segments().back();
Ed Tanous8c623a92022-05-24 11:54:51 -070047 // Note, if we hit the wildcard route, we don't know the "type" the user was
48 // actually requesting, but giving them a return with an empty string is
49 // still better than nothing.
Ed Tanous079360a2022-06-29 10:05:19 -070050 messages::resourceNotFound(asyncResp->res, "", name);
Ed Tanous8c623a92022-05-24 11:54:51 -070051}
52
Ed Tanous44c70412022-07-31 16:48:29 -070053inline void redfish405(App& app, const crow::Request& req,
54 const std::shared_ptr<bmcweb::AsyncResp>& asyncResp,
55 const std::string& path)
56{
57 // If we fall to this route, we didn't have a more specific route, so return
58 // 405
59 if (!redfish::setUpRedfishRoute(app, req, asyncResp))
60 {
61 return;
62 }
63
Ed Tanous62598e32023-07-17 17:06:25 -070064 BMCWEB_LOG_WARNING("405 on path {}", path);
Ed Tanous44c70412022-07-31 16:48:29 -070065 asyncResp->res.result(boost::beast::http::status::method_not_allowed);
66 if (req.method() == boost::beast::http::verb::delete_)
67 {
68 messages::resourceCannotBeDeleted(asyncResp->res);
69 }
70 else
71 {
72 messages::operationNotAllowed(asyncResp->res);
73 }
74}
75
Ed Tanous81d523a2022-05-25 12:00:51 -070076inline void
77 jsonSchemaIndexGet(App& app, const crow::Request& req,
78 const std::shared_ptr<bmcweb::AsyncResp>& asyncResp)
79{
80 if (!redfish::setUpRedfishRoute(app, req, asyncResp))
81 {
82 return;
83 }
84 nlohmann::json& json = asyncResp->res.jsonValue;
85 json["@odata.id"] = "/redfish/v1/JsonSchemas";
Ed Tanous81d523a2022-05-25 12:00:51 -070086 json["@odata.type"] = "#JsonSchemaFileCollection.JsonSchemaFileCollection";
87 json["Name"] = "JsonSchemaFile Collection";
88 json["Description"] = "Collection of JsonSchemaFiles";
89 nlohmann::json::array_t members;
Ed Tanousa529a6a2024-05-29 16:51:37 -070090
91 std::error_code ec;
92 std::filesystem::directory_iterator dirList(
93 "/usr/share/www/redfish/v1/JsonSchemas", ec);
94 if (ec)
Ed Tanous81d523a2022-05-25 12:00:51 -070095 {
Ed Tanousa529a6a2024-05-29 16:51:37 -070096 messages::internalError(asyncResp->res);
97 return;
98 }
99 for (const std::filesystem::path& file : dirList)
100 {
101 std::string filename = file.filename();
102 std::vector<std::string> split;
103 bmcweb::split(split, filename, '.');
104 if (split.empty())
105 {
106 continue;
107 }
Ed Tanous81d523a2022-05-25 12:00:51 -0700108 nlohmann::json::object_t member;
Patrick Williamsbd79bce2024-08-16 15:22:20 -0400109 member["@odata.id"] =
110 boost::urls::format("/redfish/v1/JsonSchemas/{}", split[0]);
Patrick Williamsad539542023-05-12 10:10:08 -0500111 members.emplace_back(std::move(member));
Ed Tanous81d523a2022-05-25 12:00:51 -0700112 }
Ed Tanousa529a6a2024-05-29 16:51:37 -0700113
114 json["Members@odata.count"] = members.size();
Ed Tanous81d523a2022-05-25 12:00:51 -0700115 json["Members"] = std::move(members);
Ed Tanous81d523a2022-05-25 12:00:51 -0700116}
117
118inline void jsonSchemaGet(App& app, const crow::Request& req,
119 const std::shared_ptr<bmcweb::AsyncResp>& asyncResp,
120 const std::string& schema)
121{
122 if (!redfish::setUpRedfishRoute(app, req, asyncResp))
123 {
124 return;
125 }
126
Ed Tanousa529a6a2024-05-29 16:51:37 -0700127 std::error_code ec;
128 std::filesystem::directory_iterator dirList(
129 "/usr/share/www/redfish/v1/JsonSchemas", ec);
130 if (ec)
131 {
132 messages::resourceNotFound(asyncResp->res, "JsonSchemaFile", schema);
133 return;
134 }
135 for (const std::filesystem::path& file : dirList)
136 {
137 std::string filename = file.filename();
138 std::vector<std::string> split;
139 bmcweb::split(split, filename, '.');
140 if (split.empty())
141 {
142 continue;
143 }
144 BMCWEB_LOG_DEBUG("Checking {}", split[0]);
145 if (split[0] != schema)
146 {
147 continue;
148 }
149
150 nlohmann::json& json = asyncResp->res.jsonValue;
Patrick Williamsbd79bce2024-08-16 15:22:20 -0400151 json["@odata.id"] =
152 boost::urls::format("/redfish/v1/JsonSchemas/{}", schema);
Ed Tanousa529a6a2024-05-29 16:51:37 -0700153 json["@odata.type"] = "#JsonSchemaFile.v1_0_2.JsonSchemaFile";
154 json["Name"] = schema + " Schema File";
155 json["Description"] = schema + " Schema File Location";
156 json["Id"] = schema;
157 std::string schemaName = std::format("#{}.{}", schema, schema);
158 json["Schema"] = std::move(schemaName);
159 constexpr std::array<std::string_view, 1> languages{"en"};
160 json["Languages"] = languages;
161 json["Languages@odata.count"] = languages.size();
162
163 nlohmann::json::array_t locationArray;
164 nlohmann::json::object_t locationEntry;
165 locationEntry["Language"] = "en";
166
167 locationEntry["PublicationUri"] = boost::urls::format(
168 "http://redfish.dmtf.org/schemas/v1/{}", filename);
169 locationEntry["Uri"] = boost::urls::format(
170 "/redfish/v1/JsonSchemas/{}/{}", schema, filename);
171
172 locationArray.emplace_back(locationEntry);
173
174 json["Location"] = std::move(locationArray);
175 json["Location@odata.count"] = 1;
176 return;
177 }
178 messages::resourceNotFound(asyncResp->res, "JsonSchemaFile", schema);
179}
180
181inline void
182 jsonSchemaGetFile(const crow::Request& /*req*/,
183 const std::shared_ptr<bmcweb::AsyncResp>& asyncResp,
184 const std::string& schema, const std::string& schemaFile)
185{
186 // Sanity check the filename
187 if (schemaFile.find_first_not_of(
188 "abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789_-.") !=
189 std::string::npos)
190 {
191 messages::resourceNotFound(asyncResp->res, "JsonSchemaFile", schema);
192 return;
193 }
194 // Schema path should look like /redfish/v1/JsonSchemas/Foo/Foo.x.json
195 // Make sure the two paths match.
196 if (!schemaFile.starts_with(schema))
197 {
198 messages::resourceNotFound(asyncResp->res, "JsonSchemaFile", schema);
199 return;
200 }
201 std::filesystem::path filepath("/usr/share/www/redfish/v1/JsonSchemas");
202 filepath /= schemaFile;
203 if (filepath.is_relative())
Ed Tanous81d523a2022-05-25 12:00:51 -0700204 {
Jiaqing Zhaod8a5d5d2022-08-05 16:21:51 +0800205 messages::resourceNotFound(asyncResp->res, "JsonSchemaFile", schema);
Ed Tanous81d523a2022-05-25 12:00:51 -0700206 return;
207 }
208
Myung Baed51c61b2024-09-13 10:35:34 -0500209 crow::OpenCode ec = asyncResp->res.openFile(filepath);
210 if (ec == crow::OpenCode::FileDoesNotExist)
211 {
212 messages::resourceNotFound(asyncResp->res, "JsonSchemaFile", schema);
213 return;
214 }
215 if (ec == crow::OpenCode::InternalError)
Ed Tanousa529a6a2024-05-29 16:51:37 -0700216 {
217 BMCWEB_LOG_DEBUG("failed to read file");
Myung Baed51c61b2024-09-13 10:35:34 -0500218 messages::internalError(asyncResp->res);
Ed Tanousa529a6a2024-05-29 16:51:37 -0700219 return;
220 }
Ed Tanous81d523a2022-05-25 12:00:51 -0700221}
222
Ed Tanousf65fca62022-05-24 12:49:41 -0700223inline void requestRoutesRedfish(App& app)
Ed Tanous1abe55e2018-09-05 08:30:59 -0700224{
225 BMCWEB_ROUTE(app, "/redfish/")
Ed Tanousb41187f2019-10-24 16:30:02 -0700226 .methods(boost::beast::http::verb::get)(
Ed Tanousd3355c52022-05-11 14:40:49 -0700227 std::bind_front(redfishGet, std::ref(app)));
Ed Tanous8c623a92022-05-24 11:54:51 -0700228
Ed Tanousa529a6a2024-05-29 16:51:37 -0700229 BMCWEB_ROUTE(app, "/redfish/v1/JsonSchemas/<str>/<str>")
230 .privileges(redfish::privileges::getJsonSchemaFile)
231 .methods(boost::beast::http::verb::get)(jsonSchemaGetFile);
232
Ed Tanous81d523a2022-05-25 12:00:51 -0700233 BMCWEB_ROUTE(app, "/redfish/v1/JsonSchemas/<str>/")
Ed Tanous0ea4b4e2022-08-29 14:30:16 -0700234 .privileges(redfish::privileges::getJsonSchemaFileCollection)
Ed Tanous81d523a2022-05-25 12:00:51 -0700235 .methods(boost::beast::http::verb::get)(
236 std::bind_front(jsonSchemaGet, std::ref(app)));
237
238 BMCWEB_ROUTE(app, "/redfish/v1/JsonSchemas/")
Ed Tanous0ea4b4e2022-08-29 14:30:16 -0700239 .privileges(redfish::privileges::getJsonSchemaFile)
Ed Tanous81d523a2022-05-25 12:00:51 -0700240 .methods(boost::beast::http::verb::get)(
241 std::bind_front(jsonSchemaIndexGet, std::ref(app)));
Ed Tanouse9dd1d32022-06-17 11:56:00 -0700242
243 // Note, this route must always be registered last
244 BMCWEB_ROUTE(app, "/redfish/<path>")
Ed Tanous0ea4b4e2022-08-29 14:30:16 -0700245 .notFound()
246 .privileges(redfish::privileges::privilegeSetLogin)(
247 std::bind_front(redfish404, std::ref(app)));
Ed Tanous44c70412022-07-31 16:48:29 -0700248
249 BMCWEB_ROUTE(app, "/redfish/<path>")
Ed Tanous0ea4b4e2022-08-29 14:30:16 -0700250 .methodNotAllowed()
251 .privileges(redfish::privileges::privilegeSetLogin)(
252 std::bind_front(redfish405, std::ref(app)));
Ed Tanous3dac7492017-08-02 13:46:20 -0700253}
Ed Tanousf65fca62022-05-24 12:49:41 -0700254
Ed Tanous1abe55e2018-09-05 08:30:59 -0700255} // namespace redfish