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
diff --git a/http/routing.hpp b/http/routing.hpp
index f3bcfbb..97daef1 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 @@
         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 0000000..0913020
--- /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 0000000..746765c
--- /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 0000000..bb3f563
--- /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 0000000..c0a4e50
--- /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 0000000..ef62ab0
--- /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 0000000..c8f706d
--- /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
diff --git a/include/dbus_privileges.hpp b/include/dbus_privileges.hpp
new file mode 100644
index 0000000..377a41c
--- /dev/null
+++ b/include/dbus_privileges.hpp
@@ -0,0 +1,180 @@
+#pragma once
+
+#include "dbus_utility.hpp"
+#include "error_messages.hpp"
+#include "http_request.hpp"
+#include "http_response.hpp"
+#include "logging.hpp"
+#include "routing/baserule.hpp"
+#include "utils/dbus_utils.hpp"
+
+#include <boost/url/format.hpp>
+#include <sdbusplus/unpack_properties.hpp>
+
+#include <memory>
+#include <vector>
+
+namespace crow
+{
+// Populate session with user information.
+inline 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;
+}
+
+inline 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 (!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(
+        [&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);
+}
+
+} // namespace crow