blob: 2f90c0752d52cdeb532da09afee342e6bfc8c482 [file] [log] [blame]
Ed Tanous7045c8d2017-04-03 10:04:37 -07001#pragma once
2
Gunnar Mills1214b7e2020-06-04 10:11:30 -05003#include "common.h"
4#include "http_request.h"
5#include "http_response.h"
6#include "logging.h"
7#include "utility.h"
8#include "websocket.h"
9
Joseph Reynolds3bf4e632020-02-06 14:44:32 -060010#include "error_messages.hpp"
Tanousf00032d2018-11-05 01:18:10 -030011#include "privileges.hpp"
Ratan Gupta6f359562019-04-03 10:39:08 +053012#include "sessions.hpp"
Ed Tanous1abe55e2018-09-05 08:30:59 -070013
Iwona Klimaszewskac0a1c8a2019-07-12 18:26:38 +020014#include <async_resp.hpp>
Tanousf00032d2018-11-05 01:18:10 -030015#include <boost/container/flat_map.hpp>
16#include <boost/container/small_vector.hpp>
Ed Tanous1abe55e2018-09-05 08:30:59 -070017#include <boost/lexical_cast.hpp>
Gunnar Mills1214b7e2020-06-04 10:11:30 -050018
Ed Tanouse0d918b2018-03-27 17:41:04 -070019#include <cerrno>
Ed Tanous7045c8d2017-04-03 10:04:37 -070020#include <cstdint>
Ed Tanouse0d918b2018-03-27 17:41:04 -070021#include <cstdlib>
Ed Tanous3dac7492017-08-02 13:46:20 -070022#include <limits>
Ed Tanous7045c8d2017-04-03 10:04:37 -070023#include <memory>
24#include <tuple>
Ed Tanous7045c8d2017-04-03 10:04:37 -070025#include <utility>
26#include <vector>
Ed Tanous9140a672017-04-24 17:01:32 -070027
Ed Tanous1abe55e2018-09-05 08:30:59 -070028namespace crow
29{
Tanousf00032d2018-11-05 01:18:10 -030030
31constexpr int maxHttpVerbCount =
32 static_cast<int>(boost::beast::http::verb::unlink);
33
Ed Tanous1abe55e2018-09-05 08:30:59 -070034class BaseRule
35{
36 public:
Tanousf00032d2018-11-05 01:18:10 -030037 BaseRule(std::string thisRule) : rule(std::move(thisRule))
Gunnar Mills1214b7e2020-06-04 10:11:30 -050038 {}
Ed Tanous7045c8d2017-04-03 10:04:37 -070039
Ed Tanous0c0084a2019-10-24 15:57:51 -070040 virtual ~BaseRule() = default;
Ed Tanous7045c8d2017-04-03 10:04:37 -070041
Ed Tanous1abe55e2018-09-05 08:30:59 -070042 virtual void validate() = 0;
43 std::unique_ptr<BaseRule> upgrade()
44 {
45 if (ruleToUpgrade)
46 return std::move(ruleToUpgrade);
47 return {};
48 }
Ed Tanous7045c8d2017-04-03 10:04:37 -070049
Ed Tanous1abe55e2018-09-05 08:30:59 -070050 virtual void handle(const Request&, Response&, const RoutingParams&) = 0;
Ed Tanousceac6f72018-12-02 11:58:47 -080051 virtual void handleUpgrade(const Request&, Response& res,
52 boost::asio::ip::tcp::socket&&)
Ed Tanous1abe55e2018-09-05 08:30:59 -070053 {
Ed Tanousde5c9f32019-03-26 09:17:55 -070054 res.result(boost::beast::http::status::not_found);
Ed Tanous1abe55e2018-09-05 08:30:59 -070055 res.end();
56 }
Ed Tanous55c7b7a2018-05-22 15:27:24 -070057#ifdef BMCWEB_ENABLE_SSL
Ed Tanousceac6f72018-12-02 11:58:47 -080058 virtual void
59 handleUpgrade(const Request&, Response& res,
60 boost::beast::ssl_stream<boost::asio::ip::tcp::socket>&&)
Ed Tanous1abe55e2018-09-05 08:30:59 -070061 {
Ed Tanousde5c9f32019-03-26 09:17:55 -070062 res.result(boost::beast::http::status::not_found);
Ed Tanous1abe55e2018-09-05 08:30:59 -070063 res.end();
64 }
Ed Tanous7045c8d2017-04-03 10:04:37 -070065#endif
66
Ed Tanous271584a2019-07-09 16:24:22 -070067 size_t getMethods()
Ed Tanous1abe55e2018-09-05 08:30:59 -070068 {
69 return methodsBitfield;
70 }
Ed Tanous7045c8d2017-04-03 10:04:37 -070071
Tanousf00032d2018-11-05 01:18:10 -030072 bool checkPrivileges(const redfish::Privileges& userPrivileges)
73 {
74 // If there are no privileges assigned, assume no privileges
75 // required
76 if (privilegesSet.empty())
77 {
78 return true;
79 }
80
81 for (const redfish::Privileges& requiredPrivileges : privilegesSet)
82 {
83 if (userPrivileges.isSupersetOf(requiredPrivileges))
84 {
85 return true;
86 }
87 }
88 return false;
89 }
90
Ed Tanous271584a2019-07-09 16:24:22 -070091 size_t methodsBitfield{
92 1 << static_cast<size_t>(boost::beast::http::verb::get)};
Ed Tanous7045c8d2017-04-03 10:04:37 -070093
Tanousf00032d2018-11-05 01:18:10 -030094 std::vector<redfish::Privileges> privilegesSet;
95
Ed Tanous1abe55e2018-09-05 08:30:59 -070096 std::string rule;
97 std::string nameStr;
Ed Tanous7045c8d2017-04-03 10:04:37 -070098
Ed Tanous1abe55e2018-09-05 08:30:59 -070099 std::unique_ptr<BaseRule> ruleToUpgrade;
Ed Tanous7045c8d2017-04-03 10:04:37 -0700100
Ed Tanous1abe55e2018-09-05 08:30:59 -0700101 friend class Router;
Gunnar Mills1214b7e2020-06-04 10:11:30 -0500102 template <typename T>
103 friend struct RuleParameterTraits;
Ed Tanous7045c8d2017-04-03 10:04:37 -0700104};
105
Ed Tanous1abe55e2018-09-05 08:30:59 -0700106namespace detail
107{
108namespace routing_handler_call_helper
109{
Gunnar Mills1214b7e2020-06-04 10:11:30 -0500110template <typename T, int Pos>
111struct CallPair
Ed Tanous1abe55e2018-09-05 08:30:59 -0700112{
113 using type = T;
114 static const int pos = Pos;
Ed Tanous7045c8d2017-04-03 10:04:37 -0700115};
116
Gunnar Mills1214b7e2020-06-04 10:11:30 -0500117template <typename H1>
118struct CallParams
Ed Tanous1abe55e2018-09-05 08:30:59 -0700119{
120 H1& handler;
121 const RoutingParams& params;
122 const Request& req;
123 Response& res;
Ed Tanous7045c8d2017-04-03 10:04:37 -0700124};
125
126template <typename F, int NInt, int NUint, int NDouble, int NString,
127 typename S1, typename S2>
Ed Tanous1abe55e2018-09-05 08:30:59 -0700128struct Call
Gunnar Mills1214b7e2020-06-04 10:11:30 -0500129{};
Ed Tanous7045c8d2017-04-03 10:04:37 -0700130
131template <typename F, int NInt, int NUint, int NDouble, int NString,
132 typename... Args1, typename... Args2>
Ed Tanous55c7b7a2018-05-22 15:27:24 -0700133struct Call<F, NInt, NUint, NDouble, NString, black_magic::S<int64_t, Args1...>,
Ed Tanous1abe55e2018-09-05 08:30:59 -0700134 black_magic::S<Args2...>>
135{
136 void operator()(F cparams)
137 {
138 using pushed = typename black_magic::S<Args2...>::template push_back<
139 CallPair<int64_t, NInt>>;
140 Call<F, NInt + 1, NUint, NDouble, NString, black_magic::S<Args1...>,
141 pushed>()(cparams);
142 }
Ed Tanous7045c8d2017-04-03 10:04:37 -0700143};
144
145template <typename F, int NInt, int NUint, int NDouble, int NString,
146 typename... Args1, typename... Args2>
Ed Tanous55c7b7a2018-05-22 15:27:24 -0700147struct Call<F, NInt, NUint, NDouble, NString,
Ed Tanous1abe55e2018-09-05 08:30:59 -0700148 black_magic::S<uint64_t, Args1...>, black_magic::S<Args2...>>
149{
150 void operator()(F cparams)
151 {
152 using pushed = typename black_magic::S<Args2...>::template push_back<
153 CallPair<uint64_t, NUint>>;
154 Call<F, NInt, NUint + 1, NDouble, NString, black_magic::S<Args1...>,
155 pushed>()(cparams);
156 }
Ed Tanous7045c8d2017-04-03 10:04:37 -0700157};
158
159template <typename F, int NInt, int NUint, int NDouble, int NString,
160 typename... Args1, typename... Args2>
Ed Tanous55c7b7a2018-05-22 15:27:24 -0700161struct Call<F, NInt, NUint, NDouble, NString, black_magic::S<double, Args1...>,
Ed Tanous1abe55e2018-09-05 08:30:59 -0700162 black_magic::S<Args2...>>
163{
164 void operator()(F cparams)
165 {
166 using pushed = typename black_magic::S<Args2...>::template push_back<
167 CallPair<double, NDouble>>;
168 Call<F, NInt, NUint, NDouble + 1, NString, black_magic::S<Args1...>,
169 pushed>()(cparams);
170 }
Ed Tanous7045c8d2017-04-03 10:04:37 -0700171};
172
173template <typename F, int NInt, int NUint, int NDouble, int NString,
174 typename... Args1, typename... Args2>
Ed Tanous55c7b7a2018-05-22 15:27:24 -0700175struct Call<F, NInt, NUint, NDouble, NString,
Ed Tanous1abe55e2018-09-05 08:30:59 -0700176 black_magic::S<std::string, Args1...>, black_magic::S<Args2...>>
177{
178 void operator()(F cparams)
179 {
180 using pushed = typename black_magic::S<Args2...>::template push_back<
181 CallPair<std::string, NString>>;
182 Call<F, NInt, NUint, NDouble, NString + 1, black_magic::S<Args1...>,
183 pushed>()(cparams);
184 }
Ed Tanous7045c8d2017-04-03 10:04:37 -0700185};
186
187template <typename F, int NInt, int NUint, int NDouble, int NString,
188 typename... Args1>
Ed Tanous55c7b7a2018-05-22 15:27:24 -0700189struct Call<F, NInt, NUint, NDouble, NString, black_magic::S<>,
Ed Tanous1abe55e2018-09-05 08:30:59 -0700190 black_magic::S<Args1...>>
191{
192 void operator()(F cparams)
193 {
194 cparams.handler(
195 cparams.req, cparams.res,
196 cparams.params.template get<typename Args1::type>(Args1::pos)...);
197 }
Ed Tanous7045c8d2017-04-03 10:04:37 -0700198};
199
Gunnar Mills1214b7e2020-06-04 10:11:30 -0500200template <typename Func, typename... ArgsWrapped>
201struct Wrapped
Ed Tanous1abe55e2018-09-05 08:30:59 -0700202{
203 template <typename... Args>
204 void set(
205 Func f,
206 typename std::enable_if<
207 !std::is_same<
208 typename std::tuple_element<0, std::tuple<Args..., void>>::type,
209 const Request&>::value,
210 int>::type = 0)
211 {
Tanousf00032d2018-11-05 01:18:10 -0300212 handler = [f = std::move(f)](const Request&, Response& res,
213 Args... args) {
Ed Tanousde5c9f32019-03-26 09:17:55 -0700214 res.result(f(args...));
Tanousf00032d2018-11-05 01:18:10 -0300215 res.end();
216 };
Ed Tanous7045c8d2017-04-03 10:04:37 -0700217 }
218
Gunnar Mills1214b7e2020-06-04 10:11:30 -0500219 template <typename Req, typename... Args>
220 struct ReqHandlerWrapper
Ed Tanous1abe55e2018-09-05 08:30:59 -0700221 {
222 ReqHandlerWrapper(Func f) : f(std::move(f))
Gunnar Mills1214b7e2020-06-04 10:11:30 -0500223 {}
Ed Tanous7045c8d2017-04-03 10:04:37 -0700224
Ed Tanous1abe55e2018-09-05 08:30:59 -0700225 void operator()(const Request& req, Response& res, Args... args)
226 {
Ed Tanousde5c9f32019-03-26 09:17:55 -0700227 res.result(f(req, args...));
Ed Tanous1abe55e2018-09-05 08:30:59 -0700228 res.end();
229 }
Ed Tanous7045c8d2017-04-03 10:04:37 -0700230
Ed Tanous1abe55e2018-09-05 08:30:59 -0700231 Func f;
232 };
Ed Tanous7045c8d2017-04-03 10:04:37 -0700233
Ed Tanous1abe55e2018-09-05 08:30:59 -0700234 template <typename... Args>
235 void set(
236 Func f,
237 typename std::enable_if<
238 std::is_same<
239 typename std::tuple_element<0, std::tuple<Args..., void>>::type,
240 const Request&>::value &&
241 !std::is_same<typename std::tuple_element<
242 1, std::tuple<Args..., void, void>>::type,
243 Response&>::value,
244 int>::type = 0)
245 {
246 handler = ReqHandlerWrapper<Args...>(std::move(f));
247 /*handler = (
248 [f = std::move(f)]
249 (const Request& req, Response& res, Args... args){
Ed Tanousde5c9f32019-03-26 09:17:55 -0700250 res.result(f(req, args...));
Ed Tanous1abe55e2018-09-05 08:30:59 -0700251 res.end();
252 });*/
253 }
Ed Tanous7045c8d2017-04-03 10:04:37 -0700254
Ed Tanous1abe55e2018-09-05 08:30:59 -0700255 template <typename... Args>
256 void set(
257 Func f,
258 typename std::enable_if<
259 std::is_same<
260 typename std::tuple_element<0, std::tuple<Args..., void>>::type,
261 const Request&>::value &&
262 std::is_same<typename std::tuple_element<
263 1, std::tuple<Args..., void, void>>::type,
264 Response&>::value,
265 int>::type = 0)
266 {
267 handler = std::move(f);
268 }
Ed Tanous7045c8d2017-04-03 10:04:37 -0700269
Gunnar Mills1214b7e2020-06-04 10:11:30 -0500270 template <typename... Args>
271 struct HandlerTypeHelper
Ed Tanous1abe55e2018-09-05 08:30:59 -0700272 {
273 using type =
274 std::function<void(const crow::Request&, crow::Response&, Args...)>;
275 using args_type =
276 black_magic::S<typename black_magic::promote_t<Args>...>;
277 };
Ed Tanous7045c8d2017-04-03 10:04:37 -0700278
Ed Tanous1abe55e2018-09-05 08:30:59 -0700279 template <typename... Args>
280 struct HandlerTypeHelper<const Request&, Args...>
281 {
282 using type =
283 std::function<void(const crow::Request&, crow::Response&, Args...)>;
284 using args_type =
285 black_magic::S<typename black_magic::promote_t<Args>...>;
286 };
Ed Tanous7045c8d2017-04-03 10:04:37 -0700287
Ed Tanous1abe55e2018-09-05 08:30:59 -0700288 template <typename... Args>
289 struct HandlerTypeHelper<const Request&, Response&, Args...>
290 {
291 using type =
292 std::function<void(const crow::Request&, crow::Response&, Args...)>;
293 using args_type =
294 black_magic::S<typename black_magic::promote_t<Args>...>;
295 };
296
297 typename HandlerTypeHelper<ArgsWrapped...>::type handler;
298
299 void operator()(const Request& req, Response& res,
300 const RoutingParams& params)
301 {
302 detail::routing_handler_call_helper::Call<
303 detail::routing_handler_call_helper::CallParams<decltype(handler)>,
304 0, 0, 0, 0, typename HandlerTypeHelper<ArgsWrapped...>::args_type,
305 black_magic::S<>>()(
306 detail::routing_handler_call_helper::CallParams<decltype(handler)>{
307 handler, params, req, res});
308 }
Ed Tanous7045c8d2017-04-03 10:04:37 -0700309};
Ed Tanous1abe55e2018-09-05 08:30:59 -0700310} // namespace routing_handler_call_helper
311} // namespace detail
Ed Tanous7045c8d2017-04-03 10:04:37 -0700312
Ed Tanous1abe55e2018-09-05 08:30:59 -0700313class WebSocketRule : public BaseRule
314{
315 using self_t = WebSocketRule;
Ed Tanous7045c8d2017-04-03 10:04:37 -0700316
Ed Tanous1abe55e2018-09-05 08:30:59 -0700317 public:
318 WebSocketRule(std::string rule) : BaseRule(std::move(rule))
Gunnar Mills1214b7e2020-06-04 10:11:30 -0500319 {}
Ed Tanous7045c8d2017-04-03 10:04:37 -0700320
Ed Tanous1abe55e2018-09-05 08:30:59 -0700321 void validate() override
Gunnar Mills1214b7e2020-06-04 10:11:30 -0500322 {}
Ed Tanous7045c8d2017-04-03 10:04:37 -0700323
Ed Tanous1abe55e2018-09-05 08:30:59 -0700324 void handle(const Request&, Response& res, const RoutingParams&) override
325 {
Ed Tanousde5c9f32019-03-26 09:17:55 -0700326 res.result(boost::beast::http::status::not_found);
Ed Tanous1abe55e2018-09-05 08:30:59 -0700327 res.end();
328 }
Ed Tanous7045c8d2017-04-03 10:04:37 -0700329
Jan Sowinskiee52ae12020-01-09 16:28:32 +0000330 void handleUpgrade(const Request& req, Response&,
Ed Tanousceac6f72018-12-02 11:58:47 -0800331 boost::asio::ip::tcp::socket&& adaptor) override
Ed Tanous1abe55e2018-09-05 08:30:59 -0700332 {
Ratan Gupta02453b12019-10-22 14:43:36 +0530333 std::shared_ptr<
334 crow::websocket::ConnectionImpl<boost::asio::ip::tcp::socket>>
335 myConnection = std::make_shared<
336 crow::websocket::ConnectionImpl<boost::asio::ip::tcp::socket>>(
Jan Sowinskiee52ae12020-01-09 16:28:32 +0000337 req, std::move(adaptor), openHandler, messageHandler,
Ratan Gupta02453b12019-10-22 14:43:36 +0530338 closeHandler, errorHandler);
339 myConnection->start();
Ed Tanous1abe55e2018-09-05 08:30:59 -0700340 }
341#ifdef BMCWEB_ENABLE_SSL
Jan Sowinskiee52ae12020-01-09 16:28:32 +0000342 void handleUpgrade(const Request& req, Response&,
Ed Tanousceac6f72018-12-02 11:58:47 -0800343 boost::beast::ssl_stream<boost::asio::ip::tcp::socket>&&
344 adaptor) override
Ed Tanous1abe55e2018-09-05 08:30:59 -0700345 {
Ed Tanousceac6f72018-12-02 11:58:47 -0800346 std::shared_ptr<crow::websocket::ConnectionImpl<
347 boost::beast::ssl_stream<boost::asio::ip::tcp::socket>>>
348 myConnection = std::make_shared<crow::websocket::ConnectionImpl<
349 boost::beast::ssl_stream<boost::asio::ip::tcp::socket>>>(
Jan Sowinskiee52ae12020-01-09 16:28:32 +0000350 req, std::move(adaptor), openHandler, messageHandler,
Ed Tanousceac6f72018-12-02 11:58:47 -0800351 closeHandler, errorHandler);
Ed Tanous1abe55e2018-09-05 08:30:59 -0700352 myConnection->start();
353 }
Ed Tanous7045c8d2017-04-03 10:04:37 -0700354#endif
355
Gunnar Mills1214b7e2020-06-04 10:11:30 -0500356 template <typename Func>
357 self_t& onopen(Func f)
Ed Tanous1abe55e2018-09-05 08:30:59 -0700358 {
359 openHandler = f;
360 return *this;
361 }
Ed Tanous7045c8d2017-04-03 10:04:37 -0700362
Gunnar Mills1214b7e2020-06-04 10:11:30 -0500363 template <typename Func>
364 self_t& onmessage(Func f)
Ed Tanous1abe55e2018-09-05 08:30:59 -0700365 {
366 messageHandler = f;
367 return *this;
368 }
Ed Tanous7045c8d2017-04-03 10:04:37 -0700369
Gunnar Mills1214b7e2020-06-04 10:11:30 -0500370 template <typename Func>
371 self_t& onclose(Func f)
Ed Tanous1abe55e2018-09-05 08:30:59 -0700372 {
373 closeHandler = f;
374 return *this;
375 }
Ed Tanous7045c8d2017-04-03 10:04:37 -0700376
Gunnar Mills1214b7e2020-06-04 10:11:30 -0500377 template <typename Func>
378 self_t& onerror(Func f)
Ed Tanous1abe55e2018-09-05 08:30:59 -0700379 {
380 errorHandler = f;
381 return *this;
382 }
Ed Tanous7045c8d2017-04-03 10:04:37 -0700383
Ed Tanous1abe55e2018-09-05 08:30:59 -0700384 protected:
Iwona Klimaszewskac0a1c8a2019-07-12 18:26:38 +0200385 std::function<void(crow::websocket::Connection&,
386 std::shared_ptr<bmcweb::AsyncResp>)>
387 openHandler;
Ed Tanous1abe55e2018-09-05 08:30:59 -0700388 std::function<void(crow::websocket::Connection&, const std::string&, bool)>
389 messageHandler;
390 std::function<void(crow::websocket::Connection&, const std::string&)>
391 closeHandler;
392 std::function<void(crow::websocket::Connection&)> errorHandler;
Ed Tanous7045c8d2017-04-03 10:04:37 -0700393};
394
Gunnar Mills1214b7e2020-06-04 10:11:30 -0500395template <typename T>
396struct RuleParameterTraits
Ed Tanous1abe55e2018-09-05 08:30:59 -0700397{
398 using self_t = T;
399 WebSocketRule& websocket()
400 {
Ed Tanous271584a2019-07-09 16:24:22 -0700401 self_t* self = static_cast<self_t*>(this);
402 WebSocketRule* p = new WebSocketRule(self->rule);
403 self->ruleToUpgrade.reset(p);
Ed Tanous1abe55e2018-09-05 08:30:59 -0700404 return *p;
405 }
Ed Tanous7045c8d2017-04-03 10:04:37 -0700406
Ed Tanous1abe55e2018-09-05 08:30:59 -0700407 self_t& name(std::string name) noexcept
408 {
Ed Tanous271584a2019-07-09 16:24:22 -0700409 self_t* self = static_cast<self_t*>(this);
410 self->nameStr = std::move(name);
411 return *self;
Ed Tanous1abe55e2018-09-05 08:30:59 -0700412 }
Ed Tanous7045c8d2017-04-03 10:04:37 -0700413
Ed Tanous1abe55e2018-09-05 08:30:59 -0700414 self_t& methods(boost::beast::http::verb method)
415 {
Ed Tanous271584a2019-07-09 16:24:22 -0700416 self_t* self = static_cast<self_t*>(this);
417 self->methodsBitfield = 1U << static_cast<size_t>(method);
418 return *self;
Ed Tanous1abe55e2018-09-05 08:30:59 -0700419 }
Ed Tanous7045c8d2017-04-03 10:04:37 -0700420
Ed Tanous1abe55e2018-09-05 08:30:59 -0700421 template <typename... MethodArgs>
422 self_t& methods(boost::beast::http::verb method, MethodArgs... args_method)
423 {
Ed Tanous271584a2019-07-09 16:24:22 -0700424 self_t* self = static_cast<self_t*>(this);
Ed Tanous1abe55e2018-09-05 08:30:59 -0700425 methods(args_method...);
Ed Tanous271584a2019-07-09 16:24:22 -0700426 self->methodsBitfield |= 1U << static_cast<size_t>(method);
427 return *self;
Ed Tanous1abe55e2018-09-05 08:30:59 -0700428 }
Tanousf00032d2018-11-05 01:18:10 -0300429
430 template <typename... MethodArgs>
431 self_t& requires(std::initializer_list<const char*> l)
432 {
Ed Tanous271584a2019-07-09 16:24:22 -0700433 self_t* self = static_cast<self_t*>(this);
434 self->privilegesSet.emplace_back(l);
435 return *self;
Tanousf00032d2018-11-05 01:18:10 -0300436 }
437
438 template <typename... MethodArgs>
439 self_t& requires(const std::vector<redfish::Privileges>& p)
440 {
Ed Tanous271584a2019-07-09 16:24:22 -0700441 self_t* self = static_cast<self_t*>(this);
Tanousf00032d2018-11-05 01:18:10 -0300442 for (const redfish::Privileges& privilege : p)
443 {
Ed Tanous271584a2019-07-09 16:24:22 -0700444 self->privilegesSet.emplace_back(privilege);
Tanousf00032d2018-11-05 01:18:10 -0300445 }
Ed Tanous271584a2019-07-09 16:24:22 -0700446 return *self;
Tanousf00032d2018-11-05 01:18:10 -0300447 }
Ed Tanous7045c8d2017-04-03 10:04:37 -0700448};
449
Ed Tanous1abe55e2018-09-05 08:30:59 -0700450class DynamicRule : public BaseRule, public RuleParameterTraits<DynamicRule>
451{
452 public:
453 DynamicRule(std::string rule) : BaseRule(std::move(rule))
Gunnar Mills1214b7e2020-06-04 10:11:30 -0500454 {}
Ed Tanous7045c8d2017-04-03 10:04:37 -0700455
Ed Tanous1abe55e2018-09-05 08:30:59 -0700456 void validate() override
457 {
458 if (!erasedHandler)
459 {
460 throw std::runtime_error(nameStr + (!nameStr.empty() ? ": " : "") +
461 "no handler for url " + rule);
462 }
Ed Tanous7045c8d2017-04-03 10:04:37 -0700463 }
Ed Tanous7045c8d2017-04-03 10:04:37 -0700464
Ed Tanous1abe55e2018-09-05 08:30:59 -0700465 void handle(const Request& req, Response& res,
466 const RoutingParams& params) override
467 {
468 erasedHandler(req, res, params);
469 }
Ed Tanous7045c8d2017-04-03 10:04:37 -0700470
Gunnar Mills1214b7e2020-06-04 10:11:30 -0500471 template <typename Func>
472 void operator()(Func f)
Ed Tanous1abe55e2018-09-05 08:30:59 -0700473 {
474 using function_t = utility::function_traits<Func>;
475
476 erasedHandler =
477 wrap(std::move(f), black_magic::gen_seq<function_t::arity>());
478 }
479
480 // enable_if Arg1 == request && Arg2 == Response
Gunnar Mills6be0e402020-07-08 13:21:51 -0500481 // enable_if Arg1 == request && Arg2 != response
Ed Tanous1abe55e2018-09-05 08:30:59 -0700482 // enable_if Arg1 != request
483
484 template <typename Func, unsigned... Indices>
485
486 std::function<void(const Request&, Response&, const RoutingParams&)>
487 wrap(Func f, black_magic::Seq<Indices...>)
488 {
489 using function_t = utility::function_traits<Func>;
490
491 if (!black_magic::isParameterTagCompatible(
492 black_magic::getParameterTagRuntime(rule.c_str()),
493 black_magic::compute_parameter_tag_from_args_list<
494 typename function_t::template arg<Indices>...>::value))
495 {
496 throw std::runtime_error("routeDynamic: Handler type is mismatched "
497 "with URL parameters: " +
498 rule);
499 }
500 auto ret = detail::routing_handler_call_helper::Wrapped<
501 Func, typename function_t::template arg<Indices>...>();
502 ret.template set<typename function_t::template arg<Indices>...>(
503 std::move(f));
504 return ret;
505 }
506
Gunnar Mills1214b7e2020-06-04 10:11:30 -0500507 template <typename Func>
508 void operator()(std::string name, Func&& f)
Ed Tanous1abe55e2018-09-05 08:30:59 -0700509 {
510 nameStr = std::move(name);
511 (*this).template operator()<Func>(std::forward(f));
512 }
513
514 private:
515 std::function<void(const Request&, Response&, const RoutingParams&)>
516 erasedHandler;
Ed Tanous7045c8d2017-04-03 10:04:37 -0700517};
518
519template <typename... Args>
Gunnar Mills1214b7e2020-06-04 10:11:30 -0500520class TaggedRule :
521 public BaseRule,
522 public RuleParameterTraits<TaggedRule<Args...>>
Ed Tanous1abe55e2018-09-05 08:30:59 -0700523{
524 public:
525 using self_t = TaggedRule<Args...>;
Ed Tanous7045c8d2017-04-03 10:04:37 -0700526
Ed Tanous271584a2019-07-09 16:24:22 -0700527 TaggedRule(std::string ruleIn) : BaseRule(std::move(ruleIn))
Gunnar Mills1214b7e2020-06-04 10:11:30 -0500528 {}
Ed Tanous7045c8d2017-04-03 10:04:37 -0700529
Ed Tanous1abe55e2018-09-05 08:30:59 -0700530 void validate() override
531 {
532 if (!handler)
533 {
534 throw std::runtime_error(nameStr + (!nameStr.empty() ? ": " : "") +
535 "no handler for url " + rule);
536 }
537 }
538
539 template <typename Func>
540 typename std::enable_if<
541 black_magic::CallHelper<Func, black_magic::S<Args...>>::value,
542 void>::type
543 operator()(Func&& f)
544 {
545 static_assert(
546 black_magic::CallHelper<Func, black_magic::S<Args...>>::value ||
547 black_magic::CallHelper<
548 Func, black_magic::S<crow::Request, Args...>>::value,
549 "Handler type is mismatched with URL parameters");
550 static_assert(
551 !std::is_same<void, decltype(f(std::declval<Args>()...))>::value,
552 "Handler function cannot have void return type; valid return "
553 "types: "
Gunnar Mills6be0e402020-07-08 13:21:51 -0500554 "string, int, crow::response, nlohmann::json");
Ed Tanous1abe55e2018-09-05 08:30:59 -0700555
556 handler = [f = std::move(f)](const Request&, Response& res,
557 Args... args) {
Ed Tanousde5c9f32019-03-26 09:17:55 -0700558 res.result(f(args...));
Ed Tanous1abe55e2018-09-05 08:30:59 -0700559 res.end();
560 };
561 }
562
563 template <typename Func>
564 typename std::enable_if<
565 !black_magic::CallHelper<Func, black_magic::S<Args...>>::value &&
Ed Tanous7045c8d2017-04-03 10:04:37 -0700566 black_magic::CallHelper<
Ed Tanous55c7b7a2018-05-22 15:27:24 -0700567 Func, black_magic::S<crow::Request, Args...>>::value,
Ed Tanous1abe55e2018-09-05 08:30:59 -0700568 void>::type
569 operator()(Func&& f)
570 {
571 static_assert(
572 black_magic::CallHelper<Func, black_magic::S<Args...>>::value ||
573 black_magic::CallHelper<
574 Func, black_magic::S<crow::Request, Args...>>::value,
575 "Handler type is mismatched with URL parameters");
576 static_assert(
577 !std::is_same<void, decltype(f(std::declval<crow::Request>(),
578 std::declval<Args>()...))>::value,
579 "Handler function cannot have void return type; valid return "
580 "types: "
Gunnar Mills6be0e402020-07-08 13:21:51 -0500581 "string, int, crow::response,nlohmann::json");
Ed Tanous7045c8d2017-04-03 10:04:37 -0700582
Ed Tanous1abe55e2018-09-05 08:30:59 -0700583 handler = [f = std::move(f)](const crow::Request& req,
584 crow::Response& res, Args... args) {
Ed Tanousde5c9f32019-03-26 09:17:55 -0700585 res.result(f(req, args...));
Ed Tanous1abe55e2018-09-05 08:30:59 -0700586 res.end();
587 };
588 }
Ed Tanous7045c8d2017-04-03 10:04:37 -0700589
Ed Tanous1abe55e2018-09-05 08:30:59 -0700590 template <typename Func>
591 typename std::enable_if<
592 !black_magic::CallHelper<Func, black_magic::S<Args...>>::value &&
593 !black_magic::CallHelper<
Ed Tanous55c7b7a2018-05-22 15:27:24 -0700594 Func, black_magic::S<crow::Request, Args...>>::value,
Ed Tanous1abe55e2018-09-05 08:30:59 -0700595 void>::type
596 operator()(Func&& f)
597 {
598 static_assert(
599 black_magic::CallHelper<Func, black_magic::S<Args...>>::value ||
600 black_magic::CallHelper<
601 Func, black_magic::S<crow::Request, Args...>>::value ||
602 black_magic::CallHelper<
603 Func, black_magic::S<crow::Request, crow::Response&,
604 Args...>>::value,
605 "Handler type is mismatched with URL parameters");
606 static_assert(
607 std::is_same<void, decltype(f(std::declval<crow::Request>(),
608 std::declval<crow::Response&>(),
609 std::declval<Args>()...))>::value,
Tanousf00032d2018-11-05 01:18:10 -0300610 "Handler function with response argument should have void "
611 "return "
Ed Tanous1abe55e2018-09-05 08:30:59 -0700612 "type");
Ed Tanous7045c8d2017-04-03 10:04:37 -0700613
Ed Tanous1abe55e2018-09-05 08:30:59 -0700614 handler = std::move(f);
615 }
Ed Tanous7045c8d2017-04-03 10:04:37 -0700616
Gunnar Mills1214b7e2020-06-04 10:11:30 -0500617 template <typename Func>
618 void operator()(std::string name, Func&& f)
Ed Tanous1abe55e2018-09-05 08:30:59 -0700619 {
620 nameStr = std::move(name);
621 (*this).template operator()<Func>(std::forward(f));
622 }
Ed Tanous7045c8d2017-04-03 10:04:37 -0700623
Ed Tanous1abe55e2018-09-05 08:30:59 -0700624 void handle(const Request& req, Response& res,
625 const RoutingParams& params) override
626 {
627 detail::routing_handler_call_helper::Call<
628 detail::routing_handler_call_helper::CallParams<decltype(handler)>,
629 0, 0, 0, 0, black_magic::S<Args...>, black_magic::S<>>()(
630 detail::routing_handler_call_helper::CallParams<decltype(handler)>{
631 handler, params, req, res});
632 }
Ed Tanous7045c8d2017-04-03 10:04:37 -0700633
Ed Tanous1abe55e2018-09-05 08:30:59 -0700634 private:
635 std::function<void(const crow::Request&, crow::Response&, Args...)> handler;
Ed Tanous7045c8d2017-04-03 10:04:37 -0700636};
637
Ed Tanous55c7b7a2018-05-22 15:27:24 -0700638const int ruleSpecialRedirectSlash = 1;
Ed Tanous7045c8d2017-04-03 10:04:37 -0700639
Ed Tanous1abe55e2018-09-05 08:30:59 -0700640class Trie
641{
642 public:
643 struct Node
644 {
645 unsigned ruleIndex{};
Ed Tanous271584a2019-07-09 16:24:22 -0700646 std::array<size_t, static_cast<size_t>(ParamType::MAX)>
647 paramChildrens{};
Ed Tanous1abe55e2018-09-05 08:30:59 -0700648 boost::container::flat_map<std::string, unsigned> children;
Ed Tanous7045c8d2017-04-03 10:04:37 -0700649
Ed Tanous1abe55e2018-09-05 08:30:59 -0700650 bool isSimpleNode() const
651 {
652 return !ruleIndex && std::all_of(std::begin(paramChildrens),
653 std::end(paramChildrens),
Ed Tanous271584a2019-07-09 16:24:22 -0700654 [](size_t x) { return !x; });
Ed Tanous7045c8d2017-04-03 10:04:37 -0700655 }
Ed Tanous55c7b7a2018-05-22 15:27:24 -0700656 };
Ed Tanous7045c8d2017-04-03 10:04:37 -0700657
Ed Tanous1abe55e2018-09-05 08:30:59 -0700658 Trie() : nodes(1)
Gunnar Mills1214b7e2020-06-04 10:11:30 -0500659 {}
Ed Tanous1abe55e2018-09-05 08:30:59 -0700660
661 private:
662 void optimizeNode(Node* node)
663 {
Ed Tanous271584a2019-07-09 16:24:22 -0700664 for (size_t x : node->paramChildrens)
Ed Tanous1abe55e2018-09-05 08:30:59 -0700665 {
666 if (!x)
667 continue;
668 Node* child = &nodes[x];
669 optimizeNode(child);
Ed Tanous7045c8d2017-04-03 10:04:37 -0700670 }
Ed Tanous1abe55e2018-09-05 08:30:59 -0700671 if (node->children.empty())
672 return;
673 bool mergeWithChild = true;
Tanousf00032d2018-11-05 01:18:10 -0300674 for (const std::pair<std::string, unsigned>& kv : node->children)
Ed Tanous1abe55e2018-09-05 08:30:59 -0700675 {
676 Node* child = &nodes[kv.second];
677 if (!child->isSimpleNode())
678 {
679 mergeWithChild = false;
680 break;
Ed Tanous7045c8d2017-04-03 10:04:37 -0700681 }
Ed Tanous1abe55e2018-09-05 08:30:59 -0700682 }
683 if (mergeWithChild)
684 {
685 decltype(node->children) merged;
Tanousf00032d2018-11-05 01:18:10 -0300686 for (const std::pair<std::string, unsigned>& kv : node->children)
Ed Tanous1abe55e2018-09-05 08:30:59 -0700687 {
688 Node* child = &nodes[kv.second];
Tanousf00032d2018-11-05 01:18:10 -0300689 for (const std::pair<std::string, unsigned>& childKv :
690 child->children)
Ed Tanous1abe55e2018-09-05 08:30:59 -0700691 {
692 merged[kv.first + childKv.first] = childKv.second;
693 }
694 }
695 node->children = std::move(merged);
696 optimizeNode(node);
697 }
698 else
699 {
Tanousf00032d2018-11-05 01:18:10 -0300700 for (const std::pair<std::string, unsigned>& kv : node->children)
Ed Tanous1abe55e2018-09-05 08:30:59 -0700701 {
702 Node* child = &nodes[kv.second];
703 optimizeNode(child);
704 }
705 }
706 }
707
708 void optimize()
709 {
710 optimizeNode(head());
711 }
712
713 public:
714 void validate()
715 {
716 if (!head()->isSimpleNode())
717 throw std::runtime_error(
718 "Internal error: Trie header should be simple!");
719 optimize();
720 }
721
722 void findRouteIndexes(const std::string& req_url,
723 std::vector<unsigned>& route_indexes,
Tanousf00032d2018-11-05 01:18:10 -0300724 const Node* node = nullptr, unsigned pos = 0) const
Ed Tanous1abe55e2018-09-05 08:30:59 -0700725 {
726 if (node == nullptr)
727 {
728 node = head();
729 }
Tanousf00032d2018-11-05 01:18:10 -0300730 for (const std::pair<std::string, unsigned>& kv : node->children)
Ed Tanous1abe55e2018-09-05 08:30:59 -0700731 {
732 const std::string& fragment = kv.first;
733 const Node* child = &nodes[kv.second];
734 if (pos >= req_url.size())
735 {
736 if (child->ruleIndex != 0 && fragment != "/")
737 {
738 route_indexes.push_back(child->ruleIndex);
739 }
740 findRouteIndexes(req_url, route_indexes, child,
Ed Tanous271584a2019-07-09 16:24:22 -0700741 static_cast<unsigned>(pos + fragment.size()));
Ed Tanous1abe55e2018-09-05 08:30:59 -0700742 }
743 else
744 {
745 if (req_url.compare(pos, fragment.size(), fragment) == 0)
746 {
Ed Tanous271584a2019-07-09 16:24:22 -0700747 findRouteIndexes(
748 req_url, route_indexes, child,
749 static_cast<unsigned>(pos + fragment.size()));
Ed Tanous1abe55e2018-09-05 08:30:59 -0700750 }
751 }
752 }
753 }
754
755 std::pair<unsigned, RoutingParams>
Ed Tanous39e77502019-03-04 17:35:53 -0800756 find(const std::string_view req_url, const Node* node = nullptr,
Ed Tanous271584a2019-07-09 16:24:22 -0700757 size_t pos = 0, RoutingParams* params = nullptr) const
Ed Tanous1abe55e2018-09-05 08:30:59 -0700758 {
759 RoutingParams empty;
760 if (params == nullptr)
761 params = &empty;
762
763 unsigned found{};
764 RoutingParams matchParams;
765
766 if (node == nullptr)
767 node = head();
768 if (pos == req_url.size())
769 return {node->ruleIndex, *params};
770
771 auto updateFound =
772 [&found, &matchParams](std::pair<unsigned, RoutingParams>& ret) {
773 if (ret.first && (!found || found > ret.first))
774 {
775 found = ret.first;
776 matchParams = std::move(ret.second);
777 }
778 };
779
Ed Tanous271584a2019-07-09 16:24:22 -0700780 if (node->paramChildrens[static_cast<size_t>(ParamType::INT)])
Ed Tanous1abe55e2018-09-05 08:30:59 -0700781 {
782 char c = req_url[pos];
783 if ((c >= '0' && c <= '9') || c == '+' || c == '-')
784 {
785 char* eptr;
786 errno = 0;
787 long long int value =
788 std::strtoll(req_url.data() + pos, &eptr, 10);
789 if (errno != ERANGE && eptr != req_url.data() + pos)
790 {
791 params->intParams.push_back(value);
Ed Tanous271584a2019-07-09 16:24:22 -0700792 std::pair<unsigned, RoutingParams> ret = find(
793 req_url,
794 &nodes[node->paramChildrens[static_cast<size_t>(
795 ParamType::INT)]],
796 static_cast<size_t>(eptr - req_url.data()), params);
Ed Tanous1abe55e2018-09-05 08:30:59 -0700797 updateFound(ret);
798 params->intParams.pop_back();
799 }
800 }
Ed Tanous7045c8d2017-04-03 10:04:37 -0700801 }
802
Ed Tanous271584a2019-07-09 16:24:22 -0700803 if (node->paramChildrens[static_cast<size_t>(ParamType::UINT)])
Ed Tanous1abe55e2018-09-05 08:30:59 -0700804 {
805 char c = req_url[pos];
806 if ((c >= '0' && c <= '9') || c == '+')
807 {
808 char* eptr;
809 errno = 0;
810 unsigned long long int value =
811 std::strtoull(req_url.data() + pos, &eptr, 10);
812 if (errno != ERANGE && eptr != req_url.data() + pos)
813 {
814 params->uintParams.push_back(value);
Ed Tanous271584a2019-07-09 16:24:22 -0700815 std::pair<unsigned, RoutingParams> ret = find(
816 req_url,
817 &nodes[node->paramChildrens[static_cast<size_t>(
818 ParamType::UINT)]],
819 static_cast<size_t>(eptr - req_url.data()), params);
Ed Tanous1abe55e2018-09-05 08:30:59 -0700820 updateFound(ret);
821 params->uintParams.pop_back();
822 }
823 }
Ed Tanous7045c8d2017-04-03 10:04:37 -0700824 }
825
Ed Tanous271584a2019-07-09 16:24:22 -0700826 if (node->paramChildrens[static_cast<size_t>(ParamType::DOUBLE)])
Ed Tanous1abe55e2018-09-05 08:30:59 -0700827 {
828 char c = req_url[pos];
829 if ((c >= '0' && c <= '9') || c == '+' || c == '-' || c == '.')
830 {
831 char* eptr;
832 errno = 0;
833 double value = std::strtod(req_url.data() + pos, &eptr);
834 if (errno != ERANGE && eptr != req_url.data() + pos)
835 {
836 params->doubleParams.push_back(value);
Tanousf00032d2018-11-05 01:18:10 -0300837 std::pair<unsigned, RoutingParams> ret = find(
Ed Tanous1abe55e2018-09-05 08:30:59 -0700838 req_url,
Ed Tanous271584a2019-07-09 16:24:22 -0700839 &nodes[node->paramChildrens[static_cast<size_t>(
840 ParamType::DOUBLE)]],
841 static_cast<size_t>(eptr - req_url.data()), params);
Ed Tanous1abe55e2018-09-05 08:30:59 -0700842 updateFound(ret);
843 params->doubleParams.pop_back();
844 }
845 }
846 }
847
Ed Tanous271584a2019-07-09 16:24:22 -0700848 if (node->paramChildrens[static_cast<size_t>(ParamType::STRING)])
Ed Tanous1abe55e2018-09-05 08:30:59 -0700849 {
Ed Tanousb01bf292019-03-25 19:25:26 +0000850 size_t epos = pos;
Ed Tanous1abe55e2018-09-05 08:30:59 -0700851 for (; epos < req_url.size(); epos++)
852 {
853 if (req_url[epos] == '/')
854 break;
855 }
856
857 if (epos != pos)
858 {
859 params->stringParams.emplace_back(
860 req_url.substr(pos, epos - pos));
Tanousf00032d2018-11-05 01:18:10 -0300861 std::pair<unsigned, RoutingParams> ret =
Ed Tanousb01bf292019-03-25 19:25:26 +0000862 find(req_url,
Ed Tanous271584a2019-07-09 16:24:22 -0700863 &nodes[node->paramChildrens[static_cast<size_t>(
864 ParamType::STRING)]],
Ed Tanousb01bf292019-03-25 19:25:26 +0000865 epos, params);
Ed Tanous1abe55e2018-09-05 08:30:59 -0700866 updateFound(ret);
867 params->stringParams.pop_back();
868 }
869 }
870
Ed Tanous271584a2019-07-09 16:24:22 -0700871 if (node->paramChildrens[static_cast<size_t>(ParamType::PATH)])
Ed Tanous1abe55e2018-09-05 08:30:59 -0700872 {
Ed Tanousb01bf292019-03-25 19:25:26 +0000873 size_t epos = req_url.size();
Ed Tanous1abe55e2018-09-05 08:30:59 -0700874
875 if (epos != pos)
876 {
877 params->stringParams.emplace_back(
878 req_url.substr(pos, epos - pos));
Ed Tanous271584a2019-07-09 16:24:22 -0700879 std::pair<unsigned, RoutingParams> ret =
880 find(req_url,
881 &nodes[node->paramChildrens[static_cast<size_t>(
882 ParamType::PATH)]],
883 epos, params);
Ed Tanous1abe55e2018-09-05 08:30:59 -0700884 updateFound(ret);
885 params->stringParams.pop_back();
886 }
887 }
888
Tanousf00032d2018-11-05 01:18:10 -0300889 for (const std::pair<std::string, unsigned>& kv : node->children)
Ed Tanous1abe55e2018-09-05 08:30:59 -0700890 {
891 const std::string& fragment = kv.first;
892 const Node* child = &nodes[kv.second];
893
894 if (req_url.compare(pos, fragment.size(), fragment) == 0)
895 {
Tanousf00032d2018-11-05 01:18:10 -0300896 std::pair<unsigned, RoutingParams> ret =
897 find(req_url, child, pos + fragment.size(), params);
Ed Tanous1abe55e2018-09-05 08:30:59 -0700898 updateFound(ret);
899 }
900 }
901
902 return {found, matchParams};
Ed Tanous7045c8d2017-04-03 10:04:37 -0700903 }
Ed Tanous1abe55e2018-09-05 08:30:59 -0700904
905 void add(const std::string& url, unsigned ruleIndex)
906 {
Ed Tanous271584a2019-07-09 16:24:22 -0700907 size_t idx = 0;
Ed Tanous1abe55e2018-09-05 08:30:59 -0700908
909 for (unsigned i = 0; i < url.size(); i++)
910 {
911 char c = url[i];
912 if (c == '<')
913 {
Tanousf00032d2018-11-05 01:18:10 -0300914 const static std::array<std::pair<ParamType, std::string>, 7>
915 paramTraits = {{
916 {ParamType::INT, "<int>"},
917 {ParamType::UINT, "<uint>"},
918 {ParamType::DOUBLE, "<float>"},
919 {ParamType::DOUBLE, "<double>"},
920 {ParamType::STRING, "<str>"},
921 {ParamType::STRING, "<string>"},
922 {ParamType::PATH, "<path>"},
923 }};
Ed Tanous1abe55e2018-09-05 08:30:59 -0700924
Tanousf00032d2018-11-05 01:18:10 -0300925 for (const std::pair<ParamType, std::string>& x : paramTraits)
Ed Tanous1abe55e2018-09-05 08:30:59 -0700926 {
Tanousf00032d2018-11-05 01:18:10 -0300927 if (url.compare(i, x.second.size(), x.second) == 0)
Ed Tanous1abe55e2018-09-05 08:30:59 -0700928 {
Ed Tanous271584a2019-07-09 16:24:22 -0700929 size_t index = static_cast<size_t>(x.first);
930 if (!nodes[idx].paramChildrens[index])
Ed Tanous1abe55e2018-09-05 08:30:59 -0700931 {
Tanousf00032d2018-11-05 01:18:10 -0300932 unsigned newNodeIdx = newNode();
Ed Tanous271584a2019-07-09 16:24:22 -0700933 nodes[idx].paramChildrens[index] = newNodeIdx;
Ed Tanous1abe55e2018-09-05 08:30:59 -0700934 }
Ed Tanous271584a2019-07-09 16:24:22 -0700935 idx = nodes[idx].paramChildrens[index];
936 i += static_cast<unsigned>(x.second.size());
Ed Tanous1abe55e2018-09-05 08:30:59 -0700937 break;
938 }
939 }
940
941 i--;
942 }
943 else
944 {
945 std::string piece(&c, 1);
946 if (!nodes[idx].children.count(piece))
947 {
Tanousf00032d2018-11-05 01:18:10 -0300948 unsigned newNodeIdx = newNode();
Ed Tanous1abe55e2018-09-05 08:30:59 -0700949 nodes[idx].children.emplace(piece, newNodeIdx);
950 }
951 idx = nodes[idx].children[piece];
952 }
953 }
954 if (nodes[idx].ruleIndex)
955 throw std::runtime_error("handler already exists for " + url);
956 nodes[idx].ruleIndex = ruleIndex;
Ed Tanous7045c8d2017-04-03 10:04:37 -0700957 }
Ed Tanous7045c8d2017-04-03 10:04:37 -0700958
Ed Tanous1abe55e2018-09-05 08:30:59 -0700959 private:
Ed Tanous271584a2019-07-09 16:24:22 -0700960 void debugNodePrint(Node* n, size_t level)
Ed Tanous1abe55e2018-09-05 08:30:59 -0700961 {
Ed Tanous271584a2019-07-09 16:24:22 -0700962 for (size_t i = 0; i < static_cast<size_t>(ParamType::MAX); i++)
Ed Tanous1abe55e2018-09-05 08:30:59 -0700963 {
964 if (n->paramChildrens[i])
965 {
966 BMCWEB_LOG_DEBUG << std::string(
Ed Tanous271584a2019-07-09 16:24:22 -0700967 2U * level, ' ') /*<< "("<<n->paramChildrens[i]<<") "*/;
968 switch (static_cast<ParamType>(i))
Ed Tanous1abe55e2018-09-05 08:30:59 -0700969 {
970 case ParamType::INT:
971 BMCWEB_LOG_DEBUG << "<int>";
972 break;
973 case ParamType::UINT:
974 BMCWEB_LOG_DEBUG << "<uint>";
975 break;
976 case ParamType::DOUBLE:
977 BMCWEB_LOG_DEBUG << "<float>";
978 break;
979 case ParamType::STRING:
980 BMCWEB_LOG_DEBUG << "<str>";
981 break;
982 case ParamType::PATH:
983 BMCWEB_LOG_DEBUG << "<path>";
984 break;
985 default:
986 BMCWEB_LOG_DEBUG << "<ERROR>";
987 break;
988 }
Ed Tanous7045c8d2017-04-03 10:04:37 -0700989
Ed Tanous1abe55e2018-09-05 08:30:59 -0700990 debugNodePrint(&nodes[n->paramChildrens[i]], level + 1);
991 }
992 }
Tanousf00032d2018-11-05 01:18:10 -0300993 for (const std::pair<std::string, unsigned>& kv : n->children)
Ed Tanous1abe55e2018-09-05 08:30:59 -0700994 {
995 BMCWEB_LOG_DEBUG
Ed Tanous271584a2019-07-09 16:24:22 -0700996 << std::string(2U * level, ' ') /*<< "(" << kv.second << ") "*/
Ed Tanous1abe55e2018-09-05 08:30:59 -0700997 << kv.first;
998 debugNodePrint(&nodes[kv.second], level + 1);
999 }
1000 }
Ed Tanous7045c8d2017-04-03 10:04:37 -07001001
Ed Tanous1abe55e2018-09-05 08:30:59 -07001002 public:
1003 void debugPrint()
1004 {
Ed Tanous271584a2019-07-09 16:24:22 -07001005 debugNodePrint(head(), 0U);
Ed Tanous1abe55e2018-09-05 08:30:59 -07001006 }
Ed Tanous7045c8d2017-04-03 10:04:37 -07001007
Ed Tanous1abe55e2018-09-05 08:30:59 -07001008 private:
1009 const Node* head() const
1010 {
1011 return &nodes.front();
1012 }
Ed Tanous7045c8d2017-04-03 10:04:37 -07001013
Ed Tanous1abe55e2018-09-05 08:30:59 -07001014 Node* head()
1015 {
1016 return &nodes.front();
1017 }
1018
1019 unsigned newNode()
1020 {
1021 nodes.resize(nodes.size() + 1);
Ed Tanous271584a2019-07-09 16:24:22 -07001022 return static_cast<unsigned>(nodes.size() - 1);
Ed Tanous1abe55e2018-09-05 08:30:59 -07001023 }
1024
1025 std::vector<Node> nodes;
Ed Tanous7045c8d2017-04-03 10:04:37 -07001026};
1027
Ed Tanous1abe55e2018-09-05 08:30:59 -07001028class Router
1029{
1030 public:
Ed Tanous0c0084a2019-10-24 15:57:51 -07001031 Router() = default;
Ed Tanous7045c8d2017-04-03 10:04:37 -07001032
Ed Tanous1abe55e2018-09-05 08:30:59 -07001033 DynamicRule& newRuleDynamic(const std::string& rule)
1034 {
1035 std::unique_ptr<DynamicRule> ruleObject =
1036 std::make_unique<DynamicRule>(rule);
1037 DynamicRule* ptr = ruleObject.get();
Tanousf00032d2018-11-05 01:18:10 -03001038 allRules.emplace_back(std::move(ruleObject));
Ed Tanous7045c8d2017-04-03 10:04:37 -07001039
Ed Tanous1abe55e2018-09-05 08:30:59 -07001040 return *ptr;
Ed Tanous7045c8d2017-04-03 10:04:37 -07001041 }
1042
Ed Tanous1abe55e2018-09-05 08:30:59 -07001043 template <uint64_t N>
1044 typename black_magic::Arguments<N>::type::template rebind<TaggedRule>&
1045 newRuleTagged(const std::string& rule)
1046 {
1047 using RuleT = typename black_magic::Arguments<N>::type::template rebind<
1048 TaggedRule>;
1049 std::unique_ptr<RuleT> ruleObject = std::make_unique<RuleT>(rule);
1050 RuleT* ptr = ruleObject.get();
Tanousf00032d2018-11-05 01:18:10 -03001051 allRules.emplace_back(std::move(ruleObject));
Ed Tanous1abe55e2018-09-05 08:30:59 -07001052
1053 return *ptr;
Ed Tanous7045c8d2017-04-03 10:04:37 -07001054 }
1055
Tanousf00032d2018-11-05 01:18:10 -03001056 void internalAddRuleObject(const std::string& rule, BaseRule* ruleObject)
Ed Tanous1abe55e2018-09-05 08:30:59 -07001057 {
Tanousf00032d2018-11-05 01:18:10 -03001058 if (ruleObject == nullptr)
Ed Tanous1abe55e2018-09-05 08:30:59 -07001059 {
Tanousf00032d2018-11-05 01:18:10 -03001060 return;
1061 }
1062 for (uint32_t method = 0, method_bit = 1; method < maxHttpVerbCount;
1063 method++, method_bit <<= 1)
1064 {
1065 if (ruleObject->methodsBitfield & method_bit)
1066 {
1067 perMethods[method].rules.emplace_back(ruleObject);
1068 perMethods[method].trie.add(
Ed Tanous271584a2019-07-09 16:24:22 -07001069 rule, static_cast<unsigned>(
1070 perMethods[method].rules.size() - 1U));
Tanousf00032d2018-11-05 01:18:10 -03001071 // directory case:
1072 // request to `/about' url matches `/about/' rule
1073 if (rule.size() > 2 && rule.back() == '/')
1074 {
1075 perMethods[method].trie.add(
1076 rule.substr(0, rule.size() - 1),
Ed Tanous271584a2019-07-09 16:24:22 -07001077 static_cast<unsigned>(perMethods[method].rules.size() -
1078 1));
Tanousf00032d2018-11-05 01:18:10 -03001079 }
1080 }
Ed Tanous1abe55e2018-09-05 08:30:59 -07001081 }
Ed Tanous7045c8d2017-04-03 10:04:37 -07001082 }
1083
Ed Tanous1abe55e2018-09-05 08:30:59 -07001084 void validate()
1085 {
Tanousf00032d2018-11-05 01:18:10 -03001086 for (std::unique_ptr<BaseRule>& rule : allRules)
Ed Tanous1abe55e2018-09-05 08:30:59 -07001087 {
1088 if (rule)
1089 {
Tanousf00032d2018-11-05 01:18:10 -03001090 std::unique_ptr<BaseRule> upgraded = rule->upgrade();
Ed Tanous1abe55e2018-09-05 08:30:59 -07001091 if (upgraded)
1092 rule = std::move(upgraded);
1093 rule->validate();
Tanousf00032d2018-11-05 01:18:10 -03001094 internalAddRuleObject(rule->rule, rule.get());
Ed Tanous1abe55e2018-09-05 08:30:59 -07001095 }
1096 }
Tanousf00032d2018-11-05 01:18:10 -03001097 for (PerMethod& perMethod : perMethods)
1098 {
1099 perMethod.trie.validate();
1100 }
Ed Tanous7045c8d2017-04-03 10:04:37 -07001101 }
1102
Ed Tanous1abe55e2018-09-05 08:30:59 -07001103 template <typename Adaptor>
1104 void handleUpgrade(const Request& req, Response& res, Adaptor&& adaptor)
1105 {
Ed Tanous271584a2019-07-09 16:24:22 -07001106 if (static_cast<size_t>(req.method()) >= perMethods.size())
Tanousf00032d2018-11-05 01:18:10 -03001107 return;
1108
Ed Tanous271584a2019-07-09 16:24:22 -07001109 PerMethod& perMethod = perMethods[static_cast<size_t>(req.method())];
Tanousf00032d2018-11-05 01:18:10 -03001110 Trie& trie = perMethod.trie;
1111 std::vector<BaseRule*>& rules = perMethod.rules;
1112
1113 const std::pair<unsigned, RoutingParams>& found = trie.find(req.url);
Ed Tanous1abe55e2018-09-05 08:30:59 -07001114 unsigned ruleIndex = found.first;
1115 if (!ruleIndex)
1116 {
1117 BMCWEB_LOG_DEBUG << "Cannot match rules " << req.url;
Ed Tanousde5c9f32019-03-26 09:17:55 -07001118 res.result(boost::beast::http::status::not_found);
Ed Tanous1abe55e2018-09-05 08:30:59 -07001119 res.end();
1120 return;
1121 }
1122
1123 if (ruleIndex >= rules.size())
1124 throw std::runtime_error("Trie internal structure corrupted!");
1125
1126 if (ruleIndex == ruleSpecialRedirectSlash)
1127 {
1128 BMCWEB_LOG_INFO << "Redirecting to a url with trailing slash: "
1129 << req.url;
Ed Tanousde5c9f32019-03-26 09:17:55 -07001130 res.result(boost::beast::http::status::moved_permanently);
Ed Tanous1abe55e2018-09-05 08:30:59 -07001131
1132 // TODO absolute url building
1133 if (req.getHeaderValue("Host").empty())
1134 {
1135 res.addHeader("Location", std::string(req.url) + "/");
1136 }
1137 else
1138 {
1139 res.addHeader(
1140 "Location",
1141 req.isSecure
1142 ? "https://"
1143 : "http://" + std::string(req.getHeaderValue("Host")) +
1144 std::string(req.url) + "/");
1145 }
1146 res.end();
1147 return;
1148 }
1149
Ed Tanous271584a2019-07-09 16:24:22 -07001150 if ((rules[ruleIndex]->getMethods() &
1151 (1U << static_cast<size_t>(req.method()))) == 0)
Ed Tanous1abe55e2018-09-05 08:30:59 -07001152 {
1153 BMCWEB_LOG_DEBUG << "Rule found but method mismatch: " << req.url
1154 << " with " << req.methodString() << "("
Ed Tanous271584a2019-07-09 16:24:22 -07001155 << static_cast<uint32_t>(req.method()) << ") / "
Ed Tanous1abe55e2018-09-05 08:30:59 -07001156 << rules[ruleIndex]->getMethods();
Ed Tanousde5c9f32019-03-26 09:17:55 -07001157 res.result(boost::beast::http::status::not_found);
Ed Tanous1abe55e2018-09-05 08:30:59 -07001158 res.end();
1159 return;
1160 }
1161
1162 BMCWEB_LOG_DEBUG << "Matched rule (upgrade) '" << rules[ruleIndex]->rule
Ed Tanous271584a2019-07-09 16:24:22 -07001163 << "' " << static_cast<uint32_t>(req.method()) << " / "
Ed Tanous1abe55e2018-09-05 08:30:59 -07001164 << rules[ruleIndex]->getMethods();
1165
1166 // any uncaught exceptions become 500s
1167 try
1168 {
1169 rules[ruleIndex]->handleUpgrade(req, res, std::move(adaptor));
1170 }
1171 catch (std::exception& e)
1172 {
1173 BMCWEB_LOG_ERROR << "An uncaught exception occurred: " << e.what();
Ed Tanousde5c9f32019-03-26 09:17:55 -07001174 res.result(boost::beast::http::status::internal_server_error);
Ed Tanous1abe55e2018-09-05 08:30:59 -07001175 res.end();
1176 return;
1177 }
1178 catch (...)
1179 {
1180 BMCWEB_LOG_ERROR
1181 << "An uncaught exception occurred. The type was unknown "
1182 "so no information was available.";
Ed Tanousde5c9f32019-03-26 09:17:55 -07001183 res.result(boost::beast::http::status::internal_server_error);
Ed Tanous1abe55e2018-09-05 08:30:59 -07001184 res.end();
1185 return;
1186 }
Ed Tanous7045c8d2017-04-03 10:04:37 -07001187 }
1188
RAJESWARAN THILLAIGOVINDAN61dbeef2019-12-13 04:26:54 -06001189 void handle(Request& req, Response& res)
Ed Tanous1abe55e2018-09-05 08:30:59 -07001190 {
Ed Tanous271584a2019-07-09 16:24:22 -07001191 if (static_cast<size_t>(req.method()) >= perMethods.size())
Tanousf00032d2018-11-05 01:18:10 -03001192 return;
Ed Tanous271584a2019-07-09 16:24:22 -07001193 PerMethod& perMethod = perMethods[static_cast<size_t>(req.method())];
Tanousf00032d2018-11-05 01:18:10 -03001194 Trie& trie = perMethod.trie;
1195 std::vector<BaseRule*>& rules = perMethod.rules;
1196
1197 const std::pair<unsigned, RoutingParams>& found = trie.find(req.url);
Ed Tanous7045c8d2017-04-03 10:04:37 -07001198
Ed Tanous1abe55e2018-09-05 08:30:59 -07001199 unsigned ruleIndex = found.first;
1200
1201 if (!ruleIndex)
1202 {
Ed Tanous2634dcd2019-03-26 09:28:06 -07001203 // Check to see if this url exists at any verb
1204 for (const PerMethod& p : perMethods)
1205 {
1206 const std::pair<unsigned, RoutingParams>& found =
1207 p.trie.find(req.url);
1208 if (found.first > 0)
1209 {
1210 res.result(boost::beast::http::status::method_not_allowed);
1211 res.end();
1212 return;
1213 }
1214 }
Ed Tanous1abe55e2018-09-05 08:30:59 -07001215 BMCWEB_LOG_DEBUG << "Cannot match rules " << req.url;
1216 res.result(boost::beast::http::status::not_found);
1217 res.end();
1218 return;
1219 }
1220
1221 if (ruleIndex >= rules.size())
1222 throw std::runtime_error("Trie internal structure corrupted!");
1223
1224 if (ruleIndex == ruleSpecialRedirectSlash)
1225 {
1226 BMCWEB_LOG_INFO << "Redirecting to a url with trailing slash: "
1227 << req.url;
Ed Tanousde5c9f32019-03-26 09:17:55 -07001228 res.result(boost::beast::http::status::moved_permanently);
Ed Tanous1abe55e2018-09-05 08:30:59 -07001229
1230 // TODO absolute url building
1231 if (req.getHeaderValue("Host").empty())
1232 {
1233 res.addHeader("Location", std::string(req.url) + "/");
1234 }
1235 else
1236 {
1237 res.addHeader("Location",
1238 (req.isSecure ? "https://" : "http://") +
1239 std::string(req.getHeaderValue("Host")) +
1240 std::string(req.url) + "/");
1241 }
1242 res.end();
1243 return;
1244 }
1245
Ed Tanous271584a2019-07-09 16:24:22 -07001246 if ((rules[ruleIndex]->getMethods() &
1247 (1U << static_cast<uint32_t>(req.method()))) == 0)
Ed Tanous1abe55e2018-09-05 08:30:59 -07001248 {
1249 BMCWEB_LOG_DEBUG << "Rule found but method mismatch: " << req.url
1250 << " with " << req.methodString() << "("
Ed Tanous271584a2019-07-09 16:24:22 -07001251 << static_cast<uint32_t>(req.method()) << ") / "
Ed Tanous1abe55e2018-09-05 08:30:59 -07001252 << rules[ruleIndex]->getMethods();
Ed Tanousde5c9f32019-03-26 09:17:55 -07001253 res.result(boost::beast::http::status::method_not_allowed);
Ed Tanous1abe55e2018-09-05 08:30:59 -07001254 res.end();
1255 return;
1256 }
1257
1258 BMCWEB_LOG_DEBUG << "Matched rule '" << rules[ruleIndex]->rule << "' "
Ed Tanous271584a2019-07-09 16:24:22 -07001259 << static_cast<uint32_t>(req.method()) << " / "
Ed Tanous1abe55e2018-09-05 08:30:59 -07001260 << rules[ruleIndex]->getMethods();
1261
RAJESWARAN THILLAIGOVINDAN61dbeef2019-12-13 04:26:54 -06001262 if (req.session == nullptr)
James Feist7166bf02019-12-10 16:52:14 +00001263 {
1264 rules[ruleIndex]->handle(req, res, found.second);
James Feist7166bf02019-12-10 16:52:14 +00001265 return;
1266 }
RAJESWARAN THILLAIGOVINDAN61dbeef2019-12-13 04:26:54 -06001267
1268 crow::connections::systemBus->async_method_call(
1269 [&req, &res, &rules, ruleIndex, found](
1270 const boost::system::error_code ec,
1271 std::map<std::string, std::variant<bool, std::string,
1272 std::vector<std::string>>>
1273 userInfo) {
1274 if (ec)
1275 {
1276 BMCWEB_LOG_ERROR << "GetUserInfo failed...";
1277 res.result(
1278 boost::beast::http::status::internal_server_error);
1279 res.end();
1280 return;
1281 }
1282
1283 const std::string* userRolePtr = nullptr;
1284 auto userInfoIter = userInfo.find("UserPrivilege");
1285 if (userInfoIter != userInfo.end())
1286 {
1287 userRolePtr =
1288 std::get_if<std::string>(&userInfoIter->second);
1289 }
1290
1291 std::string userRole{};
1292 if (userRolePtr != nullptr)
1293 {
1294 userRole = *userRolePtr;
1295 BMCWEB_LOG_DEBUG << "userName = " << req.session->username
1296 << " userRole = " << *userRolePtr;
1297 }
1298
Joseph Reynolds3bf4e632020-02-06 14:44:32 -06001299 bool* remoteUserPtr = nullptr;
1300 auto remoteUserIter = userInfo.find("RemoteUser");
1301 if (remoteUserIter != userInfo.end())
1302 {
1303 remoteUserPtr = std::get_if<bool>(&remoteUserIter->second);
1304 }
1305 if (remoteUserPtr == nullptr)
1306 {
1307 BMCWEB_LOG_ERROR
1308 << "RemoteUser property missing or wrong type";
1309 res.result(
1310 boost::beast::http::status::internal_server_error);
1311 res.end();
1312 return;
1313 }
1314 bool remoteUser = *remoteUserPtr;
1315
1316 bool passwordExpired = false; // default for remote user
1317 if (!remoteUser)
1318 {
1319 bool* passwordExpiredPtr = nullptr;
1320 auto passwordExpiredIter =
1321 userInfo.find("UserPasswordExpired");
1322 if (passwordExpiredIter != userInfo.end())
1323 {
1324 passwordExpiredPtr =
1325 std::get_if<bool>(&passwordExpiredIter->second);
1326 }
1327 if (passwordExpiredPtr != nullptr)
1328 {
1329 passwordExpired = *passwordExpiredPtr;
1330 }
1331 else
1332 {
1333 BMCWEB_LOG_ERROR
1334 << "UserPasswordExpired property is expected for"
1335 " local user but is missing or wrong type";
1336 res.result(
1337 boost::beast::http::status::internal_server_error);
1338 res.end();
1339 return;
1340 }
1341 }
1342
RAJESWARAN THILLAIGOVINDAN61dbeef2019-12-13 04:26:54 -06001343 // Get the user privileges from the role
1344 redfish::Privileges userPrivileges =
1345 redfish::getUserPrivileges(userRole);
1346
Joseph Reynolds3bf4e632020-02-06 14:44:32 -06001347 // Set isConfigureSelfOnly based on D-Bus results. This
1348 // ignores the results from both pamAuthenticateUser and the
1349 // value from any previous use of this session.
1350 req.session->isConfigureSelfOnly = passwordExpired;
1351
1352 // Modify privileges if isConfigureSelfOnly.
1353 if (req.session->isConfigureSelfOnly)
1354 {
1355 // Remove all privileges except ConfigureSelf
1356 userPrivileges = userPrivileges.intersection(
1357 redfish::Privileges{"ConfigureSelf"});
1358 BMCWEB_LOG_DEBUG << "Operation limited to ConfigureSelf";
1359 }
1360
RAJESWARAN THILLAIGOVINDAN61dbeef2019-12-13 04:26:54 -06001361 if (!rules[ruleIndex]->checkPrivileges(userPrivileges))
1362 {
1363 res.result(boost::beast::http::status::forbidden);
Joseph Reynolds3bf4e632020-02-06 14:44:32 -06001364 if (req.session->isConfigureSelfOnly)
1365 {
1366 redfish::messages::passwordChangeRequired(
1367 res, "/redfish/v1/AccountService/Accounts/" +
1368 req.session->username);
1369 }
RAJESWARAN THILLAIGOVINDAN61dbeef2019-12-13 04:26:54 -06001370 res.end();
1371 return;
1372 }
1373
1374 req.userRole = userRole;
1375
1376 rules[ruleIndex]->handle(req, res, found.second);
1377 },
1378 "xyz.openbmc_project.User.Manager", "/xyz/openbmc_project/user",
1379 "xyz.openbmc_project.User.Manager", "GetUserInfo",
1380 req.session->username);
Ed Tanous7045c8d2017-04-03 10:04:37 -07001381 }
Ed Tanous7045c8d2017-04-03 10:04:37 -07001382
Ed Tanous1abe55e2018-09-05 08:30:59 -07001383 void debugPrint()
1384 {
Ed Tanous271584a2019-07-09 16:24:22 -07001385 for (size_t i = 0; i < perMethods.size(); i++)
Tanousf00032d2018-11-05 01:18:10 -03001386 {
Ed Tanous271584a2019-07-09 16:24:22 -07001387 BMCWEB_LOG_DEBUG
1388 << methodName(static_cast<boost::beast::http::verb>(i));
Tanousf00032d2018-11-05 01:18:10 -03001389 perMethods[i].trie.debugPrint();
1390 }
Ed Tanous3dac7492017-08-02 13:46:20 -07001391 }
Ed Tanousb4a7bfa2017-04-04 17:23:00 -07001392
Ed Tanous1abe55e2018-09-05 08:30:59 -07001393 std::vector<const std::string*> getRoutes(const std::string& parent)
1394 {
Ed Tanous1abe55e2018-09-05 08:30:59 -07001395 std::vector<const std::string*> ret;
Tanousf00032d2018-11-05 01:18:10 -03001396
1397 for (const PerMethod& pm : perMethods)
Ed Tanous1abe55e2018-09-05 08:30:59 -07001398 {
Tanousf00032d2018-11-05 01:18:10 -03001399 std::vector<unsigned> x;
1400 pm.trie.findRouteIndexes(parent, x);
1401 for (unsigned index : x)
1402 {
1403 ret.push_back(&pm.rules[index]->rule);
1404 }
Ed Tanous1abe55e2018-09-05 08:30:59 -07001405 }
1406 return ret;
1407 }
1408
1409 private:
Tanousf00032d2018-11-05 01:18:10 -03001410 struct PerMethod
1411 {
1412 std::vector<BaseRule*> rules;
1413 Trie trie;
1414 // rule index 0, 1 has special meaning; preallocate it to avoid
1415 // duplication.
1416 PerMethod() : rules(2)
Gunnar Mills1214b7e2020-06-04 10:11:30 -05001417 {}
Tanousf00032d2018-11-05 01:18:10 -03001418 };
1419 std::array<PerMethod, maxHttpVerbCount> perMethods;
1420 std::vector<std::unique_ptr<BaseRule>> allRules;
Ed Tanous7045c8d2017-04-03 10:04:37 -07001421};
Ed Tanous1abe55e2018-09-05 08:30:59 -07001422} // namespace crow