Allow multiple registrations

This patchset is the beginings of the infrastructure to allow
separate registrations, and map privileges to the actual node in the
url table rather than having each registration manage privileges
manually.

Tested by:
Running redfish compliance tool.  All things still pass.

Change-Id: I72d278cc19c60ba5b6e563fbd705b0551faf9a6a
Signed-off-by: Ed Tanous <ed.tanous@intel.com>
diff --git a/crow/include/crow/app.h b/crow/include/crow/app.h
index f1f3687..95bbaed 100644
--- a/crow/include/crow/app.h
+++ b/crow/include/crow/app.h
@@ -1,5 +1,7 @@
 #pragma once
 
+#include "privileges.hpp"
+
 #include <chrono>
 #include <cstdint>
 #include <functional>
diff --git a/crow/include/crow/routing.h b/crow/include/crow/routing.h
index 746e115..e657e2e 100644
--- a/crow/include/crow/routing.h
+++ b/crow/include/crow/routing.h
@@ -1,7 +1,9 @@
 #pragma once
 
-#include "boost/container/flat_map.hpp"
+#include "privileges.hpp"
 
+#include <boost/container/flat_map.hpp>
+#include <boost/container/small_vector.hpp>
 #include <boost/lexical_cast.hpp>
 #include <cerrno>
 #include <cstdint>
@@ -21,10 +23,14 @@
 
 namespace crow
 {
+
+constexpr int maxHttpVerbCount =
+    static_cast<int>(boost::beast::http::verb::unlink);
+
 class BaseRule
 {
   public:
-    BaseRule(std::string rule) : rule(std::move(rule))
+    BaseRule(std::string thisRule) : rule(std::move(thisRule))
     {
     }
 
@@ -62,9 +68,29 @@
         return methodsBitfield;
     }
 
-  protected:
+    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;
+    }
+
     uint32_t methodsBitfield{1 << (int)boost::beast::http::verb::get};
 
+    std::vector<redfish::Privileges> privilegesSet;
+
     std::string rule;
     std::string nameStr;
 
@@ -178,16 +204,11 @@
                 const Request&>::value,
             int>::type = 0)
     {
-        handler = (
-#ifdef BMCWEB_CAN_USE_CPP14
-            [f = std::move(f)]
-#else
-            [f]
-#endif
-            (const Request&, Response& res, Args... args) {
-                res = Response(f(args...));
-                res.end();
-            });
+        handler = [f = std::move(f)](const Request&, Response& res,
+                                     Args... args) {
+            res = Response(f(args...));
+            res.end();
+        };
     }
 
     template <typename Req, typename... Args> struct ReqHandlerWrapper
@@ -362,7 +383,7 @@
     using self_t = T;
     WebSocketRule& websocket()
     {
-        auto p = new WebSocketRule(((self_t*)this)->rule);
+        WebSocketRule* p = new WebSocketRule(((self_t*)this)->rule);
         ((self_t*)this)->ruleToUpgrade.reset(p);
         return *p;
     }
@@ -386,6 +407,23 @@
         ((self_t*)this)->methodsBitfield |= 1 << (int)method;
         return (self_t&)*this;
     }
+
+    template <typename... MethodArgs>
+    self_t& requires(std::initializer_list<const char*> l)
+    {
+        ((self_t*)this)->privilegesSet.emplace_back(l);
+        return (self_t&)*this;
+    }
+
+    template <typename... MethodArgs>
+    self_t& requires(const std::vector<redfish::Privileges>& p)
+    {
+        for (const redfish::Privileges& privilege : p)
+        {
+            ((self_t*)this)->privilegesSet.emplace_back(privilege);
+        }
+        return (self_t&)*this;
+    }
 };
 
 class DynamicRule : public BaseRule, public RuleParameterTraits<DynamicRule>
@@ -547,7 +585,8 @@
             std::is_same<void, decltype(f(std::declval<crow::Request>(),
                                           std::declval<crow::Response&>(),
                                           std::declval<Args>()...))>::value,
