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