Move AccountService to node structure

AccountService was the last service hanging on to the old way of doing
things.  This moves it up to our current standard using the Node class.

Tested by: Ran service validator.  Saw no errors.

Change-Id: I84d0097f48803cb06d2ec95171f18bff04661666
Signed-off-by: Ed Tanous <ed.tanous@intel.com>
diff --git a/include/openbmc_dbus_rest.hpp b/include/openbmc_dbus_rest.hpp
index 7ed8e16..e0d9986 100644
--- a/include/openbmc_dbus_rest.hpp
+++ b/include/openbmc_dbus_rest.hpp
@@ -1,3 +1,5 @@
+#pragma once
+
 #include <crow/app.h>
 #include <tinyxml2.h>
 
diff --git a/include/redfish_v1.hpp b/include/redfish_v1.hpp
index 13e1838..f1dbfd2 100644
--- a/include/redfish_v1.hpp
+++ b/include/redfish_v1.hpp
@@ -13,13 +13,6 @@
 {
 namespace redfish
 {
-
-using ManagedObjectType = std::vector<std::pair<
-    sdbusplus::message::object_path,
-    boost::container::flat_map<
-        std::string, boost::container::flat_map<
-                         std::string, sdbusplus::message::variant<bool>>>>>;
-
 template <typename... Middlewares> void requestRoutes(Crow<Middlewares...>& app)
 {
     BMCWEB_ROUTE(app, "/redfish/")
@@ -28,122 +21,6 @@
                 res.jsonValue = {{"v1", "/redfish/v1/"}};
                 res.end();
             });
-
-    BMCWEB_ROUTE(app, "/redfish/v1/AccountService/Accounts/")
-        .methods(
-            "GET"_method)([&](const crow::Request& req, crow::Response& res) {
-            crow::connections::systemBus->async_method_call(
-                [&](const boost::system::error_code ec,
-                    const ManagedObjectType& users) {
-                    if (ec)
-                    {
-                        res.result(
-                            boost::beast::http::status::internal_server_error);
-                    }
-                    else
-                    {
-                        res.jsonValue = {
-                            {"@odata.context",
-                             "/redfish/v1/"
-                             "$metadata#ManagerAccountCollection."
-                             "ManagerAccountCollection"},
-                            {"@odata.id",
-                             "/redfish/v1/AccountService/Accounts"},
-                            {"@odata.type", "#ManagerAccountCollection."
-                                            "ManagerAccountCollection"},
-                            {"Name", "Accounts Collection"},
-                            {"Description", "BMC User Accounts"},
-                            {"Members@odata.count", users.size()}};
-                        nlohmann::json memberArray = nlohmann::json::array();
-                        int userIndex = 0;
-                        for (auto& user : users)
-                        {
-                            const std::string& path =
-                                static_cast<const std::string&>(user.first);
-                            std::size_t lastIndex = path.rfind("/");
-                            if (lastIndex == std::string::npos)
-                            {
-                                lastIndex = 0;
-                            }
-                            else
-                            {
-                                lastIndex += 1;
-                            }
-                            memberArray.push_back(
-                                {{"@odata.id",
-                                  "/redfish/v1/AccountService/Accounts/" +
-                                      path.substr(lastIndex)}});
-                        }
-                        res.jsonValue["Members"] = memberArray;
-                    }
-                    res.end();
-                },
-                "xyz.openbmc_project.User.Manager", "/xyz/openbmc_project/user",
-                "org.freedesktop.DBus.ObjectManager", "GetManagedObjects");
-        });
-
-    BMCWEB_ROUTE(app, "/redfish/v1/AccountService/Accounts/<str>/")
-        .methods("GET"_method)([](const crow::Request& req, crow::Response& res,
-                                  const std::string& account_name) {
-            crow::connections::systemBus->async_method_call(
-                [&, accountName{std::move(account_name)}](
-                    const boost::system::error_code ec,
-                    const ManagedObjectType& users) {
-                    if (ec)
-                    {
-                        res.result(
-                            boost::beast::http::status::internal_server_error);
-                    }
-                    else
-                    {
-                        for (auto& user : users)
-                        {
-                            const std::string& path =
-                                static_cast<const std::string&>(user.first);
-                            std::size_t lastIndex = path.rfind("/");
-                            if (lastIndex == std::string::npos)
-                            {
-                                lastIndex = 0;
-                            }
-                            else
-                            {
-                                lastIndex += 1;
-                            }
-                            if (path.substr(lastIndex) == accountName)
-                            {
-                                res.jsonValue = {
-                                    {"@odata.context",
-                                     "/redfish/v1/"
-                                     "$metadata#ManagerAccount.ManagerAccount"},
-                                    {"@odata.id",
-                                     "/redfish/v1/AccountService/Accounts/1"},
-                                    {"@odata.type",
-                                     "#ManagerAccount.v1_0_3.ManagerAccount"},
-                                    {"Id", "1"},
-                                    {"Name", "User Account"},
-                                    {"Description", "User Account"},
-                                    {"Enabled", false},
-                                    {"Password", nullptr},
-                                    {"UserName", accountName},
-                                    {"RoleId", "Administrator"},
-                                    {"Links",
-                                     {{"Role",
-                                       {{"@odata.id",
-                                         "/redfish/v1/AccountService/Roles/"
-                                         "Administrator"}}}}}};
-                                break;
-                            }
-                        }
-                        if (res.jsonValue.is_null())
-                        {
-                            res.result(boost::beast::http::status::not_found);
-                        }
-                    }
-                    res.end();
-                },
-                "xyz.openbmc_project.User.Manager", "/xyz/openbmc_project/user",
-                "org.freedesktop.DBus.ObjectManager", "GetManagedObjects");
-        });
 }
 } // namespace redfish
 } // namespace crow