-            "Handler function with response argument should have void return "
+            "Handler function with response argument should have void "
+            "return "
             "type");
 
         handler = std::move(f);
@@ -599,7 +638,7 @@
   private:
     void optimizeNode(Node* node)
     {
-        for (auto x : node->paramChildrens)
+        for (unsigned int x : node->paramChildrens)
         {
             if (!x)
                 continue;
@@ -609,7 +648,7 @@
         if (node->children.empty())
             return;
         bool mergeWithChild = true;
-        for (auto& kv : node->children)
+        for (const std::pair<std::string, unsigned>& kv : node->children)
         {
             Node* child = &nodes[kv.second];
             if (!child->isSimpleNode())
@@ -621,10 +660,11 @@
         if (mergeWithChild)
         {
             decltype(node->children) merged;
-            for (auto& kv : node->children)
+            for (const std::pair<std::string, unsigned>& kv : node->children)
             {
                 Node* child = &nodes[kv.second];
-                for (auto& childKv : child->children)
+                for (const std::pair<std::string, unsigned>& childKv :
+                     child->children)
                 {
                     merged[kv.first + childKv.first] = childKv.second;
                 }
@@ -634,7 +674,7 @@
         }
         else
         {
-            for (auto& kv : node->children)
+            for (const std::pair<std::string, unsigned>& kv : node->children)
             {
                 Node* child = &nodes[kv.second];
                 optimizeNode(child);
@@ -658,13 +698,13 @@
 
     void findRouteIndexes(const std::string& req_url,
                           std::vector<unsigned>& route_indexes,
-                          const Node* node = nullptr, unsigned pos = 0)
+                          const Node* node = nullptr, unsigned pos = 0) const
     {
         if (node == nullptr)
         {
             node = head();
         }
-        for (auto& kv : node->children)
+        for (const std::pair<std::string, unsigned>& kv : node->children)
         {
             const std::string& fragment = kv.first;
             const Node* child = &nodes[kv.second];
@@ -725,7 +765,7 @@
                 if (errno != ERANGE && eptr != req_url.data() + pos)
                 {
                     params->intParams.push_back(value);
-                    auto ret =
+                    std::pair<unsigned, RoutingParams> ret =
                         find(req_url,
                              &nodes[node->paramChildrens[(int)ParamType::INT]],
                              eptr - req_url.data(), params);
@@ -747,7 +787,7 @@
                 if (errno != ERANGE && eptr != req_url.data() + pos)
                 {
                     params->uintParams.push_back(value);
-                    auto ret =
+                    std::pair<unsigned, RoutingParams> ret =
                         find(req_url,
                              &nodes[node->paramChildrens[(int)ParamType::UINT]],
                              eptr - req_url.data(), params);
@@ -768,7 +808,7 @@
                 if (errno != ERANGE && eptr != req_url.data() + pos)
                 {
                     params->doubleParams.push_back(value);
-                    auto ret = find(
+                    std::pair<unsigned, RoutingParams> ret = find(
                         req_url,
                         &nodes[node->paramChildrens[(int)ParamType::DOUBLE]],
                         eptr - req_url.data(), params);
@@ -791,7 +831,7 @@
             {
                 params->stringParams.emplace_back(
                     req_url.substr(pos, epos - pos));
-                auto ret =
+                std::pair<unsigned, RoutingParams> ret =
                     find(req_url,
                          &nodes[node->paramChildrens[(int)ParamType::STRING]],
                          epos, params);
@@ -808,7 +848,7 @@
             {
                 params->stringParams.emplace_back(
                     req_url.substr(pos, epos - pos));
-                auto ret = find(
+                std::pair<unsigned, RoutingParams> ret = find(
                     req_url, &nodes[node->paramChildrens[(int)ParamType::PATH]],
                     epos, params);
                 updateFound(ret);
@@ -816,14 +856,15 @@
             }
         }
 
-        for (auto& kv : node->children)
+        for (const std::pair<std::string, unsigned>& kv : node->children)
         {
             const std::string& fragment = kv.first;
             const Node* child = &nodes[kv.second];
 
             if (req_url.compare(pos, fragment.size(), fragment) == 0)
             {
-                auto ret = find(req_url, child, pos + fragment.size(), params);
+                std::pair<unsigned, RoutingParams> ret =
+                    find(req_url, child, pos + fragment.size(), params);
                 updateFound(ret);
             }
         }
@@ -840,31 +881,29 @@
             char c = url[i];
             if (c == '<')
             {
-                static struct ParamTraits
-                {
-                    ParamType type;
-                    std::string name;
-                } paramTraits[] = {
-                    {ParamType::INT, "<int>"},
-                    {ParamType::UINT, "<uint>"},
-                    {ParamType::DOUBLE, "<float>"},
-                    {ParamType::DOUBLE, "<double>"},
-                    {ParamType::STRING, "<str>"},
-                    {ParamType::STRING, "<string>"},
-                    {ParamType::PATH, "<path>"},
-                };
+                const static std::array<std::pair<ParamType, std::string>, 7>
+                    paramTraits = {{
+                        {ParamType::INT, "<int>"},
+                        {ParamType::UINT, "<uint>"},
+                        {ParamType::DOUBLE, "<float>"},
+                        {ParamType::DOUBLE, "<double>"},
+                        {ParamType::STRING, "<str>"},
+                        {ParamType::STRING, "<string>"},
+                        {ParamType::PATH, "<path>"},
+                    }};
 
-                for (auto& x : paramTraits)
+                for (const std::pair<ParamType, std::string>& x : paramTraits)
                 {
-                    if (url.compare(i, x.name.size(), x.name) == 0)
+                    if (url.compare(i, x.second.size(), x.second) == 0)
                     {
-                        if (!nodes[idx].paramChildrens[(int)x.type])
+                        if (!nodes[idx].paramChildrens[(int)x.first])
                         {
-                            auto newNodeIdx = newNode();
-                            nodes[idx].paramChildrens[(int)x.type] = newNodeIdx;
+                            unsigned newNodeIdx = newNode();
+                            nodes[idx].paramChildrens[(int)x.first] =
+                                newNodeIdx;
                         }
-                        idx = nodes[idx].paramChildrens[(int)x.type];
-                        i += x.name.size();
+                        idx = nodes[idx].paramChildrens[(int)x.first];
+                        i += x.second.size();
                         break;
                     }
                 }
@@ -876,7 +915,7 @@
                 std::string piece(&c, 1);
                 if (!nodes[idx].children.count(piece))
                 {
-                    auto newNodeIdx = newNode();
+                    unsigned newNodeIdx = newNode();
                     nodes[idx].children.emplace(piece, newNodeIdx);
                 }
                 idx = nodes[idx].children[piece];
@@ -921,7 +960,7 @@
                 debugNodePrint(&nodes[n->paramChildrens[i]], level + 1);
             }
         }
-        for (auto& kv : n->children)
+        for (const std::pair<std::string, unsigned>& kv : n->children)
         {
             BMCWEB_LOG_DEBUG
                 << std::string(2 * level, ' ') /*<< "(" << kv.second << ") "*/
@@ -959,7 +998,7 @@
 class Router
 {
   public:
-    Router() : rules(2)
+    Router()
     {
     }
 
@@ -968,7 +1007,7 @@
         std::unique_ptr<DynamicRule> ruleObject =
             std::make_unique<DynamicRule>(rule);
         DynamicRule* ptr = ruleObject.get();
-        internalAddRuleObject(rule, std::move(ruleObject));
+        allRules.emplace_back(std::move(ruleObject));
 
         return *ptr;
     }
@@ -981,45 +1020,67 @@
             TaggedRule>;
         std::unique_ptr<RuleT> ruleObject = std::make_unique<RuleT>(rule);
         RuleT* ptr = ruleObject.get();
-
-        internalAddRuleObject(rule, std::move(ruleObject));
+        allRules.emplace_back(std::move(ruleObject));
 
         return *ptr;
     }
 
-    void internalAddRuleObject(const std::string& rule,
-                               std::unique_ptr<BaseRule> ruleObject)
+    void internalAddRuleObject(const std::string& rule, BaseRule* ruleObject)
     {
-        rules.emplace_back(std::move(ruleObject));
-        trie.add(rule, rules.size() - 1);
-
-        // directory case:
-        //   request to `/about' url matches `/about/' rule
-        if (rule.size() > 2 && rule.back() == '/')
+        if (ruleObject == nullptr)
         {
-            trie.add(rule.substr(0, rule.size() - 1), rules.size() - 1);
+            return;
+        }
+        for (uint32_t method = 0, method_bit = 1; method < maxHttpVerbCount;
+             method++, method_bit <<= 1)
+        {
+            if (ruleObject->methodsBitfield & method_bit)
+            {
+                perMethods[method].rules.emplace_back(ruleObject);
+                perMethods[method].trie.add(
+                    rule, perMethods[method].rules.size() - 1);
+                // directory case:
+                //   request to `/about' url matches `/about/' rule
+                if (rule.size() > 2 && rule.back() == '/')
+                {
+                    perMethods[method].trie.add(
+                        rule.substr(0, rule.size() - 1),
+                        perMethods[method].rules.size() - 1);
+                }
+            }
         }
     }
 
     void validate()
     {
-        trie.validate();
-        for (auto& rule : rules)
+        for (std::unique_ptr<BaseRule>& rule : allRules)
         {
             if (rule)
             {
-                auto upgraded = rule->upgrade();
+                std::unique_ptr<BaseRule> upgraded = rule->upgrade();
                 if (upgraded)
                     rule = std::move(upgraded);
                 rule->validate();
+                internalAddRuleObject(rule->rule, rule.get());
             }
         }
+        for (PerMethod& perMethod : perMethods)
+        {
+            perMethod.trie.validate();
+        }
     }
 
     template <typename Adaptor>
     void handleUpgrade(const Request& req, Response& res, Adaptor&& adaptor)
     {
-        auto found = trie.find(req.url);
+        if (static_cast<int>(req.method()) >= perMethods.size())
+            return;
+
+        PerMethod& perMethod = perMethods[(int)req.method()];
+        Trie& trie = perMethod.trie;
+        std::vector<BaseRule*>& rules = perMethod.rules;
+
+        const std::pair<unsigned, RoutingParams>& found = trie.find(req.url);
         unsigned ruleIndex = found.first;
         if (!ruleIndex)
         {
@@ -1097,7 +1158,13 @@
 
     void handle(const Request& req, Response& res)
     {
-        auto found = trie.find(req.url);
+        if ((int)req.method() >= perMethods.size())
+            return;
+        PerMethod& perMethod = perMethods[(int)req.method()];
+        Trie& trie = perMethod.trie;
+        std::vector<BaseRule*>& rules = perMethod.rules;
+
+        const std::pair<unsigned, RoutingParams>& found = trie.find(req.url);
 
         unsigned ruleIndex = found.first;
 
@@ -1150,6 +1217,19 @@
                          << (uint32_t)req.method() << " / "
                          << rules[ruleIndex]->getMethods();
 
+        // TODO: load user privileges from configuration as soon as its
+        // available now we are granting all privileges to everyone.
+        redfish::Privileges userPrivileges{"Login", "ConfigureManager",
+                                           "ConfigureSelf", "ConfigureUsers",
+                                           "ConfigureComponents"};
+
+        if (!rules[ruleIndex]->checkPrivileges(userPrivileges))
+        {
+            res.result(boost::beast::http::status::method_not_allowed);
+            res.end();
+            return;
+        }
+
         // any uncaught exceptions become 500s
         try
         {
@@ -1175,23 +1255,41 @@
 
     void debugPrint()
     {
-        trie.debugPrint();
+        for (int i = 0; i < perMethods.size(); i++)
+        {
+            BMCWEB_LOG_DEBUG << methodName((boost::beast::http::verb)i);
+            perMethods[i].trie.debugPrint();
+        }
     }
 
     std::vector<const std::string*> getRoutes(const std::string& parent)
     {
-        std::vector<unsigned> x;
         std::vector<const std::string*> ret;
-        trie.findRouteIndexes(parent, x);
-        for (unsigned index : x)
+
+        for (const PerMethod& pm : perMethods)
         {
-            ret.push_back(&rules[index]->rule);
+            std::vector<unsigned> x;
+            pm.trie.findRouteIndexes(parent, x);
+            for (unsigned index : x)
+            {
+                ret.push_back(&pm.rules[index]->rule);
+            }
         }
         return ret;
     }
 
   private:
-    std::vector<std::unique_ptr<BaseRule>> rules;
-    Trie trie;
+    struct PerMethod
+    {
+        std::vector<BaseRule*> rules;
+        Trie trie;
+        // rule index 0, 1 has special meaning; preallocate it to avoid
+        // duplication.
+        PerMethod() : rules(2)
+        {
+        }
+    };
+    std::array<PerMethod, maxHttpVerbCount> perMethods;
+    std::vector<std::unique_ptr<BaseRule>> allRules;
 };
 } // namespace crow
diff --git a/include/http_utility.hpp b/include/http_utility.hpp
index e2b1a11..b20952b 100644
--- a/include/http_utility.hpp
+++ b/include/http_utility.hpp
@@ -1,6 +1,8 @@
 #pragma once
 #include <boost/algorithm/string.hpp>
 
+#include "crow/http_request.h"
+
 namespace http_helpers
 {
 inline bool requestPrefersHtml(const crow::Request& req)
diff --git a/include/openbmc_dbus_rest.hpp b/include/openbmc_dbus_rest.hpp
index ab35bb2..9f7a855 100644
--- a/include/openbmc_dbus_rest.hpp
+++ b/include/openbmc_dbus_rest.hpp
@@ -1982,6 +1982,7 @@
 template <typename... Middlewares> void requestRoutes(Crow<Middlewares...> &app)
 {
     BMCWEB_ROUTE(app, "/bus/")
+        .requires({"Login"})
         .methods("GET"_method)(
             [](const crow::Request &req, crow::Response &res) {
                 res.jsonValue = {{"busses", {{{"name", "system"}}}},
@@ -1990,6 +1991,7 @@
             });
 
     BMCWEB_ROUTE(app, "/bus/system/")
+        .requires({"Login"})
         .methods("GET"_method)(
             [](const crow::Request &req, crow::Response &res) {
                 auto myCallback = [&res](const boost::system::error_code ec,
@@ -2018,13 +2020,23 @@
             });
 
     BMCWEB_ROUTE(app, "/list/")
+        .requires({"Login"})
         .methods("GET"_method)(
             [](const crow::Request &req, crow::Response &res) {
                 handleList(res, "/");
             });
 
     BMCWEB_ROUTE(app, "/xyz/<path>")
-        .methods("GET"_method, "PUT"_method, "POST"_method, "DELETE"_method)(
+        .requires({"Login"})
+        .methods("GET"_method)([](const crow::Request &req, crow::Response &res,
+                                  const std::string &path) {
+            std::string objectPath = "/xyz/" + path;
+            handleDBusUrl(req, res, objectPath);
+        });
+
+    BMCWEB_ROUTE(app, "/xyz/<path>")
+        .requires({"ConfigureComponents", "ConfigureManager"})
+        .methods("PUT"_method, "POST"_method, "DELETE"_method)(
             [](const crow::Request &req, crow::Response &res,
                const std::string &path) {
                 std::string objectPath = "/xyz/" + path;
@@ -2032,14 +2044,24 @@
             });
 
     BMCWEB_ROUTE(app, "/org/<path>")
-        .methods("GET"_method, "PUT"_method, "POST"_method, "DELETE"_method)(
+        .requires({"Login"})
+        .methods("GET"_method)([](const crow::Request &req, crow::Response &res,
+                                  const std::string &path) {
+            std::string objectPath = "/xyz/" + path;
+            handleDBusUrl(req, res, objectPath);
+        });
+
+    BMCWEB_ROUTE(app, "/org/<path>")
+        .requires({"ConfigureComponents", "ConfigureManager"})
+        .methods("PUT"_method, "POST"_method, "DELETE"_method)(
             [](const crow::Request &req, crow::Response &res,
                const std::string &path) {
-                std::string objectPath = "/org/" + path;
+                std::string objectPath = "/xyz/" + path;
                 handleDBusUrl(req, res, objectPath);
             });
 
     BMCWEB_ROUTE(app, "/download/dump/<str>/")
+        .requires({"ConfigureManager"})
         .methods("GET"_method)([](const crow::Request &req, crow::Response &res,
                                   const std::string &dumpId) {
             std::regex validFilename("^[\\w\\- ]+(\\.?[\\w\\- ]*)$");
@@ -2083,6 +2105,7 @@
         });
 
     BMCWEB_ROUTE(app, "/bus/system/<str>/")
+        .requires({"Login"})
         .methods("GET"_method)([](const crow::Request &req, crow::Response &res,
                                   const std::string &Connection) {
             introspectObjects(Connection, "/",
diff --git a/redfish-core/include/node.hpp b/redfish-core/include/node.hpp
index 8e94a6f..5819527 100644
--- a/redfish-core/include/node.hpp
+++ b/redfish-core/include/node.hpp
@@ -57,20 +57,88 @@
     template <typename... Params>
     Node(CrowApp& app, std::string&& entityUrl, Params... params)
     {
-        app.routeDynamic(entityUrl.c_str())
-            .methods("GET"_method, "PATCH"_method, "POST"_method,
-                     "DELETE"_method)([&](const crow::Request& req,
-                                          crow::Response& res,
-                                          Params... params) {
-                std::vector<std::string> paramVec = {params...};
-                dispatchRequest(app, req, res, paramVec);
-            });
+        crow::DynamicRule& get = app.routeDynamic(entityUrl.c_str());
+        getRule = &get;
+        get.methods("GET"_method)([this](const crow::Request& req,
+                                         crow::Response& res,
+                                         Params... params) {
+            std::vector<std::string> paramVec = {params...};
+            doGet(res, req, paramVec);
+        });
+
+        crow::DynamicRule& patch = app.routeDynamic(entityUrl.c_str());
+        patchRule = &patch;
+        patch.methods("PATCH"_method)([this](const crow::Request& req,
+                                             crow::Response& res,
+                                             Params... params) {
+            std::vector<std::string> paramVec = {params...};
+            doPatch(res, req, paramVec);
+        });
+
+        crow::DynamicRule& post = app.routeDynamic(entityUrl.c_str());
+        postRule = &post;
+        post.methods("POST"_method)([this](const crow::Request& req,
+                                           crow::Response& res,
+                                           Params... params) {
+            std::vector<std::string> paramVec = {params...};
+            doPost(res, req, paramVec);
+        });
+
+        crow::DynamicRule& delete_ = app.routeDynamic(entityUrl.c_str());
+        deleteRule = &delete_;
+        delete_.methods("DELETE"_method)([this](const crow::Request& req,
+                                                crow::Response& res,
+                                                Params... params) {
+            std::vector<std::string> paramVec = {params...};
+            doDelete(res, req, paramVec);
+        });
+    }
+
+    void initPrivileges()
+    {
+        auto it = entityPrivileges.find(boost::beast::http::verb::get);
+        if (it != entityPrivileges.end())
+        {
+            if (getRule != nullptr)
+            {
+                getRule->requires(it->second);
+            }
+        }
+        it = entityPrivileges.find(boost::beast::http::verb::post);
+        if (it != entityPrivileges.end())
+        {
+            if (postRule != nullptr)
+            {
+                postRule->requires(it->second);
+            }
+        }
+        it = entityPrivileges.find(boost::beast::http::verb::patch);
+        if (it != entityPrivileges.end())
+        {
+            if (patchRule != nullptr)
+            {
+                patchRule->requires(it->second);
+            }
+        }
+        it = entityPrivileges.find(boost::beast::http::verb::delete_);
+        if (it != entityPrivileges.end())
+        {
+            if (deleteRule != nullptr)
+            {
+                deleteRule->requires(it->second);
+            }
+        }
     }
 
     virtual ~Node() = default;
 
     OperationMap entityPrivileges;
 
+    crow::DynamicRule* getRule = nullptr;
+    crow::DynamicRule* postRule = nullptr;
+    crow::DynamicRule* patchRule = nullptr;
+    crow::DynamicRule* deleteRule = nullptr;
+
   protected:
     // Node is designed to be an abstract class, so doGet is pure virtual
     virtual void doGet(crow::Response& res, const crow::Request& req,
@@ -100,47 +168,6 @@
         res.result(boost::beast::http::status::method_not_allowed);
         res.end();
     }
-
-  private:
-    void dispatchRequest(CrowApp& app, const crow::Request& req,
-                         crow::Response& res,
-                         const std::vector<std::string>& params)
-    {
-        auto ctx =
-            app.template getContext<crow::token_authorization::Middleware>(req);
-
-        if (!isMethodAllowedForUser(req.method(), entityPrivileges,
-                                    ctx.session->username))
-        {
-            res.result(boost::beast::http::status::method_not_allowed);
-            res.end();
-            return;
-        }
-
-        switch (req.method())
-        {
-            case "GET"_method:
-                doGet(res, req, params);
-                break;
-
-            case "PATCH"_method:
-                doPatch(res, req, params);
-                break;
-
-            case "POST"_method:
-                doPost(res, req, params);
-                break;
-
-            case "DELETE"_method:
-                doDelete(res, req, params);
-                break;
-
-            default:
-                res.result(boost::beast::http::status::not_found);
-                res.end();
-        }
-        return;
-    }
 };
 
 } // namespace redfish
diff --git a/redfish-core/include/privileges.hpp b/redfish-core/include/privileges.hpp
index 3b20c9f..ca44551 100644
--- a/redfish-core/include/privileges.hpp
+++ b/redfish-core/include/privileges.hpp
@@ -15,7 +15,11 @@
 */
 #pragma once
 
+#include <crow/logging.h>
+
+#include <array>
 #include <bitset>
+#include <boost/beast/http/verb.hpp>
 #include <boost/container/flat_map.hpp>
 #include <cstdint>
 #include <vector>
diff --git a/redfish-core/include/redfish.hpp b/redfish-core/include/redfish.hpp
index 2a61c52..36b50e8 100644
--- a/redfish-core/include/redfish.hpp
+++ b/redfish-core/include/redfish.hpp
@@ -103,6 +103,11 @@
         nodes.emplace_back(std::make_unique<SystemsCollection>(app));
         nodes.emplace_back(std::make_unique<Systems>(app));
         nodes.emplace_back(std::make_unique<SystemActionsReset>(app));
+
+        for (const auto& node : nodes)
+        {
+            node->initPrivileges();
+        }
     }
 
   private:
diff --git a/redfish-core/lib/account_service.hpp b/redfish-core/lib/account_service.hpp
index 9365ebb..a5c501d 100644
--- a/redfish-core/lib/account_service.hpp
+++ b/redfish-core/lib/account_service.hpp
@@ -224,6 +224,7 @@
         }
     }
 };
+
 class AccountsCollection : public Node
 {
   public:
diff --git a/redfish-core/lib/redfish_sessions.hpp b/redfish-core/lib/redfish_sessions.hpp
index 59805a0..d4085af 100644
--- a/redfish-core/lib/redfish_sessions.hpp
+++ b/redfish-core/lib/redfish_sessions.hpp
@@ -139,6 +139,7 @@
             res.jsonValue["Members"].push_back(
                 {{"@odata.id", "/redfish/v1/SessionService/Sessions/" + *uid}});
         }
+        res.jsonValue["Members@odata.count"] = sessionIds.size();
         res.jsonValue["@odata.type"] = "#SessionCollection.SessionCollection";
         res.jsonValue["@odata.id"] = "/redfish/v1/SessionService/Sessions/";
         res.jsonValue["@odata.context"] =