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