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 messageExHandler, 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 messageExHandler, 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& onmessageex(Func f) 399 { 400 messageExHandler = f; 401 return *this; 402 } 403 404 template <typename Func> 405 self_t& onclose(Func f) 406 { 407 closeHandler = f; 408 return *this; 409 } 410 411 template <typename Func> 412 self_t& onerror(Func f) 413 { 414 errorHandler = f; 415 return *this; 416 } 417 418 protected: 419 std::function<void(crow::websocket::Connection&)> openHandler; 420 std::function<void(crow::websocket::Connection&, const std::string&, bool)> 421 messageHandler; 422 std::function<void(crow::websocket::Connection&, std::string_view, 423 crow::websocket::MessageType type, 424 std::function<void()>&& whenComplete)> 425 messageExHandler; 426 std::function<void(crow::websocket::Connection&, const std::string&)> 427 closeHandler; 428 std::function<void(crow::websocket::Connection&)> errorHandler; 429 }; 430 431 template <typename T> 432 struct RuleParameterTraits 433 { 434 using self_t = T; 435 WebSocketRule& websocket() 436 { 437 self_t* self = static_cast<self_t*>(this); 438 WebSocketRule* p = new WebSocketRule(self->rule); 439 self->ruleToUpgrade.reset(p); 440 return *p; 441 } 442 443 self_t& name(std::string_view name) noexcept 444 { 445 self_t* self = static_cast<self_t*>(this); 446 self->nameStr = name; 447 return *self; 448 } 449 450 self_t& methods(boost::beast::http::verb method) 451 { 452 self_t* self = static_cast<self_t*>(this); 453 std::optional<HttpVerb> verb = httpVerbFromBoost(method); 454 if (verb) 455 { 456 self->methodsBitfield = 1U << static_cast<size_t>(*verb); 457 } 458 return *self; 459 } 460 461 template <typename... MethodArgs> 462 self_t& methods(boost::beast::http::verb method, MethodArgs... argsMethod) 463 { 464 self_t* self = static_cast<self_t*>(this); 465 methods(argsMethod...); 466 std::optional<HttpVerb> verb = httpVerbFromBoost(method); 467 if (verb) 468 { 469 self->methodsBitfield |= 1U << static_cast<size_t>(*verb); 470 } 471 return *self; 472 } 473 474 self_t& notFound() 475 { 476 self_t* self = static_cast<self_t*>(this); 477 self->methodsBitfield = 1U << notFoundIndex; 478 return *self; 479 } 480 481 self_t& methodNotAllowed() 482 { 483 self_t* self = static_cast<self_t*>(this); 484 self->methodsBitfield = 1U << methodNotAllowedIndex; 485 return *self; 486 } 487 488 self_t& privileges( 489 const std::initializer_list<std::initializer_list<const char*>>& p) 490 { 491 self_t* self = static_cast<self_t*>(this); 492 for (const std::initializer_list<const char*>& privilege : p) 493 { 494 self->privilegesSet.emplace_back(privilege); 495 } 496 return *self; 497 } 498 499 template <size_t N, typename... MethodArgs> 500 self_t& privileges(const std::array<redfish::Privileges, N>& p) 501 { 502 self_t* self = static_cast<self_t*>(this); 503 for (const redfish::Privileges& privilege : p) 504 { 505 self->privilegesSet.emplace_back(privilege); 506 } 507 return *self; 508 } 509 }; 510 511 class DynamicRule : public BaseRule, public RuleParameterTraits<DynamicRule> 512 { 513 public: 514 explicit DynamicRule(const std::string& ruleIn) : BaseRule(ruleIn) 515 {} 516 517 void validate() override 518 { 519 if (!erasedHandler) 520 { 521 throw std::runtime_error(nameStr + (!nameStr.empty() ? ": " : "") + 522 "no handler for url " + rule); 523 } 524 } 525 526 void handle(const Request& req, 527 const std::shared_ptr<bmcweb::AsyncResp>& asyncResp, 528 const RoutingParams& params) override 529 { 530 erasedHandler(req, asyncResp, params); 531 } 532 533 template <typename Func> 534 void operator()(Func f) 535 { 536 using boost::callable_traits::args_t; 537 constexpr size_t arity = std::tuple_size<args_t<Func>>::value; 538 constexpr auto is = std::make_integer_sequence<unsigned, arity>{}; 539 erasedHandler = wrap(std::move(f), is); 540 } 541 542 // enable_if Arg1 == request && Arg2 == Response 543 // enable_if Arg1 == request && Arg2 != response 544 // enable_if Arg1 != request 545 546 template <typename Func, unsigned... Indices> 547 std::function<void(const Request&, 548 const std::shared_ptr<bmcweb::AsyncResp>&, 549 const RoutingParams&)> 550 wrap(Func f, std::integer_sequence<unsigned, Indices...> /*is*/) 551 { 552 using function_t = crow::utility::FunctionTraits<Func>; 553 554 if (!black_magic::isParameterTagCompatible( 555 black_magic::getParameterTag(rule.c_str()), 556 black_magic::computeParameterTagFromArgsList< 557 typename function_t::template arg<Indices>...>::value)) 558 { 559 throw std::runtime_error("routeDynamic: Handler type is mismatched " 560 "with URL parameters: " + 561 rule); 562 } 563 auto ret = detail::routing_handler_call_helper::Wrapped< 564 Func, typename function_t::template arg<Indices>...>(); 565 ret.template set<typename function_t::template arg<Indices>...>( 566 std::move(f)); 567 return ret; 568 } 569 570 template <typename Func> 571 void operator()(std::string name, Func&& f) 572 { 573 nameStr = std::move(name); 574 (*this).template operator()<Func>(std::forward(f)); 575 } 576 577 private: 578 std::function<void(const Request&, 579 const std::shared_ptr<bmcweb::AsyncResp>&, 580 const RoutingParams&)> 581 erasedHandler; 582 }; 583 584 template <typename... Args> 585 class TaggedRule : 586 public BaseRule, 587 public RuleParameterTraits<TaggedRule<Args...>> 588 { 589 public: 590 using self_t = TaggedRule<Args...>; 591 592 explicit TaggedRule(const std::string& ruleIn) : BaseRule(ruleIn) 593 {} 594 595 void validate() override 596 { 597 if (!handler) 598 { 599 throw std::runtime_error(nameStr + (!nameStr.empty() ? ": " : "") + 600 "no handler for url " + rule); 601 } 602 } 603 604 template <typename Func> 605 typename std::enable_if< 606 black_magic::CallHelper<Func, black_magic::S<Args...>>::value, 607 void>::type 608 operator()(Func&& f) 609 { 610 static_assert( 611 black_magic::CallHelper<Func, black_magic::S<Args...>>::value || 612 black_magic::CallHelper< 613 Func, black_magic::S<crow::Request, Args...>>::value, 614 "Handler type is mismatched with URL parameters"); 615 static_assert( 616 !std::is_same<void, decltype(f(std::declval<Args>()...))>::value, 617 "Handler function cannot have void return type; valid return " 618 "types: " 619 "string, int, crow::response, nlohmann::json"); 620 621 handler = [f = std::forward<Func>(f)]( 622 const Request&, 623 const std::shared_ptr<bmcweb::AsyncResp>& asyncResp, 624 Args... args) { asyncResp->res.result(f(args...)); }; 625 } 626 627 template <typename Func> 628 typename std::enable_if< 629 !black_magic::CallHelper<Func, black_magic::S<Args...>>::value && 630 black_magic::CallHelper< 631 Func, black_magic::S<crow::Request, Args...>>::value, 632 void>::type 633 operator()(Func&& f) 634 { 635 static_assert( 636 black_magic::CallHelper<Func, black_magic::S<Args...>>::value || 637 black_magic::CallHelper< 638 Func, black_magic::S<crow::Request, Args...>>::value, 639 "Handler type is mismatched with URL parameters"); 640 static_assert( 641 !std::is_same<void, decltype(f(std::declval<crow::Request>(), 642 std::declval<Args>()...))>::value, 643 "Handler function cannot have void return type; valid return " 644 "types: " 645 "string, int, crow::response,nlohmann::json"); 646 647 handler = [f = std::forward<Func>(f)]( 648 const crow::Request& req, 649 const std::shared_ptr<bmcweb::AsyncResp>& asyncResp, 650 Args... args) { asyncResp->res.result(f(req, args...)); }; 651 } 652 653 template <typename Func> 654 typename std::enable_if< 655 !black_magic::CallHelper<Func, black_magic::S<Args...>>::value && 656 !black_magic::CallHelper< 657 Func, black_magic::S<crow::Request, Args...>>::value, 658 void>::type 659 operator()(Func&& f) 660 { 661 static_assert( 662 black_magic::CallHelper<Func, black_magic::S<Args...>>::value || 663 black_magic::CallHelper< 664 Func, black_magic::S<crow::Request, Args...>>::value || 665 black_magic::CallHelper< 666 Func, black_magic::S<crow::Request, 667 std::shared_ptr<bmcweb::AsyncResp>&, 668 Args...>>::value, 669 "Handler type is mismatched with URL parameters"); 670 static_assert( 671 std::is_same< 672 void, 673 decltype(f(std::declval<crow::Request>(), 674 std::declval<std::shared_ptr<bmcweb::AsyncResp>&>(), 675 std::declval<Args>()...))>::value, 676 "Handler function with response argument should have void " 677 "return " 678 "type"); 679 680 handler = std::forward<Func>(f); 681 } 682 683 template <typename Func> 684 void operator()(std::string_view name, Func&& f) 685 { 686 nameStr = name; 687 (*this).template operator()<Func>(std::forward(f)); 688 } 689 690 void handle(const Request& req, 691 const std::shared_ptr<bmcweb::AsyncResp>& asyncResp, 692 const RoutingParams& params) override 693 { 694 detail::routing_handler_call_helper::Call< 695 detail::routing_handler_call_helper::CallParams<decltype(handler)>, 696 0, 0, 0, 0, black_magic::S<Args...>, black_magic::S<>>()( 697 detail::routing_handler_call_helper::CallParams<decltype(handler)>{ 698 handler, params, req, asyncResp}); 699 } 700 701 private: 702 std::function<void(const crow::Request&, 703 const std::shared_ptr<bmcweb::AsyncResp>&, Args...)> 704 handler; 705 }; 706 707 class Trie 708 { 709 public: 710 struct Node 711 { 712 unsigned ruleIndex{}; 713 std::array<size_t, static_cast<size_t>(ParamType::MAX)> 714 paramChildrens{}; 715 using ChildMap = boost::container::flat_map< 716 std::string, unsigned, std::less<>, 717 std::vector<std::pair<std::string, unsigned>>>; 718 ChildMap children; 719 720 bool isSimpleNode() const 721 { 722 return ruleIndex == 0 && 723 std::all_of(std::begin(paramChildrens), 724 std::end(paramChildrens), 725 [](size_t x) { return x == 0U; }); 726 } 727 }; 728 729 Trie() : nodes(1) 730 {} 731 732 private: 733 void optimizeNode(Node* node) 734 { 735 for (size_t x : node->paramChildrens) 736 { 737 if (x == 0U) 738 { 739 continue; 740 } 741 Node* child = &nodes[x]; 742 optimizeNode(child); 743 } 744 if (node->children.empty()) 745 { 746 return; 747 } 748 bool mergeWithChild = true; 749 for (const Node::ChildMap::value_type& kv : node->children) 750 { 751 Node* child = &nodes[kv.second]; 752 if (!child->isSimpleNode()) 753 { 754 mergeWithChild = false; 755 break; 756 } 757 } 758 if (mergeWithChild) 759 { 760 Node::ChildMap merged; 761 for (const Node::ChildMap::value_type& kv : node->children) 762 { 763 Node* child = &nodes[kv.second]; 764 for (const Node::ChildMap::value_type& childKv : 765 child->children) 766 { 767 merged[kv.first + childKv.first] = childKv.second; 768 } 769 } 770 node->children = std::move(merged); 771 optimizeNode(node); 772 } 773 else 774 { 775 for (const Node::ChildMap::value_type& kv : node->children) 776 { 777 Node* child = &nodes[kv.second]; 778 optimizeNode(child); 779 } 780 } 781 } 782 783 void optimize() 784 { 785 optimizeNode(head()); 786 } 787 788 public: 789 void validate() 790 { 791 optimize(); 792 } 793 794 void findRouteIndexes(const std::string& reqUrl, 795 std::vector<unsigned>& routeIndexes, 796 const Node* node = nullptr, unsigned pos = 0) const 797 { 798 if (node == nullptr) 799 { 800 node = head(); 801 } 802 for (const Node::ChildMap::value_type& kv : node->children) 803 { 804 const std::string& fragment = kv.first; 805 const Node* child = &nodes[kv.second]; 806 if (pos >= reqUrl.size()) 807 { 808 if (child->ruleIndex != 0 && fragment != "/") 809 { 810 routeIndexes.push_back(child->ruleIndex); 811 } 812 findRouteIndexes(reqUrl, routeIndexes, child, 813 static_cast<unsigned>(pos + fragment.size())); 814 } 815 else 816 { 817 if (reqUrl.compare(pos, fragment.size(), fragment) == 0) 818 { 819 findRouteIndexes( 820 reqUrl, routeIndexes, child, 821 static_cast<unsigned>(pos + fragment.size())); 822 } 823 } 824 } 825 } 826 827 std::pair<unsigned, RoutingParams> 828 find(std::string_view reqUrl, const Node* node = nullptr, 829 size_t pos = 0, RoutingParams* params = nullptr) const 830 { 831 RoutingParams empty; 832 if (params == nullptr) 833 { 834 params = ∅ 835 } 836 837 unsigned found{}; 838 RoutingParams matchParams; 839 840 if (node == nullptr) 841 { 842 node = head(); 843 } 844 if (pos == reqUrl.size()) 845 { 846 return {node->ruleIndex, *params}; 847 } 848 849 auto updateFound = 850 [&found, &matchParams](std::pair<unsigned, RoutingParams>& ret) { 851 if (ret.first != 0U && (found == 0U || found > ret.first)) 852 { 853 found = ret.first; 854 matchParams = std::move(ret.second); 855 } 856 }; 857 858 if (node->paramChildrens[static_cast<size_t>(ParamType::INT)] != 0U) 859 { 860 char c = reqUrl[pos]; 861 if ((c >= '0' && c <= '9') || c == '+' || c == '-') 862 { 863 char* eptr = nullptr; 864 errno = 0; 865 long long int value = 866 std::strtoll(reqUrl.data() + pos, &eptr, 10); 867 if (errno != ERANGE && eptr != reqUrl.data() + pos) 868 { 869 params->intParams.push_back(value); 870 std::pair<unsigned, RoutingParams> ret = 871 find(reqUrl, 872 &nodes[node->paramChildrens[static_cast<size_t>( 873 ParamType::INT)]], 874 static_cast<size_t>(eptr - reqUrl.data()), params); 875 updateFound(ret); 876 params->intParams.pop_back(); 877 } 878 } 879 } 880 881 if (node->paramChildrens[static_cast<size_t>(ParamType::UINT)] != 0U) 882 { 883 char c = reqUrl[pos]; 884 if ((c >= '0' && c <= '9') || c == '+') 885 { 886 char* eptr = nullptr; 887 errno = 0; 888 unsigned long long int value = 889 std::strtoull(reqUrl.data() + pos, &eptr, 10); 890 if (errno != ERANGE && eptr != reqUrl.data() + pos) 891 { 892 params->uintParams.push_back(value); 893 std::pair<unsigned, RoutingParams> ret = 894 find(reqUrl, 895 &nodes[node->paramChildrens[static_cast<size_t>( 896 ParamType::UINT)]], 897 static_cast<size_t>(eptr - reqUrl.data()), params); 898 updateFound(ret); 899 params->uintParams.pop_back(); 900 } 901 } 902 } 903 904 if (node->paramChildrens[static_cast<size_t>(ParamType::DOUBLE)] != 0U) 905 { 906 char c = reqUrl[pos]; 907 if ((c >= '0' && c <= '9') || c == '+' || c == '-' || c == '.') 908 { 909 char* eptr = nullptr; 910 errno = 0; 911 double value = std::strtod(reqUrl.data() + pos, &eptr); 912 if (errno != ERANGE && eptr != reqUrl.data() + pos) 913 { 914 params->doubleParams.push_back(value); 915 std::pair<unsigned, RoutingParams> ret = 916 find(reqUrl, 917 &nodes[node->paramChildrens[static_cast<size_t>( 918 ParamType::DOUBLE)]], 919 static_cast<size_t>(eptr - reqUrl.data()), params); 920 updateFound(ret); 921 params->doubleParams.pop_back(); 922 } 923 } 924 } 925 926 if (node->paramChildrens[static_cast<size_t>(ParamType::STRING)] != 0U) 927 { 928 size_t epos = pos; 929 for (; epos < reqUrl.size(); epos++) 930 { 931 if (reqUrl[epos] == '/') 932 { 933 break; 934 } 935 } 936 937 if (epos != pos) 938 { 939 params->stringParams.emplace_back( 940 reqUrl.substr(pos, epos - pos)); 941 std::pair<unsigned, RoutingParams> ret = 942 find(reqUrl, 943 &nodes[node->paramChildrens[static_cast<size_t>( 944 ParamType::STRING)]], 945 epos, params); 946 updateFound(ret); 947 params->stringParams.pop_back(); 948 } 949 } 950 951 if (node->paramChildrens[static_cast<size_t>(ParamType::PATH)] != 0U) 952 { 953 size_t epos = reqUrl.size(); 954 955 if (epos != pos) 956 { 957 params->stringParams.emplace_back( 958 reqUrl.substr(pos, epos - pos)); 959 std::pair<unsigned, RoutingParams> ret = 960 find(reqUrl, 961 &nodes[node->paramChildrens[static_cast<size_t>( 962 ParamType::PATH)]], 963 epos, params); 964 updateFound(ret); 965 params->stringParams.pop_back(); 966 } 967 } 968 969 for (const Node::ChildMap::value_type& kv : node->children) 970 { 971 const std::string& fragment = kv.first; 972 const Node* child = &nodes[kv.second]; 973 974 if (reqUrl.compare(pos, fragment.size(), fragment) == 0) 975 { 976 std::pair<unsigned, RoutingParams> ret = 977 find(reqUrl, child, pos + fragment.size(), params); 978 updateFound(ret); 979 } 980 } 981 982 return {found, matchParams}; 983 } 984 985 void add(const std::string& url, unsigned ruleIndex) 986 { 987 size_t idx = 0; 988 989 for (unsigned i = 0; i < url.size(); i++) 990 { 991 char c = url[i]; 992 if (c == '<') 993 { 994 const static std::array<std::pair<ParamType, std::string>, 7> 995 paramTraits = {{ 996 {ParamType::INT, "<int>"}, 997 {ParamType::UINT, "<uint>"}, 998 {ParamType::DOUBLE, "<float>"}, 999 {ParamType::DOUBLE, "<double>"}, 1000 {ParamType::STRING, "<str>"}, 1001 {ParamType::STRING, "<string>"}, 1002 {ParamType::PATH, "<path>"}, 1003 }}; 1004 1005 for (const std::pair<ParamType, std::string>& x : paramTraits) 1006 { 1007 if (url.compare(i, x.second.size(), x.second) == 0) 1008 { 1009 size_t index = static_cast<size_t>(x.first); 1010 if (nodes[idx].paramChildrens[index] == 0U) 1011 { 1012 unsigned newNodeIdx = newNode(); 1013 nodes[idx].paramChildrens[index] = newNodeIdx; 1014 } 1015 idx = nodes[idx].paramChildrens[index]; 1016 i += static_cast<unsigned>(x.second.size()); 1017 break; 1018 } 1019 } 1020 1021 i--; 1022 } 1023 else 1024 { 1025 std::string piece(&c, 1); 1026 if (nodes[idx].children.count(piece) == 0U) 1027 { 1028 unsigned newNodeIdx = newNode(); 1029 nodes[idx].children.emplace(piece, newNodeIdx); 1030 } 1031 idx = nodes[idx].children[piece]; 1032 } 1033 } 1034 if (nodes[idx].ruleIndex != 0U) 1035 { 1036 throw std::runtime_error("handler already exists for " + url); 1037 } 1038 nodes[idx].ruleIndex = ruleIndex; 1039 } 1040 1041 private: 1042 void debugNodePrint(Node* n, size_t level) 1043 { 1044 for (size_t i = 0; i < static_cast<size_t>(ParamType::MAX); i++) 1045 { 1046 if (n->paramChildrens[i] != 0U) 1047 { 1048 BMCWEB_LOG_DEBUG << std::string( 1049 2U * level, ' ') /*<< "("<<n->paramChildrens[i]<<") "*/; 1050 switch (static_cast<ParamType>(i)) 1051 { 1052 case ParamType::INT: 1053 BMCWEB_LOG_DEBUG << "<int>"; 1054 break; 1055 case ParamType::UINT: 1056 BMCWEB_LOG_DEBUG << "<uint>"; 1057 break; 1058 case ParamType::DOUBLE: 1059 BMCWEB_LOG_DEBUG << "<float>"; 1060 break; 1061 case ParamType::STRING: 1062 BMCWEB_LOG_DEBUG << "<str>"; 1063 break; 1064 case ParamType::PATH: 1065 BMCWEB_LOG_DEBUG << "<path>"; 1066 break; 1067 case ParamType::MAX: 1068 BMCWEB_LOG_DEBUG << "<ERROR>"; 1069 break; 1070 } 1071 1072 debugNodePrint(&nodes[n->paramChildrens[i]], level + 1); 1073 } 1074 } 1075 for (const Node::ChildMap::value_type& kv : n->children) 1076 { 1077 BMCWEB_LOG_DEBUG 1078 << std::string(2U * level, ' ') /*<< "(" << kv.second << ") "*/ 1079 << kv.first; 1080 debugNodePrint(&nodes[kv.second], level + 1); 1081 } 1082 } 1083 1084 public: 1085 void debugPrint() 1086 { 1087 debugNodePrint(head(), 0U); 1088 } 1089 1090 private: 1091 const Node* head() const 1092 { 1093 return &nodes.front(); 1094 } 1095 1096 Node* head() 1097 { 1098 return &nodes.front(); 1099 } 1100 1101 unsigned newNode() 1102 { 1103 nodes.resize(nodes.size() + 1); 1104 return static_cast<unsigned>(nodes.size() - 1); 1105 } 1106 1107 std::vector<Node> nodes; 1108 }; 1109 1110 class Router 1111 { 1112 public: 1113 Router() = default; 1114 1115 DynamicRule& newRuleDynamic(const std::string& rule) 1116 { 1117 std::unique_ptr<DynamicRule> ruleObject = 1118 std::make_unique<DynamicRule>(rule); 1119 DynamicRule* ptr = ruleObject.get(); 1120 allRules.emplace_back(std::move(ruleObject)); 1121 1122 return *ptr; 1123 } 1124 1125 template <uint64_t N> 1126 typename black_magic::Arguments<N>::type::template rebind<TaggedRule>& 1127 newRuleTagged(const std::string& rule) 1128 { 1129 using RuleT = typename black_magic::Arguments<N>::type::template rebind< 1130 TaggedRule>; 1131 std::unique_ptr<RuleT> ruleObject = std::make_unique<RuleT>(rule); 1132 RuleT* ptr = ruleObject.get(); 1133 allRules.emplace_back(std::move(ruleObject)); 1134 1135 return *ptr; 1136 } 1137 1138 void internalAddRuleObject(const std::string& rule, BaseRule* ruleObject) 1139 { 1140 if (ruleObject == nullptr) 1141 { 1142 return; 1143 } 1144 for (size_t method = 0, methodBit = 1; method <= methodNotAllowedIndex; 1145 method++, methodBit <<= 1) 1146 { 1147 if ((ruleObject->methodsBitfield & methodBit) > 0U) 1148 { 1149 perMethods[method].rules.emplace_back(ruleObject); 1150 perMethods[method].trie.add( 1151 rule, static_cast<unsigned>( 1152 perMethods[method].rules.size() - 1U)); 1153 // directory case: 1154 // request to `/about' url matches `/about/' rule 1155 if (rule.size() > 2 && rule.back() == '/') 1156 { 1157 perMethods[method].trie.add( 1158 rule.substr(0, rule.size() - 1), 1159 static_cast<unsigned>(perMethods[method].rules.size() - 1160 1)); 1161 } 1162 } 1163 } 1164 } 1165 1166 void validate() 1167 { 1168 for (std::unique_ptr<BaseRule>& rule : allRules) 1169 { 1170 if (rule) 1171 { 1172 std::unique_ptr<BaseRule> upgraded = rule->upgrade(); 1173 if (upgraded) 1174 { 1175 rule = std::move(upgraded); 1176 } 1177 rule->validate(); 1178 internalAddRuleObject(rule->rule, rule.get()); 1179 } 1180 } 1181 for (PerMethod& perMethod : perMethods) 1182 { 1183 perMethod.trie.validate(); 1184 } 1185 } 1186 1187 struct FindRoute 1188 { 1189 BaseRule* rule = nullptr; 1190 RoutingParams params; 1191 }; 1192 1193 struct FindRouteResponse 1194 { 1195 std::string allowHeader; 1196 FindRoute route; 1197 }; 1198 1199 FindRoute findRouteByIndex(std::string_view url, size_t index) const 1200 { 1201 FindRoute route; 1202 if (index >= perMethods.size()) 1203 { 1204 BMCWEB_LOG_CRITICAL << "Bad index???"; 1205 return route; 1206 } 1207 const PerMethod& perMethod = perMethods[index]; 1208 std::pair<unsigned, RoutingParams> found = perMethod.trie.find(url); 1209 if (found.first >= perMethod.rules.size()) 1210 { 1211 throw std::runtime_error("Trie internal structure corrupted!"); 1212 } 1213 // Found a 404 route, switch that in 1214 if (found.first != 0U) 1215 { 1216 route.rule = perMethod.rules[found.first]; 1217 route.params = std::move(found.second); 1218 } 1219 return route; 1220 } 1221 1222 FindRouteResponse findRoute(Request& req) const 1223 { 1224 FindRouteResponse findRoute; 1225 1226 std::optional<HttpVerb> verb = httpVerbFromBoost(req.method()); 1227 if (!verb) 1228 { 1229 return findRoute; 1230 } 1231 size_t reqMethodIndex = static_cast<size_t>(*verb); 1232 // Check to see if this url exists at any verb 1233 for (size_t perMethodIndex = 0; perMethodIndex <= maxVerbIndex; 1234 perMethodIndex++) 1235 { 1236 // Make sure it's safe to deference the array at that index 1237 static_assert(maxVerbIndex < 1238 std::tuple_size_v<decltype(perMethods)>); 1239 FindRoute route = 1240 findRouteByIndex(req.url().encoded_path(), perMethodIndex); 1241 if (route.rule == nullptr) 1242 { 1243 continue; 1244 } 1245 if (!findRoute.allowHeader.empty()) 1246 { 1247 findRoute.allowHeader += ", "; 1248 } 1249 HttpVerb thisVerb = static_cast<HttpVerb>(perMethodIndex); 1250 findRoute.allowHeader += httpVerbToString(thisVerb); 1251 if (perMethodIndex == reqMethodIndex) 1252 { 1253 findRoute.route = route; 1254 } 1255 } 1256 return findRoute; 1257 } 1258 1259 static bool isUserPrivileged( 1260 Request& req, const std::shared_ptr<bmcweb::AsyncResp>& asyncResp, 1261 BaseRule& rule, const dbus::utility::DBusPropertiesMap& userInfoMap) 1262 { 1263 std::string userRole{}; 1264 const std::string* userRolePtr = nullptr; 1265 const bool* remoteUser = nullptr; 1266 const bool* passwordExpired = nullptr; 1267 1268 const bool success = sdbusplus::unpackPropertiesNoThrow( 1269 redfish::dbus_utils::UnpackErrorPrinter(), userInfoMap, 1270 "UserPrivilege", userRolePtr, "RemoteUser", remoteUser, 1271 "UserPasswordExpired", passwordExpired); 1272 1273 if (!success) 1274 { 1275 asyncResp->res.result( 1276 boost::beast::http::status::internal_server_error); 1277 return false; 1278 } 1279 1280 if (userRolePtr != nullptr) 1281 { 1282 userRole = *userRolePtr; 1283 BMCWEB_LOG_DEBUG << "userName = " << req.session->username 1284 << " userRole = " << *userRolePtr; 1285 } 1286 1287 if (remoteUser == nullptr) 1288 { 1289 BMCWEB_LOG_ERROR << "RemoteUser property missing or wrong type"; 1290 asyncResp->res.result( 1291 boost::beast::http::status::internal_server_error); 1292 return false; 1293 } 1294 bool expired = false; 1295 if (passwordExpired == nullptr) 1296 { 1297 if (!*remoteUser) 1298 { 1299 BMCWEB_LOG_ERROR 1300 << "UserPasswordExpired property is expected for" 1301 " local user but is missing or wrong type"; 1302 asyncResp->res.result( 1303 boost::beast::http::status::internal_server_error); 1304 return false; 1305 } 1306 } 1307 else 1308 { 1309 expired = *passwordExpired; 1310 } 1311 1312 // Get the user's privileges from the role 1313 redfish::Privileges userPrivileges = 1314 redfish::getUserPrivileges(userRole); 1315 1316 // Set isConfigureSelfOnly based on D-Bus results. This 1317 // ignores the results from both pamAuthenticateUser and the 1318 // value from any previous use of this session. 1319 req.session->isConfigureSelfOnly = expired; 1320 1321 // Modify privileges if isConfigureSelfOnly. 1322 if (req.session->isConfigureSelfOnly) 1323 { 1324 // Remove all privileges except ConfigureSelf 1325 userPrivileges = userPrivileges.intersection( 1326 redfish::Privileges{"ConfigureSelf"}); 1327 BMCWEB_LOG_DEBUG << "Operation limited to ConfigureSelf"; 1328 } 1329 1330 if (!rule.checkPrivileges(userPrivileges)) 1331 { 1332 asyncResp->res.result(boost::beast::http::status::forbidden); 1333 if (req.session->isConfigureSelfOnly) 1334 { 1335 redfish::messages::passwordChangeRequired( 1336 asyncResp->res, crow::utility::urlFromPieces( 1337 "redfish", "v1", "AccountService", 1338 "Accounts", req.session->username)); 1339 } 1340 return false; 1341 } 1342 1343 req.userRole = userRole; 1344 1345 return true; 1346 } 1347 1348 template <typename CallbackFn> 1349 void afterGetUserInfo(Request& req, 1350 const std::shared_ptr<bmcweb::AsyncResp>& asyncResp, 1351 BaseRule& rule, CallbackFn&& callback, 1352 const boost::system::error_code& ec, 1353 const dbus::utility::DBusPropertiesMap& userInfoMap) 1354 { 1355 if (ec) 1356 { 1357 BMCWEB_LOG_ERROR << "GetUserInfo failed..."; 1358 asyncResp->res.result( 1359 boost::beast::http::status::internal_server_error); 1360 return; 1361 } 1362 1363 if (!Router::isUserPrivileged(req, asyncResp, rule, userInfoMap)) 1364 { 1365 // User is not privileged 1366 BMCWEB_LOG_ERROR << "Insufficient Privilege"; 1367 asyncResp->res.result(boost::beast::http::status::forbidden); 1368 return; 1369 } 1370 callback(req); 1371 } 1372 1373 template <typename CallbackFn> 1374 void validatePrivilege(Request& req, 1375 const std::shared_ptr<bmcweb::AsyncResp>& asyncResp, 1376 BaseRule& rule, CallbackFn&& callback) 1377 { 1378 if (req.session == nullptr) 1379 { 1380 return; 1381 } 1382 std::string username = req.session->username; 1383 crow::connections::systemBus->async_method_call( 1384 [this, &req, asyncResp, &rule, 1385 callback(std::forward<CallbackFn>(callback))]( 1386 const boost::system::error_code& ec, 1387 const dbus::utility::DBusPropertiesMap& userInfoMap) mutable { 1388 afterGetUserInfo(req, asyncResp, rule, 1389 std::forward<CallbackFn>(callback), ec, 1390 userInfoMap); 1391 }, 1392 "xyz.openbmc_project.User.Manager", "/xyz/openbmc_project/user", 1393 "xyz.openbmc_project.User.Manager", "GetUserInfo", username); 1394 } 1395 1396 template <typename Adaptor> 1397 void handleUpgrade(Request& req, 1398 const std::shared_ptr<bmcweb::AsyncResp>& asyncResp, 1399 Adaptor&& adaptor) 1400 { 1401 std::optional<HttpVerb> verb = httpVerbFromBoost(req.method()); 1402 if (!verb || static_cast<size_t>(*verb) >= perMethods.size()) 1403 { 1404 asyncResp->res.result(boost::beast::http::status::not_found); 1405 return; 1406 } 1407 PerMethod& perMethod = perMethods[static_cast<size_t>(*verb)]; 1408 Trie& trie = perMethod.trie; 1409 std::vector<BaseRule*>& rules = perMethod.rules; 1410 1411 const std::pair<unsigned, RoutingParams>& found = 1412 trie.find(req.url().encoded_path()); 1413 unsigned ruleIndex = found.first; 1414 if (ruleIndex == 0U) 1415 { 1416 BMCWEB_LOG_DEBUG << "Cannot match rules " 1417 << req.url().encoded_path(); 1418 asyncResp->res.result(boost::beast::http::status::not_found); 1419 return; 1420 } 1421 1422 if (ruleIndex >= rules.size()) 1423 { 1424 throw std::runtime_error("Trie internal structure corrupted!"); 1425 } 1426 1427 BaseRule& rule = *rules[ruleIndex]; 1428 size_t methods = rule.getMethods(); 1429 if ((methods & (1U << static_cast<size_t>(*verb))) == 0) 1430 { 1431 BMCWEB_LOG_DEBUG 1432 << "Rule found but method mismatch: " 1433 << req.url().encoded_path() << " with " << req.methodString() 1434 << "(" << static_cast<uint32_t>(*verb) << ") / " << methods; 1435 asyncResp->res.result(boost::beast::http::status::not_found); 1436 return; 1437 } 1438 1439 BMCWEB_LOG_DEBUG << "Matched rule (upgrade) '" << rule.rule << "' " 1440 << static_cast<uint32_t>(*verb) << " / " << methods; 1441 1442 // TODO(ed) This should be able to use std::bind_front, but it doesn't 1443 // appear to work with the std::move on adaptor. 1444 validatePrivilege( 1445 req, asyncResp, rule, 1446 [&rule, asyncResp, adaptor(std::forward<Adaptor>(adaptor))]( 1447 Request& thisReq) mutable { 1448 rule.handleUpgrade(thisReq, asyncResp, std::move(adaptor)); 1449 }); 1450 } 1451 1452 void handle(Request& req, 1453 const std::shared_ptr<bmcweb::AsyncResp>& asyncResp) 1454 { 1455 std::optional<HttpVerb> verb = httpVerbFromBoost(req.method()); 1456 if (!verb || static_cast<size_t>(*verb) >= perMethods.size()) 1457 { 1458 asyncResp->res.result(boost::beast::http::status::not_found); 1459 return; 1460 } 1461 1462 FindRouteResponse foundRoute = findRoute(req); 1463 1464 if (foundRoute.route.rule == nullptr) 1465 { 1466 // Couldn't find a normal route with any verb, try looking for a 404 1467 // route 1468 if (foundRoute.allowHeader.empty()) 1469 { 1470 foundRoute.route = 1471 findRouteByIndex(req.url().encoded_path(), notFoundIndex); 1472 } 1473 else 1474 { 1475 // See if we have a method not allowed (405) handler 1476 foundRoute.route = findRouteByIndex(req.url().encoded_path(), 1477 methodNotAllowedIndex); 1478 } 1479 } 1480 1481 // Fill in the allow header if it's valid 1482 if (!foundRoute.allowHeader.empty()) 1483 { 1484 1485 asyncResp->res.addHeader(boost::beast::http::field::allow, 1486 foundRoute.allowHeader); 1487 } 1488 1489 // If we couldn't find a real route or a 404 route, return a generic 1490 // response 1491 if (foundRoute.route.rule == nullptr) 1492 { 1493 if (foundRoute.allowHeader.empty()) 1494 { 1495 asyncResp->res.result(boost::beast::http::status::not_found); 1496 } 1497 else 1498 { 1499 asyncResp->res.result( 1500 boost::beast::http::status::method_not_allowed); 1501 } 1502 return; 1503 } 1504 1505 BaseRule& rule = *foundRoute.route.rule; 1506 RoutingParams params = std::move(foundRoute.route.params); 1507 1508 BMCWEB_LOG_DEBUG << "Matched rule '" << rule.rule << "' " 1509 << static_cast<uint32_t>(*verb) << " / " 1510 << rule.getMethods(); 1511 1512 if (req.session == nullptr) 1513 { 1514 rule.handle(req, asyncResp, params); 1515 return; 1516 } 1517 validatePrivilege(req, asyncResp, rule, 1518 [&rule, asyncResp, params](Request& thisReq) mutable { 1519 rule.handle(thisReq, asyncResp, params); 1520 }); 1521 } 1522 1523 void debugPrint() 1524 { 1525 for (size_t i = 0; i < perMethods.size(); i++) 1526 { 1527 BMCWEB_LOG_DEBUG << boost::beast::http::to_string( 1528 static_cast<boost::beast::http::verb>(i)); 1529 perMethods[i].trie.debugPrint(); 1530 } 1531 } 1532 1533 std::vector<const std::string*> getRoutes(const std::string& parent) 1534 { 1535 std::vector<const std::string*> ret; 1536 1537 for (const PerMethod& pm : perMethods) 1538 { 1539 std::vector<unsigned> x; 1540 pm.trie.findRouteIndexes(parent, x); 1541 for (unsigned index : x) 1542 { 1543 ret.push_back(&pm.rules[index]->rule); 1544 } 1545 } 1546 return ret; 1547 } 1548 1549 private: 1550 struct PerMethod 1551 { 1552 std::vector<BaseRule*> rules; 1553 Trie trie; 1554 // rule index 0 has special meaning; preallocate it to avoid 1555 // duplication. 1556 PerMethod() : rules(1) 1557 {} 1558 }; 1559 1560 std::array<PerMethod, methodNotAllowedIndex + 1> perMethods; 1561 std::vector<std::unique_ptr<BaseRule>> allRules; 1562 }; 1563 } // namespace crow 1564