1 /* Icinga 2 | (c) 2012 Icinga GmbH | GPLv2+ */
2
3 #include "remote/configfileshandler.hpp"
4 #include "remote/configpackageutility.hpp"
5 #include "remote/httputility.hpp"
6 #include "remote/filterutility.hpp"
7 #include "base/exception.hpp"
8 #include "base/utility.hpp"
9 #include <boost/algorithm/string/join.hpp>
10 #include <fstream>
11
12 using namespace icinga;
13
14 REGISTER_URLHANDLER("/v1/config/files", ConfigFilesHandler);
15
HandleRequest(AsioTlsStream & stream,const ApiUser::Ptr & user,boost::beast::http::request<boost::beast::http::string_body> & request,const Url::Ptr & url,boost::beast::http::response<boost::beast::http::string_body> & response,const Dictionary::Ptr & params,boost::asio::yield_context & yc,HttpServerConnection & server)16 bool ConfigFilesHandler::HandleRequest(
17 AsioTlsStream& stream,
18 const ApiUser::Ptr& user,
19 boost::beast::http::request<boost::beast::http::string_body>& request,
20 const Url::Ptr& url,
21 boost::beast::http::response<boost::beast::http::string_body>& response,
22 const Dictionary::Ptr& params,
23 boost::asio::yield_context& yc,
24 HttpServerConnection& server
25 )
26 {
27 namespace http = boost::beast::http;
28
29 if (request.method() != http::verb::get)
30 return false;
31
32 const std::vector<String>& urlPath = url->GetPath();
33
34 if (urlPath.size() >= 4)
35 params->Set("package", urlPath[3]);
36
37 if (urlPath.size() >= 5)
38 params->Set("stage", urlPath[4]);
39
40 if (urlPath.size() >= 6) {
41 std::vector<String> tmpPath(urlPath.begin() + 5, urlPath.end());
42 params->Set("path", boost::algorithm::join(tmpPath, "/"));
43 }
44
45 if (request[http::field::accept] == "application/json") {
46 HttpUtility::SendJsonError(response, params, 400, "Invalid Accept header. Either remove the Accept header or set it to 'application/octet-stream'.");
47 return true;
48 }
49
50 FilterUtility::CheckPermission(user, "config/query");
51
52 String packageName = HttpUtility::GetLastParameter(params, "package");
53 String stageName = HttpUtility::GetLastParameter(params, "stage");
54
55 if (!ConfigPackageUtility::ValidatePackageName(packageName)) {
56 HttpUtility::SendJsonError(response, params, 400, "Invalid package name.");
57 return true;
58 }
59
60 if (!ConfigPackageUtility::ValidateStageName(stageName)) {
61 HttpUtility::SendJsonError(response, params, 400, "Invalid stage name.");
62 return true;
63 }
64
65 String relativePath = HttpUtility::GetLastParameter(params, "path");
66
67 if (ConfigPackageUtility::ContainsDotDot(relativePath)) {
68 HttpUtility::SendJsonError(response, params, 400, "Path contains '..' (not allowed).");
69 return true;
70 }
71
72 String path = ConfigPackageUtility::GetPackageDir() + "/" + packageName + "/" + stageName + "/" + relativePath;
73
74 if (!Utility::PathExists(path)) {
75 HttpUtility::SendJsonError(response, params, 404, "Path not found.");
76 return true;
77 }
78
79 try {
80 std::ifstream fp(path.CStr(), std::ifstream::in | std::ifstream::binary);
81 fp.exceptions(std::ifstream::badbit);
82
83 String content((std::istreambuf_iterator<char>(fp)), std::istreambuf_iterator<char>());
84 response.result(http::status::ok);
85 response.set(http::field::content_type, "application/octet-stream");
86 response.body() = content;
87 response.content_length(response.body().size());
88 } catch (const std::exception& ex) {
89 HttpUtility::SendJsonError(response, params, 500, "Could not read file.",
90 DiagnosticInformation(ex));
91 }
92
93 return true;
94 }
95