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