summaryrefslogtreecommitdiff
path: root/http/routing.hpp
diff options
context:
space:
mode:
Diffstat (limited to 'http/routing.hpp')
-rw-r--r--http/routing.hpp767
1 files changed, 6 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,