| #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 |