diff options
author | Tanous <ed.tanous@intel.com> | 2018-11-05 07:18:10 +0300 |
---|---|---|
committer | Ed Tanous <ed.tanous@intel.com> | 2019-04-01 20:01:41 +0300 |
commit | f00032db72e7ee88946bb4839b5d5311a3e092b3 (patch) | |
tree | 5bc236c459831bd33a5e8fcaeb60e74b966798a9 /crow | |
parent | 20e6ea5dbfaa04f4298116281ddfb997847189ce (diff) | |
download | bmcweb-f00032db72e7ee88946bb4839b5d5311a3e092b3.tar.xz |
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>
Diffstat (limited to 'crow')
-rw-r--r-- | crow/include/crow/app.h | 2 | ||||
-rw-r--r-- | crow/include/crow/routing.h | 252 |
2 files changed, 177 insertions, 77 deletions
diff --git a/crow/include/crow/app.h b/crow/include/crow/app.h index f1f3687723..95bbaed0bc 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 746e115894..e657e2ef1b 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 @@ class BaseRule 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 @@ template <typename Func, typename... ArgsWrapped> struct Wrapped 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 @@ template <typename T> struct RuleParameterTraits 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 @@ template <typename T> struct RuleParameterTraits ((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 @@ class TaggedRule : public BaseRule, 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 @@ class Trie private: void optimizeNode(Node* node) { - for (auto x : node->paramChildrens) + for (unsigned int x : node->paramChildrens) { if (!x) continue; @@ -609,7 +648,7 @@ class Trie 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 @@ class Trie 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 @@ class Trie } 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 @@ class Trie 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 @@ class Trie 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 @@ class Trie 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 @@ class Trie 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 @@ class Trie { 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 @@ class Trie { 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 @@ class Trie } } - 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 @@ class Trie char c = url[i]; if (c == '<') { - static struct ParamTraits + 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 (const std::pair<ParamType, std::string>& x : 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>"}, - }; - - for (auto& 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 @@ class Trie 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 @@ class Trie 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 Trie class Router { public: - Router() : rules(2) + Router() { } @@ -968,7 +1007,7 @@ class Router 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 @@ class Router 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) + { + return; + } + for (uint32_t method = 0, method_bit = 1; method < maxHttpVerbCount; + method++, method_bit <<= 1) { - trie.add(rule.substr(0, rule.size() - 1), rules.size() - 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 @@ class Router 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 @@ class Router << (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 @@ class Router 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 |