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