blob: c083637ae12800072a86217545c9b9bc12281419 [file] [log] [blame]
#include "tlbmc/redfish/query.h"
#include <memory>
#include <string>
#include <utility>
#include <vector>
#include "absl/log/log.h"
#include "absl/status/status.h"
#include "absl/strings/str_format.h"
#include "absl/time/clock.h"
#include "boost/beast/http/string_body.hpp" //NOLINT
#include "boost/beast/http/verb.hpp" //NOLINT`
#include "boost/url/url.hpp" // NOLINT
#include "redfish_v1.pb.h"
#include "grpcpp/support/status.h"
#include "tlbmc/redfish/expand_query_aggregator.h"
#include "tlbmc/redfish/query_parameters.h"
#include "tlbmc/redfish/request.h"
#include "tlbmc/redfish/response.h"
#include "tlbmc/redfish/routing.h"
#include "tlbmc/trace/tracer.h"
#include "bmcweb_authorizer_singleton.h"
#include "router_interface.h"
namespace milotic_tlbmc {
using ::crow::RouterInterface;
using ::milotic::authz::BmcWebAuthorizerSingleton;
bool RedfishPreprocess(const RedfishRequest& req, RedfishResponse& resp) {
milotic_tlbmc::Tracer::GetInstance().AddRepeatedDatapoint("Tlbmc-Authz-Begin",
absl::Now());
// Most Redfish responses are JSON, so we can set the content type here.
resp.SetBodyToJson();
// No authorization.
if (!req.FromGrpc()) {
milotic_tlbmc::Tracer::GetInstance().AddRepeatedDatapoint("Tlbmc-Authz-End",
absl::Now());
return true;
}
BmcWebAuthorizerSingleton::RequestState request_state;
request_state.with_trust_bundle = req.WithTrustBundle();
request_state.peer_authenticated = req.PeerAuthenticated();
request_state.peer_privileges = req.PeerPrivileges();
grpc::Status status = BmcWebAuthorizerSingleton::GetInstance().Authorize(
req.Target(), req.Method(), request_state);
if (!status.ok()) {
// Log both for server side and client side.
std::string error_message =
absl::StrFormat("Authorization failure at %s: %s", req.Target(),
status.error_message());
LOG(WARNING) << error_message;
resp.SetToForbidden(error_message);
resp.End();
milotic_tlbmc::Tracer::GetInstance().AddRepeatedDatapoint("Tlbmc-Authz-End",
absl::Now());
return false;
}
milotic_tlbmc::Tracer::GetInstance().AddRepeatedDatapoint("Tlbmc-Authz-End",
absl::Now());
return true;
}
namespace {
void SynchronousExpand(const crow::Router& tlbmc_router, RedfishResponse& resp,
const QueryParameters& query_parameters) {
// For each expand level we do the following:
// 1. Get all unexpanded Redfish URLs in the response
// 2. Query the Redfish URLs.
// 3. Replace the Redfish URLs in the response with the expanded URLs.
for (int i = 0; i < query_parameters.GetExpandLevel(); ++i) {
std::vector<ExpandNode> urls_to_expand =
GetUnexpandedUrls(resp.GetJsonBody(), query_parameters.GetExpandType());
for (const ExpandNode& url_to_expand : urls_to_expand) {
boost::urls::url expand_url(url_to_expand.url);
boost::beast::http::request<boost::beast::http::string_body> request;
request.target(expand_url.encoded_path());
request.method(boost::beast::http::verb::get);
RedfishRequest sub_req(std::move(request));
// Have to create a redfish response since you cannot create a tlbmc
// response without one.
RedfishResponse sub_resp;
tlbmc_router.Handle(sub_req, sub_resp);
resp.SetKeyInJsonBody(url_to_expand.json_pointer, sub_resp.GetJsonBody());
}
}
}
void AsynchronousExpand(RouterInterface* smart_router, RedfishResponse& resp,
const QueryParameters& query_parameters) {
// We need to end the response before we can send it to the aggregator.
// This sends the current response to the async resp, and then starts the
// aggregator.
resp.End();
auto expand_query_aggregator = std::make_shared<ExpandQueryAggregator>(
resp.ReleaseAsyncResp(), smart_router, query_parameters);
LOG(INFO) << "Starting expand query aggregator";
expand_query_aggregator->StartQuery();
}
void HandleExpandQueryParameter(RouterInterface* smart_router,
const crow::Router& tlbmc_router,
const RedfishRequest& req,
RedfishResponse& resp) {
QueryParameters query_parameters = req.GetQueryParameters();
if (query_parameters.GetExpandType() == ExpandType::kNone ||
query_parameters.GetExpandLevel() == 0) {
return;
}
LOG(INFO) << "HandleExpandQueryParameter: " << req.Target();
// tlbmc only requests are guaranteed to be synchronous, therefore we can
// perform synchronous expand.
if (req.TlbmcOnly()) {
SynchronousExpand(tlbmc_router, resp, query_parameters);
return;
}
// If smart router is nullptr, then return internal error.
if (smart_router == nullptr) {
resp.SetToAbslStatus(absl::InternalError("Smart router is nullptr"));
LOG(WARNING) << "Smart router is nullptr, cannot perform asynchronous "
"expand for request: "
<< req.Target();
return;
}
// Otherwise, we need to perform asynchronous expand.
AsynchronousExpand(smart_router, resp, query_parameters);
}
} // namespace
void RedfishPostprocess(RouterInterface* smart_router,
const crow::Router& tlbmc_router,
const RedfishRequest& req, RedfishResponse& resp) {
// Currently only supports expand query parameter.
HandleExpandQueryParameter(smart_router, tlbmc_router, req, resp);
}
} // namespace milotic_tlbmc