summaryrefslogtreecommitdiff
path: root/http
diff options
context:
space:
mode:
authorEd Tanous <edtanous@google.com>2023-04-06 23:10:02 +0300
committerEd Tanous <ed@tanous.net>2023-06-09 22:13:40 +0300
commit08bbe1199f02d09f908cd3adcf4329e4bd67fd52 (patch)
tree9824a615569928f65523f13a876a0def301562dd /http
parentb90d14f220cba6de26dcf8749b2f8df062487d72 (diff)
downloadbmcweb-08bbe1199f02d09f908cd3adcf4329e4bd67fd52.tar.xz
Break up router into separate files
The router is a giant behemoth. Start breaking it down into pieces. Tested: Redfish service validator passes. Signed-off-by: Ed Tanous <edtanous@google.com> Change-Id: I9d04f53a58ffce3ecbd88dded1aa6e9648d2a762
Diffstat (limited to 'http')
-rw-r--r--http/routing.hpp767
-rw-r--r--http/routing/baserule.hpp98
-rw-r--r--http/routing/dynamicrule.hpp233
-rw-r--r--http/routing/ruleparametertraits.hpp94
-rw-r--r--http/routing/sserule.hpp79
-rw-r--r--http/routing/taggedrule.hpp68
-rw-r--r--http/routing/websocketrule.hpp108
7 files changed, 686 insertions, 761 deletions
diff --git a/http/routing.hpp b/http/routing.hpp
index f3bcfbb065..97daef1fcf 100644
--- a/http/routing.hpp
+++ b/http/routing.hpp
@@ -2,13 +2,18 @@
#include "async_resp.hpp"
#include "common.hpp"
+#include "dbus_privileges.hpp"
#include "dbus_utility.hpp"
#include "error_messages.hpp"
#include "http_request.hpp"
#include "http_response.hpp"
#include "logging.hpp"
#include "privileges.hpp"
-#include "server_sent_event.hpp"
+#include "routing/baserule.hpp"
+#include "routing/dynamicrule.hpp"
+#include "routing/sserule.hpp"
+#include "routing/taggedrule.hpp"
+#include "routing/websocketrule.hpp"
#include "sessions.hpp"
#include "utility.hpp"
#include "utils/dbus_utils.hpp"
@@ -33,601 +38,6 @@
namespace crow
{
-class BaseRule
-{
- public:
- explicit BaseRule(const std::string& thisRule) : rule(thisRule) {}
-
- virtual ~BaseRule() = default;
-
- BaseRule(const BaseRule&) = delete;
- BaseRule(BaseRule&&) = delete;
- BaseRule& operator=(const BaseRule&) = delete;
- BaseRule& operator=(const BaseRule&&) = delete;
-
- virtual void validate() = 0;
- std::unique_ptr<BaseRule> upgrade()
- {
- if (ruleToUpgrade)
- {
- return std::move(ruleToUpgrade);
- }
- return {};
- }
-
- virtual void handle(const Request& /*req*/,
- const std::shared_ptr<bmcweb::AsyncResp>&,
- const std::vector<std::string>&) = 0;
-#ifndef BMCWEB_ENABLE_SSL
- virtual void
- handleUpgrade(const Request& /*req*/,
- const std::shared_ptr<bmcweb::AsyncResp>& asyncResp,
- boost::asio::ip::tcp::socket&& /*adaptor*/)
- {
- asyncResp->res.result(boost::beast::http::status::not_found);
- }
-#else
- virtual void handleUpgrade(
- const Request& /*req*/,
- const std::shared_ptr<bmcweb::AsyncResp>& asyncResp,
- boost::beast::ssl_stream<boost::asio::ip::tcp::socket>&& /*adaptor*/)
- {
- asyncResp->res.result(boost::beast::http::status::not_found);
- }
-#endif
-
- size_t getMethods() const
- {
- return methodsBitfield;
- }
-
- bool checkPrivileges(const redfish::Privileges& userPrivileges)
- {
- // If there are no privileges assigned, assume no privileges
- // required
- if (privilegesSet.empty())
- {
- return true;
- }
-
- for (const redfish::Privileges& requiredPrivileges : privilegesSet)
- {
- if (userPrivileges.isSupersetOf(requiredPrivileges))
- {
- return true;
- }
- }
- return false;
- }
-
- size_t methodsBitfield{1 << static_cast<size_t>(HttpVerb::Get)};
- static_assert(std::numeric_limits<decltype(methodsBitfield)>::digits >
- methodNotAllowedIndex,
- "Not enough bits to store bitfield");
-
- std::vector<redfish::Privileges> privilegesSet;
-
- std::string rule;
-
- std::unique_ptr<BaseRule> ruleToUpgrade;
-
- friend class Router;
- template <typename T>
- friend struct RuleParameterTraits;
-};
-
-namespace detail
-{
-namespace routing_handler_call_helper
-{
-template <typename T, int Pos>
-struct CallPair
-{
- using type = T;
- static const int pos = Pos;
-};
-
-template <typename H1>
-struct CallParams
-{
- H1& handler;
- const std::vector<std::string>& params;
- const Request& req;
- const std::shared_ptr<bmcweb::AsyncResp>& asyncResp;
-};
-
-template <typename F, int NString, typename S1, typename S2>
-struct Call
-{};
-
-template <typename F, int NString, typename... Args1, typename... Args2>
-struct Call<F, NString, black_magic::S<std::string, Args1...>,
- black_magic::S<Args2...>>
-{
- void operator()(F cparams)
- {
- using pushed = typename black_magic::S<Args2...>::template push_back<
- CallPair<std::string, NString>>;
- Call<F, NString + 1, black_magic::S<Args1...>, pushed>()(cparams);
- }
-};
-
-template <typename F, int NString, typename... Args1>
-struct Call<F, NString, black_magic::S<>, black_magic::S<Args1...>>
-{
- void operator()(F cparams)
- {
- cparams.handler(cparams.req, cparams.asyncResp,
- cparams.params[Args1::pos]...);
- }
-};
-
-template <typename Func, typename... ArgsWrapped>
-struct Wrapped
-{
- template <typename... Args>
- void set(
- Func f,
- typename std::enable_if<
- !std::is_same<
- typename std::tuple_element<0, std::tuple<Args..., void>>::type,
- const Request&>::value,
- int>::type /*enable*/
- = 0)
- {
- handler = [f = std::forward<Func>(f)](
- const Request&,
- const std::shared_ptr<bmcweb::AsyncResp>& asyncResp,
- Args... args) { asyncResp->res.result(f(args...)); };
- }
-
- template <typename Req, typename... Args>
- struct ReqHandlerWrapper
- {
- explicit ReqHandlerWrapper(Func fIn) : f(std::move(fIn)) {}
-
- void operator()(const Request& req,
- const std::shared_ptr<bmcweb::AsyncResp>& asyncResp,
- Args... args)
- {
- asyncResp->res.result(f(req, args...));
- }
-
- Func f;
- };
-
- template <typename... Args>
- void set(
- Func f,
- typename std::enable_if<
- std::is_same<
- typename std::tuple_element<0, std::tuple<Args..., void>>::type,
- const Request&>::value &&
- !std::is_same<typename std::tuple_element<
- 1, std::tuple<Args..., void, void>>::type,
- const std::shared_ptr<bmcweb::AsyncResp>&>::value,
- int>::type /*enable*/
- = 0)
- {
- handler = ReqHandlerWrapper<Args...>(std::move(f));
- /*handler = (
- [f = std::move(f)]
- (const Request& req, Response& res, Args... args){
- res.result(f(req, args...));
- res.end();
- });*/
- }
-
- template <typename... Args>
- void set(
- Func f,
- typename std::enable_if<
- std::is_same<
- typename std::tuple_element<0, std::tuple<Args..., void>>::type,
- const Request&>::value &&
- std::is_same<typename std::tuple_element<
- 1, std::tuple<Args..., void, void>>::type,
- const std::shared_ptr<bmcweb::AsyncResp>&>::value,
- int>::type /*enable*/
- = 0)
- {
- handler = std::move(f);
- }
-
- template <typename... Args>
- struct HandlerTypeHelper
- {
- using type = std::function<void(
- const crow::Request& /*req*/,
- const std::shared_ptr<bmcweb::AsyncResp>&, Args...)>;
- using args_type = black_magic::S<Args...>;
- };
-
- template <typename... Args>
- struct HandlerTypeHelper<const Request&, Args...>
- {
- using type = std::function<void(
- const crow::Request& /*req*/,
- const std::shared_ptr<bmcweb::AsyncResp>&, Args...)>;
- using args_type = black_magic::S<Args...>;
- };
-
- template <typename... Args>
- struct HandlerTypeHelper<const Request&,
- const std::shared_ptr<bmcweb::AsyncResp>&, Args...>
- {
- using type = std::function<void(
- const crow::Request& /*req*/,
- const std::shared_ptr<bmcweb::AsyncResp>&, Args...)>;
- using args_type = black_magic::S<Args...>;
- };
-
- typename HandlerTypeHelper<ArgsWrapped...>::type handler;
-
- void operator()(const Request& req,
- const std::shared_ptr<bmcweb::AsyncResp>& asyncResp,
- const std::vector<std::string>& params)
- {
- detail::routing_handler_call_helper::Call<
- detail::routing_handler_call_helper::CallParams<decltype(handler)>,
- 0, typename HandlerTypeHelper<ArgsWrapped...>::args_type,
- black_magic::S<>>()(
- detail::routing_handler_call_helper::CallParams<decltype(handler)>{
- handler, params, req, asyncResp});
- }
-};
-} // namespace routing_handler_call_helper
-} // namespace detail
-
-class WebSocketRule : public BaseRule
-{
- using self_t = WebSocketRule;
-
- public:
- explicit WebSocketRule(const std::string& ruleIn) : BaseRule(ruleIn) {}
-
- void validate() override {}
-
- void handle(const Request& /*req*/,
- const std::shared_ptr<bmcweb::AsyncResp>& asyncResp,
- const std::vector<std::string>& /*params*/) override
- {
- asyncResp->res.result(boost::beast::http::status::not_found);
- }
-
-#ifndef BMCWEB_ENABLE_SSL
- void handleUpgrade(const Request& req,
- const std::shared_ptr<bmcweb::AsyncResp>& /*asyncResp*/,
- boost::asio::ip::tcp::socket&& adaptor) override
- {
- BMCWEB_LOG_DEBUG << "Websocket handles upgrade";
- std::shared_ptr<
- crow::websocket::ConnectionImpl<boost::asio::ip::tcp::socket>>
- myConnection = std::make_shared<
- crow::websocket::ConnectionImpl<boost::asio::ip::tcp::socket>>(
- req, req.url(), std::move(adaptor), openHandler, messageHandler,
- messageExHandler, closeHandler, errorHandler);
- myConnection->start();
- }
-#else
- void handleUpgrade(const Request& req,
- const std::shared_ptr<bmcweb::AsyncResp>& /*asyncResp*/,
- boost::beast::ssl_stream<boost::asio::ip::tcp::socket>&&
- adaptor) override
- {
- BMCWEB_LOG_DEBUG << "Websocket handles upgrade";
- std::shared_ptr<crow::websocket::ConnectionImpl<
- boost::beast::ssl_stream<boost::asio::ip::tcp::socket>>>
- myConnection = std::make_shared<crow::websocket::ConnectionImpl<
- boost::beast::ssl_stream<boost::asio::ip::tcp::socket>>>(
- req, req.url(), std::move(adaptor), openHandler, messageHandler,
- messageExHandler, closeHandler, errorHandler);
- myConnection->start();
- }
-#endif
-
- template <typename Func>
- self_t& onopen(Func f)
- {
- openHandler = f;
- return *this;
- }
-
- template <typename Func>
- self_t& onmessage(Func f)
- {
- messageHandler = f;
- return *this;
- }
-
- template <typename Func>
- self_t& onmessageex(Func f)
- {
- messageExHandler = f;
- return *this;
- }
-
- template <typename Func>
- self_t& onclose(Func f)
- {
- closeHandler = f;
- return *this;
- }
-
- template <typename Func>
- self_t& onerror(Func f)
- {
- errorHandler = f;
- return *this;
- }
-
- protected:
- std::function<void(crow::websocket::Connection&)> openHandler;
- std::function<void(crow::websocket::Connection&, const std::string&, bool)>
- messageHandler;
- std::function<void(crow::websocket::Connection&, std::string_view,
- crow::websocket::MessageType type,
- std::function<void()>&& whenComplete)>
- messageExHandler;
- std::function<void(crow::websocket::Connection&, const std::string&)>
- closeHandler;
- std::function<void(crow::websocket::Connection&)> errorHandler;
-};
-
-class SseSocketRule : public BaseRule
-{
- using self_t = SseSocketRule;
-
- public:
- explicit SseSocketRule(const std::string& ruleIn) : BaseRule(ruleIn) {}
-
- void validate() override {}
-
- void handle(const Request& /*req*/,
- const std::shared_ptr<bmcweb::AsyncResp>& asyncResp,
- const std::vector<std::string>& /*params*/) override
- {
- asyncResp->res.result(boost::beast::http::status::not_found);
- }
-
-#ifndef BMCWEB_ENABLE_SSL
- void handleUpgrade(const Request& /*req*/,
- const std::shared_ptr<bmcweb::AsyncResp>& /*asyncResp*/,
- boost::asio::ip::tcp::socket&& adaptor) override
- {
- std::shared_ptr<
- crow::sse_socket::ConnectionImpl<boost::asio::ip::tcp::socket>>
- myConnection = std::make_shared<
- crow::sse_socket::ConnectionImpl<boost::asio::ip::tcp::socket>>(
- std::move(adaptor), openHandler, closeHandler);
- myConnection->start();
- }
-#else
- void handleUpgrade(const Request& /*req*/,
- const std::shared_ptr<bmcweb::AsyncResp>& /*asyncResp*/,
- boost::beast::ssl_stream<boost::asio::ip::tcp::socket>&&
- adaptor) override
- {
- std::shared_ptr<crow::sse_socket::ConnectionImpl<
- boost::beast::ssl_stream<boost::asio::ip::tcp::socket>>>
- myConnection = std::make_shared<crow::sse_socket::ConnectionImpl<
- boost::beast::ssl_stream<boost::asio::ip::tcp::socket>>>(
- std::move(adaptor), openHandler, closeHandler);
- myConnection->start();
- }
-#endif
-
- template <typename Func>
- self_t& onopen(Func f)
- {
- openHandler = f;
- return *this;
- }
-
- template <typename Func>
- self_t& onclose(Func f)
- {
- closeHandler = f;
- return *this;
- }
-
- private:
- std::function<void(crow::sse_socket::Connection&)> openHandler;
- std::function<void(crow::sse_socket::Connection&)> closeHandler;
-};
-
-template <typename T>
-struct RuleParameterTraits
-{
- using self_t = T;
- WebSocketRule& websocket()
- {
- self_t* self = static_cast<self_t*>(this);
- WebSocketRule* p = new WebSocketRule(self->rule);
- p->privilegesSet = self->privilegesSet;
- self->ruleToUpgrade.reset(p);
- return *p;
- }
-
- SseSocketRule& serverSentEvent()
- {
- self_t* self = static_cast<self_t*>(this);
- SseSocketRule* p = new SseSocketRule(self->rule);
- self->ruleToUpgrade.reset(p);
- return *p;
- }
-
- self_t& methods(boost::beast::http::verb method)
- {
- self_t* self = static_cast<self_t*>(this);
- std::optional<HttpVerb> verb = httpVerbFromBoost(method);
- if (verb)
- {
- self->methodsBitfield = 1U << static_cast<size_t>(*verb);
- }
- return *self;
- }
-
- template <typename... MethodArgs>
- self_t& methods(boost::beast::http::verb method, MethodArgs... argsMethod)
- {
- self_t* self = static_cast<self_t*>(this);
- methods(argsMethod...);
- std::optional<HttpVerb> verb = httpVerbFromBoost(method);
- if (verb)
- {
- self->methodsBitfield |= 1U << static_cast<size_t>(*verb);
- }
- return *self;
- }
-
- self_t& notFound()
- {
- self_t* self = static_cast<self_t*>(this);
- self->methodsBitfield = 1U << notFoundIndex;
- return *self;
- }
-
- self_t& methodNotAllowed()
- {
- self_t* self = static_cast<self_t*>(this);
- self->methodsBitfield = 1U << methodNotAllowedIndex;
- return *self;
- }
-
- self_t& privileges(
- const std::initializer_list<std::initializer_list<const char*>>& p)
- {
- self_t* self = static_cast<self_t*>(this);
- for (const std::initializer_list<const char*>& privilege : p)
- {
- self->privilegesSet.emplace_back(privilege);
- }
- return *self;
- }
-
- template <size_t N, typename... MethodArgs>
- self_t& privileges(const std::array<redfish::Privileges, N>& p)
- {
- self_t* self = static_cast<self_t*>(this);
- for (const redfish::Privileges& privilege : p)
- {
- self->privilegesSet.emplace_back(privilege);
- }
- return *self;
- }
-};
-
-class DynamicRule : public BaseRule, public RuleParameterTraits<DynamicRule>
-{
- public:
- explicit DynamicRule(const std::string& ruleIn) : BaseRule(ruleIn) {}
-
- void validate() override
- {
- if (!erasedHandler)
- {
- throw std::runtime_error("no handler for url " + rule);
- }
- }
-
- void handle(const Request& req,
- const std::shared_ptr<bmcweb::AsyncResp>& asyncResp,
- const std::vector<std::string>& params) override
- {
- erasedHandler(req, asyncResp, params);
- }
-
- template <typename Func>
- void operator()(Func f)
- {
- using boost::callable_traits::args_t;
- constexpr size_t arity = std::tuple_size<args_t<Func>>::value;
- constexpr auto is = std::make_integer_sequence<unsigned, arity>{};
- erasedHandler = wrap(std::move(f), is);
- }
-
- // enable_if Arg1 == request && Arg2 == Response
- // enable_if Arg1 == request && Arg2 != response
- // enable_if Arg1 != request
-
- template <typename Func, unsigned... Indices>
- std::function<void(const Request&,
- const std::shared_ptr<bmcweb::AsyncResp>&,
- const std::vector<std::string>&)>
- wrap(Func f, std::integer_sequence<unsigned, Indices...> /*is*/)
- {
- using function_t = crow::utility::FunctionTraits<Func>;
-
- auto ret = detail::routing_handler_call_helper::Wrapped<
- Func, typename function_t::template arg<Indices>...>();
- ret.template set<typename function_t::template arg<Indices>...>(
- std::move(f));
- return ret;
- }
-
- private:
- std::function<void(const Request&,
- const std::shared_ptr<bmcweb::AsyncResp>&,
- const std::vector<std::string>&)>
- erasedHandler;
-};
-
-template <typename... Args>
-class TaggedRule :
- public BaseRule,
- public RuleParameterTraits<TaggedRule<Args...>>
-{
- public:
- using self_t = TaggedRule<Args...>;
-
- explicit TaggedRule(const std::string& ruleIn) : BaseRule(ruleIn) {}
-
- void validate() override
- {
- if (!handler)
- {
- throw std::runtime_error("no handler for url " + rule);
- }
- }
-
- template <typename Func>
- void operator()(Func&& f)
- {
- static_assert(
- black_magic::CallHelper<
- Func, black_magic::S<crow::Request,
- std::shared_ptr<bmcweb::AsyncResp>&,
- Args...>>::value,
- "Handler type is mismatched with URL parameters");
- static_assert(
- std::is_same<
- void,
- decltype(f(std::declval<crow::Request>(),
- std::declval<std::shared_ptr<bmcweb::AsyncResp>&>(),
- std::declval<Args>()...))>::value,
- "Handler function with response argument should have void return type");
-
- handler = std::forward<Func>(f);
- }
-
- void handle(const Request& req,
- const std::shared_ptr<bmcweb::AsyncResp>& asyncResp,
- const std::vector<std::string>& params) override
- {
- detail::routing_handler_call_helper::Call<
- detail::routing_handler_call_helper::CallParams<decltype(handler)>,
- 0, black_magic::S<Args...>, black_magic::S<>>()(
- detail::routing_handler_call_helper::CallParams<decltype(handler)>{
- handler, params, req, asyncResp});
- }
-
- private:
- std::function<void(const crow::Request&,
- const std::shared_ptr<bmcweb::AsyncResp>&, Args...)>
- handler;
-};
-
class Trie
{
public:
@@ -1100,171 +510,6 @@ class Router
return findRoute;
}
- // Populate session with user information.
- static bool
- populateUserInfo(Request& req,
- const std::shared_ptr<bmcweb::AsyncResp>& asyncResp,
- const dbus::utility::DBusPropertiesMap& userInfoMap)
- {
- const std::string* userRolePtr = nullptr;
- const bool* remoteUser = nullptr;
- const bool* passwordExpired = nullptr;
- const std::vector<std::string>* userGroups = nullptr;
-
- const bool success = sdbusplus::unpackPropertiesNoThrow(
- redfish::dbus_utils::UnpackErrorPrinter(), userInfoMap,
- "UserPrivilege", userRolePtr, "RemoteUser", remoteUser,
- "UserPasswordExpired", passwordExpired, "UserGroups", userGroups);
-
- if (!success)
- {
- BMCWEB_LOG_ERROR << "Failed to unpack user properties.";
- asyncResp->res.result(
- boost::beast::http::status::internal_server_error);
- return false;
- }
-
- if (userRolePtr != nullptr)
- {
- req.session->userRole = *userRolePtr;
- BMCWEB_LOG_DEBUG << "userName = " << req.session->username
- << " userRole = " << *userRolePtr;
- }
-
- if (remoteUser == nullptr)
- {
- BMCWEB_LOG_ERROR << "RemoteUser property missing or wrong type";
- asyncResp->res.result(
- boost::beast::http::status::internal_server_error);
- return false;
- }
- bool expired = false;
- if (passwordExpired == nullptr)
- {
- if (!*remoteUser)
- {
- BMCWEB_LOG_ERROR
- << "UserPasswordExpired property is expected for"
- " local user but is missing or wrong type";
- asyncResp->res.result(
- boost::beast::http::status::internal_server_error);
- return false;
- }
- }
- else
- {
- expired = *passwordExpired;
- }
-
- // Set isConfigureSelfOnly based on D-Bus results. This
- // ignores the results from both pamAuthenticateUser and the
- // value from any previous use of this session.
- req.session->isConfigureSelfOnly = expired;
-
- if (userGroups != nullptr)
- {
- // Populate session with user groups.
- for (const auto& userGroup : *userGroups)
- {
- req.session->userGroups.emplace_back(userGroup);
- }
- }
-
- return true;
- }
-
- static bool
- isUserPrivileged(Request& req,
- const std::shared_ptr<bmcweb::AsyncResp>& asyncResp,
- BaseRule& rule)
- {
- // Get the user's privileges from the role
- redfish::Privileges userPrivileges =
- redfish::getUserPrivileges(*req.session);
-
- // Modify privileges if isConfigureSelfOnly.
- if (req.session->isConfigureSelfOnly)
- {
- // Remove all privileges except ConfigureSelf
- userPrivileges = userPrivileges.intersection(
- redfish::Privileges{"ConfigureSelf"});
- BMCWEB_LOG_DEBUG << "Operation limited to ConfigureSelf";
- }
-
- if (!rule.checkPrivileges(userPrivileges))
- {
- asyncResp->res.result(boost::beast::http::status::forbidden);
- if (req.session->isConfigureSelfOnly)
- {
- redfish::messages::passwordChangeRequired(
- asyncResp->res,
- boost::urls::format(
- "/redfish/v1/AccountService/Accounts/{}",
- req.session->username));
- }
- return false;
- }
-
- req.userRole = req.session->userRole;
- return true;
- }
-
- template <typename CallbackFn>
- void afterGetUserInfo(Request& req,
- const std::shared_ptr<bmcweb::AsyncResp>& asyncResp,
- BaseRule& rule, CallbackFn&& callback,
- const boost::system::error_code& ec,
- const dbus::utility::DBusPropertiesMap& userInfoMap)
- {
- if (ec)
- {
- BMCWEB_LOG_ERROR << "GetUserInfo failed...";
- asyncResp->res.result(
- boost::beast::http::status::internal_server_error);
- return;
- }
-
- if (!populateUserInfo(req, asyncResp, userInfoMap))
- {
- BMCWEB_LOG_ERROR << "Failed to populate user information";
- asyncResp->res.result(
- boost::beast::http::status::internal_server_error);
- return;
- }
-
- if (!Router::isUserPrivileged(req, asyncResp, rule))
- {
- // User is not privileged
- BMCWEB_LOG_ERROR << "Insufficient Privilege";
- asyncResp->res.result(boost::beast::http::status::forbidden);
- return;
- }
- callback(req);
- }
-
- template <typename CallbackFn>
- void validatePrivilege(Request& req,
- const std::shared_ptr<bmcweb::AsyncResp>& asyncResp,
- BaseRule& rule, CallbackFn&& callback)
- {
- if (req.session == nullptr)
- {
- return;
- }
- std::string username = req.session->username;
- crow::connections::systemBus->async_method_call(
- [this, &req, asyncResp, &rule,
- callback(std::forward<CallbackFn>(callback))](
- const boost::system::error_code& ec,
- const dbus::utility::DBusPropertiesMap& userInfoMap) mutable {
- afterGetUserInfo(req, asyncResp, rule,
- std::forward<CallbackFn>(callback), ec,
- userInfoMap);
- },
- "xyz.openbmc_project.User.Manager", "/xyz/openbmc_project/user",
- "xyz.openbmc_project.User.Manager", "GetUserInfo", username);
- }
-
template <typename Adaptor>
void handleUpgrade(Request& req,
const std::shared_ptr<bmcweb::AsyncResp>& asyncResp,
diff --git a/http/routing/baserule.hpp b/http/routing/baserule.hpp
new file mode 100644
index 0000000000..0913020935
--- /dev/null
+++ b/http/routing/baserule.hpp
@@ -0,0 +1,98 @@
+#pragma once
+
+#include "async_resp.hpp"
+#include "http_request.hpp"
+#include "privileges.hpp"
+#include "verb.hpp"
+
+#include <boost/beast/ssl/ssl_stream.hpp>
+
+#include <memory>
+#include <string>
+
+namespace crow
+{
+class BaseRule
+{
+ public:
+ explicit BaseRule(const std::string& thisRule) : rule(thisRule) {}
+
+ virtual ~BaseRule() = default;
+
+ BaseRule(const BaseRule&) = delete;
+ BaseRule(BaseRule&&) = delete;
+ BaseRule& operator=(const BaseRule&) = delete;
+ BaseRule& operator=(const BaseRule&&) = delete;
+
+ virtual void validate() = 0;
+ std::unique_ptr<BaseRule> upgrade()
+ {
+ if (ruleToUpgrade)
+ {
+ return std::move(ruleToUpgrade);
+ }
+ return {};
+ }
+
+ virtual void handle(const Request& /*req*/,
+ const std::shared_ptr<bmcweb::AsyncResp>&,
+ const std::vector<std::string>&) = 0;
+#ifndef BMCWEB_ENABLE_SSL
+ virtual void
+ handleUpgrade(const Request& /*req*/,
+ const std::shared_ptr<bmcweb::AsyncResp>& asyncResp,
+ boost::asio::ip::tcp::socket&& /*adaptor*/)
+ {
+ asyncResp->res.result(boost::beast::http::status::not_found);
+ }
+#else
+ virtual void handleUpgrade(
+ const Request& /*req*/,
+ const std::shared_ptr<bmcweb::AsyncResp>& asyncResp,
+ boost::beast::ssl_stream<boost::asio::ip::tcp::socket>&& /*adaptor*/)
+ {
+ asyncResp->res.result(boost::beast::http::status::not_found);
+ }
+#endif
+
+ size_t getMethods() const
+ {
+ return methodsBitfield;
+ }
+
+ bool checkPrivileges(const redfish::Privileges& userPrivileges)
+ {
+ // If there are no privileges assigned, assume no privileges
+ // required
+ if (privilegesSet.empty())
+ {
+ return true;
+ }
+
+ for (const redfish::Privileges& requiredPrivileges : privilegesSet)
+ {
+ if (userPrivileges.isSupersetOf(requiredPrivileges))
+ {
+ return true;
+ }
+ }
+ return false;
+ }
+
+ size_t methodsBitfield{1 << static_cast<size_t>(HttpVerb::Get)};
+ static_assert(std::numeric_limits<decltype(methodsBitfield)>::digits >
+ methodNotAllowedIndex,
+ "Not enough bits to store bitfield");
+
+ std::vector<redfish::Privileges> privilegesSet;
+
+ std::string rule;
+
+ std::unique_ptr<BaseRule> ruleToUpgrade;
+
+ friend class Router;
+ template <typename T>
+ friend struct RuleParameterTraits;
+};
+
+} // namespace crow
diff --git a/http/routing/dynamicrule.hpp b/http/routing/dynamicrule.hpp
new file mode 100644
index 0000000000..746765cf78
--- /dev/null
+++ b/http/routing/dynamicrule.hpp
@@ -0,0 +1,233 @@
+#pragma once
+#include "baserule.hpp"
+#include "ruleparametertraits.hpp"
+#include "websocket.hpp"
+
+#include <boost/beast/http/verb.hpp>
+
+#include <functional>
+#include <limits>
+#include <string>
+#include <type_traits>
+
+namespace crow
+{
+namespace detail
+{
+namespace routing_handler_call_helper
+{
+template <typename T, int Pos>
+struct CallPair
+{
+ using type = T;
+ static const int pos = Pos;
+};
+
+template <typename H1>
+struct CallParams
+{
+ H1& handler;
+ const std::vector<std::string>& params;
+ const Request& req;
+ const std::shared_ptr<bmcweb::AsyncResp>& asyncResp;
+};
+
+template <typename F, int NString, typename S1, typename S2>
+struct Call
+{};
+
+template <typename F, int NString, typename... Args1, typename... Args2>
+struct Call<F, NString, black_magic::S<std::string, Args1...>,
+ black_magic::S<Args2...>>
+{
+ void operator()(F cparams)
+ {
+ using pushed = typename black_magic::S<Args2...>::template push_back<
+ CallPair<std::string, NString>>;
+ Call<F, NString + 1, black_magic::S<Args1...>, pushed>()(cparams);
+ }
+};
+
+template <typename F, int NString, typename... Args1>
+struct Call<F, NString, black_magic::S<>, black_magic::S<Args1...>>
+{
+ void operator()(F cparams)
+ {
+ cparams.handler(cparams.req, cparams.asyncResp,
+ cparams.params[Args1::pos]...);
+ }
+};
+
+template <typename Func, typename... ArgsWrapped>
+struct Wrapped
+{
+ template <typename... Args>
+ void set(
+ Func f,
+ typename std::enable_if<
+ !std::is_same<
+ typename std::tuple_element<0, std::tuple<Args..., void>>::type,
+ const Request&>::value,
+ int>::type /*enable*/
+ = 0)
+ {
+ handler = [f = std::forward<Func>(f)](
+ const Request&,
+ const std::shared_ptr<bmcweb::AsyncResp>& asyncResp,
+ Args... args) { asyncResp->res.result(f(args...)); };
+ }
+
+ template <typename Req, typename... Args>
+ struct ReqHandlerWrapper
+ {
+ explicit ReqHandlerWrapper(Func fIn) : f(std::move(fIn)) {}
+
+ void operator()(const Request& req,
+ const std::shared_ptr<bmcweb::AsyncResp>& asyncResp,
+ Args... args)
+ {
+ asyncResp->res.result(f(req, args...));
+ }
+
+ Func f;
+ };
+
+ template <typename... Args>
+ void set(
+ Func f,
+ typename std::enable_if<
+ std::is_same<
+ typename std::tuple_element<0, std::tuple<Args..., void>>::type,
+ const Request&>::value &&
+ !std::is_same<typename std::tuple_element<
+ 1, std::tuple<Args..., void, void>>::type,
+ const std::shared_ptr<bmcweb::AsyncResp>&>::value,
+ int>::type /*enable*/
+ = 0)
+ {
+ handler = ReqHandlerWrapper<Args...>(std::move(f));
+ /*handler = (
+ [f = std::move(f)]
+ (const Request& req, Response& res, Args... args){
+ res.result(f(req, args...));
+ res.end();
+ });*/
+ }
+
+ template <typename... Args>
+ void set(
+ Func f,
+ typename std::enable_if<
+ std::is_same<
+ typename std::tuple_element<0, std::tuple<Args..., void>>::type,
+ const Request&>::value &&
+ std::is_same<typename std::tuple_element<
+ 1, std::tuple<Args..., void, void>>::type,
+ const std::shared_ptr<bmcweb::AsyncResp>&>::value,
+ int>::type /*enable*/
+ = 0)
+ {
+ handler = std::move(f);
+ }
+
+ template <typename... Args>
+ struct HandlerTypeHelper
+ {
+ using type = std::function<void(
+ const crow::Request& /*req*/,
+ const std::shared_ptr<bmcweb::AsyncResp>&, Args...)>;
+ using args_type = black_magic::S<Args...>;
+ };
+
+ template <typename... Args>
+ struct HandlerTypeHelper<const Request&, Args...>
+ {
+ using type = std::function<void(
+ const crow::Request& /*req*/,
+ const std::shared_ptr<bmcweb::AsyncResp>&, Args...)>;
+ using args_type = black_magic::S<Args...>;
+ };
+
+ template <typename... Args>
+ struct HandlerTypeHelper<const Request&,
+ const std::shared_ptr<bmcweb::AsyncResp>&, Args...>
+ {
+ using type = std::function<void(
+ const crow::Request& /*req*/,
+ const std::shared_ptr<bmcweb::AsyncResp>&, Args...)>;
+ using args_type = black_magic::S<Args...>;
+ };
+
+ typename HandlerTypeHelper<ArgsWrapped...>::type handler;
+
+ void operator()(const Request& req,
+ const std::shared_ptr<bmcweb::AsyncResp>& asyncResp,
+ const std::vector<std::string>& params)
+ {
+ detail::routing_handler_call_helper::Call<
+ detail::routing_handler_call_helper::CallParams<decltype(handler)>,
+ 0, typename HandlerTypeHelper<ArgsWrapped...>::args_type,
+ black_magic::S<>>()(
+ detail::routing_handler_call_helper::CallParams<decltype(handler)>{
+ handler, params, req, asyncResp});
+ }
+};
+} // namespace routing_handler_call_helper
+} // namespace detail
+
+class DynamicRule : public BaseRule, public RuleParameterTraits<DynamicRule>
+{
+ public:
+ explicit DynamicRule(const std::string& ruleIn) : BaseRule(ruleIn) {}
+
+ void validate() override
+ {
+ if (!erasedHandler)
+ {
+ throw std::runtime_error("no handler for url " + rule);
+ }
+ }
+
+ void handle(const Request& req,
+ const std::shared_ptr<bmcweb::AsyncResp>& asyncResp,
+ const std::vector<std::string>& params) override
+ {
+ erasedHandler(req, asyncResp, params);
+ }
+
+ template <typename Func>
+ void operator()(Func f)
+ {
+ using boost::callable_traits::args_t;
+ constexpr size_t arity = std::tuple_size<args_t<Func>>::value;
+ constexpr auto is = std::make_integer_sequence<unsigned, arity>{};
+ erasedHandler = wrap(std::move(f), is);
+ }
+
+ // enable_if Arg1 == request && Arg2 == Response
+ // enable_if Arg1 == request && Arg2 != response
+ // enable_if Arg1 != request
+
+ template <typename Func, unsigned... Indices>
+ std::function<void(const Request&,
+ const std::shared_ptr<bmcweb::AsyncResp>&,
+ const std::vector<std::string>&)>
+ wrap(Func f, std::integer_sequence<unsigned, Indices...> /*is*/)
+ {
+ using function_t = crow::utility::FunctionTraits<Func>;
+
+ auto ret = detail::routing_handler_call_helper::Wrapped<
+ Func, typename function_t::template arg<Indices>...>();
+ ret.template set<typename function_t::template arg<Indices>...>(
+ std::move(f));
+ return ret;
+ }
+
+ private:
+ std::function<void(const Request&,
+ const std::shared_ptr<bmcweb::AsyncResp>&,
+ const std::vector<std::string>&)>
+ erasedHandler;
+};
+
+} // namespace crow
diff --git a/http/routing/ruleparametertraits.hpp b/http/routing/ruleparametertraits.hpp
new file mode 100644
index 0000000000..bb3f563301
--- /dev/null
+++ b/http/routing/ruleparametertraits.hpp
@@ -0,0 +1,94 @@
+#pragma once
+
+#include "sserule.hpp"
+#include "websocketrule.hpp"
+
+#include <boost/beast/http/verb.hpp>
+
+#include <initializer_list>
+#include <optional>
+
+namespace crow
+{
+template <typename T>
+struct RuleParameterTraits
+{
+ using self_t = T;
+ WebSocketRule& websocket()
+ {
+ self_t* self = static_cast<self_t*>(this);
+ WebSocketRule* p = new WebSocketRule(self->rule);
+ p->privilegesSet = self->privilegesSet;
+ self->ruleToUpgrade.reset(p);
+ return *p;
+ }
+
+ SseSocketRule& serverSentEvent()
+ {
+ self_t* self = static_cast<self_t*>(this);
+ SseSocketRule* p = new SseSocketRule(self->rule);
+ self->ruleToUpgrade.reset(p);
+ return *p;
+ }
+
+ self_t& methods(boost::beast::http::verb method)
+ {
+ self_t* self = static_cast<self_t*>(this);
+ std::optional<HttpVerb> verb = httpVerbFromBoost(method);
+ if (verb)
+ {
+ self->methodsBitfield = 1U << static_cast<size_t>(*verb);
+ }
+ return *self;
+ }
+
+ template <typename... MethodArgs>
+ self_t& methods(boost::beast::http::verb method, MethodArgs... argsMethod)
+ {
+ self_t* self = static_cast<self_t*>(this);
+ methods(argsMethod...);
+ std::optional<HttpVerb> verb = httpVerbFromBoost(method);
+ if (verb)
+ {
+ self->methodsBitfield |= 1U << static_cast<size_t>(*verb);
+ }
+ return *self;
+ }
+
+ self_t& notFound()
+ {
+ self_t* self = static_cast<self_t*>(this);
+ self->methodsBitfield = 1U << notFoundIndex;
+ return *self;
+ }
+
+ self_t& methodNotAllowed()
+ {
+ self_t* self = static_cast<self_t*>(this);
+ self->methodsBitfield = 1U << methodNotAllowedIndex;
+ return *self;
+ }
+
+ self_t& privileges(
+ const std::initializer_list<std::initializer_list<const char*>>& p)
+ {
+ self_t* self = static_cast<self_t*>(this);
+ for (const std::initializer_list<const char*>& privilege : p)
+ {
+ self->privilegesSet.emplace_back(privilege);
+ }
+ return *self;
+ }
+
+ template <size_t N, typename... MethodArgs>
+ self_t& privileges(const std::array<redfish::Privileges, N>& p)
+ {
+ self_t* self = static_cast<self_t*>(this);
+ for (const redfish::Privileges& privilege : p)
+ {
+ self->privilegesSet.emplace_back(privilege);
+ }
+ return *self;
+ }
+};
+} // namespace crow
diff --git a/http/routing/sserule.hpp b/http/routing/sserule.hpp
new file mode 100644
index 0000000000..c0a4e504b3
--- /dev/null
+++ b/http/routing/sserule.hpp
@@ -0,0 +1,79 @@
+#pragma once
+
+#include "baserule.hpp"
+#include "http_request.hpp"
+#include "http_response.hpp"
+#include "server_sent_event.hpp"
+
+#include <boost/beast/http/verb.hpp>
+
+#include <functional>
+#include <memory>
+#include <string>
+
+namespace crow
+{
+
+class SseSocketRule : public BaseRule
+{
+ using self_t = SseSocketRule;
+
+ public:
+ explicit SseSocketRule(const std::string& ruleIn) : BaseRule(ruleIn) {}
+
+ void validate() override {}
+
+ void handle(const Request& /*req*/,
+ const std::shared_ptr<bmcweb::AsyncResp>& asyncResp,
+ const std::vector<std::string>& /*params*/) override
+ {
+ asyncResp->res.result(boost::beast::http::status::not_found);
+ }
+
+#ifndef BMCWEB_ENABLE_SSL
+ void handleUpgrade(const Request& /*req*/,
+ const std::shared_ptr<bmcweb::AsyncResp>& /*asyncResp*/,
+ boost::asio::ip::tcp::socket&& adaptor) override
+ {
+ std::shared_ptr<
+ crow::sse_socket::ConnectionImpl<boost::asio::ip::tcp::socket>>
+ myConnection = std::make_shared<
+ crow::sse_socket::ConnectionImpl<boost::asio::ip::tcp::socket>>(
+ std::move(adaptor), openHandler, closeHandler);
+ myConnection->start();
+ }
+#else
+ void handleUpgrade(const Request& /*req*/,
+ const std::shared_ptr<bmcweb::AsyncResp>& /*asyncResp*/,
+ boost::beast::ssl_stream<boost::asio::ip::tcp::socket>&&
+ adaptor) override
+ {
+ std::shared_ptr<crow::sse_socket::ConnectionImpl<
+ boost::beast::ssl_stream<boost::asio::ip::tcp::socket>>>
+ myConnection = std::make_shared<crow::sse_socket::ConnectionImpl<
+ boost::beast::ssl_stream<boost::asio::ip::tcp::socket>>>(
+ std::move(adaptor), openHandler, closeHandler);
+ myConnection->start();
+ }
+#endif
+
+ template <typename Func>
+ self_t& onopen(Func f)
+ {
+ openHandler = f;
+ return *this;
+ }
+
+ template <typename Func>
+ self_t& onclose(Func f)
+ {
+ closeHandler = f;
+ return *this;
+ }
+
+ private:
+ std::function<void(crow::sse_socket::Connection&)> openHandler;
+ std::function<void(crow::sse_socket::Connection&)> closeHandler;
+};
+
+} // namespace crow
diff --git a/http/routing/taggedrule.hpp b/http/routing/taggedrule.hpp
new file mode 100644
index 0000000000..ef62ab08da
--- /dev/null
+++ b/http/routing/taggedrule.hpp
@@ -0,0 +1,68 @@
+#pragma once
+#include "baserule.hpp"
+#include "dynamicrule.hpp"
+#include "ruleparametertraits.hpp"
+
+#include <boost/beast/http/verb.hpp>
+
+#include <memory>
+#include <string>
+#include <vector>
+
+namespace crow
+{
+template <typename... Args>
+class TaggedRule :
+ public BaseRule,
+ public RuleParameterTraits<TaggedRule<Args...>>
+{
+ public:
+ using self_t = TaggedRule<Args...>;
+
+ explicit TaggedRule(const std::string& ruleIn) : BaseRule(ruleIn) {}
+
+ void validate() override
+ {
+ if (!handler)
+ {
+ throw std::runtime_error("no handler for url " + rule);
+ }
+ }
+
+ template <typename Func>
+ void operator()(Func&& f)
+ {
+ static_assert(
+ black_magic::CallHelper<
+ Func, black_magic::S<crow::Request,
+ std::shared_ptr<bmcweb::AsyncResp>&,
+ Args...>>::value,
+ "Handler type is mismatched with URL parameters");
+ static_assert(
+ std::is_same<
+ void,
+ decltype(f(std::declval<crow::Request>(),
+ std::declval<std::shared_ptr<bmcweb::AsyncResp>&>(),
+ std::declval<Args>()...))>::value,
+ "Handler function with response argument should have void return type");
+
+ handler = std::forward<Func>(f);
+ }
+
+ void handle(const Request& req,
+ const std::shared_ptr<bmcweb::AsyncResp>& asyncResp,
+ const std::vector<std::string>& params) override
+ {
+ detail::routing_handler_call_helper::Call<
+ detail::routing_handler_call_helper::CallParams<decltype(handler)>,
+ 0, black_magic::S<Args...>, black_magic::S<>>()(
+ detail::routing_handler_call_helper::CallParams<decltype(handler)>{
+ handler, params, req, asyncResp});
+ }
+
+ private:
+ std::function<void(const crow::Request&,
+ const std::shared_ptr<bmcweb::AsyncResp>&, Args...)>
+ handler;
+};
+} // namespace crow
diff --git a/http/routing/websocketrule.hpp b/http/routing/websocketrule.hpp
new file mode 100644
index 0000000000..c8f706db51
--- /dev/null
+++ b/http/routing/websocketrule.hpp
@@ -0,0 +1,108 @@
+#pragma once
+
+#include "baserule.hpp"
+#include "websocket.hpp"
+
+#include <boost/beast/http/verb.hpp>
+
+#include <memory>
+#include <string>
+#include <vector>
+
+namespace crow
+{
+class WebSocketRule : public BaseRule
+{
+ using self_t = WebSocketRule;
+
+ public:
+ explicit WebSocketRule(const std::string& ruleIn) : BaseRule(ruleIn) {}
+
+ void validate() override {}
+
+ void handle(const Request& /*req*/,
+ const std::shared_ptr<bmcweb::AsyncResp>& asyncResp,
+ const std::vector<std::string>& /*params*/) override
+ {
+ asyncResp->res.result(boost::beast::http::status::not_found);
+ }
+
+#ifndef BMCWEB_ENABLE_SSL
+ void handleUpgrade(const Request& req,
+ const std::shared_ptr<bmcweb::AsyncResp>& /*asyncResp*/,
+ boost::asio::ip::tcp::socket&& adaptor) override
+ {
+ BMCWEB_LOG_DEBUG << "Websocket handles upgrade";
+ std::shared_ptr<
+ crow::websocket::ConnectionImpl<boost::asio::ip::tcp::socket>>
+ myConnection = std::make_shared<
+ crow::websocket::ConnectionImpl<boost::asio::ip::tcp::socket>>(
+ req, req.url(), std::move(adaptor), openHandler, messageHandler,
+ messageExHandler, closeHandler, errorHandler);
+ myConnection->start();
+ }
+#else
+ void handleUpgrade(const Request& req,
+ const std::shared_ptr<bmcweb::AsyncResp>& /*asyncResp*/,
+ boost::beast::ssl_stream<boost::asio::ip::tcp::socket>&&
+ adaptor) override
+ {
+ BMCWEB_LOG_DEBUG << "Websocket handles upgrade";
+ std::shared_ptr<crow::websocket::ConnectionImpl<
+ boost::beast::ssl_stream<boost::asio::ip::tcp::socket>>>
+ myConnection = std::make_shared<crow::websocket::ConnectionImpl<
+ boost::beast::ssl_stream<boost::asio::ip::tcp::socket>>>(
+ req, req.url(), std::move(adaptor), openHandler, messageHandler,
+ messageExHandler, closeHandler, errorHandler);
+ myConnection->start();
+ }
+#endif
+
+ template <typename Func>
+ self_t& onopen(Func f)
+ {
+ openHandler = f;
+ return *this;
+ }
+
+ template <typename Func>
+ self_t& onmessage(Func f)
+ {
+ messageHandler = f;
+ return *this;
+ }
+
+ template <typename Func>
+ self_t& onmessageex(Func f)
+ {
+ messageExHandler = f;
+ return *this;
+ }
+
+ template <typename Func>
+ self_t& onclose(Func f)
+ {
+ closeHandler = f;
+ return *this;
+ }
+
+ template <typename Func>
+ self_t& onerror(Func f)
+ {
+ errorHandler = f;
+ return *this;
+ }
+
+ protected:
+ std::function<void(crow::websocket::Connection&)> openHandler;
+ std::function<void(crow::websocket::Connection&, const std::string&, bool)>
+ messageHandler;
+ std::function<void(crow::websocket::Connection&, std::string_view,
+ crow::websocket::MessageType type,
+ std::function<void()>&& whenComplete)>
+ messageExHandler;
+ std::function<void(crow::websocket::Connection&, const std::string&)>
+ closeHandler;
+ std::function<void(crow::websocket::Connection&)> errorHandler;
+};
+} // namespace crow