diff options
Diffstat (limited to 'http/routing.hpp')
-rw-r--r-- | http/routing.hpp | 767 |
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, |