1 #pragma once
2 
3 #include "app.hpp"
4 #include "error_messages.hpp"
5 #include "http_request.hpp"
6 #include "http_response.hpp"
7 #include "query.hpp"
8 #include "registries/privilege_registry.hpp"
9 #include "schemas.hpp"
10 #include "utility.hpp"
11 
12 #include <boost/url/format.hpp>
13 
14 #include <ranges>
15 #include <string>
16 
17 namespace redfish
18 {
19 
20 inline void redfishGet(App& app, const crow::Request& req,
21                        const std::shared_ptr<bmcweb::AsyncResp>& asyncResp)
22 {
23     if (!redfish::setUpRedfishRoute(app, req, asyncResp))
24     {
25         return;
26     }
27     asyncResp->res.jsonValue["v1"] = "/redfish/v1/";
28 }
29 
30 inline void redfish404(App& app, const crow::Request& req,
31                        const std::shared_ptr<bmcweb::AsyncResp>& asyncResp,
32                        const std::string& path)
33 {
34     asyncResp->res.addHeader(boost::beast::http::field::allow, "");
35 
36     // If we fall to this route, we didn't have a more specific route, so return
37     // 404
38     if (!redfish::setUpRedfishRoute(app, req, asyncResp))
39     {
40         return;
41     }
42 
43     BMCWEB_LOG_WARNING("404 on path {}", path);
44 
45     std::string name = req.url().segments().back();
46     // Note, if we hit the wildcard route, we don't know the "type" the user was
47     // actually requesting, but giving them a return with an empty string is
48     // still better than nothing.
49     messages::resourceNotFound(asyncResp->res, "", name);
50 }
51 
52 inline void redfish405(App& app, const crow::Request& req,
53                        const std::shared_ptr<bmcweb::AsyncResp>& asyncResp,
54                        const std::string& path)
55 {
56     // If we fall to this route, we didn't have a more specific route, so return
57     // 405
58     if (!redfish::setUpRedfishRoute(app, req, asyncResp))
59     {
60         return;
61     }
62 
63     BMCWEB_LOG_WARNING("405 on path {}", path);
64     asyncResp->res.result(boost::beast::http::status::method_not_allowed);
65     if (req.method() == boost::beast::http::verb::delete_)
66     {
67         messages::resourceCannotBeDeleted(asyncResp->res);
68     }
69     else
70     {
71         messages::operationNotAllowed(asyncResp->res);
72     }
73 }
74 
75 inline void
76     jsonSchemaIndexGet(App& app, const crow::Request& req,
77                        const std::shared_ptr<bmcweb::AsyncResp>& asyncResp)
78 {
79     if (!redfish::setUpRedfishRoute(app, req, asyncResp))
80     {
81         return;
82     }
83     nlohmann::json& json = asyncResp->res.jsonValue;
84     json["@odata.id"] = "/redfish/v1/JsonSchemas";
85     json["@odata.type"] = "#JsonSchemaFileCollection.JsonSchemaFileCollection";
86     json["Name"] = "JsonSchemaFile Collection";
87     json["Description"] = "Collection of JsonSchemaFiles";
88     nlohmann::json::array_t members;
89     for (std::string_view schema : schemas)
90     {
91         nlohmann::json::object_t member;
92         member["@odata.id"] = boost::urls::format("/redfish/v1/JsonSchemas/{}",
93                                                   schema);
94         members.emplace_back(std::move(member));
95     }
96     json["Members"] = std::move(members);
97     json["Members@odata.count"] = schemas.size();
98 }
99 
100 inline void jsonSchemaGet(App& app, const crow::Request& req,
101                           const std::shared_ptr<bmcweb::AsyncResp>& asyncResp,
102                           const std::string& schema)
103 {
104     if (!redfish::setUpRedfishRoute(app, req, asyncResp))
105     {
106         return;
107     }
108 
109     if (std::ranges::find(schemas, schema) == schemas.end())
110     {
111         messages::resourceNotFound(asyncResp->res, "JsonSchemaFile", schema);
112         return;
113     }
114 
115     nlohmann::json& json = asyncResp->res.jsonValue;
116     json["@odata.id"] = boost::urls::format("/redfish/v1/JsonSchemas/{}",
117                                             schema);
118     json["@odata.type"] = "#JsonSchemaFile.v1_0_2.JsonSchemaFile";
119     json["Name"] = schema + " Schema File";
120     json["Description"] = schema + " Schema File Location";
121     json["Id"] = schema;
122     std::string schemaName = "#";
123     schemaName += schema;
124     schemaName += ".";
125     schemaName += schema;
126     json["Schema"] = std::move(schemaName);
127     constexpr std::array<std::string_view, 1> languages{"en"};
128     json["Languages"] = languages;
129     json["Languages@odata.count"] = languages.size();
130 
131     nlohmann::json::array_t locationArray;
132     nlohmann::json::object_t locationEntry;
133     locationEntry["Language"] = "en";
134     locationEntry["PublicationUri"] = "http://redfish.dmtf.org/schemas/v1/" +
135                                       schema + ".json";
136     locationEntry["Uri"] = boost::urls::format(
137         "/redfish/v1/JsonSchemas/{}/{}", schema, std::string(schema) + ".json");
138 
139     locationArray.emplace_back(locationEntry);
140 
141     json["Location"] = std::move(locationArray);
142     json["Location@odata.count"] = 1;
143 }
144 
145 inline void requestRoutesRedfish(App& app)
146 {
147     BMCWEB_ROUTE(app, "/redfish/")
148         .methods(boost::beast::http::verb::get)(
149             std::bind_front(redfishGet, std::ref(app)));
150 
151     BMCWEB_ROUTE(app, "/redfish/v1/JsonSchemas/<str>/")
152         .privileges(redfish::privileges::getJsonSchemaFileCollection)
153         .methods(boost::beast::http::verb::get)(
154             std::bind_front(jsonSchemaGet, std::ref(app)));
155 
156     BMCWEB_ROUTE(app, "/redfish/v1/JsonSchemas/")
157         .privileges(redfish::privileges::getJsonSchemaFile)
158         .methods(boost::beast::http::verb::get)(
159             std::bind_front(jsonSchemaIndexGet, std::ref(app)));
160 
161     // Note, this route must always be registered last
162     BMCWEB_ROUTE(app, "/redfish/<path>")
163         .notFound()
164         .privileges(redfish::privileges::privilegeSetLogin)(
165             std::bind_front(redfish404, std::ref(app)));
166 
167     BMCWEB_ROUTE(app, "/redfish/<path>")
168         .methodNotAllowed()
169         .privileges(redfish::privileges::privilegeSetLogin)(
170             std::bind_front(redfish405, std::ref(app)));
171 }
172 
173 } // namespace redfish
174