14c25d66eSEd Tanous #pragma once 24c25d66eSEd Tanous 33ccb3adbSEd Tanous #include "app.hpp" 481d523a7SEd Tanous #include "error_messages.hpp" 53ccb3adbSEd Tanous #include "http_request.hpp" 63ccb3adbSEd Tanous #include "http_response.hpp" 73ccb3adbSEd Tanous #include "query.hpp" 83ccb3adbSEd Tanous #include "registries/privilege_registry.hpp" 981d523a7SEd Tanous #include "utility.hpp" 1081d523a7SEd Tanous 11ef4c65b7SEd Tanous #include <boost/url/format.hpp> 12ef4c65b7SEd Tanous 133544d2a7SEd Tanous #include <ranges> 144c25d66eSEd Tanous #include <string> 154c25d66eSEd Tanous 164c25d66eSEd Tanous namespace redfish 174c25d66eSEd Tanous { 184c25d66eSEd Tanous 19d3355c5cSEd Tanous inline void redfishGet(App& app, const crow::Request& req, 20d3355c5cSEd Tanous const std::shared_ptr<bmcweb::AsyncResp>& asyncResp) 214c25d66eSEd Tanous { 223ba00073SCarson Labrado if (!redfish::setUpRedfishRoute(app, req, asyncResp)) 231e925c84SEd Tanous { 241e925c84SEd Tanous return; 251e925c84SEd Tanous } 261476687dSEd Tanous asyncResp->res.jsonValue["v1"] = "/redfish/v1/"; 27d3355c5cSEd Tanous } 28d3355c5cSEd Tanous 298c623a96SEd Tanous inline void redfish404(App& app, const crow::Request& req, 308c623a96SEd Tanous const std::shared_ptr<bmcweb::AsyncResp>& asyncResp, 318c623a96SEd Tanous const std::string& path) 328c623a96SEd Tanous { 338c623a96SEd Tanous asyncResp->res.addHeader(boost::beast::http::field::allow, ""); 348c623a96SEd Tanous 358c623a96SEd Tanous // If we fall to this route, we didn't have a more specific route, so return 368c623a96SEd Tanous // 404 37686b7093SNan Zhou if (!redfish::setUpRedfishRoute(app, req, asyncResp)) 388c623a96SEd Tanous { 398c623a96SEd Tanous return; 408c623a96SEd Tanous } 418c623a96SEd Tanous 4262598e31SEd Tanous BMCWEB_LOG_WARNING("404 on path {}", path); 438c623a96SEd Tanous 4439662a3bSEd Tanous std::string name = req.url().segments().back(); 458c623a96SEd Tanous // Note, if we hit the wildcard route, we don't know the "type" the user was 468c623a96SEd Tanous // actually requesting, but giving them a return with an empty string is 478c623a96SEd Tanous // still better than nothing. 48079360aeSEd Tanous messages::resourceNotFound(asyncResp->res, "", name); 498c623a96SEd Tanous } 508c623a96SEd Tanous 5144c70412SEd Tanous inline void redfish405(App& app, const crow::Request& req, 5244c70412SEd Tanous const std::shared_ptr<bmcweb::AsyncResp>& asyncResp, 5344c70412SEd Tanous const std::string& path) 5444c70412SEd Tanous { 5544c70412SEd Tanous // If we fall to this route, we didn't have a more specific route, so return 5644c70412SEd Tanous // 405 5744c70412SEd Tanous if (!redfish::setUpRedfishRoute(app, req, asyncResp)) 5844c70412SEd Tanous { 5944c70412SEd Tanous return; 6044c70412SEd Tanous } 6144c70412SEd Tanous 6262598e31SEd Tanous BMCWEB_LOG_WARNING("405 on path {}", path); 6344c70412SEd Tanous asyncResp->res.result(boost::beast::http::status::method_not_allowed); 6444c70412SEd Tanous if (req.method() == boost::beast::http::verb::delete_) 6544c70412SEd Tanous { 6644c70412SEd Tanous messages::resourceCannotBeDeleted(asyncResp->res); 6744c70412SEd Tanous } 6844c70412SEd Tanous else 6944c70412SEd Tanous { 7044c70412SEd Tanous messages::operationNotAllowed(asyncResp->res); 7144c70412SEd Tanous } 7244c70412SEd Tanous } 7344c70412SEd Tanous 7481d523a7SEd Tanous inline void 7581d523a7SEd Tanous jsonSchemaIndexGet(App& app, const crow::Request& req, 7681d523a7SEd Tanous const std::shared_ptr<bmcweb::AsyncResp>& asyncResp) 7781d523a7SEd Tanous { 7881d523a7SEd Tanous if (!redfish::setUpRedfishRoute(app, req, asyncResp)) 7981d523a7SEd Tanous { 8081d523a7SEd Tanous return; 8181d523a7SEd Tanous } 8281d523a7SEd Tanous nlohmann::json& json = asyncResp->res.jsonValue; 8381d523a7SEd Tanous json["@odata.id"] = "/redfish/v1/JsonSchemas"; 8481d523a7SEd Tanous json["@odata.type"] = "#JsonSchemaFileCollection.JsonSchemaFileCollection"; 8581d523a7SEd Tanous json["Name"] = "JsonSchemaFile Collection"; 8681d523a7SEd Tanous json["Description"] = "Collection of JsonSchemaFiles"; 8781d523a7SEd Tanous nlohmann::json::array_t members; 88*a529a6aaSEd Tanous 89*a529a6aaSEd Tanous std::error_code ec; 90*a529a6aaSEd Tanous std::filesystem::directory_iterator dirList( 91*a529a6aaSEd Tanous "/usr/share/www/redfish/v1/JsonSchemas", ec); 92*a529a6aaSEd Tanous if (ec) 9381d523a7SEd Tanous { 94*a529a6aaSEd Tanous messages::internalError(asyncResp->res); 95*a529a6aaSEd Tanous return; 96*a529a6aaSEd Tanous } 97*a529a6aaSEd Tanous for (const std::filesystem::path& file : dirList) 98*a529a6aaSEd Tanous { 99*a529a6aaSEd Tanous std::string filename = file.filename(); 100*a529a6aaSEd Tanous std::vector<std::string> split; 101*a529a6aaSEd Tanous bmcweb::split(split, filename, '.'); 102*a529a6aaSEd Tanous if (split.empty()) 103*a529a6aaSEd Tanous { 104*a529a6aaSEd Tanous continue; 105*a529a6aaSEd Tanous } 10681d523a7SEd Tanous nlohmann::json::object_t member; 107ef4c65b7SEd Tanous member["@odata.id"] = boost::urls::format("/redfish/v1/JsonSchemas/{}", 108*a529a6aaSEd Tanous split[0]); 109ad539545SPatrick Williams members.emplace_back(std::move(member)); 11081d523a7SEd Tanous } 111*a529a6aaSEd Tanous 112*a529a6aaSEd Tanous json["Members@odata.count"] = members.size(); 11381d523a7SEd Tanous json["Members"] = std::move(members); 11481d523a7SEd Tanous } 11581d523a7SEd Tanous 11681d523a7SEd Tanous inline void jsonSchemaGet(App& app, const crow::Request& req, 11781d523a7SEd Tanous const std::shared_ptr<bmcweb::AsyncResp>& asyncResp, 11881d523a7SEd Tanous const std::string& schema) 11981d523a7SEd Tanous { 12081d523a7SEd Tanous if (!redfish::setUpRedfishRoute(app, req, asyncResp)) 12181d523a7SEd Tanous { 12281d523a7SEd Tanous return; 12381d523a7SEd Tanous } 12481d523a7SEd Tanous 125*a529a6aaSEd Tanous std::error_code ec; 126*a529a6aaSEd Tanous std::filesystem::directory_iterator dirList( 127*a529a6aaSEd Tanous "/usr/share/www/redfish/v1/JsonSchemas", ec); 128*a529a6aaSEd Tanous if (ec) 12981d523a7SEd Tanous { 130d8a5d5d8SJiaqing Zhao messages::resourceNotFound(asyncResp->res, "JsonSchemaFile", schema); 13181d523a7SEd Tanous return; 13281d523a7SEd Tanous } 133*a529a6aaSEd Tanous for (const std::filesystem::path& file : dirList) 134*a529a6aaSEd Tanous { 135*a529a6aaSEd Tanous std::string filename = file.filename(); 136*a529a6aaSEd Tanous std::vector<std::string> split; 137*a529a6aaSEd Tanous bmcweb::split(split, filename, '.'); 138*a529a6aaSEd Tanous if (split.empty()) 139*a529a6aaSEd Tanous { 140*a529a6aaSEd Tanous continue; 141*a529a6aaSEd Tanous } 142*a529a6aaSEd Tanous BMCWEB_LOG_DEBUG("Checking {}", split[0]); 143*a529a6aaSEd Tanous if (split[0] != schema) 144*a529a6aaSEd Tanous { 145*a529a6aaSEd Tanous continue; 146*a529a6aaSEd Tanous } 14781d523a7SEd Tanous 14881d523a7SEd Tanous nlohmann::json& json = asyncResp->res.jsonValue; 149ef4c65b7SEd Tanous json["@odata.id"] = boost::urls::format("/redfish/v1/JsonSchemas/{}", 150ef4c65b7SEd Tanous schema); 15181d523a7SEd Tanous json["@odata.type"] = "#JsonSchemaFile.v1_0_2.JsonSchemaFile"; 15281d523a7SEd Tanous json["Name"] = schema + " Schema File"; 15381d523a7SEd Tanous json["Description"] = schema + " Schema File Location"; 15481d523a7SEd Tanous json["Id"] = schema; 155*a529a6aaSEd Tanous std::string schemaName = std::format("#{}.{}", schema, schema); 15681d523a7SEd Tanous json["Schema"] = std::move(schemaName); 15781d523a7SEd Tanous constexpr std::array<std::string_view, 1> languages{"en"}; 15881d523a7SEd Tanous json["Languages"] = languages; 15981d523a7SEd Tanous json["Languages@odata.count"] = languages.size(); 16081d523a7SEd Tanous 16181d523a7SEd Tanous nlohmann::json::array_t locationArray; 16281d523a7SEd Tanous nlohmann::json::object_t locationEntry; 16381d523a7SEd Tanous locationEntry["Language"] = "en"; 164*a529a6aaSEd Tanous 165*a529a6aaSEd Tanous locationEntry["PublicationUri"] = boost::urls::format( 166*a529a6aaSEd Tanous "http://redfish.dmtf.org/schemas/v1/{}", filename); 167ef4c65b7SEd Tanous locationEntry["Uri"] = boost::urls::format( 168*a529a6aaSEd Tanous "/redfish/v1/JsonSchemas/{}/{}", schema, filename); 16981d523a7SEd Tanous 17081d523a7SEd Tanous locationArray.emplace_back(locationEntry); 17181d523a7SEd Tanous 17281d523a7SEd Tanous json["Location"] = std::move(locationArray); 17381d523a7SEd Tanous json["Location@odata.count"] = 1; 174*a529a6aaSEd Tanous return; 175*a529a6aaSEd Tanous } 176*a529a6aaSEd Tanous messages::resourceNotFound(asyncResp->res, "JsonSchemaFile", schema); 177*a529a6aaSEd Tanous } 178*a529a6aaSEd Tanous 179*a529a6aaSEd Tanous inline void 180*a529a6aaSEd Tanous jsonSchemaGetFile(const crow::Request& /*req*/, 181*a529a6aaSEd Tanous const std::shared_ptr<bmcweb::AsyncResp>& asyncResp, 182*a529a6aaSEd Tanous const std::string& schema, const std::string& schemaFile) 183*a529a6aaSEd Tanous { 184*a529a6aaSEd Tanous // Sanity check the filename 185*a529a6aaSEd Tanous if (schemaFile.find_first_not_of( 186*a529a6aaSEd Tanous "abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789_-.") != 187*a529a6aaSEd Tanous std::string::npos) 188*a529a6aaSEd Tanous { 189*a529a6aaSEd Tanous messages::resourceNotFound(asyncResp->res, "JsonSchemaFile", schema); 190*a529a6aaSEd Tanous return; 191*a529a6aaSEd Tanous } 192*a529a6aaSEd Tanous // Schema path should look like /redfish/v1/JsonSchemas/Foo/Foo.x.json 193*a529a6aaSEd Tanous // Make sure the two paths match. 194*a529a6aaSEd Tanous if (!schemaFile.starts_with(schema)) 195*a529a6aaSEd Tanous { 196*a529a6aaSEd Tanous messages::resourceNotFound(asyncResp->res, "JsonSchemaFile", schema); 197*a529a6aaSEd Tanous return; 198*a529a6aaSEd Tanous } 199*a529a6aaSEd Tanous std::filesystem::path filepath("/usr/share/www/redfish/v1/JsonSchemas"); 200*a529a6aaSEd Tanous filepath /= schemaFile; 201*a529a6aaSEd Tanous if (filepath.is_relative()) 202*a529a6aaSEd Tanous { 203*a529a6aaSEd Tanous messages::resourceNotFound(asyncResp->res, "JsonSchemaFile", schema); 204*a529a6aaSEd Tanous return; 205*a529a6aaSEd Tanous } 206*a529a6aaSEd Tanous 207*a529a6aaSEd Tanous if (!asyncResp->res.openFile(filepath)) 208*a529a6aaSEd Tanous { 209*a529a6aaSEd Tanous BMCWEB_LOG_DEBUG("failed to read file"); 210*a529a6aaSEd Tanous asyncResp->res.result( 211*a529a6aaSEd Tanous boost::beast::http::status::internal_server_error); 212*a529a6aaSEd Tanous return; 213*a529a6aaSEd Tanous } 214*a529a6aaSEd Tanous 215*a529a6aaSEd Tanous messages::resourceNotFound(asyncResp->res, "JsonSchemaFile", schema); 21681d523a7SEd Tanous } 21781d523a7SEd Tanous 218f65fca6aSEd Tanous inline void requestRoutesRedfish(App& app) 219d3355c5cSEd Tanous { 220d3355c5cSEd Tanous BMCWEB_ROUTE(app, "/redfish/") 221d3355c5cSEd Tanous .methods(boost::beast::http::verb::get)( 222d3355c5cSEd Tanous std::bind_front(redfishGet, std::ref(app))); 2238c623a96SEd Tanous 224*a529a6aaSEd Tanous BMCWEB_ROUTE(app, "/redfish/v1/JsonSchemas/<str>/<str>") 225*a529a6aaSEd Tanous .privileges(redfish::privileges::getJsonSchemaFile) 226*a529a6aaSEd Tanous .methods(boost::beast::http::verb::get)(jsonSchemaGetFile); 227*a529a6aaSEd Tanous 22881d523a7SEd Tanous BMCWEB_ROUTE(app, "/redfish/v1/JsonSchemas/<str>/") 2290ea4b4e2SEd Tanous .privileges(redfish::privileges::getJsonSchemaFileCollection) 23081d523a7SEd Tanous .methods(boost::beast::http::verb::get)( 23181d523a7SEd Tanous std::bind_front(jsonSchemaGet, std::ref(app))); 23281d523a7SEd Tanous 23381d523a7SEd Tanous BMCWEB_ROUTE(app, "/redfish/v1/JsonSchemas/") 2340ea4b4e2SEd Tanous .privileges(redfish::privileges::getJsonSchemaFile) 23581d523a7SEd Tanous .methods(boost::beast::http::verb::get)( 23681d523a7SEd Tanous std::bind_front(jsonSchemaIndexGet, std::ref(app))); 237e9dd1d31SEd Tanous 238e9dd1d31SEd Tanous // Note, this route must always be registered last 239e9dd1d31SEd Tanous BMCWEB_ROUTE(app, "/redfish/<path>") 2400ea4b4e2SEd Tanous .notFound() 2410ea4b4e2SEd Tanous .privileges(redfish::privileges::privilegeSetLogin)( 2420ea4b4e2SEd Tanous std::bind_front(redfish404, std::ref(app))); 24344c70412SEd Tanous 24444c70412SEd Tanous BMCWEB_ROUTE(app, "/redfish/<path>") 2450ea4b4e2SEd Tanous .methodNotAllowed() 2460ea4b4e2SEd Tanous .privileges(redfish::privileges::privilegeSetLogin)( 2470ea4b4e2SEd Tanous std::bind_front(redfish405, std::ref(app))); 2484c25d66eSEd Tanous } 249f65fca6aSEd Tanous 2504c25d66eSEd Tanous } // namespace redfish 251