1 /* Icinga 2 | (c) 2012 Icinga GmbH | GPLv2+ */
2
3 #include "base/logger.hpp"
4 #include "remote/httphandler.hpp"
5 #include "remote/httputility.hpp"
6 #include "base/singleton.hpp"
7 #include "base/exception.hpp"
8 #include <boost/algorithm/string/join.hpp>
9 #include <boost/beast/http.hpp>
10
11 using namespace icinga;
12
13 Dictionary::Ptr HttpHandler::m_UrlTree;
14
Register(const Url::Ptr & url,const HttpHandler::Ptr & handler)15 void HttpHandler::Register(const Url::Ptr& url, const HttpHandler::Ptr& handler)
16 {
17 if (!m_UrlTree)
18 m_UrlTree = new Dictionary();
19
20 Dictionary::Ptr node = m_UrlTree;
21
22 for (const String& elem : url->GetPath()) {
23 Dictionary::Ptr children = node->Get("children");
24
25 if (!children) {
26 children = new Dictionary();
27 node->Set("children", children);
28 }
29
30 Dictionary::Ptr sub_node = children->Get(elem);
31 if (!sub_node) {
32 sub_node = new Dictionary();
33 children->Set(elem, sub_node);
34 }
35
36 node = sub_node;
37 }
38
39 Array::Ptr handlers = node->Get("handlers");
40
41 if (!handlers) {
42 handlers = new Array();
43 node->Set("handlers", handlers);
44 }
45
46 handlers->Add(handler);
47 }
48
ProcessRequest(AsioTlsStream & stream,const ApiUser::Ptr & user,boost::beast::http::request<boost::beast::http::string_body> & request,boost::beast::http::response<boost::beast::http::string_body> & response,boost::asio::yield_context & yc,HttpServerConnection & server)49 void HttpHandler::ProcessRequest(
50 AsioTlsStream& stream,
51 const ApiUser::Ptr& user,
52 boost::beast::http::request<boost::beast::http::string_body>& request,
53 boost::beast::http::response<boost::beast::http::string_body>& response,
54 boost::asio::yield_context& yc,
55 HttpServerConnection& server
56 )
57 {
58 Dictionary::Ptr node = m_UrlTree;
59 std::vector<HttpHandler::Ptr> handlers;
60
61 Url::Ptr url = new Url(request.target().to_string());
62 auto& path (url->GetPath());
63
64 for (std::vector<String>::size_type i = 0; i <= path.size(); i++) {
65 Array::Ptr current_handlers = node->Get("handlers");
66
67 if (current_handlers) {
68 ObjectLock olock(current_handlers);
69 for (const HttpHandler::Ptr& current_handler : current_handlers) {
70 handlers.push_back(current_handler);
71 }
72 }
73
74 Dictionary::Ptr children = node->Get("children");
75
76 if (!children) {
77 node.reset();
78 break;
79 }
80
81 if (i == path.size())
82 break;
83
84 node = children->Get(path[i]);
85
86 if (!node)
87 break;
88 }
89
90 std::reverse(handlers.begin(), handlers.end());
91
92 Dictionary::Ptr params;
93
94 try {
95 params = HttpUtility::FetchRequestParameters(url, request.body());
96 } catch (const std::exception& ex) {
97 HttpUtility::SendJsonError(response, params, 400, "Invalid request body: " + DiagnosticInformation(ex, false));
98 return;
99 }
100
101 bool processed = false;
102
103 /*
104 * HandleRequest may throw a permission exception.
105 * DO NOT return a specific permission error. This
106 * allows attackers to guess from words which objects
107 * do exist.
108 */
109 try {
110 for (const HttpHandler::Ptr& handler : handlers) {
111 if (handler->HandleRequest(stream, user, request, url, response, params, yc, server)) {
112 processed = true;
113 break;
114 }
115 }
116 } catch (const std::exception& ex) {
117 Log(LogWarning, "HttpServerConnection")
118 << "Error while processing HTTP request: " << ex.what();
119
120 processed = false;
121 }
122
123 if (!processed) {
124 HttpUtility::SendJsonError(response, params, 404, "The requested path '" + boost::algorithm::join(path, "/") +
125 "' could not be found or the request method is not valid for this path.");
126 return;
127 }
128 }
129
130