1 #pragma once 2 3 #include "async_resp.hpp" 4 #include "common.hpp" 5 #include "dbus_utility.hpp" 6 #include "error_messages.hpp" 7 #include "http_request.hpp" 8 #include "http_response.hpp" 9 #include "logging.hpp" 10 #include "privileges.hpp" 11 #include "sessions.hpp" 12 #include "utility.hpp" 13 #include "utils/dbus_utils.hpp" 14 #include "verb.hpp" 15 #include "websocket.hpp" 16 17 #include <boost/beast/ssl/ssl_stream.hpp> 18 #include <boost/container/flat_map.hpp> 19 #include <sdbusplus/unpack_properties.hpp> 20 21 #include <cerrno> 22 #include <cstdint> 23 #include <cstdlib> 24 #include <limits> 25 #include <memory> 26 #include <optional> 27 #include <tuple> 28 #include <utility> 29 #include <vector> 30 31 namespace crow 32 { 33 34 class BaseRule 35 { 36 public: 37 explicit BaseRule(const std::string& thisRule) : rule(thisRule) 38 {} 39 40 virtual ~BaseRule() = default; 41 42 BaseRule(const BaseRule&) = delete; 43 BaseRule(BaseRule&&) = delete; 44 BaseRule& operator=(const BaseRule&) = delete; 45 BaseRule& operator=(const BaseRule&&) = delete; 46 47 virtual void validate() = 0; 48 std::unique_ptr<BaseRule> upgrade() 49 { 50 if (ruleToUpgrade) 51 { 52 return std::move(ruleToUpgrade); 53 } 54 return {}; 55 } 56 57 virtual void handle(const Request& /*req*/, 58 const std::shared_ptr<bmcweb::AsyncResp>&, 59 const RoutingParams&) = 0; 60 #ifndef BMCWEB_ENABLE_SSL 61 virtual void 62 handleUpgrade(const Request& /*req*/, 63 const std::shared_ptr<bmcweb::AsyncResp>& asyncResp, 64 boost::asio::ip::tcp::socket&& /*adaptor*/) 65 { 66 asyncResp->res.result(boost::beast::http::status::not_found); 67 } 68 #else 69 virtual void handleUpgrade( 70 const Request& /*req*/, 71 const std::shared_ptr<bmcweb::AsyncResp>& asyncResp, 72 boost::beast::ssl_stream<boost::asio::ip::tcp::socket>&& /*adaptor*/) 73 { 74 asyncResp->res.result(boost::beast::http::status::not_found); 75 } 76 #endif 77 78 size_t getMethods() const 79 { 80 return methodsBitfield; 81 } 82 83 bool checkPrivileges(const redfish::Privileges& userPrivileges) 84 { 85 // If there are no privileges assigned, assume no privileges 86 // required 87 if (privilegesSet.empty()) 88 { 89 return true; 90 } 91 92 for (const redfish::Privileges& requiredPrivileges : privilegesSet) 93 { 94 if (userPrivileges.isSupersetOf(requiredPrivileges)) 95 { 96 return true; 97 } 98 } 99 return false; 100 } 101 102 size_t methodsBitfield{1 << static_cast<size_t>(HttpVerb::Get)}; 103 static_assert(std::numeric_limits<decltype(methodsBitfield)>::digits > 104 methodNotAllowedIndex, 105 "Not enough bits to store bitfield"); 106 107 std::vector<redfish::Privileges> privilegesSet; 108 109 std::string rule; 110 std::string nameStr; 111 112 std::unique_ptr<BaseRule> ruleToUpgrade; 113 114 friend class Router; 115 template <typename T> 116 friend struct RuleParameterTraits; 117 }; 118 119 namespace detail 120 { 121 namespace routing_handler_call_helper 122 { 123 template <typename T, int Pos> 124 struct CallPair 125 { 126 using type = T; 127 static const int pos = Pos; 128 }; 129 130 template <typename H1> 131 struct CallParams 132 { 133 H1& handler; 134 const RoutingParams& params; 135 const Request& req; 136 const std::shared_ptr<bmcweb::AsyncResp>& asyncResp; 137 }; 138 139 template <typename F, int NInt, int NUint, int NDouble, int NString, 140 typename S1, typename S2> 141 struct Call 142 {}; 143 144 template <typename F, int NInt, int NUint, int NDouble, int NString, 145 typename... Args1, typename... Args2> 146 struct Call<F, NInt, NUint, NDouble, NString, black_magic::S<int64_t, Args1...>, 147 black_magic::S<Args2...>> 148 { 149 void operator()(F cparams) 150 { 151 using pushed = typename black_magic::S<Args2...>::template push_back< 152 CallPair<int64_t, NInt>>; 153 Call<F, NInt + 1, NUint, NDouble, NString, black_magic::S<Args1...>, 154 pushed>()(cparams); 155 } 156 }; 157 158 template <typename F, int NInt, int NUint, int NDouble, int NString, 159 typename... Args1, typename... Args2> 160 struct Call<F, NInt, NUint, NDouble, NString, 161 black_magic::S<uint64_t, Args1...>, black_magic::S<Args2...>> 162 { 163 void operator()(F cparams) 164 { 165 using pushed = typename black_magic::S<Args2...>::template push_back< 166 CallPair<uint64_t, NUint>>; 167 Call<F, NInt, NUint + 1, NDouble, NString, black_magic::S<Args1...>, 168 pushed>()(cparams); 169 } 170 }; 171 172 template <typename F, int NInt, int NUint, int NDouble, int NString, 173 typename... Args1, typename... Args2> 174 struct Call<F, NInt, NUint, NDouble, NString, black_magic::S<double, Args1...>, 175 black_magic::S<Args2...>> 176 { 177 void operator()(F cparams) 178 { 179 using pushed = typename black_magic::S<Args2...>::template push_back< 180 CallPair<double, NDouble>>; 181 Call<F, NInt, NUint, NDouble + 1, NString, black_magic::S<Args1...>, 182 pushed>()(cparams); 183 } 184 }; 185 186 template <typename F, int NInt, int NUint, int NDouble, int NString, 187 typename... Args1, typename... Args2> 188 struct Call<F, NInt, NUint, NDouble, NString, 189 black_magic::S<std::string, Args1...>, black_magic::S<Args2...>> 190 { 191 void operator()(F cparams) 192 { 193 using pushed = typename black_magic::S<Args2...>::template push_back< 194 CallPair<std::string, NString>>; 195 Call<F, NInt, NUint, NDouble, NString + 1, black_magic::S<Args1...>, 196 pushed>()(cparams); 197 } 198 }; 199 200 template <typename F, int NInt, int NUint, int NDouble, int NString, 201 typename... Args1> 202 struct Call<F, NInt, NUint, NDouble, NString, black_magic::S<>, 203 black_magic::S<Args1...>> 204 { 205 void operator()(F cparams) 206 { 207 cparams.handler( 208 cparams.req, cparams.asyncResp, 209 cparams.params.template get<typename Args1::type>(Args1::pos)...); 210 } 211 }; 212 213 template <typename Func, typename... ArgsWrapped> 214 struct Wrapped 215 { 216 template <typename... Args> 217 void set( 218 Func f, 219 typename std::enable_if< 220 !std::is_same< 221 typename std::tuple_element<0, std::tuple<Args..., void>>::type, 222 const Request&>::value, 223 int>::type /*enable*/ 224 = 0) 225 { 226 handler = [f = std::forward<Func>(f)]( 227 const Request&, 228 const std::shared_ptr<bmcweb::AsyncResp>& asyncResp, 229 Args... args) { asyncResp->res.result(f(args...)); }; 230 } 231 232 template <typename Req, typename... Args> 233 struct ReqHandlerWrapper 234 { 235 explicit ReqHandlerWrapper(Func fIn) : f(std::move(fIn)) 236 {} 237 238 void operator()(const Request& req, 239 const std::shared_ptr<bmcweb::AsyncResp>& asyncResp, 240 Args... args) 241 { 242 asyncResp->res.result(f(req, args...)); 243 } 244 245 Func f; 246 }; 247 248 template <typename... Args> 249 void set( 250 Func f, 251 typename std::enable_if< 252 std::is_same< 253 typename std::tuple_element<0, std::tuple<Args..., void>>::type, 254 const Request&>::value && 255 !std::is_same<typename std::tuple_element< 256 1, std::tuple<Args..., void, void>>::type, 257 const std::shared_ptr<bmcweb::AsyncResp>&>::value, 258 int>::type /*enable*/ 259 = 0) 260 { 261 handler = ReqHandlerWrapper<Args...>(std::move(f)); 262 /*handler = ( 263 [f = std::move(f)] 264 (const Request& req, Response& res, Args... args){ 265 res.result(f(req, args...)); 266 res.end(); 267 });*/ 268 } 269 270 template <typename... Args> 271 void set( 272 Func f, 273 typename std::enable_if< 274 std::is_same< 275 typename std::tuple_element<0, std::tuple<Args..., void>>::type, 276 const Request&>::value && 277 std::is_same<typename std::tuple_element< 278 1, std::tuple<Args..., void, void>>::type, 279 const std::shared_ptr<bmcweb::AsyncResp>&>::value, 280 int>::type /*enable*/ 281 = 0) 282 { 283 handler = std::move(f); 284 } 285 286 template <typename... Args> 287 struct HandlerTypeHelper 288 { 289 using type = std::function<void( 290 const crow::Request& /*req*/, 291 const std::shared_ptr<bmcweb::AsyncResp>&, Args...)>; 292 using args_type = 293 black_magic::S<typename black_magic::PromoteT<Args>...>; 294 }; 295 296 template <typename... Args> 297 struct HandlerTypeHelper<const Request&, Args...> 298 { 299 using type = std::function<void( 300 const crow::Request& /*req*/, 301 const std::shared_ptr<bmcweb::AsyncResp>&, Args...)>; 302 using args_type = 303 black_magic::S<typename black_magic::PromoteT<Args>...>; 304 }; 305 306 template <typename... Args> 307 struct HandlerTypeHelper<const Request&, 308 const std::shared_ptr<bmcweb::AsyncResp>&, Args...> 309 { 310 using type = std::function<void( 311 const crow::Request& /*req*/, 312 const std::shared_ptr<bmcweb::AsyncResp>&, Args...)>; 313 using args_type = 314 black_magic::S<typename black_magic::PromoteT<Args>...>; 315 }; 316 317 typename HandlerTypeHelper<ArgsWrapped...>::type handler; 318 319 void operator()(const Request& req, 320 const std::shared_ptr<bmcweb::AsyncResp>& asyncResp, 321 const RoutingParams& params) 322 { 323 detail::routing_handler_call_helper::Call< 324 detail::routing_handler_call_helper::CallParams<decltype(handler)>, 325 0, 0, 0, 0, typename HandlerTypeHelper<ArgsWrapped...>::args_type, 326 black_magic::S<>>()( 327 detail::routing_handler_call_helper::CallParams<decltype(handler)>{ 328 handler, params, req, asyncResp}); 329 } 330 }; 331 } // namespace routing_handler_call_helper 332 } // namespace detail 333 334 class WebSocketRule : public BaseRule 335 { 336 using self_t = WebSocketRule; 337 338 public: 339 explicit WebSocketRule(const std::string& ruleIn) : BaseRule(ruleIn) 340 {} 341 342 void validate() override 343 {} 344 345 void handle(const Request& /*req*/, 346 const std::shared_ptr<bmcweb::AsyncResp>& asyncResp, 347 const RoutingParams& /*params*/) override 348 { 349 asyncResp->res.result(boost::beast::http::status::not_found); 350 } 351 352 #ifndef BMCWEB_ENABLE_SSL 353 void handleUpgrade(const Request& req, 354 const std::shared_ptr<bmcweb::AsyncResp>& /*asyncResp*/, 355 boost::asio::ip::tcp::socket&& adaptor) override 356 { 357 BMCWEB_LOG_DEBUG << "Websocket handles upgrade"; 358 std::shared_ptr< 359 crow::websocket::ConnectionImpl<boost::asio::ip::tcp::socket>> 360 myConnection = std::make_shared< 361 crow::websocket::ConnectionImpl<boost::asio::ip::tcp::socket>>( 362 req, std::move(adaptor), openHandler, messageHandler, 363 closeHandler, errorHandler); 364 myConnection->start(); 365 } 366 #else 367 void handleUpgrade(const Request& req, 368 const std::shared_ptr<bmcweb::AsyncResp>& /*asyncResp*/, 369 boost::beast::ssl_stream<boost::asio::ip::tcp::socket>&& 370 adaptor) override 371 { 372 BMCWEB_LOG_DEBUG << "Websocket handles upgrade"; 373 std::shared_ptr<crow::websocket::ConnectionImpl< 374 boost::beast::ssl_stream<boost::asio::ip::tcp::socket>>> 375 myConnection = std::make_shared<crow::websocket::ConnectionImpl< 376 boost::beast::ssl_stream<boost::asio::ip::tcp::socket>>>( 377 req, std::move(adaptor), openHandler, messageHandler, 378 closeHandler, errorHandler); 379 myConnection->start(); 380 } 381 #endif 382 383 template <typename Func> 384 self_t& onopen(Func f) 385 { 386 openHandler = f; 387 return *this; 388 } 389 390 template <typename Func> 391 self_t& onmessage(Func f) 392 { 393 messageHandler = f; 394 return *this; 395 } 396 397 template <typename Func> 398 self_t& onclose(Func f) 399 { 400 closeHandler = f; 401 return *this; 402 } 403 404 template <typename Func> 405 self_t& onerror(Func f) 406 { 407 errorHandler = f; 408 return *this; 409 } 410 411 protected: 412 std::function<void(crow::websocket::Connection&)> openHandler; 413 std::function<void(crow::websocket::Connection&, const std::string&, bool)> 414 messageHandler; 415 std::function<void(crow::websocket::Connection&, const std::string&)> 416 closeHandler; 417 std::function<void(crow::websocket::Connection&)> errorHandler; 418 }; 419 420 template <typename T> 421 struct RuleParameterTraits 422 { 423 using self_t = T; 424 WebSocketRule& websocket() 425 { 426 self_t* self = static_cast<self_t*>(this); 427 WebSocketRule* p = new WebSocketRule(self->rule); 428 self->ruleToUpgrade.reset(p); 429 return *p; 430 } 431 432 self_t& name(std::string_view name) noexcept 433 { 434 self_t* self = static_cast<self_t*>(this); 435 self->nameStr = name; 436 return *self; 437 } 438 439 self_t& methods(boost::beast::http::verb method) 440 { 441 self_t* self = static_cast<self_t*>(this); 442 std::optional<HttpVerb> verb = httpVerbFromBoost(method); 443 if (verb) 444 { 445 self->methodsBitfield = 1U << static_cast<size_t>(*verb); 446 } 447 return *self; 448 } 449 450 template <typename... MethodArgs> 451 self_t& methods(boost::beast::http::verb method, MethodArgs... argsMethod) 452 { 453 self_t* self = static_cast<self_t*>(this); 454 methods(argsMethod...); 455 std::optional<HttpVerb> verb = httpVerbFromBoost(method); 456 if (verb) 457 { 458 self->methodsBitfield |= 1U << static_cast<size_t>(*verb); 459 } 460 return *self; 461 } 462 463 self_t& notFound() 464 { 465 self_t* self = static_cast<self_t*>(this); 466 self->methodsBitfield = 1U << notFoundIndex; 467 return *self; 468 } 469 470 self_t& methodNotAllowed() 471 { 472 self_t* self = static_cast<self_t*>(this); 473 self->methodsBitfield = 1U << methodNotAllowedIndex; 474 return *self; 475 } 476 477 self_t& privileges( 478 const std::initializer_list<std::initializer_list<const char*>>& p) 479 { 480 self_t* self = static_cast<self_t*>(this); 481 for (const std::initializer_list<const char*>& privilege : p) 482 { 483 self->privilegesSet.emplace_back(privilege); 484 } 485 return *self; 486 } 487 488 template <size_t N, typename... MethodArgs> 489 self_t& privileges(const std::array<redfish::Privileges, N>& p) 490 { 491 self_t* self = static_cast<self_t*>(this); 492 for (const redfish::Privileges& privilege : p) 493 { 494 self->privilegesSet.emplace_back(privilege); 495 } 496 return *self; 497 } 498 }; 499 500 class DynamicRule : public BaseRule, public RuleParameterTraits<DynamicRule> 501 { 502 public: 503 explicit DynamicRule(const std::string& ruleIn) : BaseRule(ruleIn) 504 {} 505 506 void validate() override 507 { 508 if (!erasedHandler) 509 { 510 throw std::runtime_error(nameStr + (!nameStr.empty() ? ": " : "") + 511 "no handler for url " + rule); 512 } 513 } 514 515 void handle(const Request& req, 516 const std::shared_ptr<bmcweb::AsyncResp>& asyncResp, 517 const RoutingParams& params) override 518 { 519 erasedHandler(req, asyncResp, params); 520 } 521 522 template <typename Func> 523 void operator()(Func f) 524 { 525 using boost::callable_traits::args_t; 526 constexpr size_t arity = std::tuple_size<args_t<Func>>::value; 527 constexpr auto is = std::make_integer_sequence<unsigned, arity>{}; 528 erasedHandler = wrap(std::move(f), is); 529 } 530 531 // enable_if Arg1 == request && Arg2 == Response 532 // enable_if Arg1 == request && Arg2 != response 533 // enable_if Arg1 != request 534 535 template <typename Func, unsigned... Indices> 536 std::function<void(const Request&, 537 const std::shared_ptr<bmcweb::AsyncResp>&, 538 const RoutingParams&)> 539 wrap(Func f, std::integer_sequence<unsigned, Indices...> /*is*/) 540 { 541 using function_t = crow::utility::FunctionTraits<Func>; 542 543 if (!black_magic::isParameterTagCompatible( 544 black_magic::getParameterTag(rule.c_str()), 545 black_magic::computeParameterTagFromArgsList< 546 typename function_t::template arg<Indices>...>::value)) 547 { 548 throw std::runtime_error("routeDynamic: Handler type is mismatched " 549 "with URL parameters: " + 550 rule); 551 } 552 auto ret = detail::routing_handler_call_helper::Wrapped< 553 Func, typename function_t::template arg<Indices>...>(); 554 ret.template set<typename function_t::template arg<Indices>...>( 555 std::move(f)); 556 return ret; 557 } 558 559 template <typename Func> 560 void operator()(std::string name, Func&& f) 561 { 562 nameStr = std::move(name); 563 (*this).template operator()<Func>(std::forward(f)); 564 } 565 566 private: 567 std::function<void(const Request&, 568 const std::shared_ptr<bmcweb::AsyncResp>&, 569 const RoutingParams&)> 570 erasedHandler; 571 }; 572 573 template <typename... Args> 574 class TaggedRule : 575 public BaseRule, 576 public RuleParameterTraits<TaggedRule<Args...>> 577 { 578 public: 579 using self_t = TaggedRule<Args...>; 580 581 explicit TaggedRule(const std::string& ruleIn) : BaseRule(ruleIn) 582 {} 583 584 void validate() override 585 { 586 if (!handler) 587 { 588 throw std::runtime_error(nameStr + (!nameStr.empty() ? ": " : "") + 589 "no handler for url " + rule); 590 } 591 } 592 593 template <typename Func> 594 typename std::enable_if< 595 black_magic::CallHelper<Func, black_magic::S<Args...>>::value, 596 void>::type 597 operator()(Func&& f) 598 { 599 static_assert( 600 black_magic::CallHelper<Func, black_magic::S<Args...>>::value || 601 black_magic::CallHelper< 602 Func, black_magic::S<crow::Request, Args...>>::value, 603 "Handler type is mismatched with URL parameters"); 604 static_assert( 605 !std::is_same<void, decltype(f(std::declval<Args>()...))>::value, 606 "Handler function cannot have void return type; valid return " 607 "types: " 608 "string, int, crow::response, nlohmann::json"); 609 610 handler = [f = std::forward<Func>(f)]( 611 const Request&, 612 const std::shared_ptr<bmcweb::AsyncResp>& asyncResp, 613 Args... args) { asyncResp->res.result(f(args...)); }; 614 } 615 616 template <typename Func> 617 typename std::enable_if< 618 !black_magic::CallHelper<Func, black_magic::S<Args...>>::value && 619 black_magic::CallHelper< 620 Func, black_magic::S<crow::Request, Args...>>::value, 621 void>::type 622 operator()(Func&& f) 623 { 624 static_assert( 625 black_magic::CallHelper<Func, black_magic::S<Args...>>::value || 626 black_magic::CallHelper< 627 Func, black_magic::S<crow::Request, Args...>>::value, 628 "Handler type is mismatched with URL parameters"); 629 static_assert( 630 !std::is_same<void, decltype(f(std::declval<crow::Request>(), 631 std::declval<Args>()...))>::value, 632 "Handler function cannot have void return type; valid return " 633 "types: " 634 "string, int, crow::response,nlohmann::json"); 635 636 handler = [f = std::forward<Func>(f)]( 637 const crow::Request& req, 638 const std::shared_ptr<bmcweb::AsyncResp>& asyncResp, 639 Args... args) { asyncResp->res.result(f(req, args...)); }; 640 } 641 642 template <typename Func> 643 typename std::enable_if< 644 !black_magic::CallHelper<Func, black_magic::S<Args...>>::value && 645 !black_magic::CallHelper< 646 Func, black_magic::S<crow::Request, Args...>>::value, 647 void>::type 648 operator()(Func&& f) 649 { 650 static_assert( 651 black_magic::CallHelper<Func, black_magic::S<Args...>>::value || 652 black_magic::CallHelper< 653 Func, black_magic::S<crow::Request, Args...>>::value || 654 black_magic::CallHelper< 655 Func, black_magic::S<crow::Request, 656 std::shared_ptr<bmcweb::AsyncResp>&, 657 Args...>>::value, 658 "Handler type is mismatched with URL parameters"); 659 static_assert( 660 std::is_same< 661 void, 662 decltype(f(std::declval<crow::Request>(), 663 std::declval<std::shared_ptr<bmcweb::AsyncResp>&>(), 664 std::declval<Args>()...))>::value, 665 "Handler function with response argument should have void " 666 "return " 667 "type"); 668 669 handler = std::forward<Func>(f); 670 } 671 672 template <typename Func> 673 void operator()(std::string_view name, Func&& f) 674 { 675 nameStr = name; 676 (*this).template operator()<Func>(std::forward(f)); 677 } 678 679 void handle(const Request& req, 680 const std::shared_ptr<bmcweb::AsyncResp>& asyncResp, 681 const RoutingParams& params) override 682 { 683 detail::routing_handler_call_helper::Call< 684 detail::routing_handler_call_helper::CallParams<decltype(handler)>, 685 0, 0, 0, 0, black_magic::S<Args...>, black_magic::S<>>()( 686 detail::routing_handler_call_helper::CallParams<decltype(handler)>{ 687 handler, params, req, asyncResp}); 688 } 689 690 private: 691 std::function<void(const crow::Request&, 692 const std::shared_ptr<bmcweb::AsyncResp>&, Args...)> 693 handler; 694 }; 695 696 class Trie 697 { 698 public: 699 struct Node 700 { 701 unsigned ruleIndex{}; 702 std::array<size_t, static_cast<size_t>(ParamType::MAX)> 703 paramChildrens{}; 704 using ChildMap = boost::container::flat_map< 705 std::string, unsigned, std::less<>, 706 std::vector<std::pair<std::string, unsigned>>>; 707 ChildMap children; 708 709 bool isSimpleNode() const 710 { 711 return ruleIndex == 0 && 712 std::all_of(std::begin(paramChildrens), 713 std::end(paramChildrens), 714 [](size_t x) { return x == 0U; }); 715 } 716 }; 717 718 Trie() : nodes(1) 719 {} 720 721 private: 722 void optimizeNode(Node* node) 723 { 724 for (size_t x : node->paramChildrens) 725 { 726 if (x == 0U) 727 { 728 continue; 729 } 730 Node* child = &nodes[x]; 731 optimizeNode(child); 732 } 733 if (node->children.empty()) 734 { 735 return; 736 } 737 bool mergeWithChild = true; 738 for (const Node::ChildMap::value_type& kv : node->children) 739 { 740 Node* child = &nodes[kv.second]; 741 if (!child->isSimpleNode()) 742 { 743 mergeWithChild = false; 744 break; 745 } 746 } 747 if (mergeWithChild) 748 { 749 Node::ChildMap merged; 750 for (const Node::ChildMap::value_type& kv : node->children) 751 { 752 Node* child = &nodes[kv.second]; 753 for (const Node::ChildMap::value_type& childKv : 754 child->children) 755 { 756 merged[kv.first + childKv.first] = childKv.second; 757 } 758 } 759 node->children = std::move(merged); 760 optimizeNode(node); 761 } 762 else 763 { 764 for (const Node::ChildMap::value_type& kv : node->children) 765 { 766 Node* child = &nodes[kv.second]; 767 optimizeNode(child); 768 } 769 } 770 } 771 772 void optimize() 773 { 774 optimizeNode(head()); 775 } 776 777 public: 778 void validate() 779 { 780 optimize(); 781 } 782 783 void findRouteIndexes(const std::string& reqUrl, 784 std::vector<unsigned>& routeIndexes, 785 const Node* node = nullptr, unsigned pos = 0) const 786 { 787 if (node == nullptr) 788 { 789 node = head(); 790 } 791 for (const Node::ChildMap::value_type& kv : node->children) 792 { 793 const std::string& fragment = kv.first; 794 const Node* child = &nodes[kv.second]; 795 if (pos >= reqUrl.size()) 796 { 797 if (child->ruleIndex != 0 && fragment != "/") 798 { 799 routeIndexes.push_back(child->ruleIndex); 800 } 801 findRouteIndexes(reqUrl, routeIndexes, child, 802 static_cast<unsigned>(pos + fragment.size())); 803 } 804 else 805 { 806 if (reqUrl.compare(pos, fragment.size(), fragment) == 0) 807 { 808 findRouteIndexes( 809 reqUrl, routeIndexes, child, 810 static_cast<unsigned>(pos + fragment.size())); 811 } 812 } 813 } 814 } 815 816 std::pair<unsigned, RoutingParams> 817 find(std::string_view reqUrl, const Node* node = nullptr, 818 size_t pos = 0, RoutingParams* params = nullptr) const 819 { 820 RoutingParams empty; 821 if (params == nullptr) 822 { 823 params = ∅ 824 } 825 826 unsigned found{}; 827 RoutingParams matchParams; 828 829 if (node == nullptr) 830 { 831 node = head(); 832 } 833 if (pos == reqUrl.size()) 834 { 835 return {node->ruleIndex, *params}; 836 } 837 838 auto updateFound = 839 [&found, &matchParams](std::pair<unsigned, RoutingParams>& ret) { 840 if (ret.first != 0U && (found == 0U || found > ret.first)) 841 { 842 found = ret.first; 843 matchParams = std::move(ret.second); 844 } 845 }; 846 847 if (node->paramChildrens[static_cast<size_t>(ParamType::INT)] != 0U) 848 { 849 char c = reqUrl[pos]; 850 if ((c >= '0' && c <= '9') || c == '+' || c == '-') 851 { 852 char* eptr = nullptr; 853 errno = 0; 854 long long int value = 855 std::strtoll(reqUrl.data() + pos, &eptr, 10); 856 if (errno != ERANGE && eptr != reqUrl.data() + pos) 857 { 858 params->intParams.push_back(value); 859 std::pair<unsigned, RoutingParams> ret = 860 find(reqUrl, 861 &nodes[node->paramChildrens[static_cast<size_t>( 862 ParamType::INT)]], 863 static_cast<size_t>(eptr - reqUrl.data()), params); 864 updateFound(ret); 865 params->intParams.pop_back(); 866 } 867 } 868 } 869 870 if (node->paramChildrens[static_cast<size_t>(ParamType::UINT)] != 0U) 871 { 872 char c = reqUrl[pos]; 873 if ((c >= '0' && c <= '9') || c == '+') 874 { 875 char* eptr = nullptr; 876 errno = 0; 877 unsigned long long int value = 878 std::strtoull(reqUrl.data() + pos, &eptr, 10); 879 if (errno != ERANGE && eptr != reqUrl.data() + pos) 880 { 881 params->uintParams.push_back(value); 882 std::pair<unsigned, RoutingParams> ret = 883 find(reqUrl, 884 &nodes[node->paramChildrens[static_cast<size_t>( 885 ParamType::UINT)]], 886 static_cast<size_t>(eptr - reqUrl.data()), params); 887 updateFound(ret); 888 params->uintParams.pop_back(); 889 } 890 } 891 } 892 893 if (node->paramChildrens[static_cast<size_t>(ParamType::DOUBLE)] != 0U) 894 { 895 char c = reqUrl[pos]; 896 if ((c >= '0' && c <= '9') || c == '+' || c == '-' || c == '.') 897 { 898 char* eptr = nullptr; 899 errno = 0; 900 double value = std::strtod(reqUrl.data() + pos, &eptr); 901 if (errno != ERANGE && eptr != reqUrl.data() + pos) 902 { 903 params->doubleParams.push_back(value); 904 std::pair<unsigned, RoutingParams> ret = 905 find(reqUrl, 906 &nodes[node->paramChildrens[static_cast<size_t>( 907 ParamType::DOUBLE)]], 908 static_cast<size_t>(eptr - reqUrl.data()), params); 909 updateFound(ret); 910 params->doubleParams.pop_back(); 911 } 912 } 913 } 914 915 if (node->paramChildrens[static_cast<size_t>(ParamType::STRING)] != 0U) 916 { 917 size_t epos = pos; 918 for (; epos < reqUrl.size(); epos++) 919 { 920 if (reqUrl[epos] == '/') 921 { 922 break; 923 } 924 } 925 926 if (epos != pos) 927 { 928 params->stringParams.emplace_back( 929 reqUrl.substr(pos, epos - pos)); 930 std::pair<unsigned, RoutingParams> ret = 931 find(reqUrl, 932 &nodes[node->paramChildrens[static_cast<size_t>( 933 ParamType::STRING)]], 934 epos, params); 935 updateFound(ret); 936 params->stringParams.pop_back(); 937 } 938 } 939 940 if (node->paramChildrens[static_cast<size_t>(ParamType::PATH)] != 0U) 941 { 942 size_t epos = reqUrl.size(); 943 944 if (epos != pos) 945 { 946 params->stringParams.emplace_back( 947 reqUrl.substr(pos, epos - pos)); 948 std::pair<unsigned, RoutingParams> ret = 949 find(reqUrl, 950 &nodes[node->paramChildrens[static_cast<size_t>( 951 ParamType::PATH)]], 952 epos, params); 953 updateFound(ret); 954 params->stringParams.pop_back(); 955 } 956 } 957 958 for (const Node::ChildMap::value_type& kv : node->children) 959 { 960 const std::string& fragment = kv.first; 961 const Node* child = &nodes[kv.second]; 962 963 if (reqUrl.compare(pos, fragment.size(), fragment) == 0) 964 { 965 std::pair<unsigned, RoutingParams> ret = 966 find(reqUrl, child, pos + fragment.size(), params); 967 updateFound(ret); 968 } 969 } 970 971 return {found, matchParams}; 972 } 973 974 void add(const std::string& url, unsigned ruleIndex) 975 { 976 size_t idx = 0; 977 978 for (unsigned i = 0; i < url.size(); i++) 979 { 980 char c = url[i]; 981 if (c == '<') 982 { 983 const static std::array<std::pair<ParamType, std::string>, 7> 984 paramTraits = {{ 985 {ParamType::INT, "<int>"}, 986 {ParamType::UINT, "<uint>"}, 987 {ParamType::DOUBLE, "<float>"}, 988 {ParamType::DOUBLE, "<double>"}, 989 {ParamType::STRING, "<str>"}, 990 {ParamType::STRING, "<string>"}, 991 {ParamType::PATH, "<path>"}, 992 }}; 993 994 for (const std::pair<ParamType, std::string>& x : paramTraits) 995 { 996 if (url.compare(i, x.second.size(), x.second) == 0) 997 { 998 size_t index = static_cast<size_t>(x.first); 999 if (nodes[idx].paramChildrens[index] == 0U) 1000 { 1001 unsigned newNodeIdx = newNode(); 1002 nodes[idx].paramChildrens[index] = newNodeIdx; 1003 } 1004 idx = nodes[idx].paramChildrens[index]; 1005 i += static_cast<unsigned>(x.second.size()); 1006 break; 1007 } 1008 } 1009 1010 i--; 1011 } 1012 else 1013 { 1014 std::string piece(&c, 1); 1015 if (nodes[idx].children.count(piece) == 0U) 1016 { 1017 unsigned newNodeIdx = newNode(); 1018 nodes[idx].children.emplace(piece, newNodeIdx); 1019 } 1020 idx = nodes[idx].children[piece]; 1021 } 1022 } 1023 if (nodes[idx].ruleIndex != 0U) 1024 { 1025 throw std::runtime_error("handler already exists for " + url); 1026 } 1027 nodes[idx].ruleIndex = ruleIndex; 1028 } 1029 1030 private: 1031 void debugNodePrint(Node* n, size_t level) 1032 { 1033 for (size_t i = 0; i < static_cast<size_t>(ParamType::MAX); i++) 1034 { 1035 if (n->paramChildrens[i] != 0U) 1036 { 1037 BMCWEB_LOG_DEBUG << std::string( 1038 2U * level, ' ') /*<< "("<<n->paramChildrens[i]<<") "*/; 1039 switch (static_cast<ParamType>(i)) 1040 { 1041 case ParamType::INT: 1042 BMCWEB_LOG_DEBUG << "<int>"; 1043 break; 1044 case ParamType::UINT: 1045 BMCWEB_LOG_DEBUG << "<uint>"; 1046 break; 1047 case ParamType::DOUBLE: 1048 BMCWEB_LOG_DEBUG << "<float>"; 1049 break; 1050 case ParamType::STRING: 1051 BMCWEB_LOG_DEBUG << "<str>"; 1052 break; 1053 case ParamType::PATH: 1054 BMCWEB_LOG_DEBUG << "<path>"; 1055 break; 1056 case ParamType::MAX: 1057 BMCWEB_LOG_DEBUG << "<ERROR>"; 1058 break; 1059 } 1060 1061 debugNodePrint(&nodes[n->paramChildrens[i]], level + 1); 1062 } 1063 } 1064 for (const Node::ChildMap::value_type& kv : n->children) 1065 { 1066 BMCWEB_LOG_DEBUG 1067 << std::string(2U * level, ' ') /*<< "(" << kv.second << ") "*/ 1068 << kv.first; 1069 debugNodePrint(&nodes[kv.second], level + 1); 1070 } 1071 } 1072 1073 public: 1074 void debugPrint() 1075 { 1076 debugNodePrint(head(), 0U); 1077 } 1078 1079 private: 1080 const Node* head() const 1081 { 1082 return &nodes.front(); 1083 } 1084 1085 Node* head() 1086 { 1087 return &nodes.front(); 1088 } 1089 1090 unsigned newNode() 1091 { 1092 nodes.resize(nodes.size() + 1); 1093 return static_cast<unsigned>(nodes.size() - 1); 1094 } 1095 1096 std::vector<Node> nodes; 1097 }; 1098 1099 class Router 1100 { 1101 public: 1102 Router() = default; 1103 1104 DynamicRule& newRuleDynamic(const std::string& rule) 1105 { 1106 std::unique_ptr<DynamicRule> ruleObject = 1107 std::make_unique<DynamicRule>(rule); 1108 DynamicRule* ptr = ruleObject.get(); 1109 allRules.emplace_back(std::move(ruleObject)); 1110 1111 return *ptr; 1112 } 1113 1114 template <uint64_t N> 1115 typename black_magic::Arguments<N>::type::template rebind<TaggedRule>& 1116 newRuleTagged(const std::string& rule) 1117 { 1118 using RuleT = typename black_magic::Arguments<N>::type::template rebind< 1119 TaggedRule>; 1120 std::unique_ptr<RuleT> ruleObject = std::make_unique<RuleT>(rule); 1121 RuleT* ptr = ruleObject.get(); 1122 allRules.emplace_back(std::move(ruleObject)); 1123 1124 return *ptr; 1125 } 1126 1127 void internalAddRuleObject(const std::string& rule, BaseRule* ruleObject) 1128 { 1129 if (ruleObject == nullptr) 1130 { 1131 return; 1132 } 1133 for (size_t method = 0, methodBit = 1; method <= methodNotAllowedIndex; 1134 method++, methodBit <<= 1) 1135 { 1136 if ((ruleObject->methodsBitfield & methodBit) > 0U) 1137 { 1138 perMethods[method].rules.emplace_back(ruleObject); 1139 perMethods[method].trie.add( 1140 rule, static_cast<unsigned>( 1141 perMethods[method].rules.size() - 1U)); 1142 // directory case: 1143 // request to `/about' url matches `/about/' rule 1144 if (rule.size() > 2 && rule.back() == '/') 1145 { 1146 perMethods[method].trie.add( 1147 rule.substr(0, rule.size() - 1), 1148 static_cast<unsigned>(perMethods[method].rules.size() - 1149 1)); 1150 } 1151 } 1152 } 1153 } 1154 1155 void validate() 1156 { 1157 for (std::unique_ptr<BaseRule>& rule : allRules) 1158 { 1159 if (rule) 1160 { 1161 std::unique_ptr<BaseRule> upgraded = rule->upgrade(); 1162 if (upgraded) 1163 { 1164 rule = std::move(upgraded); 1165 } 1166 rule->validate(); 1167 internalAddRuleObject(rule->rule, rule.get()); 1168 } 1169 } 1170 for (PerMethod& perMethod : perMethods) 1171 { 1172 perMethod.trie.validate(); 1173 } 1174 } 1175 1176 struct FindRoute 1177 { 1178 BaseRule* rule = nullptr; 1179 RoutingParams params; 1180 }; 1181 1182 struct FindRouteResponse 1183 { 1184 std::string allowHeader; 1185 FindRoute route; 1186 }; 1187 1188 FindRoute findRouteByIndex(std::string_view url, size_t index) const 1189 { 1190 FindRoute route; 1191 if (index >= perMethods.size()) 1192 { 1193 BMCWEB_LOG_CRITICAL << "Bad index???"; 1194 return route; 1195 } 1196 const PerMethod& perMethod = perMethods[index]; 1197 std::pair<unsigned, RoutingParams> found = perMethod.trie.find(url); 1198 if (found.first >= perMethod.rules.size()) 1199 { 1200 throw std::runtime_error("Trie internal structure corrupted!"); 1201 } 1202 // Found a 404 route, switch that in 1203 if (found.first != 0U) 1204 { 1205 route.rule = perMethod.rules[found.first]; 1206 route.params = std::move(found.second); 1207 } 1208 return route; 1209 } 1210 1211 FindRouteResponse findRoute(Request& req) const 1212 { 1213 FindRouteResponse findRoute; 1214 1215 std::optional<HttpVerb> verb = httpVerbFromBoost(req.method()); 1216 if (!verb) 1217 { 1218 return findRoute; 1219 } 1220 size_t reqMethodIndex = static_cast<size_t>(*verb); 1221 // Check to see if this url exists at any verb 1222 for (size_t perMethodIndex = 0; perMethodIndex <= maxVerbIndex; 1223 perMethodIndex++) 1224 { 1225 // Make sure it's safe to deference the array at that index 1226 static_assert(maxVerbIndex < 1227 std::tuple_size_v<decltype(perMethods)>); 1228 FindRoute route = 1229 findRouteByIndex(req.url().encoded_path(), perMethodIndex); 1230 if (route.rule == nullptr) 1231 { 1232 continue; 1233 } 1234 if (!findRoute.allowHeader.empty()) 1235 { 1236 findRoute.allowHeader += ", "; 1237 } 1238 HttpVerb thisVerb = static_cast<HttpVerb>(perMethodIndex); 1239 findRoute.allowHeader += httpVerbToString(thisVerb); 1240 if (perMethodIndex == reqMethodIndex) 1241 { 1242 findRoute.route = route; 1243 } 1244 } 1245 return findRoute; 1246 } 1247 1248 static bool isUserPrivileged( 1249 Request& req, const std::shared_ptr<bmcweb::AsyncResp>& asyncResp, 1250 BaseRule& rule, const dbus::utility::DBusPropertiesMap& userInfoMap) 1251 { 1252 std::string userRole{}; 1253 const std::string* userRolePtr = nullptr; 1254 const bool* remoteUser = nullptr; 1255 const bool* passwordExpired = nullptr; 1256 1257 const bool success = sdbusplus::unpackPropertiesNoThrow( 1258 redfish::dbus_utils::UnpackErrorPrinter(), userInfoMap, 1259 "UserPrivilege", userRolePtr, "RemoteUser", remoteUser, 1260 "UserPasswordExpired", passwordExpired); 1261 1262 if (!success) 1263 { 1264 asyncResp->res.result( 1265 boost::beast::http::status::internal_server_error); 1266 return false; 1267 } 1268 1269 if (userRolePtr != nullptr) 1270 { 1271 userRole = *userRolePtr; 1272 BMCWEB_LOG_DEBUG << "userName = " << req.session->username 1273 << " userRole = " << *userRolePtr; 1274 } 1275 1276 if (remoteUser == nullptr) 1277 { 1278 BMCWEB_LOG_ERROR << "RemoteUser property missing or wrong type"; 1279 asyncResp->res.result( 1280 boost::beast::http::status::internal_server_error); 1281 return false; 1282 } 1283 bool expired = false; 1284 if (passwordExpired == nullptr) 1285 { 1286 if (!*remoteUser) 1287 { 1288 BMCWEB_LOG_ERROR 1289 << "UserPasswordExpired property is expected for" 1290 " local user but is missing or wrong type"; 1291 asyncResp->res.result( 1292 boost::beast::http::status::internal_server_error); 1293 return false; 1294 } 1295 } 1296 else 1297 { 1298 expired = *passwordExpired; 1299 } 1300 1301 // Get the user's privileges from the role 1302 redfish::Privileges userPrivileges = 1303 redfish::getUserPrivileges(userRole); 1304 1305 // Set isConfigureSelfOnly based on D-Bus results. This 1306 // ignores the results from both pamAuthenticateUser and the 1307 // value from any previous use of this session. 1308 req.session->isConfigureSelfOnly = expired; 1309 1310 // Modify privileges if isConfigureSelfOnly. 1311 if (req.session->isConfigureSelfOnly) 1312 { 1313 // Remove all privileges except ConfigureSelf 1314 userPrivileges = userPrivileges.intersection( 1315 redfish::Privileges{"ConfigureSelf"}); 1316 BMCWEB_LOG_DEBUG << "Operation limited to ConfigureSelf"; 1317 } 1318 1319 if (!rule.checkPrivileges(userPrivileges)) 1320 { 1321 asyncResp->res.result(boost::beast::http::status::forbidden); 1322 if (req.session->isConfigureSelfOnly) 1323 { 1324 redfish::messages::passwordChangeRequired( 1325 asyncResp->res, crow::utility::urlFromPieces( 1326 "redfish", "v1", "AccountService", 1327 "Accounts", req.session->username)); 1328 } 1329 return false; 1330 } 1331 1332 req.userRole = userRole; 1333 1334 return true; 1335 } 1336 1337 template <typename CallbackFn> 1338 void afterGetUserInfo(Request&& req, 1339 const std::shared_ptr<bmcweb::AsyncResp>& asyncResp, 1340 BaseRule& rule, CallbackFn&& callback, 1341 const boost::system::error_code& ec, 1342 const dbus::utility::DBusPropertiesMap& userInfoMap) 1343 { 1344 if (ec) 1345 { 1346 BMCWEB_LOG_ERROR << "GetUserInfo failed..."; 1347 asyncResp->res.result( 1348 boost::beast::http::status::internal_server_error); 1349 return; 1350 } 1351 1352 if (!Router::isUserPrivileged(req, asyncResp, rule, userInfoMap)) 1353 { 1354 // User is not privileged 1355 BMCWEB_LOG_ERROR << "Insufficient Privilege"; 1356 asyncResp->res.result(boost::beast::http::status::forbidden); 1357 return; 1358 } 1359 callback(std::move(req)); 1360 } 1361 1362 template <typename CallbackFn> 1363 void validatePrivilege(Request&& req, 1364 const std::shared_ptr<bmcweb::AsyncResp>& asyncResp, 1365 BaseRule& rule, CallbackFn&& callback) 1366 { 1367 if (req.session == nullptr) 1368 { 1369 return; 1370 } 1371 std::string username = req.session->username; 1372 crow::connections::systemBus->async_method_call( 1373 [this, req{std::move(req)}, asyncResp, &rule, 1374 callback(std::forward<CallbackFn>(callback))]( 1375 const boost::system::error_code& ec, 1376 const dbus::utility::DBusPropertiesMap& userInfoMap) mutable { 1377 afterGetUserInfo(std::move(req), asyncResp, rule, 1378 std::forward<CallbackFn>(callback), ec, 1379 userInfoMap); 1380 }, 1381 "xyz.openbmc_project.User.Manager", "/xyz/openbmc_project/user", 1382 "xyz.openbmc_project.User.Manager", "GetUserInfo", username); 1383 } 1384 1385 template <typename Adaptor> 1386 void handleUpgrade(Request& req, 1387 const std::shared_ptr<bmcweb::AsyncResp>& asyncResp, 1388 Adaptor&& adaptor) 1389 { 1390 std::optional<HttpVerb> verb = httpVerbFromBoost(req.method()); 1391 if (!verb || static_cast<size_t>(*verb) >= perMethods.size()) 1392 { 1393 asyncResp->res.result(boost::beast::http::status::not_found); 1394 return; 1395 } 1396 PerMethod& perMethod = perMethods[static_cast<size_t>(*verb)]; 1397 Trie& trie = perMethod.trie; 1398 std::vector<BaseRule*>& rules = perMethod.rules; 1399 1400 const std::pair<unsigned, RoutingParams>& found = 1401 trie.find(req.url().encoded_path()); 1402 unsigned ruleIndex = found.first; 1403 if (ruleIndex == 0U) 1404 { 1405 BMCWEB_LOG_DEBUG << "Cannot match rules " 1406 << req.url().encoded_path(); 1407 asyncResp->res.result(boost::beast::http::status::not_found); 1408 return; 1409 } 1410 1411 if (ruleIndex >= rules.size()) 1412 { 1413 throw std::runtime_error("Trie internal structure corrupted!"); 1414 } 1415 1416 BaseRule& rule = *rules[ruleIndex]; 1417 size_t methods = rule.getMethods(); 1418 if ((methods & (1U << static_cast<size_t>(*verb))) == 0) 1419 { 1420 BMCWEB_LOG_DEBUG 1421 << "Rule found but method mismatch: " 1422 << req.url().encoded_path() << " with " << req.methodString() 1423 << "(" << static_cast<uint32_t>(*verb) << ") / " << methods; 1424 asyncResp->res.result(boost::beast::http::status::not_found); 1425 return; 1426 } 1427 1428 BMCWEB_LOG_DEBUG << "Matched rule (upgrade) '" << rule.rule << "' " 1429 << static_cast<uint32_t>(*verb) << " / " << methods; 1430 1431 // TODO(ed) This should be able to use std::bind_front, but it doesn't 1432 // appear to work with the std::move on adaptor. 1433 validatePrivilege( 1434 std::move(req), asyncResp, rule, 1435 [&rule, asyncResp, adaptor(std::forward<Adaptor>(adaptor))]( 1436 Request&& thisReq) mutable { 1437 rule.handleUpgrade(thisReq, asyncResp, std::move(adaptor)); 1438 }); 1439 } 1440 1441 void handle(Request& req, 1442 const std::shared_ptr<bmcweb::AsyncResp>& asyncResp) 1443 { 1444 std::optional<HttpVerb> verb = httpVerbFromBoost(req.method()); 1445 if (!verb || static_cast<size_t>(*verb) >= perMethods.size()) 1446 { 1447 asyncResp->res.result(boost::beast::http::status::not_found); 1448 return; 1449 } 1450 1451 FindRouteResponse foundRoute = findRoute(req); 1452 1453 if (foundRoute.route.rule == nullptr) 1454 { 1455 // Couldn't find a normal route with any verb, try looking for a 404 1456 // route 1457 if (foundRoute.allowHeader.empty()) 1458 { 1459 foundRoute.route = 1460 findRouteByIndex(req.url().encoded_path(), notFoundIndex); 1461 } 1462 else 1463 { 1464 // See if we have a method not allowed (405) handler 1465 foundRoute.route = findRouteByIndex(req.url().encoded_path(), 1466 methodNotAllowedIndex); 1467 } 1468 } 1469 1470 // Fill in the allow header if it's valid 1471 if (!foundRoute.allowHeader.empty()) 1472 { 1473 1474 asyncResp->res.addHeader(boost::beast::http::field::allow, 1475 foundRoute.allowHeader); 1476 } 1477 1478 // If we couldn't find a real route or a 404 route, return a generic 1479 // response 1480 if (foundRoute.route.rule == nullptr) 1481 { 1482 if (foundRoute.allowHeader.empty()) 1483 { 1484 asyncResp->res.result(boost::beast::http::status::not_found); 1485 } 1486 else 1487 { 1488 asyncResp->res.result( 1489 boost::beast::http::status::method_not_allowed); 1490 } 1491 return; 1492 } 1493 1494 BaseRule& rule = *foundRoute.route.rule; 1495 RoutingParams params = std::move(foundRoute.route.params); 1496 1497 BMCWEB_LOG_DEBUG << "Matched rule '" << rule.rule << "' " 1498 << static_cast<uint32_t>(*verb) << " / " 1499 << rule.getMethods(); 1500 1501 if (req.session == nullptr) 1502 { 1503 rule.handle(req, asyncResp, params); 1504 return; 1505 } 1506 validatePrivilege( 1507 std::move(req), asyncResp, rule, 1508 [&rule, asyncResp, params](Request&& thisReq) mutable { 1509 rule.handle(thisReq, asyncResp, params); 1510 }); 1511 } 1512 1513 void debugPrint() 1514 { 1515 for (size_t i = 0; i < perMethods.size(); i++) 1516 { 1517 BMCWEB_LOG_DEBUG << boost::beast::http::to_string( 1518 static_cast<boost::beast::http::verb>(i)); 1519 perMethods[i].trie.debugPrint(); 1520 } 1521 } 1522 1523 std::vector<const std::string*> getRoutes(const std::string& parent) 1524 { 1525 std::vector<const std::string*> ret; 1526 1527 for (const PerMethod& pm : perMethods) 1528 { 1529 std::vector<unsigned> x; 1530 pm.trie.findRouteIndexes(parent, x); 1531 for (unsigned index : x) 1532 { 1533 ret.push_back(&pm.rules[index]->rule); 1534 } 1535 } 1536 return ret; 1537 } 1538 1539 private: 1540 struct PerMethod 1541 { 1542 std::vector<BaseRule*> rules; 1543 Trie trie; 1544 // rule index 0 has special meaning; preallocate it to avoid 1545 // duplication. 1546 PerMethod() : rules(1) 1547 {} 1548 }; 1549 1550 std::array<PerMethod, methodNotAllowedIndex + 1> perMethods; 1551 std::vector<std::unique_ptr<BaseRule>> allRules; 1552 }; 1553 } // namespace crow 1554