blob: e106079a8cd5dd343ca24388c085d4ee02001966 [file] [log] [blame]
#pragma once
#include <crow/app.h>
#include <dbus/connection.hpp>
#include <dbus/endpoint.hpp>
#include <dbus/filter.hpp>
#include <dbus/match.hpp>
#include <dbus/message.hpp>
#include <persistent_data_middleware.hpp>
#include <token_authorization_middleware.hpp>
#include <fstream>
#include <streambuf>
#include <string>
namespace crow {
namespace redfish {
template <typename... Middlewares>
void get_redfish_sub_routes(Crow<Middlewares...>& app, const std::string& url,
nlohmann::json& j) {
auto routes = app.get_routes(url);
for (auto& route : routes) {
auto redfish_sub_route =
route.substr(url.size(), route.size() - url.size() - 1);
// check if the route is at this level, and we didn't find and exact match
// also, filter out resources that start with $ to remove $metadata
if (!redfish_sub_route.empty() && redfish_sub_route[0] != '$' &&
redfish_sub_route.find('/') == std::string::npos) {
j[redfish_sub_route] = nlohmann::json{{"@odata.id", route}};
}
}
}
template <typename... Middlewares>
void request_routes(Crow<Middlewares...>& app) {
CROW_ROUTE(app, "/redfish/")
.methods("GET"_method)([](const crow::request& req, crow::response& res) {
res.json_value = {{"v1", "/redfish/v1/"}};
res.end();
});
CROW_ROUTE(app, "/redfish/v1/")
.methods(
"GET"_method)([&](const crow::request& req, crow::response& res) {
res.json_value = {
{"@odata.context", "/redfish/v1/$metadata#ServiceRoot.ServiceRoot"},
{"@odata.id", "/redfish/v1/"},
{"@odata.type", "#ServiceRoot.v1_1_1.ServiceRoot"},
{"Id", "RootService"},
{"Name", "Root Service"},
{"RedfishVersion", "1.1.0"},
{"Links",
{{"Sessions",
{{"@odata.id", "/redfish/v1/SessionService/Sessions/"}}}}}};
res.json_value["UUID"] =
app.template get_middleware<PersistentData::Middleware>()
.system_uuid;
get_redfish_sub_routes(app, "/redfish/v1/", res.json_value);
res.end();
});
CROW_ROUTE(app, "/redfish/v1/Chassis/")
.methods("GET"_method)(
[&](const crow::request& req, crow::response& res) {
std::vector<std::string> entities;
/*std::ifstream f("~/system.json");
nlohmann::json input = nlohmann::json::parse(f);
for (auto it = input.begin(); it != input.end(); it++) {
auto value = it.value();
if (value["type"] == "Chassis") {
std::string str = value["name"];
entities.emplace_back(str);
}
}
*/
res.json_value = {
{"@odata.context",
"/redfish/v1/$metadata#ChassisCollection.ChassisCollection"},
{"@odata.id", "/redfish/v1/Chassis"},
{"@odata.type", "#ChassisCollection.ChassisCollection"},
{"Name", "Chassis Collection"},
{"Members@odata.count", entities.size()}};
get_redfish_sub_routes(app, "/redfish/v1/Chassis", res.json_value);
res.end();
});
CROW_ROUTE(app, "/redfish/v1/AccountService/")
.methods(
"GET"_method)([&](const crow::request& req, crow::response& res) {
res.json_value = {
{"@odata.context",
"/redfish/v1/$metadata#AccountService.AccountService"},
{"@odata.id", "/redfish/v1/AccountService"},
{"@odata.type", "#AccountService.v1_1_0.AccountService"},
{"Id", "AccountService"},
{"Name", "Account Service"},
{"Description", "BMC User Accounts"},
{"Status",
// TODO(ed) health rollup
{{"State", "Enabled"}, {"Health", "OK"}, {"HealthRollup", "OK"}}},
{"ServiceEnabled", true},
{"MinPasswordLength", 1},
{"MaxPasswordLength", 20},
};
get_redfish_sub_routes(app, "/redfish/v1/AccountService",
res.json_value);
res.end();
});
CROW_ROUTE(app, "/redfish/v1/AccountService/Roles/")
.methods("GET"_method)(
[&](const crow::request& req, crow::response& res) {
res.json_value = {
{"@odata.context",
"/redfish/v1/$metadata#RoleCollection.RoleCollection"},
{"@odata.id", "/redfish/v1/AccountService/Roles"},
{"@odata.type", "#RoleCollection.RoleCollection"},
{"Name", "Account Service"},
{"Description", "BMC User Roles"},
{"Members@odata.count", 1},
{"Members",
{{"@odata.id",
"/redfish/v1/AccountService/Roles/Administrator"}}}};
get_redfish_sub_routes(app, "/redfish/v1/AccountService",
res.json_value);
res.end();
});
CROW_ROUTE(app, "/redfish/v1/AccountService/Roles/Administrator/")
.methods("GET"_method)(
[&](const crow::request& req, crow::response& res) {
res.json_value = {
{"@odata.context", "/redfish/v1/$metadata#Role.Role"},
{"@odata.id", "/redfish/v1/AccountService/Roles/Administrator"},
{"@odata.type", "#Role.v1_0_2.Role"},
{"Id", "Administrator"},
{"Name", "User Role"},
{"Description", "Administrator User Role"},
{"IsPredefined", true},
{"AssignedPrivileges",
{"Login", "ConfigureManager", "ConfigureUsers",
"ConfigureSelf", "ConfigureComponents"}}};
res.end();
});
CROW_ROUTE(app, "/redfish/v1/AccountService/Accounts/")
.methods(
"GET"_method)([&](const crow::request& req, crow::response& res) {
boost::asio::io_service io;
auto bus = std::make_shared<dbus::connection>(io, dbus::bus::session);
dbus::endpoint user_list("org.openbmc.UserManager",
"/org/openbmc/UserManager/Users",
"org.openbmc.Enrol", "UserList");
bus->async_method_call(
[&](const boost::system::error_code ec,
const std::vector<std::string>& users) {
if (ec) {
res.code = 500;
} else {
res.json_value = {
{"@odata.context",
"/redfish/v1/"
"$metadata#ManagerAccountCollection."
"ManagerAccountCollection"},
{"@odata.id", "/redfish/v1/AccountService/Accounts"},
{"@odata.type",
"#ManagerAccountCollection.ManagerAccountCollection"},
{"Name", "Accounts Collection"},
{"Description", "BMC User Accounts"},
{"Members@odata.count", users.size()}};
nlohmann::json member_array = nlohmann::json::array();
int user_index = 0;
for (auto& user : users) {
member_array.push_back(
{{"@odata.id",
"/redfish/v1/AccountService/Accounts/" +
std::to_string(++user_index)}});
}
res.json_value["Members"] = member_array;
}
res.end();
},
user_list);
});
CROW_ROUTE(app, "/redfish/v1/AccountService/Accounts/<int>/")
.methods("GET"_method)([](const crow::request& req, crow::response& res,
int account_index) {
res.json_value = {
{"@odata.context",
"/redfish/v1/$metadata#ManagerAccount.ManagerAccount"},
{"@odata.id", "/redfish/v1/AccountService/Accounts/1"},
{"@odata.type", "#ManagerAccount.v1_0_3.ManagerAccount"},
{"Id", "1"},
{"Name", "User Account"},
{"Description", "User Account"},
{"Enabled", false},
{"Password", nullptr},
{"UserName", "anonymous"},
{"RoleId", "NoAccess"},
{"Links",
{{"Role",
{{"@odata.id", "/redfish/v1/AccountService/Roles/NoAccess"}}}}}};
res.end();
});
CROW_ROUTE(app, "/redfish/v1/SessionService/")
.methods(
"GET"_method)([&](const crow::request& req, crow::response& res) {
res.json_value = {
{"@odata.context",
"/redfish/v1/$metadata#SessionService.SessionService"},
{"@odata.id", "/redfish/v1/SessionService"},
{"@odata.type", "#SessionService.v1_1_1.SessionService"},
{"Id", "SessionService"},
{"Name", "SessionService"},
{"Description", "SessionService"},
{"Status",
{{"State", "Enabled"}, {"Health", "OK"}, {"HealthRollup", "OK"}}},
{"ServiceEnabled", true},
// TODO(ed) converge with session timeouts once they exist
// Bogus number for now
{"SessionTimeout", 1800}};
get_redfish_sub_routes(app, "/redfish/v1/AccountService",
res.json_value);
res.end();
});
CROW_ROUTE(app, "/redfish/v1/SessionService/Sessions/")
.methods("POST"_method, "GET"_method)([&](const crow::request& req,
crow::response& res) {
auto& data_middleware =
app.template get_middleware<PersistentData::Middleware>();
if (req.method == "POST"_method) {
// call with exceptions disabled
auto login_credentials =
nlohmann::json::parse(req.body, nullptr, false);
if (login_credentials.is_discarded()) {
res.code = 400;
res.end();
return;
}
// check for username/password in the root object
auto user_it = login_credentials.find("UserName");
auto pass_it = login_credentials.find("Password");
if (user_it == login_credentials.end() ||
pass_it == login_credentials.end()) {
res.code = 400;
res.end();
return;
}
std::string username = user_it->get<const std::string>();
std::string password = pass_it->get<const std::string>();
if (username.empty() || password.empty()) {
res.code = 400;
res.end();
return;
}
if (!pam_authenticate_user(username, password)) {
res.code = 401;
res.end();
return;
}
auto session = data_middleware.generate_user_session(username);
res.code = 200;
res.add_header("X-Auth-Token", session.session_token);
res.json_value = {
{"@odata.context", "/redfish/v1/$metadata#Session"},
{"@odata.id",
"/redfish/v1/SessionService/Sessions/" + session.unique_id},
{"@odata.type", "#Session.v1_0_3.Session"},
{"Id", session.unique_id},
{"Name", "User Session"},
{"Description", "Manager User Session"},
{"UserName", username}};
} else { // assume get
res.json_value = {
{"@odata.context",
"/redfish/v1/$metadata#SessionCollection.SessionCollection"},
{"@odata.id", "/redfish/v1/SessionService/Sessions"},
{"@odata.type", "#SessionCollection.SessionCollection"},
{"Name", "Session Collection"},
{"Description", "Session Collection"},
{"Members@odata.count", data_middleware.auth_tokens.size()}
};
nlohmann::json member_array = nlohmann::json::array();
for (auto& session : data_middleware.auth_tokens) {
member_array.push_back({{"@odata.id",
"/redfish/v1/SessionService/Sessions/" +
session.second.unique_id}});
}
res.json_value["Members"] = member_array;
}
res.end();
});
CROW_ROUTE(app, "/redfish/v1/SessionService/Sessions/<str>/")
.methods("GET"_method, "DELETE"_method)([&](const crow::request& req,
crow::response& res,
const std::string& session) {
auto& data_middleware =
app.template get_middleware<PersistentData::Middleware>();
// TODO(Ed) this is inefficient
auto session_it = data_middleware.auth_tokens.begin();
while (session_it != data_middleware.auth_tokens.end()) {
if (session_it->second.unique_id == session) {
break;
}
session_it++;
}
if (session_it == data_middleware.auth_tokens.end()) {
res.code = 404;
res.end();
return;
}
if (req.method == "DELETE"_method) {
data_middleware.auth_tokens.erase(session_it);
res.code = 200;
} else { // assume get
res.json_value = {
{"@odata.context", "/redfish/v1/$metadata#Session.Session"},
{"@odata.id", "/redfish/v1/SessionService/Sessions/" + session},
{"@odata.type", "#Session.v1_0_3.Session"},
{"Id", session_it->second.unique_id},
{"Name", "User Session"},
{"Description", "Manager User Session"},
{"UserName", session_it->second.username}};
}
res.end();
});
}
} // namespace redfish
} // namespace crow