1*40e9b92eSEd Tanous // SPDX-License-Identifier: Apache-2.0 2*40e9b92eSEd Tanous // SPDX-FileCopyrightText: Copyright OpenBMC Authors 34c25d66eSEd Tanous #pragma once 44c25d66eSEd Tanous 53ccb3adbSEd Tanous #include "app.hpp" 681d523a7SEd Tanous #include "error_messages.hpp" 73ccb3adbSEd Tanous #include "http_request.hpp" 83ccb3adbSEd Tanous #include "http_response.hpp" 93ccb3adbSEd Tanous #include "query.hpp" 103ccb3adbSEd Tanous #include "registries/privilege_registry.hpp" 1181d523a7SEd Tanous #include "utility.hpp" 1281d523a7SEd Tanous 13ef4c65b7SEd Tanous #include <boost/url/format.hpp> 14ef4c65b7SEd Tanous 153544d2a7SEd Tanous #include <ranges> 164c25d66eSEd Tanous #include <string> 174c25d66eSEd Tanous 184c25d66eSEd Tanous namespace redfish 194c25d66eSEd Tanous { 204c25d66eSEd Tanous 21d3355c5cSEd Tanous inline void redfishGet(App& app, const crow::Request& req, 22d3355c5cSEd Tanous const std::shared_ptr<bmcweb::AsyncResp>& asyncResp) 234c25d66eSEd Tanous { 243ba00073SCarson Labrado if (!redfish::setUpRedfishRoute(app, req, asyncResp)) 251e925c84SEd Tanous { 261e925c84SEd Tanous return; 271e925c84SEd Tanous } 281476687dSEd Tanous asyncResp->res.jsonValue["v1"] = "/redfish/v1/"; 29d3355c5cSEd Tanous } 30d3355c5cSEd Tanous 318c623a96SEd Tanous inline void redfish404(App& app, const crow::Request& req, 328c623a96SEd Tanous const std::shared_ptr<bmcweb::AsyncResp>& asyncResp, 338c623a96SEd Tanous const std::string& path) 348c623a96SEd Tanous { 358c623a96SEd Tanous asyncResp->res.addHeader(boost::beast::http::field::allow, ""); 368c623a96SEd Tanous 378c623a96SEd Tanous // If we fall to this route, we didn't have a more specific route, so return 388c623a96SEd Tanous // 404 39686b7093SNan Zhou if (!redfish::setUpRedfishRoute(app, req, asyncResp)) 408c623a96SEd Tanous { 418c623a96SEd Tanous return; 428c623a96SEd Tanous } 438c623a96SEd Tanous 4462598e31SEd Tanous BMCWEB_LOG_WARNING("404 on path {}", path); 458c623a96SEd Tanous 4639662a3bSEd Tanous std::string name = req.url().segments().back(); 478c623a96SEd Tanous // Note, if we hit the wildcard route, we don't know the "type" the user was 488c623a96SEd Tanous // actually requesting, but giving them a return with an empty string is 498c623a96SEd Tanous // still better than nothing. 50079360aeSEd Tanous messages::resourceNotFound(asyncResp->res, "", name); 518c623a96SEd Tanous } 528c623a96SEd Tanous 5344c70412SEd Tanous inline void redfish405(App& app, const crow::Request& req, 5444c70412SEd Tanous const std::shared_ptr<bmcweb::AsyncResp>& asyncResp, 5544c70412SEd Tanous const std::string& path) 5644c70412SEd Tanous { 5744c70412SEd Tanous // If we fall to this route, we didn't have a more specific route, so return 5844c70412SEd Tanous // 405 5944c70412SEd Tanous if (!redfish::setUpRedfishRoute(app, req, asyncResp)) 6044c70412SEd Tanous { 6144c70412SEd Tanous return; 6244c70412SEd Tanous } 6344c70412SEd Tanous 6462598e31SEd Tanous BMCWEB_LOG_WARNING("405 on path {}", path); 6544c70412SEd Tanous asyncResp->res.result(boost::beast::http::status::method_not_allowed); 6644c70412SEd Tanous if (req.method() == boost::beast::http::verb::delete_) 6744c70412SEd Tanous { 6844c70412SEd Tanous messages::resourceCannotBeDeleted(asyncResp->res); 6944c70412SEd Tanous } 7044c70412SEd Tanous else 7144c70412SEd Tanous { 7244c70412SEd Tanous messages::operationNotAllowed(asyncResp->res); 7344c70412SEd Tanous } 7444c70412SEd Tanous } 7544c70412SEd Tanous 7681d523a7SEd Tanous inline void 7781d523a7SEd Tanous jsonSchemaIndexGet(App& app, const crow::Request& req, 7881d523a7SEd Tanous const std::shared_ptr<bmcweb::AsyncResp>& asyncResp) 7981d523a7SEd Tanous { 8081d523a7SEd Tanous if (!redfish::setUpRedfishRoute(app, req, asyncResp)) 8181d523a7SEd Tanous { 8281d523a7SEd Tanous return; 8381d523a7SEd Tanous } 8481d523a7SEd Tanous nlohmann::json& json = asyncResp->res.jsonValue; 8581d523a7SEd Tanous json["@odata.id"] = "/redfish/v1/JsonSchemas"; 8681d523a7SEd Tanous json["@odata.type"] = "#JsonSchemaFileCollection.JsonSchemaFileCollection"; 8781d523a7SEd Tanous json["Name"] = "JsonSchemaFile Collection"; 8881d523a7SEd Tanous json["Description"] = "Collection of JsonSchemaFiles"; 8981d523a7SEd Tanous nlohmann::json::array_t members; 90a529a6aaSEd Tanous 91a529a6aaSEd Tanous std::error_code ec; 92a529a6aaSEd Tanous std::filesystem::directory_iterator dirList( 93a529a6aaSEd Tanous "/usr/share/www/redfish/v1/JsonSchemas", ec); 94a529a6aaSEd Tanous if (ec) 9581d523a7SEd Tanous { 96a529a6aaSEd Tanous messages::internalError(asyncResp->res); 97a529a6aaSEd Tanous return; 98a529a6aaSEd Tanous } 99a529a6aaSEd Tanous for (const std::filesystem::path& file : dirList) 100a529a6aaSEd Tanous { 101a529a6aaSEd Tanous std::string filename = file.filename(); 102a529a6aaSEd Tanous std::vector<std::string> split; 103a529a6aaSEd Tanous bmcweb::split(split, filename, '.'); 104a529a6aaSEd Tanous if (split.empty()) 105a529a6aaSEd Tanous { 106a529a6aaSEd Tanous continue; 107a529a6aaSEd Tanous } 10881d523a7SEd Tanous nlohmann::json::object_t member; 109bd79bce8SPatrick Williams member["@odata.id"] = 110bd79bce8SPatrick Williams boost::urls::format("/redfish/v1/JsonSchemas/{}", split[0]); 111ad539545SPatrick Williams members.emplace_back(std::move(member)); 11281d523a7SEd Tanous } 113a529a6aaSEd Tanous 114a529a6aaSEd Tanous json["Members@odata.count"] = members.size(); 11581d523a7SEd Tanous json["Members"] = std::move(members); 11681d523a7SEd Tanous } 11781d523a7SEd Tanous 11881d523a7SEd Tanous inline void jsonSchemaGet(App& app, const crow::Request& req, 11981d523a7SEd Tanous const std::shared_ptr<bmcweb::AsyncResp>& asyncResp, 12081d523a7SEd Tanous const std::string& schema) 12181d523a7SEd Tanous { 12281d523a7SEd Tanous if (!redfish::setUpRedfishRoute(app, req, asyncResp)) 12381d523a7SEd Tanous { 12481d523a7SEd Tanous return; 12581d523a7SEd Tanous } 12681d523a7SEd Tanous 127a529a6aaSEd Tanous std::error_code ec; 128a529a6aaSEd Tanous std::filesystem::directory_iterator dirList( 129a529a6aaSEd Tanous "/usr/share/www/redfish/v1/JsonSchemas", ec); 130a529a6aaSEd Tanous if (ec) 13181d523a7SEd Tanous { 132d8a5d5d8SJiaqing Zhao messages::resourceNotFound(asyncResp->res, "JsonSchemaFile", schema); 13381d523a7SEd Tanous return; 13481d523a7SEd Tanous } 135a529a6aaSEd Tanous for (const std::filesystem::path& file : dirList) 136a529a6aaSEd Tanous { 137a529a6aaSEd Tanous std::string filename = file.filename(); 138a529a6aaSEd Tanous std::vector<std::string> split; 139a529a6aaSEd Tanous bmcweb::split(split, filename, '.'); 140a529a6aaSEd Tanous if (split.empty()) 141a529a6aaSEd Tanous { 142a529a6aaSEd Tanous continue; 143a529a6aaSEd Tanous } 144a529a6aaSEd Tanous BMCWEB_LOG_DEBUG("Checking {}", split[0]); 145a529a6aaSEd Tanous if (split[0] != schema) 146a529a6aaSEd Tanous { 147a529a6aaSEd Tanous continue; 148a529a6aaSEd Tanous } 14981d523a7SEd Tanous 15081d523a7SEd Tanous nlohmann::json& json = asyncResp->res.jsonValue; 151bd79bce8SPatrick Williams json["@odata.id"] = 152bd79bce8SPatrick Williams boost::urls::format("/redfish/v1/JsonSchemas/{}", schema); 15381d523a7SEd Tanous json["@odata.type"] = "#JsonSchemaFile.v1_0_2.JsonSchemaFile"; 15481d523a7SEd Tanous json["Name"] = schema + " Schema File"; 15581d523a7SEd Tanous json["Description"] = schema + " Schema File Location"; 15681d523a7SEd Tanous json["Id"] = schema; 157a529a6aaSEd Tanous std::string schemaName = std::format("#{}.{}", schema, schema); 15881d523a7SEd Tanous json["Schema"] = std::move(schemaName); 15981d523a7SEd Tanous constexpr std::array<std::string_view, 1> languages{"en"}; 16081d523a7SEd Tanous json["Languages"] = languages; 16181d523a7SEd Tanous json["Languages@odata.count"] = languages.size(); 16281d523a7SEd Tanous 16381d523a7SEd Tanous nlohmann::json::array_t locationArray; 16481d523a7SEd Tanous nlohmann::json::object_t locationEntry; 16581d523a7SEd Tanous locationEntry["Language"] = "en"; 166a529a6aaSEd Tanous 167a529a6aaSEd Tanous locationEntry["PublicationUri"] = boost::urls::format( 168a529a6aaSEd Tanous "http://redfish.dmtf.org/schemas/v1/{}", filename); 169ef4c65b7SEd Tanous locationEntry["Uri"] = boost::urls::format( 170a529a6aaSEd Tanous "/redfish/v1/JsonSchemas/{}/{}", schema, filename); 17181d523a7SEd Tanous 17281d523a7SEd Tanous locationArray.emplace_back(locationEntry); 17381d523a7SEd Tanous 17481d523a7SEd Tanous json["Location"] = std::move(locationArray); 17581d523a7SEd Tanous json["Location@odata.count"] = 1; 176a529a6aaSEd Tanous return; 177a529a6aaSEd Tanous } 178a529a6aaSEd Tanous messages::resourceNotFound(asyncResp->res, "JsonSchemaFile", schema); 179a529a6aaSEd Tanous } 180a529a6aaSEd Tanous 181a529a6aaSEd Tanous inline void 182a529a6aaSEd Tanous jsonSchemaGetFile(const crow::Request& /*req*/, 183a529a6aaSEd Tanous const std::shared_ptr<bmcweb::AsyncResp>& asyncResp, 184a529a6aaSEd Tanous const std::string& schema, const std::string& schemaFile) 185a529a6aaSEd Tanous { 186a529a6aaSEd Tanous // Sanity check the filename 187a529a6aaSEd Tanous if (schemaFile.find_first_not_of( 188a529a6aaSEd Tanous "abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789_-.") != 189a529a6aaSEd Tanous std::string::npos) 190a529a6aaSEd Tanous { 191a529a6aaSEd Tanous messages::resourceNotFound(asyncResp->res, "JsonSchemaFile", schema); 192a529a6aaSEd Tanous return; 193a529a6aaSEd Tanous } 194a529a6aaSEd Tanous // Schema path should look like /redfish/v1/JsonSchemas/Foo/Foo.x.json 195a529a6aaSEd Tanous // Make sure the two paths match. 196a529a6aaSEd Tanous if (!schemaFile.starts_with(schema)) 197a529a6aaSEd Tanous { 198a529a6aaSEd Tanous messages::resourceNotFound(asyncResp->res, "JsonSchemaFile", schema); 199a529a6aaSEd Tanous return; 200a529a6aaSEd Tanous } 201a529a6aaSEd Tanous std::filesystem::path filepath("/usr/share/www/redfish/v1/JsonSchemas"); 202a529a6aaSEd Tanous filepath /= schemaFile; 203a529a6aaSEd Tanous if (filepath.is_relative()) 204a529a6aaSEd Tanous { 205a529a6aaSEd Tanous messages::resourceNotFound(asyncResp->res, "JsonSchemaFile", schema); 206a529a6aaSEd Tanous return; 207a529a6aaSEd Tanous } 208a529a6aaSEd Tanous 209d51c61b4SMyung Bae crow::OpenCode ec = asyncResp->res.openFile(filepath); 210d51c61b4SMyung Bae if (ec == crow::OpenCode::FileDoesNotExist) 211d51c61b4SMyung Bae { 212d51c61b4SMyung Bae messages::resourceNotFound(asyncResp->res, "JsonSchemaFile", schema); 213d51c61b4SMyung Bae return; 214d51c61b4SMyung Bae } 215d51c61b4SMyung Bae if (ec == crow::OpenCode::InternalError) 216a529a6aaSEd Tanous { 217a529a6aaSEd Tanous BMCWEB_LOG_DEBUG("failed to read file"); 218d51c61b4SMyung Bae messages::internalError(asyncResp->res); 219a529a6aaSEd Tanous return; 220a529a6aaSEd Tanous } 22181d523a7SEd Tanous } 22281d523a7SEd Tanous 223f65fca6aSEd Tanous inline void requestRoutesRedfish(App& app) 224d3355c5cSEd Tanous { 225d3355c5cSEd Tanous BMCWEB_ROUTE(app, "/redfish/") 226d3355c5cSEd Tanous .methods(boost::beast::http::verb::get)( 227d3355c5cSEd Tanous std::bind_front(redfishGet, std::ref(app))); 2288c623a96SEd Tanous 229a529a6aaSEd Tanous BMCWEB_ROUTE(app, "/redfish/v1/JsonSchemas/<str>/<str>") 230a529a6aaSEd Tanous .privileges(redfish::privileges::getJsonSchemaFile) 231a529a6aaSEd Tanous .methods(boost::beast::http::verb::get)(jsonSchemaGetFile); 232a529a6aaSEd Tanous 23381d523a7SEd Tanous BMCWEB_ROUTE(app, "/redfish/v1/JsonSchemas/<str>/") 2340ea4b4e2SEd Tanous .privileges(redfish::privileges::getJsonSchemaFileCollection) 23581d523a7SEd Tanous .methods(boost::beast::http::verb::get)( 23681d523a7SEd Tanous std::bind_front(jsonSchemaGet, std::ref(app))); 23781d523a7SEd Tanous 23881d523a7SEd Tanous BMCWEB_ROUTE(app, "/redfish/v1/JsonSchemas/") 2390ea4b4e2SEd Tanous .privileges(redfish::privileges::getJsonSchemaFile) 24081d523a7SEd Tanous .methods(boost::beast::http::verb::get)( 24181d523a7SEd Tanous std::bind_front(jsonSchemaIndexGet, std::ref(app))); 242e9dd1d31SEd Tanous 243e9dd1d31SEd Tanous // Note, this route must always be registered last 244e9dd1d31SEd Tanous BMCWEB_ROUTE(app, "/redfish/<path>") 2450ea4b4e2SEd Tanous .notFound() 2460ea4b4e2SEd Tanous .privileges(redfish::privileges::privilegeSetLogin)( 2470ea4b4e2SEd Tanous std::bind_front(redfish404, std::ref(app))); 24844c70412SEd Tanous 24944c70412SEd Tanous BMCWEB_ROUTE(app, "/redfish/<path>") 2500ea4b4e2SEd Tanous .methodNotAllowed() 2510ea4b4e2SEd Tanous .privileges(redfish::privileges::privilegeSetLogin)( 2520ea4b4e2SEd Tanous std::bind_front(redfish405, std::ref(app))); 2534c25d66eSEd Tanous } 254f65fca6aSEd Tanous 2554c25d66eSEd Tanous } // namespace redfish 256