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