diff --git a/redfish-core/include/redfish.hpp b/redfish-core/include/redfish.hpp
index 6e990c9..76bed8b 100644
--- a/redfish-core/include/redfish.hpp
+++ b/redfish-core/include/redfish.hpp
@@ -46,6 +46,8 @@
     RedfishService(CrowApp& app)
     {
         nodes.emplace_back(std::make_unique<AccountService>(app));
+        nodes.emplace_back(std::make_unique<AccountsCollection>(app));
+        nodes.emplace_back(std::make_unique<ManagerAccount>(app));
         nodes.emplace_back(std::make_unique<SessionCollection>(app));
         nodes.emplace_back(std::make_unique<Roles>(app));
         nodes.emplace_back(std::make_unique<RoleCollection>(app));
diff --git a/redfish-core/lib/account_service.hpp b/redfish-core/lib/account_service.hpp
index c58cafd..23e77a7 100644
--- a/redfish-core/lib/account_service.hpp
+++ b/redfish-core/lib/account_service.hpp
@@ -14,12 +14,19 @@
 // limitations under the License.
 */
 #pragma once
-
 #include "node.hpp"
 
+#include <openbmc_dbus_rest.hpp>
+
 namespace redfish
 {
 
+using ManagedObjectType = std::vector<std::pair<
+    sdbusplus::message::object_path,
+    boost::container::flat_map<
+        std::string, boost::container::flat_map<
+                         std::string, sdbusplus::message::variant<bool>>>>>;
+
 class AccountService : public Node
 {
   public:
@@ -57,5 +64,160 @@
         res.end();
     }
 };
+class AccountsCollection : public Node
+{
+  public:
+    AccountsCollection(CrowApp& app) :
+        Node(app, "/redfish/v1/AccountService/Accounts/")
+    {
+
+        Node::json = {{"@odata.context", "/redfish/v1/"
+                                         "$metadata#ManagerAccountCollection."
+                                         "ManagerAccountCollection"},
+                      {"@odata.id", "/redfish/v1/AccountService/Accounts"},
+                      {"@odata.type", "#ManagerAccountCollection."
+                                      "ManagerAccountCollection"},
+                      {"Name", "Accounts Collection"},
+                      {"Description", "BMC User Accounts"}};
+
+        entityPrivileges = {
+            {boost::beast::http::verb::get,
+             {{"ConfigureUsers"}, {"ConfigureManager"}}},
+            {boost::beast::http::verb::head, {{"Login"}}},
+            {boost::beast::http::verb::patch, {{"ConfigureUsers"}}},
+            {boost::beast::http::verb::put, {{"ConfigureUsers"}}},
+            {boost::beast::http::verb::delete_, {{"ConfigureUsers"}}},
+            {boost::beast::http::verb::post, {{"ConfigureUsers"}}}};
+    }
+
+  private:
+    void doGet(crow::Response& res, const crow::Request& req,
+               const std::vector<std::string>& params) override
+    {
+        res.jsonValue = Node::json;
+        auto asyncResp = std::make_shared<AsyncResp>(res);
+        crow::connections::systemBus->async_method_call(
+            [asyncResp](const boost::system::error_code ec,
+                        const ManagedObjectType& users) {
+                if (ec)
+                {
+                    asyncResp->res.result(
+                        boost::beast::http::status::internal_server_error);
+                    return;
+                }
+
+                nlohmann::json& memberArray =
+                    asyncResp->res.jsonValue["Members"];
+                memberArray = nlohmann::json::array();
+
+                asyncResp->res.jsonValue["Members@odata.count"] = users.size();
+                for (auto& user : users)
+                {
+                    const std::string& path =
+                        static_cast<const std::string&>(user.first);
+                    std::size_t lastIndex = path.rfind("/");
+                    if (lastIndex == std::string::npos)
+                    {
+                        lastIndex = 0;
+                    }
+                    else
+                    {
+                        lastIndex += 1;
+                    }
+                    memberArray.push_back(
+                        {{"@odata.id", "/redfish/v1/AccountService/Accounts/" +
+                                           path.substr(lastIndex)}});
+                }
+            },
+            "xyz.openbmc_project.User.Manager", "/xyz/openbmc_project/user",
+            "org.freedesktop.DBus.ObjectManager", "GetManagedObjects");
+    }
+};
+
+class ManagerAccount : public Node
+{
+  public:
+    ManagerAccount(CrowApp& app) :
+        Node(app, "/redfish/v1/AccountService/Accounts/<str>/", std::string())
+    {
+        Node::json = {{"@odata.context",
+                       "/redfish/v1/$metadata#ManagerAccount.ManagerAccount"},
+                      {"@odata.type", "#ManagerAccount.v1_0_3.ManagerAccount"},
+
+                      {"Name", "User Account"},
+                      {"Description", "User Account"},
+                      {"Enabled", false},
+                      {"Password", nullptr},
+                      {"RoleId", "Administrator"},
+                      {"Links",
+                       {{"Role",
+                         {{"@odata.id", "/redfish/v1/AccountService/Roles/"
+                                        "Administrator"}}}}}};
+
+        entityPrivileges = {
+            {boost::beast::http::verb::get,
+             {{"ConfigureUsers"}, {"ConfigureManager"}, {"ConfigureSelf"}}},
+            {boost::beast::http::verb::head, {{"Login"}}},
+            {boost::beast::http::verb::patch, {{"ConfigureUsers"}}},
+            {boost::beast::http::verb::put, {{"ConfigureUsers"}}},
+            {boost::beast::http::verb::delete_, {{"ConfigureUsers"}}},
+            {boost::beast::http::verb::post, {{"ConfigureUsers"}}}};
+    }
+
+  private:
+    void doGet(crow::Response& res, const crow::Request& req,
+               const std::vector<std::string>& params) override
+    {
+        res.jsonValue = Node::json;
+        auto asyncResp = std::make_shared<AsyncResp>(res);
+
+        if (params.size() != 1)
+        {
+            res.result(boost::beast::http::status::internal_server_error);
+            return;
+        }
+
+        crow::connections::systemBus->async_method_call(
+            [asyncResp, accountName{std::string(params[0])}](
+                const boost::system::error_code ec,
+                const ManagedObjectType& users) {
+                if (ec)
+                {
+                    asyncResp->res.result(
+                        boost::beast::http::status::internal_server_error);
+                    return;
+                }
+
+                for (auto& user : users)
+                {
+                    const std::string& path =
+                        static_cast<const std::string&>(user.first);
+                    std::size_t lastIndex = path.rfind("/");
+                    if (lastIndex == std::string::npos)
+                    {
+                        lastIndex = 0;
+                    }
+                    else
+                    {
+                        lastIndex += 1;
+                    }
+                    if (path.substr(lastIndex) == accountName)
+                    {
+                        asyncResp->res.jsonValue["@odata.id"] =
+                            "/redfish/v1/AccountService/Accounts/" +
+                            accountName;
+                        asyncResp->res.jsonValue["Id"] = accountName;
+                        asyncResp->res.jsonValue["UserName"] = accountName;
+
+                        return;
+                    }
+                }
+
+                asyncResp->res.result(boost::beast::http::status::not_found);
+            },
+            "xyz.openbmc_project.User.Manager", "/xyz/openbmc_project/user",
+            "org.freedesktop.DBus.ObjectManager", "GetManagedObjects");
+    }
+};
 
 } // namespace redfish