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