blob: cc5c75fc8c7e92ed4e5eef504c9c91f416352eb4 [file] [log] [blame]
Ed Tanous7045c8d2017-04-03 10:04:37 -07001#pragma once
2
Joseph Reynolds3bf4e632020-02-06 14:44:32 -06003#include "error_messages.hpp"
Tanousf00032d2018-11-05 01:18:10 -03004#include "privileges.hpp"
Ratan Gupta6f359562019-04-03 10:39:08 +05305#include "sessions.hpp"
Ed Tanous1abe55e2018-09-05 08:30:59 -07006
Iwona Klimaszewskac0a1c8a2019-07-12 18:26:38 +02007#include <async_resp.hpp>
Tanousf00032d2018-11-05 01:18:10 -03008#include <boost/container/flat_map.hpp>
9#include <boost/container/small_vector.hpp>
Ed Tanous1abe55e2018-09-05 08:30:59 -070010#include <boost/lexical_cast.hpp>
Ed Tanouse0d918b2018-03-27 17:41:04 -070011#include <cerrno>
Ed Tanous7045c8d2017-04-03 10:04:37 -070012#include <cstdint>
Ed Tanouse0d918b2018-03-27 17:41:04 -070013#include <cstdlib>
Ed Tanous3dac7492017-08-02 13:46:20 -070014#include <limits>
Ed Tanous7045c8d2017-04-03 10:04:37 -070015#include <memory>
16#include <tuple>
Ed Tanous7045c8d2017-04-03 10:04:37 -070017#include <utility>
18#include <vector>
Ed Tanous9140a672017-04-24 17:01:32 -070019
Ed Tanousc94ad492019-10-10 15:39:33 -070020#include "common.h"
21#include "http_request.h"
22#include "http_response.h"
23#include "logging.h"
24#include "utility.h"
25#include "websocket.h"
Ed Tanous7045c8d2017-04-03 10:04:37 -070026
Ed Tanous1abe55e2018-09-05 08:30:59 -070027namespace crow
28{
Tanousf00032d2018-11-05 01:18:10 -030029
30constexpr int maxHttpVerbCount =
31 static_cast<int>(boost::beast::http::verb::unlink);
32
Ed Tanous1abe55e2018-09-05 08:30:59 -070033class BaseRule
34{
35 public:
Tanousf00032d2018-11-05 01:18:10 -030036 BaseRule(std::string thisRule) : rule(std::move(thisRule))
Ed Tanous1abe55e2018-09-05 08:30:59 -070037 {
38 }
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;
102 template <typename T> friend struct RuleParameterTraits;
Ed Tanous7045c8d2017-04-03 10:04:37 -0700103};
104
Ed Tanous1abe55e2018-09-05 08:30:59 -0700105namespace detail
106{
107namespace routing_handler_call_helper
108{
109template <typename T, int Pos> struct CallPair
110{
111 using type = T;
112 static const int pos = Pos;
Ed Tanous7045c8d2017-04-03 10:04:37 -0700113};
114
Ed Tanous1abe55e2018-09-05 08:30:59 -0700115template <typename H1> struct CallParams
116{
117 H1& handler;
118 const RoutingParams& params;
119 const Request& req;
120 Response& res;
Ed Tanous7045c8d2017-04-03 10:04:37 -0700121};
122
123template <typename F, int NInt, int NUint, int NDouble, int NString,
124 typename S1, typename S2>
Ed Tanous1abe55e2018-09-05 08:30:59 -0700125struct Call
126{
127};
Ed Tanous7045c8d2017-04-03 10:04:37 -0700128
129template <typename F, int NInt, int NUint, int NDouble, int NString,
130 typename... Args1, typename... Args2>
Ed Tanous55c7b7a2018-05-22 15:27:24 -0700131struct Call<F, NInt, NUint, NDouble, NString, black_magic::S<int64_t, Args1...>,
Ed Tanous1abe55e2018-09-05 08:30:59 -0700132 black_magic::S<Args2...>>
133{
134 void operator()(F cparams)
135 {
136 using pushed = typename black_magic::S<Args2...>::template push_back<
137 CallPair<int64_t, NInt>>;
138 Call<F, NInt + 1, NUint, NDouble, NString, black_magic::S<Args1...>,
139 pushed>()(cparams);
140 }
Ed Tanous7045c8d2017-04-03 10:04:37 -0700141};
142
143template <typename F, int NInt, int NUint, int NDouble, int NString,
144 typename... Args1, typename... Args2>
Ed Tanous55c7b7a2018-05-22 15:27:24 -0700145struct Call<F, NInt, NUint, NDouble, NString,
Ed Tanous1abe55e2018-09-05 08:30:59 -0700146 black_magic::S<uint64_t, Args1...>, black_magic::S<Args2...>>
147{
148 void operator()(F cparams)
149 {
150 using pushed = typename black_magic::S<Args2...>::template push_back<
151 CallPair<uint64_t, NUint>>;
152 Call<F, NInt, NUint + 1, NDouble, NString, black_magic::S<Args1...>,
153 pushed>()(cparams);
154 }
Ed Tanous7045c8d2017-04-03 10:04:37 -0700155};
156
157template <typename F, int NInt, int NUint, int NDouble, int NString,
158 typename... Args1, typename... Args2>
Ed Tanous55c7b7a2018-05-22 15:27:24 -0700159struct Call<F, NInt, NUint, NDouble, NString, black_magic::S<double, Args1...>,
Ed Tanous1abe55e2018-09-05 08:30:59 -0700160 black_magic::S<Args2...>>
161{
162 void operator()(F cparams)
163 {
164 using pushed = typename black_magic::S<Args2...>::template push_back<
165 CallPair<double, NDouble>>;
166 Call<F, NInt, NUint, NDouble + 1, NString, black_magic::S<Args1...>,
167 pushed>()(cparams);
168 }
Ed Tanous7045c8d2017-04-03 10:04:37 -0700169};
170
171template <typename F, int NInt, int NUint, int NDouble, int NString,
172 typename... Args1, typename... Args2>
Ed Tanous55c7b7a2018-05-22 15:27:24 -0700173struct Call<F, NInt, NUint, NDouble, NString,
Ed Tanous1abe55e2018-09-05 08:30:59 -0700174 black_magic::S<std::string, Args1...>, black_magic::S<Args2...>>
175{
176 void operator()(F cparams)
177 {
178 using pushed = typename black_magic::S<Args2...>::template push_back<
179 CallPair<std::string, NString>>;
180 Call<F, NInt, NUint, NDouble, NString + 1, black_magic::S<Args1...>,
181 pushed>()(cparams);
182 }
Ed Tanous7045c8d2017-04-03 10:04:37 -0700183};
184
185template <typename F, int NInt, int NUint, int NDouble, int NString,
186 typename... Args1>
Ed Tanous55c7b7a2018-05-22 15:27:24 -0700187struct Call<F, NInt, NUint, NDouble, NString, black_magic::S<>,
Ed Tanous1abe55e2018-09-05 08:30:59 -0700188 black_magic::S<Args1...>>
189{
190 void operator()(F cparams)
191 {
192 cparams.handler(
193 cparams.req, cparams.res,
194 cparams.params.template get<typename Args1::type>(Args1::pos)...);
195 }
Ed Tanous7045c8d2017-04-03 10:04:37 -0700196};
197
Ed Tanous1abe55e2018-09-05 08:30:59 -0700198template <typename Func, typename... ArgsWrapped> struct Wrapped
199{
200 template <typename... Args>
201 void set(
202 Func f,
203 typename std::enable_if<
204 !std::is_same<
205 typename std::tuple_element<0, std::tuple<Args..., void>>::type,
206 const Request&>::value,
207 int>::type = 0)
208 {
Tanousf00032d2018-11-05 01:18:10 -0300209 handler = [f = std::move(f)](const Request&, Response& res,
210 Args... args) {
Ed Tanousde5c9f32019-03-26 09:17:55 -0700211 res.result(f(args...));
Tanousf00032d2018-11-05 01:18:10 -0300212 res.end();
213 };
Ed Tanous7045c8d2017-04-03 10:04:37 -0700214 }
215
Ed Tanous1abe55e2018-09-05 08:30:59 -0700216 template <typename Req, typename... Args> struct ReqHandlerWrapper
217 {
218 ReqHandlerWrapper(Func f) : f(std::move(f))
219 {
220 }
Ed Tanous7045c8d2017-04-03 10:04:37 -0700221
Ed Tanous1abe55e2018-09-05 08:30:59 -0700222 void operator()(const Request& req, Response& res, Args... args)
223 {
Ed Tanousde5c9f32019-03-26 09:17:55 -0700224 res.result(f(req, args...));
Ed Tanous1abe55e2018-09-05 08:30:59 -0700225 res.end();
226 }
Ed Tanous7045c8d2017-04-03 10:04:37 -0700227
Ed Tanous1abe55e2018-09-05 08:30:59 -0700228 Func f;
229 };
Ed Tanous7045c8d2017-04-03 10:04:37 -0700230
Ed Tanous1abe55e2018-09-05 08:30:59 -0700231 template <typename... Args>
232 void set(
233 Func f,
234 typename std::enable_if<
235 std::is_same<
236 typename std::tuple_element<0, std::tuple<Args..., void>>::type,
237 const Request&>::value &&
238 !std::is_same<typename std::tuple_element<
239 1, std::tuple<Args..., void, void>>::type,
240 Response&>::value,
241 int>::type = 0)
242 {
243 handler = ReqHandlerWrapper<Args...>(std::move(f));
244 /*handler = (
245 [f = std::move(f)]
246 (const Request& req, Response& res, Args... args){
Ed Tanousde5c9f32019-03-26 09:17:55 -0700247 res.result(f(req, args...));
Ed Tanous1abe55e2018-09-05 08:30:59 -0700248 res.end();
249 });*/
250 }
Ed Tanous7045c8d2017-04-03 10:04:37 -0700251
Ed Tanous1abe55e2018-09-05 08:30:59 -0700252 template <typename... Args>
253 void set(
254 Func f,
255 typename std::enable_if<
256 std::is_same<
257 typename std::tuple_element<0, std::tuple<Args..., void>>::type,
258 const Request&>::value &&
259 std::is_same<typename std::tuple_element<
260 1, std::tuple<Args..., void, void>>::type,
261 Response&>::value,
262 int>::type = 0)
263 {
264 handler = std::move(f);
265 }
Ed Tanous7045c8d2017-04-03 10:04:37 -0700266
Ed Tanous1abe55e2018-09-05 08:30:59 -0700267 template <typename... Args> struct HandlerTypeHelper
268 {
269 using type =
270 std::function<void(const crow::Request&, crow::Response&, Args...)>;
271 using args_type =
272 black_magic::S<typename black_magic::promote_t<Args>...>;
273 };
Ed Tanous7045c8d2017-04-03 10:04:37 -0700274
Ed Tanous1abe55e2018-09-05 08:30:59 -0700275 template <typename... Args>
276 struct HandlerTypeHelper<const Request&, Args...>
277 {
278 using type =
279 std::function<void(const crow::Request&, crow::Response&, Args...)>;
280 using args_type =
281 black_magic::S<typename black_magic::promote_t<Args>...>;
282 };
Ed Tanous7045c8d2017-04-03 10:04:37 -0700283
Ed Tanous1abe55e2018-09-05 08:30:59 -0700284 template <typename... Args>
285 struct HandlerTypeHelper<const Request&, Response&, Args...>
286 {
287 using type =
288 std::function<void(const crow::Request&, crow::Response&, Args...)>;
289 using args_type =
290 black_magic::S<typename black_magic::promote_t<Args>...>;
291 };
292
293 typename HandlerTypeHelper<ArgsWrapped...>::type handler;
294
295 void operator()(const Request& req, Response& res,
296 const RoutingParams& params)
297 {
298 detail::routing_handler_call_helper::Call<
299 detail::routing_handler_call_helper::CallParams<decltype(handler)>,
300 0, 0, 0, 0, typename HandlerTypeHelper<ArgsWrapped...>::args_type,
301 black_magic::S<>>()(
302 detail::routing_handler_call_helper::CallParams<decltype(handler)>{
303 handler, params, req, res});
304 }
Ed Tanous7045c8d2017-04-03 10:04:37 -0700305};
Ed Tanous1abe55e2018-09-05 08:30:59 -0700306} // namespace routing_handler_call_helper
307} // namespace detail
Ed Tanous7045c8d2017-04-03 10:04:37 -0700308
Ed Tanous1abe55e2018-09-05 08:30:59 -0700309class WebSocketRule : public BaseRule
310{
311 using self_t = WebSocketRule;
Ed Tanous7045c8d2017-04-03 10:04:37 -0700312
Ed Tanous1abe55e2018-09-05 08:30:59 -0700313 public:
314 WebSocketRule(std::string rule) : BaseRule(std::move(rule))
315 {
316 }
Ed Tanous7045c8d2017-04-03 10:04:37 -0700317
Ed Tanous1abe55e2018-09-05 08:30:59 -0700318 void validate() override
319 {
320 }
Ed Tanous7045c8d2017-04-03 10:04:37 -0700321
Ed Tanous1abe55e2018-09-05 08:30:59 -0700322 void handle(const Request&, Response& res, const RoutingParams&) override
323 {
Ed Tanousde5c9f32019-03-26 09:17:55 -0700324 res.result(boost::beast::http::status::not_found);
Ed Tanous1abe55e2018-09-05 08:30:59 -0700325 res.end();
326 }
Ed Tanous7045c8d2017-04-03 10:04:37 -0700327
Jan Sowinskiee52ae12020-01-09 16:28:32 +0000328 void handleUpgrade(const Request& req, Response&,
Ed Tanousceac6f72018-12-02 11:58:47 -0800329 boost::asio::ip::tcp::socket&& adaptor) override
Ed Tanous1abe55e2018-09-05 08:30:59 -0700330 {
Ratan Gupta02453b12019-10-22 14:43:36 +0530331 std::shared_ptr<
332 crow::websocket::ConnectionImpl<boost::asio::ip::tcp::socket>>
333 myConnection = std::make_shared<
334 crow::websocket::ConnectionImpl<boost::asio::ip::tcp::socket>>(
Jan Sowinskiee52ae12020-01-09 16:28:32 +0000335 req, std::move(adaptor), openHandler, messageHandler,
Ratan Gupta02453b12019-10-22 14:43:36 +0530336 closeHandler, errorHandler);
337 myConnection->start();
Ed Tanous1abe55e2018-09-05 08:30:59 -0700338 }
339#ifdef BMCWEB_ENABLE_SSL
Jan Sowinskiee52ae12020-01-09 16:28:32 +0000340 void handleUpgrade(const Request& req, Response&,
Ed Tanousceac6f72018-12-02 11:58:47 -0800341 boost::beast::ssl_stream<boost::asio::ip::tcp::socket>&&
342 adaptor) override
Ed Tanous1abe55e2018-09-05 08:30:59 -0700343 {
Ed Tanousceac6f72018-12-02 11:58:47 -0800344 std::shared_ptr<crow::websocket::ConnectionImpl<
345 boost::beast::ssl_stream<boost::asio::ip::tcp::socket>>>
346 myConnection = std::make_shared<crow::websocket::ConnectionImpl<
347 boost::beast::ssl_stream<boost::asio::ip::tcp::socket>>>(
Jan Sowinskiee52ae12020-01-09 16:28:32 +0000348 req, std::move(adaptor), openHandler, messageHandler,
Ed Tanousceac6f72018-12-02 11:58:47 -0800349 closeHandler, errorHandler);
Ed Tanous1abe55e2018-09-05 08:30:59 -0700350 myConnection->start();
351 }
Ed Tanous7045c8d2017-04-03 10:04:37 -0700352#endif
353
Ed Tanous1abe55e2018-09-05 08:30:59 -0700354 template <typename Func> self_t& onopen(Func f)
355 {
356 openHandler = f;
357 return *this;
358 }
Ed Tanous7045c8d2017-04-03 10:04:37 -0700359
Ed Tanous1abe55e2018-09-05 08:30:59 -0700360 template <typename Func> self_t& onmessage(Func f)
361 {
362 messageHandler = f;
363 return *this;
364 }
Ed Tanous7045c8d2017-04-03 10:04:37 -0700365
Ed Tanous1abe55e2018-09-05 08:30:59 -0700366 template <typename Func> self_t& onclose(Func f)
367 {
368 closeHandler = f;
369 return *this;
370 }
Ed Tanous7045c8d2017-04-03 10:04:37 -0700371
Ed Tanous1abe55e2018-09-05 08:30:59 -0700372 template <typename Func> self_t& onerror(Func f)
373 {
374 errorHandler = f;
375 return *this;
376 }
Ed Tanous7045c8d2017-04-03 10:04:37 -0700377
Ed Tanous1abe55e2018-09-05 08:30:59 -0700378 protected:
Iwona Klimaszewskac0a1c8a2019-07-12 18:26:38 +0200379 std::function<void(crow::websocket::Connection&,
380 std::shared_ptr<bmcweb::AsyncResp>)>
381 openHandler;
Ed Tanous1abe55e2018-09-05 08:30:59 -0700382 std::function<void(crow::websocket::Connection&, const std::string&, bool)>
383 messageHandler;
384 std::function<void(crow::websocket::Connection&, const std::string&)>
385 closeHandler;
386 std::function<void(crow::websocket::Connection&)> errorHandler;
Ed Tanous7045c8d2017-04-03 10:04:37 -0700387};
388
Ed Tanous1abe55e2018-09-05 08:30:59 -0700389template <typename T> struct RuleParameterTraits
390{
391 using self_t = T;
392 WebSocketRule& websocket()
393 {
Ed Tanous271584a2019-07-09 16:24:22 -0700394 self_t* self = static_cast<self_t*>(this);
395 WebSocketRule* p = new WebSocketRule(self->rule);
396 self->ruleToUpgrade.reset(p);
Ed Tanous1abe55e2018-09-05 08:30:59 -0700397 return *p;
398 }
Ed Tanous7045c8d2017-04-03 10:04:37 -0700399
Ed Tanous1abe55e2018-09-05 08:30:59 -0700400 self_t& name(std::string name) noexcept
401 {
Ed Tanous271584a2019-07-09 16:24:22 -0700402 self_t* self = static_cast<self_t*>(this);
403 self->nameStr = std::move(name);
404 return *self;
Ed Tanous1abe55e2018-09-05 08:30:59 -0700405 }
Ed Tanous7045c8d2017-04-03 10:04:37 -0700406
Ed Tanous1abe55e2018-09-05 08:30:59 -0700407 self_t& methods(boost::beast::http::verb method)
408 {
Ed Tanous271584a2019-07-09 16:24:22 -0700409 self_t* self = static_cast<self_t*>(this);
410 self->methodsBitfield = 1U << static_cast<size_t>(method);
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 template <typename... MethodArgs>
415 self_t& methods(boost::beast::http::verb method, MethodArgs... args_method)
416 {
Ed Tanous271584a2019-07-09 16:24:22 -0700417 self_t* self = static_cast<self_t*>(this);
Ed Tanous1abe55e2018-09-05 08:30:59 -0700418 methods(args_method...);
Ed Tanous271584a2019-07-09 16:24:22 -0700419 self->methodsBitfield |= 1U << static_cast<size_t>(method);
420 return *self;
Ed Tanous1abe55e2018-09-05 08:30:59 -0700421 }
Tanousf00032d2018-11-05 01:18:10 -0300422
423 template <typename... MethodArgs>
424 self_t& requires(std::initializer_list<const char*> l)
425 {
Ed Tanous271584a2019-07-09 16:24:22 -0700426 self_t* self = static_cast<self_t*>(this);
427 self->privilegesSet.emplace_back(l);
428 return *self;
Tanousf00032d2018-11-05 01:18:10 -0300429 }
430
431 template <typename... MethodArgs>
432 self_t& requires(const std::vector<redfish::Privileges>& p)
433 {
Ed Tanous271584a2019-07-09 16:24:22 -0700434 self_t* self = static_cast<self_t*>(this);
Tanousf00032d2018-11-05 01:18:10 -0300435 for (const redfish::Privileges& privilege : p)
436 {
Ed Tanous271584a2019-07-09 16:24:22 -0700437 self->privilegesSet.emplace_back(privilege);
Tanousf00032d2018-11-05 01:18:10 -0300438 }
Ed Tanous271584a2019-07-09 16:24:22 -0700439 return *self;
Tanousf00032d2018-11-05 01:18:10 -0300440 }
Ed Tanous7045c8d2017-04-03 10:04:37 -0700441};
442
Ed Tanous1abe55e2018-09-05 08:30:59 -0700443class DynamicRule : public BaseRule, public RuleParameterTraits<DynamicRule>
444{
445 public:
446 DynamicRule(std::string rule) : BaseRule(std::move(rule))
447 {
Ed Tanous7045c8d2017-04-03 10:04:37 -0700448 }
Ed Tanous7045c8d2017-04-03 10:04:37 -0700449
Ed Tanous1abe55e2018-09-05 08:30:59 -0700450 void validate() override
451 {
452 if (!erasedHandler)
453 {
454 throw std::runtime_error(nameStr + (!nameStr.empty() ? ": " : "") +
455 "no handler for url " + rule);
456 }
Ed Tanous7045c8d2017-04-03 10:04:37 -0700457 }
Ed Tanous7045c8d2017-04-03 10:04:37 -0700458
Ed Tanous1abe55e2018-09-05 08:30:59 -0700459 void handle(const Request& req, Response& res,
460 const RoutingParams& params) override
461 {
462 erasedHandler(req, res, params);
463 }
Ed Tanous7045c8d2017-04-03 10:04:37 -0700464
Ed Tanous1abe55e2018-09-05 08:30:59 -0700465 template <typename Func> void operator()(Func f)
466 {
467 using function_t = utility::function_traits<Func>;
468
469 erasedHandler =
470 wrap(std::move(f), black_magic::gen_seq<function_t::arity>());
471 }
472
473 // enable_if Arg1 == request && Arg2 == Response
474 // enable_if Arg1 == request && Arg2 != resposne
475 // enable_if Arg1 != request
476
477 template <typename Func, unsigned... Indices>
478
479 std::function<void(const Request&, Response&, const RoutingParams&)>
480 wrap(Func f, black_magic::Seq<Indices...>)
481 {
482 using function_t = utility::function_traits<Func>;
483
484 if (!black_magic::isParameterTagCompatible(
485 black_magic::getParameterTagRuntime(rule.c_str()),
486 black_magic::compute_parameter_tag_from_args_list<
487 typename function_t::template arg<Indices>...>::value))
488 {
489 throw std::runtime_error("routeDynamic: Handler type is mismatched "
490 "with URL parameters: " +
491 rule);
492 }
493 auto ret = detail::routing_handler_call_helper::Wrapped<
494 Func, typename function_t::template arg<Indices>...>();
495 ret.template set<typename function_t::template arg<Indices>...>(
496 std::move(f));
497 return ret;
498 }
499
500 template <typename Func> void operator()(std::string name, Func&& f)
501 {
502 nameStr = std::move(name);
503 (*this).template operator()<Func>(std::forward(f));
504 }
505
506 private:
507 std::function<void(const Request&, Response&, const RoutingParams&)>
508 erasedHandler;
Ed Tanous7045c8d2017-04-03 10:04:37 -0700509};
510
511template <typename... Args>
512class TaggedRule : public BaseRule,
Ed Tanous1abe55e2018-09-05 08:30:59 -0700513 public RuleParameterTraits<TaggedRule<Args...>>
514{
515 public:
516 using self_t = TaggedRule<Args...>;
Ed Tanous7045c8d2017-04-03 10:04:37 -0700517
Ed Tanous271584a2019-07-09 16:24:22 -0700518 TaggedRule(std::string ruleIn) : BaseRule(std::move(ruleIn))
Ed Tanous1abe55e2018-09-05 08:30:59 -0700519 {
Ed Tanous7045c8d2017-04-03 10:04:37 -0700520 }
Ed Tanous7045c8d2017-04-03 10:04:37 -0700521
Ed Tanous1abe55e2018-09-05 08:30:59 -0700522 void validate() override
523 {
524 if (!handler)
525 {
526 throw std::runtime_error(nameStr + (!nameStr.empty() ? ": " : "") +
527 "no handler for url " + rule);
528 }
529 }
530
531 template <typename Func>
532 typename std::enable_if<
533 black_magic::CallHelper<Func, black_magic::S<Args...>>::value,
534 void>::type
535 operator()(Func&& f)
536 {
537 static_assert(
538 black_magic::CallHelper<Func, black_magic::S<Args...>>::value ||
539 black_magic::CallHelper<
540 Func, black_magic::S<crow::Request, Args...>>::value,
541 "Handler type is mismatched with URL parameters");
542 static_assert(
543 !std::is_same<void, decltype(f(std::declval<Args>()...))>::value,
544 "Handler function cannot have void return type; valid return "
545 "types: "
546 "string, int, crow::resposne, nlohmann::json");
547
548 handler = [f = std::move(f)](const Request&, Response& res,
549 Args... args) {
Ed Tanousde5c9f32019-03-26 09:17:55 -0700550 res.result(f(args...));
Ed Tanous1abe55e2018-09-05 08:30:59 -0700551 res.end();
552 };
553 }
554
555 template <typename Func>
556 typename std::enable_if<
557 !black_magic::CallHelper<Func, black_magic::S<Args...>>::value &&
Ed Tanous7045c8d2017-04-03 10:04:37 -0700558 black_magic::CallHelper<
Ed Tanous55c7b7a2018-05-22 15:27:24 -0700559 Func, black_magic::S<crow::Request, Args...>>::value,
Ed Tanous1abe55e2018-09-05 08:30:59 -0700560 void>::type
561 operator()(Func&& f)
562 {
563 static_assert(
564 black_magic::CallHelper<Func, black_magic::S<Args...>>::value ||
565 black_magic::CallHelper<
566 Func, black_magic::S<crow::Request, Args...>>::value,
567 "Handler type is mismatched with URL parameters");
568 static_assert(
569 !std::is_same<void, decltype(f(std::declval<crow::Request>(),
570 std::declval<Args>()...))>::value,
571 "Handler function cannot have void return type; valid return "
572 "types: "
573 "string, int, crow::resposne,nlohmann::json");
Ed Tanous7045c8d2017-04-03 10:04:37 -0700574
Ed Tanous1abe55e2018-09-05 08:30:59 -0700575 handler = [f = std::move(f)](const crow::Request& req,
576 crow::Response& res, Args... args) {
Ed Tanousde5c9f32019-03-26 09:17:55 -0700577 res.result(f(req, args...));
Ed Tanous1abe55e2018-09-05 08:30:59 -0700578 res.end();
579 };
580 }
Ed Tanous7045c8d2017-04-03 10:04:37 -0700581
Ed Tanous1abe55e2018-09-05 08:30:59 -0700582 template <typename Func>
583 typename std::enable_if<
584 !black_magic::CallHelper<Func, black_magic::S<Args...>>::value &&
585 !black_magic::CallHelper<
Ed Tanous55c7b7a2018-05-22 15:27:24 -0700586 Func, black_magic::S<crow::Request, Args...>>::value,
Ed Tanous1abe55e2018-09-05 08:30:59 -0700587 void>::type
588 operator()(Func&& f)
589 {
590 static_assert(
591 black_magic::CallHelper<Func, black_magic::S<Args...>>::value ||
592 black_magic::CallHelper<
593 Func, black_magic::S<crow::Request, Args...>>::value ||
594 black_magic::CallHelper<
595 Func, black_magic::S<crow::Request, crow::Response&,
596 Args...>>::value,
597 "Handler type is mismatched with URL parameters");
598 static_assert(
599 std::is_same<void, decltype(f(std::declval<crow::Request>(),
600 std::declval<crow::Response&>(),
601 std::declval<Args>()...))>::value,
Tanousf00032d2018-11-05 01:18:10 -0300602 "Handler function with response argument should have void "
603 "return "
Ed Tanous1abe55e2018-09-05 08:30:59 -0700604 "type");
Ed Tanous7045c8d2017-04-03 10:04:37 -0700605
Ed Tanous1abe55e2018-09-05 08:30:59 -0700606 handler = std::move(f);
607 }
Ed Tanous7045c8d2017-04-03 10:04:37 -0700608
Ed Tanous1abe55e2018-09-05 08:30:59 -0700609 template <typename Func> void operator()(std::string name, Func&& f)
610 {
611 nameStr = std::move(name);
612 (*this).template operator()<Func>(std::forward(f));
613 }
Ed Tanous7045c8d2017-04-03 10:04:37 -0700614
Ed Tanous1abe55e2018-09-05 08:30:59 -0700615 void handle(const Request& req, Response& res,
616 const RoutingParams& params) override
617 {
618 detail::routing_handler_call_helper::Call<
619 detail::routing_handler_call_helper::CallParams<decltype(handler)>,
620 0, 0, 0, 0, black_magic::S<Args...>, black_magic::S<>>()(
621 detail::routing_handler_call_helper::CallParams<decltype(handler)>{
622 handler, params, req, res});
623 }
Ed Tanous7045c8d2017-04-03 10:04:37 -0700624
Ed Tanous1abe55e2018-09-05 08:30:59 -0700625 private:
626 std::function<void(const crow::Request&, crow::Response&, Args...)> handler;
Ed Tanous7045c8d2017-04-03 10:04:37 -0700627};
628
Ed Tanous55c7b7a2018-05-22 15:27:24 -0700629const int ruleSpecialRedirectSlash = 1;
Ed Tanous7045c8d2017-04-03 10:04:37 -0700630
Ed Tanous1abe55e2018-09-05 08:30:59 -0700631class Trie
632{
633 public:
634 struct Node
635 {
636 unsigned ruleIndex{};
Ed Tanous271584a2019-07-09 16:24:22 -0700637 std::array<size_t, static_cast<size_t>(ParamType::MAX)>
638 paramChildrens{};
Ed Tanous1abe55e2018-09-05 08:30:59 -0700639 boost::container::flat_map<std::string, unsigned> children;
Ed Tanous7045c8d2017-04-03 10:04:37 -0700640
Ed Tanous1abe55e2018-09-05 08:30:59 -0700641 bool isSimpleNode() const
642 {
643 return !ruleIndex && std::all_of(std::begin(paramChildrens),
644 std::end(paramChildrens),
Ed Tanous271584a2019-07-09 16:24:22 -0700645 [](size_t x) { return !x; });
Ed Tanous7045c8d2017-04-03 10:04:37 -0700646 }
Ed Tanous55c7b7a2018-05-22 15:27:24 -0700647 };
Ed Tanous7045c8d2017-04-03 10:04:37 -0700648
Ed Tanous1abe55e2018-09-05 08:30:59 -0700649 Trie() : nodes(1)
650 {
651 }
652
653 private:
654 void optimizeNode(Node* node)
655 {
Ed Tanous271584a2019-07-09 16:24:22 -0700656 for (size_t x : node->paramChildrens)
Ed Tanous1abe55e2018-09-05 08:30:59 -0700657 {
658 if (!x)
659 continue;
660 Node* child = &nodes[x];
661 optimizeNode(child);
Ed Tanous7045c8d2017-04-03 10:04:37 -0700662 }
Ed Tanous1abe55e2018-09-05 08:30:59 -0700663 if (node->children.empty())
664 return;
665 bool mergeWithChild = true;
Tanousf00032d2018-11-05 01:18:10 -0300666 for (const std::pair<std::string, unsigned>& kv : node->children)
Ed Tanous1abe55e2018-09-05 08:30:59 -0700667 {
668 Node* child = &nodes[kv.second];
669 if (!child->isSimpleNode())
670 {
671 mergeWithChild = false;
672 break;
Ed Tanous7045c8d2017-04-03 10:04:37 -0700673 }
Ed Tanous1abe55e2018-09-05 08:30:59 -0700674 }
675 if (mergeWithChild)
676 {
677 decltype(node->children) merged;
Tanousf00032d2018-11-05 01:18:10 -0300678 for (const std::pair<std::string, unsigned>& kv : node->children)
Ed Tanous1abe55e2018-09-05 08:30:59 -0700679 {
680 Node* child = &nodes[kv.second];
Tanousf00032d2018-11-05 01:18:10 -0300681 for (const std::pair<std::string, unsigned>& childKv :
682 child->children)
Ed Tanous1abe55e2018-09-05 08:30:59 -0700683 {
684 merged[kv.first + childKv.first] = childKv.second;
685 }
686 }
687 node->children = std::move(merged);
688 optimizeNode(node);
689 }
690 else
691 {
Tanousf00032d2018-11-05 01:18:10 -0300692 for (const std::pair<std::string, unsigned>& kv : node->children)
Ed Tanous1abe55e2018-09-05 08:30:59 -0700693 {
694 Node* child = &nodes[kv.second];
695 optimizeNode(child);
696 }
697 }
698 }
699
700 void optimize()
701 {
702 optimizeNode(head());
703 }
704
705 public:
706 void validate()
707 {
708 if (!head()->isSimpleNode())
709 throw std::runtime_error(
710 "Internal error: Trie header should be simple!");
711 optimize();
712 }
713
714 void findRouteIndexes(const std::string& req_url,
715 std::vector<unsigned>& route_indexes,
Tanousf00032d2018-11-05 01:18:10 -0300716 const Node* node = nullptr, unsigned pos = 0) const
Ed Tanous1abe55e2018-09-05 08:30:59 -0700717 {
718 if (node == nullptr)
719 {
720 node = head();
721 }
Tanousf00032d2018-11-05 01:18:10 -0300722 for (const std::pair<std::string, unsigned>& kv : node->children)
Ed Tanous1abe55e2018-09-05 08:30:59 -0700723 {
724 const std::string& fragment = kv.first;
725 const Node* child = &nodes[kv.second];
726 if (pos >= req_url.size())
727 {
728 if (child->ruleIndex != 0 && fragment != "/")
729 {
730 route_indexes.push_back(child->ruleIndex);
731 }
732 findRouteIndexes(req_url, route_indexes, child,
Ed Tanous271584a2019-07-09 16:24:22 -0700733 static_cast<unsigned>(pos + fragment.size()));
Ed Tanous1abe55e2018-09-05 08:30:59 -0700734 }
735 else
736 {
737 if (req_url.compare(pos, fragment.size(), fragment) == 0)
738 {
Ed Tanous271584a2019-07-09 16:24:22 -0700739 findRouteIndexes(
740 req_url, route_indexes, child,
741 static_cast<unsigned>(pos + fragment.size()));
Ed Tanous1abe55e2018-09-05 08:30:59 -0700742 }
743 }
744 }
745 }
746
747 std::pair<unsigned, RoutingParams>
Ed Tanous39e77502019-03-04 17:35:53 -0800748 find(const std::string_view req_url, const Node* node = nullptr,
Ed Tanous271584a2019-07-09 16:24:22 -0700749 size_t pos = 0, RoutingParams* params = nullptr) const
Ed Tanous1abe55e2018-09-05 08:30:59 -0700750 {
751 RoutingParams empty;
752 if (params == nullptr)
753 params = &empty;
754
755 unsigned found{};
756 RoutingParams matchParams;
757
758 if (node == nullptr)
759 node = head();
760 if (pos == req_url.size())
761 return {node->ruleIndex, *params};
762
763 auto updateFound =
764 [&found, &matchParams](std::pair<unsigned, RoutingParams>& ret) {
765 if (ret.first && (!found || found > ret.first))
766 {
767 found = ret.first;
768 matchParams = std::move(ret.second);
769 }
770 };
771
Ed Tanous271584a2019-07-09 16:24:22 -0700772 if (node->paramChildrens[static_cast<size_t>(ParamType::INT)])
Ed Tanous1abe55e2018-09-05 08:30:59 -0700773 {
774 char c = req_url[pos];
775 if ((c >= '0' && c <= '9') || c == '+' || c == '-')
776 {
777 char* eptr;
778 errno = 0;
779 long long int value =
780 std::strtoll(req_url.data() + pos, &eptr, 10);
781 if (errno != ERANGE && eptr != req_url.data() + pos)
782 {
783 params->intParams.push_back(value);
Ed Tanous271584a2019-07-09 16:24:22 -0700784 std::pair<unsigned, RoutingParams> ret = find(
785 req_url,
786 &nodes[node->paramChildrens[static_cast<size_t>(
787 ParamType::INT)]],
788 static_cast<size_t>(eptr - req_url.data()), params);
Ed Tanous1abe55e2018-09-05 08:30:59 -0700789 updateFound(ret);
790 params->intParams.pop_back();
791 }
792 }
Ed Tanous7045c8d2017-04-03 10:04:37 -0700793 }
794
Ed Tanous271584a2019-07-09 16:24:22 -0700795 if (node->paramChildrens[static_cast<size_t>(ParamType::UINT)])
Ed Tanous1abe55e2018-09-05 08:30:59 -0700796 {
797 char c = req_url[pos];
798 if ((c >= '0' && c <= '9') || c == '+')
799 {
800 char* eptr;
801 errno = 0;
802 unsigned long long int value =
803 std::strtoull(req_url.data() + pos, &eptr, 10);
804 if (errno != ERANGE && eptr != req_url.data() + pos)
805 {
806 params->uintParams.push_back(value);
Ed Tanous271584a2019-07-09 16:24:22 -0700807 std::pair<unsigned, RoutingParams> ret = find(
808 req_url,
809 &nodes[node->paramChildrens[static_cast<size_t>(
810 ParamType::UINT)]],
811 static_cast<size_t>(eptr - req_url.data()), params);
Ed Tanous1abe55e2018-09-05 08:30:59 -0700812 updateFound(ret);
813 params->uintParams.pop_back();
814 }
815 }
Ed Tanous7045c8d2017-04-03 10:04:37 -0700816 }
817
Ed Tanous271584a2019-07-09 16:24:22 -0700818 if (node->paramChildrens[static_cast<size_t>(ParamType::DOUBLE)])
Ed Tanous1abe55e2018-09-05 08:30:59 -0700819 {
820 char c = req_url[pos];
821 if ((c >= '0' && c <= '9') || c == '+' || c == '-' || c == '.')
822 {
823 char* eptr;
824 errno = 0;
825 double value = std::strtod(req_url.data() + pos, &eptr);
826 if (errno != ERANGE && eptr != req_url.data() + pos)
827 {
828 params->doubleParams.push_back(value);
Tanousf00032d2018-11-05 01:18:10 -0300829 std::pair<unsigned, RoutingParams> ret = find(
Ed Tanous1abe55e2018-09-05 08:30:59 -0700830 req_url,
Ed Tanous271584a2019-07-09 16:24:22 -0700831 &nodes[node->paramChildrens[static_cast<size_t>(
832 ParamType::DOUBLE)]],
833 static_cast<size_t>(eptr - req_url.data()), params);
Ed Tanous1abe55e2018-09-05 08:30:59 -0700834 updateFound(ret);
835 params->doubleParams.pop_back();
836 }
837 }
838 }
839
Ed Tanous271584a2019-07-09 16:24:22 -0700840 if (node->paramChildrens[static_cast<size_t>(ParamType::STRING)])
Ed Tanous1abe55e2018-09-05 08:30:59 -0700841 {
Ed Tanousb01bf292019-03-25 19:25:26 +0000842 size_t epos = pos;
Ed Tanous1abe55e2018-09-05 08:30:59 -0700843 for (; epos < req_url.size(); epos++)
844 {
845 if (req_url[epos] == '/')
846 break;
847 }
848
849 if (epos != pos)
850 {
851 params->stringParams.emplace_back(
852 req_url.substr(pos, epos - pos));
Tanousf00032d2018-11-05 01:18:10 -0300853 std::pair<unsigned, RoutingParams> ret =
Ed Tanousb01bf292019-03-25 19:25:26 +0000854 find(req_url,
Ed Tanous271584a2019-07-09 16:24:22 -0700855 &nodes[node->paramChildrens[static_cast<size_t>(
856 ParamType::STRING)]],
Ed Tanousb01bf292019-03-25 19:25:26 +0000857 epos, params);
Ed Tanous1abe55e2018-09-05 08:30:59 -0700858 updateFound(ret);
859 params->stringParams.pop_back();
860 }
861 }
862
Ed Tanous271584a2019-07-09 16:24:22 -0700863 if (node->paramChildrens[static_cast<size_t>(ParamType::PATH)])
Ed Tanous1abe55e2018-09-05 08:30:59 -0700864 {
Ed Tanousb01bf292019-03-25 19:25:26 +0000865 size_t epos = req_url.size();
Ed Tanous1abe55e2018-09-05 08:30:59 -0700866
867 if (epos != pos)
868 {
869 params->stringParams.emplace_back(
870 req_url.substr(pos, epos - pos));
Ed Tanous271584a2019-07-09 16:24:22 -0700871 std::pair<unsigned, RoutingParams> ret =
872 find(req_url,
873 &nodes[node->paramChildrens[static_cast<size_t>(
874 ParamType::PATH)]],
875 epos, params);
Ed Tanous1abe55e2018-09-05 08:30:59 -0700876 updateFound(ret);
877 params->stringParams.pop_back();
878 }
879 }
880
Tanousf00032d2018-11-05 01:18:10 -0300881 for (const std::pair<std::string, unsigned>& kv : node->children)
Ed Tanous1abe55e2018-09-05 08:30:59 -0700882 {
883 const std::string& fragment = kv.first;
884 const Node* child = &nodes[kv.second];
885
886 if (req_url.compare(pos, fragment.size(), fragment) == 0)
887 {
Tanousf00032d2018-11-05 01:18:10 -0300888 std::pair<unsigned, RoutingParams> ret =
889 find(req_url, child, pos + fragment.size(), params);
Ed Tanous1abe55e2018-09-05 08:30:59 -0700890 updateFound(ret);
891 }
892 }
893
894 return {found, matchParams};
Ed Tanous7045c8d2017-04-03 10:04:37 -0700895 }
Ed Tanous1abe55e2018-09-05 08:30:59 -0700896
897 void add(const std::string& url, unsigned ruleIndex)
898 {
Ed Tanous271584a2019-07-09 16:24:22 -0700899 size_t idx = 0;
Ed Tanous1abe55e2018-09-05 08:30:59 -0700900
901 for (unsigned i = 0; i < url.size(); i++)
902 {
903 char c = url[i];
904 if (c == '<')
905 {
Tanousf00032d2018-11-05 01:18:10 -0300906 const static std::array<std::pair<ParamType, std::string>, 7>
907 paramTraits = {{
908 {ParamType::INT, "<int>"},
909 {ParamType::UINT, "<uint>"},
910 {ParamType::DOUBLE, "<float>"},
911 {ParamType::DOUBLE, "<double>"},
912 {ParamType::STRING, "<str>"},
913 {ParamType::STRING, "<string>"},
914 {ParamType::PATH, "<path>"},
915 }};
Ed Tanous1abe55e2018-09-05 08:30:59 -0700916
Tanousf00032d2018-11-05 01:18:10 -0300917 for (const std::pair<ParamType, std::string>& x : paramTraits)
Ed Tanous1abe55e2018-09-05 08:30:59 -0700918 {
Tanousf00032d2018-11-05 01:18:10 -0300919 if (url.compare(i, x.second.size(), x.second) == 0)
Ed Tanous1abe55e2018-09-05 08:30:59 -0700920 {
Ed Tanous271584a2019-07-09 16:24:22 -0700921 size_t index = static_cast<size_t>(x.first);
922 if (!nodes[idx].paramChildrens[index])
Ed Tanous1abe55e2018-09-05 08:30:59 -0700923 {
Tanousf00032d2018-11-05 01:18:10 -0300924 unsigned newNodeIdx = newNode();
Ed Tanous271584a2019-07-09 16:24:22 -0700925 nodes[idx].paramChildrens[index] = newNodeIdx;
Ed Tanous1abe55e2018-09-05 08:30:59 -0700926 }
Ed Tanous271584a2019-07-09 16:24:22 -0700927 idx = nodes[idx].paramChildrens[index];
928 i += static_cast<unsigned>(x.second.size());
Ed Tanous1abe55e2018-09-05 08:30:59 -0700929 break;
930 }
931 }
932
933 i--;
934 }
935 else
936 {
937 std::string piece(&c, 1);
938 if (!nodes[idx].children.count(piece))
939 {
Tanousf00032d2018-11-05 01:18:10 -0300940 unsigned newNodeIdx = newNode();
Ed Tanous1abe55e2018-09-05 08:30:59 -0700941 nodes[idx].children.emplace(piece, newNodeIdx);
942 }
943 idx = nodes[idx].children[piece];
944 }
945 }
946 if (nodes[idx].ruleIndex)
947 throw std::runtime_error("handler already exists for " + url);
948 nodes[idx].ruleIndex = ruleIndex;
Ed Tanous7045c8d2017-04-03 10:04:37 -0700949 }
Ed Tanous7045c8d2017-04-03 10:04:37 -0700950
Ed Tanous1abe55e2018-09-05 08:30:59 -0700951 private:
Ed Tanous271584a2019-07-09 16:24:22 -0700952 void debugNodePrint(Node* n, size_t level)
Ed Tanous1abe55e2018-09-05 08:30:59 -0700953 {
Ed Tanous271584a2019-07-09 16:24:22 -0700954 for (size_t i = 0; i < static_cast<size_t>(ParamType::MAX); i++)
Ed Tanous1abe55e2018-09-05 08:30:59 -0700955 {
956 if (n->paramChildrens[i])
957 {
958 BMCWEB_LOG_DEBUG << std::string(
Ed Tanous271584a2019-07-09 16:24:22 -0700959 2U * level, ' ') /*<< "("<<n->paramChildrens[i]<<") "*/;
960 switch (static_cast<ParamType>(i))
Ed Tanous1abe55e2018-09-05 08:30:59 -0700961 {
962 case ParamType::INT:
963 BMCWEB_LOG_DEBUG << "<int>";
964 break;
965 case ParamType::UINT:
966 BMCWEB_LOG_DEBUG << "<uint>";
967 break;
968 case ParamType::DOUBLE:
969 BMCWEB_LOG_DEBUG << "<float>";
970 break;
971 case ParamType::STRING:
972 BMCWEB_LOG_DEBUG << "<str>";
973 break;
974 case ParamType::PATH:
975 BMCWEB_LOG_DEBUG << "<path>";
976 break;
977 default:
978 BMCWEB_LOG_DEBUG << "<ERROR>";
979 break;
980 }
Ed Tanous7045c8d2017-04-03 10:04:37 -0700981
Ed Tanous1abe55e2018-09-05 08:30:59 -0700982 debugNodePrint(&nodes[n->paramChildrens[i]], level + 1);
983 }
984 }
Tanousf00032d2018-11-05 01:18:10 -0300985 for (const std::pair<std::string, unsigned>& kv : n->children)
Ed Tanous1abe55e2018-09-05 08:30:59 -0700986 {
987 BMCWEB_LOG_DEBUG
Ed Tanous271584a2019-07-09 16:24:22 -0700988 << std::string(2U * level, ' ') /*<< "(" << kv.second << ") "*/
Ed Tanous1abe55e2018-09-05 08:30:59 -0700989 << kv.first;
990 debugNodePrint(&nodes[kv.second], level + 1);
991 }
992 }
Ed Tanous7045c8d2017-04-03 10:04:37 -0700993
Ed Tanous1abe55e2018-09-05 08:30:59 -0700994 public:
995 void debugPrint()
996 {
Ed Tanous271584a2019-07-09 16:24:22 -0700997 debugNodePrint(head(), 0U);
Ed Tanous1abe55e2018-09-05 08:30:59 -0700998 }
Ed Tanous7045c8d2017-04-03 10:04:37 -0700999
Ed Tanous1abe55e2018-09-05 08:30:59 -07001000 private:
1001 const Node* head() const
1002 {
1003 return &nodes.front();
1004 }
Ed Tanous7045c8d2017-04-03 10:04:37 -07001005
Ed Tanous1abe55e2018-09-05 08:30:59 -07001006 Node* head()
1007 {
1008 return &nodes.front();
1009 }
1010
1011 unsigned newNode()
1012 {
1013 nodes.resize(nodes.size() + 1);
Ed Tanous271584a2019-07-09 16:24:22 -07001014 return static_cast<unsigned>(nodes.size() - 1);
Ed Tanous1abe55e2018-09-05 08:30:59 -07001015 }
1016
1017 std::vector<Node> nodes;
Ed Tanous7045c8d2017-04-03 10:04:37 -07001018};
1019
Ed Tanous1abe55e2018-09-05 08:30:59 -07001020class Router
1021{
1022 public:
Ed Tanous0c0084a2019-10-24 15:57:51 -07001023 Router() = default;
Ed Tanous7045c8d2017-04-03 10:04:37 -07001024
Ed Tanous1abe55e2018-09-05 08:30:59 -07001025 DynamicRule& newRuleDynamic(const std::string& rule)
1026 {
1027 std::unique_ptr<DynamicRule> ruleObject =
1028 std::make_unique<DynamicRule>(rule);
1029 DynamicRule* ptr = ruleObject.get();
Tanousf00032d2018-11-05 01:18:10 -03001030 allRules.emplace_back(std::move(ruleObject));
Ed Tanous7045c8d2017-04-03 10:04:37 -07001031
Ed Tanous1abe55e2018-09-05 08:30:59 -07001032 return *ptr;
Ed Tanous7045c8d2017-04-03 10:04:37 -07001033 }
1034
Ed Tanous1abe55e2018-09-05 08:30:59 -07001035 template <uint64_t N>
1036 typename black_magic::Arguments<N>::type::template rebind<TaggedRule>&
1037 newRuleTagged(const std::string& rule)
1038 {
1039 using RuleT = typename black_magic::Arguments<N>::type::template rebind<
1040 TaggedRule>;
1041 std::unique_ptr<RuleT> ruleObject = std::make_unique<RuleT>(rule);
1042 RuleT* ptr = ruleObject.get();
Tanousf00032d2018-11-05 01:18:10 -03001043 allRules.emplace_back(std::move(ruleObject));
Ed Tanous1abe55e2018-09-05 08:30:59 -07001044
1045 return *ptr;
Ed Tanous7045c8d2017-04-03 10:04:37 -07001046 }
1047
Tanousf00032d2018-11-05 01:18:10 -03001048 void internalAddRuleObject(const std::string& rule, BaseRule* ruleObject)
Ed Tanous1abe55e2018-09-05 08:30:59 -07001049 {
Tanousf00032d2018-11-05 01:18:10 -03001050 if (ruleObject == nullptr)
Ed Tanous1abe55e2018-09-05 08:30:59 -07001051 {
Tanousf00032d2018-11-05 01:18:10 -03001052 return;
1053 }
1054 for (uint32_t method = 0, method_bit = 1; method < maxHttpVerbCount;
1055 method++, method_bit <<= 1)
1056 {
1057 if (ruleObject->methodsBitfield & method_bit)
1058 {
1059 perMethods[method].rules.emplace_back(ruleObject);
1060 perMethods[method].trie.add(
Ed Tanous271584a2019-07-09 16:24:22 -07001061 rule, static_cast<unsigned>(
1062 perMethods[method].rules.size() - 1U));
Tanousf00032d2018-11-05 01:18:10 -03001063 // directory case:
1064 // request to `/about' url matches `/about/' rule
1065 if (rule.size() > 2 && rule.back() == '/')
1066 {
1067 perMethods[method].trie.add(
1068 rule.substr(0, rule.size() - 1),
Ed Tanous271584a2019-07-09 16:24:22 -07001069 static_cast<unsigned>(perMethods[method].rules.size() -
1070 1));
Tanousf00032d2018-11-05 01:18:10 -03001071 }
1072 }
Ed Tanous1abe55e2018-09-05 08:30:59 -07001073 }
Ed Tanous7045c8d2017-04-03 10:04:37 -07001074 }
1075
Ed Tanous1abe55e2018-09-05 08:30:59 -07001076 void validate()
1077 {
Tanousf00032d2018-11-05 01:18:10 -03001078 for (std::unique_ptr<BaseRule>& rule : allRules)
Ed Tanous1abe55e2018-09-05 08:30:59 -07001079 {
1080 if (rule)
1081 {
Tanousf00032d2018-11-05 01:18:10 -03001082 std::unique_ptr<BaseRule> upgraded = rule->upgrade();
Ed Tanous1abe55e2018-09-05 08:30:59 -07001083 if (upgraded)
1084 rule = std::move(upgraded);
1085 rule->validate();
Tanousf00032d2018-11-05 01:18:10 -03001086 internalAddRuleObject(rule->rule, rule.get());
Ed Tanous1abe55e2018-09-05 08:30:59 -07001087 }
1088 }
Tanousf00032d2018-11-05 01:18:10 -03001089 for (PerMethod& perMethod : perMethods)
1090 {
1091 perMethod.trie.validate();
1092 }
Ed Tanous7045c8d2017-04-03 10:04:37 -07001093 }
1094
Ed Tanous1abe55e2018-09-05 08:30:59 -07001095 template <typename Adaptor>
1096 void handleUpgrade(const Request& req, Response& res, Adaptor&& adaptor)
1097 {
Ed Tanous271584a2019-07-09 16:24:22 -07001098 if (static_cast<size_t>(req.method()) >= perMethods.size())
Tanousf00032d2018-11-05 01:18:10 -03001099 return;
1100
Ed Tanous271584a2019-07-09 16:24:22 -07001101 PerMethod& perMethod = perMethods[static_cast<size_t>(req.method())];
Tanousf00032d2018-11-05 01:18:10 -03001102 Trie& trie = perMethod.trie;
1103 std::vector<BaseRule*>& rules = perMethod.rules;
1104
1105 const std::pair<unsigned, RoutingParams>& found = trie.find(req.url);
Ed Tanous1abe55e2018-09-05 08:30:59 -07001106 unsigned ruleIndex = found.first;
1107 if (!ruleIndex)
1108 {
1109 BMCWEB_LOG_DEBUG << "Cannot match rules " << req.url;
Ed Tanousde5c9f32019-03-26 09:17:55 -07001110 res.result(boost::beast::http::status::not_found);
Ed Tanous1abe55e2018-09-05 08:30:59 -07001111 res.end();
1112 return;
1113 }
1114
1115 if (ruleIndex >= rules.size())
1116 throw std::runtime_error("Trie internal structure corrupted!");
1117
1118 if (ruleIndex == ruleSpecialRedirectSlash)
1119 {
1120 BMCWEB_LOG_INFO << "Redirecting to a url with trailing slash: "
1121 << req.url;
Ed Tanousde5c9f32019-03-26 09:17:55 -07001122 res.result(boost::beast::http::status::moved_permanently);
Ed Tanous1abe55e2018-09-05 08:30:59 -07001123
1124 // TODO absolute url building
1125 if (req.getHeaderValue("Host").empty())
1126 {
1127 res.addHeader("Location", std::string(req.url) + "/");
1128 }
1129 else
1130 {
1131 res.addHeader(
1132 "Location",
1133 req.isSecure
1134 ? "https://"
1135 : "http://" + std::string(req.getHeaderValue("Host")) +
1136 std::string(req.url) + "/");
1137 }
1138 res.end();
1139 return;
1140 }
1141
Ed Tanous271584a2019-07-09 16:24:22 -07001142 if ((rules[ruleIndex]->getMethods() &
1143 (1U << static_cast<size_t>(req.method()))) == 0)
Ed Tanous1abe55e2018-09-05 08:30:59 -07001144 {
1145 BMCWEB_LOG_DEBUG << "Rule found but method mismatch: " << req.url
1146 << " with " << req.methodString() << "("
Ed Tanous271584a2019-07-09 16:24:22 -07001147 << static_cast<uint32_t>(req.method()) << ") / "
Ed Tanous1abe55e2018-09-05 08:30:59 -07001148 << rules[ruleIndex]->getMethods();
Ed Tanousde5c9f32019-03-26 09:17:55 -07001149 res.result(boost::beast::http::status::not_found);
Ed Tanous1abe55e2018-09-05 08:30:59 -07001150 res.end();
1151 return;
1152 }
1153
1154 BMCWEB_LOG_DEBUG << "Matched rule (upgrade) '" << rules[ruleIndex]->rule
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();
1157
1158 // any uncaught exceptions become 500s
1159 try
1160 {
1161 rules[ruleIndex]->handleUpgrade(req, res, std::move(adaptor));
1162 }
1163 catch (std::exception& e)
1164 {
1165 BMCWEB_LOG_ERROR << "An uncaught exception occurred: " << e.what();
Ed Tanousde5c9f32019-03-26 09:17:55 -07001166 res.result(boost::beast::http::status::internal_server_error);
Ed Tanous1abe55e2018-09-05 08:30:59 -07001167 res.end();
1168 return;
1169 }
1170 catch (...)
1171 {
1172 BMCWEB_LOG_ERROR
1173 << "An uncaught exception occurred. The type was unknown "
1174 "so no information was available.";
Ed Tanousde5c9f32019-03-26 09:17:55 -07001175 res.result(boost::beast::http::status::internal_server_error);
Ed Tanous1abe55e2018-09-05 08:30:59 -07001176 res.end();
1177 return;
1178 }
Ed Tanous7045c8d2017-04-03 10:04:37 -07001179 }
1180
RAJESWARAN THILLAIGOVINDAN61dbeef2019-12-13 04:26:54 -06001181 void handle(Request& req, Response& res)
Ed Tanous1abe55e2018-09-05 08:30:59 -07001182 {
Ed Tanous271584a2019-07-09 16:24:22 -07001183 if (static_cast<size_t>(req.method()) >= perMethods.size())
Tanousf00032d2018-11-05 01:18:10 -03001184 return;
Ed Tanous271584a2019-07-09 16:24:22 -07001185 PerMethod& perMethod = perMethods[static_cast<size_t>(req.method())];
Tanousf00032d2018-11-05 01:18:10 -03001186 Trie& trie = perMethod.trie;
1187 std::vector<BaseRule*>& rules = perMethod.rules;
1188
1189 const std::pair<unsigned, RoutingParams>& found = trie.find(req.url);
Ed Tanous7045c8d2017-04-03 10:04:37 -07001190
Ed Tanous1abe55e2018-09-05 08:30:59 -07001191 unsigned ruleIndex = found.first;
1192
1193 if (!ruleIndex)
1194 {
Ed Tanous2634dcd2019-03-26 09:28:06 -07001195 // Check to see if this url exists at any verb
1196 for (const PerMethod& p : perMethods)
1197 {
1198 const std::pair<unsigned, RoutingParams>& found =
1199 p.trie.find(req.url);
1200 if (found.first > 0)
1201 {
1202 res.result(boost::beast::http::status::method_not_allowed);
1203 res.end();
1204 return;
1205 }
1206 }
Ed Tanous1abe55e2018-09-05 08:30:59 -07001207 BMCWEB_LOG_DEBUG << "Cannot match rules " << req.url;
1208 res.result(boost::beast::http::status::not_found);
1209 res.end();
1210 return;
1211 }
1212
1213 if (ruleIndex >= rules.size())
1214 throw std::runtime_error("Trie internal structure corrupted!");
1215
1216 if (ruleIndex == ruleSpecialRedirectSlash)
1217 {
1218 BMCWEB_LOG_INFO << "Redirecting to a url with trailing slash: "
1219 << req.url;
Ed Tanousde5c9f32019-03-26 09:17:55 -07001220 res.result(boost::beast::http::status::moved_permanently);
Ed Tanous1abe55e2018-09-05 08:30:59 -07001221
1222 // TODO absolute url building
1223 if (req.getHeaderValue("Host").empty())
1224 {
1225 res.addHeader("Location", std::string(req.url) + "/");
1226 }
1227 else
1228 {
1229 res.addHeader("Location",
1230 (req.isSecure ? "https://" : "http://") +
1231 std::string(req.getHeaderValue("Host")) +
1232 std::string(req.url) + "/");
1233 }
1234 res.end();
1235 return;
1236 }
1237
Ed Tanous271584a2019-07-09 16:24:22 -07001238 if ((rules[ruleIndex]->getMethods() &
1239 (1U << static_cast<uint32_t>(req.method()))) == 0)
Ed Tanous1abe55e2018-09-05 08:30:59 -07001240 {
1241 BMCWEB_LOG_DEBUG << "Rule found but method mismatch: " << req.url
1242 << " with " << req.methodString() << "("
Ed Tanous271584a2019-07-09 16:24:22 -07001243 << static_cast<uint32_t>(req.method()) << ") / "
Ed Tanous1abe55e2018-09-05 08:30:59 -07001244 << rules[ruleIndex]->getMethods();
Ed Tanousde5c9f32019-03-26 09:17:55 -07001245 res.result(boost::beast::http::status::method_not_allowed);
Ed Tanous1abe55e2018-09-05 08:30:59 -07001246 res.end();
1247 return;
1248 }
1249
1250 BMCWEB_LOG_DEBUG << "Matched rule '" << rules[ruleIndex]->rule << "' "
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();
1253
RAJESWARAN THILLAIGOVINDAN61dbeef2019-12-13 04:26:54 -06001254 if (req.session == nullptr)
James Feist7166bf02019-12-10 16:52:14 +00001255 {
1256 rules[ruleIndex]->handle(req, res, found.second);
James Feist7166bf02019-12-10 16:52:14 +00001257 return;
1258 }
RAJESWARAN THILLAIGOVINDAN61dbeef2019-12-13 04:26:54 -06001259
1260 crow::connections::systemBus->async_method_call(
1261 [&req, &res, &rules, ruleIndex, found](
1262 const boost::system::error_code ec,
1263 std::map<std::string, std::variant<bool, std::string,
1264 std::vector<std::string>>>
1265 userInfo) {
1266 if (ec)
1267 {
1268 BMCWEB_LOG_ERROR << "GetUserInfo failed...";
1269 res.result(
1270 boost::beast::http::status::internal_server_error);
1271 res.end();
1272 return;
1273 }
1274
1275 const std::string* userRolePtr = nullptr;
1276 auto userInfoIter = userInfo.find("UserPrivilege");
1277 if (userInfoIter != userInfo.end())
1278 {
1279 userRolePtr =
1280 std::get_if<std::string>(&userInfoIter->second);
1281 }
1282
1283 std::string userRole{};
1284 if (userRolePtr != nullptr)
1285 {
1286 userRole = *userRolePtr;
1287 BMCWEB_LOG_DEBUG << "userName = " << req.session->username
1288 << " userRole = " << *userRolePtr;
1289 }
1290
Joseph Reynolds3bf4e632020-02-06 14:44:32 -06001291 bool* remoteUserPtr = nullptr;
1292 auto remoteUserIter = userInfo.find("RemoteUser");
1293 if (remoteUserIter != userInfo.end())
1294 {
1295 remoteUserPtr = std::get_if<bool>(&remoteUserIter->second);
1296 }
1297 if (remoteUserPtr == nullptr)
1298 {
1299 BMCWEB_LOG_ERROR
1300 << "RemoteUser property missing or wrong type";
1301 res.result(
1302 boost::beast::http::status::internal_server_error);
1303 res.end();
1304 return;
1305 }
1306 bool remoteUser = *remoteUserPtr;
1307
1308 bool passwordExpired = false; // default for remote user
1309 if (!remoteUser)
1310 {
1311 bool* passwordExpiredPtr = nullptr;
1312 auto passwordExpiredIter =
1313 userInfo.find("UserPasswordExpired");
1314 if (passwordExpiredIter != userInfo.end())
1315 {
1316 passwordExpiredPtr =
1317 std::get_if<bool>(&passwordExpiredIter->second);
1318 }
1319 if (passwordExpiredPtr != nullptr)
1320 {
1321 passwordExpired = *passwordExpiredPtr;
1322 }
1323 else
1324 {
1325 BMCWEB_LOG_ERROR
1326 << "UserPasswordExpired property is expected for"
1327 " local user but is missing or wrong type";
1328 res.result(
1329 boost::beast::http::status::internal_server_error);
1330 res.end();
1331 return;
1332 }
1333 }
1334
RAJESWARAN THILLAIGOVINDAN61dbeef2019-12-13 04:26:54 -06001335 // Get the user privileges from the role
1336 redfish::Privileges userPrivileges =
1337 redfish::getUserPrivileges(userRole);
1338
Joseph Reynolds3bf4e632020-02-06 14:44:32 -06001339 // Set isConfigureSelfOnly based on D-Bus results. This
1340 // ignores the results from both pamAuthenticateUser and the
1341 // value from any previous use of this session.
1342 req.session->isConfigureSelfOnly = passwordExpired;
1343
1344 // Modify privileges if isConfigureSelfOnly.
1345 if (req.session->isConfigureSelfOnly)
1346 {
1347 // Remove all privileges except ConfigureSelf
1348 userPrivileges = userPrivileges.intersection(
1349 redfish::Privileges{"ConfigureSelf"});
1350 BMCWEB_LOG_DEBUG << "Operation limited to ConfigureSelf";
1351 }
1352
RAJESWARAN THILLAIGOVINDAN61dbeef2019-12-13 04:26:54 -06001353 if (!rules[ruleIndex]->checkPrivileges(userPrivileges))
1354 {
1355 res.result(boost::beast::http::status::forbidden);
Joseph Reynolds3bf4e632020-02-06 14:44:32 -06001356 if (req.session->isConfigureSelfOnly)
1357 {
1358 redfish::messages::passwordChangeRequired(
1359 res, "/redfish/v1/AccountService/Accounts/" +
1360 req.session->username);
1361 }
RAJESWARAN THILLAIGOVINDAN61dbeef2019-12-13 04:26:54 -06001362 res.end();
1363 return;
1364 }
1365
1366 req.userRole = userRole;
1367
1368 rules[ruleIndex]->handle(req, res, found.second);
1369 },
1370 "xyz.openbmc_project.User.Manager", "/xyz/openbmc_project/user",
1371 "xyz.openbmc_project.User.Manager", "GetUserInfo",
1372 req.session->username);
Ed Tanous7045c8d2017-04-03 10:04:37 -07001373 }
Ed Tanous7045c8d2017-04-03 10:04:37 -07001374
Ed Tanous1abe55e2018-09-05 08:30:59 -07001375 void debugPrint()
1376 {
Ed Tanous271584a2019-07-09 16:24:22 -07001377 for (size_t i = 0; i < perMethods.size(); i++)
Tanousf00032d2018-11-05 01:18:10 -03001378 {
Ed Tanous271584a2019-07-09 16:24:22 -07001379 BMCWEB_LOG_DEBUG
1380 << methodName(static_cast<boost::beast::http::verb>(i));
Tanousf00032d2018-11-05 01:18:10 -03001381 perMethods[i].trie.debugPrint();
1382 }
Ed Tanous3dac7492017-08-02 13:46:20 -07001383 }
Ed Tanousb4a7bfa2017-04-04 17:23:00 -07001384
Ed Tanous1abe55e2018-09-05 08:30:59 -07001385 std::vector<const std::string*> getRoutes(const std::string& parent)
1386 {
Ed Tanous1abe55e2018-09-05 08:30:59 -07001387 std::vector<const std::string*> ret;
Tanousf00032d2018-11-05 01:18:10 -03001388
1389 for (const PerMethod& pm : perMethods)
Ed Tanous1abe55e2018-09-05 08:30:59 -07001390 {
Tanousf00032d2018-11-05 01:18:10 -03001391 std::vector<unsigned> x;
1392 pm.trie.findRouteIndexes(parent, x);
1393 for (unsigned index : x)
1394 {
1395 ret.push_back(&pm.rules[index]->rule);
1396 }
Ed Tanous1abe55e2018-09-05 08:30:59 -07001397 }
1398 return ret;
1399 }
1400
1401 private:
Tanousf00032d2018-11-05 01:18:10 -03001402 struct PerMethod
1403 {
1404 std::vector<BaseRule*> rules;
1405 Trie trie;
1406 // rule index 0, 1 has special meaning; preallocate it to avoid
1407 // duplication.
1408 PerMethod() : rules(2)
1409 {
1410 }
1411 };
1412 std::array<PerMethod, maxHttpVerbCount> perMethods;
1413 std::vector<std::unique_ptr<BaseRule>> allRules;
Ed Tanous7045c8d2017-04-03 10:04:37 -07001414};
Ed Tanous1abe55e2018-09-05 08:30:59 -07001415} // namespace crow