1 /* Icinga 2 | (c) 2012 Icinga GmbH | GPLv2+ */
2 
3 #include "remote/configpackageshandler.hpp"
4 #include "remote/configpackageutility.hpp"
5 #include "remote/httputility.hpp"
6 #include "remote/filterutility.hpp"
7 #include "base/exception.hpp"
8 
9 using namespace icinga;
10 
11 REGISTER_URLHANDLER("/v1/config/packages", ConfigPackagesHandler);
12 
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)13 bool ConfigPackagesHandler::HandleRequest(
14 	AsioTlsStream& stream,
15 	const ApiUser::Ptr& user,
16 	boost::beast::http::request<boost::beast::http::string_body>& request,
17 	const Url::Ptr& url,
18 	boost::beast::http::response<boost::beast::http::string_body>& response,
19 	const Dictionary::Ptr& params,
20 	boost::asio::yield_context& yc,
21 	HttpServerConnection& server
22 )
23 {
24 	namespace http = boost::beast::http;
25 
26 	if (url->GetPath().size() > 4)
27 		return false;
28 
29 	if (request.method() == http::verb::get)
30 		HandleGet(user, request, url, response, params);
31 	else if (request.method() == http::verb::post)
32 		HandlePost(user, request, url, response, params);
33 	else if (request.method() == http::verb::delete_)
34 		HandleDelete(user, request, url, response, params);
35 	else
36 		return false;
37 
38 	return true;
39 }
40 
HandleGet(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)41 void ConfigPackagesHandler::HandleGet(
42 	const ApiUser::Ptr& user,
43 	boost::beast::http::request<boost::beast::http::string_body>& request,
44 	const Url::Ptr& url,
45 	boost::beast::http::response<boost::beast::http::string_body>& response,
46 	const Dictionary::Ptr& params
47 )
48 {
49 	namespace http = boost::beast::http;
50 
51 	FilterUtility::CheckPermission(user, "config/query");
52 
53 	std::vector<String> packages;
54 
55 	try {
56 		packages = ConfigPackageUtility::GetPackages();
57 	} catch (const std::exception& ex) {
58 		HttpUtility::SendJsonError(response, params, 500, "Could not retrieve packages.",
59 			DiagnosticInformation(ex));
60 		return;
61 	}
62 
63 	ArrayData results;
64 
65 	{
66 		std::unique_lock<std::mutex> lock(ConfigPackageUtility::GetStaticPackageMutex());
67 
68 		for (const String& package : packages) {
69 			String activeStage;
70 
71 			try {
72 				activeStage = ConfigPackageUtility::GetActiveStage(package);
73 			} catch (const std::exception&) { } /* Should never happen. */
74 
75 			results.emplace_back(new Dictionary({
76 				{ "name", package },
77 				{ "stages", Array::FromVector(ConfigPackageUtility::GetStages(package)) },
78 				{ "active-stage", activeStage }
79 			}));
80 		}
81 	}
82 
83 	Dictionary::Ptr result = new Dictionary({
84 		{ "results", new Array(std::move(results)) }
85 	});
86 
87 	response.result(http::status::ok);
88 	HttpUtility::SendJsonBody(response, params, result);
89 }
90 
HandlePost(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)91 void ConfigPackagesHandler::HandlePost(
92 	const ApiUser::Ptr& user,
93 	boost::beast::http::request<boost::beast::http::string_body>& request,
94 	const Url::Ptr& url,
95 	boost::beast::http::response<boost::beast::http::string_body>& response,
96 	const Dictionary::Ptr& params
97 )
98 {
99 	namespace http = boost::beast::http;
100 
101 	FilterUtility::CheckPermission(user, "config/modify");
102 
103 	if (url->GetPath().size() >= 4)
104 		params->Set("package", url->GetPath()[3]);
105 
106 	String packageName = HttpUtility::GetLastParameter(params, "package");
107 
108 	if (!ConfigPackageUtility::ValidatePackageName(packageName)) {
109 		HttpUtility::SendJsonError(response, params, 400, "Invalid package name '" + packageName + "'.");
110 		return;
111 	}
112 
113 	try {
114 		std::unique_lock<std::mutex> lock(ConfigPackageUtility::GetStaticPackageMutex());
115 
116 		ConfigPackageUtility::CreatePackage(packageName);
117 	} catch (const std::exception& ex) {
118 		HttpUtility::SendJsonError(response, params, 500, "Could not create package '" + packageName + "'.",
119 			DiagnosticInformation(ex));
120 		return;
121 	}
122 
123 	Dictionary::Ptr result1 = new Dictionary({
124 		{ "code", 200 },
125 		{ "package", packageName },
126 		{ "status", "Created package." }
127 	});
128 
129 	Dictionary::Ptr result = new Dictionary({
130 		{ "results", new Array({ result1 }) }
131 	});
132 
133 	response.result(http::status::ok);
134 	HttpUtility::SendJsonBody(response, params, result);
135 }
136 
HandleDelete(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)137 void ConfigPackagesHandler::HandleDelete(
138 	const ApiUser::Ptr& user,
139 	boost::beast::http::request<boost::beast::http::string_body>& request,
140 	const Url::Ptr& url,
141 	boost::beast::http::response<boost::beast::http::string_body>& response,
142 	const Dictionary::Ptr& params
143 )
144 {
145 	namespace http = boost::beast::http;
146 
147 	FilterUtility::CheckPermission(user, "config/modify");
148 
149 	if (url->GetPath().size() >= 4)
150 		params->Set("package", url->GetPath()[3]);
151 
152 	String packageName = HttpUtility::GetLastParameter(params, "package");
153 
154 	if (!ConfigPackageUtility::ValidatePackageName(packageName)) {
155 		HttpUtility::SendJsonError(response, params, 400, "Invalid package name '" + packageName + "'.");
156 		return;
157 	}
158 
159 	try {
160 		ConfigPackageUtility::DeletePackage(packageName);
161 	} catch (const std::exception& ex) {
162 		HttpUtility::SendJsonError(response, params, 500, "Failed to delete package '" + packageName + "'.",
163 			DiagnosticInformation(ex));
164 		return;
165 	}
166 
167 	Dictionary::Ptr result1 = new Dictionary({
168 		{ "code", 200 },
169 		{ "package", packageName },
170 		{ "status", "Deleted package." }
171 	});
172 
173 	Dictionary::Ptr result = new Dictionary({
174 		{ "results", new Array({ result1 }) }
175 	});
176 
177 	response.result(http::status::ok);
178 	HttpUtility::SendJsonBody(response, params, result);
179 }
180